📄 q3dns.cpp
字号:
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 ( !ns || ns->isEmpty() ) Q3Dns::doResInit(); if ( !ns || ns->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 = *ns->at( q->step % ns->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 ( ns->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=ns->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 < ns->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 = ++::id; 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 = ns->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 = ++::id; 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}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -