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),但您仍然需要将传递给它的值转换为 unsigned1:

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_addrtypeh_addr_list[n] 的解释将取决于 h_addrtype。对于 h_addrtype==AF_INET,地址是您熟悉的 4 字节 IP 地址格式。对于其他地址类型,它可能是其他东西。

h_addr_listchar **类型应该理解为"dynamically allocated array of opaque buffers"。它可能是 void **void 尚未发明。

事实证明,IPv4 成为唯一有人关心的网络协议,直到 IPv6 出现,然后才决定完全取代主机查找接口(参见 getaddrinfo)。所以很少有机会在野外看到非AF_INET h_addrtype