unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#38218: network-interface-list only returns IPv4 addresses
@ 2019-11-15 10:13 Robert Pluim
  2019-11-15 10:18 ` Robert Pluim
  0 siblings, 1 reply; 9+ messages in thread
From: Robert Pluim @ 2019-11-15 10:13 UTC (permalink / raw)
  To: 38218

The current (2019-11-15) implementation of network-interface-list only
supports returning IPv4 addresses, it should return IPv6 addresses as
well (where possible).





^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#38218: network-interface-list only returns IPv4 addresses
  2019-11-15 10:13 bug#38218: network-interface-list only returns IPv4 addresses Robert Pluim
@ 2019-11-15 10:18 ` Robert Pluim
  2019-11-16  5:17   ` Lars Ingebrigtsen
  0 siblings, 1 reply; 9+ messages in thread
From: Robert Pluim @ 2019-11-15 10:18 UTC (permalink / raw)
  To: 38218

[-- Attachment #1: Type: text/plain, Size: 375 bytes --]

>>>>> On Fri, 15 Nov 2019 11:13:03 +0100, Robert Pluim <rpluim@gmail.com> said:

    Robert> The current (2019-11-15) implementation of network-interface-list only
    Robert> supports returning IPv4 addresses, it should return IPv6 addresses as
    Robert> well (where possible).

The following has been tested on macOS 10.14, Fedora 30, and mingw64
running on Windows 10.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Extend-network-interface-list-to-return-IPv6-and-net.patch --]
[-- Type: text/x-patch, Size: 21680 bytes --]

From 5eb703f8ed12af8205bf8cd5ba99baa5137a5dfa Mon Sep 17 00:00:00 2001
From: Robert Pluim <rpluim@gmail.com>
Date: Fri, 15 Nov 2019 11:11:30 +0100
Subject: [PATCH] Extend network-interface-list to return IPv6 and network info
To: emacs-devel@gnu.org

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 |  29 ++++-
 etc/NEWS                   |   5 +
 nt/inc/sys/socket.h        |   4 +
 src/process.c              | 158 +++++++++++++++---------
 src/process.h              |   2 +-
 src/w32.c                  | 244 ++++++++++++++++++++++++++++++++++++-
 6 files changed, 375 insertions(+), 67 deletions(-)

diff --git a/doc/lispref/processes.texi b/doc/lispref/processes.texi
index 5caf0a2426..63383efb70 100644
--- a/doc/lispref/processes.texi
+++ b/doc/lispref/processes.texi
@@ -2971,12 +2971,27 @@ Misc Network
 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.
 @end defun
 
 @defun network-interface-info ifname
@@ -2996,6 +3011,8 @@ Misc Network
 @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
diff --git a/etc/NEWS b/etc/NEWS
index 485d2b1fdf..d73db73c9e 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -234,6 +234,11 @@ To get the old, less-secure behavior, you can set the
 ** 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'
diff --git a/nt/inc/sys/socket.h b/nt/inc/sys/socket.h
index 6d26ff907e..0f3943b453 100644
--- a/nt/inc/sys/socket.h
+++ b/nt/inc/sys/socket.h
@@ -92,6 +92,8 @@ #define bind           sys_bind
 #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 @@ #define freeaddrinfo   sys_freeaddrinfo
 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);
diff --git a/src/process.c b/src/process.c
index 9158cfd347..0f82682ae5 100644
--- a/src/process.c
+++ b/src/process.c
@@ -4255,73 +4255,86 @@ DEFUN ("make-network-process", Fmake_network_process, Smake_network_process,
 }
 
 \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
diff --git a/src/process.h b/src/process.h
index 5e957c4298..bf15317eb4 100644
--- a/src/process.h
+++ b/src/process.h
@@ -291,7 +291,7 @@ pset_gnutls_cred_type (struct Lisp_Process *p, Lisp_Object val)
 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
 
diff --git a/src/w32.c b/src/w32.c
index 26ea15d891..76c226892a 100644
--- a/src/w32.c
+++ b/src/w32.c
@@ -227,6 +227,8 @@ #define FSCTL_GET_REPARSE_POINT \
 #undef connect
 #undef htons
 #undef ntohs
+#undef htonl
+#undef ntohl
 #undef inet_addr
 #undef gethostname
 #undef gethostbyname
@@ -326,6 +328,7 @@ #define FSCTL_GET_REPARSE_POINT \
 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 @@ #define FSCTL_GET_REPARSE_POINT \
 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 @@ #define LOAD_PROC(fn) \
       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;
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* bug#38218: network-interface-list only returns IPv4 addresses
  2019-11-15 10:18 ` Robert Pluim
@ 2019-11-16  5:17   ` Lars Ingebrigtsen
  2019-11-17 20:50     ` Robert Pluim
  0 siblings, 1 reply; 9+ messages in thread
From: Lars Ingebrigtsen @ 2019-11-16  5:17 UTC (permalink / raw)
  To: Robert Pluim; +Cc: 38218

Robert Pluim <rpluim@gmail.com> writes:

> +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.

I think it might make sense to include an example output from the
function here to make it clearer what the string bit is, in particular.

I've otherwise just skimmed the patch (and not tried it), but it makes
sense to me.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no





^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#38218: network-interface-list only returns IPv4 addresses
  2019-11-16  5:17   ` Lars Ingebrigtsen
@ 2019-11-17 20:50     ` Robert Pluim
  2019-11-26  8:37       ` Robert Pluim
  0 siblings, 1 reply; 9+ messages in thread
From: Robert Pluim @ 2019-11-17 20:50 UTC (permalink / raw)
  To: Lars Ingebrigtsen; +Cc: 38218

>>>>> On Sat, 16 Nov 2019 06:17:10 +0100, Lars Ingebrigtsen <larsi@gnus.org> said:

    Lars> Robert Pluim <rpluim@gmail.com> writes:
    >> +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.

    Lars> I think it might make sense to include an example output from the
    Lars> function here to make it clearer what the string bit is, in particular.

Sure.

    Lars> I've otherwise just skimmed the patch (and not tried it), but it makes
    Lars> sense to me.

Iʼll just push and wait for the screams then. (Eli, thatʼs a joke).

Robert





^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#38218: network-interface-list only returns IPv4 addresses
  2019-11-17 20:50     ` Robert Pluim
@ 2019-11-26  8:37       ` Robert Pluim
  2019-11-26 17:17         ` Eli Zaretskii
  0 siblings, 1 reply; 9+ messages in thread
From: Robert Pluim @ 2019-11-26  8:37 UTC (permalink / raw)
  To: Lars Ingebrigtsen; +Cc: 38218

tags 38218 fixed
close 38218 27.1
quit

>>>>> On Sun, 17 Nov 2019 21:50:00 +0100, Robert Pluim <rpluim@gmail.com> said:

>>>>> On Sat, 16 Nov 2019 06:17:10 +0100, Lars Ingebrigtsen <larsi@gnus.org> said:

    Lars> I've otherwise just skimmed the patch (and not tried it), but it makes
    Lars> sense to me.

    Robert> Iʼll just push and wait for the screams then. (Eli, thatʼs a joke).

Iʼve now pushed it.

Closing.
Committed as 650a514e99





^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#38218: network-interface-list only returns IPv4 addresses
  2019-11-26  8:37       ` Robert Pluim
@ 2019-11-26 17:17         ` Eli Zaretskii
  2019-11-26 18:10           ` Robert Pluim
  0 siblings, 1 reply; 9+ messages in thread
From: Eli Zaretskii @ 2019-11-26 17:17 UTC (permalink / raw)
  To: Robert Pluim; +Cc: 38218

> From: Robert Pluim <rpluim@gmail.com>
> Date: Tue, 26 Nov 2019 09:37:53 +0100
> Cc: 38218@debbugs.gnu.org
> 
>     Robert> Iʼll just push and wait for the screams then. (Eli, thatʼs a joke).
> 
> Iʼve now pushed it.

It needed a bit of tweaking for mingw.org's MinGW, so that it would
work on XP.  However, after making the changes, one test from
nsm-tests.el fails here:

  127.1/0 No such host is known.
  127.1/0 No such host is known.
  Test nsm-check-local-subnet-ipv4 backtrace:
    signal(ert-test-failed (((should (eq nil (nsm-should-check "127.1")
    ert-fail(((should (eq nil (nsm-should-check "127.1"))) :form (eq ni
    (if (unwind-protect (setq value-44 (apply fn-42 args-43)) (setq for
    (let (form-description-46) (if (unwind-protect (setq value-44 (appl
    (let ((value-44 'ert-form-evaluation-aborted-45)) (let (form-descri
    (let* ((fn-42 #'eq) (args-43 (condition-case err (let ((signal-hook
    (let ((nsm-trust-local-network t)) (let* ((fn-37 #'eq) (args-38 (co
    (let ((local-ip '[172 26 128 160 0]) (mask '[255 255 255 0 0]) (wro
    (closure (t) nil (let ((local-ip '[172 26 128 160 0]) (mask '[255 2
    ert--run-test-internal(#s(ert--test-execution-info :test #s(ert-tes
    ert-run-test(#s(ert-test :name nsm-check-local-subnet-ipv4 :documen
    ert-run-or-rerun-test(#s(ert--stats :selector (not (tag :unstable))
    ert-run-tests((not (tag :unstable)) #f(compiled-function (event-typ
    ert-run-tests-batch((not (tag :unstable)))
    ert-run-tests-batch-and-exit((not (tag :unstable)))
    eval((ert-run-tests-batch-and-exit '(not (tag :unstable))) t)
    command-line-1((#("-L" 0 2 (charset cp862)) #(";." 0 2 (charset cp8
    command-line()
    normal-top-level()
  Test nsm-check-local-subnet-ipv4 condition:
      (ert-test-failed
       ((should
	 (eq nil
	     (nsm-should-check "127.1")))
	:form
	(eq nil t)
	:value nil))
     FAILED  1/2  nsm-check-local-subnet-ipv4 (4.656250 sec)

Does it work for you in your MinGW build?  If so, could you eyeball
the code I added/modified in w32.c to see if I goofed somewhere?

Thanks.





^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#38218: network-interface-list only returns IPv4 addresses
  2019-11-26 17:17         ` Eli Zaretskii
@ 2019-11-26 18:10           ` Robert Pluim
  2019-11-26 18:23             ` Eli Zaretskii
  0 siblings, 1 reply; 9+ messages in thread
From: Robert Pluim @ 2019-11-26 18:10 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 38218

[-- Attachment #1: Type: text/plain, Size: 2299 bytes --]

>>>>> On Tue, 26 Nov 2019 19:17:49 +0200, Eli Zaretskii <eliz@gnu.org> said:

    Eli> It needed a bit of tweaking for mingw.org's MinGW, so that it would
    Eli> work on XP.  However, after making the changes, one test from
    Eli> nsm-tests.el fails here:

Thanks for that

    Eli>   127.1/0 No such host is known.
    Eli>   127.1/0 No such host is known.
    Eli>   Test nsm-check-local-subnet-ipv4 backtrace:
    Eli>     signal(ert-test-failed (((should (eq nil (nsm-should-check "127.1")
    Eli>     ert-fail(((should (eq nil (nsm-should-check "127.1"))) :form (eq ni
    Eli>     (if (unwind-protect (setq value-44 (apply fn-42 args-43)) (setq for
    Eli>     (let (form-description-46) (if (unwind-protect (setq value-44 (appl
    Eli>     (let ((value-44 'ert-form-evaluation-aborted-45)) (let (form-descri
    Eli>     (let* ((fn-42 #'eq) (args-43 (condition-case err (let ((signal-hook
    Eli>     (let ((nsm-trust-local-network t)) (let* ((fn-37 #'eq) (args-38 (co
    Eli>     (let ((local-ip '[172 26 128 160 0]) (mask '[255 255 255 0 0]) (wro
    Eli>     (closure (t) nil (let ((local-ip '[172 26 128 160 0]) (mask '[255 2
    Eli>     ert--run-test-internal(#s(ert--test-execution-info :test #s(ert-tes
    Eli>     ert-run-test(#s(ert-test :name nsm-check-local-subnet-ipv4 :documen
    Eli>     ert-run-or-rerun-test(#s(ert--stats :selector (not (tag :unstable))
    Eli>     ert-run-tests((not (tag :unstable)) #f(compiled-function (event-typ
    Eli>     ert-run-tests-batch((not (tag :unstable)))
    Eli>     ert-run-tests-batch-and-exit((not (tag :unstable)))
    Eli>     eval((ert-run-tests-batch-and-exit '(not (tag :unstable))) t)
    Eli>     command-line-1((#("-L" 0 2 (charset cp862)) #(";." 0 2 (charset cp8
    Eli>     command-line()
    Eli>     normal-top-level()
    Eli>   Test nsm-check-local-subnet-ipv4 condition:
    Eli>       (ert-test-failed
    Eli>        ((should
    Eli> 	 (eq nil
    Eli> 	     (nsm-should-check "127.1")))
    Eli> 	:form
    Eli> 	(eq nil t)
    Eli> 	:value nil))
    Eli>      FAILED  1/2  nsm-check-local-subnet-ipv4 (4.656250 sec)

Eek. mingw (or winsock) doesnʼt support getaddrinfo("127.1",...),
although getaddrinfo("127.0.0.1",...) seems to work. This fixes it for me:


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Use-127.0.0.1-in-nsm-tests.patch --]
[-- Type: text/x-patch, Size: 1375 bytes --]

From e441c874ac7e7a0c7724af85ebf2d1678aea3a88 Mon Sep 17 00:00:00 2001
From: Robert Pluim <rpluim@gmail.com>
Date: Tue, 26 Nov 2019 19:07:42 +0100
Subject: [PATCH] Use 127.0.0.1 in nsm-tests
To: emacs-devel@gnu.org

Winsock doesn't like "127.1"

* test/lisp/net/nsm-tests.el (nsm-check-local-subnet-ipv4): Spell
numeric localhost as "127.0.0.1" instead of "127.1".
---
 test/lisp/net/nsm-tests.el | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/test/lisp/net/nsm-tests.el b/test/lisp/net/nsm-tests.el
index 8e52568d71..97edcca80d 100644
--- a/test/lisp/net/nsm-tests.el
+++ b/test/lisp/net/nsm-tests.el
@@ -42,11 +42,11 @@ nsm-check-local-subnet-ipv4
     (should-error (nsm-network-same-subnet local-ip wrong-length-mask remote-ip-yes))
     (should (eq nil (nsm-network-same-subnet local-ip wrong-mask remote-ip-yes)))
     (should (eq t (nsm-should-check "google.com")))
-    (should (eq t (nsm-should-check "127.1")))
+    (should (eq t (nsm-should-check "127.0.0.1")))
     (should (eq t (nsm-should-check "localhost")))
     (let ((nsm-trust-local-network t))
       (should (eq t (nsm-should-check "google.com")))
-      (should (eq nil (nsm-should-check "127.1")))
+      (should (eq nil (nsm-should-check "127.0.0.1")))
       (should (eq nil (nsm-should-check "localhost"))))))
 
 (defun nsm-ipv6-is-available ()
-- 
2.24.0.155.gd9f6f3b619


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* bug#38218: network-interface-list only returns IPv4 addresses
  2019-11-26 18:10           ` Robert Pluim
@ 2019-11-26 18:23             ` Eli Zaretskii
  2019-11-26 18:29               ` Robert Pluim
  0 siblings, 1 reply; 9+ messages in thread
From: Eli Zaretskii @ 2019-11-26 18:23 UTC (permalink / raw)
  To: Robert Pluim; +Cc: 38218

> From: Robert Pluim <rpluim@gmail.com>
> Cc: 38218@debbugs.gnu.org
> Date: Tue, 26 Nov 2019 19:10:04 +0100
> 
>     Eli> It needed a bit of tweaking for mingw.org's MinGW, so that it would
>     Eli> work on XP.  However, after making the changes, one test from
>     Eli> nsm-tests.el fails here:
> 
> Thanks for that

Thanks for reviewing it.

>     Eli>   Test nsm-check-local-subnet-ipv4 condition:
>     Eli>       (ert-test-failed
>     Eli>        ((should
>     Eli> 	 (eq nil
>     Eli> 	     (nsm-should-check "127.1")))
>     Eli> 	:form
>     Eli> 	(eq nil t)
>     Eli> 	:value nil))
>     Eli>      FAILED  1/2  nsm-check-local-subnet-ipv4 (4.656250 sec)
> 
> Eek. mingw (or winsock) doesnʼt support getaddrinfo("127.1",...),
> although getaddrinfo("127.0.0.1",...) seems to work. This fixes it for me:

For me as well, thanks.





^ permalink raw reply	[flat|nested] 9+ messages in thread

* bug#38218: network-interface-list only returns IPv4 addresses
  2019-11-26 18:23             ` Eli Zaretskii
@ 2019-11-26 18:29               ` Robert Pluim
  0 siblings, 0 replies; 9+ messages in thread
From: Robert Pluim @ 2019-11-26 18:29 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 38218

>>>>> On Tue, 26 Nov 2019 20:23:30 +0200, Eli Zaretskii <eliz@gnu.org> said:

    >> From: Robert Pluim <rpluim@gmail.com>
    >> Cc: 38218@debbugs.gnu.org
    >> Date: Tue, 26 Nov 2019 19:10:04 +0100
    >> 
    Eli> It needed a bit of tweaking for mingw.org's MinGW, so that it would
    Eli> work on XP.  However, after making the changes, one test from
    Eli> nsm-tests.el fails here:
    >> 
    >> Thanks for that

    Eli> Thanks for reviewing it.

Thanks for writing it. The Windows handling of IP prefixes is weird,
to say the least.

    Eli> Test nsm-check-local-subnet-ipv4 condition:
    Eli> (ert-test-failed
    Eli> ((should
    Eli> (eq nil
    Eli> (nsm-should-check "127.1")))
    Eli> :form
    Eli> (eq nil t)
    Eli> :value nil))
    Eli> FAILED  1/2  nsm-check-local-subnet-ipv4 (4.656250 sec)
    >> 
    >> Eek. mingw (or winsock) doesnʼt support getaddrinfo("127.1",...),
    >> although getaddrinfo("127.0.0.1",...) seems to work. This fixes it for me:

    Eli> For me as well, thanks.

Pushed as 95e218af14.

Thanks

Robert





^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2019-11-26 18:29 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-11-15 10:13 bug#38218: network-interface-list only returns IPv4 addresses Robert Pluim
2019-11-15 10:18 ` Robert Pluim
2019-11-16  5:17   ` Lars Ingebrigtsen
2019-11-17 20:50     ` Robert Pluim
2019-11-26  8:37       ` Robert Pluim
2019-11-26 17:17         ` Eli Zaretskii
2019-11-26 18:10           ` Robert Pluim
2019-11-26 18:23             ` Eli Zaretskii
2019-11-26 18:29               ` Robert Pluim

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).