src/qt3support/network/q3dns.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 **
00003 ** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
00004 **
00005 ** This file is part of the Qt3Support module of the Qt Toolkit.
00006 **
00007 ** This file may be used under the terms of the GNU General Public
00008 ** License version 2.0 as published by the Free Software Foundation
00009 ** and appearing in the file LICENSE.GPL included in the packaging of
00010 ** this file.  Please review the following information to ensure GNU
00011 ** General Public Licensing requirements will be met:
00012 ** http://www.trolltech.com/products/qt/opensource.html
00013 **
00014 ** If you are unsure which license is appropriate for your use, please
00015 ** review the following information:
00016 ** http://www.trolltech.com/products/qt/licensing.html or contact the
00017 ** sales department at sales@trolltech.com.
00018 **
00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021 **
00022 ****************************************************************************/
00023 
00024 #include "qplatformdefs.h"
00025 #include "qbytearray.h"
00026 #ifdef Q_OS_WIN32
00027 # include "qt_windows.h"
00028 #else
00029 # include <sys/types.h>
00030 # include <netinet/in.h>
00031 # include <arpa/nameser.h>
00032 # include <resolv.h>
00033 extern "C" int res_init();
00034 #endif
00035 
00036 // POSIX Large File Support redefines open -> open64
00037 #if defined(open)
00038 # undef open
00039 #endif
00040 
00041 // POSIX Large File Support redefines truncate -> truncate64
00042 #if defined(truncate)
00043 # undef truncate
00044 #endif
00045 
00046 // Solaris redefines connect -> __xnet_connect with _XOPEN_SOURCE_EXTENDED.
00047 #if defined(connect)
00048 # undef connect
00049 #endif
00050 
00051 // UnixWare 7 redefines socket -> _socket
00052 #if defined(socket)
00053 # undef socket
00054 #endif
00055 
00056 #include "q3dns.h"
00057 
00058 #ifndef QT_NO_DNS
00059 
00060 #include "qdatetime.h"
00061 #include "q3dict.h"
00062 #include "q3ptrlist.h"
00063 #include "qstring.h"
00064 #include "qtimer.h"
00065 #include "qapplication.h"
00066 #include "q3ptrvector.h"
00067 #include "q3strlist.h"
00068 #include "q3ptrdict.h"
00069 #include "qfile.h"
00070 #include "qtextstream.h"
00071 #include "q3socketdevice.h"
00072 #include "q3cleanuphandler.h"
00073 #include <limits.h>
00074 
00075 //#define Q3DNS_DEBUG
00076 
00077 static Q_UINT16 id; // ### seeded started by now()
00078 
00079 
00080 static QDateTime * originOfTime = 0;
00081 
00082 static Q3CleanupHandler<QDateTime> q3dns_cleanup_time;
00083 
00084 static Q_UINT32 now()
00085 {
00086     if ( originOfTime )
00087   return originOfTime->secsTo( QDateTime::currentDateTime() );
00088 
00089     originOfTime = new QDateTime( QDateTime::currentDateTime() );
00090     ::id = originOfTime->time().msec() * 60 + originOfTime->time().second();
00091     q3dns_cleanup_time.add( &originOfTime );
00092     return 0;
00093 }
00094 
00095 
00096 static Q3PtrList<QHostAddress> * ns = 0;
00097 static Q3StrList * domains = 0;
00098 static bool ipv6support = false;
00099 
00100 class Q3DnsPrivate {
00101 public:
00102     Q3DnsPrivate() : queryTimer( 0 ), noNames(false)
00103     {
00104 #if defined(Q_DNS_SYNCHRONOUS)
00105 #if defined(Q_OS_UNIX)
00106   noEventLoop = qApp==0 || qApp->loopLevel()==0;
00107 #else
00108   noEventLoop = false;
00109 #endif
00110 #endif
00111     }
00112     ~Q3DnsPrivate()
00113     {
00114   delete queryTimer;
00115     }
00116 private:
00117     QTimer * queryTimer;
00118     bool noNames;
00119 #if defined(Q_DNS_SYNCHRONOUS)
00120     bool noEventLoop;
00121 #endif
00122 
00123     friend class Q3Dns;
00124     friend class Q3DnsAnswer;
00125 };
00126 
00127 
00128 class Q3DnsRR;
00129 class Q3DnsDomain;
00130 
00131 
00132 
00133 // Q3DnsRR is the class used to store a single RR.  Q3DnsRR can store
00134 // all of the supported RR types.  a Q3DnsRR is always cached.
00135 
00136 // Q3DnsRR is mostly constructed from the outside.  a but hacky, but
00137 // permissible since the entire class is internal.
00138 
00139 class Q3DnsRR {
00140 public:
00141     Q3DnsRR( const QString & label );
00142     ~Q3DnsRR();
00143 
00144 public:
00145     Q3DnsDomain * domain;
00146     Q3Dns::RecordType t;
00147     bool nxdomain;
00148     bool current;
00149     Q_UINT32 expireTime;
00150     Q_UINT32 deleteTime;
00151     // somewhat space-wasting per-type data
00152     // a / aaaa
00153     QHostAddress address;
00154     // cname / mx / srv / ptr
00155     QString target;
00156     // mx / srv
00157     Q_UINT16 priority;
00158     // srv
00159     Q_UINT16 weight;
00160     Q_UINT16 port;
00161     // txt
00162     QString text; // could be overloaded into target...
00163 private:
00164 
00165 };
00166 
00167 
00168 class Q3DnsDomain {
00169 public:
00170     Q3DnsDomain( const QString & label );
00171     ~Q3DnsDomain();
00172 
00173     static void add( const QString & label, Q3DnsRR * );
00174     static Q3PtrList<Q3DnsRR> * cached( const Q3Dns * );
00175 
00176     void take( Q3DnsRR * );
00177 
00178     void sweep( Q_UINT32 thisSweep );
00179 
00180     bool isEmpty() const { return rrs == 0 || rrs->isEmpty(); }
00181 
00182     QString name() const { return l; }
00183 
00184 public:
00185     QString l;
00186     Q3PtrList<Q3DnsRR> * rrs;
00187 };
00188 
00189 
00190 class Q3DnsQuery: public QTimer { // this inheritance is a very evil hack
00191 public:
00192     Q3DnsQuery():
00193   id( 0 ), t( Q3Dns::None ), step(0), started(0),
00194   dns( new Q3PtrDict<void>(17) ) {}
00195     ~Q3DnsQuery() { delete dns; }
00196     Q_UINT16 id;
00197     Q3Dns::RecordType t;
00198     QString l;
00199 
00200     uint step;
00201     Q_UINT32 started;
00202 
00203     Q3PtrDict<void> * dns;
00204 };
00205 
00206 
00207 
00208 class Q3DnsAnswer {
00209 public:
00210     Q3DnsAnswer( Q3DnsQuery * );
00211     Q3DnsAnswer( const QByteArray &, Q3DnsQuery * );
00212     ~Q3DnsAnswer();
00213 
00214     void parse();
00215     void notify();
00216 
00217     bool ok;
00218 
00219 private:
00220     Q3DnsQuery * query;
00221 
00222     Q_UINT8 * answer;
00223     int size;
00224     int pp;
00225 
00226     Q3PtrList<Q3DnsRR> * rrs;
00227 
00228     // convenience
00229     int next;
00230     int ttl;
00231     QString label;
00232     Q3DnsRR * rr;
00233 
00234     QString readString(bool multipleLabels = true);
00235     void parseA();
00236     void parseAaaa();
00237     void parseMx();
00238     void parseSrv();
00239     void parseCname();
00240     void parsePtr();
00241     void parseTxt();
00242     void parseNs();
00243 };
00244 
00245 
00246 Q3DnsRR::Q3DnsRR( const QString & label )
00247     : domain( 0 ), t( Q3Dns::None ),
00248       nxdomain( false ), current( false ),
00249       expireTime( 0 ), deleteTime( 0 ),
00250       priority( 0 ), weight( 0 ), port( 0 )
00251 {
00252     Q3DnsDomain::add( label, this );
00253 }
00254 
00255 
00256 // not supposed to be deleted except by Q3DnsDomain
00257 Q3DnsRR::~Q3DnsRR()
00258 {
00259     // nothing is necessary
00260 }
00261 
00262 
00263 // this one just sticks in a NXDomain
00264 Q3DnsAnswer::Q3DnsAnswer( Q3DnsQuery * query_ )
00265 {
00266     ok = true;
00267 
00268     answer = 0;
00269     size = 0;
00270     query = query_;
00271     pp = 0;
00272     rrs = new Q3PtrList<Q3DnsRR>;
00273     rrs->setAutoDelete( false );
00274     next = size;
00275     ttl = 0;
00276     label.clear();
00277     rr = 0;
00278 
00279     Q3DnsRR * newrr = new Q3DnsRR( query->l );
00280     newrr->t = query->t;
00281     newrr->deleteTime = query->started + 10;
00282     newrr->expireTime = query->started + 10;
00283     newrr->nxdomain = true;
00284     newrr->current = true;
00285     rrs->append( newrr );
00286 }
00287 
00288 
00289 Q3DnsAnswer::Q3DnsAnswer( const QByteArray& answer_,
00290       Q3DnsQuery * query_ )
00291 {
00292     ok = true;
00293 
00294     answer = (Q_UINT8 *)(answer_.data());
00295     size = (int)answer_.size();
00296     query = query_;
00297     pp = 0;
00298     rrs = new Q3PtrList<Q3DnsRR>;
00299     rrs->setAutoDelete( false );
00300     next = size;
00301     ttl = 0;
00302     label.clear();
00303     rr = 0;
00304 }
00305 
00306 
00307 Q3DnsAnswer::~Q3DnsAnswer()
00308 {
00309     if ( !ok && rrs ) {
00310   Q3PtrListIterator<Q3DnsRR> it( *rrs );
00311   Q3DnsRR * tmprr;
00312   while( (tmprr=it.current()) != 0 ) {
00313       ++it;
00314       tmprr->t = Q3Dns::None; // will be deleted soonish
00315   }
00316     }
00317     delete rrs;
00318 }
00319 
00320 
00321 QString Q3DnsAnswer::readString(bool multipleLabels)
00322 {
00323     int p = pp;
00324     QString r;
00325     Q_UINT8 b;
00326     for( ;; ) {
00327   b = 128;
00328         // Read one character
00329         if ( p >= 0 && p < size )
00330       b = answer[p];
00331 
00332   switch( b >> 6 ) {
00333   case 0:
00334             // b is less than 64
00335       p++;
00336 
00337             // Detect end of data
00338       if ( b == 0 ) {
00339     if ( p > pp )
00340         pp = p;
00341                 return r.isNull() ? QString( "." ) : r;
00342       }
00343 
00344             // Read a label of size 'b' characters
00345             if ( !r.isNull() )
00346     r += '.';
00347       while( b-- > 0 )
00348                 r += QChar( answer[p++] );
00349 
00350             // Return immediately if we were only supposed to read one
00351             // label.
00352             if (!multipleLabels)
00353                 return r;
00354 
00355       break;
00356   default:
00357             // Ignore unrecognized control character, or p was out of
00358             // range.
00359       goto not_ok;
00360   case 3:
00361             // Use the next character to determine the relative offset
00362             // to jump to before continuing the packet parsing.
00363       int q = ( (answer[p] & 0x3f) << 8 ) + answer[p+1];
00364 
00365       if ( q >= pp || q >= p )
00366     goto not_ok;
00367       if ( p >= pp )
00368     pp = p + 2;
00369       p = q;
00370         }
00371     }
00372 not_ok:
00373     ok = false;
00374     return QString();
00375 }
00376 
00377 
00378 
00379 void Q3DnsAnswer::parseA()
00380 {
00381     if ( next != pp + 4 ) {
00382 #if defined(Q3DNS_DEBUG)
00383   qDebug( "Q3Dns: saw %d bytes long IN A for %s",
00384     next - pp, label.ascii() );
00385 #endif
00386   return;
00387     }
00388 
00389     rr = new Q3DnsRR( label );
00390     rr->t = Q3Dns::A;
00391     rr->address = QHostAddress( ( answer[pp+0] << 24 ) +
00392         ( answer[pp+1] << 16 ) +
00393         ( answer[pp+2] <<  8 ) +
00394         ( answer[pp+3] ) );
00395 #if defined(Q3DNS_DEBUG)
00396     qDebug( "Q3Dns: saw %s IN A %s (ttl %d)", label.ascii(),
00397       rr->address.toString().ascii(), ttl );
00398 #endif
00399 }
00400 
00401 
00402 void Q3DnsAnswer::parseAaaa()
00403 {
00404     if ( next != pp + 16 ) {
00405 #if defined(Q3DNS_DEBUG)
00406   qDebug( "Q3Dns: saw %d bytes long IN Aaaa for %s",
00407     next - pp, label.ascii() );
00408 #endif
00409   return;
00410     }
00411 
00412     rr = new Q3DnsRR( label );
00413     rr->t = Q3Dns::Aaaa;
00414     rr->address = QHostAddress( answer+pp );
00415 #if defined(Q3DNS_DEBUG)
00416     qDebug( "Q3Dns: saw %s IN Aaaa %s (ttl %d)", label.ascii(),
00417       rr->address.toString().ascii(), ttl );
00418 #endif
00419 }
00420 
00421 
00422 
00423 void Q3DnsAnswer::parseMx()
00424 {
00425     if ( next < pp + 2 ) {
00426 #if defined(Q3DNS_DEBUG)
00427   qDebug( "Q3Dns: saw %d bytes long IN MX for %s",
00428     next - pp, label.ascii() );
00429 #endif
00430   return;
00431     }
00432 
00433     rr = new Q3DnsRR( label );
00434     rr->priority = (answer[pp] << 8) + answer[pp+1];
00435     pp += 2;
00436     rr->target = readString().lower();
00437     if ( !ok ) {
00438 #if defined(Q3DNS_DEBUG)
00439   qDebug( "Q3Dns: saw bad string in MX for %s", label.ascii() );
00440 #endif
00441   return;
00442     }
00443     rr->t = Q3Dns::Mx;
00444 #if defined(Q3DNS_DEBUG)
00445     qDebug( "Q3Dns: saw %s IN MX %d %s (ttl %d)", label.ascii(),
00446       rr->priority, rr->target.ascii(), ttl );
00447 #endif
00448 }
00449 
00450 
00451 void Q3DnsAnswer::parseSrv()
00452 {
00453     if ( next < pp + 6 ) {
00454 #if defined(Q3DNS_DEBUG)
00455   qDebug( "Q3Dns: saw %d bytes long IN SRV for %s",
00456     next - pp, label.ascii() );
00457 #endif
00458   return;
00459     }
00460 
00461     rr = new Q3DnsRR( label );
00462     rr->priority = (answer[pp] << 8) + answer[pp+1];
00463     rr->weight = (answer[pp+2] << 8) + answer[pp+3];
00464     rr->port = (answer[pp+4] << 8) + answer[pp+5];
00465     pp += 6;
00466     rr->target = readString().lower();
00467     if ( !ok ) {
00468 #if defined(Q3DNS_DEBUG)
00469   qDebug( "Q3Dns: saw bad string in SRV for %s", label.ascii() );
00470 #endif
00471   return;
00472     }
00473     rr->t = Q3Dns::Srv;
00474 #if defined(Q3DNS_DEBUG)
00475     qDebug( "Q3Dns: saw %s IN SRV %d %d %d %s (ttl %d)", label.ascii(),
00476       rr->priority, rr->weight, rr->port, rr->target.ascii(), ttl );
00477 #endif
00478 }
00479 
00480 
00481 void Q3DnsAnswer::parseCname()
00482 {
00483     QString target = readString().lower();
00484     if ( !ok ) {
00485 #if defined(Q3DNS_DEBUG)
00486   qDebug( "Q3Dns: saw bad cname for for %s", label.ascii() );
00487 #endif
00488   return;
00489     }
00490 
00491     rr = new Q3DnsRR( label );
00492     rr->t = Q3Dns::Cname;
00493     rr->target = target;
00494 #if defined(Q3DNS_DEBUG)
00495     qDebug( "Q3Dns: saw %s IN CNAME %s (ttl %d)", label.ascii(),
00496       rr->target.ascii(), ttl );
00497 #endif
00498 }
00499 
00500 
00501 void Q3DnsAnswer::parseNs()
00502 {
00503     QString target = readString().lower();
00504     if ( !ok ) {
00505 #if defined(Q3DNS_DEBUG)
00506   qDebug( "Q3Dns: saw bad cname for for %s", label.ascii() );
00507 #endif
00508   return;
00509     }
00510 
00511     // parse, but ignore
00512 
00513 #if defined(Q3DNS_DEBUG)
00514     qDebug( "Q3Dns: saw %s IN NS %s (ttl %d)", label.ascii(),
00515       target.ascii(), ttl );
00516 #endif
00517 }
00518 
00519 
00520 void Q3DnsAnswer::parsePtr()
00521 {
00522     QString target = readString().lower();
00523     if ( !ok ) {
00524 #if defined(Q3DNS_DEBUG)
00525   qDebug( "Q3Dns: saw bad PTR for for %s", label.ascii() );
00526 #endif
00527   return;
00528     }
00529 
00530     rr = new Q3DnsRR( label );
00531     rr->t = Q3Dns::Ptr;
00532     rr->target = target;
00533 #if defined(Q3DNS_DEBUG)
00534     qDebug( "Q3Dns: saw %s IN PTR %s (ttl %d)", label.ascii(),
00535       rr->target.ascii(), ttl );
00536 #endif
00537 }
00538 
00539 
00540 void Q3DnsAnswer::parseTxt()
00541 {
00542     QString text = readString(false);
00543     if ( !ok ) {
00544 #if defined(Q3DNS_DEBUG)
00545   qDebug( "Q3Dns: saw bad TXT for for %s", label.ascii() );
00546 #endif
00547   return;
00548     }
00549 
00550     rr = new Q3DnsRR( label );
00551     rr->t = Q3Dns::Txt;
00552     rr->text = text;
00553 #if defined(Q3DNS_DEBUG)
00554     qDebug( "Q3Dns: saw %s IN TXT \"%s\" (ttl %d)", label.ascii(),
00555       rr->text.ascii(), ttl );
00556 #endif
00557 }
00558 
00559 
00560 void Q3DnsAnswer::parse()
00561 {
00562     // okay, do the work...
00563     if ( (answer[2] & 0x78) != 0 ) {
00564 #if defined(Q3DNS_DEBUG)
00565   qDebug( "DNS Manager: answer to wrong query type (%d)", answer[1] );
00566 #endif
00567   ok = false;
00568   return;
00569     }
00570 
00571     // AA
00572     bool aa = (answer[2] & 4) != 0;
00573 
00574     // TC
00575     if ( (answer[2] & 2) != 0 ) {
00576 #if defined(Q3DNS_DEBUG)
00577   qDebug( "DNS Manager: truncated answer; pressing on" );
00578 #endif
00579     }
00580 
00581     // RD
00582     bool rd = (answer[2] & 1) != 0;
00583 
00584     // we don't test RA
00585     // we don't test the MBZ fields
00586 
00587     if ( (answer[3] & 0x0f) == 3 ) {
00588 #if defined(Q3DNS_DEBUG)
00589   qDebug( "DNS Manager: saw NXDomain for %s", query->l.ascii() );
00590 #endif
00591   // NXDomain.  cache that for one minute.
00592   rr = new Q3DnsRR( query->l );
00593   rr->t = query->t;
00594   rr->deleteTime = query->started + 60;
00595   rr->expireTime = query->started + 60;
00596   rr->nxdomain = true;
00597   rr->current = true;
00598   rrs->append( rr );
00599   return;
00600     }
00601 
00602     if ( (answer[3] & 0x0f) != 0 ) {
00603 #if defined(Q3DNS_DEBUG)
00604   qDebug( "DNS Manager: error code %d", answer[3] & 0x0f );
00605 #endif
00606   ok = false;
00607   return;
00608     }
00609 
00610     int qdcount = ( answer[4] << 8 ) + answer[5];
00611     int ancount = ( answer[6] << 8 ) + answer[7];
00612     int nscount = ( answer[8] << 8 ) + answer[9];
00613     int adcount = (answer[10] << 8 ) +answer[11];
00614 
00615     pp = 12;
00616 
00617     // read query
00618     while( qdcount > 0 && pp < size ) {
00619   // should I compare the string against query->l?
00620   (void)readString();
00621   if ( !ok )
00622       return;
00623   pp += 4;
00624   qdcount--;
00625     }
00626 
00627     // answers and stuff
00628     int rrno = 0;
00629     // if we parse the answer completely, but there are no answers,
00630     // ignore the entire thing.
00631     int answers = 0;
00632     while( ( rrno < ancount ||
00633        ( ok && answers >0 && rrno < ancount + nscount + adcount ) ) &&
00634      pp < size ) {
00635   label = readString().lower();
00636   if ( !ok )
00637       return;
00638   int rdlength = 0;
00639   if ( pp + 10 <= size )
00640       rdlength = ( answer[pp+8] << 8 ) + answer[pp+9];
00641   if ( pp + 10 + rdlength > size ) {
00642 #if defined(Q3DNS_DEBUG)
00643       qDebug( "DNS Manager: ran out of stuff to parse (%d+%d>%d (%d)",
00644         pp, rdlength, size, rrno < ancount );
00645 #endif
00646       // if we're still in the AN section, we should go back and
00647       // at least down the TTLs.  probably best to invalidate
00648       // the results.
00649       // the rrs list is good for this
00650       ok = ( rrno < ancount );
00651       return;
00652   }
00653   uint type, clas;
00654   type = ( answer[pp+0] << 8 ) + answer[pp+1];
00655   clas = ( answer[pp+2] << 8 ) + answer[pp+3];
00656   ttl = ( answer[pp+4] << 24 ) + ( answer[pp+5] << 16 ) +
00657         ( answer[pp+6] <<  8 ) + answer[pp+7];
00658   pp = pp + 10;
00659   if ( clas != 1 ) {
00660 #if defined(Q3DNS_DEBUG)
00661       qDebug( "DNS Manager: class %d (not internet) for %s",
00662         clas, label.isNull() ? "." : label.ascii() );
00663 #endif
00664   } else {
00665             next = pp + rdlength;
00666       rr = 0;
00667       switch( type ) {
00668       case 1:
00669     parseA();
00670     break;
00671       case 28:
00672     parseAaaa();
00673     break;
00674       case 15:
00675     parseMx();
00676     break;
00677       case 33:
00678     parseSrv();
00679     break;
00680       case 5:
00681     parseCname();
00682     break;
00683       case 12:
00684     parsePtr();
00685     break;
00686       case 16:
00687     parseTxt();
00688     break;
00689       case 2:
00690     parseNs();
00691     break;
00692       default:
00693     // something we don't know
00694 #if defined(Q3DNS_DEBUG)
00695     qDebug( "DNS Manager: type %d for %s", type,
00696       label.isNull() ? "." : label.ascii() );
00697 #endif
00698     break;
00699       }
00700       if ( rr ) {
00701     rr->deleteTime = 0;
00702     if ( ttl > 0 )
00703         rr->expireTime = query->started + ttl;
00704     else
00705         rr->expireTime = query->started + 20;
00706     if ( rrno < ancount ) {
00707         answers++;
00708         rr->deleteTime = rr->expireTime;
00709     }
00710     rr->current = true;
00711     rrs->append( rr );
00712       }
00713         }
00714   if ( !ok )
00715       return;
00716   pp = next;
00717   next = size;
00718   rrno++;
00719     }
00720     if ( answers == 0 ) {
00721 #if defined(Q3DNS_DEBUG)
00722   qDebug( "DNS Manager: answer contained no answers" );
00723 #endif
00724   ok = ( aa && rd );
00725     }
00726 
00727     // now go through the list and mark all the As that are referenced
00728     // by something we care about.  we want to cache such As.
00729     rrs->first();
00730     Q3Dict<void> used( 17 );
00731     used.setAutoDelete( false );
00732     while( (rr=rrs->current()) != 0 ) {
00733   rrs->next();
00734   if ( rr->target.length() && rr->deleteTime > 0 && rr->current )
00735       used.insert( rr->target, (void*)42 );
00736   if ( ( rr->t == Q3Dns::A || rr->t == Q3Dns::Aaaa ) &&
00737        used.find( rr->domain->name() ) != 0 )
00738       rr->deleteTime = rr->expireTime;
00739     }
00740 
00741     // next, for each RR, delete any older RRs that are equal to it
00742     rrs->first();
00743     while( (rr=rrs->current()) != 0 ) {
00744   rrs->next();
00745   if ( rr && rr->domain && rr->domain->rrs ) {
00746       Q3PtrList<Q3DnsRR> * drrs = rr->domain->rrs;
00747       drrs->first();
00748       Q3DnsRR * older;
00749       while( (older=drrs->current()) != 0 ) {
00750     if ( older != rr &&
00751          older->t == rr->t &&
00752          older->nxdomain == rr->nxdomain &&
00753          older->address == rr->address &&
00754          older->target == rr->target &&
00755          older->priority == rr->priority &&
00756          older->weight == rr->weight &&
00757          older->port == rr->port &&
00758          older->text == rr->text ) {
00759         // well, it's equal, but it's not the same. so we kill it,
00760         // but use its expiry time.
00761 #if defined(Q3DNS_DEBUG)
00762         qDebug( "killing off old %d for %s, expire was %d",
00763                             older->t, older->domain->name().latin1(),
00764                             rr->expireTime );
00765 #endif
00766         older->t = Q3Dns::None;
00767         rr->expireTime = QMAX( older->expireTime, rr->expireTime );
00768         rr->deleteTime = QMAX( older->deleteTime, rr->deleteTime );
00769         older->deleteTime = 0;
00770 #if defined(Q3DNS_DEBUG)
00771         qDebug( "    adjusted expire is %d", rr->expireTime );
00772 #endif
00773     }
00774     drrs->next();
00775       }
00776   }
00777     }
00778 
00779 #if defined(Q3DNS_DEBUG)
00780     //qDebug( "DNS Manager: ()" );
00781 #endif
00782 }
00783 
00784 
00785 class Q3DnsUgleHack: public Q3Dns {
00786 public:
00787     void ugle( bool emitAnyway=false );
00788 };
00789 
00790 
00791 void Q3DnsAnswer::notify()
00792 {
00793     if ( !rrs || !ok || !query || !query->dns )
00794   return;
00795 
00796     Q3PtrDict<void> notified;
00797     notified.setAutoDelete( false );
00798 
00799     Q3PtrDictIterator<void> it( *query->dns );
00800     Q3Dns * dns;
00801     it.toFirst();
00802     while( (dns=(Q3Dns*)(it.current())) != 0 ) {
00803   ++it;
00804   if ( notified.find( (void*)dns ) == 0 ) {
00805       notified.insert( (void*)dns, (void*)42 );
00806       if ( rrs->count() == 0 ) {
00807 #if defined(Q3DNS_DEBUG)
00808     qDebug( "DNS Manager: found no answers!" );
00809 #endif
00810     dns->d->noNames = true;
00811     ((Q3DnsUgleHack*)dns)->ugle( true );
00812       } else {
00813     QStringList n = dns->qualifiedNames();
00814     if ( n.contains(query->l) )
00815         ((Q3DnsUgleHack*)dns)->ugle();
00816 #if defined(Q3DNS_DEBUG)
00817     else
00818         qDebug( "DNS Manager: DNS thing %s not notified for %s",
00819           dns->label().ascii(), query->l.ascii() );
00820 #endif
00821       }
00822   }
00823     }
00824 }
00825 
00826 
00827 //
00828 //
00829 // Q3DnsManager
00830 //
00831 //
00832 
00833 
00834 class Q3DnsManager: public Q3DnsSocket {
00835 private:
00836 public: // just to silence the moronic g++.
00837     Q3DnsManager();
00838     ~Q3DnsManager();
00839 public:
00840     static Q3DnsManager * manager();
00841 
00842     Q3DnsDomain * domain( const QString & );
00843 
00844     void transmitQuery( Q3DnsQuery * );
00845     void transmitQuery( int );
00846 
00847     // reimplementation of the slots
00848     void cleanCache();
00849     void retransmit();
00850     void answer();
00851 
00852 public:
00853     Q3PtrVector<Q3DnsQuery> queries;
00854     Q3Dict<Q3DnsDomain> cache;
00855     Q3SocketDevice * ipv4Socket;
00856 #if !defined (QT_NO_IPV6)
00857     Q3SocketDevice * ipv6Socket;
00858 #endif
00859 };
00860 
00861 
00862 
00863 static Q3DnsManager * globalManager = 0;
00864 
00865 static void cleanupDns()
00866 {
00867     delete globalManager;
00868     globalManager = 0;
00869 }
00870 
00871 Q3DnsManager * Q3DnsManager::manager()
00872 {
00873     if ( !globalManager ) {
00874         qAddPostRoutine(cleanupDns);
00875   new Q3DnsManager();
00876     }
00877     return globalManager;
00878 }
00879 
00880 
00881 void Q3DnsUgleHack::ugle( bool emitAnyway)
00882 {
00883     if ( emitAnyway || !isWorking() ) {
00884 #if defined(Q3DNS_DEBUG)
00885   qDebug( "DNS Manager: status change for %s (type %d)",
00886     label().ascii(), recordType() );
00887 #endif
00888   emit resultsReady();
00889     }
00890 }
00891 
00892 
00893 Q3DnsManager::Q3DnsManager()
00894     : Q3DnsSocket( qApp, "Internal DNS manager" ),
00895       queries( Q3PtrVector<Q3DnsQuery>( 0 ) ),
00896       cache( Q3Dict<Q3DnsDomain>( 83, false ) ),
00897       ipv4Socket( new Q3SocketDevice( Q3SocketDevice::Datagram, Q3SocketDevice::IPv4, 0 ) )
00898 #if !defined (QT_NO_IPV6)
00899       , ipv6Socket( new Q3SocketDevice( Q3SocketDevice::Datagram, Q3SocketDevice::IPv6, 0 ) )
00900 #endif
00901 {
00902     cache.setAutoDelete( true );
00903     globalManager = this;
00904 
00905     QTimer * sweepTimer = new QTimer( this );
00906     sweepTimer->start( 1000 * 60 * 3 );
00907     connect( sweepTimer, SIGNAL(timeout()),
00908        this, SLOT(cleanCache()) );
00909 
00910     QSocketNotifier * rn4 = new QSocketNotifier( ipv4Socket->socket(),
00911              QSocketNotifier::Read,
00912              this, "dns IPv4 socket watcher" );
00913     ipv4Socket->setAddressReusable( false );
00914     ipv4Socket->setBlocking( false );
00915     connect( rn4, SIGNAL(activated(int)), SLOT(answer()) );
00916 
00917 #if !defined (QT_NO_IPV6)
00918     // Don't connect the IPv6 socket notifier if the host does not
00919     // support IPv6.
00920     if ( ipv6Socket->socket() != -1 ) {
00921   QSocketNotifier * rn6 = new QSocketNotifier( ipv6Socket->socket(),
00922                  QSocketNotifier::Read,
00923                  this, "dns IPv6 socket watcher" );
00924 
00925   ipv6support = true;
00926   ipv6Socket->setAddressReusable( false );
00927   ipv6Socket->setBlocking( false );
00928   connect( rn6, SIGNAL(activated(int)), SLOT(answer()) );
00929     }
00930 #endif
00931 
00932     if ( !ns )
00933   Q3Dns::doResInit();
00934 
00935     // O(n*n) stuff here.  but for 3 and 6, O(n*n) with a low k should
00936     // be perfect.  the point is to eliminate any duplicates that
00937     // might be hidden in the lists.
00938     Q3PtrList<QHostAddress> * ns = new Q3PtrList<QHostAddress>;
00939 
00940     ::ns->first();
00941     QHostAddress * h;
00942     while( (h=::ns->current()) != 0 ) {
00943   ns->first();
00944   while( ns->current() != 0 && !(*ns->current() == *h) )
00945       ns->next();
00946   if ( !ns->current() ) {
00947       ns->append( new QHostAddress(*h) );
00948 #if defined(Q3DNS_DEBUG)
00949       qDebug( "using name server %s", h->toString().latin1() );
00950   } else {
00951       qDebug( "skipping address %s", h->toString().latin1() );
00952 #endif
00953   }
00954 	::ns->next();
00955     }
00956 
00957     delete ::ns;
00958     ::ns = ns;
00959     ::ns->setAutoDelete( true );
00960 
00961     Q3StrList * domains = new Q3StrList( true );
00962 
00963     ::domains->first();
00964     const char * s;
00965     while( (s=::domains->current()) != 0 ) {
00966   domains->first();
00967   while( domains->current() != 0 && qstrcmp( domains->current(), s ) )
00968       domains->next();
00969   if ( !domains->current() ) {
00970       domains->append( s );
00971 #if defined(Q3DNS_DEBUG)
00972       qDebug( "searching domain %s", s );
00973   } else {
00974       qDebug( "skipping domain %s", s );
00975 #endif
00976   }
00977 	::domains->next();
00978     }
00979 
00980     delete ::domains;
00981     ::domains = domains;
00982     ::domains->setAutoDelete( true );
00983 }
00984 
00985 
00986 Q3DnsManager::~Q3DnsManager()
00987 {
00988     if ( globalManager )
00989   globalManager = 0;
00990     queries.setAutoDelete( true );
00991     cache.setAutoDelete( true );
00992     delete ipv4Socket;
00993 #if !defined (QT_NO_IPV6)
00994     delete ipv6Socket;
00995 #endif
00996 }
00997 
00998 static Q_UINT32 lastSweep = 0;
00999 
01000 void Q3DnsManager::cleanCache()
01001 {
01002     bool again = false;
01003     Q3DictIterator<Q3DnsDomain> it( cache );
01004     Q3DnsDomain * d;
01005     Q_UINT32 thisSweep = now();
01006 #if defined(Q3DNS_DEBUG)
01007     qDebug( "Q3DnsManager::cleanCache(: Called, time is %u, last was %u",
01008      thisSweep, lastSweep );
01009 #endif
01010 
01011     while( (d=it.current()) != 0 ) {
01012   ++it;
01013   d->sweep( thisSweep ); // after this, d may be empty
01014   if ( !again )
01015       again = !d->isEmpty();
01016     }
01017     if ( !again )
01018   delete this;
01019     lastSweep = thisSweep;
01020 }
01021 
01022 
01023 void Q3DnsManager::retransmit()
01024 {
01025     const QObject * o = sender();
01026     if ( o == 0 || globalManager == 0 || this != globalManager )
01027   return;
01028     uint q = 0;
01029     while( q < queries.size() && queries[q] != o )
01030   q++;
01031     if ( q < queries.size() )
01032   transmitQuery( q );
01033 }
01034 
01035 
01036 void Q3DnsManager::answer()
01037 {
01038     QByteArray a( 16383 ); // large enough for anything, one suspects
01039 
01040     int r;
01041 #if defined (QT_NO_IPV6)
01042     r = ipv4Socket->readBlock(a.data(), a.size());
01043 #else
01044     if (((QSocketNotifier *)sender())->socket() == ipv4Socket->socket())
01045         r = ipv4Socket->readBlock(a.data(), a.size());
01046     else
01047         r = ipv6Socket->readBlock(a.data(), a.size());
01048 #endif
01049 #if defined(Q3DNS_DEBUG)
01050 #if !defined (QT_NO_IPV6)
01051     qDebug("DNS Manager: answer arrived: %d bytes from %s:%d", r,
01052      useIpv4Socket ? ipv4Socket->peerAddress().toString().ascii()
01053      : ipv6Socket->peerAddress().toString().ascii(),
01054      useIpv4Socket ? ipv4Socket->peerPort() : ipv6Socket->peerPort() );
01055 #else
01056     qDebug("DNS Manager: answer arrived: %d bytes from %s:%d", r,
01057            ipv4Socket->peerAddress().toString().ascii(), ipv4Socket->peerPort());;
01058 #endif
01059 #endif
01060     if ( r < 12 )
01061   return;
01062     // maybe we should check that the answer comes from port 53 on one
01063     // of our name servers...
01064     a.resize( r );
01065 
01066     Q_UINT16 aid = (((Q_UINT8)a[0]) << 8) + ((Q_UINT8)a[1]);
01067     uint i = 0;
01068     while( i < queries.size() &&
01069      !( queries[i] && queries[i]->id == aid ) )
01070   i++;
01071     if ( i == queries.size() ) {
01072 #if defined(Q3DNS_DEBUG)
01073   qDebug( "DNS Manager: bad id (0x%04x) %d", aid, i );
01074 #endif
01075   return;
01076     }
01077 
01078     // at this point queries[i] is whatever we asked for.
01079 
01080     if ( ( (Q_UINT8)(a[2]) & 0x80 ) == 0 ) {
01081 #if defined(Q3DNS_DEBUG)
01082   qDebug( "DNS Manager: received a query" );
01083 #endif
01084   return;
01085     }
01086 
01087     Q3DnsQuery * q = queries[i];
01088     Q3DnsAnswer answer( a, q );
01089     answer.parse();
01090     if ( answer.ok ) {
01091   queries.take( i );
01092   answer.notify();
01093   delete q;
01094     }
01095 }
01096 
01097 
01098 void Q3DnsManager::transmitQuery( Q3DnsQuery * query_ )
01099 {
01100     if ( !query_ )
01101   return;
01102 
01103     uint i = 0;
01104     while( i < queries.size() && queries[i] != 0 )
01105   i++;
01106     if ( i == queries.size() )
01107   queries.resize( i+1 );
01108     queries.insert( i, query_ );
01109     transmitQuery( i );
01110 }
01111 
01112 
01113 void Q3DnsManager::transmitQuery( int i )
01114 {
01115     if ( i < 0 || i >= (int)queries.size() )
01116   return;
01117     Q3DnsQuery * q = queries[i];
01118 
01119     if ( q && q->step > 8 ) {
01120   // okay, we've run out of retransmissions. we fake an NXDomain
01121   // with a very short life time...
01122   Q3DnsAnswer answer( q );
01123   answer.notify();
01124   // and then get rid of the query
01125   queries.take( i );
01126 #if defined(Q3DNS_DEBUG)
01127   qDebug( "DNS Manager: giving up on query 0x%04x", q->id );
01128 #endif
01129   delete q;
01130   QTimer::singleShot( 0, Q3DnsManager::manager(), SLOT(cleanCache()) );
01131   // and don't process anything more
01132   return;
01133     }
01134 
01135     if ( q && !q->dns || q->dns->isEmpty() )
01136   // no one currently wants the answer, so there's no point in
01137   // retransmitting the query. we keep it, though. an answer may
01138   // arrive for an earlier query transmission, and if it does we
01139   // may benefit from caching the result.
01140   return;
01141 
01142     QByteArray p( 12 + q->l.length() + 2 + 4 );
01143     if ( p.size() > 500 )
01144   return; // way over the limit, so don't even try
01145 
01146     // header
01147     // id
01148     p[0] = (q->id & 0xff00) >> 8;
01149     p[1] =  q->id & 0x00ff;
01150     p[2] = 1; // recursion desired, rest is 0
01151     p[3] = 0; // all is 0
01152     // one query
01153     p[4] = 0;
01154     p[5] = 1;
01155     // no answers, name servers or additional data
01156     p[6] = p[7] = p[8] = p[9] = p[10] = p[11] = 0;
01157 
01158     // the name is composed of several components.  each needs to be
01159     // written by itself... so we write...
01160     // oh, and we assume that there's no funky characters in there.
01161     int pp = 12;
01162     uint lp = 0;
01163     while( lp < (uint) q->l.length() ) {
01164   int le = q->l.find( '.', lp );
01165   if ( le < 0 )
01166       le = q->l.length();
01167   QString component = q->l.mid( lp, le-lp );
01168   p[pp++] = component.length();
01169   int cp;
01170   for( cp=0; cp < (int)component.length(); cp++ )
01171       p[pp++] = component[cp].latin1();
01172   lp = le + 1;
01173     }
01174     // final null
01175     p[pp++] = 0;
01176     // query type
01177     p[pp++] = 0;
01178     switch( q->t ) {
01179     case Q3Dns::A:
01180   p[pp++] = 1;
01181   break;
01182     case Q3Dns::Aaaa:
01183   p[pp++] = 28;
01184   break;
01185     case Q3Dns::Mx:
01186   p[pp++] = 15;
01187   break;
01188     case Q3Dns::Srv:
01189   p[pp++] = 33;
01190   break;
01191     case Q3Dns::Cname:
01192   p[pp++] = 5;
01193   break;
01194     case Q3Dns::Ptr:
01195   p[pp++] = 12;
01196   break;
01197     case Q3Dns::Txt:
01198   p[pp++] = 16;
01199   break;
01200     default:
01201   p[pp++] = (char)255; // any
01202   break;
01203     }
01204     // query class (always internet)
01205     p[pp++] = 0;
01206     p[pp++] = 1;
01207 
01208     // if we have no name servers, we should regenerate ns in case
01209     // name servers have recently been defined (like on windows,
01210     // plugging/unplugging the network cable will change the name
01211     // server entries)
01212     if ( !ns || ns->isEmpty() )
01213         Q3Dns::doResInit();
01214 
01215     if ( !ns || ns->isEmpty() ) {
01216   // we don't find any name servers. We fake an NXDomain
01217   // with a very short life time...
01218   Q3DnsAnswer answer( q );
01219   answer.notify();
01220   // and then get rid of the query
01221   queries.take( i );
01222 #if defined(Q3DNS_DEBUG)
01223   qDebug( "DNS Manager: no DNS server found on query 0x%04x", q->id );
01224 #endif
01225   delete q;
01226   QTimer::singleShot( 1000*10, Q3DnsManager::manager(), SLOT(cleanCache()) );
01227   // and don't process anything more
01228   return;
01229     }
01230 
01231     QHostAddress receiver = *ns->at( q->step % ns->count() );
01232     if (receiver.isIPv4Address())
01233   ipv4Socket->writeBlock( p.data(), pp, receiver, 53 );
01234 #if !defined (QT_NO_IPV6)
01235     else
01236   ipv6Socket->writeBlock( p.data(), pp, receiver, 53 );
01237 #endif
01238 #if defined(Q3DNS_DEBUG)
01239     qDebug( "issuing query 0x%04x (%d) about %s type %d to %s",
01240       q->id, q->step, q->l.ascii(), q->t,
01241       ns->at( q->step % ns->count() )->toString().ascii() );
01242 #endif
01243     if ( ns->count() > 1 && q->step == 0 && queries.count() == 1 ) {
01244   // if it's the first time, and we don't have any other
01245   // outstanding queries, send nonrecursive queries to the other
01246   // name servers too.
01247   p[2] = 0;
01248   QHostAddress * server;
01249   while( (server=ns->next()) != 0 ) {
01250       if (server->isIPv4Address())
01251     ipv4Socket->writeBlock( p.data(), pp, *server, 53 );
01252 #if !defined (QT_NO_IPV6)
01253       else
01254     ipv6Socket->writeBlock( p.data(), pp, *server, 53 );
01255 #endif
01256 #if defined(Q3DNS_DEBUG)
01257       qDebug( "copying query to %s", server->toString().ascii() );
01258 #endif
01259   }
01260     }
01261     q->step++;
01262     // some testing indicates that normal dns queries take up to 0.6
01263     // seconds.  the graph becomes steep around that point, and the
01264     // number of errors rises... so it seems good to retry at that
01265     // point.
01266     q->start( q->step < ns->count() ? 800 : 1500, true );
01267 }
01268 
01269 
01270 Q3DnsDomain * Q3DnsManager::domain( const QString & label )
01271 {
01272     Q3DnsDomain * d = cache.find( label );
01273     if ( !d ) {
01274   d = new Q3DnsDomain( label );
01275   cache.insert( label, d );
01276     }
01277     return d;
01278 }
01279 
01280 
01281 //
01282 //
01283 // the Q3DnsDomain class looks after and coordinates queries for Q3DnsRRs for
01284 // each domain, and the cached Q3DnsRRs.  (A domain, in DNS terminology, is
01285 // a node in the DNS.  "no", "trolltech.com" and "lupinella.troll.no" are
01286 // all domains.)
01287 //
01288 //
01289 
01290 
01291 Q3DnsDomain::Q3DnsDomain( const QString & label )
01292 {
01293     l = label;
01294     rrs = 0;
01295 }
01296 
01297 
01298 Q3DnsDomain::~Q3DnsDomain()
01299 {
01300     delete rrs;
01301     rrs = 0;
01302 }
01303 
01304 
01305 void Q3DnsDomain::add( const QString & label, Q3DnsRR * rr )
01306 {
01307     Q3DnsDomain * d = Q3DnsManager::manager()->domain( label );
01308     if ( !d->rrs ) {
01309   d->rrs = new Q3PtrList<Q3DnsRR>;
01310   d->rrs->setAutoDelete( true );
01311     }
01312     d->rrs->append( rr );
01313     rr->domain = d;
01314 }
01315 
01316 
01317 Q3PtrList<Q3DnsRR> * Q3DnsDomain::cached( const Q3Dns * r )
01318 {
01319     Q3PtrList<Q3DnsRR> * l = new Q3PtrList<Q3DnsRR>;
01320 
01321     // test at first if you have to start a query at all
01322     if ( r->recordType() == Q3Dns::A ) {
01323   if ( r->label().lower() == "localhost" ) {
01324       // undocumented hack. ipv4-specific. also, may be a memory
01325       // leak? not sure. would be better to do this in doResInit(),
01326       // anyway.
01327       Q3DnsRR *rrTmp = new Q3DnsRR( r->label() );
01328       rrTmp->t = Q3Dns::A;
01329       rrTmp->address = QHostAddress( 0x7f000001 );
01330       rrTmp->current = true;
01331       l->append( rrTmp );
01332       return l;
01333   }
01334   QHostAddress tmp;
01335   if ( tmp.setAddress( r->label() ) ) {
01336       Q3DnsRR *rrTmp = new Q3DnsRR( r->label() );
01337       if ( tmp.isIPv4Address() ) {
01338     rrTmp->t = Q3Dns::A;
01339                 rrTmp->address = tmp;
01340                 rrTmp->current = true;
01341                 l->append( rrTmp );
01342             } else {
01343                 rrTmp->nxdomain = true;
01344             }
01345       return l;
01346   }
01347     }
01348     if ( r->recordType() == Q3Dns::Aaaa ) {
01349   QHostAddress tmp;
01350   if ( tmp.setAddress(r->label()) ) {
01351       Q3DnsRR *rrTmp = new Q3DnsRR( r->label() );
01352       if ( tmp.isIPv6Address() ) {
01353     rrTmp->t = Q3Dns::Aaaa;
01354                 rrTmp->address = tmp;
01355                 rrTmp->current = true;
01356                 l->append( rrTmp );
01357             } else {
01358                 rrTmp->nxdomain = true;
01359             }
01360       return l;
01361   }
01362     }
01363 
01364     // if you reach this point, you have to do the query
01365     Q3DnsManager * m = Q3DnsManager::manager();
01366     QStringList n = r->qualifiedNames();
01367     bool nxdomain;
01368     int cnamecount = 0;
01369     int it = 0;
01370     while( it < n.count() ) {
01371   QString s = n.at(it++);
01372   nxdomain = false;
01373 #if defined(Q3DNS_DEBUG)
01374   qDebug( "looking at cache for %s (%s %d)",
01375     s.ascii(), r->label().ascii(), r->recordType() );
01376 #endif
01377   Q3DnsDomain * d = m->domain( s );
01378 #if defined(Q3DNS_DEBUG)
01379   qDebug( " - found %d RRs", d && d->rrs ? d->rrs->count() : 0 );
01380 #endif
01381   if ( d->rrs )
01382       d->rrs->first();
01383   Q3DnsRR * rr;
01384   bool answer = false;
01385   while( d->rrs && (rr=d->rrs->current()) != 0 ) {
01386       if ( rr->t == Q3Dns::Cname && r->recordType() != Q3Dns::Cname &&
01387      !rr->nxdomain && cnamecount < 16 ) {
01388     // cname.  if the code is ugly, that may just
01389     // possibly be because the concept is.
01390 #if defined(Q3DNS_DEBUG)
01391     qDebug( "found cname from %s to %s",
01392       r->label().ascii(), rr->target.ascii() );
01393 #endif
01394     s = rr->target;
01395     d = m->domain( s );
01396     if ( d->rrs )
01397         d->rrs->first();
01398     it = n.count();
01399     // we've elegantly moved over to whatever the cname
01400     // pointed to.  well, not elegantly.  let's remember
01401     // that we've done something, anyway, so we can't be
01402     // fooled into an infinte loop as well.
01403     cnamecount++;
01404       } else {
01405     if ( rr->t == r->recordType() ) {
01406         if ( rr->nxdomain )
01407       nxdomain = true;
01408         else
01409       answer = true;
01410         l->append( rr );
01411         if ( rr->deleteTime <= lastSweep ) {
01412       // we're returning something that'll be
01413       // deleted soon.  we assume that if the client
01414       // wanted it twice, it'll want it again, so we
01415       // ask the name server again right now.
01416       Q3DnsQuery * query = new Q3DnsQuery;
01417       query->started = now();
01418       query->id = ++::id;
01419       query->t = rr->t;
01420       query->l = rr->domain->name();
01421       // note that here, we don't bother about
01422       // notification. but we do bother about
01423       // timeouts: we make sure to use high timeouts
01424       // and few tramsissions.
01425       query->step = ns->count();
01426       QObject::connect( query, SIGNAL(timeout()),
01427             Q3DnsManager::manager(),
01428             SLOT(retransmit()) );
01429       Q3DnsManager::manager()->transmitQuery( query );
01430         }
01431     }
01432     d->rrs->next();
01433       }
01434   }
01435   // if we found a positive result, return quickly
01436   if ( answer && l->count() ) {
01437 #if defined(Q3DNS_DEBUG)
01438       qDebug( "found %d records for %s",
01439         l->count(), r->label().ascii() );
01440       l->first();
01441       while( l->current() ) {
01442     qDebug( "  type %d target %s address %s",
01443            l->current()->t,
01444            l->current()->target.latin1(),
01445            l->current()->address.toString().latin1() );
01446     l->next();
01447       }
01448 #endif
01449       l->first();
01450       return l;
01451   }
01452 
01453 #if defined(Q3DNS_DEBUG)
01454   if ( nxdomain )
01455       qDebug( "found NXDomain %s", s.ascii() );
01456 #endif
01457 
01458   if ( !nxdomain ) {
01459       // if we didn't, and not a negative result either, perhaps
01460       // we need to transmit a query.
01461       uint q = 0;
01462       while ( q < m->queries.size() &&
01463         ( m->queries[q] == 0 ||
01464           m->queries[q]->t != r->recordType() ||
01465           m->queries[q]->l != s ) )
01466     q++;
01467       // we haven't done it before, so maybe we should.  but
01468       // wait - if it's an unqualified name, only ask when all
01469       // the other alternatives are exhausted.
01470       if ( q == m->queries.size() && ( s.find( '.' ) >= 0 ||
01471                int(l->count()) >= n.count()-1 ) ) {
01472     Q3DnsQuery * query = new Q3DnsQuery;
01473     query->started = now();
01474     query->id = ++::id;
01475     query->t = r->recordType();
01476     query->l = s;
01477     query->dns->replace( (void*)r, (void*)r );
01478     QObject::connect( query, SIGNAL(timeout()),
01479           Q3DnsManager::manager(), SLOT(retransmit()) );
01480     Q3DnsManager::manager()->transmitQuery( query );
01481       } else if ( q < m->queries.size() ) {
01482     // if we've found an earlier query for the same
01483     // domain/type, subscribe to its answer
01484     m->queries[q]->dns->replace( (void*)r, (void*)r );
01485       }
01486   }
01487     }
01488     l->first();
01489     return l;
01490 }
01491 
01492 
01493 void Q3DnsDomain::sweep( Q_UINT32 thisSweep )
01494 {
01495     if ( !rrs )
01496   return;
01497 
01498     Q3DnsRR * rr;
01499     rrs->first();
01500     while( (rr=rrs->current()) != 0 ) {
01501   if ( !rr->deleteTime )
01502       rr->deleteTime = thisSweep; // will hit next time around
01503 
01504 #if defined(Q3DNS_DEBUG)
01505   qDebug( "Q3Dns::sweep: %s type %d expires %u %u - %s / %s",
01506          rr->domain->name().latin1(), rr->t,
01507          rr->expireTime, rr->deleteTime,
01508          rr->target.latin1(), rr->address.toString().latin1());
01509 #endif
01510   if ( rr->current == false ||
01511        rr->t == Q3Dns::None ||
01512        rr->deleteTime <= thisSweep ||
01513        rr->expireTime <= thisSweep )
01514       rrs->remove();
01515   else
01516       rrs->next();
01517     }
01518 
01519     if ( rrs->isEmpty() ) {
01520   delete rrs;
01521   rrs = 0;
01522     }
01523 }
01524 
01525 
01526 
01527 
01528 // the itsy-bitsy little socket class I don't really need except for
01529 // so I can subclass and reimplement the slots.
01530 
01531 
01532 Q3DnsSocket::Q3DnsSocket( QObject * parent, const char * name )
01533     : QObject( parent, name )
01534 {
01535     // nothing
01536 }
01537 
01538 
01539 Q3DnsSocket::~Q3DnsSocket()
01540 {
01541     // nothing
01542 }
01543 
01544 
01545 void Q3DnsSocket::cleanCache()
01546 {
01547     // nothing
01548 }
01549 
01550 
01551 void Q3DnsSocket::retransmit()
01552 {
01553     // nothing
01554 }
01555 
01556 
01557 void Q3DnsSocket::answer()
01558 {
01559     // nothing
01560 }
01561 
01562 
01606 Q3Dns::Q3Dns()
01607 {
01608     d = new Q3DnsPrivate;
01609     t = None;
01610 }
01611 
01612 
01613 
01614 
01626 Q3Dns::Q3Dns( const QString & label, RecordType rr )
01627 {
01628     d = new Q3DnsPrivate;
01629     t = rr;
01630     setLabel( label );
01631     setStartQueryTimer(); // start query the next time we enter event loop
01632 }
01633 
01634 
01635 
01650 Q3Dns::Q3Dns( const QHostAddress & address, RecordType rr )
01651 {
01652     d = new Q3DnsPrivate;
01653     t = rr;
01654     setLabel( address );
01655     setStartQueryTimer(); // start query the next time we enter event loop
01656 }
01657 
01658 
01659 
01660 
01665 Q3Dns::~Q3Dns()
01666 {
01667     if ( globalManager ) {
01668   uint q = 0;
01669   Q3DnsManager * m = globalManager;
01670   while( q < m->queries.size() ) {
01671       Q3DnsQuery * query=m->queries[q];
01672       if ( query && query->dns )
01673         (void)query->dns->take( (void*) this );
01674     q++;
01675   }
01676 
01677     }
01678 
01679     delete d;
01680     d = 0;
01681 }
01682 
01683 
01684 
01685 
01698 void Q3Dns::setLabel( const QString & label )
01699 {
01700     l = label;
01701     d->noNames = false;
01702 
01703     // construct a list of qualified names
01704     n.clear();
01705     if ( l.length() > 1 && l[(int)l.length()-1] == '.' ) {
01706   n.append( l.left( l.length()-1 ).lower() );
01707     } else {
01708   int i = l.length();
01709   int dots = 0;
01710   const int maxDots = 2;
01711   while( i && dots < maxDots ) {
01712       if ( l[--i] == '.' )
01713     dots++;
01714   }
01715   if ( dots < maxDots ) {
01716       (void)Q3DnsManager::manager(); // create a Q3DnsManager, if it is not already there
01717       Q3StrListIterator it( *domains );
01718       const char * dom;
01719       while( (dom=it.current()) != 0 ) {
01720     ++it;
01721     n.append( l.lower() + "." + dom );
01722       }
01723   }
01724   n.append( l.lower() );
01725     }
01726 
01727 #if defined(Q_DNS_SYNCHRONOUS)
01728     if ( d->noEventLoop ) {
01729   doSynchronousLookup();
01730     } else {
01731   setStartQueryTimer(); // start query the next time we enter event loop
01732     }
01733 #else
01734     setStartQueryTimer(); // start query the next time we enter event loop
01735 #endif
01736 #if defined(Q3DNS_DEBUG)
01737     qDebug( "Q3Dns::setLabel: %d address(es) for %s", n.count(), l.ascii() );
01738     int i = 0;
01739     for( i = 0; i < (int)n.count(); i++ )
01740   qDebug( "Q3Dns::setLabel: %d: %s", i, n[i].ascii() );
01741 #endif
01742 }
01743 
01744 
01754 void Q3Dns::setLabel( const QHostAddress & address )
01755 {
01756     setLabel( toInAddrArpaDomain( address ) );
01757 }
01758 
01759 
01829 void Q3Dns::setRecordType( RecordType rr )
01830 {
01831     t = rr;
01832     d->noNames = false;
01833     setStartQueryTimer(); // start query the next time we enter event loop
01834 }
01835 
01841 void Q3Dns::startQuery()
01842 {
01843     // isWorking() starts the query (if necessary)
01844     if ( !isWorking() )
01845   emit resultsReady();
01846 }
01847 
01853 void Q3Dns::setStartQueryTimer()
01854 {
01855 #if defined(Q_DNS_SYNCHRONOUS)
01856     if ( !d->queryTimer && !d->noEventLoop )
01857 #else
01858     if ( !d->queryTimer )
01859 #endif
01860     {
01861   // start the query the next time we enter event loop
01862   d->queryTimer = new QTimer( this );
01863   connect( d->queryTimer, SIGNAL(timeout()),
01864      this, SLOT(startQuery()) );
01865   d->queryTimer->start( 0, true );
01866     }
01867 }
01868 
01869 /*
01870     Transforms the host address \a address to the IN-ADDR.ARPA domain
01871     name. Returns something indeterminate if you're sloppy or
01872     naughty. This function has an IPv4-specific name, but works for
01873     IPv6 too.
01874 */
01875 QString Q3Dns::toInAddrArpaDomain( const QHostAddress &address )
01876 {
01877     QString s;
01878     if ( address.isNull() ) {
01879   // if the address isn't valid, neither of the other two make
01880   // cases make sense. better to just return.
01881     } else if ( address.isIp4Addr() ) {
01882   Q_UINT32 i = address.ip4Addr();
01883   s.sprintf( "%d.%d.%d.%d.IN-ADDR.ARPA",
01884        i & 0xff, (i >> 8) & 0xff, (i>>16) & 0xff, (i>>24) & 0xff );
01885     } else {
01886   // RFC 3152. (1886 is deprecated, and clients no longer need to
01887   // support it, in practice).
01888   Q_IPV6ADDR i = address.toIPv6Address();
01889   s = "ip6.arpa";
01890   uint b = 0;
01891   while( b < 16 ) {
01892       s = QString::number( i.c[b]%16, 16 ) + "." +
01893     QString::number( i.c[b]/16, 16 ) + "." + s;
01894       b++;
01895   }
01896     }
01897     return s;
01898 }
01899 
01900 
01924 bool Q3Dns::isWorking() const
01925 {
01926 #if defined(Q3DNS_DEBUG)
01927     qDebug( "Q3Dns::isWorking (%s, %d)", l.ascii(), t );
01928 #endif
01929     if ( t == None )
01930   return false;
01931 
01932 #if defined(Q_DNS_SYNCHRONOUS)
01933     if ( d->noEventLoop )
01934   return true;
01935 #endif
01936 
01937     Q3PtrList<Q3DnsRR> * ll = Q3DnsDomain::cached( this );
01938     Q_LONG queries = n.count();
01939     while( ll->current() != 0 ) {
01940   if ( ll->current()->nxdomain ) {
01941       queries--;
01942   } else {
01943       delete ll;
01944       return false;
01945   }
01946   ll->next();
01947     }
01948     delete ll;
01949 
01950     if ( queries <= 0 )
01951   return false;
01952     if ( d->noNames )
01953   return false;
01954     return true;
01955 }
01956 
01957 
01979 Q3ValueList<QHostAddress> Q3Dns::addresses() const
01980 {
01981 #if defined(Q3DNS_DEBUG)
01982     qDebug( "Q3Dns::addresses (%s)", l.ascii() );
01983 #endif
01984     Q3ValueList<QHostAddress> result;
01985     if ( t != A && t != Aaaa )
01986   return result;
01987 
01988     Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
01989 
01990     Q3DnsRR * rr;
01991     while( (rr=cached->current()) != 0 ) {
01992   if ( rr->current && !rr->nxdomain )
01993       result.append( rr->address );
01994   cached->next();
01995     }
01996     delete cached;
01997     return result;
01998 }
01999 
02000 
02028 Q3ValueList<Q3Dns::MailServer> Q3Dns::mailServers() const
02029 {
02030 #if defined(Q3DNS_DEBUG)
02031     qDebug( "Q3Dns::mailServers (%s)", l.ascii() );
02032 #endif
02033     Q3ValueList<Q3Dns::MailServer> result;
02034     if ( t != Mx )
02035   return result;
02036 
02037     Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
02038 
02039     Q3DnsRR * rr;
02040     while( (rr=cached->current()) != 0 ) {
02041   if ( rr->current && !rr->nxdomain ) {
02042       MailServer ms( rr->target, rr->priority );
02043       result.append( ms );
02044   }
02045   cached->next();
02046     }
02047     delete cached;
02048     return result;
02049 }
02050 
02051 
02080 Q3ValueList<Q3Dns::Server> Q3Dns::servers() const
02081 {
02082 #if defined(Q3DNS_DEBUG)
02083     qDebug( "Q3Dns::servers (%s)", l.ascii() );
02084 #endif
02085     Q3ValueList<Q3Dns::Server> result;
02086     if ( t != Srv )
02087   return result;
02088 
02089     Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
02090 
02091     Q3DnsRR * rr;
02092     while( (rr=cached->current()) != 0 ) {
02093   if ( rr->current && !rr->nxdomain ) {
02094       Server s( rr->target, rr->priority, rr->weight, rr->port );
02095       result.append( s );
02096   }
02097   cached->next();
02098     }
02099     delete cached;
02100     return result;
02101 }
02102 
02103 
02119 QStringList Q3Dns::hostNames() const
02120 {
02121 #if defined(Q3DNS_DEBUG)
02122     qDebug( "Q3Dns::hostNames (%s)", l.ascii() );
02123 #endif
02124     QStringList result;
02125     if ( t != Ptr )
02126   return result;
02127 
02128     Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
02129 
02130     Q3DnsRR * rr;
02131     while( (rr=cached->current()) != 0 ) {
02132   if ( rr->current && !rr->nxdomain ) {
02133       QString str( rr->target );
02134       result.append( str );
02135   }
02136   cached->next();
02137     }
02138     delete cached;
02139     return result;
02140 }
02141 
02142 
02157 QStringList Q3Dns::texts() const
02158 {
02159 #if defined(Q3DNS_DEBUG)
02160     qDebug( "Q3Dns::texts (%s)", l.ascii() );
02161 #endif
02162     QStringList result;
02163     if ( t != Txt )
02164   return result;
02165 
02166     Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
02167 
02168     Q3DnsRR * rr;
02169     while( (rr=cached->current()) != 0 ) {
02170   if ( rr->current && !rr->nxdomain ) {
02171       QString str( rr->text );
02172       result.append( str );
02173   }
02174   cached->next();
02175     }
02176     delete cached;
02177     return result;
02178 }
02179 
02180 
02196 QString Q3Dns::canonicalName() const
02197 {
02198     // the cname should work regardless of the recordType(), so set the record
02199     // type temporarily to cname when you look at the cache
02200     Q3Dns *that = (Q3Dns*) this; // mutable function
02201     RecordType oldType = t;
02202     that->t = Cname;
02203     Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( that );
02204     that->t = oldType;
02205 
02206     Q3DnsRR * rr;
02207     while( (rr=cached->current()) != 0 ) {
02208   if ( rr->current && !rr->nxdomain && rr->domain ) {
02209       delete cached;
02210       return rr->target;
02211   }
02212   cached->next();
02213     }
02214     delete cached;
02215     return QString();
02216 }
02217 
02218 #if defined(Q_DNS_SYNCHRONOUS)
02219 
02221 void Q3Dns::connectNotify( const char *signal )
02222 {
02223     if ( d->noEventLoop && qstrcmp(signal,SIGNAL(resultsReady()) )==0 ) {
02224   doSynchronousLookup();
02225     }
02226 }
02227 #endif
02228 
02229 #if defined(Q_OS_WIN32) || defined(Q_OS_CYGWIN)
02230 
02231 #if defined(Q_DNS_SYNCHRONOUS)
02232 void Q3Dns::doSynchronousLookup()
02233 {
02234     // ### not implemented yet
02235 }
02236 #endif
02237 
02238 // the following typedefs are needed for GetNetworkParams() API call
02239 #ifndef IP_TYPES_INCLUDED
02240 #define MAX_HOSTNAME_LEN    128
02241 #define MAX_DOMAIN_NAME_LEN 128
02242 #define MAX_SCOPE_ID_LEN    256
02243 typedef struct {
02244     char String[4 * 4];
02245 } IP_ADDRESS_STRING, *PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING;
02246 typedef struct _IP_ADDR_STRING {
02247     struct _IP_ADDR_STRING* Next;
02248     IP_ADDRESS_STRING IpAddress;
02249     IP_MASK_STRING IpMask;
02250     DWORD Context;
02251 } IP_ADDR_STRING, *PIP_ADDR_STRING;
02252 typedef struct {
02253     char HostName[MAX_HOSTNAME_LEN + 4] ;
02254     char DomainName[MAX_DOMAIN_NAME_LEN + 4];
02255     PIP_ADDR_STRING CurrentDnsServer;
02256     IP_ADDR_STRING DnsServerList;
02257     UINT NodeType;
02258     char ScopeId[MAX_SCOPE_ID_LEN + 4];
02259     UINT EnableRouting;
02260     UINT EnableProxy;
02261     UINT EnableDns;
02262 } FIXED_INFO, *PFIXED_INFO;
02263 #endif
02264 typedef DWORD (WINAPI *GNP)( PFIXED_INFO, PULONG );
02265 
02266 // ### FIXME: this code is duplicated in qfiledialog.cpp
02267 static QString getWindowsRegString( HKEY key, const QString &subKey )
02268 {
02269     QString s;
02270     QT_WA( {
02271   char buf[1024];
02272   DWORD bsz = sizeof(buf);
02273   int r = RegQueryValueEx( key, (TCHAR*)subKey.ucs2(), 0, 0, (LPBYTE)buf, &bsz );
02274   if ( r == ERROR_SUCCESS ) {
02275       s = QString::fromUcs2( (unsigned short *)buf );
02276   } else if ( r == ERROR_MORE_DATA ) {
02277       char *ptr = new char[bsz+1];
02278       r = RegQueryValueEx( key, (TCHAR*)subKey.ucs2(), 0, 0, (LPBYTE)ptr, &bsz );
02279       if ( r == ERROR_SUCCESS )
02280     s = ptr;
02281       delete [] ptr;
02282   }
02283     } , {
02284   char buf[512];
02285   DWORD bsz = sizeof(buf);
02286   int r = RegQueryValueExA( key, subKey.local8Bit(), 0, 0, (LPBYTE)buf, &bsz );
02287   if ( r == ERROR_SUCCESS ) {
02288       s = buf;
02289   } else if ( r == ERROR_MORE_DATA ) {
02290       char *ptr = new char[bsz+1];
02291       r = RegQueryValueExA( key, subKey.local8Bit(), 0, 0, (LPBYTE)ptr, &bsz );
02292       if ( r == ERROR_SUCCESS )
02293     s = ptr;
02294       delete [] ptr;
02295   }
02296     } );
02297     return s;
02298 }
02299 
02300 static bool getDnsParamsFromRegistry( const QString &path,
02301   QString *domainName, QString *nameServer, QString *searchList )
02302 {
02303     HKEY k;
02304     int r;
02305     QT_WA( {
02306   r = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
02307         (TCHAR*)path.ucs2(),
02308         0, KEY_READ, &k );
02309     } , {
02310   r = RegOpenKeyExA( HKEY_LOCAL_MACHINE,
02311          path.latin1(),
02312          0, KEY_READ, &k );
02313     } );
02314 
02315     if ( r == ERROR_SUCCESS ) {
02316   *domainName = getWindowsRegString( k, "DhcpDomain" );
02317   if ( domainName->isEmpty() )
02318       *domainName = getWindowsRegString( k, "Domain" );
02319 
02320   *nameServer = getWindowsRegString( k, "DhcpNameServer" );
02321   if ( nameServer->isEmpty() )
02322       *nameServer = getWindowsRegString( k, "NameServer" );
02323 
02324   *searchList = getWindowsRegString( k, "SearchList" );
02325     }
02326     RegCloseKey( k );
02327     return r == ERROR_SUCCESS;
02328 }
02329 
02330 void Q3Dns::doResInit()
02331 {
02332     char separator = 0;
02333 
02334     if ( ns )
02335         delete ns;
02336     ns = new Q3PtrList<QHostAddress>;
02337     ns->setAutoDelete( true );
02338     domains = new Q3StrList( true );
02339     domains->setAutoDelete( true );
02340 
02341     QString domainName, nameServer, searchList;
02342 
02343     bool gotNetworkParams = false;
02344     // try the API call GetNetworkParams() first and use registry lookup only
02345     // as a fallback
02346 #ifdef Q_OS_TEMP
02347     HINSTANCE hinstLib = LoadLibraryW( L"iphlpapi" );
02348 #else
02349     HINSTANCE hinstLib = LoadLibraryA( "iphlpapi" );
02350 #endif
02351     if ( hinstLib != 0 ) {
02352 #ifdef Q_OS_TEMP
02353   GNP getNetworkParams = (GNP) GetProcAddressW( hinstLib, L"GetNetworkParams" );
02354 #else
02355   GNP getNetworkParams = (GNP) GetProcAddress( hinstLib, "GetNetworkParams" );
02356 #endif
02357   if ( getNetworkParams != 0 ) {
02358       ULONG l = 0;
02359       DWORD res;
02360       res = getNetworkParams( 0, &l );
02361       if ( res == ERROR_BUFFER_OVERFLOW ) {
02362     FIXED_INFO *finfo = (FIXED_INFO*)new char[l];
02363     res = getNetworkParams( finfo, &l );
02364     if ( res == ERROR_SUCCESS ) {
02365         domainName = finfo->DomainName;
02366         nameServer = "";
02367         IP_ADDR_STRING *dnsServer = &finfo->DnsServerList;
02368         while ( dnsServer != 0 ) {
02369       nameServer += dnsServer->IpAddress.String;
02370       dnsServer = dnsServer->Next;
02371       if ( dnsServer != 0 )
02372           nameServer += " ";
02373         }
02374         searchList = "";
02375         separator = ' ';
02376         gotNetworkParams = true;
02377     }
02378     delete[] finfo;
02379       }
02380   }
02381   FreeLibrary( hinstLib );
02382     }
02383     if ( !gotNetworkParams ) {
02384   if ( getDnsParamsFromRegistry(
02385       QString( "System\\CurrentControlSet\\Services\\Tcpip\\Parameters" ),
02386         &domainName, &nameServer, &searchList )) {
02387       // for NT
02388       separator = ' ';
02389   } else if ( getDnsParamsFromRegistry(
02390       QString( "System\\CurrentControlSet\\Services\\VxD\\MSTCP" ),
02391         &domainName, &nameServer, &searchList )) {
02392       // for Windows 98
02393       separator = ',';
02394   } else {
02395       // Could not access the TCP/IP parameters
02396       domainName = "";
02397       nameServer = "127.0.0.1";
02398       searchList = "";
02399       separator = ' ';
02400   }
02401     }
02402 
02403     nameServer = nameServer.simplifyWhiteSpace();
02404     int first, last;
02405     if ( !nameServer.isEmpty() ) {
02406   first = 0;
02407   do {
02408       last = nameServer.find( separator, first );
02409       if ( last < 0 )
02410     last = nameServer.length();
02411       Q3Dns tmp( nameServer.mid( first, last-first ), Q3Dns::A );
02412       Q3ValueList<QHostAddress> address = tmp.addresses();
02413       Q_LONG i = address.count();
02414       while( i )
02415     ns->append( new QHostAddress(address[--i]) );
02416       first = last+1;
02417   } while( first < (int)nameServer.length() );
02418     }
02419 
02420     searchList = searchList + " " + domainName;
02421     searchList = searchList.simplifyWhiteSpace().lower();
02422     first = 0;
02423     do {
02424   last = searchList.find( separator, first );
02425   if ( last < 0 )
02426       last = searchList.length();
02427   domains->append( qstrdup( searchList.mid( first, last-first ).latin1() ) );
02428   first = last+1;
02429     } while( first < (int)searchList.length() );
02430 }
02431 
02432 #elif defined(Q_OS_UNIX)
02433 
02434 #if defined(Q_DNS_SYNCHRONOUS)
02435 void Q3Dns::doSynchronousLookup()
02436 {
02437     if ( t!=None && !l.isEmpty() ) {
02438   Q3ValueListIterator<QString> it = n.begin();
02439   Q3ValueListIterator<QString> end = n.end();
02440   int type;
02441   switch( t ) {
02442       case Q3Dns::A:
02443     type = 1;
02444     break;
02445       case Q3Dns::Aaaa:
02446     type = 28;
02447     break;
02448       case Q3Dns::Mx:
02449     type = 15;
02450     break;
02451       case Q3Dns::Srv:
02452     type = 33;
02453     break;
02454       case Q3Dns::Cname:
02455     type = 5;
02456     break;
02457       case Q3Dns::Ptr:
02458     type = 12;
02459     break;
02460       case Q3Dns::Txt:
02461     type = 16;
02462     break;
02463       default:
02464     type = (char)255; // any
02465     break;
02466   }
02467   while( it != end ) {
02468       QString s = *it;
02469       it++;
02470       QByteArray ba( 512 );
02471       int len = res_search( s.latin1(), 1, type, (uchar*)ba.data(), ba.size() );
02472       if ( len > 0 ) {
02473     ba.resize( len );
02474 
02475     Q3DnsQuery * query = new Q3DnsQuery;
02476     query->started = now();
02477     query->id = ++::id;
02478     query->t = t;
02479     query->l = s;
02480     Q3DnsAnswer a( ba, query );
02481     a.parse();
02482       } else if ( len == -1 ) {
02483     // res_search error
02484       }
02485   }
02486   emit resultsReady();
02487     }
02488 }
02489 #endif
02490 
02491 #if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 3)))
02492 #define Q_MODERN_RES_API
02493 #endif
02494 
02495 void Q3Dns::doResInit()
02496 {
02497     if ( ns )
02498   return;
02499     ns = new Q3PtrList<QHostAddress>;
02500     ns->setAutoDelete( true );
02501     domains = new Q3StrList( true );
02502     domains->setAutoDelete( true );
02503 
02504     // read resolv.conf manually.
02505     QFile resolvConf("/etc/resolv.conf");
02506     if (resolvConf.open(QIODevice::ReadOnly)) {
02507         QTextStream stream( &resolvConf );
02508   QString line;
02509 
02510   while ( !stream.atEnd() ) {
02511             line = stream.readLine();
02512       QStringList list = QStringList::split( " ", line );
02513       if( line.startsWith( "#" ) || list.size() < 2 )
02514          continue;
02515       const QString type = list[0].lower();
02516 
02517       if ( type == "nameserver" ) {
02518     QHostAddress *address = new QHostAddress();
02519     if ( address->setAddress( QString(list[1]) ) ) {
02520         // only add ipv6 addresses from resolv.conf if
02521         // this host supports ipv6.
02522         if ( address->isIPv4Address() || ipv6support )
02523       ns->append( address );
02524                     else
02525                         delete address;
02526     } else {
02527         delete address;
02528     }
02529       } else if ( type == "search" ) {
02530     QStringList srch = QStringList::split( " ", list[1] );
02531     for ( QStringList::Iterator i = srch.begin(); i != srch.end(); ++i )
02532         domains->append( (*i).lower().local8Bit() );
02533 
02534       } else if ( type == "domain" ) {
02535     domains->append( list[1].lower().local8Bit() );
02536       }
02537   }
02538     }
02539 
02540     if (ns->isEmpty()) {
02541 #if defined(Q_MODERN_RES_API)
02542   struct __res_state res;
02543   res_ninit( &res );
02544   int i;
02545   // find the name servers to use
02546   for( i=0; i < MAXNS && i < res.nscount; i++ )
02547       ns->append( new QHostAddress( ntohl( res.nsaddr_list[i].sin_addr.s_addr ) ) );
02548 #  if defined(MAXDFLSRCH)
02549   for( i=0; i < MAXDFLSRCH; i++ ) {
02550       if ( res.dnsrch[i] && *(res.dnsrch[i]) )
02551     domains->append( QString::fromLatin1( res.dnsrch[i] ).lower().local8Bit() );
02552       else
02553     break;
02554   }
02555 #  endif
02556   if ( *res.defdname )
02557       domains->append( QString::fromLatin1( res.defdname ).lower().local8Bit() );
02558 #else
02559   res_init();
02560   int i;
02561   // find the name servers to use
02562   for( i=0; i < MAXNS && i < _res.nscount; i++ )
02563       ns->append( new QHostAddress( ntohl( _res.nsaddr_list[i].sin_addr.s_addr ) ) );
02564 #  if defined(MAXDFLSRCH)
02565   for( i=0; i < MAXDFLSRCH; i++ ) {
02566       if ( _res.dnsrch[i] && *(_res.dnsrch[i]) )
02567     domains->append( QString::fromLatin1( _res.dnsrch[i] ).lower().local8Bit() );
02568       else
02569     break;
02570   }
02571 #  endif
02572   if ( *_res.defdname )
02573       domains->append( QString::fromLatin1( _res.defdname ).lower().local8Bit() );
02574 #endif
02575 
02576   // the code above adds "0.0.0.0" as a name server at the slightest
02577   // hint of trouble. so remove those again.
02578   ns->first();
02579   while( ns->current() ) {
02580       if ( ns->current()->isNull() )
02581     delete ns->take();
02582       else
02583     ns->next();
02584   }
02585     }
02586 
02587     QFile hosts( QString::fromLatin1( "/etc/hosts" ) );
02588     if ( hosts.open( QIODevice::ReadOnly ) ) {
02589   // read the /etc/hosts file, creating long-life A and PTR RRs
02590   // for the things we find.
02591   QTextStream i( &hosts );
02592   QString line;
02593   while( !i.atEnd() ) {
02594       line = i.readLine().simplifyWhiteSpace().lower();
02595       uint n = 0;
02596       while( (int) n < line.length() && line[(int)n] != '#' )
02597     n++;
02598       line.truncate( n );
02599       n = 0;
02600       while( (int) n < line.length() && !line[(int)n].isSpace() )
02601     n++;
02602       QString ip = line.left( n );
02603       QHostAddress a;
02604       a.setAddress( ip );
02605       if ( ( a.isIPv4Address() || a.isIPv6Address() ) && !a.isNull() ) {
02606     bool first = true;
02607     line = line.mid( n+1 );
02608     n = 0;
02609     while( (int) n < line.length() && !line[(int)n].isSpace() )
02610         n++;
02611     QString hostname = line.left( n );
02612     // ### in case of bad syntax, hostname is invalid. do we care?
02613     if ( n ) {
02614         Q3DnsRR * rr = new Q3DnsRR( hostname );
02615         if ( a.isIPv4Address() )
02616       rr->t = Q3Dns::A;
02617         else
02618       rr->t = Q3Dns::Aaaa;
02619         rr->address = a;
02620         rr->deleteTime = UINT_MAX;
02621         rr->expireTime = UINT_MAX;
02622         rr->current = true;
02623         if ( first ) {
02624       first = false;
02625       Q3DnsRR * ptr = new Q3DnsRR( Q3Dns::toInAddrArpaDomain( a ) );
02626       ptr->t = Q3Dns::Ptr;
02627       ptr->target = hostname;
02628       ptr->deleteTime = UINT_MAX;
02629       ptr->expireTime = UINT_MAX;
02630       ptr->current = true;
02631         }
02632     }
02633       }
02634   }
02635     }
02636 }
02637 
02638 #endif
02639 
02640 #endif // QT_NO_DNS

Generated on Thu Mar 15 11:58:13 2007 for Qt 4.2 User's Guide by  doxygen 1.5.1