Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect error code for IO::Socket::INET [rt.cpan.org #86791] #17459

Open
toddr opened this issue Apr 19, 2018 · 0 comments
Open

Incorrect error code for IO::Socket::INET [rt.cpan.org #86791] #17459

toddr opened this issue Apr 19, 2018 · 0 comments
Labels
dist-IO issues in the dual-life blead-first IO distribution Needs Triage

Comments

@toddr
Copy link
Member

toddr commented Apr 19, 2018

Migrated from rt.cpan.org#86791 (status was 'new')

Requestors:

Attachments:

From [email protected] on 2013-07-08 20:57:09:

See 115706 in the old CPAN tracking system.

Per Ben Morrow [email protected] in thread "IO::Socket::INET hostname
restrictions? " on comp.lang.perl.misc, archived at
http://www.rhinocerus.net/forum/lang-perl-misc/690988-io-socket-inet-hostname-restrictions.html:

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.]

The attached patch from Ben Morrow works fine for OS/2.

--
Shmuel (Seymour J.) Metz, SysProg and JOAT
Atid/2 http://patriot.net/~shmuel
We don't care. We don't have to care, we're Congress.
(S877: The Shut up and Eat Your spam act of 2003)

@toddr toddr transferred this issue from Dual-Life/IO Jan 20, 2020
@toddr toddr added Needs Triage dist-IO issues in the dual-life blead-first IO distribution labels Jan 20, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dist-IO issues in the dual-life blead-first IO distribution Needs Triage
Projects
None yet
Development

No branches or pull requests

1 participant