使用有效的C代码将struct sockaddr *转换为struct sockaddr_in6 *的正确方法是什么?

时间:2016-09-11 04:11:45

标签: c sockets struct casting

这是一个简单的程序,它显示了在编写套接字程序时我们通常如何将{ "globalDependencies": { "core-js": "registry:dt/core-js#0.0.0+20160725163759", "jasmine": "registry:dt/jasmine#2.2.0+20160621224255", "node": "registry:dt/node#6.0.0+20160831021119" } } 转换为struct sockaddr *struct sockaddr_in *

struct sockaddr_in6 *

Beej's Guide to Network Programming在第10页也建议这样做。

  

为了处理struct sockaddr,程序员创建了一个并行结构:struct sockaddr_in(“in”表示“Internet”)将与IPv4一起使用。

     

这是重要的一点:指向struct sockaddr_in的指针可以转换为指向struct sockaddr的指针,反之亦然。所以即使connect()想要一个struct sockaddr *,你仍然可以使用struct sockaddr_in并在最后一刻投出它!

但是从another question的讨论来看,这似乎只是一个黑客攻击,而不是符合C标准的有效C代码。

特别是,请参阅提及的AnT's answer

  

至于在struct sockaddr *,struct sockaddr_in *和struct sockaddr_in6 *之间使用强制转换的流行技术 - 这些只是与C语言无关的黑客攻击。它们只是在实践中工作,但就C语言而言,该技术无效。

因此,如果我们用来进行套接字编程的这种技术(以及书籍推荐的内容)是无效的,那么重写上述代码的有效方法是什么,以便它也是符合C标准的有效C代码?

4 个答案:

答案 0 :(得分:3)

  

因此,如果我们进行套接字编程的方式(以及书籍推荐的内容)是一个hack,重写上述代码的正确方法是什么,以便它也是符合C标准的有效C代码?

TL; DR:继续执行您在示例中提供的内容。

您提供的代码似乎在语法上是正确的。在某些情况下,它可能会也可能不会表现出不确定的行为。是否确实取决于getaddrinfo()的行为。

在C语言中无法满足所有功能要求,并且与您提供的标准技术相比,可以更好地防止未定义的行为。这就是它成为标准技术的原因。这里的问题是该函数必须支持所有可想到的地址类型,包括尚未定义的类型。它可以将套接字地址指针声明为void *,这不需要转换,但实际上不会改变任何给定程序是否显示未定义的行为。

就其本身而言,getaddrinfo()在设计时考虑了这种用法,因此如果在结果上使用预期的强制转换允许不当行为,则 问题。此外,getaddrinfo()不是C标准库的一部分 - 它由POSIX标准化(仅),它也包含C标准。因此,根据C单独分析该功能表明不适当的超聚焦。虽然演员阵容仅根据C引起了一些关注,但你应该期望在使用getaddrinfo()的{​​{1}}和其他POSIX网络函数的上下文中,转换为正确的特定地址类型并访问引用的对象会产生可靠的结果。

此外,我认为AnT对您的其他问题的回答过于简单且过于消极。我正在考虑是否要写一个对比鲜明的答案。

答案 1 :(得分:1)

POSIX标准保证指向任何类型套接字的指针都可以转换为struct sockaddr*。因此,您可以将指向任何类型套接字的指针强制转换为struct sockaddr*,以便在bind()connect()中使用它;库知道要检查哪些位。您还可以检查套接字的sa_family字段以查看其实际内容,假设它包含有效数据,然后转换为适当的指针类型。如果您需要分配足够大的内存块来安全地存储任何类型的套接字,请使用sockaddr_storage。保证从sockaddr_storage*到任何其他套接字指针的转换正确对齐,并且保证包含套接字族的字段仍可正常工作。

要从sockaddr_in获取IPv6套接字,您可以将IPv4地址转换为IPv6表示法并使用getaddrinfo()。但是,现代查找功能可能会为您提供包含IPv4和IPv6套接字的链接列表。

答案 2 :(得分:0)

答案在man getaddrinfosys/socket.hman getaddrinfo提供了使用通用struct sockaddr

的理性
Given node and service, which identify an Internet host and a service, 
getaddrinfo() returns one or more addrinfo structures, each of which 
contains an Internet address that can be specified in a call to bind(2) 
or connect(2). The getaddrinfo() function combines the functionality 
provided by the gethostbyname(3) and getservbyname(3) functions into a 
single interface, but unlike the latter functions, getaddrinfo() is 
reentrant and allows programs to eliminate IPv4-versus-IPv6 dependencies.

只有一个struct sockaddr。看起来各种类型都只是在透明联合中使用,以提供所需的任何struct sockaddr_X。例如:

/* This is the type we use for generic socket address arguments.

   With GCC 2.7 and later, the funky union causes redeclarations or
   uses with any of the listed types to be allowed without complaint.
   G++ 2.7 does not support transparent unions so there we want the
   old-style declaration, too.  */
#if defined __cplusplus || !__GNUC_PREREQ (2, 7) || !defined __USE_GNU
# define __SOCKADDR_ARG         struct sockaddr *__restrict
# define __CONST_SOCKADDR_ARG   const struct sockaddr *
#else
/* Add more `struct sockaddr_AF' types here as necessary.
   These are all the ones I found on NetBSD and Linux.  */
# define __SOCKADDR_ALLTYPES \
  __SOCKADDR_ONETYPE (sockaddr) \
  __SOCKADDR_ONETYPE (sockaddr_at) \
  __SOCKADDR_ONETYPE (sockaddr_ax25) \
  __SOCKADDR_ONETYPE (sockaddr_dl) \
  __SOCKADDR_ONETYPE (sockaddr_eon) \
  __SOCKADDR_ONETYPE (sockaddr_in) \
  __SOCKADDR_ONETYPE (sockaddr_in6) \
  __SOCKADDR_ONETYPE (sockaddr_inarp) \
  __SOCKADDR_ONETYPE (sockaddr_ipx) \
  __SOCKADDR_ONETYPE (sockaddr_iso) \
  __SOCKADDR_ONETYPE (sockaddr_ns) \
  __SOCKADDR_ONETYPE (sockaddr_un) \
  __SOCKADDR_ONETYPE (sockaddr_x25)

# define __SOCKADDR_ONETYPE(type) struct type *__restrict __##type##__;
typedef union { __SOCKADDR_ALLTYPES
            } __SOCKADDR_ARG __attribute__ ((__transparent_union__));
# undef __SOCKADDR_ONETYPE
# define __SOCKADDR_ONETYPE(type) const struct type *__restrict __##type##__;
typedef union { __SOCKADDR_ALLTYPES
            } __CONST_SOCKADDR_ARG __attribute__ ((__transparent_union__));
# undef __SOCKADDR_ONETYPE
#endif

我还没有吃掉所有的大汤,但看起来你对这两种类型都很安全。

答案 3 :(得分:-2)

参考此链接Is it legal to type-cast pointers of different struct types (e.g. struct sockaddr * to struct sockaddr_in6 *)?。 这些并不完全是黑客攻击。 要做你想做的事,如果正确理解,我会做类似的事情:

struct base
{
    int a;
    char b;
    double *n;
}
struct derived 
{
  struct base b; //(no pointer, but the whole struct)
  int c;
  int d;
}

通过这种方式,当您从派生到基础转换时,您确定派生的前n个字节与基础完全重叠。代码工作并且完全可移植。 不同的问题不同的解决方案实际上根据我的经验,我曾经倾向于基础包含派生,而不是反之亦然。所以要有一个"多态"结构体。但是1)如果它有效,2)人们将阅读代码将会理解3)你觉得有用...为什么不呢?听你的。可能c ++正是以这种方式实现了hinerhitance!谁能说呢? 只需要小心它们的数组,使用正确的类型进行索引,并注意将它放在第一位。 (但是C ++也遇到了多态对象数组的问题,它只能指出它们的指针)