diff options
Diffstat (limited to 'kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp')
-rw-r--r-- | kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp new file mode 100644 index 00000000..7fe60973 --- /dev/null +++ b/kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp @@ -0,0 +1,378 @@ +/* + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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" |