Skip Menu |

This queue is for tickets about the IO-Socket-IP CPAN distribution.

Report information
The Basics
Id: 77536
Status: resolved
Priority: 0/
Queue: IO-Socket-IP

People
Owner: Nobody in particular
Requestors: STEPHANJ [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Critical
Broken in: (no value)
Fixed in: 0.11



Subject: The Module dies on Unexpected Parameters to Constructor
Hi, Today i had to suffer from an nasty bug with your module.. I tried to use IO::Socket::SSL with IO::Socket::IP. As soon as i made it work my app crashed with the following error: perl -E 'use IO::Socket::IP; use IO::Socket::SSL; $IO::Socket::SSL::ISA[0] = 'IO::Socket::IP'; my $test = IO::Socket::SSL->new(SSL_ =>1);' Unexpected keys - SSL_, SSL_cert_file, SSL_check_crl, SSL_honor_cipher_order, SSL_key_file, SSL_server, SSL_use_cert, SSL_verify_mode, SSL_version at -e line 1 The workaround is just to load the Module into IO::Socket::SSL. You see as as soon as IO::Socket::SSL is constructed the underlying constructor of IO::Socket::IP dies cause it doesn't like the Keys! Another Example: perl -MIO::Socket::IP -E 'my $ip = IO::Socket::IP->new(blabla => 1);' Unexpected keys - blabla at -e line 1 This is a critical bug and i either request you fixing this behavior and just don't care about these keys(don't doing the checks), or to remove the "drop in replacement" advert from the Modules Text, because in this state it can't be called a drop in at all! As Reference here the Functionality of both Examples when using IO::Socket::INET: perl -E 'use IO::Socket::SSL; my $test = IO::Socket::SSL->new(SSL_ =>1);' perl -MIO::Socket::INET -E 'my $ip = IO::Socket::INET->new(blabla => 1);' It doesn't complain, and the same i expect from your module.
On Wed May 30 13:15:36 2012, STEPHANJ wrote: Show quoted text
> This is a critical bug and i either request you fixing this behavior and > just don't care about these keys(don't doing the checks), or to remove > the "drop in replacement" advert from the Modules Text, because in this > state it can't be called a drop in at all! > > As Reference here the Functionality of both Examples when using > IO::Socket::INET: > > perl -E 'use IO::Socket::SSL; my $test = IO::Socket::SSL->new(SSL_ =>1);' > > perl -MIO::Socket::INET -E 'my $ip = IO::Socket::INET->new(blabla => > 1);' > > It doesn't complain, and the same i expect from your module.
Ahhh yes. I wasn't aware that ::INET had that behaviour; my naturally defensive coding just had the checking for unrecognised keys in the constructor. That is somewhat unfortunate but it looks like to remain compatible it will indeed have to not care also here. I'll make that change. -- Paul Evans
Attached patch should fix this. -- Paul Evans
Subject: rt77536.patch
=== modified file 'lib/IO/Socket/IP.pm' --- lib/IO/Socket/IP.pm 2012-05-08 19:05:06 +0000 +++ lib/IO/Socket/IP.pm 2012-05-30 21:11:32 +0000 @@ -306,7 +306,8 @@ If neither C<Type> nor C<Proto> hints are provided, a default of C<SOCK_STREAM> and C<IPPROTO_TCP> respectively will be set, to maintain -compatibility with C<IO::Socket::INET>. +compatibility with C<IO::Socket::INET>. Other named arguments that are not +recognised are ignored. If the constructor fails, it will set C<$@> to an appropriate error message; this may be from C<$!> or it may be some other string; not every failure @@ -370,20 +371,17 @@ my @localinfos; my @peerinfos; - $hints{flags} = ( delete $arg->{GetAddrInfoFlags} || 0 ) | $AI_ADDRCONFIG; - - # Check for definedness of args, but delete them anyway even if they're - # not defined. Then the only remaining keys will be unrecognised ones. - - if( defined( my $family = delete $arg->{Family} ) ) { + $hints{flags} = ( $arg->{GetAddrInfoFlags} || 0 ) | $AI_ADDRCONFIG; + + if( defined( my $family = $arg->{Family} ) ) { $hints{family} = $family; } - if( defined( my $type = delete $arg->{Type} ) ) { + if( defined( my $type = $arg->{Type} ) ) { $hints{socktype} = $type; } - if( defined( my $proto = delete $arg->{Proto} ) ) { + if( defined( my $proto = $arg->{Proto} ) ) { unless( $proto =~ m/^\d+$/ ) { my $protonum = getprotobyname( $proto ); defined $protonum or croak "Unrecognised protocol $proto"; @@ -407,7 +405,7 @@ $hints{socktype} = SOCK_DGRAM if $hints{protocol} == IPPROTO_UDP; } - if( my $info = delete $arg->{LocalAddrInfo} ) { + if( my $info = $arg->{LocalAddrInfo} ) { ref $info eq "ARRAY" or croak "Expected 'LocalAddrInfo' to be an ARRAY ref"; @localinfos = @$info; } @@ -434,17 +432,15 @@ return; } } - delete $arg->{LocalHost}; - delete $arg->{LocalService}; - if( my $info = delete $arg->{PeerAddrInfo} ) { + if( my $info = $arg->{PeerAddrInfo} ) { ref $info eq "ARRAY" or croak "Expected 'PeerAddrInfo' to be an ARRAY ref"; @peerinfos = @$info; } elsif( defined $arg->{PeerHost} or defined $arg->{PeerService} ) { - defined( my $host = delete $arg->{PeerHost} ) or + defined( my $host = $arg->{PeerHost} ) or croak "Expected 'PeerHost'"; - defined( my $service = delete $arg->{PeerService} ) or + defined( my $service = $arg->{PeerService} ) or croak "Expected 'PeerService'"; local $1; # Placate a taint-related bug; [perl #67962] @@ -463,22 +459,20 @@ return; } } - delete $arg->{PeerHost}; - delete $arg->{PeerService}; my @sockopts_enabled; - push @sockopts_enabled, SO_REUSEADDR if delete $arg->{ReuseAddr}; - push @sockopts_enabled, SO_REUSEPORT if delete $arg->{ReusePort}; - push @sockopts_enabled, SO_BROADCAST if delete $arg->{Broadcast}; + push @sockopts_enabled, SO_REUSEADDR if $arg->{ReuseAddr}; + push @sockopts_enabled, SO_REUSEPORT if $arg->{ReusePort}; + push @sockopts_enabled, SO_BROADCAST if $arg->{Broadcast}; - my $listenqueue = delete $arg->{Listen}; + my $listenqueue = $arg->{Listen}; croak "Cannot Listen with a PeerHost" if defined $listenqueue and @peerinfos; - my $blocking = delete $arg->{Blocking}; + my $blocking = $arg->{Blocking}; defined $blocking or $blocking = 1; - my $v6only = delete $arg->{V6Only}; + my $v6only = $arg->{V6Only}; # IO::Socket::INET defines this key. IO::Socket::IP always implements the # behaviour it requests, so we can ignore it, unless the caller is for some @@ -486,9 +480,6 @@ if( defined $arg->{MultiHomed} and !$arg->{MultiHomed} ) { croak "Cannot disable the MultiHomed parameter"; } - delete $arg->{MultiHomed}; - - keys %$arg and croak "Unexpected keys - " . join( ", ", sort keys %$arg ); my @infos; foreach my $local ( @localinfos ? @localinfos : {} ) {
Hi, This code segfaults: http://paste.stejau.de/?20 Without the override everything works.
On Fri Jun 01 02:28:01 2012, STEPHANJ wrote: Show quoted text
> This code segfaults: > http://paste.stejau.de/?20
(code attached for future reference) Indeed it does. $ perl -Mblib rt77536.pl Segmentation fault That's rather worrying, as IO::Socket::IP is pureperl. I suspect this fault comes out of IO::Socket::SSL, perhaps somewhere it is expecting to definitely get an IPv4 address and gets confused by the wrong length of an IPv6 one. I'll take a look but at this point I suspect the bug is likely to be in IO::Socket::SSL, not IO::Socket::IP. -- Paul Evans
Subject: rt77536.pl
use strict; use IO::Socket::IP; use IO::Socket::SSL; $IO::Socket::SSL::ISA[0] = 'IO::Socket::IP'; my $client = IO::Socket::SSL->new("twitter.com:https") || warn "I encountered a problem: ".IO::Socket::SSL::errstr(); $client->verify_hostname( 'twitter.com','http' ) || die "hostname verification failed"; print $client->opened(),"\n"; print $client "GET / HTTP/1.0\r\n\r\n"; print <$client>;
Hmm. The plot thickens: $ strace perl -Mblib rt77536.pl ... socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7fff93d8f570) = -1 ENOTTY (Inappropriate ioctl for device) lseek(3, 0, SEEK_CUR) = -1 ESPIPE (Illegal seek) ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7fff93d8f570) = -1 ENOTTY (Inappropriate ioctl for device) lseek(3, 0, SEEK_CUR) = -1 ESPIPE (Illegal seek) fcntl(3, F_SETFD, FD_CLOEXEC) = 0 connect(3, {sa_family=AF_INET, sin_port=htons(443), sin_addr=inet_addr("199.59.150.39")}, 16) = 0 write(2, "Constructed IO::Socket::SSL=GLOB"..., 44Constructed IO::Socket::SSL=GLOB(0x2215678) ) = 44 --- SIGSEGV (Segmentation fault) @ 0 (0) --- +++ killed by SIGSEGV +++ Segmentation fault So it is working to IPv4, not IPv6. $ gdb --args perl -Mblib rt77536.pl GNU gdb (GDB) 7.4.1-debian ... (gdb) run Starting program: /usr/bin/perl -Mblib rt77536.pl [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Constructed IO::Socket::SSL=GLOB(0xb106b0) Program received signal SIGSEGV, Segmentation fault. 0x00007ffff5c3fe10 in X509_get_subject_name () from /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0 (gdb) bt #0 0x00007ffff5c3fe10 in X509_get_subject_name () from /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0 #1 0x00007ffff619d117 in XS_Net__SSLeay_X509_get_subject_name () from /usr/lib/perl5/auto/Net/SSLeay/SSLeay.so #2 0x00007ffff7b1572f in Perl_pp_entersub () from /usr/lib/libperl.so.5.14 #3 0x00007ffff7b0cbc6 in Perl_runops_standard () from /usr/lib/libperl.so.5.14 #4 0x00007ffff7aae26a in perl_run () from /usr/lib/libperl.so.5.14 #5 0x0000000000400f19 in main () -- Paul Evans
I have made progress. IO::Socket::INET's new/configure methods eventually call $self- Show quoted text
>connect() to set up the connection. IO::Socket::SSL overrides the
connect method to insert its own SSL setup code in there. This is what provides a number of state keys on the %{*$self} hash, including _SSL_object. IO::Socket::IP's configure method doesn't call $self->connect() because of the way that IO::Socket::connect masks certain types of error relating to nonblocking, that IO::Socket::IP needs to use in order to make nonblocking connections correctly. Because of this, the SSL connection setup is never performed when using IO::Socket::SSL via ::IP. A further and unrelated problem is that your monkeypatch hackery of setting @IO::Socket::SSL::ISA after it has been constructed will break a lot of things. All over the SSL.pm code, it uses SUPER:: dispatches. These are resolved at compiletime, the moment the file is 'use'd; changing @ISA at runtime will not fix these up. The only safe way to use IO::Socket::IP with IO::Socket::SSL is to edit the latter to use the former. I am in the process of writing that logic now. -- Paul Evans
On Fri Jun 01 10:51:08 2012, PEVANS wrote: Show quoted text
> I have made progress. >
... Show quoted text
> > IO::Socket::IP's configure method doesn't call $self->connect() because > of the way that IO::Socket::connect masks certain types of error > relating to nonblocking, that IO::Socket::IP needs to use in order to > make nonblocking connections correctly. > > Because of this, the SSL connection setup is never performed when using > IO::Socket::SSL via ::IP.
Attached also is a patch that fixes this one. Show quoted text
> A further and unrelated problem is that your monkeypatch hackery of > setting @IO::Socket::SSL::ISA after it has been constructed will break a > lot of things. All over the SSL.pm code, it uses SUPER:: dispatches. > These are resolved at compiletime, the moment the file is 'use'd; > changing @ISA at runtime will not fix these up. The only safe way to use > IO::Socket::IP with IO::Socket::SSL is to edit the latter to use the > former. I am in the process of writing that logic now.
I in fact already reported a bug to write such a fix: https://rt.cpan.org/Ticket/Display.html?id=75218 I'll get to work on that now. -- Paul Evans
Subject: rt77536-2.patch
=== modified file 'lib/IO/Socket/IP.pm' --- lib/IO/Socket/IP.pm 2012-05-30 21:11:55 +0000 +++ lib/IO/Socket/IP.pm 2012-06-01 15:01:00 +0000 @@ -560,12 +560,7 @@ } if( defined( my $addr = $info->{peeraddr} ) ) { - # It seems that IO::Socket hides EINPROGRESS errors, making them look - # like a success. This is annoying here. - # Instead of putting up with its frankly-irritating intentional - # breakage of useful APIs I'm just going to end-run around it and - # call CORE::connect() directly - if( CORE::connect( $self, $addr ) ) { + if( $self->connect( $addr ) ) { $! = 0; return 1; } @@ -590,7 +585,14 @@ sub connect { my $self = shift; - return $self->SUPER::connect( @_ ) if @_; + + # It seems that IO::Socket hides EINPROGRESS errors, making them look like + # a success. This is annoying here. + # Instead of putting up with its frankly-irritating intentional breakage of + # useful APIs I'm just going to end-run around it and call CORE::connect() + # directly + + return CORE::connect( $self, $_[0] ) if @_; $! = 0, return 1 if $self->fileno and defined $self->peername;
These patches now released to CPAN in version 0.11. On Fri Jun 01 11:06:33 2012, PEVANS wrote: Show quoted text
> > A further and unrelated problem is that your monkeypatch hackery of > > setting @IO::Socket::SSL::ISA after it has been constructed will break a > > lot of things. All over the SSL.pm code, it uses SUPER:: dispatches. > > These are resolved at compiletime, the moment the file is 'use'd; > > changing @ISA at runtime will not fix these up. The only safe way to use > > IO::Socket::IP with IO::Socket::SSL is to edit the latter to use the > > former. I am in the process of writing that logic now.
> > I in fact already reported a bug to write such a fix: > > https://rt.cpan.org/Ticket/Display.html?id=75218 > > I'll get to work on that now.
Patch now in place in the above bug. That patch + IO::Socket::IP 0.11 makes this all work fine. With both modules installed, IO::Socket::SSL will automatically use IO::Socket::IP if it can, only falling back on IO::Socket::INET6 if the former isn't available. I hope to get IO::Socket::IP into core quite soon so that should be solved. -- Paul Evans
Released as 0.11. -- Paul Evans