1
/****************************************************************************
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
5
** This file is part of the Qt 3 compatibility classes of the Qt Toolkit.
7
** This file may be distributed under the terms of the Q Public License
8
** as defined by Trolltech AS of Norway and appearing in the file
9
** LICENSE.QPL included in the packaging of this file.
11
** This file may be distributed and/or modified under the terms of the
12
** GNU General Public License version 2 as published by the Free Software
13
** Foundation and appearing in the file LICENSE.GPL included in the
14
** packaging of this file.
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
17
** information about Qt Commercial License Agreements.
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
21
** Contact info@trolltech.com if any conditions of this licensing are
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27
****************************************************************************/
29
#include "qplatformdefs.h"
30
#include "qbytearray.h"
32
# include "qt_windows.h"
34
# include <sys/types.h>
35
# include <netinet/in.h>
36
# include <arpa/nameser.h>
38
extern "C" int res_init();
41
// POSIX Large File Support redefines open -> open64
46
// POSIX Large File Support redefines truncate -> truncate64
51
// Solaris redefines connect -> __xnet_connect with _XOPEN_SOURCE_EXTENDED.
56
// UnixWare 7 redefines socket -> _socket
65
#include "qdatetime.h"
67
#include "q3ptrlist.h"
70
#include "qapplication.h"
71
#include "q3ptrvector.h"
72
#include "q3strlist.h"
73
#include "q3ptrdict.h"
75
#include "qtextstream.h"
76
#include "q3socketdevice.h"
77
#include "q3cleanuphandler.h"
80
#include "../3rdparty/dlcompat/dlfcn.h"
85
static Q_UINT16 id; // ### seeded started by now()
88
static QDateTime * originOfTime = 0;
90
static Q3CleanupHandler<QDateTime> q3dns_cleanup_time;
95
return originOfTime->secsTo( QDateTime::currentDateTime() );
97
originOfTime = new QDateTime( QDateTime::currentDateTime() );
98
::id = originOfTime->time().msec() * 60 + originOfTime->time().second();
99
q3dns_cleanup_time.add( &originOfTime );
104
static Q3PtrList<QHostAddress> * ns = 0;
105
static Q3StrList * domains = 0;
106
static bool ipv6support = false;
110
Q3DnsPrivate() : queryTimer( 0 ), noNames(false)
112
#if defined(Q_DNS_SYNCHRONOUS)
113
#if defined(Q_OS_UNIX)
114
noEventLoop = qApp==0 || qApp->loopLevel()==0;
127
#if defined(Q_DNS_SYNCHRONOUS)
132
friend class Q3DnsAnswer;
141
// Q3DnsRR is the class used to store a single RR. Q3DnsRR can store
142
// all of the supported RR types. a Q3DnsRR is always cached.
144
// Q3DnsRR is mostly constructed from the outside. a but hacky, but
145
// permissible since the entire class is internal.
149
Q3DnsRR( const QString & label );
153
Q3DnsDomain * domain;
159
// somewhat space-wasting per-type data
161
QHostAddress address;
162
// cname / mx / srv / ptr
170
QString text; // could be overloaded into target...
178
Q3DnsDomain( const QString & label );
181
static void add( const QString & label, Q3DnsRR * );
182
static Q3PtrList<Q3DnsRR> * cached( const Q3Dns * );
184
void take( Q3DnsRR * );
186
void sweep( Q_UINT32 thisSweep );
188
bool isEmpty() const { return rrs == 0 || rrs->isEmpty(); }
190
QString name() const { return l; }
194
Q3PtrList<Q3DnsRR> * rrs;
198
class Q3DnsQuery: public QTimer { // this inheritance is a very evil hack
201
id( 0 ), t( Q3Dns::None ), step(0), started(0),
202
dns( new Q3PtrDict<void>(17) ) {}
203
~Q3DnsQuery() { delete dns; }
211
Q3PtrDict<void> * dns;
218
Q3DnsAnswer( Q3DnsQuery * );
219
Q3DnsAnswer( const QByteArray &, Q3DnsQuery * );
234
Q3PtrList<Q3DnsRR> * rrs;
242
QString readString(bool multipleLabels = true);
254
Q3DnsRR::Q3DnsRR( const QString & label )
255
: domain( 0 ), t( Q3Dns::None ),
256
nxdomain( false ), current( false ),
257
expireTime( 0 ), deleteTime( 0 ),
258
priority( 0 ), weight( 0 ), port( 0 )
260
Q3DnsDomain::add( label, this );
264
// not supposed to be deleted except by Q3DnsDomain
267
// nothing is necessary
271
// this one just sticks in a NXDomain
272
Q3DnsAnswer::Q3DnsAnswer( Q3DnsQuery * query_ )
280
rrs = new Q3PtrList<Q3DnsRR>;
281
rrs->setAutoDelete( false );
287
Q3DnsRR * newrr = new Q3DnsRR( query->l );
289
newrr->deleteTime = query->started + 10;
290
newrr->expireTime = query->started + 10;
291
newrr->nxdomain = true;
292
newrr->current = true;
293
rrs->append( newrr );
297
Q3DnsAnswer::Q3DnsAnswer( const QByteArray& answer_,
298
Q3DnsQuery * query_ )
302
answer = (Q_UINT8 *)(answer_.data());
303
size = (int)answer_.size();
306
rrs = new Q3PtrList<Q3DnsRR>;
307
rrs->setAutoDelete( false );
315
Q3DnsAnswer::~Q3DnsAnswer()
318
Q3PtrListIterator<Q3DnsRR> it( *rrs );
320
while( (tmprr=it.current()) != 0 ) {
322
tmprr->t = Q3Dns::None; // will be deleted soonish
329
QString Q3DnsAnswer::readString(bool multipleLabels)
336
// Read one character
337
if ( p >= 0 && p < size )
345
// Detect end of data
349
return r.isNull() ? QString( "." ) : r;
352
// Read a label of size 'b' characters
356
r += QChar( answer[p++] );
358
// Return immediately if we were only supposed to read one
365
// Ignore unrecognized control character, or p was out of
369
// Use the next character to determine the relative offset
370
// to jump to before continuing the packet parsing.
371
int q = ( (answer[p] & 0x3f) << 8 ) + answer[p+1];
373
if ( q >= pp || q >= p )
387
void Q3DnsAnswer::parseA()
389
if ( next != pp + 4 ) {
390
#if defined(Q3DNS_DEBUG)
391
qDebug( "Q3Dns: saw %d bytes long IN A for %s",
392
next - pp, label.ascii() );
397
rr = new Q3DnsRR( label );
399
rr->address = QHostAddress( ( answer[pp+0] << 24 ) +
400
( answer[pp+1] << 16 ) +
401
( answer[pp+2] << 8 ) +
403
#if defined(Q3DNS_DEBUG)
404
qDebug( "Q3Dns: saw %s IN A %s (ttl %d)", label.ascii(),
405
rr->address.toString().ascii(), ttl );
410
void Q3DnsAnswer::parseAaaa()
412
if ( next != pp + 16 ) {
413
#if defined(Q3DNS_DEBUG)
414
qDebug( "Q3Dns: saw %d bytes long IN Aaaa for %s",
415
next - pp, label.ascii() );
420
rr = new Q3DnsRR( label );
422
rr->address = QHostAddress( answer+pp );
423
#if defined(Q3DNS_DEBUG)
424
qDebug( "Q3Dns: saw %s IN Aaaa %s (ttl %d)", label.ascii(),
425
rr->address.toString().ascii(), ttl );
431
void Q3DnsAnswer::parseMx()
433
if ( next < pp + 2 ) {
434
#if defined(Q3DNS_DEBUG)
435
qDebug( "Q3Dns: saw %d bytes long IN MX for %s",
436
next - pp, label.ascii() );
441
rr = new Q3DnsRR( label );
442
rr->priority = (answer[pp] << 8) + answer[pp+1];
444
rr->target = readString().lower();
446
#if defined(Q3DNS_DEBUG)
447
qDebug( "Q3Dns: saw bad string in MX for %s", label.ascii() );
452
#if defined(Q3DNS_DEBUG)
453
qDebug( "Q3Dns: saw %s IN MX %d %s (ttl %d)", label.ascii(),
454
rr->priority, rr->target.ascii(), ttl );
459
void Q3DnsAnswer::parseSrv()
461
if ( next < pp + 6 ) {
462
#if defined(Q3DNS_DEBUG)
463
qDebug( "Q3Dns: saw %d bytes long IN SRV for %s",
464
next - pp, label.ascii() );
469
rr = new Q3DnsRR( label );
470
rr->priority = (answer[pp] << 8) + answer[pp+1];
471
rr->weight = (answer[pp+2] << 8) + answer[pp+3];
472
rr->port = (answer[pp+4] << 8) + answer[pp+5];
474
rr->target = readString().lower();
476
#if defined(Q3DNS_DEBUG)
477
qDebug( "Q3Dns: saw bad string in SRV for %s", label.ascii() );
482
#if defined(Q3DNS_DEBUG)
483
qDebug( "Q3Dns: saw %s IN SRV %d %d %d %s (ttl %d)", label.ascii(),
484
rr->priority, rr->weight, rr->port, rr->target.ascii(), ttl );
489
void Q3DnsAnswer::parseCname()
491
QString target = readString().lower();
493
#if defined(Q3DNS_DEBUG)
494
qDebug( "Q3Dns: saw bad cname for for %s", label.ascii() );
499
rr = new Q3DnsRR( label );
500
rr->t = Q3Dns::Cname;
502
#if defined(Q3DNS_DEBUG)
503
qDebug( "Q3Dns: saw %s IN CNAME %s (ttl %d)", label.ascii(),
504
rr->target.ascii(), ttl );
509
void Q3DnsAnswer::parseNs()
511
QString target = readString().lower();
513
#if defined(Q3DNS_DEBUG)
514
qDebug( "Q3Dns: saw bad cname for for %s", label.ascii() );
521
#if defined(Q3DNS_DEBUG)
522
qDebug( "Q3Dns: saw %s IN NS %s (ttl %d)", label.ascii(),
523
target.ascii(), ttl );
528
void Q3DnsAnswer::parsePtr()
530
QString target = readString().lower();
532
#if defined(Q3DNS_DEBUG)
533
qDebug( "Q3Dns: saw bad PTR for for %s", label.ascii() );
538
rr = new Q3DnsRR( label );
541
#if defined(Q3DNS_DEBUG)
542
qDebug( "Q3Dns: saw %s IN PTR %s (ttl %d)", label.ascii(),
543
rr->target.ascii(), ttl );
548
void Q3DnsAnswer::parseTxt()
550
QString text = readString(false);
552
#if defined(Q3DNS_DEBUG)
553
qDebug( "Q3Dns: saw bad TXT for for %s", label.ascii() );
558
rr = new Q3DnsRR( label );
561
#if defined(Q3DNS_DEBUG)
562
qDebug( "Q3Dns: saw %s IN TXT \"%s\" (ttl %d)", label.ascii(),
563
rr->text.ascii(), ttl );
568
void Q3DnsAnswer::parse()
570
// okay, do the work...
571
if ( (answer[2] & 0x78) != 0 ) {
572
#if defined(Q3DNS_DEBUG)
573
qDebug( "DNS Manager: answer to wrong query type (%d)", answer[1] );
580
bool aa = (answer[2] & 4) != 0;
583
if ( (answer[2] & 2) != 0 ) {
584
#if defined(Q3DNS_DEBUG)
585
qDebug( "DNS Manager: truncated answer; pressing on" );
590
bool rd = (answer[2] & 1) != 0;
593
// we don't test the MBZ fields
595
if ( (answer[3] & 0x0f) == 3 ) {
596
#if defined(Q3DNS_DEBUG)
597
qDebug( "DNS Manager: saw NXDomain for %s", query->l.ascii() );
599
// NXDomain. cache that for one minute.
600
rr = new Q3DnsRR( query->l );
602
rr->deleteTime = query->started + 60;
603
rr->expireTime = query->started + 60;
610
if ( (answer[3] & 0x0f) != 0 ) {
611
#if defined(Q3DNS_DEBUG)
612
qDebug( "DNS Manager: error code %d", answer[3] & 0x0f );
618
int qdcount = ( answer[4] << 8 ) + answer[5];
619
int ancount = ( answer[6] << 8 ) + answer[7];
620
int nscount = ( answer[8] << 8 ) + answer[9];
621
int adcount = (answer[10] << 8 ) +answer[11];
626
while( qdcount > 0 && pp < size ) {
627
// should I compare the string against query->l?
637
// if we parse the answer completely, but there are no answers,
638
// ignore the entire thing.
640
while( ( rrno < ancount ||
641
( ok && answers >0 && rrno < ancount + nscount + adcount ) ) &&
643
label = readString().lower();
647
if ( pp + 10 <= size )
648
rdlength = ( answer[pp+8] << 8 ) + answer[pp+9];
649
if ( pp + 10 + rdlength > size ) {
650
#if defined(Q3DNS_DEBUG)
651
qDebug( "DNS Manager: ran out of stuff to parse (%d+%d>%d (%d)",
652
pp, rdlength, size, rrno < ancount );
654
// if we're still in the AN section, we should go back and
655
// at least down the TTLs. probably best to invalidate
657
// the rrs list is good for this
658
ok = ( rrno < ancount );
662
type = ( answer[pp+0] << 8 ) + answer[pp+1];
663
clas = ( answer[pp+2] << 8 ) + answer[pp+3];
664
ttl = ( answer[pp+4] << 24 ) + ( answer[pp+5] << 16 ) +
665
( answer[pp+6] << 8 ) + answer[pp+7];
668
#if defined(Q3DNS_DEBUG)
669
qDebug( "DNS Manager: class %d (not internet) for %s",
670
clas, label.isNull() ? "." : label.ascii() );
673
next = pp + rdlength;
701
// something we don't know
702
#if defined(Q3DNS_DEBUG)
703
qDebug( "DNS Manager: type %d for %s", type,
704
label.isNull() ? "." : label.ascii() );
711
rr->expireTime = query->started + ttl;
713
rr->expireTime = query->started + 20;
714
if ( rrno < ancount ) {
716
rr->deleteTime = rr->expireTime;
728
if ( answers == 0 ) {
729
#if defined(Q3DNS_DEBUG)
730
qDebug( "DNS Manager: answer contained no answers" );
735
// now go through the list and mark all the As that are referenced
736
// by something we care about. we want to cache such As.
738
Q3Dict<void> used( 17 );
739
used.setAutoDelete( false );
740
while( (rr=rrs->current()) != 0 ) {
742
if ( rr->target.length() && rr->deleteTime > 0 && rr->current )
743
used.insert( rr->target, (void*)42 );
744
if ( ( rr->t == Q3Dns::A || rr->t == Q3Dns::Aaaa ) &&
745
used.find( rr->domain->name() ) != 0 )
746
rr->deleteTime = rr->expireTime;
749
// next, for each RR, delete any older RRs that are equal to it
751
while( (rr=rrs->current()) != 0 ) {
753
if ( rr && rr->domain && rr->domain->rrs ) {
754
Q3PtrList<Q3DnsRR> * drrs = rr->domain->rrs;
757
while( (older=drrs->current()) != 0 ) {
760
older->nxdomain == rr->nxdomain &&
761
older->address == rr->address &&
762
older->target == rr->target &&
763
older->priority == rr->priority &&
764
older->weight == rr->weight &&
765
older->port == rr->port &&
766
older->text == rr->text ) {
767
// well, it's equal, but it's not the same. so we kill it,
768
// but use its expiry time.
769
#if defined(Q3DNS_DEBUG)
770
qDebug( "killing off old %d for %s, expire was %d",
771
older->t, older->domain->name().latin1(),
774
older->t = Q3Dns::None;
775
rr->expireTime = QMAX( older->expireTime, rr->expireTime );
776
rr->deleteTime = QMAX( older->deleteTime, rr->deleteTime );
777
older->deleteTime = 0;
778
#if defined(Q3DNS_DEBUG)
779
qDebug( " adjusted expire is %d", rr->expireTime );
787
#if defined(Q3DNS_DEBUG)
788
//qDebug( "DNS Manager: ()" );
793
class Q3DnsUgleHack: public Q3Dns {
795
void ugle( bool emitAnyway=false );
799
void Q3DnsAnswer::notify()
801
if ( !rrs || !ok || !query || !query->dns )
804
Q3PtrDict<void> notified;
805
notified.setAutoDelete( false );
807
Q3PtrDictIterator<void> it( *query->dns );
810
while( (dns=(Q3Dns*)(it.current())) != 0 ) {
812
if ( notified.find( (void*)dns ) == 0 ) {
813
notified.insert( (void*)dns, (void*)42 );
814
if ( rrs->count() == 0 ) {
815
#if defined(Q3DNS_DEBUG)
816
qDebug( "DNS Manager: found no answers!" );
818
dns->d->noNames = true;
819
((Q3DnsUgleHack*)dns)->ugle( true );
821
QStringList n = dns->qualifiedNames();
822
if ( n.contains(query->l) )
823
((Q3DnsUgleHack*)dns)->ugle();
824
#if defined(Q3DNS_DEBUG)
826
qDebug( "DNS Manager: DNS thing %s not notified for %s",
827
dns->label().ascii(), query->l.ascii() );
842
class Q3DnsManager: public Q3DnsSocket {
844
public: // just to silence the moronic g++.
848
static Q3DnsManager * manager();
850
Q3DnsDomain * domain( const QString & );
852
void transmitQuery( Q3DnsQuery * );
853
void transmitQuery( int );
855
// reimplementation of the slots
861
Q3PtrVector<Q3DnsQuery> queries;
862
Q3Dict<Q3DnsDomain> cache;
863
Q3SocketDevice * ipv4Socket;
864
#if !defined (QT_NO_IPV6)
865
Q3SocketDevice * ipv6Socket;
871
static Q3DnsManager * globalManager = 0;
873
static void cleanupDns()
875
delete globalManager;
879
Q3DnsManager * Q3DnsManager::manager()
881
if ( !globalManager ) {
882
qAddPostRoutine(cleanupDns);
885
return globalManager;
889
void Q3DnsUgleHack::ugle( bool emitAnyway)
891
if ( emitAnyway || !isWorking() ) {
892
#if defined(Q3DNS_DEBUG)
893
qDebug( "DNS Manager: status change for %s (type %d)",
894
label().ascii(), recordType() );
901
Q3DnsManager::Q3DnsManager()
902
: Q3DnsSocket( qApp, "Internal DNS manager" ),
903
queries( Q3PtrVector<Q3DnsQuery>( 0 ) ),
904
cache( Q3Dict<Q3DnsDomain>( 83, false ) ),
905
ipv4Socket( new Q3SocketDevice( Q3SocketDevice::Datagram, Q3SocketDevice::IPv4, 0 ) )
906
#if !defined (QT_NO_IPV6)
907
, ipv6Socket( new Q3SocketDevice( Q3SocketDevice::Datagram, Q3SocketDevice::IPv6, 0 ) )
910
cache.setAutoDelete( true );
911
globalManager = this;
913
QTimer * sweepTimer = new QTimer( this );
914
sweepTimer->start( 1000 * 60 * 3 );
915
connect( sweepTimer, SIGNAL(timeout()),
916
this, SLOT(cleanCache()) );
918
QSocketNotifier * rn4 = new QSocketNotifier( ipv4Socket->socket(),
919
QSocketNotifier::Read,
920
this, "dns IPv4 socket watcher" );
921
ipv4Socket->setAddressReusable( false );
922
ipv4Socket->setBlocking( false );
923
connect( rn4, SIGNAL(activated(int)), SLOT(answer()) );
925
#if !defined (QT_NO_IPV6)
926
// Don't connect the IPv6 socket notifier if the host does not
928
if ( ipv6Socket->socket() != -1 ) {
929
QSocketNotifier * rn6 = new QSocketNotifier( ipv6Socket->socket(),
930
QSocketNotifier::Read,
931
this, "dns IPv6 socket watcher" );
934
ipv6Socket->setAddressReusable( false );
935
ipv6Socket->setBlocking( false );
936
connect( rn6, SIGNAL(activated(int)), SLOT(answer()) );
943
// O(n*n) stuff here. but for 3 and 6, O(n*n) with a low k should
944
// be perfect. the point is to eliminate any duplicates that
945
// might be hidden in the lists.
946
Q3PtrList<QHostAddress> * ns = new Q3PtrList<QHostAddress>;
950
while( (h=::ns->current()) != 0 ) {
952
while( ns->current() != 0 && !(*ns->current() == *h) )
954
if ( !ns->current() ) {
955
ns->append( new QHostAddress(*h) );
956
#if defined(Q3DNS_DEBUG)
957
qDebug( "using name server %s", h->toString().latin1() );
959
qDebug( "skipping address %s", h->toString().latin1() );
967
::ns->setAutoDelete( true );
969
Q3StrList * domains = new Q3StrList( true );
973
while( (s=::domains->current()) != 0 ) {
975
while( domains->current() != 0 && qstrcmp( domains->current(), s ) )
977
if ( !domains->current() ) {
978
domains->append( s );
979
#if defined(Q3DNS_DEBUG)
980
qDebug( "searching domain %s", s );
982
qDebug( "skipping domain %s", s );
990
::domains->setAutoDelete( true );
994
Q3DnsManager::~Q3DnsManager()
998
queries.setAutoDelete( true );
999
cache.setAutoDelete( true );
1001
#if !defined (QT_NO_IPV6)
1006
static Q_UINT32 lastSweep = 0;
1008
void Q3DnsManager::cleanCache()
1011
Q3DictIterator<Q3DnsDomain> it( cache );
1013
Q_UINT32 thisSweep = now();
1014
#if defined(Q3DNS_DEBUG)
1015
qDebug( "Q3DnsManager::cleanCache(: Called, time is %u, last was %u",
1016
thisSweep, lastSweep );
1019
while( (d=it.current()) != 0 ) {
1021
d->sweep( thisSweep ); // after this, d may be empty
1023
again = !d->isEmpty();
1027
lastSweep = thisSweep;
1031
void Q3DnsManager::retransmit()
1033
const QObject * o = sender();
1034
if ( o == 0 || globalManager == 0 || this != globalManager )
1037
while( q < queries.size() && queries[q] != o )
1039
if ( q < queries.size() )
1044
void Q3DnsManager::answer()
1046
QByteArray a( 16383 ); // large enough for anything, one suspects
1049
#if defined (QT_NO_IPV6)
1050
r = ipv4Socket->readBlock(a.data(), a.size());
1052
if (((QSocketNotifier *)sender())->socket() == ipv4Socket->socket())
1053
r = ipv4Socket->readBlock(a.data(), a.size());
1055
r = ipv6Socket->readBlock(a.data(), a.size());
1057
#if defined(Q3DNS_DEBUG)
1058
#if !defined (QT_NO_IPV6)
1059
qDebug("DNS Manager: answer arrived: %d bytes from %s:%d", r,
1060
useIpv4Socket ? ipv4Socket->peerAddress().toString().ascii()
1061
: ipv6Socket->peerAddress().toString().ascii(),
1062
useIpv4Socket ? ipv4Socket->peerPort() : ipv6Socket->peerPort() );
1064
qDebug("DNS Manager: answer arrived: %d bytes from %s:%d", r,
1065
ipv4Socket->peerAddress().toString().ascii(), ipv4Socket->peerPort());;
1070
// maybe we should check that the answer comes from port 53 on one
1071
// of our name servers...
1074
Q_UINT16 aid = (((Q_UINT8)a[0]) << 8) + ((Q_UINT8)a[1]);
1076
while( i < queries.size() &&
1077
!( queries[i] && queries[i]->id == aid ) )
1079
if ( i == queries.size() ) {
1080
#if defined(Q3DNS_DEBUG)
1081
qDebug( "DNS Manager: bad id (0x%04x) %d", aid, i );
1086
// at this point queries[i] is whatever we asked for.
1088
if ( ( (Q_UINT8)(a[2]) & 0x80 ) == 0 ) {
1089
#if defined(Q3DNS_DEBUG)
1090
qDebug( "DNS Manager: received a query" );
1095
Q3DnsQuery * q = queries[i];
1096
Q3DnsAnswer answer( a, q );
1106
void Q3DnsManager::transmitQuery( Q3DnsQuery * query_ )
1112
while( i < queries.size() && queries[i] != 0 )
1114
if ( i == queries.size() )
1115
queries.resize( i+1 );
1116
queries.insert( i, query_ );
1121
void Q3DnsManager::transmitQuery( int i )
1123
if ( i < 0 || i >= (int)queries.size() )
1125
Q3DnsQuery * q = queries[i];
1127
if ( q && q->step > 8 ) {
1128
// okay, we've run out of retransmissions. we fake an NXDomain
1129
// with a very short life time...
1130
Q3DnsAnswer answer( q );
1132
// and then get rid of the query
1134
#if defined(Q3DNS_DEBUG)
1135
qDebug( "DNS Manager: giving up on query 0x%04x", q->id );
1138
QTimer::singleShot( 0, Q3DnsManager::manager(), SLOT(cleanCache()) );
1139
// and don't process anything more
1143
if ( q && !q->dns || q->dns->isEmpty() )
1144
// noone currently wants the answer, so there's no point in
1145
// retransmitting the query. we keep it, though. an answer may
1146
// arrive for an earlier query transmission, and if it does we
1147
// may benefit from caching the result.
1150
QByteArray p( 12 + q->l.length() + 2 + 4 );
1151
if ( p.size() > 500 )
1152
return; // way over the limit, so don't even try
1156
p[0] = (q->id & 0xff00) >> 8;
1157
p[1] = q->id & 0x00ff;
1158
p[2] = 1; // recursion desired, rest is 0
1159
p[3] = 0; // all is 0
1163
// no answers, name servers or additional data
1164
p[6] = p[7] = p[8] = p[9] = p[10] = p[11] = 0;
1166
// the name is composed of several components. each needs to be
1167
// written by itself... so we write...
1168
// oh, and we assume that there's no funky characters in there.
1171
while( lp < (uint) q->l.length() ) {
1172
int le = q->l.find( '.', lp );
1175
QString component = q->l.mid( lp, le-lp );
1176
p[pp++] = component.length();
1178
for( cp=0; cp < (int)component.length(); cp++ )
1179
p[pp++] = component[cp].latin1();
1209
p[pp++] = (char)255; // any
1212
// query class (always internet)
1216
// if we have no name servers, we should regenerate ns in case
1217
// name servers have recently been defined (like on windows,
1218
// plugging/unplugging the network cable will change the name
1220
if ( !ns || ns->isEmpty() )
1223
if ( !ns || ns->isEmpty() ) {
1224
// we don't find any name servers. We fake an NXDomain
1225
// with a very short life time...
1226
Q3DnsAnswer answer( q );
1228
// and then get rid of the query
1230
#if defined(Q3DNS_DEBUG)
1231
qDebug( "DNS Manager: no DNS server found on query 0x%04x", q->id );
1234
QTimer::singleShot( 1000*10, Q3DnsManager::manager(), SLOT(cleanCache()) );
1235
// and don't process anything more
1239
QHostAddress receiver = *ns->at( q->step % ns->count() );
1240
if (receiver.isIPv4Address())
1241
ipv4Socket->writeBlock( p.data(), pp, receiver, 53 );
1242
#if !defined (QT_NO_IPV6)
1244
ipv6Socket->writeBlock( p.data(), pp, receiver, 53 );
1246
#if defined(Q3DNS_DEBUG)
1247
qDebug( "issuing query 0x%04x (%d) about %s type %d to %s",
1248
q->id, q->step, q->l.ascii(), q->t,
1249
ns->at( q->step % ns->count() )->toString().ascii() );
1251
if ( ns->count() > 1 && q->step == 0 && queries.count() == 1 ) {
1252
// if it's the first time, and we don't have any other
1253
// outstanding queries, send nonrecursive queries to the other
1254
// name servers too.
1256
QHostAddress * server;
1257
while( (server=ns->next()) != 0 ) {
1258
if (server->isIPv4Address())
1259
ipv4Socket->writeBlock( p.data(), pp, *server, 53 );
1260
#if !defined (QT_NO_IPV6)
1262
ipv6Socket->writeBlock( p.data(), pp, *server, 53 );
1264
#if defined(Q3DNS_DEBUG)
1265
qDebug( "copying query to %s", server->toString().ascii() );
1270
// some testing indicates that normal dns queries take up to 0.6
1271
// seconds. the graph becomes steep around that point, and the
1272
// number of errors rises... so it seems good to retry at that
1274
q->start( q->step < ns->count() ? 800 : 1500, true );
1278
Q3DnsDomain * Q3DnsManager::domain( const QString & label )
1280
Q3DnsDomain * d = cache.find( label );
1282
d = new Q3DnsDomain( label );
1283
cache.insert( label, d );
1291
// the Q3DnsDomain class looks after and coordinates queries for Q3DnsRRs for
1292
// each domain, and the cached Q3DnsRRs. (A domain, in DNS terminology, is
1293
// a node in the DNS. "no", "trolltech.com" and "lupinella.troll.no" are
1299
// this is ONLY to be called by Q3DnsManager::domain(). noone else.
1300
Q3DnsDomain::Q3DnsDomain( const QString & label )
1307
Q3DnsDomain::~Q3DnsDomain()
1314
void Q3DnsDomain::add( const QString & label, Q3DnsRR * rr )
1316
Q3DnsDomain * d = Q3DnsManager::manager()->domain( label );
1318
d->rrs = new Q3PtrList<Q3DnsRR>;
1319
d->rrs->setAutoDelete( true );
1321
d->rrs->append( rr );
1326
Q3PtrList<Q3DnsRR> * Q3DnsDomain::cached( const Q3Dns * r )
1328
Q3PtrList<Q3DnsRR> * l = new Q3PtrList<Q3DnsRR>;
1330
// test at first if you have to start a query at all
1331
if ( r->recordType() == Q3Dns::A ) {
1332
if ( r->label().lower() == "localhost" ) {
1333
// undocumented hack. ipv4-specific. also, may be a memory
1334
// leak? not sure. would be better to do this in doResInit(),
1336
Q3DnsRR *rrTmp = new Q3DnsRR( r->label() );
1337
rrTmp->t = Q3Dns::A;
1338
rrTmp->address = QHostAddress( 0x7f000001 );
1339
rrTmp->current = true;
1344
if ( tmp.setAddress( r->label() ) ) {
1345
Q3DnsRR *rrTmp = new Q3DnsRR( r->label() );
1346
if ( tmp.isIPv4Address() ) {
1347
rrTmp->t = Q3Dns::A;
1348
rrTmp->address = tmp;
1349
rrTmp->current = true;
1352
rrTmp->nxdomain = true;
1357
if ( r->recordType() == Q3Dns::Aaaa ) {
1359
if ( tmp.setAddress(r->label()) ) {
1360
Q3DnsRR *rrTmp = new Q3DnsRR( r->label() );
1361
if ( tmp.isIPv6Address() ) {
1362
rrTmp->t = Q3Dns::Aaaa;
1363
rrTmp->address = tmp;
1364
rrTmp->current = true;
1367
rrTmp->nxdomain = true;
1373
// if you reach this point, you have to do the query
1374
Q3DnsManager * m = Q3DnsManager::manager();
1375
QStringList n = r->qualifiedNames();
1379
while( it < n.count() ) {
1380
QString s = n.at(it++);
1382
#if defined(Q3DNS_DEBUG)
1383
qDebug( "looking at cache for %s (%s %d)",
1384
s.ascii(), r->label().ascii(), r->recordType() );
1386
Q3DnsDomain * d = m->domain( s );
1387
#if defined(Q3DNS_DEBUG)
1388
qDebug( " - found %d RRs", d && d->rrs ? d->rrs->count() : 0 );
1393
bool answer = false;
1394
while( d->rrs && (rr=d->rrs->current()) != 0 ) {
1395
if ( rr->t == Q3Dns::Cname && r->recordType() != Q3Dns::Cname &&
1396
!rr->nxdomain && cnamecount < 16 ) {
1397
// cname. if the code is ugly, that may just
1398
// possibly be because the concept is.
1399
#if defined(Q3DNS_DEBUG)
1400
qDebug( "found cname from %s to %s",
1401
r->label().ascii(), rr->target.ascii() );
1408
// we've elegantly moved over to whatever the cname
1409
// pointed to. well, not elegantly. let's remember
1410
// that we've done something, anyway, so we can't be
1411
// fooled into an infinte loop as well.
1414
if ( rr->t == r->recordType() ) {
1420
if ( rr->deleteTime <= lastSweep ) {
1421
// we're returning something that'll be
1422
// deleted soon. we assume that if the client
1423
// wanted it twice, it'll want it again, so we
1424
// ask the name server again right now.
1425
Q3DnsQuery * query = new Q3DnsQuery;
1426
query->started = now();
1429
query->l = rr->domain->name();
1430
// note that here, we don't bother about
1431
// notification. but we do bother about
1432
// timeouts: we make sure to use high timeouts
1433
// and few tramsissions.
1434
query->step = ns->count();
1435
QObject::connect( query, SIGNAL(timeout()),
1436
Q3DnsManager::manager(),
1437
SLOT(retransmit()) );
1438
Q3DnsManager::manager()->transmitQuery( query );
1444
// if we found a positive result, return quickly
1445
if ( answer && l->count() ) {
1446
#if defined(Q3DNS_DEBUG)
1447
qDebug( "found %d records for %s",
1448
l->count(), r->label().ascii() );
1450
while( l->current() ) {
1451
qDebug( " type %d target %s address %s",
1453
l->current()->target.latin1(),
1454
l->current()->address.toString().latin1() );
1462
#if defined(Q3DNS_DEBUG)
1464
qDebug( "found NXDomain %s", s.ascii() );
1468
// if we didn't, and not a negative result either, perhaps
1469
// we need to transmit a query.
1471
while ( q < m->queries.size() &&
1472
( m->queries[q] == 0 ||
1473
m->queries[q]->t != r->recordType() ||
1474
m->queries[q]->l != s ) )
1476
// we haven't done it before, so maybe we should. but
1477
// wait - if it's an unqualified name, only ask when all
1478
// the other alternatives are exhausted.
1479
if ( q == m->queries.size() && ( s.find( '.' ) >= 0 ||
1480
int(l->count()) >= n.count()-1 ) ) {
1481
Q3DnsQuery * query = new Q3DnsQuery;
1482
query->started = now();
1484
query->t = r->recordType();
1486
query->dns->replace( (void*)r, (void*)r );
1487
QObject::connect( query, SIGNAL(timeout()),
1488
Q3DnsManager::manager(), SLOT(retransmit()) );
1489
Q3DnsManager::manager()->transmitQuery( query );
1490
} else if ( q < m->queries.size() ) {
1491
// if we've found an earlier query for the same
1492
// domain/type, subscribe to its answer
1493
m->queries[q]->dns->replace( (void*)r, (void*)r );
1502
void Q3DnsDomain::sweep( Q_UINT32 thisSweep )
1509
while( (rr=rrs->current()) != 0 ) {
1510
if ( !rr->deleteTime )
1511
rr->deleteTime = thisSweep; // will hit next time around
1513
#if defined(Q3DNS_DEBUG)
1514
qDebug( "Q3Dns::sweep: %s type %d expires %u %u - %s / %s",
1515
rr->domain->name().latin1(), rr->t,
1516
rr->expireTime, rr->deleteTime,
1517
rr->target.latin1(), rr->address.toString().latin1());
1519
if ( rr->current == false ||
1520
rr->t == Q3Dns::None ||
1521
rr->deleteTime <= thisSweep ||
1522
rr->expireTime <= thisSweep )
1528
if ( rrs->isEmpty() ) {
1537
// the itsy-bitsy little socket class I don't really need except for
1538
// so I can subclass and reimplement the slots.
1541
Q3DnsSocket::Q3DnsSocket( QObject * parent, const char * name )
1542
: QObject( parent, name )
1548
Q3DnsSocket::~Q3DnsSocket()
1554
void Q3DnsSocket::cleanCache()
1560
void Q3DnsSocket::retransmit()
1566
void Q3DnsSocket::answer()
1573
\class Q3Dns q3dns.h
1574
\brief The Q3Dns class provides asynchronous DNS lookups.
1578
Both Windows and Unix provide synchronous DNS lookups; Windows
1579
provides some asynchronous support too. At the time of writing
1580
neither operating system provides asynchronous support for
1581
anything other than hostname-to-address mapping.
1583
Q3Dns rectifies this shortcoming, by providing asynchronous caching
1584
lookups for the record types that we expect modern GUI
1585
applications to need in the near future.
1587
The class is \e not straightforward to use (although it is much
1588
simpler than the native APIs); Q3Socket provides much easier to use
1589
TCP connection facilities. The aim of Q3Dns is to provide a correct
1590
and small API to the DNS and nothing more. (We use "correctness"
1591
to mean that the DNS information is correctly cached, and
1592
correctly timed out.)
1594
The API comprises a constructor, functions to set the DNS node
1595
(the domain in DNS terminology) and record type (setLabel() and
1596
setRecordType()), the corresponding get functions, an isWorking()
1597
function to determine whether Q3Dns is working or reading, a
1598
resultsReady() signal and query functions for the result.
1600
There is one query function for each RecordType, namely
1601
addresses(), mailServers(), servers(), hostNames() and texts().
1602
There are also two generic query functions: canonicalName()
1603
returns the name you'll presumably end up using (the exact meaning
1604
of this depends on the record type) and qualifiedNames() returns a
1605
list of the fully qualified names label() maps to.
1611
Constructs a DNS query object with invalid settings for both the
1612
label and the search type.
1617
d = new Q3DnsPrivate;
1625
Constructs a DNS query object that will return record type \a rr
1626
information about \a label.
1628
The DNS lookup is started the next time the application enters the
1629
event loop. When the result is found the signal resultsReady() is
1632
\a rr defaults to \c A, IPv4 addresses.
1635
Q3Dns::Q3Dns( const QString & label, RecordType rr )
1637
d = new Q3DnsPrivate;
1640
setStartQueryTimer(); // start query the next time we enter event loop
1646
Constructs a DNS query object that will return record type \a rr
1647
information about host address \a address. The label is set to the
1648
IN-ADDR.ARPA domain name. This is useful in combination with the
1649
\c Ptr record type (e.g. if you want to look up a hostname for a
1652
The DNS lookup is started the next time the application enters the
1653
event loop. When the result is found the signal resultsReady() is
1656
\a rr defaults to \c Ptr, that maps addresses to hostnames.
1659
Q3Dns::Q3Dns( const QHostAddress & address, RecordType rr )
1661
d = new Q3DnsPrivate;
1663
setLabel( address );
1664
setStartQueryTimer(); // start query the next time we enter event loop
1671
Destroys the DNS query object and frees its allocated resources.
1676
if ( globalManager ) {
1678
Q3DnsManager * m = globalManager;
1679
while( q < m->queries.size() ) {
1680
Q3DnsQuery * query=m->queries[q];
1681
if ( query && query->dns )
1682
(void)query->dns->take( (void*) this );
1696
Sets this DNS query object to query for information about \a
1699
This does not change the recordType(), but its isWorking() status
1700
will probably change as a result.
1702
The DNS lookup is started the next time the application enters the
1703
event loop. When the result is found the signal resultsReady() is
1707
void Q3Dns::setLabel( const QString & label )
1712
// construct a list of qualified names
1714
if ( l.length() > 1 && l[(int)l.length()-1] == '.' ) {
1715
n.append( l.left( l.length()-1 ).lower() );
1719
const int maxDots = 2;
1720
while( i && dots < maxDots ) {
1721
if ( l[--i] == '.' )
1724
if ( dots < maxDots ) {
1725
(void)Q3DnsManager::manager(); // create a Q3DnsManager, if it is not already there
1726
Q3StrListIterator it( *domains );
1728
while( (dom=it.current()) != 0 ) {
1730
n.append( l.lower() + "." + dom );
1733
n.append( l.lower() );
1736
#if defined(Q_DNS_SYNCHRONOUS)
1737
if ( d->noEventLoop ) {
1738
doSynchronousLookup();
1740
setStartQueryTimer(); // start query the next time we enter event loop
1743
setStartQueryTimer(); // start query the next time we enter event loop
1745
#if defined(Q3DNS_DEBUG)
1746
qDebug( "Q3Dns::setLabel: %d address(es) for %s", n.count(), l.ascii() );
1748
for( i = 0; i < (int)n.count(); i++ )
1749
qDebug( "Q3Dns::setLabel: %d: %s", i, n[i].ascii() );
1757
Sets this DNS query object to query for information about the host
1758
address \a address. The label is set to the IN-ADDR.ARPA domain
1759
name. This is useful in combination with the \c Ptr record type
1760
(e.g. if you want to look up a hostname for a given address).
1763
void Q3Dns::setLabel( const QHostAddress & address )
1765
setLabel( toInAddrArpaDomain( address ) );
1770
\fn QStringList Q3Dns::qualifiedNames() const
1772
Returns a list of the fully qualified names label() maps to.
1774
Note that if you want to iterate over the list, you should iterate
1777
QStringList list = myDns.qualifiedNames();
1778
QStringList::Iterator it = list.begin();
1779
while( it != list.end() ) {
1780
myProcessing( *it );
1789
\fn QString Q3Dns::label() const
1791
Returns the domain name for which this object returns information.
1797
\enum Q3Dns::RecordType
1799
This enum type defines the record types Q3Dns can handle. The DNS
1800
provides many more; these are the ones we've judged to be in
1801
current use, useful for GUI programs and important enough to
1804
\value None No information. This exists only so that Q3Dns can
1807
\value A IPv4 addresses. By far the most common type.
1809
\value Aaaa IPv6 addresses. So far mostly unused.
1811
\value Mx Mail eXchanger names. Used for mail delivery.
1813
\value Srv SeRVer names. Generic record type for finding
1814
servers. So far mostly unused.
1816
\value Cname Canonical names. Maps from nicknames to the true
1817
name (the canonical name) for a host.
1819
\value Ptr name PoinTeRs. Maps from IPv4 or IPv6 addresses to hostnames.
1821
\value Txt arbitrary TeXT for domains.
1823
We expect that some support for the
1824
\link http://www.dns.net/dnsrd/rfc/rfc2535.html RFC-2535 \endlink
1825
extensions will be added in future versions.
1829
Sets this object to query for record type \a rr records.
1831
The DNS lookup is started the next time the application enters the
1832
event loop. When the result is found the signal resultsReady() is
1838
void Q3Dns::setRecordType( RecordType rr )
1842
setStartQueryTimer(); // start query the next time we enter event loop
1848
Private slot for starting the query.
1850
void Q3Dns::startQuery()
1852
// isWorking() starts the query (if necessary)
1854
emit resultsReady();
1858
The three functions Q3Dns::Q3Dns(QString, RecordType),
1859
Q3Dns::setLabel() and Q3Dns::setRecordType() may start a DNS lookup.
1860
This function handles setting up the single shot timer.
1862
void Q3Dns::setStartQueryTimer()
1864
#if defined(Q_DNS_SYNCHRONOUS)
1865
if ( !d->queryTimer && !d->noEventLoop )
1867
if ( !d->queryTimer )
1870
// start the query the next time we enter event loop
1871
d->queryTimer = new QTimer( this );
1872
connect( d->queryTimer, SIGNAL(timeout()),
1873
this, SLOT(startQuery()) );
1874
d->queryTimer->start( 0, true );
1879
Transforms the host address \a address to the IN-ADDR.ARPA domain
1880
name. Returns something indeterminate if you're sloppy or
1881
naughty. This function has an IPv4-specific name, but works for
1884
QString Q3Dns::toInAddrArpaDomain( const QHostAddress &address )
1887
if ( address.isNull() ) {
1888
// if the address isn't valid, neither of the other two make
1889
// cases make sense. better to just return.
1890
} else if ( address.isIp4Addr() ) {
1891
Q_UINT32 i = address.ip4Addr();
1892
s.sprintf( "%d.%d.%d.%d.IN-ADDR.ARPA",
1893
i & 0xff, (i >> 8) & 0xff, (i>>16) & 0xff, (i>>24) & 0xff );
1895
// RFC 3152. (1886 is deprecated, and clients no longer need to
1896
// support it, in practice).
1897
Q_IPV6ADDR i = address.toIPv6Address();
1901
s = QString::number( i.c[b]%16, 16 ) + "." +
1902
QString::number( i.c[b]/16, 16 ) + "." + s;
1911
\fn Q3Dns::RecordType Q3Dns::recordType() const
1913
Returns the record type of this DNS query object.
1915
\sa setRecordType() RecordType
1919
\fn void Q3Dns::resultsReady()
1921
This signal is emitted when results are available for one of the
1926
Returns true if Q3Dns is doing a lookup for this object (i.e. if it
1927
does not already have the necessary information); otherwise
1930
Q3Dns emits the resultsReady() signal when the status changes to false.
1933
bool Q3Dns::isWorking() const
1935
#if defined(Q3DNS_DEBUG)
1936
qDebug( "Q3Dns::isWorking (%s, %d)", l.ascii(), t );
1941
#if defined(Q_DNS_SYNCHRONOUS)
1942
if ( d->noEventLoop )
1946
Q3PtrList<Q3DnsRR> * ll = Q3DnsDomain::cached( this );
1947
Q_LONG queries = n.count();
1948
while( ll->current() != 0 ) {
1949
if ( ll->current()->nxdomain ) {
1968
Returns a list of the addresses for this name if this Q3Dns object
1969
has a recordType() of \c Q3Dns::A or \c Q3Dns::Aaaa and the answer
1970
is available; otherwise returns an empty list.
1972
As a special case, if label() is a valid numeric IP address, this
1973
function returns that address.
1975
Note that if you want to iterate over the list, you should iterate
1978
Q3ValueList<QHostAddress> list = myDns.addresses();
1979
Q3ValueList<QHostAddress>::Iterator it = list.begin();
1980
while( it != list.end() ) {
1981
myProcessing( *it );
1988
Q3ValueList<QHostAddress> Q3Dns::addresses() const
1990
#if defined(Q3DNS_DEBUG)
1991
qDebug( "Q3Dns::addresses (%s)", l.ascii() );
1993
Q3ValueList<QHostAddress> result;
1994
if ( t != A && t != Aaaa )
1997
Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
2000
while( (rr=cached->current()) != 0 ) {
2001
if ( rr->current && !rr->nxdomain )
2002
result.append( rr->address );
2011
\class Q3Dns::MailServer
2012
\brief The Q3Dns::MailServer class is described in Q3Dns::mailServers().
2018
Returns a list of mail servers if the record type is \c Mx. The
2019
class \c Q3Dns::MailServer contains the following public variables:
2021
\i QString Q3Dns::MailServer::name
2022
\i Q_UINT16 Q3Dns::MailServer::priority
2025
Note that if you want to iterate over the list, you should iterate
2028
Q3ValueList<Q3Dns::MailServer> list = myDns.mailServers();
2029
Q3ValueList<Q3Dns::MailServer>::Iterator it = list.begin();
2030
while( it != list.end() ) {
2031
myProcessing( *it );
2037
Q3ValueList<Q3Dns::MailServer> Q3Dns::mailServers() const
2039
#if defined(Q3DNS_DEBUG)
2040
qDebug( "Q3Dns::mailServers (%s)", l.ascii() );
2042
Q3ValueList<Q3Dns::MailServer> result;
2046
Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
2049
while( (rr=cached->current()) != 0 ) {
2050
if ( rr->current && !rr->nxdomain ) {
2051
MailServer ms( rr->target, rr->priority );
2052
result.append( ms );
2062
\class Q3Dns::Server
2063
\brief The Q3Dns::Server class is described in Q3Dns::servers().
2069
Returns a list of servers if the record type is \c Srv. The class
2070
\c Q3Dns::Server contains the following public variables:
2072
\i QString Q3Dns::Server::name
2073
\i Q_UINT16 Q3Dns::Server::priority
2074
\i Q_UINT16 Q3Dns::Server::weight
2075
\i Q_UINT16 Q3Dns::Server::port
2078
Note that if you want to iterate over the list, you should iterate
2081
Q3ValueList<Q3Dns::Server> list = myDns.servers();
2082
Q3ValueList<Q3Dns::Server>::Iterator it = list.begin();
2083
while( it != list.end() ) {
2084
myProcessing( *it );
2089
Q3ValueList<Q3Dns::Server> Q3Dns::servers() const
2091
#if defined(Q3DNS_DEBUG)
2092
qDebug( "Q3Dns::servers (%s)", l.ascii() );
2094
Q3ValueList<Q3Dns::Server> result;
2098
Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
2101
while( (rr=cached->current()) != 0 ) {
2102
if ( rr->current && !rr->nxdomain ) {
2103
Server s( rr->target, rr->priority, rr->weight, rr->port );
2114
Returns a list of host names if the record type is \c Ptr.
2116
Note that if you want to iterate over the list, you should iterate
2119
QStringList list = myDns.hostNames();
2120
QStringList::Iterator it = list.begin();
2121
while( it != list.end() ) {
2122
myProcessing( *it );
2128
QStringList Q3Dns::hostNames() const
2130
#if defined(Q3DNS_DEBUG)
2131
qDebug( "Q3Dns::hostNames (%s)", l.ascii() );
2137
Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
2140
while( (rr=cached->current()) != 0 ) {
2141
if ( rr->current && !rr->nxdomain ) {
2142
QString str( rr->target );
2143
result.append( str );
2153
Returns a list of texts if the record type is \c Txt.
2155
Note that if you want to iterate over the list, you should iterate
2158
QStringList list = myDns.texts();
2159
QStringList::Iterator it = list.begin();
2160
while( it != list.end() ) {
2161
myProcessing( *it );
2166
QStringList Q3Dns::texts() const
2168
#if defined(Q3DNS_DEBUG)
2169
qDebug( "Q3Dns::texts (%s)", l.ascii() );
2175
Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
2178
while( (rr=cached->current()) != 0 ) {
2179
if ( rr->current && !rr->nxdomain ) {
2180
QString str( rr->text );
2181
result.append( str );
2191
Returns the canonical name for this DNS node. (This works
2192
regardless of what recordType() is set to.)
2194
If the canonical name isn't known, this function returns a null
2197
The canonical name of a DNS node is its full name, or the full
2198
name of the target of its CNAME. For example, if l.trolltech.com
2199
is a CNAME to lillian.troll.no, and the search path for Q3Dns is
2200
"trolltech.com", then the canonical name for all of "lillian",
2201
"l", "lillian.troll.no." and "l.trolltech.com" is
2202
"lillian.troll.no.".
2205
QString Q3Dns::canonicalName() const
2207
// the cname should work regardless of the recordType(), so set the record
2208
// type temporarily to cname when you look at the cache
2209
Q3Dns *that = (Q3Dns*) this; // mutable function
2210
RecordType oldType = t;
2212
Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( that );
2216
while( (rr=cached->current()) != 0 ) {
2217
if ( rr->current && !rr->nxdomain && rr->domain ) {
2227
#if defined(Q_DNS_SYNCHRONOUS)
2230
void Q3Dns::connectNotify( const char *signal )
2232
if ( d->noEventLoop && qstrcmp(signal,SIGNAL(resultsReady()) )==0 ) {
2233
doSynchronousLookup();
2238
#if defined(Q_OS_WIN32) || defined(Q_OS_CYGWIN)
2240
#if defined(Q_DNS_SYNCHRONOUS)
2241
void Q3Dns::doSynchronousLookup()
2243
// ### not implemented yet
2247
// the following typedefs are needed for GetNetworkParams() API call
2248
#ifndef IP_TYPES_INCLUDED
2249
#define MAX_HOSTNAME_LEN 128
2250
#define MAX_DOMAIN_NAME_LEN 128
2251
#define MAX_SCOPE_ID_LEN 256
2254
} IP_ADDRESS_STRING, *PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING;
2255
typedef struct _IP_ADDR_STRING {
2256
struct _IP_ADDR_STRING* Next;
2257
IP_ADDRESS_STRING IpAddress;
2258
IP_MASK_STRING IpMask;
2260
} IP_ADDR_STRING, *PIP_ADDR_STRING;
2262
char HostName[MAX_HOSTNAME_LEN + 4] ;
2263
char DomainName[MAX_DOMAIN_NAME_LEN + 4];
2264
PIP_ADDR_STRING CurrentDnsServer;
2265
IP_ADDR_STRING DnsServerList;
2267
char ScopeId[MAX_SCOPE_ID_LEN + 4];
2271
} FIXED_INFO, *PFIXED_INFO;
2273
typedef DWORD (WINAPI *GNP)( PFIXED_INFO, PULONG );
2275
// ### FIXME: this code is duplicated in qfiledialog.cpp
2276
static QString getWindowsRegString( HKEY key, const QString &subKey )
2281
DWORD bsz = sizeof(buf);
2282
int r = RegQueryValueEx( key, (TCHAR*)subKey.ucs2(), 0, 0, (LPBYTE)buf, &bsz );
2283
if ( r == ERROR_SUCCESS ) {
2284
s = QString::fromUcs2( (unsigned short *)buf );
2285
} else if ( r == ERROR_MORE_DATA ) {
2286
char *ptr = new char[bsz+1];
2287
r = RegQueryValueEx( key, (TCHAR*)subKey.ucs2(), 0, 0, (LPBYTE)ptr, &bsz );
2288
if ( r == ERROR_SUCCESS )
2294
DWORD bsz = sizeof(buf);
2295
int r = RegQueryValueExA( key, subKey.local8Bit(), 0, 0, (LPBYTE)buf, &bsz );
2296
if ( r == ERROR_SUCCESS ) {
2298
} else if ( r == ERROR_MORE_DATA ) {
2299
char *ptr = new char[bsz+1];
2300
r = RegQueryValueExA( key, subKey.local8Bit(), 0, 0, (LPBYTE)ptr, &bsz );
2301
if ( r == ERROR_SUCCESS )
2309
static bool getDnsParamsFromRegistry( const QString &path,
2310
QString *domainName, QString *nameServer, QString *searchList )
2315
r = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
2316
(TCHAR*)path.ucs2(),
2319
r = RegOpenKeyExA( HKEY_LOCAL_MACHINE,
2324
if ( r == ERROR_SUCCESS ) {
2325
*domainName = getWindowsRegString( k, "DhcpDomain" );
2326
if ( domainName->isEmpty() )
2327
*domainName = getWindowsRegString( k, "Domain" );
2329
*nameServer = getWindowsRegString( k, "DhcpNameServer" );
2330
if ( nameServer->isEmpty() )
2331
*nameServer = getWindowsRegString( k, "NameServer" );
2333
*searchList = getWindowsRegString( k, "SearchList" );
2336
return r == ERROR_SUCCESS;
2339
void Q3Dns::doResInit()
2345
ns = new Q3PtrList<QHostAddress>;
2346
ns->setAutoDelete( true );
2347
domains = new Q3StrList( true );
2348
domains->setAutoDelete( true );
2350
QString domainName, nameServer, searchList;
2352
bool gotNetworkParams = false;
2353
// try the API call GetNetworkParams() first and use registry lookup only
2356
HINSTANCE hinstLib = LoadLibraryW( L"iphlpapi" );
2358
HINSTANCE hinstLib = LoadLibraryA( "iphlpapi" );
2360
if ( hinstLib != 0 ) {
2362
GNP getNetworkParams = (GNP) GetProcAddressW( hinstLib, L"GetNetworkParams" );
2364
GNP getNetworkParams = (GNP) GetProcAddress( hinstLib, "GetNetworkParams" );
2366
if ( getNetworkParams != 0 ) {
2369
res = getNetworkParams( 0, &l );
2370
if ( res == ERROR_BUFFER_OVERFLOW ) {
2371
FIXED_INFO *finfo = (FIXED_INFO*)new char[l];
2372
res = getNetworkParams( finfo, &l );
2373
if ( res == ERROR_SUCCESS ) {
2374
domainName = finfo->DomainName;
2376
IP_ADDR_STRING *dnsServer = &finfo->DnsServerList;
2377
while ( dnsServer != 0 ) {
2378
nameServer += dnsServer->IpAddress.String;
2379
dnsServer = dnsServer->Next;
2380
if ( dnsServer != 0 )
2385
gotNetworkParams = true;
2390
FreeLibrary( hinstLib );
2392
if ( !gotNetworkParams ) {
2393
if ( getDnsParamsFromRegistry(
2394
QString( "System\\CurrentControlSet\\Services\\Tcpip\\Parameters" ),
2395
&domainName, &nameServer, &searchList )) {
2398
} else if ( getDnsParamsFromRegistry(
2399
QString( "System\\CurrentControlSet\\Services\\VxD\\MSTCP" ),
2400
&domainName, &nameServer, &searchList )) {
2404
// Could not access the TCP/IP parameters
2406
nameServer = "127.0.0.1";
2412
nameServer = nameServer.simplifyWhiteSpace();
2414
if ( !nameServer.isEmpty() ) {
2417
last = nameServer.find( separator, first );
2419
last = nameServer.length();
2420
Q3Dns tmp( nameServer.mid( first, last-first ), Q3Dns::A );
2421
Q3ValueList<QHostAddress> address = tmp.addresses();
2422
Q_LONG i = address.count();
2424
ns->append( new QHostAddress(address[--i]) );
2426
} while( first < (int)nameServer.length() );
2429
searchList = searchList + " " + domainName;
2430
searchList = searchList.simplifyWhiteSpace().lower();
2433
last = searchList.find( separator, first );
2435
last = searchList.length();
2436
domains->append( qstrdup( searchList.mid( first, last-first ).latin1() ) );
2438
} while( first < (int)searchList.length() );
2441
#elif defined(Q_OS_UNIX)
2443
#if defined(Q_DNS_SYNCHRONOUS)
2444
void Q3Dns::doSynchronousLookup()
2446
if ( t!=None && !l.isEmpty() ) {
2447
Q3ValueListIterator<QString> it = n.begin();
2448
Q3ValueListIterator<QString> end = n.end();
2473
type = (char)255; // any
2476
while( it != end ) {
2479
QByteArray ba( 512 );
2480
int len = res_search( s.latin1(), 1, type, (uchar*)ba.data(), ba.size() );
2484
Q3DnsQuery * query = new Q3DnsQuery;
2485
query->started = now();
2489
Q3DnsAnswer a( ba, query );
2491
} else if ( len == -1 ) {
2495
emit resultsReady();
2500
#if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 3)))
2501
#define Q_MODERN_RES_API
2503
static int q3dns_res_init()
2506
extern void *qt_mac_resolve_sys(void *handle, const char *symbol);
2508
typedef int (*PtrRes_init)();
2509
static PtrRes_init ptrRes_init = 0;
2511
ptrRes_init = (PtrRes_init)qt_mac_resolve_sys(RTLD_NEXT, "res_init");
2513
return (*ptrRes_init)();
2516
#elif defined(Q_OS_UNIX)
2519
return 0; // not called at all on Windows.
2524
void Q3Dns::doResInit()
2528
ns = new Q3PtrList<QHostAddress>;
2529
ns->setAutoDelete( true );
2530
domains = new Q3StrList( true );
2531
domains->setAutoDelete( true );
2533
// read resolv.conf manually.
2534
QFile resolvConf("/etc/resolv.conf");
2535
if (resolvConf.open(QIODevice::ReadOnly)) {
2536
QTextStream stream( &resolvConf );
2539
while ( !stream.atEnd() ) {
2540
line = stream.readLine();
2541
QStringList list = QStringList::split( " ", line );
2542
if( list.isEmpty() )
2544
const QString type = list[0].lower();
2546
if ( type == "nameserver" ) {
2547
QHostAddress *address = new QHostAddress();
2548
if ( address->setAddress( QString(line[1]) ) ) {
2549
// only add ipv6 addresses from resolv.conf if
2550
// this host supports ipv6.
2551
if ( address->isIPv4Address() || ipv6support )
2552
ns->append( address );
2556
} else if ( type == "search" ) {
2557
QStringList srch = QStringList::split( " ", list[1] );
2558
for ( QStringList::Iterator i = srch.begin(); i != srch.end(); ++i )
2559
domains->append( (*i).lower().local8Bit() );
2561
} else if ( type == "domain" ) {
2562
domains->append( list[1].lower().local8Bit() );
2567
if (ns->isEmpty()) {
2568
#if defined(Q_MODERN_RES_API)
2569
struct __res_state res;
2572
// find the name servers to use
2573
for( i=0; i < MAXNS && i < res.nscount; i++ )
2574
ns->append( new QHostAddress( ntohl( res.nsaddr_list[i].sin_addr.s_addr ) ) );
2575
# if defined(MAXDFLSRCH)
2576
for( i=0; i < MAXDFLSRCH; i++ ) {
2577
if ( res.dnsrch[i] && *(res.dnsrch[i]) )
2578
domains->append( QString::fromLatin1( res.dnsrch[i] ).lower().local8Bit() );
2583
if ( *res.defdname )
2584
domains->append( QString::fromLatin1( res.defdname ).lower().local8Bit() );
2588
// find the name servers to use
2589
for( i=0; i < MAXNS && i < _res.nscount; i++ )
2590
ns->append( new QHostAddress( ntohl( _res.nsaddr_list[i].sin_addr.s_addr ) ) );
2591
# if defined(MAXDFLSRCH)
2592
for( i=0; i < MAXDFLSRCH; i++ ) {
2593
if ( _res.dnsrch[i] && *(_res.dnsrch[i]) )
2594
domains->append( QString::fromLatin1( _res.dnsrch[i] ).lower().local8Bit() );
2599
if ( *_res.defdname )
2600
domains->append( QString::fromLatin1( _res.defdname ).lower().local8Bit() );
2603
// the code above adds "0.0.0.0" as a name server at the slightest
2604
// hint of trouble. so remove those again.
2606
while( ns->current() ) {
2607
if ( ns->current()->isNull() )
2614
QFile hosts( QString::fromLatin1( "/etc/hosts" ) );
2615
if ( hosts.open( QIODevice::ReadOnly ) ) {
2616
// read the /etc/hosts file, creating long-life A and PTR RRs
2617
// for the things we find.
2618
QTextStream i( &hosts );
2620
while( !i.atEnd() ) {
2621
line = i.readLine().simplifyWhiteSpace().lower();
2623
while( (int) n < line.length() && line[(int)n] != '#' )
2627
while( (int) n < line.length() && !line[(int)n].isSpace() )
2629
QString ip = line.left( n );
2632
if ( ( a.isIPv4Address() || a.isIPv6Address() ) && !a.isNull() ) {
2634
line = line.mid( n+1 );
2636
while( (int) n < line.length() && !line[(int)n].isSpace() )
2638
QString hostname = line.left( n );
2639
// ### in case of bad syntax, hostname is invalid. do we care?
2641
Q3DnsRR * rr = new Q3DnsRR( hostname );
2642
if ( a.isIPv4Address() )
2645
rr->t = Q3Dns::Aaaa;
2647
rr->deleteTime = UINT_MAX;
2648
rr->expireTime = UINT_MAX;
2652
Q3DnsRR * ptr = new Q3DnsRR( Q3Dns::toInAddrArpaDomain( a ) );
2653
ptr->t = Q3Dns::Ptr;
2654
ptr->target = hostname;
2655
ptr->deleteTime = UINT_MAX;
2656
ptr->expireTime = UINT_MAX;
2657
ptr->current = true;