On Wed, Feb 14, 2007 at 12:10:37AM +0900, noreply / rubyforge.org wrote:
> On 1/25/07, Sjoerd Simons <sjoerd / sp...> wrote:
> > You connect to an abstract socket by specifying the first byte of the path as
> > '\0', unfortunately the ruby socket layer doesn't allow you to do this.. It
> > gives you:
> > ArgumentError: string contains null byte

Yes, problem is pack_sockaddr_un() doesn't allow a leading NUL, though
Linux allows this (it appears to be a Linux-only extension). Ruby also
arbitrarily limits path lengths to UNIX_PATH_MAX-1, instead of
UNIX_PATH_MAX.

This patch allows the OS to validate the AF_UNIX address, so ruby does
not have to pre-know what the OS considers acceptable - bind() will fail
if address is unacceptable:

% ./ruby1.8.x -r ./.ext/i686-linux/socket.so -e 'include Socket::Constants; Socket.new(AF_UNIX, SOCK_STREAM, % 0).bind(Socket.pack_sockaddr_un("/"*108))'
-e:1:in `bind': Address already in use - bind(2) (Errno::EADDRINUSE)
        from -e:1


# path length used to be limited to 107, but linux supports 108:

sroberts@pebble:~/s/ruby/svn-ruby-1.8.5% ./ruby1.8.x -r ./.ext/i686-linux/socket.so -e 'include Socket::Constants; Socket.new(AF_UNIX, SOCK_STREAM, 0).bind(Socket.pack_sockaddr_un("y"*108))'
sroberts@pebble:~/s/ruby/svn-ruby-1.8.5% ls -l y*
srwxr-xr-x 1 sroberts users 0 2007-02-13 11:03 yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy=

# leading zero is allowed:

sroberts@pebble:~/s/ruby/svn-ruby-1.8.5% ./ruby1.8.x -r
./.ext/i686-linux/socket.so -e 'include Socket::Constants; Socket.new(AF_UNIX, SOCK_STREAM, 0).bind(Socket.pack_sockaddr_un("\0"+"y"*107))'

% svn diff socket.c  
Index: socket.c
===================================================================
--- socket.c    (revision 11598)
+++ socket.c    (working copy)
@@ -3765,16 +3765,18 @@
 {
     struct sockaddr_un sockaddr;
     char *sun_path;
+    long sun_path_len;
     VALUE addr;
 
     MEMZERO(&sockaddr, struct sockaddr_un, 1);
     sockaddr.sun_family = AF_UNIX;
-    sun_path = StringValueCStr(path);
-    if (sizeof(sockaddr.sun_path) <= strlen(sun_path)) {
+       sun_path = StringValuePtr(path);
+       sun_path_len = RSTRING(path)->len;
+    if (sizeof(sockaddr.sun_path) < sun_path_len) {
         rb_raise(rb_eArgError, "too long unix socket path (max: %dbytes)",
-            (int)sizeof(sockaddr.sun_path)-1);
+            (int)sizeof(sockaddr.sun_path));
     }
-    strncpy(sockaddr.sun_path, sun_path, sizeof(sockaddr.sun_path)-1);
+    memcpy(sockaddr.sun_path, sun_path, sun_path_len);
     addr = rb_str_new((char*)&sockaddr, sizeof(sockaddr));
     OBJ_INFECT(addr, path);