【问题标题】:BSD getaddrinfo() endiannessBSD getaddrinfo() 字节序
【发布时间】:2017-07-24 12:56:13
【问题描述】:

我正在尝试熟悉网络 BSD API。我理解提交给 BSD API 的多字节数据(如地址和端口)必须按网络字节顺序排列,我们应该使用 htons() 和 htonl() 之类的转换函数来做到这一点。这段代码的 sn-p 表明:

#define IP_ADDRESS(a, b, c, d)   ((((uint32_t)a) << 24) | (((uint32_t)b) << 16) | (((uint32_t)c) << 8) | ((uint32_t)d))

/* Set server port in BSD format */
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(IP_ADDRESS(192,168,1,1));
serverAddr.sin_port = htons(21);

/* Connect to server */
status = connect(H->bsdSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr));

现在我正在尝试使用 getaddrinfo() 来从服务器名称中检索地址信息。我尝试了以下代码:

memset( &hints, 0, sizeof( hints ) );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;

getaddrinfo( "srvdc01", "21", &hints, &addr_list );

if (addr_list != NULL)
{
    ctx->fd = (int) socket( addr_list->ai_family, addr_list->ai_socktype,
                        addr_list->ai_protocol );
    if (ctx->fd >= 0)
        status = connect( ctx->fd, addr_list->ai_addr, addr_list->ai_addrlen );
}

我原以为 getaddrinfo() 返回的地址会按照网络字节顺序排列,这样就可以直接将其提供给 connect() 函数。不幸的是,这不适用于我正在使用的库,因为生成的连接正在恢复地址字节(我使用 Wireshark 来调查这个问题)。

是否允许安全地使用 getaddrinfo() 返回的地址来提供 socket() 和 connect() 调用?

谢谢,弗兰克

=== 更新 ===

感谢你们的 cmets。我可以再澄清一点。我运行了以下代码 sn-p,结果如下:

struct addrinfo hints, *addr_list, *cur;
struct sockaddr_in addr_in;

memset(&addr_in, 0, sizeof(addr_in));
addr_in.sin_family = AF_INET;
addr_in.sin_port = htons(21);
addr_in.sin_addr.s_addr = htonl(0xC0A80101UL);  // 192.168.1.1
myprintf("%08x\n", addr_in.sin_addr.s_addr);

hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;

if( getaddrinfo( "srvdc01", "21", &hints, &addr_list ) == 0 )
{
    for( cur = addr_list; cur != NULL; cur = cur->ai_next )
    {
        ctx->fd = (int) socket( cur->ai_family, cur->ai_socktype,
                            cur->ai_protocol );
        if( ctx->fd >= 0 )
        {
            myprintf("%08x\n", ((struct sockaddr_in *)cur->ai_addr)->sin_addr.s_addr);
        }
    }
}

freeaddrinfo( addr_list );

第一个 printf 输出(在 htonl 之后)给出 c0a80101,而第二个(在创建套接字之后)给出 0101a8c0。这令人惊讶,因为在我的 little endian 平台上,我会期望 htonl() 调用交换字节以将它们带入网络字节顺序。

查看我们的嵌入式网络库(第三方商业库)的文档后,我们可以发现他们提供的 BSD API 实际上是在其供应商特定网络库 API 之上的(相当)有限的包装器。有一点需要注意的是,他们的库始终使用本机系统字节序,因此他们的 htonl() 函数始终是一个不执行任何操作的虚拟调用。

现在,查看 Martin R.http://pubs.opengroup.org/onlinepubs/009695399/functions/freeaddrinfo.html 提供的链接,我同意他的观点,即 getaddrinfo() 应该返回适​​合调用 connect() 的地址信息,而这个嵌入式库显然不是这种情况。

【问题讨论】:

  • 什么是 IP_ADDRESS?
  • 我很确定 getaddrinfo 按网络字节顺序填充 ai_addr 条目,以便它们可以直接与 socket() 和 connect() 一起使用。
  • 但是您应该尝试 all 返回的地址,例如beej.us/guide/bgnet/output/html/multipage/getaddrinfoman.html 示例代码。
  • 嗯,那个宏是怎么工作的?
  • ipv4 的调试打印应该是printf("%08x\n", ((struct sockaddr_in *)addr_list-&gt;ai_addr)-&gt;sin_addr.s_addr);

标签: c sockets


【解决方案1】:

getaddrinfo() 的 POSIX 文档 声明

getaddrinfo()成功返回后, res指向的位置应该引用一个addrinfo结构的链表,每个结构都应该指定一个socket地址和用于创建使用该套接字地址的套接字的信息。该列表应包括至少一个 addrinfo 结构。每个结构的 ai_next 字段包含一个指向列表中下一个结构的指针,如果它是列表中的最后一个结构,则为空指针。列表中的每个结构都应包括用于调用 socket() 函数的值,以及用于 connect() 函数的套接字地址,或者,如果AI_PASSIVE 标志被指定,用于 bind() 函数。 ai_familyai_socktypeai_protocol 字段可用作 socket() 函数的参数以创建适合与返回地址一起使用的套接字。 ai_addrai_addrlen 字段可用作 connect()bind() 函数的参数一个套接字,根据 AI_PASSIVE 标志。

这意味着——在符合 POSIX 的系统上——ai_addr 中的 IP 地址和端口号是 在网络字节序中, 正如connect() 函数所期望的那样。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-02
    • 2012-05-04
    相关资源
    最近更新 更多