我正在编写一个程序来检查给定网址或IP上的端口是否打开。为了获得给定网址的IP地址,我使用gethostbyname()
。当尝试查找localhost的地址时,它返回正确的值,但是,尝试查找它通常失败的远程主机的地址并返回带有负数的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 **
的类型?不应该是整数,还是更好的无符号?
答案 0 :(得分:4)
IP地址的组成部分是无符号字节,但在struct hostent
中,它们存储为char
(即已签名)。这意味着值128
.. 255
被解释为负数。
您使用%d
格式打印它们,这些格式将值打印为已签名,因为这是它接收它们的方式。将值传递给unsigned char
时,将值转换为unsigned int
(或printf()
,如果您愿意):
printf("%d", (unsigned char)he->h_addr_list[0][i]);
您也可以使用%u
代替%d
(它将其收到的值视为unsigned
),但您仍需要转换值以传递给{{1} }} 1 :
unsigned
另一种选择是强制printf("%u", (unsigned char)he->h_addr_list[0][i]);
仅使用它获得的值的最低有效字节,并使用printf()
将其打印为无符号。但是,这看起来更像是一个黑客,而不是正确的解决方案。
1 没有转换,因为"%hhu"
是variadic function,从printf()
提升作为参数(he->h_addr_list[0][i]
)传递给它的值到(signed) char
。使用(signed) int
打印它们会为大于"%u"
的组件生成非常大的数字而不是负数。
答案 1 :(得分:2)
这个答案特别针对h_addr_list
的类型。
在发明gethostbyname
的古代BSD程序员的脑海中,它将用于查找各种网络地址,而不仅仅是IP地址。这就是为什么它也有h_addrtype
的原因。 h_addr_list[n]
的解释取决于h_addrtype
。对于h_addrtype==AF_INET
,地址是您熟悉的4字节IP地址格式。对于其他地址类型,可能是其他类型。
char **
类型的h_addr_list
应理解为&#34;动态分配的不透明缓冲区数组&#34;。可能是void **
但void
尚未发明。
事实证明,IPv4成为唯一关注的网络协议,直到IPv6出现,然后决定完全替换主机查找接口(参见getaddrinfo
)。因此,在野外看到非AF_INET
h_addrtype
的机会很少。