summaryrefslogtreecommitdiff
path: root/src/w32.c
diff options
context:
space:
mode:
authorRobert Pluim <rpluim@gmail.com>2019-11-15 11:11:30 +0100
committerRobert Pluim <rpluim@gmail.com>2019-11-26 08:46:13 +0100
commit650a514e996287106f9a9525b6f27068ec2a0cbf (patch)
tree188170aae76f32d894d8df1baa7f435fbfe99d88 /src/w32.c
parent5c3d0cf7910afa6b3fbdba24ac5c5817f268eb0e (diff)
downloademacs-650a514e996287106f9a9525b6f27068ec2a0cbf.tar.gz
Extend network-interface-list to return IPv6 and network info
Bug#38218 * src/process.c (Fnetwork_interface_list): Extend argument list to allow requesting full network info and/or IPv4/IPv6 info. (network_interface_list) [HAVE_GETIFADDRS]: Use getifaddrs to retrieve interface IP addresses. * src/process.h: Update prototype of network_interface_list. * src/w32.c (g_b_init_get_adapters_addresses): New init flag. (globals_of_w32): Initialize it. (GetAdaptersAddresses_Proc): New function typedef. (get_adapters_addresses): New wrapper function. (init_winsock): Load htonl and ntohl. (sys_htonl, sys_ntohl): New wrapper functions. (network_interface_list): Implement in terms of get_adapters_addresses. * nt/inc/sys/socket.h: Add sys_htonl and sys_ntohl prototypes. * etc/NEWS: Announce IPv4/IPv6 changes in network-interface-list. * doc/lispref/processes.texi (Misc Network): Document updated arglist and return values for network-interface-list.
Diffstat (limited to 'src/w32.c')
-rw-r--r--src/w32.c244
1 files changed, 242 insertions, 2 deletions
diff --git a/src/w32.c b/src/w32.c
index 26ea15d8910..76c226892a4 100644
--- a/src/w32.c
+++ b/src/w32.c
@@ -227,6 +227,8 @@ typedef struct _REPARSE_DATA_BUFFER {
#undef connect
#undef htons
#undef ntohs
+#undef htonl
+#undef ntohl
#undef inet_addr
#undef gethostname
#undef gethostbyname
@@ -326,6 +328,7 @@ static BOOL g_b_init_set_file_security_a;
static BOOL g_b_init_set_named_security_info_w;
static BOOL g_b_init_set_named_security_info_a;
static BOOL g_b_init_get_adapters_info;
+static BOOL g_b_init_get_adapters_addresses;
static BOOL g_b_init_reg_open_key_ex_w;
static BOOL g_b_init_reg_query_value_ex_w;
static BOOL g_b_init_expand_environment_strings_w;
@@ -503,6 +506,12 @@ typedef BOOL (WINAPI *IsValidSecurityDescriptor_Proc) (PSECURITY_DESCRIPTOR);
typedef DWORD (WINAPI *GetAdaptersInfo_Proc) (
PIP_ADAPTER_INFO pAdapterInfo,
PULONG pOutBufLen);
+typedef DWORD (WINAPI *GetAdaptersAddresses_Proc) (
+ ULONG,
+ ULONG,
+ PVOID,
+ PIP_ADAPTER_ADDRESSES,
+ PULONG);
int (WINAPI *pMultiByteToWideChar)(UINT,DWORD,LPCSTR,int,LPWSTR,int);
int (WINAPI *pWideCharToMultiByte)(UINT,DWORD,LPCWSTR,int,LPSTR,int,LPCSTR,LPBOOL);
@@ -1368,6 +1377,31 @@ get_adapters_info (PIP_ADAPTER_INFO pAdapterInfo, PULONG pOutBufLen)
return s_pfn_Get_Adapters_Info (pAdapterInfo, pOutBufLen);
}
+static DWORD WINAPI
+get_adapters_addresses (ULONG family, PIP_ADAPTER_ADDRESSES pAdapterAddresses, PULONG pOutBufLen)
+{
+ static GetAdaptersAddresses_Proc s_pfn_Get_Adapters_Addresses = NULL;
+ HMODULE hm_iphlpapi = NULL;
+
+ if (is_windows_9x () == TRUE)
+ return ERROR_NOT_SUPPORTED;
+
+ if (g_b_init_get_adapters_addresses == 0)
+ {
+ g_b_init_get_adapters_addresses = 1;
+ hm_iphlpapi = LoadLibrary ("Iphlpapi.dll");
+ if (hm_iphlpapi)
+ s_pfn_Get_Adapters_Addresses = (GetAdaptersAddresses_Proc)
+ get_proc_addr (hm_iphlpapi, "GetAdaptersAddresses");
+ }
+ if (s_pfn_Get_Adapters_Addresses == NULL)
+ return ERROR_NOT_SUPPORTED;
+ ULONG flags = GAA_FLAG_SKIP_ANYCAST
+ | GAA_FLAG_SKIP_MULTICAST
+ | GAA_FLAG_SKIP_DNS_SERVER;
+ return s_pfn_Get_Adapters_Addresses (family, flags, NULL, pAdapterAddresses, pOutBufLen);
+}
+
static LONG WINAPI
reg_open_key_ex_w (HKEY hkey, LPCWSTR lpSubKey, DWORD ulOptions,
REGSAM samDesired, PHKEY phkResult)
@@ -7414,6 +7448,8 @@ int (PASCAL *pfn_WSACleanup) (void);
u_short (PASCAL *pfn_htons) (u_short hostshort);
u_short (PASCAL *pfn_ntohs) (u_short netshort);
+u_long (PASCAL *pfn_htonl) (u_long hostlong);
+u_long (PASCAL *pfn_ntohl) (u_long netlong);
unsigned long (PASCAL *pfn_inet_addr) (const char * cp);
int (PASCAL *pfn_gethostname) (char * name, int namelen);
struct hostent * (PASCAL *pfn_gethostbyname) (const char * name);
@@ -7504,6 +7540,8 @@ init_winsock (int load_now)
LOAD_PROC (shutdown);
LOAD_PROC (htons);
LOAD_PROC (ntohs);
+ LOAD_PROC (htonl);
+ LOAD_PROC (ntohl);
LOAD_PROC (inet_addr);
LOAD_PROC (gethostname);
LOAD_PROC (gethostbyname);
@@ -7884,6 +7922,19 @@ sys_ntohs (u_short netshort)
return (winsock_lib != NULL) ?
pfn_ntohs (netshort) : netshort;
}
+u_long
+sys_htonl (u_long hostlong)
+{
+ return (winsock_lib != NULL) ?
+ pfn_htonl (hostlong) : hostlong;
+}
+
+u_long
+sys_ntohl (u_long netlong)
+{
+ return (winsock_lib != NULL) ?
+ pfn_ntohl (netlong) : netlong;
+}
unsigned long
sys_inet_addr (const char * cp)
@@ -9382,9 +9433,197 @@ network_interface_get_info (Lisp_Object ifname)
}
Lisp_Object
-network_interface_list (void)
+network_interface_list (bool full, unsigned short match)
{
- return network_interface_get_info (Qnil);
+ ULONG ainfo_len = sizeof (IP_ADAPTER_ADDRESSES);
+ ULONG family = match;
+ IP_ADAPTER_ADDRESSES *adapter, *ainfo = xmalloc (ainfo_len);
+ DWORD retval = get_adapters_addresses (family, ainfo, &ainfo_len);
+ Lisp_Object res = Qnil;
+
+ if (retval == ERROR_BUFFER_OVERFLOW)
+ {
+ ainfo = xrealloc (ainfo, ainfo_len);
+ retval = get_adapters_addresses (family, ainfo, &ainfo_len);
+ }
+
+ if (retval != ERROR_SUCCESS)
+ {
+ xfree (ainfo);
+ return res;
+ }
+
+ /* For the below, we need some winsock functions, so make sure
+ the winsock DLL is loaded. If we cannot successfully load
+ it, they will have no use of the information we provide,
+ anyway, so punt. */
+ if (!winsock_lib && !init_winsock (1))
+ return res;
+
+ int eth_count = 0, tr_count = 0, fddi_count = 0, ppp_count = 0;
+ int sl_count = 0, wlan_count = 0, lo_count = 0, ifx_count = 0;
+ int tnl_count = 0;
+ int if_num;
+ char namebuf[MAX_ADAPTER_NAME_LENGTH + 4];
+ static const char *ifmt[] = {
+ "eth%d", "tr%d", "fddi%d", "ppp%d", "sl%d", "wlan%d",
+ "lo%d", "ifx%d", "tunnel%d"
+ };
+ enum {
+ NONE = -1,
+ ETHERNET = 0,
+ TOKENRING = 1,
+ FDDI = 2,
+ PPP = 3,
+ SLIP = 4,
+ WLAN = 5,
+ LOOPBACK = 6,
+ OTHER_IF = 7,
+ TUNNEL = 8
+ } ifmt_idx;
+
+ for (adapter = ainfo; adapter; adapter = adapter->Next)
+ {
+
+ /* Present Unix-compatible interface names, instead of the
+ Windows names, which are really GUIDs not readable by
+ humans. */
+
+ switch (adapter->IfType)
+ {
+ case IF_TYPE_ETHERNET_CSMACD:
+ ifmt_idx = ETHERNET;
+ if_num = eth_count++;
+ break;
+ case IF_TYPE_ISO88025_TOKENRING:
+ ifmt_idx = TOKENRING;
+ if_num = tr_count++;
+ break;
+ case IF_TYPE_FDDI:
+ ifmt_idx = FDDI;
+ if_num = fddi_count++;
+ break;
+ case IF_TYPE_PPP:
+ ifmt_idx = PPP;
+ if_num = ppp_count++;
+ break;
+ case IF_TYPE_SLIP:
+ ifmt_idx = SLIP;
+ if_num = sl_count++;
+ break;
+ case IF_TYPE_IEEE80211:
+ ifmt_idx = WLAN;
+ if_num = wlan_count++;
+ break;
+ case IF_TYPE_SOFTWARE_LOOPBACK:
+ ifmt_idx = LOOPBACK;
+ if_num = lo_count++;
+ break;
+ case IF_TYPE_TUNNEL:
+ ifmt_idx = TUNNEL;
+ if_num = tnl_count++;
+ break;
+ default:
+ ifmt_idx = OTHER_IF;
+ if_num = ifx_count++;
+ break;
+ }
+ sprintf (namebuf, ifmt[ifmt_idx], if_num);
+
+ IP_ADAPTER_UNICAST_ADDRESS *address;
+ for (address = adapter->FirstUnicastAddress; address; address = address->Next)
+ {
+ int len;
+ int addr_len;
+ uint32_t *maskp;
+ uint32_t *addrp;
+ Lisp_Object elt = Qnil;
+ struct sockaddr *ifa_addr = address->Address.lpSockaddr;
+
+ if (ifa_addr == NULL)
+ continue;
+ if (match && ifa_addr->sa_family != match)
+ continue;
+
+ struct sockaddr_in ipv4;
+#ifdef AF_INET6
+ struct sockaddr_in6 ipv6;
+#endif
+ struct sockaddr *sin;
+
+ if (ifa_addr->sa_family == AF_INET)
+ {
+ ipv4.sin_family = AF_INET;
+ ipv4.sin_port = 0;
+ DECLARE_POINTER_ALIAS (sin_in, struct sockaddr_in, ifa_addr);
+ addrp = (uint32_t *)&sin_in->sin_addr;
+ maskp = (uint32_t *)&ipv4.sin_addr;
+ sin = (struct sockaddr *)&ipv4;
+ len = sizeof (struct sockaddr_in);
+ addr_len = 1;
+ }
+#ifdef AF_INET6
+ else if (ifa_addr->sa_family == AF_INET6)
+ {
+ ipv6.sin6_family = AF_INET6;
+ ipv6.sin6_port = 0;
+ DECLARE_POINTER_ALIAS (sin_in6, struct sockaddr_in6, ifa_addr);
+ addrp = (uint32_t *)&sin_in6->sin6_addr;
+ maskp = (uint32_t *)&ipv6.sin6_addr;
+ sin = (struct sockaddr *)&ipv6;
+ len = sizeof (struct sockaddr_in6);
+ addr_len = 4;
+ }
+#endif
+ else
+ continue;
+
+ Lisp_Object addr = conv_sockaddr_to_lisp (ifa_addr, len);
+
+ if (full)
+ {
+ /* GetAdaptersAddress returns information in network
+ byte order, so convert from host to network order
+ when generating the netmask. */
+ int i;
+ ULONG numbits = address->OnLinkPrefixLength;
+ for (i = 0; i < addr_len; i++)
+ {
+ if (numbits >= 32)
+ {
+ maskp[i] = -1U;
+ numbits -= 32;
+ }
+ else if (numbits)
+ {
+ maskp[i] = sys_htonl (-1U << (32 - numbits));
+ numbits = 0;
+ }
+ else
+ {
+ maskp[i] = 0;
+ }
+ }
+ elt = Fcons (conv_sockaddr_to_lisp (sin, len), elt);
+ uint32_t mask;
+ for (i = 0; i < addr_len; i++)
+ {
+ mask = maskp[i];
+ maskp[i] = (addrp[i] & mask) | ~mask;
+
+ }
+ elt = Fcons (conv_sockaddr_to_lisp (sin, len), elt);
+ elt = Fcons (addr, elt);
+ }
+ else
+ {
+ elt = addr;
+ }
+ res = Fcons (Fcons (build_string (namebuf), elt), res);
+ }
+ }
+ xfree (ainfo);
+ return res;
}
Lisp_Object
@@ -10099,6 +10338,7 @@ globals_of_w32 (void)
g_b_init_set_named_security_info_w = 0;
g_b_init_set_named_security_info_a = 0;
g_b_init_get_adapters_info = 0;
+ g_b_init_get_adapters_addresses = 0;
g_b_init_reg_open_key_ex_w = 0;
g_b_init_reg_query_value_ex_w = 0;
g_b_init_expand_environment_strings_w = 0;