[PATCH v2] ws2_32: Add WSAConnectByName() functions

Julian Klemann jklemann at codeweavers.com
Thu Jun 2 13:59:27 CDT 2022


Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=50941
Signed-off-by: Julian Klemann <jklemann at codeweavers.com>
---
v2: Add WS prefix to include/winsock2.h, change %llu to %Iu for sizeof
---
 dlls/ws2_32/socket.c         | 168 +++++++++++++++++++++++++++++++++++
 dlls/ws2_32/tests/sock.c     | 151 +++++++++++++++++++++++++++++++
 dlls/ws2_32/ws2_32.spec      |   2 +
 dlls/ws2_32/ws2_32_private.h |  12 +++
 include/winsock2.h           |   3 +
 5 files changed, 336 insertions(+)

diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c
index 88089fa8d74..6a93d00730a 100644
--- a/dlls/ws2_32/socket.c
+++ b/dlls/ws2_32/socket.c
@@ -1284,6 +1284,174 @@ static BOOL WINAPI WS2_ConnectEx( SOCKET s, const struct sockaddr *name, int nam
 }
 
 
+/***********************************************************************
+ *          WSAConnectByNameA      (WS2_32.@)
+ */
+BOOL WINAPI WSAConnectByNameA(SOCKET s, const char *node_name, const char *service_name,
+                              DWORD *local_addr_len, struct sockaddr *local_addr,
+                              DWORD *remote_addr_len, struct sockaddr *remote_addr,
+                              const struct timeval *timeout, WSAOVERLAPPED *reserved)
+{
+    WSAPROTOCOL_INFOA proto_info;
+    WSAPOLLFD pollout;
+    struct addrinfo *service, hints;
+    int ret, proto_len, sockaddr_size, sockname_size, sock_err, int_len;
+
+    TRACE("socket %#Ix, node_name %s, service_name %s, local_addr_len %p, local_addr %p, \
+          remote_addr_len %p, remote_addr %p, timeout %p, reserved %p\n",
+          s, debugstr_a(node_name), debugstr_a(service_name), local_addr_len, local_addr,
+          remote_addr_len, remote_addr, timeout, reserved );
+
+    if (!node_name || !service_name || reserved)
+    {
+        SetLastError(WSAEINVAL);
+        return FALSE;
+    }
+
+    if (!s)
+    {
+        SetLastError(WSAENOTSOCK);
+        return FALSE;
+    }
+
+    if (timeout)
+        FIXME("WSAConnectByName timeout stub\n");
+
+    proto_len = sizeof(WSAPROTOCOL_INFOA);
+    ret = getsockopt(s, SOL_SOCKET, SO_PROTOCOL_INFOA, (char *)&proto_info, &proto_len);
+    if (ret)
+        return FALSE;
+
+    memset(&hints, 0, sizeof(struct addrinfo));
+    hints.ai_socktype = proto_info.iSocketType;
+    hints.ai_family = proto_info.iAddressFamily;
+    hints.ai_protocol = proto_info.iProtocol;
+    ret = getaddrinfo(node_name, service_name, &hints, &service);
+    if (ret)
+        return FALSE;
+
+    if (proto_info.iSocketType != SOCK_STREAM)
+    {
+        freeaddrinfo(service);
+        SetLastError(WSAEFAULT);
+        return FALSE;
+    }
+
+    switch (proto_info.iAddressFamily)
+    {
+    case AF_INET:
+        sockaddr_size = sizeof(SOCKADDR_IN);
+        break;
+    case AF_INET6:
+        sockaddr_size = sizeof(SOCKADDR_IN6);
+        break;
+    default:
+        freeaddrinfo(service);
+        SetLastError(WSAENOTSOCK);
+        return FALSE;
+    }
+
+    ret = connect(s, service->ai_addr, sockaddr_size);
+    if (ret)
+    {
+        freeaddrinfo(service);
+        return FALSE;
+    }
+
+    pollout.fd = s;
+    pollout.events = POLLWRNORM;
+    ret = WSAPoll(&pollout, 1, -1);
+    if (ret == SOCKET_ERROR)
+    {
+        freeaddrinfo(service);
+        return FALSE;
+    }
+    if (pollout.revents & (POLLERR | POLLHUP | POLLNVAL))
+    {
+        freeaddrinfo(service);
+        int_len = sizeof(int);
+        ret = getsockopt(s, SOL_SOCKET, SO_ERROR, (char *)&sock_err, &int_len);
+        if (ret == SOCKET_ERROR)
+            return FALSE;
+        SetLastError(sock_err);
+        return FALSE;
+    }
+
+    if (remote_addr_len && remote_addr)
+    {
+        if (*remote_addr_len >= sockaddr_size)
+        {
+            memcpy(remote_addr, service->ai_addr, sockaddr_size);
+            *remote_addr_len = sockaddr_size;
+        }
+        else
+        {
+            freeaddrinfo(service);
+            SetLastError(WSAEFAULT);
+            return FALSE;
+        }
+    }
+
+    freeaddrinfo(service);
+
+    if (local_addr_len && local_addr)
+    {
+        if (*local_addr_len >= sockaddr_size)
+        {
+            sockname_size = sockaddr_size;
+            ret = getsockname(s, local_addr, &sockname_size);
+            if (ret)
+                return FALSE;
+            if (proto_info.iAddressFamily == AF_INET6)
+                ((SOCKADDR_IN6 *)local_addr)->sin6_port = 0;
+            else
+                ((SOCKADDR_IN *)local_addr)->sin_port = 0;
+            *local_addr_len = sockaddr_size;
+        }
+        else
+        {
+            SetLastError(WSAEFAULT);
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *          WSAConnectByNameW      (WS2_32.@)
+ */
+BOOL WINAPI WSAConnectByNameW(SOCKET s, const WCHAR *node_name, const WCHAR *service_name,
+                              DWORD *local_addr_len, struct sockaddr *local_addr,
+                              DWORD *remote_addr_len, struct sockaddr *remote_addr,
+                              const struct timeval *timeout, WSAOVERLAPPED *reserved)
+{
+    char *node_nameA, *service_nameA;
+    BOOL ret;
+
+    if (!node_name || !service_name)
+    {
+        SetLastError(WSAEINVAL);
+        return FALSE;
+    }
+
+    node_nameA = strdupWtoA(node_name);
+    service_nameA = strdupWtoA(service_name);
+    if (!node_nameA || !service_nameA)
+    {
+        SetLastError(WSAENOBUFS);
+        return FALSE;
+    }
+
+    ret = WSAConnectByNameA(s, node_nameA, service_nameA, local_addr_len, local_addr,
+                             remote_addr_len, remote_addr, timeout, reserved);
+    free(node_nameA);
+    free(service_nameA);
+    return ret;
+}
+
+
 static BOOL WINAPI WS2_DisconnectEx( SOCKET s, OVERLAPPED *overlapped, DWORD flags, DWORD reserved )
 {
     IO_STATUS_BLOCK iosb, *piosb = &iosb;
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c
index bdb683e6796..4a4a3a84e51 100644
--- a/dlls/ws2_32/tests/sock.c
+++ b/dlls/ws2_32/tests/sock.c
@@ -3197,6 +3197,156 @@ static void test_WSADuplicateSocket(void)
     closesocket(source);
 }
 
+static void test_WSAConnectByName(void)
+{
+    SOCKET s;
+    SOCKADDR_IN local_addr = {0}, remote_addr = {0},
+                sock_addr = {0}, peer_addr = {0};
+    DWORD local_len, remote_len, conn_ctx;
+    int ret, err, sock_len, peer_len;
+    WSAOVERLAPPED overlap;
+    struct addrinfo *first_addrinfo, first_hints;
+
+    conn_ctx = TRUE;
+
+    /* First call of getaddrinfo fails on w8adm */
+    first_addrinfo = NULL;
+    memset(&first_hints, 0, sizeof(struct addrinfo));
+    first_hints.ai_socktype = SOCK_STREAM;
+    first_hints.ai_family = AF_INET;
+    first_hints.ai_protocol = IPPROTO_TCP;
+    getaddrinfo("winehq.org", "http", &first_hints, &first_addrinfo);
+    if (first_addrinfo)
+        freeaddrinfo(first_addrinfo);
+    SetLastError(0xdeadbeef);
+
+    /* Fill all fields */
+    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    local_len = remote_len = sizeof(SOCKADDR_IN);
+    ret = WSAConnectByNameA(s, "winehq.org", "http", &local_len, (struct sockaddr *)&local_addr,
+                             &remote_len, (struct sockaddr *)&remote_addr, NULL, NULL);
+    ok(ret, "WSAConnectByNameA should have succeeded, error %u\n", WSAGetLastError());
+    setsockopt(s, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, (char *)&conn_ctx, sizeof(DWORD));
+    sock_len = peer_len = sizeof(SOCKADDR_IN);
+    ret = getsockname(s, (struct sockaddr *)&sock_addr, &sock_len);
+    ok(!ret, "getsockname should have succeeded, error %u\n", WSAGetLastError());
+    ret = getpeername(s, (struct sockaddr *)&peer_addr, &peer_len);
+    ok(!ret, "getpeername should have succeeded, error %u\n", WSAGetLastError());
+    ok(sock_len == sizeof(SOCKADDR_IN), "got sockname size of %d\n", sock_len);
+    ok(peer_len == sizeof(SOCKADDR_IN), "got peername size of %d\n", peer_len);
+    ok(local_len == sizeof(SOCKADDR_IN), "got local size of %lu\n", local_len);
+    ok(remote_len == sizeof(SOCKADDR_IN), "got remote size of %lu\n", remote_len);
+    ok(!local_addr.sin_port, "local_addr has non-zero sin_port: %hu.\n", local_addr.sin_port);
+    ok(!memcmp(&sock_addr.sin_addr, &local_addr.sin_addr, sizeof(struct in_addr)),
+       "local_addr did not receive data.\n");
+    ok(!memcmp(&peer_addr, &remote_addr, sizeof(SOCKADDR_IN)), "remote_addr did not receive data.\n");
+    closesocket(s);
+
+    /* Passing NULL length but a pointer to a sockaddr */
+    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    local_len = remote_len = sizeof(SOCKADDR_IN);
+    memset(&local_addr, 0, sizeof(SOCKADDR_IN));
+    memset(&remote_addr, 0, sizeof(SOCKADDR_IN));
+    memset(&sock_addr, 0, sizeof(SOCKADDR_IN));
+    memset(&peer_addr, 0, sizeof(SOCKADDR_IN));
+    ret = WSAConnectByNameA(s, "winehq.org", "http", NULL, (struct sockaddr *)&local_addr,
+                             NULL, (struct sockaddr *)&remote_addr, NULL, NULL);
+    ok(ret, "WSAConnectByNameA should have succeeded, error %u\n", WSAGetLastError());
+    setsockopt(s, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, (char *)&conn_ctx, sizeof(DWORD));
+    sock_len = peer_len = sizeof(SOCKADDR_IN);
+    ret = getsockname(s, (struct sockaddr *)&sock_addr, &sock_len);
+    ok(!ret, "getsockname should have succeeded, error %u\n", WSAGetLastError());
+    ret = getpeername(s, (struct sockaddr *)&peer_addr, &peer_len);
+    ok(!ret, "getpeername should have succeeded, error %u\n", WSAGetLastError());
+    ok(sock_len == sizeof(SOCKADDR_IN), "got sockname size of %d\n", sock_len);
+    ok(peer_len == sizeof(SOCKADDR_IN), "got peername size of %d\n", peer_len);
+    ok(!local_addr.sin_family, "local_addr received data.\n");
+    ok(!remote_addr.sin_family, "remote_addr received data.\n");
+    closesocket(s);
+
+    /* Passing NULLs for node or service */
+    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ret = WSAConnectByNameA(s, NULL, "http", NULL, NULL, NULL, NULL, NULL, NULL);
+    err = WSAGetLastError();
+    ok(!ret, "WSAConnectByNameA should have failed\n");
+    ok(err == WSAEINVAL, "expected error %u (WSAEINVAL), got %u\n", WSAEINVAL, err);
+    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    closesocket(s);
+    ret = WSAConnectByNameA(s, "winehq.org", NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+    err = WSAGetLastError();
+    ok(!ret, "WSAConnectByNameA should have failed\n");
+    ok(err == WSAEINVAL, "expected error %u (WSAEINVAL), got %u\n", WSAEINVAL, err);
+    closesocket(s);
+
+    /* Passing NULL for the addresses and address lengths */
+    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ret = WSAConnectByNameA(s, "winehq.org", "http", NULL, NULL, NULL, NULL, NULL, NULL);
+    ok(ret, "WSAConnectByNameA should have succeeded, error %u\n", WSAGetLastError());
+    closesocket(s);
+
+    /* Passing NULL for the addresses and passing correct lengths */
+    local_len = remote_len = sizeof(SOCKADDR_IN);
+    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ret = WSAConnectByNameA(s, "winehq.org", "http", &local_len, NULL,
+                            &remote_len, NULL, NULL, NULL);
+    ok(ret, "WSAConnectByNameA should have succeeded, error %u\n", WSAGetLastError());
+    ok(local_len == sizeof(SOCKADDR_IN), "local_len should have been %Iu, got %ld\n", sizeof(SOCKADDR_IN),
+       local_len);
+    ok(remote_len == sizeof(SOCKADDR_IN), "remote_len should have been %Iu, got %ld\n", sizeof(SOCKADDR_IN),
+       remote_len);
+    closesocket(s);
+
+    /* Passing addresses and passing short lengths */
+    local_len = remote_len = 3;
+    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ret = WSAConnectByNameA(s, "winehq.org", "http", &local_len, (struct sockaddr *)&local_addr,
+                            &remote_len, (struct sockaddr *)&remote_addr, NULL, NULL);
+    err = WSAGetLastError();
+    ok(!ret, "WSAConnectByNameA should have failed\n");
+    ok(err == WSAEFAULT, "expected error %u (WSAEFAULT), got %u\n", WSAEFAULT, err);
+    ok(local_len == 3, "local_len should have been 3, got %ld\n", local_len);
+    ok(remote_len == 3, "remote_len should have been 3, got %ld\n", remote_len);
+    closesocket(s);
+
+    /* Passing addresses and passing long lengths */
+    local_len = remote_len = 50;
+    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ret = WSAConnectByNameA(s, "winehq.org", "http", &local_len, (struct sockaddr *)&local_addr,
+                            &remote_len, (struct sockaddr *)&remote_addr, NULL, NULL);
+    ok(ret, "WSAConnectByNameA should have succeeded, error %u\n", WSAGetLastError());
+    ok(local_len == sizeof(SOCKADDR_IN), "local_len should have been %Iu, got %ld\n", sizeof(SOCKADDR_IN),
+       local_len);
+    ok(remote_len == sizeof(SOCKADDR_IN), "remote_len should have been %Iu, got %ld\n", sizeof(SOCKADDR_IN),
+       remote_len);
+    closesocket(s);
+
+    /* Unknown service */
+    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ret = WSAConnectByNameA(s, "winehq.org", "nonexistentservice", NULL, NULL, NULL, NULL, NULL, NULL);
+    err = WSAGetLastError();
+    ok(!ret, "WSAConnectByNameA should have failed\n");
+    ok(err == WSATYPE_NOT_FOUND, "expected error %u (WSATYPE_NOT_FOUND), got %u\n",
+       WSATYPE_NOT_FOUND, err);
+    closesocket(s);
+
+    /* Connecting with a UDP socket */
+    s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+    ret = WSAConnectByNameA(s, "winehq.org", "https", NULL, NULL, NULL, NULL, NULL, NULL);
+    err = WSAGetLastError();
+    ok(!ret, "WSAConnectByNameA should have failed\n");
+    ok(err == WSAEINVAL || err == WSAEFAULT, "expected error %u (WSAEINVAL) or %u (WSAEFAULT), got %u\n",
+       WSAEINVAL, WSAEFAULT, err); /* WSAEFAULT win10 >= 1809 */
+    closesocket(s);
+
+    /* Passing non-null as the reserved parameter */
+    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ret = WSAConnectByNameA(s, "winehq.org", "http", NULL, NULL, NULL, NULL, NULL, &overlap);
+    err = WSAGetLastError();
+    ok(!ret, "WSAConnectByNameA should have failed\n");
+    ok(err == WSAEINVAL, "expected error %u (WSAEINVAL), got %u\n", WSAEINVAL, err);
+    closesocket(s);
+}
+
 static void test_WSAEnumNetworkEvents(void)
 {
     SOCKET s, s2;
@@ -12573,6 +12723,7 @@ START_TEST( sock )
 
     test_WSASocket();
     test_WSADuplicateSocket();
+    test_WSAConnectByName();
     test_WSAEnumNetworkEvents();
 
     test_errors();
diff --git a/dlls/ws2_32/ws2_32.spec b/dlls/ws2_32/ws2_32.spec
index 1fbd8c55c75..6a07895d2f6 100644
--- a/dlls/ws2_32/ws2_32.spec
+++ b/dlls/ws2_32/ws2_32.spec
@@ -68,6 +68,8 @@
 @ stdcall WSAAddressToStringW(ptr long ptr ptr ptr)
 @ stdcall WSACloseEvent(long)
 @ stdcall WSAConnect(long ptr long ptr ptr ptr ptr)
+@ stdcall WSAConnectByNameA(long str str ptr ptr ptr ptr ptr ptr)
+@ stdcall WSAConnectByNameW(long str str ptr ptr ptr ptr ptr ptr)
 @ stdcall WSACreateEvent ()
 @ stdcall WSADuplicateSocketA(long long ptr)
 @ stdcall WSADuplicateSocketW(long long ptr)
diff --git a/dlls/ws2_32/ws2_32_private.h b/dlls/ws2_32/ws2_32_private.h
index f6b6ecc7eba..b21936a34e1 100644
--- a/dlls/ws2_32/ws2_32_private.h
+++ b/dlls/ws2_32/ws2_32_private.h
@@ -61,6 +61,18 @@
       0, 0, { (DWORD_PTR)(__FILE__ ": " # cs) }}; \
     static CRITICAL_SECTION cs = { &cs##_debug, -1, 0, 0, 0, 0 }
 
+static inline char *strdupWtoA( const WCHAR *str )
+{
+    char *ret = NULL;
+    if (str)
+    {
+        DWORD len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
+        if ((ret = malloc( len )))
+            WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
+    }
+    return ret;
+}
+
 static const char magic_loopback_addr[] = {127, 12, 34, 56};
 
 const char *debugstr_sockaddr( const struct sockaddr *addr ) DECLSPEC_HIDDEN;
diff --git a/include/winsock2.h b/include/winsock2.h
index f1d43acad40..86050025074 100644
--- a/include/winsock2.h
+++ b/include/winsock2.h
@@ -1157,6 +1157,9 @@ int WINAPI WSACancelBlockingCall(void);
 int WINAPI WSACleanup(void);
 BOOL WINAPI WSACloseEvent(WSAEVENT);
 int WINAPI WSAConnect(SOCKET,const struct WS(sockaddr)*,int,LPWSABUF,LPWSABUF,LPQOS,LPQOS);
+BOOL WINAPI WSAConnectByNameA(SOCKET,const char *,const char *,DWORD *,struct WS(sockaddr) *,DWORD *,struct WS(sockaddr) *,const struct WS(timeval) *,WSAOVERLAPPED *);
+BOOL WINAPI WSAConnectByNameW(SOCKET,const WCHAR *,const WCHAR *,DWORD *,struct WS(sockaddr) *,DWORD *,struct WS(sockaddr) *,const struct WS(timeval) *,WSAOVERLAPPED *);
+#define WSAConnectByName           WINELIB_NAME_AW(WSAConnectByName)
 WSAEVENT WINAPI WSACreateEvent(void);
 INT WINAPI WSADuplicateSocketA(SOCKET,DWORD,LPWSAPROTOCOL_INFOA);
 INT WINAPI WSADuplicateSocketW(SOCKET,DWORD,LPWSAPROTOCOL_INFOW);
-- 
2.36.1




More information about the wine-devel mailing list