1 /*	$NetBSD: inet_addr_local.c,v 1.4 2017/02/14 01:16:49 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	inet_addr_local 3
6 /* SUMMARY
7 /*	determine if IP address is local
8 /* SYNOPSIS
9 /*	#include <inet_addr_local.h>
10 /*
11 /*	int	inet_addr_local(addr_list, mask_list, addr_family_list)
12 /*	INET_ADDR_LIST *addr_list;
13 /*	INET_ADDR_LIST *mask_list;
14 /*	unsigned *addr_family;
15 /* DESCRIPTION
16 /*	inet_addr_local() determines all active IP interface addresses
17 /*	of the local system. Any address found is appended to the
18 /*	specified address list. The result value is the number of
19 /*	active interfaces found.
20 /*
21 /*	The mask_list is either a null pointer, or it is a list that
22 /*	receives the netmasks of the interface addresses that were found.
23 /*
24 /*	The addr_family_list specifies one or more of AF_INET or AF_INET6.
25 /* DIAGNOSTICS
26 /*	Fatal errors: out of memory.
27 /* SEE ALSO
28 /*	inet_addr_list(3) address list management
29 /* LICENSE
30 /* .ad
31 /* .fi
32 /*	The Secure Mailer license must be distributed with this software.
33 /* AUTHOR(S)
34 /*	Wietse Venema
35 /*	IBM T.J. Watson Research
36 /*	P.O. Box 704
37 /*	Yorktown Heights, NY 10598, USA
38 /*
39 /*	Dean C. Strik
40 /*	Department ICT
41 /*	Eindhoven University of Technology
42 /*	P.O. Box 513
43 /*	5600 MB  Eindhoven, Netherlands
44 /*	E-mail: <dean@ipnet6.org>
45 /*--*/
46 
47 /* System library. */
48 
49 #include <sys_defs.h>
50 #include <sys/socket.h>
51 #include <sys/time.h>
52 #include <netinet/in.h>
53 #include <net/if.h>
54 #include <sys/ioctl.h>
55 #include <arpa/inet.h>
56 #include <unistd.h>
57 #ifdef USE_SYS_SOCKIO_H
58 #include <sys/sockio.h>
59 #endif
60 #include <errno.h>
61 #include <string.h>
62 #ifdef HAS_IPV6				/* Linux only? */
63 #include <netdb.h>
64 #include <stdio.h>
65 #endif
66 #ifdef HAVE_GETIFADDRS
67 #include <ifaddrs.h>
68 #endif
69 
70 /* Utility library. */
71 
72 #include <msg.h>
73 #include <mymalloc.h>
74 #include <vstring.h>
75 #include <inet_addr_list.h>
76 #include <inet_addr_local.h>
77 #include <myaddrinfo.h>
78 #include <sock_addr.h>
79 #include <mask_addr.h>
80 #include <hex_code.h>
81 
82  /*
83   * Postfix needs its own interface address information to determine whether
84   * or not it is an MX host for some destination; without this information,
85   * mail would loop between MX hosts. Postfix also needs its interface
86   * addresses to figure out whether or not it is final destination for
87   * addresses of the form username@[ipaddress].
88   *
89   * Postfix needs its own interface netmask information when no explicit
90   * mynetworks setting is given in main.cf, and "mynetworks_style = subnet".
91   * The mynetworks parameter controls, among others, what mail clients are
92   * allowed to relay mail through Postfix.
93   *
94   * Different systems have different ways to find out this information. We will
95   * therefore use OS dependent methods. An overview:
96   *
97   * - Use getifaddrs() when available.  This supports both IPv4/IPv6 addresses.
98   * The implementation however is not present in all major operating systems.
99   *
100   * - Use SIOCGLIFCONF when available. This supports both IPv4/IPv6 addresses.
101   * With SIOCGLIFNETMASK we can obtain the netmask for either address family.
102   * Again, this is not present in all major operating systems.
103   *
104   * - On Linux, glibc's getifaddrs(3) has returned IPv4 information for some
105   * time, but IPv6 information was not returned until 2.3.3. With older Linux
106   * versions we get IPv4 interface information with SIOCGIFCONF, and read
107   * IPv6 address/prefix information from a file in the /proc filesystem.
108   *
109   * - On other systems we expect SIOCGIFCONF to return IPv6 addresses. Since
110   * SIOCGIFNETMASK does not work reliably for IPv6 addresses, we always set
111   * the prefix length to /128 (host), and expect the user to configure a more
112   * appropriate mynetworks setting if needed.
113   *
114   * XXX: Each lookup method is implemented by its own function, so we duplicate
115   * some code. In this case, I think this is better than really drowning in
116   * the #ifdefs...
117   *
118   * -- Dean Strik (dcs)
119   */
120 
121 #ifndef HAVE_GETIFADDRS
122 
123 /* ial_socket - make socket for ioctl() operations */
124 
ial_socket(int af)125 static int ial_socket(int af)
126 {
127     const char *myname = "inet_addr_local[socket]";
128     int     sock;
129 
130     /*
131      * The host may not be actually configured with IPv6. When IPv6 support
132      * is not actually in the kernel, don't consider failure to create an
133      * IPv6 socket as fatal. This could be tuned better though. For other
134      * families, the error is fatal.
135      *
136      * XXX Now that Postfix controls protocol support centrally with the
137      * inet_proto(3) module, this workaround should no longer be needed.
138      */
139     if ((sock = socket(af, SOCK_DGRAM, 0)) < 0) {
140 #ifdef HAS_IPV6
141 	if (af == AF_INET6) {
142 	    if (msg_verbose)
143 		msg_warn("%s: socket: %m", myname);
144 	    return (-1);
145 	}
146 #endif
147 	msg_fatal("%s: socket: %m", myname);
148     }
149     return (sock);
150 }
151 
152 #endif
153 
154 #ifdef HAVE_GETIFADDRS
155 
156 /*
157  * The getifaddrs(3) function, introduced by BSD/OS, provides a
158  * platform-independent way of requesting interface addresses,
159  * including IPv6 addresses. The implementation however is not
160  * present in all major operating systems.
161  */
162 
163 /* ial_getifaddrs - determine IP addresses using getifaddrs(3) */
164 
ial_getifaddrs(INET_ADDR_LIST * addr_list,INET_ADDR_LIST * mask_list,int af)165 static int ial_getifaddrs(INET_ADDR_LIST *addr_list,
166 			          INET_ADDR_LIST *mask_list,
167 			          int af)
168 {
169     const char *myname = "inet_addr_local[getifaddrs]";
170     struct ifaddrs *ifap, *ifa;
171     struct sockaddr *sa, *sam;
172 
173     if (getifaddrs(&ifap) < 0)
174 	msg_fatal("%s: getifaddrs: %m", myname);
175 
176     /*
177      * Get the address of each IP network interface. According to BIND we
178      * must include interfaces that are down because the machine may still
179      * receive packets for that address (yes, via some other interface).
180      * Having no way to verify this claim on every machine, I will give them
181      * the benefit of the doubt.
182      *
183      * FIX 200501: The IPv6 patch did not report NetBSD loopback interfaces;
184      * fixed by replacing IFF_RUNNING by IFF_UP.
185      *
186      * FIX 200501: The IPV6 patch did not skip wild-card interface addresses
187      * (tested on FreeBSD).
188      */
189     for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
190 	if (!(ifa->ifa_flags & IFF_UP) || ifa->ifa_addr == 0)
191 	    continue;
192 	sa = ifa->ifa_addr;
193 	if (af != AF_UNSPEC && sa->sa_family != af)
194 	    continue;
195 	sam = ifa->ifa_netmask;
196 	if (sam == 0) {
197 	    /* XXX In mynetworks, a null netmask would match everyone. */
198 	    msg_warn("ignoring interface with null netmask, address family %d",
199 		     sa->sa_family);
200 	    continue;
201 	}
202 	switch (sa->sa_family) {
203 	case AF_INET:
204 	    if (SOCK_ADDR_IN_ADDR(sa).s_addr == INADDR_ANY)
205 		continue;
206 	    break;
207 #ifdef HAS_IPV6
208 	case AF_INET6:
209 	    if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa)))
210 		continue;
211 	    break;
212 #endif
213 	default:
214 	    continue;
215 	}
216 
217 	inet_addr_list_append(addr_list, sa);
218 	if (mask_list != 0) {
219 
220 	    /*
221 	     * Unfortunately, sa_len/sa_family may be broken in the netmask
222 	     * sockaddr structure. We must fix this manually to have correct
223 	     * addresses.   --dcs
224 	     */
225 #ifdef HAS_SA_LEN
226 	    sam->sa_len = sa->sa_family == AF_INET6 ?
227 		sizeof(struct sockaddr_in6) :
228 		sizeof(struct sockaddr_in);
229 #endif
230 	    sam->sa_family = sa->sa_family;
231 	    inet_addr_list_append(mask_list, sam);
232 	}
233     }
234     freeifaddrs(ifap);
235     return (0);
236 }
237 
238 #elif defined(HAS_SIOCGLIF)		/* HAVE_GETIFADDRS */
239 
240 /*
241  * The SIOCLIF* ioctls are the successors of SIOCGIF* on the Solaris
242  * and HP/UX operating systems. The data is stored in sockaddr_storage
243  * structure. Both IPv4 and IPv6 addresses are returned though these
244  * calls.
245  */
246 #define NEXT_INTERFACE(lifr)	(lifr + 1)
247 #define LIFREQ_SIZE(lifr)	sizeof(lifr[0])
248 
249 /* ial_siocglif - determine IP addresses using ioctl(SIOCGLIF*) */
250 
ial_siocglif(INET_ADDR_LIST * addr_list,INET_ADDR_LIST * mask_list,int af)251 static int ial_siocglif(INET_ADDR_LIST *addr_list,
252 			        INET_ADDR_LIST *mask_list,
253 			        int af)
254 {
255     const char *myname = "inet_addr_local[siocglif]";
256     struct lifconf lifc;
257     struct lifreq *lifr;
258     struct lifreq *lifr_mask;
259     struct lifreq *the_end;
260     struct sockaddr *sa;
261     int     sock;
262     VSTRING *buf;
263 
264     /*
265      * See also comments in ial_siocgif()
266      */
267     if (af != AF_INET && af != AF_INET6)
268 	msg_fatal("%s: address family was %d, must be AF_INET (%d) or "
269 		  "AF_INET6 (%d)", myname, af, AF_INET, AF_INET6);
270     sock = ial_socket(af);
271     if (sock < 0)
272 	return (0);
273     buf = vstring_alloc(1024);
274     for (;;) {
275 	memset(&lifc, 0, sizeof(lifc));
276 	lifc.lifc_family = AF_UNSPEC;		/* XXX Why??? */
277 	lifc.lifc_len = vstring_avail(buf);
278 	lifc.lifc_buf = vstring_str(buf);
279 	if (ioctl(sock, SIOCGLIFCONF, (char *) &lifc) < 0) {
280 	    if (errno != EINVAL)
281 		msg_fatal("%s: ioctl SIOCGLIFCONF: %m", myname);
282 	} else if (lifc.lifc_len < vstring_avail(buf) / 2)
283 	    break;
284 	VSTRING_SPACE(buf, vstring_avail(buf) * 2);
285     }
286 
287     the_end = (struct lifreq *) (lifc.lifc_buf + lifc.lifc_len);
288     for (lifr = lifc.lifc_req; lifr < the_end;) {
289 	sa = (struct sockaddr *) &lifr->lifr_addr;
290 	if (sa->sa_family != af) {
291 	    lifr = NEXT_INTERFACE(lifr);
292 	    continue;
293 	}
294 	if (af == AF_INET) {
295 	    if (SOCK_ADDR_IN_ADDR(sa).s_addr == INADDR_ANY) {
296 		lifr = NEXT_INTERFACE(lifr);
297 		continue;
298 	    }
299 	}
300 #ifdef HAS_IPV6
301 	else if (af == AF_INET6) {
302 	    if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa))) {
303 		lifr = NEXT_INTERFACE(lifr);
304 		continue;
305 	    }
306 	}
307 #endif
308 	inet_addr_list_append(addr_list, sa);
309 	if (mask_list) {
310 	    lifr_mask = (struct lifreq *) mymalloc(sizeof(struct lifreq));
311 	    memcpy((void *) lifr_mask, (void *) lifr, sizeof(struct lifreq));
312 	    if (ioctl(sock, SIOCGLIFNETMASK, lifr_mask) < 0)
313 		msg_fatal("%s: ioctl(SIOCGLIFNETMASK): %m", myname);
314 	    /* XXX: Check whether sa_len/family are honoured --dcs */
315 	    inet_addr_list_append(mask_list,
316 				  (struct sockaddr *) &lifr_mask->lifr_addr);
317 	    myfree((void *) lifr_mask);
318 	}
319 	lifr = NEXT_INTERFACE(lifr);
320     }
321     vstring_free(buf);
322     (void) close(sock);
323     return (0);
324 }
325 
326 #else					/* HAVE_SIOCGLIF */
327 
328 /*
329  * The classic SIOCGIF* ioctls. Modern BSD operating systems will
330  * also return IPv6 addresses through these structure. Note however
331  * that recent versions of these operating systems have getifaddrs.
332  */
333 #if defined(_SIZEOF_ADDR_IFREQ)
334 #define NEXT_INTERFACE(ifr)	((struct ifreq *) \
335 	((char *) ifr + _SIZEOF_ADDR_IFREQ(*ifr)))
336 #define IFREQ_SIZE(ifr)	_SIZEOF_ADDR_IFREQ(*ifr)
337 #elif defined(HAS_SA_LEN)
338 #define NEXT_INTERFACE(ifr)	((struct ifreq *) \
339 	((char *) ifr + sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len))
340 #define IFREQ_SIZE(ifr)	(sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len)
341 #else
342 #define NEXT_INTERFACE(ifr)	(ifr + 1)
343 #define IFREQ_SIZE(ifr)	sizeof(ifr[0])
344 #endif
345 
346 /* ial_siocgif - determine IP addresses using ioctl(SIOCGIF*) */
347 
ial_siocgif(INET_ADDR_LIST * addr_list,INET_ADDR_LIST * mask_list,int af)348 static int ial_siocgif(INET_ADDR_LIST *addr_list,
349 		               INET_ADDR_LIST *mask_list,
350 		               int af)
351 {
352     const char *myname = "inet_addr_local[siocgif]";
353     struct in_addr addr;
354     struct ifconf ifc;
355     struct ifreq *ifr;
356     struct ifreq *ifr_mask;
357     struct ifreq *the_end;
358     int     sock;
359     VSTRING *buf;
360 
361     /*
362      * Get the network interface list. XXX The socket API appears to have no
363      * function that returns the number of network interfaces, so we have to
364      * guess how much space is needed to store the result.
365      *
366      * On BSD-derived systems, ioctl SIOCGIFCONF returns as much information as
367      * possible, leaving it up to the application to repeat the request with
368      * a larger buffer if the result caused a tight fit.
369      *
370      * Other systems, such as Solaris 2.5, generate an EINVAL error when the
371      * buffer is too small for the entire result. Workaround: ignore EINVAL
372      * errors and repeat the request with a larger buffer. The downside is
373      * that the program can run out of memory due to a non-memory problem,
374      * making it more difficult than necessary to diagnose the real problem.
375      */
376     sock = ial_socket(af);
377     if (sock < 0)
378 	return (0);
379     buf = vstring_alloc(1024);
380     for (;;) {
381 	ifc.ifc_len = vstring_avail(buf);
382 	ifc.ifc_buf = vstring_str(buf);
383 	if (ioctl(sock, SIOCGIFCONF, (char *) &ifc) < 0) {
384 	    if (errno != EINVAL)
385 		msg_fatal("%s: ioctl SIOCGIFCONF: %m", myname);
386 	} else if (ifc.ifc_len < vstring_avail(buf) / 2)
387 	    break;
388 	VSTRING_SPACE(buf, vstring_avail(buf) * 2);
389     }
390 
391     the_end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
392     for (ifr = ifc.ifc_req; ifr < the_end;) {
393 	if (ifr->ifr_addr.sa_family != af) {
394 	    ifr = NEXT_INTERFACE(ifr);
395 	    continue;
396 	}
397 	if (af == AF_INET) {
398 	    addr = ((struct sockaddr_in *) & ifr->ifr_addr)->sin_addr;
399 	    if (addr.s_addr != INADDR_ANY) {
400 		inet_addr_list_append(addr_list, &ifr->ifr_addr);
401 		if (mask_list) {
402 		    ifr_mask = (struct ifreq *) mymalloc(IFREQ_SIZE(ifr));
403 		    memcpy((void *) ifr_mask, (void *) ifr, IFREQ_SIZE(ifr));
404 		    if (ioctl(sock, SIOCGIFNETMASK, ifr_mask) < 0)
405 			msg_fatal("%s: ioctl SIOCGIFNETMASK: %m", myname);
406 
407 		    /*
408 		     * Note that this SIOCGIFNETMASK has truly screwed up the
409 		     * contents of sa_len/sa_family. We must fix this
410 		     * manually to have correct addresses.   --dcs
411 		     */
412 		    ifr_mask->ifr_addr.sa_family = af;
413 #ifdef HAS_SA_LEN
414 		    ifr_mask->ifr_addr.sa_len = sizeof(struct sockaddr_in);
415 #endif
416 		    inet_addr_list_append(mask_list, &ifr_mask->ifr_addr);
417 		    myfree((void *) ifr_mask);
418 		}
419 	    }
420 	}
421 #ifdef HAS_IPV6
422 	else if (af == AF_INET6) {
423 	    struct sockaddr *sa;
424 
425 	    sa = SOCK_ADDR_PTR(&ifr->ifr_addr);
426 	    if (!(IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa)))) {
427 		inet_addr_list_append(addr_list, sa);
428 		if (mask_list) {
429 		    /* XXX Assume /128 for everything */
430 		    struct sockaddr_in6 mask6;
431 
432 		    mask6 = *SOCK_ADDR_IN6_PTR(sa);
433 		    memset((void *) &mask6.sin6_addr, ~0,
434 			   sizeof(mask6.sin6_addr));
435 		    inet_addr_list_append(mask_list, SOCK_ADDR_PTR(&mask6));
436 		}
437 	    }
438 	}
439 #endif
440 	ifr = NEXT_INTERFACE(ifr);
441     }
442     vstring_free(buf);
443     (void) close(sock);
444     return (0);
445 }
446 
447 #endif					/* HAVE_SIOCGLIF */
448 
449 #ifdef HAS_PROCNET_IFINET6
450 
451 /*
452  * Older Linux versions lack proper calls to retrieve IPv6 interface
453  * addresses. Instead, the addresses can be read from a file in the
454  * /proc tree. The most important issue with this approach however
455  * is that the /proc tree may not always be available, for example
456  * in a chrooted environment or in "hardened" (sic) installations.
457  */
458 
459 /* ial_procnet_ifinet6 - determine IPv6 addresses using /proc/net/if_inet6 */
460 
ial_procnet_ifinet6(INET_ADDR_LIST * addr_list,INET_ADDR_LIST * mask_list)461 static int ial_procnet_ifinet6(INET_ADDR_LIST *addr_list,
462 			               INET_ADDR_LIST *mask_list)
463 {
464     const char *myname = "inet_addr_local[procnet_ifinet6]";
465     FILE   *fp;
466     char    buf[BUFSIZ];
467     unsigned plen;
468     VSTRING *addrbuf;
469     struct sockaddr_in6 addr;
470     struct sockaddr_in6 mask;
471 
472     /*
473      * Example: 00000000000000000000000000000001 01 80 10 80 lo
474      *
475      * Fields: address, interface index, prefix length, scope value
476      * (net/ipv6.h), interface flags (linux/rtnetlink.h), device name.
477      *
478      * FIX 200501 The IPv6 patch used fscanf(), which will hang on unexpected
479      * input. Use fgets() + sscanf() instead.
480      */
481     if ((fp = fopen(_PATH_PROCNET_IFINET6, "r")) != 0) {
482 	addrbuf = vstring_alloc(MAI_V6ADDR_BYTES + 1);
483 	memset((void *) &addr, 0, sizeof(addr));
484 	addr.sin6_family = AF_INET6;
485 #ifdef HAS_SA_LEN
486 	addr.sin6_len = sizeof(addr);
487 #endif
488 	mask = addr;
489 	while (fgets(buf, sizeof(buf), fp) != 0) {
490 	    /* 200501 hex_decode() is light-weight compared to getaddrinfo(). */
491 	    if (hex_decode(addrbuf, buf, MAI_V6ADDR_BYTES * 2) == 0
492 		|| sscanf(buf + MAI_V6ADDR_BYTES * 2, " %*x %x", &plen) != 1
493 		|| plen > MAI_V6ADDR_BITS) {
494 		msg_warn("unexpected data in %s - skipping IPv6 configuration",
495 			 _PATH_PROCNET_IFINET6);
496 		break;
497 	    }
498 	    /* vstring_str(addrbuf) has worst-case alignment. */
499 	    addr.sin6_addr = *(struct in6_addr *) vstring_str(addrbuf);
500 	    inet_addr_list_append(addr_list, SOCK_ADDR_PTR(&addr));
501 
502 	    memset((void *) &mask.sin6_addr, ~0, sizeof(mask.sin6_addr));
503 	    mask_addr((unsigned char *) &mask.sin6_addr,
504 		      sizeof(mask.sin6_addr), plen);
505 	    inet_addr_list_append(mask_list, SOCK_ADDR_PTR(&mask));
506 	}
507 	vstring_free(addrbuf);
508 	fclose(fp);				/* FIX 200501 */
509     } else {
510 	msg_warn("can't open %s (%m) - skipping IPv6 configuration",
511 		 _PATH_PROCNET_IFINET6);
512     }
513     return (0);
514 }
515 
516 #endif					/* HAS_PROCNET_IFINET6 */
517 
518 /* inet_addr_local - find all IP addresses for this host */
519 
inet_addr_local(INET_ADDR_LIST * addr_list,INET_ADDR_LIST * mask_list,unsigned * addr_family_list)520 int     inet_addr_local(INET_ADDR_LIST *addr_list, INET_ADDR_LIST *mask_list,
521 			        unsigned *addr_family_list)
522 {
523     const char *myname = "inet_addr_local";
524     int     initial_count = addr_list->used;
525     unsigned family;
526     int     count;
527 
528     while ((family = *addr_family_list++) != 0) {
529 
530 	/*
531 	 * IP Version 4
532 	 */
533 	if (family == AF_INET) {
534 	    count = addr_list->used;
535 #if defined(HAVE_GETIFADDRS)
536 	    ial_getifaddrs(addr_list, mask_list, AF_INET);
537 #elif defined (HAS_SIOCGLIF)
538 	    ial_siocglif(addr_list, mask_list, AF_INET);
539 #else
540 	    ial_siocgif(addr_list, mask_list, AF_INET);
541 #endif
542 	    if (msg_verbose)
543 		msg_info("%s: configured %d IPv4 addresses",
544 			 myname, addr_list->used - count);
545 	}
546 
547 	/*
548 	 * IP Version 6
549 	 */
550 #ifdef HAS_IPV6
551 	else if (family == AF_INET6) {
552 	    count = addr_list->used;
553 #if defined(HAVE_GETIFADDRS)
554 	    ial_getifaddrs(addr_list, mask_list, AF_INET6);
555 #elif defined(HAS_PROCNET_IFINET6)
556 	    ial_procnet_ifinet6(addr_list, mask_list);
557 #elif defined(HAS_SIOCGLIF)
558 	    ial_siocglif(addr_list, mask_list, AF_INET6);
559 #else
560 	    ial_siocgif(addr_list, mask_list, AF_INET6);
561 #endif
562 	    if (msg_verbose)
563 		msg_info("%s: configured %d IPv6 addresses", myname,
564 			 addr_list->used - count);
565 	}
566 #endif
567 
568 	/*
569 	 * Something's not right.
570 	 */
571 	else
572 	    msg_panic("%s: unknown address family %d", myname, family);
573     }
574     return (addr_list->used - initial_count);
575 }
576 
577 #ifdef TEST
578 
579 #include <string.h>
580 #include <vstream.h>
581 #include <msg_vstream.h>
582 #include <inet_proto.h>
583 
main(int unused_argc,char ** argv)584 int     main(int unused_argc, char **argv)
585 {
586     INET_ADDR_LIST addr_list;
587     INET_ADDR_LIST mask_list;
588     MAI_HOSTADDR_STR hostaddr;
589     MAI_HOSTADDR_STR hostmask;
590     struct sockaddr *sa;
591     int     i;
592     INET_PROTO_INFO *proto_info;
593 
594     msg_vstream_init(argv[0], VSTREAM_ERR);
595     msg_verbose = 1;
596 
597     proto_info = inet_proto_init(argv[0],
598 				 argv[1] ? argv[1] : INET_PROTO_NAME_ALL);
599     inet_addr_list_init(&addr_list);
600     inet_addr_list_init(&mask_list);
601     inet_addr_local(&addr_list, &mask_list, proto_info->ai_family_list);
602 
603     if (addr_list.used == 0)
604 	msg_fatal("cannot find any active network interfaces");
605 
606     if (addr_list.used == 1)
607 	msg_warn("found only one active network interface");
608 
609     for (i = 0; i < addr_list.used; i++) {
610 	sa = SOCK_ADDR_PTR(addr_list.addrs + i);
611 	SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa),
612 			     &hostaddr, (MAI_SERVPORT_STR *) 0, 0);
613 	sa = SOCK_ADDR_PTR(mask_list.addrs + i);
614 	SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa),
615 			     &hostmask, (MAI_SERVPORT_STR *) 0, 0);
616 	vstream_printf("%s/%s\n", hostaddr.buf, hostmask.buf);
617 	vstream_fflush(VSTREAM_OUT);
618     }
619     inet_addr_list_free(&addr_list);
620     inet_addr_list_free(&mask_list);
621     return (0);
622 }
623 
624 #endif
625