Signed-off-by: Huw Davies huw@codeweavers.com --- dlls/dnsapi/libresolv.c | 131 +++++++++++++++++++++++++++++++++++--- dlls/dnsapi/query.c | 4 ++ dlls/dnsapi/tests/query.c | 30 ++++++++- 3 files changed, 154 insertions(+), 11 deletions(-)
diff --git a/dlls/dnsapi/libresolv.c b/dlls/dnsapi/libresolv.c index 7e754d73af3..b0374927b6d 100644 --- a/dlls/dnsapi/libresolv.c +++ b/dlls/dnsapi/libresolv.c @@ -28,6 +28,7 @@ #include <stdarg.h> #include <string.h> #include <stdio.h> +#include <stdlib.h> #include <sys/types.h>
#ifdef HAVE_NETINET_IN_H @@ -228,35 +229,147 @@ static DNS_STATUS map_h_errno( int error ) } }
+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 + DNS_STATUS CDECL resolv_get_serverlist( USHORT family, DNS_ADDR_ARRAY *addrs, DWORD *len ) { - DWORD needed, i; + struct __res_state *state = &_res; + DWORD i, found, total, needed; + union res_sockaddr_union *buf;
init_resolver();
- if (family != WS_AF_INET) return ERROR_NOT_SUPPORTED; + total = res_getservers( state, NULL, 0 ); + if (!total) return DNS_ERROR_NO_DNS_SERVERS;
- needed = FIELD_OFFSET(DNS_ADDR_ARRAY, AddrArray[_res.nscount]); + 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 + +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_INSUFFICIENT_BUFFER; + } *len = needed; memset( addrs, 0, needed ); - addrs->AddrCount = addrs->MaxCount = _res.nscount; + addrs->AddrCount = addrs->MaxCount = found;
- for (i = 0; i < _res.nscount; i++) + for (i = 0, found = 0; i < _res.nscount; i++) { - SOCKADDR_INET *inet = (SOCKADDR_INET *)addrs->AddrArray[i].MaxSa; - inet->Ipv4.sin_family = WS_AF_INET; - inet->Ipv4.sin_addr.WS_s_addr = _res.nsaddr_list[i].sin_addr.s_addr; - addrs->AddrArray[i].Data.DnsAddrUserDword[0] = sizeof(SOCKADDR_IN); + 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
DNS_STATUS CDECL resolv_set_serverlist( const IP4_ARRAY *addrs ) { diff --git a/dlls/dnsapi/query.c b/dlls/dnsapi/query.c index b836cdaaf48..9900166aeb8 100644 --- a/dlls/dnsapi/query.c +++ b/dlls/dnsapi/query.c @@ -351,8 +351,12 @@ DNS_STATUS WINAPI DnsQueryConfig( DNS_CONFIG_TYPE config, DWORD flag, PCWSTR ada FIXME( "unimplemented config type %d\n", config ); break;
+ case DnsConfigDnsServersUnspec: + return resolv_funcs->get_serverlist( AF_UNSPEC, buffer, len ); case DnsConfigDnsServersIpv4: return resolv_funcs->get_serverlist( AF_INET, buffer, len ); + case DnsConfigDnsServersIpv6: + return resolv_funcs->get_serverlist( AF_INET6, buffer, len );
default: WARN( "unknown config type: %d\n", config ); diff --git a/dlls/dnsapi/tests/query.c b/dlls/dnsapi/tests/query.c index d11a53b5306..5fb47d89782 100644 --- a/dlls/dnsapi/tests/query.c +++ b/dlls/dnsapi/tests/query.c @@ -138,10 +138,10 @@ static IP_ADAPTER_ADDRESSES *get_adapters(void)
static void test_DnsQueryConfig( void ) { - DWORD err, size, i; + DWORD err, size, i, ipv6_count; WCHAR name[MAX_ADAPTER_NAME_LENGTH + 1]; IP_ADAPTER_ADDRESSES *adapters, *ptr; - DNS_ADDR_ARRAY *ipv4; + DNS_ADDR_ARRAY *ipv4, *ipv6, *unspec; IP4_ARRAY *ip4_array;
if (!(adapters = get_adapters())) return; @@ -186,7 +186,33 @@ static void test_DnsQueryConfig( void ) ok( ipv4->AddrArray[i].Data.DnsAddrUserDword[0] == sizeof(*sa), "got %d\n", ipv4->AddrArray[i].Data.DnsAddrUserDword[0] ); } + + size = 0; + err = DnsQueryConfig( DnsConfigDnsServersIpv6, 0, name, NULL, NULL, &size ); + ok( !err || err == DNS_ERROR_NO_DNS_SERVERS, "got %d\n", err ); + ipv6_count = 0; + ipv6 = NULL; + if (!err) + { + ipv6 = malloc( size ); + err = DnsQueryConfig( DnsConfigDnsServersIpv6, 0, name, NULL, ipv6, &size ); + ok( !err, "got %d\n", err ); + ipv6_count = ipv6->AddrCount; + } + + size = 0; + err = DnsQueryConfig( DnsConfigDnsServersUnspec, 0, name, NULL, NULL, &size ); + ok( !err, "got %d\n", err ); + unspec = malloc( size ); + err = DnsQueryConfig( DnsConfigDnsServersUnspec, 0, name, NULL, unspec, &size ); + ok( !err, "got %d\n", err ); + + ok( unspec->AddrCount == ipv4->AddrCount + ipv6_count, "got %d vs %d + %d\n", + unspec->AddrCount, ipv4->AddrCount, ipv6_count ); + free( ip4_array ); + free( unspec ); + free( ipv6 ); free( ipv4 ); }