develooper Front page | perl.perl5.porters | Postings from October 2003

Bug: sockaddr_un() and abstract unix domain sockets

Thread Next
From:
Alex Hudson
Date:
October 4, 2003 11:43
Subject:
Bug: sockaddr_un() and abstract unix domain sockets
Message ID:
1065292973.17297.34.camel@debian
Hi porters

Attempting to write some IPC code this weekend was slightly stymied, by
the need to connect to an abstract unix domain socket using Perl - it
doesn't appear to work.

Abstract unix domain sockets are like unix domain sockets, but don't
exist on the filesystem fully (although they do appear to inherit
permissions). They exist on Linux versions 2.2+. The following test I
wrote appears to show my problem on 5.8.1:

--- perl-5.8.1/ext/Socket/t/Socket.t	2003-09-02 14:40:15.000000000 +0100
+++ perl-5.8.1-alex/ext/Socket/t/Socket.t	2003-10-04 18:56:14.000000000
+0100
@@ -14,7 +14,7 @@
 	
 use Socket;
 
-print "1..16\n";
+print "1..17\n";
 
 $has_echo = $^O ne 'MSWin32';
 $alarmed = 0;
@@ -149,3 +149,18 @@
 
 eval { sockaddr_family("") };
 print (($@ =~ /^Bad arg length for Socket::sockaddr_family, length is
0, should be at least \d+/) ? "ok 16\n" : "not ok 16\n");
+
+if ($^O eq 'linux') {
+    # see we can handle abstract sockets
+    my $test_abstract_socket = chr(0) . '/tmp/test-perl-socket';
+    my $addr = sockaddr_un ($test_abstract_socket);
+    my ($path) = sockaddr_un ($addr);
+    if ($test_abstract_socket eq $path) {
+        print "ok 17\n";
+    } else {
+        print "not ok 17\n";
+    }
+} else {
+    # doesn't have abstract socket support
+    print "ok 17 - skipped on this platform\n";
+}


Hopefully that shows roughly what the problem is. The special
incantation to make something 'abstract' is to put the 'abstract' byte
at the start of the filename - hence the chr(0) line. sockaddr_un fails
to understand this, and passes back something which isn't valid. The
lovely hack that I have been using to get around this issue thus far has
been along the lines of:

        my $path = chr(0) . '/tmp/socket';
        my $badpath = '!' x length($path);
        my $addr = sockaddr_un ($path);
        $addr =~ s/$badpath/$path/;

Yummy :) 

The following patch 'fixes' Perl (i.e., it passes the above test ;) over
here:

--- perl-5.8.1/ext/Socket/Socket.pm	2003-09-02 14:40:15.000000000 +0100
+++ perl-5.8.1-alex/ext/Socket/Socket.pm	2003-10-04 18:40:12.000000000
+0100
@@ -380,7 +380,8 @@
         unpack_sockaddr_un(@_);
     } else {
 	croak "usage:   sun_sv = sockaddr_un(filename)" unless @_ == 1;
-        pack_sockaddr_un(@_);
+        my $path_len = length ($_[0]);
+        pack_sockaddr_un($path_len, @_);
     }
 }
 
--- perl-5.8.1/ext/Socket/Socket.xs	2003-09-02 14:40:15.000000000 +0100
+++ perl-5.8.1-alex/ext/Socket/Socket.xs	2003-10-04 18:41:32.000000000
+0100
@@ -297,17 +297,16 @@
 	ST(0) = sv_2mortal(newSViv(((struct
sockaddr*)sockaddr_pv)->sa_family));
 
 void
-pack_sockaddr_un(pathname)
+pack_sockaddr_un(len, pathname)
+	STRLEN len
 	char *	pathname
 	CODE:
 	{
 #ifdef I_SYS_UN
 	struct sockaddr_un sun_ad; /* fear using sun */
-	STRLEN len;
 
 	Zero( &sun_ad, sizeof sun_ad, char );
 	sun_ad.sun_family = AF_UNIX;
-	len = strlen(pathname);
 	if (len > sizeof(sun_ad.sun_path))
 	    len = sizeof(sun_ad.sun_path);
 #  ifdef OS2	/* Name should start with \socket\ and contain
backslashes! */
@@ -372,8 +371,13 @@
 			AF_UNIX);
 	}
 	e = (char*)addr.sun_path;
-	while (*e && e < (char*)addr.sun_path + sizeof addr.sun_path)
+	while ((*e || (e == (char*)addr.sun_path)) 
+	       && e < (char*)addr.sun_path + sizeof addr.sun_path) 
 	    ++e;
+	if ((e == (char*)addr.sun_path + 1) && (*addr.sun_path == 0)) {
+	     e = (char*)addr.sun_path;
+	}
+		
 	ST(0) = sv_2mortal(newSVpvn(addr.sun_path, e - (char*)addr.sun_path));
 #else
 	ST(0) = (SV *) not_here("unpack_sockaddr_un");


The problem is, of course, the cunning use of chr(0) as both "this the
end of my character array which I pretend is a string", and "this is an
abstract filename". I'm guessing this breakage was probably intentional.
Now, my C skillz are not up do finding out whether or not a "string"
ends at 0, or whether it goes on and ends at a different 0 - basically,
how to differentiate an abstract filename and the empty string. But,
this might just be an uninteresting corner case. This is why I worked
out the string length in Perl and broke pack_sockaddr_un - I'm guessing
this makes the patch unusable, but it serves as an example.

The logic in the second bit - the while() loop - tries to do things more
correctly, but it ain't pretty and possibly not even right. 

Cheers,

Alex.


Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About