inet_addr_local.c revision 1.3
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
123static 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
161static 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
249static 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
346static 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
459static 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
518int     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
582int     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