xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/inet_addr_local.c (revision 46f5119e40af2e51998f686b2fdcc76b5488f7f3)
1 /*	$NetBSD: inet_addr_local.c,v 1.3 2010/06/17 18:18:16 tron 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 /* ial_socket - make socket for ioctl() operations */
122 
123 static int ial_socket(int af)
124 {
125     const char *myname = "inet_addr_local[socket]";
126     int     sock;
127 
128     /*
129      * The host may not be actually configured with IPv6. When IPv6 support
130      * is not actually in the kernel, don't consider failure to create an
131      * IPv6 socket as fatal. This could be tuned better though. For other
132      * families, the error is fatal.
133      *
134      * XXX Now that Postfix controls protocol support centrally with the
135      * inet_proto(3) module, this workaround should no longer be needed.
136      */
137     if ((sock = socket(af, SOCK_DGRAM, 0)) < 0) {
138 #ifdef HAS_IPV6
139 	if (af == AF_INET6) {
140 	    if (msg_verbose)
141 		msg_warn("%s: socket: %m", myname);
142 	    return (-1);
143 	}
144 #endif
145 	msg_fatal("%s: socket: %m", myname);
146     }
147     return (sock);
148 }
149 
150 #ifdef HAVE_GETIFADDRS
151 
152 /*
153  * The getifaddrs(3) function, introduced by BSD/OS, provides a
154  * platform-independent way of requesting interface addresses,
155  * including IPv6 addresses. The implementation however is not
156  * present in all major operating systems.
157  */
158 
159 /* ial_getifaddrs - determine IP addresses using getifaddrs(3) */
160 
161 static int ial_getifaddrs(INET_ADDR_LIST *addr_list,
162 			          INET_ADDR_LIST *mask_list,
163 			          int af)
164 {
165     const char *myname = "inet_addr_local[getifaddrs]";
166     struct ifaddrs *ifap, *ifa;
167     struct sockaddr *sa, *sam;
168 
169     if (getifaddrs(&ifap) < 0)
170 	msg_fatal("%s: getifaddrs: %m", myname);
171 
172     /*
173      * Get the address of each IP network interface. According to BIND we
174      * must include interfaces that are down because the machine may still
175      * receive packets for that address (yes, via some other interface).
176      * Having no way to verify this claim on every machine, I will give them
177      * the benefit of the doubt.
178      *
179      * FIX 200501: The IPv6 patch did not report NetBSD loopback interfaces;
180      * fixed by replacing IFF_RUNNING by IFF_UP.
181      *
182      * FIX 200501: The IPV6 patch did not skip wild-card interface addresses
183      * (tested on FreeBSD).
184      */
185     for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
186 	if (!(ifa->ifa_flags & IFF_UP) || ifa->ifa_addr == 0)
187 	    continue;
188 	sa = ifa->ifa_addr;
189 	if (af != AF_UNSPEC && sa->sa_family != af)
190 	    continue;
191 	sam = ifa->ifa_netmask;
192 	if (sam == 0) {
193 	    /* XXX In mynetworks, a null netmask would match everyone. */
194 	    msg_warn("ignoring interface with null netmask, address family %d",
195 		     sa->sa_family);
196 	    continue;
197 	}
198 	switch (sa->sa_family) {
199 	case AF_INET:
200 	    if (SOCK_ADDR_IN_ADDR(sa).s_addr == INADDR_ANY)
201 		continue;
202 	    break;
203 #ifdef HAS_IPV6
204 	case AF_INET6:
205 	    if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa)))
206 		continue;
207 	    break;
208 #endif
209 	default:
210 	    continue;
211 	}
212 
213 	inet_addr_list_append(addr_list, sa);
214 	if (mask_list != 0) {
215 
216 	    /*
217 	     * Unfortunately, sa_len/sa_family may be broken in the netmask
218 	     * sockaddr structure. We must fix this manually to have correct
219 	     * addresses.   --dcs
220 	     */
221 #ifdef HAS_SA_LEN
222 	    sam->sa_len = sa->sa_family == AF_INET6 ?
223 		sizeof(struct sockaddr_in6) :
224 		sizeof(struct sockaddr_in);
225 #endif
226 	    sam->sa_family = sa->sa_family;
227 	    inet_addr_list_append(mask_list, sam);
228 	}
229     }
230     freeifaddrs(ifap);
231     return (0);
232 }
233 
234 #endif					/* HAVE_GETIFADDRS */
235 
236 #ifdef HAS_SIOCGLIF
237 
238 /*
239  * The SIOCLIF* ioctls are the successors of SIOCGIF* on the Solaris
240  * and HP/UX operating systems. The data is stored in sockaddr_storage
241  * structure. Both IPv4 and IPv6 addresses are returned though these
242  * calls.
243  */
244 #define NEXT_INTERFACE(lifr)	(lifr + 1)
245 #define LIFREQ_SIZE(lifr)	sizeof(lifr[0])
246 
247 /* ial_siocglif - determine IP addresses using ioctl(SIOCGLIF*) */
248 
249 static int ial_siocglif(INET_ADDR_LIST *addr_list,
250 			        INET_ADDR_LIST *mask_list,
251 			        int af)
252 {
253     const char *myname = "inet_addr_local[siocglif]";
254     struct lifconf lifc;
255     struct lifreq *lifr;
256     struct lifreq *lifr_mask;
257     struct lifreq *the_end;
258     struct sockaddr *sa;
259     int     sock;
260     VSTRING *buf;
261 
262     /*
263      * See also comments in ial_siocgif()
264      */
265     if (af != AF_INET && af != AF_INET6)
266 	msg_fatal("%s: address family was %d, must be AF_INET (%d) or "
267 		  "AF_INET6 (%d)", myname, af, AF_INET, AF_INET6);
268     sock = ial_socket(af);
269     if (sock < 0)
270 	return (0);
271     buf = vstring_alloc(1024);
272     for (;;) {
273 	memset(&lifc, 0, sizeof(lifc));
274 	lifc.lifc_family = AF_UNSPEC;		/* XXX Why??? */
275 	lifc.lifc_len = vstring_avail(buf);
276 	lifc.lifc_buf = vstring_str(buf);
277 	if (ioctl(sock, SIOCGLIFCONF, (char *) &lifc) < 0) {
278 	    if (errno != EINVAL)
279 		msg_fatal("%s: ioctl SIOCGLIFCONF: %m", myname);
280 	} else if (lifc.lifc_len < vstring_avail(buf) / 2)
281 	    break;
282 	VSTRING_SPACE(buf, vstring_avail(buf) * 2);
283     }
284 
285     the_end = (struct lifreq *) (lifc.lifc_buf + lifc.lifc_len);
286     for (lifr = lifc.lifc_req; lifr < the_end;) {
287 	sa = (struct sockaddr *) & lifr->lifr_addr;
288 	if (sa->sa_family != af) {
289 	    lifr = NEXT_INTERFACE(lifr);
290 	    continue;
291 	}
292 	if (af == AF_INET) {
293 	    if (SOCK_ADDR_IN_ADDR(sa).s_addr == INADDR_ANY) {
294 		lifr = NEXT_INTERFACE(lifr);
295 		continue;
296 	    }
297 	}
298 #ifdef HAS_IPV6
299 	else if (af == AF_INET6) {
300 	    if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa))) {
301 		lifr = NEXT_INTERFACE(lifr);
302 		continue;
303 	    }
304 	}
305 #endif
306 	inet_addr_list_append(addr_list, sa);
307 	if (mask_list) {
308 	    lifr_mask = (struct lifreq *) mymalloc(sizeof(struct lifreq));
309 	    memcpy((char *) lifr_mask, (char *) lifr, sizeof(struct lifreq));
310 	    if (ioctl(sock, SIOCGLIFNETMASK, lifr_mask) < 0)
311 		msg_fatal("%s: ioctl(SIOCGLIFNETMASK): %m", myname);
312 	    /* XXX: Check whether sa_len/family are honoured --dcs */
313 	    inet_addr_list_append(mask_list,
314 				(struct sockaddr *) & lifr_mask->lifr_addr);
315 	    myfree((char *) lifr_mask);
316 	}
317 	lifr = NEXT_INTERFACE(lifr);
318     }
319     vstring_free(buf);
320     (void) close(sock);
321     return (0);
322 }
323 
324 #else					/* HAVE_SIOCGLIF */
325 
326 /*
327  * The classic SIOCGIF* ioctls. Modern BSD operating systems will
328  * also return IPv6 addresses through these structure. Note however
329  * that recent versions of these operating systems have getifaddrs.
330  */
331 #if defined(_SIZEOF_ADDR_IFREQ)
332 #define NEXT_INTERFACE(ifr)	((struct ifreq *) \
333 	((char *) ifr + _SIZEOF_ADDR_IFREQ(*ifr)))
334 #define IFREQ_SIZE(ifr)	_SIZEOF_ADDR_IFREQ(*ifr)
335 #elif defined(HAS_SA_LEN)
336 #define NEXT_INTERFACE(ifr)	((struct ifreq *) \
337 	((char *) ifr + sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len))
338 #define IFREQ_SIZE(ifr)	(sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len)
339 #else
340 #define NEXT_INTERFACE(ifr)	(ifr + 1)
341 #define IFREQ_SIZE(ifr)	sizeof(ifr[0])
342 #endif
343 
344 /* ial_siocgif - determine IP addresses using ioctl(SIOCGIF*) */
345 
346 static int ial_siocgif(INET_ADDR_LIST *addr_list,
347 		               INET_ADDR_LIST *mask_list,
348 		               int af)
349 {
350     const char *myname = "inet_addr_local[siocgif]";
351     struct in_addr addr;
352     struct ifconf ifc;
353     struct ifreq *ifr;
354     struct ifreq *ifr_mask;
355     struct ifreq *the_end;
356     int     sock;
357     VSTRING *buf;
358 
359     /*
360      * Get the network interface list. XXX The socket API appears to have no
361      * function that returns the number of network interfaces, so we have to
362      * guess how much space is needed to store the result.
363      *
364      * On BSD-derived systems, ioctl SIOCGIFCONF returns as much information as
365      * possible, leaving it up to the application to repeat the request with
366      * a larger buffer if the result caused a tight fit.
367      *
368      * Other systems, such as Solaris 2.5, generate an EINVAL error when the
369      * buffer is too small for the entire result. Workaround: ignore EINVAL
370      * errors and repeat the request with a larger buffer. The downside is
371      * that the program can run out of memory due to a non-memory problem,
372      * making it more difficult than necessary to diagnose the real problem.
373      */
374     sock = ial_socket(af);
375     if (sock < 0)
376 	return (0);
377     buf = vstring_alloc(1024);
378     for (;;) {
379 	ifc.ifc_len = vstring_avail(buf);
380 	ifc.ifc_buf = vstring_str(buf);
381 	if (ioctl(sock, SIOCGIFCONF, (char *) &ifc) < 0) {
382 	    if (errno != EINVAL)
383 		msg_fatal("%s: ioctl SIOCGIFCONF: %m", myname);
384 	} else if (ifc.ifc_len < vstring_avail(buf) / 2)
385 	    break;
386 	VSTRING_SPACE(buf, vstring_avail(buf) * 2);
387     }
388 
389     the_end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
390     for (ifr = ifc.ifc_req; ifr < the_end;) {
391 	if (ifr->ifr_addr.sa_family != af) {
392 	    ifr = NEXT_INTERFACE(ifr);
393 	    continue;
394 	}
395 	if (af == AF_INET) {
396 	    addr = ((struct sockaddr_in *) & ifr->ifr_addr)->sin_addr;
397 	    if (addr.s_addr != INADDR_ANY) {
398 		inet_addr_list_append(addr_list, &ifr->ifr_addr);
399 		if (mask_list) {
400 		    ifr_mask = (struct ifreq *) mymalloc(IFREQ_SIZE(ifr));
401 		    memcpy((char *) ifr_mask, (char *) ifr, IFREQ_SIZE(ifr));
402 		    if (ioctl(sock, SIOCGIFNETMASK, ifr_mask) < 0)
403 			msg_fatal("%s: ioctl SIOCGIFNETMASK: %m", myname);
404 
405 		    /*
406 		     * Note that this SIOCGIFNETMASK has truly screwed up the
407 		     * contents of sa_len/sa_family. We must fix this
408 		     * manually to have correct addresses.   --dcs
409 		     */
410 		    ifr_mask->ifr_addr.sa_family = af;
411 #ifdef HAS_SA_LEN
412 		    ifr_mask->ifr_addr.sa_len = sizeof(struct sockaddr_in);
413 #endif
414 		    inet_addr_list_append(mask_list, &ifr_mask->ifr_addr);
415 		    myfree((char *) ifr_mask);
416 		}
417 	    }
418 	}
419 #ifdef HAS_IPV6
420 	else if (af == AF_INET6) {
421 	    struct sockaddr *sa;
422 
423 	    sa = SOCK_ADDR_PTR(&ifr->ifr_addr);
424 	    if (!(IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa)))) {
425 		inet_addr_list_append(addr_list, sa);
426 		if (mask_list) {
427 		    /* XXX Assume /128 for everything */
428 		    struct sockaddr_in6 mask6;
429 
430 		    mask6 = *SOCK_ADDR_IN6_PTR(sa);
431 		    memset((char *) &mask6.sin6_addr, ~0,
432 			   sizeof(mask6.sin6_addr));
433 		    inet_addr_list_append(mask_list, SOCK_ADDR_PTR(&mask6));
434 		}
435 	    }
436 	}
437 #endif
438 	ifr = NEXT_INTERFACE(ifr);
439     }
440     vstring_free(buf);
441     (void) close(sock);
442     return (0);
443 }
444 
445 #endif					/* HAVE_SIOCGLIF */
446 
447 #ifdef HAS_PROCNET_IFINET6
448 
449 /*
450  * Older Linux versions lack proper calls to retrieve IPv6 interface
451  * addresses. Instead, the addresses can be read from a file in the
452  * /proc tree. The most important issue with this approach however
453  * is that the /proc tree may not always be available, for example
454  * in a chrooted environment or in "hardened" (sic) installations.
455  */
456 
457 /* ial_procnet_ifinet6 - determine IPv6 addresses using /proc/net/if_inet6 */
458 
459 static int ial_procnet_ifinet6(INET_ADDR_LIST *addr_list,
460 			               INET_ADDR_LIST *mask_list)
461 {
462     const char *myname = "inet_addr_local[procnet_ifinet6]";
463     FILE   *fp;
464     char    buf[BUFSIZ];
465     unsigned plen;
466     VSTRING *addrbuf;
467     struct sockaddr_in6 addr;
468     struct sockaddr_in6 mask;
469 
470     /*
471      * Example: 00000000000000000000000000000001 01 80 10 80 lo
472      *
473      * Fields: address, interface index, prefix length, scope value
474      * (net/ipv6.h), interface flags (linux/rtnetlink.h), device name.
475      *
476      * FIX 200501 The IPv6 patch used fscanf(), which will hang on unexpected
477      * input. Use fgets() + sscanf() instead.
478      */
479     if ((fp = fopen(_PATH_PROCNET_IFINET6, "r")) != 0) {
480 	addrbuf = vstring_alloc(MAI_V6ADDR_BYTES + 1);
481 	memset((char *) &addr, 0, sizeof(addr));
482 	addr.sin6_family = AF_INET6;
483 #ifdef HAS_SA_LEN
484 	addr.sin6_len = sizeof(addr);
485 #endif
486 	mask = addr;
487 	while (fgets(buf, sizeof(buf), fp) != 0) {
488 	    /* 200501 hex_decode() is light-weight compared to getaddrinfo(). */
489 	    if (hex_decode(addrbuf, buf, MAI_V6ADDR_BYTES * 2) == 0
490 		|| sscanf(buf + MAI_V6ADDR_BYTES * 2, " %*x %x", &plen) != 1
491 		|| plen > MAI_V6ADDR_BITS) {
492 		msg_warn("unexpected data in %s - skipping IPv6 configuration",
493 			 _PATH_PROCNET_IFINET6);
494 		break;
495 	    }
496 	    /* vstring_str(addrbuf) has worst-case alignment. */
497 	    addr.sin6_addr = *(struct in6_addr *) vstring_str(addrbuf);
498 	    inet_addr_list_append(addr_list, SOCK_ADDR_PTR(&addr));
499 
500 	    memset((char *) &mask.sin6_addr, ~0, sizeof(mask.sin6_addr));
501 	    mask_addr((unsigned char *) &mask.sin6_addr,
502 		      sizeof(mask.sin6_addr), plen);
503 	    inet_addr_list_append(mask_list, SOCK_ADDR_PTR(&mask));
504 	}
505 	vstring_free(addrbuf);
506 	fclose(fp);				/* FIX 200501 */
507     } else {
508 	msg_warn("can't open %s (%m) - skipping IPv6 configuration",
509 		 _PATH_PROCNET_IFINET6);
510     }
511     return (0);
512 }
513 
514 #endif					/* HAS_PROCNET_IFINET6 */
515 
516 /* inet_addr_local - find all IP addresses for this host */
517 
518 int     inet_addr_local(INET_ADDR_LIST *addr_list, INET_ADDR_LIST *mask_list,
519 			        unsigned *addr_family_list)
520 {
521     const char *myname = "inet_addr_local";
522     int     initial_count = addr_list->used;
523     unsigned family;
524     int     count;
525 
526     while ((family = *addr_family_list++) != 0) {
527 
528 	/*
529 	 * IP Version 4
530 	 */
531 	if (family == AF_INET) {
532 	    count = addr_list->used;
533 #if defined(HAVE_GETIFADDRS)
534 	    ial_getifaddrs(addr_list, mask_list, AF_INET);
535 #elif defined (HAS_SIOCGLIF)
536 	    ial_siocglif(addr_list, mask_list, AF_INET);
537 #else
538 	    ial_siocgif(addr_list, mask_list, AF_INET);
539 #endif
540 	    if (msg_verbose)
541 		msg_info("%s: configured %d IPv4 addresses",
542 			 myname, addr_list->used - count);
543 	}
544 
545 	/*
546 	 * IP Version 6
547 	 */
548 #ifdef HAS_IPV6
549 	else if (family == AF_INET6) {
550 	    count = addr_list->used;
551 #if defined(HAVE_GETIFADDRS)
552 	    ial_getifaddrs(addr_list, mask_list, AF_INET6);
553 #elif defined(HAS_PROCNET_IFINET6)
554 	    ial_procnet_ifinet6(addr_list, mask_list);
555 #elif defined(HAS_SIOCGLIF)
556 	    ial_siocglif(addr_list, mask_list, AF_INET6);
557 #else
558 	    ial_siocgif(addr_list, mask_list, AF_INET6);
559 #endif
560 	    if (msg_verbose)
561 		msg_info("%s: configured %d IPv6 addresses", myname,
562 			 addr_list->used - count);
563 	}
564 #endif
565 
566 	/*
567 	 * Something's not right.
568 	 */
569 	else
570 	    msg_panic("%s: unknown address family %d", myname, family);
571     }
572     return (addr_list->used - initial_count);
573 }
574 
575 #ifdef TEST
576 
577 #include <string.h>
578 #include <vstream.h>
579 #include <msg_vstream.h>
580 #include <inet_proto.h>
581 
582 int     main(int unused_argc, char **argv)
583 {
584     INET_ADDR_LIST addr_list;
585     INET_ADDR_LIST mask_list;
586     MAI_HOSTADDR_STR hostaddr;
587     MAI_HOSTADDR_STR hostmask;
588     struct sockaddr *sa;
589     int     i;
590     INET_PROTO_INFO *proto_info;
591 
592     msg_vstream_init(argv[0], VSTREAM_ERR);
593     msg_verbose = 1;
594 
595     proto_info = inet_proto_init(argv[0],
596 				 argv[1] ? argv[1] : INET_PROTO_NAME_ALL);
597     inet_addr_list_init(&addr_list);
598     inet_addr_list_init(&mask_list);
599     inet_addr_local(&addr_list, &mask_list, proto_info->ai_family_list);
600 
601     if (addr_list.used == 0)
602 	msg_fatal("cannot find any active network interfaces");
603 
604     if (addr_list.used == 1)
605 	msg_warn("found only one active network interface");
606 
607     for (i = 0; i < addr_list.used; i++) {
608 	sa = SOCK_ADDR_PTR(addr_list.addrs + i);
609 	SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa),
610 			     &hostaddr, (MAI_SERVPORT_STR *) 0, 0);
611 	sa = SOCK_ADDR_PTR(mask_list.addrs + i);
612 	SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa),
613 			     &hostmask, (MAI_SERVPORT_STR *) 0, 0);
614 	vstream_printf("%s/%s\n", hostaddr.buf, hostmask.buf);
615 	vstream_fflush(VSTREAM_OUT);
616     }
617     inet_addr_list_free(&addr_list);
618     inet_addr_list_free(&mask_list);
619     return (0);
620 }
621 
622 #endif
623