0

I'm trying to use getpeerinfo to ensure i can get peer info after connecting.

It fails with:

WSAENOTCONN (10057)

Socket is not connected.

A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using sendto) no address was supplied.

The basic flow is:

  • WSAStartup
  • socket()
  • connect()
  • getpeerinfo()

What am i doing wrong?

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  Winapi.Winsock2;

procedure Main;
var
    hSocket: TSocket;
    wsData: TWSAData;
    nodeName: string;
    serviceName: string;
    localAddressLength: Cardinal;
    localAddress: TSockAddr;
    remoteAddressLength: Cardinal;
    remoteAddress: TSockAddr;
    name: TSockAddr;
    nameLen: Integer;
    errorCode: Integer;
    bRes: Boolean;
begin
    WSAStartup($0202, {var}wsData);

    hSocket := socket(AF_INET, SOCK_STREAM, 0);

    nodeName := 'stackoverflow.com';
    serviceName := '80';

    bRes := WSAConnectByNameW(hSocket, PChar(nodeName), PChar(serviceName),
            {var}localAddressLength, {var}localAddress,
            {var}remoteAddressLength, {var}remoteAddress,
            nil, nil);
    if not bRes then
    begin
        errorCode := WSAGetLastError;
        RaiseLastOSError(errorCode);
    end;

    //If no error occurs, getpeername returns zero.
    //Otherwise, a value of SOCKET_ERROR is returned,
    //and a specific error code can be retrieved by calling WSAGetLastError.
    nameLen := sizeof(name);
    errorCode := getpeername(hSocket, {var}name, {var}nameLen);
    if errorCode <> 0 then
    begin
        errorCode := WSAGetLastError;
        RaiseLastOSError(errorCode);
    end;
end;

begin
    try
        Main;
    except
        on E: Exception do
            Writeln(E.ClassName, ': ', E.Message);
    end;
end.

We know the connection is connected because:

  • we just connected
  • we did the thing to check if a socket is connected

Bonus Reading

Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
  • 'It looks like your connection was reset' after the connect, to quote from your citation. – user207421 Mar 09 '20 at 00:31
  • I'm also curious why you think you need the peername when you already have it. – user207421 Mar 09 '20 at 00:37
  • For the situations where i don't have it (not every socket will be opened with WSAConnectByName, or even connected by me). Also that MSFT forums person didn't try it, and is only guessing. – Ian Boyd Mar 09 '20 at 01:47
  • 1. The fact that you just connected doesn't prove that it's still connected. 2. You did what 'thing' to check if it's still connected? None mentioned in your link. The only valid test is a successful `recv()`. 3. The fact that someone may be guessing doesn't meant make them wrong. – user207421 Mar 09 '20 at 02:16
  • The fact that i'm still connected proves i'm still connected. The thing is the thing in the citation you cited. Does it work for you; have you tried it? [Sobs in sockets api](https://www.reddit.com/r/ProgrammerHumor/comments/ff9gfo/sobs_in_compiler_errors/) – Ian Boyd Mar 09 '20 at 02:40
  • @IanBoyd "*The fact that i'm still connected proves i'm still connected*" - no, it doesn't. The peer could have disconnected on its end, and your code hasn't reacted to it yet. In any case, why are you using `getpeername()` at all, when the same info is already returned by `WSAConnectByNameW()`? – Remy Lebeau Mar 09 '20 at 21:54
  • @RemyLebeau I know i'm connected because i can see the connection on the other end, the `syn`+`syn+ack`+`ack` has been handshook, i can see no `FIN` has been send, i can see the established connections in TCP view and Resource Monitor. The fact that i am still connected proves i'm still connected. If i was not connected it would prove i'm not connected. But since i am connected, i am connected. – Ian Boyd Mar 09 '20 at 23:42

1 Answers1

2

The answer is in the WinSock documentation.

WSAConnectByNameW() function

When the WSAConnectByName function returns TRUE, the socket s is in the default state for a connected socket. The socket s does not enable previously set properties or options until SO_UPDATE_CONNECT_CONTEXT is set on the socket. Use the setsockopt function to set the SO_UPDATE_CONNECT_CONTEXT option.

So, when WSAConnectByNameW() returns TRUE, getpeername() fails with WSAENOTCONN because you are not calling setsockopt(SO_UPDATE_CONNECT_CONTEXT) to put the socket into the proper state. This is clarified in the SOL_SOCKET Socket Options documentation:

SO_UPDATE_CONNECT_CONTEXT

This option is used with the ConnectEx, WSAConnectByList, and WSAConnectByName functions. This option updates the properties of the socket after the connection is established. This option should be set if the getpeername, getsockname, getsockopt, setsockopt, or shutdown functions are to be used on the connected socket.

Try this:

bRes := WSAConnectByNameW(hSocket, PChar(nodeName), PChar(serviceName),
  {var}localAddressLength, {var}localAddress,
  {var}remoteAddressLength, {var}remoteAddress,
  nil, nil);
if not bRes then
begin
  errorCode := WSAGetLastError;
  RaiseLastOSError(errorCode);
end;

// ADD THIS..
errorCode := setsockopt(hSocket, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, nil, 0);
if errorCode <> 0 then
begin
  errorCode := WSAGetLastError;
  RaiseLastOSError(errorCode);
end;

...

That being said, there is no need to use getpeername() in your example, because it returns the same info that WSAConnectByNameW() already returns in the variable you are passing to its RemoteAddress parameter.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Wow, nice catch. Even having read that i wouldn't have/didn't catch the implications. I thought it was just just `WSAAsyncSelect` flags that were applied to a socket. There are other examples where nobody calls `WSAConnectByName` - all i know is i have a `Socket` handle; and wasn't the one who connected it. You'd hope your same code would work if the socked was connected through `connect`, `connectEx`, `WSAConnectByName`, `WSAConnectByList`, connected though a listening socket, or any other mechanisms that might be used to connect a socket. – Ian Boyd Mar 09 '20 at 23:36
  • `connect()` and `accept()` ensure the socket is in the proper state before exiting. With `ConnectEx()`, `WSAConnectByList()`, `WSAConnectByName()`, and `AcceptEx()`, the state has to be updated separately after they have exited. Whoever is responsible for making the connection is responsible for making sure the socket is in the proper state before handing the `SOCKET` off to someone else – Remy Lebeau Mar 09 '20 at 23:46