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

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

Transferred Qt for OS/2 version 3.3.1-rc5 sources from the CVS

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