From aec19e86f66988aec25b258258017d648110f95b Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 2 Nov 2021 12:48:15 +0100 Subject: [PATCH] dnsapi: Implement DnsExtractRecordsFromMessage(). Signed-off-by: Alexandre Julliard --- dlls/dnsapi/main.c | 26 --- dlls/dnsapi/record.c | 433 +++++++++++++++++++++++++++++++++++++ dlls/dnsapi/tests/record.c | 199 +++++++++++++++++ 3 files changed, 632 insertions(+), 26 deletions(-) diff --git a/dlls/dnsapi/main.c b/dlls/dnsapi/main.c index f83a54d1469..3beef6a1fd3 100644 --- a/dlls/dnsapi/main.c +++ b/dlls/dnsapi/main.c @@ -150,32 +150,6 @@ VOID WINAPI DnsReleaseContextHandle( HANDLE context ) FIXME( "(%p) stub\n", context ); } -/****************************************************************************** - * DnsExtractRecordsFromMessage_UTF8 [DNSAPI.@] - * - */ -DNS_STATUS WINAPI DnsExtractRecordsFromMessage_UTF8( PDNS_MESSAGE_BUFFER buffer, - WORD len, PDNS_RECORDA *record ) -{ - FIXME( "(%p,%d,%p) stub\n", buffer, len, record ); - - *record = NULL; - return ERROR_SUCCESS; -} - -/****************************************************************************** - * DnsExtractRecordsFromMessage_W [DNSAPI.@] - * - */ -DNS_STATUS WINAPI DnsExtractRecordsFromMessage_W( PDNS_MESSAGE_BUFFER buffer, - WORD len, PDNS_RECORDW *record ) -{ - FIXME( "(%p,%d,%p) stub\n", buffer, len, record ); - - *record = NULL; - return ERROR_SUCCESS; -} - /****************************************************************************** * DnsModifyRecordsInSet_A [DNSAPI.@] * diff --git a/dlls/dnsapi/record.c b/dlls/dnsapi/record.c index 1fdbae523eb..c3dfd63fd65 100644 --- a/dlls/dnsapi/record.c +++ b/dlls/dnsapi/record.c @@ -2,6 +2,7 @@ * DNS support * * Copyright (C) 2006 Hans Leidekker + * Copyright (C) 2021 Alexandre Julliard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -95,6 +96,18 @@ const char *debugstr_type( unsigned short type ) } } +static const char *debugstr_section( DNS_SECTION section ) +{ + switch (section) + { + case DnsSectionQuestion: return "Question"; + case DnsSectionAnswer: return "Answer"; + case DnsSectionAuthority: return "Authority"; + case DnsSectionAddtional: return "Additional"; + default: return "??"; + } +} + static int strcmpX( LPCVOID str1, LPCVOID str2, BOOL wide ) { if (wide) @@ -103,6 +116,95 @@ static int strcmpX( LPCVOID str1, LPCVOID str2, BOOL wide ) return lstrcmpiA( str1, str2 ); } +static WORD get_word( const BYTE **ptr ) +{ + WORD ret = ((*ptr)[0] << 8) + (*ptr)[1]; + *ptr += sizeof(WORD); + return ret; +} + +static DWORD get_dword( const BYTE **ptr ) +{ + DWORD ret = ((*ptr)[0] << 24) + ((*ptr)[1] << 16) + ((*ptr)[2] << 8) + (*ptr)[3]; + *ptr += sizeof(DWORD); + return ret; +} + +static const BYTE *skip_name( const BYTE *ptr, const BYTE *end ) +{ + BYTE len; + + while (ptr < end && (len = *ptr++)) + { + switch (len & 0xc0) + { + case 0: + ptr += len; + continue; + case 0xc0: + ptr++; + break; + default: + return NULL; + } + break; + } + if (ptr < end) return ptr; + return NULL; +} + +static const BYTE *skip_record( const BYTE *ptr, const BYTE *end, DNS_SECTION section ) +{ + WORD len; + + if (!(ptr = skip_name( ptr, end ))) return NULL; + ptr += 2; /* type */ + ptr += 2; /* class */ + if (section != DnsSectionQuestion) + { + ptr += 4; /* ttl */ + if (ptr + 2 > end) return NULL; + len = get_word( &ptr ); + ptr += len; + } + if (ptr > end) return NULL; + return ptr; +} + +static const BYTE *get_name( const BYTE *base, const BYTE *end, const BYTE *ptr, + char name[DNS_MAX_NAME_BUFFER_LENGTH] ) +{ + BYTE len; + char *out = name; + const BYTE *next = NULL; + int loop = 0; + + while (ptr < end && (len = *ptr++)) + { + switch (len & 0xc0) + { + case 0: + if (out + len + 1 >= name + DNS_MAX_NAME_BUFFER_LENGTH) return NULL; + if (out > name) *out++ = '.'; + memcpy( out, ptr, len ); + out += len; + ptr += len; + continue; + case 0xc0: + if (!next) next = ptr + 1; + if (++loop >= end - base) return NULL; + if (ptr < end) ptr = base + ((len & 0x3f) << 8) + *ptr; + break; + default: + return NULL; + } + } + if (ptr >= end) return NULL; + if (out == name) *out++ = '.'; + *out = 0; + return next ? next : ptr; +} + /****************************************************************************** * DnsRecordCompare [DNSAPI.@] * @@ -786,3 +888,334 @@ PDNS_RECORD WINAPI DnsRecordSetDetach( PDNS_RECORD set ) } return NULL; } + + +static unsigned int get_record_size( WORD type, const BYTE *data, WORD len ) +{ + switch (type) + { + case DNS_TYPE_KEY: + return offsetof( DNS_RECORDA, Data.Key.Key[len] ); + + case DNS_TYPE_SIG: + return offsetof( DNS_RECORDA, Data.Sig.Signature[len] ); + + case DNS_TYPE_HINFO: + case DNS_TYPE_ISDN: + case DNS_TYPE_TEXT: + case DNS_TYPE_X25: + { + int i; + const BYTE *pos = data; + for (i = 0; pos < data + len; i++) pos += pos[0] + 1; + return offsetof( DNS_RECORDA, Data.Txt.pStringArray[i] ); + } + + case DNS_TYPE_NULL: + case DNS_TYPE_OPT: + return offsetof( DNS_RECORDA, Data.Null.Data[len] ); + + case DNS_TYPE_WKS: + return offsetof( DNS_RECORDA, Data.Wks.BitMask[len / 8] ); + + case DNS_TYPE_NXT: + return offsetof( DNS_RECORDA, Data.Nxt.wTypes[len * 8] ); + + case DNS_TYPE_WINS: + return offsetof( DNS_RECORDA, Data.Wins.WinsServers[len / 4] ); + + default: + return sizeof(DNS_RECORDA); + } +} + +static DNS_STATUS extract_rdata( const BYTE *base, const BYTE *end, const BYTE *pos, WORD len, WORD type, + DNS_RECORDA *r ) +{ + DNS_CHARSET in = DnsCharSetUtf8, out = r->Flags.S.CharSet; + char name[DNS_MAX_NAME_BUFFER_LENGTH]; + DNS_STATUS ret = ERROR_SUCCESS; + const BYTE *rrend = pos + len; + unsigned int i; + + switch (type) + { + case DNS_TYPE_A: + if (pos + sizeof(DWORD) > rrend) return DNS_ERROR_BAD_PACKET; + r->Data.A.IpAddress = *(const DWORD *)pos; + r->wDataLength = sizeof(DNS_A_DATA); + break; + + case DNS_TYPE_AAAA: + if (pos + sizeof(IP6_ADDRESS) > rrend) return DNS_ERROR_BAD_PACKET; + for (i = 0; i < sizeof(IP6_ADDRESS)/sizeof(DWORD); i++) + { + r->Data.AAAA.Ip6Address.IP6Dword[i] = *(const DWORD *)pos; + pos += sizeof(DWORD); + } + r->wDataLength = sizeof(DNS_AAAA_DATA); + break; + + case DNS_TYPE_KEY: + if (pos + 4 > rrend) return DNS_ERROR_BAD_PACKET; + r->Data.KEY.wFlags = get_word( &pos ); + r->Data.KEY.chProtocol = *pos++; + r->Data.KEY.chAlgorithm = *pos++; + r->Data.KEY.wKeyLength = rrend - pos; + memcpy( r->Data.KEY.Key, pos, r->Data.KEY.wKeyLength ); + r->wDataLength = offsetof( DNS_KEY_DATA, Key[r->Data.KEY.wKeyLength] ); + break; + + case DNS_TYPE_RP: + case DNS_TYPE_MINFO: + if (!(pos = get_name( base, end, pos, name ))) return DNS_ERROR_BAD_PACKET; + if (!(r->Data.MINFO.pNameMailbox = strcpyX( name, in, out ))) return ERROR_NOT_ENOUGH_MEMORY; + if (!get_name( base, end, pos, name )) return DNS_ERROR_BAD_PACKET; + if (!(r->Data.MINFO.pNameErrorsMailbox = strcpyX( name, in, out ))) + { + heap_free( r->Data.MINFO.pNameMailbox ); + return ERROR_NOT_ENOUGH_MEMORY; + } + r->wDataLength = sizeof(DNS_MINFO_DATAA); + break; + + case DNS_TYPE_AFSDB: + case DNS_TYPE_RT: + case DNS_TYPE_MX: + if (pos + sizeof(WORD) > rrend) return DNS_ERROR_BAD_PACKET; + r->Data.MX.wPreference = get_word( &pos ); + if (!get_name( base, end, pos, name )) return DNS_ERROR_BAD_PACKET; + if (!(r->Data.MX.pNameExchange = strcpyX( name, in, out ))) return ERROR_NOT_ENOUGH_MEMORY; + r->wDataLength = sizeof(DNS_MX_DATAA) + sizeof(DWORD); + break; + + case DNS_TYPE_NULL: + r->Data.Null.dwByteCount = len; + memcpy( r->Data.Null.Data, pos, len ); + r->wDataLength = offsetof( DNS_NULL_DATA, Data[len] ); + break; + + case DNS_TYPE_OPT: + r->Data.OPT.wDataLength = len; + r->Data.OPT.wPad = 0; + memcpy( r->Data.OPT.Data, pos, len ); + r->wDataLength = offsetof( DNS_OPT_DATA, Data[len] ); + break; + + case DNS_TYPE_CNAME: + case DNS_TYPE_NS: + case DNS_TYPE_MB: + case DNS_TYPE_MD: + case DNS_TYPE_MF: + case DNS_TYPE_MG: + case DNS_TYPE_MR: + case DNS_TYPE_PTR: + if (!get_name( base, end, pos, name )) return DNS_ERROR_BAD_PACKET; + if (!(r->Data.PTR.pNameHost = strcpyX( name, in, out ))) return ERROR_NOT_ENOUGH_MEMORY; + r->wDataLength = sizeof(DNS_PTR_DATAA) + sizeof(DWORD); + break; + + case DNS_TYPE_SIG: + if (pos + 18 > rrend) return DNS_ERROR_BAD_PACKET; + r->Data.SIG.wTypeCovered = get_word( &pos ); + r->Data.SIG.chAlgorithm = *pos++; + r->Data.SIG.chLabelCount = *pos++; + r->Data.SIG.dwOriginalTtl = get_dword( &pos ); + r->Data.SIG.dwExpiration = get_dword( &pos ); + r->Data.SIG.dwTimeSigned = get_dword( &pos ); + r->Data.SIG.wKeyTag = get_word( &pos ); + if (!(pos = get_name( base, end, pos, name ))) return DNS_ERROR_BAD_PACKET; + if (!(r->Data.SIG.pNameSigner = strcpyX( name, in, out ))) return ERROR_NOT_ENOUGH_MEMORY; + r->Data.SIG.wSignatureLength = rrend - pos; + memcpy( r->Data.SIG.Signature, pos, r->Data.SIG.wSignatureLength ); + r->wDataLength = offsetof( DNS_SIG_DATAA, Signature[r->Data.SIG.wSignatureLength] ); + break; + + case DNS_TYPE_SOA: + if (!(pos = get_name( base, end, pos, name ))) return DNS_ERROR_BAD_PACKET; + if (!(r->Data.SOA.pNamePrimaryServer = strcpyX( name, in, out ))) return ERROR_NOT_ENOUGH_MEMORY; + if (!(pos = get_name( base, end, pos, name ))) return DNS_ERROR_BAD_PACKET; + if (!(r->Data.SOA.pNameAdministrator = strcpyX( name, in, out ))) + { + heap_free( r->Data.SOA.pNamePrimaryServer ); + return ERROR_NOT_ENOUGH_MEMORY; + } + if (pos + 5 * sizeof(DWORD) > rrend) return DNS_ERROR_BAD_PACKET; + r->Data.SOA.dwSerialNo = get_dword( &pos ); + r->Data.SOA.dwRefresh = get_dword( &pos ); + r->Data.SOA.dwRetry = get_dword( &pos ); + r->Data.SOA.dwExpire = get_dword( &pos ); + r->Data.SOA.dwDefaultTtl = get_dword( &pos ); + r->wDataLength = sizeof(DNS_SOA_DATAA); + break; + + case DNS_TYPE_SRV: + if (pos + 3 * sizeof(WORD) > rrend) return DNS_ERROR_BAD_PACKET; + r->Data.SRV.wPriority = get_word( &pos ); + r->Data.SRV.wWeight = get_word( &pos ); + r->Data.SRV.wPort = get_word( &pos ); + if (!get_name( base, end, pos, name )) return DNS_ERROR_BAD_PACKET; + if (!(r->Data.SRV.pNameTarget = strcpyX( name, in, out ))) return ERROR_NOT_ENOUGH_MEMORY; + r->wDataLength = sizeof(DNS_SRV_DATAA); + break; + + case DNS_TYPE_HINFO: + case DNS_TYPE_ISDN: + case DNS_TYPE_X25: + case DNS_TYPE_TEXT: + for (i = 0; pos < rrend; i++) + { + BYTE len = pos[0]; + if (pos + len + 1 > rrend) return DNS_ERROR_BAD_PACKET; + memcpy( name, pos + 1, len ); + name[len] = 0; + if (!(r->Data.TXT.pStringArray[i] = strcpyX( name, in, out ))) + { + while (i > 0) heap_free( r->Data.TXT.pStringArray[--i] ); + return ERROR_NOT_ENOUGH_MEMORY; + } + pos += len + 1; + } + r->Data.TXT.dwStringCount = i; + r->wDataLength = offsetof( DNS_TXT_DATAA, pStringArray[r->Data.TXT.dwStringCount] ); + break; + + case DNS_TYPE_ATMA: + case DNS_TYPE_LOC: + case DNS_TYPE_NXT: + case DNS_TYPE_TSIG: + case DNS_TYPE_WKS: + case DNS_TYPE_TKEY: + case DNS_TYPE_WINS: + case DNS_TYPE_WINSR: + default: + FIXME( "unhandled type: %s\n", debugstr_type( type )); + return DNS_ERROR_RCODE_NOT_IMPLEMENTED; + } + + return ret; +} + +static DNS_STATUS extract_record( const DNS_MESSAGE_BUFFER *hdr, const BYTE *end, const BYTE **pos, + DNS_SECTION section, DNS_CHARSET charset, DNS_RECORDA **recp ) +{ + DNS_STATUS ret; + DNS_RECORDA *record; + char name[DNS_MAX_NAME_BUFFER_LENGTH]; + WORD type, rdlen; + DWORD ttl; + const BYTE *base = (const BYTE *)hdr; + const BYTE *ptr = *pos; + + if (!(ptr = get_name( base, end, ptr, name ))) return DNS_ERROR_BAD_PACKET; + + if (ptr + 10 > end) return DNS_ERROR_BAD_PACKET; + type = get_word( &ptr ); + ptr += sizeof(WORD); /* class */ + ttl = get_dword( &ptr ); + rdlen = get_word( &ptr ); + if (ptr + rdlen > end) return DNS_ERROR_BAD_PACKET; + *pos = ptr + rdlen; + + if (!(record = heap_alloc_zero( get_record_size( type, ptr, rdlen ) ))) return ERROR_NOT_ENOUGH_MEMORY; + + record->wType = type; + record->Flags.S.Section = section; + record->Flags.S.CharSet = charset; + record->dwTtl = ttl; + + if (!(record->pName = strcpyX( name, DnsCharSetUtf8, charset ))) + { + heap_free( record ); + return ERROR_NOT_ENOUGH_MEMORY; + } + if ((ret = extract_rdata( base, end, ptr, rdlen, type, record ))) + { + heap_free( record->pName ); + heap_free( record ); + return ret; + } + *recp = record; + TRACE( "found %s record in %s section\n", debugstr_type( record->wType ), debugstr_section( section ) ); + return ERROR_SUCCESS; +} + +static DNS_STATUS extract_message_records( const DNS_MESSAGE_BUFFER *buffer, WORD len, DNS_CHARSET charset, + DNS_RRSET *rrset ) +{ + DNS_STATUS ret = ERROR_SUCCESS; + const DNS_HEADER *hdr = &buffer->MessageHead; + DNS_RECORDA *record; + const BYTE *base = (const BYTE *)hdr; + const BYTE *end = base + len; + const BYTE *ptr = (const BYTE *)buffer->MessageBody; + unsigned int num; + + if (hdr->IsResponse && !hdr->AnswerCount) return DNS_ERROR_BAD_PACKET; + + for (num = 0; num < hdr->QuestionCount; num++) + if (!(ptr = skip_record( ptr, end, DnsSectionQuestion ))) return DNS_ERROR_BAD_PACKET; + + for (num = 0; num < hdr->AnswerCount; num++) + { + if ((ret = extract_record( buffer, end, &ptr, DnsSectionAnswer, charset, &record ))) + return ret; + DNS_RRSET_ADD( *rrset, (DNS_RECORD *)record ); + } + + for (num = 0; num < hdr->NameServerCount; num++) + if (!(ptr = skip_record( ptr, end, DnsSectionAuthority ))) return DNS_ERROR_BAD_PACKET; + + for (num = 0; num < hdr->AdditionalCount; num++) + { + if ((ret = extract_record( buffer, end, &ptr, DnsSectionAddtional, charset, &record ))) return ret; + DNS_RRSET_ADD( *rrset, (DNS_RECORD *)record ); + } + + if (ptr != end) ret = DNS_ERROR_BAD_PACKET; + return ret; +} + +/****************************************************************************** + * DnsExtractRecordsFromMessage_UTF8 [DNSAPI.@] + * + */ +DNS_STATUS WINAPI DnsExtractRecordsFromMessage_UTF8( DNS_MESSAGE_BUFFER *buffer, WORD len, + DNS_RECORDA **result ) +{ + DNS_STATUS ret = DNS_ERROR_BAD_PACKET; + DNS_RRSET rrset; + + if (len < sizeof(*buffer)) return ERROR_INVALID_PARAMETER; + + DNS_RRSET_INIT( rrset ); + ret = extract_message_records( buffer, len, DnsCharSetUtf8, &rrset ); + DNS_RRSET_TERMINATE( rrset ); + + if (!ret) *result = (DNS_RECORDA *)rrset.pFirstRR; + else DnsRecordListFree( rrset.pFirstRR, DnsFreeRecordList ); + + return ret; +} + +/****************************************************************************** + * DnsExtractRecordsFromMessage_W [DNSAPI.@] + * + */ +DNS_STATUS WINAPI DnsExtractRecordsFromMessage_W( DNS_MESSAGE_BUFFER *buffer, WORD len, + DNS_RECORDW **result ) +{ + DNS_STATUS ret = DNS_ERROR_BAD_PACKET; + DNS_RRSET rrset; + + if (len < sizeof(*buffer)) return ERROR_INVALID_PARAMETER; + + DNS_RRSET_INIT( rrset ); + ret = extract_message_records( buffer, len, DnsCharSetUnicode, &rrset ); + DNS_RRSET_TERMINATE( rrset ); + + if (!ret) *result = (DNS_RECORDW *)rrset.pFirstRR; + else DnsRecordListFree( rrset.pFirstRR, DnsFreeRecordList ); + + return ret; +} diff --git a/dlls/dnsapi/tests/record.c b/dlls/dnsapi/tests/record.c index c2f61b40843..c07c3400e78 100644 --- a/dlls/dnsapi/tests/record.c +++ b/dlls/dnsapi/tests/record.c @@ -138,9 +138,208 @@ static void test_DnsRecordSetDetach( void ) ok( !r2.pNext, "failed unexpectedly\n" ); } +static BYTE msg_empty[] = /* empty message */ +{ + 0x12, 0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00 +}; +static BYTE msg_empty_answer[] = /* marked as answer but contains only question */ +{ + 0x12, 0x34, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01 +}; +static BYTE msg_question[] = /* question only */ +{ + 0x12, 0x34, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01 +}; +static BYTE msg_winehq[] = /* valid answer for winehq.org */ +{ + 0x12, 0x34, 0x81, 0x80, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 'w', 'i', 'n', 'e', 'h', 'q', 0x03, 'o', 'r', 'g', 0x00, 0x00, 0x01, 0x00, 0x01, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x04, 0x05, 0x06, 0x07, 0x00, 0x04, 0x04, 0x04, 0x51, 0x7c +}; +static BYTE msg_invchars[] = /* invalid characters in name */ +{ + 0x12, 0x34, 0x81, 0x80, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 'a', '$', 0x02, 'b', '\\', 0x02, 'c', '.', 0x02, 0x09, 'd', 0x00, 0x00, 0x01, 0x00, 0x01, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x04, 0x04, 0x51, 0x7c +}; +static BYTE msg_root[] = /* root name */ +{ + 0x12, 0x34, 0x81, 0x80, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x04, 0x04, 0x51, 0x7c +}; +static BYTE msg_label[] = /* name with binary label, not supported on Windows */ +{ + 0x12, 0x34, 0x81, 0x80, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 'a', 'b', 'c', 0x41, 0x01, 0x34, 0x00, 0x00, 0x01, 0x00, 0x01, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x04, 0x04, 0x51, 0x7c +}; +static BYTE msg_loop[] = /* name with loop */ +{ + 0x12, 0x34, 0x81, 0x80, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x04, 0x04, 0x51, 0x7c +}; +static BYTE msg_types[] = /* various record types */ +{ + 0x12, 0x34, 0x81, 0x80, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 'w', 'i', 'n', 'e', 'h', 'q', 0x03, 'o', 'r', 'g', 0x00, 0x00, 0x01, 0x00, 0x01, +/* CNAME */ 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x04, 0x05, 0x06, 0x07, 0x00, 0x05, 0x01, 'a', 0x01, 'b', 0x00, +/* MX */ 0xc0, 0x0c, 0x00, 0x0f, 0x00, 0x01, 0x04, 0x05, 0x06, 0x07, 0x00, 0x07, 0x03, 0x04, 0x01, 'c', 0x01, 'd', 0x00, +/* AAAA */ 0xc0, 0x0c, 0x00, 0x1c, 0x00, 0x01, 0x04, 0x05, 0x06, 0x07, 0x00, 0x10, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, +/* KEY */ 0xc0, 0x0c, 0x00, 0x19, 0x00, 0x01, 0x04, 0x05, 0x06, 0x07, 0x00, 0x06, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, +/* TXT */ 0x01, 't', 0x01, 'x', 0x00, 0x00, 0x10, 0x00, 0x01, 0x04, 0x05, 0x06, 0x07, 0x00, 0x09, 0x02, 'z', 'y', 0x00, 0x04, 'X', 'Y', 0xc3, 0xa9 +}; + +static void test_DnsExtractRecordsFromMessage(void) +{ + DNS_STATUS ret; + DNS_RECORDA *rec, *r; + DNS_RECORDW *recW, *rW; + + ret = DnsExtractRecordsFromMessage_UTF8( (DNS_MESSAGE_BUFFER *)msg_empty, sizeof(msg_empty) - 1, &rec ); + ok( ret == ERROR_INVALID_PARAMETER || broken(ret == DNS_ERROR_BAD_PACKET) /* win7 */, + "failed %u\n", ret ); + + ret = DnsExtractRecordsFromMessage_UTF8( (DNS_MESSAGE_BUFFER *)msg_empty, sizeof(msg_empty), &rec ); + ok( ret == DNS_ERROR_BAD_PACKET, "failed %u\n", ret ); + + ret = DnsExtractRecordsFromMessage_UTF8( (DNS_MESSAGE_BUFFER *)msg_empty_answer, sizeof(msg_empty_answer), &rec ); + ok( ret == DNS_ERROR_BAD_PACKET, "failed %u\n", ret ); + + rec = (void *)0xdeadbeef; + ret = DnsExtractRecordsFromMessage_UTF8( (DNS_MESSAGE_BUFFER *)msg_question, sizeof(msg_question), &rec ); + ok( !ret, "failed %u\n", ret ); + ok( !rec, "record %p\n", rec ); + + rec = (void *)0xdeadbeef; + ret = DnsExtractRecordsFromMessage_UTF8( (DNS_MESSAGE_BUFFER *)msg_winehq, sizeof(msg_winehq), &rec ); + ok( !ret, "failed %u\n", ret ); + ok( rec != NULL, "record not set\n" ); + ok( !strcmp( rec->pName, "winehq.org" ), "wrong name %s\n", rec->pName ); + ok( rec->Flags.S.Section == DnsSectionAnswer, "wrong section %u\n", rec->Flags.S.Section ); + ok( rec->Flags.S.CharSet == DnsCharSetUtf8, "wrong charset %u\n", rec->Flags.S.CharSet ); + ok( rec->wType == DNS_TYPE_A, "wrong type %u\n", rec->wType ); + ok( rec->wDataLength == sizeof(DNS_A_DATA), "wrong len %u\n", rec->wDataLength ); + ok( rec->dwTtl == 0x04050607, "wrong ttl %x\n", rec->dwTtl ); + ok( rec->Data.A.IpAddress == 0x7c510404, "wrong addr %08x\n", rec->Data.A.IpAddress ); + ok( !rec->pNext, "next record %p\n", rec->pNext ); + DnsRecordListFree( (DNS_RECORD *)rec, DnsFreeRecordList ); + + recW = (void *)0xdeadbeef; + ret = DnsExtractRecordsFromMessage_W( (DNS_MESSAGE_BUFFER *)msg_winehq, sizeof(msg_winehq), &recW ); + ok( !ret, "failed %u\n", ret ); + ok( recW != NULL, "record not set\n" ); + ok( !wcscmp( recW->pName, L"winehq.org" ), "wrong name %s\n", debugstr_w(recW->pName) ); + DnsRecordListFree( (DNS_RECORD *)recW, DnsFreeRecordList ); + + ret = DnsExtractRecordsFromMessage_UTF8( (DNS_MESSAGE_BUFFER *)msg_invchars, sizeof(msg_invchars), &rec ); + ok( !ret, "failed %u\n", ret ); + ok( !strcmp( rec->pName, "a$.b\\.c..\td" ), "wrong name %s\n", rec->pName ); + DnsRecordListFree( (DNS_RECORD *)rec, DnsFreeRecordList ); + + ret = DnsExtractRecordsFromMessage_UTF8( (DNS_MESSAGE_BUFFER *)msg_root, sizeof(msg_root), &rec ); + ok( !ret, "failed %u\n", ret ); + ok( !strcmp( rec->pName, "." ), "wrong name %s\n", rec->pName ); + DnsRecordListFree( (DNS_RECORD *)rec, DnsFreeRecordList ); + + ret = DnsExtractRecordsFromMessage_UTF8( (DNS_MESSAGE_BUFFER *)msg_label, sizeof(msg_label), &rec ); + ok( ret == DNS_ERROR_BAD_PACKET, "failed %u\n", ret ); + + ret = DnsExtractRecordsFromMessage_UTF8( (DNS_MESSAGE_BUFFER *)msg_loop, sizeof(msg_loop), &rec ); + ok( ret == DNS_ERROR_BAD_PACKET, "failed %u\n", ret ); + + ret = DnsExtractRecordsFromMessage_UTF8( (DNS_MESSAGE_BUFFER *)msg_types, sizeof(msg_types), &rec ); + ok( !ret, "failed %u\n", ret ); + r = rec; + ok( r != NULL, "record not set\n" ); + ok( !strcmp( r->pName, "winehq.org" ), "wrong name %s\n", r->pName ); + ok( r->wType == DNS_TYPE_CNAME, "wrong type %u\n", r->wType ); + ok( !strcmp( r->Data.CNAME.pNameHost, "a.b" ), "wrong cname %s\n", r->Data.CNAME.pNameHost ); + ok( r->wDataLength == sizeof(DNS_PTR_DATAA) + 4, "wrong len %x\n", r->wDataLength ); + r = r->pNext; + ok( r != NULL, "record not set\n" ); + ok( !strcmp( r->pName, "winehq.org" ), "wrong name %s\n", r->pName ); + ok( r->wType == DNS_TYPE_MX, "wrong type %u\n", r->wType ); + ok( r->Data.MX.wPreference == 0x0304, "wrong pref %x\n", r->Data.MX.wPreference ); + ok( !strcmp( r->Data.MX.pNameExchange, "c.d" ), "wrong mx %s\n", r->Data.MX.pNameExchange ); + ok( r->wDataLength == sizeof(DNS_MX_DATAA) + 4, "wrong len %x\n", r->wDataLength ); + r = r->pNext; + ok( r != NULL, "record not set\n" ); + ok( !strcmp( r->pName, "winehq.org" ), "wrong name %s\n", r->pName ); + ok( r->wType == DNS_TYPE_AAAA, "wrong type %u\n", r->wType ); + ok( r->Data.AAAA.Ip6Address.IP6Dword[0] == 0x04030201, "wrong addr %x\n", + r->Data.AAAA.Ip6Address.IP6Dword[0] ); + ok( r->Data.AAAA.Ip6Address.IP6Dword[1] == 0x08070605, "wrong addr %x\n", + r->Data.AAAA.Ip6Address.IP6Dword[1] ); + ok( r->Data.AAAA.Ip6Address.IP6Dword[2] == 0x0c0b0a09, "wrong addr %x\n", + r->Data.AAAA.Ip6Address.IP6Dword[2] ); + ok( r->Data.AAAA.Ip6Address.IP6Dword[3] == 0x000f0e0d, "wrong addr %x\n", + r->Data.AAAA.Ip6Address.IP6Dword[3] ); + ok( r->wDataLength == sizeof(DNS_AAAA_DATA), "wrong len %x\n", r->wDataLength ); + r = r->pNext; + ok( r != NULL, "record not set\n" ); + ok( !strcmp( r->pName, "winehq.org" ), "wrong name %s\n", r->pName ); + ok( r->wType == DNS_TYPE_KEY, "wrong type %u\n", r->wType ); + ok( r->Data.KEY.wFlags == 0x1122, "wrong flags %x\n", r->Data.KEY.wFlags ); + ok( r->Data.KEY.chProtocol == 0x33, "wrong protocol %x\n", r->Data.KEY.chProtocol ); + ok( r->Data.KEY.chAlgorithm == 0x44, "wrong algorithm %x\n", r->Data.KEY.chAlgorithm ); + ok( r->Data.KEY.wKeyLength == 0x02, "wrong len %x\n", r->Data.KEY.wKeyLength ); + ok( r->Data.KEY.Key[0] == 0x55, "wrong key %x\n", r->Data.KEY.Key[0] ); + ok( r->Data.KEY.Key[1] == 0x66, "wrong key %x\n", r->Data.KEY.Key[1] ); + ok( r->wDataLength == offsetof( DNS_KEY_DATA, Key[r->Data.KEY.wKeyLength] ), + "wrong len %x\n", r->wDataLength ); + r = r->pNext; + ok( r != NULL, "record not set\n" ); + ok( !strcmp( r->pName, "t.x" ), "wrong name %s\n", r->pName ); + ok( r->wType == DNS_TYPE_TEXT, "wrong type %u\n", r->wType ); + ok( r->Data.TXT.dwStringCount == 3, "wrong count %u\n", r->Data.TXT.dwStringCount ); + ok( !strcmp( r->Data.TXT.pStringArray[0], "zy" ), "wrong string %s\n", r->Data.TXT.pStringArray[0] ); + ok( !strcmp( r->Data.TXT.pStringArray[1], "" ), "wrong string %s\n", r->Data.TXT.pStringArray[1] ); + ok( !strcmp( r->Data.TXT.pStringArray[2], "XY\xc3\xa9" ), "wrong string %s\n", r->Data.TXT.pStringArray[2] ); + ok( r->wDataLength == offsetof( DNS_TXT_DATAA, pStringArray[r->Data.TXT.dwStringCount] ), + "wrong len %x\n", r->wDataLength ); + ok( !r->pNext, "next record %p\n", r->pNext ); + DnsRecordListFree( (DNS_RECORD *)rec, DnsFreeRecordList ); + + ret = DnsExtractRecordsFromMessage_W( (DNS_MESSAGE_BUFFER *)msg_types, sizeof(msg_types), &recW ); + ok( !ret, "failed %u\n", ret ); + rW = recW; + ok( rW != NULL, "record not set\n" ); + ok( !wcscmp( rW->pName, L"winehq.org" ), "wrong name %s\n", debugstr_w(rW->pName) ); + ok( rW->wType == DNS_TYPE_CNAME, "wrong type %u\n", rW->wType ); + ok( !wcscmp( rW->Data.CNAME.pNameHost, L"a.b" ), "wrong cname %s\n", debugstr_w(rW->Data.CNAME.pNameHost) ); + rW = rW->pNext; + ok( rW != NULL, "record not set\n" ); + ok( !wcscmp( rW->pName, L"winehq.org" ), "wrong name %s\n", debugstr_w(rW->pName) ); + ok( rW->wType == DNS_TYPE_MX, "wrong type %u\n", rW->wType ); + ok( !wcscmp( rW->Data.MX.pNameExchange, L"c.d" ), "wrong mx %s\n", debugstr_w(rW->Data.MX.pNameExchange) ); + rW = rW->pNext; + ok( r != NULL, "record not set\n" ); + ok( !wcscmp( rW->pName, L"winehq.org" ), "wrong name %s\n", debugstr_w(rW->pName) ); + ok( rW->wType == DNS_TYPE_AAAA, "wrong type %u\n", rW->wType ); + rW = rW->pNext; + ok( r != NULL, "record not set\n" ); + ok( !wcscmp( rW->pName, L"winehq.org" ), "wrong name %s\n", debugstr_w(rW->pName) ); + rW = rW->pNext; + ok( r != NULL, "record not set\n" ); + ok( !wcscmp( rW->pName, L"t.x" ), "wrong name %s\n", debugstr_w(rW->pName) ); + ok( rW->wType == DNS_TYPE_TEXT, "wrong type %u\n", rW->wType ); + ok( rW->Data.TXT.dwStringCount == 3, "wrong count %u\n", rW->Data.TXT.dwStringCount ); + ok( !wcscmp( rW->Data.TXT.pStringArray[0], L"zy" ), "wrong string %s\n", debugstr_w(rW->Data.TXT.pStringArray[0]) ); + ok( !wcscmp( rW->Data.TXT.pStringArray[1], L"" ), "wrong string %s\n", debugstr_w(rW->Data.TXT.pStringArray[1]) ); + ok( !wcscmp( rW->Data.TXT.pStringArray[2], L"XY\x00e9" ), "wrong string %s\n", debugstr_w(rW->Data.TXT.pStringArray[2]) ); + ok( !rW->pNext, "next record %p\n", rW->pNext ); + DnsRecordListFree( (DNS_RECORD *)recW, DnsFreeRecordList ); +} + START_TEST(record) { test_DnsRecordCompare(); test_DnsRecordSetCompare(); test_DnsRecordSetDetach(); + test_DnsExtractRecordsFromMessage(); }