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

getsockopt with SOL_SOCKET, SO_ERROR returns -26 #412

Open
Midar opened this issue Aug 27, 2018 · 9 comments
Open

getsockopt with SOL_SOCKET, SO_ERROR returns -26 #412

Midar opened this issue Aug 27, 2018 · 9 comments

Comments

@Midar
Copy link

Midar commented Aug 27, 2018

When connecting a non-blocking socket and using poll() to wait for it to become ready for writing, calling getsockopt() on it with SOL_SOCKET, SO_ERROR itself returns 0 and returns the value -26. Looking through the translation table, that should be EINPROGRESS. This is doubly unexpected: It should not return a negative value (on which, to make matters worse, strerror() even seems to crash) and it should not return EINPROGRESS anymore once the socket is connected. My guess is that the last error from the socket is returned, but the connection being established not resetting it to 0.

@Midar
Copy link
Author

Midar commented Aug 28, 2018

Looking at the code, I think there's a need for special casing depending on the passed level and option. For SOL_SOCKET, SO_ERROR, _net_convert_error() needs to be called on *opt. In getsockopt, a simple if (level == SOL_SOCKET && optname == SO_SOCKET) *opt = _net_convert_error(*opt) before returning should be sufficient.

@Midar
Copy link
Author

Midar commented Aug 28, 2018

That does not solve the part that it still returns EINPROGRESS, but that at least is easily patched in software (treat EINPROGRESS after the poll triggered the same as 0)

@mtheall
Copy link
Contributor

mtheall commented Aug 28, 2018

Can you try just calling connect() again and see what it returns? Hopefully you'd get errno=EALREADY when it's still connecting, errno=EISCONN when connection succeeded, and some other errno for connection failures.

@Midar
Copy link
Author

Midar commented Aug 28, 2018

I wait for it using poll(). When I call connect again, it indeed tells me EISCONN. However, if there is an error, the poll() seems to never return, and I'm stuck there forever. On desktop operating systems, this seems to trigger POLLOUT in any case, and getsockopt can then be used to determine whether connecting was successful or not.

@AliceLR
Copy link

AliceLR commented Sep 23, 2020

I can confirm that both of these issues (poll may wait for the entire timeout; getsockopt returns -26 and requires that connect EISCONN workaround hack) still exist in libctru 2.0.0-2.

@mtheall
Copy link
Contributor

mtheall commented Sep 23, 2020

You shouldn't be calling connect() again. Did you request POLLOUT in your pollfd::events? I use this for non-blocking connects in ftpd and it's never given me an issue on 3DS.

@AliceLR
Copy link

AliceLR commented Sep 23, 2020

Yes. For reference, here's the connection code in question (note anything that's Socket:: is a thin wrapper around a standard BSD sockets function in this case) and the poll function it calls, which explicitly sets POLLOUT. This is old code I've been refactoring but I've confirmed that this method of polling a non-blocking socket works on every other platform I've tried that supports poll.

Here's my version of the connect() workaround as per this issue.

edit: replaced the first two links with permanent links.

@AliceLR
Copy link

AliceLR commented Sep 24, 2020

I took a look at ftpd and it looks like it's using poll in a loop with a fixed 100ms timeout, which might be why there aren't any noticeable issues. My code currently provides poll with a 10 second timeout, which it seems to block for the entirety of before indicating writeability.

It looks like POSIX is pretty vague about when/if poll should return before the timeout ends (on the other hand, the Linux manual pages say it should return immediately when the socket is ready). I'll probably just work around this issue by splitting it up over multiple poll calls.

That still leaves the getsockopt with SOL_SOCKET, SO_ERROR issue.

@ghost
Copy link

ghost commented Sep 9, 2022

We ran into this same issue when implementing our non-blocking connect for RetroArch's network.

libretro/RetroArch#14381

Function: https://github.com/libretro/RetroArch/blob/a6654987755ae19866a6651992b732294b182785/libretro-common/net/net_socket.c#L689-L746

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants