Note: I looked extensively at a similar (but not duplicate) question here: GetAddrInfo cannot resolve ipv6.google.com (but nslookup can). This question is 9 years old, the OP is not experiencing the exact same behavior as I am, and the already-accepted answer does not solve my problem—in fact, I am specifically following the advice of that answer to no avail in the sample code below, and I'm getting a different error than on that question. Furthermore, the likelihood that I would succeed in getting anyone to engage on a question that old by merely commenting on it is nil. It is not the same problem as the problem I am having today, so I am created a new, more-detailed question.
I have a Windows 10 machine running Visual Studio 2017 and a Windows Server 2016 machine running Visual Studio 2019. Both of the machines have IPv4 and IPv6 addresses configured. On both machines, nslookup.exe returns the expected results for three different domains:
> nslookup www.google.com
...
Addresses: 2607:f8b0:4002:80a::2004
172.217.3.228
...
> nslookup ipv4.google.com
...
Addresses: 74.125.136.102
...
> nslookup ipv6.google.com
...
Addresses: 2607:f8b0:4002:812::200e
...
Now I'm trying to write an example program that uses GetAddrInfo to lookup these same three domains:
#include <iostream>
#include <sstream>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib,"WS2_32.Lib")
int main(int argc, char* argv[])
{
WORD wVersionRequested = MAKEWORD(2, 2);
WSADATA wsaData;
int err( WSAStartup(wVersionRequested, &wsaData) );
if (err != 0)
{
std::cout << "WSAStartup failed with error " << err << std::endl << std::flush;
return 1;
}
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM; // so that we get each IP address once, instead of once per socket type
hints.ai_protocol = 0; // we don't care about the protocol
hints.ai_canonname = NULL; // should be NULL for hints
hints.ai_addr = NULL; // should be NULL for hints
hints.ai_next = NULL; // should be NULL for hints
hints.ai_flags = 0;
hints.ai_family = AF_UNSPEC; // I vary this and the hostname below
struct addrinfo* results;
int error = getaddrinfo("ipv6.google.com", NULL, &hints, &results);
if (error != 0)
{
std::ostringstream msg;
std::cout << "Could not resolve hostname due to ";
std::cout << "the following error in getaddrinfo: " << error << ": ";
if (error == -11)
{
std::cout << "EAI_SYSTEM";
}
else
{
std::cout << gai_strerrorA(error);
}
std::cout << std::endl << std::flush;
return 1;
}
for (struct addrinfo* result = results; result != NULL; result = result->ai_next)
{
if (result->ai_family == AF_INET6)
{
char buffer[INET6_ADDRSTRLEN];
std::string ipv6Str(
inet_ntop(
AF_INET6,
&((struct sockaddr_in6*)result->ai_addr)->sin6_addr,
buffer,
sizeof(buffer)));
std::cout << ipv6Str << std::endl << std::flush;
}
else
{
char buffer[INET_ADDRSTRLEN];
std::string ipv4Str(
inet_ntop(
AF_INET,
&((struct sockaddr_in*)result->ai_addr)->sin_addr,
buffer,
sizeof(buffer)));
std::cout << ipv4Str << std::endl << std::flush;
}
}
freeaddrinfo(results);
return 0;
}
For simplicity, I'm just changing the hints.ai_family and hard-coded hostname, then re-compiling and re-running (with Visual Studio). These are the results I get:
| Hostname | hints.ai_family | Result | Expected? |
|---|---|---|---|
| www.google.com | AF_UNSPEC |
One IPv4 and one IPv6 address | ✅ |
| www.google.com | AF_INET |
One IPv4 address only | ✅ |
| www.google.com | AF_INET6 |
Err 11004: Name valid, no data | ⛔️ |
| ipv4.google.com | AF_UNSPEC |
One IPv4 address only | ✅ |
| ipv4.google.com | AF_INET |
One IPv4 address only | ✅ |
| ipv4.google.com | AF_INET6 |
Err 11004: Name valid, no data | ✅ |
| ipv6.google.com | AF_UNSPEC |
Err 11004: Name valid, no data | ⛔️ |
| ipv6.google.com | AF_INET |
Err 11004: Name valid, no data | ✅ |
| ipv6.google.com | AF_INET6 |
Err 11004: Name valid, no data | ⛔️ |
I've hammered at this for a full day now and I can't get getaddrinfo to return the expected results. Why isn't this working? It appears that nslookup.exe using something other than getaddrinfo (perhaps a more low-level query of root and glue name servers), because it works fine there. Also note that this code (with different includes and without the WSA startup procedure) is working just fine in RedHat, CentOS, Ubuntu, and macOS High Sierra.
Some things I've tried other than varying the ai_family, all of which yield the same results as in the table below:
ai_flags = AI_NUMERICSERV
ai_flags = AI_ALL
ai_flags = AI_ALL | AI_NUMERICSERV
ai_socktype = SOCK_DGRAM
ai_socktype = 0
ai_protocol = IPPROTO_TCP
Also, it turns out that ipv6.google.com is actually a CNAME for ipv6.l.google.com, so I tried using that domain, instead, to no avail.