~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/qt3support/network/q3dns.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

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