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

StackOverflow https://stackoverflow.com/questions/1429645

Вопрос

Я использую руководство Beej по работе с сетями и столкнулся с проблемой псевдонимов. Он предлагает функцию для возврата адреса 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);

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

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

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

Это было полезно?

Решение

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


/*! 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) {
    *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 .

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

& amp; (((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-разновидностей и компиляторов

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top