From 094eb04ce5d8c1ccef78113c8cc6791d1d3b6bf8 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Tue, 26 Nov 2019 19:13:12 +0200 Subject: [PATCH] Fix MS-Windows build with mingw.org's MinGW mingw.org's MinGW by default targets Windows 9X, so _WIN32_WINNT is set to a value that bypasses declarations in system headers we need to compile network_interface_list. Also, the code needed a workaround for Windows XP, where some functionality is missing from the GetAdaptersAddresses API. * src/w32.c (_WIN32_WINNT): Define to 0x0501, if the value is lower, temporarily while processing iphlpapi.h. (address_prefix_match): New helper function. (network_interface_list): Work around the fact that the OnLinkPrefixLength member of IP_ADAPTER_UNICAST_ADDRESS is not available when _WIN32_WINNT < 0x0600. On Windows XP use special code that calls address_prefix_match to compute the network prefix length. --- src/w32.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/src/w32.c b/src/w32.c index 76c226892a4..cb82d51fb92 100644 --- a/src/w32.c +++ b/src/w32.c @@ -242,8 +242,22 @@ typedef struct _REPARSE_DATA_BUFFER { #undef recvfrom #undef sendto +/* We need at least XP level for GetAdaptersAddresses stuff. */ +#if _WIN32_WINNT < 0x0501 +# undef ORIG_WIN32_WINNT +# define ORIG_WIN32_WINNT _WIN32_WINNT +# undef _WIN32_WINNT +# define _WIN32_WINNT 0x0501 +#endif + #include /* should be after winsock2.h */ +#ifdef ORIG_WIN32_WINNT +# undef _WIN32_WINNT +# define _WIN32_WINNT ORIG_WIN32_WINNT +# undef ORIG_WIN32_WINNT +#endif + #include #include @@ -9432,6 +9446,40 @@ network_interface_get_info (Lisp_Object ifname) return res; } +static bool +address_prefix_match (int family, struct sockaddr *address, + struct sockaddr *prefix_address, ULONG prefix_len) +{ + UINT8 *address_data; + UINT8 *prefix_address_data; + int i; + + if (family == AF_INET6) + { + address_data = (UINT8 *) &(((struct sockaddr_in6 *) address)->sin6_addr); + prefix_address_data = + (UINT8 *) &(((struct sockaddr_in6 *) prefix_address)->sin6_addr); + } + else + { + address_data = (UINT8 *) &(((struct sockaddr_in *) address)->sin_addr); + prefix_address_data = + (UINT8 *) &(((struct sockaddr_in *) prefix_address)->sin_addr); + } + + for (i = 0; i < prefix_len >> 3; i++) + { + if (address_data[i] != prefix_address_data[i]) + return false; + } + + if (prefix_len % 8) + return (prefix_address_data[i] == + (address_data[i] & (0xff << (8 - prefix_len % 8)))); + + return true; +} + Lisp_Object network_interface_list (bool full, unsigned short match) { @@ -9586,7 +9634,37 @@ network_interface_list (bool full, unsigned short match) byte order, so convert from host to network order when generating the netmask. */ int i; - ULONG numbits = address->OnLinkPrefixLength; + ULONG numbits; + if (w32_major_version >= 6) /* Vista or later */ + { +#if _WIN32_WINNT >= 0x0600 + numbits = address->OnLinkPrefixLength; +#else + /* Kludge alert! OnLinkPrefixLength is only defined + when compiling for Vista and later. */ + numbits = *(UINT8 *) (address->LeaseLifetime + + sizeof (address->LeaseLifetime)); +#endif + } + else /* Windows XP */ + { + IP_ADAPTER_PREFIX *prefix = adapter->FirstPrefix; + numbits = 0; + for ( ; prefix; prefix = prefix->Next) + { + /* We want the longest matching prefix. */ + if (prefix->Address.lpSockaddr->sa_family + != ifa_addr->sa_family + || prefix->PrefixLength <= numbits) + continue; + if (address_prefix_match (ifa_addr->sa_family, ifa_addr, + prefix->Address.lpSockaddr, + prefix->PrefixLength)) + numbits = prefix->PrefixLength; + } + if (!numbits) + numbits = (ifa_addr->sa_family == AF_INET6) ? 128 : 32; + } for (i = 0; i < addr_len; i++) { if (numbits >= 32) -- 2.39.5