[prev in list] [next in list] [prev in thread] [next in thread]
List: wine-devel
Subject: [PATCH] ws2_32: Add WSAConnectByName() functions
From: Julian Klemann <jklemann () codeweavers ! com>
Date: 2022-05-31 21:52:52
Message-ID: 20220531215252.299340-1-jklemann () codeweavers ! com
[Download RAW message or body]
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=50941
Signed-off-by: Julian Klemann <jklemann@codeweavers.com>
---
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..15e029b3b8f 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 %llu, got %ld\n", sizeof(SOCKADDR_IN), + local_len);
+ ok(remote_len == sizeof(SOCKADDR_IN), "remote_len should have been %llu, 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 %llu, got %ld\n", sizeof(SOCKADDR_IN), + local_len);
+ ok(remote_len == sizeof(SOCKADDR_IN), "remote_len should have been %llu, 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..8394ea6471a 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 sockaddr *,DWORD \
*,struct sockaddr *,const struct timeval *,WSAOVERLAPPED *); +BOOL WINAPI \
WSAConnectByNameW(SOCKET,const WCHAR *,const WCHAR *,DWORD *,struct sockaddr *,DWORD \
*,struct sockaddr *,const struct 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
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic