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
125static 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
165static 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
251static 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
348static 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
461static 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
520int     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
584int     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