[Bug 47560] New: ws2_32: WS_getpeername behavior for on non-blocking tcp sockets during handshake

wine-bugs at winehq.org wine-bugs at winehq.org
Sat Jul 27 05:26:32 CDT 2019


https://bugs.winehq.org/show_bug.cgi?id=47560

            Bug ID: 47560
           Summary: ws2_32:WS_getpeername behavior for on non-blocking tcp
                    sockets during handshake
           Product: Wine
           Version: 4.12.1
          Hardware: x86
                OS: Linux
            Status: UNCONFIRMED
          Severity: minor
          Priority: P2
         Component: winsock
          Assignee: wine-bugs at winehq.org
          Reporter: oysstu at gmail.com
      Distribution: ---

Created attachment 64948
  --> https://bugs.winehq.org/attachment.cgi?id=64948
Winedebug output

This bug report deals with a difference in behavior of ws2_32:getpeername. It
was discovered in the game Titan Quest Anniversary Edition when connection to a
multiplayer lobby, but can be reproduced quite easily elsewhere.

Once a non-blocking socket is connected to using ws2_32:WS_connect, and
ws2_32:getpeername is called before the TCP handshake is completed, the
resulting behavior is different on Windows and Linux.

On Linux; before the TCP handshake is completed WSAENOTCONN is returned by
ws2_32:getpeername. I.e. a socket is not connected before the handshake
succeeds, hence one cannot get the peer name

On Windows; from msdn the documentation clearly states that "The getpeername
function can be used only on a connected socket.". Apparently, once the
ws2_32:connect call has been made, it's defined as "connected" until the
handshake times out because Windows returns successfully without an error and
gives the peer info of the remote address it's trying to connect to. The error
can be reproduced by using connect to an IP/port that does not reply followed
by the call to getpeername

Now, what happens in Titan Quest is that the developers has handled the
WSAENOTCONN error by recreating the socket and doing the exact thing again
instead of waiting for the socket to connect. Obviously, that's an error on
their part, but it does work consistently on Windows.

During the handshake, the tcpi_state field of TCP_INFO will show as
TCP_SYN_SENT/TCP_SYN_RECV, which can be used to detect when this happens. I
don't know the best way to get the peer name without calling getpeername
though.  I suppose it can be retrieved by re-implementing af_inet:inetgetname()
without the check for TCP_SYN_SENT, but this probably affects portability (?).
Another more portable way is to cache the address/port in wine, which obviously
only works for outgoing connections (don't see why the behavior has to change
for incoming connections anyway).

af_inet:inetgetname():
https://github.com/torvalds/linux/blob/master/net/ipv4/af_inet.c#L760

I've attached a wine debug log, which shows the connect() call followed by a
getpeername() that fails and raises an exception.

-- 
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.



More information about the wine-bugs mailing list