/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the Qt3Support module of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt LGPL Exception | |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include "qplatformdefs.h" | |
#include "qbytearray.h" | |
#include <private/qsystemlibrary_p.h> | |
#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_CYGWIN) | |
# include "qt_windows.h" | |
#else | |
# include <sys/types.h> | |
# include <netinet/in.h> | |
# include <arpa/nameser.h> | |
# include <resolv.h> | |
extern "C" int res_init(); | |
#endif | |
// POSIX Large File Support redefines open -> open64 | |
#if defined(open) | |
# undef open | |
#endif | |
// POSIX Large File Support redefines truncate -> truncate64 | |
#if defined(truncate) | |
# undef truncate | |
#endif | |
// Solaris redefines connect -> __xnet_connect with _XOPEN_SOURCE_EXTENDED. | |
#if defined(connect) | |
# undef connect | |
#endif | |
// UnixWare 7 redefines socket -> _socket | |
#if defined(socket) | |
# undef socket | |
#endif | |
#include "q3dns.h" | |
#ifndef QT_NO_DNS | |
#include "qdatetime.h" | |
#include "q3dict.h" | |
#include "q3ptrlist.h" | |
#include "qstring.h" | |
#include "qtimer.h" | |
#include "qapplication.h" | |
#include "q3ptrvector.h" | |
#include "q3strlist.h" | |
#include "q3ptrdict.h" | |
#include "qfile.h" | |
#include "qtextstream.h" | |
#include "q3socketdevice.h" | |
#include "q3cleanuphandler.h" | |
#include <limits.h> | |
QT_BEGIN_NAMESPACE | |
//#define Q3DNS_DEBUG | |
static Q_UINT16 theId; // ### seeded started by now() | |
static QDateTime * originOfTime = 0; | |
static Q3CleanupHandler<QDateTime> q3dns_cleanup_time; | |
static Q_UINT32 now() | |
{ | |
if ( originOfTime ) | |
return originOfTime->secsTo( QDateTime::currentDateTime() ); | |
originOfTime = new QDateTime( QDateTime::currentDateTime() ); | |
theId = originOfTime->time().msec() * 60 + originOfTime->time().second(); | |
q3dns_cleanup_time.add( &originOfTime ); | |
return 0; | |
} | |
static Q3PtrList<QHostAddress> * theNs = 0; | |
static Q3StrList * theDomains = 0; | |
static bool ipv6support = false; | |
class Q3DnsPrivate { | |
public: | |
Q3DnsPrivate() : queryTimer( 0 ), noNames(false) | |
{ | |
#if defined(Q_DNS_SYNCHRONOUS) | |
#if defined(Q_OS_UNIX) | |
noEventLoop = qApp==0 || qApp->loopLevel()==0; | |
#else | |
noEventLoop = false; | |
#endif | |
#endif | |
} | |
~Q3DnsPrivate() | |
{ | |
delete queryTimer; | |
} | |
private: | |
QTimer * queryTimer; | |
bool noNames; | |
#if defined(Q_DNS_SYNCHRONOUS) | |
bool noEventLoop; | |
#endif | |
friend class Q3Dns; | |
friend class Q3DnsAnswer; | |
}; | |
class Q3DnsRR; | |
class Q3DnsDomain; | |
// Q3DnsRR is the class used to store a single RR. Q3DnsRR can store | |
// all of the supported RR types. a Q3DnsRR is always cached. | |
// Q3DnsRR is mostly constructed from the outside. a but hacky, but | |
// permissible since the entire class is internal. | |
class Q3DnsRR { | |
public: | |
Q3DnsRR( const QString & label ); | |
~Q3DnsRR(); | |
public: | |
Q3DnsDomain * domain; | |
Q3Dns::RecordType t; | |
bool nxdomain; | |
bool current; | |
Q_UINT32 expireTime; | |
Q_UINT32 deleteTime; | |
// somewhat space-wasting per-type data | |
// a / aaaa | |
QHostAddress address; | |
// cname / mx / srv / ptr | |
QString target; | |
// mx / srv | |
Q_UINT16 priority; | |
// srv | |
Q_UINT16 weight; | |
Q_UINT16 port; | |
// txt | |
QString text; // could be overloaded into target... | |
private: | |
}; | |
class Q3DnsDomain { | |
public: | |
Q3DnsDomain( const QString & label ); | |
~Q3DnsDomain(); | |
static void add( const QString & label, Q3DnsRR * ); | |
static Q3PtrList<Q3DnsRR> * cached( const Q3Dns * ); | |
void take( Q3DnsRR * ); | |
void sweep( Q_UINT32 thisSweep ); | |
bool isEmpty() const { return rrs == 0 || rrs->isEmpty(); } | |
QString name() const { return l; } | |
public: | |
QString l; | |
Q3PtrList<Q3DnsRR> * rrs; | |
}; | |
class Q3DnsQuery: public QTimer { // this inheritance is a very evil hack | |
public: | |
Q3DnsQuery(): | |
id( 0 ), t( Q3Dns::None ), step(0), started(0), | |
dns( new Q3PtrDict<void>(17) ) {} | |
~Q3DnsQuery() { delete dns; } | |
Q_UINT16 id; | |
Q3Dns::RecordType t; | |
QString l; | |
uint step; | |
Q_UINT32 started; | |
Q3PtrDict<void> * dns; | |
}; | |
class Q3DnsAnswer { | |
public: | |
Q3DnsAnswer( Q3DnsQuery * ); | |
Q3DnsAnswer( const QByteArray &, Q3DnsQuery * ); | |
~Q3DnsAnswer(); | |
void parse(); | |
void notify(); | |
bool ok; | |
private: | |
Q3DnsQuery * query; | |
Q_UINT8 * answer; | |
int size; | |
int pp; | |
Q3PtrList<Q3DnsRR> * rrs; | |
// convenience | |
int next; | |
int ttl; | |
QString label; | |
Q3DnsRR * rr; | |
QString readString(bool multipleLabels = true); | |
void parseA(); | |
void parseAaaa(); | |
void parseMx(); | |
void parseSrv(); | |
void parseCname(); | |
void parsePtr(); | |
void parseTxt(); | |
void parseNs(); | |
}; | |
Q3DnsRR::Q3DnsRR( const QString & label ) | |
: domain( 0 ), t( Q3Dns::None ), | |
nxdomain( false ), current( false ), | |
expireTime( 0 ), deleteTime( 0 ), | |
priority( 0 ), weight( 0 ), port( 0 ) | |
{ | |
Q3DnsDomain::add( label, this ); | |
} | |
// not supposed to be deleted except by Q3DnsDomain | |
Q3DnsRR::~Q3DnsRR() | |
{ | |
// nothing is necessary | |
} | |
// this one just sticks in a NXDomain | |
Q3DnsAnswer::Q3DnsAnswer( Q3DnsQuery * query_ ) | |
{ | |
ok = true; | |
answer = 0; | |
size = 0; | |
query = query_; | |
pp = 0; | |
rrs = new Q3PtrList<Q3DnsRR>; | |
rrs->setAutoDelete( false ); | |
next = size; | |
ttl = 0; | |
label.clear(); | |
rr = 0; | |
Q3DnsRR * newrr = new Q3DnsRR( query->l ); | |
newrr->t = query->t; | |
newrr->deleteTime = query->started + 10; | |
newrr->expireTime = query->started + 10; | |
newrr->nxdomain = true; | |
newrr->current = true; | |
rrs->append( newrr ); | |
} | |
Q3DnsAnswer::Q3DnsAnswer( const QByteArray& answer_, | |
Q3DnsQuery * query_ ) | |
{ | |
ok = true; | |
answer = (Q_UINT8 *)(answer_.data()); | |
size = (int)answer_.size(); | |
query = query_; | |
pp = 0; | |
rrs = new Q3PtrList<Q3DnsRR>; | |
rrs->setAutoDelete( false ); | |
next = size; | |
ttl = 0; | |
label.clear(); | |
rr = 0; | |
} | |
Q3DnsAnswer::~Q3DnsAnswer() | |
{ | |
if ( !ok && rrs ) { | |
Q3PtrListIterator<Q3DnsRR> it( *rrs ); | |
Q3DnsRR * tmprr; | |
while( (tmprr=it.current()) != 0 ) { | |
++it; | |
tmprr->t = Q3Dns::None; // will be deleted soonish | |
} | |
} | |
delete rrs; | |
} | |
QString Q3DnsAnswer::readString(bool multipleLabels) | |
{ | |
int p = pp; | |
QString r; | |
Q_UINT8 b; | |
for( ;; ) { | |
b = 128; | |
// Read one character | |
if ( p >= 0 && p < size ) | |
b = answer[p]; | |
switch( b >> 6 ) { | |
case 0: | |
// b is less than 64 | |
p++; | |
// Detect end of data | |
if ( b == 0 ) { | |
if ( p > pp ) | |
pp = p; | |
return r.isNull() ? QLatin1String( "." ) : r; | |
} | |
// Read a label of size 'b' characters | |
if ( !r.isNull() ) | |
r += QLatin1Char('.'); | |
while( b-- > 0 ) | |
r += QLatin1Char( answer[p++] ); | |
// Return immediately if we were only supposed to read one | |
// label. | |
if (!multipleLabels) | |
return r; | |
break; | |
default: | |
// Ignore unrecognized control character, or p was out of | |
// range. | |
goto not_ok; | |
case 3: | |
// Use the next character to determine the relative offset | |
// to jump to before continuing the packet parsing. | |
int q = ( (answer[p] & 0x3f) << 8 ) + answer[p+1]; | |
if ( q >= pp || q >= p ) | |
goto not_ok; | |
if ( p >= pp ) | |
pp = p + 2; | |
p = q; | |
} | |
} | |
not_ok: | |
ok = false; | |
return QString(); | |
} | |
void Q3DnsAnswer::parseA() | |
{ | |
if ( next != pp + 4 ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns: saw %d bytes long IN A for %s", | |
next - pp, label.ascii() ); | |
#endif | |
return; | |
} | |
rr = new Q3DnsRR( label ); | |
rr->t = Q3Dns::A; | |
rr->address = QHostAddress( ( answer[pp+0] << 24 ) + | |
( answer[pp+1] << 16 ) + | |
( answer[pp+2] << 8 ) + | |
( answer[pp+3] ) ); | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns: saw %s IN A %s (ttl %d)", label.ascii(), | |
rr->address.toString().ascii(), ttl ); | |
#endif | |
} | |
void Q3DnsAnswer::parseAaaa() | |
{ | |
if ( next != pp + 16 ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns: saw %d bytes long IN Aaaa for %s", | |
next - pp, label.ascii() ); | |
#endif | |
return; | |
} | |
rr = new Q3DnsRR( label ); | |
rr->t = Q3Dns::Aaaa; | |
rr->address = QHostAddress( answer+pp ); | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns: saw %s IN Aaaa %s (ttl %d)", label.ascii(), | |
rr->address.toString().ascii(), ttl ); | |
#endif | |
} | |
void Q3DnsAnswer::parseMx() | |
{ | |
if ( next < pp + 2 ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns: saw %d bytes long IN MX for %s", | |
next - pp, label.ascii() ); | |
#endif | |
return; | |
} | |
rr = new Q3DnsRR( label ); | |
rr->priority = (answer[pp] << 8) + answer[pp+1]; | |
pp += 2; | |
rr->target = readString().lower(); | |
if ( !ok ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns: saw bad string in MX for %s", label.ascii() ); | |
#endif | |
return; | |
} | |
rr->t = Q3Dns::Mx; | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns: saw %s IN MX %d %s (ttl %d)", label.ascii(), | |
rr->priority, rr->target.ascii(), ttl ); | |
#endif | |
} | |
void Q3DnsAnswer::parseSrv() | |
{ | |
if ( next < pp + 6 ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns: saw %d bytes long IN SRV for %s", | |
next - pp, label.ascii() ); | |
#endif | |
return; | |
} | |
rr = new Q3DnsRR( label ); | |
rr->priority = (answer[pp] << 8) + answer[pp+1]; | |
rr->weight = (answer[pp+2] << 8) + answer[pp+3]; | |
rr->port = (answer[pp+4] << 8) + answer[pp+5]; | |
pp += 6; | |
rr->target = readString().lower(); | |
if ( !ok ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns: saw bad string in SRV for %s", label.ascii() ); | |
#endif | |
return; | |
} | |
rr->t = Q3Dns::Srv; | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns: saw %s IN SRV %d %d %d %s (ttl %d)", label.ascii(), | |
rr->priority, rr->weight, rr->port, rr->target.ascii(), ttl ); | |
#endif | |
} | |
void Q3DnsAnswer::parseCname() | |
{ | |
QString target = readString().lower(); | |
if ( !ok ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns: saw bad cname for for %s", label.ascii() ); | |
#endif | |
return; | |
} | |
rr = new Q3DnsRR( label ); | |
rr->t = Q3Dns::Cname; | |
rr->target = target; | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns: saw %s IN CNAME %s (ttl %d)", label.ascii(), | |
rr->target.ascii(), ttl ); | |
#endif | |
} | |
void Q3DnsAnswer::parseNs() | |
{ | |
QString target = readString().lower(); | |
if ( !ok ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns: saw bad cname for for %s", label.ascii() ); | |
#endif | |
return; | |
} | |
// parse, but ignore | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns: saw %s IN NS %s (ttl %d)", label.ascii(), | |
target.ascii(), ttl ); | |
#endif | |
} | |
void Q3DnsAnswer::parsePtr() | |
{ | |
QString target = readString().lower(); | |
if ( !ok ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns: saw bad PTR for for %s", label.ascii() ); | |
#endif | |
return; | |
} | |
rr = new Q3DnsRR( label ); | |
rr->t = Q3Dns::Ptr; | |
rr->target = target; | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns: saw %s IN PTR %s (ttl %d)", label.ascii(), | |
rr->target.ascii(), ttl ); | |
#endif | |
} | |
void Q3DnsAnswer::parseTxt() | |
{ | |
QString text = readString(false); | |
if ( !ok ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns: saw bad TXT for for %s", label.ascii() ); | |
#endif | |
return; | |
} | |
rr = new Q3DnsRR( label ); | |
rr->t = Q3Dns::Txt; | |
rr->text = text; | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns: saw %s IN TXT \"%s\" (ttl %d)", label.ascii(), | |
rr->text.ascii(), ttl ); | |
#endif | |
} | |
void Q3DnsAnswer::parse() | |
{ | |
// okay, do the work... | |
if ( (answer[2] & 0x78) != 0 ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "DNS Manager: answer to wrong query type (%d)", answer[1] ); | |
#endif | |
ok = false; | |
return; | |
} | |
// AA | |
bool aa = (answer[2] & 4) != 0; | |
// TC | |
if ( (answer[2] & 2) != 0 ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "DNS Manager: truncated answer; pressing on" ); | |
#endif | |
} | |
// RD | |
bool rd = (answer[2] & 1) != 0; | |
// we don't test RA | |
// we don't test the MBZ fields | |
if ( (answer[3] & 0x0f) == 3 ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "DNS Manager: saw NXDomain for %s", query->l.ascii() ); | |
#endif | |
// NXDomain. cache that for one minute. | |
rr = new Q3DnsRR( query->l ); | |
rr->t = query->t; | |
rr->deleteTime = query->started + 60; | |
rr->expireTime = query->started + 60; | |
rr->nxdomain = true; | |
rr->current = true; | |
rrs->append( rr ); | |
return; | |
} | |
if ( (answer[3] & 0x0f) != 0 ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "DNS Manager: error code %d", answer[3] & 0x0f ); | |
#endif | |
ok = false; | |
return; | |
} | |
int qdcount = ( answer[4] << 8 ) + answer[5]; | |
int ancount = ( answer[6] << 8 ) + answer[7]; | |
int nscount = ( answer[8] << 8 ) + answer[9]; | |
int adcount = (answer[10] << 8 ) +answer[11]; | |
pp = 12; | |
// read query | |
while( qdcount > 0 && pp < size ) { | |
// should I compare the string against query->l? | |
(void)readString(); | |
if ( !ok ) | |
return; | |
pp += 4; | |
qdcount--; | |
} | |
// answers and stuff | |
int rrno = 0; | |
// if we parse the answer completely, but there are no answers, | |
// ignore the entire thing. | |
int answers = 0; | |
while( ( rrno < ancount || | |
( ok && answers >0 && rrno < ancount + nscount + adcount ) ) && | |
pp < size ) { | |
label = readString().lower(); | |
if ( !ok ) | |
return; | |
int rdlength = 0; | |
if ( pp + 10 <= size ) | |
rdlength = ( answer[pp+8] << 8 ) + answer[pp+9]; | |
if ( pp + 10 + rdlength > size ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "DNS Manager: ran out of stuff to parse (%d+%d>%d (%d)", | |
pp, rdlength, size, rrno < ancount ); | |
#endif | |
// if we're still in the AN section, we should go back and | |
// at least down the TTLs. probably best to invalidate | |
// the results. | |
// the rrs list is good for this | |
ok = ( rrno < ancount ); | |
return; | |
} | |
uint type, clas; | |
type = ( answer[pp+0] << 8 ) + answer[pp+1]; | |
clas = ( answer[pp+2] << 8 ) + answer[pp+3]; | |
ttl = ( answer[pp+4] << 24 ) + ( answer[pp+5] << 16 ) + | |
( answer[pp+6] << 8 ) + answer[pp+7]; | |
pp = pp + 10; | |
if ( clas != 1 ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "DNS Manager: class %d (not internet) for %s", | |
clas, label.isNull() ? "." : label.ascii() ); | |
#endif | |
} else { | |
next = pp + rdlength; | |
rr = 0; | |
switch( type ) { | |
case 1: | |
parseA(); | |
break; | |
case 28: | |
parseAaaa(); | |
break; | |
case 15: | |
parseMx(); | |
break; | |
case 33: | |
parseSrv(); | |
break; | |
case 5: | |
parseCname(); | |
break; | |
case 12: | |
parsePtr(); | |
break; | |
case 16: | |
parseTxt(); | |
break; | |
case 2: | |
parseNs(); | |
break; | |
default: | |
// something we don't know | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "DNS Manager: type %d for %s", type, | |
label.isNull() ? "." : label.ascii() ); | |
#endif | |
break; | |
} | |
if ( rr ) { | |
rr->deleteTime = 0; | |
if ( ttl > 0 ) | |
rr->expireTime = query->started + ttl; | |
else | |
rr->expireTime = query->started + 20; | |
if ( rrno < ancount ) { | |
answers++; | |
rr->deleteTime = rr->expireTime; | |
} | |
rr->current = true; | |
rrs->append( rr ); | |
} | |
} | |
if ( !ok ) | |
return; | |
pp = next; | |
next = size; | |
rrno++; | |
} | |
if ( answers == 0 ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "DNS Manager: answer contained no answers" ); | |
#endif | |
ok = ( aa && rd ); | |
} | |
// now go through the list and mark all the As that are referenced | |
// by something we care about. we want to cache such As. | |
rrs->first(); | |
Q3Dict<void> used( 17 ); | |
used.setAutoDelete( false ); | |
while( (rr=rrs->current()) != 0 ) { | |
rrs->next(); | |
if ( rr->target.length() && rr->deleteTime > 0 && rr->current ) | |
used.insert( rr->target, (void*)42 ); | |
if ( ( rr->t == Q3Dns::A || rr->t == Q3Dns::Aaaa ) && | |
used.find( rr->domain->name() ) != 0 ) | |
rr->deleteTime = rr->expireTime; | |
} | |
// next, for each RR, delete any older RRs that are equal to it | |
rrs->first(); | |
while( (rr=rrs->current()) != 0 ) { | |
rrs->next(); | |
if ( rr && rr->domain && rr->domain->rrs ) { | |
Q3PtrList<Q3DnsRR> * drrs = rr->domain->rrs; | |
drrs->first(); | |
Q3DnsRR * older; | |
while( (older=drrs->current()) != 0 ) { | |
if ( older != rr && | |
older->t == rr->t && | |
older->nxdomain == rr->nxdomain && | |
older->address == rr->address && | |
older->target == rr->target && | |
older->priority == rr->priority && | |
older->weight == rr->weight && | |
older->port == rr->port && | |
older->text == rr->text ) { | |
// well, it's equal, but it's not the same. so we kill it, | |
// but use its expiry time. | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "killing off old %d for %s, expire was %d", | |
older->t, older->domain->name().latin1(), | |
rr->expireTime ); | |
#endif | |
older->t = Q3Dns::None; | |
rr->expireTime = QMAX( older->expireTime, rr->expireTime ); | |
rr->deleteTime = QMAX( older->deleteTime, rr->deleteTime ); | |
older->deleteTime = 0; | |
#if defined(Q3DNS_DEBUG) | |
qDebug( " adjusted expire is %d", rr->expireTime ); | |
#endif | |
} | |
drrs->next(); | |
} | |
} | |
} | |
#if defined(Q3DNS_DEBUG) | |
//qDebug( "DNS Manager: ()" ); | |
#endif | |
} | |
class Q3DnsUgleHack: public Q3Dns { | |
public: | |
void ugle( bool emitAnyway=false ); | |
}; | |
void Q3DnsAnswer::notify() | |
{ | |
if ( !rrs || !ok || !query || !query->dns ) | |
return; | |
Q3PtrDict<void> notified; | |
notified.setAutoDelete( false ); | |
Q3PtrDictIterator<void> it( *query->dns ); | |
Q3Dns * dns; | |
it.toFirst(); | |
while( (dns=(Q3Dns*)(it.current())) != 0 ) { | |
++it; | |
if ( notified.find( (void*)dns ) == 0 ) { | |
notified.insert( (void*)dns, (void*)42 ); | |
if ( rrs->count() == 0 ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "DNS Manager: found no answers!" ); | |
#endif | |
dns->d->noNames = true; | |
((Q3DnsUgleHack*)dns)->ugle( true ); | |
} else { | |
QStringList n = dns->qualifiedNames(); | |
if ( query && n.contains(query->l) ) | |
((Q3DnsUgleHack*)dns)->ugle(); | |
#if defined(Q3DNS_DEBUG) | |
else | |
qDebug( "DNS Manager: DNS thing %s not notified for %s", | |
dns->label().ascii(), query->l.ascii() ); | |
#endif | |
} | |
} | |
} | |
} | |
// | |
// | |
// Q3DnsManager | |
// | |
// | |
class Q3DnsManager: public Q3DnsSocket { | |
private: | |
public: // just to silence the moronic g++. | |
Q3DnsManager(); | |
~Q3DnsManager(); | |
public: | |
static Q3DnsManager * manager(); | |
Q3DnsDomain * domain( const QString & ); | |
void transmitQuery( Q3DnsQuery * ); | |
void transmitQuery( int ); | |
// reimplementation of the slots | |
void cleanCache(); | |
void retransmit(); | |
void answer(); | |
public: | |
Q3PtrVector<Q3DnsQuery> queries; | |
Q3Dict<Q3DnsDomain> cache; | |
Q3SocketDevice * ipv4Socket; | |
#if !defined (QT_NO_IPV6) | |
Q3SocketDevice * ipv6Socket; | |
#endif | |
}; | |
static Q3DnsManager * globalManager = 0; | |
static void cleanupDns() | |
{ | |
delete globalManager; | |
globalManager = 0; | |
} | |
Q3DnsManager * Q3DnsManager::manager() | |
{ | |
if ( !globalManager ) { | |
qAddPostRoutine(cleanupDns); | |
new Q3DnsManager(); | |
} | |
return globalManager; | |
} | |
void Q3DnsUgleHack::ugle( bool emitAnyway) | |
{ | |
if ( emitAnyway || !isWorking() ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "DNS Manager: status change for %s (type %d)", | |
label().ascii(), recordType() ); | |
#endif | |
emit resultsReady(); | |
} | |
} | |
Q3DnsManager::Q3DnsManager() | |
: Q3DnsSocket( qApp, "Internal DNS manager" ), | |
queries( Q3PtrVector<Q3DnsQuery>( 0 ) ), | |
cache( Q3Dict<Q3DnsDomain>( 83, false ) ), | |
ipv4Socket( new Q3SocketDevice( Q3SocketDevice::Datagram, Q3SocketDevice::IPv4, 0 ) ) | |
#if !defined (QT_NO_IPV6) | |
, ipv6Socket( new Q3SocketDevice( Q3SocketDevice::Datagram, Q3SocketDevice::IPv6, 0 ) ) | |
#endif | |
{ | |
cache.setAutoDelete( true ); | |
globalManager = this; | |
QTimer * sweepTimer = new QTimer( this ); | |
sweepTimer->start( 1000 * 60 * 3 ); | |
connect( sweepTimer, SIGNAL(timeout()), | |
this, SLOT(cleanCache()) ); | |
QSocketNotifier * rn4 = new QSocketNotifier( ipv4Socket->socket(), | |
QSocketNotifier::Read, | |
this, "dns IPv4 socket watcher" ); | |
ipv4Socket->setAddressReusable( false ); | |
ipv4Socket->setBlocking( false ); | |
connect( rn4, SIGNAL(activated(int)), SLOT(answer()) ); | |
#if !defined (QT_NO_IPV6) | |
// Don't connect the IPv6 socket notifier if the host does not | |
// support IPv6. | |
if ( ipv6Socket->socket() != -1 ) { | |
QSocketNotifier * rn6 = new QSocketNotifier( ipv6Socket->socket(), | |
QSocketNotifier::Read, | |
this, "dns IPv6 socket watcher" ); | |
ipv6support = true; | |
ipv6Socket->setAddressReusable( false ); | |
ipv6Socket->setBlocking( false ); | |
connect( rn6, SIGNAL(activated(int)), SLOT(answer()) ); | |
} | |
#endif | |
if ( !theNs ) | |
Q3Dns::doResInit(); | |
// O(n*n) stuff here. but for 3 and 6, O(n*n) with a low k should | |
// be perfect. the point is to eliminate any duplicates that | |
// might be hidden in the lists. | |
Q3PtrList<QHostAddress> * ns = new Q3PtrList<QHostAddress>; | |
theNs->first(); | |
QHostAddress * h; | |
while( (h=theNs->current()) != 0 ) { | |
ns->first(); | |
while( ns->current() != 0 && !(*ns->current() == *h) ) | |
ns->next(); | |
if ( !ns->current() ) { | |
ns->append( new QHostAddress(*h) ); | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "using name server %s", h->toString().latin1() ); | |
} else { | |
qDebug( "skipping address %s", h->toString().latin1() ); | |
#endif | |
} | |
theNs->next(); | |
} | |
delete theNs; | |
theNs = ns; | |
theNs->setAutoDelete( true ); | |
Q3StrList * domains = new Q3StrList( true ); | |
theDomains->first(); | |
const char * s; | |
while( (s=theDomains->current()) != 0 ) { | |
domains->first(); | |
while( domains->current() != 0 && qstrcmp( domains->current(), s ) ) | |
domains->next(); | |
if ( !domains->current() ) { | |
domains->append( s ); | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "searching domain %s", s ); | |
} else { | |
qDebug( "skipping domain %s", s ); | |
#endif | |
} | |
theDomains->next(); | |
} | |
delete theDomains; | |
theDomains = domains; | |
theDomains->setAutoDelete( true ); | |
} | |
Q3DnsManager::~Q3DnsManager() | |
{ | |
if ( globalManager ) | |
globalManager = 0; | |
queries.setAutoDelete( true ); | |
cache.setAutoDelete( true ); | |
delete ipv4Socket; | |
#if !defined (QT_NO_IPV6) | |
delete ipv6Socket; | |
#endif | |
} | |
static Q_UINT32 lastSweep = 0; | |
void Q3DnsManager::cleanCache() | |
{ | |
bool again = false; | |
Q3DictIterator<Q3DnsDomain> it( cache ); | |
Q3DnsDomain * d; | |
Q_UINT32 thisSweep = now(); | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3DnsManager::cleanCache(: Called, time is %u, last was %u", | |
thisSweep, lastSweep ); | |
#endif | |
while( (d=it.current()) != 0 ) { | |
++it; | |
d->sweep( thisSweep ); // after this, d may be empty | |
if ( !again ) | |
again = !d->isEmpty(); | |
} | |
if ( !again ) | |
delete this; | |
lastSweep = thisSweep; | |
} | |
void Q3DnsManager::retransmit() | |
{ | |
const QObject * o = sender(); | |
if ( o == 0 || globalManager == 0 || this != globalManager ) | |
return; | |
uint q = 0; | |
while( q < queries.size() && queries[q] != o ) | |
q++; | |
if ( q < queries.size() ) | |
transmitQuery( q ); | |
} | |
void Q3DnsManager::answer() | |
{ | |
QByteArray a( 16383 ); // large enough for anything, one suspects | |
int r; | |
#if defined (QT_NO_IPV6) | |
r = ipv4Socket->readBlock(a.data(), a.size()); | |
#else | |
if (((QSocketNotifier *)sender())->socket() == ipv4Socket->socket()) | |
r = ipv4Socket->readBlock(a.data(), a.size()); | |
else | |
r = ipv6Socket->readBlock(a.data(), a.size()); | |
#endif | |
#if defined(Q3DNS_DEBUG) | |
#if !defined (QT_NO_IPV6) | |
qDebug("DNS Manager: answer arrived: %d bytes from %s:%d", r, | |
useIpv4Socket ? ipv4Socket->peerAddress().toString().ascii() | |
: ipv6Socket->peerAddress().toString().ascii(), | |
useIpv4Socket ? ipv4Socket->peerPort() : ipv6Socket->peerPort() ); | |
#else | |
qDebug("DNS Manager: answer arrived: %d bytes from %s:%d", r, | |
ipv4Socket->peerAddress().toString().ascii(), ipv4Socket->peerPort());; | |
#endif | |
#endif | |
if ( r < 12 ) | |
return; | |
// maybe we should check that the answer comes from port 53 on one | |
// of our name servers... | |
a.resize( r ); | |
Q_UINT16 aid = (((Q_UINT8)a[0]) << 8) + ((Q_UINT8)a[1]); | |
uint i = 0; | |
while( i < queries.size() && | |
!( queries[i] && queries[i]->id == aid ) ) | |
i++; | |
if ( i == queries.size() ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "DNS Manager: bad id (0x%04x) %d", aid, i ); | |
#endif | |
return; | |
} | |
// at this point queries[i] is whatever we asked for. | |
if ( ( (Q_UINT8)(a[2]) & 0x80 ) == 0 ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "DNS Manager: received a query" ); | |
#endif | |
return; | |
} | |
Q3DnsQuery * q = queries[i]; | |
Q3DnsAnswer answer( a, q ); | |
answer.parse(); | |
if ( answer.ok ) { | |
queries.take( i ); | |
answer.notify(); | |
delete q; | |
} | |
} | |
void Q3DnsManager::transmitQuery( Q3DnsQuery * query_ ) | |
{ | |
if ( !query_ ) | |
return; | |
uint i = 0; | |
while( i < queries.size() && queries[i] != 0 ) | |
i++; | |
if ( i == queries.size() ) | |
queries.resize( i+1 ); | |
queries.insert( i, query_ ); | |
transmitQuery( i ); | |
} | |
void Q3DnsManager::transmitQuery( int i ) | |
{ | |
if ( i < 0 || i >= (int)queries.size() ) | |
return; | |
Q3DnsQuery * q = queries[i]; | |
if ( q && q->step > 8 ) { | |
// okay, we've run out of retransmissions. we fake an NXDomain | |
// with a very short life time... | |
Q3DnsAnswer answer( q ); | |
answer.notify(); | |
// and then get rid of the query | |
queries.take( i ); | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "DNS Manager: giving up on query 0x%04x", q->id ); | |
#endif | |
delete q; | |
QTimer::singleShot( 0, Q3DnsManager::manager(), SLOT(cleanCache()) ); | |
// and don't process anything more | |
return; | |
} | |
if ((q && !q->dns) || q->dns->isEmpty()) | |
// no one currently wants the answer, so there's no point in | |
// retransmitting the query. we keep it, though. an answer may | |
// arrive for an earlier query transmission, and if it does we | |
// may benefit from caching the result. | |
return; | |
QByteArray p( 12 + q->l.length() + 2 + 4 ); | |
if ( p.size() > 500 ) | |
return; // way over the limit, so don't even try | |
// header | |
// id | |
p[0] = (q->id & 0xff00) >> 8; | |
p[1] = q->id & 0x00ff; | |
p[2] = 1; // recursion desired, rest is 0 | |
p[3] = 0; // all is 0 | |
// one query | |
p[4] = 0; | |
p[5] = 1; | |
// no answers, name servers or additional data | |
p[6] = p[7] = p[8] = p[9] = p[10] = p[11] = 0; | |
// the name is composed of several components. each needs to be | |
// written by itself... so we write... | |
// oh, and we assume that there's no funky characters in there. | |
int pp = 12; | |
uint lp = 0; | |
while( lp < (uint) q->l.length() ) { | |
int le = q->l.find( QLatin1Char('.'), lp ); | |
if ( le < 0 ) | |
le = q->l.length(); | |
QString component = q->l.mid( lp, le-lp ); | |
p[pp++] = component.length(); | |
int cp; | |
for( cp=0; cp < (int)component.length(); cp++ ) | |
p[pp++] = component[cp].latin1(); | |
lp = le + 1; | |
} | |
// final null | |
p[pp++] = 0; | |
// query type | |
p[pp++] = 0; | |
switch( q->t ) { | |
case Q3Dns::A: | |
p[pp++] = 1; | |
break; | |
case Q3Dns::Aaaa: | |
p[pp++] = 28; | |
break; | |
case Q3Dns::Mx: | |
p[pp++] = 15; | |
break; | |
case Q3Dns::Srv: | |
p[pp++] = 33; | |
break; | |
case Q3Dns::Cname: | |
p[pp++] = 5; | |
break; | |
case Q3Dns::Ptr: | |
p[pp++] = 12; | |
break; | |
case Q3Dns::Txt: | |
p[pp++] = 16; | |
break; | |
default: | |
p[pp++] = (char)255; // any | |
break; | |
} | |
// query class (always internet) | |
p[pp++] = 0; | |
p[pp++] = 1; | |
// if we have no name servers, we should regenerate ns in case | |
// name servers have recently been defined (like on windows, | |
// plugging/unplugging the network cable will change the name | |
// server entries) | |
if ( !theNs || theNs->isEmpty() ) | |
Q3Dns::doResInit(); | |
if ( !theNs || theNs->isEmpty() ) { | |
// we don't find any name servers. We fake an NXDomain | |
// with a very short life time... | |
Q3DnsAnswer answer( q ); | |
answer.notify(); | |
// and then get rid of the query | |
queries.take( i ); | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "DNS Manager: no DNS server found on query 0x%04x", q->id ); | |
#endif | |
delete q; | |
QTimer::singleShot( 1000*10, Q3DnsManager::manager(), SLOT(cleanCache()) ); | |
// and don't process anything more | |
return; | |
} | |
QHostAddress receiver = *theNs->at( q->step % theNs->count() ); | |
if (receiver.isIPv4Address()) | |
ipv4Socket->writeBlock( p.data(), pp, receiver, 53 ); | |
#if !defined (QT_NO_IPV6) | |
else | |
ipv6Socket->writeBlock( p.data(), pp, receiver, 53 ); | |
#endif | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "issuing query 0x%04x (%d) about %s type %d to %s", | |
q->id, q->step, q->l.ascii(), q->t, | |
ns->at( q->step % ns->count() )->toString().ascii() ); | |
#endif | |
if ( theNs->count() > 1 && q->step == 0 && queries.count() == 1 ) { | |
// if it's the first time, and we don't have any other | |
// outstanding queries, send nonrecursive queries to the other | |
// name servers too. | |
p[2] = 0; | |
QHostAddress * server; | |
while( (server=theNs->next()) != 0 ) { | |
if (server->isIPv4Address()) | |
ipv4Socket->writeBlock( p.data(), pp, *server, 53 ); | |
#if !defined (QT_NO_IPV6) | |
else | |
ipv6Socket->writeBlock( p.data(), pp, *server, 53 ); | |
#endif | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "copying query to %s", server->toString().ascii() ); | |
#endif | |
} | |
} | |
q->step++; | |
// some testing indicates that normal dns queries take up to 0.6 | |
// seconds. the graph becomes steep around that point, and the | |
// number of errors rises... so it seems good to retry at that | |
// point. | |
q->start( q->step < theNs->count() ? 800 : 1500, true ); | |
} | |
Q3DnsDomain * Q3DnsManager::domain( const QString & label ) | |
{ | |
Q3DnsDomain * d = cache.find( label ); | |
if ( !d ) { | |
d = new Q3DnsDomain( label ); | |
cache.insert( label, d ); | |
} | |
return d; | |
} | |
// | |
// | |
// the Q3DnsDomain class looks after and coordinates queries for Q3DnsRRs for | |
// each domain, and the cached Q3DnsRRs. (A domain, in DNS terminology, is | |
// a node in the DNS. "no", "trolltech.com" and "lupinella.troll.no" are | |
// all domains.) | |
// | |
// | |
Q3DnsDomain::Q3DnsDomain( const QString & label ) | |
{ | |
l = label; | |
rrs = 0; | |
} | |
Q3DnsDomain::~Q3DnsDomain() | |
{ | |
delete rrs; | |
rrs = 0; | |
} | |
void Q3DnsDomain::add( const QString & label, Q3DnsRR * rr ) | |
{ | |
Q3DnsDomain * d = Q3DnsManager::manager()->domain( label ); | |
if ( !d->rrs ) { | |
d->rrs = new Q3PtrList<Q3DnsRR>; | |
d->rrs->setAutoDelete( true ); | |
} | |
d->rrs->append( rr ); | |
rr->domain = d; | |
} | |
Q3PtrList<Q3DnsRR> * Q3DnsDomain::cached( const Q3Dns * r ) | |
{ | |
Q3PtrList<Q3DnsRR> * l = new Q3PtrList<Q3DnsRR>; | |
// test at first if you have to start a query at all | |
if ( r->recordType() == Q3Dns::A ) { | |
if ( r->label().lower() == QLatin1String("localhost") ) { | |
// undocumented hack. ipv4-specific. also, may be a memory | |
// leak? not sure. would be better to do this in doResInit(), | |
// anyway. | |
Q3DnsRR *rrTmp = new Q3DnsRR( r->label() ); | |
rrTmp->t = Q3Dns::A; | |
rrTmp->address = QHostAddress( 0x7f000001 ); | |
rrTmp->current = true; | |
l->append( rrTmp ); | |
return l; | |
} | |
QHostAddress tmp; | |
if ( tmp.setAddress( r->label() ) ) { | |
Q3DnsRR *rrTmp = new Q3DnsRR( r->label() ); | |
if ( tmp.isIPv4Address() ) { | |
rrTmp->t = Q3Dns::A; | |
rrTmp->address = tmp; | |
rrTmp->current = true; | |
l->append( rrTmp ); | |
} else { | |
rrTmp->nxdomain = true; | |
} | |
return l; | |
} | |
} | |
if ( r->recordType() == Q3Dns::Aaaa ) { | |
QHostAddress tmp; | |
if ( tmp.setAddress(r->label()) ) { | |
Q3DnsRR *rrTmp = new Q3DnsRR( r->label() ); | |
if ( tmp.isIPv6Address() ) { | |
rrTmp->t = Q3Dns::Aaaa; | |
rrTmp->address = tmp; | |
rrTmp->current = true; | |
l->append( rrTmp ); | |
} else { | |
rrTmp->nxdomain = true; | |
} | |
return l; | |
} | |
} | |
// if you reach this point, you have to do the query | |
Q3DnsManager * m = Q3DnsManager::manager(); | |
QStringList n = r->qualifiedNames(); | |
bool nxdomain; | |
int cnamecount = 0; | |
int it = 0; | |
while( it < n.count() ) { | |
QString s = n.at(it++); | |
nxdomain = false; | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "looking at cache for %s (%s %d)", | |
s.ascii(), r->label().ascii(), r->recordType() ); | |
#endif | |
Q3DnsDomain * d = m->domain( s ); | |
#if defined(Q3DNS_DEBUG) | |
qDebug( " - found %d RRs", d && d->rrs ? d->rrs->count() : 0 ); | |
#endif | |
if ( d->rrs ) | |
d->rrs->first(); | |
Q3DnsRR * rr; | |
bool answer = false; | |
while( d->rrs && (rr=d->rrs->current()) != 0 ) { | |
if ( rr->t == Q3Dns::Cname && r->recordType() != Q3Dns::Cname && | |
!rr->nxdomain && cnamecount < 16 ) { | |
// cname. if the code is ugly, that may just | |
// possibly be because the concept is. | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "found cname from %s to %s", | |
r->label().ascii(), rr->target.ascii() ); | |
#endif | |
s = rr->target; | |
d = m->domain( s ); | |
if ( d->rrs ) | |
d->rrs->first(); | |
it = n.count(); | |
// we've elegantly moved over to whatever the cname | |
// pointed to. well, not elegantly. let's remember | |
// that we've done something, anyway, so we can't be | |
// fooled into an infinte loop as well. | |
cnamecount++; | |
} else { | |
if ( rr->t == r->recordType() ) { | |
if ( rr->nxdomain ) | |
nxdomain = true; | |
else | |
answer = true; | |
l->append( rr ); | |
if ( rr->deleteTime <= lastSweep ) { | |
// we're returning something that'll be | |
// deleted soon. we assume that if the client | |
// wanted it twice, it'll want it again, so we | |
// ask the name server again right now. | |
Q3DnsQuery * query = new Q3DnsQuery; | |
query->started = now(); | |
query->id = ++theId; | |
query->t = rr->t; | |
query->l = rr->domain->name(); | |
// note that here, we don't bother about | |
// notification. but we do bother about | |
// timeouts: we make sure to use high timeouts | |
// and few tramsissions. | |
query->step = theNs->count(); | |
QObject::connect( query, SIGNAL(timeout()), | |
Q3DnsManager::manager(), | |
SLOT(retransmit()) ); | |
Q3DnsManager::manager()->transmitQuery( query ); | |
} | |
} | |
d->rrs->next(); | |
} | |
} | |
// if we found a positive result, return quickly | |
if ( answer && l->count() ) { | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "found %d records for %s", | |
l->count(), r->label().ascii() ); | |
l->first(); | |
while( l->current() ) { | |
qDebug( " type %d target %s address %s", | |
l->current()->t, | |
l->current()->target.latin1(), | |
l->current()->address.toString().latin1() ); | |
l->next(); | |
} | |
#endif | |
l->first(); | |
return l; | |
} | |
#if defined(Q3DNS_DEBUG) | |
if ( nxdomain ) | |
qDebug( "found NXDomain %s", s.ascii() ); | |
#endif | |
if ( !nxdomain ) { | |
// if we didn't, and not a negative result either, perhaps | |
// we need to transmit a query. | |
uint q = 0; | |
while ( q < m->queries.size() && | |
( m->queries[q] == 0 || | |
m->queries[q]->t != r->recordType() || | |
m->queries[q]->l != s ) ) | |
q++; | |
// we haven't done it before, so maybe we should. but | |
// wait - if it's an unqualified name, only ask when all | |
// the other alternatives are exhausted. | |
if ( q == m->queries.size() && ( s.find( QLatin1Char('.') ) >= 0 || | |
int(l->count()) >= n.count()-1 ) ) { | |
Q3DnsQuery * query = new Q3DnsQuery; | |
query->started = now(); | |
query->id = ++theId; | |
query->t = r->recordType(); | |
query->l = s; | |
query->dns->replace( (void*)r, (void*)r ); | |
QObject::connect( query, SIGNAL(timeout()), | |
Q3DnsManager::manager(), SLOT(retransmit()) ); | |
Q3DnsManager::manager()->transmitQuery( query ); | |
} else if ( q < m->queries.size() ) { | |
// if we've found an earlier query for the same | |
// domain/type, subscribe to its answer | |
m->queries[q]->dns->replace( (void*)r, (void*)r ); | |
} | |
} | |
} | |
l->first(); | |
return l; | |
} | |
void Q3DnsDomain::sweep( Q_UINT32 thisSweep ) | |
{ | |
if ( !rrs ) | |
return; | |
Q3DnsRR * rr; | |
rrs->first(); | |
while( (rr=rrs->current()) != 0 ) { | |
if ( !rr->deleteTime ) | |
rr->deleteTime = thisSweep; // will hit next time around | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns::sweep: %s type %d expires %u %u - %s / %s", | |
rr->domain->name().latin1(), rr->t, | |
rr->expireTime, rr->deleteTime, | |
rr->target.latin1(), rr->address.toString().latin1()); | |
#endif | |
if ( rr->current == false || | |
rr->t == Q3Dns::None || | |
rr->deleteTime <= thisSweep || | |
rr->expireTime <= thisSweep ) | |
rrs->remove(); | |
else | |
rrs->next(); | |
} | |
if ( rrs->isEmpty() ) { | |
delete rrs; | |
rrs = 0; | |
} | |
} | |
// the itsy-bitsy little socket class I don't really need except for | |
// so I can subclass and reimplement the slots. | |
Q3DnsSocket::Q3DnsSocket( QObject * parent, const char * name ) | |
: QObject( parent, name ) | |
{ | |
// nothing | |
} | |
Q3DnsSocket::~Q3DnsSocket() | |
{ | |
// nothing | |
} | |
void Q3DnsSocket::cleanCache() | |
{ | |
// nothing | |
} | |
void Q3DnsSocket::retransmit() | |
{ | |
// nothing | |
} | |
void Q3DnsSocket::answer() | |
{ | |
// nothing | |
} | |
/*! | |
\class Q3Dns | |
\brief The Q3Dns class provides asynchronous DNS lookups. | |
\compat | |
Both Windows and Unix provide synchronous DNS lookups; Windows | |
provides some asynchronous support too. At the time of writing | |
neither operating system provides asynchronous support for | |
anything other than hostname-to-address mapping. | |
Q3Dns rectifies this shortcoming, by providing asynchronous caching | |
lookups for the record types that we expect modern GUI | |
applications to need in the near future. | |
The class is \e not straightforward to use (although it is much | |
simpler than the native APIs); Q3Socket provides much easier to use | |
TCP connection facilities. The aim of Q3Dns is to provide a correct | |
and small API to the DNS and nothing more. (We use "correctness" | |
to mean that the DNS information is correctly cached, and | |
correctly timed out.) | |
The API comprises a constructor, functions to set the DNS node | |
(the domain in DNS terminology) and record type (setLabel() and | |
setRecordType()), the corresponding get functions, an isWorking() | |
function to determine whether Q3Dns is working or reading, a | |
resultsReady() signal and query functions for the result. | |
There is one query function for each RecordType, namely | |
addresses(), mailServers(), servers(), hostNames() and texts(). | |
There are also two generic query functions: canonicalName() | |
returns the name you'll presumably end up using (the exact meaning | |
of this depends on the record type) and qualifiedNames() returns a | |
list of the fully qualified names label() maps to. | |
\sa Q3Socket | |
*/ | |
/*! | |
Constructs a DNS query object with invalid settings for both the | |
label and the search type. | |
*/ | |
Q3Dns::Q3Dns() | |
{ | |
d = new Q3DnsPrivate; | |
t = None; | |
} | |
/*! | |
Constructs a DNS query object that will return record type \a rr | |
information about \a label. | |
The DNS lookup is started the next time the application enters the | |
event loop. When the result is found the signal resultsReady() is | |
emitted. | |
\a rr defaults to \c A, IPv4 addresses. | |
*/ | |
Q3Dns::Q3Dns( const QString & label, RecordType rr ) | |
{ | |
d = new Q3DnsPrivate; | |
t = rr; | |
setLabel( label ); | |
setStartQueryTimer(); // start query the next time we enter event loop | |
} | |
/*! | |
Constructs a DNS query object that will return record type \a rr | |
information about host address \a address. The label is set to the | |
IN-ADDR.ARPA domain name. This is useful in combination with the | |
\c Ptr record type (e.g. if you want to look up a hostname for a | |
given address). | |
The DNS lookup is started the next time the application enters the | |
event loop. When the result is found the signal resultsReady() is | |
emitted. | |
\a rr defaults to \c Ptr, that maps addresses to hostnames. | |
*/ | |
Q3Dns::Q3Dns( const QHostAddress & address, RecordType rr ) | |
{ | |
d = new Q3DnsPrivate; | |
t = rr; | |
setLabel( address ); | |
setStartQueryTimer(); // start query the next time we enter event loop | |
} | |
/*! | |
Destroys the DNS query object and frees its allocated resources. | |
*/ | |
Q3Dns::~Q3Dns() | |
{ | |
if ( globalManager ) { | |
uint q = 0; | |
Q3DnsManager * m = globalManager; | |
while( q < m->queries.size() ) { | |
Q3DnsQuery * query=m->queries[q]; | |
if ( query && query->dns ) | |
(void)query->dns->take( (void*) this ); | |
q++; | |
} | |
} | |
delete d; | |
d = 0; | |
} | |
/*! | |
Sets this DNS query object to query for information about \a | |
label. | |
This does not change the recordType(), but its isWorking() status | |
will probably change as a result. | |
The DNS lookup is started the next time the application enters the | |
event loop. When the result is found the signal resultsReady() is | |
emitted. | |
*/ | |
void Q3Dns::setLabel( const QString & label ) | |
{ | |
l = label; | |
d->noNames = false; | |
// construct a list of qualified names | |
n.clear(); | |
if ( l.length() > 1 && l[(int)l.length()-1] == QLatin1Char('.') ) { | |
n.append( l.left( l.length()-1 ).lower() ); | |
} else { | |
int i = l.length(); | |
int dots = 0; | |
const int maxDots = 2; | |
while( i && dots < maxDots ) { | |
if ( l[--i] == QLatin1Char('.') ) | |
dots++; | |
} | |
if ( dots < maxDots ) { | |
(void)Q3DnsManager::manager(); // create a Q3DnsManager, if it is not already there | |
Q3StrListIterator it( *theDomains ); | |
const char * dom; | |
while( (dom=it.current()) != 0 ) { | |
++it; | |
n.append( l.lower() + QLatin1Char('.') + QLatin1String(dom) ); | |
} | |
} | |
n.append( l.lower() ); | |
} | |
#if defined(Q_DNS_SYNCHRONOUS) | |
if ( d->noEventLoop ) { | |
doSynchronousLookup(); | |
} else { | |
setStartQueryTimer(); // start query the next time we enter event loop | |
} | |
#else | |
setStartQueryTimer(); // start query the next time we enter event loop | |
#endif | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns::setLabel: %d address(es) for %s", n.count(), l.ascii() ); | |
int i = 0; | |
for( i = 0; i < (int)n.count(); i++ ) | |
qDebug( "Q3Dns::setLabel: %d: %s", i, n[i].ascii() ); | |
#endif | |
} | |
/*! | |
\overload | |
Sets this DNS query object to query for information about the host | |
address \a address. The label is set to the IN-ADDR.ARPA domain | |
name. This is useful in combination with the \c Ptr record type | |
(e.g. if you want to look up a hostname for a given address). | |
*/ | |
void Q3Dns::setLabel( const QHostAddress & address ) | |
{ | |
setLabel( toInAddrArpaDomain( address ) ); | |
} | |
/*! | |
\fn QStringList Q3Dns::qualifiedNames() const | |
Returns a list of the fully qualified names label() maps to. | |
Note that if you want to iterate over the list, you should iterate | |
over a copy, e.g. | |
\snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 0 | |
*/ | |
/*! | |
\fn QString Q3Dns::label() const | |
Returns the domain name for which this object returns information. | |
\sa setLabel() | |
*/ | |
/*! | |
\enum Q3Dns::RecordType | |
This enum type defines the record types Q3Dns can handle. The DNS | |
provides many more; these are the ones we've judged to be in | |
current use, useful for GUI programs and important enough to | |
support right away: | |
\value None No information. This exists only so that Q3Dns can | |
have a default. | |
\value A IPv4 addresses. By far the most common type. | |
\value Aaaa IPv6 addresses. So far mostly unused. | |
\value Mx Mail eXchanger names. Used for mail delivery. | |
\value Srv SeRVer names. Generic record type for finding | |
servers. So far mostly unused. | |
\value Cname Canonical names. Maps from nicknames to the true | |
name (the canonical name) for a host. | |
\value Ptr name PoinTeRs. Maps from IPv4 or IPv6 addresses to hostnames. | |
\value Txt arbitrary TeXT for domains. | |
We expect that some support for the | |
\l{http://www.rfc-editor.org/rfc/rfc2535.txt}{RFC 2535} | |
extensions will be added in future versions. | |
*/ | |
/*! | |
Sets this object to query for record type \a rr records. | |
The DNS lookup is started the next time the application enters the | |
event loop. When the result is found the signal resultsReady() is | |
emitted. | |
\sa RecordType | |
*/ | |
void Q3Dns::setRecordType( RecordType rr ) | |
{ | |
t = rr; | |
d->noNames = false; | |
setStartQueryTimer(); // start query the next time we enter event loop | |
} | |
/*! | |
\internal | |
Private slot for starting the query. | |
*/ | |
void Q3Dns::startQuery() | |
{ | |
// isWorking() starts the query (if necessary) | |
if ( !isWorking() ) | |
emit resultsReady(); | |
} | |
/*! | |
The three functions Q3Dns::Q3Dns(QString, RecordType), | |
Q3Dns::setLabel() and Q3Dns::setRecordType() may start a DNS lookup. | |
This function handles setting up the single shot timer. | |
*/ | |
void Q3Dns::setStartQueryTimer() | |
{ | |
#if defined(Q_DNS_SYNCHRONOUS) | |
if ( !d->queryTimer && !d->noEventLoop ) | |
#else | |
if ( !d->queryTimer ) | |
#endif | |
{ | |
// start the query the next time we enter event loop | |
d->queryTimer = new QTimer( this ); | |
connect( d->queryTimer, SIGNAL(timeout()), | |
this, SLOT(startQuery()) ); | |
d->queryTimer->start( 0, true ); | |
} | |
} | |
/* | |
Transforms the host address \a address to the IN-ADDR.ARPA domain | |
name. Returns something indeterminate if you're sloppy or | |
naughty. This function has an IPv4-specific name, but works for | |
IPv6 too. | |
*/ | |
QString Q3Dns::toInAddrArpaDomain( const QHostAddress &address ) | |
{ | |
QString s; | |
if ( address.isNull() ) { | |
// if the address isn't valid, neither of the other two make | |
// cases make sense. better to just return. | |
} else if ( address.isIp4Addr() ) { | |
Q_UINT32 i = address.ip4Addr(); | |
s.sprintf( "%d.%d.%d.%d.IN-ADDR.ARPA", | |
i & 0xff, (i >> 8) & 0xff, (i>>16) & 0xff, (i>>24) & 0xff ); | |
} else { | |
// RFC 3152. (1886 is deprecated, and clients no longer need to | |
// support it, in practice). | |
Q_IPV6ADDR i = address.toIPv6Address(); | |
s = QLatin1String("ip6.arpa"); | |
uint b = 0; | |
while( b < 16 ) { | |
s = QString::number( i.c[b]%16, 16 ) + QLatin1Char('.') + | |
QString::number( i.c[b]/16, 16 ) + QLatin1Char('.') + s; | |
b++; | |
} | |
} | |
return s; | |
} | |
/*! | |
\fn Q3Dns::RecordType Q3Dns::recordType() const | |
Returns the record type of this DNS query object. | |
\sa setRecordType() RecordType | |
*/ | |
/*! | |
\fn void Q3Dns::resultsReady() | |
This signal is emitted when results are available for one of the | |
qualifiedNames(). | |
*/ | |
/*! | |
Returns true if Q3Dns is doing a lookup for this object (i.e. if it | |
does not already have the necessary information); otherwise | |
returns false. | |
Q3Dns emits the resultsReady() signal when the status changes to false. | |
*/ | |
bool Q3Dns::isWorking() const | |
{ | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns::isWorking (%s, %d)", l.ascii(), t ); | |
#endif | |
if ( t == None ) | |
return false; | |
#if defined(Q_DNS_SYNCHRONOUS) | |
if ( d->noEventLoop ) | |
return true; | |
#endif | |
Q3PtrList<Q3DnsRR> * ll = Q3DnsDomain::cached( this ); | |
Q_LONG queries = n.count(); | |
while( ll->current() != 0 ) { | |
if ( ll->current()->nxdomain ) { | |
queries--; | |
} else { | |
delete ll; | |
return false; | |
} | |
ll->next(); | |
} | |
delete ll; | |
if ( queries <= 0 ) | |
return false; | |
if ( d->noNames ) | |
return false; | |
return true; | |
} | |
/*! | |
Returns a list of the addresses for this name if this Q3Dns object | |
has a recordType() of Q3Dns::A or Q3Dns::Aaaa and the answer | |
is available; otherwise returns an empty list. | |
As a special case, if label() is a valid numeric IP address, this | |
function returns that address. | |
Note that if you want to iterate over the list, you should iterate | |
over a copy, e.g. | |
\snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 1 | |
*/ | |
Q3ValueList<QHostAddress> Q3Dns::addresses() const | |
{ | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns::addresses (%s)", l.ascii() ); | |
#endif | |
Q3ValueList<QHostAddress> result; | |
if ( t != A && t != Aaaa ) | |
return result; | |
Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this ); | |
Q3DnsRR * rr; | |
while( (rr=cached->current()) != 0 ) { | |
if ( rr->current && !rr->nxdomain ) | |
result.append( rr->address ); | |
cached->next(); | |
} | |
delete cached; | |
return result; | |
} | |
/*! | |
\class Q3Dns::MailServer | |
\brief The Q3Dns::MailServer class is described in Q3Dns::mailServers(). | |
*/ | |
/*! \fn Q3Dns::MailServer::MailServer(const QString& n, Q_UINT16 p) | |
The constructor sets the public data members name and priority. | |
\a n is the name and \a p is the priority. | |
*/ | |
/*! | |
Returns a list of mail servers if the record type is \c Mx. The | |
class Q3Dns::MailServer contains the following public variables: | |
\list | |
\o QString Q3Dns::MailServer::name | |
\o Q_UINT16 Q3Dns::MailServer::priority | |
\endlist | |
Note that if you want to iterate over the list, you should iterate | |
over a copy, e.g. | |
\snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 2 | |
*/ | |
Q3ValueList<Q3Dns::MailServer> Q3Dns::mailServers() const | |
{ | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns::mailServers (%s)", l.ascii() ); | |
#endif | |
Q3ValueList<Q3Dns::MailServer> result; | |
if ( t != Mx ) | |
return result; | |
Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this ); | |
Q3DnsRR * rr; | |
while( (rr=cached->current()) != 0 ) { | |
if ( rr->current && !rr->nxdomain ) { | |
MailServer ms( rr->target, rr->priority ); | |
result.append( ms ); | |
} | |
cached->next(); | |
} | |
delete cached; | |
return result; | |
} | |
/*! | |
\class Q3Dns::Server | |
\brief The Q3Dns::Server class is described in Q3Dns::servers(). | |
*/ | |
/*! \fn Q3Dns::Server::Server(const QString& n, Q_UINT16 p, Q_UINT16 w, Q_UINT16 po) | |
The constructor sets the public data members name, priority, | |
weight, and port. \a n is the name, \a p is the priority, | |
\a w is the weight, and \a po is the port. | |
*/ | |
/*! | |
Returns a list of servers if the record type is \c Srv. The class | |
Q3Dns::Server contains the following public variables: | |
\list | |
\o QString Q3Dns::Server::name | |
\o Q_UINT16 Q3Dns::Server::priority | |
\o Q_UINT16 Q3Dns::Server::weight | |
\o Q_UINT16 Q3Dns::Server::port | |
\endlist | |
Note that if you want to iterate over the list, you should iterate | |
over a copy, e.g. | |
\snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 3 | |
*/ | |
Q3ValueList<Q3Dns::Server> Q3Dns::servers() const | |
{ | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns::servers (%s)", l.ascii() ); | |
#endif | |
Q3ValueList<Q3Dns::Server> result; | |
if ( t != Srv ) | |
return result; | |
Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this ); | |
Q3DnsRR * rr; | |
while( (rr=cached->current()) != 0 ) { | |
if ( rr->current && !rr->nxdomain ) { | |
Server s( rr->target, rr->priority, rr->weight, rr->port ); | |
result.append( s ); | |
} | |
cached->next(); | |
} | |
delete cached; | |
return result; | |
} | |
/*! | |
Returns a list of host names if the record type is \c Ptr. | |
Note that if you want to iterate over the list, you should iterate | |
over a copy, e.g. | |
\snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 4 | |
*/ | |
QStringList Q3Dns::hostNames() const | |
{ | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns::hostNames (%s)", l.ascii() ); | |
#endif | |
QStringList result; | |
if ( t != Ptr ) | |
return result; | |
Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this ); | |
Q3DnsRR * rr; | |
while( (rr=cached->current()) != 0 ) { | |
if ( rr->current && !rr->nxdomain ) { | |
QString str( rr->target ); | |
result.append( str ); | |
} | |
cached->next(); | |
} | |
delete cached; | |
return result; | |
} | |
/*! | |
Returns a list of texts if the record type is \c Txt. | |
Note that if you want to iterate over the list, you should iterate | |
over a copy, e.g. | |
\snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 5 | |
*/ | |
QStringList Q3Dns::texts() const | |
{ | |
#if defined(Q3DNS_DEBUG) | |
qDebug( "Q3Dns::texts (%s)", l.ascii() ); | |
#endif | |
QStringList result; | |
if ( t != Txt ) | |
return result; | |
Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this ); | |
Q3DnsRR * rr; | |
while( (rr=cached->current()) != 0 ) { | |
if ( rr->current && !rr->nxdomain ) { | |
QString str( rr->text ); | |
result.append( str ); | |
} | |
cached->next(); | |
} | |
delete cached; | |
return result; | |
} | |
/*! | |
Returns the canonical name for this DNS node. (This works | |
regardless of what recordType() is set to.) | |
If the canonical name isn't known, this function returns a null | |
string. | |
The canonical name of a DNS node is its full name, or the full | |
name of the target of its CNAME. For example, if l.trolltech.com | |
is a CNAME to lillian.troll.no, and the search path for Q3Dns is | |
"trolltech.com", then the canonical name for all of "lillian", | |
"l", "lillian.troll.no." and "l.trolltech.com" is | |
"lillian.troll.no.". | |
*/ | |
QString Q3Dns::canonicalName() const | |
{ | |
// the cname should work regardless of the recordType(), so set the record | |
// type temporarily to cname when you look at the cache | |
Q3Dns *that = (Q3Dns*) this; // mutable function | |
RecordType oldType = t; | |
that->t = Cname; | |
Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( that ); | |
that->t = oldType; | |
Q3DnsRR * rr; | |
while( (rr=cached->current()) != 0 ) { | |
if ( rr->current && !rr->nxdomain && rr->domain ) { | |
delete cached; | |
return rr->target; | |
} | |
cached->next(); | |
} | |
delete cached; | |
return QString(); | |
} | |
#if defined(Q_DNS_SYNCHRONOUS) | |
/*! \reimp | |
*/ | |
void Q3Dns::connectNotify( const char *signal ) | |
{ | |
if ( d->noEventLoop && qstrcmp(signal,SIGNAL(resultsReady()) )==0 ) { | |
doSynchronousLookup(); | |
} | |
} | |
#endif | |
#if defined(Q_OS_WIN32) || defined(Q_OS_CYGWIN) | |
#if defined(Q_DNS_SYNCHRONOUS) | |
void Q3Dns::doSynchronousLookup() | |
{ | |
// ### not implemented yet | |
} | |
#endif | |
// the following typedefs are needed for GetNetworkParams() API call | |
#ifndef IP_TYPES_INCLUDED | |
#define MAX_HOSTNAME_LEN 128 | |
#define MAX_DOMAIN_NAME_LEN 128 | |
#define MAX_SCOPE_ID_LEN 256 | |
typedef struct { | |
char String[4 * 4]; | |
} IP_ADDRESS_STRING, *PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING; | |
typedef struct _IP_ADDR_STRING { | |
struct _IP_ADDR_STRING* Next; | |
IP_ADDRESS_STRING IpAddress; | |
IP_MASK_STRING IpMask; | |
DWORD Context; | |
} IP_ADDR_STRING, *PIP_ADDR_STRING; | |
typedef struct { | |
char HostName[MAX_HOSTNAME_LEN + 4] ; | |
char DomainName[MAX_DOMAIN_NAME_LEN + 4]; | |
PIP_ADDR_STRING CurrentDnsServer; | |
IP_ADDR_STRING DnsServerList; | |
UINT NodeType; | |
char ScopeId[MAX_SCOPE_ID_LEN + 4]; | |
UINT EnableRouting; | |
UINT EnableProxy; | |
UINT EnableDns; | |
} FIXED_INFO, *PFIXED_INFO; | |
#endif | |
typedef DWORD (WINAPI *GNP)( PFIXED_INFO, PULONG ); | |
// ### FIXME: this code is duplicated in qfiledialog.cpp | |
static QString getWindowsRegString(HKEY key, const QString &subKey) | |
{ | |
QString s; | |
wchar_t buf[1024]; | |
DWORD bsz = sizeof(buf) / sizeof(wchar_t); | |
int r = RegQueryValueEx(key, (wchar_t*)subKey.utf16(), 0, 0, (LPBYTE)buf, &bsz); | |
if (r == ERROR_SUCCESS) { | |
s = QString::fromWCharArray(buf); | |
} else if (r == ERROR_MORE_DATA) { | |
char *ptr = new char[bsz+1]; | |
r = RegQueryValueEx(key, (wchar_t*)subKey.utf16(), 0, 0, (LPBYTE)ptr, &bsz); | |
if (r == ERROR_SUCCESS) | |
s = QLatin1String(ptr); | |
delete [] ptr; | |
} | |
return s; | |
} | |
static bool getDnsParamsFromRegistry( const QString &path, | |
QString *domainName, QString *nameServer, QString *searchList ) | |
{ | |
HKEY k; | |
int r = RegOpenKeyEx( HKEY_LOCAL_MACHINE, (wchar_t*)path.utf16(), 0, KEY_READ, &k ); | |
if ( r == ERROR_SUCCESS ) { | |
*domainName = getWindowsRegString( k, QLatin1String("DhcpDomain") ); | |
if ( domainName->isEmpty() ) | |
*domainName = getWindowsRegString( k, QLatin1String("Domain") ); | |
*nameServer = getWindowsRegString( k, QLatin1String("DhcpNameServer") ); | |
if ( nameServer->isEmpty() ) | |
*nameServer = getWindowsRegString( k, QLatin1String("NameServer") ); | |
*searchList = getWindowsRegString( k, QLatin1String("SearchList") ); | |
} | |
RegCloseKey( k ); | |
return r == ERROR_SUCCESS; | |
} | |
void Q3Dns::doResInit() | |
{ | |
char separator = 0; | |
if ( theNs ) | |
delete theNs; | |
theNs = new Q3PtrList<QHostAddress>; | |
theNs->setAutoDelete( true ); | |
theDomains = new Q3StrList( true ); | |
theDomains->setAutoDelete( true ); | |
QString domainName, nameServer, searchList; | |
bool gotNetworkParams = false; | |
// try the API call GetNetworkParams() first and use registry lookup only | |
// as a fallback | |
HINSTANCE hinstLib = QSystemLibrary::load( L"iphlpapi" ); | |
if ( hinstLib != 0 ) { | |
#ifdef Q_OS_WINCE | |
GNP getNetworkParams = (GNP) GetProcAddress( hinstLib, L"GetNetworkParams" ); | |
#else | |
GNP getNetworkParams = (GNP) GetProcAddress( hinstLib, "GetNetworkParams" ); | |
#endif | |
if ( getNetworkParams != 0 ) { | |
ULONG l = 0; | |
DWORD res; | |
res = getNetworkParams( 0, &l ); | |
if ( res == ERROR_BUFFER_OVERFLOW ) { | |
FIXED_INFO *finfo = (FIXED_INFO*)new char[l]; | |
res = getNetworkParams( finfo, &l ); | |
if ( res == ERROR_SUCCESS ) { | |
domainName = QLatin1String(finfo->DomainName); | |
nameServer = QLatin1String(""); | |
IP_ADDR_STRING *dnsServer = &finfo->DnsServerList; | |
while ( dnsServer != 0 ) { | |
nameServer += QLatin1String(dnsServer->IpAddress.String); | |
dnsServer = dnsServer->Next; | |
if ( dnsServer != 0 ) | |
nameServer += QLatin1Char(' '); | |
} | |
searchList = QLatin1String(""); | |
separator = ' '; | |
gotNetworkParams = true; | |
} | |
delete[] finfo; | |
} | |
} | |
FreeLibrary( hinstLib ); | |
} | |
if ( !gotNetworkParams ) { | |
if ( getDnsParamsFromRegistry( | |
QLatin1String("System\\CurrentControlSet\\Services\\Tcpip\\Parameters"), | |
&domainName, &nameServer, &searchList )) { | |
separator = ' '; | |
} else { | |
// Could not access the TCP/IP parameters | |
domainName = QLatin1String(""); | |
nameServer = QLatin1String("127.0.0.1"); | |
searchList = QLatin1String(""); | |
separator = ' '; | |
} | |
} | |
nameServer = nameServer.simplifyWhiteSpace(); | |
int first, last; | |
if ( !nameServer.isEmpty() ) { | |
first = 0; | |
do { | |
last = nameServer.find( QLatin1Char(separator), first ); | |
if ( last < 0 ) | |
last = nameServer.length(); | |
Q3Dns tmp( nameServer.mid( first, last-first ), Q3Dns::A ); | |
Q3ValueList<QHostAddress> address = tmp.addresses(); | |
Q_LONG i = address.count(); | |
while( i ) | |
theNs->append( new QHostAddress(address[--i]) ); | |
first = last+1; | |
} while( first < (int)nameServer.length() ); | |
} | |
searchList += QLatin1Char(' ') + domainName; | |
searchList = searchList.simplifyWhiteSpace().lower(); | |
first = 0; | |
do { | |
last = searchList.find( QLatin1Char(separator), first ); | |
if ( last < 0 ) | |
last = searchList.length(); | |
theDomains->append( qstrdup( searchList.mid( first, last-first ).latin1() ) ); | |
first = last+1; | |
} while( first < (int)searchList.length() ); | |
} | |
#elif defined(Q_OS_UNIX) | |
#if defined(Q_DNS_SYNCHRONOUS) | |
void Q3Dns::doSynchronousLookup() | |
{ | |
if ( t!=None && !l.isEmpty() ) { | |
Q3ValueListIterator<QString> it = n.begin(); | |
Q3ValueListIterator<QString> end = n.end(); | |
int type; | |
switch( t ) { | |
case Q3Dns::A: | |
type = 1; | |
break; | |
case Q3Dns::Aaaa: | |
type = 28; | |
break; | |
case Q3Dns::Mx: | |
type = 15; | |
break; | |
case Q3Dns::Srv: | |
type = 33; | |
break; | |
case Q3Dns::Cname: | |
type = 5; | |
break; | |
case Q3Dns::Ptr: | |
type = 12; | |
break; | |
case Q3Dns::Txt: | |
type = 16; | |
break; | |
default: | |
type = (char)255; // any | |
break; | |
} | |
while( it != end ) { | |
QString s = *it; | |
it++; | |
QByteArray ba( 512 ); | |
int len = res_search( s.latin1(), 1, type, (uchar*)ba.data(), ba.size() ); | |
if ( len > 0 ) { | |
ba.resize( len ); | |
Q3DnsQuery * query = new Q3DnsQuery; | |
query->started = now(); | |
query->id = ++theId; | |
query->t = t; | |
query->l = s; | |
Q3DnsAnswer a( ba, query ); | |
a.parse(); | |
} else if ( len == -1 ) { | |
// res_search error | |
} | |
} | |
emit resultsReady(); | |
} | |
} | |
#endif | |
#if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 3))) | |
#define Q_MODERN_RES_API | |
#endif | |
void Q3Dns::doResInit() | |
{ | |
if ( theNs ) | |
return; | |
theNs = new Q3PtrList<QHostAddress>; | |
theNs->setAutoDelete( true ); | |
theDomains = new Q3StrList( true ); | |
theDomains->setAutoDelete( true ); | |
// read resolv.conf manually. | |
QFile resolvConf(QLatin1String("/etc/resolv.conf")); | |
if (resolvConf.open(QIODevice::ReadOnly)) { | |
QTextStream stream( &resolvConf ); | |
QString line; | |
while ( !stream.atEnd() ) { | |
line = stream.readLine(); | |
QStringList list = QStringList::split( QLatin1String(" "), line ); | |
if( line.startsWith( QLatin1Char('#') ) || list.size() < 2 ) | |
continue; | |
const QString type = list[0].lower(); | |
if ( type == QLatin1String("nameserver") ) { | |
QHostAddress *address = new QHostAddress(); | |
if ( address->setAddress( QString(list[1]) ) ) { | |
// only add ipv6 addresses from resolv.conf if | |
// this host supports ipv6. | |
if ( address->isIPv4Address() || ipv6support ) | |
theNs->append( address ); | |
else | |
delete address; | |
} else { | |
delete address; | |
} | |
} else if ( type == QLatin1String("search") ) { | |
QStringList srch = QStringList::split( QLatin1String(" "), list[1] ); | |
for ( QStringList::Iterator i = srch.begin(); i != srch.end(); ++i ) | |
theDomains->append( (*i).lower().local8Bit() ); | |
} else if ( type == QLatin1String("domain") ) { | |
theDomains->append( list[1].lower().local8Bit() ); | |
} | |
} | |
} | |
if (theNs->isEmpty()) { | |
#if defined(Q_MODERN_RES_API) | |
struct __res_state res; | |
res_ninit( &res ); | |
int i; | |
// find the name servers to use | |
for( i=0; i < MAXNS && i < res.nscount; i++ ) | |
theNs->append( new QHostAddress( ntohl( res.nsaddr_list[i].sin_addr.s_addr ) ) ); | |
# if defined(MAXDFLSRCH) | |
for( i=0; i < MAXDFLSRCH; i++ ) { | |
if ( res.dnsrch[i] && *(res.dnsrch[i]) ) | |
theDomains->append( QString::fromLatin1( res.dnsrch[i] ).lower().local8Bit() ); | |
else | |
break; | |
} | |
# endif | |
if ( *res.defdname ) | |
theDomains->append( QString::fromLatin1( res.defdname ).lower().local8Bit() ); | |
#else | |
res_init(); | |
int i; | |
// find the name servers to use | |
for( i=0; i < MAXNS && i < _res.nscount; i++ ) | |
theNs->append( new QHostAddress( ntohl( _res.nsaddr_list[i].sin_addr.s_addr ) ) ); | |
# if defined(MAXDFLSRCH) | |
for( i=0; i < MAXDFLSRCH; i++ ) { | |
if ( _res.dnsrch[i] && *(_res.dnsrch[i]) ) | |
theDomains->append( QString::fromLatin1( _res.dnsrch[i] ).lower().local8Bit() ); | |
else | |
break; | |
} | |
# endif | |
if ( *_res.defdname ) | |
theDomains->append( QString::fromLatin1( _res.defdname ).lower().local8Bit() ); | |
#endif | |
// the code above adds "0.0.0.0" as a name server at the slightest | |
// hint of trouble. so remove those again. | |
theNs->first(); | |
while( theNs->current() ) { | |
if ( theNs->current()->isNull() ) | |
delete theNs->take(); | |
else | |
theNs->next(); | |
} | |
} | |
QFile hosts( QString::fromLatin1( "/etc/hosts" ) ); | |
if ( hosts.open( QIODevice::ReadOnly ) ) { | |
// read the /etc/hosts file, creating long-life A and PTR RRs | |
// for the things we find. | |
QTextStream i( &hosts ); | |
QString line; | |
while( !i.atEnd() ) { | |
line = i.readLine().simplifyWhiteSpace().lower(); | |
uint n = 0; | |
while( (int) n < line.length() && line[(int)n] != QLatin1Char('#') ) | |
n++; | |
line.truncate( n ); | |
n = 0; | |
while( (int) n < line.length() && !line[(int)n].isSpace() ) | |
n++; | |
QString ip = line.left( n ); | |
QHostAddress a; | |
a.setAddress( ip ); | |
if ( ( a.isIPv4Address() || a.isIPv6Address() ) && !a.isNull() ) { | |
bool first = true; | |
line = line.mid( n+1 ); | |
n = 0; | |
while( (int) n < line.length() && !line[(int)n].isSpace() ) | |
n++; | |
QString hostname = line.left( n ); | |
// ### in case of bad syntax, hostname is invalid. do we care? | |
if ( n ) { | |
Q3DnsRR * rr = new Q3DnsRR( hostname ); | |
if ( a.isIPv4Address() ) | |
rr->t = Q3Dns::A; | |
else | |
rr->t = Q3Dns::Aaaa; | |
rr->address = a; | |
rr->deleteTime = UINT_MAX; | |
rr->expireTime = UINT_MAX; | |
rr->current = true; | |
if ( first ) { | |
first = false; | |
Q3DnsRR * ptr = new Q3DnsRR( Q3Dns::toInAddrArpaDomain( a ) ); | |
ptr->t = Q3Dns::Ptr; | |
ptr->target = hostname; | |
ptr->deleteTime = UINT_MAX; | |
ptr->expireTime = UINT_MAX; | |
ptr->current = true; | |
} | |
} | |
} | |
} | |
} | |
} | |
#endif | |
QT_END_NAMESPACE | |
#endif // QT_NO_DNS |