Module: wine Branch: master Commit: 61ed82fc8685af0cf55de4b547a38e3e5893dd99 URL: http://source.winehq.org/git/wine.git/?a=commit;h=61ed82fc8685af0cf55de4b547...
Author: Bruno Jesus 00cpxxx@gmail.com Date: Thu Apr 9 00:09:29 2015 -0300
ws2_32: Restore the local socket address that was bound with filter for getsockname().
---
dlls/ws2_32/socket.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++-- dlls/ws2_32/tests/sock.c | 1 - 2 files changed, 66 insertions(+), 3 deletions(-)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index 6903909..bfcc755 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -3218,6 +3218,61 @@ int WINAPI WS_getpeername(SOCKET s, struct WS_sockaddr *name, int *namelen) return res; }
+/* When binding to an UDP address with filter support the getsockname call on the socket + * will always return 0.0.0.0 instead of the filtered interface address. This function + * checks if the socket is interface-bound on UDP and return the correct address. + * This is required because applications often do a bind() with port zero followed by a + * getsockname() to retrieve the port and address acquired. + */ +static void interface_bind_check(int fd, struct sockaddr_in *addr) +{ +#if !defined(IP_BOUND_IF) && !defined(LINUX_BOUND_IF) + return; +#else + int ifindex; + socklen_t len = sizeof(ifindex); + + /* Check for IPv4, address 0.0.0.0 and UDP socket */ + if (addr->sin_family != AF_INET || addr->sin_addr.s_addr != 0) + return; + if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &ifindex, &len) || ifindex != SOCK_DGRAM) + return; + + ifindex = -1; + len = sizeof(ifindex); +#if defined(IP_BOUND_IF) + getsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &ifindex, &len); +#elif defined(LINUX_BOUND_IF) + getsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, &len); + if (ifindex > 0) ifindex = ntohl(ifindex); +#endif + if (ifindex > 0) + { + PIP_ADAPTER_INFO adapters, adapter; + DWORD adap_size; + + if (GetAdaptersInfo(NULL, &adap_size) != ERROR_BUFFER_OVERFLOW) + return; + adapters = HeapAlloc(GetProcessHeap(), 0, adap_size); + if (adapters && GetAdaptersInfo(adapters, &adap_size) == NO_ERROR) + { + /* Search the IPv4 adapter list for the appropriate bound interface */ + for (adapter = adapters; adapter != NULL; adapter = adapter->Next) + { + in_addr_t adapter_addr; + if (adapter->Index != ifindex) continue; + + adapter_addr = inet_addr(adapter->IpAddressList.IpAddress.String); + addr->sin_addr.s_addr = adapter_addr; + TRACE("reporting interface address from adapter %d\n", ifindex); + break; + } + } + HeapFree(GetProcessHeap(), 0, adapters); + } +#endif +} + /*********************************************************************** * getsockname (WS2_32.6) */ @@ -3255,8 +3310,17 @@ int WINAPI WS_getsockname(SOCKET s, struct WS_sockaddr *name, int *namelen) } else { - res = 0; - TRACE("=> %s\n", debugstr_sockaddr(name)); + interface_bind_check(fd, (struct sockaddr_in*) &uaddr); + if (ws_sockaddr_u2ws(&uaddr.addr, name, namelen) != 0) + { + /* The buffer was too small */ + SetLastError(WSAEFAULT); + } + else + { + res = 0; + TRACE("=> %s\n", debugstr_sockaddr(name)); + } } release_sock_fd( s, fd ); } diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 8690579..8b69cf6 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -4238,7 +4238,6 @@ static void test_getsockname(void) ok(ret == 0, "getsockname failed with %d\n", GetLastError()); strcpy(ipstr, inet_ntoa(sa_get.sin_addr)); trace("testing bind on interface %s\n", ipstr); -todo_wine ok(sa_get.sin_addr.s_addr == sa_set.sin_addr.s_addr, "address does not match: %s != %s", ipstr, inet_ntoa(sa_set.sin_addr));