Perl / perl5

🐪 The Perl programming language
https://dev.perl.org/perl5/
Other
1.92k stars 551 forks source link

IO::Socket::INET does not bind UDP sockets to ephemeral ports #11809

Open p5pRT opened 12 years ago

p5pRT commented 12 years ago

Migrated from rt.perl.org#106370 (status was 'open')

Searchable as RT106370$

p5pRT commented 12 years ago

From imran.patel@gmail.com

Created by imran.patel@gmail.com

Perls' IO​::Socket​::INET core library module has a bug which prevents creating a socket that is bound to an OS-selected ephemeral port.

The standard way to bind to an available port is to set the port to zero when calling bind(). With IO​::Socket​::INET\, you'd do something like​:

my @​socket_args = (   Proto => 'udp'\,   LocalPort => 0\, ); my $sock = IO​::Socket​::INET->new( @​socket_args ) or die "socket​: $@​";

Except this doesn't work. Here's the offending bit from IO/Socket/INET.pm​:

if($lport || ($laddr ne INADDR_ANY) || exists $arg->{Listen}) {   $sock->bind($lport || 0\, $laddr) or   return _error($sock\, $!\, "$!"); }

In reality\, $lport is undef instead of zero because of another bug\, but it doesn't matter either way. I verified that this if branch is not taken by using the debugger.

The same thing is true for TCP. But since the Listen option is usually supplied for TCP\, it makes the if expression evaluate to true and the bind() happens by accident.

I think the best workaround is to use the core Socket.pm module directly.

Perl Info ``` Flags: category=library severity=low This perlbug was built using Perl v5.8.8 in the Red Hat build system. It is being executed now by Perl v5.8.8 - Thu Jul 2 05:48:14 EDT 2009. Site configuration information for perl v5.8.8: Configured by Red Hat, Inc. at Thu Jul 2 05:48:14 EDT 2009. Summary of my perl5 (revision 5 version 8 subversion 8) configuration: Platform: osname=linux, osvers=2.6.18-128.1.10.el5, archname=i386-linux-thread-multi uname='linux ls20-bc1-14.build.redhat.com 2.6.18-128.1.10.el5 #1 smp wed apr 29 13:53:08 edt 2009 i686 athlon i386 gnulinux ' config_args='-des -Doptimize=-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m32 -march=i386 -mtune=generic -fasynchronous-unwind-tables -Dversion=5.8.8 -Dmyhostname=localhost -Dperladmin=root@localhost -Dcc=gcc -Dcf_by=Red Hat, Inc. -Dinstallprefix=/usr -Dprefix=/usr -Darchname=i386-linux-thread-multi -Dvendorprefix=/usr -Dsiteprefix=/usr -Duseshrplib -Dusethreads -Duseithreads -Duselargefiles -Dd_dosuid -Dd_semctl_semun -Di_db -Ui_ndbm -Di_gdbm -Di_shadow -Di_syslog -Dman3ext=3pm -Duseperlio -Dinstallusrbinperl=n -Ubincompat5005 -Uversiononly -Dpager=/usr/bin/less -isr -Dd_gethostent_r_proto -Ud_endhostent_r_proto -Ud_sethostent_r_proto -Ud_endprotoent_r_proto -Ud_setprotoent_r_proto -Ud_endservent_r_proto -Ud_setservent_r_proto -Dinc_version_list=5.8.7 5.8.6 5.8.5 -Dscriptdir=/usr/bin' hint=recommended, useposix=true, d_sigaction=define usethreads=define use5005threads=undef useithreads=define usemultiplicity=define useperlio=define d_sfio=undef uselargefiles=define usesocks=undef use64bitint=undef use64bitall=undef uselongdouble=undef usemymalloc=n, bincompat5005=undef Compiler: cc='gcc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -fno-strict-aliasing -pipe -Wdeclaration-after-statement -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/gdbm', optimize='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m32 -march=i386 -mtune=generic -fasynchronous-unwind-tables', cppflags='-D_REENTRANT -D_GNU_SOURCE -fno-strict-aliasing -pipe -Wdeclaration-after-statement -I/usr/local/include -I/usr/include/gdbm' ccversion='', gccversion='4.1.2 20080704 (Red Hat 4.1.2-44)', gccosandvers='' intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234 d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12 ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8 alignbytes=4, prototype=define Linker and Libraries: ld='gcc', ldflags =' -L/usr/local/lib' libpth=/usr/local/lib /lib /usr/lib libs=-lresolv -lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lpthread -lc perllibs=-lresolv -lnsl -ldl -lm -lcrypt -lutil -lpthread -lc libc=/lib/libc-2.5.so, so=so, useshrplib=true, libperl=libperl.so gnulibc_version='2.5' Dynamic Linking: dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E -Wl,-rpath,/usr/lib/perl5/5.8.8/i386-linux-thread-multi/CORE' cccdlflags='-fPIC', lddlflags='-shared -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m32 -march=i386 -mtune=generic -fasynchronous-unwind-tables -L/usr/local/lib' Locally applied patches: @INC for perl v5.8.8: /usr/lib/perl5/site_perl/5.8.8/i386-linux-thread-multi /usr/lib/perl5/site_perl/5.8.8 /usr/lib/perl5/site_perl/5.8.5 /usr/lib/perl5/site_perl /usr/lib/perl5/vendor_perl/5.8.8/i386-linux-thread-multi /usr/lib/perl5/vendor_perl/5.8.8 /usr/lib/perl5/vendor_perl/5.8.5 /usr/lib/perl5/vendor_perl /usr/lib/perl5/5.8.8/i386-linux-thread-multi /usr/lib/perl5/5.8.8 . Environment for perl v5.8.8: HOME=/home/imranp LANG=en_US.UTF-8 LANGUAGE (unset) LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib:/usr/lib/xorg:/usr/lib/xorg LOGDIR (unset) PATH=/usr/local/bin:/home/imranp/bin:/home/imranp/usr/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/sbin:/usr/X11R6/bin:/usr/X11/bin:/usr/kerberos/bin PERL_BADLANG (unset) SHELL=/bin/zsh ```
p5pRT commented 12 years ago

From @jkeenan

On Thu Dec 15 16​:15​:45 2011\, imran.patel@​gmail.com wrote​:

This is a bug report for perl from imran.patel@​gmail.com\, generated with the help of perlbug 1.35 running under perl v5.8.8.

Perls' IO​::Socket​::INET core library module has a bug which prevents creating a socket that is bound to an OS-selected ephemeral port.

The standard way to bind to an available port is to set the port to zero when calling bind(). With IO​::Socket​::INET\, you'd do something like​:

my @​socket_args = ( Proto => 'udp'\, LocalPort => 0\, ); my $sock = IO​::Socket​::INET->new( @​socket_args ) or die "socket​: $@​";

Except this doesn't work.

Can you be a bit more explicit as to what doesn't work?

Also\, can you try this on a currently supported version of Perl (5.12 or 5.14)?

Here's the offending bit from IO/Socket/INET.pm​:

if($lport || ($laddr ne INADDR_ANY) || exists $arg->{Listen}) { $sock->bind($lport || 0\, $laddr) or return _error($sock\, $!\, "$!"); }

In reality\, $lport is undef instead of zero because of another bug\, but it doesn't matter either way. I verified that this if branch is not taken by using the debugger.

The same thing is true for TCP. But since the Listen option is usually supplied for TCP\, it makes the if expression evaluate to true and the bind() happens by accident.

I think the best workaround is to use the core Socket.pm module directly.

Thank you very much. Jim Keenan

p5pRT commented 12 years ago

The RT System itself - Status changed from 'new' to 'open'

p5pRT commented 12 years ago

From @ikegami

On Fri\, Jan 6\, 2012 at 7​:12 PM\, James E Keenan via RT \< perlbug-followup@​perl.org> wrote​:

On Thu Dec 15 16​:15​:45 2011\, imran.patel@​gmail.com wrote​:

This is a bug report for perl from imran.patel@​gmail.com\, generated with the help of perlbug 1.35 running under perl v5.8.8.

Perls' IO​::Socket​::INET core library module has a bug which prevents creating a socket that is bound to an OS-selected ephemeral port.

The standard way to bind to an available port is to set the port to zero when calling bind(). With IO​::Socket​::INET\, you'd do something like​:

my @​socket_args = ( Proto => 'udp'\, LocalPort => 0\, ); my $sock = IO​::Socket​::INET->new( @​socket_args ) or die "socket​: $@​";

Except this doesn't work.

Can you be a bit more explicit as to what doesn't work?

He wants bind() to be called\, and it isn't. So the real question is​: Why does he want bind() to be called?

Maybe one can't query the socket to find out which port it's bound to until bind() is called or a connection is established? That would be a problem for server sockets (which would explain why bind() is explicitly called when Listen is provided).

Also\, can you try this on a currently supported version of Perl (5.12 or

5.14)?

The statement in question is still present unchanged in the latest distribution of IO (1.25).

- Eric

p5pRT commented 12 years ago

From imran.patel@gmail.com

my @​socket_args = (     Proto => 'udp'\,     LocalPort => 0\, ); my $sock = IO​::Socket​::INET->new( @​socket_args ) or die "socket​: $@​";

Except this doesn't work.

Can you be a bit more explicit as to what doesn't work? The UDP socket won't bind to an ephemeral port.

Also\, can you try this on a currently supported version of Perl (5.12 or 5.14)? Ok\, will do.

Imran

p5pRT commented 12 years ago

From @jkeenan

On Sun Jan 08 14​:57​:13 2012\, imran.patel@​gmail.com wrote​:

my @​socket_args = ( � � Proto => 'udp'\, � � LocalPort => 0\, ); my $sock = IO​::Socket​::INET->new( @​socket_args ) or die "socket​: $@​";

Except this doesn't work.

Can you be a bit more explicit as to what doesn't work? The UDP socket won't bind to an ephemeral port.

Also\, can you try this on a currently supported version of Perl (5.12 or 5.14)? Ok\, will do.

Have you had any opportunity to try this on a supported version of Perl?

Thank you very much. Jim Keenan

p5pRT commented 12 years ago

From imran.patel@gmail.com

No I haven't had access to anything more modern than 5.8.8. Do you want me to provide a test case?

Imran

On Fri\, Mar 23\, 2012 at 6​:29 PM\, James E Keenan via RT \< perlbug-followup@​perl.org> wrote​:

On Sun Jan 08 14​:57​:13 2012\, imran.patel@​gmail.com wrote​:

my @​socket_args = ( � � Proto => 'udp'\, � � LocalPort => 0\, ); my $sock = IO​::Socket​::INET->new( @​socket_args ) or die "socket​: $@​";

Except this doesn't work.

Can you be a bit more explicit as to what doesn't work? The UDP socket won't bind to an ephemeral port.

Also\, can you try this on a currently supported version of Perl (5.12 or 5.14)? Ok\, will do.

Have you had any opportunity to try this on a supported version of Perl?

Thank you very much. Jim Keenan

richardleach commented 4 years ago

This behaviour is unchanged in the version of IO::Socket::INET bundled with v5.28.1.

However, IO::Socket::IP exhibits the behaviour that the op wanted.

use IO::Socket::IP;
my @socket_args = (
  Proto => 'udp',
  LocalPort => 0,
);
my $sock = IO::Socket::IP->new( @socket_args ) or die "socket​: $@​";
print $sock->sockport."\n";

e.g.

root@debian:~/PerlExperiments# perl socky.pl
48185
root@debian:~/PerlExperiments# perl socky.pl
33403
root@debian:~/PerlExperiments# perl socky.pl
58575
root@debian:~/PerlExperiments# perl socky.pl
35671

Is there therefore still a perceived need to keep this ticket open with the intention of changing the longstanding behaviour of IO::Socket::INET?

xsawyerx commented 4 years ago

IO::Socket::IP is available with core, so that does give a good way out without jumping to CPAN. But that doesn't mean we should fix this. Can the same approach that IO::Socket::IP takes be used for this?