/****************************************************************************
*
* Copyright (C) 2000-2001 RealNetworks, Inc. All rights reserved.
*
* This program is free software. It may be distributed under the terms
* in the file LICENSE, found in the top level of the source distribution.
*
*/
#include "resolver.h"
#include "parser.h"
#include "app.h"
#include "dbg.h"
#include <errno.h>
#ifdef _UNIX
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
// RCODE values
#define RC_OK 0 /* Success */
#define RC_FMT 1 /* Format error - server cannot grok */
#define RC_FAIL 2 /* Server failed */
#define RC_EXIST 3 /* No such host/domain */
#define RC_NOTIMPL 4 /* Not implemented */
#define RC_ACCESS 5 /* Access denied */
// CLASS values
#define CL_IN 1 /* Internet */
#define CL_CS 2
#define CL_CH 3
#define CL_HS 4
// RRTYPE and QTYPE values (QTYPE is a superset of RRTYPE)
#define RR_A 1 /* Address */
#define RR_NS 2
#define RR_MD 3
#define RR_MF 4
#define RR_CNAME 5 /* Canonical name (alias) */
#define RR_SOA 6
#define RR_MB 7
#define RR_MG 8
#define RR_MR 9
#define RR_NULL 10
#define RR_WKS 11
#define RR_PTR 12 /* Pointer */
#define RR_HINFO 13
#define RR_MINFO 14
#define RR_MX 15
#define RR_TXT 16
/*
* Header flags:
* QR = Query/Response? 0=query, 1=response
* OPCODE: Opcode:
* 0 = Forward query
* 1 = Inverse query
* 2 = Server Status Request
* 3..15 reserved
* AA: Authoritative Answer? 0=no, 1=yes
* TC: Truncated? 0=no, 1=yes
* RD: Recursion Desired? 0=no, 1=yes
* RA: Recursion Available? 0=no, 1=yes
* Z: Must be zero (reserved)
* RCODE: Response code:
* 0 = No Error
* 1 = Format Error
* 2 = Server Failure
* 3 = Name Error (nonexistent host/domain)
* 4 = Not Implemented (query type not supported)
* 5 = Refused (access denied)
* 6..15 reserved
*
* Question format:
* QNAME: series of (bytelen,label) terminated by nul octet
* QTYPE: two-octet query type
* QCLASS: two-octet domain class (eg. IN)
*
* RR (answer, authority, additional) format:
* NAME: series of (bytelen,label) terminated by nul octet
* TYPE: two-octet RR type
* CLASS: two-octet domain class (eg. IN)
* TTL: four-octet time-to-live in seconds (0=nocache)
* RDLEN: two-octet length of RDATA field
* RDATA: description of resource (eg. four-octet addr)
*/
/*
* Sample query: www.aa.net
* e6 4f query_id
* 01 00 flags: QR=Q, OP=0, AA=0, TC=0, RD=1, RA=0, RCODE=0
* 00 01 qdcnt: 1
* 00 00 ancnt: 0
* 00 00 nscnt: 0
* 00 00 arcnt: 0
* qd@0C: 03 77 77 77 02 61 61 03 6e 65 74 00 00 01 00 01
* "www.aa.net" A IN
*
* Sample response: www.aa.net
* e6 4f query_id
* 85 80 flags: QR=R, OP=0, AA=1, TC=0, RD=1, RA=1, RCODE=0
* 00 01 qdcnt: 1
* 00 01 ancnt: 1
* 00 02 nscnt: 2
* 00 02 arcnt: 2
* qd@0C: 03 77 77 77 02 61 61 03 6e 65 74 00 00 01 00 01
* "www.aa.net" A IN
* an@1C: c0 0c 00 01 00 01 00 01 51 80
* 00 04 cc 9d dc 02
* "www.aa.net" A IN, TTL=1day, 204.157.220.2
* ns@2C: 02 61 61 03 6e 65 74 00 00 02 00 01 00 01 51 80
* 00 0a 07 68 65 6e 64 72 69 78 c0 2c
* "aa.net" NS IN, TTL=1day, "hendrix.aa.net"
*
* ns@48: c0 2c 00 02 00 01 00 01 51 80
* 00 06 03 6e 73 32 c0 2c
* "aa.net" NS IN, TTL=1day, "ns2.aa.net"
* ar@5A: c0 3e 00 01 00 01 00 01 51 80
* 00 04 cc 9d dc 04
* "hendrix.aa.net" A IN, TTL=1day, 204.157.220.4
* ar@6A: c0 54 00 01 00 01 00 01 51 80 00 04 cf 11 70 04
* "ns2.aa.net" A IN, TTL=1day, 207.17.112.4
*/
// inet_aton for in-addr.arpa hack: "4.3.2.1.in-addr.arpa" -> 1.2.3.4
static int inet_aton_rev( const char* cp, struct in_addr* inp )
{
int ret = 0;
char host[16]; // aaa.bbb.ccc.ddd
const char* end = cp;
while( *end && (isdigit(*end) || '.' == *end) ) end++;
if( end > cp && end-cp <= 16 && !strcasecmp( end, "in-addr.arpa" ) )
{
struct in_addr addr;
end--;
memcpy( host, cp, end-cp );
host[end-cp] = '\0';
ret = inet_aton( host, &addr );
if( ret )
{
unsigned n;
BYTE* src = (BYTE*)&addr.s_addr + sizeof(struct in_addr);
BYTE* dst = (BYTE*)&inp->s_addr;
for( n = 0; n < sizeof(struct in_addr); n++ )
{
src--;
*dst = *src;
dst++;
}
}
}
return ret;
}
// inet_ntoa for in-addr.arpa hack: 1.2.3.4 -> "4.3.2.1.in-addr.arpa"
static char* inet_ntoa_rev( struct in_addr in )
{
static char host[29]; // aaa.bbb.ccc.ddd.in-addr.arpa\0
BYTE qa[4];
memcpy( qa, &in.s_addr, 4 );
sprintf( host, "%u.%u.%u.%u.in-addr.arpa", qa[3], qa[2], qa[1], qa[0] );
return host;
}
/**************************************
*
* CResolver
*
**************************************/
CResolver * CResolver::m_pResolver = NULL;
CResolver::CResolver( void ) :
m_sock(this),
m_timer(this),
m_usQueryID(0)
{
ReadConfig();
}
CResolver::~CResolver( void )
{
// Empty
}
CResolver * CResolver::GetResolver( void )
{
if( m_pResolver == NULL )
m_pResolver = new CResolver;
return m_pResolver;
}
bool CResolver::GetHost( CResolverResponse* pResponse, const CString& strHost )
{
dbgout( "CResolver::GetHost: query for %s", (CPCHAR)strHost );
CHostInfo* pInfo;
// Determine if this is a number or name
bool bIsNumeric = true;
CPCHAR p = strHost;
while( *p )
{
if( !isdigit( *p ) && *p != '.' )
{
bIsNumeric = false;
break;
}
p++;
}
if( bIsNumeric )
{
int err = 0;
in_addr addr;
if( !inet_aton( strHost, &addr ) )
{
err = ENOENT;
}
pResponse->GetHostDone( err, strHost, addr );
return true;
}
// First search for name as given
CHostInfo info( strHost );
pInfo = m_treeHostInfo.Search( info );
if( pInfo )
{
if( pInfo->m_tExpire > time(NULL) )
{
pResponse->GetHostDone( 0, strHost, pInfo->m_addr );
return true;
}
m_treeHostInfo.Delete( info );
}
// Not found - if it's unqualified, search the domain list
if( !strchr( strHost, '.' ) )
{
CDomainList::Iterator itr( m_listDomains.Begin() );
if( !itr )
{
// Domain list is empty - game over, man
in_addr addr;
addr.s_addr = INADDR_NONE;
pResponse->GetHostDone( ENOENT, strHost, addr );
return true;
}
while( itr )
{
CString strFQDN = strHost;
strFQDN.Append( "." );
strFQDN.Append( *itr );
CHostInfo info( strFQDN );
CHostInfo* pInfo = m_treeHostInfo.Search( info );
if( pInfo )
{
if( pInfo->m_tExpire > time(NULL) )
{
pResponse->GetHostDone( 0, strHost, pInfo->m_addr );
return true;
}
m_treeHostInfo.Delete( info );
}
itr++;
}
}
// Looks like we have to send a query
CHostQuery query( strHost );
CHostQuery* pQuery = m_treeHostQueries.Insert( query );
if( ! pQuery )
{
pQuery = m_treeHostQueries.Search( query );
assert( pQuery );
pQuery->m_tExpire = time(NULL) + 4;
pQuery->m_tDelta = 4;
if( !strchr( strHost, '.' ) )
{
// Unqualified - search the domain list
CDomainList::Iterator itr( m_listDomains.Begin() );
assert( itr ); // should have caught this already
pQuery->m_strFQDN = strHost;
pQuery->m_strFQDN.Append( "." );
pQuery->m_strFQDN.Append( *itr );
pQuery->m_itrDomain = itr;
}
pQuery->m_itrServer = m_listServers.Begin();
SendQuery( *pQuery->m_itrServer, pQuery->m_strFQDN, RR_A );
}
pQuery->AddResponse( pResponse );
return true;
}
bool CResolver::GetHost( CResolverResponse* pResponse, struct in_addr addr )
{
dbgout( "CResolver::GetHost: query for %s", inet_ntoa(addr) );
CAddrInfo info( addr );
// See if we already have it
CAddrInfo* pInfo = m_treeAddrInfo.Search( info );
if( pInfo )
{
if( pInfo->m_tExpire > time(NULL) )
{
pResponse->GetHostDone( 0, addr, pInfo->m_strName );
return true;
}
m_treeAddrInfo.Delete( info );
}
// Nope, gotta send a query
CAddrQuery query( addr );
CAddrQuery* pQuery = m_treeAddrQueries.Insert( query );
if( ! pQuery )
{
pQuery = m_treeAddrQueries.Search( query );
assert( pQuery );
pQuery->m_tExpire = time(NULL) + 4;
pQuery->m_tDelta = 4;
pQuery->m_itrServer = m_listServers.Begin();
SendQuery( *pQuery->m_itrServer, inet_ntoa_rev(addr), RR_PTR );
}
pQuery->AddResponse( pResponse );
return true;
}
void CResolver::WalkHostTree( AvlNode<CHostQuery>* pNode )
{
if( !pNode ) return;
WalkHostTree( pNode->Subtree(LEFT) );
WalkHostTree( pNode->Subtree(RIGHT) );
CHostQuery& rquery = pNode->Key();
if( rquery.m_tExpire < time(NULL) )
{
// Timed out, next nameserver
rquery.m_itrServer++;
if( rquery.m_itrServer )
{
rquery.m_tExpire = time(NULL)+4;
rquery.m_tDelta = 4;
SendQuery( *rquery.m_itrServer, rquery.m_strHost, RR_A );
return;
}
// Exhausted server list, so bump the timeout and start over
rquery.m_tExpire += rquery.m_tDelta;
rquery.m_tDelta *= 2;
rquery.m_itrServer = m_listServers.Begin();
if( rquery.m_tDelta > 30 )
{
// Everything timed out - we're all alone, and it's getting dark!
struct in_addr addr;
addr.s_addr = INADDR_NONE;
CResolverResponseList::Iterator itr( rquery.m_listResponses.Begin() );
while( itr )
{
(*itr)->GetHostDone( EAGAIN, rquery.m_strHost, addr );
itr++;
}
m_treeHostQueries.Delete( rquery );
}
}
}
void CResolver::WalkAddrTree( AvlNode<CAddrQuery>* pNode )
{
if( !pNode ) return;
WalkAddrTree( pNode->Subtree(LEFT) );
WalkAddrTree( pNode->Subtree(RIGHT) );
CAddrQuery& rquery = pNode->Key();
if( rquery.m_tExpire < time(NULL) )
{
// Timed out, next nameserver
rquery.m_itrServer++;
if( rquery.m_itrServer )
{
rquery.m_tExpire = time(NULL)+4;
rquery.m_tDelta = 4;
SendQuery( *rquery.m_itrServer, inet_ntoa_rev(rquery.m_addr), RR_PTR );
return;
}
// Exhausted server list, so bump the timeout and start over
rquery.m_tExpire += rquery.m_tDelta;
rquery.m_tDelta *= 2;
rquery.m_itrServer = m_listServers.Begin();
if( rquery.m_tDelta > 30 )
{
// Everything timed out - we're all alone, and it's getting dark!
CString host;
CResolverResponseList::Iterator itr( rquery.m_listResponses.Begin() );
while( itr )
{
(*itr)->GetHostDone( EAGAIN, rquery.m_addr, host );
itr++;
}
m_treeAddrQueries.Delete( rquery );
}
}
}
void CResolver::OnTimer( void )
{
dbgout( "CResolver::OnTimer" );
// iterate through queries, remove stale ones and respond fail
WalkHostTree( m_treeHostQueries.GetRoot() );
WalkAddrTree( m_treeAddrQueries.GetRoot() );
if( m_treeHostQueries.IsEmpty() && m_treeAddrQueries.IsEmpty() )
{
dbgout( "CResolver::OnTimer: no more queries" );
m_sock.Close();
m_timer.Disable();
}
}
void CResolver::OnConnectDone( int err )
{
assert(false);
}
void CResolver::OnReadReady( void )
{
dbgout( "CResolver::OnReadReady" );
CBuffer buf;
buf.SetSize( 513 );
if( !m_sock.Read( &buf ) )
{
//XXX: fail all pending queries
dbgout( "CResolver::OnReadReady: socket read failed" );
return;
}
if( buf.GetSize() > 512 )
{
dbgout( "CResolver::OnReadReady: packet too large, ignoring" );
return;
}
CPBYTE p = buf.GetBuffer();
size_t len = buf.GetSize();
size_t pos = 0;
dns_hdr hdr;
if( len < sizeof(dns_hdr) )
{
dbgout( "CResolver::OnReadReady: short packet" );
return;
}
ntohs_buf( &hdr.id, p+pos, sizeof(dns_hdr)/sizeof(UINT16) );
pos += sizeof(dns_hdr);
// QR=1, OPCODE=0, AA=x, TC=0, RD=1, RA=x, Z=0, RCODE=0
// 10000x01x000xxxx
if( (hdr.flags & 0xFB70) != 0x8100 )
{
dbgout( "CResolver::OnReadReady: bad response flags %04X", hdr.flags );
return;
}
if( hdr.qdcnt != 1 )
{
dbgout( "CResolver::OnReadReady: bad counts %hu, %hu, %hu, %hu",
hdr.qdcnt, hdr.ancnt, hdr.nscnt, hdr.arcnt );
return;
}
time_t tnow;
dns_qr_hdr qrhdr;
dns_rr_hdr rrhdr;
tnow = time(NULL);
while( hdr.qdcnt )
{
if( ! ParseQuestionHeader( &qrhdr, buf, pos ) )
{
dbgout( "CResolver: bad question" );
return;
}
hdr.qdcnt--;
}
//XXX: match question to pending query (hostname/serveraddr)
UINT rc = (hdr.flags & 0xF);
if( rc != RC_OK )
{
if( qrhdr.qtype == RR_A )
{
// Does not exist - try next domain
CHostQuery query( qrhdr.strHost );
CHostQuery* pQuery = m_treeHostQueries.Search( query );
if( ! pQuery )
{
dbgout( "CResolver::OnReadReady: received rc=%u for unexpected host '%s'",
rc, (CPCHAR)qrhdr.strHost );
return;
}
// If query was unqualified, bump the domain iterator
if( pQuery->m_itrDomain ) pQuery->m_itrDomain++;
if( ! pQuery->m_itrDomain )
{
// Exhausted search list
struct in_addr addr;
addr.s_addr = INADDR_NONE;
CResolverResponseList::Iterator itr( pQuery->m_listResponses.Begin() );
while( itr )
{
(*itr)->GetHostDone( ENOENT, qrhdr.strHost, addr );
itr++;
}
m_treeHostQueries.Delete( query );
}
else
{
pQuery->m_strFQDN = pQuery->m_strHost;
pQuery->m_strFQDN.Append( "." );
pQuery->m_strFQDN.Append( *pQuery->m_itrDomain );
pQuery->m_itrDomain++;
//XXX: reset query's server iterator here?
SendQuery( *pQuery->m_itrServer, pQuery->m_strFQDN, RR_A );
}
}
else if( qrhdr.qtype == RR_PTR )
{
struct in_addr addr;
if( inet_aton_rev( qrhdr.strHost, &addr ) )
{
CAddrQuery query( addr );
CAddrQuery* pQuery = m_treeAddrQueries.Search( query );
if( ! pQuery )
{
dbgout( "CResolver::OnReadReady: received rc=%u for unexpected host '%s'",
rc, (CPCHAR)qrhdr.strHost );
return;
}
CString host;
CResolverResponseList::Iterator itr( pQuery->m_listResponses.Begin() );
while( itr )
{
(*itr)->GetHostDone( ENOENT, addr, host );
itr++;
}
m_treeAddrQueries.Delete( query );
}
}
else
{
dbgout( "CResolver::OnReadReady: error %u for unexpected qtype %hu",
rc, qrhdr.qtype );
return;
}
// Don't bother parsing the answers
hdr.ancnt = 0;
}
while( hdr.ancnt )
{
struct in_addr addr;
char szHostAnswer[256];
if( ! ParseAnswerHeader( &rrhdr, buf, pos ) )
{
dbgout( "CResolver: bad answer" );
return;
}
switch( rrhdr.rtype )
{
case RR_A:
if( !rrhdr.rdlen || rrhdr.rdlen%4 ) return;
//XXX: keep multiple addrs?
memcpy( &addr, buf.GetBuffer()+pos, 4 );
dbgout( "Got A: %s = %s", (CPCHAR)qrhdr.strHost, inet_ntoa(addr) );
AddHostEntry( tnow+rrhdr.ttl, qrhdr.strHost, addr );
break;
case RR_CNAME:
if( ! DecodeName( szHostAnswer, buf, pos ) ) return;
dbgout( "Got CNAME: %s = %s", (CPCHAR)qrhdr.strHost, szHostAnswer );
break;
case RR_PTR:
if( ! DecodeName( szHostAnswer, buf, pos ) ) return;
if( inet_aton_rev( (CPCHAR)qrhdr.strHost, &addr ) )
{
dbgout( "Got PTR: %s (%s) = %s", (CPCHAR)qrhdr.strHost, inet_ntoa(addr), szHostAnswer );
AddHostEntry( tnow+rrhdr.ttl, szHostAnswer, addr );
}
break;
default: // Looks valid but useless
break;
}
hdr.ancnt--;
}
// Ignore authority and additional RR's
if( m_treeHostQueries.IsEmpty() && m_treeAddrQueries.IsEmpty() )
{
dbgout( "CResolver::OnReadReady: no more queries" );
m_sock.Close();
m_timer.Disable();
}
}
void CResolver::OnWriteReady( void )
{
assert(false);
}
void CResolver::OnExceptReady( void )
{
assert(false);
}
void CResolver::OnClosed( void )
{
dbgout( "CResolver::OnClosed" );
// iterate through host queries, respond fail and delete
// iterate through addr queries, respond fail and delete
}
static BYTE s_byQueryTmpl[] = {
0x00, 0x00, // Query ID (fill this in)
0x01, 0x00, // QR=0, OPCODE=0, AA=0, TC=0, RD=1, RA=0, Z=0, RCODE=0
0x00, 0x01, // QD count
0x00, 0x00, // AN count
0x00, 0x00, // NS count
0x00, 0x00 // AR count
};
void CResolver::SendQuery( const CSockAddr& addr, const CString& strHost, UINT16 qtype )
{
dbgout( "CResolver::SendQuery: host %s", (CPCHAR)strHost );
// Fire up our socket and timer
if( ! m_sock.IsOpen() )
{
if( ! m_sock.Bind( CSockAddr::Any() ) )
{
dbgout( "CResolver::SendQuery: cannot bind socket" );
return;
}
m_sock.Select( SF_READ );
}
if( CTimer::Repeating != m_timer.GetMode() )
{
dbgout( "CResolver::SendQuery: setting timer" );
m_timer.SetRepeating( 2*1000 );
}
// Create the query buffer
CBuffer buf;
buf.SetSize( 512 );
PBYTE p = buf.GetBuffer();
size_t len = buf.GetSize();
// Encode the header
m_usQueryID++;
memcpy( p, s_byQueryTmpl, sizeof(s_byQueryTmpl) );
*(p+0) = m_usQueryID >> 8; *(p+1) = m_usQueryID & 0xFF;
p += sizeof(s_byQueryTmpl);
len -= sizeof(s_byQueryTmpl);
// Encode the hostname
if( !EncodeName( strHost, p, len ) ) return;
if( len < 4 ) return;
// qtype, qclass
*(p+0) = qtype >> 8; *(p+1) = qtype & 0xFF;
*(p+2) = 0; *(p+3) = 1;
p += 4; len -= 4;
buf.SetSize( 512-len );
m_sock.SendTo( addr, buf.GetBuffer(), buf.GetSize() );
}
void CResolver::AddHostEntry( time_t tExpire, const CString& strHost, struct in_addr addr )
{
dbgout( "AddHostEntry: host %s = %s", (CPCHAR)strHost, inet_ntoa(addr) );
// Add host to our host tree
m_treeHostInfo.Insert( CHostInfo( tExpire, strHost, addr ) );
// Add addr to our addr tree
m_treeAddrInfo.Insert( CAddrInfo( tExpire, addr, strHost ) );
// See if anyone is waiting for this host
CHostQuery queryHost( strHost );
CHostQuery* pHostQuery = m_treeHostQueries.Search( queryHost );
if( pHostQuery )
{
dbgout( "\tfound host query in tree, calling responses" );
CResolverResponseList::Iterator itr( pHostQuery->m_listResponses.Begin() );
while( itr )
{
(*itr)->GetHostDone( 0, strHost, addr );
itr++;
}
m_treeHostQueries.Delete( queryHost );
}
CPCHAR pdot;
if( (pdot = strchr( strHost, '.' )) )
{
// Perhaps someone is waiting on the unqualified name
queryHost.m_strHost.Set( strHost, pdot - (CPCHAR)strHost );
pHostQuery = m_treeHostQueries.Search( queryHost );
if( pHostQuery )
{
dbgout( "\tfound bare host query in tree, calling responses" );
CResolverResponseList::Iterator itr( pHostQuery->m_listResponses.Begin() );
while( itr )
{
(*itr)->GetHostDone( 0, strHost, addr );
itr++;
}
m_treeHostQueries.Delete( queryHost );
}
}
// See if anyone is waiting for this addr
CAddrQuery queryAddr( addr );
CAddrQuery* pAddrQuery = m_treeAddrQueries.Search( queryAddr );
if( pAddrQuery )
{
dbgout( "\tfound addr query in tree, calling responses" );
CResolverResponseList::Iterator itr( pAddrQuery->m_listResponses.Begin() );
while( itr )
{
(*itr)->GetHostDone( 0, addr, strHost );
itr++;
}
m_treeAddrQueries.Delete( queryAddr );
}
}
void CResolver::ReadConfig( void )
{
CConfigParser parser;
CToken tok;
#ifdef _UNIX
// Read resolv.conf for domain name(s), nameservers
parser.Open( "/etc/resolv.conf" );
tok = parser.NextToken();
while( CToken::TOK_EOF != tok.type )
{
if( '#' == tok.val[0] )
{
parser.NextLine();
}
else if( 0 == strcmp( tok.val, "nameserver" ) )
{
tok = parser.NextToken();
if( CToken::TOK_STRING == tok.type )
{
dbgout( "nameserver '%s'", (CPCHAR)tok.val );
m_listServers.InsertTail( CSockAddr(tok.val,53) );
}
parser.NextLine();
}
else if( 0 == strcmp( tok.val, "domain" ) )
{
tok = parser.NextToken();
if( CToken::TOK_STRING == tok.type )
{
dbgout( "domain '%s'", (CPCHAR)tok.val );
}
parser.NextLine();
}
else if( 0 == strcmp( tok.val, "search" ) )
{
tok = parser.NextToken();
while( CToken::TOK_STRING == tok.type )
{
dbgout( "search '%s'", (CPCHAR)tok.val );
m_listDomains.InsertTail( tok.val );
tok = parser.NextToken();
}
}
else
{
dbgout( "unknown token '%s'", (CPCHAR)tok.val );
while( CToken::TOK_STRING == tok.type )
{
tok = parser.NextToken();
}
}
tok = parser.NextToken();
}
parser.Close();
#endif
#ifdef _WIN32
char strHolder[255];
DWORD size = 255;
HKEY hKey;
long lRet;
lRet = RegOpenKey( HKEY_LOCAL_MACHINE,
"System\\CurrentControlSet\\Services\\Tcpip\\Parameters",
&hKey );
if(lRet == ERROR_SUCCESS)
{
lRet = RegQueryValueEx( hKey,
"NameServer",
NULL,
NULL,
(LPBYTE)strHolder,
&size );
//if can find it, try DhcpNameServer
if(lRet != ERROR_SUCCESS || strHolder[0] == '\0')
{
size = 255;
lRet = RegQueryValueEx( hKey,
"DhcpNameServer",
NULL,
NULL,
(LPBYTE)strHolder,
&size );
}
if(lRet == ERROR_SUCCESS && strHolder[0] != '\0')
{
char * pCur = strHolder;
char * pStart = pCur;
while(*pCur)
{
if(*pCur == ' ')
{
*pCur = '\0';
m_listServers.InsertTail( CSockAddr(pStart,53) );
pStart = pCur + 1;
}
pCur++;
}
if(*pStart)
m_listServers.InsertTail( CSockAddr(pStart,53) );
}
// for searchlist
size = 255;
lRet = RegQueryValueEx( hKey,
"SearchList",
NULL,
NULL,
(LPBYTE)strHolder,
&size );
//if can find it, try Domain
if(lRet != ERROR_SUCCESS || strHolder[0] == '\0')
{
size = 255;
lRet = RegQueryValueEx( hKey,
"DhcpDomain",
NULL,
NULL,
(LPBYTE)strHolder,
&size );
//if can find it, try Domain
if(lRet != ERROR_SUCCESS || strHolder[0] == '\0')
{
size = 255;
lRet = RegQueryValueEx( hKey,
"DhcpDomain",
NULL,
NULL,
(LPBYTE)strHolder,
&size );
}
}
if(lRet == ERROR_SUCCESS && strHolder[0] != '\0')
{
char * pCur = strHolder;
char * pStart = pCur;
while(*pCur)
{
if(*pCur == ' ')
{
*pCur = '\0';
m_listDomains.InsertTail(pStart);
pStart = pCur + 1;
}
pCur++;
}
if(*pStart)
m_listDomains.InsertTail(pStart);
}
RegCloseKey(hKey);
}
#endif
#ifdef _UNIX
parser.Open( "/etc/hosts" );
#endif
#ifdef _WIN32
char strPath[255];
ExpandEnvironmentStrings("%windir%\\system32\\drivers\\etc\\hosts", strPath, 255);
parser.Open( strPath );
#endif
tok = parser.NextToken();
while( CToken::TOK_EOF != tok.type )
{
struct in_addr addr;
if( '#' == tok.val[0] )
{
parser.NextLine();
}
else if( 0 == inet_aton( tok.val, &addr ) )
{
// It's probably an IP6 address
parser.NextLine();
}
else
{
tok = parser.NextToken();
while( CToken::TOK_EOL != tok.type )
{
AddHostEntry( MAX_TIME_T, tok.val, addr );
tok = parser.NextToken();
}
}
tok = parser.NextToken();
}
parser.Close();
// If no nameservers are specified, use localhost
if( m_listServers.IsEmpty() )
{
m_listServers.InsertTail( CSockAddr( "127.0.0.1", 53 ) );
}
}
bool CResolver::EncodeName( CPCHAR szName, PBYTE& rpbuf, size_t& rlen )
{
while( *szName && rlen > 0 )
{
CPCHAR pLabel = szName;
BYTE nLabelLen = 0;
BYTE nOverLen = min( 64, rlen-1 );
while( *szName && '.' != *szName && nLabelLen < nOverLen )
{
nLabelLen++;
szName++;
}
if( nLabelLen == nOverLen ) return false;
*rpbuf++ = nLabelLen;
rlen--;
memcpy( rpbuf, pLabel, nLabelLen );
rpbuf += nLabelLen;
rlen -= nLabelLen;
if( *szName == '.' ) szName++;
}
if( rlen < 1 ) return false;
*rpbuf++ = 0;
rlen--;
return true;
}
bool CResolver::ParseQuestionHeader( dns_qr_hdr* phdr, const CBuffer& rbuf, size_t& rpos )
{
char szHost[256];
if( ! DecodeName( szHost, rbuf, rpos ) ) return false;
if( rpos+4 > rbuf.GetSize() ) return false;
phdr->strHost = szHost;
ntohs_buf( &phdr->qtype, rbuf.GetBuffer()+rpos, 2 ); rpos += 2*2;
if( phdr->qclass != CL_IN ) return false;
return true;
}
bool CResolver::ParseAnswerHeader( dns_rr_hdr* phdr, const CBuffer& rbuf, size_t& rpos )
{
char szHost[256];
UINT16 usTmp;
UINT32 ulTmp;
if( ! DecodeName( szHost, rbuf, rpos ) ) return false;
if( rpos+10 > rbuf.GetSize() ) return false;
memcpy( &usTmp, rbuf.GetBuffer()+rpos, 2 ); rpos += 2;
phdr->rtype = ntohs( usTmp );
memcpy( &usTmp, rbuf.GetBuffer()+rpos, 2 ); rpos += 2;
phdr->rclass = ntohs( usTmp );
memcpy( &ulTmp, rbuf.GetBuffer()+rpos, 4 ); rpos += 4;
phdr->ttl = ntohl( ulTmp );
memcpy( &usTmp, rbuf.GetBuffer()+rpos, 2 ); rpos += 2;
phdr->rdlen = ntohs( usTmp );
if( phdr->rclass != CL_IN ) return false;
return true;
}
bool CResolver::DecodeName( PCHAR pname, const CBuffer& buf, size_t& rpos )
{
CPBYTE pbuf = buf.GetBuffer();
size_t buflen = buf.GetSize();
size_t pos = rpos;
size_t namelen = 0;
assert( buflen > 0 && buflen <= 512 && pos < buflen );
bool bHasPtr = false;
while( pbuf[pos] )
{
UINT8 len = pbuf[pos];
if( !(len & 0xC0) )
{
// Label
pos++;
if( len >= buflen-pos || len+namelen > 254 ) return false;
memcpy( pname, pbuf+pos, len );
pos += len;
*(pname+len) = '.';
len++;
pname += len;
namelen += len;
if( !bHasPtr ) rpos += len;
}
else
{
// Pointer
if( (len & 0xC0) != 0xC0 || pos > buflen-2 ) return false;
pos = (UINT16)(pbuf[pos] & 0x3F)*256 + (UINT16)(pbuf[pos+1]);
if( pos >= buflen-1 ) return false;
rpos += 2;
bHasPtr = true;
}
}
if( !namelen ) return false; //XXX: is root domain
*(pname-1) = '\0';
if( !bHasPtr ) rpos++;
return true;
}
syntax highlighted by Code2HTML, v. 0.9.1