summaryrefslogtreecommitdiffstats
path: root/src/qt3support/network/q3dns.cpp
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2009-03-23 09:18:55 (GMT)
committerSimon Hausmann <simon.hausmann@nokia.com>2009-03-23 09:18:55 (GMT)
commite5fcad302d86d316390c6b0f62759a067313e8a9 (patch)
treec2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/qt3support/network/q3dns.cpp
downloadQt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip
Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz
Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2
Long live Qt 4.5!
Diffstat (limited to 'src/qt3support/network/q3dns.cpp')
-rw-r--r--src/qt3support/network/q3dns.cpp2620
1 files changed, 2620 insertions, 0 deletions
diff --git a/src/qt3support/network/q3dns.cpp b/src/qt3support/network/q3dns.cpp
new file mode 100644
index 0000000..c53f2ff
--- /dev/null
+++ b/src/qt3support/network/q3dns.cpp
@@ -0,0 +1,2620 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "qbytearray.h"
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_CYGWIN)
+# include "qt_windows.h"
+#else
+# include <sys/types.h>
+# include <netinet/in.h>
+# include <arpa/nameser.h>
+# include <resolv.h>
+extern "C" int res_init();
+#endif
+
+// POSIX Large File Support redefines open -> open64
+#if defined(open)
+# undef open
+#endif
+
+// POSIX Large File Support redefines truncate -> truncate64
+#if defined(truncate)
+# undef truncate
+#endif
+
+// Solaris redefines connect -> __xnet_connect with _XOPEN_SOURCE_EXTENDED.
+#if defined(connect)
+# undef connect
+#endif
+
+// UnixWare 7 redefines socket -> _socket
+#if defined(socket)
+# undef socket
+#endif
+
+#include "q3dns.h"
+
+#ifndef QT_NO_DNS
+
+#include "qdatetime.h"
+#include "q3dict.h"
+#include "q3ptrlist.h"
+#include "qstring.h"
+#include "qtimer.h"
+#include "qapplication.h"
+#include "q3ptrvector.h"
+#include "q3strlist.h"
+#include "q3ptrdict.h"
+#include "qfile.h"
+#include "qtextstream.h"
+#include "q3socketdevice.h"
+#include "q3cleanuphandler.h"
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define Q3DNS_DEBUG
+
+static Q_UINT16 theId; // ### seeded started by now()
+
+
+static QDateTime * originOfTime = 0;
+
+static Q3CleanupHandler<QDateTime> q3dns_cleanup_time;
+
+static Q_UINT32 now()
+{
+ if ( originOfTime )
+ return originOfTime->secsTo( QDateTime::currentDateTime() );
+
+ originOfTime = new QDateTime( QDateTime::currentDateTime() );
+ theId = originOfTime->time().msec() * 60 + originOfTime->time().second();
+ q3dns_cleanup_time.add( &originOfTime );
+ return 0;
+}
+
+
+static Q3PtrList<QHostAddress> * theNs = 0;
+static Q3StrList * theDomains = 0;
+static bool ipv6support = false;
+
+class Q3DnsPrivate {
+public:
+ Q3DnsPrivate() : queryTimer( 0 ), noNames(false)
+ {
+#if defined(Q_DNS_SYNCHRONOUS)
+#if defined(Q_OS_UNIX)
+ noEventLoop = qApp==0 || qApp->loopLevel()==0;
+#else
+ noEventLoop = false;
+#endif
+#endif
+ }
+ ~Q3DnsPrivate()
+ {
+ delete queryTimer;
+ }
+private:
+ QTimer * queryTimer;
+ bool noNames;
+#if defined(Q_DNS_SYNCHRONOUS)
+ bool noEventLoop;
+#endif
+
+ friend class Q3Dns;
+ friend class Q3DnsAnswer;
+};
+
+
+class Q3DnsRR;
+class Q3DnsDomain;
+
+
+
+// Q3DnsRR is the class used to store a single RR. Q3DnsRR can store
+// all of the supported RR types. a Q3DnsRR is always cached.
+
+// Q3DnsRR is mostly constructed from the outside. a but hacky, but
+// permissible since the entire class is internal.
+
+class Q3DnsRR {
+public:
+ Q3DnsRR( const QString & label );
+ ~Q3DnsRR();
+
+public:
+ Q3DnsDomain * domain;
+ Q3Dns::RecordType t;
+ bool nxdomain;
+ bool current;
+ Q_UINT32 expireTime;
+ Q_UINT32 deleteTime;
+ // somewhat space-wasting per-type data
+ // a / aaaa
+ QHostAddress address;
+ // cname / mx / srv / ptr
+ QString target;
+ // mx / srv
+ Q_UINT16 priority;
+ // srv
+ Q_UINT16 weight;
+ Q_UINT16 port;
+ // txt
+ QString text; // could be overloaded into target...
+private:
+
+};
+
+
+class Q3DnsDomain {
+public:
+ Q3DnsDomain( const QString & label );
+ ~Q3DnsDomain();
+
+ static void add( const QString & label, Q3DnsRR * );
+ static Q3PtrList<Q3DnsRR> * cached( const Q3Dns * );
+
+ void take( Q3DnsRR * );
+
+ void sweep( Q_UINT32 thisSweep );
+
+ bool isEmpty() const { return rrs == 0 || rrs->isEmpty(); }
+
+ QString name() const { return l; }
+
+public:
+ QString l;
+ Q3PtrList<Q3DnsRR> * rrs;
+};
+
+
+class Q3DnsQuery: public QTimer { // this inheritance is a very evil hack
+public:
+ Q3DnsQuery():
+ id( 0 ), t( Q3Dns::None ), step(0), started(0),
+ dns( new Q3PtrDict<void>(17) ) {}
+ ~Q3DnsQuery() { delete dns; }
+ Q_UINT16 id;
+ Q3Dns::RecordType t;
+ QString l;
+
+ uint step;
+ Q_UINT32 started;
+
+ Q3PtrDict<void> * dns;
+};
+
+
+
+class Q3DnsAnswer {
+public:
+ Q3DnsAnswer( Q3DnsQuery * );
+ Q3DnsAnswer( const QByteArray &, Q3DnsQuery * );
+ ~Q3DnsAnswer();
+
+ void parse();
+ void notify();
+
+ bool ok;
+
+private:
+ Q3DnsQuery * query;
+
+ Q_UINT8 * answer;
+ int size;
+ int pp;
+
+ Q3PtrList<Q3DnsRR> * rrs;
+
+ // convenience
+ int next;
+ int ttl;
+ QString label;
+ Q3DnsRR * rr;
+
+ QString readString(bool multipleLabels = true);
+ void parseA();
+ void parseAaaa();
+ void parseMx();
+ void parseSrv();
+ void parseCname();
+ void parsePtr();
+ void parseTxt();
+ void parseNs();
+};
+
+
+Q3DnsRR::Q3DnsRR( const QString & label )
+ : domain( 0 ), t( Q3Dns::None ),
+ nxdomain( false ), current( false ),
+ expireTime( 0 ), deleteTime( 0 ),
+ priority( 0 ), weight( 0 ), port( 0 )
+{
+ Q3DnsDomain::add( label, this );
+}
+
+
+// not supposed to be deleted except by Q3DnsDomain
+Q3DnsRR::~Q3DnsRR()
+{
+ // nothing is necessary
+}
+
+
+// this one just sticks in a NXDomain
+Q3DnsAnswer::Q3DnsAnswer( Q3DnsQuery * query_ )
+{
+ ok = true;
+
+ answer = 0;
+ size = 0;
+ query = query_;
+ pp = 0;
+ rrs = new Q3PtrList<Q3DnsRR>;
+ rrs->setAutoDelete( false );
+ next = size;
+ ttl = 0;
+ label.clear();
+ rr = 0;
+
+ Q3DnsRR * newrr = new Q3DnsRR( query->l );
+ newrr->t = query->t;
+ newrr->deleteTime = query->started + 10;
+ newrr->expireTime = query->started + 10;
+ newrr->nxdomain = true;
+ newrr->current = true;
+ rrs->append( newrr );
+}
+
+
+Q3DnsAnswer::Q3DnsAnswer( const QByteArray& answer_,
+ Q3DnsQuery * query_ )
+{
+ ok = true;
+
+ answer = (Q_UINT8 *)(answer_.data());
+ size = (int)answer_.size();
+ query = query_;
+ pp = 0;
+ rrs = new Q3PtrList<Q3DnsRR>;
+ rrs->setAutoDelete( false );
+ next = size;
+ ttl = 0;
+ label.clear();
+ rr = 0;
+}
+
+
+Q3DnsAnswer::~Q3DnsAnswer()
+{
+ if ( !ok && rrs ) {
+ Q3PtrListIterator<Q3DnsRR> it( *rrs );
+ Q3DnsRR * tmprr;
+ while( (tmprr=it.current()) != 0 ) {
+ ++it;
+ tmprr->t = Q3Dns::None; // will be deleted soonish
+ }
+ }
+ delete rrs;
+}
+
+
+QString Q3DnsAnswer::readString(bool multipleLabels)
+{
+ int p = pp;
+ QString r;
+ Q_UINT8 b;
+ for( ;; ) {
+ b = 128;
+ // Read one character
+ if ( p >= 0 && p < size )
+ b = answer[p];
+
+ switch( b >> 6 ) {
+ case 0:
+ // b is less than 64
+ p++;
+
+ // Detect end of data
+ if ( b == 0 ) {
+ if ( p > pp )
+ pp = p;
+ return r.isNull() ? QLatin1String( "." ) : r;
+ }
+
+ // Read a label of size 'b' characters
+ if ( !r.isNull() )
+ r += QLatin1Char('.');
+ while( b-- > 0 )
+ r += QLatin1Char( answer[p++] );
+
+ // Return immediately if we were only supposed to read one
+ // label.
+ if (!multipleLabels)
+ return r;
+
+ break;
+ default:
+ // Ignore unrecognized control character, or p was out of
+ // range.
+ goto not_ok;
+ case 3:
+ // Use the next character to determine the relative offset
+ // to jump to before continuing the packet parsing.
+ int q = ( (answer[p] & 0x3f) << 8 ) + answer[p+1];
+
+ if ( q >= pp || q >= p )
+ goto not_ok;
+ if ( p >= pp )
+ pp = p + 2;
+ p = q;
+ }
+ }
+not_ok:
+ ok = false;
+ return QString();
+}
+
+
+
+void Q3DnsAnswer::parseA()
+{
+ if ( next != pp + 4 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %d bytes long IN A for %s",
+ next - pp, label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->t = Q3Dns::A;
+ rr->address = QHostAddress( ( answer[pp+0] << 24 ) +
+ ( answer[pp+1] << 16 ) +
+ ( answer[pp+2] << 8 ) +
+ ( answer[pp+3] ) );
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN A %s (ttl %d)", label.ascii(),
+ rr->address.toString().ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parseAaaa()
+{
+ if ( next != pp + 16 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %d bytes long IN Aaaa for %s",
+ next - pp, label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->t = Q3Dns::Aaaa;
+ rr->address = QHostAddress( answer+pp );
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN Aaaa %s (ttl %d)", label.ascii(),
+ rr->address.toString().ascii(), ttl );
+#endif
+}
+
+
+
+void Q3DnsAnswer::parseMx()
+{
+ if ( next < pp + 2 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %d bytes long IN MX for %s",
+ next - pp, label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->priority = (answer[pp] << 8) + answer[pp+1];
+ pp += 2;
+ rr->target = readString().lower();
+ if ( !ok ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw bad string in MX for %s", label.ascii() );
+#endif
+ return;
+ }
+ rr->t = Q3Dns::Mx;
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN MX %d %s (ttl %d)", label.ascii(),
+ rr->priority, rr->target.ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parseSrv()
+{
+ if ( next < pp + 6 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %d bytes long IN SRV for %s",
+ next - pp, label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->priority = (answer[pp] << 8) + answer[pp+1];
+ rr->weight = (answer[pp+2] << 8) + answer[pp+3];
+ rr->port = (answer[pp+4] << 8) + answer[pp+5];
+ pp += 6;
+ rr->target = readString().lower();
+ if ( !ok ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw bad string in SRV for %s", label.ascii() );
+#endif
+ return;
+ }
+ rr->t = Q3Dns::Srv;
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN SRV %d %d %d %s (ttl %d)", label.ascii(),
+ rr->priority, rr->weight, rr->port, rr->target.ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parseCname()
+{
+ QString target = readString().lower();
+ if ( !ok ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw bad cname for for %s", label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->t = Q3Dns::Cname;
+ rr->target = target;
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN CNAME %s (ttl %d)", label.ascii(),
+ rr->target.ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parseNs()
+{
+ QString target = readString().lower();
+ if ( !ok ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw bad cname for for %s", label.ascii() );
+#endif
+ return;
+ }
+
+ // parse, but ignore
+
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN NS %s (ttl %d)", label.ascii(),
+ target.ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parsePtr()
+{
+ QString target = readString().lower();
+ if ( !ok ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw bad PTR for for %s", label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->t = Q3Dns::Ptr;
+ rr->target = target;
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN PTR %s (ttl %d)", label.ascii(),
+ rr->target.ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parseTxt()
+{
+ QString text = readString(false);
+ if ( !ok ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw bad TXT for for %s", label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->t = Q3Dns::Txt;
+ rr->text = text;
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN TXT \"%s\" (ttl %d)", label.ascii(),
+ rr->text.ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parse()
+{
+ // okay, do the work...
+ if ( (answer[2] & 0x78) != 0 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: answer to wrong query type (%d)", answer[1] );
+#endif
+ ok = false;
+ return;
+ }
+
+ // AA
+ bool aa = (answer[2] & 4) != 0;
+
+ // TC
+ if ( (answer[2] & 2) != 0 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: truncated answer; pressing on" );
+#endif
+ }
+
+ // RD
+ bool rd = (answer[2] & 1) != 0;
+
+ // we don't test RA
+ // we don't test the MBZ fields
+
+ if ( (answer[3] & 0x0f) == 3 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: saw NXDomain for %s", query->l.ascii() );
+#endif
+ // NXDomain. cache that for one minute.
+ rr = new Q3DnsRR( query->l );
+ rr->t = query->t;
+ rr->deleteTime = query->started + 60;
+ rr->expireTime = query->started + 60;
+ rr->nxdomain = true;
+ rr->current = true;
+ rrs->append( rr );
+ return;
+ }
+
+ if ( (answer[3] & 0x0f) != 0 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: error code %d", answer[3] & 0x0f );
+#endif
+ ok = false;
+ return;
+ }
+
+ int qdcount = ( answer[4] << 8 ) + answer[5];
+ int ancount = ( answer[6] << 8 ) + answer[7];
+ int nscount = ( answer[8] << 8 ) + answer[9];
+ int adcount = (answer[10] << 8 ) +answer[11];
+
+ pp = 12;
+
+ // read query
+ while( qdcount > 0 && pp < size ) {
+ // should I compare the string against query->l?
+ (void)readString();
+ if ( !ok )
+ return;
+ pp += 4;
+ qdcount--;
+ }
+
+ // answers and stuff
+ int rrno = 0;
+ // if we parse the answer completely, but there are no answers,
+ // ignore the entire thing.
+ int answers = 0;
+ while( ( rrno < ancount ||
+ ( ok && answers >0 && rrno < ancount + nscount + adcount ) ) &&
+ pp < size ) {
+ label = readString().lower();
+ if ( !ok )
+ return;
+ int rdlength = 0;
+ if ( pp + 10 <= size )
+ rdlength = ( answer[pp+8] << 8 ) + answer[pp+9];
+ if ( pp + 10 + rdlength > size ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: ran out of stuff to parse (%d+%d>%d (%d)",
+ pp, rdlength, size, rrno < ancount );
+#endif
+ // if we're still in the AN section, we should go back and
+ // at least down the TTLs. probably best to invalidate
+ // the results.
+ // the rrs list is good for this
+ ok = ( rrno < ancount );
+ return;
+ }
+ uint type, clas;
+ type = ( answer[pp+0] << 8 ) + answer[pp+1];
+ clas = ( answer[pp+2] << 8 ) + answer[pp+3];
+ ttl = ( answer[pp+4] << 24 ) + ( answer[pp+5] << 16 ) +
+ ( answer[pp+6] << 8 ) + answer[pp+7];
+ pp = pp + 10;
+ if ( clas != 1 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: class %d (not internet) for %s",
+ clas, label.isNull() ? "." : label.ascii() );
+#endif
+ } else {
+ next = pp + rdlength;
+ rr = 0;
+ switch( type ) {
+ case 1:
+ parseA();
+ break;
+ case 28:
+ parseAaaa();
+ break;
+ case 15:
+ parseMx();
+ break;
+ case 33:
+ parseSrv();
+ break;
+ case 5:
+ parseCname();
+ break;
+ case 12:
+ parsePtr();
+ break;
+ case 16:
+ parseTxt();
+ break;
+ case 2:
+ parseNs();
+ break;
+ default:
+ // something we don't know
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: type %d for %s", type,
+ label.isNull() ? "." : label.ascii() );
+#endif
+ break;
+ }
+ if ( rr ) {
+ rr->deleteTime = 0;
+ if ( ttl > 0 )
+ rr->expireTime = query->started + ttl;
+ else
+ rr->expireTime = query->started + 20;
+ if ( rrno < ancount ) {
+ answers++;
+ rr->deleteTime = rr->expireTime;
+ }
+ rr->current = true;
+ rrs->append( rr );
+ }
+ }
+ if ( !ok )
+ return;
+ pp = next;
+ next = size;
+ rrno++;
+ }
+ if ( answers == 0 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: answer contained no answers" );
+#endif
+ ok = ( aa && rd );
+ }
+
+ // now go through the list and mark all the As that are referenced
+ // by something we care about. we want to cache such As.
+ rrs->first();
+ Q3Dict<void> used( 17 );
+ used.setAutoDelete( false );
+ while( (rr=rrs->current()) != 0 ) {
+ rrs->next();
+ if ( rr->target.length() && rr->deleteTime > 0 && rr->current )
+ used.insert( rr->target, (void*)42 );
+ if ( ( rr->t == Q3Dns::A || rr->t == Q3Dns::Aaaa ) &&
+ used.find( rr->domain->name() ) != 0 )
+ rr->deleteTime = rr->expireTime;
+ }
+
+ // next, for each RR, delete any older RRs that are equal to it
+ rrs->first();
+ while( (rr=rrs->current()) != 0 ) {
+ rrs->next();
+ if ( rr && rr->domain && rr->domain->rrs ) {
+ Q3PtrList<Q3DnsRR> * drrs = rr->domain->rrs;
+ drrs->first();
+ Q3DnsRR * older;
+ while( (older=drrs->current()) != 0 ) {
+ if ( older != rr &&
+ older->t == rr->t &&
+ older->nxdomain == rr->nxdomain &&
+ older->address == rr->address &&
+ older->target == rr->target &&
+ older->priority == rr->priority &&
+ older->weight == rr->weight &&
+ older->port == rr->port &&
+ older->text == rr->text ) {
+ // well, it's equal, but it's not the same. so we kill it,
+ // but use its expiry time.
+#if defined(Q3DNS_DEBUG)
+ qDebug( "killing off old %d for %s, expire was %d",
+ older->t, older->domain->name().latin1(),
+ rr->expireTime );
+#endif
+ older->t = Q3Dns::None;
+ rr->expireTime = QMAX( older->expireTime, rr->expireTime );
+ rr->deleteTime = QMAX( older->deleteTime, rr->deleteTime );
+ older->deleteTime = 0;
+#if defined(Q3DNS_DEBUG)
+ qDebug( " adjusted expire is %d", rr->expireTime );
+#endif
+ }
+ drrs->next();
+ }
+ }
+ }
+
+#if defined(Q3DNS_DEBUG)
+ //qDebug( "DNS Manager: ()" );
+#endif
+}
+
+
+class Q3DnsUgleHack: public Q3Dns {
+public:
+ void ugle( bool emitAnyway=false );
+};
+
+
+void Q3DnsAnswer::notify()
+{
+ if ( !rrs || !ok || !query || !query->dns )
+ return;
+
+ Q3PtrDict<void> notified;
+ notified.setAutoDelete( false );
+
+ Q3PtrDictIterator<void> it( *query->dns );
+ Q3Dns * dns;
+ it.toFirst();
+ while( (dns=(Q3Dns*)(it.current())) != 0 ) {
+ ++it;
+ if ( notified.find( (void*)dns ) == 0 ) {
+ notified.insert( (void*)dns, (void*)42 );
+ if ( rrs->count() == 0 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: found no answers!" );
+#endif
+ dns->d->noNames = true;
+ ((Q3DnsUgleHack*)dns)->ugle( true );
+ } else {
+ QStringList n = dns->qualifiedNames();
+ if ( query && n.contains(query->l) )
+ ((Q3DnsUgleHack*)dns)->ugle();
+#if defined(Q3DNS_DEBUG)
+ else
+ qDebug( "DNS Manager: DNS thing %s not notified for %s",
+ dns->label().ascii(), query->l.ascii() );
+#endif
+ }
+ }
+ }
+}
+
+
+//
+//
+// Q3DnsManager
+//
+//
+
+
+class Q3DnsManager: public Q3DnsSocket {
+private:
+public: // just to silence the moronic g++.
+ Q3DnsManager();
+ ~Q3DnsManager();
+public:
+ static Q3DnsManager * manager();
+
+ Q3DnsDomain * domain( const QString & );
+
+ void transmitQuery( Q3DnsQuery * );
+ void transmitQuery( int );
+
+ // reimplementation of the slots
+ void cleanCache();
+ void retransmit();
+ void answer();
+
+public:
+ Q3PtrVector<Q3DnsQuery> queries;
+ Q3Dict<Q3DnsDomain> cache;
+ Q3SocketDevice * ipv4Socket;
+#if !defined (QT_NO_IPV6)
+ Q3SocketDevice * ipv6Socket;
+#endif
+};
+
+
+
+static Q3DnsManager * globalManager = 0;
+
+static void cleanupDns()
+{
+ delete globalManager;
+ globalManager = 0;
+}
+
+Q3DnsManager * Q3DnsManager::manager()
+{
+ if ( !globalManager ) {
+ qAddPostRoutine(cleanupDns);
+ new Q3DnsManager();
+ }
+ return globalManager;
+}
+
+
+void Q3DnsUgleHack::ugle( bool emitAnyway)
+{
+ if ( emitAnyway || !isWorking() ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: status change for %s (type %d)",
+ label().ascii(), recordType() );
+#endif
+ emit resultsReady();
+ }
+}
+
+
+Q3DnsManager::Q3DnsManager()
+ : Q3DnsSocket( qApp, "Internal DNS manager" ),
+ queries( Q3PtrVector<Q3DnsQuery>( 0 ) ),
+ cache( Q3Dict<Q3DnsDomain>( 83, false ) ),
+ ipv4Socket( new Q3SocketDevice( Q3SocketDevice::Datagram, Q3SocketDevice::IPv4, 0 ) )
+#if !defined (QT_NO_IPV6)
+ , ipv6Socket( new Q3SocketDevice( Q3SocketDevice::Datagram, Q3SocketDevice::IPv6, 0 ) )
+#endif
+{
+ cache.setAutoDelete( true );
+ globalManager = this;
+
+ QTimer * sweepTimer = new QTimer( this );
+ sweepTimer->start( 1000 * 60 * 3 );
+ connect( sweepTimer, SIGNAL(timeout()),
+ this, SLOT(cleanCache()) );
+
+ QSocketNotifier * rn4 = new QSocketNotifier( ipv4Socket->socket(),
+ QSocketNotifier::Read,
+ this, "dns IPv4 socket watcher" );
+ ipv4Socket->setAddressReusable( false );
+ ipv4Socket->setBlocking( false );
+ connect( rn4, SIGNAL(activated(int)), SLOT(answer()) );
+
+#if !defined (QT_NO_IPV6)
+ // Don't connect the IPv6 socket notifier if the host does not
+ // support IPv6.
+ if ( ipv6Socket->socket() != -1 ) {
+ QSocketNotifier * rn6 = new QSocketNotifier( ipv6Socket->socket(),
+ QSocketNotifier::Read,
+ this, "dns IPv6 socket watcher" );
+
+ ipv6support = true;
+ ipv6Socket->setAddressReusable( false );
+ ipv6Socket->setBlocking( false );
+ connect( rn6, SIGNAL(activated(int)), SLOT(answer()) );
+ }
+#endif
+
+ if ( !theNs )
+ Q3Dns::doResInit();
+
+ // O(n*n) stuff here. but for 3 and 6, O(n*n) with a low k should
+ // be perfect. the point is to eliminate any duplicates that
+ // might be hidden in the lists.
+ Q3PtrList<QHostAddress> * ns = new Q3PtrList<QHostAddress>;
+
+ theNs->first();
+ QHostAddress * h;
+ while( (h=theNs->current()) != 0 ) {
+ ns->first();
+ while( ns->current() != 0 && !(*ns->current() == *h) )
+ ns->next();
+ if ( !ns->current() ) {
+ ns->append( new QHostAddress(*h) );
+#if defined(Q3DNS_DEBUG)
+ qDebug( "using name server %s", h->toString().latin1() );
+ } else {
+ qDebug( "skipping address %s", h->toString().latin1() );
+#endif
+ }
+ theNs->next();
+ }
+
+ delete theNs;
+ theNs = ns;
+ theNs->setAutoDelete( true );
+
+ Q3StrList * domains = new Q3StrList( true );
+
+ theDomains->first();
+ const char * s;
+ while( (s=theDomains->current()) != 0 ) {
+ domains->first();
+ while( domains->current() != 0 && qstrcmp( domains->current(), s ) )
+ domains->next();
+ if ( !domains->current() ) {
+ domains->append( s );
+#if defined(Q3DNS_DEBUG)
+ qDebug( "searching domain %s", s );
+ } else {
+ qDebug( "skipping domain %s", s );
+#endif
+ }
+ theDomains->next();
+ }
+
+ delete theDomains;
+ theDomains = domains;
+ theDomains->setAutoDelete( true );
+}
+
+
+Q3DnsManager::~Q3DnsManager()
+{
+ if ( globalManager )
+ globalManager = 0;
+ queries.setAutoDelete( true );
+ cache.setAutoDelete( true );
+ delete ipv4Socket;
+#if !defined (QT_NO_IPV6)
+ delete ipv6Socket;
+#endif
+}
+
+static Q_UINT32 lastSweep = 0;
+
+void Q3DnsManager::cleanCache()
+{
+ bool again = false;
+ Q3DictIterator<Q3DnsDomain> it( cache );
+ Q3DnsDomain * d;
+ Q_UINT32 thisSweep = now();
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3DnsManager::cleanCache(: Called, time is %u, last was %u",
+ thisSweep, lastSweep );
+#endif
+
+ while( (d=it.current()) != 0 ) {
+ ++it;
+ d->sweep( thisSweep ); // after this, d may be empty
+ if ( !again )
+ again = !d->isEmpty();
+ }
+ if ( !again )
+ delete this;
+ lastSweep = thisSweep;
+}
+
+
+void Q3DnsManager::retransmit()
+{
+ const QObject * o = sender();
+ if ( o == 0 || globalManager == 0 || this != globalManager )
+ return;
+ uint q = 0;
+ while( q < queries.size() && queries[q] != o )
+ q++;
+ if ( q < queries.size() )
+ transmitQuery( q );
+}
+
+
+void Q3DnsManager::answer()
+{
+ QByteArray a( 16383 ); // large enough for anything, one suspects
+
+ int r;
+#if defined (QT_NO_IPV6)
+ r = ipv4Socket->readBlock(a.data(), a.size());
+#else
+ if (((QSocketNotifier *)sender())->socket() == ipv4Socket->socket())
+ r = ipv4Socket->readBlock(a.data(), a.size());
+ else
+ r = ipv6Socket->readBlock(a.data(), a.size());
+#endif
+#if defined(Q3DNS_DEBUG)
+#if !defined (QT_NO_IPV6)
+ qDebug("DNS Manager: answer arrived: %d bytes from %s:%d", r,
+ useIpv4Socket ? ipv4Socket->peerAddress().toString().ascii()
+ : ipv6Socket->peerAddress().toString().ascii(),
+ useIpv4Socket ? ipv4Socket->peerPort() : ipv6Socket->peerPort() );
+#else
+ qDebug("DNS Manager: answer arrived: %d bytes from %s:%d", r,
+ ipv4Socket->peerAddress().toString().ascii(), ipv4Socket->peerPort());;
+#endif
+#endif
+ if ( r < 12 )
+ return;
+ // maybe we should check that the answer comes from port 53 on one
+ // of our name servers...
+ a.resize( r );
+
+ Q_UINT16 aid = (((Q_UINT8)a[0]) << 8) + ((Q_UINT8)a[1]);
+ uint i = 0;
+ while( i < queries.size() &&
+ !( queries[i] && queries[i]->id == aid ) )
+ i++;
+ if ( i == queries.size() ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: bad id (0x%04x) %d", aid, i );
+#endif
+ return;
+ }
+
+ // at this point queries[i] is whatever we asked for.
+
+ if ( ( (Q_UINT8)(a[2]) & 0x80 ) == 0 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: received a query" );
+#endif
+ return;
+ }
+
+ Q3DnsQuery * q = queries[i];
+ Q3DnsAnswer answer( a, q );
+ answer.parse();
+ if ( answer.ok ) {
+ queries.take( i );
+ answer.notify();
+ delete q;
+ }
+}
+
+
+void Q3DnsManager::transmitQuery( Q3DnsQuery * query_ )
+{
+ if ( !query_ )
+ return;
+
+ uint i = 0;
+ while( i < queries.size() && queries[i] != 0 )
+ i++;
+ if ( i == queries.size() )
+ queries.resize( i+1 );
+ queries.insert( i, query_ );
+ transmitQuery( i );
+}
+
+
+void Q3DnsManager::transmitQuery( int i )
+{
+ if ( i < 0 || i >= (int)queries.size() )
+ return;
+ Q3DnsQuery * q = queries[i];
+
+ if ( q && q->step > 8 ) {
+ // okay, we've run out of retransmissions. we fake an NXDomain
+ // with a very short life time...
+ Q3DnsAnswer answer( q );
+ answer.notify();
+ // and then get rid of the query
+ queries.take( i );
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: giving up on query 0x%04x", q->id );
+#endif
+ delete q;
+ QTimer::singleShot( 0, Q3DnsManager::manager(), SLOT(cleanCache()) );
+ // and don't process anything more
+ return;
+ }
+
+ if ((q && !q->dns) || q->dns->isEmpty())
+ // no one currently wants the answer, so there's no point in
+ // retransmitting the query. we keep it, though. an answer may
+ // arrive for an earlier query transmission, and if it does we
+ // may benefit from caching the result.
+ return;
+
+ QByteArray p( 12 + q->l.length() + 2 + 4 );
+ if ( p.size() > 500 )
+ return; // way over the limit, so don't even try
+
+ // header
+ // id
+ p[0] = (q->id & 0xff00) >> 8;
+ p[1] = q->id & 0x00ff;
+ p[2] = 1; // recursion desired, rest is 0
+ p[3] = 0; // all is 0
+ // one query
+ p[4] = 0;
+ p[5] = 1;
+ // no answers, name servers or additional data
+ p[6] = p[7] = p[8] = p[9] = p[10] = p[11] = 0;
+
+ // the name is composed of several components. each needs to be
+ // written by itself... so we write...
+ // oh, and we assume that there's no funky characters in there.
+ int pp = 12;
+ uint lp = 0;
+ while( lp < (uint) q->l.length() ) {
+ int le = q->l.find( QLatin1Char('.'), lp );
+ if ( le < 0 )
+ le = q->l.length();
+ QString component = q->l.mid( lp, le-lp );
+ p[pp++] = component.length();
+ int cp;
+ for( cp=0; cp < (int)component.length(); cp++ )
+ p[pp++] = component[cp].latin1();
+ lp = le + 1;
+ }
+ // final null
+ p[pp++] = 0;
+ // query type
+ p[pp++] = 0;
+ switch( q->t ) {
+ case Q3Dns::A:
+ p[pp++] = 1;
+ break;
+ case Q3Dns::Aaaa:
+ p[pp++] = 28;
+ break;
+ case Q3Dns::Mx:
+ p[pp++] = 15;
+ break;
+ case Q3Dns::Srv:
+ p[pp++] = 33;
+ break;
+ case Q3Dns::Cname:
+ p[pp++] = 5;
+ break;
+ case Q3Dns::Ptr:
+ p[pp++] = 12;
+ break;
+ case Q3Dns::Txt:
+ p[pp++] = 16;
+ break;
+ default:
+ p[pp++] = (char)255; // any
+ break;
+ }
+ // query class (always internet)
+ p[pp++] = 0;
+ p[pp++] = 1;
+
+ // if we have no name servers, we should regenerate ns in case
+ // name servers have recently been defined (like on windows,
+ // plugging/unplugging the network cable will change the name
+ // server entries)
+ if ( !theNs || theNs->isEmpty() )
+ Q3Dns::doResInit();
+
+ if ( !theNs || theNs->isEmpty() ) {
+ // we don't find any name servers. We fake an NXDomain
+ // with a very short life time...
+ Q3DnsAnswer answer( q );
+ answer.notify();
+ // and then get rid of the query
+ queries.take( i );
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: no DNS server found on query 0x%04x", q->id );
+#endif
+ delete q;
+ QTimer::singleShot( 1000*10, Q3DnsManager::manager(), SLOT(cleanCache()) );
+ // and don't process anything more
+ return;
+ }
+
+ QHostAddress receiver = *theNs->at( q->step % theNs->count() );
+ if (receiver.isIPv4Address())
+ ipv4Socket->writeBlock( p.data(), pp, receiver, 53 );
+#if !defined (QT_NO_IPV6)
+ else
+ ipv6Socket->writeBlock( p.data(), pp, receiver, 53 );
+#endif
+#if defined(Q3DNS_DEBUG)
+ qDebug( "issuing query 0x%04x (%d) about %s type %d to %s",
+ q->id, q->step, q->l.ascii(), q->t,
+ ns->at( q->step % ns->count() )->toString().ascii() );
+#endif
+ if ( theNs->count() > 1 && q->step == 0 && queries.count() == 1 ) {
+ // if it's the first time, and we don't have any other
+ // outstanding queries, send nonrecursive queries to the other
+ // name servers too.
+ p[2] = 0;
+ QHostAddress * server;
+ while( (server=theNs->next()) != 0 ) {
+ if (server->isIPv4Address())
+ ipv4Socket->writeBlock( p.data(), pp, *server, 53 );
+#if !defined (QT_NO_IPV6)
+ else
+ ipv6Socket->writeBlock( p.data(), pp, *server, 53 );
+#endif
+#if defined(Q3DNS_DEBUG)
+ qDebug( "copying query to %s", server->toString().ascii() );
+#endif
+ }
+ }
+ q->step++;
+ // some testing indicates that normal dns queries take up to 0.6
+ // seconds. the graph becomes steep around that point, and the
+ // number of errors rises... so it seems good to retry at that
+ // point.
+ q->start( q->step < theNs->count() ? 800 : 1500, true );
+}
+
+
+Q3DnsDomain * Q3DnsManager::domain( const QString & label )
+{
+ Q3DnsDomain * d = cache.find( label );
+ if ( !d ) {
+ d = new Q3DnsDomain( label );
+ cache.insert( label, d );
+ }
+ return d;
+}
+
+
+//
+//
+// the Q3DnsDomain class looks after and coordinates queries for Q3DnsRRs for
+// each domain, and the cached Q3DnsRRs. (A domain, in DNS terminology, is
+// a node in the DNS. "no", "trolltech.com" and "lupinella.troll.no" are
+// all domains.)
+//
+//
+
+
+Q3DnsDomain::Q3DnsDomain( const QString & label )
+{
+ l = label;
+ rrs = 0;
+}
+
+
+Q3DnsDomain::~Q3DnsDomain()
+{
+ delete rrs;
+ rrs = 0;
+}
+
+
+void Q3DnsDomain::add( const QString & label, Q3DnsRR * rr )
+{
+ Q3DnsDomain * d = Q3DnsManager::manager()->domain( label );
+ if ( !d->rrs ) {
+ d->rrs = new Q3PtrList<Q3DnsRR>;
+ d->rrs->setAutoDelete( true );
+ }
+ d->rrs->append( rr );
+ rr->domain = d;
+}
+
+
+Q3PtrList<Q3DnsRR> * Q3DnsDomain::cached( const Q3Dns * r )
+{
+ Q3PtrList<Q3DnsRR> * l = new Q3PtrList<Q3DnsRR>;
+
+ // test at first if you have to start a query at all
+ if ( r->recordType() == Q3Dns::A ) {
+ if ( r->label().lower() == QLatin1String("localhost") ) {
+ // undocumented hack. ipv4-specific. also, may be a memory
+ // leak? not sure. would be better to do this in doResInit(),
+ // anyway.
+ Q3DnsRR *rrTmp = new Q3DnsRR( r->label() );
+ rrTmp->t = Q3Dns::A;
+ rrTmp->address = QHostAddress( 0x7f000001 );
+ rrTmp->current = true;
+ l->append( rrTmp );
+ return l;
+ }
+ QHostAddress tmp;
+ if ( tmp.setAddress( r->label() ) ) {
+ Q3DnsRR *rrTmp = new Q3DnsRR( r->label() );
+ if ( tmp.isIPv4Address() ) {
+ rrTmp->t = Q3Dns::A;
+ rrTmp->address = tmp;
+ rrTmp->current = true;
+ l->append( rrTmp );
+ } else {
+ rrTmp->nxdomain = true;
+ }
+ return l;
+ }
+ }
+ if ( r->recordType() == Q3Dns::Aaaa ) {
+ QHostAddress tmp;
+ if ( tmp.setAddress(r->label()) ) {
+ Q3DnsRR *rrTmp = new Q3DnsRR( r->label() );
+ if ( tmp.isIPv6Address() ) {
+ rrTmp->t = Q3Dns::Aaaa;
+ rrTmp->address = tmp;
+ rrTmp->current = true;
+ l->append( rrTmp );
+ } else {
+ rrTmp->nxdomain = true;
+ }
+ return l;
+ }
+ }
+
+ // if you reach this point, you have to do the query
+ Q3DnsManager * m = Q3DnsManager::manager();
+ QStringList n = r->qualifiedNames();
+ bool nxdomain;
+ int cnamecount = 0;
+ int it = 0;
+ while( it < n.count() ) {
+ QString s = n.at(it++);
+ nxdomain = false;
+#if defined(Q3DNS_DEBUG)
+ qDebug( "looking at cache for %s (%s %d)",
+ s.ascii(), r->label().ascii(), r->recordType() );
+#endif
+ Q3DnsDomain * d = m->domain( s );
+#if defined(Q3DNS_DEBUG)
+ qDebug( " - found %d RRs", d && d->rrs ? d->rrs->count() : 0 );
+#endif
+ if ( d->rrs )
+ d->rrs->first();
+ Q3DnsRR * rr;
+ bool answer = false;
+ while( d->rrs && (rr=d->rrs->current()) != 0 ) {
+ if ( rr->t == Q3Dns::Cname && r->recordType() != Q3Dns::Cname &&
+ !rr->nxdomain && cnamecount < 16 ) {
+ // cname. if the code is ugly, that may just
+ // possibly be because the concept is.
+#if defined(Q3DNS_DEBUG)
+ qDebug( "found cname from %s to %s",
+ r->label().ascii(), rr->target.ascii() );
+#endif
+ s = rr->target;
+ d = m->domain( s );
+ if ( d->rrs )
+ d->rrs->first();
+ it = n.count();
+ // we've elegantly moved over to whatever the cname
+ // pointed to. well, not elegantly. let's remember
+ // that we've done something, anyway, so we can't be
+ // fooled into an infinte loop as well.
+ cnamecount++;
+ } else {
+ if ( rr->t == r->recordType() ) {
+ if ( rr->nxdomain )
+ nxdomain = true;
+ else
+ answer = true;
+ l->append( rr );
+ if ( rr->deleteTime <= lastSweep ) {
+ // we're returning something that'll be
+ // deleted soon. we assume that if the client
+ // wanted it twice, it'll want it again, so we
+ // ask the name server again right now.
+ Q3DnsQuery * query = new Q3DnsQuery;
+ query->started = now();
+ query->id = ++theId;
+ query->t = rr->t;
+ query->l = rr->domain->name();
+ // note that here, we don't bother about
+ // notification. but we do bother about
+ // timeouts: we make sure to use high timeouts
+ // and few tramsissions.
+ query->step = theNs->count();
+ QObject::connect( query, SIGNAL(timeout()),
+ Q3DnsManager::manager(),
+ SLOT(retransmit()) );
+ Q3DnsManager::manager()->transmitQuery( query );
+ }
+ }
+ d->rrs->next();
+ }
+ }
+ // if we found a positive result, return quickly
+ if ( answer && l->count() ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "found %d records for %s",
+ l->count(), r->label().ascii() );
+ l->first();
+ while( l->current() ) {
+ qDebug( " type %d target %s address %s",
+ l->current()->t,
+ l->current()->target.latin1(),
+ l->current()->address.toString().latin1() );
+ l->next();
+ }
+#endif
+ l->first();
+ return l;
+ }
+
+#if defined(Q3DNS_DEBUG)
+ if ( nxdomain )
+ qDebug( "found NXDomain %s", s.ascii() );
+#endif
+
+ if ( !nxdomain ) {
+ // if we didn't, and not a negative result either, perhaps
+ // we need to transmit a query.
+ uint q = 0;
+ while ( q < m->queries.size() &&
+ ( m->queries[q] == 0 ||
+ m->queries[q]->t != r->recordType() ||
+ m->queries[q]->l != s ) )
+ q++;
+ // we haven't done it before, so maybe we should. but
+ // wait - if it's an unqualified name, only ask when all
+ // the other alternatives are exhausted.
+ if ( q == m->queries.size() && ( s.find( QLatin1Char('.') ) >= 0 ||
+ int(l->count()) >= n.count()-1 ) ) {
+ Q3DnsQuery * query = new Q3DnsQuery;
+ query->started = now();
+ query->id = ++theId;
+ query->t = r->recordType();
+ query->l = s;
+ query->dns->replace( (void*)r, (void*)r );
+ QObject::connect( query, SIGNAL(timeout()),
+ Q3DnsManager::manager(), SLOT(retransmit()) );
+ Q3DnsManager::manager()->transmitQuery( query );
+ } else if ( q < m->queries.size() ) {
+ // if we've found an earlier query for the same
+ // domain/type, subscribe to its answer
+ m->queries[q]->dns->replace( (void*)r, (void*)r );
+ }
+ }
+ }
+ l->first();
+ return l;
+}
+
+
+void Q3DnsDomain::sweep( Q_UINT32 thisSweep )
+{
+ if ( !rrs )
+ return;
+
+ Q3DnsRR * rr;
+ rrs->first();
+ while( (rr=rrs->current()) != 0 ) {
+ if ( !rr->deleteTime )
+ rr->deleteTime = thisSweep; // will hit next time around
+
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::sweep: %s type %d expires %u %u - %s / %s",
+ rr->domain->name().latin1(), rr->t,
+ rr->expireTime, rr->deleteTime,
+ rr->target.latin1(), rr->address.toString().latin1());
+#endif
+ if ( rr->current == false ||
+ rr->t == Q3Dns::None ||
+ rr->deleteTime <= thisSweep ||
+ rr->expireTime <= thisSweep )
+ rrs->remove();
+ else
+ rrs->next();
+ }
+
+ if ( rrs->isEmpty() ) {
+ delete rrs;
+ rrs = 0;
+ }
+}
+
+
+
+
+// the itsy-bitsy little socket class I don't really need except for
+// so I can subclass and reimplement the slots.
+
+
+Q3DnsSocket::Q3DnsSocket( QObject * parent, const char * name )
+ : QObject( parent, name )
+{
+ // nothing
+}
+
+
+Q3DnsSocket::~Q3DnsSocket()
+{
+ // nothing
+}
+
+
+void Q3DnsSocket::cleanCache()
+{
+ // nothing
+}
+
+
+void Q3DnsSocket::retransmit()
+{
+ // nothing
+}
+
+
+void Q3DnsSocket::answer()
+{
+ // nothing
+}
+
+
+/*!
+ \class Q3Dns
+ \brief The Q3Dns class provides asynchronous DNS lookups.
+
+ \compat
+
+ Both Windows and Unix provide synchronous DNS lookups; Windows
+ provides some asynchronous support too. At the time of writing
+ neither operating system provides asynchronous support for
+ anything other than hostname-to-address mapping.
+
+ Q3Dns rectifies this shortcoming, by providing asynchronous caching
+ lookups for the record types that we expect modern GUI
+ applications to need in the near future.
+
+ The class is \e not straightforward to use (although it is much
+ simpler than the native APIs); Q3Socket provides much easier to use
+ TCP connection facilities. The aim of Q3Dns is to provide a correct
+ and small API to the DNS and nothing more. (We use "correctness"
+ to mean that the DNS information is correctly cached, and
+ correctly timed out.)
+
+ The API comprises a constructor, functions to set the DNS node
+ (the domain in DNS terminology) and record type (setLabel() and
+ setRecordType()), the corresponding get functions, an isWorking()
+ function to determine whether Q3Dns is working or reading, a
+ resultsReady() signal and query functions for the result.
+
+ There is one query function for each RecordType, namely
+ addresses(), mailServers(), servers(), hostNames() and texts().
+ There are also two generic query functions: canonicalName()
+ returns the name you'll presumably end up using (the exact meaning
+ of this depends on the record type) and qualifiedNames() returns a
+ list of the fully qualified names label() maps to.
+
+ \sa Q3Socket
+*/
+
+/*!
+ Constructs a DNS query object with invalid settings for both the
+ label and the search type.
+*/
+
+Q3Dns::Q3Dns()
+{
+ d = new Q3DnsPrivate;
+ t = None;
+}
+
+
+
+
+/*!
+ Constructs a DNS query object that will return record type \a rr
+ information about \a label.
+
+ The DNS lookup is started the next time the application enters the
+ event loop. When the result is found the signal resultsReady() is
+ emitted.
+
+ \a rr defaults to \c A, IPv4 addresses.
+*/
+
+Q3Dns::Q3Dns( const QString & label, RecordType rr )
+{
+ d = new Q3DnsPrivate;
+ t = rr;
+ setLabel( label );
+ setStartQueryTimer(); // start query the next time we enter event loop
+}
+
+
+
+/*!
+ Constructs a DNS query object that will return record type \a rr
+ information about host address \a address. The label is set to the
+ IN-ADDR.ARPA domain name. This is useful in combination with the
+ \c Ptr record type (e.g. if you want to look up a hostname for a
+ given address).
+
+ The DNS lookup is started the next time the application enters the
+ event loop. When the result is found the signal resultsReady() is
+ emitted.
+
+ \a rr defaults to \c Ptr, that maps addresses to hostnames.
+*/
+
+Q3Dns::Q3Dns( const QHostAddress & address, RecordType rr )
+{
+ d = new Q3DnsPrivate;
+ t = rr;
+ setLabel( address );
+ setStartQueryTimer(); // start query the next time we enter event loop
+}
+
+
+
+
+/*!
+ Destroys the DNS query object and frees its allocated resources.
+*/
+
+Q3Dns::~Q3Dns()
+{
+ if ( globalManager ) {
+ uint q = 0;
+ Q3DnsManager * m = globalManager;
+ while( q < m->queries.size() ) {
+ Q3DnsQuery * query=m->queries[q];
+ if ( query && query->dns )
+ (void)query->dns->take( (void*) this );
+ q++;
+ }
+
+ }
+
+ delete d;
+ d = 0;
+}
+
+
+
+
+/*!
+ Sets this DNS query object to query for information about \a
+ label.
+
+ This does not change the recordType(), but its isWorking() status
+ will probably change as a result.
+
+ The DNS lookup is started the next time the application enters the
+ event loop. When the result is found the signal resultsReady() is
+ emitted.
+*/
+
+void Q3Dns::setLabel( const QString & label )
+{
+ l = label;
+ d->noNames = false;
+
+ // construct a list of qualified names
+ n.clear();
+ if ( l.length() > 1 && l[(int)l.length()-1] == QLatin1Char('.') ) {
+ n.append( l.left( l.length()-1 ).lower() );
+ } else {
+ int i = l.length();
+ int dots = 0;
+ const int maxDots = 2;
+ while( i && dots < maxDots ) {
+ if ( l[--i] == QLatin1Char('.') )
+ dots++;
+ }
+ if ( dots < maxDots ) {
+ (void)Q3DnsManager::manager(); // create a Q3DnsManager, if it is not already there
+ Q3StrListIterator it( *theDomains );
+ const char * dom;
+ while( (dom=it.current()) != 0 ) {
+ ++it;
+ n.append( l.lower() + QLatin1String(".") + QLatin1String(dom) );
+ }
+ }
+ n.append( l.lower() );
+ }
+
+#if defined(Q_DNS_SYNCHRONOUS)
+ if ( d->noEventLoop ) {
+ doSynchronousLookup();
+ } else {
+ setStartQueryTimer(); // start query the next time we enter event loop
+ }
+#else
+ setStartQueryTimer(); // start query the next time we enter event loop
+#endif
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::setLabel: %d address(es) for %s", n.count(), l.ascii() );
+ int i = 0;
+ for( i = 0; i < (int)n.count(); i++ )
+ qDebug( "Q3Dns::setLabel: %d: %s", i, n[i].ascii() );
+#endif
+}
+
+
+/*!
+ \overload
+
+ Sets this DNS query object to query for information about the host
+ address \a address. The label is set to the IN-ADDR.ARPA domain
+ name. This is useful in combination with the \c Ptr record type
+ (e.g. if you want to look up a hostname for a given address).
+*/
+
+void Q3Dns::setLabel( const QHostAddress & address )
+{
+ setLabel( toInAddrArpaDomain( address ) );
+}
+
+
+/*!
+ \fn QStringList Q3Dns::qualifiedNames() const
+
+ Returns a list of the fully qualified names label() maps to.
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 0
+
+*/
+
+
+/*!
+ \fn QString Q3Dns::label() const
+
+ Returns the domain name for which this object returns information.
+
+ \sa setLabel()
+*/
+
+/*!
+ \enum Q3Dns::RecordType
+
+ This enum type defines the record types Q3Dns can handle. The DNS
+ provides many more; these are the ones we've judged to be in
+ current use, useful for GUI programs and important enough to
+ support right away:
+
+ \value None No information. This exists only so that Q3Dns can
+ have a default.
+
+ \value A IPv4 addresses. By far the most common type.
+
+ \value Aaaa IPv6 addresses. So far mostly unused.
+
+ \value Mx Mail eXchanger names. Used for mail delivery.
+
+ \value Srv SeRVer names. Generic record type for finding
+ servers. So far mostly unused.
+
+ \value Cname Canonical names. Maps from nicknames to the true
+ name (the canonical name) for a host.
+
+ \value Ptr name PoinTeRs. Maps from IPv4 or IPv6 addresses to hostnames.
+
+ \value Txt arbitrary TeXT for domains.
+
+ We expect that some support for the
+ \l{http://www.rfc-editor.org/rfc/rfc2535.txt}{RFC 2535}
+ extensions will be added in future versions.
+*/
+
+/*!
+ Sets this object to query for record type \a rr records.
+
+ The DNS lookup is started the next time the application enters the
+ event loop. When the result is found the signal resultsReady() is
+ emitted.
+
+ \sa RecordType
+*/
+
+void Q3Dns::setRecordType( RecordType rr )
+{
+ t = rr;
+ d->noNames = false;
+ setStartQueryTimer(); // start query the next time we enter event loop
+}
+
+/*!
+ \internal
+
+ Private slot for starting the query.
+*/
+void Q3Dns::startQuery()
+{
+ // isWorking() starts the query (if necessary)
+ if ( !isWorking() )
+ emit resultsReady();
+}
+
+/*!
+ The three functions Q3Dns::Q3Dns(QString, RecordType),
+ Q3Dns::setLabel() and Q3Dns::setRecordType() may start a DNS lookup.
+ This function handles setting up the single shot timer.
+*/
+void Q3Dns::setStartQueryTimer()
+{
+#if defined(Q_DNS_SYNCHRONOUS)
+ if ( !d->queryTimer && !d->noEventLoop )
+#else
+ if ( !d->queryTimer )
+#endif
+ {
+ // start the query the next time we enter event loop
+ d->queryTimer = new QTimer( this );
+ connect( d->queryTimer, SIGNAL(timeout()),
+ this, SLOT(startQuery()) );
+ d->queryTimer->start( 0, true );
+ }
+}
+
+/*
+ Transforms the host address \a address to the IN-ADDR.ARPA domain
+ name. Returns something indeterminate if you're sloppy or
+ naughty. This function has an IPv4-specific name, but works for
+ IPv6 too.
+*/
+QString Q3Dns::toInAddrArpaDomain( const QHostAddress &address )
+{
+ QString s;
+ if ( address.isNull() ) {
+ // if the address isn't valid, neither of the other two make
+ // cases make sense. better to just return.
+ } else if ( address.isIp4Addr() ) {
+ Q_UINT32 i = address.ip4Addr();
+ s.sprintf( "%d.%d.%d.%d.IN-ADDR.ARPA",
+ i & 0xff, (i >> 8) & 0xff, (i>>16) & 0xff, (i>>24) & 0xff );
+ } else {
+ // RFC 3152. (1886 is deprecated, and clients no longer need to
+ // support it, in practice).
+ Q_IPV6ADDR i = address.toIPv6Address();
+ s = QLatin1String("ip6.arpa");
+ uint b = 0;
+ while( b < 16 ) {
+ s = QString::number( i.c[b]%16, 16 ) + QLatin1String(".") +
+ QString::number( i.c[b]/16, 16 ) + QLatin1String(".") + s;
+ b++;
+ }
+ }
+ return s;
+}
+
+
+/*!
+ \fn Q3Dns::RecordType Q3Dns::recordType() const
+
+ Returns the record type of this DNS query object.
+
+ \sa setRecordType() RecordType
+*/
+
+/*!
+ \fn void Q3Dns::resultsReady()
+
+ This signal is emitted when results are available for one of the
+ qualifiedNames().
+*/
+
+/*!
+ Returns true if Q3Dns is doing a lookup for this object (i.e. if it
+ does not already have the necessary information); otherwise
+ returns false.
+
+ Q3Dns emits the resultsReady() signal when the status changes to false.
+*/
+
+bool Q3Dns::isWorking() const
+{
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::isWorking (%s, %d)", l.ascii(), t );
+#endif
+ if ( t == None )
+ return false;
+
+#if defined(Q_DNS_SYNCHRONOUS)
+ if ( d->noEventLoop )
+ return true;
+#endif
+
+ Q3PtrList<Q3DnsRR> * ll = Q3DnsDomain::cached( this );
+ Q_LONG queries = n.count();
+ while( ll->current() != 0 ) {
+ if ( ll->current()->nxdomain ) {
+ queries--;
+ } else {
+ delete ll;
+ return false;
+ }
+ ll->next();
+ }
+ delete ll;
+
+ if ( queries <= 0 )
+ return false;
+ if ( d->noNames )
+ return false;
+ return true;
+}
+
+
+/*!
+ Returns a list of the addresses for this name if this Q3Dns object
+ has a recordType() of Q3Dns::A or Q3Dns::Aaaa and the answer
+ is available; otherwise returns an empty list.
+
+ As a special case, if label() is a valid numeric IP address, this
+ function returns that address.
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 1
+
+*/
+
+Q3ValueList<QHostAddress> Q3Dns::addresses() const
+{
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::addresses (%s)", l.ascii() );
+#endif
+ Q3ValueList<QHostAddress> result;
+ if ( t != A && t != Aaaa )
+ return result;
+
+ Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
+
+ Q3DnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain )
+ result.append( rr->address );
+ cached->next();
+ }
+ delete cached;
+ return result;
+}
+
+
+/*!
+ \class Q3Dns::MailServer
+ \brief The Q3Dns::MailServer class is described in Q3Dns::mailServers().
+
+ \internal
+*/
+
+/*!
+ Returns a list of mail servers if the record type is \c Mx. The
+ class Q3Dns::MailServer contains the following public variables:
+ \list
+ \i QString Q3Dns::MailServer::name
+ \i Q_UINT16 Q3Dns::MailServer::priority
+ \endlist
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 2
+
+*/
+Q3ValueList<Q3Dns::MailServer> Q3Dns::mailServers() const
+{
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::mailServers (%s)", l.ascii() );
+#endif
+ Q3ValueList<Q3Dns::MailServer> result;
+ if ( t != Mx )
+ return result;
+
+ Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
+
+ Q3DnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain ) {
+ MailServer ms( rr->target, rr->priority );
+ result.append( ms );
+ }
+ cached->next();
+ }
+ delete cached;
+ return result;
+}
+
+
+/*!
+ \class Q3Dns::Server
+ \brief The Q3Dns::Server class is described in Q3Dns::servers().
+
+ \internal
+*/
+
+/*!
+ Returns a list of servers if the record type is \c Srv. The class
+ Q3Dns::Server contains the following public variables:
+ \list
+ \i QString Q3Dns::Server::name
+ \i Q_UINT16 Q3Dns::Server::priority
+ \i Q_UINT16 Q3Dns::Server::weight
+ \i Q_UINT16 Q3Dns::Server::port
+ \endlist
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 3
+*/
+Q3ValueList<Q3Dns::Server> Q3Dns::servers() const
+{
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::servers (%s)", l.ascii() );
+#endif
+ Q3ValueList<Q3Dns::Server> result;
+ if ( t != Srv )
+ return result;
+
+ Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
+
+ Q3DnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain ) {
+ Server s( rr->target, rr->priority, rr->weight, rr->port );
+ result.append( s );
+ }
+ cached->next();
+ }
+ delete cached;
+ return result;
+}
+
+
+/*!
+ Returns a list of host names if the record type is \c Ptr.
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 4
+
+*/
+QStringList Q3Dns::hostNames() const
+{
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::hostNames (%s)", l.ascii() );
+#endif
+ QStringList result;
+ if ( t != Ptr )
+ return result;
+
+ Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
+
+ Q3DnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain ) {
+ QString str( rr->target );
+ result.append( str );
+ }
+ cached->next();
+ }
+ delete cached;
+ return result;
+}
+
+
+/*!
+ Returns a list of texts if the record type is \c Txt.
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 5
+*/
+QStringList Q3Dns::texts() const
+{
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::texts (%s)", l.ascii() );
+#endif
+ QStringList result;
+ if ( t != Txt )
+ return result;
+
+ Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
+
+ Q3DnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain ) {
+ QString str( rr->text );
+ result.append( str );
+ }
+ cached->next();
+ }
+ delete cached;
+ return result;
+}
+
+
+/*!
+ Returns the canonical name for this DNS node. (This works
+ regardless of what recordType() is set to.)
+
+ If the canonical name isn't known, this function returns a null
+ string.
+
+ The canonical name of a DNS node is its full name, or the full
+ name of the target of its CNAME. For example, if l.trolltech.com
+ is a CNAME to lillian.troll.no, and the search path for Q3Dns is
+ "trolltech.com", then the canonical name for all of "lillian",
+ "l", "lillian.troll.no." and "l.trolltech.com" is
+ "lillian.troll.no.".
+*/
+
+QString Q3Dns::canonicalName() const
+{
+ // the cname should work regardless of the recordType(), so set the record
+ // type temporarily to cname when you look at the cache
+ Q3Dns *that = (Q3Dns*) this; // mutable function
+ RecordType oldType = t;
+ that->t = Cname;
+ Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( that );
+ that->t = oldType;
+
+ Q3DnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain && rr->domain ) {
+ delete cached;
+ return rr->target;
+ }
+ cached->next();
+ }
+ delete cached;
+ return QString();
+}
+
+#if defined(Q_DNS_SYNCHRONOUS)
+/*! \reimp
+*/
+void Q3Dns::connectNotify( const char *signal )
+{
+ if ( d->noEventLoop && qstrcmp(signal,SIGNAL(resultsReady()) )==0 ) {
+ doSynchronousLookup();
+ }
+}
+#endif
+
+#if defined(Q_OS_WIN32) || defined(Q_OS_CYGWIN)
+
+#if defined(Q_DNS_SYNCHRONOUS)
+void Q3Dns::doSynchronousLookup()
+{
+ // ### not implemented yet
+}
+#endif
+
+// the following typedefs are needed for GetNetworkParams() API call
+#ifndef IP_TYPES_INCLUDED
+#define MAX_HOSTNAME_LEN 128
+#define MAX_DOMAIN_NAME_LEN 128
+#define MAX_SCOPE_ID_LEN 256
+typedef struct {
+ char String[4 * 4];
+} IP_ADDRESS_STRING, *PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING;
+typedef struct _IP_ADDR_STRING {
+ struct _IP_ADDR_STRING* Next;
+ IP_ADDRESS_STRING IpAddress;
+ IP_MASK_STRING IpMask;
+ DWORD Context;
+} IP_ADDR_STRING, *PIP_ADDR_STRING;
+typedef struct {
+ char HostName[MAX_HOSTNAME_LEN + 4] ;
+ char DomainName[MAX_DOMAIN_NAME_LEN + 4];
+ PIP_ADDR_STRING CurrentDnsServer;
+ IP_ADDR_STRING DnsServerList;
+ UINT NodeType;
+ char ScopeId[MAX_SCOPE_ID_LEN + 4];
+ UINT EnableRouting;
+ UINT EnableProxy;
+ UINT EnableDns;
+} FIXED_INFO, *PFIXED_INFO;
+#endif
+typedef DWORD (WINAPI *GNP)( PFIXED_INFO, PULONG );
+
+// ### FIXME: this code is duplicated in qfiledialog.cpp
+static QString getWindowsRegString( HKEY key, const QString &subKey )
+{
+ QString s;
+ QT_WA( {
+ char buf[1024];
+ DWORD bsz = sizeof(buf);
+ int r = RegQueryValueEx( key, (TCHAR*)subKey.ucs2(), 0, 0, (LPBYTE)buf, &bsz );
+ if ( r == ERROR_SUCCESS ) {
+ s = QString::fromUcs2( (unsigned short *)buf );
+ } else if ( r == ERROR_MORE_DATA ) {
+ char *ptr = new char[bsz+1];
+ r = RegQueryValueEx( key, (TCHAR*)subKey.ucs2(), 0, 0, (LPBYTE)ptr, &bsz );
+ if ( r == ERROR_SUCCESS )
+ s = QLatin1String(ptr);
+ delete [] ptr;
+ }
+ } , {
+ char buf[512];
+ DWORD bsz = sizeof(buf);
+ int r = RegQueryValueExA( key, subKey.local8Bit(), 0, 0, (LPBYTE)buf, &bsz );
+ if ( r == ERROR_SUCCESS ) {
+ s = QLatin1String(buf);
+ } else if ( r == ERROR_MORE_DATA ) {
+ char *ptr = new char[bsz+1];
+ r = RegQueryValueExA( key, subKey.local8Bit(), 0, 0, (LPBYTE)ptr, &bsz );
+ if ( r == ERROR_SUCCESS )
+ s = QLatin1String(ptr);
+ delete [] ptr;
+ }
+ } );
+ return s;
+}
+
+static bool getDnsParamsFromRegistry( const QString &path,
+ QString *domainName, QString *nameServer, QString *searchList )
+{
+ HKEY k;
+ int r;
+ QT_WA( {
+ r = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
+ (TCHAR*)path.ucs2(),
+ 0, KEY_READ, &k );
+ } , {
+ r = RegOpenKeyExA( HKEY_LOCAL_MACHINE,
+ path.latin1(),
+ 0, KEY_READ, &k );
+ } );
+
+ if ( r == ERROR_SUCCESS ) {
+ *domainName = getWindowsRegString( k, QLatin1String("DhcpDomain") );
+ if ( domainName->isEmpty() )
+ *domainName = getWindowsRegString( k, QLatin1String("Domain") );
+
+ *nameServer = getWindowsRegString( k, QLatin1String("DhcpNameServer") );
+ if ( nameServer->isEmpty() )
+ *nameServer = getWindowsRegString( k, QLatin1String("NameServer") );
+
+ *searchList = getWindowsRegString( k, QLatin1String("SearchList") );
+ }
+ RegCloseKey( k );
+ return r == ERROR_SUCCESS;
+}
+
+void Q3Dns::doResInit()
+{
+ char separator = 0;
+
+ if ( theNs )
+ delete theNs;
+ theNs = new Q3PtrList<QHostAddress>;
+ theNs->setAutoDelete( true );
+ theDomains = new Q3StrList( true );
+ theDomains->setAutoDelete( true );
+
+ QString domainName, nameServer, searchList;
+
+ bool gotNetworkParams = false;
+ // try the API call GetNetworkParams() first and use registry lookup only
+ // as a fallback
+#ifdef Q_OS_WINCE
+ HINSTANCE hinstLib = LoadLibraryW( L"iphlpapi" );
+#else
+ HINSTANCE hinstLib = LoadLibraryA( "iphlpapi" );
+#endif
+ if ( hinstLib != 0 ) {
+#ifdef Q_OS_WINCE
+ GNP getNetworkParams = (GNP) GetProcAddressW( hinstLib, L"GetNetworkParams" );
+#else
+ GNP getNetworkParams = (GNP) GetProcAddress( hinstLib, "GetNetworkParams" );
+#endif
+ if ( getNetworkParams != 0 ) {
+ ULONG l = 0;
+ DWORD res;
+ res = getNetworkParams( 0, &l );
+ if ( res == ERROR_BUFFER_OVERFLOW ) {
+ FIXED_INFO *finfo = (FIXED_INFO*)new char[l];
+ res = getNetworkParams( finfo, &l );
+ if ( res == ERROR_SUCCESS ) {
+ domainName = QLatin1String(finfo->DomainName);
+ nameServer = QLatin1String("");
+ IP_ADDR_STRING *dnsServer = &finfo->DnsServerList;
+ while ( dnsServer != 0 ) {
+ nameServer += QLatin1String(dnsServer->IpAddress.String);
+ dnsServer = dnsServer->Next;
+ if ( dnsServer != 0 )
+ nameServer += QLatin1String(" ");
+ }
+ searchList = QLatin1String("");
+ separator = ' ';
+ gotNetworkParams = true;
+ }
+ delete[] finfo;
+ }
+ }
+ FreeLibrary( hinstLib );
+ }
+ if ( !gotNetworkParams ) {
+ if ( getDnsParamsFromRegistry(
+ QString( QLatin1String("System\\CurrentControlSet\\Services\\Tcpip\\Parameters") ),
+ &domainName, &nameServer, &searchList )) {
+ // for NT
+ separator = ' ';
+ } else if ( getDnsParamsFromRegistry(
+ QString( QLatin1String("System\\CurrentControlSet\\Services\\VxD\\MSTCP") ),
+ &domainName, &nameServer, &searchList )) {
+ // for Windows 98
+ separator = ',';
+ } else {
+ // Could not access the TCP/IP parameters
+ domainName = QLatin1String("");
+ nameServer = QLatin1String("127.0.0.1");
+ searchList = QLatin1String("");
+ separator = ' ';
+ }
+ }
+
+ nameServer = nameServer.simplifyWhiteSpace();
+ int first, last;
+ if ( !nameServer.isEmpty() ) {
+ first = 0;
+ do {
+ last = nameServer.find( QLatin1Char(separator), first );
+ if ( last < 0 )
+ last = nameServer.length();
+ Q3Dns tmp( nameServer.mid( first, last-first ), Q3Dns::A );
+ Q3ValueList<QHostAddress> address = tmp.addresses();
+ Q_LONG i = address.count();
+ while( i )
+ theNs->append( new QHostAddress(address[--i]) );
+ first = last+1;
+ } while( first < (int)nameServer.length() );
+ }
+
+ searchList = searchList + QLatin1String(" ") + domainName;
+ searchList = searchList.simplifyWhiteSpace().lower();
+ first = 0;
+ do {
+ last = searchList.find( QLatin1Char(separator), first );
+ if ( last < 0 )
+ last = searchList.length();
+ theDomains->append( qstrdup( searchList.mid( first, last-first ).latin1() ) );
+ first = last+1;
+ } while( first < (int)searchList.length() );
+}
+
+#elif defined(Q_OS_UNIX)
+
+#if defined(Q_DNS_SYNCHRONOUS)
+void Q3Dns::doSynchronousLookup()
+{
+ if ( t!=None && !l.isEmpty() ) {
+ Q3ValueListIterator<QString> it = n.begin();
+ Q3ValueListIterator<QString> end = n.end();
+ int type;
+ switch( t ) {
+ case Q3Dns::A:
+ type = 1;
+ break;
+ case Q3Dns::Aaaa:
+ type = 28;
+ break;
+ case Q3Dns::Mx:
+ type = 15;
+ break;
+ case Q3Dns::Srv:
+ type = 33;
+ break;
+ case Q3Dns::Cname:
+ type = 5;
+ break;
+ case Q3Dns::Ptr:
+ type = 12;
+ break;
+ case Q3Dns::Txt:
+ type = 16;
+ break;
+ default:
+ type = (char)255; // any
+ break;
+ }
+ while( it != end ) {
+ QString s = *it;
+ it++;
+ QByteArray ba( 512 );
+ int len = res_search( s.latin1(), 1, type, (uchar*)ba.data(), ba.size() );
+ if ( len > 0 ) {
+ ba.resize( len );
+
+ Q3DnsQuery * query = new Q3DnsQuery;
+ query->started = now();
+ query->id = ++theId;
+ query->t = t;
+ query->l = s;
+ Q3DnsAnswer a( ba, query );
+ a.parse();
+ } else if ( len == -1 ) {
+ // res_search error
+ }
+ }
+ emit resultsReady();
+ }
+}
+#endif
+
+#if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 3)))
+#define Q_MODERN_RES_API
+#endif
+
+void Q3Dns::doResInit()
+{
+ if ( theNs )
+ return;
+ theNs = new Q3PtrList<QHostAddress>;
+ theNs->setAutoDelete( true );
+ theDomains = new Q3StrList( true );
+ theDomains->setAutoDelete( true );
+
+ // read resolv.conf manually.
+ QFile resolvConf(QLatin1String("/etc/resolv.conf"));
+ if (resolvConf.open(QIODevice::ReadOnly)) {
+ QTextStream stream( &resolvConf );
+ QString line;
+
+ while ( !stream.atEnd() ) {
+ line = stream.readLine();
+ QStringList list = QStringList::split( QLatin1String(" "), line );
+ if( line.startsWith( QLatin1String("#") ) || list.size() < 2 )
+ continue;
+ const QString type = list[0].lower();
+
+ if ( type == QLatin1String("nameserver") ) {
+ QHostAddress *address = new QHostAddress();
+ if ( address->setAddress( QString(list[1]) ) ) {
+ // only add ipv6 addresses from resolv.conf if
+ // this host supports ipv6.
+ if ( address->isIPv4Address() || ipv6support )
+ theNs->append( address );
+ else
+ delete address;
+ } else {
+ delete address;
+ }
+ } else if ( type == QLatin1String("search") ) {
+ QStringList srch = QStringList::split( QLatin1String(" "), list[1] );
+ for ( QStringList::Iterator i = srch.begin(); i != srch.end(); ++i )
+ theDomains->append( (*i).lower().local8Bit() );
+
+ } else if ( type == QLatin1String("domain") ) {
+ theDomains->append( list[1].lower().local8Bit() );
+ }
+ }
+ }
+
+ if (theNs->isEmpty()) {
+#if defined(Q_MODERN_RES_API)
+ struct __res_state res;
+ res_ninit( &res );
+ int i;
+ // find the name servers to use
+ for( i=0; i < MAXNS && i < res.nscount; i++ )
+ theNs->append( new QHostAddress( ntohl( res.nsaddr_list[i].sin_addr.s_addr ) ) );
+# if defined(MAXDFLSRCH)
+ for( i=0; i < MAXDFLSRCH; i++ ) {
+ if ( res.dnsrch[i] && *(res.dnsrch[i]) )
+ theDomains->append( QString::fromLatin1( res.dnsrch[i] ).lower().local8Bit() );
+ else
+ break;
+ }
+# endif
+ if ( *res.defdname )
+ theDomains->append( QString::fromLatin1( res.defdname ).lower().local8Bit() );
+#else
+ res_init();
+ int i;
+ // find the name servers to use
+ for( i=0; i < MAXNS && i < _res.nscount; i++ )
+ theNs->append( new QHostAddress( ntohl( _res.nsaddr_list[i].sin_addr.s_addr ) ) );
+# if defined(MAXDFLSRCH)
+ for( i=0; i < MAXDFLSRCH; i++ ) {
+ if ( _res.dnsrch[i] && *(_res.dnsrch[i]) )
+ theDomains->append( QString::fromLatin1( _res.dnsrch[i] ).lower().local8Bit() );
+ else
+ break;
+ }
+# endif
+ if ( *_res.defdname )
+ theDomains->append( QString::fromLatin1( _res.defdname ).lower().local8Bit() );
+#endif
+
+ // the code above adds "0.0.0.0" as a name server at the slightest
+ // hint of trouble. so remove those again.
+ theNs->first();
+ while( theNs->current() ) {
+ if ( theNs->current()->isNull() )
+ delete theNs->take();
+ else
+ theNs->next();
+ }
+ }
+
+ QFile hosts( QString::fromLatin1( "/etc/hosts" ) );
+ if ( hosts.open( QIODevice::ReadOnly ) ) {
+ // read the /etc/hosts file, creating long-life A and PTR RRs
+ // for the things we find.
+ QTextStream i( &hosts );
+ QString line;
+ while( !i.atEnd() ) {
+ line = i.readLine().simplifyWhiteSpace().lower();
+ uint n = 0;
+ while( (int) n < line.length() && line[(int)n] != QLatin1Char('#') )
+ n++;
+ line.truncate( n );
+ n = 0;
+ while( (int) n < line.length() && !line[(int)n].isSpace() )
+ n++;
+ QString ip = line.left( n );
+ QHostAddress a;
+ a.setAddress( ip );
+ if ( ( a.isIPv4Address() || a.isIPv6Address() ) && !a.isNull() ) {
+ bool first = true;
+ line = line.mid( n+1 );
+ n = 0;
+ while( (int) n < line.length() && !line[(int)n].isSpace() )
+ n++;
+ QString hostname = line.left( n );
+ // ### in case of bad syntax, hostname is invalid. do we care?
+ if ( n ) {
+ Q3DnsRR * rr = new Q3DnsRR( hostname );
+ if ( a.isIPv4Address() )
+ rr->t = Q3Dns::A;
+ else
+ rr->t = Q3Dns::Aaaa;
+ rr->address = a;
+ rr->deleteTime = UINT_MAX;
+ rr->expireTime = UINT_MAX;
+ rr->current = true;
+ if ( first ) {
+ first = false;
+ Q3DnsRR * ptr = new Q3DnsRR( Q3Dns::toInAddrArpaDomain( a ) );
+ ptr->t = Q3Dns::Ptr;
+ ptr->target = hostname;
+ ptr->deleteTime = UINT_MAX;
+ ptr->expireTime = UINT_MAX;
+ ptr->current = true;
+ }
+ }
+ }
+ }
+ }
+}
+
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DNS