linux 下 sockaddr_in 与 sockaddr 结构体 使用时的注意要点

第一,先说说两个结构体分别所在头文件的位置:

sockaddr_in  在头文件  #include <netinet/in.h>中

sockaddr  在头文件   #include <sys/socket.h>中

第二,两个结构体的原型:

  1. /* Internet address.  */
  2. typedef uint32_t in_addr_t;
  3. struct in_addr
  4.   {
  5.     in_addr_t s_addr;
  6.   };

/* Structure describing an Internet socket address.  */

  1. struct sockaddr_in
  2.   {
  3.     __SOCKADDR_COMMON (sin_);
  4.     in_port_t sin_port;            /* Port number.  */
  5.     struct in_addr sin_addr;        /* Internet address.  */
  6.     /* Pad to size of `struct sockaddr'.  */
  7.     unsigned char sin_zero[sizeof (struct sockaddr) -
  8.                __SOCKADDR_COMMON_SIZE -
  9.                sizeof (in_port_t) -
  10.                sizeof (struct in_addr)];
  11.   };

/* Structure describing a generic socket address.  */

  1. struct sockaddr
  2.   {
  3.     __SOCKADDR_COMMON (sa_);    /* Common data: address family and length.  */
  4.     char sa_data[14];        /* Address data.  */
  5.   };

第三,二者的用法和区别:

有人在建立socket的时候,很少有人去在意两个结构体的实际区别,而是直接套用demo,一气呵成,适当改改传入参数就OK了,人家的结构体之间的关联肯定是一点问题都没有的,这一点我们必须承认。

但是对于新手,在程序设计使用这些结构体时往往会疏忽大意,这里有一个罕见但是极为隐蔽的BUG存在,我们一起看看,

以一段代码做示例,进行说明:

  1.   int tcp_connect(unsigned short port, char *addr)
  2. {
  3.     int f;
  4.     int on=1;
  5.     int one = 1;/*used to set SO_KEEPALIVE*/
  6.     struct sockaddr_in s;
  7.     int v = 1;
  8.     if((f = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))<0)
  9.     {
  10.         fprintf(stderr, "socket() error in tcp_connect.\n");
  11.         return -1;
  12.     }
  13.     setsockopt(f, SOL_SOCKET, SO_REUSEADDR, (char *) &v, sizeof(int));
  14.     s.sin_family = AF_INET;
  15.     s.sin_addr.s_addr = inet_addr(addr); 
  16.     s.sin_port = htons(port);
  17.     // set to non-blocking
  18.     if(ioctl(f, FIONBIO, &on) < 0)
  19.     {
  20.         fprintf(stderr,"ioctl() error in tcp_connect.\n");
  21.         return -1;
  22.     }
  23.     if(connect(f,(struct sockaddr*)&s, sizeof(s)) < 0)
  24.     {
  25.         fprintf(stderr,"connect() error in tcp_connect.\n");
  26.         return -1;
  27.     }
  28.     if(setsockopt(f, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one))<0)
  29.     {
  30.         fprintf(stderr,"setsockopt() SO_KEEPALIVE error in tcp_connect.\n");
  31.         return -1;
  32.     }
  33.     return f;
  34. }

其中第6行,第26行红色字体部分为两个结构体使用的方式,sockaddr_in 用于结构体定义,而sockaddr 用于结构体类型的转换

这里对照一下两个结构体的原型,我们发现,真正存储IP地址的变量是什么?

sockaddr_in   中的IP地址存储在  sin_addr

sockaddr        中的IP地址存储在 sa_data[14]

问题就在这,sin_addr的原型是  typedef uint32_t in_addr_t; 是一个占用4个字节的无符号整形,这样他在存储四段IP地址例如255.255.255.255以下的IP 是不存在任何问题的

但是  sa_data[14]存储 255.255.255.255的IP地址就出问题了,它是一种以字符形式存储的,所以14字节的空间是不够的,出现溢出问题,这里我们先了解一下两个结构体的区别

   1)sa_data[14], 把目标地址和端口信息存在了一起,是一个通用地址结构,为统一地址结构,统一接口函数,使不同的地址结构可以被bind()、connect()、recvfrom()、sendto()等函数调用

 2)sin_family:指代协议族,在socket编程中只能是AF_INET
  sin_port:存储端口号(使用网络字节顺序)
  sin_addr:存储IP地址,使用in_addr这个数据结构
  sin_zero:是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节,是通过宏计算得到的动态地址空间。

以上两点就是主要区别,注意,sockaddr_in 的地址空间会随着 sockaddr 的地址空间变化

关于网上很多讲解,不多说!

我们回到之前提出的问题:

当你使用 类似 这种字符串拷贝函数strcpy()的时候,想把字符数组中的网络字符型地址IP拷贝到sockaddr 变量中,看似没有问题,档遇到四段位的IP地址拷贝是,会溢出了14字节的空间,想解决这个问题有两个方法

其一:修改 sockaddr 中  sa_data[14] 为 sa_data[16]

其二:不要使用strcpy这类的函数直接拷贝,其实在程序设计时,有时候会忽视这里,有的朋友如果是遇到此类问题,看到本文会有所体会,但是如果没有遇到过可能不会理解(这里除外那些明白的大神会 噗呲的笑出声,什么智商会这么用啊

所以本文只针对那些对 结构体不太透彻的人 

为说明问题,继续举例:

  1.     struct ifreq stIfr;  //linux 网络接口用来配置ip地址 
  2.     struct sockaddr stServerAddr; 
  3.     char s8Str[128];
  4.     strcpy(stIfr.ifr_name, "eth0");
  5.     if(ioctl( fd, SIOCGIFADDR, &stIfr) < 0)
  6.     {
  7.         //printf("Failed to get host eth0 ip\n");
  8.         strcpy(stIfr.ifr_name, "wlan0");
  9.         if(ioctl( fd, SIOCGIFADDR, &stIfr) < 0)
  10.         {
  11.             printf("Failed to get host eth0 or wlan0 ip\n");
  12.         }
  13.     }
  14.     sock_ntop_host(&stIfr.ifr_addr, sizeof(struct sockaddr), s8Str, 128);

经过以上调用后,IP地址就顺理成章的存储在了 s8Str 数组中,这时,你先提取IP地址,

信手拈来 strcpy (stServerAddr.sa_data,s8Str , sizeof(s8Str ));

这就是问题导火线,sa_data[14]极有可能发生溢出,为后续使用带来诸多麻烦

笔者在此做出提示,也是对我遇到此类问题产生的总结

取之网络,馈与网络

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值