ws2_32: Update WS_get_local_ips to not return a bogus IP address.

Jamie Taylor Jamie.Taylor at pobox.com
Sat Nov 21 00:12:45 CST 2015


From: Jamie Taylor <Jamie.Taylor at pobox.com>
Date: Sat, 21 Nov 2015 01:03:47 -0500
Subject: ws2_32: Update WS_get_local_ips to not return a bogus IP  address.

There were many shortcomings in the previous behavior, but most
importantly it resulted in the first IP address in the
returned hostent structure being the magic loopback address in
the default network configuration on Mac OS X.  The new behavior
returns all IP addresses from non-loopback network adapters, and
ensures that the addresses from the adapter for the default route
are at the front of the list.  Testing confirmed this behavior
on Mac OS X.

This will most likely fix https://bugs.winehq.org/show_bug.cgi?id=37271
and https://bugs.winehq.org/show_bug.cgi?id=22819
but I do not have the programs under test in those bugs, so I can't
verify.

I also do not have convenient access to a linux machine with an
equivalent network configuration (i.e., at least two interfaces
with default routes) to test.

Signed-off-by: Jamie Taylor <Jamie.Taylor at pobox.com>
---
 dlls/ws2_32/socket.c |  134 ++++++++++++++++++++------------------------------
 1 file changed, 52 insertions(+), 82 deletions(-)

diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c
index 9e8510d..411c049 100644
--- a/dlls/ws2_32/socket.c
+++ b/dlls/ws2_32/socket.c
@@ -590,13 +590,6 @@ struct per_thread_data
     char ntoa_buffer[16]; /* 4*3 digits + 3 '.' + 1 '\0' */
 };
 
-/* internal: routing description information */
-struct route {
-    struct in_addr addr;
-    IF_INDEX interface;
-    DWORD metric;
-};
-
 static INT num_startup;          /* reference counter */
 static FARPROC blocking_hook = (FARPROC)WSA_DefaultBlockingHook;
 
@@ -5921,38 +5914,36 @@ struct WS_hostent* WINAPI WS_gethostbyaddr(const char *addr, int len, int type)
     return retval;
 }
 
-/***********************************************************************
- *		WS_compare_routes_by_metric_asc (INTERNAL)
- *
- * Comparison function for qsort(), for sorting two routes (struct route)
- * by metric in ascending order.
- */
-static int WS_compare_routes_by_metric_asc(const void *left, const void *right)
-{
-    return ((const struct route*)left)->metric - ((const struct route*)right)->metric;
-}
 
 /***********************************************************************
  *		WS_get_local_ips		(INTERNAL)
  *
  * Returns the list of local IP addresses by going through the network
- * adapters and using the local routing table to sort the addresses
- * from highest routing priority to lowest routing priority. This
- * functionality is inferred from the description for obtaining local
- * IP addresses given in the Knowledge Base Article Q160215.
+ * adapters and using the local routing table to ensure that the
+ * address(es) of the adapter that is the default route appear first.
+ *
+ * This functionality is inferred from the description for obtaining local
+ * IP addresses given in the Knowledge Base Article Q160215.  The
+ * requirement that the first entry be the "primary" IP address is stated
+ * in Knowledge Base Article 822713.  Some versions of Windows provided
+ * various ways to influence the order in which the addresses are
+ * returned, as described in Knowledge Base Articles 171320 and 164023,
+ * but this code does not attempt anything nearly so ambitious.
  *
  * Please note that the returned hostent is only freed when the thread
  * closes and is replaced if another hostent is requested.
  */
 static struct WS_hostent* WS_get_local_ips( char *hostname )
 {
-    int numroutes = 0, i, j;
+    int i, j;
+    int numaddrs = 0, iface_addrs, default_iface_addrs = 0;
     DWORD n;
     PIP_ADAPTER_INFO adapters = NULL, k;
+    PIP_ADDR_STRING addrs;
     struct WS_hostent *hostlist = NULL;
     PMIB_IPFORWARDTABLE routes = NULL;
-    struct route *route_addrs = NULL;
     DWORD adap_size, route_size;
+    IF_INDEX default_iface = 0;
 
     /* Obtain the size of the adapter list and routing table, also allocate memory */
     if (GetAdaptersInfo(NULL, &adap_size) != ERROR_BUFFER_OVERFLOW)
@@ -5961,86 +5952,65 @@ static struct WS_hostent* WS_get_local_ips( char *hostname )
         return NULL;
     adapters = HeapAlloc(GetProcessHeap(), 0, adap_size);
     routes = HeapAlloc(GetProcessHeap(), 0, route_size);
-    route_addrs = HeapAlloc(GetProcessHeap(), 0, 0); /* HeapReAlloc doesn't work on NULL */
-    if (adapters == NULL || routes == NULL || route_addrs == NULL)
+    if (adapters == NULL || routes == NULL)
         goto cleanup;
     /* Obtain the adapter list and the full routing table */
     if (GetAdaptersInfo(adapters, &adap_size) != NO_ERROR)
         goto cleanup;
     if (GetIpForwardTable(routes, &route_size, FALSE) != NO_ERROR)
         goto cleanup;
-    /* Store the interface associated with each route */
-    for (n = 0; n < routes->dwNumEntries; n++)
-    {
-        IF_INDEX ifindex;
-        DWORD ifmetric;
-        BOOL exists = FALSE;
-
-        if (routes->table[n].u1.ForwardType != MIB_IPROUTE_TYPE_DIRECT)
-            continue;
-        ifindex = routes->table[n].dwForwardIfIndex;
-        ifmetric = routes->table[n].dwForwardMetric1;
-        /* Only store the lowest valued metric for an interface */
-        for (j = 0; j < numroutes; j++)
-        {
-            if (route_addrs[j].interface == ifindex)
-            {
-                if (route_addrs[j].metric > ifmetric)
-                    route_addrs[j].metric = ifmetric;
-                exists = TRUE;
-            }
-        }
-        if (exists)
-            continue;
-        route_addrs = HeapReAlloc(GetProcessHeap(), 0, route_addrs, (numroutes+1)*sizeof(struct route));
-        if (route_addrs == NULL)
-            goto cleanup; /* Memory allocation error, fail gracefully */
-        route_addrs[numroutes].interface = ifindex;
-        route_addrs[numroutes].metric = ifmetric;
-        /* If no IP is found in the next step (for whatever reason)
-         * then fall back to the magic loopback address.
-         */
-        memcpy(&(route_addrs[numroutes].addr.s_addr), magic_loopback_addr, 4);
-        numroutes++;
+    /* Find the interface for the default route.
+     * NB: a machine can have more than one default route; this code selects the first.
+     * (This will yield correct behavior on Mac OS X, which returns the preferred interface
+     * first in the forwarding table.  The correct entry could also be distinguished by 
+     * examining the RTF_IFSCOPE flag on all of the entries with default routes and 
+     * eliminating those that have the flag set, but that information is not propaged into
+     * the MIB_IPFORWARDTABLE structure.) */
+    for (n = 0; n < routes->dwNumEntries; n++) {
+      if (0 == routes->table[n].dwForwardDest) {
+        default_iface = routes->table[n].dwForwardIfIndex;
+        break;
+      }
     }
-   if (numroutes == 0)
-       goto cleanup; /* No routes, fall back to the Magic IP */
-    /* Find the IP address associated with each found interface */
-    for (i = 0; i < numroutes; i++)
-    {
-        for (k = adapters; k != NULL; k = k->Next)
-        {
-            char *ip = k->IpAddressList.IpAddress.String;
-
-            if (route_addrs[i].interface == k->Index)
-                route_addrs[i].addr.s_addr = (in_addr_t) inet_addr(ip);
+    /* Count the number of addresses we're going to return.  This avoids re-alloc overhead. */
+    for (k = adapters; k != NULL; k = k->Next) {
+      iface_addrs = 0;
+      for (addrs = &(k->IpAddressList); addrs != NULL; addrs = addrs->Next) {
+        if (strcmp(addrs->IpAddress.String, "0.0.0.0") ) {
+          iface_addrs++;
         }
+      }
+      numaddrs += iface_addrs;
+      if (k->Index == default_iface) default_iface_addrs = iface_addrs;
     }
+    if (0 == numaddrs)
+      goto cleanup;
     /* Allocate a hostent and enough memory for all the IPs,
      * including the NULL at the end of the list.
      */
-    hostlist = WS_create_he(hostname, 1, 0, numroutes+1, sizeof(struct in_addr));
+    hostlist = WS_create_he(hostname, 1, 0, numaddrs+1, sizeof(struct in_addr));
     if (hostlist == NULL)
         goto cleanup; /* Failed to allocate a hostent for the list of IPs */
-    hostlist->h_addr_list[numroutes] = NULL; /* NULL-terminate the address list */
+    hostlist->h_addr_list[numaddrs] = NULL; /* NULL-terminate the address list */
     hostlist->h_aliases[0] = NULL; /* NULL-terminate the alias list */
     hostlist->h_addrtype = AF_INET;
     hostlist->h_length = sizeof(struct in_addr); /* = 4 */
-    /* Reorder the entries before placing them in the host list. Windows expects
-     * the IP list in order from highest priority to lowest (the critical thing
-     * is that most applications expect the first IP to be the default route).
-     */
-    if (numroutes > 1)
-        qsort(route_addrs, numroutes, sizeof(struct route), WS_compare_routes_by_metric_asc);
-
-    for (i = 0; i < numroutes; i++)
-        (*(struct in_addr *) hostlist->h_addr_list[i]) = route_addrs[i].addr;
-
+    /* Fill in the addresses, with the ones from the default interface in the front. */
+    i = default_iface_addrs;
+    for (k = adapters; k != NULL; k = k->Next) {
+      j = (k->Index == default_iface) ? 0 : i;
+      for (addrs = &(k->IpAddressList); addrs != NULL; addrs = addrs->Next) {
+        if (strcmp(addrs->IpAddress.String, "0.0.0.0") ) {
+          (*(struct in_addr *) hostlist->h_addr_list[j]).s_addr = inet_addr(addrs->IpAddress.String);
+          j++;
+        }
+      }
+      if (k->Index != default_iface) i = j;
+    }
     /* Cleanup all allocated memory except the address list,
      * the address list is used by the calling app.
      */
 cleanup:
-    HeapFree(GetProcessHeap(), 0, route_addrs);
     HeapFree(GetProcessHeap(), 0, adapters);
     HeapFree(GetProcessHeap(), 0, routes);
     return hostlist;
-- 
1.7.9.6 (Apple Git-31.1)




More information about the wine-patches mailing list