diff options
Diffstat (limited to 'src/process.c')
| -rw-r--r-- | src/process.c | 158 |
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 |
