diff options
| author | Robert Pluim | 2019-11-15 11:11:30 +0100 |
|---|---|---|
| committer | Robert Pluim | 2019-11-26 08:46:13 +0100 |
| commit | 650a514e996287106f9a9525b6f27068ec2a0cbf (patch) | |
| tree | 188170aae76f32d894d8df1baa7f435fbfe99d88 /src/process.c | |
| parent | 5c3d0cf7910afa6b3fbdba24ac5c5817f268eb0e (diff) | |
| download | emacs-650a514e996287106f9a9525b6f27068ec2a0cbf.tar.gz emacs-650a514e996287106f9a9525b6f27068ec2a0cbf.zip | |
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/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) */) | |||
| 4255 | } | 4255 | } |
| 4256 | 4256 | ||
| 4257 | 4257 | ||
| 4258 | #ifdef HAVE_NET_IF_H | ||
| 4259 | 4258 | ||
| 4260 | #ifdef SIOCGIFCONF | 4259 | #ifdef HAVE_GETIFADDRS |
| 4261 | static Lisp_Object | 4260 | static Lisp_Object |
| 4262 | network_interface_list (void) | 4261 | network_interface_list (bool full, unsigned short match) |
| 4263 | { | 4262 | { |
| 4264 | struct ifconf ifconf; | 4263 | Lisp_Object res = Qnil; |
| 4265 | struct ifreq *ifreq; | 4264 | struct ifaddrs *ifap; |
| 4266 | void *buf = NULL; | ||
| 4267 | ptrdiff_t buf_size = 512; | ||
| 4268 | int s; | ||
| 4269 | Lisp_Object res; | ||
| 4270 | ptrdiff_t count; | ||
| 4271 | 4265 | ||
| 4272 | s = socket (AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); | 4266 | if (getifaddrs (&ifap) == -1) |
| 4273 | if (s < 0) | ||
| 4274 | return Qnil; | 4267 | return Qnil; |
| 4275 | count = SPECPDL_INDEX (); | ||
| 4276 | record_unwind_protect_int (close_file_unwind, s); | ||
| 4277 | 4268 | ||
| 4278 | do | 4269 | for (struct ifaddrs *it = ifap; it != NULL; it = it->ifa_next) |
| 4279 | { | 4270 | { |
| 4280 | buf = xpalloc (buf, &buf_size, 1, INT_MAX, 1); | 4271 | int len; |
| 4281 | ifconf.ifc_buf = buf; | 4272 | int addr_len; |
| 4282 | ifconf.ifc_len = buf_size; | 4273 | uint32_t *maskp; |
| 4283 | if (ioctl (s, SIOCGIFCONF, &ifconf)) | 4274 | uint32_t *addrp; |
| 4284 | { | 4275 | Lisp_Object elt = Qnil; |
| 4285 | emacs_close (s); | ||
| 4286 | xfree (buf); | ||
| 4287 | return Qnil; | ||
| 4288 | } | ||
| 4289 | } | ||
| 4290 | while (ifconf.ifc_len == buf_size); | ||
| 4291 | |||
| 4292 | res = unbind_to (count, Qnil); | ||
| 4293 | ifreq = ifconf.ifc_req; | ||
| 4294 | while ((char *) ifreq < (char *) ifconf.ifc_req + ifconf.ifc_len) | ||
| 4295 | { | ||
| 4296 | struct ifreq *ifq = ifreq; | ||
| 4297 | #ifdef HAVE_STRUCT_IFREQ_IFR_ADDR_SA_LEN | ||
| 4298 | #define SIZEOF_IFREQ(sif) \ | ||
| 4299 | ((sif)->ifr_addr.sa_len < sizeof (struct sockaddr) \ | ||
| 4300 | ? sizeof (*(sif)) : sizeof ((sif)->ifr_name) + (sif)->ifr_addr.sa_len) | ||
| 4301 | 4276 | ||
| 4302 | int len = SIZEOF_IFREQ (ifq); | 4277 | /* BSD can allegedly return interfaces with a NULL address. */ |
| 4303 | #else | 4278 | if (it->ifa_addr == NULL) |
| 4304 | int len = sizeof (*ifreq); | 4279 | continue; |
| 4280 | if (match && it->ifa_addr->sa_family != match) | ||
| 4281 | continue; | ||
| 4282 | if (it->ifa_addr->sa_family == AF_INET) | ||
| 4283 | { | ||
| 4284 | DECLARE_POINTER_ALIAS (sin1, struct sockaddr_in, it->ifa_netmask); | ||
| 4285 | maskp = (uint32_t *)&sin1->sin_addr; | ||
| 4286 | DECLARE_POINTER_ALIAS (sin2, struct sockaddr_in, it->ifa_addr); | ||
| 4287 | addrp = (uint32_t *)&sin2->sin_addr; | ||
| 4288 | len = sizeof (struct sockaddr_in); | ||
| 4289 | addr_len = 1; | ||
| 4290 | } | ||
| 4291 | #ifdef AF_INET6 | ||
| 4292 | else if (it->ifa_addr->sa_family == AF_INET6) | ||
| 4293 | { | ||
| 4294 | DECLARE_POINTER_ALIAS (sin6_1, struct sockaddr_in6, it->ifa_netmask); | ||
| 4295 | maskp = (uint32_t *) &sin6_1->sin6_addr; | ||
| 4296 | DECLARE_POINTER_ALIAS (sin6_2, struct sockaddr_in6, it->ifa_addr); | ||
| 4297 | addrp = (uint32_t *) &sin6_2->sin6_addr; | ||
| 4298 | len = sizeof (struct sockaddr_in6); | ||
| 4299 | addr_len = 4; | ||
| 4300 | } | ||
| 4305 | #endif | 4301 | #endif |
| 4306 | char namebuf[sizeof (ifq->ifr_name) + 1]; | 4302 | else |
| 4307 | ifreq = (struct ifreq *) ((char *) ifreq + len); | 4303 | continue; |
| 4308 | 4304 | ||
| 4309 | if (ifq->ifr_addr.sa_family != AF_INET) | 4305 | Lisp_Object addr = conv_sockaddr_to_lisp (it->ifa_addr, len); |
| 4310 | continue; | ||
| 4311 | 4306 | ||
| 4312 | memcpy (namebuf, ifq->ifr_name, sizeof (ifq->ifr_name)); | 4307 | if (full) |
| 4313 | namebuf[sizeof (ifq->ifr_name)] = 0; | 4308 | { |
| 4314 | res = Fcons (Fcons (build_string (namebuf), | 4309 | elt = Fcons (conv_sockaddr_to_lisp (it->ifa_netmask, len), elt); |
| 4315 | conv_sockaddr_to_lisp (&ifq->ifr_addr, | 4310 | /* There is an it->ifa_broadaddr field, but its contents are |
| 4316 | sizeof (struct sockaddr))), | 4311 | unreliable, so always calculate the broadcast address from |
| 4317 | res); | 4312 | the address and the netmask. */ |
| 4313 | int i; | ||
| 4314 | uint32_t mask; | ||
| 4315 | for (i = 0; i < addr_len; i++) | ||
| 4316 | { | ||
| 4317 | mask = maskp[i]; | ||
| 4318 | maskp[i] = (addrp[i] & mask) | ~mask; | ||
| 4319 | } | ||
| 4320 | elt = Fcons (conv_sockaddr_to_lisp (it->ifa_netmask, len), elt); | ||
| 4321 | elt = Fcons (addr, elt); | ||
| 4322 | } | ||
| 4323 | else | ||
| 4324 | { | ||
| 4325 | elt = addr; | ||
| 4326 | } | ||
| 4327 | res = Fcons (Fcons (build_string (it->ifa_name), elt), res); | ||
| 4318 | } | 4328 | } |
| 4329 | #ifdef HAVE_FREEIFADDRS | ||
| 4330 | freeifaddrs (ifap); | ||
| 4331 | #endif | ||
| 4319 | 4332 | ||
| 4320 | xfree (buf); | ||
| 4321 | return res; | 4333 | return res; |
| 4322 | } | 4334 | } |
| 4323 | #endif /* SIOCGIFCONF */ | 4335 | #endif /* HAVE_GETIFADDRS */ |
| 4324 | 4336 | ||
| 4337 | #ifdef HAVE_NET_IF_H | ||
| 4325 | #if defined (SIOCGIFADDR) || defined (SIOCGIFHWADDR) || defined (SIOCGIFFLAGS) | 4338 | #if defined (SIOCGIFADDR) || defined (SIOCGIFHWADDR) || defined (SIOCGIFFLAGS) |
| 4326 | 4339 | ||
| 4327 | struct ifflag_def { | 4340 | struct ifflag_def { |
| @@ -4550,17 +4563,46 @@ network_interface_info (Lisp_Object ifname) | |||
| 4550 | #endif /* defined (HAVE_NET_IF_H) */ | 4563 | #endif /* defined (HAVE_NET_IF_H) */ |
| 4551 | 4564 | ||
| 4552 | DEFUN ("network-interface-list", Fnetwork_interface_list, | 4565 | DEFUN ("network-interface-list", Fnetwork_interface_list, |
| 4553 | Snetwork_interface_list, 0, 0, 0, | 4566 | Snetwork_interface_list, 0, 2, 0, |
| 4554 | doc: /* Return an alist of all network interfaces and their network address. | 4567 | doc: /* Return an alist of all network interfaces and their network address. |
| 4555 | Each element is a cons, the car of which is a string containing the | 4568 | Each element is cons of the form (IFNAME . IP) where IFNAME is a |
| 4556 | interface name, and the cdr is the network address in internal | 4569 | string containing the interface name, and IP is the network address in |
| 4557 | format; see the description of ADDRESS in `make-network-process'. | 4570 | internal format; see the description of ADDRESS in |
| 4571 | `make-network-process'. The interface name is not guaranteed to be | ||
| 4572 | unique. | ||
| 4573 | |||
| 4574 | Optional parameter FULL non-nil means return all IP address info for | ||
| 4575 | each interface. Each element is then a list of the form | ||
| 4576 | (IFNAME IP BCAST MASK) | ||
| 4577 | where IFNAME is the interface name, IP the IP address, | ||
| 4578 | BCAST the broadcast address, and MASK the network mask. | ||
| 4579 | |||
| 4580 | Optional parameter FAMILY controls the type of addresses to return. | ||
| 4581 | The default of nil means both IPv4 and IPv6, symbol `ipv4' means IPv4 | ||
| 4582 | only, symbol `ipv6' means IPv6 only. | ||
| 4583 | |||
| 4584 | See also `network-interface-info', which is limited to IPv4 only. | ||
| 4558 | 4585 | ||
| 4559 | If the information is not available, return nil. */) | 4586 | If the information is not available, return nil. */) |
| 4560 | (void) | 4587 | (Lisp_Object full, Lisp_Object family) |
| 4561 | { | 4588 | { |
| 4562 | #if (defined HAVE_NET_IF_H && defined SIOCGIFCONF) || defined WINDOWSNT | 4589 | #if defined HAVE_GETIFADDRS || defined WINDOWSNT |
| 4563 | return network_interface_list (); | 4590 | unsigned short match; |
| 4591 | bool full_info = false; | ||
| 4592 | |||
| 4593 | if (! NILP (full)) | ||
| 4594 | full_info = true; | ||
| 4595 | if (NILP (family)) | ||
| 4596 | match = 0; | ||
| 4597 | else if (EQ (family, Qipv4)) | ||
| 4598 | match = AF_INET; | ||
| 4599 | #ifdef AF_INET6 | ||
| 4600 | else if (EQ (family, Qipv6)) | ||
| 4601 | match = AF_INET6; | ||
| 4602 | #endif | ||
| 4603 | else | ||
| 4604 | error ("Unsupported address family"); | ||
| 4605 | return network_interface_list (full_info, match); | ||
| 4564 | #else | 4606 | #else |
| 4565 | return Qnil; | 4607 | return Qnil; |
| 4566 | #endif | 4608 | #endif |