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);
Sorry, please disregard my sign-off; I noticed a warning while compiling which I missed initially:
On 5/31/22 16:52, Julian Klemann wrote:
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);
This doesn't work when the file is included in Unix libraries; the "sockaddr" and "timeval" symbols need to be protected with the WS prefix. See the way the WS() macro is used in other declarations.