Skip Menu |

This queue is for tickets about the IO CPAN distribution.

Report information
The Basics
Id: 86791
Status: resolved
Priority: 0/
Queue: IO

Owner: Nobody in particular
Requestors: [...]

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

Subject: Incorrect error code for IO::Socket::INET
Date: Mon, 08 Jul 2013 16:57:11 -0400
To: bug-IO [...]
From: Shmuel (Seymour J.) Metz < [...]>
Download (untitled) / with headers
text/plain 3.5k
See 115706 in the old CPAN tracking system. Per Ben Morrow <> in thread "IO::Socket::INET hostname restrictions? " on comp.lang.perl.misc, archived at <>: *Interesting*. I believe there's at least one bug here, in IO::Socket, but it's not entirely clear. First, the workaround: don't use Timeout. Find some other way of timing out the operation, like alarm(). The CNAMEs are a red herring: what matters is that that machine doesn't accept connections on port 43. The 'Invalid argument' is just the wrong error: without Timeout, you should see 'Connection refused', which is the right answer. [You don't need to read the rest unless you're interested...] That ->new call gives me exactly the same result as you: EINVAL from connect(). Since I didn't think connect(2) was even *able* to return EINVAL, I ran it under ktrace: this showed two calls to connect. The first failed with EINPROGRESS, the second with EINVAL. Retrying without Timeout consistently gave 'Connection refused' instead. Looking at the source of IO::Socket, the logic if Timeout is specified is approximately set non-blocking mode on the socket try to connect if connect fails with EINPROGRESS select(2) for write, with the given timeout if the select doesn't time out retry the connect if that connect succeeds, return success if it failed with EISCONN, or we are on Win32 and it failed with WSAEINVAL, return *success* otherwise, return the error from the second connect The EISCONN case is the strange one. There is a comment # Some systems refuse to re-connect() to # an already open socket and set errno to EISCONN. # Windows sets errno to WSAEINVAL (10022) and it turns out that it's not just Win32 that returns EINVAL in that case, it's at least current versions of FreeBSD and, presumably, OS/2 as well. However, even given that, returning success is *completely wrong*. The sequence of events is - app calls connect(2) on a non-blocking socket - connect(2) fails with EINPROGRESS - app calls select(2)-for-write - some time later, a RST packet comes in - select(2) returns socket as ready - app calls connect(2) again - connect(2) fails with EISCONN/EINVAL The EISCONN/EINVAL error is saying 'this socket isn't fit to be reused': it doesn't tell you *anything* about whether the second connection attempt succeeded or failed. (In fact, you get the same EINVAL if you attempt to re-connect a TCP socket while it's in TIME_WAIT state after a successful close.) Looking about a bit, I found, which mentions this connect-twice strategy as a way of performing a non-blocking connect and still getting the correct error. It also says that it doesn't always work, and that you should use getpeername instead (and for all djb's... um... charm, he's usually right about things like this). So, I think this counts as a bug in IO::Socket, since there are common situations on common operating systems where it doesn't work properly. [Oddly, it looks from the CVS log as though FreeBSD used to return EISCONN in this situation, as the IO::Socket code was kind-of expecting, but it was changed to EINVAL in 2006. It's not clear to me why, especially since there now appears to be no way to get an EISCONN error from a TCP socket.] The attached patch from Ben Morrow works fine for OS/2. -- Shmuel (Seymour J.) Metz, SysProg and JOAT Atid/2 <> We don't care. We don't have to care, we're Congress. (S877: The Shut up and Eat Your spam act of 2003)
text/x-diff 3k
Path:!novia!!!!!!ecngs!!!!!!!not-for-mail NNTP-Posting-Date: Wed, 07 Dec 2011 18:03:06 -0600 Newsgroups: comp.lang.perl.misc Subject: Re: IO::Socket::INET hostname restrictions? References: <4edae754$3$fuzhry+tra$> <> <4edf82d9$21$fuzhry+tra$> <> From: Ben Morrow <> Content-type: text/plain; charset=UTF-8 X-Newsreader: trn 4.0-test77 (Sep 1, 2010) Originator: (Ben Morrow) Date: Wed, 7 Dec 2011 23:54:09 +0000 Message-ID: <> Lines: 42 X-Usenet-Provider: X-AuthenticatedUsername: NoAuthUser X-Trace: sv3-6W3s8xwBWJ4+yCaM6dkp6rgox1m2lVg+sCrGOm/PkK43FvIOrD5BwGh8lI3mCq6PMcpgny6zD7AJ32x!vIziyMZB8hczj/6H1D1GvkShrARl6AOkp+K2QvbExwVXWfWqAZnlMTqxdrsOpBilsyksvkQ= X-Complaints-To: X-DMCA-Complaints-To: X-Abuse-and-DMCA-Info: Please be sure to forward a copy of ALL headers X-Abuse-and-DMCA-Info: Otherwise we will be unable to process your complaint properly X-Postfilter: 1.3.40 Bytes: 3102 X-Original-Bytes: 2912 Xref: comp.lang.perl.misc:690120 Quoth Ben Morrow <>: > > I do intend to report this as a bug in IO::Socket, when I get round to > it. If and when you get gcc working, would you be able to test this patch? You would need to download and unpack the IO-1.25 tarball from CPAN, apply the patch, and verify that it both passes 'make test' and gives a more sensible error in the situation we've been discussing. If you don't want to install the patched version you can use 'perl -Mblib' from within the build directory to test the built-but-not- installed modules. If you do want to install it, it should be perfectly safe to install with 'make install': it will simply overwrite the IO::* modules which came with your perl with newer versions. Ben diff -ur IO-1.25/lib/IO/ IO/lib/IO/ --- IO-1.25/lib/IO/ 2009-05-14 00:47:42.000000000 +0100 +++ IO/lib/IO/ 2011-12-07 16:35:52.859688028 +0000 @@ -122,12 +122,12 @@ $err = $! || (exists &Errno::ETIMEDOUT ? &Errno::ETIMEDOUT : 1); $@ = "connect: timeout"; } - elsif (!connect($sock,$addr) && - not ($!{EISCONN} || ($! == 10022 && $^O eq 'MSWin32')) - ) { - # Some systems refuse to re-connect() to - # an already open socket and set errno to EISCONN. - # Windows sets errno to WSAEINVAL (10022) + elsif (!getpeername($sock)) { + if ($!{ENOTCONN}) { + # this read will fail, but will give us the error + # from connect(2) in $! + sysread $sock, my $tmp, 1; + } $err = $!; $@ = "connect: $!"; }
Ticket migrated to github as

This service is sponsored and maintained by Best Practical Solutions and runs on infrastructure.

Please report any issues with to