summaryrefslogtreecommitdiffstats
path: root/src/util/inet_addr_local.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/inet_addr_local.c')
-rw-r--r--src/util/inet_addr_local.c621
1 files changed, 621 insertions, 0 deletions
diff --git a/src/util/inet_addr_local.c b/src/util/inet_addr_local.c
new file mode 100644
index 0000000..e48803a
--- /dev/null
+++ b/src/util/inet_addr_local.c
@@ -0,0 +1,621 @@
+/*++
+/* NAME
+/* inet_addr_local 3
+/* SUMMARY
+/* determine if IP address is local
+/* SYNOPSIS
+/* #include <inet_addr_local.h>
+/*
+/* int inet_addr_local(addr_list, mask_list, addr_family_list)
+/* INET_ADDR_LIST *addr_list;
+/* INET_ADDR_LIST *mask_list;
+/* unsigned *addr_family;
+/* DESCRIPTION
+/* inet_addr_local() determines all active IP interface addresses
+/* of the local system. Any address found is appended to the
+/* specified address list. The result value is the number of
+/* active interfaces found.
+/*
+/* The mask_list is either a null pointer, or it is a list that
+/* receives the netmasks of the interface addresses that were found.
+/*
+/* The addr_family_list specifies one or more of AF_INET or AF_INET6.
+/* DIAGNOSTICS
+/* Fatal errors: out of memory.
+/* SEE ALSO
+/* inet_addr_list(3) address list management
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Dean C. Strik
+/* Department ICT
+/* Eindhoven University of Technology
+/* P.O. Box 513
+/* 5600 MB Eindhoven, Netherlands
+/* E-mail: <dean@ipnet6.org>
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#ifdef USE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+#include <errno.h>
+#include <string.h>
+#ifdef HAS_IPV6 /* Linux only? */
+#include <netdb.h>
+#include <stdio.h>
+#endif
+#ifdef HAVE_GETIFADDRS
+#include <ifaddrs.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <inet_addr_list.h>
+#include <inet_addr_local.h>
+#include <myaddrinfo.h>
+#include <sock_addr.h>
+#include <mask_addr.h>
+#include <hex_code.h>
+
+ /*
+ * Postfix needs its own interface address information to determine whether
+ * or not it is an MX host for some destination; without this information,
+ * mail would loop between MX hosts. Postfix also needs its interface
+ * addresses to figure out whether or not it is final destination for
+ * addresses of the form username@[ipaddress].
+ *
+ * Postfix needs its own interface netmask information when no explicit
+ * mynetworks setting is given in main.cf, and "mynetworks_style = subnet".
+ * The mynetworks parameter controls, among others, what mail clients are
+ * allowed to relay mail through Postfix.
+ *
+ * Different systems have different ways to find out this information. We will
+ * therefore use OS dependent methods. An overview:
+ *
+ * - Use getifaddrs() when available. This supports both IPv4/IPv6 addresses.
+ * The implementation however is not present in all major operating systems.
+ *
+ * - Use SIOCGLIFCONF when available. This supports both IPv4/IPv6 addresses.
+ * With SIOCGLIFNETMASK we can obtain the netmask for either address family.
+ * Again, this is not present in all major operating systems.
+ *
+ * - On Linux, glibc's getifaddrs(3) has returned IPv4 information for some
+ * time, but IPv6 information was not returned until 2.3.3. With older Linux
+ * versions we get IPv4 interface information with SIOCGIFCONF, and read
+ * IPv6 address/prefix information from a file in the /proc filesystem.
+ *
+ * - On other systems we expect SIOCGIFCONF to return IPv6 addresses. Since
+ * SIOCGIFNETMASK does not work reliably for IPv6 addresses, we always set
+ * the prefix length to /128 (host), and expect the user to configure a more
+ * appropriate mynetworks setting if needed.
+ *
+ * XXX: Each lookup method is implemented by its own function, so we duplicate
+ * some code. In this case, I think this is better than really drowning in
+ * the #ifdefs...
+ *
+ * -- Dean Strik (dcs)
+ */
+
+#ifndef HAVE_GETIFADDRS
+
+/* ial_socket - make socket for ioctl() operations */
+
+static int ial_socket(int af)
+{
+ const char *myname = "inet_addr_local[socket]";
+ int sock;
+
+ /*
+ * The host may not be actually configured with IPv6. When IPv6 support
+ * is not actually in the kernel, don't consider failure to create an
+ * IPv6 socket as fatal. This could be tuned better though. For other
+ * families, the error is fatal.
+ *
+ * XXX Now that Postfix controls protocol support centrally with the
+ * inet_proto(3) module, this workaround should no longer be needed.
+ */
+ if ((sock = socket(af, SOCK_DGRAM, 0)) < 0) {
+#ifdef HAS_IPV6
+ if (af == AF_INET6) {
+ if (msg_verbose)
+ msg_warn("%s: socket: %m", myname);
+ return (-1);
+ }
+#endif
+ msg_fatal("%s: socket: %m", myname);
+ }
+ return (sock);
+}
+
+#endif
+
+#ifdef HAVE_GETIFADDRS
+
+/*
+ * The getifaddrs(3) function, introduced by BSD/OS, provides a
+ * platform-independent way of requesting interface addresses,
+ * including IPv6 addresses. The implementation however is not
+ * present in all major operating systems.
+ */
+
+/* ial_getifaddrs - determine IP addresses using getifaddrs(3) */
+
+static int ial_getifaddrs(INET_ADDR_LIST *addr_list,
+ INET_ADDR_LIST *mask_list,
+ int af)
+{
+ const char *myname = "inet_addr_local[getifaddrs]";
+ struct ifaddrs *ifap, *ifa;
+ struct sockaddr *sa, *sam;
+
+ if (getifaddrs(&ifap) < 0)
+ msg_fatal("%s: getifaddrs: %m", myname);
+
+ /*
+ * Get the address of each IP network interface. According to BIND we
+ * must include interfaces that are down because the machine may still
+ * receive packets for that address (yes, via some other interface).
+ * Having no way to verify this claim on every machine, I will give them
+ * the benefit of the doubt.
+ *
+ * FIX 200501: The IPv6 patch did not report NetBSD loopback interfaces;
+ * fixed by replacing IFF_RUNNING by IFF_UP.
+ *
+ * FIX 200501: The IPV6 patch did not skip wild-card interface addresses
+ * (tested on FreeBSD).
+ */
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (!(ifa->ifa_flags & IFF_UP) || ifa->ifa_addr == 0)
+ continue;
+ sa = ifa->ifa_addr;
+ if (af != AF_UNSPEC && sa->sa_family != af)
+ continue;
+ sam = ifa->ifa_netmask;
+ if (sam == 0) {
+ /* XXX In mynetworks, a null netmask would match everyone. */
+ msg_warn("ignoring interface with null netmask, address family %d",
+ sa->sa_family);
+ continue;
+ }
+ switch (sa->sa_family) {
+ case AF_INET:
+ if (SOCK_ADDR_IN_ADDR(sa).s_addr == INADDR_ANY)
+ continue;
+ break;
+#ifdef HAS_IPV6
+ case AF_INET6:
+ if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa)))
+ continue;
+ break;
+#endif
+ default:
+ continue;
+ }
+
+ inet_addr_list_append(addr_list, sa);
+ if (mask_list != 0) {
+
+ /*
+ * Unfortunately, sa_len/sa_family may be broken in the netmask
+ * sockaddr structure. We must fix this manually to have correct
+ * addresses. --dcs
+ */
+#ifdef HAS_SA_LEN
+ sam->sa_len = sa->sa_family == AF_INET6 ?
+ sizeof(struct sockaddr_in6) :
+ sizeof(struct sockaddr_in);
+#endif
+ sam->sa_family = sa->sa_family;
+ inet_addr_list_append(mask_list, sam);
+ }
+ }
+ freeifaddrs(ifap);
+ return (0);
+}
+
+#elif defined(HAS_SIOCGLIF) /* HAVE_GETIFADDRS */
+
+/*
+ * The SIOCLIF* ioctls are the successors of SIOCGIF* on the Solaris
+ * and HP/UX operating systems. The data is stored in sockaddr_storage
+ * structure. Both IPv4 and IPv6 addresses are returned though these
+ * calls.
+ */
+#define NEXT_INTERFACE(lifr) (lifr + 1)
+#define LIFREQ_SIZE(lifr) sizeof(lifr[0])
+
+/* ial_siocglif - determine IP addresses using ioctl(SIOCGLIF*) */
+
+static int ial_siocglif(INET_ADDR_LIST *addr_list,
+ INET_ADDR_LIST *mask_list,
+ int af)
+{
+ const char *myname = "inet_addr_local[siocglif]";
+ struct lifconf lifc;
+ struct lifreq *lifr;
+ struct lifreq *lifr_mask;
+ struct lifreq *the_end;
+ struct sockaddr *sa;
+ int sock;
+ VSTRING *buf;
+
+ /*
+ * See also comments in ial_siocgif()
+ */
+ if (af != AF_INET && af != AF_INET6)
+ msg_fatal("%s: address family was %d, must be AF_INET (%d) or "
+ "AF_INET6 (%d)", myname, af, AF_INET, AF_INET6);
+ sock = ial_socket(af);
+ if (sock < 0)
+ return (0);
+ buf = vstring_alloc(1024);
+ for (;;) {
+ memset(&lifc, 0, sizeof(lifc));
+ lifc.lifc_family = AF_UNSPEC; /* XXX Why??? */
+ lifc.lifc_len = vstring_avail(buf);
+ lifc.lifc_buf = vstring_str(buf);
+ if (ioctl(sock, SIOCGLIFCONF, (char *) &lifc) < 0) {
+ if (errno != EINVAL)
+ msg_fatal("%s: ioctl SIOCGLIFCONF: %m", myname);
+ } else if (lifc.lifc_len < vstring_avail(buf) / 2)
+ break;
+ VSTRING_SPACE(buf, vstring_avail(buf) * 2);
+ }
+
+ the_end = (struct lifreq *) (lifc.lifc_buf + lifc.lifc_len);
+ for (lifr = lifc.lifc_req; lifr < the_end;) {
+ sa = (struct sockaddr *) &lifr->lifr_addr;
+ if (sa->sa_family != af) {
+ lifr = NEXT_INTERFACE(lifr);
+ continue;
+ }
+ if (af == AF_INET) {
+ if (SOCK_ADDR_IN_ADDR(sa).s_addr == INADDR_ANY) {
+ lifr = NEXT_INTERFACE(lifr);
+ continue;
+ }
+#ifdef HAS_IPV6
+ } else if (af == AF_INET6) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa))) {
+ lifr = NEXT_INTERFACE(lifr);
+ continue;
+ }
+ }
+#endif
+ inet_addr_list_append(addr_list, sa);
+ if (mask_list) {
+ lifr_mask = (struct lifreq *) mymalloc(sizeof(struct lifreq));
+ memcpy((void *) lifr_mask, (void *) lifr, sizeof(struct lifreq));
+ if (ioctl(sock, SIOCGLIFNETMASK, lifr_mask) < 0)
+ msg_fatal("%s: ioctl(SIOCGLIFNETMASK): %m", myname);
+ /* XXX: Check whether sa_len/family are honoured --dcs */
+ inet_addr_list_append(mask_list,
+ (struct sockaddr *) &lifr_mask->lifr_addr);
+ myfree((void *) lifr_mask);
+ }
+ lifr = NEXT_INTERFACE(lifr);
+ }
+ vstring_free(buf);
+ (void) close(sock);
+ return (0);
+}
+
+#else /* HAVE_SIOCGLIF */
+
+/*
+ * The classic SIOCGIF* ioctls. Modern BSD operating systems will
+ * also return IPv6 addresses through these structure. Note however
+ * that recent versions of these operating systems have getifaddrs.
+ */
+#if defined(_SIZEOF_ADDR_IFREQ)
+#define NEXT_INTERFACE(ifr) ((struct ifreq *) \
+ ((char *) ifr + _SIZEOF_ADDR_IFREQ(*ifr)))
+#define IFREQ_SIZE(ifr) _SIZEOF_ADDR_IFREQ(*ifr)
+#elif defined(HAS_SA_LEN)
+#define NEXT_INTERFACE(ifr) ((struct ifreq *) \
+ ((char *) ifr + sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len))
+#define IFREQ_SIZE(ifr) (sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len)
+#else
+#define NEXT_INTERFACE(ifr) (ifr + 1)
+#define IFREQ_SIZE(ifr) sizeof(ifr[0])
+#endif
+
+/* ial_siocgif - determine IP addresses using ioctl(SIOCGIF*) */
+
+static int ial_siocgif(INET_ADDR_LIST *addr_list,
+ INET_ADDR_LIST *mask_list,
+ int af)
+{
+ const char *myname = "inet_addr_local[siocgif]";
+ struct in_addr addr;
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ struct ifreq *ifr_mask;
+ struct ifreq *the_end;
+ int sock;
+ VSTRING *buf;
+
+ /*
+ * Get the network interface list. XXX The socket API appears to have no
+ * function that returns the number of network interfaces, so we have to
+ * guess how much space is needed to store the result.
+ *
+ * On BSD-derived systems, ioctl SIOCGIFCONF returns as much information as
+ * possible, leaving it up to the application to repeat the request with
+ * a larger buffer if the result caused a tight fit.
+ *
+ * Other systems, such as Solaris 2.5, generate an EINVAL error when the
+ * buffer is too small for the entire result. Workaround: ignore EINVAL
+ * errors and repeat the request with a larger buffer. The downside is
+ * that the program can run out of memory due to a non-memory problem,
+ * making it more difficult than necessary to diagnose the real problem.
+ */
+ sock = ial_socket(af);
+ if (sock < 0)
+ return (0);
+ buf = vstring_alloc(1024);
+ for (;;) {
+ ifc.ifc_len = vstring_avail(buf);
+ ifc.ifc_buf = vstring_str(buf);
+ if (ioctl(sock, SIOCGIFCONF, (char *) &ifc) < 0) {
+ if (errno != EINVAL)
+ msg_fatal("%s: ioctl SIOCGIFCONF: %m", myname);
+ } else if (ifc.ifc_len < vstring_avail(buf) / 2)
+ break;
+ VSTRING_SPACE(buf, vstring_avail(buf) * 2);
+ }
+
+ the_end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+ for (ifr = ifc.ifc_req; ifr < the_end;) {
+ if (ifr->ifr_addr.sa_family != af) {
+ ifr = NEXT_INTERFACE(ifr);
+ continue;
+ }
+ if (af == AF_INET) {
+ addr = ((struct sockaddr_in *) & ifr->ifr_addr)->sin_addr;
+ if (addr.s_addr != INADDR_ANY) {
+ inet_addr_list_append(addr_list, &ifr->ifr_addr);
+ if (mask_list) {
+ ifr_mask = (struct ifreq *) mymalloc(IFREQ_SIZE(ifr));
+ memcpy((void *) ifr_mask, (void *) ifr, IFREQ_SIZE(ifr));
+ if (ioctl(sock, SIOCGIFNETMASK, ifr_mask) < 0)
+ msg_fatal("%s: ioctl SIOCGIFNETMASK: %m", myname);
+
+ /*
+ * Note that this SIOCGIFNETMASK has truly screwed up the
+ * contents of sa_len/sa_family. We must fix this
+ * manually to have correct addresses. --dcs
+ */
+ ifr_mask->ifr_addr.sa_family = af;
+#ifdef HAS_SA_LEN
+ ifr_mask->ifr_addr.sa_len = sizeof(struct sockaddr_in);
+#endif
+ inet_addr_list_append(mask_list, &ifr_mask->ifr_addr);
+ myfree((void *) ifr_mask);
+ }
+ }
+ }
+#ifdef HAS_IPV6
+ else if (af == AF_INET6) {
+ struct sockaddr *sa;
+
+ sa = SOCK_ADDR_PTR(&ifr->ifr_addr);
+ if (!(IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa)))) {
+ inet_addr_list_append(addr_list, sa);
+ if (mask_list) {
+ /* XXX Assume /128 for everything */
+ struct sockaddr_in6 mask6;
+
+ mask6 = *SOCK_ADDR_IN6_PTR(sa);
+ memset((void *) &mask6.sin6_addr, ~0,
+ sizeof(mask6.sin6_addr));
+ inet_addr_list_append(mask_list, SOCK_ADDR_PTR(&mask6));
+ }
+ }
+ }
+#endif
+ ifr = NEXT_INTERFACE(ifr);
+ }
+ vstring_free(buf);
+ (void) close(sock);
+ return (0);
+}
+
+#endif /* HAVE_SIOCGLIF */
+
+#ifdef HAS_PROCNET_IFINET6
+
+/*
+ * Older Linux versions lack proper calls to retrieve IPv6 interface
+ * addresses. Instead, the addresses can be read from a file in the
+ * /proc tree. The most important issue with this approach however
+ * is that the /proc tree may not always be available, for example
+ * in a chrooted environment or in "hardened" (sic) installations.
+ */
+
+/* ial_procnet_ifinet6 - determine IPv6 addresses using /proc/net/if_inet6 */
+
+static int ial_procnet_ifinet6(INET_ADDR_LIST *addr_list,
+ INET_ADDR_LIST *mask_list)
+{
+ const char *myname = "inet_addr_local[procnet_ifinet6]";
+ FILE *fp;
+ char buf[BUFSIZ];
+ unsigned plen;
+ VSTRING *addrbuf;
+ struct sockaddr_in6 addr;
+ struct sockaddr_in6 mask;
+
+ /*
+ * Example: 00000000000000000000000000000001 01 80 10 80 lo
+ *
+ * Fields: address, interface index, prefix length, scope value
+ * (net/ipv6.h), interface flags (linux/rtnetlink.h), device name.
+ *
+ * FIX 200501 The IPv6 patch used fscanf(), which will hang on unexpected
+ * input. Use fgets() + sscanf() instead.
+ */
+ if ((fp = fopen(_PATH_PROCNET_IFINET6, "r")) != 0) {
+ addrbuf = vstring_alloc(MAI_V6ADDR_BYTES + 1);
+ memset((void *) &addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+#ifdef HAS_SA_LEN
+ addr.sin6_len = sizeof(addr);
+#endif
+ mask = addr;
+ while (fgets(buf, sizeof(buf), fp) != 0) {
+ /* 200501 hex_decode() is light-weight compared to getaddrinfo(). */
+ if (hex_decode(addrbuf, buf, MAI_V6ADDR_BYTES * 2) == 0
+ || sscanf(buf + MAI_V6ADDR_BYTES * 2, " %*x %x", &plen) != 1
+ || plen > MAI_V6ADDR_BITS) {
+ msg_warn("unexpected data in %s - skipping IPv6 configuration",
+ _PATH_PROCNET_IFINET6);
+ break;
+ }
+ /* vstring_str(addrbuf) has worst-case alignment. */
+ addr.sin6_addr = *(struct in6_addr *) vstring_str(addrbuf);
+ inet_addr_list_append(addr_list, SOCK_ADDR_PTR(&addr));
+
+ memset((void *) &mask.sin6_addr, ~0, sizeof(mask.sin6_addr));
+ mask_addr((unsigned char *) &mask.sin6_addr,
+ sizeof(mask.sin6_addr), plen);
+ inet_addr_list_append(mask_list, SOCK_ADDR_PTR(&mask));
+ }
+ vstring_free(addrbuf);
+ fclose(fp); /* FIX 200501 */
+ } else {
+ msg_warn("can't open %s (%m) - skipping IPv6 configuration",
+ _PATH_PROCNET_IFINET6);
+ }
+ return (0);
+}
+
+#endif /* HAS_PROCNET_IFINET6 */
+
+/* inet_addr_local - find all IP addresses for this host */
+
+int inet_addr_local(INET_ADDR_LIST *addr_list, INET_ADDR_LIST *mask_list,
+ unsigned *addr_family_list)
+{
+ const char *myname = "inet_addr_local";
+ int initial_count = addr_list->used;
+ unsigned family;
+ int count;
+
+ while ((family = *addr_family_list++) != 0) {
+
+ /*
+ * IP Version 4
+ */
+ if (family == AF_INET) {
+ count = addr_list->used;
+#if defined(HAVE_GETIFADDRS)
+ ial_getifaddrs(addr_list, mask_list, AF_INET);
+#elif defined (HAS_SIOCGLIF)
+ ial_siocglif(addr_list, mask_list, AF_INET);
+#else
+ ial_siocgif(addr_list, mask_list, AF_INET);
+#endif
+ if (msg_verbose)
+ msg_info("%s: configured %d IPv4 addresses",
+ myname, addr_list->used - count);
+ }
+
+ /*
+ * IP Version 6
+ */
+#ifdef HAS_IPV6
+ else if (family == AF_INET6) {
+ count = addr_list->used;
+#if defined(HAVE_GETIFADDRS)
+ ial_getifaddrs(addr_list, mask_list, AF_INET6);
+#elif defined(HAS_PROCNET_IFINET6)
+ ial_procnet_ifinet6(addr_list, mask_list);
+#elif defined(HAS_SIOCGLIF)
+ ial_siocglif(addr_list, mask_list, AF_INET6);
+#else
+ ial_siocgif(addr_list, mask_list, AF_INET6);
+#endif
+ if (msg_verbose)
+ msg_info("%s: configured %d IPv6 addresses", myname,
+ addr_list->used - count);
+ }
+#endif
+
+ /*
+ * Something's not right.
+ */
+ else
+ msg_panic("%s: unknown address family %d", myname, family);
+ }
+ return (addr_list->used - initial_count);
+}
+
+#ifdef TEST
+
+#include <string.h>
+#include <vstream.h>
+#include <msg_vstream.h>
+#include <inet_proto.h>
+
+int main(int unused_argc, char **argv)
+{
+ INET_ADDR_LIST addr_list;
+ INET_ADDR_LIST mask_list;
+ MAI_HOSTADDR_STR hostaddr;
+ MAI_HOSTADDR_STR hostmask;
+ struct sockaddr *sa;
+ int i;
+ INET_PROTO_INFO *proto_info;
+
+ msg_vstream_init(argv[0], VSTREAM_ERR);
+ msg_verbose = 1;
+
+ proto_info = inet_proto_init(argv[0],
+ argv[1] ? argv[1] : INET_PROTO_NAME_ALL);
+ inet_addr_list_init(&addr_list);
+ inet_addr_list_init(&mask_list);
+ inet_addr_local(&addr_list, &mask_list, proto_info->ai_family_list);
+
+ if (addr_list.used == 0)
+ msg_fatal("cannot find any active network interfaces");
+
+ if (addr_list.used == 1)
+ msg_warn("found only one active network interface");
+
+ for (i = 0; i < addr_list.used; i++) {
+ sa = SOCK_ADDR_PTR(addr_list.addrs + i);
+ SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa),
+ &hostaddr, (MAI_SERVPORT_STR *) 0, 0);
+ sa = SOCK_ADDR_PTR(mask_list.addrs + i);
+ SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa),
+ &hostmask, (MAI_SERVPORT_STR *) 0, 0);
+ vstream_printf("%s/%s\n", hostaddr.buf, hostmask.buf);
+ vstream_fflush(VSTREAM_OUT);
+ }
+ inet_addr_list_free(&addr_list);
+ inet_addr_list_free(&mask_list);
+ return (0);
+}
+
+#endif