Подтвердить что ты не робот

Как отличить sockaddr_storage и не нарушать правила строгого сглаживания

Я использую Beej Guide to Networking и столкнулся с проблемой псевдонимов. Он предлагает функцию для возврата адреса IPv4 или IPv6 определенной структуры:

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  }

Это приводит к тому, что GCC выдает ошибку строгой сглаживания для sa в строке 3. Как я понимаю, это потому, что я вызываю эту функцию следующим образом:

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

Я предполагаю, что псевдонимы связаны с тем, что переменная their_addr имеет тип sockaddr_storage, а другой указатель другого типа указывает на одну и ту же память.

Лучший способ обойти это вложение sockaddr_storage, sockaddr_in и sockaddr_in6 в объединение? Похоже, что это должно быть хорошо изношенной территории в сети, я просто не могу найти хороших примеров с лучшими практиками.

Кроме того, если кто-то может объяснить, где именно происходит проблема с псевдонимом, я бы очень признателен.

4b9b3361

Ответ 1

Я стараюсь сделать это, чтобы GCC поступил правильно с type-punning, который явно разрешен с помощью union:


/*! 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;

Ответ 2

Я стараюсь сделать это, чтобы GCC делал правильные вещи с помощью функции "пин-код", которая явно разрешена с помощью union

Я уверен, что это (неправильное) использование союза не будет работать (или только случайно) с 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);
}

Правильный способ сделать это с memcpy, а не union.

Ответ 3

Проблема не имеет ничего общего с вызовом функции. Скорее, он с ((struct sockaddr_in*)sa)->sin_addr. Проблема заключается в том, что sa является указателем одного типа, но вы переводите его в указатель другого типа, а затем разыгрываете его. Это нарушает правило, называемое "строгий псевдоним", в котором говорится, что переменные разных типов никогда не могут быть псевдонимом. В вашем случае псевдоним другого типа - это именно то, что вы хотите сделать.

Простое решение состоит в том, чтобы отключить эту оптимизацию, которая позволяет накладывать подобным образом. На GCC флаг -fno-strict-aliasing.

Лучшее решение - использовать союз, как упоминал Николай.

void *get_in_addr(struct sockaddr *sa)
{
    union {
        struct sockaddr     *sa;
        struct sockaddr_in  *sa_in;
        struct sockaddr_in6 *sa_in6;
    } u;
    u.sa = sa;
    if (sa->sa_family == AF_INET)
        return &(u.sa_in->sin_addr);
    else
        return &(u.sa_in6->sin6_addr);
}

Тем не менее, я не могу заставить GCC дать мне предупреждение при использовании вашего исходного кода, поэтому я не уверен, что это вам что-то покупает.

Ответ 4

Недавно я получил аналогичное предупреждение об алиасе в системе HPUX при попытке написать код для получения MAC-адреса машины

&(((struct sockaddr_in *)addr)->sin_addr) жалуется на правила строгого сглаживания

Это код в некотором контексте

 char ip[INET6_ADDRSTRLEN] = {0};
 strucut sockaddr *addr

 ...
 get addr from ioctl(socket,SOCGIFCONF...) call
 ...
 inet_ntop(AF_INET, &(((struct sockaddr_in *)addr)->sin_addr),ip,sizeof ip);

Я преодолел предупреждение о псевдониме, выполнив следующие

struct sockaddr_in sin;
memcpy(&sin,addr,sizeof(struct sockaddr));
inet_ntop(AF_INET, &sin.sin_addr,ip,sizeof ip);

И хотя это потенциально опасно, я добавил перед ним следующие строки

 static_assert(sizeof(sockaddr)==sizeof(sockaddr_in));

Я не уверен, что это будет считаться плохой практикой, но это сработало и было перекрестной платформой для других * Nix-вкусов и компиляторов