source: trunk/src/network/qdns.cpp@ 7

Last change on this file since 7 was 2, checked in by dmik, 20 years ago

Imported xplatform parts of the official release 3.3.1 from Trolltech

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