From owner-freebsd-perl@FreeBSD.ORG Tue Jul 7 17:25:25 2009 Return-Path: Delivered-To: freebsd-perl@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 76889106567D for ; Tue, 7 Jul 2009 17:25:25 +0000 (UTC) (envelope-from max@scoubidou.com) Received: from smtpfb1-g21.free.fr (smtpfb1-g21.free.fr [212.27.42.9]) by mx1.freebsd.org (Postfix) with ESMTP id 00E658FC22 for ; Tue, 7 Jul 2009 17:25:23 +0000 (UTC) (envelope-from max@scoubidou.com) Received: from smtp1-g21.free.fr (smtp1-g21.free.fr [212.27.42.1]) by smtpfb1-g21.free.fr (Postfix) with ESMTP id C25A777CA30 for ; Tue, 7 Jul 2009 19:09:10 +0200 (CEST) Received: from smtp1-g21.free.fr (localhost [127.0.0.1]) by smtp1-g21.free.fr (Postfix) with ESMTP id EE009940055; Tue, 7 Jul 2009 19:09:04 +0200 (CEST) Received: from auber.mobigard.com (local.mobigard.com [88.161.118.105]) by smtp1-g21.free.fr (Postfix) with ESMTP id EAB269400F3; Tue, 7 Jul 2009 19:09:01 +0200 (CEST) Message-ID: <4A53812D.8040507@scoubidou.com> Date: Tue, 07 Jul 2009 19:09:01 +0200 From: =?ISO-8859-1?Q?Maxime_Soul=E9?= User-Agent: Thunderbird 2.0.0.22 (X11/20090701) MIME-Version: 1.0 To: freebsd-perl@freebsd.org Content-Type: multipart/mixed; boundary="------------060804070906090906020502" Subject: Bug with UNIX domain sockets on FreeBSD X-BeenThere: freebsd-perl@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: maintainer of a number of perl-related ports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 07 Jul 2009 17:25:25 -0000 This is a multi-part message in MIME format. --------------060804070906090906020502 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Hi, I just submitted a bug report to perlbug concerning FreeBSD, so I copy it to the list too. Best regards, Max. --------------060804070906090906020502 Content-Type: text/plain; name="perl-socket-bug.txt" Content-Transfer-Encoding: 8bit Content-Disposition: inline; filename="perl-socket-bug.txt" Bug with UNIX domain sockets on FreeBSD (at least 7.0 until 7.2 RELEASE). The unpack_sockaddr_un of perl-5.10.0/ext/Socket/Socket.xs does not handle correctly struct sockaddr_un under FreeBSD in some cases. It returns errors like: Bad arg length for Socket::unpack_sockaddr_un, length is 16, should be 106 at ... When the system returns a struct sockaddr_un in getpeername(), it returns the same structure the server gives to bind(). In some cases, servers (X for /tmp/.X11-unix/X0 and devd for /var/run/devd.pipe, for example) give a shortest structure than sizeof(struct sockaddr_un) but with a correct sun_len field. In these cases, sun_path may not be '\0' terminated. Then getpeername() returns a structure that Socket.xs unpack_sockaddr_un() function think wrong sized. But it is not. One can reproduce the problem with the following simple server and client code. Server side : --------------------------------------- use strict; use Socket; my $name = $ARGV[0] or die "usage: $0 server_socket_file"; #my $sun = sockaddr_un($name); # Use a shortest sockaddr_un as FreeBSD allows it for its daemons my $sun = pack('CCA*', length($name)+2, 1, $name); socket(my $fh, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!\n"; bind($fh, $sun) or die "bind: $!\n"; listen($fh, 1) or die "listen: $!\n"; while (1) { accept(my $new_fh, $fh) or die "accept: $!\n"; sleep 1; } --------------------------------------- Client side (note that /var/run/devd.pipe socket is always available on standard FreeBSD installation): --------------------------------------- use strict; use Socket; my $name = $ARGV[0] // '/var/run/devd.pipe'; warn "*** Connecting to $name\n"; my $sun = sockaddr_un($name); socket(my $fh, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!\n"; connect($fh, $sun) or die "connect: $!\n"; my $sockaddr = getpeername($fh) or die "getpeername: $!\n"; my $warn; ($warn = $sockaddr) =~ s/([\x00-\x19\x7f-\xff])/sprintf("<%02x>",ord$1)/ge; warn "hexdump: $warn--\n"; warn "real length: " . length($sockaddr) . "\n"; warn "sun_len field: " . unpack('C', $sockaddr) . "\n"; my $file = unpack_sockaddr_un($sockaddr); warn "unpack: $file--\n"; close $fh; --------------------------------------- I join a patch that correct the problem on FreeBSD. Note that the version in the GIT repository have the same problem. Perhaps the same problem occurs on other BSD (Open, Net, DragonFly), but I don't have any of them to try... Many thanks for your work, dont hesitate to contact me to do some tests if you need... Best regards, Maxime Soulé. Patch to perl-5.10.0/ext/Socket/Socket.xs : --- perl-5.10.0/ext/Socket/Socket.xs.orig 2009-07-07 17:29:59.000000000 +0200 +++ perl-5.10.0/ext/Socket/Socket.xs 2009-07-07 18:25:57.000000000 +0200 @@ -351,19 +351,25 @@ #ifdef I_SYS_UN struct sockaddr_un addr; STRLEN sockaddrlen; + STRLEN sockaddrmaxlen; char * sun_ad = SvPVbyte(sun_sv,sockaddrlen); char * e; +# ifdef __FreeBSD__ + sockaddrmaxlen = addr.sun_len; +# else + sockaddrmaxlen = sizeof(addr); +# endif # ifndef __linux__ /* On Linux sockaddrlen on sockets returned by accept, recvfrom, getpeername and getsockname is not equal to sizeof(addr). */ - if (sockaddrlen != sizeof(addr)) { + if (sockaddrlen != sockaddrmaxlen) { croak("Bad arg length for %s, length is %d, should be %d", "Socket::unpack_sockaddr_un", - sockaddrlen, sizeof(addr)); + sockaddrlen, sockaddrmaxlen); } # endif - Copy( sun_ad, &addr, sizeof addr, char ); + Copy( sun_ad, &addr, sockaddrmaxlen, char ); if ( addr.sun_family != AF_UNIX ) { croak("Bad address family for %s, got %d, should be %d", @@ -372,11 +378,18 @@ AF_UNIX); } e = (char*)addr.sun_path; +# ifdef __FreeBSD__ + /* On FreeBSD sun_path ends not always with a '\0'. + * How do other BSDs work? */ + while (e < (char*)&addr + sockaddrmaxlen && *e) + ++e; +# else /* On Linux, the name of abstract unix domain sockets begins * with a '\0', so allow this. */ while ((*e || (e == addr.sun_path && e[1] && sockaddrlen > 1)) && e < (char*)addr.sun_path + sizeof addr.sun_path) ++e; +# endif ST(0) = sv_2mortal(newSVpvn(addr.sun_path, e - (char*)addr.sun_path)); #else ST(0) = (SV *) not_here("unpack_sockaddr_un"); --------------060804070906090906020502--