【问题标题】:Proper length of an AF_UNIX socket when calling bind()调用 bind() 时 AF_UNIX 套接字的正确长度
【发布时间】:2011-01-19 10:25:14
【问题描述】:

bind() 需要你给它的 sockaddr 结构的长度。 现在,对于 unix 套接字,使用 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) 可以吗?或者正确的方法是什么?

【问题讨论】:

    标签: c unix sockets


    【解决方案1】:

    您应该使用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 ) 可能更安全,但告诉内核确切长度也无妨。

    【讨论】:

    • 我不知道SUN_LEN,也没有投票给你。但我对 SUN_LEN 的来源很感兴趣,所以我用 Google 搜索了一下,发现这篇内容非常丰富的帖子:mail-index.netbsd.org/tech-net/2006/10/11/0008.html
    • SUN_LEN 不可移植,并且不在 POSIX 中。
    • @nos,反过来 - strlen 被称为正常路径。
    【解决方案2】:

    sizeof(struct sockaddr_un) 很好。

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

    【讨论】:

    • 是的,man 2 bind 也有一个绑定 Unix 域套接字的示例。 opengroup.org/onlinepubs/9699919799/functions/bind.html
    • 该示例只是将sockaddr_un 结构归零,然后仅填充sun_familysun_path。这会将结构的sun_len 字段在拥有它的系统上设置为零(可能是所有BSD,包括MacOS)。那是便携式的吗?
    猜你喜欢
    • 1970-01-01
    • 2012-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-03
    • 2019-06-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多