调用bind()时AF_UNIX套接字的正确长度是多少?

9

bind()需要您提供的sockaddr结构体的长度。 对于unix sockets,使用sockaddr_un

当您填写了sun_path成员时,计算其长度的正确方法是什么?我见过多种方法:

socklen_t len = sizeof(sockaddr_un);
socklen_t len = offsetof(sockaddr_un,sun_path) + strlen(addr.sun_path);
socklen_t len = offsetof(sockaddr_un,sun_path) + strlen(addr.sun_path) + 1;
socklen_t len = sizeof(sockaddr.sun_family  ) + strlen(addr.sun_path);

还有其他方法。只取sizeof(sockaddr_un)可以吗?或者有更合适的方式吗?

2个回答

10

你应该使用SUN_LEN宏。这是来自我的Mac上的/usr/include/sys/un.h

#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
/* actual length of an initialized sockaddr_un */
#define SUN_LEN(su) \
        (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
#endif  /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */

编辑:

是的,它不具备可移植性和POSIX兼容性,但我们在真实的平台上工作,不是吗?

问题是你必须将路径以零结尾,并且上面的代码和sizeof( struct sockaddr_un )一样好,但在从用户复制到内核时可能会节省一些字节,但浪费一些strlen周期。

看看Linux如何处理该长度(来自http://lxr.linux.no/linux+v2.6.32/net/unix/af_unix.c#L200):

static int unix_mkname(struct sockaddr_un *sunaddr, int len, unsigned *hashp)
{
    if (len <= sizeof(short) || len > sizeof(*sunaddr))
        return -EINVAL;
    if (!sunaddr || sunaddr->sun_family != AF_UNIX)
        return -EINVAL;
    if (sunaddr->sun_path[0]) {
        /*
         * This may look like an off by one error but it is a bit more
         * subtle. 108 is the longest valid AF_UNIX path for a binding.
         * sun_path[108] doesnt as such exist.  However in kernel space
         * we are guaranteed that it is a valid memory location in our
         * kernel address buffer.
         */
        ((char *)sunaddr)[len] = 0;
        len = strlen(sunaddr->sun_path)+1+sizeof(short);
        return len;
    }

    *hashp = unix_hash_fold(csum_partial(sunaddr, len, 0));
    return len;
}

这里,len 直接来自于第三个参数传递到 bind 系统调用,但是 sunaddr 已经被复制到内核空间并具有该长度。你不能拥有比 sizeof(sockadd_un) 更长的地址。内核无论如何都会执行 strlen

所以,在整个过程中使用 sizeof(sockaddr_un) 可能更加安全,但是告诉内核确切的长度也不会有任何问题。


3
我不知道SUN_LEN,也没有对你进行投票。但我对SUN_LEN的来源很感兴趣,所以我谷歌了一下,并找到了这篇非常有信息量的帖子:http://mail-index.netbsd.org/tech-net/2006/10/11/0008.html - Stéphan Kochen
2
SUN_LEN 不具可移植性,也不在 POSIX 中。 - mark4o
@nos,反过来说-strlen是为正常路径调用的。 - Nikolai Fetissov

4

sizeof(struct sockaddr_un)很好。

看一下unix(7)的手册。字段sun_path是结构体的一部分,它是一个字符数组。


3
是的,man 2 bind也有绑定Unix域套接字的示例。 http://www.opengroup.org/onlinepubs/9699919799/functions/bind.html - mark4o
那个例子只是将 sockaddr_un 结构体清零,然后只填充 sun_familysun_path。这会在有 sun_len 字段的系统上将结构体的 sun_len 字段设置为零(可能包括所有的BSD,包括MacOS)。这是可移植的吗? - Lassi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接