[PATCH 7/7] ws2_32: Reimplement bind() and getsockname() on top of IOCTL_AFD_BIND and IOCTL_AFD_GETSOCKNAME respectively.
Zebediah Figura
z.figura12 at gmail.com
Tue Jun 15 22:07:29 CDT 2021
Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
dlls/ws2_32/socket.c | 352 ++++++-----------------------------
dlls/ws2_32/tests/afd.c | 8 +-
dlls/ws2_32/tests/sock.c | 4 +-
dlls/ws2_32/ws2_32_private.h | 4 -
4 files changed, 59 insertions(+), 309 deletions(-)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c
index 2fa1471ae3b..63c26a9c769 100644
--- a/dlls/ws2_32/socket.c
+++ b/dlls/ws2_32/socket.c
@@ -160,44 +160,8 @@ static const WSAPROTOCOL_INFOW supported_protocols[] =
},
};
-#if defined(IP_UNICAST_IF) && defined(SO_ATTACH_FILTER)
-# define LINUX_BOUND_IF
-struct interface_filter {
- struct sock_filter iface_memaddr;
- struct sock_filter iface_rule;
- struct sock_filter ip_memaddr;
- struct sock_filter ip_rule;
- struct sock_filter return_keep;
- struct sock_filter return_dump;
-};
-# define FILTER_JUMP_DUMP(here) (u_char)(offsetof(struct interface_filter, return_dump) \
- -offsetof(struct interface_filter, here)-sizeof(struct sock_filter)) \
- /sizeof(struct sock_filter)
-# define FILTER_JUMP_KEEP(here) (u_char)(offsetof(struct interface_filter, return_keep) \
- -offsetof(struct interface_filter, here)-sizeof(struct sock_filter)) \
- /sizeof(struct sock_filter)
-# define FILTER_JUMP_NEXT() (u_char)(0)
-# define SKF_NET_DESTIP 16 /* offset in the network header to the destination IP */
-static struct interface_filter generic_interface_filter = {
- /* This filter rule allows incoming packets on the specified interface, which works for all
- * remotely generated packets and for locally generated broadcast packets. */
- BPF_STMT(BPF_LD+BPF_W+BPF_ABS, SKF_AD_OFF+SKF_AD_IFINDEX),
- BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0xdeadbeef, FILTER_JUMP_KEEP(iface_rule), FILTER_JUMP_NEXT()),
- /* This rule allows locally generated packets targeted at the specific IP address of the chosen
- * adapter (local packets not destined for the broadcast address do not have IFINDEX set) */
- BPF_STMT(BPF_LD+BPF_W+BPF_ABS, SKF_NET_OFF+SKF_NET_DESTIP),
- BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0xdeadbeef, FILTER_JUMP_KEEP(ip_rule), FILTER_JUMP_DUMP(ip_rule)),
- BPF_STMT(BPF_RET+BPF_K, (u_int)-1), /* keep packet */
- BPF_STMT(BPF_RET+BPF_K, 0) /* dump packet */
-};
-#endif /* LINUX_BOUND_IF */
-
-DECLARE_CRITICAL_SECTION(cs_if_addr_cache);
DECLARE_CRITICAL_SECTION(cs_socket_list);
-static in_addr_t *if_addr_cache;
-static unsigned int if_addr_cache_size;
-
static SOCKET *socket_list;
static unsigned int socket_list_size;
@@ -1915,197 +1879,57 @@ static int WINAPI WS2_WSARecvMsg( SOCKET s, LPWSAMSG msg, LPDWORD lpNumberOfByte
lpOverlapped, lpCompletionRoutine, &msg->Control );
}
-/***********************************************************************
- * interface_bind (INTERNAL)
- *
- * Take bind() calls on any name corresponding to a local network adapter and restrict the given socket to
- * operating only on the specified interface. This restriction consists of two components:
- * 1) An outgoing packet restriction suggesting the egress interface for all packets.
- * 2) An incoming packet restriction dropping packets not meant for the interface.
- * If the function succeeds in placing these restrictions (returns TRUE) then the name for the bind() may
- * safely be changed to INADDR_ANY, permitting the transmission and receipt of broadcast packets on the
- * socket. This behavior is only relevant to UDP sockets and is needed for applications that expect to be able
- * to receive broadcast packets on a socket that is bound to a specific network interface.
- */
-static BOOL interface_bind( SOCKET s, int fd, struct sockaddr *addr )
-{
-#if defined(HAVE_GETIFADDRS) && (defined(IP_BOUND_IF) || defined(LINUX_BOUND_IF))
- struct sockaddr_in *in_sock = (struct sockaddr_in *) addr;
- in_addr_t bind_addr = in_sock->sin_addr.s_addr;
- struct ifaddrs *ifaddrs, *ifaddr;
- unsigned int index;
- BOOL ret = FALSE;
- int enable = 1;
-
- if (bind_addr == htonl(INADDR_ANY) || bind_addr == htonl(INADDR_LOOPBACK))
- return FALSE; /* Not binding to a network adapter, special interface binding unnecessary. */
- if (_get_fd_type(fd) != SOCK_DGRAM)
- return FALSE; /* Special interface binding is only necessary for UDP datagrams. */
-
- if (getifaddrs( &ifaddrs ) < 0) return FALSE;
-
- for (ifaddr = ifaddrs; ifaddr != NULL; ifaddr = ifaddr->ifa_next)
- {
- if (ifaddr->ifa_addr && ifaddr->ifa_addr->sa_family == AF_INET
- && ((struct sockaddr_in *)ifaddr->ifa_addr)->sin_addr.s_addr == bind_addr)
- {
- index = if_nametoindex( ifaddr->ifa_name );
- if (!index)
- {
- ERR( "Unable to look up interface index for %s: %s\n", ifaddr->ifa_name, strerror( errno ) );
- continue;
- }
-
-#if defined(IP_BOUND_IF)
- /* IP_BOUND_IF sets both the incoming and outgoing restriction at once */
- if (setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &index, sizeof(index)) != 0)
- goto cleanup;
- ret = TRUE;
-#elif defined(LINUX_BOUND_IF)
- {
- in_addr_t ifindex = (in_addr_t) htonl(index);
- struct interface_filter specific_interface_filter;
- struct sock_fprog filter_prog;
-
- if (setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex)) != 0)
- goto cleanup; /* Failed to suggest egress interface */
- specific_interface_filter = generic_interface_filter;
- specific_interface_filter.iface_rule.k = index;
- specific_interface_filter.ip_rule.k = htonl(bind_addr);
- filter_prog.len = sizeof(generic_interface_filter)/sizeof(struct sock_filter);
- filter_prog.filter = (struct sock_filter *) &specific_interface_filter;
- if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, sizeof(filter_prog)) != 0)
- goto cleanup; /* Failed to specify incoming packet filter */
- ret = TRUE;
- }
-#endif
- if (ret)
- {
- EnterCriticalSection(&cs_if_addr_cache);
- if (if_addr_cache_size <= index)
- {
- unsigned int new_size;
- in_addr_t *new;
-
- new_size = max(if_addr_cache_size * 2, index + 1);
- if (!(new = heap_realloc(if_addr_cache, sizeof(*if_addr_cache) * new_size)))
- {
- ERR("No memory.\n");
- ret = FALSE;
- LeaveCriticalSection(&cs_if_addr_cache);
- break;
- }
- memset(new + if_addr_cache_size, 0, sizeof(*if_addr_cache)
- * (new_size - if_addr_cache_size));
- if_addr_cache = new;
- if_addr_cache_size = new_size;
- }
- if (if_addr_cache[index] && if_addr_cache[index] != bind_addr)
- WARN("Adapter addr for iface index %u has changed.\n", index);
-
- if_addr_cache[index] = bind_addr;
- LeaveCriticalSection(&cs_if_addr_cache);
- }
- break;
- }
- }
- /* Will soon be switching to INADDR_ANY: permit address reuse */
- if (ret && setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) == 0)
- TRACE("Socket %04lx bound to interface index %d\n", s, index);
- else
- ret = FALSE;
-
-cleanup:
- if(!ret)
- ERR("Failed to bind to interface, receiving broadcast packets will not work on socket %04lx.\n", s);
- freeifaddrs( ifaddrs );
- return ret;
-#else
- FIXME( "Broadcast packets on interface-bound sockets are not currently supported on this platform.\n" );
- return FALSE;
-#endif
-}
/***********************************************************************
- * bind (WS2_32.2)
+ * bind (ws2_32.2)
*/
-int WINAPI WS_bind(SOCKET s, const struct WS_sockaddr* name, int namelen)
+int WINAPI WS_bind( SOCKET s, const struct WS_sockaddr *addr, int len )
{
- int fd = get_sock_fd( s, 0, NULL );
- int res = SOCKET_ERROR;
+ struct afd_bind_params *params;
+ struct WS_sockaddr *ret_addr;
+ IO_STATUS_BLOCK io;
+ HANDLE sync_event;
+ NTSTATUS status;
- TRACE("socket %04lx, ptr %p %s, length %d\n", s, name, debugstr_sockaddr(name), namelen);
+ TRACE( "socket %#lx, addr %s\n", s, debugstr_sockaddr(addr) );
- if (fd != -1)
+ if (!addr || (addr->sa_family && !supported_pf( addr->sa_family )))
{
- if (!name || (name->sa_family && !supported_pf(name->sa_family)))
- {
- SetLastError(WSAEAFNOSUPPORT);
- }
- else
- {
- union generic_unix_sockaddr uaddr;
- unsigned int uaddrlen = ws_sockaddr_ws2u(name, namelen, &uaddr);
- if (!uaddrlen)
- {
- SetLastError(WSAEFAULT);
- }
- else
- {
- if (name->sa_family == WS_AF_INET)
- {
- struct sockaddr_in *in4 = (struct sockaddr_in*) &uaddr;
- if (memcmp(&in4->sin_addr, magic_loopback_addr, 4) == 0)
- {
- /* Trying to bind to the default host interface, using
- * INADDR_ANY instead*/
- WARN("Trying to bind to magic IP address, using "
- "INADDR_ANY instead.\n");
- in4->sin_addr.s_addr = htonl(INADDR_ANY);
- }
- else if (interface_bind(s, fd, &uaddr.addr))
- in4->sin_addr.s_addr = htonl(INADDR_ANY);
- }
- if (bind(fd, &uaddr.addr, uaddrlen) < 0)
- {
- int loc_errno = errno;
- WARN("\tfailure - errno = %i\n", errno);
- errno = loc_errno;
- switch (errno)
- {
- case EADDRNOTAVAIL:
- SetLastError(WSAEINVAL);
- break;
- case EADDRINUSE:
- {
- int optval = 0;
- socklen_t optlen = sizeof(optval);
- /* Windows >= 2003 will return different results depending on
- * SO_REUSEADDR, WSAEACCES may be returned representing that
- * the socket hijacking protection prevented the bind */
- if (!getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, &optlen) && optval)
- {
- SetLastError(WSAEACCES);
- break;
- }
- /* fall through */
- }
- default:
- SetLastError(wsaErrno());
- break;
- }
- }
- else
- {
- res=0; /* success */
- }
- }
- }
- release_sock_fd( s, fd );
+ SetLastError( WSAEAFNOSUPPORT );
+ return -1;
}
- return res;
+
+ if (!(sync_event = get_sync_event())) return -1;
+
+ params = HeapAlloc( GetProcessHeap(), 0, sizeof(int) + len );
+ ret_addr = HeapAlloc( GetProcessHeap(), 0, len );
+ if (!params || !ret_addr)
+ {
+ HeapFree( GetProcessHeap(), 0, params );
+ HeapFree( GetProcessHeap(), 0, ret_addr );
+ SetLastError( WSAENOBUFS );
+ return -1;
+ }
+ params->unknown = 0;
+ memcpy( ¶ms->addr, addr, len );
+
+ status = NtDeviceIoControlFile( (HANDLE)s, sync_event, NULL, NULL, &io, IOCTL_AFD_BIND,
+ params, sizeof(int) + len, ret_addr, len );
+ if (status == STATUS_PENDING)
+ {
+ if (WaitForSingleObject( sync_event, INFINITE ) == WAIT_FAILED)
+ return -1;
+ status = io.u.Status;
+ }
+
+ HeapFree( GetProcessHeap(), 0, params );
+ HeapFree( GetProcessHeap(), 0, ret_addr );
+
+ SetLastError( status == STATUS_INVALID_PARAMETER ? WSAEFAULT : NtStatusToWSAError( status ) );
+ return status ? -1 : 0;
}
+
/***********************************************************************
* closesocket (WS2_32.3)
*/
@@ -2339,98 +2163,28 @@ 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
- unsigned int ifindex;
- int ret;
- socklen_t len;
-
- /* 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 (_get_fd_type(fd) != SOCK_DGRAM)
- return;
-
- len = sizeof(ifindex);
-#if defined(IP_BOUND_IF)
- ret = getsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &ifindex, &len);
-#elif defined(LINUX_BOUND_IF)
- ret = getsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, &len);
- if (!ret) ifindex = ntohl(ifindex);
-#endif
- if (!ret && ifindex)
- {
- EnterCriticalSection(&cs_if_addr_cache);
- if (ifindex < if_addr_cache_size)
- addr->sin_addr.s_addr = if_addr_cache[ifindex];
- else
- ERR("No cache entry for ifindex %u.\n", ifindex);
- LeaveCriticalSection(&cs_if_addr_cache);
- }
-#endif
-}
/***********************************************************************
- * getsockname (WS2_32.6)
+ * getsockname (ws2_32.6)
*/
-int WINAPI WS_getsockname(SOCKET s, struct WS_sockaddr *name, int *namelen)
+int WINAPI WS_getsockname( SOCKET s, struct WS_sockaddr *addr, int *len )
{
- int fd;
- int res;
+ IO_STATUS_BLOCK io;
+ NTSTATUS status;
- TRACE("socket %04lx, ptr %p, len %08x\n", s, name, namelen ? *namelen : 0);
+ TRACE( "socket %#lx, addr %p, len %d\n", s, addr, len ? *len : 0 );
- /* Check if what we've received is valid. Should we use IsBadReadPtr? */
- if( (name == NULL) || (namelen == NULL) )
+ if (!addr)
{
SetLastError( WSAEFAULT );
- return SOCKET_ERROR;
+ return -1;
}
- fd = get_sock_fd( s, 0, NULL );
- res = SOCKET_ERROR;
-
- if (fd != -1)
- {
- union generic_unix_sockaddr uaddr;
- socklen_t uaddrlen;
- int bound = is_fd_bound(fd, &uaddr, &uaddrlen);
-
- if (bound <= 0)
- {
- SetLastError(bound == -1 ? wsaErrno() : WSAEINVAL);
- }
- else if (ws_sockaddr_u2ws(&uaddr.addr, name, namelen) != 0)
- {
- /* The buffer was too small */
- SetLastError(WSAEFAULT);
- }
- else
- {
- 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 );
- }
- return res;
+ status = NtDeviceIoControlFile( (HANDLE)s, NULL, NULL, NULL, &io, IOCTL_AFD_GETSOCKNAME, NULL, 0, addr, *len );
+ if (!status)
+ *len = io.Information;
+ WSASetLastError( NtStatusToWSAError( status ) );
+ return status ? -1 : 0;
}
/***********************************************************************
diff --git a/dlls/ws2_32/tests/afd.c b/dlls/ws2_32/tests/afd.c
index eb4f0a42f8d..8f7f4acf4f2 100644
--- a/dlls/ws2_32/tests/afd.c
+++ b/dlls/ws2_32/tests/afd.c
@@ -1549,13 +1549,13 @@ static void test_getsockname(void)
memset(&addr, 0xcc, sizeof(addr));
ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io,
IOCTL_AFD_GETSOCKNAME, NULL, 0, &addr, sizeof(addr));
- todo_wine ok(!ret, "got %#x\n", ret);
- todo_wine ok(!io.Status, "got %#x\n", io.Status);
- todo_wine ok(io.Information == sizeof(addr), "got %#Ix\n", io.Information);
+ ok(!ret, "got %#x\n", ret);
+ ok(!io.Status, "got %#x\n", io.Status);
+ ok(io.Information == sizeof(addr), "got %#Ix\n", io.Information);
len = sizeof(addr2);
ret = getsockname(client, (struct sockaddr *)&addr2, &len);
ok(!ret, "got error %u\n", WSAGetLastError());
- todo_wine ok(!memcmp(&addr, &addr2, sizeof(struct sockaddr)), "addresses didn't match\n");
+ ok(!memcmp(&addr, &addr2, sizeof(struct sockaddr)), "addresses didn't match\n");
closesocket(client);
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c
index 2d2b4c40141..bb662d78f92 100644
--- a/dlls/ws2_32/tests/sock.c
+++ b/dlls/ws2_32/tests/sock.c
@@ -3604,7 +3604,7 @@ static void test_getsockname(void)
WSASetLastError(0xdeadbeef);
ret = getsockname(sock, (struct sockaddr *) &sa_get, &sa_get_len);
ok(!ret, "got %d\n", ret);
- todo_wine ok(!WSAGetLastError(), "got error %u\n", WSAGetLastError());
+ ok(!WSAGetLastError(), "got error %u\n", WSAGetLastError());
ret = memcmp(sa_get.sin_zero, null_padding, 8);
ok(ret == 0, "getsockname did not zero the sockaddr_in structure\n");
@@ -10292,7 +10292,7 @@ static void test_bind(void)
WSASetLastError(0xdeadbeef);
ret = bind(s, (const struct sockaddr *)&invalid_addr, sizeof(invalid_addr));
ok(ret == -1, "expected failure\n");
- todo_wine ok(WSAGetLastError() == WSAEADDRNOTAVAIL, "got error %u\n", WSAGetLastError());
+ ok(WSAGetLastError() == WSAEADDRNOTAVAIL, "got error %u\n", WSAGetLastError());
WSASetLastError(0xdeadbeef);
ret = bind(s, (const struct sockaddr *)&bind_addr, sizeof(bind_addr));
diff --git a/dlls/ws2_32/ws2_32_private.h b/dlls/ws2_32/ws2_32_private.h
index c27c697a8b6..8baa62908dd 100644
--- a/dlls/ws2_32/ws2_32_private.h
+++ b/dlls/ws2_32/ws2_32_private.h
@@ -89,9 +89,6 @@
# undef if_indextoname
# undef if_nametoindex
#endif
-#ifdef HAVE_LINUX_FILTER_H
-# include <linux/filter.h>
-#endif
#ifdef HAVE_IFADDRS_H
# include <ifaddrs.h>
#endif
@@ -151,7 +148,6 @@
#include "winnt.h"
#define USE_WC_PREFIX /* For CMSG_DATA */
#include "iphlpapi.h"
-#include "netioapi.h"
#include "ip2string.h"
#include "wine/afd.h"
#include "wine/server.h"
--
2.30.2
More information about the wine-devel
mailing list