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