xref: /titanic_51/usr/src/lib/gss_mechs/mech_krb5/krb5/os/localaddr.c (revision 159d09a20817016f09b3ea28d1bdada4a336bb91)
1 /*
2  * lib/krb5/os/localaddr.c
3  *
4  * Copyright 1990,1991,2000,2001,2002,2004 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  *
26  *
27  * Return the protocol addresses supported by this host.
28  * Exports from this file:
29  *   krb5int_foreach_localaddr (does callbacks)
30  *   krb5int_local_addresses (includes krb5.conf extra_addresses)
31  *   krb5_os_localaddr (doesn't)
32  *
33  * XNS support is untested, but "Should just work".  (Hah!)
34  */
35 
36 #include "k5-int.h"
37 
38 #if !defined(_WIN32)
39 
40 /* needed for solaris, harmless elsewhere... */
41 #define BSD_COMP
42 #include <sys/ioctl.h>
43 #include <sys/time.h>
44 #include <errno.h>
45 #include <stddef.h>
46 #include <ctype.h>
47 
48 #if defined(TEST) || defined(DEBUG)
49 # include "fake-addrinfo.h"
50 #endif
51 
52 #include "foreachaddr.h"
53 
54 /* Note: foreach_localaddr is exported from the library through
55    krb5int_accessor, for the KDC to use.
56 
57    This function iterates over all the addresses it can find for the
58    local system, in one or two passes.  In each pass, and between the
59    two, it can invoke callback functions supplied by the caller.  The
60    two passes should operate on the same information, though not
61    necessarily in the same order each time.  Duplicate and local
62    addresses should be eliminated.  Storage passed to callback
63    functions should not be assumed to be valid after foreach_localaddr
64    returns.
65 
66    The int return value is an errno value (XXX or krb5_error_code
67    returned for a socket error) if something internal to
68    foreach_localaddr fails.  If one of the callback functions wants to
69    indicate an error, it should store something via the 'data' handle.
70    If any callback function returns a non-zero value,
71    foreach_localaddr will clean up and return immediately.
72 
73    Multiple definitions are provided below, dependent on various
74    system facilities for extracting the necessary information.  */
75 
76 /* Now, on to the implementations, and heaps of debugging code.  */
77 
78 #ifdef TEST
79 # define Tprintf(X) printf X
80 # define Tperror(X) perror(X)
81 #else
82 # define Tprintf(X) (void) X
83 # define Tperror(X) (void)(X)
84 #endif
85 
86 /*
87  * The SIOCGIF* ioctls require a socket.
88  * It doesn't matter *what* kind of socket they use, but it has to be
89  * a socket.
90  *
91  * Of course, you can't just ask the kernel for a socket of arbitrary
92  * type; you have to ask for one with a valid type.
93  *
94  */
95 #ifdef HAVE_NETINET_IN_H
96 #include <netinet/in.h>
97 #ifndef USE_AF
98 #define USE_AF AF_INET
99 #define USE_TYPE SOCK_DGRAM
100 #define USE_PROTO 0
101 #endif
102 #endif
103 
104 #ifdef KRB5_USE_NS
105 #include <netns/ns.h>
106 #ifndef USE_AF
107 #define USE_AF AF_NS
108 #define USE_TYPE SOCK_DGRAM
109 #define USE_PROTO 0		/* guess */
110 #endif
111 #endif
112 /*
113  * Add more address families here.
114  */
115 
116 
117 #if defined(__linux__) && defined(KRB5_USE_INET6) && !defined(HAVE_IFADDRS_H)
118 #define LINUX_IPV6_HACK
119 #endif
120 
121 #include <errno.h>
122 
123 /*
124  * Return all the protocol addresses of this host.
125  *
126  * We could kludge up something to return all addresses, assuming that
127  * they're valid kerberos protocol addresses, but we wouldn't know the
128  * real size of the sockaddr or know which part of it was actually the
129  * host part.
130  *
131  * This uses the SIOCGIFCONF, SIOCGIFFLAGS, and SIOCGIFADDR ioctl's.
132  */
133 
134 /*
135  * BSD 4.4 defines the size of an ifreq to be
136  * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
137  * However, under earlier systems, sa_len isn't present, so the size is
138  * just sizeof(struct ifreq).
139  */
140 #ifdef HAVE_SA_LEN
141 #ifndef max
142 #define max(a,b) ((a) > (b) ? (a) : (b))
143 #endif
144 #define ifreq_size(i) max(sizeof(struct ifreq),\
145      sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
146 #else
147 #define ifreq_size(i) sizeof(struct ifreq)
148 #endif /* HAVE_SA_LEN*/
149 
150 #if defined(DEBUG) || defined(TEST)
151 #include <netinet/in.h>
152 #include <net/if.h>
153 
154 #include "socket-utils.h"
155 #include "fake-addrinfo.h"
156 
157 void printaddr (struct sockaddr *);
158 
159 void printaddr (struct sockaddr *sa)
160     /*@modifies fileSystem@*/
161 {
162     char buf[NI_MAXHOST];
163     int err;
164 
165     printf ("%p ", (void *) sa);
166     err = getnameinfo (sa, socklen (sa), buf, sizeof (buf), 0, 0,
167 		       NI_NUMERICHOST);
168     if (err)
169 	printf ("<getnameinfo error %d: %s> family=%d",
170 		err, gai_strerror (err),
171 		sa->sa_family);
172     else
173 	printf ("%s", buf);
174 }
175 #endif
176 
177 #ifdef HAVE_IFADDRS_H
178 #include <ifaddrs.h>
179 
180 #ifdef DEBUG
181 void printifaddr (struct ifaddrs *ifp)
182 {
183     printf ("%p={\n", ifp);
184 /*  printf ("\tnext=%p\n", ifp->ifa_next); */
185     printf ("\tname=%s\n", ifp->ifa_name);
186     printf ("\tflags=");
187     {
188 	int ch, flags = ifp->ifa_flags;
189 	printf ("%x", flags);
190 	ch = '<';
191 #define X(F) if (flags & IFF_##F) { printf ("%c%s", ch, #F); flags &= ~IFF_##F; ch = ','; }
192 	X (UP); X (BROADCAST); X (DEBUG); X (LOOPBACK); X (POINTOPOINT);
193 	X (NOTRAILERS); X (RUNNING); X (NOARP); X (PROMISC); X (ALLMULTI);
194 #ifdef IFF_OACTIVE
195 	X (OACTIVE);
196 #endif
197 #ifdef IFF_SIMPLE
198 	X (SIMPLEX);
199 #endif
200 	X (MULTICAST);
201 	printf (">");
202 #undef X
203     }
204     if (ifp->ifa_addr)
205 	printf ("\n\taddr="), printaddr (ifp->ifa_addr);
206     if (ifp->ifa_netmask)
207 	printf ("\n\tnetmask="), printaddr (ifp->ifa_netmask);
208     if (ifp->ifa_broadaddr)
209 	printf ("\n\tbroadaddr="), printaddr (ifp->ifa_broadaddr);
210     if (ifp->ifa_dstaddr)
211 	printf ("\n\tdstaddr="), printaddr (ifp->ifa_dstaddr);
212     if (ifp->ifa_data)
213 	printf ("\n\tdata=%p", ifp->ifa_data);
214     printf ("\n}\n");
215 }
216 #endif /* DEBUG */
217 
218 #include <string.h>
219 #include <stdlib.h>
220 
221 static int
222 addr_eq (const struct sockaddr *s1, const struct sockaddr *s2)
223 {
224     if (s1->sa_family != s2->sa_family)
225 	return 0;
226 #ifdef HAVE_SA_LEN
227     if (s1->sa_len != s2->sa_len)
228 	return 0;
229     return !memcmp (s1, s2, s1->sa_len);
230 #else
231 #define CMPTYPE(T,F) (!memcmp(&((const T*)s1)->F,&((const T*)s2)->F,sizeof(((const T*)s1)->F)))
232     switch (s1->sa_family) {
233     case AF_INET:
234 	return CMPTYPE (struct sockaddr_in, sin_addr);
235     case AF_INET6:
236 	return CMPTYPE (struct sockaddr_in6, sin6_addr);
237     default:
238 	/* Err on side of duplicate listings.  */
239 	return 0;
240     }
241 #endif
242 }
243 #endif
244 
245 #ifndef HAVE_IFADDRS_H
246 /*@-usereleased@*/ /* lclint doesn't understand realloc */
247 static /*@null@*/ void *
248 grow_or_free (/*@only@*/ void *ptr, size_t newsize)
249      /*@*/
250 {
251     void *newptr;
252     newptr = realloc (ptr, newsize);
253     if (newptr == NULL && newsize != 0) {
254 	free (ptr);		/* lclint complains but this is right */
255 	return NULL;
256     }
257     return newptr;
258 }
259 /*@=usereleased@*/
260 
261 static int
262 get_ifconf (int s, size_t *lenp, /*@out@*/ char *buf)
263     /*@modifies *buf,*lenp@*/
264 {
265     int ret;
266     struct ifconf ifc;
267 
268     /*@+matchanyintegral@*/
269     ifc.ifc_len = *lenp;
270     /*@=matchanyintegral@*/
271     ifc.ifc_buf = buf;
272     memset(buf, 0, *lenp);
273     /*@-moduncon@*/
274     ret = ioctl (s, SIOCGIFCONF, (char *)&ifc);
275     /*@=moduncon@*/
276     /*@+matchanyintegral@*/
277     *lenp = ifc.ifc_len;
278     /*@=matchanyintegral@*/
279     return ret;
280 }
281 
282 /* Solaris uses SIOCGLIFCONF to return struct lifconf which is just
283    an extended version of struct ifconf.
284 
285    HP-UX 11 also appears to have SIOCGLIFCONF, but uses struct
286    if_laddrconf, and struct if_laddrreq to be used with
287    SIOCGLIFADDR.  */
288 #if defined(SIOCGLIFCONF) && defined(HAVE_STRUCT_LIFCONF)
289 static int
290 get_lifconf (int af, int s, size_t *lenp, /*@out@*/ char *buf)
291     /*@modifies *buf,*lenp@*/
292 {
293     int ret;
294     struct lifconf lifc;
295 
296     lifc.lifc_family = af;
297     lifc.lifc_flags = 0;
298     /*@+matchanyintegral@*/
299     lifc.lifc_len = *lenp;
300     /*@=matchanyintegral@*/
301     lifc.lifc_buf = buf;
302     memset(buf, 0, *lenp);
303     /*@-moduncon@*/
304     ret = ioctl (s, SIOCGLIFCONF, (char *)&lifc);
305     if (ret)
306 	Tperror ("SIOCGLIFCONF");
307     /*@=moduncon@*/
308     /*@+matchanyintegral@*/
309     *lenp = lifc.lifc_len;
310     /*@=matchanyintegral@*/
311     return ret;
312 }
313 #endif
314 #if defined(SIOCGLIFCONF) && defined(HAVE_STRUCT_IF_LADDRCONF) && 0
315 /* I'm not sure if this is needed or if net/if.h will pull it in.  */
316 /* #include <net/if6.h> */
317 static int
318 get_if_laddrconf (int af, int s, size_t *lenp, /*@out@*/ char *buf)
319     /*@modifies *buf,*lenp@*/
320 {
321     int ret;
322     struct if_laddrconf iflc;
323 
324     /*@+matchanyintegral@*/
325     iflc.iflc_len = *lenp;
326     /*@=matchanyintegral@*/
327     iflc.iflc_buf = buf;
328     memset(buf, 0, *lenp);
329     /*@-moduncon@*/
330     ret = ioctl (s, SIOCGLIFCONF, (char *)&iflc);
331     if (ret)
332 	Tperror ("SIOCGLIFCONF");
333     /*@=moduncon@*/
334     /*@+matchanyintegral@*/
335     *lenp = iflc.iflc_len;
336     /*@=matchanyintegral@*/
337     return ret;
338 }
339 #endif
340 #endif /* ! HAVE_IFADDRS_H */
341 
342 #ifdef LINUX_IPV6_HACK
343 #include <stdio.h>
344 /* Read IPv6 addresses out of /proc/net/if_inet6, since there isn't
345    (currently) any ioctl to return them.  */
346 struct linux_ipv6_addr_list {
347     struct sockaddr_in6 addr;
348     struct linux_ipv6_addr_list *next;
349 };
350 static struct linux_ipv6_addr_list *
351 get_linux_ipv6_addrs ()
352 {
353     struct linux_ipv6_addr_list *lst = 0;
354     FILE *f;
355 
356     /* _PATH_PROCNET_IFINET6 */
357     f = fopen("/proc/net/if_inet6", "r");
358     if (f) {
359 	char ifname[21];
360 	unsigned int idx, pfxlen, scope, dadstat;
361 	struct in6_addr a6;
362 	struct linux_ipv6_addr_list *nw;
363 	int i;
364 	unsigned int addrbyte[16];
365 
366 	while (fscanf(f,
367 		      "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x"
368 		      " %2x %2x %2x %2x %20s\n",
369 		      &addrbyte[0], &addrbyte[1], &addrbyte[2], &addrbyte[3],
370 		      &addrbyte[4], &addrbyte[5], &addrbyte[6], &addrbyte[7],
371 		      &addrbyte[8], &addrbyte[9], &addrbyte[10], &addrbyte[11],
372 		      &addrbyte[12], &addrbyte[13], &addrbyte[14],
373 		      &addrbyte[15],
374 		      &idx, &pfxlen, &scope, &dadstat, ifname) != EOF) {
375 	    for (i = 0; i < 16; i++)
376 		a6.s6_addr[i] = addrbyte[i];
377 	    if (scope != 0)
378 		continue;
379 #if 0 /* These symbol names are as used by ifconfig, but none of the
380 	 system header files export them.  Dig up the kernel versions
381 	 someday and see if they're exported.  */
382 	    switch (scope) {
383 	    case 0:
384 	    default:
385 		break;
386 	    case IPV6_ADDR_LINKLOCAL:
387 	    case IPV6_ADDR_SITELOCAL:
388 	    case IPV6_ADDR_COMPATv4:
389 	    case IPV6_ADDR_LOOPBACK:
390 		continue;
391 	    }
392 #endif
393 	    nw = malloc (sizeof (struct linux_ipv6_addr_list));
394 	    if (nw == 0)
395 		continue;
396 	    memset (nw, 0, sizeof (*nw));
397 	    nw->addr.sin6_addr = a6;
398 	    nw->addr.sin6_family = AF_INET6;
399 	    /* Ignore other fields, we don't actually use them here.  */
400 	    nw->next = lst;
401 	    lst = nw;
402 	}
403 	fclose (f);
404     }
405     return lst;
406 }
407 #endif
408 
409 /* Return value is errno if internal stuff failed, otherwise zero,
410    even in the case where a called function terminated the iteration.
411 
412    If one of the callback functions wants to pass back an error
413    indication, it should do it via some field pointed to by the DATA
414    argument.  */
415 
416 #ifdef HAVE_IFADDRS_H
417 
418 int
419 foreach_localaddr (/*@null@*/ void *data,
420 		   int (*pass1fn) (/*@null@*/ void *, struct sockaddr *) /*@*/,
421 		   /*@null@*/ int (*betweenfn) (/*@null@*/ void *) /*@*/,
422 		   /*@null@*/ int (*pass2fn) (/*@null@*/ void *,
423 					      struct sockaddr *) /*@*/)
424 #if defined(DEBUG) || defined(TEST)
425      /*@modifies fileSystem@*/
426 #endif
427 {
428     struct ifaddrs *ifp_head, *ifp, *ifp2;
429     int match;
430 
431     if (getifaddrs (&ifp_head) < 0)
432 	return errno;
433     for (ifp = ifp_head; ifp; ifp = ifp->ifa_next) {
434 #ifdef DEBUG
435 	printifaddr (ifp);
436 #endif
437 	if ((ifp->ifa_flags & IFF_UP) == 0)
438 	    continue;
439 	if (ifp->ifa_flags & IFF_LOOPBACK) {
440 	    /* Pretend it's not up, so the second pass will skip
441 	       it.  */
442 	    ifp->ifa_flags &= ~IFF_UP;
443 	    continue;
444 	}
445 	if (ifp->ifa_addr == NULL) {
446 	    /* Can't use an interface without an address.  Linux
447 	       apparently does this sometimes.  [RT ticket 1770 from
448 	       Maurice Massar, also Debian bug 206851, shows the
449 	       problem with a PPP link on a newer kernel than I'm
450 	       running.]
451 
452 	       Pretend it's not up, so the second pass will skip
453 	       it.  */
454 	    ifp->ifa_flags &= ~IFF_UP;
455 	    continue;
456 	}
457 	/* If this address is a duplicate, punt.  */
458 	match = 0;
459 	for (ifp2 = ifp_head; ifp2 && ifp2 != ifp; ifp2 = ifp2->ifa_next) {
460 	    if ((ifp2->ifa_flags & IFF_UP) == 0)
461 		continue;
462 	    if (ifp2->ifa_flags & IFF_LOOPBACK)
463 		continue;
464 	    if (addr_eq (ifp->ifa_addr, ifp2->ifa_addr)) {
465 		match = 1;
466 		ifp->ifa_flags &= ~IFF_UP;
467 		break;
468 	    }
469 	}
470 	if (match)
471 	    continue;
472 	if ((*pass1fn) (data, ifp->ifa_addr))
473 	    goto punt;
474     }
475     if (betweenfn && (*betweenfn)(data))
476 	goto punt;
477     if (pass2fn)
478 	for (ifp = ifp_head; ifp; ifp = ifp->ifa_next) {
479 	    if (ifp->ifa_flags & IFF_UP)
480 		if ((*pass2fn) (data, ifp->ifa_addr))
481 		    goto punt;
482 	}
483  punt:
484     freeifaddrs (ifp_head);
485     return 0;
486 }
487 
488 #elif defined (SIOCGLIFNUM) && defined(HAVE_STRUCT_LIFCONF) /* Solaris 8 and later; Sol 7? */
489 
490 int
491 foreach_localaddr (/*@null@*/ void *data,
492 		   int (*pass1fn) (/*@null@*/ void *, struct sockaddr *) /*@*/,
493 		   /*@null@*/ int (*betweenfn) (/*@null@*/ void *) /*@*/,
494 		   /*@null@*/ int (*pass2fn) (/*@null@*/ void *,
495 					      struct sockaddr *) /*@*/)
496 #if defined(DEBUG) || defined(TEST)
497      /*@modifies fileSystem@*/
498 #endif
499 {
500     /* Okay, this is kind of odd.  We have to use each of the address
501        families we care about, because with an AF_INET socket, extra
502        interfaces like hme0:1 that have only AF_INET6 addresses will
503        cause errors.  Similarly, if hme0 has more AF_INET addresses
504        than AF_INET6 addresses, we won't be able to retrieve all of
505        the AF_INET addresses if we use an AF_INET6 socket.  Since
506        neither family is guaranteed to have the greater number of
507        addresses, we should use both.
508 
509        If it weren't for this little quirk, we could use one socket of
510        any type, and ask for addresses of all types.  At least, it
511        seems to work that way.  */
512 
513     static const int afs[] = { AF_INET, AF_NS, AF_INET6 };
514 #define N_AFS (sizeof (afs) / sizeof (afs[0]))
515     struct {
516 	int af;
517 	int sock;
518 	void *buf;
519 	size_t buf_size;
520 	struct lifnum lifnum;
521     } afp[N_AFS];
522     int code, i, j;
523     int retval = 0, afidx;
524     krb5_error_code sock_err = 0;
525     struct lifreq *lifr, lifreq, *lifr2;
526 
527 #define FOREACH_AF() for (afidx = 0; afidx < N_AFS; afidx++)
528 #define P (afp[afidx])
529 
530     /* init */
531     FOREACH_AF () {
532 	P.af = afs[afidx];
533 	P.sock = -1;
534 	P.buf = 0;
535     }
536 
537     /* first pass: get raw data, discard uninteresting addresses, callback */
538     FOREACH_AF () {
539 	Tprintf (("trying af %d...\n", P.af));
540 	P.sock = socket (P.af, USE_TYPE, USE_PROTO);
541 	if (P.sock < 0) {
542 	    sock_err = SOCKET_ERROR;
543 	    Tperror ("socket");
544 	    continue;
545 	}
546 
547 	P.lifnum.lifn_family = P.af;
548 	P.lifnum.lifn_flags = 0;
549 	P.lifnum.lifn_count = 0;
550 	code = ioctl (P.sock, SIOCGLIFNUM, &P.lifnum);
551 	if (code) {
552 	    Tperror ("ioctl(SIOCGLIFNUM)");
553 	    retval = errno;
554 	    goto punt;
555 	}
556 
557 	P.buf_size = P.lifnum.lifn_count * sizeof (struct lifreq) * 2;
558 	P.buf = malloc (P.buf_size);
559 	if (P.buf == NULL) {
560 	    retval = errno;
561 	    goto punt;
562 	}
563 
564 	code = get_lifconf (P.af, P.sock, &P.buf_size, P.buf);
565 	if (code < 0) {
566 	    retval = errno;
567 	    goto punt;
568 	}
569 
570 	for (i = 0; i + sizeof(*lifr) <= P.buf_size; i+= sizeof (*lifr)) {
571 	    lifr = (struct lifreq *)((caddr_t) P.buf+i);
572 
573 	    strncpy(lifreq.lifr_name, lifr->lifr_name,
574 		    sizeof (lifreq.lifr_name));
575 	    Tprintf (("interface %s\n", lifreq.lifr_name));
576 	    /*@-moduncon@*/ /* ioctl unknown to lclint */
577 	    if (ioctl (P.sock, SIOCGLIFFLAGS, (char *)&lifreq) < 0) {
578 		Tperror ("ioctl(SIOCGLIFFLAGS)");
579 	    skip:
580 		/* mark for next pass */
581 		lifr->lifr_name[0] = '\0';
582 		continue;
583 	    }
584 	    /*@=moduncon@*/
585 
586 #ifdef IFF_LOOPBACK
587 	    /* None of the current callers want loopback addresses.  */
588 	    if (lifreq.lifr_flags & IFF_LOOPBACK) {
589 		Tprintf (("  loopback\n"));
590 		goto skip;
591 	    }
592 #endif
593 	    /* Ignore interfaces that are down.  */
594 	    if ((lifreq.lifr_flags & IFF_UP) == 0) {
595 		Tprintf (("  down\n"));
596 		goto skip;
597 	    }
598 
599 	    /* Make sure we didn't process this address already.  */
600 	    for (j = 0; j < i; j += sizeof (*lifr2)) {
601 		lifr2 = (struct lifreq *)((caddr_t) P.buf+j);
602 		if (lifr2->lifr_name[0] == '\0')
603 		    continue;
604 		if (lifr2->lifr_addr.ss_family == lifr->lifr_addr.ss_family
605 		    /* Compare address info.  If this isn't good enough --
606 		       i.e., if random padding bytes turn out to differ
607 		       when the addresses are the same -- then we'll have
608 		       to do it on a per address family basis.  */
609 		    && !memcmp (&lifr2->lifr_addr, &lifr->lifr_addr,
610 				sizeof (*lifr))) {
611 		    Tprintf (("  duplicate addr\n"));
612 		    goto skip;
613 		}
614 	    }
615 
616 	    /*@-moduncon@*/
617 	    if ((*pass1fn) (data, ss2sa (&lifr->lifr_addr)))
618 		goto punt;
619 	    /*@=moduncon@*/
620 	}
621     }
622 
623     /* Did we actually get any working sockets?  */
624     FOREACH_AF ()
625 	if (P.sock != -1)
626 	    goto have_working_socket;
627     retval = sock_err;
628     goto punt;
629 have_working_socket:
630 
631     /*@-moduncon@*/
632     if (betweenfn != NULL && (*betweenfn)(data))
633 	goto punt;
634     /*@=moduncon@*/
635 
636     if (pass2fn)
637 	FOREACH_AF ()
638 	    if (P.sock >= 0) {
639 		for (i = 0; i + sizeof (*lifr) <= P.buf_size; i+= sizeof (*lifr)) {
640 		    lifr = (struct lifreq *)((caddr_t) P.buf+i);
641 
642 		    if (lifr->lifr_name[0] == '\0')
643 			/* Marked in first pass to be ignored.  */
644 			continue;
645 
646 		    /*@-moduncon@*/
647 		    if ((*pass2fn) (data, ss2sa (&lifr->lifr_addr)))
648 			goto punt;
649 		    /*@=moduncon@*/
650 		}
651 	    }
652 punt:
653     FOREACH_AF () {
654 	/*@-moduncon@*/
655 	closesocket(P.sock);
656 	/*@=moduncon@*/
657 	free (P.buf);
658     }
659 
660     return retval;
661 }
662 
663 #elif defined (SIOCGLIFNUM) && defined(HAVE_STRUCT_IF_LADDRCONF) && 0 /* HP-UX 11 support being debugged */
664 
665 int
666 foreach_localaddr (/*@null@*/ void *data,
667 		   int (*pass1fn) (/*@null@*/ void *, struct sockaddr *) /*@*/,
668 		   /*@null@*/ int (*betweenfn) (/*@null@*/ void *) /*@*/,
669 		   /*@null@*/ int (*pass2fn) (/*@null@*/ void *,
670 					      struct sockaddr *) /*@*/)
671 #if defined(DEBUG) || defined(TEST)
672      /*@modifies fileSystem@*/
673 #endif
674 {
675     /* Okay, this is kind of odd.  We have to use each of the address
676        families we care about, because with an AF_INET socket, extra
677        interfaces like hme0:1 that have only AF_INET6 addresses will
678        cause errors.  Similarly, if hme0 has more AF_INET addresses
679        than AF_INET6 addresses, we won't be able to retrieve all of
680        the AF_INET addresses if we use an AF_INET6 socket.  Since
681        neither family is guaranteed to have the greater number of
682        addresses, we should use both.
683 
684        If it weren't for this little quirk, we could use one socket of
685        any type, and ask for addresses of all types.  At least, it
686        seems to work that way.  */
687 
688     static const int afs[] = { AF_INET, AF_NS, AF_INET6 };
689 #define N_AFS (sizeof (afs) / sizeof (afs[0]))
690     struct {
691 	int af;
692 	int sock;
693 	void *buf;
694 	size_t buf_size;
695 	int if_num;
696     } afp[N_AFS];
697     int code, i, j;
698     int retval = 0, afidx;
699     krb5_error_code sock_err = 0;
700     struct if_laddrreq *lifr, lifreq, *lifr2;
701 
702 #define FOREACH_AF() for (afidx = 0; afidx < N_AFS; afidx++)
703 #define P (afp[afidx])
704 
705     /* init */
706     FOREACH_AF () {
707 	P.af = afs[afidx];
708 	P.sock = -1;
709 	P.buf = 0;
710     }
711 
712     /* first pass: get raw data, discard uninteresting addresses, callback */
713     FOREACH_AF () {
714 	Tprintf (("trying af %d...\n", P.af));
715 	P.sock = socket (P.af, USE_TYPE, USE_PROTO);
716 	if (P.sock < 0) {
717 	    sock_err = SOCKET_ERROR;
718 	    Tperror ("socket");
719 	    continue;
720 	}
721 
722 	code = ioctl (P.sock, SIOCGLIFNUM, &P.if_num);
723 	if (code) {
724 	    Tperror ("ioctl(SIOCGLIFNUM)");
725 	    retval = errno;
726 	    goto punt;
727 	}
728 
729 	P.buf_size = P.if_num * sizeof (struct if_laddrreq) * 2;
730 	P.buf = malloc (P.buf_size);
731 	if (P.buf == NULL) {
732 	    retval = errno;
733 	    goto punt;
734 	}
735 
736 	code = get_if_laddrconf (P.af, P.sock, &P.buf_size, P.buf);
737 	if (code < 0) {
738 	    retval = errno;
739 	    goto punt;
740 	}
741 
742 	for (i = 0; i + sizeof(*lifr) <= P.buf_size; i+= sizeof (*lifr)) {
743 	    lifr = (struct if_laddrreq *)((caddr_t) P.buf+i);
744 
745 	    strncpy(lifreq.iflr_name, lifr->iflr_name,
746 		    sizeof (lifreq.iflr_name));
747 	    Tprintf (("interface %s\n", lifreq.iflr_name));
748 	    /*@-moduncon@*/ /* ioctl unknown to lclint */
749 	    if (ioctl (P.sock, SIOCGLIFFLAGS, (char *)&lifreq) < 0) {
750 		Tperror ("ioctl(SIOCGLIFFLAGS)");
751 	    skip:
752 		/* mark for next pass */
753 		lifr->iflr_name[0] = '\0';
754 		continue;
755 	    }
756 	    /*@=moduncon@*/
757 
758 #ifdef IFF_LOOPBACK
759 	    /* None of the current callers want loopback addresses.  */
760 	    if (lifreq.iflr_flags & IFF_LOOPBACK) {
761 		Tprintf (("  loopback\n"));
762 		goto skip;
763 	    }
764 #endif
765 	    /* Ignore interfaces that are down.  */
766 	    if ((lifreq.iflr_flags & IFF_UP) == 0) {
767 		Tprintf (("  down\n"));
768 		goto skip;
769 	    }
770 
771 	    /* Make sure we didn't process this address already.  */
772 	    for (j = 0; j < i; j += sizeof (*lifr2)) {
773 		lifr2 = (struct if_laddrreq *)((caddr_t) P.buf+j);
774 		if (lifr2->iflr_name[0] == '\0')
775 		    continue;
776 		if (lifr2->iflr_addr.sa_family == lifr->iflr_addr.sa_family
777 		    /* Compare address info.  If this isn't good enough --
778 		       i.e., if random padding bytes turn out to differ
779 		       when the addresses are the same -- then we'll have
780 		       to do it on a per address family basis.  */
781 		    && !memcmp (&lifr2->iflr_addr, &lifr->iflr_addr,
782 				sizeof (*lifr))) {
783 		    Tprintf (("  duplicate addr\n"));
784 		    goto skip;
785 		}
786 	    }
787 
788 	    /*@-moduncon@*/
789 	    if ((*pass1fn) (data, ss2sa (&lifr->iflr_addr)))
790 		goto punt;
791 	    /*@=moduncon@*/
792 	}
793     }
794 
795     /* Did we actually get any working sockets?  */
796     FOREACH_AF ()
797 	if (P.sock != -1)
798 	    goto have_working_socket;
799     retval = sock_err;
800     goto punt;
801 have_working_socket:
802 
803     /*@-moduncon@*/
804     if (betweenfn != NULL && (*betweenfn)(data))
805 	goto punt;
806     /*@=moduncon@*/
807 
808     if (pass2fn)
809 	FOREACH_AF ()
810 	    if (P.sock >= 0) {
811 		for (i = 0; i + sizeof(*lifr) <= P.buf_size; i+= sizeof (*lifr)) {
812 		    lifr = (struct if_laddrreq *)((caddr_t) P.buf+i);
813 
814 		    if (lifr->iflr_name[0] == '\0')
815 			/* Marked in first pass to be ignored.  */
816 			continue;
817 
818 		    /*@-moduncon@*/
819 		    if ((*pass2fn) (data, ss2sa (&lifr->iflr_addr)))
820 			goto punt;
821 		    /*@=moduncon@*/
822 		}
823 	    }
824 punt:
825     FOREACH_AF () {
826 	/*@-moduncon@*/
827 	closesocket(P.sock);
828 	/*@=moduncon@*/
829 	free (P.buf);
830     }
831 
832     return retval;
833 }
834 
835 #else /* not defined (SIOCGLIFNUM) */
836 
837 #define SLOP (sizeof (struct ifreq) + 128)
838 
839 static int
840 get_ifreq_array(char **bufp, size_t *np, int s)
841 {
842     int code;
843     int est_if_count = 8;
844     size_t est_ifreq_size;
845     char *buf = 0;
846     size_t current_buf_size = 0, size, n;
847 #ifdef SIOCGSIZIFCONF
848     int ifconfsize = -1;
849 #endif
850 #ifdef SIOCGIFNUM
851     int numifs = -1;
852 #endif
853 
854     /* At least on NetBSD, an ifreq can hold an IPv4 address, but
855        isn't big enough for an IPv6 or ethernet address.  So add a
856        little more space.  */
857     est_ifreq_size = sizeof (struct ifreq) + 8;
858 #ifdef SIOCGSIZIFCONF
859     code = ioctl (s, SIOCGSIZIFCONF, &ifconfsize);
860     if (!code) {
861 	current_buf_size = ifconfsize;
862 	est_if_count = ifconfsize / est_ifreq_size;
863     }
864 #elif defined (SIOCGIFNUM)
865     code = ioctl (s, SIOCGIFNUM, &numifs);
866     if (!code && numifs > 0)
867 	est_if_count = numifs;
868 #endif
869     if (current_buf_size == 0)
870 	current_buf_size = est_ifreq_size * est_if_count + SLOP;
871     buf = malloc (current_buf_size);
872     if (buf == NULL)
873 	return errno;
874 
875 ask_again:
876     size = current_buf_size;
877     code = get_ifconf (s, &size, buf);
878     if (code < 0) {
879 	code = errno;
880 	free (buf);
881 	return code;
882     }
883     /* Test that the buffer was big enough that another ifreq could've
884        fit easily, if the OS wanted to provide one.  That seems to be
885        the only indication we get, complicated by the fact that the
886        associated address may make the required storage a little
887        bigger than the size of an ifreq.  */
888     if (current_buf_size - size < SLOP
889 #ifdef SIOCGSIZIFCONF
890 	/* Unless we hear SIOCGSIZIFCONF is broken somewhere, let's
891 	   trust the value it returns.  */
892 	&& ifconfsize <= 0
893 #elif defined (SIOCGIFNUM)
894 	&& numifs <= 0
895 #endif
896 	/* And we need *some* sort of bounds.  */
897 	&& current_buf_size <= 100000
898 	) {
899 	size_t new_size;
900 
901 	est_if_count *= 2;
902 	new_size = est_ifreq_size * est_if_count + SLOP;
903 	buf = grow_or_free (buf, new_size);
904 	if (buf == 0)
905 	    return errno;
906 	current_buf_size = new_size;
907 	goto ask_again;
908     }
909 
910     n = size;
911     if (n > current_buf_size)
912 	n = current_buf_size;
913 
914     *bufp = buf;
915     *np = n;
916     return 0;
917 }
918 
919 int
920 foreach_localaddr (/*@null@*/ void *data,
921 		   int (*pass1fn) (/*@null@*/ void *, struct sockaddr *) /*@*/,
922 		   /*@null@*/ int (*betweenfn) (/*@null@*/ void *) /*@*/,
923 		   /*@null@*/ int (*pass2fn) (/*@null@*/ void *,
924 					      struct sockaddr *) /*@*/)
925 #if defined(DEBUG) || defined(TEST)
926      /*@modifies fileSystem@*/
927 #endif
928 {
929     struct ifreq *ifr, ifreq, *ifr2;
930     int s, code;
931     char *buf = 0;
932     size_t size, n, i, j;
933     int retval = 0;
934 #ifdef LINUX_IPV6_HACK
935     struct linux_ipv6_addr_list *linux_ipv6_addrs = get_linux_ipv6_addrs ();
936     struct linux_ipv6_addr_list *lx_v6;
937 #endif
938 
939     s = socket (USE_AF, USE_TYPE, USE_PROTO);
940     if (s < 0)
941 	return SOCKET_ERRNO;
942 
943     retval = get_ifreq_array(&buf, &n, s);
944     if (retval) {
945 	/*@-moduncon@*/ /* close() unknown to lclint */
946 	closesocket(s);
947 	/*@=moduncon@*/
948 	return retval;
949     }
950 
951     /* Note: Apparently some systems put the size (used or wanted?)
952        into the start of the buffer, just none that I'm actually
953        using.  Fix this when there's such a test system available.
954        The Samba mailing list archives mention that NTP looks for the
955        size on these systems: *-fujitsu-uxp* *-ncr-sysv4*
956        *-univel-sysv*.  */
957     for (i = 0; i + sizeof(struct ifreq) <= n; i+= ifreq_size(*ifr) ) {
958 	ifr = (struct ifreq *)((caddr_t) buf+i);
959 	/* In case ifreq_size is more than sizeof().  */
960 	if (i + ifreq_size(*ifr) > n)
961 	  break;
962 
963 	strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof (ifreq.ifr_name));
964 	Tprintf (("interface %s\n", ifreq.ifr_name));
965 	/*@-moduncon@*/ /* ioctl unknown to lclint */
966 	if (ioctl (s, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
967 	skip:
968 	    /* mark for next pass */
969 	    ifr->ifr_name[0] = '\0';
970 	    continue;
971 	}
972 	/*@=moduncon@*/
973 
974 #ifdef IFF_LOOPBACK
975 	/* None of the current callers want loopback addresses.  */
976 	if (ifreq.ifr_flags & IFF_LOOPBACK) {
977 	    Tprintf (("  loopback\n"));
978 	    goto skip;
979 	}
980 #endif
981 	/* Ignore interfaces that are down.  */
982 	if ((ifreq.ifr_flags & IFF_UP) == 0) {
983 	    Tprintf (("  down\n"));
984 	    goto skip;
985 	}
986 
987 	/* Make sure we didn't process this address already.  */
988 	for (j = 0; j < i; j += ifreq_size(*ifr2)) {
989 	    ifr2 = (struct ifreq *)((caddr_t) buf+j);
990 	    if (ifr2->ifr_name[0] == '\0')
991 		continue;
992 	    if (ifr2->ifr_addr.sa_family == ifr->ifr_addr.sa_family
993 		&& ifreq_size (*ifr) == ifreq_size (*ifr2)
994 		/* Compare address info.  If this isn't good enough --
995 		   i.e., if random padding bytes turn out to differ
996 		   when the addresses are the same -- then we'll have
997 		   to do it on a per address family basis.  */
998 		&& !memcmp (&ifr2->ifr_addr.sa_data, &ifr->ifr_addr.sa_data,
999 			    (ifreq_size (*ifr)
1000 			     - offsetof (struct ifreq, ifr_addr.sa_data)))) {
1001 		Tprintf (("  duplicate addr\n"));
1002 		goto skip;
1003 	    }
1004 	}
1005 
1006 	/*@-moduncon@*/
1007 	if ((*pass1fn) (data, &ifr->ifr_addr))
1008 	    goto punt;
1009 	/*@=moduncon@*/
1010     }
1011 
1012 #ifdef LINUX_IPV6_HACK
1013     for (lx_v6 = linux_ipv6_addrs; lx_v6; lx_v6 = lx_v6->next)
1014 	if ((*pass1fn) (data, (struct sockaddr *) &lx_v6->addr))
1015 	    goto punt;
1016 #endif
1017 
1018     /*@-moduncon@*/
1019     if (betweenfn != NULL && (*betweenfn)(data))
1020 	goto punt;
1021     /*@=moduncon@*/
1022 
1023     if (pass2fn) {
1024 	for (i = 0; i + sizeof(struct ifreq) <= n; i+= ifreq_size(*ifr) ) {
1025 	    ifr = (struct ifreq *)((caddr_t) buf+i);
1026 
1027 	    if (ifr->ifr_name[0] == '\0')
1028 		/* Marked in first pass to be ignored.  */
1029 		continue;
1030 
1031 	    /*@-moduncon@*/
1032 	    if ((*pass2fn) (data, &ifr->ifr_addr))
1033 		goto punt;
1034 	    /*@=moduncon@*/
1035 	}
1036 #ifdef LINUX_IPV6_HACK
1037 	for (lx_v6 = linux_ipv6_addrs; lx_v6; lx_v6 = lx_v6->next)
1038 	    if ((*pass2fn) (data, (struct sockaddr *) &lx_v6->addr))
1039 		goto punt;
1040 #endif
1041     }
1042  punt:
1043     /*@-moduncon@*/
1044     closesocket(s);
1045     /*@=moduncon@*/
1046     free (buf);
1047 #ifdef LINUX_IPV6_HACK
1048     while (linux_ipv6_addrs) {
1049 	lx_v6 = linux_ipv6_addrs->next;
1050 	free (linux_ipv6_addrs);
1051 	linux_ipv6_addrs = lx_v6;
1052     }
1053 #endif
1054 
1055     return retval;
1056 }
1057 
1058 #endif /* not HAVE_IFADDRS_H and not SIOCGLIFNUM */
1059 
1060 static krb5_error_code
1061 get_localaddrs (krb5_context context, krb5_address ***addr, int use_profile);
1062 
1063 #ifdef TEST
1064 
1065 static int print_addr (/*@unused@*/ void *dataptr, struct sockaddr *sa)
1066      /*@modifies fileSystem@*/
1067 {
1068     char hostbuf[NI_MAXHOST];
1069     int err;
1070     socklen_t len;
1071 
1072     printf ("  --> family %2d ", sa->sa_family);
1073     len = socklen (sa);
1074     err = getnameinfo (sa, len, hostbuf, (socklen_t) sizeof (hostbuf),
1075 		       (char *) NULL, 0, NI_NUMERICHOST);
1076     if (err) {
1077 	int e = errno;
1078 	printf ("<getnameinfo error %d: %s>\n", err, gai_strerror (err));
1079 	if (err == EAI_SYSTEM)
1080 	    printf ("\t\t<errno is %d: %s>\n", e, strerror(e));
1081     } else
1082 	printf ("addr %s\n", hostbuf);
1083     return 0;
1084 }
1085 
1086 int main ()
1087 {
1088     int r;
1089 
1090     (void) setvbuf (stdout, (char *)NULL, _IONBF, 0);
1091     r = foreach_localaddr (0, print_addr, NULL, NULL);
1092     printf ("return value = %d\n", r);
1093     return 0;
1094 }
1095 
1096 #else /* not TESTing */
1097 
1098 struct localaddr_data {
1099     int count, mem_err, cur_idx, cur_size;
1100     krb5_address **addr_temp;
1101 };
1102 
1103 static int
1104 count_addrs (void *P_data, struct sockaddr *a)
1105      /*@*/
1106 {
1107     struct localaddr_data *data = P_data;
1108     switch (a->sa_family) {
1109     case AF_INET:
1110 #ifdef KRB5_USE_INET6
1111     case AF_INET6:
1112 #endif
1113 #ifdef KRB5_USE_NS
1114     case AF_XNS:
1115 #endif
1116 	data->count++;
1117 	break;
1118     default:
1119 	break;
1120     }
1121     return 0;
1122 }
1123 
1124 static int
1125 allocate (void *P_data)
1126      /*@*/
1127 {
1128     struct localaddr_data *data = P_data;
1129     int i;
1130     void *n;
1131 
1132     n = realloc (data->addr_temp,
1133 		 (1 + data->count + data->cur_idx) * sizeof (krb5_address *));
1134     if (n == 0) {
1135 	data->mem_err++;
1136 	return 1;
1137     }
1138     data->addr_temp = n;
1139     data->cur_size = 1 + data->count + data->cur_idx;
1140     for (i = data->cur_idx; i <= data->count + data->cur_idx; i++)
1141 	data->addr_temp[i] = 0;
1142     return 0;
1143 }
1144 
1145 static /*@null@*/ krb5_address *
1146 make_addr (int type, size_t length, const void *contents)
1147     /*@*/
1148 {
1149     krb5_address *a;
1150     void *data;
1151 
1152     data = malloc (length);
1153     if (data == NULL)
1154 	return NULL;
1155     a = malloc (sizeof (krb5_address));
1156     if (a == NULL) {
1157 	free (data);
1158 	return NULL;
1159     }
1160     memcpy (data, contents, length);
1161     a->magic = KV5M_ADDRESS;
1162     a->addrtype = type;
1163     a->length = length;
1164     a->contents = data;
1165     return a;
1166 }
1167 
1168 static int
1169 add_addr (void *P_data, struct sockaddr *a)
1170      /*@modifies *P_data@*/
1171 {
1172     struct localaddr_data *data = P_data;
1173     /*@null@*/ krb5_address *address = 0;
1174 
1175     switch (a->sa_family) {
1176 #ifdef HAVE_NETINET_IN_H
1177     case AF_INET:
1178 	address = make_addr (ADDRTYPE_INET, sizeof (struct in_addr),
1179 			     &((const struct sockaddr_in *) a)->sin_addr);
1180 	if (address == NULL)
1181 	    data->mem_err++;
1182 	break;
1183 
1184 #ifdef KRB5_USE_INET6
1185     case AF_INET6:
1186     {
1187 	const struct sockaddr_in6 *in = (const struct sockaddr_in6 *) a;
1188 
1189 	if (IN6_IS_ADDR_LINKLOCAL (&in->sin6_addr))
1190 	    break;
1191 
1192 	address = make_addr (ADDRTYPE_INET6, sizeof (struct in6_addr),
1193 			     &in->sin6_addr);
1194 	if (address == NULL)
1195 	    data->mem_err++;
1196 	break;
1197     }
1198 #endif /* KRB5_USE_INET6 */
1199 #endif /* netinet/in.h */
1200 
1201 #ifdef KRB5_USE_NS
1202     case AF_XNS:
1203 	address = make_addr (ADDRTYPE_XNS, sizeof (struct ns_addr),
1204 			     &((const struct sockaddr_ns *)a)->sns_addr);
1205 	if (address == NULL)
1206 	    data->mem_err++;
1207 	break;
1208 #endif
1209 
1210 #ifdef AF_LINK
1211 	/* Some BSD-based systems (e.g. NetBSD 1.5) and AIX will
1212 	   include the ethernet address, but we don't want that, at
1213 	   least for now.  */
1214     case AF_LINK:
1215 	break;
1216 #endif
1217     /*
1218      * Add more address families here..
1219      */
1220     default:
1221 	break;
1222     }
1223 #ifdef __LCLINT__
1224     /* Redundant but unconditional store un-confuses lclint.  */
1225     data->addr_temp[data->cur_idx] = address;
1226 #endif
1227     if (address) {
1228 	data->addr_temp[data->cur_idx++] = address;
1229     }
1230 
1231     return data->mem_err;
1232 }
1233 
1234 static krb5_error_code
1235 krb5_os_localaddr_profile (krb5_context context, struct localaddr_data *datap)
1236 {
1237     krb5_error_code err;
1238     static const char *const profile_name[] = {
1239 	"libdefaults", "extra_addresses", 0
1240     };
1241     char **values;
1242     char **iter;
1243     krb5_address **newaddrs;
1244 
1245 #ifdef DEBUG
1246     fprintf (stderr, "looking up extra_addresses foo\n");
1247 #endif
1248 
1249     err = profile_get_values (context->profile, profile_name, &values);
1250     /* Ignore all errors for now?  */
1251     if (err)
1252 	return 0;
1253 
1254     for (iter = values; *iter; iter++) {
1255 	char *cp = *iter, *next, *current;
1256 	int i, count;
1257 
1258 #ifdef DEBUG
1259 	fprintf (stderr, "  found line: '%s'\n", cp);
1260 #endif
1261 
1262 	for (cp = *iter, next = 0; *cp; cp = next) {
1263 	    while (isspace ((int) *cp) || *cp == ',')
1264 		cp++;
1265 	    if (*cp == 0)
1266 		break;
1267 	    /* Start of an address.  */
1268 #ifdef DEBUG
1269 	    fprintf (stderr, "    addr found in '%s'\n", cp);
1270 #endif
1271 	    current = cp;
1272 	    while (*cp != 0 && !isspace((int) *cp) && *cp != ',')
1273 		cp++;
1274 	    if (*cp != 0) {
1275 		next = cp + 1;
1276 		*cp = 0;
1277 	    } else
1278 		next = cp;
1279 	    /* Got a single address, process it.  */
1280 #ifdef DEBUG
1281 	    fprintf (stderr, "    processing '%s'\n", current);
1282 #endif
1283 	    newaddrs = 0;
1284 	    err = krb5_os_hostaddr (context, current, &newaddrs);
1285 	    if (err)
1286 		continue;
1287 	    for (i = 0; newaddrs[i]; i++) {
1288 #ifdef DEBUG
1289 		fprintf (stderr, "    %d: family %d", i,
1290 			 newaddrs[i]->addrtype);
1291 		fprintf (stderr, "\n");
1292 #endif
1293 	    }
1294 	    count = i;
1295 #ifdef DEBUG
1296 	    fprintf (stderr, "    %d addresses\n", count);
1297 #endif
1298 	    if (datap->cur_idx + count >= datap->cur_size) {
1299 		krb5_address **bigger;
1300 		bigger = realloc (datap->addr_temp,
1301 				  sizeof (krb5_address *) * (datap->cur_idx + count));
1302 		if (bigger) {
1303 		    datap->addr_temp = bigger;
1304 		    datap->cur_size = datap->cur_idx + count;
1305 		}
1306 	    }
1307 	    for (i = 0; i < count; i++) {
1308 		if (datap->cur_idx < datap->cur_size)
1309 		    datap->addr_temp[datap->cur_idx++] = newaddrs[i];
1310 		else
1311 		    free (newaddrs[i]->contents), free (newaddrs[i]);
1312 	    }
1313 	    free (newaddrs);
1314 	}
1315     }
1316     return 0;
1317 }
1318 
1319 krb5_error_code KRB5_CALLCONV
1320 krb5_os_localaddr(krb5_context context, krb5_address ***addr)
1321 {
1322     return get_localaddrs(context, addr, 1);
1323 }
1324 
1325 krb5_error_code
1326 krb5int_local_addresses(krb5_context context, krb5_address ***addr)
1327 {
1328     return get_localaddrs(context, addr, 0);
1329 }
1330 
1331 static krb5_error_code
1332 get_localaddrs (krb5_context context, krb5_address ***addr, int use_profile)
1333 {
1334     struct localaddr_data data = { 0 };
1335     int r;
1336     krb5_error_code err;
1337 
1338     if (use_profile) {
1339 	err = krb5_os_localaddr_profile (context, &data);
1340 	/* ignore err for now */
1341     }
1342 
1343     r = foreach_localaddr (&data, count_addrs, allocate, add_addr);
1344     if (r != 0) {
1345 	int i;
1346 	if (data.addr_temp) {
1347 	    for (i = 0; i < data.count; i++)
1348 		krb5_xfree (data.addr_temp[i]);
1349 	    free (data.addr_temp);
1350 	}
1351 	if (data.mem_err)
1352 	    return ENOMEM;
1353 	else
1354 	    return r;
1355     }
1356 
1357     data.cur_idx++; /* null termination */
1358     if (data.mem_err)
1359 	return ENOMEM;
1360     else if (data.cur_idx == data.count)
1361 	*addr = data.addr_temp;
1362     else {
1363 	/* This can easily happen if we have IPv6 link-local
1364 	   addresses.  Just shorten the array.  */
1365 	*addr = (krb5_address **) realloc (data.addr_temp,
1366 					   (sizeof (krb5_address *)
1367 					    * data.cur_idx));
1368 	if (*addr == 0)
1369 	    /* Okay, shortening failed, but the original should still
1370 	       be intact.  */
1371 	    *addr = data.addr_temp;
1372     }
1373 
1374 #ifdef DEBUG
1375     {
1376 	int j;
1377 	fprintf (stderr, "addresses:\n");
1378 	for (j = 0; addr[0][j]; j++) {
1379 	    struct sockaddr_storage ss;
1380 	    int err2;
1381 	    char namebuf[NI_MAXHOST];
1382 	    void *addrp = 0;
1383 
1384 	    fprintf (stderr, "%2d: ", j);
1385 	    fprintf (stderr, "addrtype %2d, length %2d", addr[0][j]->addrtype,
1386 		     addr[0][j]->length);
1387 	    memset (&ss, 0, sizeof (ss));
1388 	    switch (addr[0][j]->addrtype) {
1389 	    case ADDRTYPE_INET:
1390 	    {
1391 		struct sockaddr_in *sinp = ss2sin (&ss);
1392 		sinp->sin_family = AF_INET;
1393 		addrp = &sinp->sin_addr;
1394 #ifdef HAVE_SA_LEN
1395 		sinp->sin_len = sizeof (struct sockaddr_in);
1396 #endif
1397 		break;
1398 	    }
1399 #ifdef KRB5_USE_INET6
1400 	    case ADDRTYPE_INET6:
1401 	    {
1402 		struct sockaddr_in6 *sin6p = ss2sin6 (&ss);
1403 		sin6p->sin6_family = AF_INET6;
1404 		addrp = &sin6p->sin6_addr;
1405 #ifdef HAVE_SA_LEN
1406 		sin6p->sin6_len = sizeof (struct sockaddr_in6);
1407 #endif
1408 		break;
1409 	    }
1410 #endif
1411 	    default:
1412 		ss2sa(&ss)->sa_family = 0;
1413 		break;
1414 	    }
1415 	    if (addrp)
1416 		memcpy (addrp, addr[0][j]->contents, addr[0][j]->length);
1417 	    err2 = getnameinfo (ss2sa(&ss), socklen (ss2sa (&ss)),
1418 				namebuf, sizeof (namebuf), 0, 0,
1419 				NI_NUMERICHOST);
1420 	    if (err2 == 0)
1421 		fprintf (stderr, ": addr %s\n", namebuf);
1422 	    else
1423 		fprintf (stderr, ": getnameinfo error %d\n", err2);
1424 	}
1425     }
1426 #endif
1427 
1428     return 0;
1429 }
1430 
1431 #endif /* not TESTing */
1432 
1433 #else /* Windows/Mac version */
1434 
1435 /*
1436  * Hold on to your lunch!  Backup kludge method of obtaining your
1437  * local IP address, courtesy of Windows Socket Network Programming,
1438  * by Robert Quinn
1439  */
1440 #if defined(_WIN32)
1441 static struct hostent *local_addr_fallback_kludge()
1442 {
1443 	static struct hostent	host;
1444 	static SOCKADDR_IN	addr;
1445 	static char *		ip_ptrs[2];
1446 	SOCKET			sock;
1447 	int			size = sizeof(SOCKADDR);
1448 	int			err;
1449 
1450 	sock = socket(AF_INET, SOCK_DGRAM, 0);
1451 	if (sock == INVALID_SOCKET)
1452 		return NULL;
1453 
1454 	/* connect to arbitrary port and address (NOT loopback) */
1455 	addr.sin_family = AF_INET;
1456 	addr.sin_port = htons(IPPORT_ECHO);
1457 	addr.sin_addr.s_addr = inet_addr("204.137.220.51");
1458 
1459 	err = connect(sock, (LPSOCKADDR) &addr, sizeof(SOCKADDR));
1460 	if (err == SOCKET_ERROR)
1461 		return NULL;
1462 
1463 	err = getsockname(sock, (LPSOCKADDR) &addr, (int *) size);
1464 	if (err == SOCKET_ERROR)
1465 		return NULL;
1466 
1467 	closesocket(sock);
1468 
1469 	host.h_name = 0;
1470 	host.h_aliases = 0;
1471 	host.h_addrtype = AF_INET;
1472 	host.h_length = 4;
1473 	host.h_addr_list = ip_ptrs;
1474 	ip_ptrs[0] = (char *) &addr.sin_addr.s_addr;
1475 	ip_ptrs[1] = NULL;
1476 
1477 	return &host;
1478 }
1479 #endif
1480 
1481 /* No ioctls in winsock so we just assume there is only one networking
1482  * card per machine, so gethostent is good enough.
1483  */
1484 krb5_error_code KRB5_CALLCONV
1485 krb5_os_localaddr (krb5_context context, krb5_address ***addr) {
1486     char host[64];                              /* Name of local machine */
1487     struct hostent *hostrec;
1488     int err, count, i;
1489     krb5_address ** paddr;
1490 
1491     *addr = 0;
1492     paddr = 0;
1493     err = 0;
1494 
1495     if (gethostname (host, sizeof(host))) {
1496         err = SOCKET_ERRNO;
1497     }
1498 
1499     if (!err) {
1500 	    hostrec = gethostbyname (host);
1501 	    if (hostrec == NULL) {
1502 		    err = SOCKET_ERRNO;
1503 	    }
1504     }
1505 
1506     if (err) {
1507 	    hostrec = local_addr_fallback_kludge();
1508 	    if (!hostrec)
1509 		    return err;
1510 		else
1511 			err = 0;  /* otherwise we will die at cleanup */
1512     }
1513 
1514     for (count = 0; hostrec->h_addr_list[count]; count++);
1515 
1516 
1517     paddr = (krb5_address **)malloc(sizeof(krb5_address *) * (count+1));
1518     if (!paddr) {
1519         err = ENOMEM;
1520         goto cleanup;
1521     }
1522 
1523     memset(paddr, 0, sizeof(krb5_address *) * (count+1));
1524 
1525     for (i = 0; i < count; i++)
1526     {
1527         paddr[i] = (krb5_address *)malloc(sizeof(krb5_address));
1528         if (paddr[i] == NULL) {
1529             err = ENOMEM;
1530             goto cleanup;
1531         }
1532 
1533         paddr[i]->magic = KV5M_ADDRESS;
1534         paddr[i]->addrtype = hostrec->h_addrtype;
1535         paddr[i]->length = hostrec->h_length;
1536         paddr[i]->contents = (unsigned char *)malloc(paddr[i]->length);
1537         if (!paddr[i]->contents) {
1538             err = ENOMEM;
1539             goto cleanup;
1540         }
1541         memcpy(paddr[i]->contents,
1542                hostrec->h_addr_list[i],
1543                paddr[i]->length);
1544     }
1545 
1546  cleanup:
1547     if (err) {
1548         if (paddr) {
1549             for (i = 0; i < count; i++)
1550             {
1551                 if (paddr[i]) {
1552                     if (paddr[i]->contents)
1553                         free(paddr[i]->contents);
1554                     free(paddr[i]);
1555                 }
1556             }
1557             free(paddr);
1558         }
1559     }
1560     else
1561         *addr = paddr;
1562 
1563     return(err);
1564 }
1565 #endif
1566