|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the Qt3Support module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
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 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "qplatformdefs.h" |
|
43 #include "qbytearray.h" |
|
44 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_CYGWIN) |
|
45 # include "qt_windows.h" |
|
46 #else |
|
47 # include <sys/types.h> |
|
48 # include <netinet/in.h> |
|
49 # include <arpa/nameser.h> |
|
50 # include <resolv.h> |
|
51 extern "C" int res_init(); |
|
52 #endif |
|
53 |
|
54 // POSIX Large File Support redefines open -> open64 |
|
55 #if defined(open) |
|
56 # undef open |
|
57 #endif |
|
58 |
|
59 // POSIX Large File Support redefines truncate -> truncate64 |
|
60 #if defined(truncate) |
|
61 # undef truncate |
|
62 #endif |
|
63 |
|
64 // Solaris redefines connect -> __xnet_connect with _XOPEN_SOURCE_EXTENDED. |
|
65 #if defined(connect) |
|
66 # undef connect |
|
67 #endif |
|
68 |
|
69 // UnixWare 7 redefines socket -> _socket |
|
70 #if defined(socket) |
|
71 # undef socket |
|
72 #endif |
|
73 |
|
74 #include "q3dns.h" |
|
75 |
|
76 #ifndef QT_NO_DNS |
|
77 |
|
78 #include "qdatetime.h" |
|
79 #include "q3dict.h" |
|
80 #include "q3ptrlist.h" |
|
81 #include "qstring.h" |
|
82 #include "qtimer.h" |
|
83 #include "qapplication.h" |
|
84 #include "q3ptrvector.h" |
|
85 #include "q3strlist.h" |
|
86 #include "q3ptrdict.h" |
|
87 #include "qfile.h" |
|
88 #include "qtextstream.h" |
|
89 #include "q3socketdevice.h" |
|
90 #include "q3cleanuphandler.h" |
|
91 #include <limits.h> |
|
92 |
|
93 QT_BEGIN_NAMESPACE |
|
94 |
|
95 //#define Q3DNS_DEBUG |
|
96 |
|
97 static Q_UINT16 theId; // ### seeded started by now() |
|
98 |
|
99 |
|
100 static QDateTime * originOfTime = 0; |
|
101 |
|
102 static Q3CleanupHandler<QDateTime> q3dns_cleanup_time; |
|
103 |
|
104 static Q_UINT32 now() |
|
105 { |
|
106 if ( originOfTime ) |
|
107 return originOfTime->secsTo( QDateTime::currentDateTime() ); |
|
108 |
|
109 originOfTime = new QDateTime( QDateTime::currentDateTime() ); |
|
110 theId = originOfTime->time().msec() * 60 + originOfTime->time().second(); |
|
111 q3dns_cleanup_time.add( &originOfTime ); |
|
112 return 0; |
|
113 } |
|
114 |
|
115 |
|
116 static Q3PtrList<QHostAddress> * theNs = 0; |
|
117 static Q3StrList * theDomains = 0; |
|
118 static bool ipv6support = false; |
|
119 |
|
120 class Q3DnsPrivate { |
|
121 public: |
|
122 Q3DnsPrivate() : queryTimer( 0 ), noNames(false) |
|
123 { |
|
124 #if defined(Q_DNS_SYNCHRONOUS) |
|
125 #if defined(Q_OS_UNIX) |
|
126 noEventLoop = qApp==0 || qApp->loopLevel()==0; |
|
127 #else |
|
128 noEventLoop = false; |
|
129 #endif |
|
130 #endif |
|
131 } |
|
132 ~Q3DnsPrivate() |
|
133 { |
|
134 delete queryTimer; |
|
135 } |
|
136 private: |
|
137 QTimer * queryTimer; |
|
138 bool noNames; |
|
139 #if defined(Q_DNS_SYNCHRONOUS) |
|
140 bool noEventLoop; |
|
141 #endif |
|
142 |
|
143 friend class Q3Dns; |
|
144 friend class Q3DnsAnswer; |
|
145 }; |
|
146 |
|
147 |
|
148 class Q3DnsRR; |
|
149 class Q3DnsDomain; |
|
150 |
|
151 |
|
152 |
|
153 // Q3DnsRR is the class used to store a single RR. Q3DnsRR can store |
|
154 // all of the supported RR types. a Q3DnsRR is always cached. |
|
155 |
|
156 // Q3DnsRR is mostly constructed from the outside. a but hacky, but |
|
157 // permissible since the entire class is internal. |
|
158 |
|
159 class Q3DnsRR { |
|
160 public: |
|
161 Q3DnsRR( const QString & label ); |
|
162 ~Q3DnsRR(); |
|
163 |
|
164 public: |
|
165 Q3DnsDomain * domain; |
|
166 Q3Dns::RecordType t; |
|
167 bool nxdomain; |
|
168 bool current; |
|
169 Q_UINT32 expireTime; |
|
170 Q_UINT32 deleteTime; |
|
171 // somewhat space-wasting per-type data |
|
172 // a / aaaa |
|
173 QHostAddress address; |
|
174 // cname / mx / srv / ptr |
|
175 QString target; |
|
176 // mx / srv |
|
177 Q_UINT16 priority; |
|
178 // srv |
|
179 Q_UINT16 weight; |
|
180 Q_UINT16 port; |
|
181 // txt |
|
182 QString text; // could be overloaded into target... |
|
183 private: |
|
184 |
|
185 }; |
|
186 |
|
187 |
|
188 class Q3DnsDomain { |
|
189 public: |
|
190 Q3DnsDomain( const QString & label ); |
|
191 ~Q3DnsDomain(); |
|
192 |
|
193 static void add( const QString & label, Q3DnsRR * ); |
|
194 static Q3PtrList<Q3DnsRR> * cached( const Q3Dns * ); |
|
195 |
|
196 void take( Q3DnsRR * ); |
|
197 |
|
198 void sweep( Q_UINT32 thisSweep ); |
|
199 |
|
200 bool isEmpty() const { return rrs == 0 || rrs->isEmpty(); } |
|
201 |
|
202 QString name() const { return l; } |
|
203 |
|
204 public: |
|
205 QString l; |
|
206 Q3PtrList<Q3DnsRR> * rrs; |
|
207 }; |
|
208 |
|
209 |
|
210 class Q3DnsQuery: public QTimer { // this inheritance is a very evil hack |
|
211 public: |
|
212 Q3DnsQuery(): |
|
213 id( 0 ), t( Q3Dns::None ), step(0), started(0), |
|
214 dns( new Q3PtrDict<void>(17) ) {} |
|
215 ~Q3DnsQuery() { delete dns; } |
|
216 Q_UINT16 id; |
|
217 Q3Dns::RecordType t; |
|
218 QString l; |
|
219 |
|
220 uint step; |
|
221 Q_UINT32 started; |
|
222 |
|
223 Q3PtrDict<void> * dns; |
|
224 }; |
|
225 |
|
226 |
|
227 |
|
228 class Q3DnsAnswer { |
|
229 public: |
|
230 Q3DnsAnswer( Q3DnsQuery * ); |
|
231 Q3DnsAnswer( const QByteArray &, Q3DnsQuery * ); |
|
232 ~Q3DnsAnswer(); |
|
233 |
|
234 void parse(); |
|
235 void notify(); |
|
236 |
|
237 bool ok; |
|
238 |
|
239 private: |
|
240 Q3DnsQuery * query; |
|
241 |
|
242 Q_UINT8 * answer; |
|
243 int size; |
|
244 int pp; |
|
245 |
|
246 Q3PtrList<Q3DnsRR> * rrs; |
|
247 |
|
248 // convenience |
|
249 int next; |
|
250 int ttl; |
|
251 QString label; |
|
252 Q3DnsRR * rr; |
|
253 |
|
254 QString readString(bool multipleLabels = true); |
|
255 void parseA(); |
|
256 void parseAaaa(); |
|
257 void parseMx(); |
|
258 void parseSrv(); |
|
259 void parseCname(); |
|
260 void parsePtr(); |
|
261 void parseTxt(); |
|
262 void parseNs(); |
|
263 }; |
|
264 |
|
265 |
|
266 Q3DnsRR::Q3DnsRR( const QString & label ) |
|
267 : domain( 0 ), t( Q3Dns::None ), |
|
268 nxdomain( false ), current( false ), |
|
269 expireTime( 0 ), deleteTime( 0 ), |
|
270 priority( 0 ), weight( 0 ), port( 0 ) |
|
271 { |
|
272 Q3DnsDomain::add( label, this ); |
|
273 } |
|
274 |
|
275 |
|
276 // not supposed to be deleted except by Q3DnsDomain |
|
277 Q3DnsRR::~Q3DnsRR() |
|
278 { |
|
279 // nothing is necessary |
|
280 } |
|
281 |
|
282 |
|
283 // this one just sticks in a NXDomain |
|
284 Q3DnsAnswer::Q3DnsAnswer( Q3DnsQuery * query_ ) |
|
285 { |
|
286 ok = true; |
|
287 |
|
288 answer = 0; |
|
289 size = 0; |
|
290 query = query_; |
|
291 pp = 0; |
|
292 rrs = new Q3PtrList<Q3DnsRR>; |
|
293 rrs->setAutoDelete( false ); |
|
294 next = size; |
|
295 ttl = 0; |
|
296 label.clear(); |
|
297 rr = 0; |
|
298 |
|
299 Q3DnsRR * newrr = new Q3DnsRR( query->l ); |
|
300 newrr->t = query->t; |
|
301 newrr->deleteTime = query->started + 10; |
|
302 newrr->expireTime = query->started + 10; |
|
303 newrr->nxdomain = true; |
|
304 newrr->current = true; |
|
305 rrs->append( newrr ); |
|
306 } |
|
307 |
|
308 |
|
309 Q3DnsAnswer::Q3DnsAnswer( const QByteArray& answer_, |
|
310 Q3DnsQuery * query_ ) |
|
311 { |
|
312 ok = true; |
|
313 |
|
314 answer = (Q_UINT8 *)(answer_.data()); |
|
315 size = (int)answer_.size(); |
|
316 query = query_; |
|
317 pp = 0; |
|
318 rrs = new Q3PtrList<Q3DnsRR>; |
|
319 rrs->setAutoDelete( false ); |
|
320 next = size; |
|
321 ttl = 0; |
|
322 label.clear(); |
|
323 rr = 0; |
|
324 } |
|
325 |
|
326 |
|
327 Q3DnsAnswer::~Q3DnsAnswer() |
|
328 { |
|
329 if ( !ok && rrs ) { |
|
330 Q3PtrListIterator<Q3DnsRR> it( *rrs ); |
|
331 Q3DnsRR * tmprr; |
|
332 while( (tmprr=it.current()) != 0 ) { |
|
333 ++it; |
|
334 tmprr->t = Q3Dns::None; // will be deleted soonish |
|
335 } |
|
336 } |
|
337 delete rrs; |
|
338 } |
|
339 |
|
340 |
|
341 QString Q3DnsAnswer::readString(bool multipleLabels) |
|
342 { |
|
343 int p = pp; |
|
344 QString r; |
|
345 Q_UINT8 b; |
|
346 for( ;; ) { |
|
347 b = 128; |
|
348 // Read one character |
|
349 if ( p >= 0 && p < size ) |
|
350 b = answer[p]; |
|
351 |
|
352 switch( b >> 6 ) { |
|
353 case 0: |
|
354 // b is less than 64 |
|
355 p++; |
|
356 |
|
357 // Detect end of data |
|
358 if ( b == 0 ) { |
|
359 if ( p > pp ) |
|
360 pp = p; |
|
361 return r.isNull() ? QLatin1String( "." ) : r; |
|
362 } |
|
363 |
|
364 // Read a label of size 'b' characters |
|
365 if ( !r.isNull() ) |
|
366 r += QLatin1Char('.'); |
|
367 while( b-- > 0 ) |
|
368 r += QLatin1Char( answer[p++] ); |
|
369 |
|
370 // Return immediately if we were only supposed to read one |
|
371 // label. |
|
372 if (!multipleLabels) |
|
373 return r; |
|
374 |
|
375 break; |
|
376 default: |
|
377 // Ignore unrecognized control character, or p was out of |
|
378 // range. |
|
379 goto not_ok; |
|
380 case 3: |
|
381 // Use the next character to determine the relative offset |
|
382 // to jump to before continuing the packet parsing. |
|
383 int q = ( (answer[p] & 0x3f) << 8 ) + answer[p+1]; |
|
384 |
|
385 if ( q >= pp || q >= p ) |
|
386 goto not_ok; |
|
387 if ( p >= pp ) |
|
388 pp = p + 2; |
|
389 p = q; |
|
390 } |
|
391 } |
|
392 not_ok: |
|
393 ok = false; |
|
394 return QString(); |
|
395 } |
|
396 |
|
397 |
|
398 |
|
399 void Q3DnsAnswer::parseA() |
|
400 { |
|
401 if ( next != pp + 4 ) { |
|
402 #if defined(Q3DNS_DEBUG) |
|
403 qDebug( "Q3Dns: saw %d bytes long IN A for %s", |
|
404 next - pp, label.ascii() ); |
|
405 #endif |
|
406 return; |
|
407 } |
|
408 |
|
409 rr = new Q3DnsRR( label ); |
|
410 rr->t = Q3Dns::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(Q3DNS_DEBUG) |
|
416 qDebug( "Q3Dns: saw %s IN A %s (ttl %d)", label.ascii(), |
|
417 rr->address.toString().ascii(), ttl ); |
|
418 #endif |
|
419 } |
|
420 |
|
421 |
|
422 void Q3DnsAnswer::parseAaaa() |
|
423 { |
|
424 if ( next != pp + 16 ) { |
|
425 #if defined(Q3DNS_DEBUG) |
|
426 qDebug( "Q3Dns: saw %d bytes long IN Aaaa for %s", |
|
427 next - pp, label.ascii() ); |
|
428 #endif |
|
429 return; |
|
430 } |
|
431 |
|
432 rr = new Q3DnsRR( label ); |
|
433 rr->t = Q3Dns::Aaaa; |
|
434 rr->address = QHostAddress( answer+pp ); |
|
435 #if defined(Q3DNS_DEBUG) |
|
436 qDebug( "Q3Dns: saw %s IN Aaaa %s (ttl %d)", label.ascii(), |
|
437 rr->address.toString().ascii(), ttl ); |
|
438 #endif |
|
439 } |
|
440 |
|
441 |
|
442 |
|
443 void Q3DnsAnswer::parseMx() |
|
444 { |
|
445 if ( next < pp + 2 ) { |
|
446 #if defined(Q3DNS_DEBUG) |
|
447 qDebug( "Q3Dns: saw %d bytes long IN MX for %s", |
|
448 next - pp, label.ascii() ); |
|
449 #endif |
|
450 return; |
|
451 } |
|
452 |
|
453 rr = new Q3DnsRR( label ); |
|
454 rr->priority = (answer[pp] << 8) + answer[pp+1]; |
|
455 pp += 2; |
|
456 rr->target = readString().lower(); |
|
457 if ( !ok ) { |
|
458 #if defined(Q3DNS_DEBUG) |
|
459 qDebug( "Q3Dns: saw bad string in MX for %s", label.ascii() ); |
|
460 #endif |
|
461 return; |
|
462 } |
|
463 rr->t = Q3Dns::Mx; |
|
464 #if defined(Q3DNS_DEBUG) |
|
465 qDebug( "Q3Dns: saw %s IN MX %d %s (ttl %d)", label.ascii(), |
|
466 rr->priority, rr->target.ascii(), ttl ); |
|
467 #endif |
|
468 } |
|
469 |
|
470 |
|
471 void Q3DnsAnswer::parseSrv() |
|
472 { |
|
473 if ( next < pp + 6 ) { |
|
474 #if defined(Q3DNS_DEBUG) |
|
475 qDebug( "Q3Dns: saw %d bytes long IN SRV for %s", |
|
476 next - pp, label.ascii() ); |
|
477 #endif |
|
478 return; |
|
479 } |
|
480 |
|
481 rr = new Q3DnsRR( 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(Q3DNS_DEBUG) |
|
489 qDebug( "Q3Dns: saw bad string in SRV for %s", label.ascii() ); |
|
490 #endif |
|
491 return; |
|
492 } |
|
493 rr->t = Q3Dns::Srv; |
|
494 #if defined(Q3DNS_DEBUG) |
|
495 qDebug( "Q3Dns: 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 |
|
501 void Q3DnsAnswer::parseCname() |
|
502 { |
|
503 QString target = readString().lower(); |
|
504 if ( !ok ) { |
|
505 #if defined(Q3DNS_DEBUG) |
|
506 qDebug( "Q3Dns: saw bad cname for for %s", label.ascii() ); |
|
507 #endif |
|
508 return; |
|
509 } |
|
510 |
|
511 rr = new Q3DnsRR( label ); |
|
512 rr->t = Q3Dns::Cname; |
|
513 rr->target = target; |
|
514 #if defined(Q3DNS_DEBUG) |
|
515 qDebug( "Q3Dns: saw %s IN CNAME %s (ttl %d)", label.ascii(), |
|
516 rr->target.ascii(), ttl ); |
|
517 #endif |
|
518 } |
|
519 |
|
520 |
|
521 void Q3DnsAnswer::parseNs() |
|
522 { |
|
523 QString target = readString().lower(); |
|
524 if ( !ok ) { |
|
525 #if defined(Q3DNS_DEBUG) |
|
526 qDebug( "Q3Dns: saw bad cname for for %s", label.ascii() ); |
|
527 #endif |
|
528 return; |
|
529 } |
|
530 |
|
531 // parse, but ignore |
|
532 |
|
533 #if defined(Q3DNS_DEBUG) |
|
534 qDebug( "Q3Dns: saw %s IN NS %s (ttl %d)", label.ascii(), |
|
535 target.ascii(), ttl ); |
|
536 #endif |
|
537 } |
|
538 |
|
539 |
|
540 void Q3DnsAnswer::parsePtr() |
|
541 { |
|
542 QString target = readString().lower(); |
|
543 if ( !ok ) { |
|
544 #if defined(Q3DNS_DEBUG) |
|
545 qDebug( "Q3Dns: saw bad PTR for for %s", label.ascii() ); |
|
546 #endif |
|
547 return; |
|
548 } |
|
549 |
|
550 rr = new Q3DnsRR( label ); |
|
551 rr->t = Q3Dns::Ptr; |
|
552 rr->target = target; |
|
553 #if defined(Q3DNS_DEBUG) |
|
554 qDebug( "Q3Dns: saw %s IN PTR %s (ttl %d)", label.ascii(), |
|
555 rr->target.ascii(), ttl ); |
|
556 #endif |
|
557 } |
|
558 |
|
559 |
|
560 void Q3DnsAnswer::parseTxt() |
|
561 { |
|
562 QString text = readString(false); |
|
563 if ( !ok ) { |
|
564 #if defined(Q3DNS_DEBUG) |
|
565 qDebug( "Q3Dns: saw bad TXT for for %s", label.ascii() ); |
|
566 #endif |
|
567 return; |
|
568 } |
|
569 |
|
570 rr = new Q3DnsRR( label ); |
|
571 rr->t = Q3Dns::Txt; |
|
572 rr->text = text; |
|
573 #if defined(Q3DNS_DEBUG) |
|
574 qDebug( "Q3Dns: saw %s IN TXT \"%s\" (ttl %d)", label.ascii(), |
|
575 rr->text.ascii(), ttl ); |
|
576 #endif |
|
577 } |
|
578 |
|
579 |
|
580 void Q3DnsAnswer::parse() |
|
581 { |
|
582 // okay, do the work... |
|
583 if ( (answer[2] & 0x78) != 0 ) { |
|
584 #if defined(Q3DNS_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(Q3DNS_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(Q3DNS_DEBUG) |
|
609 qDebug( "DNS Manager: saw NXDomain for %s", query->l.ascii() ); |
|
610 #endif |
|
611 // NXDomain. cache that for one minute. |
|
612 rr = new Q3DnsRR( 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(Q3DNS_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(Q3DNS_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(Q3DNS_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(Q3DNS_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(Q3DNS_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 Q3Dict<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 == Q3Dns::A || rr->t == Q3Dns::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 Q3PtrList<Q3DnsRR> * drrs = rr->domain->rrs; |
|
767 drrs->first(); |
|
768 Q3DnsRR * 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(Q3DNS_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 = Q3Dns::None; |
|
787 rr->expireTime = QMAX( older->expireTime, rr->expireTime ); |
|
788 rr->deleteTime = QMAX( older->deleteTime, rr->deleteTime ); |
|
789 older->deleteTime = 0; |
|
790 #if defined(Q3DNS_DEBUG) |
|
791 qDebug( " adjusted expire is %d", rr->expireTime ); |
|
792 #endif |
|
793 } |
|
794 drrs->next(); |
|
795 } |
|
796 } |
|
797 } |
|
798 |
|
799 #if defined(Q3DNS_DEBUG) |
|
800 //qDebug( "DNS Manager: ()" ); |
|
801 #endif |
|
802 } |
|
803 |
|
804 |
|
805 class Q3DnsUgleHack: public Q3Dns { |
|
806 public: |
|
807 void ugle( bool emitAnyway=false ); |
|
808 }; |
|
809 |
|
810 |
|
811 void Q3DnsAnswer::notify() |
|
812 { |
|
813 if ( !rrs || !ok || !query || !query->dns ) |
|
814 return; |
|
815 |
|
816 Q3PtrDict<void> notified; |
|
817 notified.setAutoDelete( false ); |
|
818 |
|
819 Q3PtrDictIterator<void> it( *query->dns ); |
|
820 Q3Dns * dns; |
|
821 it.toFirst(); |
|
822 while( (dns=(Q3Dns*)(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(Q3DNS_DEBUG) |
|
828 qDebug( "DNS Manager: found no answers!" ); |
|
829 #endif |
|
830 dns->d->noNames = true; |
|
831 ((Q3DnsUgleHack*)dns)->ugle( true ); |
|
832 } else { |
|
833 QStringList n = dns->qualifiedNames(); |
|
834 if ( query && n.contains(query->l) ) |
|
835 ((Q3DnsUgleHack*)dns)->ugle(); |
|
836 #if defined(Q3DNS_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 // Q3DnsManager |
|
850 // |
|
851 // |
|
852 |
|
853 |
|
854 class Q3DnsManager: public Q3DnsSocket { |
|
855 private: |
|
856 public: // just to silence the moronic g++. |
|
857 Q3DnsManager(); |
|
858 ~Q3DnsManager(); |
|
859 public: |
|
860 static Q3DnsManager * manager(); |
|
861 |
|
862 Q3DnsDomain * domain( const QString & ); |
|
863 |
|
864 void transmitQuery( Q3DnsQuery * ); |
|
865 void transmitQuery( int ); |
|
866 |
|
867 // reimplementation of the slots |
|
868 void cleanCache(); |
|
869 void retransmit(); |
|
870 void answer(); |
|
871 |
|
872 public: |
|
873 Q3PtrVector<Q3DnsQuery> queries; |
|
874 Q3Dict<Q3DnsDomain> cache; |
|
875 Q3SocketDevice * ipv4Socket; |
|
876 #if !defined (QT_NO_IPV6) |
|
877 Q3SocketDevice * ipv6Socket; |
|
878 #endif |
|
879 }; |
|
880 |
|
881 |
|
882 |
|
883 static Q3DnsManager * globalManager = 0; |
|
884 |
|
885 static void cleanupDns() |
|
886 { |
|
887 delete globalManager; |
|
888 globalManager = 0; |
|
889 } |
|
890 |
|
891 Q3DnsManager * Q3DnsManager::manager() |
|
892 { |
|
893 if ( !globalManager ) { |
|
894 qAddPostRoutine(cleanupDns); |
|
895 new Q3DnsManager(); |
|
896 } |
|
897 return globalManager; |
|
898 } |
|
899 |
|
900 |
|
901 void Q3DnsUgleHack::ugle( bool emitAnyway) |
|
902 { |
|
903 if ( emitAnyway || !isWorking() ) { |
|
904 #if defined(Q3DNS_DEBUG) |
|
905 qDebug( "DNS Manager: status change for %s (type %d)", |
|
906 label().ascii(), recordType() ); |
|
907 #endif |
|
908 emit resultsReady(); |
|
909 } |
|
910 } |
|
911 |
|
912 |
|
913 Q3DnsManager::Q3DnsManager() |
|
914 : Q3DnsSocket( qApp, "Internal DNS manager" ), |
|
915 queries( Q3PtrVector<Q3DnsQuery>( 0 ) ), |
|
916 cache( Q3Dict<Q3DnsDomain>( 83, false ) ), |
|
917 ipv4Socket( new Q3SocketDevice( Q3SocketDevice::Datagram, Q3SocketDevice::IPv4, 0 ) ) |
|
918 #if !defined (QT_NO_IPV6) |
|
919 , ipv6Socket( new Q3SocketDevice( Q3SocketDevice::Datagram, Q3SocketDevice::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 ( !theNs ) |
|
953 Q3Dns::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 Q3PtrList<QHostAddress> * ns = new Q3PtrList<QHostAddress>; |
|
959 |
|
960 theNs->first(); |
|
961 QHostAddress * h; |
|
962 while( (h=theNs->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(Q3DNS_DEBUG) |
|
969 qDebug( "using name server %s", h->toString().latin1() ); |
|
970 } else { |
|
971 qDebug( "skipping address %s", h->toString().latin1() ); |
|
972 #endif |
|
973 } |
|
974 theNs->next(); |
|
975 } |
|
976 |
|
977 delete theNs; |
|
978 theNs = ns; |
|
979 theNs->setAutoDelete( true ); |
|
980 |
|
981 Q3StrList * domains = new Q3StrList( true ); |
|
982 |
|
983 theDomains->first(); |
|
984 const char * s; |
|
985 while( (s=theDomains->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(Q3DNS_DEBUG) |
|
992 qDebug( "searching domain %s", s ); |
|
993 } else { |
|
994 qDebug( "skipping domain %s", s ); |
|
995 #endif |
|
996 } |
|
997 theDomains->next(); |
|
998 } |
|
999 |
|
1000 delete theDomains; |
|
1001 theDomains = domains; |
|
1002 theDomains->setAutoDelete( true ); |
|
1003 } |
|
1004 |
|
1005 |
|
1006 Q3DnsManager::~Q3DnsManager() |
|
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 |
|
1018 static Q_UINT32 lastSweep = 0; |
|
1019 |
|
1020 void Q3DnsManager::cleanCache() |
|
1021 { |
|
1022 bool again = false; |
|
1023 Q3DictIterator<Q3DnsDomain> it( cache ); |
|
1024 Q3DnsDomain * d; |
|
1025 Q_UINT32 thisSweep = now(); |
|
1026 #if defined(Q3DNS_DEBUG) |
|
1027 qDebug( "Q3DnsManager::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 |
|
1043 void Q3DnsManager::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 |
|
1056 void Q3DnsManager::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(Q3DNS_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(Q3DNS_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(Q3DNS_DEBUG) |
|
1102 qDebug( "DNS Manager: received a query" ); |
|
1103 #endif |
|
1104 return; |
|
1105 } |
|
1106 |
|
1107 Q3DnsQuery * q = queries[i]; |
|
1108 Q3DnsAnswer answer( a, q ); |
|
1109 answer.parse(); |
|
1110 if ( answer.ok ) { |
|
1111 queries.take( i ); |
|
1112 answer.notify(); |
|
1113 delete q; |
|
1114 } |
|
1115 } |
|
1116 |
|
1117 |
|
1118 void Q3DnsManager::transmitQuery( Q3DnsQuery * 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 |
|
1133 void Q3DnsManager::transmitQuery( int i ) |
|
1134 { |
|
1135 if ( i < 0 || i >= (int)queries.size() ) |
|
1136 return; |
|
1137 Q3DnsQuery * 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 Q3DnsAnswer answer( q ); |
|
1143 answer.notify(); |
|
1144 // and then get rid of the query |
|
1145 queries.take( i ); |
|
1146 #if defined(Q3DNS_DEBUG) |
|
1147 qDebug( "DNS Manager: giving up on query 0x%04x", q->id ); |
|
1148 #endif |
|
1149 delete q; |
|
1150 QTimer::singleShot( 0, Q3DnsManager::manager(), SLOT(cleanCache()) ); |
|
1151 // and don't process anything more |
|
1152 return; |
|
1153 } |
|
1154 |
|
1155 if ((q && !q->dns) || q->dns->isEmpty()) |
|
1156 // no one 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 < (uint) q->l.length() ) { |
|
1184 int le = q->l.find( QLatin1Char('.'), 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 Q3Dns::A: |
|
1200 p[pp++] = 1; |
|
1201 break; |
|
1202 case Q3Dns::Aaaa: |
|
1203 p[pp++] = 28; |
|
1204 break; |
|
1205 case Q3Dns::Mx: |
|
1206 p[pp++] = 15; |
|
1207 break; |
|
1208 case Q3Dns::Srv: |
|
1209 p[pp++] = 33; |
|
1210 break; |
|
1211 case Q3Dns::Cname: |
|
1212 p[pp++] = 5; |
|
1213 break; |
|
1214 case Q3Dns::Ptr: |
|
1215 p[pp++] = 12; |
|
1216 break; |
|
1217 case Q3Dns::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 we have no name servers, we should regenerate ns in case |
|
1229 // name servers have recently been defined (like on windows, |
|
1230 // plugging/unplugging the network cable will change the name |
|
1231 // server entries) |
|
1232 if ( !theNs || theNs->isEmpty() ) |
|
1233 Q3Dns::doResInit(); |
|
1234 |
|
1235 if ( !theNs || theNs->isEmpty() ) { |
|
1236 // we don't find any name servers. We fake an NXDomain |
|
1237 // with a very short life time... |
|
1238 Q3DnsAnswer answer( q ); |
|
1239 answer.notify(); |
|
1240 // and then get rid of the query |
|
1241 queries.take( i ); |
|
1242 #if defined(Q3DNS_DEBUG) |
|
1243 qDebug( "DNS Manager: no DNS server found on query 0x%04x", q->id ); |
|
1244 #endif |
|
1245 delete q; |
|
1246 QTimer::singleShot( 1000*10, Q3DnsManager::manager(), SLOT(cleanCache()) ); |
|
1247 // and don't process anything more |
|
1248 return; |
|
1249 } |
|
1250 |
|
1251 QHostAddress receiver = *theNs->at( q->step % theNs->count() ); |
|
1252 if (receiver.isIPv4Address()) |
|
1253 ipv4Socket->writeBlock( p.data(), pp, receiver, 53 ); |
|
1254 #if !defined (QT_NO_IPV6) |
|
1255 else |
|
1256 ipv6Socket->writeBlock( p.data(), pp, receiver, 53 ); |
|
1257 #endif |
|
1258 #if defined(Q3DNS_DEBUG) |
|
1259 qDebug( "issuing query 0x%04x (%d) about %s type %d to %s", |
|
1260 q->id, q->step, q->l.ascii(), q->t, |
|
1261 ns->at( q->step % ns->count() )->toString().ascii() ); |
|
1262 #endif |
|
1263 if ( theNs->count() > 1 && q->step == 0 && queries.count() == 1 ) { |
|
1264 // if it's the first time, and we don't have any other |
|
1265 // outstanding queries, send nonrecursive queries to the other |
|
1266 // name servers too. |
|
1267 p[2] = 0; |
|
1268 QHostAddress * server; |
|
1269 while( (server=theNs->next()) != 0 ) { |
|
1270 if (server->isIPv4Address()) |
|
1271 ipv4Socket->writeBlock( p.data(), pp, *server, 53 ); |
|
1272 #if !defined (QT_NO_IPV6) |
|
1273 else |
|
1274 ipv6Socket->writeBlock( p.data(), pp, *server, 53 ); |
|
1275 #endif |
|
1276 #if defined(Q3DNS_DEBUG) |
|
1277 qDebug( "copying query to %s", server->toString().ascii() ); |
|
1278 #endif |
|
1279 } |
|
1280 } |
|
1281 q->step++; |
|
1282 // some testing indicates that normal dns queries take up to 0.6 |
|
1283 // seconds. the graph becomes steep around that point, and the |
|
1284 // number of errors rises... so it seems good to retry at that |
|
1285 // point. |
|
1286 q->start( q->step < theNs->count() ? 800 : 1500, true ); |
|
1287 } |
|
1288 |
|
1289 |
|
1290 Q3DnsDomain * Q3DnsManager::domain( const QString & label ) |
|
1291 { |
|
1292 Q3DnsDomain * d = cache.find( label ); |
|
1293 if ( !d ) { |
|
1294 d = new Q3DnsDomain( label ); |
|
1295 cache.insert( label, d ); |
|
1296 } |
|
1297 return d; |
|
1298 } |
|
1299 |
|
1300 |
|
1301 // |
|
1302 // |
|
1303 // the Q3DnsDomain class looks after and coordinates queries for Q3DnsRRs for |
|
1304 // each domain, and the cached Q3DnsRRs. (A domain, in DNS terminology, is |
|
1305 // a node in the DNS. "no", "trolltech.com" and "lupinella.troll.no" are |
|
1306 // all domains.) |
|
1307 // |
|
1308 // |
|
1309 |
|
1310 |
|
1311 Q3DnsDomain::Q3DnsDomain( const QString & label ) |
|
1312 { |
|
1313 l = label; |
|
1314 rrs = 0; |
|
1315 } |
|
1316 |
|
1317 |
|
1318 Q3DnsDomain::~Q3DnsDomain() |
|
1319 { |
|
1320 delete rrs; |
|
1321 rrs = 0; |
|
1322 } |
|
1323 |
|
1324 |
|
1325 void Q3DnsDomain::add( const QString & label, Q3DnsRR * rr ) |
|
1326 { |
|
1327 Q3DnsDomain * d = Q3DnsManager::manager()->domain( label ); |
|
1328 if ( !d->rrs ) { |
|
1329 d->rrs = new Q3PtrList<Q3DnsRR>; |
|
1330 d->rrs->setAutoDelete( true ); |
|
1331 } |
|
1332 d->rrs->append( rr ); |
|
1333 rr->domain = d; |
|
1334 } |
|
1335 |
|
1336 |
|
1337 Q3PtrList<Q3DnsRR> * Q3DnsDomain::cached( const Q3Dns * r ) |
|
1338 { |
|
1339 Q3PtrList<Q3DnsRR> * l = new Q3PtrList<Q3DnsRR>; |
|
1340 |
|
1341 // test at first if you have to start a query at all |
|
1342 if ( r->recordType() == Q3Dns::A ) { |
|
1343 if ( r->label().lower() == QLatin1String("localhost") ) { |
|
1344 // undocumented hack. ipv4-specific. also, may be a memory |
|
1345 // leak? not sure. would be better to do this in doResInit(), |
|
1346 // anyway. |
|
1347 Q3DnsRR *rrTmp = new Q3DnsRR( r->label() ); |
|
1348 rrTmp->t = Q3Dns::A; |
|
1349 rrTmp->address = QHostAddress( 0x7f000001 ); |
|
1350 rrTmp->current = true; |
|
1351 l->append( rrTmp ); |
|
1352 return l; |
|
1353 } |
|
1354 QHostAddress tmp; |
|
1355 if ( tmp.setAddress( r->label() ) ) { |
|
1356 Q3DnsRR *rrTmp = new Q3DnsRR( r->label() ); |
|
1357 if ( tmp.isIPv4Address() ) { |
|
1358 rrTmp->t = Q3Dns::A; |
|
1359 rrTmp->address = tmp; |
|
1360 rrTmp->current = true; |
|
1361 l->append( rrTmp ); |
|
1362 } else { |
|
1363 rrTmp->nxdomain = true; |
|
1364 } |
|
1365 return l; |
|
1366 } |
|
1367 } |
|
1368 if ( r->recordType() == Q3Dns::Aaaa ) { |
|
1369 QHostAddress tmp; |
|
1370 if ( tmp.setAddress(r->label()) ) { |
|
1371 Q3DnsRR *rrTmp = new Q3DnsRR( r->label() ); |
|
1372 if ( tmp.isIPv6Address() ) { |
|
1373 rrTmp->t = Q3Dns::Aaaa; |
|
1374 rrTmp->address = tmp; |
|
1375 rrTmp->current = true; |
|
1376 l->append( rrTmp ); |
|
1377 } else { |
|
1378 rrTmp->nxdomain = true; |
|
1379 } |
|
1380 return l; |
|
1381 } |
|
1382 } |
|
1383 |
|
1384 // if you reach this point, you have to do the query |
|
1385 Q3DnsManager * m = Q3DnsManager::manager(); |
|
1386 QStringList n = r->qualifiedNames(); |
|
1387 bool nxdomain; |
|
1388 int cnamecount = 0; |
|
1389 int it = 0; |
|
1390 while( it < n.count() ) { |
|
1391 QString s = n.at(it++); |
|
1392 nxdomain = false; |
|
1393 #if defined(Q3DNS_DEBUG) |
|
1394 qDebug( "looking at cache for %s (%s %d)", |
|
1395 s.ascii(), r->label().ascii(), r->recordType() ); |
|
1396 #endif |
|
1397 Q3DnsDomain * d = m->domain( s ); |
|
1398 #if defined(Q3DNS_DEBUG) |
|
1399 qDebug( " - found %d RRs", d && d->rrs ? d->rrs->count() : 0 ); |
|
1400 #endif |
|
1401 if ( d->rrs ) |
|
1402 d->rrs->first(); |
|
1403 Q3DnsRR * rr; |
|
1404 bool answer = false; |
|
1405 while( d->rrs && (rr=d->rrs->current()) != 0 ) { |
|
1406 if ( rr->t == Q3Dns::Cname && r->recordType() != Q3Dns::Cname && |
|
1407 !rr->nxdomain && cnamecount < 16 ) { |
|
1408 // cname. if the code is ugly, that may just |
|
1409 // possibly be because the concept is. |
|
1410 #if defined(Q3DNS_DEBUG) |
|
1411 qDebug( "found cname from %s to %s", |
|
1412 r->label().ascii(), rr->target.ascii() ); |
|
1413 #endif |
|
1414 s = rr->target; |
|
1415 d = m->domain( s ); |
|
1416 if ( d->rrs ) |
|
1417 d->rrs->first(); |
|
1418 it = n.count(); |
|
1419 // we've elegantly moved over to whatever the cname |
|
1420 // pointed to. well, not elegantly. let's remember |
|
1421 // that we've done something, anyway, so we can't be |
|
1422 // fooled into an infinte loop as well. |
|
1423 cnamecount++; |
|
1424 } else { |
|
1425 if ( rr->t == r->recordType() ) { |
|
1426 if ( rr->nxdomain ) |
|
1427 nxdomain = true; |
|
1428 else |
|
1429 answer = true; |
|
1430 l->append( rr ); |
|
1431 if ( rr->deleteTime <= lastSweep ) { |
|
1432 // we're returning something that'll be |
|
1433 // deleted soon. we assume that if the client |
|
1434 // wanted it twice, it'll want it again, so we |
|
1435 // ask the name server again right now. |
|
1436 Q3DnsQuery * query = new Q3DnsQuery; |
|
1437 query->started = now(); |
|
1438 query->id = ++theId; |
|
1439 query->t = rr->t; |
|
1440 query->l = rr->domain->name(); |
|
1441 // note that here, we don't bother about |
|
1442 // notification. but we do bother about |
|
1443 // timeouts: we make sure to use high timeouts |
|
1444 // and few tramsissions. |
|
1445 query->step = theNs->count(); |
|
1446 QObject::connect( query, SIGNAL(timeout()), |
|
1447 Q3DnsManager::manager(), |
|
1448 SLOT(retransmit()) ); |
|
1449 Q3DnsManager::manager()->transmitQuery( query ); |
|
1450 } |
|
1451 } |
|
1452 d->rrs->next(); |
|
1453 } |
|
1454 } |
|
1455 // if we found a positive result, return quickly |
|
1456 if ( answer && l->count() ) { |
|
1457 #if defined(Q3DNS_DEBUG) |
|
1458 qDebug( "found %d records for %s", |
|
1459 l->count(), r->label().ascii() ); |
|
1460 l->first(); |
|
1461 while( l->current() ) { |
|
1462 qDebug( " type %d target %s address %s", |
|
1463 l->current()->t, |
|
1464 l->current()->target.latin1(), |
|
1465 l->current()->address.toString().latin1() ); |
|
1466 l->next(); |
|
1467 } |
|
1468 #endif |
|
1469 l->first(); |
|
1470 return l; |
|
1471 } |
|
1472 |
|
1473 #if defined(Q3DNS_DEBUG) |
|
1474 if ( nxdomain ) |
|
1475 qDebug( "found NXDomain %s", s.ascii() ); |
|
1476 #endif |
|
1477 |
|
1478 if ( !nxdomain ) { |
|
1479 // if we didn't, and not a negative result either, perhaps |
|
1480 // we need to transmit a query. |
|
1481 uint q = 0; |
|
1482 while ( q < m->queries.size() && |
|
1483 ( m->queries[q] == 0 || |
|
1484 m->queries[q]->t != r->recordType() || |
|
1485 m->queries[q]->l != s ) ) |
|
1486 q++; |
|
1487 // we haven't done it before, so maybe we should. but |
|
1488 // wait - if it's an unqualified name, only ask when all |
|
1489 // the other alternatives are exhausted. |
|
1490 if ( q == m->queries.size() && ( s.find( QLatin1Char('.') ) >= 0 || |
|
1491 int(l->count()) >= n.count()-1 ) ) { |
|
1492 Q3DnsQuery * query = new Q3DnsQuery; |
|
1493 query->started = now(); |
|
1494 query->id = ++theId; |
|
1495 query->t = r->recordType(); |
|
1496 query->l = s; |
|
1497 query->dns->replace( (void*)r, (void*)r ); |
|
1498 QObject::connect( query, SIGNAL(timeout()), |
|
1499 Q3DnsManager::manager(), SLOT(retransmit()) ); |
|
1500 Q3DnsManager::manager()->transmitQuery( query ); |
|
1501 } else if ( q < m->queries.size() ) { |
|
1502 // if we've found an earlier query for the same |
|
1503 // domain/type, subscribe to its answer |
|
1504 m->queries[q]->dns->replace( (void*)r, (void*)r ); |
|
1505 } |
|
1506 } |
|
1507 } |
|
1508 l->first(); |
|
1509 return l; |
|
1510 } |
|
1511 |
|
1512 |
|
1513 void Q3DnsDomain::sweep( Q_UINT32 thisSweep ) |
|
1514 { |
|
1515 if ( !rrs ) |
|
1516 return; |
|
1517 |
|
1518 Q3DnsRR * rr; |
|
1519 rrs->first(); |
|
1520 while( (rr=rrs->current()) != 0 ) { |
|
1521 if ( !rr->deleteTime ) |
|
1522 rr->deleteTime = thisSweep; // will hit next time around |
|
1523 |
|
1524 #if defined(Q3DNS_DEBUG) |
|
1525 qDebug( "Q3Dns::sweep: %s type %d expires %u %u - %s / %s", |
|
1526 rr->domain->name().latin1(), rr->t, |
|
1527 rr->expireTime, rr->deleteTime, |
|
1528 rr->target.latin1(), rr->address.toString().latin1()); |
|
1529 #endif |
|
1530 if ( rr->current == false || |
|
1531 rr->t == Q3Dns::None || |
|
1532 rr->deleteTime <= thisSweep || |
|
1533 rr->expireTime <= thisSweep ) |
|
1534 rrs->remove(); |
|
1535 else |
|
1536 rrs->next(); |
|
1537 } |
|
1538 |
|
1539 if ( rrs->isEmpty() ) { |
|
1540 delete rrs; |
|
1541 rrs = 0; |
|
1542 } |
|
1543 } |
|
1544 |
|
1545 |
|
1546 |
|
1547 |
|
1548 // the itsy-bitsy little socket class I don't really need except for |
|
1549 // so I can subclass and reimplement the slots. |
|
1550 |
|
1551 |
|
1552 Q3DnsSocket::Q3DnsSocket( QObject * parent, const char * name ) |
|
1553 : QObject( parent, name ) |
|
1554 { |
|
1555 // nothing |
|
1556 } |
|
1557 |
|
1558 |
|
1559 Q3DnsSocket::~Q3DnsSocket() |
|
1560 { |
|
1561 // nothing |
|
1562 } |
|
1563 |
|
1564 |
|
1565 void Q3DnsSocket::cleanCache() |
|
1566 { |
|
1567 // nothing |
|
1568 } |
|
1569 |
|
1570 |
|
1571 void Q3DnsSocket::retransmit() |
|
1572 { |
|
1573 // nothing |
|
1574 } |
|
1575 |
|
1576 |
|
1577 void Q3DnsSocket::answer() |
|
1578 { |
|
1579 // nothing |
|
1580 } |
|
1581 |
|
1582 |
|
1583 /*! |
|
1584 \class Q3Dns |
|
1585 \brief The Q3Dns class provides asynchronous DNS lookups. |
|
1586 |
|
1587 \compat |
|
1588 |
|
1589 Both Windows and Unix provide synchronous DNS lookups; Windows |
|
1590 provides some asynchronous support too. At the time of writing |
|
1591 neither operating system provides asynchronous support for |
|
1592 anything other than hostname-to-address mapping. |
|
1593 |
|
1594 Q3Dns rectifies this shortcoming, by providing asynchronous caching |
|
1595 lookups for the record types that we expect modern GUI |
|
1596 applications to need in the near future. |
|
1597 |
|
1598 The class is \e not straightforward to use (although it is much |
|
1599 simpler than the native APIs); Q3Socket provides much easier to use |
|
1600 TCP connection facilities. The aim of Q3Dns is to provide a correct |
|
1601 and small API to the DNS and nothing more. (We use "correctness" |
|
1602 to mean that the DNS information is correctly cached, and |
|
1603 correctly timed out.) |
|
1604 |
|
1605 The API comprises a constructor, functions to set the DNS node |
|
1606 (the domain in DNS terminology) and record type (setLabel() and |
|
1607 setRecordType()), the corresponding get functions, an isWorking() |
|
1608 function to determine whether Q3Dns is working or reading, a |
|
1609 resultsReady() signal and query functions for the result. |
|
1610 |
|
1611 There is one query function for each RecordType, namely |
|
1612 addresses(), mailServers(), servers(), hostNames() and texts(). |
|
1613 There are also two generic query functions: canonicalName() |
|
1614 returns the name you'll presumably end up using (the exact meaning |
|
1615 of this depends on the record type) and qualifiedNames() returns a |
|
1616 list of the fully qualified names label() maps to. |
|
1617 |
|
1618 \sa Q3Socket |
|
1619 */ |
|
1620 |
|
1621 /*! |
|
1622 Constructs a DNS query object with invalid settings for both the |
|
1623 label and the search type. |
|
1624 */ |
|
1625 |
|
1626 Q3Dns::Q3Dns() |
|
1627 { |
|
1628 d = new Q3DnsPrivate; |
|
1629 t = None; |
|
1630 } |
|
1631 |
|
1632 |
|
1633 |
|
1634 |
|
1635 /*! |
|
1636 Constructs a DNS query object that will return record type \a rr |
|
1637 information about \a label. |
|
1638 |
|
1639 The DNS lookup is started the next time the application enters the |
|
1640 event loop. When the result is found the signal resultsReady() is |
|
1641 emitted. |
|
1642 |
|
1643 \a rr defaults to \c A, IPv4 addresses. |
|
1644 */ |
|
1645 |
|
1646 Q3Dns::Q3Dns( const QString & label, RecordType rr ) |
|
1647 { |
|
1648 d = new Q3DnsPrivate; |
|
1649 t = rr; |
|
1650 setLabel( label ); |
|
1651 setStartQueryTimer(); // start query the next time we enter event loop |
|
1652 } |
|
1653 |
|
1654 |
|
1655 |
|
1656 /*! |
|
1657 Constructs a DNS query object that will return record type \a rr |
|
1658 information about host address \a address. The label is set to the |
|
1659 IN-ADDR.ARPA domain name. This is useful in combination with the |
|
1660 \c Ptr record type (e.g. if you want to look up a hostname for a |
|
1661 given address). |
|
1662 |
|
1663 The DNS lookup is started the next time the application enters the |
|
1664 event loop. When the result is found the signal resultsReady() is |
|
1665 emitted. |
|
1666 |
|
1667 \a rr defaults to \c Ptr, that maps addresses to hostnames. |
|
1668 */ |
|
1669 |
|
1670 Q3Dns::Q3Dns( const QHostAddress & address, RecordType rr ) |
|
1671 { |
|
1672 d = new Q3DnsPrivate; |
|
1673 t = rr; |
|
1674 setLabel( address ); |
|
1675 setStartQueryTimer(); // start query the next time we enter event loop |
|
1676 } |
|
1677 |
|
1678 |
|
1679 |
|
1680 |
|
1681 /*! |
|
1682 Destroys the DNS query object and frees its allocated resources. |
|
1683 */ |
|
1684 |
|
1685 Q3Dns::~Q3Dns() |
|
1686 { |
|
1687 if ( globalManager ) { |
|
1688 uint q = 0; |
|
1689 Q3DnsManager * m = globalManager; |
|
1690 while( q < m->queries.size() ) { |
|
1691 Q3DnsQuery * query=m->queries[q]; |
|
1692 if ( query && query->dns ) |
|
1693 (void)query->dns->take( (void*) this ); |
|
1694 q++; |
|
1695 } |
|
1696 |
|
1697 } |
|
1698 |
|
1699 delete d; |
|
1700 d = 0; |
|
1701 } |
|
1702 |
|
1703 |
|
1704 |
|
1705 |
|
1706 /*! |
|
1707 Sets this DNS query object to query for information about \a |
|
1708 label. |
|
1709 |
|
1710 This does not change the recordType(), but its isWorking() status |
|
1711 will probably change as a result. |
|
1712 |
|
1713 The DNS lookup is started the next time the application enters the |
|
1714 event loop. When the result is found the signal resultsReady() is |
|
1715 emitted. |
|
1716 */ |
|
1717 |
|
1718 void Q3Dns::setLabel( const QString & label ) |
|
1719 { |
|
1720 l = label; |
|
1721 d->noNames = false; |
|
1722 |
|
1723 // construct a list of qualified names |
|
1724 n.clear(); |
|
1725 if ( l.length() > 1 && l[(int)l.length()-1] == QLatin1Char('.') ) { |
|
1726 n.append( l.left( l.length()-1 ).lower() ); |
|
1727 } else { |
|
1728 int i = l.length(); |
|
1729 int dots = 0; |
|
1730 const int maxDots = 2; |
|
1731 while( i && dots < maxDots ) { |
|
1732 if ( l[--i] == QLatin1Char('.') ) |
|
1733 dots++; |
|
1734 } |
|
1735 if ( dots < maxDots ) { |
|
1736 (void)Q3DnsManager::manager(); // create a Q3DnsManager, if it is not already there |
|
1737 Q3StrListIterator it( *theDomains ); |
|
1738 const char * dom; |
|
1739 while( (dom=it.current()) != 0 ) { |
|
1740 ++it; |
|
1741 n.append( l.lower() + QLatin1Char('.') + QLatin1String(dom) ); |
|
1742 } |
|
1743 } |
|
1744 n.append( l.lower() ); |
|
1745 } |
|
1746 |
|
1747 #if defined(Q_DNS_SYNCHRONOUS) |
|
1748 if ( d->noEventLoop ) { |
|
1749 doSynchronousLookup(); |
|
1750 } else { |
|
1751 setStartQueryTimer(); // start query the next time we enter event loop |
|
1752 } |
|
1753 #else |
|
1754 setStartQueryTimer(); // start query the next time we enter event loop |
|
1755 #endif |
|
1756 #if defined(Q3DNS_DEBUG) |
|
1757 qDebug( "Q3Dns::setLabel: %d address(es) for %s", n.count(), l.ascii() ); |
|
1758 int i = 0; |
|
1759 for( i = 0; i < (int)n.count(); i++ ) |
|
1760 qDebug( "Q3Dns::setLabel: %d: %s", i, n[i].ascii() ); |
|
1761 #endif |
|
1762 } |
|
1763 |
|
1764 |
|
1765 /*! |
|
1766 \overload |
|
1767 |
|
1768 Sets this DNS query object to query for information about the host |
|
1769 address \a address. The label is set to the IN-ADDR.ARPA domain |
|
1770 name. This is useful in combination with the \c Ptr record type |
|
1771 (e.g. if you want to look up a hostname for a given address). |
|
1772 */ |
|
1773 |
|
1774 void Q3Dns::setLabel( const QHostAddress & address ) |
|
1775 { |
|
1776 setLabel( toInAddrArpaDomain( address ) ); |
|
1777 } |
|
1778 |
|
1779 |
|
1780 /*! |
|
1781 \fn QStringList Q3Dns::qualifiedNames() const |
|
1782 |
|
1783 Returns a list of the fully qualified names label() maps to. |
|
1784 |
|
1785 Note that if you want to iterate over the list, you should iterate |
|
1786 over a copy, e.g. |
|
1787 \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 0 |
|
1788 |
|
1789 */ |
|
1790 |
|
1791 |
|
1792 /*! |
|
1793 \fn QString Q3Dns::label() const |
|
1794 |
|
1795 Returns the domain name for which this object returns information. |
|
1796 |
|
1797 \sa setLabel() |
|
1798 */ |
|
1799 |
|
1800 /*! |
|
1801 \enum Q3Dns::RecordType |
|
1802 |
|
1803 This enum type defines the record types Q3Dns can handle. The DNS |
|
1804 provides many more; these are the ones we've judged to be in |
|
1805 current use, useful for GUI programs and important enough to |
|
1806 support right away: |
|
1807 |
|
1808 \value None No information. This exists only so that Q3Dns can |
|
1809 have a default. |
|
1810 |
|
1811 \value A IPv4 addresses. By far the most common type. |
|
1812 |
|
1813 \value Aaaa IPv6 addresses. So far mostly unused. |
|
1814 |
|
1815 \value Mx Mail eXchanger names. Used for mail delivery. |
|
1816 |
|
1817 \value Srv SeRVer names. Generic record type for finding |
|
1818 servers. So far mostly unused. |
|
1819 |
|
1820 \value Cname Canonical names. Maps from nicknames to the true |
|
1821 name (the canonical name) for a host. |
|
1822 |
|
1823 \value Ptr name PoinTeRs. Maps from IPv4 or IPv6 addresses to hostnames. |
|
1824 |
|
1825 \value Txt arbitrary TeXT for domains. |
|
1826 |
|
1827 We expect that some support for the |
|
1828 \l{http://www.rfc-editor.org/rfc/rfc2535.txt}{RFC 2535} |
|
1829 extensions will be added in future versions. |
|
1830 */ |
|
1831 |
|
1832 /*! |
|
1833 Sets this object to query for record type \a rr records. |
|
1834 |
|
1835 The DNS lookup is started the next time the application enters the |
|
1836 event loop. When the result is found the signal resultsReady() is |
|
1837 emitted. |
|
1838 |
|
1839 \sa RecordType |
|
1840 */ |
|
1841 |
|
1842 void Q3Dns::setRecordType( RecordType rr ) |
|
1843 { |
|
1844 t = rr; |
|
1845 d->noNames = false; |
|
1846 setStartQueryTimer(); // start query the next time we enter event loop |
|
1847 } |
|
1848 |
|
1849 /*! |
|
1850 \internal |
|
1851 |
|
1852 Private slot for starting the query. |
|
1853 */ |
|
1854 void Q3Dns::startQuery() |
|
1855 { |
|
1856 // isWorking() starts the query (if necessary) |
|
1857 if ( !isWorking() ) |
|
1858 emit resultsReady(); |
|
1859 } |
|
1860 |
|
1861 /*! |
|
1862 The three functions Q3Dns::Q3Dns(QString, RecordType), |
|
1863 Q3Dns::setLabel() and Q3Dns::setRecordType() may start a DNS lookup. |
|
1864 This function handles setting up the single shot timer. |
|
1865 */ |
|
1866 void Q3Dns::setStartQueryTimer() |
|
1867 { |
|
1868 #if defined(Q_DNS_SYNCHRONOUS) |
|
1869 if ( !d->queryTimer && !d->noEventLoop ) |
|
1870 #else |
|
1871 if ( !d->queryTimer ) |
|
1872 #endif |
|
1873 { |
|
1874 // start the query the next time we enter event loop |
|
1875 d->queryTimer = new QTimer( this ); |
|
1876 connect( d->queryTimer, SIGNAL(timeout()), |
|
1877 this, SLOT(startQuery()) ); |
|
1878 d->queryTimer->start( 0, true ); |
|
1879 } |
|
1880 } |
|
1881 |
|
1882 /* |
|
1883 Transforms the host address \a address to the IN-ADDR.ARPA domain |
|
1884 name. Returns something indeterminate if you're sloppy or |
|
1885 naughty. This function has an IPv4-specific name, but works for |
|
1886 IPv6 too. |
|
1887 */ |
|
1888 QString Q3Dns::toInAddrArpaDomain( const QHostAddress &address ) |
|
1889 { |
|
1890 QString s; |
|
1891 if ( address.isNull() ) { |
|
1892 // if the address isn't valid, neither of the other two make |
|
1893 // cases make sense. better to just return. |
|
1894 } else if ( address.isIp4Addr() ) { |
|
1895 Q_UINT32 i = address.ip4Addr(); |
|
1896 s.sprintf( "%d.%d.%d.%d.IN-ADDR.ARPA", |
|
1897 i & 0xff, (i >> 8) & 0xff, (i>>16) & 0xff, (i>>24) & 0xff ); |
|
1898 } else { |
|
1899 // RFC 3152. (1886 is deprecated, and clients no longer need to |
|
1900 // support it, in practice). |
|
1901 Q_IPV6ADDR i = address.toIPv6Address(); |
|
1902 s = QLatin1String("ip6.arpa"); |
|
1903 uint b = 0; |
|
1904 while( b < 16 ) { |
|
1905 s = QString::number( i.c[b]%16, 16 ) + QLatin1Char('.') + |
|
1906 QString::number( i.c[b]/16, 16 ) + QLatin1Char('.') + s; |
|
1907 b++; |
|
1908 } |
|
1909 } |
|
1910 return s; |
|
1911 } |
|
1912 |
|
1913 |
|
1914 /*! |
|
1915 \fn Q3Dns::RecordType Q3Dns::recordType() const |
|
1916 |
|
1917 Returns the record type of this DNS query object. |
|
1918 |
|
1919 \sa setRecordType() RecordType |
|
1920 */ |
|
1921 |
|
1922 /*! |
|
1923 \fn void Q3Dns::resultsReady() |
|
1924 |
|
1925 This signal is emitted when results are available for one of the |
|
1926 qualifiedNames(). |
|
1927 */ |
|
1928 |
|
1929 /*! |
|
1930 Returns true if Q3Dns is doing a lookup for this object (i.e. if it |
|
1931 does not already have the necessary information); otherwise |
|
1932 returns false. |
|
1933 |
|
1934 Q3Dns emits the resultsReady() signal when the status changes to false. |
|
1935 */ |
|
1936 |
|
1937 bool Q3Dns::isWorking() const |
|
1938 { |
|
1939 #if defined(Q3DNS_DEBUG) |
|
1940 qDebug( "Q3Dns::isWorking (%s, %d)", l.ascii(), t ); |
|
1941 #endif |
|
1942 if ( t == None ) |
|
1943 return false; |
|
1944 |
|
1945 #if defined(Q_DNS_SYNCHRONOUS) |
|
1946 if ( d->noEventLoop ) |
|
1947 return true; |
|
1948 #endif |
|
1949 |
|
1950 Q3PtrList<Q3DnsRR> * ll = Q3DnsDomain::cached( this ); |
|
1951 Q_LONG queries = n.count(); |
|
1952 while( ll->current() != 0 ) { |
|
1953 if ( ll->current()->nxdomain ) { |
|
1954 queries--; |
|
1955 } else { |
|
1956 delete ll; |
|
1957 return false; |
|
1958 } |
|
1959 ll->next(); |
|
1960 } |
|
1961 delete ll; |
|
1962 |
|
1963 if ( queries <= 0 ) |
|
1964 return false; |
|
1965 if ( d->noNames ) |
|
1966 return false; |
|
1967 return true; |
|
1968 } |
|
1969 |
|
1970 |
|
1971 /*! |
|
1972 Returns a list of the addresses for this name if this Q3Dns object |
|
1973 has a recordType() of Q3Dns::A or Q3Dns::Aaaa and the answer |
|
1974 is available; otherwise returns an empty list. |
|
1975 |
|
1976 As a special case, if label() is a valid numeric IP address, this |
|
1977 function returns that address. |
|
1978 |
|
1979 Note that if you want to iterate over the list, you should iterate |
|
1980 over a copy, e.g. |
|
1981 \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 1 |
|
1982 |
|
1983 */ |
|
1984 |
|
1985 Q3ValueList<QHostAddress> Q3Dns::addresses() const |
|
1986 { |
|
1987 #if defined(Q3DNS_DEBUG) |
|
1988 qDebug( "Q3Dns::addresses (%s)", l.ascii() ); |
|
1989 #endif |
|
1990 Q3ValueList<QHostAddress> result; |
|
1991 if ( t != A && t != Aaaa ) |
|
1992 return result; |
|
1993 |
|
1994 Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this ); |
|
1995 |
|
1996 Q3DnsRR * rr; |
|
1997 while( (rr=cached->current()) != 0 ) { |
|
1998 if ( rr->current && !rr->nxdomain ) |
|
1999 result.append( rr->address ); |
|
2000 cached->next(); |
|
2001 } |
|
2002 delete cached; |
|
2003 return result; |
|
2004 } |
|
2005 |
|
2006 |
|
2007 /*! |
|
2008 \class Q3Dns::MailServer |
|
2009 \brief The Q3Dns::MailServer class is described in Q3Dns::mailServers(). |
|
2010 |
|
2011 \internal |
|
2012 */ |
|
2013 |
|
2014 /*! |
|
2015 Returns a list of mail servers if the record type is \c Mx. The |
|
2016 class Q3Dns::MailServer contains the following public variables: |
|
2017 \list |
|
2018 \i QString Q3Dns::MailServer::name |
|
2019 \i Q_UINT16 Q3Dns::MailServer::priority |
|
2020 \endlist |
|
2021 |
|
2022 Note that if you want to iterate over the list, you should iterate |
|
2023 over a copy, e.g. |
|
2024 \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 2 |
|
2025 |
|
2026 */ |
|
2027 Q3ValueList<Q3Dns::MailServer> Q3Dns::mailServers() const |
|
2028 { |
|
2029 #if defined(Q3DNS_DEBUG) |
|
2030 qDebug( "Q3Dns::mailServers (%s)", l.ascii() ); |
|
2031 #endif |
|
2032 Q3ValueList<Q3Dns::MailServer> result; |
|
2033 if ( t != Mx ) |
|
2034 return result; |
|
2035 |
|
2036 Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this ); |
|
2037 |
|
2038 Q3DnsRR * rr; |
|
2039 while( (rr=cached->current()) != 0 ) { |
|
2040 if ( rr->current && !rr->nxdomain ) { |
|
2041 MailServer ms( rr->target, rr->priority ); |
|
2042 result.append( ms ); |
|
2043 } |
|
2044 cached->next(); |
|
2045 } |
|
2046 delete cached; |
|
2047 return result; |
|
2048 } |
|
2049 |
|
2050 |
|
2051 /*! |
|
2052 \class Q3Dns::Server |
|
2053 \brief The Q3Dns::Server class is described in Q3Dns::servers(). |
|
2054 |
|
2055 \internal |
|
2056 */ |
|
2057 |
|
2058 /*! |
|
2059 Returns a list of servers if the record type is \c Srv. The class |
|
2060 Q3Dns::Server contains the following public variables: |
|
2061 \list |
|
2062 \i QString Q3Dns::Server::name |
|
2063 \i Q_UINT16 Q3Dns::Server::priority |
|
2064 \i Q_UINT16 Q3Dns::Server::weight |
|
2065 \i Q_UINT16 Q3Dns::Server::port |
|
2066 \endlist |
|
2067 |
|
2068 Note that if you want to iterate over the list, you should iterate |
|
2069 over a copy, e.g. |
|
2070 \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 3 |
|
2071 */ |
|
2072 Q3ValueList<Q3Dns::Server> Q3Dns::servers() const |
|
2073 { |
|
2074 #if defined(Q3DNS_DEBUG) |
|
2075 qDebug( "Q3Dns::servers (%s)", l.ascii() ); |
|
2076 #endif |
|
2077 Q3ValueList<Q3Dns::Server> result; |
|
2078 if ( t != Srv ) |
|
2079 return result; |
|
2080 |
|
2081 Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this ); |
|
2082 |
|
2083 Q3DnsRR * rr; |
|
2084 while( (rr=cached->current()) != 0 ) { |
|
2085 if ( rr->current && !rr->nxdomain ) { |
|
2086 Server s( rr->target, rr->priority, rr->weight, rr->port ); |
|
2087 result.append( s ); |
|
2088 } |
|
2089 cached->next(); |
|
2090 } |
|
2091 delete cached; |
|
2092 return result; |
|
2093 } |
|
2094 |
|
2095 |
|
2096 /*! |
|
2097 Returns a list of host names if the record type is \c Ptr. |
|
2098 |
|
2099 Note that if you want to iterate over the list, you should iterate |
|
2100 over a copy, e.g. |
|
2101 \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 4 |
|
2102 |
|
2103 */ |
|
2104 QStringList Q3Dns::hostNames() const |
|
2105 { |
|
2106 #if defined(Q3DNS_DEBUG) |
|
2107 qDebug( "Q3Dns::hostNames (%s)", l.ascii() ); |
|
2108 #endif |
|
2109 QStringList result; |
|
2110 if ( t != Ptr ) |
|
2111 return result; |
|
2112 |
|
2113 Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this ); |
|
2114 |
|
2115 Q3DnsRR * rr; |
|
2116 while( (rr=cached->current()) != 0 ) { |
|
2117 if ( rr->current && !rr->nxdomain ) { |
|
2118 QString str( rr->target ); |
|
2119 result.append( str ); |
|
2120 } |
|
2121 cached->next(); |
|
2122 } |
|
2123 delete cached; |
|
2124 return result; |
|
2125 } |
|
2126 |
|
2127 |
|
2128 /*! |
|
2129 Returns a list of texts if the record type is \c Txt. |
|
2130 |
|
2131 Note that if you want to iterate over the list, you should iterate |
|
2132 over a copy, e.g. |
|
2133 \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 5 |
|
2134 */ |
|
2135 QStringList Q3Dns::texts() const |
|
2136 { |
|
2137 #if defined(Q3DNS_DEBUG) |
|
2138 qDebug( "Q3Dns::texts (%s)", l.ascii() ); |
|
2139 #endif |
|
2140 QStringList result; |
|
2141 if ( t != Txt ) |
|
2142 return result; |
|
2143 |
|
2144 Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this ); |
|
2145 |
|
2146 Q3DnsRR * rr; |
|
2147 while( (rr=cached->current()) != 0 ) { |
|
2148 if ( rr->current && !rr->nxdomain ) { |
|
2149 QString str( rr->text ); |
|
2150 result.append( str ); |
|
2151 } |
|
2152 cached->next(); |
|
2153 } |
|
2154 delete cached; |
|
2155 return result; |
|
2156 } |
|
2157 |
|
2158 |
|
2159 /*! |
|
2160 Returns the canonical name for this DNS node. (This works |
|
2161 regardless of what recordType() is set to.) |
|
2162 |
|
2163 If the canonical name isn't known, this function returns a null |
|
2164 string. |
|
2165 |
|
2166 The canonical name of a DNS node is its full name, or the full |
|
2167 name of the target of its CNAME. For example, if l.trolltech.com |
|
2168 is a CNAME to lillian.troll.no, and the search path for Q3Dns is |
|
2169 "trolltech.com", then the canonical name for all of "lillian", |
|
2170 "l", "lillian.troll.no." and "l.trolltech.com" is |
|
2171 "lillian.troll.no.". |
|
2172 */ |
|
2173 |
|
2174 QString Q3Dns::canonicalName() const |
|
2175 { |
|
2176 // the cname should work regardless of the recordType(), so set the record |
|
2177 // type temporarily to cname when you look at the cache |
|
2178 Q3Dns *that = (Q3Dns*) this; // mutable function |
|
2179 RecordType oldType = t; |
|
2180 that->t = Cname; |
|
2181 Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( that ); |
|
2182 that->t = oldType; |
|
2183 |
|
2184 Q3DnsRR * rr; |
|
2185 while( (rr=cached->current()) != 0 ) { |
|
2186 if ( rr->current && !rr->nxdomain && rr->domain ) { |
|
2187 delete cached; |
|
2188 return rr->target; |
|
2189 } |
|
2190 cached->next(); |
|
2191 } |
|
2192 delete cached; |
|
2193 return QString(); |
|
2194 } |
|
2195 |
|
2196 #if defined(Q_DNS_SYNCHRONOUS) |
|
2197 /*! \reimp |
|
2198 */ |
|
2199 void Q3Dns::connectNotify( const char *signal ) |
|
2200 { |
|
2201 if ( d->noEventLoop && qstrcmp(signal,SIGNAL(resultsReady()) )==0 ) { |
|
2202 doSynchronousLookup(); |
|
2203 } |
|
2204 } |
|
2205 #endif |
|
2206 |
|
2207 #if defined(Q_OS_WIN32) || defined(Q_OS_CYGWIN) |
|
2208 |
|
2209 #if defined(Q_DNS_SYNCHRONOUS) |
|
2210 void Q3Dns::doSynchronousLookup() |
|
2211 { |
|
2212 // ### not implemented yet |
|
2213 } |
|
2214 #endif |
|
2215 |
|
2216 // the following typedefs are needed for GetNetworkParams() API call |
|
2217 #ifndef IP_TYPES_INCLUDED |
|
2218 #define MAX_HOSTNAME_LEN 128 |
|
2219 #define MAX_DOMAIN_NAME_LEN 128 |
|
2220 #define MAX_SCOPE_ID_LEN 256 |
|
2221 typedef struct { |
|
2222 char String[4 * 4]; |
|
2223 } IP_ADDRESS_STRING, *PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING; |
|
2224 typedef struct _IP_ADDR_STRING { |
|
2225 struct _IP_ADDR_STRING* Next; |
|
2226 IP_ADDRESS_STRING IpAddress; |
|
2227 IP_MASK_STRING IpMask; |
|
2228 DWORD Context; |
|
2229 } IP_ADDR_STRING, *PIP_ADDR_STRING; |
|
2230 typedef struct { |
|
2231 char HostName[MAX_HOSTNAME_LEN + 4] ; |
|
2232 char DomainName[MAX_DOMAIN_NAME_LEN + 4]; |
|
2233 PIP_ADDR_STRING CurrentDnsServer; |
|
2234 IP_ADDR_STRING DnsServerList; |
|
2235 UINT NodeType; |
|
2236 char ScopeId[MAX_SCOPE_ID_LEN + 4]; |
|
2237 UINT EnableRouting; |
|
2238 UINT EnableProxy; |
|
2239 UINT EnableDns; |
|
2240 } FIXED_INFO, *PFIXED_INFO; |
|
2241 #endif |
|
2242 typedef DWORD (WINAPI *GNP)( PFIXED_INFO, PULONG ); |
|
2243 |
|
2244 // ### FIXME: this code is duplicated in qfiledialog.cpp |
|
2245 static QString getWindowsRegString(HKEY key, const QString &subKey) |
|
2246 { |
|
2247 QString s; |
|
2248 |
|
2249 wchar_t buf[1024]; |
|
2250 DWORD bsz = sizeof(buf) / sizeof(wchar_t); |
|
2251 int r = RegQueryValueEx(key, (wchar_t*)subKey.utf16(), 0, 0, (LPBYTE)buf, &bsz); |
|
2252 if (r == ERROR_SUCCESS) { |
|
2253 s = QString::fromWCharArray(buf); |
|
2254 } else if (r == ERROR_MORE_DATA) { |
|
2255 char *ptr = new char[bsz+1]; |
|
2256 r = RegQueryValueEx(key, (wchar_t*)subKey.utf16(), 0, 0, (LPBYTE)ptr, &bsz); |
|
2257 if (r == ERROR_SUCCESS) |
|
2258 s = QLatin1String(ptr); |
|
2259 delete [] ptr; |
|
2260 } |
|
2261 |
|
2262 return s; |
|
2263 } |
|
2264 |
|
2265 static bool getDnsParamsFromRegistry( const QString &path, |
|
2266 QString *domainName, QString *nameServer, QString *searchList ) |
|
2267 { |
|
2268 HKEY k; |
|
2269 int r = RegOpenKeyEx( HKEY_LOCAL_MACHINE, (wchar_t*)path.utf16(), 0, KEY_READ, &k ); |
|
2270 |
|
2271 if ( r == ERROR_SUCCESS ) { |
|
2272 *domainName = getWindowsRegString( k, QLatin1String("DhcpDomain") ); |
|
2273 if ( domainName->isEmpty() ) |
|
2274 *domainName = getWindowsRegString( k, QLatin1String("Domain") ); |
|
2275 |
|
2276 *nameServer = getWindowsRegString( k, QLatin1String("DhcpNameServer") ); |
|
2277 if ( nameServer->isEmpty() ) |
|
2278 *nameServer = getWindowsRegString( k, QLatin1String("NameServer") ); |
|
2279 |
|
2280 *searchList = getWindowsRegString( k, QLatin1String("SearchList") ); |
|
2281 } |
|
2282 RegCloseKey( k ); |
|
2283 return r == ERROR_SUCCESS; |
|
2284 } |
|
2285 |
|
2286 void Q3Dns::doResInit() |
|
2287 { |
|
2288 char separator = 0; |
|
2289 |
|
2290 if ( theNs ) |
|
2291 delete theNs; |
|
2292 theNs = new Q3PtrList<QHostAddress>; |
|
2293 theNs->setAutoDelete( true ); |
|
2294 theDomains = new Q3StrList( true ); |
|
2295 theDomains->setAutoDelete( true ); |
|
2296 |
|
2297 QString domainName, nameServer, searchList; |
|
2298 |
|
2299 bool gotNetworkParams = false; |
|
2300 // try the API call GetNetworkParams() first and use registry lookup only |
|
2301 // as a fallback |
|
2302 HINSTANCE hinstLib = LoadLibrary( L"iphlpapi" ); |
|
2303 if ( hinstLib != 0 ) { |
|
2304 #ifdef Q_OS_WINCE |
|
2305 GNP getNetworkParams = (GNP) GetProcAddress( hinstLib, L"GetNetworkParams" ); |
|
2306 #else |
|
2307 GNP getNetworkParams = (GNP) GetProcAddress( hinstLib, "GetNetworkParams" ); |
|
2308 #endif |
|
2309 if ( getNetworkParams != 0 ) { |
|
2310 ULONG l = 0; |
|
2311 DWORD res; |
|
2312 res = getNetworkParams( 0, &l ); |
|
2313 if ( res == ERROR_BUFFER_OVERFLOW ) { |
|
2314 FIXED_INFO *finfo = (FIXED_INFO*)new char[l]; |
|
2315 res = getNetworkParams( finfo, &l ); |
|
2316 if ( res == ERROR_SUCCESS ) { |
|
2317 domainName = QLatin1String(finfo->DomainName); |
|
2318 nameServer = QLatin1String(""); |
|
2319 IP_ADDR_STRING *dnsServer = &finfo->DnsServerList; |
|
2320 while ( dnsServer != 0 ) { |
|
2321 nameServer += QLatin1String(dnsServer->IpAddress.String); |
|
2322 dnsServer = dnsServer->Next; |
|
2323 if ( dnsServer != 0 ) |
|
2324 nameServer += QLatin1Char(' '); |
|
2325 } |
|
2326 searchList = QLatin1String(""); |
|
2327 separator = ' '; |
|
2328 gotNetworkParams = true; |
|
2329 } |
|
2330 delete[] finfo; |
|
2331 } |
|
2332 } |
|
2333 FreeLibrary( hinstLib ); |
|
2334 } |
|
2335 if ( !gotNetworkParams ) { |
|
2336 if ( getDnsParamsFromRegistry( |
|
2337 QLatin1String("System\\CurrentControlSet\\Services\\Tcpip\\Parameters"), |
|
2338 &domainName, &nameServer, &searchList )) { |
|
2339 separator = ' '; |
|
2340 } else { |
|
2341 // Could not access the TCP/IP parameters |
|
2342 domainName = QLatin1String(""); |
|
2343 nameServer = QLatin1String("127.0.0.1"); |
|
2344 searchList = QLatin1String(""); |
|
2345 separator = ' '; |
|
2346 } |
|
2347 } |
|
2348 |
|
2349 nameServer = nameServer.simplifyWhiteSpace(); |
|
2350 int first, last; |
|
2351 if ( !nameServer.isEmpty() ) { |
|
2352 first = 0; |
|
2353 do { |
|
2354 last = nameServer.find( QLatin1Char(separator), first ); |
|
2355 if ( last < 0 ) |
|
2356 last = nameServer.length(); |
|
2357 Q3Dns tmp( nameServer.mid( first, last-first ), Q3Dns::A ); |
|
2358 Q3ValueList<QHostAddress> address = tmp.addresses(); |
|
2359 Q_LONG i = address.count(); |
|
2360 while( i ) |
|
2361 theNs->append( new QHostAddress(address[--i]) ); |
|
2362 first = last+1; |
|
2363 } while( first < (int)nameServer.length() ); |
|
2364 } |
|
2365 |
|
2366 searchList += QLatin1Char(' ') + domainName; |
|
2367 searchList = searchList.simplifyWhiteSpace().lower(); |
|
2368 first = 0; |
|
2369 do { |
|
2370 last = searchList.find( QLatin1Char(separator), first ); |
|
2371 if ( last < 0 ) |
|
2372 last = searchList.length(); |
|
2373 theDomains->append( qstrdup( searchList.mid( first, last-first ).latin1() ) ); |
|
2374 first = last+1; |
|
2375 } while( first < (int)searchList.length() ); |
|
2376 } |
|
2377 |
|
2378 #elif defined(Q_OS_UNIX) |
|
2379 |
|
2380 #if defined(Q_DNS_SYNCHRONOUS) |
|
2381 void Q3Dns::doSynchronousLookup() |
|
2382 { |
|
2383 if ( t!=None && !l.isEmpty() ) { |
|
2384 Q3ValueListIterator<QString> it = n.begin(); |
|
2385 Q3ValueListIterator<QString> end = n.end(); |
|
2386 int type; |
|
2387 switch( t ) { |
|
2388 case Q3Dns::A: |
|
2389 type = 1; |
|
2390 break; |
|
2391 case Q3Dns::Aaaa: |
|
2392 type = 28; |
|
2393 break; |
|
2394 case Q3Dns::Mx: |
|
2395 type = 15; |
|
2396 break; |
|
2397 case Q3Dns::Srv: |
|
2398 type = 33; |
|
2399 break; |
|
2400 case Q3Dns::Cname: |
|
2401 type = 5; |
|
2402 break; |
|
2403 case Q3Dns::Ptr: |
|
2404 type = 12; |
|
2405 break; |
|
2406 case Q3Dns::Txt: |
|
2407 type = 16; |
|
2408 break; |
|
2409 default: |
|
2410 type = (char)255; // any |
|
2411 break; |
|
2412 } |
|
2413 while( it != end ) { |
|
2414 QString s = *it; |
|
2415 it++; |
|
2416 QByteArray ba( 512 ); |
|
2417 int len = res_search( s.latin1(), 1, type, (uchar*)ba.data(), ba.size() ); |
|
2418 if ( len > 0 ) { |
|
2419 ba.resize( len ); |
|
2420 |
|
2421 Q3DnsQuery * query = new Q3DnsQuery; |
|
2422 query->started = now(); |
|
2423 query->id = ++theId; |
|
2424 query->t = t; |
|
2425 query->l = s; |
|
2426 Q3DnsAnswer a( ba, query ); |
|
2427 a.parse(); |
|
2428 } else if ( len == -1 ) { |
|
2429 // res_search error |
|
2430 } |
|
2431 } |
|
2432 emit resultsReady(); |
|
2433 } |
|
2434 } |
|
2435 #endif |
|
2436 |
|
2437 #if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 3))) |
|
2438 #define Q_MODERN_RES_API |
|
2439 #endif |
|
2440 |
|
2441 void Q3Dns::doResInit() |
|
2442 { |
|
2443 if ( theNs ) |
|
2444 return; |
|
2445 theNs = new Q3PtrList<QHostAddress>; |
|
2446 theNs->setAutoDelete( true ); |
|
2447 theDomains = new Q3StrList( true ); |
|
2448 theDomains->setAutoDelete( true ); |
|
2449 |
|
2450 // read resolv.conf manually. |
|
2451 QFile resolvConf(QLatin1String("/etc/resolv.conf")); |
|
2452 if (resolvConf.open(QIODevice::ReadOnly)) { |
|
2453 QTextStream stream( &resolvConf ); |
|
2454 QString line; |
|
2455 |
|
2456 while ( !stream.atEnd() ) { |
|
2457 line = stream.readLine(); |
|
2458 QStringList list = QStringList::split( QLatin1String(" "), line ); |
|
2459 if( line.startsWith( QLatin1Char('#') ) || list.size() < 2 ) |
|
2460 continue; |
|
2461 const QString type = list[0].lower(); |
|
2462 |
|
2463 if ( type == QLatin1String("nameserver") ) { |
|
2464 QHostAddress *address = new QHostAddress(); |
|
2465 if ( address->setAddress( QString(list[1]) ) ) { |
|
2466 // only add ipv6 addresses from resolv.conf if |
|
2467 // this host supports ipv6. |
|
2468 if ( address->isIPv4Address() || ipv6support ) |
|
2469 theNs->append( address ); |
|
2470 else |
|
2471 delete address; |
|
2472 } else { |
|
2473 delete address; |
|
2474 } |
|
2475 } else if ( type == QLatin1String("search") ) { |
|
2476 QStringList srch = QStringList::split( QLatin1String(" "), list[1] ); |
|
2477 for ( QStringList::Iterator i = srch.begin(); i != srch.end(); ++i ) |
|
2478 theDomains->append( (*i).lower().local8Bit() ); |
|
2479 |
|
2480 } else if ( type == QLatin1String("domain") ) { |
|
2481 theDomains->append( list[1].lower().local8Bit() ); |
|
2482 } |
|
2483 } |
|
2484 } |
|
2485 |
|
2486 if (theNs->isEmpty()) { |
|
2487 #if defined(Q_MODERN_RES_API) |
|
2488 struct __res_state res; |
|
2489 res_ninit( &res ); |
|
2490 int i; |
|
2491 // find the name servers to use |
|
2492 for( i=0; i < MAXNS && i < res.nscount; i++ ) |
|
2493 theNs->append( new QHostAddress( ntohl( res.nsaddr_list[i].sin_addr.s_addr ) ) ); |
|
2494 # if defined(MAXDFLSRCH) |
|
2495 for( i=0; i < MAXDFLSRCH; i++ ) { |
|
2496 if ( res.dnsrch[i] && *(res.dnsrch[i]) ) |
|
2497 theDomains->append( QString::fromLatin1( res.dnsrch[i] ).lower().local8Bit() ); |
|
2498 else |
|
2499 break; |
|
2500 } |
|
2501 # endif |
|
2502 if ( *res.defdname ) |
|
2503 theDomains->append( QString::fromLatin1( res.defdname ).lower().local8Bit() ); |
|
2504 #else |
|
2505 res_init(); |
|
2506 int i; |
|
2507 // find the name servers to use |
|
2508 for( i=0; i < MAXNS && i < _res.nscount; i++ ) |
|
2509 theNs->append( new QHostAddress( ntohl( _res.nsaddr_list[i].sin_addr.s_addr ) ) ); |
|
2510 # if defined(MAXDFLSRCH) |
|
2511 for( i=0; i < MAXDFLSRCH; i++ ) { |
|
2512 if ( _res.dnsrch[i] && *(_res.dnsrch[i]) ) |
|
2513 theDomains->append( QString::fromLatin1( _res.dnsrch[i] ).lower().local8Bit() ); |
|
2514 else |
|
2515 break; |
|
2516 } |
|
2517 # endif |
|
2518 if ( *_res.defdname ) |
|
2519 theDomains->append( QString::fromLatin1( _res.defdname ).lower().local8Bit() ); |
|
2520 #endif |
|
2521 |
|
2522 // the code above adds "0.0.0.0" as a name server at the slightest |
|
2523 // hint of trouble. so remove those again. |
|
2524 theNs->first(); |
|
2525 while( theNs->current() ) { |
|
2526 if ( theNs->current()->isNull() ) |
|
2527 delete theNs->take(); |
|
2528 else |
|
2529 theNs->next(); |
|
2530 } |
|
2531 } |
|
2532 |
|
2533 QFile hosts( QString::fromLatin1( "/etc/hosts" ) ); |
|
2534 if ( hosts.open( QIODevice::ReadOnly ) ) { |
|
2535 // read the /etc/hosts file, creating long-life A and PTR RRs |
|
2536 // for the things we find. |
|
2537 QTextStream i( &hosts ); |
|
2538 QString line; |
|
2539 while( !i.atEnd() ) { |
|
2540 line = i.readLine().simplifyWhiteSpace().lower(); |
|
2541 uint n = 0; |
|
2542 while( (int) n < line.length() && line[(int)n] != QLatin1Char('#') ) |
|
2543 n++; |
|
2544 line.truncate( n ); |
|
2545 n = 0; |
|
2546 while( (int) n < line.length() && !line[(int)n].isSpace() ) |
|
2547 n++; |
|
2548 QString ip = line.left( n ); |
|
2549 QHostAddress a; |
|
2550 a.setAddress( ip ); |
|
2551 if ( ( a.isIPv4Address() || a.isIPv6Address() ) && !a.isNull() ) { |
|
2552 bool first = true; |
|
2553 line = line.mid( n+1 ); |
|
2554 n = 0; |
|
2555 while( (int) n < line.length() && !line[(int)n].isSpace() ) |
|
2556 n++; |
|
2557 QString hostname = line.left( n ); |
|
2558 // ### in case of bad syntax, hostname is invalid. do we care? |
|
2559 if ( n ) { |
|
2560 Q3DnsRR * rr = new Q3DnsRR( hostname ); |
|
2561 if ( a.isIPv4Address() ) |
|
2562 rr->t = Q3Dns::A; |
|
2563 else |
|
2564 rr->t = Q3Dns::Aaaa; |
|
2565 rr->address = a; |
|
2566 rr->deleteTime = UINT_MAX; |
|
2567 rr->expireTime = UINT_MAX; |
|
2568 rr->current = true; |
|
2569 if ( first ) { |
|
2570 first = false; |
|
2571 Q3DnsRR * ptr = new Q3DnsRR( Q3Dns::toInAddrArpaDomain( a ) ); |
|
2572 ptr->t = Q3Dns::Ptr; |
|
2573 ptr->target = hostname; |
|
2574 ptr->deleteTime = UINT_MAX; |
|
2575 ptr->expireTime = UINT_MAX; |
|
2576 ptr->current = true; |
|
2577 } |
|
2578 } |
|
2579 } |
|
2580 } |
|
2581 } |
|
2582 } |
|
2583 |
|
2584 #endif |
|
2585 |
|
2586 QT_END_NAMESPACE |
|
2587 |
|
2588 #endif // QT_NO_DNS |