dnsapi: Implement DnsQuery_{A,UTF8,W}.

This commit is contained in:
Hans Leidekker 2006-05-29 12:47:31 +02:00 committed by Alexandre Julliard
parent 4cbbce8ea6
commit 5931739cde
3 changed files with 591 additions and 5 deletions

View File

@ -138,6 +138,8 @@ static inline LPSTR dns_strdup_ua( const char *src )
return dst; return dst;
} }
const char *dns_type_to_str( unsigned short );
int dns_ns_initparse( const u_char *, int, ns_msg * ); int dns_ns_initparse( const u_char *, int, ns_msg * );
int dns_ns_parserr( ns_msg *, ns_sect, int, ns_rr * ); int dns_ns_parserr( ns_msg *, ns_sect, int, ns_rr * );
int dns_ns_name_skip( const u_char **, const u_char * ); int dns_ns_name_skip( const u_char **, const u_char * );

View File

@ -76,11 +76,11 @@
@ stub DnsNameCopy @ stub DnsNameCopy
@ stub DnsNameCopyAllocate @ stub DnsNameCopyAllocate
@ stub DnsNotifyResolver @ stub DnsNotifyResolver
@ stub DnsQuery_A @ stdcall DnsQuery_A(str long long ptr ptr ptr)
@ stdcall DnsQueryConfig(long long wstr ptr ptr ptr) @ stdcall DnsQueryConfig(long long wstr ptr ptr ptr)
@ stub DnsQueryEx @ stub DnsQueryEx
@ stub DnsQuery_UTF8 @ stdcall DnsQuery_UTF8(str long long ptr ptr ptr)
@ stub DnsQuery_W @ stdcall DnsQuery_W(wstr long long ptr ptr ptr)
@ stub DnsRecordBuild_UTF8 @ stub DnsRecordBuild_UTF8
@ stub DnsRecordBuild_W @ stub DnsRecordBuild_W
@ stdcall DnsRecordCompare(ptr ptr) @ stdcall DnsRecordCompare(ptr ptr)

View File

@ -65,9 +65,434 @@ static CRITICAL_SECTION resolver_cs = { &resolver_cs_debug, -1, 0, 0, 0, 0 };
#define UNLOCK_RESOLVER() do { LeaveCriticalSection( &resolver_cs ); } while (0) #define UNLOCK_RESOLVER() do { LeaveCriticalSection( &resolver_cs ); } while (0)
/* the resolver lock must be held and res_init() must have been static const char *dns_section_to_str( ns_sect section )
* called before calling this function. {
switch (section)
{
case ns_s_qd: return "Question";
case ns_s_an: return "Answer";
case ns_s_ns: return "Authority";
case ns_s_ar: return "Additional";
default:
{
static char tmp[5];
FIXME( "unknown section: 0x%02x\n", section );
sprintf( tmp, "0x%02x", section );
return tmp;
}
}
}
static unsigned long dns_map_options( DWORD options )
{
unsigned long ret = 0;
if (options & DNS_QUERY_STANDARD)
ret |= RES_DEFAULT;
if (options & DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE)
ret |= RES_IGNTC;
if (options & DNS_QUERY_USE_TCP_ONLY)
ret |= RES_USEVC;
if (options & DNS_QUERY_NO_RECURSION)
ret &= ~RES_RECURSE;
if (options & DNS_QUERY_NO_LOCAL_NAME)
ret &= ~RES_DNSRCH;
if (options & DNS_QUERY_NO_HOSTS_FILE)
ret |= RES_NOALIASES;
if (options & DNS_QUERY_TREAT_AS_FQDN)
ret &= ~RES_DEFNAMES;
if (options & DNS_QUERY_DONT_RESET_TTL_VALUES)
FIXME( "option DNS_QUERY_DONT_RESET_TTL_VALUES not implemented\n" );
if (options & DNS_QUERY_RESERVED)
FIXME( "option DNS_QUERY_RESERVED not implemented\n" );
if (options & DNS_QUERY_WIRE_ONLY)
FIXME( "option DNS_QUERY_WIRE_ONLY not implemented\n" );
if (options & DNS_QUERY_NO_WIRE_QUERY)
FIXME( "option DNS_QUERY_NO_WIRE_QUERY not implemented\n" );
if (options & DNS_QUERY_BYPASS_CACHE)
FIXME( "option DNS_QUERY_BYPASS_CACHE not implemented\n" );
if (options & DNS_QUERY_RETURN_MESSAGE)
FIXME( "option DNS_QUERY_RETURN_MESSAGE not implemented\n" );
if (options & DNS_QUERY_NO_NETBT)
TRACE( "netbios query disabled\n" );
return ret;
}
static DNS_STATUS dns_map_error( int error )
{
switch (error)
{
case NOERROR: return ERROR_SUCCESS;
case FORMERR: return DNS_ERROR_RCODE_FORMAT_ERROR;
case SERVFAIL: return DNS_ERROR_RCODE_SERVER_FAILURE;
case NXDOMAIN: return DNS_ERROR_RCODE_NAME_ERROR;
case NOTIMP: return DNS_ERROR_RCODE_NOT_IMPLEMENTED;
case REFUSED: return DNS_ERROR_RCODE_REFUSED;
case YXDOMAIN: return DNS_ERROR_RCODE_YXDOMAIN;
case YXRRSET: return DNS_ERROR_RCODE_YXRRSET;
case NXRRSET: return DNS_ERROR_RCODE_NXRRSET;
case NOTAUTH: return DNS_ERROR_RCODE_NOTAUTH;
case NOTZONE: return DNS_ERROR_RCODE_NOTZONE;
default:
FIXME( "unmapped error code: %d\n", error );
return DNS_ERROR_RCODE_NOT_IMPLEMENTED;
}
}
static DNS_STATUS dns_map_h_errno( int error )
{
switch (error)
{
case NO_DATA:
case HOST_NOT_FOUND: return DNS_ERROR_RCODE_NAME_ERROR;
case TRY_AGAIN: return DNS_ERROR_RCODE_SERVER_FAILURE;
case NO_RECOVERY: return DNS_ERROR_RCODE_REFUSED;
case NETDB_INTERNAL: return DNS_ERROR_RCODE;
default:
FIXME( "unmapped error code: %d\n", error );
return DNS_ERROR_RCODE_NOT_IMPLEMENTED;
}
}
static char *dns_dname_from_msg( ns_msg msg, const unsigned char *pos )
{
int len;
char *str, dname[NS_MAXDNAME] = ".";
/* returns *compressed* length, ignore it */
len = dns_ns_name_uncompress( ns_msg_base( msg ), ns_msg_end( msg ),
pos, dname, sizeof(dname) );
len = strlen( dname );
str = dns_alloc( len + 1 );
if (str) strcpy( str, dname );
return str;
}
static char *dns_str_from_rdata( const unsigned char *rdata )
{
char *str;
unsigned int len = rdata[0];
str = dns_alloc( len + 1 );
if (str)
{
memcpy( str, ++rdata, len );
str[len] = '\0';
}
return str;
}
static unsigned int dns_get_record_size( ns_rr *rr )
{
const unsigned char *pos = rr->rdata;
unsigned int num = 0, size = sizeof(DNS_RECORDA);
switch (rr->type)
{
case T_KEY:
{
pos += sizeof(WORD) + sizeof(BYTE) + sizeof(BYTE);
size += rr->rdata + rr->rdlength - pos - 1;
break;
}
case T_SIG:
{
pos += sizeof(PCHAR) + sizeof(WORD) + 2 * sizeof(BYTE);
pos += 3 * sizeof(DWORD) + 2 * sizeof(WORD);
size += rr->rdata + rr->rdlength - pos - 1;
break;
}
case T_HINFO:
case T_ISDN:
case T_TXT:
case T_X25:
{
while (pos[0] && pos < rr->rdata + rr->rdlength)
{
num++;
pos += pos[0] + 1;
}
size += (num - 1) * sizeof(PCHAR);
break;
}
case T_NULL:
{
size += rr->rdlength - 1;
break;
}
case T_NXT:
case T_WKS:
case 0xff01: /* WINS */
{
FIXME( "unhandled type: %s\n", dns_type_to_str( rr->type ) );
break;
}
default:
break;
}
return size;
}
static DNS_STATUS dns_copy_rdata( ns_msg msg, ns_rr *rr, DNS_RECORDA *r, WORD *dlen )
{
DNS_STATUS ret = ERROR_SUCCESS;
const unsigned char *pos = rr->rdata;
unsigned int i, size;
switch (rr->type)
{
case T_A:
{
r->Data.A.IpAddress = *(DWORD *)pos;
*dlen = sizeof(DNS_A_DATA);
break;
}
case T_AAAA:
{
for (i = 0; i < sizeof(IP6_ADDRESS)/sizeof(DWORD); i++)
{
r->Data.AAAA.Ip6Address.IP6Dword[i] = *(DWORD *)pos;
pos += sizeof(DWORD);
}
*dlen = sizeof(DNS_AAAA_DATA);
break;
}
case T_KEY:
{
/* FIXME: byte order? */
r->Data.KEY.wFlags = *(WORD *)pos; pos += sizeof(WORD);
r->Data.KEY.chProtocol = *(BYTE *)pos++;
r->Data.KEY.chAlgorithm = *(BYTE *)pos++;
size = rr->rdata + rr->rdlength - pos;
for (i = 0; i < size; i++)
r->Data.KEY.Key[i] = *(BYTE *)pos++;
*dlen = sizeof(DNS_KEY_DATA) + (size - 1) * sizeof(BYTE);
break;
}
case T_RP:
case T_MINFO:
{
r->Data.MINFO.pNameMailbox = dns_dname_from_msg( msg, pos );
if (!r->Data.MINFO.pNameMailbox) return ERROR_NOT_ENOUGH_MEMORY;
if (dns_ns_name_skip( &pos, ns_msg_end( msg ) ) < 0)
return DNS_ERROR_BAD_PACKET;
r->Data.MINFO.pNameErrorsMailbox = dns_dname_from_msg( msg, pos );
if (!r->Data.MINFO.pNameErrorsMailbox)
{
dns_free( r->Data.MINFO.pNameMailbox );
return ERROR_NOT_ENOUGH_MEMORY;
}
*dlen = sizeof(DNS_MINFO_DATAA);
break;
}
case T_AFSDB:
case T_RT:
case T_MX:
{
r->Data.MX.wPreference = ntohs( *(WORD *)pos );
r->Data.MX.pNameExchange = dns_dname_from_msg( msg, pos + sizeof(WORD) );
if (!r->Data.MX.pNameExchange) return ERROR_NOT_ENOUGH_MEMORY;
*dlen = sizeof(DNS_MX_DATAA);
break;
}
case T_NULL:
{
r->Data.Null.dwByteCount = rr->rdlength;
memcpy( r->Data.Null.Data, rr->rdata, rr->rdlength );
*dlen = sizeof(DNS_NULL_DATA) + rr->rdlength - 1;
break;
}
case T_CNAME:
case T_NS:
case T_MB:
case T_MD:
case T_MF:
case T_MG:
case T_MR:
case T_PTR:
{
r->Data.PTR.pNameHost = dns_dname_from_msg( msg, pos );
if (!r->Data.PTR.pNameHost) return ERROR_NOT_ENOUGH_MEMORY;
*dlen = sizeof(DNS_PTR_DATAA);
break;
}
case T_SIG:
{
r->Data.SIG.pNameSigner = dns_dname_from_msg( msg, pos );
if (!r->Data.SIG.pNameSigner) return ERROR_NOT_ENOUGH_MEMORY;
if (dns_ns_name_skip( &pos, ns_msg_end( msg ) ) < 0)
return DNS_ERROR_BAD_PACKET;
/* FIXME: byte order? */
r->Data.SIG.wTypeCovered = *(WORD *)pos; pos += sizeof(WORD);
r->Data.SIG.chAlgorithm = *(BYTE *)pos++;
r->Data.SIG.chLabelCount = *(BYTE *)pos++;
r->Data.SIG.dwOriginalTtl = *(DWORD *)pos; pos += sizeof(DWORD);
r->Data.SIG.dwExpiration = *(DWORD *)pos; pos += sizeof(DWORD);
r->Data.SIG.dwTimeSigned = *(DWORD *)pos; pos += sizeof(DWORD);
r->Data.SIG.wKeyTag = *(WORD *)pos;
size = rr->rdata + rr->rdlength - pos;
for (i = 0; i < size; i++)
r->Data.SIG.Signature[i] = *(BYTE *)pos++;
*dlen = sizeof(DNS_SIG_DATAA) + (size - 1) * sizeof(BYTE);
break;
}
case T_SOA:
{
r->Data.SOA.pNamePrimaryServer = dns_dname_from_msg( msg, pos );
if (!r->Data.SOA.pNamePrimaryServer) return ERROR_NOT_ENOUGH_MEMORY;
if (dns_ns_name_skip( &pos, ns_msg_end( msg ) ) < 0)
return DNS_ERROR_BAD_PACKET;
r->Data.SOA.pNameAdministrator = dns_dname_from_msg( msg, pos );
if (!r->Data.SOA.pNameAdministrator)
{
dns_free( r->Data.SOA.pNamePrimaryServer );
return ERROR_NOT_ENOUGH_MEMORY;
}
if (dns_ns_name_skip( &pos, ns_msg_end( msg ) ) < 0)
return DNS_ERROR_BAD_PACKET;
r->Data.SOA.dwSerialNo = ntohl( *(DWORD *)pos ); pos += sizeof(DWORD);
r->Data.SOA.dwRefresh = ntohl( *(DWORD *)pos ); pos += sizeof(DWORD);
r->Data.SOA.dwRetry = ntohl( *(DWORD *)pos ); pos += sizeof(DWORD);
r->Data.SOA.dwExpire = ntohl( *(DWORD *)pos ); pos += sizeof(DWORD);
r->Data.SOA.dwDefaultTtl = ntohl( *(DWORD *)pos ); pos += sizeof(DWORD);
*dlen = sizeof(DNS_SOA_DATAA);
break;
}
case T_SRV:
{
r->Data.SRV.wPriority = ntohs( *(WORD *)pos ); pos += sizeof(WORD);
r->Data.SRV.wWeight = ntohs( *(WORD *)pos ); pos += sizeof(WORD);
r->Data.SRV.wPort = ntohs( *(WORD *)pos ); pos += sizeof(WORD);
r->Data.SRV.pNameTarget = dns_dname_from_msg( msg, pos );
if (!r->Data.SRV.pNameTarget) return ERROR_NOT_ENOUGH_MEMORY;
*dlen = sizeof(DNS_SRV_DATAA);
break;
}
case T_HINFO:
case T_ISDN:
case T_X25:
case T_TXT:
{
i = 0;
while (pos[0] && pos < rr->rdata + rr->rdlength)
{
r->Data.TXT.pStringArray[i] = dns_str_from_rdata( pos );
if (!r->Data.TXT.pStringArray[i])
{
for (--i; i >= 0; i--)
dns_free( r->Data.TXT.pStringArray[i] );
return ERROR_NOT_ENOUGH_MEMORY;
}
i++;
pos += pos[0] + 1;
}
r->Data.TXT.dwStringCount = i;
*dlen = sizeof(DNS_TXT_DATAA) + (i - 1) * sizeof(PCHAR);
break;
}
case T_ATMA:
case T_LOC:
case T_NXT:
case T_TSIG:
case T_WKS:
case 0x00f9: /* TKEY */
case 0xff01: /* WINS */
case 0xff02: /* WINSR */
default:
FIXME( "unhandled type: %s\n", dns_type_to_str( rr->type ) );
return DNS_ERROR_RCODE_NOT_IMPLEMENTED;
}
return ret;
}
static DNS_STATUS dns_copy_record( ns_msg msg, ns_sect section,
unsigned short num, DNS_RECORDA **recp )
{
DNS_STATUS ret;
DNS_RECORDA *record;
WORD dlen;
ns_rr rr;
if (dns_ns_parserr( &msg, section, num, &rr ) < 0)
return DNS_ERROR_BAD_PACKET;
if (!(record = dns_zero_alloc( dns_get_record_size( &rr ) )))
return ERROR_NOT_ENOUGH_MEMORY;
record->pName = dns_strdup_u( rr.name );
if (!record->pName)
{
dns_free( record );
return ERROR_NOT_ENOUGH_MEMORY;
}
record->wType = rr.type;
record->Flags.S.Section = section;
record->Flags.S.CharSet = DnsCharSetUtf8;
record->dwTtl = rr.ttl;
if ((ret = dns_copy_rdata( msg, &rr, record, &dlen )))
{
dns_free( record->pName );
dns_free( record );
return ret;
}
record->wDataLength = dlen;
*recp = record;
TRACE( "found %s record in %s section\n",
dns_type_to_str( rr.type ), dns_section_to_str( section ) );
return ERROR_SUCCESS;
}
/* The resolver lock must be held and res_init() must have been
* called before calling these three functions.
*/ */
static DNS_STATUS dns_set_serverlist( PIP4_ARRAY addrs )
{
unsigned int i;
if (addrs->AddrCount > MAXNS)
{
WARN( "too many servers: %ld only using the first: %d\n",
addrs->AddrCount, MAXNS );
_res.nscount = MAXNS;
}
else _res.nscount = addrs->AddrCount;
for (i = 0; i < _res.nscount; i++)
_res.nsaddr_list[i].sin_addr.s_addr = addrs->AddrArray[i];
return ERROR_SUCCESS;
}
static DNS_STATUS dns_get_serverlist( PIP4_ARRAY addrs, PDWORD len ) static DNS_STATUS dns_get_serverlist( PIP4_ARRAY addrs, PDWORD len )
{ {
unsigned int i, size; unsigned int i, size;
@ -87,8 +512,167 @@ static DNS_STATUS dns_get_serverlist( PIP4_ARRAY addrs, PDWORD len )
return ERROR_SUCCESS; return ERROR_SUCCESS;
} }
static DNS_STATUS dns_do_query( PCSTR name, WORD type, DWORD options,
PDNS_RECORDA *result )
{
DNS_STATUS ret = DNS_ERROR_RCODE_NOT_IMPLEMENTED;
unsigned int i, num;
unsigned char answer[NS_PACKETSZ];
ns_sect sections[] = { ns_s_an, ns_s_ar };
ns_msg msg;
DNS_RECORDA *record = NULL;
DNS_RRSET rrset;
int len;
DNS_RRSET_INIT( rrset );
len = __res_query( name, C_IN, type, answer, sizeof(answer) );
if (len < 0)
{
ret = dns_map_h_errno( h_errno );
goto exit;
}
if (dns_ns_initparse( answer, len, &msg ) < 0)
{
ret = DNS_ERROR_BAD_PACKET;
goto exit;
}
if (ns_msg_getflag( msg, ns_f_rcode ) != ns_r_noerror)
{
ret = dns_map_error( ns_msg_getflag( msg, ns_f_rcode ) );
goto exit;
}
for (i = 0; i < sizeof(sections)/sizeof(sections[0]); i++)
{
for (num = 0; num < ns_msg_count( msg, sections[i] ); num++)
{
ret = dns_copy_record( msg, sections[i], num, &record );
if (ret != ERROR_SUCCESS) goto exit;
DNS_RRSET_ADD( rrset, (DNS_RECORD *)record );
}
}
exit:
DNS_RRSET_TERMINATE( rrset );
if (ret != ERROR_SUCCESS)
DnsRecordListFree( rrset.pFirstRR, DnsFreeRecordList );
else
*result = (DNS_RECORDA *)rrset.pFirstRR;
return ret;
}
#endif /* HAVE_RESOLV */ #endif /* HAVE_RESOLV */
/******************************************************************************
* DnsQuery_A [DNSAPI.@]
*
*/
DNS_STATUS WINAPI DnsQuery_A( PCSTR name, WORD type, DWORD options, PIP4_ARRAY servers,
PDNS_RECORDA *result, PVOID *reserved )
{
WCHAR *nameW;
DNS_RECORDW *resultW;
DNS_STATUS status;
TRACE( "(%s,%s,0x%08lx,%p,%p,%p)\n", debugstr_a(name), dns_type_to_str( type ),
options, servers, result, reserved );
if (!name || !result)
return ERROR_INVALID_PARAMETER;
nameW = dns_strdup_aw( name );
if (!nameW) return ERROR_NOT_ENOUGH_MEMORY;
status = DnsQuery_W( nameW, type, options, servers, &resultW, reserved );
if (status == ERROR_SUCCESS)
{
*result = (DNS_RECORDA *)DnsRecordSetCopyEx(
(DNS_RECORD *)resultW, DnsCharSetUnicode, DnsCharSetAnsi );
if (!*result) status = ERROR_NOT_ENOUGH_MEMORY;
DnsRecordListFree( (DNS_RECORD *)resultW, DnsFreeRecordList );
}
dns_free( nameW );
return status;
}
/******************************************************************************
* DnsQuery_UTF8 [DNSAPI.@]
*
*/
DNS_STATUS WINAPI DnsQuery_UTF8( PCSTR name, WORD type, DWORD options, PIP4_ARRAY servers,
PDNS_RECORDA *result, PVOID *reserved )
{
DNS_STATUS ret = DNS_ERROR_RCODE_NOT_IMPLEMENTED;
#ifdef HAVE_RESOLV
TRACE( "(%s,%s,0x%08lx,%p,%p,%p)\n", debugstr_a(name), dns_type_to_str( type ),
options, servers, result, reserved );
if (!name || !result)
return ERROR_INVALID_PARAMETER;
LOCK_RESOLVER();
res_init();
_res.options |= dns_map_options( options );
if (servers && (ret = dns_set_serverlist( servers )))
return ret;
ret = dns_do_query( name, type, options, result );
UNLOCK_RESOLVER();
#endif
return ret;
}
/******************************************************************************
* DnsQuery_W [DNSAPI.@]
*
*/
DNS_STATUS WINAPI DnsQuery_W( PCWSTR name, WORD type, DWORD options, PIP4_ARRAY servers,
PDNS_RECORDW *result, PVOID *reserved )
{
char *nameU;
DNS_RECORDA *resultA;
DNS_STATUS status;
TRACE( "(%s,%s,0x%08lx,%p,%p,%p)\n", debugstr_w(name), dns_type_to_str( type ),
options, servers, result, reserved );
if (!name || !result)
return ERROR_INVALID_PARAMETER;
nameU = dns_strdup_wu( name );
if (!nameU) return ERROR_NOT_ENOUGH_MEMORY;
status = DnsQuery_UTF8( nameU, type, options, servers, &resultA, reserved );
if (status == ERROR_SUCCESS)
{
*result = (DNS_RECORDW *)DnsRecordSetCopyEx(
(DNS_RECORD *)resultA, DnsCharSetUtf8, DnsCharSetUnicode );
if (!*result) status = ERROR_NOT_ENOUGH_MEMORY;
DnsRecordListFree( (DNS_RECORD *)resultA, DnsFreeRecordList );
}
dns_free( nameU );
return status;
}
static DNS_STATUS dns_get_hostname_a( COMPUTER_NAME_FORMAT format, static DNS_STATUS dns_get_hostname_a( COMPUTER_NAME_FORMAT format,
LPSTR buffer, PDWORD len ) LPSTR buffer, PDWORD len )
{ {