[前][次][番号順一覧][スレッド一覧]

ruby-changes:26887

From: shugo <ko1@a...>
Date: Fri, 25 Jan 2013 17:15:39 +0900 (JST)
Subject: [ruby-changes:26887] shugo:r38939 (trunk): * ext/socket/raddrinfo.c (rsock_unixpath_len, init_unix_addrinfo),

shugo	2013-01-25 17:15:26 +0900 (Fri, 25 Jan 2013)

  New Revision: 38939

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=38939

  Log:
    * ext/socket/raddrinfo.c (rsock_unixpath_len, init_unix_addrinfo),
      ext/socket/unixsocket.c (unixsock_connect_internal,
      rsock_init_unixsock): calculate the correct address length of
      an abstract socket.  Without this fix, sizeof(struct sockaddr_un)
      is specified as the length of an abstract socket for bind(2) or
      connect(2), so the address of the socket is filled with extra NUL
      characters.  See unix(7) for details.
    
    * ext/socket/lib/socket.rb (unix_server_socket): don't access the
      file system if the platform is Linux and path starts with NUL,
      which means that the socket is an abstract socket.
    
    * test/socket/test_unix.rb: related test.

  Modified files:
    trunk/ChangeLog
    trunk/ext/socket/lib/socket.rb
    trunk/ext/socket/raddrinfo.c
    trunk/ext/socket/rubysocket.h
    trunk/ext/socket/unixsocket.c
    trunk/test/socket/test_unix.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 38938)
+++ ChangeLog	(revision 38939)
@@ -1,3 +1,19 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Fri Jan 25 16:47:31 2013  Shugo Maeda  <shugo@r...>
+
+	* ext/socket/raddrinfo.c (rsock_unixpath_len, init_unix_addrinfo),
+	  ext/socket/unixsocket.c (unixsock_connect_internal,
+	  rsock_init_unixsock): calculate the correct address length of
+	  an abstract socket.  Without this fix, sizeof(struct sockaddr_un)
+	  is specified as the length of an abstract socket for bind(2) or
+	  connect(2), so the address of the socket is filled with extra NUL
+	  characters.  See unix(7) for details.
+
+	* ext/socket/lib/socket.rb (unix_server_socket): don't access the
+	  file system if the platform is Linux and path starts with NUL,
+	  which means that the socket is an abstract socket.
+
+	* test/socket/test_unix.rb: related test.
+
 Fri Jan 25 13:02:27 2013  Eric Hodel  <drbrain@s...>
 
 	* lib/drb/drb.rb:  Updated documentation based on patch from Vincent
Index: ext/socket/rubysocket.h
===================================================================
--- ext/socket/rubysocket.h	(revision 38938)
+++ ext/socket/rubysocket.h	(revision 38939)
@@ -240,6 +240,7 @@ int rsock_revlookup_flag(VALUE revlookup https://github.com/ruby/ruby/blob/trunk/ext/socket/rubysocket.h#L240
 #ifdef HAVE_SYS_UN_H
 VALUE rsock_unixpath_str(struct sockaddr_un *sockaddr, socklen_t len);
 VALUE rsock_unixaddr(struct sockaddr_un *sockaddr, socklen_t len);
+socklen_t rsock_unixpath_len(VALUE path);
 #endif
 
 int rsock_socket(int domain, int type, int proto);
Index: ext/socket/raddrinfo.c
===================================================================
--- ext/socket/raddrinfo.c	(revision 38938)
+++ ext/socket/raddrinfo.c	(revision 38939)
@@ -441,6 +441,23 @@ rsock_unixaddr(struct sockaddr_un *socka https://github.com/ruby/ruby/blob/trunk/ext/socket/raddrinfo.c#L441
     return rb_assoc_new(rb_str_new2("AF_UNIX"),
                         rsock_unixpath_str(sockaddr, len));
 }
+
+socklen_t
+rsock_unixpath_len(VALUE path)
+{
+#ifdef __linux__
+    if (RSTRING_PTR(path)[0] == '\0') {
+	/* abstract namespace; see unix(7) for details. */
+	return (socklen_t) offsetof(struct sockaddr_un, sun_path) +
+	    RSTRING_LEN(path);
+    }
+    else {
+#endif
+	return (socklen_t) sizeof(struct sockaddr_un);
+#ifdef __linux__
+    }
+#endif
+}
 #endif
 
 struct hostent_arg {
@@ -769,6 +786,7 @@ static void https://github.com/ruby/ruby/blob/trunk/ext/socket/raddrinfo.c#L786
 init_unix_addrinfo(rb_addrinfo_t *rai, VALUE path, int socktype)
 {
     struct sockaddr_un un;
+    socklen_t len;
 
     StringValue(path);
 
@@ -782,7 +800,8 @@ init_unix_addrinfo(rb_addrinfo_t *rai, V https://github.com/ruby/ruby/blob/trunk/ext/socket/raddrinfo.c#L800
     un.sun_family = AF_UNIX;
     memcpy((void*)&un.sun_path, RSTRING_PTR(path), RSTRING_LEN(path));
 
-    init_addrinfo(rai, (struct sockaddr *)&un, (socklen_t)sizeof(un),
+    len = rsock_unixpath_len(path);
+    init_addrinfo(rai, (struct sockaddr *)&un, len,
 		  PF_UNIX, socktype, 0, Qnil, Qnil);
 }
 #endif
Index: ext/socket/lib/socket.rb
===================================================================
--- ext/socket/lib/socket.rb	(revision 38938)
+++ ext/socket/lib/socket.rb	(revision 38939)
@@ -791,12 +791,14 @@ class Socket < BasicSocket https://github.com/ruby/ruby/blob/trunk/ext/socket/lib/socket.rb#L791
   #   }
   #
   def self.unix_server_socket(path)
-    begin
-      st = File.lstat(path)
-    rescue Errno::ENOENT
-    end
-    if st && st.socket? && st.owned?
-      File.unlink path
+    if !unix_socket_abstract_name?(path)
+      begin
+        st = File.lstat(path)
+      rescue Errno::ENOENT
+      end
+      if st && st.socket? && st.owned?
+        File.unlink path
+      end
     end
     s = Addrinfo.unix(path).listen
     if block_given?
@@ -804,13 +806,23 @@ class Socket < BasicSocket https://github.com/ruby/ruby/blob/trunk/ext/socket/lib/socket.rb#L806
         yield s
       ensure
         s.close if !s.closed?
-        File.unlink path
+        if !unix_socket_abstract_name?(path)
+          File.unlink path
+        end
       end
     else
       s
     end
   end
 
+  class << self
+    private
+
+    def unix_socket_abstract_name?(path)
+      /linux/ =~ RUBY_PLATFORM && /\A\0/ =~ path
+    end
+  end
+
   # creates a UNIX socket server on _path_.
   # It calls the block for each socket accepted.
   #
Index: ext/socket/unixsocket.c
===================================================================
--- ext/socket/unixsocket.c	(revision 38938)
+++ ext/socket/unixsocket.c	(revision 38939)
@@ -13,6 +13,7 @@ https://github.com/ruby/ruby/blob/trunk/ext/socket/unixsocket.c#L13
 #ifdef HAVE_SYS_UN_H
 struct unixsock_arg {
     struct sockaddr_un *sockaddr;
+    socklen_t sockaddrlen;
     int fd;
 };
 
@@ -21,13 +22,14 @@ unixsock_connect_internal(VALUE a) https://github.com/ruby/ruby/blob/trunk/ext/socket/unixsocket.c#L22
 {
     struct unixsock_arg *arg = (struct unixsock_arg *)a;
     return (VALUE)rsock_connect(arg->fd, (struct sockaddr*)arg->sockaddr,
-			        (socklen_t)sizeof(*arg->sockaddr), 0);
+			        arg->sockaddrlen, 0);
 }
 
 VALUE
 rsock_init_unixsock(VALUE sock, VALUE path, int server)
 {
     struct sockaddr_un sockaddr;
+    socklen_t sockaddrlen;
     int fd, status;
     rb_io_t *fptr;
 
@@ -44,14 +46,16 @@ rsock_init_unixsock(VALUE sock, VALUE pa https://github.com/ruby/ruby/blob/trunk/ext/socket/unixsocket.c#L46
             RSTRING_LEN(path), (int)sizeof(sockaddr.sun_path));
     }
     memcpy(sockaddr.sun_path, RSTRING_PTR(path), RSTRING_LEN(path));
+    sockaddrlen = rsock_unixpath_len(path);
 
     if (server) {
-        status = bind(fd, (struct sockaddr*)&sockaddr, (socklen_t)sizeof(sockaddr));
+        status = bind(fd, (struct sockaddr*)&sockaddr, sockaddrlen);
     }
     else {
 	int prot;
 	struct unixsock_arg arg;
 	arg.sockaddr = &sockaddr;
+	arg.sockaddrlen = sockaddrlen;
 	arg.fd = fd;
         status = (int)rb_protect(unixsock_connect_internal, (VALUE)&arg, &prot);
 	if (prot) {
Index: test/socket/test_unix.rb
===================================================================
--- test/socket/test_unix.rb	(revision 38938)
+++ test/socket/test_unix.rb	(revision 38939)
@@ -531,4 +531,34 @@ class TestSocket_UNIXSocket < Test::Unit https://github.com/ruby/ruby/blob/trunk/test/socket/test_unix.rb#L531
     }
   end
 
+  def test_abstract_unix_server
+    return if /linux/ !~ RUBY_PLATFORM
+    name = "\0ruby-test_unix"
+    s0 = nil
+    UNIXServer.open(name) {|s|
+      assert_equal(name, s.local_address.unix_path)
+      s0 = s
+      UNIXSocket.open(name) {|c|
+        sock = s.accept
+        assert_equal(name, c.remote_address.unix_path)
+      }
+    }
+    assert(s0.closed?)
+  end
+
+  def test_abstract_unix_server_socket
+    return if /linux/ !~ RUBY_PLATFORM
+    name = "\0ruby-test_unix"
+    s0 = nil
+    Socket.unix_server_socket(name) {|s|
+      assert_equal(name, s.local_address.unix_path)
+      s0 = s
+      Socket.unix(name) {|c|
+        sock, = s.accept
+        assert_equal(name, c.remote_address.unix_path)
+      }
+    }
+    assert(s0.closed?)
+  end
+
 end if defined?(UNIXSocket) && /cygwin/ !~ RUBY_PLATFORM

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]