diff options
author | Timothy Pearson <[email protected]> | 2011-11-08 12:31:36 -0600 |
---|---|---|
committer | Timothy Pearson <[email protected]> | 2011-11-08 12:31:36 -0600 |
commit | d796c9dd933ab96ec83b9a634feedd5d32e1ba3f (patch) | |
tree | 6e3dcca4f77e20ec8966c666aac7c35bd4704053 /src/network/qdns.cpp | |
download | tqt3-d796c9dd933ab96ec83b9a634feedd5d32e1ba3f.tar.gz tqt3-d796c9dd933ab96ec83b9a634feedd5d32e1ba3f.zip |
Test conversion to TQt3 from Qt3 8c6fc1f8e35fd264dd01c582ca5e7549b32ab731
Diffstat (limited to 'src/network/qdns.cpp')
-rw-r--r-- | src/network/qdns.cpp | 2692 |
1 files changed, 2692 insertions, 0 deletions
diff --git a/src/network/qdns.cpp b/src/network/qdns.cpp new file mode 100644 index 000000000..65deec7f6 --- /dev/null +++ b/src/network/qdns.cpp @@ -0,0 +1,2692 @@ +/**************************************************************************** +** +** Implementation of TQDns class. +** +** Created : 991122 +** +** Copyright (C) 1999-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the network module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at [email protected]. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qplatformdefs.h" + +// POSIX Large File Support redefines open -> open64 +#if defined(open) +# undef open +#endif + +// POSIX Large File Support redefines truncate -> truncate64 +#if defined(truncate) +# undef truncate +#endif + +// Solaris redefines connect -> __xnet_connect with _XOPEN_SOURCE_EXTENDED. +#if defined(connect) +# undef connect +#endif + +// UnixWare 7 redefines socket -> _socket +#if defined(socket) +# undef socket +#endif + +#include "qdns.h" + +#ifndef QT_NO_DNS + +#include "qdatetime.h" +#include "qdict.h" +#include "qptrlist.h" +#include "qstring.h" +#include "qtimer.h" +#include "qapplication.h" +#include "qptrvector.h" +#include "qstrlist.h" +#include "qptrdict.h" +#include "qfile.h" +#include "qtextstream.h" +#include "qsocketdevice.h" +#include "qcleanuphandler.h" +#include <limits.h> +#ifdef Q_OS_MAC +#include "../3rdparty/dlcompat/dlfcn.h" +#endif + +//#define TQDNS_DEBUG + +static Q_UINT16 id; // ### seeded started by now() + + +static TQDateTime * originOfTime = 0; + +static TQCleanupHandler<TQDateTime> qdns_cleanup_time; + +static Q_UINT32 now() +{ + if ( originOfTime ) + return originOfTime->secsTo( TQDateTime::currentDateTime() ); + + originOfTime = new TQDateTime( TQDateTime::currentDateTime() ); + ::id = originOfTime->time().msec() * 60 + originOfTime->time().second(); + qdns_cleanup_time.add( &originOfTime ); + return 0; +} + + +static TQPtrList<TQHostAddress> * ns = 0; +static TQStrList * domains = 0; +static bool ipv6support = FALSE; + +static int qdns_res_init() +{ +#ifdef Q_OS_MAC + typedef int (*PtrRes_init)(); + static PtrRes_init ptrRes_init = 0; + if (!ptrRes_init) + ptrRes_init = (PtrRes_init)DL_PREFIX(dlsym)(RTLD_NEXT, "res_init"); + if (ptrRes_init) + return (*ptrRes_init)(); + else + return -1; +#elif defined(Q_OS_UNIX) + return res_init(); +#else + return 0; // not called at all on Windows. +#endif +} + + +class TQDnsPrivate { +public: + TQDnsPrivate() : queryTimer( 0 ), noNames(FALSE) + { +#if defined(Q_DNS_SYNCHRONOUS) +#if defined(Q_OS_UNIX) + noEventLoop = qApp==0 || qApp->loopLevel()==0; +#else + noEventLoop = FALSE; +#endif +#endif + } + ~TQDnsPrivate() + { + delete queryTimer; + } +private: + TQTimer * queryTimer; + bool noNames; +#if defined(Q_DNS_SYNCHRONOUS) + bool noEventLoop; +#endif + + friend class TQDns; + friend class TQDnsAnswer; +}; + + +class TQDnsRR; +class TQDnsDomain; + + + +// TQDnsRR is the class used to store a single RR. TQDnsRR can store +// all of the supported RR types. a TQDnsRR is always cached. + +// TQDnsRR is mostly constructed from the outside. a but hacky, but +// permissible since the entire class is internal. + +class TQDnsRR { +public: + TQDnsRR( const TQString & label ); + ~TQDnsRR(); + +public: + TQDnsDomain * domain; + TQDns::RecordType t; + bool nxdomain; + bool current; + Q_UINT32 expireTime; + Q_UINT32 deleteTime; + // somewhat space-wasting per-type data + // a / aaaa + TQHostAddress address; + // cname / mx / srv / ptr + TQString target; + // mx / srv + Q_UINT16 priority; + // srv + Q_UINT16 weight; + Q_UINT16 port; + // txt + TQString text; // could be overloaded into target... +private: + +}; + + +class TQDnsDomain { +public: + TQDnsDomain( const TQString & label ); + ~TQDnsDomain(); + + static void add( const TQString & label, TQDnsRR * ); + static TQPtrList<TQDnsRR> * cached( const TQDns * ); + + void take( TQDnsRR * ); + + void sweep( Q_UINT32 thisSweep ); + + bool isEmpty() const { return rrs == 0 || rrs->isEmpty(); } + + TQString name() const { return l; } + +public: + TQString l; + TQPtrList<TQDnsRR> * rrs; +}; + + +class TQDnsQuery: public TQTimer { // this inheritance is a very evil hack +public: + TQDnsQuery(): + id( 0 ), t( TQDns::None ), step(0), started(0), + dns( new TQPtrDict<void>(17) ) {} + ~TQDnsQuery() { delete dns; } + Q_UINT16 id; + TQDns::RecordType t; + TQString l; + + uint step; + Q_UINT32 started; + + TQPtrDict<void> * dns; +}; + + + +class TQDnsAnswer { +public: + TQDnsAnswer( TQDnsQuery * ); + TQDnsAnswer( const TQByteArray &, TQDnsQuery * ); + ~TQDnsAnswer(); + + void parse(); + void notify(); + + bool ok; + +private: + TQDnsQuery * query; + + Q_UINT8 * answer; + int size; + int pp; + + TQPtrList<TQDnsRR> * rrs; + + // convenience + int next; + int ttl; + TQString label; + TQDnsRR * rr; + + TQString readString(bool multipleLabels = TRUE); + void parseA(); + void parseAaaa(); + void parseMx(); + void parseSrv(); + void parseCname(); + void parsePtr(); + void parseTxt(); + void parseNs(); +}; + + +TQDnsRR::TQDnsRR( const TQString & label ) + : domain( 0 ), t( TQDns::None ), + nxdomain( FALSE ), current( FALSE ), + expireTime( 0 ), deleteTime( 0 ), + priority( 0 ), weight( 0 ), port( 0 ) +{ + TQDnsDomain::add( label, this ); +} + + +// not supposed to be deleted except by TQDnsDomain +TQDnsRR::~TQDnsRR() +{ + // nothing is necessary +} + + +// this one just sticks in a NXDomain +TQDnsAnswer::TQDnsAnswer( TQDnsQuery * query_ ) +{ + ok = TRUE; + + answer = 0; + size = 0; + query = query_; + pp = 0; + rrs = new TQPtrList<TQDnsRR>; + rrs->setAutoDelete( FALSE ); + next = size; + ttl = 0; + label = TQString::null; + rr = 0; + + TQDnsRR * newrr = new TQDnsRR( query->l ); + newrr->t = query->t; + newrr->deleteTime = query->started + 10; + newrr->expireTime = query->started + 10; + newrr->nxdomain = TRUE; + newrr->current = TRUE; + rrs->append( newrr ); +} + + +TQDnsAnswer::TQDnsAnswer( const TQByteArray& answer_, + TQDnsQuery * query_ ) +{ + ok = TRUE; + + answer = (Q_UINT8 *)(answer_.data()); + size = (int)answer_.size(); + query = query_; + pp = 0; + rrs = new TQPtrList<TQDnsRR>; + rrs->setAutoDelete( FALSE ); + next = size; + ttl = 0; + label = TQString::null; + rr = 0; +} + + +TQDnsAnswer::~TQDnsAnswer() +{ + if ( !ok && rrs ) { + TQPtrListIterator<TQDnsRR> it( *rrs ); + TQDnsRR * tmprr; + while( (tmprr=it.current()) != 0 ) { + ++it; + tmprr->t = TQDns::None; // will be deleted soonish + } + } + delete rrs; +} + + +TQString TQDnsAnswer::readString(bool multipleLabels) +{ + int p = pp; + TQString r = TQString::null; + Q_UINT8 b; + for( ;; ) { + b = 128; + // Read one character + if ( p >= 0 && p < size ) + b = answer[p]; + + switch( b >> 6 ) { + case 0: + // b is less than 64 + p++; + + // Detect end of data + if ( b == 0 ) { + if ( p > pp ) + pp = p; + return r.isNull() ? TQString( "." ) : r; + } + + // Read a label of size 'b' characters + if ( !r.isNull() ) + r += '.'; + while( b-- > 0 ) { + r += TQChar( answer[p] ); + p++; + } + + // Return immediately if we were only supposed to read one + // label. + if (!multipleLabels) + return r; + + break; + default: + // Ignore unrecognized control character, or p was out of + // range. + goto not_ok; + case 3: + // Use the next character to determine the relative offset + // to jump to before continuing the packet parsing. + int q = ( (answer[p] & 0x3f) << 8 ) + answer[p+1]; + + if ( q >= pp || q >= p ) + goto not_ok; + if ( p >= pp ) + pp = p + 2; + p = q; + } + } +not_ok: + ok = FALSE; + return TQString::null; +} + + + +void TQDnsAnswer::parseA() +{ + if ( next != pp + 4 ) { +#if defined(TQDNS_DEBUG) + qDebug( "TQDns: saw %d bytes long IN A for %s", + next - pp, label.ascii() ); +#endif + return; + } + + rr = new TQDnsRR( label ); + rr->t = TQDns::A; + rr->address = TQHostAddress( ( answer[pp+0] << 24 ) + + ( answer[pp+1] << 16 ) + + ( answer[pp+2] << 8 ) + + ( answer[pp+3] ) ); +#if defined(TQDNS_DEBUG) + qDebug( "TQDns: saw %s IN A %s (ttl %d)", label.ascii(), + rr->address.toString().ascii(), ttl ); +#endif +} + + +void TQDnsAnswer::parseAaaa() +{ + if ( next != pp + 16 ) { +#if defined(TQDNS_DEBUG) + qDebug( "TQDns: saw %d bytes long IN Aaaa for %s", + next - pp, label.ascii() ); +#endif + return; + } + + rr = new TQDnsRR( label ); + rr->t = TQDns::Aaaa; + rr->address = TQHostAddress( answer+pp ); +#if defined(TQDNS_DEBUG) + qDebug( "TQDns: saw %s IN Aaaa %s (ttl %d)", label.ascii(), + rr->address.toString().ascii(), ttl ); +#endif +} + + + +void TQDnsAnswer::parseMx() +{ + if ( next < pp + 2 ) { +#if defined(TQDNS_DEBUG) + qDebug( "TQDns: saw %d bytes long IN MX for %s", + next - pp, label.ascii() ); +#endif + return; + } + + rr = new TQDnsRR( label ); + rr->priority = (answer[pp] << 8) + answer[pp+1]; + pp += 2; + rr->target = readString().lower(); + if ( !ok ) { +#if defined(TQDNS_DEBUG) + qDebug( "TQDns: saw bad string in MX for %s", label.ascii() ); +#endif + return; + } + rr->t = TQDns::Mx; +#if defined(TQDNS_DEBUG) + qDebug( "TQDns: saw %s IN MX %d %s (ttl %d)", label.ascii(), + rr->priority, rr->target.ascii(), ttl ); +#endif +} + + +void TQDnsAnswer::parseSrv() +{ + if ( next < pp + 6 ) { +#if defined(TQDNS_DEBUG) + qDebug( "TQDns: saw %d bytes long IN SRV for %s", + next - pp, label.ascii() ); +#endif + return; + } + + rr = new TQDnsRR( label ); + rr->priority = (answer[pp] << 8) + answer[pp+1]; + rr->weight = (answer[pp+2] << 8) + answer[pp+3]; + rr->port = (answer[pp+4] << 8) + answer[pp+5]; + pp += 6; + rr->target = readString().lower(); + if ( !ok ) { +#if defined(TQDNS_DEBUG) + qDebug( "TQDns: saw bad string in SRV for %s", label.ascii() ); +#endif + return; + } + rr->t = TQDns::Srv; +#if defined(TQDNS_DEBUG) + qDebug( "TQDns: saw %s IN SRV %d %d %d %s (ttl %d)", label.ascii(), + rr->priority, rr->weight, rr->port, rr->target.ascii(), ttl ); +#endif +} + + +void TQDnsAnswer::parseCname() +{ + TQString target = readString().lower(); + if ( !ok ) { +#if defined(TQDNS_DEBUG) + qDebug( "TQDns: saw bad cname for for %s", label.ascii() ); +#endif + return; + } + + rr = new TQDnsRR( label ); + rr->t = TQDns::Cname; + rr->target = target; +#if defined(TQDNS_DEBUG) + qDebug( "TQDns: saw %s IN CNAME %s (ttl %d)", label.ascii(), + rr->target.ascii(), ttl ); +#endif +} + + +void TQDnsAnswer::parseNs() +{ + TQString target = readString().lower(); + if ( !ok ) { +#if defined(TQDNS_DEBUG) + qDebug( "TQDns: saw bad cname for for %s", label.ascii() ); +#endif + return; + } + + // parse, but ignore + +#if defined(TQDNS_DEBUG) + qDebug( "TQDns: saw %s IN NS %s (ttl %d)", label.ascii(), + target.ascii(), ttl ); +#endif +} + + +void TQDnsAnswer::parsePtr() +{ + TQString target = readString().lower(); + if ( !ok ) { +#if defined(TQDNS_DEBUG) + qDebug( "TQDns: saw bad PTR for for %s", label.ascii() ); +#endif + return; + } + + rr = new TQDnsRR( label ); + rr->t = TQDns::Ptr; + rr->target = target; +#if defined(TQDNS_DEBUG) + qDebug( "TQDns: saw %s IN PTR %s (ttl %d)", label.ascii(), + rr->target.ascii(), ttl ); +#endif +} + + +void TQDnsAnswer::parseTxt() +{ + TQString text = readString(FALSE); + if ( !ok ) { +#if defined(TQDNS_DEBUG) + qDebug( "TQDns: saw bad TXT for for %s", label.ascii() ); +#endif + return; + } + + rr = new TQDnsRR( label ); + rr->t = TQDns::Txt; + rr->text = text; +#if defined(TQDNS_DEBUG) + qDebug( "TQDns: saw %s IN TXT \"%s\" (ttl %d)", label.ascii(), + rr->text.ascii(), ttl ); +#endif +} + + +void TQDnsAnswer::parse() +{ + // okay, do the work... + if ( (answer[2] & 0x78) != 0 ) { +#if defined(TQDNS_DEBUG) + qDebug( "DNS Manager: answer to wrong query type (%d)", answer[1] ); +#endif + ok = FALSE; + return; + } + + // AA + bool aa = (answer[2] & 4) != 0; + + // TC + if ( (answer[2] & 2) != 0 ) { +#if defined(TQDNS_DEBUG) + qDebug( "DNS Manager: truncated answer; pressing on" ); +#endif + } + + // RD + bool rd = (answer[2] & 1) != 0; + + // we don't test RA + // we don't test the MBZ fields + + if ( (answer[3] & 0x0f) == 3 ) { +#if defined(TQDNS_DEBUG) + qDebug( "DNS Manager: saw NXDomain for %s", query->l.ascii() ); +#endif + // NXDomain. cache that for one minute. + rr = new TQDnsRR( query->l ); + rr->t = query->t; + rr->deleteTime = query->started + 60; + rr->expireTime = query->started + 60; + rr->nxdomain = TRUE; + rr->current = TRUE; + rrs->append( rr ); + return; + } + + if ( (answer[3] & 0x0f) != 0 ) { +#if defined(TQDNS_DEBUG) + qDebug( "DNS Manager: error code %d", answer[3] & 0x0f ); +#endif + ok = FALSE; + return; + } + + int qdcount = ( answer[4] << 8 ) + answer[5]; + int ancount = ( answer[6] << 8 ) + answer[7]; + int nscount = ( answer[8] << 8 ) + answer[9]; + int adcount = (answer[10] << 8 ) +answer[11]; + + pp = 12; + + // read query + while( qdcount > 0 && pp < size ) { + // should I compare the string against query->l? + (void)readString(); + if ( !ok ) + return; + pp += 4; + qdcount--; + } + + // answers and stuff + int rrno = 0; + // if we parse the answer completely, but there are no answers, + // ignore the entire thing. + int answers = 0; + while( ( rrno < ancount || + ( ok && answers >0 && rrno < ancount + nscount + adcount ) ) && + pp < size ) { + label = readString().lower(); + if ( !ok ) + return; + int rdlength = 0; + if ( pp + 10 <= size ) + rdlength = ( answer[pp+8] << 8 ) + answer[pp+9]; + if ( pp + 10 + rdlength > size ) { +#if defined(TQDNS_DEBUG) + qDebug( "DNS Manager: ran out of stuff to parse (%d+%d>%d (%d)", + pp, rdlength, size, rrno < ancount ); +#endif + // if we're still in the AN section, we should go back and + // at least down the TTLs. probably best to invalidate + // the results. + // the rrs list is good for this + ok = ( rrno < ancount ); + return; + } + uint type, clas; + type = ( answer[pp+0] << 8 ) + answer[pp+1]; + clas = ( answer[pp+2] << 8 ) + answer[pp+3]; + ttl = ( answer[pp+4] << 24 ) + ( answer[pp+5] << 16 ) + + ( answer[pp+6] << 8 ) + answer[pp+7]; + pp = pp + 10; + if ( clas != 1 ) { +#if defined(TQDNS_DEBUG) + qDebug( "DNS Manager: class %d (not internet) for %s", + clas, label.isNull() ? "." : label.ascii() ); +#endif + } else { + next = pp + rdlength; + rr = 0; + switch( type ) { + case 1: + parseA(); + break; + case 28: + parseAaaa(); + break; + case 15: + parseMx(); + break; + case 33: + parseSrv(); + break; + case 5: + parseCname(); + break; + case 12: + parsePtr(); + break; + case 16: + parseTxt(); + break; + case 2: + parseNs(); + break; + default: + // something we don't know +#if defined(TQDNS_DEBUG) + qDebug( "DNS Manager: type %d for %s", type, + label.isNull() ? "." : label.ascii() ); +#endif + break; + } + if ( rr ) { + rr->deleteTime = 0; + if ( ttl > 0 ) + rr->expireTime = query->started + ttl; + else + rr->expireTime = query->started + 20; + if ( rrno < ancount ) { + answers++; + rr->deleteTime = rr->expireTime; + } + rr->current = TRUE; + rrs->append( rr ); + } + } + if ( !ok ) + return; + pp = next; + next = size; + rrno++; + } + if ( answers == 0 ) { +#if defined(TQDNS_DEBUG) + qDebug( "DNS Manager: answer contained no answers" ); +#endif + ok = ( aa && rd ); + } + + // now go through the list and mark all the As that are referenced + // by something we care about. we want to cache such As. + rrs->first(); + TQDict<void> used( 17 ); + used.setAutoDelete( FALSE ); + while( (rr=rrs->current()) != 0 ) { + rrs->next(); + if ( rr->target.length() && rr->deleteTime > 0 && rr->current ) + used.insert( rr->target, (void*)42 ); + if ( ( rr->t == TQDns::A || rr->t == TQDns::Aaaa ) && + used.find( rr->domain->name() ) != 0 ) + rr->deleteTime = rr->expireTime; + } + + // next, for each RR, delete any older RRs that are equal to it + rrs->first(); + while( (rr=rrs->current()) != 0 ) { + rrs->next(); + if ( rr && rr->domain && rr->domain->rrs ) { + TQPtrList<TQDnsRR> * drrs = rr->domain->rrs; + drrs->first(); + TQDnsRR * older; + while( (older=drrs->current()) != 0 ) { + if ( older != rr && + older->t == rr->t && + older->nxdomain == rr->nxdomain && + older->address == rr->address && + older->target == rr->target && + older->priority == rr->priority && + older->weight == rr->weight && + older->port == rr->port && + older->text == rr->text ) { + // well, it's equal, but it's not the same. so we kill it, + // but use its expiry time. +#if defined(TQDNS_DEBUG) + qDebug( "killing off old %d for %s, expire was %d", + older->t, older->domain->name().latin1(), + rr->expireTime ); +#endif + older->t = TQDns::None; + rr->expireTime = TQMAX( older->expireTime, rr->expireTime ); + rr->deleteTime = TQMAX( older->deleteTime, rr->deleteTime ); + older->deleteTime = 0; +#if defined(TQDNS_DEBUG) + qDebug( " adjusted expire is %d", rr->expireTime ); +#endif + } + drrs->next(); + } + } + } + +#if defined(TQDNS_DEBUG) + //qDebug( "DNS Manager: ()" ); +#endif +} + + +class TQDnsUgleHack: public TQDns { +public: + void ugle( bool emitAnyway=FALSE ); +}; + + +void TQDnsAnswer::notify() +{ + if ( !rrs || !ok || !query || !query->dns ) + return; + + TQPtrDict<void> notified; + notified.setAutoDelete( FALSE ); + + TQPtrDictIterator<void> it( *query->dns ); + TQDns * dns; + it.toFirst(); + while( (dns=(TQDns*)(it.current())) != 0 ) { + ++it; + if ( notified.find( (void*)dns ) == 0 ) { + notified.insert( (void*)dns, (void*)42 ); + if ( rrs->count() == 0 ) { +#if defined(TQDNS_DEBUG) + qDebug( "DNS Manager: found no answers!" ); +#endif + dns->d->noNames = TRUE; + ((TQDnsUgleHack*)dns)->ugle( TRUE ); + } else { + TQStringList n = dns->qualifiedNames(); + if ( query && n.contains(query->l) ) + ((TQDnsUgleHack*)dns)->ugle(); +#if defined(TQDNS_DEBUG) + else + qDebug( "DNS Manager: DNS thing %s not notified for %s", + dns->label().ascii(), query->l.ascii() ); +#endif + } + } + } +} + + +// +// +// TQDnsManager +// +// + + +class TQDnsManager: public TQDnsSocket { +private: +public: // just to silence the moronic g++. + TQDnsManager(); + ~TQDnsManager(); +public: + static TQDnsManager * manager(); + + TQDnsDomain * domain( const TQString & ); + + void transmitQuery( TQDnsQuery * ); + void transmitQuery( int ); + + // reimplementation of the slots + void cleanCache(); + void retransmit(); + void answer(); + +public: + TQPtrVector<TQDnsQuery> queries; + TQDict<TQDnsDomain> cache; + TQSocketDevice * ipv4Socket; +#if !defined (QT_NO_IPV6) + TQSocketDevice * ipv6Socket; +#endif +}; + + + +static TQDnsManager * globalManager = 0; + +static void cleanupDns() +{ + delete globalManager; + globalManager = 0; +} + +TQDnsManager * TQDnsManager::manager() +{ + if ( !globalManager ) { + qAddPostRoutine(cleanupDns); + new TQDnsManager(); + } + return globalManager; +} + + +void TQDnsUgleHack::ugle( bool emitAnyway) +{ + if ( emitAnyway || !isWorking() ) { +#if defined(TQDNS_DEBUG) + qDebug( "DNS Manager: status change for %s (type %d)", + label().ascii(), recordType() ); +#endif + emit resultsReady(); + } +} + + +TQDnsManager::TQDnsManager() + : TQDnsSocket( qApp, "Internal DNS manager" ), + queries( TQPtrVector<TQDnsQuery>( 0 ) ), + cache( TQDict<TQDnsDomain>( 83, FALSE ) ), + ipv4Socket( new TQSocketDevice( TQSocketDevice::Datagram, TQSocketDevice::IPv4, 0 ) ) +#if !defined (QT_NO_IPV6) + , ipv6Socket( new TQSocketDevice( TQSocketDevice::Datagram, TQSocketDevice::IPv6, 0 ) ) +#endif +{ + cache.setAutoDelete( TRUE ); + globalManager = this; + + TQTimer * sweepTimer = new TQTimer( this ); + sweepTimer->start( 1000 * 60 * 3 ); + connect( sweepTimer, SIGNAL(timeout()), + this, SLOT(cleanCache()) ); + + TQSocketNotifier * rn4 = new TQSocketNotifier( ipv4Socket->socket(), + TQSocketNotifier::Read, + this, "dns IPv4 socket watcher" ); + ipv4Socket->setAddressReusable( FALSE ); + ipv4Socket->setBlocking( FALSE ); + connect( rn4, SIGNAL(activated(int)), SLOT(answer()) ); + +#if !defined (QT_NO_IPV6) + // Don't connect the IPv6 socket notifier if the host does not + // support IPv6. + if ( ipv6Socket->socket() != -1 ) { + TQSocketNotifier * rn6 = new TQSocketNotifier( ipv6Socket->socket(), + TQSocketNotifier::Read, + this, "dns IPv6 socket watcher" ); + + ipv6support = TRUE; + ipv6Socket->setAddressReusable( FALSE ); + ipv6Socket->setBlocking( FALSE ); + connect( rn6, SIGNAL(activated(int)), SLOT(answer()) ); + } +#endif + + if ( !ns ) + TQDns::doResInit(); + + // O(n*n) stuff here. but for 3 and 6, O(n*n) with a low k should + // be perfect. the point is to eliminate any duplicates that + // might be hidden in the lists. + TQPtrList<TQHostAddress> * ns = new TQPtrList<TQHostAddress>; + + ::ns->first(); + TQHostAddress * h; + while( (h=::ns->current()) != 0 ) { + ns->first(); + while( ns->current() != 0 && !(*ns->current() == *h) ) + ns->next(); + if ( !ns->current() ) { + ns->append( new TQHostAddress(*h) ); +#if defined(TQDNS_DEBUG) + qDebug( "using name server %s", h->toString().latin1() ); + } else { + qDebug( "skipping address %s", h->toString().latin1() ); +#endif + } + ::ns->next(); + } + + delete ::ns; + ::ns = ns; + ::ns->setAutoDelete( TRUE ); + + TQStrList * domains = new TQStrList( TRUE ); + + ::domains->first(); + const char * s; + while( (s=::domains->current()) != 0 ) { + domains->first(); + while( domains->current() != 0 && qstrcmp( domains->current(), s ) ) + domains->next(); + if ( !domains->current() ) { + domains->append( s ); +#if defined(TQDNS_DEBUG) + qDebug( "searching domain %s", s ); + } else { + qDebug( "skipping domain %s", s ); +#endif + } + ::domains->next(); + } + + delete ::domains; + ::domains = domains; + ::domains->setAutoDelete( TRUE ); +} + + +TQDnsManager::~TQDnsManager() +{ + if ( globalManager ) + globalManager = 0; + queries.setAutoDelete( TRUE ); + cache.setAutoDelete( TRUE ); + delete ipv4Socket; +#if !defined (QT_NO_IPV6) + delete ipv6Socket; +#endif +} + +static Q_UINT32 lastSweep = 0; + +void TQDnsManager::cleanCache() +{ + bool again = FALSE; + TQDictIterator<TQDnsDomain> it( cache ); + TQDnsDomain * d; + Q_UINT32 thisSweep = now(); +#if defined(TQDNS_DEBUG) + qDebug( "TQDnsManager::cleanCache(: Called, time is %u, last was %u", + thisSweep, lastSweep ); +#endif + + while( (d=it.current()) != 0 ) { + ++it; + d->sweep( thisSweep ); // after this, d may be empty + if ( !again ) + again = !d->isEmpty(); + } + if ( !again ) + delete this; + lastSweep = thisSweep; +} + + +void TQDnsManager::retransmit() +{ + const TQObject * o = sender(); + if ( o == 0 || globalManager == 0 || this != globalManager ) + return; + uint q = 0; + while( q < queries.size() && queries[q] != o ) + q++; + if ( q < queries.size() ) + transmitQuery( q ); +} + + +void TQDnsManager::answer() +{ + TQByteArray a( 16383 ); // large enough for anything, one suspects + + int r; +#if defined (QT_NO_IPV6) + r = ipv4Socket->readBlock(a.data(), a.size()); +#else + if (((TQSocketNotifier *)sender())->socket() == ipv4Socket->socket()) + r = ipv4Socket->readBlock(a.data(), a.size()); + else + r = ipv6Socket->readBlock(a.data(), a.size()); +#endif +#if defined(TQDNS_DEBUG) +#if !defined (QT_NO_IPV6) + qDebug("DNS Manager: answer arrived: %d bytes from %s:%d", r, + useIpv4Socket ? ipv4Socket->peerAddress().toString().ascii() + : ipv6Socket->peerAddress().toString().ascii(), + useIpv4Socket ? ipv4Socket->peerPort() : ipv6Socket->peerPort() ); +#else + qDebug("DNS Manager: answer arrived: %d bytes from %s:%d", r, + ipv4Socket->peerAddress().toString().ascii(), ipv4Socket->peerPort());; +#endif +#endif + if ( r < 12 ) + return; + // maybe we should check that the answer comes from port 53 on one + // of our name servers... + a.resize( r ); + + Q_UINT16 aid = (((Q_UINT8)a[0]) << 8) + ((Q_UINT8)a[1]); + uint i = 0; + while( i < queries.size() && + !( queries[i] && queries[i]->id == aid ) ) + i++; + if ( i == queries.size() ) { +#if defined(TQDNS_DEBUG) + qDebug( "DNS Manager: bad id (0x%04x) %d", aid, i ); +#endif + return; + } + + // at this point queries[i] is whatever we asked for. + + if ( ( (Q_UINT8)(a[2]) & 0x80 ) == 0 ) { +#if defined(TQDNS_DEBUG) + qDebug( "DNS Manager: received a query" ); +#endif + return; + } + + TQDnsQuery * q = queries[i]; + TQDnsAnswer answer( a, q ); + answer.parse(); + if ( answer.ok ) { + queries.take( i ); + answer.notify(); + delete q; + } +} + + +void TQDnsManager::transmitQuery( TQDnsQuery * query_ ) +{ + if ( !query_ ) + return; + + uint i = 0; + while( i < queries.size() && queries[i] != 0 ) + i++; + if ( i == queries.size() ) + queries.resize( i+1 ); + queries.insert( i, query_ ); + transmitQuery( i ); +} + + +void TQDnsManager::transmitQuery( int i ) +{ + if ( i < 0 || i >= (int)queries.size() ) + return; + TQDnsQuery * q = queries[i]; + + if ( q && q->step > 8 ) { + // okay, we've run out of retransmissions. we fake an NXDomain + // with a very short life time... + TQDnsAnswer answer( q ); + answer.notify(); + + if (globalManager == 0) + return; + + // and then get rid of the query + queries.take( i ); +#if defined(TQDNS_DEBUG) + qDebug( "DNS Manager: giving up on query 0x%04x", q->id ); +#endif + delete q; + TQTimer::singleShot( 0, TQDnsManager::manager(), SLOT(cleanCache()) ); + // and don't process anything more + return; + } + + if ( q && !q->dns || q->dns->isEmpty() ) + // noone currently wants the answer, so there's no point in + // retransmitting the query. we keep it, though. an answer may + // arrive for an earlier query transmission, and if it does we + // may benefit from caching the result. + return; + + TQByteArray p( 12 + q->l.length() + 2 + 4 ); + if ( p.size() > 500 ) + return; // way over the limit, so don't even try + + // header + // id + p[0] = (q->id & 0xff00) >> 8; + p[1] = q->id & 0x00ff; + p[2] = 1; // recursion desired, rest is 0 + p[3] = 0; // all is 0 + // one query + p[4] = 0; + p[5] = 1; + // no answers, name servers or additional data + p[6] = p[7] = p[8] = p[9] = p[10] = p[11] = 0; + + // the name is composed of several components. each needs to be + // written by itself... so we write... + // oh, and we assume that there's no funky characters in there. + int pp = 12; + uint lp = 0; + while( lp < q->l.length() ) { + int le = q->l.find( '.', lp ); + if ( le < 0 ) + le = q->l.length(); + TQString component = q->l.mid( lp, le-lp ); + p[pp++] = component.length(); + int cp; + for( cp=0; cp < (int)component.length(); cp++ ) + p[pp++] = component[cp].latin1(); + lp = le + 1; + } + // final null + p[pp++] = 0; + // query type + p[pp++] = 0; + switch( q->t ) { + case TQDns::A: + p[pp++] = 1; + break; + case TQDns::Aaaa: + p[pp++] = 28; + break; + case TQDns::Mx: + p[pp++] = 15; + break; + case TQDns::Srv: + p[pp++] = 33; + break; + case TQDns::Cname: + p[pp++] = 5; + break; + case TQDns::Ptr: + p[pp++] = 12; + break; + case TQDns::Txt: + p[pp++] = 16; + break; + default: + p[pp++] = (char)255; // any + break; + } + // query class (always internet) + p[pp++] = 0; + p[pp++] = 1; + + // if we have no name servers, we should regenerate ns in case + // name servers have recently been defined (like on windows, + // plugging/unplugging the network cable will change the name + // server entries) + if ( !ns || ns->isEmpty() ) + TQDns::doResInit(); + + if ( !ns || ns->isEmpty() ) { + // we don't find any name servers. We fake an NXDomain + // with a very short life time... + TQDnsAnswer answer( q ); + answer.notify(); + // and then get rid of the query + queries.take( i ); +#if defined(TQDNS_DEBUG) + qDebug( "DNS Manager: no DNS server found on query 0x%04x", q->id ); +#endif + delete q; + TQTimer::singleShot( 1000*10, TQDnsManager::manager(), SLOT(cleanCache()) ); + // and don't process anything more + return; + } + + TQHostAddress receiver = *ns->at( q->step % ns->count() ); + if (receiver.isIPv4Address()) + ipv4Socket->writeBlock( p.data(), pp, receiver, 53 ); +#if !defined (QT_NO_IPV6) + else + ipv6Socket->writeBlock( p.data(), pp, receiver, 53 ); +#endif +#if defined(TQDNS_DEBUG) + qDebug( "issuing query 0x%04x (%d) about %s type %d to %s", + q->id, q->step, q->l.ascii(), q->t, + ns->at( q->step % ns->count() )->toString().ascii() ); +#endif + if ( ns->count() > 1 && q->step == 0 && queries.count() == 1 ) { + // if it's the first time, and we don't have any other + // outstanding queries, send nonrecursive queries to the other + // name servers too. + p[2] = 0; + TQHostAddress * server; + while( (server=ns->next()) != 0 ) { + if (server->isIPv4Address()) + ipv4Socket->writeBlock( p.data(), pp, *server, 53 ); +#if !defined (QT_NO_IPV6) + else + ipv6Socket->writeBlock( p.data(), pp, *server, 53 ); +#endif +#if defined(TQDNS_DEBUG) + qDebug( "copying query to %s", server->toString().ascii() ); +#endif + } + } + q->step++; + // some testing indicates that normal dns queries take up to 0.6 + // seconds. the graph becomes steep around that point, and the + // number of errors rises... so it seems good to retry at that + // point. + q->start( q->step < ns->count() ? 800 : 1500, TRUE ); +} + + +TQDnsDomain * TQDnsManager::domain( const TQString & label ) +{ + TQDnsDomain * d = cache.find( label ); + if ( !d ) { + d = new TQDnsDomain( label ); + cache.insert( label, d ); + } + return d; +} + + +// +// +// the TQDnsDomain class looks after and coordinates queries for TQDnsRRs for +// each domain, and the cached TQDnsRRs. (A domain, in DNS terminology, is +// a node in the DNS. "no", "trolltech.com" and "lupinella.troll.no" are +// all domains.) +// +// + + +// this is ONLY to be called by TQDnsManager::domain(). noone else. +TQDnsDomain::TQDnsDomain( const TQString & label ) +{ + l = label; + rrs = 0; +} + + +TQDnsDomain::~TQDnsDomain() +{ + delete rrs; + rrs = 0; +} + + +void TQDnsDomain::add( const TQString & label, TQDnsRR * rr ) +{ + TQDnsDomain * d = TQDnsManager::manager()->domain( label ); + if ( !d->rrs ) { + d->rrs = new TQPtrList<TQDnsRR>; + d->rrs->setAutoDelete( TRUE ); + } + d->rrs->append( rr ); + rr->domain = d; +} + + +TQPtrList<TQDnsRR> * TQDnsDomain::cached( const TQDns * r ) +{ + TQPtrList<TQDnsRR> * l = new TQPtrList<TQDnsRR>; + + // test at first if you have to start a query at all + if ( r->recordType() == TQDns::A ) { + if ( r->label().lower() == "localhost" ) { + // undocumented hack. ipv4-specific. also, may be a memory + // leak? not sure. would be better to do this in doResInit(), + // anyway. + TQDnsRR *rrTmp = new TQDnsRR( r->label() ); + rrTmp->t = TQDns::A; + rrTmp->address = TQHostAddress( 0x7f000001 ); + rrTmp->current = TRUE; + l->append( rrTmp ); + return l; + } + TQHostAddress tmp; + if ( tmp.setAddress( r->label() ) ) { + TQDnsRR *rrTmp = new TQDnsRR( r->label() ); + if ( tmp.isIPv4Address() ) { + rrTmp->t = TQDns::A; + rrTmp->address = tmp; + rrTmp->current = TRUE; + l->append( rrTmp ); + } else { + rrTmp->nxdomain = TRUE; + } + return l; + } + } + if ( r->recordType() == TQDns::Aaaa ) { + TQHostAddress tmp; + if ( tmp.setAddress(r->label()) ) { + TQDnsRR *rrTmp = new TQDnsRR( r->label() ); + if ( tmp.isIPv6Address() ) { + rrTmp->t = TQDns::Aaaa; + rrTmp->address = tmp; + rrTmp->current = TRUE; + l->append( rrTmp ); + } else { + rrTmp->nxdomain = TRUE; + } + return l; + } + } + + // if you reach this point, you have to do the query + TQDnsManager * m = TQDnsManager::manager(); + TQStringList n = r->qualifiedNames(); + TQValueListIterator<TQString> it = n.begin(); + TQValueListIterator<TQString> end = n.end(); + bool nxdomain; + int cnamecount = 0; + while( it != end ) { + TQString s = *it++; + nxdomain = FALSE; +#if defined(TQDNS_DEBUG) + qDebug( "looking at cache for %s (%s %d)", + s.ascii(), r->label().ascii(), r->recordType() ); +#endif + TQDnsDomain * d = m->domain( s ); +#if defined(TQDNS_DEBUG) + qDebug( " - found %d RRs", d && d->rrs ? d->rrs->count() : 0 ); +#endif + if ( d->rrs ) + d->rrs->first(); + TQDnsRR * rr; + bool answer = FALSE; + while( d->rrs && (rr=d->rrs->current()) != 0 ) { + if ( rr->t == TQDns::Cname && r->recordType() != TQDns::Cname && + !rr->nxdomain && cnamecount < 16 ) { + // cname. if the code is ugly, that may just + // possibly be because the concept is. +#if defined(TQDNS_DEBUG) + qDebug( "found cname from %s to %s", + r->label().ascii(), rr->target.ascii() ); +#endif + s = rr->target; + d = m->domain( s ); + if ( d->rrs ) + d->rrs->first(); + it = end; + // we've elegantly moved over to whatever the cname + // pointed to. well, not elegantly. let's remember + // that we've done something, anyway, so we can't be + // fooled into an infinte loop as well. + cnamecount++; + } else { + if ( rr->t == r->recordType() ) { + if ( rr->nxdomain ) + nxdomain = TRUE; + else + answer = TRUE; + l->append( rr ); + if ( rr->deleteTime <= lastSweep ) { + // we're returning something that'll be + // deleted soon. we assume that if the client + // wanted it twice, it'll want it again, so we + // ask the name server again right now. + TQDnsQuery * query = new TQDnsQuery; + query->started = now(); + query->id = ++::id; + query->t = rr->t; + query->l = rr->domain->name(); + // note that here, we don't bother about + // notification. but we do bother about + // timeouts: we make sure to use high timeouts + // and few tramsissions. + query->step = ns->count(); + TQObject::connect( query, SIGNAL(timeout()), + TQDnsManager::manager(), + SLOT(retransmit()) ); + TQDnsManager::manager()->transmitQuery( query ); + } + } + d->rrs->next(); + } + } + // if we found a positive result, return tquickly + if ( answer && l->count() ) { +#if defined(TQDNS_DEBUG) + qDebug( "found %d records for %s", + l->count(), r->label().ascii() ); + l->first(); + while( l->current() ) { + qDebug( " type %d target %s address %s", + l->current()->t, + l->current()->target.latin1(), + l->current()->address.toString().latin1() ); + l->next(); + } +#endif + l->first(); + return l; + } + +#if defined(TQDNS_DEBUG) + if ( nxdomain ) + qDebug( "found NXDomain %s", s.ascii() ); +#endif + + if ( !nxdomain ) { + // if we didn't, and not a negative result either, perhaps + // we need to transmit a query. + uint q = 0; + while ( q < m->queries.size() && + ( m->queries[q] == 0 || + m->queries[q]->t != r->recordType() || + m->queries[q]->l != s ) ) + q++; + // we haven't done it before, so maybe we should. but + // wait - if it's an unqualified name, only ask when all + // the other alternatives are exhausted. + if ( q == m->queries.size() && ( s.find( '.' ) >= 0 || + l->count() >= n.count()-1 ) ) { + TQDnsQuery * query = new TQDnsQuery; + query->started = now(); + query->id = ++::id; + query->t = r->recordType(); + query->l = s; + query->dns->replace( (void*)r, (void*)r ); + TQObject::connect( query, SIGNAL(timeout()), + TQDnsManager::manager(), SLOT(retransmit()) ); + TQDnsManager::manager()->transmitQuery( query ); + } else if ( q < m->queries.size() ) { + // if we've found an earlier query for the same + // domain/type, subscribe to its answer + m->queries[q]->dns->replace( (void*)r, (void*)r ); + } + } + } + l->first(); + return l; +} + + +void TQDnsDomain::sweep( Q_UINT32 thisSweep ) +{ + if ( !rrs ) + return; + + TQDnsRR * rr; + rrs->first(); + while( (rr=rrs->current()) != 0 ) { + if ( !rr->deleteTime ) + rr->deleteTime = thisSweep; // will hit next time around + +#if defined(TQDNS_DEBUG) + qDebug( "TQDns::sweep: %s type %d expires %u %u - %s / %s", + rr->domain->name().latin1(), rr->t, + rr->expireTime, rr->deleteTime, + rr->target.latin1(), rr->address.toString().latin1()); +#endif + if ( rr->current == FALSE || + rr->t == TQDns::None || + rr->deleteTime <= thisSweep || + rr->expireTime <= thisSweep ) + rrs->remove(); + else + rrs->next(); + } + + if ( rrs->isEmpty() ) { + delete rrs; + rrs = 0; + } +} + + + + +// the itsy-bitsy little socket class I don't really need except for +// so I can subclass and reimplement the slots. + + +TQDnsSocket::TQDnsSocket( TQObject * parent, const char * name ) + : TQObject( parent, name ) +{ + // nothing +} + + +TQDnsSocket::~TQDnsSocket() +{ + // nothing +} + + +void TQDnsSocket::cleanCache() +{ + // nothing +} + + +void TQDnsSocket::retransmit() +{ + // nothing +} + + +void TQDnsSocket::answer() +{ + // nothing +} + + +/*! + \class TQDns qdns.h + \brief The TQDns class provides asynchronous DNS lookups. +\if defined(commercial) + It is part of the <a href="commercialeditions.html">TQt Enterprise Edition</a>. +\endif + + \module network + \ingroup io + + Both Windows and Unix provide synchronous DNS lookups; Windows + provides some asynchronous support too. At the time of writing + neither operating system provides asynchronous support for + anything other than hostname-to-address mapping. + + TQDns rectifies this shortcoming, by providing asynchronous caching + lookups for the record types that we expect modern GUI + applications to need in the near future. + + The class is \e not straightforward to use (although it is much + simpler than the native APIs); TQSocket provides much easier to use + TCP connection facilities. The aim of TQDns is to provide a correct + and small API to the DNS and nothing more. (We use "correctness" + to mean that the DNS information is correctly cached, and + correctly timed out.) + + The API comprises a constructor, functions to set the DNS node + (the domain in DNS terminology) and record type (setLabel() and + setRecordType()), the corresponding get functions, an isWorking() + function to determine whether TQDns is working or reading, a + resultsReady() signal and query functions for the result. + + There is one query function for each RecordType, namely + addresses(), mailServers(), servers(), hostNames() and texts(). + There are also two generic query functions: canonicalName() + returns the name you'll presumably end up using (the exact meaning + of this depends on the record type) and qualifiedNames() returns a + list of the fully qualified names label() maps to. + + \sa TQSocket +*/ + +/*! + Constructs a DNS query object with invalid settings for both the + label and the search type. +*/ + +TQDns::TQDns() +{ + d = new TQDnsPrivate; + t = None; +} + + + + +/*! + Constructs a DNS query object that will return record type \a rr + information about \a label. + + The DNS lookup is started the next time the application enters the + event loop. When the result is found the signal resultsReady() is + emitted. + + \a rr defaults to \c A, IPv4 addresses. +*/ + +TQDns::TQDns( const TQString & label, RecordType rr ) +{ + d = new TQDnsPrivate; + t = rr; + setLabel( label ); + setStartQueryTimer(); // start query the next time we enter event loop +} + + + +/*! + Constructs a DNS query object that will return record type \a rr + information about host address \a address. The label is set to the + IN-ADDR.ARPA domain name. This is useful in combination with the + \c Ptr record type (e.g. if you want to look up a hostname for a + given address). + + The DNS lookup is started the next time the application enters the + event loop. When the result is found the signal resultsReady() is + emitted. + + \a rr defaults to \c Ptr, that maps addresses to hostnames. +*/ + +TQDns::TQDns( const TQHostAddress & address, RecordType rr ) +{ + d = new TQDnsPrivate; + t = rr; + setLabel( address ); + setStartQueryTimer(); // start query the next time we enter event loop +} + + + + +/*! + Destroys the DNS query object and frees its allocated resources. +*/ + +TQDns::~TQDns() +{ + if ( globalManager ) { + uint q = 0; + TQDnsManager * m = globalManager; + while( q < m->queries.size() ) { + TQDnsQuery * query=m->queries[q]; + if ( query && query->dns ) + (void)query->dns->take( (void*) this ); + q++; + } + + } + + delete d; + d = 0; +} + + + + +/*! + Sets this DNS query object to query for information about \a + label. + + This does not change the recordType(), but its isWorking() status + will probably change as a result. + + The DNS lookup is started the next time the application enters the + event loop. When the result is found the signal resultsReady() is + emitted. +*/ + +void TQDns::setLabel( const TQString & label ) +{ + l = label; + d->noNames = FALSE; + + // construct a list of qualified names + n.clear(); + if ( l.length() > 1 && l[(int)l.length()-1] == '.' ) { + n.append( l.left( l.length()-1 ).lower() ); + } else { + int i = l.length(); + int dots = 0; + const int maxDots = 2; + while( i && dots < maxDots ) { + if ( l[--i] == '.' ) + dots++; + } + if ( dots < maxDots ) { + (void)TQDnsManager::manager(); // create a TQDnsManager, if it is not already there + TQStrListIterator it( *domains ); + const char * dom; + while( (dom=it.current()) != 0 ) { + ++it; + n.append( l.lower() + "." + dom ); + } + } + n.append( l.lower() ); + } + +#if defined(Q_DNS_SYNCHRONOUS) + if ( d->noEventLoop ) { + doSynchronousLookup(); + } else { + setStartQueryTimer(); // start query the next time we enter event loop + } +#else + setStartQueryTimer(); // start query the next time we enter event loop +#endif +#if defined(TQDNS_DEBUG) + qDebug( "TQDns::setLabel: %d address(es) for %s", n.count(), l.ascii() ); + int i = 0; + for( i = 0; i < (int)n.count(); i++ ) + qDebug( "TQDns::setLabel: %d: %s", i, n[i].ascii() ); +#endif +} + + +/*! + \overload + + Sets this DNS query object to query for information about the host + address \a address. The label is set to the IN-ADDR.ARPA domain + name. This is useful in combination with the \c Ptr record type + (e.g. if you want to look up a hostname for a given address). +*/ + +void TQDns::setLabel( const TQHostAddress & address ) +{ + setLabel( toInAddrArpaDomain( address ) ); +} + + +/*! + \fn TQStringList TQDns::qualifiedNames() const + + Returns a list of the fully qualified names label() maps to. + + Note that if you want to iterate over the list, you should iterate + over a copy, e.g. + \code + TQStringList list = myDns.qualifiedNames(); + TQStringList::Iterator it = list.begin(); + while( it != list.end() ) { + myProcessing( *it ); + ++it; + } + \endcode + +*/ + + +/*! + \fn TQString TQDns::label() const + + Returns the domain name for which this object returns information. + + \sa setLabel() +*/ + +/*! + \enum TQDns::RecordType + + This enum type defines the record types TQDns can handle. The DNS + provides many more; these are the ones we've judged to be in + current use, useful for GUI programs and important enough to + support right away: + + \value None No information. This exists only so that TQDns can + have a default. + + \value A IPv4 addresses. By far the most common type. + + \value Aaaa IPv6 addresses. So far mostly unused. + + \value Mx Mail eXchanger names. Used for mail delivery. + + \value Srv SeRVer names. Generic record type for finding + servers. So far mostly unused. + + \value Cname Canonical names. Maps from nicknames to the true + name (the canonical name) for a host. + + \value Ptr name PoinTeRs. Maps from IPv4 or IPv6 addresses to hostnames. + + \value Txt arbitrary TeXT for domains. + + We expect that some support for the + \link http://www.dns.net/dnsrd/rfc/rfc2535.html RFC-2535 \endlink + extensions will be added in future versions. +*/ + +/*! + Sets this object to query for record type \a rr records. + + The DNS lookup is started the next time the application enters the + event loop. When the result is found the signal resultsReady() is + emitted. + + \sa RecordType +*/ + +void TQDns::setRecordType( RecordType rr ) +{ + t = rr; + d->noNames = FALSE; + setStartQueryTimer(); // start query the next time we enter event loop +} + +/*! + \internal + + Private slot for starting the query. +*/ +void TQDns::startQuery() +{ + // isWorking() starts the query (if necessary) + if ( !isWorking() ) + emit resultsReady(); +} + +/*! + The three functions TQDns::TQDns(TQString, RecordType), + TQDns::setLabel() and TQDns::setRecordType() may start a DNS lookup. + This function handles setting up the single shot timer. +*/ +void TQDns::setStartQueryTimer() +{ +#if defined(Q_DNS_SYNCHRONOUS) + if ( !d->queryTimer && !d->noEventLoop ) +#else + if ( !d->queryTimer ) +#endif + { + // start the query the next time we enter event loop + d->queryTimer = new TQTimer( this ); + connect( d->queryTimer, SIGNAL(timeout()), + this, SLOT(startQuery()) ); + d->queryTimer->start( 0, TRUE ); + } +} + +/* + Transforms the host address \a address to the IN-ADDR.ARPA domain + name. Returns something indeterminate if you're sloppy or + naughty. This function has an IPv4-specific name, but works for + IPv6 too. +*/ +TQString TQDns::toInAddrArpaDomain( const TQHostAddress &address ) +{ + TQString s; + if ( address.isNull() ) { + // if the address isn't valid, neither of the other two make + // cases make sense. better to just return. + } else if ( address.isIp4Addr() ) { + Q_UINT32 i = address.ip4Addr(); + s.sprintf( "%d.%d.%d.%d.IN-ADDR.ARPA", + i & 0xff, (i >> 8) & 0xff, (i>>16) & 0xff, (i>>24) & 0xff ); + } else { + // RFC 3152. (1886 is deprecated, and clients no longer need to + // support it, in practice). + Q_IPV6ADDR i = address.toIPv6Address(); + s = "ip6.arpa"; + uint b = 0; + while( b < 16 ) { + s = TQString::number( i.c[b]%16, 16 ) + "." + + TQString::number( i.c[b]/16, 16 ) + "." + s; + b++; + } + } + return s; +} + + +/*! + \fn TQDns::RecordType TQDns::recordType() const + + Returns the record type of this DNS query object. + + \sa setRecordType() RecordType +*/ + +/*! + \fn void TQDns::resultsReady() + + This signal is emitted when results are available for one of the + qualifiedNames(). +*/ + +/*! + Returns TRUE if TQDns is doing a lookup for this object (i.e. if it + does not already have the necessary information); otherwise + returns FALSE. + + TQDns emits the resultsReady() signal when the status changes to FALSE. +*/ + +bool TQDns::isWorking() const +{ +#if defined(TQDNS_DEBUG) + qDebug( "TQDns::isWorking (%s, %d)", l.ascii(), t ); +#endif + if ( t == None ) + return FALSE; + +#if defined(Q_DNS_SYNCHRONOUS) + if ( d->noEventLoop ) + return TRUE; +#endif + + TQPtrList<TQDnsRR> * ll = TQDnsDomain::cached( this ); + Q_LONG queries = n.count(); + while( ll->current() != 0 ) { + if ( ll->current()->nxdomain ) { + queries--; + } else { + delete ll; + return FALSE; + } + ll->next(); + } + delete ll; + + if ( queries <= 0 ) + return FALSE; + if ( d->noNames ) + return FALSE; + return TRUE; +} + + +/*! + Returns a list of the addresses for this name if this TQDns object + has a recordType() of \c TQDns::A or \c TQDns::Aaaa and the answer + is available; otherwise returns an empty list. + + As a special case, if label() is a valid numeric IP address, this + function returns that address. + + Note that if you want to iterate over the list, you should iterate + over a copy, e.g. + \code + TQValueList<TQHostAddress> list = myDns.addresses(); + TQValueList<TQHostAddress>::Iterator it = list.begin(); + while( it != list.end() ) { + myProcessing( *it ); + ++it; + } + \endcode + +*/ + +TQValueList<TQHostAddress> TQDns::addresses() const +{ +#if defined(TQDNS_DEBUG) + qDebug( "TQDns::addresses (%s)", l.ascii() ); +#endif + TQValueList<TQHostAddress> result; + if ( t != A && t != Aaaa ) + return result; + + TQPtrList<TQDnsRR> * cached = TQDnsDomain::cached( this ); + + TQDnsRR * rr; + while( (rr=cached->current()) != 0 ) { + if ( rr->current && !rr->nxdomain ) + result.append( rr->address ); + cached->next(); + } + delete cached; + return result; +} + + +/*! + \class TQDns::MailServer + \brief The TQDns::MailServer class is described in TQDns::mailServers(). +\if defined(commercial) + It is part of the <a href="commercialeditions.html">TQt Enterprise Edition</a>. +\endif + + \ingroup io + + \internal +*/ + +/*! + Returns a list of mail servers if the record type is \c Mx. The + class \c TQDns::MailServer contains the following public variables: + \list + \i TQString TQDns::MailServer::name + \i Q_UINT16 TQDns::MailServer::priority + \endlist + + Note that if you want to iterate over the list, you should iterate + over a copy, e.g. + \code + TQValueList<TQDns::MailServer> list = myDns.mailServers(); + TQValueList<TQDns::MailServer>::Iterator it = list.begin(); + while( it != list.end() ) { + myProcessing( *it ); + ++it; + } + \endcode + +*/ +TQValueList<TQDns::MailServer> TQDns::mailServers() const +{ +#if defined(TQDNS_DEBUG) + qDebug( "TQDns::mailServers (%s)", l.ascii() ); +#endif + TQValueList<TQDns::MailServer> result; + if ( t != Mx ) + return result; + + TQPtrList<TQDnsRR> * cached = TQDnsDomain::cached( this ); + + TQDnsRR * rr; + while( (rr=cached->current()) != 0 ) { + if ( rr->current && !rr->nxdomain ) { + MailServer ms( rr->target, rr->priority ); + result.append( ms ); + } + cached->next(); + } + delete cached; + return result; +} + + +/*! + \class TQDns::Server + \brief The TQDns::Server class is described in TQDns::servers(). +\if defined(commercial) + It is part of the <a href="commercialeditions.html">TQt Enterprise Edition</a>. +\endif + + \ingroup io + + \internal +*/ + +/*! + Returns a list of servers if the record type is \c Srv. The class + \c TQDns::Server contains the following public variables: + \list + \i TQString TQDns::Server::name + \i Q_UINT16 TQDns::Server::priority + \i Q_UINT16 TQDns::Server::weight + \i Q_UINT16 TQDns::Server::port + \endlist + + Note that if you want to iterate over the list, you should iterate + over a copy, e.g. + \code + TQValueList<TQDns::Server> list = myDns.servers(); + TQValueList<TQDns::Server>::Iterator it = list.begin(); + while( it != list.end() ) { + myProcessing( *it ); + ++it; + } + \endcode +*/ +TQValueList<TQDns::Server> TQDns::servers() const +{ +#if defined(TQDNS_DEBUG) + qDebug( "TQDns::servers (%s)", l.ascii() ); +#endif + TQValueList<TQDns::Server> result; + if ( t != Srv ) + return result; + + TQPtrList<TQDnsRR> * cached = TQDnsDomain::cached( this ); + + TQDnsRR * rr; + while( (rr=cached->current()) != 0 ) { + if ( rr->current && !rr->nxdomain ) { + Server s( rr->target, rr->priority, rr->weight, rr->port ); + result.append( s ); + } + cached->next(); + } + delete cached; + return result; +} + + +/*! + Returns a list of host names if the record type is \c Ptr. + + Note that if you want to iterate over the list, you should iterate + over a copy, e.g. + \code + TQStringList list = myDns.hostNames(); + TQStringList::Iterator it = list.begin(); + while( it != list.end() ) { + myProcessing( *it ); + ++it; + } + \endcode + +*/ +TQStringList TQDns::hostNames() const +{ +#if defined(TQDNS_DEBUG) + qDebug( "TQDns::hostNames (%s)", l.ascii() ); +#endif + TQStringList result; + if ( t != Ptr ) + return result; + + TQPtrList<TQDnsRR> * cached = TQDnsDomain::cached( this ); + + TQDnsRR * rr; + while( (rr=cached->current()) != 0 ) { + if ( rr->current && !rr->nxdomain ) { + TQString str( rr->target ); + result.append( str ); + } + cached->next(); + } + delete cached; + return result; +} + + +/*! + Returns a list of texts if the record type is \c Txt. + + Note that if you want to iterate over the list, you should iterate + over a copy, e.g. + \code + TQStringList list = myDns.texts(); + TQStringList::Iterator it = list.begin(); + while( it != list.end() ) { + myProcessing( *it ); + ++it; + } + \endcode +*/ +TQStringList TQDns::texts() const +{ +#if defined(TQDNS_DEBUG) + qDebug( "TQDns::texts (%s)", l.ascii() ); +#endif + TQStringList result; + if ( t != Txt ) + return result; + + TQPtrList<TQDnsRR> * cached = TQDnsDomain::cached( this ); + + TQDnsRR * rr; + while( (rr=cached->current()) != 0 ) { + if ( rr->current && !rr->nxdomain ) { + TQString str( rr->text ); + result.append( str ); + } + cached->next(); + } + delete cached; + return result; +} + + +/*! + Returns the canonical name for this DNS node. (This works + regardless of what recordType() is set to.) + + If the canonical name isn't known, this function returns a null + string. + + The canonical name of a DNS node is its full name, or the full + name of the target of its CNAME. For example, if l.trolltech.com + is a CNAME to lillian.troll.no, and the search path for TQDns is + "trolltech.com", then the canonical name for all of "lillian", + "l", "lillian.troll.no." and "l.trolltech.com" is + "lillian.troll.no.". +*/ + +TQString TQDns::canonicalName() const +{ + // the cname should work regardless of the recordType(), so set the record + // type temporarily to cname when you look at the cache + TQDns *that = (TQDns*) this; // mutable function + RecordType oldType = t; + that->t = Cname; + TQPtrList<TQDnsRR> * cached = TQDnsDomain::cached( that ); + that->t = oldType; + + TQDnsRR * rr; + while( (rr=cached->current()) != 0 ) { + if ( rr->current && !rr->nxdomain && rr->domain ) { + delete cached; + return rr->target; + } + cached->next(); + } + delete cached; + return TQString::null; +} + +#if defined(Q_DNS_SYNCHRONOUS) +/*! \reimp +*/ +void TQDns::connectNotify( const char *signal ) +{ + if ( d->noEventLoop && qstrcmp(signal,SIGNAL(resultsReady()) )==0 ) { + doSynchronousLookup(); + } +} +#endif + +#if defined(Q_OS_WIN32) || defined(Q_OS_CYGWIN) + +#if defined(Q_DNS_SYNCHRONOUS) +void TQDns::doSynchronousLookup() +{ + // ### not implemented yet +} +#endif + +// the following typedefs are needed for GetNetworkParams() API call +#ifndef IP_TYPES_INCLUDED +#define MAX_HOSTNAME_LEN 128 +#define MAX_DOMAIN_NAME_LEN 128 +#define MAX_SCOPE_ID_LEN 256 +typedef struct { + char String[4 * 4]; +} IP_ADDRESS_STRING, *PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING; +typedef struct _IP_ADDR_STRING { + struct _IP_ADDR_STRING* Next; + IP_ADDRESS_STRING IpAddress; + IP_MASK_STRING IpMask; + DWORD Context; +} IP_ADDR_STRING, *PIP_ADDR_STRING; +typedef struct { + char HostName[MAX_HOSTNAME_LEN + 4] ; + char DomainName[MAX_DOMAIN_NAME_LEN + 4]; + PIP_ADDR_STRING CurrentDnsServer; + IP_ADDR_STRING DnsServerList; + UINT NodeType; + char ScopeId[MAX_SCOPE_ID_LEN + 4]; + UINT EnableRouting; + UINT EnableProxy; + UINT EnableDns; +} FIXED_INFO, *PFIXED_INFO; +#endif +typedef DWORD (WINAPI *GNP)( PFIXED_INFO, PULONG ); + +// ### FIXME: this code is duplicated in qfiledialog.cpp +static TQString getWindowsRegString( HKEY key, const TQString &subKey ) +{ + TQString s; + QT_WA( { + char buf[1024]; + DWORD bsz = sizeof(buf); + int r = RegQueryValueEx( key, (TCHAR*)subKey.ucs2(), 0, 0, (LPBYTE)buf, &bsz ); + if ( r == ERROR_SUCCESS ) { + s = TQString::fromUcs2( (unsigned short *)buf ); + } else if ( r == ERROR_MORE_DATA ) { + char *ptr = new char[bsz+1]; + r = RegQueryValueEx( key, (TCHAR*)subKey.ucs2(), 0, 0, (LPBYTE)ptr, &bsz ); + if ( r == ERROR_SUCCESS ) + s = ptr; + delete [] ptr; + } + } , { + char buf[512]; + DWORD bsz = sizeof(buf); + int r = RegQueryValueExA( key, subKey.local8Bit(), 0, 0, (LPBYTE)buf, &bsz ); + if ( r == ERROR_SUCCESS ) { + s = buf; + } else if ( r == ERROR_MORE_DATA ) { + char *ptr = new char[bsz+1]; + r = RegQueryValueExA( key, subKey.local8Bit(), 0, 0, (LPBYTE)ptr, &bsz ); + if ( r == ERROR_SUCCESS ) + s = ptr; + delete [] ptr; + } + } ); + return s; +} + +static bool getDnsParamsFromRegistry( const TQString &path, + TQString *domainName, TQString *nameServer, TQString *searchList ) +{ + HKEY k; + int r; + QT_WA( { + r = RegOpenKeyEx( HKEY_LOCAL_MACHINE, + (TCHAR*)path.ucs2(), + 0, KEY_READ, &k ); + } , { + r = RegOpenKeyExA( HKEY_LOCAL_MACHINE, + path, + 0, KEY_READ, &k ); + } ); + + if ( r == ERROR_SUCCESS ) { + *domainName = getWindowsRegString( k, "DhcpDomain" ); + if ( domainName->isEmpty() ) + *domainName = getWindowsRegString( k, "Domain" ); + + *nameServer = getWindowsRegString( k, "DhcpNameServer" ); + if ( nameServer->isEmpty() ) + *nameServer = getWindowsRegString( k, "NameServer" ); + + *searchList = getWindowsRegString( k, "SearchList" ); + } + RegCloseKey( k ); + return r == ERROR_SUCCESS; +} + +void TQDns::doResInit() +{ + char separator = 0; + + if ( ns ) + delete ns; + ns = new TQPtrList<TQHostAddress>; + ns->setAutoDelete( TRUE ); + domains = new TQStrList( TRUE ); + domains->setAutoDelete( TRUE ); + + TQString domainName, nameServer, searchList; + + bool gotNetworkParams = FALSE; + // try the API call GetNetworkParams() first and use registry lookup only + // as a fallback +#ifdef Q_OS_TEMP + HINSTANCE hinstLib = LoadLibraryW( L"iphlpapi" ); +#else + HINSTANCE hinstLib = LoadLibraryA( "iphlpapi" ); +#endif + if ( hinstLib != 0 ) { +#ifdef Q_OS_TEMP + GNP getNetworkParams = (GNP) GetProcAddressW( hinstLib, L"GetNetworkParams" ); +#else + GNP getNetworkParams = (GNP) GetProcAddress( hinstLib, "GetNetworkParams" ); +#endif + if ( getNetworkParams != 0 ) { + ULONG l = 0; + DWORD res; + res = getNetworkParams( 0, &l ); + if ( res == ERROR_BUFFER_OVERFLOW ) { + FIXED_INFO *finfo = (FIXED_INFO*)new char[l]; + res = getNetworkParams( finfo, &l ); + if ( res == ERROR_SUCCESS ) { + domainName = finfo->DomainName; + nameServer = ""; + IP_ADDR_STRING *dnsServer = &finfo->DnsServerList; + while ( dnsServer != 0 ) { + nameServer += dnsServer->IpAddress.String; + dnsServer = dnsServer->Next; + if ( dnsServer != 0 ) + nameServer += " "; + } + searchList = ""; + separator = ' '; + gotNetworkParams = TRUE; + } + delete[] finfo; + } + } + FreeLibrary( hinstLib ); + } + if ( !gotNetworkParams ) { + if ( getDnsParamsFromRegistry( + TQString( "System\\CurrentControlSet\\Services\\Tcpip\\Parameters" ), + &domainName, &nameServer, &searchList )) { + // for NT + separator = ' '; + } else if ( getDnsParamsFromRegistry( + TQString( "System\\CurrentControlSet\\Services\\VxD\\MSTCP" ), + &domainName, &nameServer, &searchList )) { + // for 95/98 + separator = ','; + } else { + // Could not access the TCP/IP parameters + domainName = ""; + nameServer = "127.0.0.1"; + searchList = ""; + separator = ' '; + } + } + + nameServer = nameServer.simplifyWhiteSpace(); + int first, last; + if ( !nameServer.isEmpty() ) { + first = 0; + do { + last = nameServer.find( separator, first ); + if ( last < 0 ) + last = nameServer.length(); + TQDns tmp( nameServer.mid( first, last-first ), TQDns::A ); + TQValueList<TQHostAddress> address = tmp.addresses(); + Q_LONG i = address.count(); + while( i ) + ns->append( new TQHostAddress(address[--i]) ); + first = last+1; + } while( first < (int)nameServer.length() ); + } + + searchList = searchList + " " + domainName; + searchList = searchList.simplifyWhiteSpace().lower(); + first = 0; + do { + last = searchList.find( separator, first ); + if ( last < 0 ) + last = searchList.length(); + domains->append( qstrdup( searchList.mid( first, last-first ) ) ); + first = last+1; + } while( first < (int)searchList.length() ); +} + +#elif defined(Q_OS_UNIX) + +#if defined(Q_DNS_SYNCHRONOUS) +void TQDns::doSynchronousLookup() +{ + if ( t!=None && !l.isEmpty() ) { + TQValueListIterator<TQString> it = n.begin(); + TQValueListIterator<TQString> end = n.end(); + int type; + switch( t ) { + case TQDns::A: + type = 1; + break; + case TQDns::Aaaa: + type = 28; + break; + case TQDns::Mx: + type = 15; + break; + case TQDns::Srv: + type = 33; + break; + case TQDns::Cname: + type = 5; + break; + case TQDns::Ptr: + type = 12; + break; + case TQDns::Txt: + type = 16; + break; + default: + type = (char)255; // any + break; + } + while( it != end ) { + TQString s = *it; + it++; + TQByteArray ba( 512 ); + int len = res_search( s.latin1(), 1, type, (uchar*)ba.data(), ba.size() ); + if ( len > 0 ) { + ba.resize( len ); + + TQDnsQuery * query = new TQDnsQuery; + query->started = now(); + query->id = ++::id; + query->t = t; + query->l = s; + TQDnsAnswer a( ba, query ); + a.parse(); + } else if ( len == -1 ) { + // res_search error + } + } + emit resultsReady(); + } +} +#endif + +#if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 3))) +#define Q_MODERN_RES_API +#else +#endif + +void TQDns::doResInit() +{ + if ( ns ) + return; + ns = new TQPtrList<TQHostAddress>; + ns->setAutoDelete( TRUE ); + domains = new TQStrList( TRUE ); + domains->setAutoDelete( TRUE ); + + // read resolv.conf manually. + TQFile resolvConf("/etc/resolv.conf"); + if (resolvConf.open(IO_ReadOnly)) { + TQTextStream stream( &resolvConf ); + TQString line; + + while ( !stream.atEnd() ) { + line = stream.readLine(); + TQStringList list = TQStringList::split( " ", line ); + if ( line.startsWith( "#" ) || list.count() < 2 ) + continue; + const TQString type = list[0].lower(); + + if ( type == "nameserver" ) { + TQHostAddress *address = new TQHostAddress(); + if ( address->setAddress( TQString(list[1]) ) ) { + // only add ipv6 addresses from resolv.conf if + // this host supports ipv6. + if ( address->isIPv4Address() || ipv6support ) + ns->append( address ); + else + delete address; + } else { + delete address; + } + } else if ( type == "search" ) { + TQStringList srch = TQStringList::split( " ", list[1] ); + for ( TQStringList::Iterator i = srch.begin(); i != srch.end(); ++i ) + domains->append( (*i).lower() ); + + } else if ( type == "domain" ) { + domains->append( list[1].lower() ); + } + } + } + + if (ns->isEmpty()) { +#if defined(Q_MODERN_RES_API) + struct __res_state res; + res_ninit( &res ); + int i; + // find the name servers to use + for( i=0; i < MAXNS && i < res.nscount; i++ ) + ns->append( new TQHostAddress( ntohl( res.nsaddr_list[i].sin_addr.s_addr ) ) ); +# if defined(MAXDFLSRCH) + for( i=0; i < MAXDFLSRCH; i++ ) { + if ( res.dnsrch[i] && *(res.dnsrch[i]) ) + domains->append( TQString::fromLatin1( res.dnsrch[i] ).lower() ); + else + break; + } +# endif + if ( *res.defdname ) + domains->append( TQString::fromLatin1( res.defdname ).lower() ); +#else + qdns_res_init(); + int i; + // find the name servers to use + for( i=0; i < MAXNS && i < _res.nscount; i++ ) + ns->append( new TQHostAddress( ntohl( _res.nsaddr_list[i].sin_addr.s_addr ) ) ); +# if defined(MAXDFLSRCH) + for( i=0; i < MAXDFLSRCH; i++ ) { + if ( _res.dnsrch[i] && *(_res.dnsrch[i]) ) + domains->append( TQString::fromLatin1( _res.dnsrch[i] ).lower() ); + else + break; + } +# endif + if ( *_res.defdname ) + domains->append( TQString::fromLatin1( _res.defdname ).lower() ); +#endif + + // the code above adds "0.0.0.0" as a name server at the slightest + // hint of trouble. so remove those again. + ns->first(); + while( ns->current() ) { + if ( ns->current()->isNull() ) + delete ns->take(); + else + ns->next(); + } + } + + TQFile hosts( TQString::fromLatin1( "/etc/hosts" ) ); + if ( hosts.open( IO_ReadOnly ) ) { + // read the /etc/hosts file, creating long-life A and PTR RRs + // for the things we find. + TQTextStream i( &hosts ); + TQString line; + while( !i.atEnd() ) { + line = i.readLine().simplifyWhiteSpace().lower(); + uint n = 0; + while( n < line.length() && line[(int)n] != '#' ) + n++; + line.truncate( n ); + n = 0; + while( n < line.length() && !line[(int)n].isSpace() ) + n++; + TQString ip = line.left( n ); + TQHostAddress a; + a.setAddress( ip ); + if ( ( a.isIPv4Address() || a.isIPv6Address() ) && !a.isNull() ) { + bool first = TRUE; + line = line.mid( n+1 ); + n = 0; + while( n < line.length() && !line[(int)n].isSpace() ) + n++; + TQString hostname = line.left( n ); + // ### in case of bad syntax, hostname is invalid. do we care? + if ( n ) { + TQDnsRR * rr = new TQDnsRR( hostname ); + if ( a.isIPv4Address() ) + rr->t = TQDns::A; + else + rr->t = TQDns::Aaaa; + rr->address = a; + rr->deleteTime = UINT_MAX; + rr->expireTime = UINT_MAX; + rr->current = TRUE; + if ( first ) { + first = FALSE; + TQDnsRR * ptr = new TQDnsRR( TQDns::toInAddrArpaDomain( a ) ); + ptr->t = TQDns::Ptr; + ptr->target = hostname; + ptr->deleteTime = UINT_MAX; + ptr->expireTime = UINT_MAX; + ptr->current = TRUE; + } + } + } + } + } +} + +#endif + +#endif // QT_NO_DNS |