Quoth Shmuel (Seymour J.) Metz <spam...@library.lspace.org.invalid>:
>
> So why does IO::Socket::INET fail?
>
> IO::Socket::INET->new(
> PeerAddr =>
whois.nic.mn,
> PeerPort => 43,
> Proto => tcp,
> Timeout => 60,
> LocalAddr => )
> errno 22: Invalid argument
> host
whois.nic.mn not found
> 22
> IO::Socket::INET: connect: Invalid argument
*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
http://cr.yp.to/docs/connect.html, 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.]
Ben