Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cast sockaddr_storage and avoid breaking strict-aliasing rules

I'm using Beej's Guide to Networking and came across an aliasing issue. He proposes a function to return either the IPv4 or IPv6 address of a particular struct:

1  void *get_in_addr( struct sockaddr *sa )
2  {
3      if (sa->sa_family == AF_INET)
4        return &(((struct sockaddr_in*)sa)->sin_addr);
5      else
6        return &(((struct sockaddr_in6*)sa)->sin6_addr);
7  }

This causes GCC to spit out a strict-aliasing error for sa on line 3. As I understand it, it is because I call this function like so:

struct sockaddr_storage their_addr;
...
inet_ntop(their_addr.ss_family,
          get_in_addr((struct sockaddr *)&their_addr),
          connection_name,
          sizeof connection_name);

I'm guessing the aliasing has to do with the fact that the their_addr variable is of type sockaddr_storage and another pointer of a differing type points to the same memory.

Is the best way to get around this sticking sockaddr_storage, sockaddr_in, and sockaddr_in6 into a union? It seems like this should be well worn territory in networking, I just can't find any good examples with best practices.

Also, if anyone can explain exactly where the aliasing issue takes place, I'd much appreciate it.

like image 506
sinoth Avatar asked Sep 15 '09 21:09

sinoth


2 Answers

I tend to do this to get GCC do the right thing with type-punning, which is explicitly allowed with unions:


/*! Multi-family socket end-point address. */
typedef union address
{
    struct sockaddr sa;
    struct sockaddr_in sa_in;
    struct sockaddr_in6 sa_in6;
    struct sockaddr_storage sa_stor;
}
address_t;
like image 71
Nikolai Fetissov Avatar answered Nov 23 '22 12:11

Nikolai Fetissov


I tend to do this to get GCC do the right thing with type-punning, which is explicitly allowed with unions

I am pretty sure this (mis)use of union will not work (or only by accident) with GCC:

short type_pun2 (int i, int *pi, short *ps) {
    *pi = i;
    return *ps;
}

union U {
    int i;
    short s;
};

short type_pun (int i) {
    U u;
    return type_pun2 (i, &u.i, &u.s);
}

The correct way to do that is with memcpy, not union.

like image 28
curiousguy Avatar answered Nov 23 '22 14:11

curiousguy