IPv6 的 getaddrinfo() 没有意义
getaddrinfo() with IPv6 not making sense
我不明白为什么 getaddrinfo
不是 return 有效的 IPv6 地址。
在我的系统上,下面的代码正在打印 22:B8:00:00:00:00:00:00:00:00:00:00:00:00
,但我预计某处会出现 01
,因为 localhost
应该解析为 ::1
。
同时,sa_data
只有14个字节,而IPv6地址是16个字节,所以似乎最后几个字节总是被砍掉,函数可以't return 一个 IPv6 地址?
谁能解释一下这是怎么回事?我该如何在 IPv6 上使用此功能?
#include <stdio.h>
#include <WinSock2.h>
#include <WS2TCPIP.h>
#pragma comment(lib, "WS2_32")
int main(int argc, char *argv[])
{
WSADATA wsadata;
WSAStartup(0x0002, &wsadata);
addrinfo addr_hints = { 0, PF_INET6, SOCK_DGRAM, IPPROTO_UDP }, *addrs_out;
getaddrinfo("localhost", "8888", &addr_hints, &addrs_out);
fprintf(stderr,
"%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 0]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 1]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 2]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 3]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 4]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 5]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 6]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 7]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 8]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 9]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[10]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[11]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[12]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[13]));
freeaddrinfo(addrs_out);
return 0;
}
sockaddr
结构定义供参考:
struct sockaddr {
ushort sa_family;
char sa_data[14];
};
struct sockaddr_in6 {
short sin6_family;
u_short sin6_port;
u_long sin6_flowinfo;
struct in6_addr sin6_addr;
u_long sin6_scope_id;
};
当ai_family == AF_INET6
ai_addr
实际指向一个struct sockaddr_in6
。您要打印的前几个字节是 sin6_port
和 sin6_flowinfo
。 IPv6 地址在后面。
编辑添加:
您可以直接将 ai_addr
与 bind()
和 getnameinfo()
等函数一起使用。您通常不需要深入研究结构定义细节。例如,我会使用 getnameinfo()
和 NI_NUMERICHOST
来获取可打印的地址。
sockaddr are not strictly interpreted to be pointers to a
sockaddr structure. The structure is interpreted differently in the
context of different address families.
所以我们需要首先检查 sockaddr
or ai_family
from addrinfo
(in this base it must be equal) and based on this need "reinterpret" pointer from sockaddr
的 sa_family
(这就像 void*
指针)到真实结构。说为此使用联合:
addrinfo *addrs_out, *addr;
if (getaddrinfo("localhost", "8888", 0, &addrs_out) == NOERROR)
{
addr = addrs_out;
CHAR buf[256], *sz, srv[128];
ULONG n;
PUCHAR Byte;
do
{
union {
sockaddr* ai_addr;
SOCKADDR_IN* pa;
SOCKADDR_IN6* pa6;
};
ai_addr = addr->ai_addr;
if (addr->ai_family != ai_addr->sa_family)
{
__debugbreak();
}
switch (addr->ai_family)
{
case AF_INET6:
Byte = pa6->sin6_addr.u.Byte, n = RTL_NUMBER_OF(pa6->sin6_addr.u.Byte), sz = buf;
do
{
sz += sprintf(sz, "%02X:", *Byte++);
} while (--n);
sz[-1] = 0;
DbgPrint("AF_INET6: %s\n", buf);
break;
case AF_INET:
if (0 <= RtlIpv4AddressToStringExA(&pa->sin_addr.S_un.S_addr, pa->sin_port, buf, &(n = RTL_NUMBER_OF(buf))))
{
DbgPrint("AF_INET: %s\n", buf);
}
break;
}
// alt print
if (getnameinfo(ai_addr, (socklen_t)addr->ai_addrlen, buf, RTL_NUMBER_OF(buf), srv, RTL_NUMBER_OF(srv), NI_NUMERICHOST ) == NOERROR)
{
DbgPrint("%s:%s\n", buf, srv);
}
} while (addr = addr->ai_next);
freeaddrinfo(addrs_out);
}
我不明白为什么 getaddrinfo
不是 return 有效的 IPv6 地址。
在我的系统上,下面的代码正在打印 22:B8:00:00:00:00:00:00:00:00:00:00:00:00
,但我预计某处会出现 01
,因为 localhost
应该解析为 ::1
。
同时,sa_data
只有14个字节,而IPv6地址是16个字节,所以似乎最后几个字节总是被砍掉,函数可以't return 一个 IPv6 地址?
谁能解释一下这是怎么回事?我该如何在 IPv6 上使用此功能?
#include <stdio.h>
#include <WinSock2.h>
#include <WS2TCPIP.h>
#pragma comment(lib, "WS2_32")
int main(int argc, char *argv[])
{
WSADATA wsadata;
WSAStartup(0x0002, &wsadata);
addrinfo addr_hints = { 0, PF_INET6, SOCK_DGRAM, IPPROTO_UDP }, *addrs_out;
getaddrinfo("localhost", "8888", &addr_hints, &addrs_out);
fprintf(stderr,
"%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 0]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 1]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 2]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 3]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 4]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 5]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 6]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 7]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 8]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 9]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[10]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[11]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[12]),
static_cast<unsigned char>(addrs_out->ai_addr->sa_data[13]));
freeaddrinfo(addrs_out);
return 0;
}
sockaddr
结构定义供参考:
struct sockaddr {
ushort sa_family;
char sa_data[14];
};
struct sockaddr_in6 {
short sin6_family;
u_short sin6_port;
u_long sin6_flowinfo;
struct in6_addr sin6_addr;
u_long sin6_scope_id;
};
当ai_family == AF_INET6
ai_addr
实际指向一个struct sockaddr_in6
。您要打印的前几个字节是 sin6_port
和 sin6_flowinfo
。 IPv6 地址在后面。
编辑添加:
您可以直接将 ai_addr
与 bind()
和 getnameinfo()
等函数一起使用。您通常不需要深入研究结构定义细节。例如,我会使用 getnameinfo()
和 NI_NUMERICHOST
来获取可打印的地址。
sockaddr are not strictly interpreted to be pointers to a sockaddr structure. The structure is interpreted differently in the context of different address families.
所以我们需要首先检查 sockaddr
or ai_family
from addrinfo
(in this base it must be equal) and based on this need "reinterpret" pointer from sockaddr
的 sa_family
(这就像 void*
指针)到真实结构。说为此使用联合:
addrinfo *addrs_out, *addr;
if (getaddrinfo("localhost", "8888", 0, &addrs_out) == NOERROR)
{
addr = addrs_out;
CHAR buf[256], *sz, srv[128];
ULONG n;
PUCHAR Byte;
do
{
union {
sockaddr* ai_addr;
SOCKADDR_IN* pa;
SOCKADDR_IN6* pa6;
};
ai_addr = addr->ai_addr;
if (addr->ai_family != ai_addr->sa_family)
{
__debugbreak();
}
switch (addr->ai_family)
{
case AF_INET6:
Byte = pa6->sin6_addr.u.Byte, n = RTL_NUMBER_OF(pa6->sin6_addr.u.Byte), sz = buf;
do
{
sz += sprintf(sz, "%02X:", *Byte++);
} while (--n);
sz[-1] = 0;
DbgPrint("AF_INET6: %s\n", buf);
break;
case AF_INET:
if (0 <= RtlIpv4AddressToStringExA(&pa->sin_addr.S_un.S_addr, pa->sin_port, buf, &(n = RTL_NUMBER_OF(buf))))
{
DbgPrint("AF_INET: %s\n", buf);
}
break;
}
// alt print
if (getnameinfo(ai_addr, (socklen_t)addr->ai_addrlen, buf, RTL_NUMBER_OF(buf), srv, RTL_NUMBER_OF(srv), NI_NUMERICHOST ) == NOERROR)
{
DbgPrint("%s:%s\n", buf, srv);
}
} while (addr = addr->ai_next);
freeaddrinfo(addrs_out);
}