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

Я использую Руководство Биджа по работе с сетями и столкнулся с проблемой псевдонимов. Он предлагает функцию для возврата адреса 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 в союз? Кажется, что это должно быть изношенной территорией в сети, я просто не могу найти хороших примеров с лучшими практиками.

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

4 ответа

Решение

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


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

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

Я уверен, что это (неправильное) использование объединения не будет работать (или только случайно) с GCC:

short type_pun2 (int i, int * pi, short * ps) {
    * пи = я;
    возврат * пс;
}

союз U {
    Int I;
    короткие с;
};

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

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

Недавно у меня было похожее предупреждение псевдонима в системе 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-разновидностей и компиляторов.

... Я просто не могу найти хороших примеров с лучшими практиками.

Может быть, посмотрите здесь:

http://my-sample-code.googlecode.com/svn/trunk/socket/

Ура,

Lupo

Проблема не имеет ничего общего с вызовом функции. Скорее это с ((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 предупреждать меня при использовании вашего исходного кода, поэтому я не уверен, что это вас что-то покупает.

Другие вопросы по тегам