[2/2] ws2_32: Restore the local socket address that was bound with filter for getsockname() (try 2)

Bruno Jesus 00cpxxx at gmail.com
Wed Apr 8 22:09:29 CDT 2015


try 2:
change function name and simplify a bit

original:
To be able to receive UDP broadcasts wine bind() instead of binding to
the passed IP binds to 0.0.0.0 and set a filter for a specific
interface (which was the original requested bind address). This fixed
many directplay games that work on LAN by sending broadcasts to find
and advertise games. But there is a problem at least for Age of
Mythology, right after binding it will use getsockname() to read the
address back but the function will return 0.0.0.0 as expected by our
code but unexpected by the application, so this patch checks if the
socket is interface bound and retrieve the correct address to it.

//Sample output of wine-git (note the wrong 0.0.0.0 returned on getsockname):
trace:winsock:WS_bind socket 01b8, ptr 0x2434248 { family AF_INET,
address 192.168.0.190, port 2300 }, length 16
trace:winsock:interface_bind Socket 01b8 bound to interface index 3
trace:winsock:WS_getsockname socket 01b8, ptr 0x2434248, len 00000010
trace:winsock:WS_getsockname => { family AF_INET, address 0.0.0.0, port 2300 }

//Sample after patch:
trace:winsock:WS_bind socket 01b8, ptr 0x2434248 { family AF_INET,
address 192.168.0.190, port 2300 }, length 16
trace:winsock:interface_bind Socket 01b8 bound to interface index 3
trace:winsock:WS_getsockname socket 01b8, ptr 0x2434248, len 00000010
trace:winsock:interface_bind_check reporting interface address from adapter 3
trace:winsock:WS_getsockname => { family AF_INET, address
192.168.0.190, port 2300 }

Fix a problem detected from bug
https://bugs.winehq.org/show_bug.cgi?id=35831 which probably is the
bug subject. This is also a prequel to fixing bug
https://bugs.winehq.org/show_bug.cgi?id=31994
-------------- next part --------------

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



More information about the wine-patches mailing list