2
* ndns.cpp - native DNS resolution
3
* Copyright (C) 2001, 2002 Justin Karneges
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.
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.
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
21
//! \class NDns ndns.h
22
//! \brief Simple DNS resolution using native system calls
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.
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
39
//! dns.resolve("psi.affinix.com");
41
//! // The class will emit the resultsReady() signal when the resolution
42
//! // is finished. You may then retrieve the results:
44
//! QHostAddress ip_address = dns.result();
46
//! // or if you want to get the IP address as a string:
48
//! QString ip_address = dns.resultString();
53
#include <qapplication.h>
54
#include <q3socketdevice.h>
55
#include <q3ptrlist.h>
56
#include <qeventloop.h>
58
#include <QCustomEvent>
64
#include <sys/types.h>
65
#include <sys/socket.h>
66
#include <netinet/in.h>
67
#include <arpa/inet.h>
77
class NDnsWorkerEvent : public QCustomEvent
80
enum Type { WorkerEvent = QEvent::User + 100 };
81
NDnsWorkerEvent(NDnsWorker *);
86
class NDnsWorker : public QThread
89
NDnsWorker(QObject *, const Q3CString &);
104
//----------------------------------------------------------------------------
106
//----------------------------------------------------------------------------
107
#ifndef HAVE_GETHOSTBYNAME_R
108
static QMutex *workerMutex = 0;
109
static QMutex *workerCancelled = 0;
111
static NDnsManager *man = 0;
112
bool winsock_init = false;
114
class NDnsManager::Item
121
class NDnsManager::Private
124
Item *find(const NDns *n)
126
Q3PtrListIterator<Item> it(list);
127
for(Item *i; (i = it.current()); ++it) {
134
Item *find(const NDnsWorker *w)
136
Q3PtrListIterator<Item> it(list);
137
for(Item *i; (i = it.current()); ++it) {
144
Q3PtrList<Item> list;
147
NDnsManager::NDnsManager()
149
#ifndef HAVE_GETHOSTBYNAME_R
150
workerMutex = new QMutex;
151
workerCancelled = new QMutex;
157
Q3SocketDevice *sd = new Q3SocketDevice;
163
d->list.setAutoDelete(true);
165
connect(qApp, SIGNAL(aboutToQuit()), SLOT(app_aboutToQuit()));
168
NDnsManager::~NDnsManager()
172
#ifndef HAVE_GETHOSTBYNAME_R
175
delete workerCancelled;
180
void NDnsManager::resolve(NDns *self, const QString &name)
184
i->worker = new NDnsWorker(this, name.utf8());
190
void NDnsManager::stop(NDns *self)
192
Item *i = d->find(self);
198
#ifndef HAVE_GETHOSTBYNAME_R
200
workerCancelled->lock();
201
i->worker->cancelled = true;
202
workerCancelled->unlock();
206
bool NDnsManager::isBusy(const NDns *self) const
208
Item *i = d->find(self);
209
return (i ? true: false);
212
bool NDnsManager::event(QEvent *e)
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
218
Item *i = d->find(we->worker);
223
QHostAddress addr = i->worker->addr;
224
NDns *ndns = i->ndns;
226
d->list.removeRef(i);
228
// nuke manager if no longer needed (code that follows MUST BE SAFE!)
231
// requestor still around?
233
ndns->finished(addr);
239
void NDnsManager::tryDestroy()
241
if(d->list.isEmpty()) {
247
void NDnsManager::app_aboutToQuit()
250
qApp->processEvents(QEventLoop::WaitForMoreEvents);
255
//----------------------------------------------------------------------------
257
//----------------------------------------------------------------------------
259
//! \fn void NDns::resultsReady()
260
//! This signal is emitted when the DNS resolution succeeds or fails.
263
//! Constructs an NDns object with parent \a parent.
264
NDns::NDns(QObject *parent)
270
//! Destroys the object and frees allocated resources.
277
//! Resolves hostname \a host (eg. psi.affinix.com)
278
void NDns::resolve(const QString &host)
282
man = new NDnsManager;
283
man->resolve(this, host);
287
//! Cancels the lookup action.
288
//! \note This will not stop the underlying system call, which must finish before the next lookup will proceed.
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
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
311
return addr.toString();
315
//! Returns TRUE if busy resolving a hostname.
316
bool NDns::isBusy() const
320
return man->isBusy(this);
323
void NDns::finished(const QHostAddress &a)
329
//----------------------------------------------------------------------------
331
//----------------------------------------------------------------------------
332
NDnsWorkerEvent::NDnsWorkerEvent(NDnsWorker *p)
333
:QCustomEvent(WorkerEvent)
338
//----------------------------------------------------------------------------
340
//----------------------------------------------------------------------------
341
NDnsWorker::NDnsWorker(QObject *_par, const Q3CString &_host)
343
success = cancelled = false;
345
host = _host.copy(); // do we need this to avoid sharing across threads?
348
void NDnsWorker::run()
352
#ifdef HAVE_GETHOSTBYNAME_R
356
gethostbyname_r(host.data(), &buf, char_buf, sizeof(char_buf), &h, &err);
358
// lock for gethostbyname
359
QMutexLocker locker(workerMutex);
362
workerCancelled->lock();
363
bool cancel = cancelled;
364
workerCancelled->unlock();
367
h = gethostbyname(host.data());
370
// FIXME: not ipv6 clean, currently.
371
if(!h || h->h_addrtype != AF_INET) {
373
QApplication::postEvent(par, new NDnsWorkerEvent(this));
377
in_addr a = *((struct in_addr *)h->h_addr_list[0]);
378
addr.setAddress(ntohl(a.s_addr));
381
QApplication::postEvent(par, new NDnsWorkerEvent(this));