From: Jamie Taylor Subject: ws2_32: Update WS_get_local_ips to not return a bogus IP address. Message-Id: <20151121061245.GA1221@cloud.preoccupied.net> Date: Sat, 21 Nov 2015 01:12:45 -0500 From: Jamie Taylor 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 --- 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)