src/qt3support/network/q3dns.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt3Support module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qplatformdefs.h"
       
    43 #include "qbytearray.h"
       
    44 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_CYGWIN)
       
    45 # include "qt_windows.h"
       
    46 #else
       
    47 # include <sys/types.h>
       
    48 # include <netinet/in.h>
       
    49 # include <arpa/nameser.h>
       
    50 # include <resolv.h>
       
    51 extern "C" int res_init();
       
    52 #endif
       
    53 
       
    54 // POSIX Large File Support redefines open -> open64
       
    55 #if defined(open)
       
    56 # undef open
       
    57 #endif
       
    58 
       
    59 // POSIX Large File Support redefines truncate -> truncate64
       
    60 #if defined(truncate)
       
    61 # undef truncate
       
    62 #endif
       
    63 
       
    64 // Solaris redefines connect -> __xnet_connect with _XOPEN_SOURCE_EXTENDED.
       
    65 #if defined(connect)
       
    66 # undef connect
       
    67 #endif
       
    68 
       
    69 // UnixWare 7 redefines socket -> _socket
       
    70 #if defined(socket)
       
    71 # undef socket
       
    72 #endif
       
    73 
       
    74 #include "q3dns.h"
       
    75 
       
    76 #ifndef QT_NO_DNS
       
    77 
       
    78 #include "qdatetime.h"
       
    79 #include "q3dict.h"
       
    80 #include "q3ptrlist.h"
       
    81 #include "qstring.h"
       
    82 #include "qtimer.h"
       
    83 #include "qapplication.h"
       
    84 #include "q3ptrvector.h"
       
    85 #include "q3strlist.h"
       
    86 #include "q3ptrdict.h"
       
    87 #include "qfile.h"
       
    88 #include "qtextstream.h"
       
    89 #include "q3socketdevice.h"
       
    90 #include "q3cleanuphandler.h"
       
    91 #include <limits.h>
       
    92 
       
    93 QT_BEGIN_NAMESPACE
       
    94 
       
    95 //#define Q3DNS_DEBUG
       
    96 
       
    97 static Q_UINT16 theId; // ### seeded started by now()
       
    98 
       
    99 
       
   100 static QDateTime * originOfTime = 0;
       
   101 
       
   102 static Q3CleanupHandler<QDateTime> q3dns_cleanup_time;
       
   103 
       
   104 static Q_UINT32 now()
       
   105 {
       
   106     if ( originOfTime )
       
   107 	return originOfTime->secsTo( QDateTime::currentDateTime() );
       
   108 
       
   109     originOfTime = new QDateTime( QDateTime::currentDateTime() );
       
   110     theId = originOfTime->time().msec() * 60 + originOfTime->time().second();
       
   111     q3dns_cleanup_time.add( &originOfTime );
       
   112     return 0;
       
   113 }
       
   114 
       
   115 
       
   116 static Q3PtrList<QHostAddress> * theNs = 0;
       
   117 static Q3StrList * theDomains = 0;
       
   118 static bool ipv6support = false;
       
   119 
       
   120 class Q3DnsPrivate {
       
   121 public:
       
   122     Q3DnsPrivate() : queryTimer( 0 ), noNames(false)
       
   123     {
       
   124 #if defined(Q_DNS_SYNCHRONOUS)
       
   125 #if defined(Q_OS_UNIX)
       
   126 	noEventLoop = qApp==0 || qApp->loopLevel()==0;
       
   127 #else
       
   128 	noEventLoop = false;
       
   129 #endif
       
   130 #endif
       
   131     }
       
   132     ~Q3DnsPrivate()
       
   133     {
       
   134 	delete queryTimer;
       
   135     }
       
   136 private:
       
   137     QTimer * queryTimer;
       
   138     bool noNames;
       
   139 #if defined(Q_DNS_SYNCHRONOUS)
       
   140     bool noEventLoop;
       
   141 #endif
       
   142 
       
   143     friend class Q3Dns;
       
   144     friend class Q3DnsAnswer;
       
   145 };
       
   146 
       
   147 
       
   148 class Q3DnsRR;
       
   149 class Q3DnsDomain;
       
   150 
       
   151 
       
   152 
       
   153 // Q3DnsRR is the class used to store a single RR.  Q3DnsRR can store
       
   154 // all of the supported RR types.  a Q3DnsRR is always cached.
       
   155 
       
   156 // Q3DnsRR is mostly constructed from the outside.  a but hacky, but
       
   157 // permissible since the entire class is internal.
       
   158 
       
   159 class Q3DnsRR {
       
   160 public:
       
   161     Q3DnsRR( const QString & label );
       
   162     ~Q3DnsRR();
       
   163 
       
   164 public:
       
   165     Q3DnsDomain * domain;
       
   166     Q3Dns::RecordType t;
       
   167     bool nxdomain;
       
   168     bool current;
       
   169     Q_UINT32 expireTime;
       
   170     Q_UINT32 deleteTime;
       
   171     // somewhat space-wasting per-type data
       
   172     // a / aaaa
       
   173     QHostAddress address;
       
   174     // cname / mx / srv / ptr
       
   175     QString target;
       
   176     // mx / srv
       
   177     Q_UINT16 priority;
       
   178     // srv
       
   179     Q_UINT16 weight;
       
   180     Q_UINT16 port;
       
   181     // txt
       
   182     QString text; // could be overloaded into target...
       
   183 private:
       
   184 
       
   185 };
       
   186 
       
   187 
       
   188 class Q3DnsDomain {
       
   189 public:
       
   190     Q3DnsDomain( const QString & label );
       
   191     ~Q3DnsDomain();
       
   192 
       
   193     static void add( const QString & label, Q3DnsRR * );
       
   194     static Q3PtrList<Q3DnsRR> * cached( const Q3Dns * );
       
   195 
       
   196     void take( Q3DnsRR * );
       
   197 
       
   198     void sweep( Q_UINT32 thisSweep );
       
   199 
       
   200     bool isEmpty() const { return rrs == 0 || rrs->isEmpty(); }
       
   201 
       
   202     QString name() const { return l; }
       
   203 
       
   204 public:
       
   205     QString l;
       
   206     Q3PtrList<Q3DnsRR> * rrs;
       
   207 };
       
   208 
       
   209 
       
   210 class Q3DnsQuery: public QTimer { // this inheritance is a very evil hack
       
   211 public:
       
   212     Q3DnsQuery():
       
   213 	id( 0 ), t( Q3Dns::None ), step(0), started(0),
       
   214 	dns( new Q3PtrDict<void>(17) ) {}
       
   215     ~Q3DnsQuery() { delete dns; }
       
   216     Q_UINT16 id;
       
   217     Q3Dns::RecordType t;
       
   218     QString l;
       
   219 
       
   220     uint step;
       
   221     Q_UINT32 started;
       
   222 
       
   223     Q3PtrDict<void> * dns;
       
   224 };
       
   225 
       
   226 
       
   227 
       
   228 class Q3DnsAnswer {
       
   229 public:
       
   230     Q3DnsAnswer( Q3DnsQuery * );
       
   231     Q3DnsAnswer( const QByteArray &, Q3DnsQuery * );
       
   232     ~Q3DnsAnswer();
       
   233 
       
   234     void parse();
       
   235     void notify();
       
   236 
       
   237     bool ok;
       
   238 
       
   239 private:
       
   240     Q3DnsQuery * query;
       
   241 
       
   242     Q_UINT8 * answer;
       
   243     int size;
       
   244     int pp;
       
   245 
       
   246     Q3PtrList<Q3DnsRR> * rrs;
       
   247 
       
   248     // convenience
       
   249     int next;
       
   250     int ttl;
       
   251     QString label;
       
   252     Q3DnsRR * rr;
       
   253 
       
   254     QString readString(bool multipleLabels = true);
       
   255     void parseA();
       
   256     void parseAaaa();
       
   257     void parseMx();
       
   258     void parseSrv();
       
   259     void parseCname();
       
   260     void parsePtr();
       
   261     void parseTxt();
       
   262     void parseNs();
       
   263 };
       
   264 
       
   265 
       
   266 Q3DnsRR::Q3DnsRR( const QString & label )
       
   267     : domain( 0 ), t( Q3Dns::None ),
       
   268       nxdomain( false ), current( false ),
       
   269       expireTime( 0 ), deleteTime( 0 ),
       
   270       priority( 0 ), weight( 0 ), port( 0 )
       
   271 {
       
   272     Q3DnsDomain::add( label, this );
       
   273 }
       
   274 
       
   275 
       
   276 // not supposed to be deleted except by Q3DnsDomain
       
   277 Q3DnsRR::~Q3DnsRR()
       
   278 {
       
   279     // nothing is necessary
       
   280 }
       
   281 
       
   282 
       
   283 // this one just sticks in a NXDomain
       
   284 Q3DnsAnswer::Q3DnsAnswer( Q3DnsQuery * query_ )
       
   285 {
       
   286     ok = true;
       
   287 
       
   288     answer = 0;
       
   289     size = 0;
       
   290     query = query_;
       
   291     pp = 0;
       
   292     rrs = new Q3PtrList<Q3DnsRR>;
       
   293     rrs->setAutoDelete( false );
       
   294     next = size;
       
   295     ttl = 0;
       
   296     label.clear();
       
   297     rr = 0;
       
   298 
       
   299     Q3DnsRR * newrr = new Q3DnsRR( query->l );
       
   300     newrr->t = query->t;
       
   301     newrr->deleteTime = query->started + 10;
       
   302     newrr->expireTime = query->started + 10;
       
   303     newrr->nxdomain = true;
       
   304     newrr->current = true;
       
   305     rrs->append( newrr );
       
   306 }
       
   307 
       
   308 
       
   309 Q3DnsAnswer::Q3DnsAnswer( const QByteArray& answer_,
       
   310 			Q3DnsQuery * query_ )
       
   311 {
       
   312     ok = true;
       
   313 
       
   314     answer = (Q_UINT8 *)(answer_.data());
       
   315     size = (int)answer_.size();
       
   316     query = query_;
       
   317     pp = 0;
       
   318     rrs = new Q3PtrList<Q3DnsRR>;
       
   319     rrs->setAutoDelete( false );
       
   320     next = size;
       
   321     ttl = 0;
       
   322     label.clear();
       
   323     rr = 0;
       
   324 }
       
   325 
       
   326 
       
   327 Q3DnsAnswer::~Q3DnsAnswer()
       
   328 {
       
   329     if ( !ok && rrs ) {
       
   330 	Q3PtrListIterator<Q3DnsRR> it( *rrs );
       
   331 	Q3DnsRR * tmprr;
       
   332 	while( (tmprr=it.current()) != 0 ) {
       
   333 	    ++it;
       
   334 	    tmprr->t = Q3Dns::None; // will be deleted soonish
       
   335 	}
       
   336     }
       
   337     delete rrs;
       
   338 }
       
   339 
       
   340 
       
   341 QString Q3DnsAnswer::readString(bool multipleLabels)
       
   342 {
       
   343     int p = pp;
       
   344     QString r;
       
   345     Q_UINT8 b;
       
   346     for( ;; ) {
       
   347 	b = 128;
       
   348         // Read one character
       
   349         if ( p >= 0 && p < size )
       
   350 	    b = answer[p];
       
   351 
       
   352 	switch( b >> 6 ) {
       
   353 	case 0:
       
   354             // b is less than 64
       
   355 	    p++;
       
   356 
       
   357             // Detect end of data
       
   358 	    if ( b == 0 ) {
       
   359 		if ( p > pp )
       
   360 		    pp = p;
       
   361                 return r.isNull() ? QLatin1String( "." ) : r;
       
   362 	    }
       
   363 
       
   364             // Read a label of size 'b' characters
       
   365             if ( !r.isNull() )
       
   366 		r += QLatin1Char('.');
       
   367 	    while( b-- > 0 )
       
   368                 r += QLatin1Char( answer[p++] );
       
   369 
       
   370             // Return immediately if we were only supposed to read one
       
   371             // label.
       
   372             if (!multipleLabels)
       
   373                 return r;
       
   374 
       
   375 	    break;
       
   376 	default:
       
   377             // Ignore unrecognized control character, or p was out of
       
   378             // range.
       
   379 	    goto not_ok;
       
   380 	case 3:
       
   381             // Use the next character to determine the relative offset
       
   382             // to jump to before continuing the packet parsing.
       
   383 	    int q = ( (answer[p] & 0x3f) << 8 ) + answer[p+1];
       
   384 
       
   385 	    if ( q >= pp || q >= p )
       
   386 		goto not_ok;
       
   387 	    if ( p >= pp )
       
   388 		pp = p + 2;
       
   389 	    p = q;
       
   390         }
       
   391     }
       
   392 not_ok:
       
   393     ok = false;
       
   394     return QString();
       
   395 }
       
   396 
       
   397 
       
   398 
       
   399 void Q3DnsAnswer::parseA()
       
   400 {
       
   401     if ( next != pp + 4 ) {
       
   402 #if defined(Q3DNS_DEBUG)
       
   403 	qDebug( "Q3Dns: saw %d bytes long IN A for %s",
       
   404 		next - pp, label.ascii() );
       
   405 #endif
       
   406 	return;
       
   407     }
       
   408 
       
   409     rr = new Q3DnsRR( label );
       
   410     rr->t = Q3Dns::A;
       
   411     rr->address = QHostAddress( ( answer[pp+0] << 24 ) +
       
   412 				( answer[pp+1] << 16 ) +
       
   413 				( answer[pp+2] <<  8 ) +
       
   414 				( answer[pp+3] ) );
       
   415 #if defined(Q3DNS_DEBUG)
       
   416     qDebug( "Q3Dns: saw %s IN A %s (ttl %d)", label.ascii(),
       
   417 	    rr->address.toString().ascii(), ttl );
       
   418 #endif
       
   419 }
       
   420 
       
   421 
       
   422 void Q3DnsAnswer::parseAaaa()
       
   423 {
       
   424     if ( next != pp + 16 ) {
       
   425 #if defined(Q3DNS_DEBUG)
       
   426 	qDebug( "Q3Dns: saw %d bytes long IN Aaaa for %s",
       
   427 		next - pp, label.ascii() );
       
   428 #endif
       
   429 	return;
       
   430     }
       
   431 
       
   432     rr = new Q3DnsRR( label );
       
   433     rr->t = Q3Dns::Aaaa;
       
   434     rr->address = QHostAddress( answer+pp );
       
   435 #if defined(Q3DNS_DEBUG)
       
   436     qDebug( "Q3Dns: saw %s IN Aaaa %s (ttl %d)", label.ascii(),
       
   437 	    rr->address.toString().ascii(), ttl );
       
   438 #endif
       
   439 }
       
   440 
       
   441 
       
   442 
       
   443 void Q3DnsAnswer::parseMx()
       
   444 {
       
   445     if ( next < pp + 2 ) {
       
   446 #if defined(Q3DNS_DEBUG)
       
   447 	qDebug( "Q3Dns: saw %d bytes long IN MX for %s",
       
   448 		next - pp, label.ascii() );
       
   449 #endif
       
   450 	return;
       
   451     }
       
   452 
       
   453     rr = new Q3DnsRR( label );
       
   454     rr->priority = (answer[pp] << 8) + answer[pp+1];
       
   455     pp += 2;
       
   456     rr->target = readString().lower();
       
   457     if ( !ok ) {
       
   458 #if defined(Q3DNS_DEBUG)
       
   459 	qDebug( "Q3Dns: saw bad string in MX for %s", label.ascii() );
       
   460 #endif
       
   461 	return;
       
   462     }
       
   463     rr->t = Q3Dns::Mx;
       
   464 #if defined(Q3DNS_DEBUG)
       
   465     qDebug( "Q3Dns: saw %s IN MX %d %s (ttl %d)", label.ascii(),
       
   466 	    rr->priority, rr->target.ascii(), ttl );
       
   467 #endif
       
   468 }
       
   469 
       
   470 
       
   471 void Q3DnsAnswer::parseSrv()
       
   472 {
       
   473     if ( next < pp + 6 ) {
       
   474 #if defined(Q3DNS_DEBUG)
       
   475 	qDebug( "Q3Dns: saw %d bytes long IN SRV for %s",
       
   476 		next - pp, label.ascii() );
       
   477 #endif
       
   478 	return;
       
   479     }
       
   480 
       
   481     rr = new Q3DnsRR( label );
       
   482     rr->priority = (answer[pp] << 8) + answer[pp+1];
       
   483     rr->weight = (answer[pp+2] << 8) + answer[pp+3];
       
   484     rr->port = (answer[pp+4] << 8) + answer[pp+5];
       
   485     pp += 6;
       
   486     rr->target = readString().lower();
       
   487     if ( !ok ) {
       
   488 #if defined(Q3DNS_DEBUG)
       
   489 	qDebug( "Q3Dns: saw bad string in SRV for %s", label.ascii() );
       
   490 #endif
       
   491 	return;
       
   492     }
       
   493     rr->t = Q3Dns::Srv;
       
   494 #if defined(Q3DNS_DEBUG)
       
   495     qDebug( "Q3Dns: saw %s IN SRV %d %d %d %s (ttl %d)", label.ascii(),
       
   496 	    rr->priority, rr->weight, rr->port, rr->target.ascii(), ttl );
       
   497 #endif
       
   498 }
       
   499 
       
   500 
       
   501 void Q3DnsAnswer::parseCname()
       
   502 {
       
   503     QString target = readString().lower();
       
   504     if ( !ok ) {
       
   505 #if defined(Q3DNS_DEBUG)
       
   506 	qDebug( "Q3Dns: saw bad cname for for %s", label.ascii() );
       
   507 #endif
       
   508 	return;
       
   509     }
       
   510 
       
   511     rr = new Q3DnsRR( label );
       
   512     rr->t = Q3Dns::Cname;
       
   513     rr->target = target;
       
   514 #if defined(Q3DNS_DEBUG)
       
   515     qDebug( "Q3Dns: saw %s IN CNAME %s (ttl %d)", label.ascii(),
       
   516 	    rr->target.ascii(), ttl );
       
   517 #endif
       
   518 }
       
   519 
       
   520 
       
   521 void Q3DnsAnswer::parseNs()
       
   522 {
       
   523     QString target = readString().lower();
       
   524     if ( !ok ) {
       
   525 #if defined(Q3DNS_DEBUG)
       
   526 	qDebug( "Q3Dns: saw bad cname for for %s", label.ascii() );
       
   527 #endif
       
   528 	return;
       
   529     }
       
   530 
       
   531     // parse, but ignore
       
   532 
       
   533 #if defined(Q3DNS_DEBUG)
       
   534     qDebug( "Q3Dns: saw %s IN NS %s (ttl %d)", label.ascii(),
       
   535 	    target.ascii(), ttl );
       
   536 #endif
       
   537 }
       
   538 
       
   539 
       
   540 void Q3DnsAnswer::parsePtr()
       
   541 {
       
   542     QString target = readString().lower();
       
   543     if ( !ok ) {
       
   544 #if defined(Q3DNS_DEBUG)
       
   545 	qDebug( "Q3Dns: saw bad PTR for for %s", label.ascii() );
       
   546 #endif
       
   547 	return;
       
   548     }
       
   549 
       
   550     rr = new Q3DnsRR( label );
       
   551     rr->t = Q3Dns::Ptr;
       
   552     rr->target = target;
       
   553 #if defined(Q3DNS_DEBUG)
       
   554     qDebug( "Q3Dns: saw %s IN PTR %s (ttl %d)", label.ascii(),
       
   555 	    rr->target.ascii(), ttl );
       
   556 #endif
       
   557 }
       
   558 
       
   559 
       
   560 void Q3DnsAnswer::parseTxt()
       
   561 {
       
   562     QString text = readString(false);
       
   563     if ( !ok ) {
       
   564 #if defined(Q3DNS_DEBUG)
       
   565 	qDebug( "Q3Dns: saw bad TXT for for %s", label.ascii() );
       
   566 #endif
       
   567 	return;
       
   568     }
       
   569 
       
   570     rr = new Q3DnsRR( label );
       
   571     rr->t = Q3Dns::Txt;
       
   572     rr->text = text;
       
   573 #if defined(Q3DNS_DEBUG)
       
   574     qDebug( "Q3Dns: saw %s IN TXT \"%s\" (ttl %d)", label.ascii(),
       
   575 	    rr->text.ascii(), ttl );
       
   576 #endif
       
   577 }
       
   578 
       
   579 
       
   580 void Q3DnsAnswer::parse()
       
   581 {
       
   582     // okay, do the work...
       
   583     if ( (answer[2] & 0x78) != 0 ) {
       
   584 #if defined(Q3DNS_DEBUG)
       
   585 	qDebug( "DNS Manager: answer to wrong query type (%d)", answer[1] );
       
   586 #endif
       
   587 	ok = false;
       
   588 	return;
       
   589     }
       
   590 
       
   591     // AA
       
   592     bool aa = (answer[2] & 4) != 0;
       
   593 
       
   594     // TC
       
   595     if ( (answer[2] & 2) != 0 ) {
       
   596 #if defined(Q3DNS_DEBUG)
       
   597 	qDebug( "DNS Manager: truncated answer; pressing on" );
       
   598 #endif
       
   599     }
       
   600 
       
   601     // RD
       
   602     bool rd = (answer[2] & 1) != 0;
       
   603 
       
   604     // we don't test RA
       
   605     // we don't test the MBZ fields
       
   606 
       
   607     if ( (answer[3] & 0x0f) == 3 ) {
       
   608 #if defined(Q3DNS_DEBUG)
       
   609 	qDebug( "DNS Manager: saw NXDomain for %s", query->l.ascii() );
       
   610 #endif
       
   611 	// NXDomain.  cache that for one minute.
       
   612 	rr = new Q3DnsRR( query->l );
       
   613 	rr->t = query->t;
       
   614 	rr->deleteTime = query->started + 60;
       
   615 	rr->expireTime = query->started + 60;
       
   616 	rr->nxdomain = true;
       
   617 	rr->current = true;
       
   618 	rrs->append( rr );
       
   619 	return;
       
   620     }
       
   621 
       
   622     if ( (answer[3] & 0x0f) != 0 ) {
       
   623 #if defined(Q3DNS_DEBUG)
       
   624 	qDebug( "DNS Manager: error code %d", answer[3] & 0x0f );
       
   625 #endif
       
   626 	ok = false;
       
   627 	return;
       
   628     }
       
   629 
       
   630     int qdcount = ( answer[4] << 8 ) + answer[5];
       
   631     int ancount = ( answer[6] << 8 ) + answer[7];
       
   632     int nscount = ( answer[8] << 8 ) + answer[9];
       
   633     int adcount = (answer[10] << 8 ) +answer[11];
       
   634 
       
   635     pp = 12;
       
   636 
       
   637     // read query
       
   638     while( qdcount > 0 && pp < size ) {
       
   639 	// should I compare the string against query->l?
       
   640 	(void)readString();
       
   641 	if ( !ok )
       
   642 	    return;
       
   643 	pp += 4;
       
   644 	qdcount--;
       
   645     }
       
   646 
       
   647     // answers and stuff
       
   648     int rrno = 0;
       
   649     // if we parse the answer completely, but there are no answers,
       
   650     // ignore the entire thing.
       
   651     int answers = 0;
       
   652     while( ( rrno < ancount ||
       
   653 	     ( ok && answers >0 && rrno < ancount + nscount + adcount ) ) &&
       
   654 	   pp < size ) {
       
   655 	label = readString().lower();
       
   656 	if ( !ok )
       
   657 	    return;
       
   658 	int rdlength = 0;
       
   659 	if ( pp + 10 <= size )
       
   660 	    rdlength = ( answer[pp+8] << 8 ) + answer[pp+9];
       
   661 	if ( pp + 10 + rdlength > size ) {
       
   662 #if defined(Q3DNS_DEBUG)
       
   663 	    qDebug( "DNS Manager: ran out of stuff to parse (%d+%d>%d (%d)",
       
   664 		    pp, rdlength, size, rrno < ancount );
       
   665 #endif
       
   666 	    // if we're still in the AN section, we should go back and
       
   667 	    // at least down the TTLs.  probably best to invalidate
       
   668 	    // the results.
       
   669 	    // the rrs list is good for this
       
   670 	    ok = ( rrno < ancount );
       
   671 	    return;
       
   672 	}
       
   673 	uint type, clas;
       
   674 	type = ( answer[pp+0] << 8 ) + answer[pp+1];
       
   675 	clas = ( answer[pp+2] << 8 ) + answer[pp+3];
       
   676 	ttl = ( answer[pp+4] << 24 ) + ( answer[pp+5] << 16 ) +
       
   677 	      ( answer[pp+6] <<  8 ) + answer[pp+7];
       
   678 	pp = pp + 10;
       
   679 	if ( clas != 1 ) {
       
   680 #if defined(Q3DNS_DEBUG)
       
   681 	    qDebug( "DNS Manager: class %d (not internet) for %s",
       
   682 		    clas, label.isNull() ? "." : label.ascii() );
       
   683 #endif
       
   684 	} else {
       
   685             next = pp + rdlength;
       
   686 	    rr = 0;
       
   687 	    switch( type ) {
       
   688 	    case 1:
       
   689 		parseA();
       
   690 		break;
       
   691 	    case 28:
       
   692 		parseAaaa();
       
   693 		break;
       
   694 	    case 15:
       
   695 		parseMx();
       
   696 		break;
       
   697 	    case 33:
       
   698 		parseSrv();
       
   699 		break;
       
   700 	    case 5:
       
   701 		parseCname();
       
   702 		break;
       
   703 	    case 12:
       
   704 		parsePtr();
       
   705 		break;
       
   706 	    case 16:
       
   707 		parseTxt();
       
   708 		break;
       
   709 	    case 2:
       
   710 		parseNs();
       
   711 		break;
       
   712 	    default:
       
   713 		// something we don't know
       
   714 #if defined(Q3DNS_DEBUG)
       
   715 		qDebug( "DNS Manager: type %d for %s", type,
       
   716 			label.isNull() ? "." : label.ascii() );
       
   717 #endif
       
   718 		break;
       
   719 	    }
       
   720 	    if ( rr ) {
       
   721 		rr->deleteTime = 0;
       
   722 		if ( ttl > 0 )
       
   723 		    rr->expireTime = query->started + ttl;
       
   724 		else
       
   725 		    rr->expireTime = query->started + 20;
       
   726 		if ( rrno < ancount ) {
       
   727 		    answers++;
       
   728 		    rr->deleteTime = rr->expireTime;
       
   729 		}
       
   730 		rr->current = true;
       
   731 		rrs->append( rr );
       
   732 	    }
       
   733         }
       
   734 	if ( !ok )
       
   735 	    return;
       
   736 	pp = next;
       
   737 	next = size;
       
   738 	rrno++;
       
   739     }
       
   740     if ( answers == 0 ) {
       
   741 #if defined(Q3DNS_DEBUG)
       
   742 	qDebug( "DNS Manager: answer contained no answers" );
       
   743 #endif
       
   744 	ok = ( aa && rd );
       
   745     }
       
   746 
       
   747     // now go through the list and mark all the As that are referenced
       
   748     // by something we care about.  we want to cache such As.
       
   749     rrs->first();
       
   750     Q3Dict<void> used( 17 );
       
   751     used.setAutoDelete( false );
       
   752     while( (rr=rrs->current()) != 0 ) {
       
   753 	rrs->next();
       
   754 	if ( rr->target.length() && rr->deleteTime > 0 && rr->current )
       
   755 	    used.insert( rr->target, (void*)42 );
       
   756 	if ( ( rr->t == Q3Dns::A || rr->t == Q3Dns::Aaaa ) &&
       
   757 	     used.find( rr->domain->name() ) != 0 )
       
   758 	    rr->deleteTime = rr->expireTime;
       
   759     }
       
   760 
       
   761     // next, for each RR, delete any older RRs that are equal to it
       
   762     rrs->first();
       
   763     while( (rr=rrs->current()) != 0 ) {
       
   764 	rrs->next();
       
   765 	if ( rr && rr->domain && rr->domain->rrs ) {
       
   766 	    Q3PtrList<Q3DnsRR> * drrs = rr->domain->rrs;
       
   767 	    drrs->first();
       
   768 	    Q3DnsRR * older;
       
   769 	    while( (older=drrs->current()) != 0 ) {
       
   770 		if ( older != rr &&
       
   771 		     older->t == rr->t &&
       
   772 		     older->nxdomain == rr->nxdomain &&
       
   773 		     older->address == rr->address &&
       
   774 		     older->target == rr->target &&
       
   775 		     older->priority == rr->priority &&
       
   776 		     older->weight == rr->weight &&
       
   777 		     older->port == rr->port &&
       
   778 		     older->text == rr->text ) {
       
   779 		    // well, it's equal, but it's not the same. so we kill it,
       
   780 		    // but use its expiry time.
       
   781 #if defined(Q3DNS_DEBUG)
       
   782 		    qDebug( "killing off old %d for %s, expire was %d",
       
   783                             older->t, older->domain->name().latin1(),
       
   784                             rr->expireTime );
       
   785 #endif
       
   786 		    older->t = Q3Dns::None;
       
   787 		    rr->expireTime = QMAX( older->expireTime, rr->expireTime );
       
   788 		    rr->deleteTime = QMAX( older->deleteTime, rr->deleteTime );
       
   789 		    older->deleteTime = 0;
       
   790 #if defined(Q3DNS_DEBUG)
       
   791 		    qDebug( "    adjusted expire is %d", rr->expireTime );
       
   792 #endif
       
   793 		}
       
   794 		drrs->next();
       
   795 	    }
       
   796 	}
       
   797     }
       
   798 
       
   799 #if defined(Q3DNS_DEBUG)
       
   800     //qDebug( "DNS Manager: ()" );
       
   801 #endif
       
   802 }
       
   803 
       
   804 
       
   805 class Q3DnsUgleHack: public Q3Dns {
       
   806 public:
       
   807     void ugle( bool emitAnyway=false );
       
   808 };
       
   809 
       
   810 
       
   811 void Q3DnsAnswer::notify()
       
   812 {
       
   813     if ( !rrs || !ok || !query || !query->dns )
       
   814 	return;
       
   815 
       
   816     Q3PtrDict<void> notified;
       
   817     notified.setAutoDelete( false );
       
   818 
       
   819     Q3PtrDictIterator<void> it( *query->dns );
       
   820     Q3Dns * dns;
       
   821     it.toFirst();
       
   822     while( (dns=(Q3Dns*)(it.current())) != 0 ) {
       
   823 	++it;
       
   824 	if ( notified.find( (void*)dns ) == 0 ) {
       
   825 	    notified.insert( (void*)dns, (void*)42 );
       
   826 	    if ( rrs->count() == 0 ) {
       
   827 #if defined(Q3DNS_DEBUG)
       
   828 		qDebug( "DNS Manager: found no answers!" );
       
   829 #endif
       
   830 		dns->d->noNames = true;
       
   831 		((Q3DnsUgleHack*)dns)->ugle( true );
       
   832 	    } else {
       
   833 		QStringList n = dns->qualifiedNames();
       
   834 		if ( query && n.contains(query->l) )
       
   835 		    ((Q3DnsUgleHack*)dns)->ugle();
       
   836 #if defined(Q3DNS_DEBUG)
       
   837 		else
       
   838 		    qDebug( "DNS Manager: DNS thing %s not notified for %s",
       
   839 			    dns->label().ascii(), query->l.ascii() );
       
   840 #endif
       
   841 	    }
       
   842 	}
       
   843     }
       
   844 }
       
   845 
       
   846 
       
   847 //
       
   848 //
       
   849 // Q3DnsManager
       
   850 //
       
   851 //
       
   852 
       
   853 
       
   854 class Q3DnsManager: public Q3DnsSocket {
       
   855 private:
       
   856 public: // just to silence the moronic g++.
       
   857     Q3DnsManager();
       
   858     ~Q3DnsManager();
       
   859 public:
       
   860     static Q3DnsManager * manager();
       
   861 
       
   862     Q3DnsDomain * domain( const QString & );
       
   863 
       
   864     void transmitQuery( Q3DnsQuery * );
       
   865     void transmitQuery( int );
       
   866 
       
   867     // reimplementation of the slots
       
   868     void cleanCache();
       
   869     void retransmit();
       
   870     void answer();
       
   871 
       
   872 public:
       
   873     Q3PtrVector<Q3DnsQuery> queries;
       
   874     Q3Dict<Q3DnsDomain> cache;
       
   875     Q3SocketDevice * ipv4Socket;
       
   876 #if !defined (QT_NO_IPV6)
       
   877     Q3SocketDevice * ipv6Socket;
       
   878 #endif
       
   879 };
       
   880 
       
   881 
       
   882 
       
   883 static Q3DnsManager * globalManager = 0;
       
   884 
       
   885 static void cleanupDns()
       
   886 {
       
   887     delete globalManager;
       
   888     globalManager = 0;
       
   889 }
       
   890 
       
   891 Q3DnsManager * Q3DnsManager::manager()
       
   892 {
       
   893     if ( !globalManager ) {
       
   894         qAddPostRoutine(cleanupDns);
       
   895 	new Q3DnsManager();
       
   896     }
       
   897     return globalManager;
       
   898 }
       
   899 
       
   900 
       
   901 void Q3DnsUgleHack::ugle( bool emitAnyway)
       
   902 {
       
   903     if ( emitAnyway || !isWorking() ) {
       
   904 #if defined(Q3DNS_DEBUG)
       
   905 	qDebug( "DNS Manager: status change for %s (type %d)",
       
   906 		label().ascii(), recordType() );
       
   907 #endif
       
   908 	emit resultsReady();
       
   909     }
       
   910 }
       
   911 
       
   912 
       
   913 Q3DnsManager::Q3DnsManager()
       
   914     : Q3DnsSocket( qApp, "Internal DNS manager" ),
       
   915       queries( Q3PtrVector<Q3DnsQuery>( 0 ) ),
       
   916       cache( Q3Dict<Q3DnsDomain>( 83, false ) ),
       
   917       ipv4Socket( new Q3SocketDevice( Q3SocketDevice::Datagram, Q3SocketDevice::IPv4, 0 ) )
       
   918 #if !defined (QT_NO_IPV6)
       
   919       , ipv6Socket( new Q3SocketDevice( Q3SocketDevice::Datagram, Q3SocketDevice::IPv6, 0 ) )
       
   920 #endif
       
   921 {
       
   922     cache.setAutoDelete( true );
       
   923     globalManager = this;
       
   924 
       
   925     QTimer * sweepTimer = new QTimer( this );
       
   926     sweepTimer->start( 1000 * 60 * 3 );
       
   927     connect( sweepTimer, SIGNAL(timeout()),
       
   928 	     this, SLOT(cleanCache()) );
       
   929 
       
   930     QSocketNotifier * rn4 = new QSocketNotifier( ipv4Socket->socket(),
       
   931 						 QSocketNotifier::Read,
       
   932 						 this, "dns IPv4 socket watcher" );
       
   933     ipv4Socket->setAddressReusable( false );
       
   934     ipv4Socket->setBlocking( false );
       
   935     connect( rn4, SIGNAL(activated(int)), SLOT(answer()) );
       
   936 
       
   937 #if !defined (QT_NO_IPV6)
       
   938     // Don't connect the IPv6 socket notifier if the host does not
       
   939     // support IPv6.
       
   940     if ( ipv6Socket->socket() != -1 ) {
       
   941 	QSocketNotifier * rn6 = new QSocketNotifier( ipv6Socket->socket(),
       
   942 						     QSocketNotifier::Read,
       
   943 						     this, "dns IPv6 socket watcher" );
       
   944 
       
   945 	ipv6support = true;
       
   946 	ipv6Socket->setAddressReusable( false );
       
   947 	ipv6Socket->setBlocking( false );
       
   948 	connect( rn6, SIGNAL(activated(int)), SLOT(answer()) );
       
   949     }
       
   950 #endif
       
   951 
       
   952     if ( !theNs )
       
   953 	Q3Dns::doResInit();
       
   954 
       
   955     // O(n*n) stuff here.  but for 3 and 6, O(n*n) with a low k should
       
   956     // be perfect.  the point is to eliminate any duplicates that
       
   957     // might be hidden in the lists.
       
   958     Q3PtrList<QHostAddress> * ns = new Q3PtrList<QHostAddress>;
       
   959 
       
   960     theNs->first();
       
   961     QHostAddress * h;
       
   962     while( (h=theNs->current()) != 0 ) {
       
   963 	ns->first();
       
   964 	while( ns->current() != 0 && !(*ns->current() == *h) )
       
   965 	    ns->next();
       
   966 	if ( !ns->current() ) {
       
   967 	    ns->append( new QHostAddress(*h) );
       
   968 #if defined(Q3DNS_DEBUG)
       
   969 	    qDebug( "using name server %s", h->toString().latin1() );
       
   970 	} else {
       
   971 	    qDebug( "skipping address %s", h->toString().latin1() );
       
   972 #endif
       
   973 	}
       
   974 	theNs->next();
       
   975     }
       
   976 
       
   977     delete theNs;
       
   978     theNs = ns;
       
   979     theNs->setAutoDelete( true );
       
   980 
       
   981     Q3StrList * domains = new Q3StrList( true );
       
   982 
       
   983     theDomains->first();
       
   984     const char * s;
       
   985     while( (s=theDomains->current()) != 0 ) {
       
   986 	domains->first();
       
   987 	while( domains->current() != 0 && qstrcmp( domains->current(), s ) )
       
   988 	    domains->next();
       
   989 	if ( !domains->current() ) {
       
   990 	    domains->append( s );
       
   991 #if defined(Q3DNS_DEBUG)
       
   992 	    qDebug( "searching domain %s", s );
       
   993 	} else {
       
   994 	    qDebug( "skipping domain %s", s );
       
   995 #endif
       
   996 	}
       
   997 	theDomains->next();
       
   998     }
       
   999 
       
  1000     delete theDomains;
       
  1001     theDomains = domains;
       
  1002     theDomains->setAutoDelete( true );
       
  1003 }
       
  1004 
       
  1005 
       
  1006 Q3DnsManager::~Q3DnsManager()
       
  1007 {
       
  1008     if ( globalManager )
       
  1009 	globalManager = 0;
       
  1010     queries.setAutoDelete( true );
       
  1011     cache.setAutoDelete( true );
       
  1012     delete ipv4Socket;
       
  1013 #if !defined (QT_NO_IPV6)
       
  1014     delete ipv6Socket;
       
  1015 #endif
       
  1016 }
       
  1017 
       
  1018 static Q_UINT32 lastSweep = 0;
       
  1019 
       
  1020 void Q3DnsManager::cleanCache()
       
  1021 {
       
  1022     bool again = false;
       
  1023     Q3DictIterator<Q3DnsDomain> it( cache );
       
  1024     Q3DnsDomain * d;
       
  1025     Q_UINT32 thisSweep = now();
       
  1026 #if defined(Q3DNS_DEBUG)
       
  1027     qDebug( "Q3DnsManager::cleanCache(: Called, time is %u, last was %u",
       
  1028 	   thisSweep, lastSweep );
       
  1029 #endif
       
  1030 
       
  1031     while( (d=it.current()) != 0 ) {
       
  1032 	++it;
       
  1033 	d->sweep( thisSweep ); // after this, d may be empty
       
  1034 	if ( !again )
       
  1035 	    again = !d->isEmpty();
       
  1036     }
       
  1037     if ( !again )
       
  1038 	delete this;
       
  1039     lastSweep = thisSweep;
       
  1040 }
       
  1041 
       
  1042 
       
  1043 void Q3DnsManager::retransmit()
       
  1044 {
       
  1045     const QObject * o = sender();
       
  1046     if ( o == 0 || globalManager == 0 || this != globalManager )
       
  1047 	return;
       
  1048     uint q = 0;
       
  1049     while( q < queries.size() && queries[q] != o )
       
  1050 	q++;
       
  1051     if ( q < queries.size() )
       
  1052 	transmitQuery( q );
       
  1053 }
       
  1054 
       
  1055 
       
  1056 void Q3DnsManager::answer()
       
  1057 {
       
  1058     QByteArray a( 16383 ); // large enough for anything, one suspects
       
  1059 
       
  1060     int r;
       
  1061 #if defined (QT_NO_IPV6)
       
  1062     r = ipv4Socket->readBlock(a.data(), a.size());
       
  1063 #else
       
  1064     if (((QSocketNotifier *)sender())->socket() == ipv4Socket->socket())
       
  1065         r = ipv4Socket->readBlock(a.data(), a.size());
       
  1066     else
       
  1067         r = ipv6Socket->readBlock(a.data(), a.size());
       
  1068 #endif
       
  1069 #if defined(Q3DNS_DEBUG)
       
  1070 #if !defined (QT_NO_IPV6)
       
  1071     qDebug("DNS Manager: answer arrived: %d bytes from %s:%d", r,
       
  1072 	   useIpv4Socket ? ipv4Socket->peerAddress().toString().ascii()
       
  1073 	   : ipv6Socket->peerAddress().toString().ascii(),
       
  1074 	   useIpv4Socket ? ipv4Socket->peerPort() : ipv6Socket->peerPort() );
       
  1075 #else
       
  1076     qDebug("DNS Manager: answer arrived: %d bytes from %s:%d", r,
       
  1077            ipv4Socket->peerAddress().toString().ascii(), ipv4Socket->peerPort());;
       
  1078 #endif
       
  1079 #endif
       
  1080     if ( r < 12 )
       
  1081 	return;
       
  1082     // maybe we should check that the answer comes from port 53 on one
       
  1083     // of our name servers...
       
  1084     a.resize( r );
       
  1085 
       
  1086     Q_UINT16 aid = (((Q_UINT8)a[0]) << 8) + ((Q_UINT8)a[1]);
       
  1087     uint i = 0;
       
  1088     while( i < queries.size() &&
       
  1089 	   !( queries[i] && queries[i]->id == aid ) )
       
  1090 	i++;
       
  1091     if ( i == queries.size() ) {
       
  1092 #if defined(Q3DNS_DEBUG)
       
  1093 	qDebug( "DNS Manager: bad id (0x%04x) %d", aid, i );
       
  1094 #endif
       
  1095 	return;
       
  1096     }
       
  1097 
       
  1098     // at this point queries[i] is whatever we asked for.
       
  1099 
       
  1100     if ( ( (Q_UINT8)(a[2]) & 0x80 ) == 0 ) {
       
  1101 #if defined(Q3DNS_DEBUG)
       
  1102 	qDebug( "DNS Manager: received a query" );
       
  1103 #endif
       
  1104 	return;
       
  1105     }
       
  1106 
       
  1107     Q3DnsQuery * q = queries[i];
       
  1108     Q3DnsAnswer answer( a, q );
       
  1109     answer.parse();
       
  1110     if ( answer.ok ) {
       
  1111 	queries.take( i );
       
  1112 	answer.notify();
       
  1113 	delete q;
       
  1114     }
       
  1115 }
       
  1116 
       
  1117 
       
  1118 void Q3DnsManager::transmitQuery( Q3DnsQuery * query_ )
       
  1119 {
       
  1120     if ( !query_ )
       
  1121 	return;
       
  1122 
       
  1123     uint i = 0;
       
  1124     while( i < queries.size() && queries[i] != 0 )
       
  1125 	i++;
       
  1126     if ( i == queries.size() )
       
  1127 	queries.resize( i+1 );
       
  1128     queries.insert( i, query_ );
       
  1129     transmitQuery( i );
       
  1130 }
       
  1131 
       
  1132 
       
  1133 void Q3DnsManager::transmitQuery( int i )
       
  1134 {
       
  1135     if ( i < 0 || i >= (int)queries.size() )
       
  1136 	return;
       
  1137     Q3DnsQuery * q = queries[i];
       
  1138 
       
  1139     if ( q && q->step > 8 ) {
       
  1140 	// okay, we've run out of retransmissions. we fake an NXDomain
       
  1141 	// with a very short life time...
       
  1142 	Q3DnsAnswer answer( q );
       
  1143 	answer.notify();
       
  1144 	// and then get rid of the query
       
  1145 	queries.take( i );
       
  1146 #if defined(Q3DNS_DEBUG)
       
  1147 	qDebug( "DNS Manager: giving up on query 0x%04x", q->id );
       
  1148 #endif
       
  1149 	delete q;
       
  1150 	QTimer::singleShot( 0, Q3DnsManager::manager(), SLOT(cleanCache()) );
       
  1151 	// and don't process anything more
       
  1152 	return;
       
  1153     }
       
  1154 
       
  1155     if ((q && !q->dns) || q->dns->isEmpty())
       
  1156 	// no one currently wants the answer, so there's no point in
       
  1157 	// retransmitting the query. we keep it, though. an answer may
       
  1158 	// arrive for an earlier query transmission, and if it does we
       
  1159 	// may benefit from caching the result.
       
  1160 	return;
       
  1161 
       
  1162     QByteArray p( 12 + q->l.length() + 2 + 4 );
       
  1163     if ( p.size() > 500 )
       
  1164 	return; // way over the limit, so don't even try
       
  1165 
       
  1166     // header
       
  1167     // id
       
  1168     p[0] = (q->id & 0xff00) >> 8;
       
  1169     p[1] =  q->id & 0x00ff;
       
  1170     p[2] = 1; // recursion desired, rest is 0
       
  1171     p[3] = 0; // all is 0
       
  1172     // one query
       
  1173     p[4] = 0;
       
  1174     p[5] = 1;
       
  1175     // no answers, name servers or additional data
       
  1176     p[6] = p[7] = p[8] = p[9] = p[10] = p[11] = 0;
       
  1177 
       
  1178     // the name is composed of several components.  each needs to be
       
  1179     // written by itself... so we write...
       
  1180     // oh, and we assume that there's no funky characters in there.
       
  1181     int pp = 12;
       
  1182     uint lp = 0;
       
  1183     while( lp < (uint) q->l.length() ) {
       
  1184 	int le = q->l.find( QLatin1Char('.'), lp );
       
  1185 	if ( le < 0 )
       
  1186 	    le = q->l.length();
       
  1187 	QString component = q->l.mid( lp, le-lp );
       
  1188 	p[pp++] = component.length();
       
  1189 	int cp;
       
  1190 	for( cp=0; cp < (int)component.length(); cp++ )
       
  1191 	    p[pp++] = component[cp].latin1();
       
  1192 	lp = le + 1;
       
  1193     }
       
  1194     // final null
       
  1195     p[pp++] = 0;
       
  1196     // query type
       
  1197     p[pp++] = 0;
       
  1198     switch( q->t ) {
       
  1199     case Q3Dns::A:
       
  1200 	p[pp++] = 1;
       
  1201 	break;
       
  1202     case Q3Dns::Aaaa:
       
  1203 	p[pp++] = 28;
       
  1204 	break;
       
  1205     case Q3Dns::Mx:
       
  1206 	p[pp++] = 15;
       
  1207 	break;
       
  1208     case Q3Dns::Srv:
       
  1209 	p[pp++] = 33;
       
  1210 	break;
       
  1211     case Q3Dns::Cname:
       
  1212 	p[pp++] = 5;
       
  1213 	break;
       
  1214     case Q3Dns::Ptr:
       
  1215 	p[pp++] = 12;
       
  1216 	break;
       
  1217     case Q3Dns::Txt:
       
  1218 	p[pp++] = 16;
       
  1219 	break;
       
  1220     default:
       
  1221 	p[pp++] = (char)255; // any
       
  1222 	break;
       
  1223     }
       
  1224     // query class (always internet)
       
  1225     p[pp++] = 0;
       
  1226     p[pp++] = 1;
       
  1227 
       
  1228     // if we have no name servers, we should regenerate ns in case
       
  1229     // name servers have recently been defined (like on windows,
       
  1230     // plugging/unplugging the network cable will change the name
       
  1231     // server entries)
       
  1232     if ( !theNs || theNs->isEmpty() )
       
  1233         Q3Dns::doResInit();
       
  1234 
       
  1235     if ( !theNs || theNs->isEmpty() ) {
       
  1236 	// we don't find any name servers. We fake an NXDomain
       
  1237 	// with a very short life time...
       
  1238 	Q3DnsAnswer answer( q );
       
  1239 	answer.notify();
       
  1240 	// and then get rid of the query
       
  1241 	queries.take( i );
       
  1242 #if defined(Q3DNS_DEBUG)
       
  1243 	qDebug( "DNS Manager: no DNS server found on query 0x%04x", q->id );
       
  1244 #endif
       
  1245 	delete q;
       
  1246 	QTimer::singleShot( 1000*10, Q3DnsManager::manager(), SLOT(cleanCache()) );
       
  1247 	// and don't process anything more
       
  1248 	return;
       
  1249     }
       
  1250 
       
  1251     QHostAddress receiver = *theNs->at( q->step % theNs->count() );
       
  1252     if (receiver.isIPv4Address())
       
  1253 	ipv4Socket->writeBlock( p.data(), pp, receiver, 53 );
       
  1254 #if !defined (QT_NO_IPV6)
       
  1255     else
       
  1256 	ipv6Socket->writeBlock( p.data(), pp, receiver, 53 );
       
  1257 #endif
       
  1258 #if defined(Q3DNS_DEBUG)
       
  1259     qDebug( "issuing query 0x%04x (%d) about %s type %d to %s",
       
  1260 	    q->id, q->step, q->l.ascii(), q->t,
       
  1261 	    ns->at( q->step % ns->count() )->toString().ascii() );
       
  1262 #endif
       
  1263     if ( theNs->count() > 1 && q->step == 0 && queries.count() == 1 ) {
       
  1264 	// if it's the first time, and we don't have any other
       
  1265 	// outstanding queries, send nonrecursive queries to the other
       
  1266 	// name servers too.
       
  1267 	p[2] = 0;
       
  1268 	QHostAddress * server;
       
  1269 	while( (server=theNs->next()) != 0 ) {
       
  1270 	    if (server->isIPv4Address())
       
  1271 		ipv4Socket->writeBlock( p.data(), pp, *server, 53 );
       
  1272 #if !defined (QT_NO_IPV6)
       
  1273 	    else
       
  1274 		ipv6Socket->writeBlock( p.data(), pp, *server, 53 );
       
  1275 #endif
       
  1276 #if defined(Q3DNS_DEBUG)
       
  1277 	    qDebug( "copying query to %s", server->toString().ascii() );
       
  1278 #endif
       
  1279 	}
       
  1280     }
       
  1281     q->step++;
       
  1282     // some testing indicates that normal dns queries take up to 0.6
       
  1283     // seconds.  the graph becomes steep around that point, and the
       
  1284     // number of errors rises... so it seems good to retry at that
       
  1285     // point.
       
  1286     q->start( q->step < theNs->count() ? 800 : 1500, true );
       
  1287 }
       
  1288 
       
  1289 
       
  1290 Q3DnsDomain * Q3DnsManager::domain( const QString & label )
       
  1291 {
       
  1292     Q3DnsDomain * d = cache.find( label );
       
  1293     if ( !d ) {
       
  1294 	d = new Q3DnsDomain( label );
       
  1295 	cache.insert( label, d );
       
  1296     }
       
  1297     return d;
       
  1298 }
       
  1299 
       
  1300 
       
  1301 //
       
  1302 //
       
  1303 // the Q3DnsDomain class looks after and coordinates queries for Q3DnsRRs for
       
  1304 // each domain, and the cached Q3DnsRRs.  (A domain, in DNS terminology, is
       
  1305 // a node in the DNS.  "no", "trolltech.com" and "lupinella.troll.no" are
       
  1306 // all domains.)
       
  1307 //
       
  1308 //
       
  1309 
       
  1310 
       
  1311 Q3DnsDomain::Q3DnsDomain( const QString & label )
       
  1312 {
       
  1313     l = label;
       
  1314     rrs = 0;
       
  1315 }
       
  1316 
       
  1317 
       
  1318 Q3DnsDomain::~Q3DnsDomain()
       
  1319 {
       
  1320     delete rrs;
       
  1321     rrs = 0;
       
  1322 }
       
  1323 
       
  1324 
       
  1325 void Q3DnsDomain::add( const QString & label, Q3DnsRR * rr )
       
  1326 {
       
  1327     Q3DnsDomain * d = Q3DnsManager::manager()->domain( label );
       
  1328     if ( !d->rrs ) {
       
  1329 	d->rrs = new Q3PtrList<Q3DnsRR>;
       
  1330 	d->rrs->setAutoDelete( true );
       
  1331     }
       
  1332     d->rrs->append( rr );
       
  1333     rr->domain = d;
       
  1334 }
       
  1335 
       
  1336 
       
  1337 Q3PtrList<Q3DnsRR> * Q3DnsDomain::cached( const Q3Dns * r )
       
  1338 {
       
  1339     Q3PtrList<Q3DnsRR> * l = new Q3PtrList<Q3DnsRR>;
       
  1340 
       
  1341     // test at first if you have to start a query at all
       
  1342     if ( r->recordType() == Q3Dns::A ) {
       
  1343 	if ( r->label().lower() == QLatin1String("localhost") ) {
       
  1344 	    // undocumented hack. ipv4-specific. also, may be a memory
       
  1345 	    // leak? not sure. would be better to do this in doResInit(),
       
  1346 	    // anyway.
       
  1347 	    Q3DnsRR *rrTmp = new Q3DnsRR( r->label() );
       
  1348 	    rrTmp->t = Q3Dns::A;
       
  1349 	    rrTmp->address = QHostAddress( 0x7f000001 );
       
  1350 	    rrTmp->current = true;
       
  1351 	    l->append( rrTmp );
       
  1352 	    return l;
       
  1353 	}
       
  1354 	QHostAddress tmp;
       
  1355 	if ( tmp.setAddress( r->label() ) ) {
       
  1356 	    Q3DnsRR *rrTmp = new Q3DnsRR( r->label() );
       
  1357 	    if ( tmp.isIPv4Address() ) {
       
  1358 		rrTmp->t = Q3Dns::A;
       
  1359                 rrTmp->address = tmp;
       
  1360                 rrTmp->current = true;
       
  1361                 l->append( rrTmp );
       
  1362             } else {
       
  1363                 rrTmp->nxdomain = true;
       
  1364             }
       
  1365 	    return l;
       
  1366 	}
       
  1367     }
       
  1368     if ( r->recordType() == Q3Dns::Aaaa ) {
       
  1369 	QHostAddress tmp;
       
  1370 	if ( tmp.setAddress(r->label()) ) {
       
  1371 	    Q3DnsRR *rrTmp = new Q3DnsRR( r->label() );
       
  1372 	    if ( tmp.isIPv6Address() ) {
       
  1373 		rrTmp->t = Q3Dns::Aaaa;
       
  1374                 rrTmp->address = tmp;
       
  1375                 rrTmp->current = true;
       
  1376                 l->append( rrTmp );
       
  1377             } else {
       
  1378                 rrTmp->nxdomain = true;
       
  1379             }
       
  1380 	    return l;
       
  1381 	}
       
  1382     }
       
  1383 
       
  1384     // if you reach this point, you have to do the query
       
  1385     Q3DnsManager * m = Q3DnsManager::manager();
       
  1386     QStringList n = r->qualifiedNames();
       
  1387     bool nxdomain;
       
  1388     int cnamecount = 0;
       
  1389     int it = 0;
       
  1390     while( it < n.count() ) {
       
  1391 	QString s = n.at(it++);
       
  1392 	nxdomain = false;
       
  1393 #if defined(Q3DNS_DEBUG)
       
  1394 	qDebug( "looking at cache for %s (%s %d)",
       
  1395 		s.ascii(), r->label().ascii(), r->recordType() );
       
  1396 #endif
       
  1397 	Q3DnsDomain * d = m->domain( s );
       
  1398 #if defined(Q3DNS_DEBUG)
       
  1399 	qDebug( " - found %d RRs", d && d->rrs ? d->rrs->count() : 0 );
       
  1400 #endif
       
  1401 	if ( d->rrs )
       
  1402 	    d->rrs->first();
       
  1403 	Q3DnsRR * rr;
       
  1404 	bool answer = false;
       
  1405 	while( d->rrs && (rr=d->rrs->current()) != 0 ) {
       
  1406 	    if ( rr->t == Q3Dns::Cname && r->recordType() != Q3Dns::Cname &&
       
  1407 		 !rr->nxdomain && cnamecount < 16 ) {
       
  1408 		// cname.  if the code is ugly, that may just
       
  1409 		// possibly be because the concept is.
       
  1410 #if defined(Q3DNS_DEBUG)
       
  1411 		qDebug( "found cname from %s to %s",
       
  1412 			r->label().ascii(), rr->target.ascii() );
       
  1413 #endif
       
  1414 		s = rr->target;
       
  1415 		d = m->domain( s );
       
  1416 		if ( d->rrs )
       
  1417 		    d->rrs->first();
       
  1418 		it = n.count();
       
  1419 		// we've elegantly moved over to whatever the cname
       
  1420 		// pointed to.  well, not elegantly.  let's remember
       
  1421 		// that we've done something, anyway, so we can't be
       
  1422 		// fooled into an infinte loop as well.
       
  1423 		cnamecount++;
       
  1424 	    } else {
       
  1425 		if ( rr->t == r->recordType() ) {
       
  1426 		    if ( rr->nxdomain )
       
  1427 			nxdomain = true;
       
  1428 		    else
       
  1429 			answer = true;
       
  1430 		    l->append( rr );
       
  1431 		    if ( rr->deleteTime <= lastSweep ) {
       
  1432 			// we're returning something that'll be
       
  1433 			// deleted soon.  we assume that if the client
       
  1434 			// wanted it twice, it'll want it again, so we
       
  1435 			// ask the name server again right now.
       
  1436 			Q3DnsQuery * query = new Q3DnsQuery;
       
  1437 			query->started = now();
       
  1438 			query->id = ++theId;
       
  1439 			query->t = rr->t;
       
  1440 			query->l = rr->domain->name();
       
  1441 			// note that here, we don't bother about
       
  1442 			// notification. but we do bother about
       
  1443 			// timeouts: we make sure to use high timeouts
       
  1444 			// and few tramsissions.
       
  1445 			query->step = theNs->count();
       
  1446 			QObject::connect( query, SIGNAL(timeout()),
       
  1447 					  Q3DnsManager::manager(),
       
  1448 					  SLOT(retransmit()) );
       
  1449 			Q3DnsManager::manager()->transmitQuery( query );
       
  1450 		    }
       
  1451 		}
       
  1452 		d->rrs->next();
       
  1453 	    }
       
  1454 	}
       
  1455 	// if we found a positive result, return quickly
       
  1456 	if ( answer && l->count() ) {
       
  1457 #if defined(Q3DNS_DEBUG)
       
  1458 	    qDebug( "found %d records for %s",
       
  1459 		    l->count(), r->label().ascii() );
       
  1460 	    l->first();
       
  1461 	    while( l->current() ) {
       
  1462 		qDebug( "  type %d target %s address %s",
       
  1463 		       l->current()->t,
       
  1464 		       l->current()->target.latin1(),
       
  1465 		       l->current()->address.toString().latin1() );
       
  1466 		l->next();
       
  1467 	    }
       
  1468 #endif
       
  1469 	    l->first();
       
  1470 	    return l;
       
  1471 	}
       
  1472 
       
  1473 #if defined(Q3DNS_DEBUG)
       
  1474 	if ( nxdomain )
       
  1475 	    qDebug( "found NXDomain %s", s.ascii() );
       
  1476 #endif
       
  1477 
       
  1478 	if ( !nxdomain ) {
       
  1479 	    // if we didn't, and not a negative result either, perhaps
       
  1480 	    // we need to transmit a query.
       
  1481 	    uint q = 0;
       
  1482 	    while ( q < m->queries.size() &&
       
  1483 		    ( m->queries[q] == 0 ||
       
  1484 		      m->queries[q]->t != r->recordType() ||
       
  1485 		      m->queries[q]->l != s ) )
       
  1486 		q++;
       
  1487 	    // we haven't done it before, so maybe we should.  but
       
  1488 	    // wait - if it's an unqualified name, only ask when all
       
  1489 	    // the other alternatives are exhausted.
       
  1490 	    if ( q == m->queries.size() && ( s.find( QLatin1Char('.') ) >= 0 ||
       
  1491 					     int(l->count()) >= n.count()-1 ) ) {
       
  1492 		Q3DnsQuery * query = new Q3DnsQuery;
       
  1493 		query->started = now();
       
  1494 		query->id = ++theId;
       
  1495 		query->t = r->recordType();
       
  1496 		query->l = s;
       
  1497 		query->dns->replace( (void*)r, (void*)r );
       
  1498 		QObject::connect( query, SIGNAL(timeout()),
       
  1499 				  Q3DnsManager::manager(), SLOT(retransmit()) );
       
  1500 		Q3DnsManager::manager()->transmitQuery( query );
       
  1501 	    } else if ( q < m->queries.size() ) {
       
  1502 		// if we've found an earlier query for the same
       
  1503 		// domain/type, subscribe to its answer
       
  1504 		m->queries[q]->dns->replace( (void*)r, (void*)r );
       
  1505 	    }
       
  1506 	}
       
  1507     }
       
  1508     l->first();
       
  1509     return l;
       
  1510 }
       
  1511 
       
  1512 
       
  1513 void Q3DnsDomain::sweep( Q_UINT32 thisSweep )
       
  1514 {
       
  1515     if ( !rrs )
       
  1516 	return;
       
  1517 
       
  1518     Q3DnsRR * rr;
       
  1519     rrs->first();
       
  1520     while( (rr=rrs->current()) != 0 ) {
       
  1521 	if ( !rr->deleteTime )
       
  1522 	    rr->deleteTime = thisSweep; // will hit next time around
       
  1523 
       
  1524 #if defined(Q3DNS_DEBUG)
       
  1525 	qDebug( "Q3Dns::sweep: %s type %d expires %u %u - %s / %s",
       
  1526 	       rr->domain->name().latin1(), rr->t,
       
  1527 	       rr->expireTime, rr->deleteTime,
       
  1528 	       rr->target.latin1(), rr->address.toString().latin1());
       
  1529 #endif
       
  1530 	if ( rr->current == false ||
       
  1531 	     rr->t == Q3Dns::None ||
       
  1532 	     rr->deleteTime <= thisSweep ||
       
  1533 	     rr->expireTime <= thisSweep )
       
  1534 	    rrs->remove();
       
  1535 	else
       
  1536 	    rrs->next();
       
  1537     }
       
  1538 
       
  1539     if ( rrs->isEmpty() ) {
       
  1540 	delete rrs;
       
  1541 	rrs = 0;
       
  1542     }
       
  1543 }
       
  1544 
       
  1545 
       
  1546 
       
  1547 
       
  1548 // the itsy-bitsy little socket class I don't really need except for
       
  1549 // so I can subclass and reimplement the slots.
       
  1550 
       
  1551 
       
  1552 Q3DnsSocket::Q3DnsSocket( QObject * parent, const char * name )
       
  1553     : QObject( parent, name )
       
  1554 {
       
  1555     // nothing
       
  1556 }
       
  1557 
       
  1558 
       
  1559 Q3DnsSocket::~Q3DnsSocket()
       
  1560 {
       
  1561     // nothing
       
  1562 }
       
  1563 
       
  1564 
       
  1565 void Q3DnsSocket::cleanCache()
       
  1566 {
       
  1567     // nothing
       
  1568 }
       
  1569 
       
  1570 
       
  1571 void Q3DnsSocket::retransmit()
       
  1572 {
       
  1573     // nothing
       
  1574 }
       
  1575 
       
  1576 
       
  1577 void Q3DnsSocket::answer()
       
  1578 {
       
  1579     // nothing
       
  1580 }
       
  1581 
       
  1582 
       
  1583 /*!
       
  1584     \class Q3Dns
       
  1585     \brief The Q3Dns class provides asynchronous DNS lookups.
       
  1586 
       
  1587     \compat
       
  1588 
       
  1589     Both Windows and Unix provide synchronous DNS lookups; Windows
       
  1590     provides some asynchronous support too. At the time of writing
       
  1591     neither operating system provides asynchronous support for
       
  1592     anything other than hostname-to-address mapping.
       
  1593 
       
  1594     Q3Dns rectifies this shortcoming, by providing asynchronous caching
       
  1595     lookups for the record types that we expect modern GUI
       
  1596     applications to need in the near future.
       
  1597 
       
  1598     The class is \e not straightforward to use (although it is much
       
  1599     simpler than the native APIs); Q3Socket provides much easier to use
       
  1600     TCP connection facilities. The aim of Q3Dns is to provide a correct
       
  1601     and small API to the DNS and nothing more. (We use "correctness"
       
  1602     to mean that the DNS information is correctly cached, and
       
  1603     correctly timed out.)
       
  1604 
       
  1605     The API comprises a constructor, functions to set the DNS node
       
  1606     (the domain in DNS terminology) and record type (setLabel() and
       
  1607     setRecordType()), the corresponding get functions, an isWorking()
       
  1608     function to determine whether Q3Dns is working or reading, a
       
  1609     resultsReady() signal and query functions for the result.
       
  1610 
       
  1611     There is one query function for each RecordType, namely
       
  1612     addresses(), mailServers(), servers(), hostNames() and texts().
       
  1613     There are also two generic query functions: canonicalName()
       
  1614     returns the name you'll presumably end up using (the exact meaning
       
  1615     of this depends on the record type) and qualifiedNames() returns a
       
  1616     list of the fully qualified names label() maps to.
       
  1617 
       
  1618     \sa Q3Socket
       
  1619 */
       
  1620 
       
  1621 /*!
       
  1622     Constructs a DNS query object with invalid settings for both the
       
  1623     label and the search type.
       
  1624 */
       
  1625 
       
  1626 Q3Dns::Q3Dns()
       
  1627 {
       
  1628     d = new Q3DnsPrivate;
       
  1629     t = None;
       
  1630 }
       
  1631 
       
  1632 
       
  1633 
       
  1634 
       
  1635 /*!
       
  1636     Constructs a DNS query object that will return record type \a rr
       
  1637     information about \a label.
       
  1638 
       
  1639     The DNS lookup is started the next time the application enters the
       
  1640     event loop. When the result is found the signal resultsReady() is
       
  1641     emitted.
       
  1642 
       
  1643     \a rr defaults to \c A, IPv4 addresses.
       
  1644 */
       
  1645 
       
  1646 Q3Dns::Q3Dns( const QString & label, RecordType rr )
       
  1647 {
       
  1648     d = new Q3DnsPrivate;
       
  1649     t = rr;
       
  1650     setLabel( label );
       
  1651     setStartQueryTimer(); // start query the next time we enter event loop
       
  1652 }
       
  1653 
       
  1654 
       
  1655 
       
  1656 /*!
       
  1657     Constructs a DNS query object that will return record type \a rr
       
  1658     information about host address \a address. The label is set to the
       
  1659     IN-ADDR.ARPA domain name. This is useful in combination with the
       
  1660     \c Ptr record type (e.g. if you want to look up a hostname for a
       
  1661     given address).
       
  1662 
       
  1663     The DNS lookup is started the next time the application enters the
       
  1664     event loop. When the result is found the signal resultsReady() is
       
  1665     emitted.
       
  1666 
       
  1667     \a rr defaults to \c Ptr, that maps addresses to hostnames.
       
  1668 */
       
  1669 
       
  1670 Q3Dns::Q3Dns( const QHostAddress & address, RecordType rr )
       
  1671 {
       
  1672     d = new Q3DnsPrivate;
       
  1673     t = rr;
       
  1674     setLabel( address );
       
  1675     setStartQueryTimer(); // start query the next time we enter event loop
       
  1676 }
       
  1677 
       
  1678 
       
  1679 
       
  1680 
       
  1681 /*!
       
  1682     Destroys the DNS query object and frees its allocated resources.
       
  1683 */
       
  1684 
       
  1685 Q3Dns::~Q3Dns()
       
  1686 {
       
  1687     if ( globalManager ) {
       
  1688 	uint q = 0;
       
  1689 	Q3DnsManager * m = globalManager;
       
  1690 	while( q < m->queries.size() ) {
       
  1691 	    Q3DnsQuery * query=m->queries[q];
       
  1692 	    if ( query && query->dns )
       
  1693 		    (void)query->dns->take( (void*) this );
       
  1694 		q++;
       
  1695 	}
       
  1696 
       
  1697     }
       
  1698 
       
  1699     delete d;
       
  1700     d = 0;
       
  1701 }
       
  1702 
       
  1703 
       
  1704 
       
  1705 
       
  1706 /*!
       
  1707     Sets this DNS query object to query for information about \a
       
  1708     label.
       
  1709 
       
  1710     This does not change the recordType(), but its isWorking() status
       
  1711     will probably change as a result.
       
  1712 
       
  1713     The DNS lookup is started the next time the application enters the
       
  1714     event loop. When the result is found the signal resultsReady() is
       
  1715     emitted.
       
  1716 */
       
  1717 
       
  1718 void Q3Dns::setLabel( const QString & label )
       
  1719 {
       
  1720     l = label;
       
  1721     d->noNames = false;
       
  1722 
       
  1723     // construct a list of qualified names
       
  1724     n.clear();
       
  1725     if ( l.length() > 1 && l[(int)l.length()-1] == QLatin1Char('.') ) {
       
  1726 	n.append( l.left( l.length()-1 ).lower() );
       
  1727     } else {
       
  1728 	int i = l.length();
       
  1729 	int dots = 0;
       
  1730 	const int maxDots = 2;
       
  1731 	while( i && dots < maxDots ) {
       
  1732 	    if ( l[--i] == QLatin1Char('.') )
       
  1733 		dots++;
       
  1734 	}
       
  1735 	if ( dots < maxDots ) {
       
  1736 	    (void)Q3DnsManager::manager(); // create a Q3DnsManager, if it is not already there
       
  1737 	    Q3StrListIterator it( *theDomains );
       
  1738 	    const char * dom;
       
  1739 	    while( (dom=it.current()) != 0 ) {
       
  1740 		++it;
       
  1741 		n.append( l.lower() + QLatin1Char('.') + QLatin1String(dom) );
       
  1742 	    }
       
  1743 	}
       
  1744 	n.append( l.lower() );
       
  1745     }
       
  1746 
       
  1747 #if defined(Q_DNS_SYNCHRONOUS)
       
  1748     if ( d->noEventLoop ) {
       
  1749 	doSynchronousLookup();
       
  1750     } else {
       
  1751 	setStartQueryTimer(); // start query the next time we enter event loop
       
  1752     }
       
  1753 #else
       
  1754     setStartQueryTimer(); // start query the next time we enter event loop
       
  1755 #endif
       
  1756 #if defined(Q3DNS_DEBUG)
       
  1757     qDebug( "Q3Dns::setLabel: %d address(es) for %s", n.count(), l.ascii() );
       
  1758     int i = 0;
       
  1759     for( i = 0; i < (int)n.count(); i++ )
       
  1760 	qDebug( "Q3Dns::setLabel: %d: %s", i, n[i].ascii() );
       
  1761 #endif
       
  1762 }
       
  1763 
       
  1764 
       
  1765 /*!
       
  1766     \overload
       
  1767 
       
  1768     Sets this DNS query object to query for information about the host
       
  1769     address \a address. The label is set to the IN-ADDR.ARPA domain
       
  1770     name. This is useful in combination with the \c Ptr record type
       
  1771     (e.g. if you want to look up a hostname for a given address).
       
  1772 */
       
  1773 
       
  1774 void Q3Dns::setLabel( const QHostAddress & address )
       
  1775 {
       
  1776     setLabel( toInAddrArpaDomain( address ) );
       
  1777 }
       
  1778 
       
  1779 
       
  1780 /*!
       
  1781     \fn QStringList Q3Dns::qualifiedNames() const
       
  1782 
       
  1783     Returns a list of the fully qualified names label() maps to.
       
  1784 
       
  1785     Note that if you want to iterate over the list, you should iterate
       
  1786     over a copy, e.g.
       
  1787     \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 0
       
  1788 
       
  1789 */
       
  1790 
       
  1791 
       
  1792 /*!
       
  1793     \fn QString Q3Dns::label() const
       
  1794 
       
  1795     Returns the domain name for which this object returns information.
       
  1796 
       
  1797     \sa setLabel()
       
  1798 */
       
  1799 
       
  1800 /*!
       
  1801     \enum Q3Dns::RecordType
       
  1802 
       
  1803     This enum type defines the record types Q3Dns can handle. The DNS
       
  1804     provides many more; these are the ones we've judged to be in
       
  1805     current use, useful for GUI programs and important enough to
       
  1806     support right away:
       
  1807 
       
  1808     \value None  No information. This exists only so that Q3Dns can
       
  1809     have a default.
       
  1810 
       
  1811     \value A  IPv4 addresses. By far the most common type.
       
  1812 
       
  1813     \value Aaaa  IPv6 addresses. So far mostly unused.
       
  1814 
       
  1815     \value Mx  Mail eXchanger names. Used for mail delivery.
       
  1816 
       
  1817     \value Srv  SeRVer names. Generic record type for finding
       
  1818     servers. So far mostly unused.
       
  1819 
       
  1820     \value Cname  Canonical names. Maps from nicknames to the true
       
  1821     name (the canonical name) for a host.
       
  1822 
       
  1823     \value Ptr  name PoinTeRs. Maps from IPv4 or IPv6 addresses to hostnames.
       
  1824 
       
  1825     \value Txt  arbitrary TeXT for domains.
       
  1826 
       
  1827     We expect that some support for the
       
  1828     \l{http://www.rfc-editor.org/rfc/rfc2535.txt}{RFC 2535}
       
  1829     extensions will be added in future versions.
       
  1830 */
       
  1831 
       
  1832 /*!
       
  1833     Sets this object to query for record type \a rr records.
       
  1834 
       
  1835     The DNS lookup is started the next time the application enters the
       
  1836     event loop. When the result is found the signal resultsReady() is
       
  1837     emitted.
       
  1838 
       
  1839     \sa RecordType
       
  1840 */
       
  1841 
       
  1842 void Q3Dns::setRecordType( RecordType rr )
       
  1843 {
       
  1844     t = rr;
       
  1845     d->noNames = false;
       
  1846     setStartQueryTimer(); // start query the next time we enter event loop
       
  1847 }
       
  1848 
       
  1849 /*!
       
  1850   \internal
       
  1851 
       
  1852   Private slot for starting the query.
       
  1853 */
       
  1854 void Q3Dns::startQuery()
       
  1855 {
       
  1856     // isWorking() starts the query (if necessary)
       
  1857     if ( !isWorking() )
       
  1858 	emit resultsReady();
       
  1859 }
       
  1860 
       
  1861 /*!
       
  1862     The three functions Q3Dns::Q3Dns(QString, RecordType),
       
  1863     Q3Dns::setLabel() and Q3Dns::setRecordType() may start a DNS lookup.
       
  1864     This function handles setting up the single shot timer.
       
  1865 */
       
  1866 void Q3Dns::setStartQueryTimer()
       
  1867 {
       
  1868 #if defined(Q_DNS_SYNCHRONOUS)
       
  1869     if ( !d->queryTimer && !d->noEventLoop )
       
  1870 #else
       
  1871     if ( !d->queryTimer )
       
  1872 #endif
       
  1873     {
       
  1874 	// start the query the next time we enter event loop
       
  1875 	d->queryTimer = new QTimer( this );
       
  1876 	connect( d->queryTimer, SIGNAL(timeout()),
       
  1877 		 this, SLOT(startQuery()) );
       
  1878 	d->queryTimer->start( 0, true );
       
  1879     }
       
  1880 }
       
  1881 
       
  1882 /*
       
  1883     Transforms the host address \a address to the IN-ADDR.ARPA domain
       
  1884     name. Returns something indeterminate if you're sloppy or
       
  1885     naughty. This function has an IPv4-specific name, but works for
       
  1886     IPv6 too.
       
  1887 */
       
  1888 QString Q3Dns::toInAddrArpaDomain( const QHostAddress &address )
       
  1889 {
       
  1890     QString s;
       
  1891     if ( address.isNull() ) {
       
  1892 	// if the address isn't valid, neither of the other two make
       
  1893 	// cases make sense. better to just return.
       
  1894     } else if ( address.isIp4Addr() ) {
       
  1895 	Q_UINT32 i = address.ip4Addr();
       
  1896 	s.sprintf( "%d.%d.%d.%d.IN-ADDR.ARPA",
       
  1897 		   i & 0xff, (i >> 8) & 0xff, (i>>16) & 0xff, (i>>24) & 0xff );
       
  1898     } else {
       
  1899 	// RFC 3152. (1886 is deprecated, and clients no longer need to
       
  1900 	// support it, in practice).
       
  1901 	Q_IPV6ADDR i = address.toIPv6Address();
       
  1902 	s = QLatin1String("ip6.arpa");
       
  1903 	uint b = 0;
       
  1904 	while( b < 16 ) {
       
  1905 	    s = QString::number( i.c[b]%16, 16 ) + QLatin1Char('.') +
       
  1906 		QString::number( i.c[b]/16, 16 ) + QLatin1Char('.') + s;
       
  1907 	    b++;
       
  1908 	}
       
  1909     }
       
  1910     return s;
       
  1911 }
       
  1912 
       
  1913 
       
  1914 /*!
       
  1915     \fn Q3Dns::RecordType Q3Dns::recordType() const
       
  1916 
       
  1917     Returns the record type of this DNS query object.
       
  1918 
       
  1919     \sa setRecordType() RecordType
       
  1920 */
       
  1921 
       
  1922 /*!
       
  1923     \fn void Q3Dns::resultsReady()
       
  1924 
       
  1925     This signal is emitted when results are available for one of the
       
  1926     qualifiedNames().
       
  1927 */
       
  1928 
       
  1929 /*!
       
  1930     Returns true if Q3Dns is doing a lookup for this object (i.e. if it
       
  1931     does not already have the necessary information); otherwise
       
  1932     returns false.
       
  1933 
       
  1934     Q3Dns emits the resultsReady() signal when the status changes to false.
       
  1935 */
       
  1936 
       
  1937 bool Q3Dns::isWorking() const
       
  1938 {
       
  1939 #if defined(Q3DNS_DEBUG)
       
  1940     qDebug( "Q3Dns::isWorking (%s, %d)", l.ascii(), t );
       
  1941 #endif
       
  1942     if ( t == None )
       
  1943 	return false;
       
  1944 
       
  1945 #if defined(Q_DNS_SYNCHRONOUS)
       
  1946     if ( d->noEventLoop )
       
  1947 	return true;
       
  1948 #endif
       
  1949 
       
  1950     Q3PtrList<Q3DnsRR> * ll = Q3DnsDomain::cached( this );
       
  1951     Q_LONG queries = n.count();
       
  1952     while( ll->current() != 0 ) {
       
  1953 	if ( ll->current()->nxdomain ) {
       
  1954 	    queries--;
       
  1955 	} else {
       
  1956 	    delete ll;
       
  1957 	    return false;
       
  1958 	}
       
  1959 	ll->next();
       
  1960     }
       
  1961     delete ll;
       
  1962 
       
  1963     if ( queries <= 0 )
       
  1964 	return false;
       
  1965     if ( d->noNames )
       
  1966 	return false;
       
  1967     return true;
       
  1968 }
       
  1969 
       
  1970 
       
  1971 /*!
       
  1972     Returns a list of the addresses for this name if this Q3Dns object
       
  1973     has a recordType() of Q3Dns::A or Q3Dns::Aaaa and the answer
       
  1974     is available; otherwise returns an empty list.
       
  1975 
       
  1976     As a special case, if label() is a valid numeric IP address, this
       
  1977     function returns that address.
       
  1978 
       
  1979     Note that if you want to iterate over the list, you should iterate
       
  1980     over a copy, e.g.
       
  1981     \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 1
       
  1982 
       
  1983 */
       
  1984 
       
  1985 Q3ValueList<QHostAddress> Q3Dns::addresses() const
       
  1986 {
       
  1987 #if defined(Q3DNS_DEBUG)
       
  1988     qDebug( "Q3Dns::addresses (%s)", l.ascii() );
       
  1989 #endif
       
  1990     Q3ValueList<QHostAddress> result;
       
  1991     if ( t != A && t != Aaaa )
       
  1992 	return result;
       
  1993 
       
  1994     Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
       
  1995 
       
  1996     Q3DnsRR * rr;
       
  1997     while( (rr=cached->current()) != 0 ) {
       
  1998 	if ( rr->current && !rr->nxdomain )
       
  1999 	    result.append( rr->address );
       
  2000 	cached->next();
       
  2001     }
       
  2002     delete cached;
       
  2003     return result;
       
  2004 }
       
  2005 
       
  2006 
       
  2007 /*!
       
  2008     \class Q3Dns::MailServer
       
  2009     \brief The Q3Dns::MailServer class is  described in Q3Dns::mailServers().
       
  2010 
       
  2011     \internal
       
  2012 */
       
  2013 
       
  2014 /*!
       
  2015     Returns a list of mail servers if the record type is \c Mx. The
       
  2016     class Q3Dns::MailServer contains the following public variables:
       
  2017     \list
       
  2018     \i QString Q3Dns::MailServer::name
       
  2019     \i Q_UINT16 Q3Dns::MailServer::priority
       
  2020     \endlist
       
  2021 
       
  2022     Note that if you want to iterate over the list, you should iterate
       
  2023     over a copy, e.g.
       
  2024     \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 2
       
  2025 
       
  2026 */
       
  2027 Q3ValueList<Q3Dns::MailServer> Q3Dns::mailServers() const
       
  2028 {
       
  2029 #if defined(Q3DNS_DEBUG)
       
  2030     qDebug( "Q3Dns::mailServers (%s)", l.ascii() );
       
  2031 #endif
       
  2032     Q3ValueList<Q3Dns::MailServer> result;
       
  2033     if ( t != Mx )
       
  2034 	return result;
       
  2035 
       
  2036     Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
       
  2037 
       
  2038     Q3DnsRR * rr;
       
  2039     while( (rr=cached->current()) != 0 ) {
       
  2040 	if ( rr->current && !rr->nxdomain ) {
       
  2041 	    MailServer ms( rr->target, rr->priority );
       
  2042 	    result.append( ms );
       
  2043 	}
       
  2044 	cached->next();
       
  2045     }
       
  2046     delete cached;
       
  2047     return result;
       
  2048 }
       
  2049 
       
  2050 
       
  2051 /*!
       
  2052     \class Q3Dns::Server
       
  2053     \brief The Q3Dns::Server class is described in Q3Dns::servers().
       
  2054 
       
  2055     \internal
       
  2056 */
       
  2057 
       
  2058 /*!
       
  2059     Returns a list of servers if the record type is \c Srv. The class
       
  2060     Q3Dns::Server contains the following public variables:
       
  2061     \list
       
  2062     \i QString Q3Dns::Server::name
       
  2063     \i Q_UINT16 Q3Dns::Server::priority
       
  2064     \i Q_UINT16 Q3Dns::Server::weight
       
  2065     \i Q_UINT16 Q3Dns::Server::port
       
  2066     \endlist
       
  2067 
       
  2068     Note that if you want to iterate over the list, you should iterate
       
  2069     over a copy, e.g.
       
  2070     \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 3
       
  2071 */
       
  2072 Q3ValueList<Q3Dns::Server> Q3Dns::servers() const
       
  2073 {
       
  2074 #if defined(Q3DNS_DEBUG)
       
  2075     qDebug( "Q3Dns::servers (%s)", l.ascii() );
       
  2076 #endif
       
  2077     Q3ValueList<Q3Dns::Server> result;
       
  2078     if ( t != Srv )
       
  2079 	return result;
       
  2080 
       
  2081     Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
       
  2082 
       
  2083     Q3DnsRR * rr;
       
  2084     while( (rr=cached->current()) != 0 ) {
       
  2085 	if ( rr->current && !rr->nxdomain ) {
       
  2086 	    Server s( rr->target, rr->priority, rr->weight, rr->port );
       
  2087 	    result.append( s );
       
  2088 	}
       
  2089 	cached->next();
       
  2090     }
       
  2091     delete cached;
       
  2092     return result;
       
  2093 }
       
  2094 
       
  2095 
       
  2096 /*!
       
  2097     Returns a list of host names if the record type is \c Ptr.
       
  2098 
       
  2099     Note that if you want to iterate over the list, you should iterate
       
  2100     over a copy, e.g.
       
  2101     \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 4
       
  2102 
       
  2103 */
       
  2104 QStringList Q3Dns::hostNames() const
       
  2105 {
       
  2106 #if defined(Q3DNS_DEBUG)
       
  2107     qDebug( "Q3Dns::hostNames (%s)", l.ascii() );
       
  2108 #endif
       
  2109     QStringList result;
       
  2110     if ( t != Ptr )
       
  2111 	return result;
       
  2112 
       
  2113     Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
       
  2114 
       
  2115     Q3DnsRR * rr;
       
  2116     while( (rr=cached->current()) != 0 ) {
       
  2117 	if ( rr->current && !rr->nxdomain ) {
       
  2118 	    QString str( rr->target );
       
  2119 	    result.append( str );
       
  2120 	}
       
  2121 	cached->next();
       
  2122     }
       
  2123     delete cached;
       
  2124     return result;
       
  2125 }
       
  2126 
       
  2127 
       
  2128 /*!
       
  2129     Returns a list of texts if the record type is \c Txt.
       
  2130 
       
  2131     Note that if you want to iterate over the list, you should iterate
       
  2132     over a copy, e.g.
       
  2133     \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 5
       
  2134 */
       
  2135 QStringList Q3Dns::texts() const
       
  2136 {
       
  2137 #if defined(Q3DNS_DEBUG)
       
  2138     qDebug( "Q3Dns::texts (%s)", l.ascii() );
       
  2139 #endif
       
  2140     QStringList result;
       
  2141     if ( t != Txt )
       
  2142 	return result;
       
  2143 
       
  2144     Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
       
  2145 
       
  2146     Q3DnsRR * rr;
       
  2147     while( (rr=cached->current()) != 0 ) {
       
  2148 	if ( rr->current && !rr->nxdomain ) {
       
  2149 	    QString str( rr->text );
       
  2150 	    result.append( str );
       
  2151 	}
       
  2152 	cached->next();
       
  2153     }
       
  2154     delete cached;
       
  2155     return result;
       
  2156 }
       
  2157 
       
  2158 
       
  2159 /*!
       
  2160     Returns the canonical name for this DNS node. (This works
       
  2161     regardless of what recordType() is set to.)
       
  2162 
       
  2163     If the canonical name isn't known, this function returns a null
       
  2164     string.
       
  2165 
       
  2166     The canonical name of a DNS node is its full name, or the full
       
  2167     name of the target of its CNAME. For example, if l.trolltech.com
       
  2168     is a CNAME to lillian.troll.no, and the search path for Q3Dns is
       
  2169     "trolltech.com", then the canonical name for all of "lillian",
       
  2170     "l", "lillian.troll.no." and "l.trolltech.com" is
       
  2171     "lillian.troll.no.".
       
  2172 */
       
  2173 
       
  2174 QString Q3Dns::canonicalName() const
       
  2175 {
       
  2176     // the cname should work regardless of the recordType(), so set the record
       
  2177     // type temporarily to cname when you look at the cache
       
  2178     Q3Dns *that = (Q3Dns*) this; // mutable function
       
  2179     RecordType oldType = t;
       
  2180     that->t = Cname;
       
  2181     Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( that );
       
  2182     that->t = oldType;
       
  2183 
       
  2184     Q3DnsRR * rr;
       
  2185     while( (rr=cached->current()) != 0 ) {
       
  2186 	if ( rr->current && !rr->nxdomain && rr->domain ) {
       
  2187 	    delete cached;
       
  2188 	    return rr->target;
       
  2189 	}
       
  2190 	cached->next();
       
  2191     }
       
  2192     delete cached;
       
  2193     return QString();
       
  2194 }
       
  2195 
       
  2196 #if defined(Q_DNS_SYNCHRONOUS)
       
  2197 /*! \reimp
       
  2198 */
       
  2199 void Q3Dns::connectNotify( const char *signal )
       
  2200 {
       
  2201     if ( d->noEventLoop && qstrcmp(signal,SIGNAL(resultsReady()) )==0 ) {
       
  2202 	doSynchronousLookup();
       
  2203     }
       
  2204 }
       
  2205 #endif
       
  2206 
       
  2207 #if defined(Q_OS_WIN32) || defined(Q_OS_CYGWIN)
       
  2208 
       
  2209 #if defined(Q_DNS_SYNCHRONOUS)
       
  2210 void Q3Dns::doSynchronousLookup()
       
  2211 {
       
  2212     // ### not implemented yet
       
  2213 }
       
  2214 #endif
       
  2215 
       
  2216 // the following typedefs are needed for GetNetworkParams() API call
       
  2217 #ifndef IP_TYPES_INCLUDED
       
  2218 #define MAX_HOSTNAME_LEN    128
       
  2219 #define MAX_DOMAIN_NAME_LEN 128
       
  2220 #define MAX_SCOPE_ID_LEN    256
       
  2221 typedef struct {
       
  2222     char String[4 * 4];
       
  2223 } IP_ADDRESS_STRING, *PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING;
       
  2224 typedef struct _IP_ADDR_STRING {
       
  2225     struct _IP_ADDR_STRING* Next;
       
  2226     IP_ADDRESS_STRING IpAddress;
       
  2227     IP_MASK_STRING IpMask;
       
  2228     DWORD Context;
       
  2229 } IP_ADDR_STRING, *PIP_ADDR_STRING;
       
  2230 typedef struct {
       
  2231     char HostName[MAX_HOSTNAME_LEN + 4] ;
       
  2232     char DomainName[MAX_DOMAIN_NAME_LEN + 4];
       
  2233     PIP_ADDR_STRING CurrentDnsServer;
       
  2234     IP_ADDR_STRING DnsServerList;
       
  2235     UINT NodeType;
       
  2236     char ScopeId[MAX_SCOPE_ID_LEN + 4];
       
  2237     UINT EnableRouting;
       
  2238     UINT EnableProxy;
       
  2239     UINT EnableDns;
       
  2240 } FIXED_INFO, *PFIXED_INFO;
       
  2241 #endif
       
  2242 typedef DWORD (WINAPI *GNP)( PFIXED_INFO, PULONG );
       
  2243 
       
  2244 // ### FIXME: this code is duplicated in qfiledialog.cpp
       
  2245 static QString getWindowsRegString(HKEY key, const QString &subKey)
       
  2246 {
       
  2247     QString s;
       
  2248 
       
  2249     wchar_t buf[1024];
       
  2250     DWORD bsz = sizeof(buf) / sizeof(wchar_t);
       
  2251     int r = RegQueryValueEx(key, (wchar_t*)subKey.utf16(), 0, 0, (LPBYTE)buf, &bsz);
       
  2252     if (r == ERROR_SUCCESS) {
       
  2253         s = QString::fromWCharArray(buf);
       
  2254     } else if (r == ERROR_MORE_DATA) {
       
  2255         char *ptr = new char[bsz+1];
       
  2256         r = RegQueryValueEx(key, (wchar_t*)subKey.utf16(), 0, 0, (LPBYTE)ptr, &bsz);
       
  2257         if (r == ERROR_SUCCESS)
       
  2258             s = QLatin1String(ptr);
       
  2259         delete [] ptr;
       
  2260     }
       
  2261 
       
  2262     return s;
       
  2263 }
       
  2264 
       
  2265 static bool getDnsParamsFromRegistry( const QString &path,
       
  2266     QString *domainName, QString *nameServer, QString *searchList )
       
  2267 {
       
  2268     HKEY k;
       
  2269     int r = RegOpenKeyEx( HKEY_LOCAL_MACHINE, (wchar_t*)path.utf16(), 0, KEY_READ, &k );
       
  2270 
       
  2271     if ( r == ERROR_SUCCESS ) {
       
  2272 	*domainName = getWindowsRegString( k, QLatin1String("DhcpDomain") );
       
  2273 	if ( domainName->isEmpty() )
       
  2274 	    *domainName = getWindowsRegString( k, QLatin1String("Domain") );
       
  2275 
       
  2276 	*nameServer = getWindowsRegString( k, QLatin1String("DhcpNameServer") );
       
  2277 	if ( nameServer->isEmpty() )
       
  2278 	    *nameServer = getWindowsRegString( k, QLatin1String("NameServer") );
       
  2279 
       
  2280 	*searchList = getWindowsRegString( k, QLatin1String("SearchList") );
       
  2281     }
       
  2282     RegCloseKey( k );
       
  2283     return r == ERROR_SUCCESS;
       
  2284 }
       
  2285 
       
  2286 void Q3Dns::doResInit()
       
  2287 {
       
  2288     char separator = 0;
       
  2289 
       
  2290     if ( theNs )
       
  2291         delete theNs;
       
  2292     theNs = new Q3PtrList<QHostAddress>;
       
  2293     theNs->setAutoDelete( true );
       
  2294     theDomains = new Q3StrList( true );
       
  2295     theDomains->setAutoDelete( true );
       
  2296 
       
  2297     QString domainName, nameServer, searchList;
       
  2298 
       
  2299     bool gotNetworkParams = false;
       
  2300     // try the API call GetNetworkParams() first and use registry lookup only
       
  2301     // as a fallback
       
  2302     HINSTANCE hinstLib = LoadLibrary( L"iphlpapi" );
       
  2303     if ( hinstLib != 0 ) {
       
  2304 #ifdef Q_OS_WINCE
       
  2305 	GNP getNetworkParams = (GNP) GetProcAddress( hinstLib, L"GetNetworkParams" );
       
  2306 #else
       
  2307 	GNP getNetworkParams = (GNP) GetProcAddress( hinstLib, "GetNetworkParams" );
       
  2308 #endif
       
  2309 	if ( getNetworkParams != 0 ) {
       
  2310 	    ULONG l = 0;
       
  2311 	    DWORD res;
       
  2312 	    res = getNetworkParams( 0, &l );
       
  2313 	    if ( res == ERROR_BUFFER_OVERFLOW ) {
       
  2314 		FIXED_INFO *finfo = (FIXED_INFO*)new char[l];
       
  2315 		res = getNetworkParams( finfo, &l );
       
  2316 		if ( res == ERROR_SUCCESS ) {
       
  2317 		    domainName = QLatin1String(finfo->DomainName);
       
  2318 		    nameServer = QLatin1String("");
       
  2319 		    IP_ADDR_STRING *dnsServer = &finfo->DnsServerList;
       
  2320 		    while ( dnsServer != 0 ) {
       
  2321 			nameServer += QLatin1String(dnsServer->IpAddress.String);
       
  2322 			dnsServer = dnsServer->Next;
       
  2323 			if ( dnsServer != 0 )
       
  2324 			    nameServer += QLatin1Char(' ');
       
  2325 		    }
       
  2326 		    searchList = QLatin1String("");
       
  2327 		    separator = ' ';
       
  2328 		    gotNetworkParams = true;
       
  2329 		}
       
  2330 		delete[] finfo;
       
  2331 	    }
       
  2332 	}
       
  2333 	FreeLibrary( hinstLib );
       
  2334     }
       
  2335     if ( !gotNetworkParams ) {
       
  2336 	if ( getDnsParamsFromRegistry(
       
  2337 	    QLatin1String("System\\CurrentControlSet\\Services\\Tcpip\\Parameters"),
       
  2338 		    &domainName, &nameServer, &searchList )) {
       
  2339 	    separator = ' ';
       
  2340 	} else {
       
  2341 	    // Could not access the TCP/IP parameters
       
  2342 	    domainName = QLatin1String("");
       
  2343 	    nameServer = QLatin1String("127.0.0.1");
       
  2344 	    searchList = QLatin1String("");
       
  2345 	    separator = ' ';
       
  2346 	}
       
  2347     }
       
  2348 
       
  2349     nameServer = nameServer.simplifyWhiteSpace();
       
  2350     int first, last;
       
  2351     if ( !nameServer.isEmpty() ) {
       
  2352 	first = 0;
       
  2353 	do {
       
  2354 	    last = nameServer.find( QLatin1Char(separator), first );
       
  2355 	    if ( last < 0 )
       
  2356 		last = nameServer.length();
       
  2357 	    Q3Dns tmp( nameServer.mid( first, last-first ), Q3Dns::A );
       
  2358 	    Q3ValueList<QHostAddress> address = tmp.addresses();
       
  2359 	    Q_LONG i = address.count();
       
  2360 	    while( i )
       
  2361 		theNs->append( new QHostAddress(address[--i]) );
       
  2362 	    first = last+1;
       
  2363 	} while( first < (int)nameServer.length() );
       
  2364     }
       
  2365 
       
  2366     searchList += QLatin1Char(' ') + domainName;
       
  2367     searchList = searchList.simplifyWhiteSpace().lower();
       
  2368     first = 0;
       
  2369     do {
       
  2370 	last = searchList.find( QLatin1Char(separator), first );
       
  2371 	if ( last < 0 )
       
  2372 	    last = searchList.length();
       
  2373 	theDomains->append( qstrdup( searchList.mid( first, last-first ).latin1() ) );
       
  2374 	first = last+1;
       
  2375     } while( first < (int)searchList.length() );
       
  2376 }
       
  2377 
       
  2378 #elif defined(Q_OS_UNIX)
       
  2379 
       
  2380 #if defined(Q_DNS_SYNCHRONOUS)
       
  2381 void Q3Dns::doSynchronousLookup()
       
  2382 {
       
  2383     if ( t!=None && !l.isEmpty() ) {
       
  2384 	Q3ValueListIterator<QString> it = n.begin();
       
  2385 	Q3ValueListIterator<QString> end = n.end();
       
  2386 	int type;
       
  2387 	switch( t ) {
       
  2388 	    case Q3Dns::A:
       
  2389 		type = 1;
       
  2390 		break;
       
  2391 	    case Q3Dns::Aaaa:
       
  2392 		type = 28;
       
  2393 		break;
       
  2394 	    case Q3Dns::Mx:
       
  2395 		type = 15;
       
  2396 		break;
       
  2397 	    case Q3Dns::Srv:
       
  2398 		type = 33;
       
  2399 		break;
       
  2400 	    case Q3Dns::Cname:
       
  2401 		type = 5;
       
  2402 		break;
       
  2403 	    case Q3Dns::Ptr:
       
  2404 		type = 12;
       
  2405 		break;
       
  2406 	    case Q3Dns::Txt:
       
  2407 		type = 16;
       
  2408 		break;
       
  2409 	    default:
       
  2410 		type = (char)255; // any
       
  2411 		break;
       
  2412 	}
       
  2413 	while( it != end ) {
       
  2414 	    QString s = *it;
       
  2415 	    it++;
       
  2416 	    QByteArray ba( 512 );
       
  2417 	    int len = res_search( s.latin1(), 1, type, (uchar*)ba.data(), ba.size() );
       
  2418 	    if ( len > 0 ) {
       
  2419 		ba.resize( len );
       
  2420 
       
  2421 		Q3DnsQuery * query = new Q3DnsQuery;
       
  2422 		query->started = now();
       
  2423 		query->id = ++theId;
       
  2424 		query->t = t;
       
  2425 		query->l = s;
       
  2426 		Q3DnsAnswer a( ba, query );
       
  2427 		a.parse();
       
  2428 	    } else if ( len == -1 ) {
       
  2429 		// res_search error
       
  2430 	    }
       
  2431 	}
       
  2432 	emit resultsReady();
       
  2433     }
       
  2434 }
       
  2435 #endif
       
  2436 
       
  2437 #if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 3)))
       
  2438 #define Q_MODERN_RES_API
       
  2439 #endif
       
  2440 
       
  2441 void Q3Dns::doResInit()
       
  2442 {
       
  2443     if ( theNs )
       
  2444 	return;
       
  2445     theNs = new Q3PtrList<QHostAddress>;
       
  2446     theNs->setAutoDelete( true );
       
  2447     theDomains = new Q3StrList( true );
       
  2448     theDomains->setAutoDelete( true );
       
  2449 
       
  2450     // read resolv.conf manually.
       
  2451     QFile resolvConf(QLatin1String("/etc/resolv.conf"));
       
  2452     if (resolvConf.open(QIODevice::ReadOnly)) {
       
  2453         QTextStream stream( &resolvConf );
       
  2454 	QString line;
       
  2455 
       
  2456 	while ( !stream.atEnd() ) {
       
  2457             line = stream.readLine();
       
  2458 	    QStringList list = QStringList::split( QLatin1String(" "), line );
       
  2459 	    if( line.startsWith( QLatin1Char('#') ) || list.size() < 2 )
       
  2460 	       continue;
       
  2461 	    const QString type = list[0].lower();
       
  2462 
       
  2463 	    if ( type == QLatin1String("nameserver") ) {
       
  2464 		QHostAddress *address = new QHostAddress();
       
  2465 		if ( address->setAddress( QString(list[1]) ) ) {
       
  2466 		    // only add ipv6 addresses from resolv.conf if
       
  2467 		    // this host supports ipv6.
       
  2468 		    if ( address->isIPv4Address() || ipv6support )
       
  2469 			theNs->append( address );
       
  2470                     else
       
  2471                         delete address;
       
  2472 		} else {
       
  2473 		    delete address;
       
  2474 		}
       
  2475 	    } else if ( type == QLatin1String("search") ) {
       
  2476 		QStringList srch = QStringList::split( QLatin1String(" "), list[1] );
       
  2477 		for ( QStringList::Iterator i = srch.begin(); i != srch.end(); ++i )
       
  2478 		    theDomains->append( (*i).lower().local8Bit() );
       
  2479 
       
  2480 	    } else if ( type == QLatin1String("domain") ) {
       
  2481 		theDomains->append( list[1].lower().local8Bit() );
       
  2482 	    }
       
  2483 	}
       
  2484     }
       
  2485 
       
  2486     if (theNs->isEmpty()) {
       
  2487 #if defined(Q_MODERN_RES_API)
       
  2488 	struct __res_state res;
       
  2489 	res_ninit( &res );
       
  2490 	int i;
       
  2491 	// find the name servers to use
       
  2492 	for( i=0; i < MAXNS && i < res.nscount; i++ )
       
  2493 	    theNs->append( new QHostAddress( ntohl( res.nsaddr_list[i].sin_addr.s_addr ) ) );
       
  2494 #  if defined(MAXDFLSRCH)
       
  2495 	for( i=0; i < MAXDFLSRCH; i++ ) {
       
  2496 	    if ( res.dnsrch[i] && *(res.dnsrch[i]) )
       
  2497 		theDomains->append( QString::fromLatin1( res.dnsrch[i] ).lower().local8Bit() );
       
  2498 	    else
       
  2499 		break;
       
  2500 	}
       
  2501 #  endif
       
  2502 	if ( *res.defdname )
       
  2503 	    theDomains->append( QString::fromLatin1( res.defdname ).lower().local8Bit() );
       
  2504 #else
       
  2505 	res_init();
       
  2506 	int i;
       
  2507 	// find the name servers to use
       
  2508 	for( i=0; i < MAXNS && i < _res.nscount; i++ )
       
  2509 	    theNs->append( new QHostAddress( ntohl( _res.nsaddr_list[i].sin_addr.s_addr ) ) );
       
  2510 #  if defined(MAXDFLSRCH)
       
  2511 	for( i=0; i < MAXDFLSRCH; i++ ) {
       
  2512 	    if ( _res.dnsrch[i] && *(_res.dnsrch[i]) )
       
  2513 		theDomains->append( QString::fromLatin1( _res.dnsrch[i] ).lower().local8Bit() );
       
  2514 	    else
       
  2515 		break;
       
  2516 	}
       
  2517 #  endif
       
  2518 	if ( *_res.defdname )
       
  2519 	    theDomains->append( QString::fromLatin1( _res.defdname ).lower().local8Bit() );
       
  2520 #endif
       
  2521 
       
  2522 	// the code above adds "0.0.0.0" as a name server at the slightest
       
  2523 	// hint of trouble. so remove those again.
       
  2524 	theNs->first();
       
  2525 	while( theNs->current() ) {
       
  2526 	    if ( theNs->current()->isNull() )
       
  2527 		delete theNs->take();
       
  2528 	    else
       
  2529 		theNs->next();
       
  2530 	}
       
  2531     }
       
  2532 
       
  2533     QFile hosts( QString::fromLatin1( "/etc/hosts" ) );
       
  2534     if ( hosts.open( QIODevice::ReadOnly ) ) {
       
  2535 	// read the /etc/hosts file, creating long-life A and PTR RRs
       
  2536 	// for the things we find.
       
  2537 	QTextStream i( &hosts );
       
  2538 	QString line;
       
  2539 	while( !i.atEnd() ) {
       
  2540 	    line = i.readLine().simplifyWhiteSpace().lower();
       
  2541 	    uint n = 0;
       
  2542 	    while( (int) n < line.length() && line[(int)n] != QLatin1Char('#') )
       
  2543 		n++;
       
  2544 	    line.truncate( n );
       
  2545 	    n = 0;
       
  2546 	    while( (int) n < line.length() && !line[(int)n].isSpace() )
       
  2547 		n++;
       
  2548 	    QString ip = line.left( n );
       
  2549 	    QHostAddress a;
       
  2550 	    a.setAddress( ip );
       
  2551 	    if ( ( a.isIPv4Address() || a.isIPv6Address() ) && !a.isNull() ) {
       
  2552 		bool first = true;
       
  2553 		line = line.mid( n+1 );
       
  2554 		n = 0;
       
  2555 		while( (int) n < line.length() && !line[(int)n].isSpace() )
       
  2556 		    n++;
       
  2557 		QString hostname = line.left( n );
       
  2558 		// ### in case of bad syntax, hostname is invalid. do we care?
       
  2559 		if ( n ) {
       
  2560 		    Q3DnsRR * rr = new Q3DnsRR( hostname );
       
  2561 		    if ( a.isIPv4Address() )
       
  2562 			rr->t = Q3Dns::A;
       
  2563 		    else
       
  2564 			rr->t = Q3Dns::Aaaa;
       
  2565 		    rr->address = a;
       
  2566 		    rr->deleteTime = UINT_MAX;
       
  2567 		    rr->expireTime = UINT_MAX;
       
  2568 		    rr->current = true;
       
  2569 		    if ( first ) {
       
  2570 			first = false;
       
  2571 			Q3DnsRR * ptr = new Q3DnsRR( Q3Dns::toInAddrArpaDomain( a ) );
       
  2572 			ptr->t = Q3Dns::Ptr;
       
  2573 			ptr->target = hostname;
       
  2574 			ptr->deleteTime = UINT_MAX;
       
  2575 			ptr->expireTime = UINT_MAX;
       
  2576 			ptr->current = true;
       
  2577 		    }
       
  2578 		}
       
  2579 	    }
       
  2580 	}
       
  2581     }
       
  2582 }
       
  2583 
       
  2584 #endif
       
  2585 
       
  2586 QT_END_NAMESPACE
       
  2587 
       
  2588 #endif // QT_NO_DNS