]> git.eshelyaron.com Git - emacs.git/commitdiff
Extend network-interface-list to return IPv6 and network info
authorRobert Pluim <rpluim@gmail.com>
Fri, 15 Nov 2019 10:11:30 +0000 (11:11 +0100)
committerRobert Pluim <rpluim@gmail.com>
Tue, 26 Nov 2019 07:46:13 +0000 (08:46 +0100)
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.

doc/lispref/processes.texi
etc/NEWS
lisp/net/nsm.el
nt/inc/sys/socket.h
src/process.c
src/process.h
src/w32.c

index fc5832253f9fd7ce173e521ba4c4bedd0e30fffc..e33ae287ff90dbad95b09e27d628621e6926936b 100644 (file)
@@ -2971,12 +2971,67 @@ non-@code{nil} if that particular network option is supported by
 on network connections.  Note that they are supported only on some
 systems.
 
-@defun network-interface-list
-This function returns a list describing the network interfaces
-of the machine you are using.  The value is an alist whose
-elements have the form @code{(@var{name} . @var{address})}.
-@var{address} has the same form as the @var{local-address}
-and @var{remote-address} arguments to @code{make-network-process}.
+@defun network-interface-list &optional full family
+This function returns a list describing the network interfaces of the
+machine you are using.  The value is an alist whose elements have the
+form @code{(@var{ifname} . @var{address})}.  @var{ifname} is a string
+naming the interface, @var{address} has the same form as the
+@var{local-address} and @var{remote-address} arguments to
+@code{make-network-process}, i.e. a vector of integers.  By default
+both IPv4 and IPv6 addresses are returned if possible.
+
+Optional argument @var{full} non-@code{nil} means to instead return a
+list of one or more elements of the form @w{@code{(@var{ifname}
+@var{addr} @var{bcast} @var{netmask})}}.  @var{ifname} is a non-unique
+string naming the interface.  @var{addr}, @var{bcast}, and
+@var{netmask} are vectors of integers detailing the IP address,
+broadcast address, and network mask.
+
+Optional argument @var{family} specified as symbol @code{ipv4} or
+@code{ipv6} restricts the returned information to IPv4 and IPv6
+addresses respectively, independently of the value of @var{full}.
+Speficying @code{ipv6} when IPv6 support is not available will result
+in an error being signaled.
+
+Some examples:
+
+@example
+(network-interface-list) @result{}
+(("vmnet8" .
+  [172 16 76 1 0])
+ ("vmnet1" .
+  [172 16 206 1 0])
+ ("lo0" .
+  [65152 0 0 0 0 0 0 1 0])
+ ("lo0" .
+  [0 0 0 0 0 0 0 1 0])
+ ("lo0" .
+  [127 0 0 1 0]))
+@end example
+
+@example
+(network-interface-list t) @result{}
+(("vmnet8"
+  [172 16 76 1 0]
+  [172 16 76 255 0]
+  [255 255 255 0 0])
+ ("vmnet1"
+  [172 16 206 1 0]
+  [172 16 206 255 0]
+  [255 255 255 0 0])
+ ("lo0"
+  [65152 0 0 0 0 0 0 1 0]
+  [65152 0 0 0 65535 65535 65535 65535 0]
+  [65535 65535 65535 65535 0 0 0 0 0])
+ ("lo0"
+  [0 0 0 0 0 0 0 1 0]
+  [0 0 0 0 0 0 0 1 0]
+  [65535 65535 65535 65535 65535 65535 65535 65535 0])
+ ("lo0"
+  [127 0 0 1 0]
+  [127 255 255 255 0]
+  [255 0 0 0 0]))
+@end example
 @end defun
 
 @defun network-interface-info ifname
@@ -2996,6 +3051,8 @@ The layer 2 address (Ethernet MAC address, for instance).
 @item flags
 The current flags of the interface.
 @end table
+
+Note that this function returns only IPv4 information.
 @end defun
 
 @defun format-network-address address &optional omit-port
index 7e86ccc71e390d2728fb1b3f792ca4723d767758..a97cf20ea7c807797794eb7c83dc6dae1fe21159 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -240,6 +240,11 @@ in addition to the decimal/hex/octal representation.  Default nil.
 ** New function 'network-lookup-address-info'.
 This does IPv4 and/or IPv6 address lookups on hostnames.
 
++++
+** 'network-interface-list' can now return IPv4 and IPv6 addresses.
+IPv4 and IPv6 addresses are now returned by default if available,
+optionally including netmask/broadcast address information.
+
 ---
 ** Control of the threshold for using the 'distant-foreground' color.
 The threshold for color distance below which the 'distant-foreground'
index 205b7974883f843d8528eaf904ecb4d491ae11bf..5dc5244e6d54084f098b8a42eda8ea6b3f76a2f2 100644 (file)
@@ -228,21 +228,22 @@ host address is a localhost address, or in the same subnet as one
 of the local interfaces, this function returns nil.  Non-nil
 otherwise."
   (let ((addresses (network-lookup-address-info host))
-        (network-interface-list (network-interface-list))
+        (network-interface-list (network-interface-list t))
         (off-net t))
     (when
      (or (and (functionp nsm-trust-local-network)
               (funcall nsm-trust-local-network))
          nsm-trust-local-network)
      (mapc
-      (lambda (address)
+      (lambda (ip)
         (mapc
-         (lambda (iface)
-           (let ((info (network-interface-info (car iface))))
+         (lambda (info)
+           (let ((local-ip (nth 1 info))
+                 (mask (nth 2 info)))
              (when
-                 (nsm-network-same-subnet (substring (car info) 0 -1)
-                                          (substring (car (cddr info)) 0 -1)
-                                          (substring address 0 -1))
+                 (nsm-network-same-subnet (substring local-ip 0 -1)
+                                          (substring mask 0 -1)
+                                          (substring ip 0 -1))
                (setq off-net nil))))
          network-interface-list))
       addresses))
index 6d26ff907e649a3c602011ff4b126711075fb8e9..0f3943b453a57d591b184f0223642a6451efb251 100644 (file)
@@ -92,6 +92,8 @@ typedef unsigned short uint16_t;
 #define connect        sys_connect
 #define htons          sys_htons
 #define ntohs          sys_ntohs
+#define htonl          sys_htonl
+#define ntohl          sys_ntohl
 #define inet_addr      sys_inet_addr
 #define gethostname    sys_gethostname
 #define gethostbyname  sys_gethostbyname
@@ -112,6 +114,8 @@ int sys_bind (int s, const struct sockaddr *addr, int namelen);
 int sys_connect (int s, const struct sockaddr *addr, int namelen);
 u_short sys_htons (u_short hostshort);
 u_short sys_ntohs (u_short netshort);
+u_long sys_htonl (u_long hostlong);
+u_long sys_ntohl (u_long netlong);
 unsigned long sys_inet_addr (const char * cp);
 int sys_gethostname (char * name, int namelen);
 struct hostent * sys_gethostbyname (const char * name);
index 9158cfd347ce7881e0b54400b41aa1932e39a2c9..0f82682ae5f76cc79f39b4164843417af065bf29 100644 (file)
@@ -4255,73 +4255,86 @@ usage: (make-network-process &rest ARGS)  */)
 }
 
 \f
-#ifdef HAVE_NET_IF_H
 
-#ifdef SIOCGIFCONF
+#ifdef HAVE_GETIFADDRS
 static Lisp_Object
-network_interface_list (void)
+network_interface_list (bool full, unsigned short match)
 {
-  struct ifconf ifconf;
-  struct ifreq *ifreq;
-  void *buf = NULL;
-  ptrdiff_t buf_size = 512;
-  int s;
-  Lisp_Object res;
-  ptrdiff_t count;
+  Lisp_Object res = Qnil;
+  struct ifaddrs *ifap;
 
-  s = socket (AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
-  if (s < 0)
+  if (getifaddrs (&ifap) == -1)
     return Qnil;
-  count = SPECPDL_INDEX ();
-  record_unwind_protect_int (close_file_unwind, s);
 
-  do
+  for (struct ifaddrs *it = ifap; it != NULL; it = it->ifa_next)
     {
-      buf = xpalloc (buf, &buf_size, 1, INT_MAX, 1);
-      ifconf.ifc_buf = buf;
-      ifconf.ifc_len = buf_size;
-      if (ioctl (s, SIOCGIFCONF, &ifconf))
-       {
-         emacs_close (s);
-         xfree (buf);
-         return Qnil;
-       }
-    }
-  while (ifconf.ifc_len == buf_size);
-
-  res = unbind_to (count, Qnil);
-  ifreq = ifconf.ifc_req;
-  while ((char *) ifreq < (char *) ifconf.ifc_req + ifconf.ifc_len)
-    {
-      struct ifreq *ifq = ifreq;
-#ifdef HAVE_STRUCT_IFREQ_IFR_ADDR_SA_LEN
-#define SIZEOF_IFREQ(sif)                                              \
-      ((sif)->ifr_addr.sa_len < sizeof (struct sockaddr)               \
-       ? sizeof (*(sif)) : sizeof ((sif)->ifr_name) + (sif)->ifr_addr.sa_len)
+      int len;
+      int addr_len;
+      uint32_t *maskp;
+      uint32_t *addrp;
+      Lisp_Object elt = Qnil;
 
-      int len = SIZEOF_IFREQ (ifq);
-#else
-      int len = sizeof (*ifreq);
+      /* BSD can allegedly return interfaces with a NULL address.  */
+      if (it->ifa_addr == NULL)
+        continue;
+      if (match && it->ifa_addr->sa_family != match)
+        continue;
+      if (it->ifa_addr->sa_family == AF_INET)
+        {
+          DECLARE_POINTER_ALIAS (sin1, struct sockaddr_in, it->ifa_netmask);
+          maskp = (uint32_t *)&sin1->sin_addr;
+          DECLARE_POINTER_ALIAS (sin2, struct sockaddr_in, it->ifa_addr);
+          addrp = (uint32_t *)&sin2->sin_addr;
+          len = sizeof (struct sockaddr_in);
+          addr_len = 1;
+        }
+#ifdef AF_INET6
+      else if (it->ifa_addr->sa_family == AF_INET6)
+        {
+          DECLARE_POINTER_ALIAS (sin6_1, struct sockaddr_in6, it->ifa_netmask);
+          maskp = (uint32_t *) &sin6_1->sin6_addr;
+          DECLARE_POINTER_ALIAS (sin6_2, struct sockaddr_in6, it->ifa_addr);
+          addrp = (uint32_t *) &sin6_2->sin6_addr;
+          len = sizeof (struct sockaddr_in6);
+          addr_len = 4;
+        }
 #endif
-      char namebuf[sizeof (ifq->ifr_name) + 1];
-      ifreq = (struct ifreq *) ((char *) ifreq + len);
+      else
+        continue;
 
-      if (ifq->ifr_addr.sa_family != AF_INET)
-       continue;
+      Lisp_Object addr = conv_sockaddr_to_lisp (it->ifa_addr, len);
 
-      memcpy (namebuf, ifq->ifr_name, sizeof (ifq->ifr_name));
-      namebuf[sizeof (ifq->ifr_name)] = 0;
-      res = Fcons (Fcons (build_string (namebuf),
-                         conv_sockaddr_to_lisp (&ifq->ifr_addr,
-                                                sizeof (struct sockaddr))),
-                  res);
+      if (full)
+        {
+          elt = Fcons (conv_sockaddr_to_lisp (it->ifa_netmask, len), elt);
+          /* There is an it->ifa_broadaddr field, but its contents are
+             unreliable, so always calculate the broadcast address from
+             the address and the netmask.  */
+          int i;
+          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 (it->ifa_netmask, len), elt);
+          elt = Fcons (addr, elt);
+        }
+      else
+        {
+          elt = addr;
+        }
+      res = Fcons (Fcons (build_string (it->ifa_name), elt), res);
     }
+#ifdef HAVE_FREEIFADDRS
+  freeifaddrs (ifap);
+#endif
 
-  xfree (buf);
   return res;
 }
-#endif /* SIOCGIFCONF */
+#endif  /* HAVE_GETIFADDRS */
 
+#ifdef HAVE_NET_IF_H
 #if defined (SIOCGIFADDR) || defined (SIOCGIFHWADDR) || defined (SIOCGIFFLAGS)
 
 struct ifflag_def {
@@ -4550,17 +4563,46 @@ network_interface_info (Lisp_Object ifname)
 #endif /* defined (HAVE_NET_IF_H) */
 
 DEFUN ("network-interface-list", Fnetwork_interface_list,
-       Snetwork_interface_list, 0, 0, 0,
+       Snetwork_interface_list, 0, 2, 0,
        doc: /* Return an alist of all network interfaces and their network address.
-Each element is a cons, the car of which is a string containing the
-interface name, and the cdr is the network address in internal
-format; see the description of ADDRESS in `make-network-process'.
+Each element is cons of the form (IFNAME . IP) where IFNAME is a
+string containing the interface name, and IP is the network address in
+internal format; see the description of ADDRESS in
+`make-network-process'.  The interface name is not guaranteed to be
+unique.
+
+Optional parameter FULL non-nil means return all IP address info for
+each interface.  Each element is then a list of the form
+    (IFNAME IP BCAST MASK)
+where IFNAME is the interface name, IP the IP address,
+BCAST the broadcast address, and MASK the network mask.
+
+Optional parameter FAMILY controls the type of addresses to return.
+The default of nil means both IPv4 and IPv6, symbol `ipv4' means IPv4
+only, symbol `ipv6' means IPv6 only.
+
+See also `network-interface-info', which is limited to IPv4 only.
 
 If the information is not available, return nil.  */)
-  (void)
+  (Lisp_Object full, Lisp_Object family)
 {
-#if (defined HAVE_NET_IF_H && defined SIOCGIFCONF) || defined WINDOWSNT
-  return network_interface_list ();
+#if defined HAVE_GETIFADDRS || defined WINDOWSNT
+  unsigned short match;
+  bool full_info = false;
+
+  if (! NILP (full))
+    full_info = true;
+  if (NILP (family))
+    match = 0;
+  else if (EQ (family, Qipv4))
+    match = AF_INET;
+#ifdef AF_INET6
+  else if (EQ (family, Qipv6))
+    match = AF_INET6;
+#endif
+  else
+    error ("Unsupported address family");
+  return network_interface_list (full_info, match);
 #else
   return Qnil;
 #endif
index 5e957c4298ef9414837f61428395c608091da014..bf15317eb4f84dc80fa150dfc7b0c3409c1654dc 100644 (file)
@@ -291,7 +291,7 @@ extern void catch_child_signal (void);
 extern void restore_nofile_limit (void);
 
 #ifdef WINDOWSNT
-extern Lisp_Object network_interface_list (void);
+extern Lisp_Object network_interface_list (bool full, unsigned short match);
 extern Lisp_Object network_interface_info (Lisp_Object);
 #endif
 
index 26ea15d89103c471c9f6163adb237280a030838d..76c226892a45fb937e3215cb8600b844226c854f 100644 (file)
--- 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;