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
//! uint 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<qsocketdevice.h>
56
#include<qeventloop.h>
61
#include<netinet/in.h>
72
class NDnsWorkerEvent : public QCustomEvent
75
enum Type { WorkerEvent = QEvent::User + 100 };
76
NDnsWorkerEvent(NDnsWorker *);
81
class NDnsWorker : public QThread
84
NDnsWorker(QObject *, const QCString &);
99
//----------------------------------------------------------------------------
101
//----------------------------------------------------------------------------
102
#ifndef HAVE_GETHOSTBYNAME_R
103
static QMutex *workerMutex = 0;
104
static QMutex *workerCancelled = 0;
106
static NDnsManager *man = 0;
107
bool winsock_init = false;
109
class NDnsManager::Item
116
class NDnsManager::Private
119
Item *find(const NDns *n)
121
QPtrListIterator<Item> it(list);
122
for(Item *i; (i = it.current()); ++it) {
129
Item *find(const NDnsWorker *w)
131
QPtrListIterator<Item> it(list);
132
for(Item *i; (i = it.current()); ++it) {
142
NDnsManager::NDnsManager()
144
#ifndef HAVE_GETHOSTBYNAME_R
145
workerMutex = new QMutex;
146
workerCancelled = new QMutex;
152
QSocketDevice *sd = new QSocketDevice;
158
d->list.setAutoDelete(true);
160
connect(qApp, SIGNAL(aboutToQuit()), SLOT(app_aboutToQuit()));
163
NDnsManager::~NDnsManager()
167
#ifndef HAVE_GETHOSTBYNAME_R
170
delete workerCancelled;
175
void NDnsManager::resolve(NDns *self, const QString &name)
179
i->worker = new NDnsWorker(this, name.utf8());
185
void NDnsManager::stop(NDns *self)
187
Item *i = d->find(self);
193
#ifndef HAVE_GETHOSTBYNAME_R
195
workerCancelled->lock();
196
i->worker->cancelled = true;
197
workerCancelled->unlock();
201
bool NDnsManager::isBusy(const NDns *self) const
203
Item *i = d->find(self);
204
return (i ? true: false);
207
bool NDnsManager::event(QEvent *e)
209
if((int)e->type() == (int)NDnsWorkerEvent::WorkerEvent) {
210
NDnsWorkerEvent *we = static_cast<NDnsWorkerEvent*>(e);
211
we->worker->wait(); // ensure that the thread is terminated
213
Item *i = d->find(we->worker);
218
QHostAddress addr = i->worker->addr;
219
NDns *ndns = i->ndns;
221
d->list.removeRef(i);
223
// nuke manager if no longer needed (code that follows MUST BE SAFE!)
226
// requestor still around?
228
ndns->finished(addr);
234
void NDnsManager::tryDestroy()
236
if(d->list.isEmpty()) {
242
void NDnsManager::app_aboutToQuit()
245
QEventLoop *e = qApp->eventLoop();
246
e->processEvents(QEventLoop::WaitForMore);
251
//----------------------------------------------------------------------------
253
//----------------------------------------------------------------------------
255
//! \fn void NDns::resultsReady()
256
//! This signal is emitted when the DNS resolution succeeds or fails.
259
//! Constructs an NDns object with parent \a parent.
260
NDns::NDns(QObject *parent)
266
//! Destroys the object and frees allocated resources.
273
//! Resolves hostname \a host (eg. psi.affinix.com)
274
void NDns::resolve(const QString &host)
278
man = new NDnsManager;
279
man->resolve(this, host);
283
//! Cancels the lookup action.
284
//! \note This will not stop the underlying system call, which must finish before the next lookup will proceed.
292
//! Returns the IP address as a 32-bit integer in host-byte-order. This will be 0 if the lookup failed.
293
//! \sa resultsReady()
294
uint NDns::result() const
296
return addr.ip4Addr();
300
//! Returns the IP address as a string. This will be an empty string if the lookup failed.
301
//! \sa resultsReady()
302
QString NDns::resultString() const
304
return addr.toString();
308
//! Returns TRUE if busy resolving a hostname.
309
bool NDns::isBusy() const
313
return man->isBusy(this);
316
void NDns::finished(const QHostAddress &a)
322
//----------------------------------------------------------------------------
324
//----------------------------------------------------------------------------
325
NDnsWorkerEvent::NDnsWorkerEvent(NDnsWorker *p)
326
:QCustomEvent(WorkerEvent)
331
//----------------------------------------------------------------------------
333
//----------------------------------------------------------------------------
334
NDnsWorker::NDnsWorker(QObject *_par, const QCString &_host)
336
success = cancelled = false;
338
host = _host.copy(); // do we need this to avoid sharing across threads?
341
void NDnsWorker::run()
345
#ifdef HAVE_GETHOSTBYNAME_R
349
gethostbyname_r(host.data(), &buf, char_buf, sizeof(char_buf), &h, &err);
351
// lock for gethostbyname
352
QMutexLocker locker(workerMutex);
355
workerCancelled->lock();
356
bool cancel = cancelled;
357
workerCancelled->unlock();
360
h = gethostbyname(host.data());
365
QApplication::postEvent(par, new NDnsWorkerEvent(this));
369
in_addr a = *((struct in_addr *)h->h_addr_list[0]);
370
addr.setAddress(ntohl(a.s_addr));
373
QApplication::postEvent(par, new NDnsWorkerEvent(this));