For Socket, this allows abstract sockets by allowing first character to
be a NUL.  I didn't do it for UNIXClient and UNIXServer, I don't know
what to do about code like:

    init_sock(sock, fd);
    GetOpenFile(sock, fptr);
    if (server) {
        fptr->path = strdup(RSTRING(path)->ptr);
    }

The "paths shall not contain nul" is fairly deep here. Maybe its OK,
there is no need to use those helper classes.


Index: ext/socket/socket.c
===================================================================
--- ext/socket/socket.c	(revision 11744)
+++ ext/socket/socket.c	(working copy)
@@ -2193,7 +2193,7 @@
     GetOpenFile(sock, fptr);
 
     if (getpeername(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0)
-	rb_sys_fail("getsockname(2)");
+	rb_sys_fail("getpeername(2)");
     return unixaddr(&addr, len);
 }
 #endif
@@ -3769,27 +3769,43 @@
 
     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);
+    if(RSTRING(path)->len > 1 && !sun_path[0] && sun_path[1]) {
+	/* special checking for case of a path with a single leading NUL,
+	 * a linux abstract path */
+	if(RSTRING(path)->len - 1 != strlen(sun_path + 1))
+	    rb_raise(rb_eArgError, "string contains null byte");
+    } else {
+	sun_path = StringValueCStr(path);
+    }
+
+    if (sizeof(sockaddr.sun_path) <= RSTRING(path)->len) {
         rb_raise(rb_eArgError, "too long unix socket path (max: %dbytes)",
-            (int)sizeof(sockaddr.sun_path)-1);
+        (int)sizeof(sockaddr.sun_path) - 1);
     }
-    strncpy(sockaddr.sun_path, sun_path, sizeof(sockaddr.sun_path)-1);
+    memcpy(sockaddr.sun_path, sun_path, RSTRING(path)->len);
     addr = rb_str_new((char*)&sockaddr, sizeof(sockaddr));
     OBJ_INFECT(addr, path);
 
     return addr;
 }
 
+#define SA_UN_MIN_LENGTH ((long) (((struct sockaddr_un *) 0)->sun_path))
+
 static VALUE
 sock_s_unpack_sockaddr_un(self, addr)
     VALUE self, addr;
 {
     struct sockaddr_un * sockaddr;
     char *sun_path;
+    long sun_path_len;
     VALUE path;
 
     sockaddr = (struct sockaddr_un*)StringValuePtr(addr);
+    if(RSTRING(addr)->len < SA_UN_MIN_LENGTH) {
+	rb_raise(rb_eTypeError, "too short sockaddr_un - %ld shorter than %d",
+		RSTRING(addr)->len, SA_UN_MIN_LENGTH);
+    }
     if (((struct sockaddr *)sockaddr)->sa_family != AF_UNIX) {
         rb_raise(rb_eArgError, "not an AF_UNIX sockaddr");
     }
@@ -3797,13 +3813,12 @@
         rb_raise(rb_eTypeError, "too long sockaddr_un - %ld longer than %d",
 		 RSTRING(addr)->len, sizeof(struct sockaddr_un));
     }
-    sun_path = unixpath(sockaddr, RSTRING(addr)->len);
-    if (sizeof(struct sockaddr_un) == RSTRING(addr)->len &&
-        sun_path == sockaddr->sun_path &&
-        sun_path + strlen(sun_path) == RSTRING(addr)->ptr + RSTRING(addr)->len) {
-        rb_raise(rb_eArgError, "sockaddr_un.sun_path not NUL terminated");
-    }
-    path = rb_str_new2(sun_path);
+    sun_path = sockaddr->sun_path;
+    sun_path_len = RSTRING(addr)->len - SA_UN_MIN_LENGTH;
+    /* Don't include trailing nul in path, but allow a starting nul. */
+    while(sun_path_len > 0 && sun_path[sun_path_len-1] == '\0')
+	sun_path_len--;
+    path = rb_str_new(sun_path, sun_path_len);
     OBJ_INFECT(path, addr);
     return path;
 }
Index: test/socket/test_unix.rb
===================================================================
--- test/socket/test_unix.rb	(revision 11744)
+++ test/socket/test_unix.rb	(working copy)
@@ -118,9 +118,23 @@
 
   def test_nul
     assert_raise(ArgumentError) { Socket.sockaddr_un("a\0b") }
+    assert_raise(ArgumentError) { Socket.sockaddr_un("\0a\0") }
+    assert_raise(ArgumentError) { Socket.sockaddr_un("\0\0") }
+    assert_raise(ArgumentError) { Socket.sockaddr_un("\0\0a") }
     assert_raise(ArgumentError) { UNIXServer.new("a\0b") }
   end
 
+  def test_invalid
+    assert_raise(TypeError) { Socket.unpack_sockaddr_un "" }
+  end
+
+  def test_unpack
+    assert_equal("a", Socket.unpack_sockaddr_un(Socket.sockaddr_un("a").chop))
+    assert_equal("a", Socket.unpack_sockaddr_un(Socket.sockaddr_un("a").chop.chop.chop))
+    assert_equal("\0a", Socket.unpack_sockaddr_un(Socket.sockaddr_un("\0a").chop))
+    assert_equal("\0a", Socket.unpack_sockaddr_un(Socket.sockaddr_un("\0a").chop.chop.chop))
+  end
+
   def test_dgram_pair
     s1, s2 = UNIXSocket.pair(Socket::SOCK_DGRAM)
     assert_raise(Errno::EAGAIN) { s1.recv_nonblock(10) }