You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
379 lines
7.7 KiB
379 lines
7.7 KiB
/*
|
|
* ndns.cpp - native DNS resolution
|
|
* Copyright (C) 2001, 2002 Justin Karneges
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
//! \class NDns ndns.h
|
|
//! \brief Simple DNS resolution using native system calls
|
|
//!
|
|
//! This class is to be used when Qt's QDns is not good enough. Because QDns
|
|
//! does not use threads, it cannot make a system call asyncronously. Thus,
|
|
//! QDns tries to imitate the behavior of each platform's native behavior, and
|
|
//! generally falls short.
|
|
//!
|
|
//! NDns uses a thread to make the system call happen in the background. This
|
|
//! gives your program native DNS behavior, at the cost of requiring threads
|
|
//! to build.
|
|
//!
|
|
//! \code
|
|
//! #include "ndns.h"
|
|
//!
|
|
//! ...
|
|
//!
|
|
//! NDns dns;
|
|
//! dns.resolve("psi.affinix.com");
|
|
//!
|
|
//! // The class will emit the resultsReady() signal when the resolution
|
|
//! // is finished. You may then retrieve the results:
|
|
//!
|
|
//! uint ip_address = dns.result();
|
|
//!
|
|
//! // or if you want to get the IP address as a string:
|
|
//!
|
|
//! QString ip_address = dns.resultString();
|
|
//! \endcode
|
|
|
|
#include"ndns.h"
|
|
|
|
#include<qapplication.h>
|
|
#include<qsocketdevice.h>
|
|
#include<qptrlist.h>
|
|
#include<qeventloop.h>
|
|
|
|
#ifdef Q_OS_UNIX
|
|
#include<netdb.h>
|
|
#include<sys/types.h>
|
|
#include<netinet/in.h>
|
|
#include<arpa/inet.h>
|
|
#endif
|
|
|
|
#ifdef Q_OS_WIN32
|
|
#include<windows.h>
|
|
#endif
|
|
|
|
// CS_NAMESPACE_BEGIN
|
|
|
|
//! \if _hide_doc_
|
|
class NDnsWorkerEvent : public QCustomEvent
|
|
{
|
|
public:
|
|
enum Type { WorkerEvent = QEvent::User + 100 };
|
|
NDnsWorkerEvent(NDnsWorker *);
|
|
|
|
NDnsWorker *worker;
|
|
};
|
|
|
|
class NDnsWorker : public QThread
|
|
{
|
|
public:
|
|
NDnsWorker(QObject *, const QCString &);
|
|
|
|
bool success;
|
|
bool cancelled;
|
|
QHostAddress addr;
|
|
|
|
protected:
|
|
void run();
|
|
|
|
private:
|
|
QCString host;
|
|
QObject *par;
|
|
};
|
|
//! \endif
|
|
|
|
//----------------------------------------------------------------------------
|
|
// NDnsManager
|
|
//----------------------------------------------------------------------------
|
|
#ifndef HAVE_GETHOSTBYNAME_R
|
|
static QMutex *workerMutex = 0;
|
|
static QMutex *workerCancelled = 0;
|
|
#endif
|
|
static NDnsManager *man = 0;
|
|
bool winsock_init = false;
|
|
|
|
class NDnsManager::Item
|
|
{
|
|
public:
|
|
NDns *ndns;
|
|
NDnsWorker *worker;
|
|
};
|
|
|
|
class NDnsManager::Private
|
|
{
|
|
public:
|
|
Item *find(const NDns *n)
|
|
{
|
|
QPtrListIterator<Item> it(list);
|
|
for(Item *i; (i = it.current()); ++it) {
|
|
if(i->ndns == n)
|
|
return i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Item *find(const NDnsWorker *w)
|
|
{
|
|
QPtrListIterator<Item> it(list);
|
|
for(Item *i; (i = it.current()); ++it) {
|
|
if(i->worker == w)
|
|
return i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
QPtrList<Item> list;
|
|
};
|
|
|
|
NDnsManager::NDnsManager()
|
|
{
|
|
#ifndef HAVE_GETHOSTBYNAME_R
|
|
workerMutex = new QMutex;
|
|
workerCancelled = new QMutex;
|
|
#endif
|
|
|
|
#ifdef Q_OS_WIN32
|
|
if(!winsock_init) {
|
|
winsock_init = true;
|
|
QSocketDevice *sd = new QSocketDevice;
|
|
delete sd;
|
|
}
|
|
#endif
|
|
|
|
d = new Private;
|
|
d->list.setAutoDelete(true);
|
|
|
|
connect(qApp, SIGNAL(aboutToQuit()), SLOT(app_aboutToQuit()));
|
|
}
|
|
|
|
NDnsManager::~NDnsManager()
|
|
{
|
|
delete d;
|
|
|
|
#ifndef HAVE_GETHOSTBYNAME_R
|
|
delete workerMutex;
|
|
workerMutex = 0;
|
|
delete workerCancelled;
|
|
workerCancelled = 0;
|
|
#endif
|
|
}
|
|
|
|
void NDnsManager::resolve(NDns *self, const QString &name)
|
|
{
|
|
Item *i = new Item;
|
|
i->ndns = self;
|
|
i->worker = new NDnsWorker(this, name.utf8());
|
|
d->list.append(i);
|
|
|
|
i->worker->start();
|
|
}
|
|
|
|
void NDnsManager::stop(NDns *self)
|
|
{
|
|
Item *i = d->find(self);
|
|
if(!i)
|
|
return;
|
|
// disassociate
|
|
i->ndns = 0;
|
|
|
|
#ifndef HAVE_GETHOSTBYNAME_R
|
|
// cancel
|
|
workerCancelled->lock();
|
|
i->worker->cancelled = true;
|
|
workerCancelled->unlock();
|
|
#endif
|
|
}
|
|
|
|
bool NDnsManager::isBusy(const NDns *self) const
|
|
{
|
|
Item *i = d->find(self);
|
|
return (i ? true: false);
|
|
}
|
|
|
|
bool NDnsManager::event(QEvent *e)
|
|
{
|
|
if((int)e->type() == (int)NDnsWorkerEvent::WorkerEvent) {
|
|
NDnsWorkerEvent *we = static_cast<NDnsWorkerEvent*>(e);
|
|
we->worker->wait(); // ensure that the thread is terminated
|
|
|
|
Item *i = d->find(we->worker);
|
|
if(!i) {
|
|
// should NOT happen
|
|
return true;
|
|
}
|
|
QHostAddress addr = i->worker->addr;
|
|
NDns *ndns = i->ndns;
|
|
delete i->worker;
|
|
d->list.removeRef(i);
|
|
|
|
// nuke manager if no longer needed (code that follows MUST BE SAFE!)
|
|
tryDestroy();
|
|
|
|
// requestor still around?
|
|
if(ndns)
|
|
ndns->finished(addr);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void NDnsManager::tryDestroy()
|
|
{
|
|
if(d->list.isEmpty()) {
|
|
man = 0;
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
void NDnsManager::app_aboutToQuit()
|
|
{
|
|
while(man) {
|
|
QEventLoop *e = qApp->eventLoop();
|
|
e->processEvents(QEventLoop::WaitForMore);
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// NDns
|
|
//----------------------------------------------------------------------------
|
|
|
|
//! \fn void NDns::resultsReady()
|
|
//! This signal is emitted when the DNS resolution succeeds or fails.
|
|
|
|
//!
|
|
//! Constructs an NDns object with parent \a parent.
|
|
NDns::NDns(QObject *parent)
|
|
:QObject(parent)
|
|
{
|
|
}
|
|
|
|
//!
|
|
//! Destroys the object and frees allocated resources.
|
|
NDns::~NDns()
|
|
{
|
|
stop();
|
|
}
|
|
|
|
//!
|
|
//! Resolves hostname \a host (eg. psi.affinix.com)
|
|
void NDns::resolve(const QString &host)
|
|
{
|
|
stop();
|
|
if(!man)
|
|
man = new NDnsManager;
|
|
man->resolve(this, host);
|
|
}
|
|
|
|
//!
|
|
//! Cancels the lookup action.
|
|
//! \note This will not stop the underlying system call, which must finish before the next lookup will proceed.
|
|
void NDns::stop()
|
|
{
|
|
if(man)
|
|
man->stop(this);
|
|
}
|
|
|
|
//!
|
|
//! Returns the IP address as a 32-bit integer in host-byte-order. This will be 0 if the lookup failed.
|
|
//! \sa resultsReady()
|
|
uint NDns::result() const
|
|
{
|
|
return addr.ip4Addr();
|
|
}
|
|
|
|
//!
|
|
//! Returns the IP address as a string. This will be an empty string if the lookup failed.
|
|
//! \sa resultsReady()
|
|
QString NDns::resultString() const
|
|
{
|
|
return addr.toString();
|
|
}
|
|
|
|
//!
|
|
//! Returns TRUE if busy resolving a hostname.
|
|
bool NDns::isBusy() const
|
|
{
|
|
if(!man)
|
|
return false;
|
|
return man->isBusy(this);
|
|
}
|
|
|
|
void NDns::finished(const QHostAddress &a)
|
|
{
|
|
addr = a;
|
|
resultsReady();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// NDnsWorkerEvent
|
|
//----------------------------------------------------------------------------
|
|
NDnsWorkerEvent::NDnsWorkerEvent(NDnsWorker *p)
|
|
:QCustomEvent(WorkerEvent)
|
|
{
|
|
worker = p;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// NDnsWorker
|
|
//----------------------------------------------------------------------------
|
|
NDnsWorker::NDnsWorker(QObject *_par, const QCString &_host)
|
|
{
|
|
success = cancelled = false;
|
|
par = _par;
|
|
host = _host.copy(); // do we need this to avoid sharing across threads?
|
|
}
|
|
|
|
void NDnsWorker::run()
|
|
{
|
|
hostent *h = 0;
|
|
|
|
#ifdef HAVE_GETHOSTBYNAME_R
|
|
hostent buf;
|
|
char char_buf[1024];
|
|
int err;
|
|
gethostbyname_r(host.data(), &buf, char_buf, sizeof(char_buf), &h, &err);
|
|
#else
|
|
// lock for gethostbyname
|
|
QMutexLocker locker(workerMutex);
|
|
|
|
// check for cancel
|
|
workerCancelled->lock();
|
|
bool cancel = cancelled;
|
|
workerCancelled->unlock();
|
|
|
|
if(!cancel)
|
|
h = gethostbyname(host.data());
|
|
#endif
|
|
|
|
if(!h) {
|
|
success = false;
|
|
QApplication::postEvent(par, new NDnsWorkerEvent(this));
|
|
return;
|
|
}
|
|
|
|
in_addr a = *((struct in_addr *)h->h_addr_list[0]);
|
|
addr.setAddress(ntohl(a.s_addr));
|
|
success = true;
|
|
|
|
QApplication::postEvent(par, new NDnsWorkerEvent(this));
|
|
}
|
|
|
|
// CS_NAMESPACE_END
|
|
|
|
#include "ndns.moc"
|