source: trunk/src/qt3support/network/q3dns.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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