/* * Unix interface for libresolv * * Copyright 2021 Hans Leidekker for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #if 0 #pragma makedep unix #endif #include "config.h" #ifdef HAVE_RESOLV #include #include #include #include #include #ifdef HAVE_NETINET_IN_H # include #endif #ifdef HAVE_ARPA_NAMESER_H # include #endif #ifdef HAVE_RESOLV_H # include #endif #ifdef HAVE_NETDB_H # include #endif #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" #include "winternl.h" #include "winbase.h" #include "winnls.h" #include "windns.h" #define USE_WS_PREFIX #include "ws2def.h" #include "ws2ipdef.h" #include "wine/debug.h" #include "wine/heap.h" #include "dnsapi.h" WINE_DEFAULT_DEBUG_CHANNEL(dnsapi); static CPTABLEINFO unix_cptable; static ULONG unix_cp = CP_UTF8; static DWORD WINAPI get_unix_codepage_once( RTL_RUN_ONCE *once, void *param, void **context ) { static const WCHAR wineunixcpW[] = { 'W','I','N','E','U','N','I','X','C','P',0 }; UNICODE_STRING name, value; WCHAR value_buffer[13]; SIZE_T size; void *ptr; RtlInitUnicodeString( &name, wineunixcpW ); value.Buffer = value_buffer; value.MaximumLength = sizeof(value_buffer); if (!RtlQueryEnvironmentVariable_U( NULL, &name, &value )) RtlUnicodeStringToInteger( &value, 10, &unix_cp ); if (unix_cp != CP_UTF8 && !NtGetNlsSectionPtr( 11, unix_cp, NULL, &ptr, &size )) RtlInitCodePageTable( ptr, &unix_cptable ); return TRUE; } static BOOL get_unix_codepage( void ) { static RTL_RUN_ONCE once = RTL_RUN_ONCE_INIT; return !RtlRunOnceExecuteOnce( &once, get_unix_codepage_once, NULL, NULL ); } static DWORD dnsapi_umbstowcs( const char *src, WCHAR *dst, DWORD dstlen ) { DWORD srclen = strlen( src ) + 1; DWORD len; get_unix_codepage(); if (unix_cp == CP_UTF8) { RtlUTF8ToUnicodeN( dst, dstlen, &len, src, srclen ); return len; } else { len = srclen * sizeof(WCHAR); if (dst) RtlCustomCPToUnicodeN( &unix_cptable, dst, dstlen, &len, src, srclen ); return len; } } static const char *debugstr_type( unsigned short type ) { const char *str; switch (type) { #define X(x) case (x): str = #x; break; X(DNS_TYPE_ZERO) X(DNS_TYPE_A) X(DNS_TYPE_NS) X(DNS_TYPE_MD) X(DNS_TYPE_MF) X(DNS_TYPE_CNAME) X(DNS_TYPE_SOA) X(DNS_TYPE_MB) X(DNS_TYPE_MG) X(DNS_TYPE_MR) X(DNS_TYPE_NULL) X(DNS_TYPE_WKS) X(DNS_TYPE_PTR) X(DNS_TYPE_HINFO) X(DNS_TYPE_MINFO) X(DNS_TYPE_MX) X(DNS_TYPE_TEXT) X(DNS_TYPE_RP) X(DNS_TYPE_AFSDB) X(DNS_TYPE_X25) X(DNS_TYPE_ISDN) X(DNS_TYPE_RT) X(DNS_TYPE_NSAP) X(DNS_TYPE_NSAPPTR) X(DNS_TYPE_SIG) X(DNS_TYPE_KEY) X(DNS_TYPE_PX) X(DNS_TYPE_GPOS) X(DNS_TYPE_AAAA) X(DNS_TYPE_LOC) X(DNS_TYPE_NXT) X(DNS_TYPE_EID) X(DNS_TYPE_NIMLOC) X(DNS_TYPE_SRV) X(DNS_TYPE_ATMA) X(DNS_TYPE_NAPTR) X(DNS_TYPE_KX) X(DNS_TYPE_CERT) X(DNS_TYPE_A6) X(DNS_TYPE_DNAME) X(DNS_TYPE_SINK) X(DNS_TYPE_OPT) X(DNS_TYPE_UINFO) X(DNS_TYPE_UID) X(DNS_TYPE_GID) X(DNS_TYPE_UNSPEC) X(DNS_TYPE_ADDRS) X(DNS_TYPE_TKEY) X(DNS_TYPE_TSIG) X(DNS_TYPE_IXFR) X(DNS_TYPE_AXFR) X(DNS_TYPE_MAILB) X(DNS_TYPE_MAILA) X(DNS_TYPE_ANY) X(DNS_TYPE_WINS) X(DNS_TYPE_WINSR) #undef X default: return wine_dbg_sprintf( "0x%04x", type ); } return wine_dbg_sprintf( "%s", str ); } static const char *debugstr_section( 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: return wine_dbg_sprintf( "0x%02x", section ); } } /* call res_init() just once because of a bug in Mac OS X 10.4 */ /* call once per thread on systems that have per-thread _res */ static void init_resolver( void ) { if (!(_res.options & RES_INIT)) res_init(); } static unsigned long map_options( DWORD options ) { unsigned long ret = 0; if (options == DNS_QUERY_STANDARD) return 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 map_error( int error ) { switch (error) { case ns_r_noerror: return ERROR_SUCCESS; case ns_r_formerr: return DNS_ERROR_RCODE_FORMAT_ERROR; case ns_r_servfail: return DNS_ERROR_RCODE_SERVER_FAILURE; case ns_r_nxdomain: return DNS_ERROR_RCODE_NAME_ERROR; case ns_r_notimpl: return DNS_ERROR_RCODE_NOT_IMPLEMENTED; case ns_r_refused: return DNS_ERROR_RCODE_REFUSED; case ns_r_yxdomain: return DNS_ERROR_RCODE_YXDOMAIN; case ns_r_yxrrset: return DNS_ERROR_RCODE_YXRRSET; case ns_r_nxrrset: return DNS_ERROR_RCODE_NXRRSET; case ns_r_notauth: return DNS_ERROR_RCODE_NOTAUTH; case ns_r_notzone: return DNS_ERROR_RCODE_NOTZONE; default: FIXME( "unmapped error code: %d\n", error ); return DNS_ERROR_RCODE_NOT_IMPLEMENTED; } } static DNS_STATUS 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; #ifdef NETDB_INTERNAL case NETDB_INTERNAL: return DNS_ERROR_RCODE; #endif default: FIXME( "unmapped error code: %d\n", error ); return DNS_ERROR_RCODE_NOT_IMPLEMENTED; } } static DNS_STATUS CDECL resolv_get_searchlist( DNS_TXT_DATAW *list, DWORD *len ) { DWORD i, needed, str_needed = 0; char *ptr, *end; init_resolver(); for (i = 0; i < MAXDNSRCH + 1 && _res.dnsrch[i]; i++) str_needed += dnsapi_umbstowcs( _res.dnsrch[i], NULL, 0 ); needed = FIELD_OFFSET(DNS_TXT_DATAW, pStringArray[i]) + str_needed; if (!list || *len < needed) { *len = needed; return !list ? ERROR_SUCCESS : ERROR_MORE_DATA; } *len = needed; list->dwStringCount = i; ptr = (char *)(list->pStringArray + i); end = ptr + str_needed; for (i = 0; i < MAXDNSRCH + 1 && _res.dnsrch[i]; i++) { list->pStringArray[i] = (WCHAR *)ptr; ptr += dnsapi_umbstowcs( _res.dnsrch[i], list->pStringArray[i], end - ptr ); } return ERROR_SUCCESS; } static inline int filter( unsigned short sin_family, USHORT family ) { if (sin_family != AF_INET && sin_family != AF_INET6) return TRUE; if (sin_family == AF_INET6 && family == WS_AF_INET) return TRUE; if (sin_family == AF_INET && family == WS_AF_INET6) return TRUE; return FALSE; } #ifdef HAVE_RES_GETSERVERS static DNS_STATUS CDECL resolv_get_serverlist( USHORT family, DNS_ADDR_ARRAY *addrs, DWORD *len ) { struct __res_state *state = &_res; DWORD i, found, total, needed; union res_sockaddr_union *buf; init_resolver(); total = res_getservers( state, NULL, 0 ); if (!total) return DNS_ERROR_NO_DNS_SERVERS; if (!addrs && family != WS_AF_INET && family != WS_AF_INET6) { *len = FIELD_OFFSET(DNS_ADDR_ARRAY, AddrArray[total]); return ERROR_SUCCESS; } buf = malloc( total * sizeof(union res_sockaddr_union) ); if (!buf) return ERROR_NOT_ENOUGH_MEMORY; total = res_getservers( state, buf, total ); for (i = 0, found = 0; i < total; i++) { if (filter( buf[i].sin.sin_family, family )) continue; found++; } if (!found) return DNS_ERROR_NO_DNS_SERVERS; needed = FIELD_OFFSET(DNS_ADDR_ARRAY, AddrArray[found]); if (!addrs || *len < needed) { *len = needed; return !addrs ? ERROR_SUCCESS : ERROR_MORE_DATA; } *len = needed; memset( addrs, 0, needed ); addrs->AddrCount = addrs->MaxCount = found; for (i = 0, found = 0; i < total; i++) { if (filter( buf[i].sin.sin_family, family )) continue; if (buf[i].sin6.sin6_family == AF_INET6) { SOCKADDR_IN6 *sa = (SOCKADDR_IN6 *)addrs->AddrArray[found].MaxSa; sa->sin6_family = WS_AF_INET6; memcpy( &sa->sin6_addr, &buf[i].sin6.sin6_addr, sizeof(sa->sin6_addr) ); addrs->AddrArray[found].Data.DnsAddrUserDword[0] = sizeof(*sa); } else { SOCKADDR_IN *sa = (SOCKADDR_IN *)addrs->AddrArray[found].MaxSa; sa->sin_family = WS_AF_INET; sa->sin_addr.WS_s_addr = buf[i].sin.sin_addr.s_addr; addrs->AddrArray[found].Data.DnsAddrUserDword[0] = sizeof(*sa); } found++; } free( buf ); return ERROR_SUCCESS; } #else static DNS_STATUS CDECL resolv_get_serverlist( USHORT family, DNS_ADDR_ARRAY *addrs, DWORD *len ) { DWORD needed, found, i; init_resolver(); if (!_res.nscount) return DNS_ERROR_NO_DNS_SERVERS; if (!addrs && family != WS_AF_INET && family != WS_AF_INET6) { *len = FIELD_OFFSET(DNS_ADDR_ARRAY, AddrArray[_res.nscount]); return ERROR_SUCCESS; } for (i = 0, found = 0; i < _res.nscount; i++) { unsigned short sin_family = AF_INET; #ifdef HAVE_STRUCT___RES_STATE__U__EXT_NSCOUNT6 if (_res._u._ext.nsaddrs[i]) sin_family = _res._u._ext.nsaddrs[i]->sin6_family; #endif if (filter( sin_family, family )) continue; found++; } if (!found) return DNS_ERROR_NO_DNS_SERVERS; needed = FIELD_OFFSET(DNS_ADDR_ARRAY, AddrArray[found]); if (!addrs || *len < needed) { *len = needed; return !addrs ? ERROR_SUCCESS : ERROR_MORE_DATA; } *len = needed; memset( addrs, 0, needed ); addrs->AddrCount = addrs->MaxCount = found; for (i = 0, found = 0; i < _res.nscount; i++) { unsigned short sin_family = AF_INET; #ifdef HAVE_STRUCT___RES_STATE__U__EXT_NSCOUNT6 if (_res._u._ext.nsaddrs[i]) sin_family = _res._u._ext.nsaddrs[i]->sin6_family; #endif if (filter( sin_family, family )) continue; #ifdef HAVE_STRUCT___RES_STATE__U__EXT_NSCOUNT6 if (sin_family == AF_INET6) { SOCKADDR_IN6 *sa = (SOCKADDR_IN6 *)addrs->AddrArray[found].MaxSa; sa->sin6_family = WS_AF_INET6; memcpy( &sa->sin6_addr, &_res._u._ext.nsaddrs[i]->sin6_addr, sizeof(sa->sin6_addr) ); addrs->AddrArray[found].Data.DnsAddrUserDword[0] = sizeof(*sa); } else #endif { SOCKADDR_IN *sa = (SOCKADDR_IN *)addrs->AddrArray[found].MaxSa; sa->sin_family = WS_AF_INET; sa->sin_addr.WS_s_addr = _res.nsaddr_list[i].sin_addr.s_addr; addrs->AddrArray[found].Data.DnsAddrUserDword[0] = sizeof(*sa); } found++; } return ERROR_SUCCESS; } #endif static DNS_STATUS CDECL resolv_set_serverlist( const IP4_ARRAY *addrs ) { int i; init_resolver(); if (!addrs || !addrs->AddrCount) return ERROR_SUCCESS; if (addrs->AddrCount > MAXNS) { WARN( "too many servers: %d 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 char *dname_from_msg( ns_msg msg, const unsigned char *pos ) { char *str, dname[NS_MAXDNAME] = "."; /* returns *compressed* length, ignore it */ ns_name_uncompress( ns_msg_base(msg), ns_msg_end(msg), pos, dname, sizeof(dname) ); if ((str = RtlAllocateHeap( GetProcessHeap(), 0, strlen(dname) + 1 ))) strcpy( str, dname ); return str; } static char *str_from_rdata( const unsigned char *rdata ) { char *str; unsigned int len = rdata[0]; if ((str = RtlAllocateHeap( GetProcessHeap(), 0, len + 1 ))) { memcpy( str, ++rdata, len ); str[len] = 0; } return str; } static unsigned int get_record_size( const ns_rr *rr ) { const unsigned char *pos = rr->rdata; unsigned int num = 0, size = sizeof(DNS_RECORDA); switch (rr->type) { case ns_t_key: { pos += sizeof(WORD) + sizeof(BYTE) + sizeof(BYTE); size += rr->rdata + rr->rdlength - pos - 1; break; } case ns_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 ns_t_hinfo: case ns_t_isdn: case ns_t_txt: case ns_t_x25: { while (pos[0] && pos < rr->rdata + rr->rdlength) { num++; pos += pos[0] + 1; } size += (num - 1) * sizeof(PCHAR); break; } case ns_t_null: case ns_t_opt: { size += rr->rdlength - 1; break; } case ns_t_nxt: case ns_t_wks: case 0xff01: /* WINS */ { FIXME( "unhandled type: %s\n", debugstr_type( rr->type ) ); break; } default: break; } return size; } static DNS_STATUS copy_rdata( ns_msg msg, const 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 ns_t_a: { r->Data.A.IpAddress = *(const DWORD *)pos; *dlen = sizeof(DNS_A_DATA); break; } case ns_t_aaaa: { for (i = 0; i < sizeof(IP6_ADDRESS)/sizeof(DWORD); i++) { r->Data.AAAA.Ip6Address.IP6Dword[i] = *(const DWORD *)pos; pos += sizeof(DWORD); } *dlen = sizeof(DNS_AAAA_DATA); break; } case ns_t_key: { /* FIXME: byte order? */ r->Data.KEY.wFlags = *(const WORD *)pos; pos += sizeof(WORD); r->Data.KEY.chProtocol = *pos++; r->Data.KEY.chAlgorithm = *pos++; size = rr->rdata + rr->rdlength - pos; for (i = 0; i < size; i++) r->Data.KEY.Key[i] = *pos++; *dlen = sizeof(DNS_KEY_DATA) + (size - 1) * sizeof(BYTE); break; } case ns_t_rp: case ns_t_minfo: { r->Data.MINFO.pNameMailbox = dname_from_msg( msg, pos ); if (!r->Data.MINFO.pNameMailbox) return ERROR_NOT_ENOUGH_MEMORY; if (ns_name_skip( &pos, ns_msg_end( msg ) ) < 0) return DNS_ERROR_BAD_PACKET; r->Data.MINFO.pNameErrorsMailbox = dname_from_msg( msg, pos ); if (!r->Data.MINFO.pNameErrorsMailbox) { RtlFreeHeap( GetProcessHeap(), 0, r->Data.MINFO.pNameMailbox ); return ERROR_NOT_ENOUGH_MEMORY; } *dlen = sizeof(DNS_MINFO_DATAA); break; } case ns_t_afsdb: case ns_t_rt: case ns_t_mx: { r->Data.MX.wPreference = ntohs( *(const WORD *)pos ); r->Data.MX.pNameExchange = dname_from_msg( msg, pos + sizeof(WORD) ); if (!r->Data.MX.pNameExchange) return ERROR_NOT_ENOUGH_MEMORY; *dlen = sizeof(DNS_MX_DATAA); break; } case ns_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 ns_t_opt: { r->Data.OPT.wDataLength = rr->rdlength; r->Data.OPT.wPad = 0; memcpy( r->Data.OPT.Data, rr->rdata, rr->rdlength ); *dlen = sizeof(DNS_OPT_DATA) + rr->rdlength - 1; break; } case ns_t_cname: case ns_t_ns: case ns_t_mb: case ns_t_md: case ns_t_mf: case ns_t_mg: case ns_t_mr: case ns_t_ptr: { r->Data.PTR.pNameHost = dname_from_msg( msg, pos ); if (!r->Data.PTR.pNameHost) return ERROR_NOT_ENOUGH_MEMORY; *dlen = sizeof(DNS_PTR_DATAA); break; } case ns_t_sig: { r->Data.SIG.pNameSigner = dname_from_msg( msg, pos ); if (!r->Data.SIG.pNameSigner) return ERROR_NOT_ENOUGH_MEMORY; if (ns_name_skip( &pos, ns_msg_end( msg ) ) < 0) return DNS_ERROR_BAD_PACKET; /* FIXME: byte order? */ r->Data.SIG.wTypeCovered = *(const WORD *)pos; pos += sizeof(WORD); r->Data.SIG.chAlgorithm = *pos++; r->Data.SIG.chLabelCount = *pos++; r->Data.SIG.dwOriginalTtl = *(const DWORD *)pos; pos += sizeof(DWORD); r->Data.SIG.dwExpiration = *(const DWORD *)pos; pos += sizeof(DWORD); r->Data.SIG.dwTimeSigned = *(const DWORD *)pos; pos += sizeof(DWORD); r->Data.SIG.wKeyTag = *(const WORD *)pos; size = rr->rdata + rr->rdlength - pos; for (i = 0; i < size; i++) r->Data.SIG.Signature[i] = *pos++; *dlen = sizeof(DNS_SIG_DATAA) + (size - 1) * sizeof(BYTE); break; } case ns_t_soa: { r->Data.SOA.pNamePrimaryServer = dname_from_msg( msg, pos ); if (!r->Data.SOA.pNamePrimaryServer) return ERROR_NOT_ENOUGH_MEMORY; if (ns_name_skip( &pos, ns_msg_end( msg ) ) < 0) return DNS_ERROR_BAD_PACKET; r->Data.SOA.pNameAdministrator = dname_from_msg( msg, pos ); if (!r->Data.SOA.pNameAdministrator) { RtlFreeHeap( GetProcessHeap(), 0, r->Data.SOA.pNamePrimaryServer ); return ERROR_NOT_ENOUGH_MEMORY; } if (ns_name_skip( &pos, ns_msg_end( msg ) ) < 0) return DNS_ERROR_BAD_PACKET; r->Data.SOA.dwSerialNo = ntohl( *(const DWORD *)pos ); pos += sizeof(DWORD); r->Data.SOA.dwRefresh = ntohl( *(const DWORD *)pos ); pos += sizeof(DWORD); r->Data.SOA.dwRetry = ntohl( *(const DWORD *)pos ); pos += sizeof(DWORD); r->Data.SOA.dwExpire = ntohl( *(const DWORD *)pos ); pos += sizeof(DWORD); r->Data.SOA.dwDefaultTtl = ntohl( *(const DWORD *)pos ); pos += sizeof(DWORD); *dlen = sizeof(DNS_SOA_DATAA); break; } case ns_t_srv: { r->Data.SRV.wPriority = ntohs( *(const WORD *)pos ); pos += sizeof(WORD); r->Data.SRV.wWeight = ntohs( *(const WORD *)pos ); pos += sizeof(WORD); r->Data.SRV.wPort = ntohs( *(const WORD *)pos ); pos += sizeof(WORD); r->Data.SRV.pNameTarget = dname_from_msg( msg, pos ); if (!r->Data.SRV.pNameTarget) return ERROR_NOT_ENOUGH_MEMORY; *dlen = sizeof(DNS_SRV_DATAA); break; } case ns_t_hinfo: case ns_t_isdn: case ns_t_x25: case ns_t_txt: { i = 0; while (pos[0] && pos < rr->rdata + rr->rdlength) { r->Data.TXT.pStringArray[i] = str_from_rdata( pos ); if (!r->Data.TXT.pStringArray[i]) { while (i > 0) RtlFreeHeap( GetProcessHeap(), 0, 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 ns_t_atma: case ns_t_loc: case ns_t_nxt: case ns_t_tsig: case ns_t_wks: case 0x00f9: /* TKEY */ case 0xff01: /* WINS */ case 0xff02: /* WINSR */ default: FIXME( "unhandled type: %s\n", debugstr_type( rr->type ) ); return DNS_ERROR_RCODE_NOT_IMPLEMENTED; } return ret; } static inline char *heap_strdup( const char *src ) { char *dst; if (!src) return NULL; if ((dst = RtlAllocateHeap( GetProcessHeap(), 0, (strlen( src ) + 1) * sizeof(char) ))) strcpy( dst, src ); return dst; } static DNS_STATUS 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 (ns_parserr( &msg, section, num, &rr ) < 0) return DNS_ERROR_BAD_PACKET; if (!(record = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, get_record_size( &rr ) ))) return ERROR_NOT_ENOUGH_MEMORY; if (!(record->pName = heap_strdup( rr.name ))) { RtlFreeHeap( GetProcessHeap(), 0, 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 = copy_rdata( msg, &rr, record, &dlen ))) { RtlFreeHeap( GetProcessHeap(), 0, record->pName ); RtlFreeHeap( GetProcessHeap(), 0, record ); return ret; } record->wDataLength = dlen; *recp = record; TRACE( "found %s record in %s section\n", debugstr_type( rr.type ), debugstr_section( section ) ); return ERROR_SUCCESS; } static void free_record_list( DNS_RECORD *list ) { DNS_RECORD *r, *next; unsigned int i; for (r = list; (list = r); r = next) { RtlFreeHeap( GetProcessHeap(), 0, r->pName ); switch (r->wType) { case DNS_TYPE_HINFO: case DNS_TYPE_ISDN: case DNS_TYPE_TEXT: case DNS_TYPE_X25: { for (i = 0; i < r->Data.TXT.dwStringCount; i++) RtlFreeHeap( GetProcessHeap(), 0, r->Data.TXT.pStringArray[i] ); break; } case DNS_TYPE_MINFO: case DNS_TYPE_RP: { RtlFreeHeap( GetProcessHeap(), 0, r->Data.MINFO.pNameMailbox ); RtlFreeHeap( GetProcessHeap(), 0, r->Data.MINFO.pNameErrorsMailbox ); break; } case DNS_TYPE_AFSDB: case DNS_TYPE_RT: case DNS_TYPE_MX: { RtlFreeHeap( GetProcessHeap(), 0, r->Data.MX.pNameExchange ); break; } case DNS_TYPE_NXT: { RtlFreeHeap( GetProcessHeap(), 0, r->Data.NXT.pNameNext ); break; } case DNS_TYPE_CNAME: case DNS_TYPE_MB: case DNS_TYPE_MD: case DNS_TYPE_MF: case DNS_TYPE_MG: case DNS_TYPE_MR: case DNS_TYPE_NS: case DNS_TYPE_PTR: { RtlFreeHeap( GetProcessHeap(), 0, r->Data.PTR.pNameHost ); break; } case DNS_TYPE_SIG: { RtlFreeHeap( GetProcessHeap(), 0, r->Data.SIG.pNameSigner ); break; } case DNS_TYPE_SOA: { RtlFreeHeap( GetProcessHeap(), 0, r->Data.SOA.pNamePrimaryServer ); RtlFreeHeap( GetProcessHeap(), 0, r->Data.SOA.pNameAdministrator ); break; } case DNS_TYPE_SRV: { RtlFreeHeap( GetProcessHeap(), 0, r->Data.SRV.pNameTarget ); break; } default: break; } next = r->pNext; RtlFreeHeap( GetProcessHeap(), 0, r ); } } #define DNS_MAX_PACKET_SIZE 4096 static DNS_STATUS CDECL resolv_query( const char *name, WORD type, DWORD options, DNS_RECORDA **result ) { DNS_STATUS ret = DNS_ERROR_RCODE_NOT_IMPLEMENTED; unsigned int i, num; unsigned char answer[DNS_MAX_PACKET_SIZE]; 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 ); init_resolver(); _res.options |= map_options( options ); if ((len = res_query( name, ns_c_in, type, answer, sizeof(answer) )) < 0) { ret = map_h_errno( h_errno ); goto exit; } if (ns_initparse( answer, len, &msg ) < 0) { ret = DNS_ERROR_BAD_PACKET; goto exit; } #define RCODE_MASK 0x0f if ((msg._flags & RCODE_MASK) != ns_r_noerror) { ret = map_error( msg._flags & RCODE_MASK ); goto exit; } for (i = 0; i < ARRAY_SIZE(sections); i++) { for (num = 0; num < ns_msg_count( msg, sections[i] ); num++) { ret = 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) free_record_list( rrset.pFirstRR ); else *result = (DNS_RECORDA *)rrset.pFirstRR; return ret; } static const struct resolv_funcs funcs = { resolv_get_searchlist, resolv_get_serverlist, resolv_query, resolv_set_serverlist }; NTSTATUS CDECL __wine_init_unix_lib( HMODULE module, DWORD reason, const void *ptr_in, void *ptr_out ) { if (reason != DLL_PROCESS_ATTACH) return STATUS_SUCCESS; *(const struct resolv_funcs **)ptr_out = &funcs; return STATUS_SUCCESS; } #endif /* HAVE_RESOLV */