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