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