ws2_32: Restore the local socket address that was bound with filter for getsockname().

This commit is contained in:
Bruno Jesus 2015-04-09 00:09:29 -03:00 committed by Alexandre Julliard
parent dcfde9a9df
commit 61ed82fc86
2 changed files with 66 additions and 3 deletions

View File

@ -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 );
}

View File

@ -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));