即使有更多的cmsghdr对象,CMSG_NXTHDR()也会返回NULL

时间:2014-12-22 11:31:00

标签: linux sockets

我需要通过Unix域套接字发送一些辅助数据,但是我在创建msghdr时遇到了问题。我似乎只能访问cmsghdr中的第一个msghdr.msg_control。无论我做多大CMSG_NXTHDR()msghdr.msg_control都会返回NULL。

我正在使用eglibc 2.19运行64位Linux 3.13.0,如果这很重要的话。以下是一些演示此问题的示例代码。我用gcc -std=c99 -Wall -Werror -Wpedantic test.c编译了它。我知道我可以在同一条消息中发送两个文件描述符 - 这只是一个测试。

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>


int main()
{
    int data1 = STDIN_FILENO;
    int data2 = STDOUT_FILENO;

    char control[CMSG_SPACE(sizeof(data1)) + CMSG_SPACE(sizeof(data2))];
    struct msghdr mh = {
        .msg_namelen = 0,
        .msg_iovlen = 0,
        .msg_control = control,
        .msg_controllen = sizeof(control),
        .msg_flags = 0
    };

    struct cmsghdr *cmh = CMSG_FIRSTHDR(&mh);
    if (cmh == NULL) {
        puts("Can't get first cmsg");
        return 1;
    }
    cmh->cmsg_len = CMSG_LEN(sizeof(data1));
    cmh->cmsg_level = SOL_SOCKET;
    cmh->cmsg_type = SCM_RIGHTS;
    memcpy(CMSG_DATA(cmh), &data1, sizeof(data1));

    cmh = CMSG_NXTHDR(&mh, cmh);
    if (cmh == NULL) {
        puts("Can't get second cmsg");
        return 1;
    }
    cmh->cmsg_len = CMSG_LEN(sizeof(data2));
    cmh->cmsg_level = SOL_SOCKET;
    cmh->cmsg_type = SCM_RIGHTS;
    memcpy(CMSG_DATA(cmh), &data2, sizeof(data2));
}

当我构建并运行此程序时,它会打印出“#34;无法获得第二个cmsg&#34;”。即使我将control的大小增加了几百个字节,也是如此。显然,CMSG_NXTHDR()并不认为cmsghdr中有第二个.msg_control。我怎么能说服它们有两个,而不仅仅是一个?

1 个答案:

答案 0 :(得分:2)

我在这个问题上浪费了几个小时,所以想到了我找到的文件。 这是未初始化的控制缓冲区的情况。手册页没有具体说明这一点,但如果查看头文件/usr/include/bits/socket.h,则代码为

_EXTERN_INLINE struct cmsghdr *
__NTH (__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg))
{
    if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr))
    /* The kernel header does this so there may be a reason.  */
    return (struct cmsghdr *) 0;

    __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg
                   + CMSG_ALIGN (__cmsg->cmsg_len));

    if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) __mhdr->msg_control
                                          + __mhdr->msg_controllen)
        || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len)
         > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen)))
         /* No more entries.  */
             return (struct cmsghdr *) 0;

     return __cmsg;
 }

第一次检查后,__ cmsg指向一个被视为下一个标题的地址。随后,第二个if语句中的rhs表达式使用__cmsg-&gt; cmsg_len的值,如果您尚未首先初始化控制缓冲区,则可能是垃圾。根据垃圾值的不同,您可能会检查此错误,从而无论您将控制缓冲区的大小调整多少,都会导致空值。

解决方案可以像(来自您的代码)一样简单

char control[CMSG_SPACE(sizeof(data1)) + CMSG_SPACE(sizeof(data2))] = {};

这是为我做的。