gethostbyname() returns 具有负地址的结构
gethostbyname() returns a struct with negative address
我正在编写一个程序来检查给定 url 或 ip 上的端口是否打开。为了获得给定 url 的 ip 地址,我使用 gethostbyname()
。当试图查找本地主机的地址时,它 returns 是正确的值,但是,试图查找远程主机的地址通常会失败,并且 returns 一个带有负数的 ip 地址。例如:
/test.out google.com
ip: -40.58.-42.78
/test.out reddit.com
ip: -105.101.-127.-116
./test.out facebook.com
ip: 31.13.84.36
奇怪的是,最后一个有效。这是我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
int main(int argc, char **argv)
{
struct hostent *he;
struct in_addr **addr_list;
if ((he = gethostbyname(argv[1])) == NULL) {
herror("gethostbyname");
return 1;
}
printf("ip: ");
for (int i = 0; i < he->h_length; i++) {
printf("%d", he->h_addr_list[0][i]);
if (i != he->h_length - 1) printf(".");
}
printf("\n");
}
还有,为什么h_addr_list
的类型是char **
?它不应该是一个整数,或者更好的是一个无符号整数吗?
IP 地址的组成部分是无符号字节,但在 struct hostent
中它们存储为 char
(即已签名)。这意味着值 128
..255
被解释为负数。
您使用 %d
格式打印它们,该格式将值打印为带符号的,因为这是它接收它们的方式。当您将值传递给 printf()
:
时,将值转换为 unsigned char
(或 unsigned int
,如果您愿意)
printf("%d", (unsigned char)he->h_addr_list[0][i]);
您也可以使用 %u
而不是 %d
(它将接收到的值视为 unsigned
),但您仍然需要将传递给它的值转换为 unsigned
1:
printf("%u", (unsigned char)he->h_addr_list[0][i]);
另一种选择是强制 printf()
仅使用它获取的值的最低有效字节并将其打印为无符号,使用 "%hhu"
。然而,这看起来更像是一个 hack 而不是正确的解决方案。
1 没有转换,因为 printf()
是一个 variadic function,作为参数传递给它的值 (he->h_addr_list[0][i]
) 被提升从 (signed) char
到 (signed) int
。对于大于 127
.
的组件,使用 "%u"
打印它们会产生非常大的数字而不是负数
这个答案专门针对 h_addr_list
的类型。
在发明gethostbyname
的古代BSD程序员的心目中,它将被用来查找各种网络地址,而不仅仅是IP地址。这就是为什么它也有一个 h_addrtype
。 h_addr_list[n]
的解释将取决于 h_addrtype
。对于 h_addrtype==AF_INET
,地址是您熟悉的 4 字节 IP 地址格式。对于其他地址类型,它可能是其他东西。
h_addr_list
的char **
类型应该理解为"dynamically allocated array of opaque buffers"。它可能是 void **
但 void
尚未发明。
事实证明,IPv4 成为唯一有人关心的网络协议,直到 IPv6 出现,然后才决定完全取代主机查找接口(参见 getaddrinfo
)。所以很少有机会在野外看到非AF_INET
h_addrtype
。
我正在编写一个程序来检查给定 url 或 ip 上的端口是否打开。为了获得给定 url 的 ip 地址,我使用 gethostbyname()
。当试图查找本地主机的地址时,它 returns 是正确的值,但是,试图查找远程主机的地址通常会失败,并且 returns 一个带有负数的 ip 地址。例如:
/test.out google.com
ip: -40.58.-42.78
/test.out reddit.com
ip: -105.101.-127.-116
./test.out facebook.com
ip: 31.13.84.36
奇怪的是,最后一个有效。这是我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
int main(int argc, char **argv)
{
struct hostent *he;
struct in_addr **addr_list;
if ((he = gethostbyname(argv[1])) == NULL) {
herror("gethostbyname");
return 1;
}
printf("ip: ");
for (int i = 0; i < he->h_length; i++) {
printf("%d", he->h_addr_list[0][i]);
if (i != he->h_length - 1) printf(".");
}
printf("\n");
}
还有,为什么h_addr_list
的类型是char **
?它不应该是一个整数,或者更好的是一个无符号整数吗?
IP 地址的组成部分是无符号字节,但在 struct hostent
中它们存储为 char
(即已签名)。这意味着值 128
..255
被解释为负数。
您使用 %d
格式打印它们,该格式将值打印为带符号的,因为这是它接收它们的方式。当您将值传递给 printf()
:
unsigned char
(或 unsigned int
,如果您愿意)
printf("%d", (unsigned char)he->h_addr_list[0][i]);
您也可以使用 %u
而不是 %d
(它将接收到的值视为 unsigned
),但您仍然需要将传递给它的值转换为 unsigned
1:
printf("%u", (unsigned char)he->h_addr_list[0][i]);
另一种选择是强制 printf()
仅使用它获取的值的最低有效字节并将其打印为无符号,使用 "%hhu"
。然而,这看起来更像是一个 hack 而不是正确的解决方案。
1 没有转换,因为 printf()
是一个 variadic function,作为参数传递给它的值 (he->h_addr_list[0][i]
) 被提升从 (signed) char
到 (signed) int
。对于大于 127
.
"%u"
打印它们会产生非常大的数字而不是负数
这个答案专门针对 h_addr_list
的类型。
在发明gethostbyname
的古代BSD程序员的心目中,它将被用来查找各种网络地址,而不仅仅是IP地址。这就是为什么它也有一个 h_addrtype
。 h_addr_list[n]
的解释将取决于 h_addrtype
。对于 h_addrtype==AF_INET
,地址是您熟悉的 4 字节 IP 地址格式。对于其他地址类型,它可能是其他东西。
h_addr_list
的char **
类型应该理解为"dynamically allocated array of opaque buffers"。它可能是 void **
但 void
尚未发明。
事实证明,IPv4 成为唯一有人关心的网络协议,直到 IPv6 出现,然后才决定完全取代主机查找接口(参见 getaddrinfo
)。所以很少有机会在野外看到非AF_INET
h_addrtype
。