~ubuntu-branches/ubuntu/wily/psi/wily-proposed

« back to all changes in this revision

Viewing changes to cutestuff/legacy/ndns.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2009-09-25 17:49:51 UTC
  • mfrom: (6.1.3 sid)
  • Revision ID: james.westby@ubuntu.com-20090925174951-lvm7kdap82o8xhn3
Tags: 0.13-1
* Updated to upstream version 0.13
* Set Standards-Version to 3.8.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * ndns.cpp - native DNS resolution
3
 
 * Copyright (C) 2001, 2002  Justin Karneges
4
 
 *
5
 
 * This library is free software; you can redistribute it and/or
6
 
 * modify it under the terms of the GNU Lesser General Public
7
 
 * License as published by the Free Software Foundation; either
8
 
 * version 2.1 of the License, or (at your option) any later version.
9
 
 *
10
 
 * This library is distributed in the hope that it will be useful,
11
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 
 * Lesser General Public License for more details.
14
 
 *
15
 
 * You should have received a copy of the GNU Lesser General Public
16
 
 * License along with this library; if not, write to the Free Software
17
 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
 
 *
19
 
 */
20
 
 
21
 
//! \class NDns ndns.h
22
 
//! \brief Simple DNS resolution using native system calls
23
 
//!
24
 
//! This class is to be used when Qt's QDns is not good enough.  Because QDns
25
 
//! does not use threads, it cannot make a system call asyncronously.  Thus,
26
 
//! QDns tries to imitate the behavior of each platform's native behavior, and
27
 
//! generally falls short.
28
 
//!
29
 
//! NDns uses a thread to make the system call happen in the background.  This
30
 
//! gives your program native DNS behavior, at the cost of requiring threads
31
 
//! to build.
32
 
//!
33
 
//! \code
34
 
//! #include "ndns.h"
35
 
//!
36
 
//! ...
37
 
//!
38
 
//! NDns dns;
39
 
//! dns.resolve("psi.affinix.com");
40
 
//!
41
 
//! // The class will emit the resultsReady() signal when the resolution
42
 
//! // is finished. You may then retrieve the results:
43
 
//!
44
 
//! QHostAddress ip_address = dns.result();
45
 
//!
46
 
//! // or if you want to get the IP address as a string:
47
 
//!
48
 
//! QString ip_address = dns.resultString();
49
 
//! \endcode
50
 
 
51
 
#include "ndns.h"
52
 
 
53
 
#include <qapplication.h>
54
 
#include <q3socketdevice.h>
55
 
#include <q3ptrlist.h>
56
 
#include <qeventloop.h>
57
 
//Added by qt3to4:
58
 
#include <QCustomEvent>
59
 
#include <QEvent>
60
 
#include <Q3CString>
61
 
 
62
 
#ifdef Q_OS_UNIX
63
 
#include <netdb.h>
64
 
#include <sys/types.h>
65
 
#include <sys/socket.h>
66
 
#include <netinet/in.h>
67
 
#include <arpa/inet.h>
68
 
#endif
69
 
 
70
 
#ifdef Q_OS_WIN32
71
 
#include <windows.h>
72
 
#endif
73
 
 
74
 
// CS_NAMESPACE_BEGIN
75
 
 
76
 
//! \if _hide_doc_
77
 
class NDnsWorkerEvent : public QCustomEvent
78
 
{
79
 
public:
80
 
        enum Type { WorkerEvent = QEvent::User + 100 };
81
 
        NDnsWorkerEvent(NDnsWorker *);
82
 
 
83
 
        NDnsWorker *worker;
84
 
};
85
 
 
86
 
class NDnsWorker : public QThread
87
 
{
88
 
public:
89
 
        NDnsWorker(QObject *, const Q3CString &);
90
 
 
91
 
        bool success;
92
 
        bool cancelled;
93
 
        QHostAddress addr;
94
 
 
95
 
protected:
96
 
        void run();
97
 
 
98
 
private:
99
 
        Q3CString host;
100
 
        QObject *par;
101
 
};
102
 
//! \endif
103
 
 
104
 
//----------------------------------------------------------------------------
105
 
// NDnsManager
106
 
//----------------------------------------------------------------------------
107
 
#ifndef HAVE_GETHOSTBYNAME_R
108
 
static QMutex *workerMutex = 0;
109
 
static QMutex *workerCancelled = 0;
110
 
#endif
111
 
static NDnsManager *man = 0;
112
 
bool winsock_init = false;
113
 
 
114
 
class NDnsManager::Item
115
 
{
116
 
public:
117
 
        NDns *ndns;
118
 
        NDnsWorker *worker;
119
 
};
120
 
 
121
 
class NDnsManager::Private
122
 
{
123
 
public:
124
 
        Item *find(const NDns *n)
125
 
        {
126
 
                Q3PtrListIterator<Item> it(list);
127
 
                for(Item *i; (i = it.current()); ++it) {
128
 
                        if(i->ndns == n)
129
 
                                return i;
130
 
                }
131
 
                return 0;
132
 
        }
133
 
 
134
 
        Item *find(const NDnsWorker *w)
135
 
        {
136
 
                Q3PtrListIterator<Item> it(list);
137
 
                for(Item *i; (i = it.current()); ++it) {
138
 
                        if(i->worker == w)
139
 
                                return i;
140
 
                }
141
 
                return 0;
142
 
        }
143
 
 
144
 
        Q3PtrList<Item> list;
145
 
};
146
 
 
147
 
NDnsManager::NDnsManager()
148
 
{
149
 
#ifndef HAVE_GETHOSTBYNAME_R
150
 
        workerMutex = new QMutex;
151
 
        workerCancelled = new QMutex;
152
 
#endif
153
 
 
154
 
#ifdef Q_OS_WIN32
155
 
        if(!winsock_init) {
156
 
                winsock_init = true;
157
 
                Q3SocketDevice *sd = new Q3SocketDevice;
158
 
                delete sd;
159
 
        }
160
 
#endif
161
 
 
162
 
        d = new Private;
163
 
        d->list.setAutoDelete(true);
164
 
 
165
 
        connect(qApp, SIGNAL(aboutToQuit()), SLOT(app_aboutToQuit()));
166
 
}
167
 
 
168
 
NDnsManager::~NDnsManager()
169
 
{
170
 
        delete d;
171
 
 
172
 
#ifndef HAVE_GETHOSTBYNAME_R
173
 
        delete workerMutex;
174
 
        workerMutex = 0;
175
 
        delete workerCancelled;
176
 
        workerCancelled = 0;
177
 
#endif
178
 
}
179
 
 
180
 
void NDnsManager::resolve(NDns *self, const QString &name)
181
 
{
182
 
        Item *i = new Item;
183
 
        i->ndns = self;
184
 
        i->worker = new NDnsWorker(this, name.utf8());
185
 
        d->list.append(i);
186
 
 
187
 
        i->worker->start();
188
 
}
189
 
 
190
 
void NDnsManager::stop(NDns *self)
191
 
{
192
 
        Item *i = d->find(self);
193
 
        if(!i)
194
 
                return;
195
 
        // disassociate
196
 
        i->ndns = 0;
197
 
 
198
 
#ifndef HAVE_GETHOSTBYNAME_R
199
 
        // cancel
200
 
        workerCancelled->lock();
201
 
        i->worker->cancelled = true;
202
 
        workerCancelled->unlock();
203
 
#endif
204
 
}
205
 
 
206
 
bool NDnsManager::isBusy(const NDns *self) const
207
 
{
208
 
        Item *i = d->find(self);
209
 
        return (i ? true: false);
210
 
}
211
 
 
212
 
bool NDnsManager::event(QEvent *e)
213
 
{
214
 
        if((int)e->type() == (int)NDnsWorkerEvent::WorkerEvent) {
215
 
                NDnsWorkerEvent *we = static_cast<NDnsWorkerEvent*>(e);
216
 
                we->worker->wait(); // ensure that the thread is terminated
217
 
 
218
 
                Item *i = d->find(we->worker);
219
 
                if(!i) {
220
 
                        // should NOT happen
221
 
                        return true;
222
 
                }
223
 
                QHostAddress addr = i->worker->addr;
224
 
                NDns *ndns = i->ndns;
225
 
                delete i->worker;
226
 
                d->list.removeRef(i);
227
 
 
228
 
                // nuke manager if no longer needed (code that follows MUST BE SAFE!)
229
 
                tryDestroy();
230
 
 
231
 
                // requestor still around?
232
 
                if(ndns)
233
 
                        ndns->finished(addr);
234
 
                return true;
235
 
        }
236
 
        return false;
237
 
}
238
 
 
239
 
void NDnsManager::tryDestroy()
240
 
{
241
 
        if(d->list.isEmpty()) {
242
 
                man = 0;
243
 
                deleteLater();
244
 
        }
245
 
}
246
 
 
247
 
void NDnsManager::app_aboutToQuit()
248
 
{
249
 
        while(man) {
250
 
                qApp->processEvents(QEventLoop::WaitForMoreEvents);
251
 
        }
252
 
}
253
 
 
254
 
 
255
 
//----------------------------------------------------------------------------
256
 
// NDns
257
 
//----------------------------------------------------------------------------
258
 
 
259
 
//! \fn void NDns::resultsReady()
260
 
//! This signal is emitted when the DNS resolution succeeds or fails.
261
 
 
262
 
//!
263
 
//! Constructs an NDns object with parent \a parent.
264
 
NDns::NDns(QObject *parent)
265
 
:QObject(parent)
266
 
{
267
 
}
268
 
 
269
 
//!
270
 
//! Destroys the object and frees allocated resources.
271
 
NDns::~NDns()
272
 
{
273
 
        stop();
274
 
}
275
 
 
276
 
//!
277
 
//! Resolves hostname \a host (eg. psi.affinix.com)
278
 
void NDns::resolve(const QString &host)
279
 
{
280
 
        stop();
281
 
        if(!man)
282
 
                man = new NDnsManager;
283
 
        man->resolve(this, host);
284
 
}
285
 
 
286
 
//!
287
 
//! Cancels the lookup action.
288
 
//! \note This will not stop the underlying system call, which must finish before the next lookup will proceed.
289
 
void NDns::stop()
290
 
{
291
 
        if(man)
292
 
                man->stop(this);
293
 
}
294
 
 
295
 
//!
296
 
//! Returns the IP address as QHostAddress.  This will be a Null QHostAddress if the lookup failed.
297
 
//! \sa resultsReady()
298
 
QHostAddress NDns::result() const
299
 
{
300
 
        return addr;
301
 
}
302
 
 
303
 
//!
304
 
//! Returns the IP address as a string.  This will be an empty string if the lookup failed.
305
 
//! \sa resultsReady()
306
 
QString NDns::resultString() const
307
 
{
308
 
        if (addr.isNull()) 
309
 
                return QString();
310
 
        else
311
 
                return addr.toString();
312
 
}
313
 
 
314
 
//!
315
 
//! Returns TRUE if busy resolving a hostname.
316
 
bool NDns::isBusy() const
317
 
{
318
 
        if(!man)
319
 
                return false;
320
 
        return man->isBusy(this);
321
 
}
322
 
 
323
 
void NDns::finished(const QHostAddress &a)
324
 
{
325
 
        addr = a;
326
 
        resultsReady();
327
 
}
328
 
 
329
 
//----------------------------------------------------------------------------
330
 
// NDnsWorkerEvent
331
 
//----------------------------------------------------------------------------
332
 
NDnsWorkerEvent::NDnsWorkerEvent(NDnsWorker *p)
333
 
:QCustomEvent(WorkerEvent)
334
 
{
335
 
        worker = p;
336
 
}
337
 
 
338
 
//----------------------------------------------------------------------------
339
 
// NDnsWorker
340
 
//----------------------------------------------------------------------------
341
 
NDnsWorker::NDnsWorker(QObject *_par, const Q3CString &_host)
342
 
{
343
 
        success = cancelled = false;
344
 
        par = _par;
345
 
        host = _host.copy(); // do we need this to avoid sharing across threads?
346
 
}
347
 
 
348
 
void NDnsWorker::run()
349
 
{
350
 
        hostent *h = 0;
351
 
 
352
 
#ifdef HAVE_GETHOSTBYNAME_R
353
 
        hostent buf;
354
 
        char char_buf[1024];
355
 
        int err;
356
 
        gethostbyname_r(host.data(), &buf, char_buf, sizeof(char_buf), &h, &err);
357
 
#else
358
 
        // lock for gethostbyname
359
 
        QMutexLocker locker(workerMutex);
360
 
 
361
 
        // check for cancel
362
 
        workerCancelled->lock();
363
 
        bool cancel = cancelled;
364
 
        workerCancelled->unlock();
365
 
 
366
 
        if(!cancel)
367
 
                h = gethostbyname(host.data());
368
 
#endif
369
 
 
370
 
        // FIXME: not ipv6 clean, currently.
371
 
        if(!h || h->h_addrtype != AF_INET) {
372
 
                success = false;
373
 
                QApplication::postEvent(par, new NDnsWorkerEvent(this));
374
 
                return;
375
 
        }
376
 
 
377
 
        in_addr a = *((struct in_addr *)h->h_addr_list[0]);
378
 
        addr.setAddress(ntohl(a.s_addr));
379
 
        success = true;
380
 
 
381
 
        QApplication::postEvent(par, new NDnsWorkerEvent(this));
382
 
}
383
 
 
384
 
// CS_NAMESPACE_END