GetAddrInfoExW调用异常:仅获取IPv4地址,无法返回IPv6地址
GetAddrInfoExW仅返回IPv4地址的问题排查与解决
问题概述
使用GetAddrInfoExW函数解析主机名(如google.com)时,指定hints.ai_family = AF_UNSPEC但仅返回IPv4地址;指定AF_INET6时查询失败,错误码11004。nslookup可正常获取IPv4和IPv6地址。
相关代码片段
#include <iostream> #include <winsock2.h> #include <ws2tcpip.h> #include <thread> #include <atomic> #pragma comment(lib, "ws2_32.lib") #define MAX_ADDRESS_STRING_LENGTH 64 struct DNS_QUERY_CONTEXT { OVERLAPPED uOverlapped; PADDRINFOEX uResult; HANDLE uCancelOpsHandle; }; bool InitWinsock() { WSADATA wsaData; return WSAStartup(MAKEWORD(2, 2), &wsaData) == 0; } void CALLBACK LookupServiceCompletionRoutine(DWORD dwError, DWORD dwBytes, LPWSAOVERLAPPED lpOverlapped) { if (dwError != 0) { std::cout << "Failed to resolve DNS " << dwError; return; } DNS_QUERY_CONTEXT * context = (DNS_QUERY_CONTEXT *) lpOverlapped; ADDRINFOEX * ptr = context->uResult; if (ptr == NULL) { std::cout << "No results found\n"; return; } int i = 1; wchar_t ipstringbuffer [128]; while (ptr != NULL) { std::cout << "GetAddrInfoEx response " << i++ << std::endl; std::cout << " Flags: " << ptr->ai_flags << std::endl; std::cout << " Family: "; switch (ptr->ai_family) { case AF_UNSPEC: std::cout << "UNSPEC (Unspecified)" << std::endl; break; case AF_INET: { std::cout << "AF_INET (IPv4)" << std::endl; struct sockaddr_in * sockaddr_ipv4 = (struct sockaddr_in *) ptr->ai_addr; InetNtop(AF_INET, &(sockaddr_ipv4->sin_addr), ipstringbuffer, INET_ADDRSTRLEN); std::wcout << L" IPv4 Address: " << ipstringbuffer << std::endl; break; } case AF_INET6: { // IPv6 std::cout << "AF_INET6 (IPv6)" << std::endl; struct sockaddr_in6 * sockaddr_ipv6 = (struct sockaddr_in6 *) ptr->ai_addr; InetNtop(AF_INET6, &(sockaddr_ipv6->sin6_addr), ipstringbuffer, INET6_ADDRSTRLEN); std::wcout << L" IPv6 Address: " << ipstringbuffer << std::endl; break; } default: std::cout << "Unknown Family: " << ptr->ai_family << std::endl; break; } ptr = ptr->ai_next; } FreeAddrInfoEx(context->uResult); delete context; } int main() { DNS_QUERY_CONTEXT * context; ADDRINFOEXW hints; const wchar_t * query = L"google.com"; int error; ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_UNSPEC; // IPv4 or IPv6 hints.ai_socktype = 0; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; context = new DNS_QUERY_CONTEXT; ZeroMemory(context, sizeof(DNS_QUERY_CONTEXT)); if (!InitWinsock()) { std::cerr << "WSAStartup failed" << std::endl; return 1; } context->uOverlapped.hEvent = NULL; error = GetAddrInfoExW( query, // Hostname to resolve NULL, // Service name NS_DNS, // Namespace NULL, // Query context &hints, // Hints &context->uResult, // Result NULL, // Timeout &context->uOverlapped, // Overlapped structure LookupServiceCompletionRoutine, // Completion routine &context->uCancelOpsHandle // Cancel handle ); if (error != WSA_IO_PENDING) { std::cerr << "GetAddrInfoExW failed: " << WSAGetLastError() << std::endl; delete context; WSACleanup(); return 1; } std::cout << "DNS query initiated, waiting for results..." << std::endl; while (true); WSACleanup(); return 0; }
原因分析
- hints结构参数缺失:默认情况下,设置
AF_UNSPEC时,GetAddrInfoExW可能优先返回系统倾向的地址族(如IPv4),不会主动返回所有可用地址类型,需显式设置标志强制获取全量结果。 - 错误码11004含义:该错误为
WSANO_DATA,表示请求名称有效但无对应数据记录。指定AF_INET6时触发此错误,多因缺少必要标志导致解析逻辑未正确请求IPv6记录。 - 系统解析策略影响:部分系统配置了IPv4优先的解析策略,即使DNS存在IPv6记录,
GetAddrInfoExW也仅返回IPv4结果,除非显式要求。
解决方案
1. 修改hints结构的ai_flags参数
在初始化hints时添加AI_ALL | AI_V4MAPPED标志,强制返回所有地址族结果:
ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = 0; hints.ai_protocol = 0; hints.ai_flags = AI_ALL | AI_V4MAPPED; // 新增标志 hints.ai_addrlen = 0; hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL;
AI_ALL:强制返回所有匹配的IPv4和IPv6地址AI_V4MAPPED:当IPv6地址不可用时,返回IPv4映射的IPv6地址(可选,按需调整)
2. 优化GetAddrInfoExW调用参数
添加超时参数,避免因超时导致仅返回部分结果:
struct timeval timeout; timeout.tv_sec = 5; timeout.tv_usec = 0; error = GetAddrInfoExW( query, NULL, NS_DNS, NULL, &hints, &context->uResult, &timeout, // 传入超时参数 &context->uOverlapped, LookupServiceCompletionRoutine, &context->uCancelOpsHandle );
3. 验证系统IPv6配置
- 确认网络适配器已启用IPv6
- 通过
nslookup -type=AAAA google.com验证DNS服务器可返回IPv6记录
4. 调整系统解析优先级(可选)
Windows系统中可通过以下命令提升IPv6解析优先级:
netsh interface ipv6 set prefixpolicy ::/0 50 0
内容的提问来源于stack exchange,提问作者Harshal Goyal
相关产品推荐
相关产品推荐

