Fill sockaddr_storage struct with values of sockad

2019-02-26 07:22发布

I've a sockaddr_storage object and I need to fill it with values provided by the user. Note that user can provide either of AF_INET or AF_INET6 as domain for filling up the struct.


void fill(sockaddr_storage &addrStruct, int domain,
      const char addr[], const int port)
{
    std::memset(&addrStruct, 0, sizeof(addrStruct));

    switch(domain) {

        case AF_INET: addrStruct.sin_family = AF_INET;
                      addrStruct.sin_port= htons(port);

                      inet_pton(AF_INET, addr, addrStruct.sin_addr);

        case AF_INET6: ....
        ....
        ....
        default: ....
    }
}

Pretty sure this doesn't works since addrStruct is of type struct sockaddr_storage and these members are present in struct sockaddr_in. I also tried static_cast<sockaddr_in>(addrStruct).sin_port and similar but that again doesn't works. So how should I be filling up this struct so that it holds valid values while respecting alignment of casted structs.

3条回答
我只想做你的唯一
2楼-- · 2019-02-26 07:22

You have to do reinterpret cast of addrStruct to sockaddr_in. In your case the code will look as following:

    case AF_INET: 
    {
        struct sockaddr_in * tmp =
            reinterpret_cast<struct sockaddr_in *> (&addrStruct);
        tmp->sin_family = AF_INET;
        tmp->sin_port = htons(port);
        inet_pton(AF_INET, addr, tmp->sin_addr);
    }
    break;

But I recommend you to use getaddrinfo(3) instead your approach.

查看更多
Luminary・发光体
3楼-- · 2019-02-26 07:31

It is almost always better to use getaddrinfo than the inet_* family of address conversion functions. It does all of the allocation and initialization of sockaddr objects for you—you don't have to mess with sockaddr_storage yourself. It handles IPv4 and IPv6 seamlessly, it handles "multihomed" hosts with more than one address, and it can (optionally) do DNS lookups.

To use getaddrinfo, you completely throw away the fill function that you showed. The code that uses fill probably looks something like this:

struct sockaddr_storage ss;
fill(&ss, AF_INET, "10.0.0.21", 25);
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == -1) {
     perror("socket");
     return -1;
}
if (connect(sock, (struct sockaddr*)&ss, sizeof(struct sockaddr_in)) {
     perror("10.0.0.21:25");
     return -1;
}
/* use connected sock here */

You replace that with

struct addrinfo hints;
struct addrinfo *rp, *result;
memset(hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_ADDRCONFIG;
hints.ai_socktype = SOCK_STREAM;

int err = getaddrinfo("10.0.0.21", "25", &hints, &result);
if (err) {
    if (err == EAI_SYSTEM) {
        fprintf(stderr, "10.0.0.21:25: %s\n", strerror(errno));
    } else {
        fprintf(stderr, "10.0.0.21:25: %s\n", gai_strerror(err));
    }
    return 1;
}

int sock;
for (rp = result; rp; rp = rp->ai_next) {
    sock = socket(rp->ai_family, rp->ai_socktype,
                  rp->ai_protocol);
    if (sock == -1)
        continue;
    if (connect(sock, rp->ai_addr, rp->ai_addrlen))
        break;
    close(sock);
}
if (rp == 0) {
    perror("10.0.0.21:25");
    return -1;
}
freeaddrinfo(result);
/* use sock here */

That looks more complicated, but remember that it totally replaces the fill function you didn't know how to write, and also remember that it will seamlessly handle DNS for you. (If you have a concrete reason to want to process numeric addresses only, you can set flags in hints for that.)

For more detailed example usage see the manpage I linked to.

查看更多
叛逆
4楼-- · 2019-02-26 07:44

You need to use the appropriate address structure first, and fill it out:

struct sockaddr_in sin;

sin.sin_family = AF_INET;
sin.sin_port= htons(port);

Then, after you're done, memcpy() it into the sockaddr_storage:

memcpy(reinterpret_cast<char *>(&addrStruct),
       reinterpret_cast<char *>(&sin), sizeof(sin));

Note that you should still zero out the full sockaddr_storage buffer beforehand, as you did.

Another way to do it is to define your own union of all the address structures, including sockaddr_storage, then initialize the appropriate union member.

查看更多
登录 后发表回答