summaryrefslogtreecommitdiff
path: root/src/process.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/process.c')
-rw-r--r--src/process.c158
1 files changed, 100 insertions, 58 deletions
diff --git a/src/process.c b/src/process.c
index 9158cfd347c..0f82682ae5f 100644
--- a/src/process.c
+++ b/src/process.c
@@ -4255,73 +4255,86 @@ usage: (make-network-process &rest ARGS) */)
}
-#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