在代码here中,有一行:
struct iphdr * iph = (struct iphdr *)buffer;
ProcessPacket
函数中的,其中buffer
的类型为char*
。 buffer
已在主函数中被recvfrom
赋予值。如何将简单字符串(buffer
)转换为结构以及如何安全地提取数据?
iphdr:
struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 ihl:4,
version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
__u8 version:4,
ihl:4;
#else
#error "Please fix <asm/byteorder.h>"
#endif
__u8 tos;
__u16 tot_len;
__u16 id;
__u16 frag_off;
__u8 ttl;
__u8 protocol;
__u16 check;
__u32 saddr;
__u32 daddr;
/*The options start here. */
};
答案 0 :(得分:5)
buffer
不 a string
。它是指向原始二进制数据的指针。 recvfrom
使用原始IP / TCP帧(也称为数据包)填充(在此示例中,见下文)buffer
。因此,sizeof(iphdr)
的前buffer
个字节是IP-header结构:iphdr
。这正是博客作者使用您提供的代码段的原因:
struct iphdr * iph = (struct iphdr *)buffer;
如果包含IP标头选项,则标头的实际大小为iph->ihl*4
。
然后在iph->protocol
(在博客中)检查标头的协议字段(ProcessPacket
),以确定数据包包含哪种传输协议。
如果使用的传输协议是TCP,则可以使用(来自博客的片段)提取TCP标头(以及稍后的数据):
unsigned short iphdrlen = iph->ihl*4;
struct tcphdr *tcph = (struct tcphdr*)(buffer + iphdrlen);
原始框架
博客作者使用以下方式创建了套接字:
sock_raw = socket(AF_INET , SOCK_RAW , IPPROTO_TCP);
或者,如果您想要UDP帧,可以使用:
sock_raw = socket(AF_INET , SOCK_RAW , IPPROTO_UDP);
如果您贪婪并且想要每个数据包使用(请在使用之前先阅读框架格式!):
socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
答案 1 :(得分:2)
我认为代码引入了未定义的行为,因为在程序过程中执行了以下两行:
unsigned char *buffer = (unsigned char *)malloc(65536);
...
struct iphdr *iph = (struct iphdr*)buffer;
buffer
是指向保留为unsigned char*
的内存块的指针,然后将其转换为类型为struct iphdr
的指针;但是struct iphdr
很可能具有与char*
不同的对齐限制,这是未定义的行为(例如,参见this online c11 draft standard):
6.3.2.3指针
(7)指向对象类型的指针可以转换为指向a的指针 不同的对象类型。如果结果指针不正确 对于引用的类型,行为是未定义的。 ...
虽然它可能有用(它仍然是UB的选项之一),但也可能是程序的行为与你不打算一样。
我建议将信息复制到正确对齐的struct iphdr
- 对象中:
unsigned char *buffer = (unsigned char *)malloc(65536);
...
struct iphdr iphobj;
memcpy(&iphobj,buffer,sizeof(struct iphdr));
...
然后照顾对象的生命周期。
请注意,您已对代码C
和C++
进行了标记,并且两种语言都有不同的规则(例如,有关malloc
结果的显式转换,这在C ++中是必需的,但不是C)鼓励。
但是关于UB,我很确定代码在C和C ++这两种语言中引入了UB。
答案 2 :(得分:0)
首先要理解的是,无论转换(struct iphdr *)
如何,内存中的位都保持完全相同。只是你现在说buffer
现在被视为指向struct iphdr
而不是之前的指针。您只是告诉编译器使用不同的眼镜看比特,因此进行相应的解释。编译器突然发现buffer
已成为struct iphdr *
。说&#34;好的&#34;这就是全部。重要的是,您确切地知道buffer
是什么,并将其转换为正确的类型。
如果你愿意,你可以使用类型转换buffer
到int *
(或任何其他指针类型),编译器也不会说什么。虽然你以后会遇到问题。