如何投struct sockaddr_storage的,避免破严格走样规则(How to cast

2019-07-18 04:03发布

我使用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吐出了上线3 SA严格的混叠误差据我了解,那是因为我调用这个函数像这样:

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_storagesockaddr_insockaddr_in6组成工会? 看起来这应该是网络陈旧的领土,我只是无法找到最佳实践任何很好的例子。

此外,如果任何人都可以解释究竟在何处混淆问题发生,我会非常感激。

Answer 1:

我倾向于这样做是为了让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; 



Answer 2:

我倾向于这样做是为了让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



Answer 3:

试图编写代码来获取本机的MAC地址时,我最近有HPUX系统上的类似别名警告

&(((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口味和编译器



Answer 4:

这个问题无关与呼叫的功能。 相反,它与((struct sockaddr_in*)sa)->sin_addr 。 问题是, sa是一个类型的指针,但你它强制转换为不同类型的指针,然后解引用它。 这打破了所谓的“严格走样”的规则,它说,不同的永远不能别名类型的变量。 在你的情况下,混叠不同类型的正是你想做的事。

简单的解决方案是关闭这种优化,其允许以这种方式混叠。 在海湾合作委员会,该标志为-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使用原密码的时候给我一个警告,所以我不知道这是否买你的东西。



文章来源: How to cast sockaddr_storage and avoid breaking strict-aliasing rules