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