From 5931739cdea8465618bbcc1d37840fcdef7b5d5e Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Mon, 29 May 2006 12:47:31 +0200 Subject: [PATCH] dnsapi: Implement DnsQuery_{A,UTF8,W}. --- dlls/dnsapi/dnsapi.h | 2 + dlls/dnsapi/dnsapi.spec | 6 +- dlls/dnsapi/query.c | 588 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 591 insertions(+), 5 deletions(-) diff --git a/dlls/dnsapi/dnsapi.h b/dlls/dnsapi/dnsapi.h index 915b3a0f54a..2060a475b35 100644 --- a/dlls/dnsapi/dnsapi.h +++ b/dlls/dnsapi/dnsapi.h @@ -138,6 +138,8 @@ static inline LPSTR dns_strdup_ua( const char *src ) return dst; } +const char *dns_type_to_str( unsigned short ); + int dns_ns_initparse( const u_char *, int, ns_msg * ); int dns_ns_parserr( ns_msg *, ns_sect, int, ns_rr * ); int dns_ns_name_skip( const u_char **, const u_char * ); diff --git a/dlls/dnsapi/dnsapi.spec b/dlls/dnsapi/dnsapi.spec index ccbc5555819..002dba0bc7f 100644 --- a/dlls/dnsapi/dnsapi.spec +++ b/dlls/dnsapi/dnsapi.spec @@ -76,11 +76,11 @@ @ stub DnsNameCopy @ stub DnsNameCopyAllocate @ stub DnsNotifyResolver -@ stub DnsQuery_A +@ stdcall DnsQuery_A(str long long ptr ptr ptr) @ stdcall DnsQueryConfig(long long wstr ptr ptr ptr) @ stub DnsQueryEx -@ stub DnsQuery_UTF8 -@ stub DnsQuery_W +@ stdcall DnsQuery_UTF8(str long long ptr ptr ptr) +@ stdcall DnsQuery_W(wstr long long ptr ptr ptr) @ stub DnsRecordBuild_UTF8 @ stub DnsRecordBuild_W @ stdcall DnsRecordCompare(ptr ptr) diff --git a/dlls/dnsapi/query.c b/dlls/dnsapi/query.c index bb75a56228e..f1b31f75a02 100644 --- a/dlls/dnsapi/query.c +++ b/dlls/dnsapi/query.c @@ -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) -/* the resolver lock must be held and res_init() must have been - * called before calling this function. +static const char *dns_section_to_str( ns_sect section ) +{ + 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 ) { unsigned int i, size; @@ -87,8 +512,167 @@ static DNS_STATUS dns_get_serverlist( PIP4_ARRAY addrs, PDWORD len ) 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 */ +/****************************************************************************** + * 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, LPSTR buffer, PDWORD len ) {