[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