Sockaddr union and getaddrinfo()

Sockaddr union and getaddrinfo()

本文关键字:getaddrinfo and union Sockaddr      更新时间:2023-10-16

我正在编写一个从命令行提供的地址。

要使发送和写作更容易,我正在使用getAddrinfo将地址转换为sockaddr struct:sockaddr_in或sockaddr_in6。现在我知道我应该使用sockaddrs的结合:

typedef union address
{
    struct sockaddr s;
    struct sockaddr_in s4;
    struct sockaddr_in6 s6;
    struct sockaddr_storage ss;
} address_t;

我知道,他们不能避免隐藏严格的别名问题。我遇到了麻烦,将GetAddrinfo的Addrinfo中的信息无缝地放入此地址:

struct addrinfo hint, *serv = NULL;
address_t addr;
hint.ai_family = AF_UNSPEC;
hint.ai_flags = 0;
hint.ai_socktype = SOCK_DGRAM;
hint.ai_protocol = IPPROTO_UDP;
ret = getaddrinfo(address_sr.c_str(), s_port.c_str(), &hint, &serv);
//address_sr and s_port are strings with the address and port
switch (serv->ai_addr) {
    case AF_INET: {
        addr->s4 = * (sockaddr_in*) serv->ai_addr;
        //Here I want to fill the address_t struct with information
        //This line causes a segfault 
    }
    break;
    case AF_INET6: {
        addr->s6 = * (sockaddr_in6*) serv->ai_addr;
        //Conversion here
    }
    break;

另外,复制内存:

    memcpy(&addr, serv->ai_addr, serv->ai_addrlen);

也会引起segfault。

我应该怎么做?我尝试了十几种不同的方式,但我无法弄清楚。如何将Addrinfo的地址放在该工会中?我使用sockaddr_storage或sockaddr_ins吗?

编辑:编辑清晰度和其他代码信息。

您需要将指针退还。

addr->s4 = *(sockaddr_in*) serv->ai_addr;

我认为您不正确。

关于第三个参数:

const struct addrinfo *hints

提示参数指向指定的Addrinfo结构 选择返回的插座地址结构的标准 列表指向Res。如果提示不是null,则指向Addrinfo ai_family,ai_socktype和ai_protocol指定的结构 限制由归还的插座地址集的标准 getAddrinfo()[...]

例如,您只能要求IPv4地址族和/或仅用于数据报插座(考虑到您尝试使用UDP的尝试可以很好)。基本上,您提供了一个AddRinfo实例,设置了感兴趣的字段,然后将指针传递给该函数,作为其第三个参数:

struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
ret = getaddrinfo(address_sr.c_str(), s_port.c_str(), &hint, &serv);

在此示例中,该函数不仅可以返回一个,而且可以返回地址结构的整个列表:

getAddrInfo()函数分配并初始化链接的列表 Addrinfo结构,每个网络地址都与节点匹配 和服务,受提示施加的任何限制,并退货 指向res列表开始的指针。链接中的项目 列表由AI_NEXT字段链接。

因此,您必须以这种方式循环浏览功能结果:

for (rp = serv; rp != NULL; rp = rp->ai_next)

我强烈建议在我提供的链接中仔细阅读文档。还有一个漫长而详细的例子,可以单独解决您的问题。

sockaddr_storage足够大,可以容纳任何 sockaddr_...类型,从而使address_t同样大。因此,我只会在一个操作中 memcpy()整个serv->ai_addr,然后摆脱switch,例如:

struct addrinfo hints = {};
struct addrinfo *addrs, *serv;
address_t addr;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
... 
ret = getaddrinfo(address_sr.c_str(), s_port.c_str(), &hint, &addrs);
if (ret == 0)
{
    for (serv = addrs; serv != NULL; serv = serv->ai_next)
    {
        memcpy(&addr, serv->ai_addr, serv->ai_addrlen);
        ...
    }
    freeaddrinfo(addrs);
} 

派对很晚,但我也有兴趣使用联合来用于不同的套接字地址结构并找到了这个问题。由于缺少一个完整的示例,尤其是address_srs_port

因此,我在这里使用了雷米·勒博(Remy Lebeau)的建议来编码副本和粘贴示例来检查情况。我发现与工会无需使用类型的铸件。为了显示如何在没有segfaults的情况下访问address_t addr联合的成员,我只是将它们输出到std :: cout。这是一个示例:

#include <netdb.h>
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
typedef union {
    struct sockaddr s;
    struct sockaddr_in s4;
    struct sockaddr_in6 s6;
    struct sockaddr_storage ss;
} address_t;
struct addrinfo hint{};
struct addrinfo *addrs, *serv;
address_t addr;
std::string address_sr{"example.com"}; // Resolves real IP addresses by DNS
std::string s_port{"https"};

int main() {
    hint.ai_family = AF_UNSPEC;
    hint.ai_socktype = SOCK_DGRAM;
    // hint.ai_protocol = IPPROTO_UDP; // Already given by SOCK_DGRAM
    int ret = getaddrinfo(address_sr.c_str(), s_port.c_str(), &hint, &addrs);
    if (ret != 0) {
        std::cerr << "ERROR! " << gai_strerror(ret) << ".n";
        return 1;
    }
    for (serv = addrs; serv != NULL; serv = serv->ai_next) {
        memcpy(&addr, serv->ai_addr, serv->ai_addrlen);
        char addr_buf[INET6_ADDRSTRLEN]{};
        switch (addr.ss.ss_family) {
        case AF_INET: {
            const char* ret = inet_ntop(AF_INET, &addr.s4.sin_addr, addr_buf,
                                        sizeof(addr_buf));
            if (ret == nullptr) {
                std::cerr << "ERROR! " << std::strerror(errno) << ".n";
                return 3;
            }
            std::cout << address_sr << " AF_INET  = " << addr_buf << ":"
                      << ntohs(addr.s4.sin_port) << "n";
        } break;
        case AF_INET6: {
            const char* ret = inet_ntop(AF_INET6, &addr.s6.sin6_addr, addr_buf,
                                        sizeof(addr_buf));
            if (ret == nullptr) {
                std::cerr << "ERROR! " << std::strerror(errno) << ".n";
                return 1;
            }
            std::cout << address_sr << " AF_INET6 = [" << addr_buf << "]:"
                      << ntohs(addr.s6.sin6_port) << "n";
        } break;
        default:
            std::cerr << "ERROR! invalid address family " << addr.ss.ss_family << ".n";
            return 1;
        } // switch
    } // for
    freeaddrinfo(addrs);
    return 0;
}

我测试机上程序的输出是:

example.com AF_INET6 = [2606:2800:220:1:248:1893:25c8:1946]:443
example.com AF_INET  = 93.184.216.34:443

我用:

编译了它
g++ -std=c++11 -Wall -Wpedantic -Wextra -Werror -Wuninitialized -Wsuggest-override -Wdeprecated example.cpp