From 62c0fedccf95a5c5a2530c91a4d93163f8db8d7a Mon Sep 17 00:00:00 2001 From: Ken Raeburn Date: Wed, 5 May 2004 22:21:35 +0000 Subject: [PATCH] * localaddr.c: Incorporate foreach_localaddr implementation. Export it as krb5int_foreach_localaddr. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@16311 dc483132-0cff-0310-8789-dd5450dbe970 --- src/lib/krb5/os/ChangeLog | 5 + src/lib/krb5/os/localaddr.c | 788 +++++++++++++++++++++++++++++++++++- 2 files changed, 792 insertions(+), 1 deletion(-) diff --git a/src/lib/krb5/os/ChangeLog b/src/lib/krb5/os/ChangeLog index 7b2d330ed..d1532b983 100644 --- a/src/lib/krb5/os/ChangeLog +++ b/src/lib/krb5/os/ChangeLog @@ -1,3 +1,8 @@ +2004-05-05 Ken Raeburn + + * localaddr.c: Incorporate foreach_localaddr implementation. + Export it as krb5int_foreach_localaddr. + 2004-03-22 Ken Raeburn * sendto_kdc.c (krb5int_sendto): Initialize select_state.end_time. diff --git a/src/lib/krb5/os/localaddr.c b/src/lib/krb5/os/localaddr.c index a8b752d15..e89a51ae9 100644 --- a/src/lib/krb5/os/localaddr.c +++ b/src/lib/krb5/os/localaddr.c @@ -1,7 +1,7 @@ /* * lib/krb5/os/localaddr.c * - * Copyright 1990,1991,2000,2001,2002 by the Massachusetts Institute of Technology. + * Copyright 1990,1991,2000,2001,2002,2004 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may @@ -25,6 +25,10 @@ * * * Return the protocol addresses supported by this host. + * Exports from this file: + * krb5int_foreach_localaddr (does callbacks) + * krb5int_local_addresses (includes krb5.conf extra_addresses) + * krb5_os_localaddr (doesn't) * * XNS support is untested, but "Should just work". (Hah!) */ @@ -48,6 +52,788 @@ #include "foreachaddr.c" +/* Note: foreach_localaddr is exported from the library through + krb5int_accessor, for the KDC to use. + + This function iterates over all the addresses it can find for the + local system, in one or two passes. In each pass, and between the + two, it can invoke callback functions supplied by the caller. The + two passes should operate on the same information, though not + necessarily in the same order each time. Duplicate and local + addresses should be eliminated. Storage passed to callback + functions should not be assumed to be valid after foreach_localaddr + returns. + + The int return value is an errno value (XXX or krb5_error_code + returned for a socket error) if something internal to + foreach_localaddr fails. If one of the callback functions wants to + indicate an error, it should store something via the 'data' handle. + If any callback function returns a non-zero value, + foreach_localaddr will clean up and return immediately. + + Multiple definitions are provided below, dependent on various + system facilities for extracting the necessary information. */ + +/* Now, on to the implementations, and heaps of debugging code. */ + +#ifdef TEST +# define Tprintf(X) printf X +# define Tperror(X) perror(X) +#else +# define Tprintf(X) (void) X +# define Tperror(X) (void)(X) +#endif + +/* + * The SIOCGIF* ioctls require a socket. + * It doesn't matter *what* kind of socket they use, but it has to be + * a socket. + * + * Of course, you can't just ask the kernel for a socket of arbitrary + * type; you have to ask for one with a valid type. + * + */ +#ifdef HAVE_NETINET_IN_H +#include +#ifndef USE_AF +#define USE_AF AF_INET +#define USE_TYPE SOCK_DGRAM +#define USE_PROTO 0 +#endif +#endif + +#ifdef KRB5_USE_NS +#include +#ifndef USE_AF +#define USE_AF AF_NS +#define USE_TYPE SOCK_DGRAM +#define USE_PROTO 0 /* guess */ +#endif +#endif +/* + * Add more address families here. + */ + + +#if defined(__linux__) && defined(KRB5_USE_INET6) && !defined(HAVE_IFADDRS_H) +#define LINUX_IPV6_HACK +#endif + +#include + +/* + * Return all the protocol addresses of this host. + * + * We could kludge up something to return all addresses, assuming that + * they're valid kerberos protocol addresses, but we wouldn't know the + * real size of the sockaddr or know which part of it was actually the + * host part. + * + * This uses the SIOCGIFCONF, SIOCGIFFLAGS, and SIOCGIFADDR ioctl's. + */ + +/* + * BSD 4.4 defines the size of an ifreq to be + * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len + * However, under earlier systems, sa_len isn't present, so the size is + * just sizeof(struct ifreq). + */ +#ifdef HAVE_SA_LEN +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif +#define ifreq_size(i) max(sizeof(struct ifreq),\ + sizeof((i).ifr_name)+(i).ifr_addr.sa_len) +#else +#define ifreq_size(i) sizeof(struct ifreq) +#endif /* HAVE_SA_LEN*/ + +#if defined(DEBUG) || defined(TEST) +#include +#include + +#include "socket-utils.h" +#include "fake-addrinfo.h" + +void printaddr (struct sockaddr *); + +void printaddr (struct sockaddr *sa) + /*@modifies fileSystem@*/ +{ + char buf[NI_MAXHOST]; + int err; + + printf ("%p ", (void *) sa); + err = getnameinfo (sa, socklen (sa), buf, sizeof (buf), 0, 0, + NI_NUMERICHOST); + if (err) + printf (" family=%d", + err, gai_strerror (err), + sa->sa_family); + else + printf ("%s", buf); +} +#endif + +#ifdef HAVE_IFADDRS_H +#include + +#ifdef DEBUG +void printifaddr (struct ifaddrs *ifp) +{ + printf ("%p={\n", ifp); +/* printf ("\tnext=%p\n", ifp->ifa_next); */ + printf ("\tname=%s\n", ifp->ifa_name); + printf ("\tflags="); + { + int ch, flags = ifp->ifa_flags; + printf ("%x", flags); + ch = '<'; +#define X(F) if (flags & IFF_##F) { printf ("%c%s", ch, #F); flags &= ~IFF_##F; ch = ','; } + X (UP); X (BROADCAST); X (DEBUG); X (LOOPBACK); X (POINTOPOINT); + X (NOTRAILERS); X (RUNNING); X (NOARP); X (PROMISC); X (ALLMULTI); +#ifdef IFF_OACTIVE + X (OACTIVE); +#endif +#ifdef IFF_SIMPLE + X (SIMPLEX); +#endif + X (MULTICAST); + printf (">"); +#undef X + } + if (ifp->ifa_addr) + printf ("\n\taddr="), printaddr (ifp->ifa_addr); + if (ifp->ifa_netmask) + printf ("\n\tnetmask="), printaddr (ifp->ifa_netmask); + if (ifp->ifa_broadaddr) + printf ("\n\tbroadaddr="), printaddr (ifp->ifa_broadaddr); + if (ifp->ifa_dstaddr) + printf ("\n\tdstaddr="), printaddr (ifp->ifa_dstaddr); + if (ifp->ifa_data) + printf ("\n\tdata=%p", ifp->ifa_data); + printf ("\n}\n"); +} +#endif /* DEBUG */ + +#include +#include + +static int +addr_eq (const struct sockaddr *s1, const struct sockaddr *s2) +{ + if (s1->sa_family != s2->sa_family) + return 0; +#ifdef HAVE_SA_LEN + if (s1->sa_len != s2->sa_len) + return 0; + return !memcmp (s1, s2, s1->sa_len); +#else +#define CMPTYPE(T,F) (!memcmp(&((const T*)s1)->F,&((const T*)s2)->F,sizeof(((const T*)s1)->F))) + switch (s1->sa_family) { + case AF_INET: + return CMPTYPE (struct sockaddr_in, sin_addr); + case AF_INET6: + return CMPTYPE (struct sockaddr_in6, sin6_addr); + default: + /* Err on side of duplicate listings. */ + return 0; + } +#endif +} +#endif + +#ifndef HAVE_IFADDRS_H +/*@-usereleased@*/ /* lclint doesn't understand realloc */ +static /*@null@*/ void * +grow_or_free (/*@only@*/ void *ptr, size_t newsize) + /*@*/ +{ + void *newptr; + newptr = realloc (ptr, newsize); + if (newptr == NULL && newsize != 0) { + free (ptr); /* lclint complains but this is right */ + return NULL; + } + return newptr; +} +/*@=usereleased@*/ + +static int +get_ifconf (int s, size_t *lenp, /*@out@*/ char *buf) + /*@modifies *buf,*lenp@*/ +{ + int ret; + struct ifconf ifc; + + /*@+matchanyintegral@*/ + ifc.ifc_len = *lenp; + /*@=matchanyintegral@*/ + ifc.ifc_buf = buf; + memset(buf, 0, *lenp); + /*@-moduncon@*/ + ret = ioctl (s, SIOCGIFCONF, (char *)&ifc); + /*@=moduncon@*/ + /*@+matchanyintegral@*/ + *lenp = ifc.ifc_len; + /*@=matchanyintegral@*/ + return ret; +} + +#ifdef SIOCGLIFCONF /* Solaris */ +static int +get_lifconf (int af, int s, size_t *lenp, /*@out@*/ char *buf) + /*@modifies *buf,*lenp@*/ +{ + int ret; + struct lifconf lifc; + + lifc.lifc_family = af; + lifc.lifc_flags = 0; + /*@+matchanyintegral@*/ + lifc.lifc_len = *lenp; + /*@=matchanyintegral@*/ + lifc.lifc_buf = buf; + memset(buf, 0, *lenp); + /*@-moduncon@*/ + ret = ioctl (s, SIOCGLIFCONF, (char *)&lifc); + if (ret) + Tperror ("SIOCGLIFCONF"); + /*@=moduncon@*/ + /*@+matchanyintegral@*/ + *lenp = lifc.lifc_len; + /*@=matchanyintegral@*/ + return ret; +} +#endif +#endif /* ! HAVE_IFADDRS_H */ + +#ifdef LINUX_IPV6_HACK +#include +/* Read IPv6 addresses out of /proc/net/if_inet6, since there isn't + (currently) any ioctl to return them. */ +struct linux_ipv6_addr_list { + struct sockaddr_in6 addr; + struct linux_ipv6_addr_list *next; +}; +static struct linux_ipv6_addr_list * +get_linux_ipv6_addrs () +{ + struct linux_ipv6_addr_list *lst = 0; + FILE *f; + + /* _PATH_PROCNET_IFINET6 */ + f = fopen("/proc/net/if_inet6", "r"); + if (f) { + char ifname[21]; + unsigned int idx, pfxlen, scope, dadstat; + struct in6_addr a6; + struct linux_ipv6_addr_list *nw; + int i; + unsigned int addrbyte[16]; + + while (fscanf(f, + "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x" + " %2x %2x %2x %2x %20s\n", + &addrbyte[0], &addrbyte[1], &addrbyte[2], &addrbyte[3], + &addrbyte[4], &addrbyte[5], &addrbyte[6], &addrbyte[7], + &addrbyte[8], &addrbyte[9], &addrbyte[10], &addrbyte[11], + &addrbyte[12], &addrbyte[13], &addrbyte[14], + &addrbyte[15], + &idx, &pfxlen, &scope, &dadstat, ifname) != EOF) { + for (i = 0; i < 16; i++) + a6.s6_addr[i] = addrbyte[i]; + if (scope != 0) + continue; +#if 0 /* These symbol names are as used by ifconfig, but none of the + system header files export them. Dig up the kernel versions + someday and see if they're exported. */ + switch (scope) { + case 0: + default: + break; + case IPV6_ADDR_LINKLOCAL: + case IPV6_ADDR_SITELOCAL: + case IPV6_ADDR_COMPATv4: + case IPV6_ADDR_LOOPBACK: + continue; + } +#endif + nw = malloc (sizeof (struct linux_ipv6_addr_list)); + if (nw == 0) + continue; + memset (nw, 0, sizeof (*nw)); + nw->addr.sin6_addr = a6; + nw->addr.sin6_family = AF_INET6; + /* Ignore other fields, we don't actually use them here. */ + nw->next = lst; + lst = nw; + } + fclose (f); + } + return lst; +} +#endif + +/* Return value is errno if internal stuff failed, otherwise zero, + even in the case where a called function terminated the iteration. + + If one of the callback functions wants to pass back an error + indication, it should do it via some field pointed to by the DATA + argument. */ + +#ifdef HAVE_IFADDRS_H + +int +foreach_localaddr (/*@null@*/ void *data, + int (*pass1fn) (/*@null@*/ void *, struct sockaddr *) /*@*/, + /*@null@*/ int (*betweenfn) (/*@null@*/ void *) /*@*/, + /*@null@*/ int (*pass2fn) (/*@null@*/ void *, + struct sockaddr *) /*@*/) +#if defined(DEBUG) || defined(TEST) + /*@modifies fileSystem@*/ +#endif +{ + struct ifaddrs *ifp_head, *ifp, *ifp2; + int match; + + if (getifaddrs (&ifp_head) < 0) + return errno; + for (ifp = ifp_head; ifp; ifp = ifp->ifa_next) { +#ifdef DEBUG + printifaddr (ifp); +#endif + if ((ifp->ifa_flags & IFF_UP) == 0) + continue; + if (ifp->ifa_flags & IFF_LOOPBACK) { + /* Pretend it's not up, so the second pass will skip + it. */ + ifp->ifa_flags &= ~IFF_UP; + continue; + } + if (ifp->ifa_addr == NULL) { + /* Can't use an interface without an address. Linux + apparently does this sometimes. [RT ticket 1770 from + Maurice Massar, also Debian bug 206851, shows the + problem with a PPP link on a newer kernel than I'm + running.] + + Pretend it's not up, so the second pass will skip + it. */ + ifp->ifa_flags &= ~IFF_UP; + continue; + } + /* If this address is a duplicate, punt. */ + match = 0; + for (ifp2 = ifp_head; ifp2 && ifp2 != ifp; ifp2 = ifp2->ifa_next) { + if ((ifp2->ifa_flags & IFF_UP) == 0) + continue; + if (ifp2->ifa_flags & IFF_LOOPBACK) + continue; + if (addr_eq (ifp->ifa_addr, ifp2->ifa_addr)) { + match = 1; + ifp->ifa_flags &= ~IFF_UP; + break; + } + } + if (match) + continue; + if ((*pass1fn) (data, ifp->ifa_addr)) + goto punt; + } + if (betweenfn && (*betweenfn)(data)) + goto punt; + if (pass2fn) + for (ifp = ifp_head; ifp; ifp = ifp->ifa_next) { + if (ifp->ifa_flags & IFF_UP) + if ((*pass2fn) (data, ifp->ifa_addr)) + goto punt; + } + punt: + freeifaddrs (ifp_head); + return 0; +} + +#elif defined (SIOCGLIFNUM) /* Solaris 8 and later; Sol 7? */ + +int +foreach_localaddr (/*@null@*/ void *data, + int (*pass1fn) (/*@null@*/ void *, struct sockaddr *) /*@*/, + /*@null@*/ int (*betweenfn) (/*@null@*/ void *) /*@*/, + /*@null@*/ int (*pass2fn) (/*@null@*/ void *, + struct sockaddr *) /*@*/) +#if defined(DEBUG) || defined(TEST) + /*@modifies fileSystem@*/ +#endif +{ + /* Okay, this is kind of odd. We have to use each of the address + families we care about, because with an AF_INET socket, extra + interfaces like hme0:1 that have only AF_INET6 addresses will + cause errors. Similarly, if hme0 has more AF_INET addresses + than AF_INET6 addresses, we won't be able to retrieve all of + the AF_INET addresses if we use an AF_INET6 socket. Since + neither family is guaranteed to have the greater number of + addresses, we should use both. + + If it weren't for this little quirk, we could use one socket of + any type, and ask for addresses of all types. At least, it + seems to work that way. */ + + static const int afs[] = { AF_INET, AF_NS, AF_INET6 }; +#define N_AFS (sizeof (afs) / sizeof (afs[0])) + struct { + int af; + int sock; + void *buf; + size_t buf_size; + struct lifnum lifnum; + } afp[N_AFS]; + int code, i, j; + int retval = 0, afidx; + krb5_error_code sock_err = 0; + struct lifreq *lifr, lifreq, *lifr2; + +#define FOREACH_AF() for (afidx = 0; afidx < N_AFS; afidx++) +#define P (afp[afidx]) + + /* init */ + FOREACH_AF () { + P.af = afs[afidx]; + P.sock = -1; + P.buf = 0; + } + + /* first pass: get raw data, discard uninteresting addresses, callback */ + FOREACH_AF () { + Tprintf (("trying af %d...\n", P.af)); + P.sock = socket (P.af, USE_TYPE, USE_PROTO); + if (P.sock < 0) { + sock_err = SOCKET_ERROR; + Tperror ("socket"); + continue; + } + + P.lifnum.lifn_family = P.af; + P.lifnum.lifn_flags = 0; + P.lifnum.lifn_count = 0; + code = ioctl (P.sock, SIOCGLIFNUM, &P.lifnum); + if (code) { + Tperror ("ioctl(SIOCGLIFNUM)"); + retval = errno; + goto punt; + } + + P.buf_size = P.lifnum.lifn_count * sizeof (struct lifreq) * 2; + P.buf = malloc (P.buf_size); + if (P.buf == NULL) { + retval = errno; + goto punt; + } + + code = get_lifconf (P.af, P.sock, &P.buf_size, P.buf); + if (code < 0) { + retval = errno; + goto punt; + } + + for (i = 0; i < P.buf_size; i+= sizeof (*lifr)) { + lifr = (struct lifreq *)((caddr_t) P.buf+i); + + strncpy(lifreq.lifr_name, lifr->lifr_name, + sizeof (lifreq.lifr_name)); + Tprintf (("interface %s\n", lifreq.lifr_name)); + /*@-moduncon@*/ /* ioctl unknown to lclint */ + if (ioctl (P.sock, SIOCGLIFFLAGS, (char *)&lifreq) < 0) { + Tperror ("ioctl(SIOCGLIFFLAGS)"); + skip: + /* mark for next pass */ + lifr->lifr_name[0] = '\0'; + continue; + } + /*@=moduncon@*/ + +#ifdef IFF_LOOPBACK + /* None of the current callers want loopback addresses. */ + if (lifreq.lifr_flags & IFF_LOOPBACK) { + Tprintf ((" loopback\n")); + goto skip; + } +#endif + /* Ignore interfaces that are down. */ + if ((lifreq.lifr_flags & IFF_UP) == 0) { + Tprintf ((" down\n")); + goto skip; + } + + /* Make sure we didn't process this address already. */ + for (j = 0; j < i; j += sizeof (*lifr2)) { + lifr2 = (struct lifreq *)((caddr_t) P.buf+j); + if (lifr2->lifr_name[0] == '\0') + continue; + if (lifr2->lifr_addr.ss_family == lifr->lifr_addr.ss_family + /* Compare address info. If this isn't good enough -- + i.e., if random padding bytes turn out to differ + when the addresses are the same -- then we'll have + to do it on a per address family basis. */ + && !memcmp (&lifr2->lifr_addr, &lifr->lifr_addr, + sizeof (*lifr))) { + Tprintf ((" duplicate addr\n")); + goto skip; + } + } + + /*@-moduncon@*/ + if ((*pass1fn) (data, ss2sa (&lifr->lifr_addr))) + goto punt; + /*@=moduncon@*/ + } + } + + /* Did we actually get any working sockets? */ + FOREACH_AF () + if (P.sock != -1) + goto have_working_socket; + retval = sock_err; + goto punt; +have_working_socket: + + /*@-moduncon@*/ + if (betweenfn != NULL && (*betweenfn)(data)) + goto punt; + /*@=moduncon@*/ + + if (pass2fn) + FOREACH_AF () + if (P.sock >= 0) { + for (i = 0; i < P.buf_size; i+= sizeof (*lifr)) { + lifr = (struct lifreq *)((caddr_t) P.buf+i); + + if (lifr->lifr_name[0] == '\0') + /* Marked in first pass to be ignored. */ + continue; + + /*@-moduncon@*/ + if ((*pass2fn) (data, ss2sa (&lifr->lifr_addr))) + goto punt; + /*@=moduncon@*/ + } + } +punt: + FOREACH_AF () { + /*@-moduncon@*/ + closesocket(P.sock); + /*@=moduncon@*/ + free (P.buf); + } + + return retval; +} + +#else /* not defined (SIOCGLIFNUM) */ + +#define SLOP (sizeof (struct ifreq) + 128) + +int +foreach_localaddr (/*@null@*/ void *data, + int (*pass1fn) (/*@null@*/ void *, struct sockaddr *) /*@*/, + /*@null@*/ int (*betweenfn) (/*@null@*/ void *) /*@*/, + /*@null@*/ int (*pass2fn) (/*@null@*/ void *, + struct sockaddr *) /*@*/) +#if defined(DEBUG) || defined(TEST) + /*@modifies fileSystem@*/ +#endif +{ + struct ifreq *ifr, ifreq, *ifr2; + int s, code; + int est_if_count = 8; + size_t est_ifreq_size; + char *buf = 0; + size_t current_buf_size = 0, size, n, i, j; +#ifdef SIOCGSIZIFCONF + int ifconfsize = -1; +#endif + int retval = 0; +#ifdef SIOCGIFNUM + int numifs = -1; +#endif +#ifdef LINUX_IPV6_HACK + struct linux_ipv6_addr_list *linux_ipv6_addrs = get_linux_ipv6_addrs (); + struct linux_ipv6_addr_list *lx_v6; +#endif + + s = socket (USE_AF, USE_TYPE, USE_PROTO); + if (s < 0) + return SOCKET_ERRNO; + + /* At least on NetBSD, an ifreq can hold an IPv4 address, but + isn't big enough for an IPv6 or ethernet address. So add a + little more space. */ + est_ifreq_size = sizeof (struct ifreq) + 8; +#ifdef SIOCGSIZIFCONF + code = ioctl (s, SIOCGSIZIFCONF, &ifconfsize); + if (!code) { + current_buf_size = ifconfsize; + est_if_count = ifconfsize / est_ifreq_size; + } +#elif defined (SIOCGIFNUM) + code = ioctl (s, SIOCGIFNUM, &numifs); + if (!code && numifs > 0) + est_if_count = numifs; +#endif + if (current_buf_size == 0) + current_buf_size = est_ifreq_size * est_if_count + SLOP; + buf = malloc (current_buf_size); + if (buf == NULL) + return errno; + + ask_again: + size = current_buf_size; + code = get_ifconf (s, &size, buf); + if (code < 0) { + retval = errno; + /*@-moduncon@*/ /* close() unknown to lclint */ + closesocket (s); + /*@=moduncon@*/ + free (buf); + return retval; + } + /* Test that the buffer was big enough that another ifreq could've + fit easily, if the OS wanted to provide one. That seems to be + the only indication we get, complicated by the fact that the + associated address may make the required storage a little + bigger than the size of an ifreq. */ + if (current_buf_size - size < SLOP +#ifdef SIOCGSIZIFCONF + /* Unless we hear SIOCGSIZIFCONF is broken somewhere, let's + trust the value it returns. */ + && ifconfsize <= 0 +#elif defined (SIOCGIFNUM) + && numifs <= 0 +#endif + /* And we need *some* sort of bounds. */ + && current_buf_size <= 100000 + ) { + size_t new_size; + + est_if_count *= 2; + new_size = est_ifreq_size * est_if_count + SLOP; + buf = grow_or_free (buf, new_size); + if (buf == 0) + return errno; + current_buf_size = new_size; + goto ask_again; + } + + n = size; + if (n > current_buf_size) + n = current_buf_size; + + /* Note: Apparently some systems put the size (used or wanted?) + into the start of the buffer, just none that I'm actually + using. Fix this when there's such a test system available. + The Samba mailing list archives mention that NTP looks for the + size on these systems: *-fujitsu-uxp* *-ncr-sysv4* + *-univel-sysv*. */ + for (i = 0; i < n; i+= ifreq_size(*ifr) ) { + ifr = (struct ifreq *)((caddr_t) buf+i); + + strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof (ifreq.ifr_name)); + Tprintf (("interface %s\n", ifreq.ifr_name)); + /*@-moduncon@*/ /* ioctl unknown to lclint */ + if (ioctl (s, SIOCGIFFLAGS, (char *)&ifreq) < 0) { + skip: + /* mark for next pass */ + ifr->ifr_name[0] = '\0'; + continue; + } + /*@=moduncon@*/ + +#ifdef IFF_LOOPBACK + /* None of the current callers want loopback addresses. */ + if (ifreq.ifr_flags & IFF_LOOPBACK) { + Tprintf ((" loopback\n")); + goto skip; + } +#endif + /* Ignore interfaces that are down. */ + if ((ifreq.ifr_flags & IFF_UP) == 0) { + Tprintf ((" down\n")); + goto skip; + } + + /* Make sure we didn't process this address already. */ + for (j = 0; j < i; j += ifreq_size(*ifr2)) { + ifr2 = (struct ifreq *)((caddr_t) buf+j); + if (ifr2->ifr_name[0] == '\0') + continue; + if (ifr2->ifr_addr.sa_family == ifr->ifr_addr.sa_family + && ifreq_size (*ifr) == ifreq_size (*ifr2) + /* Compare address info. If this isn't good enough -- + i.e., if random padding bytes turn out to differ + when the addresses are the same -- then we'll have + to do it on a per address family basis. */ + && !memcmp (&ifr2->ifr_addr.sa_data, &ifr->ifr_addr.sa_data, + (ifreq_size (*ifr) + - offsetof (struct ifreq, ifr_addr.sa_data)))) { + Tprintf ((" duplicate addr\n")); + goto skip; + } + } + + /*@-moduncon@*/ + if ((*pass1fn) (data, &ifr->ifr_addr)) + goto punt; + /*@=moduncon@*/ + } + +#ifdef LINUX_IPV6_HACK + for (lx_v6 = linux_ipv6_addrs; lx_v6; lx_v6 = lx_v6->next) + if ((*pass1fn) (data, (struct sockaddr *) &lx_v6->addr)) + goto punt; +#endif + + /*@-moduncon@*/ + if (betweenfn != NULL && (*betweenfn)(data)) + goto punt; + /*@=moduncon@*/ + + if (pass2fn) { + for (i = 0; i < n; i+= ifreq_size(*ifr) ) { + ifr = (struct ifreq *)((caddr_t) buf+i); + + if (ifr->ifr_name[0] == '\0') + /* Marked in first pass to be ignored. */ + continue; + + /*@-moduncon@*/ + if ((*pass2fn) (data, &ifr->ifr_addr)) + goto punt; + /*@=moduncon@*/ + } +#ifdef LINUX_IPV6_HACK + for (lx_v6 = linux_ipv6_addrs; lx_v6; lx_v6 = lx_v6->next) + if ((*pass2fn) (data, (struct sockaddr *) &lx_v6->addr)) + goto punt; +#endif + } + punt: + /*@-moduncon@*/ + closesocket(s); + /*@=moduncon@*/ + free (buf); +#ifdef LINUX_IPV6_HACK + while (linux_ipv6_addrs) { + lx_v6 = linux_ipv6_addrs->next; + free (linux_ipv6_addrs); + linux_ipv6_addrs = lx_v6; + } +#endif + + return retval; +} + +#endif /* not HAVE_IFADDRS_H and not SIOCGLIFNUM */ + static krb5_error_code get_localaddrs (krb5_context context, krb5_address ***addr, int use_profile); -- 2.26.2