[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( &params->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