17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * lib/krb5/os/localaddr.c
37c478bd9Sstevel@tonic-gate  *
4505d05c7Sgtb  * Copyright 1990,1991,2000,2001,2002,2004 by the Massachusetts Institute of Technology.
57c478bd9Sstevel@tonic-gate  * All Rights Reserved.
67c478bd9Sstevel@tonic-gate  *
77c478bd9Sstevel@tonic-gate  * Export of this software from the United States of America may
87c478bd9Sstevel@tonic-gate  *   require a specific license from the United States Government.
97c478bd9Sstevel@tonic-gate  *   It is the responsibility of any person or organization contemplating
107c478bd9Sstevel@tonic-gate  *   export to obtain such a license before exporting.
11*55fea89dSDan Cross  *
127c478bd9Sstevel@tonic-gate  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
137c478bd9Sstevel@tonic-gate  * distribute this software and its documentation for any purpose and
147c478bd9Sstevel@tonic-gate  * without fee is hereby granted, provided that the above copyright
157c478bd9Sstevel@tonic-gate  * notice appear in all copies and that both that copyright notice and
167c478bd9Sstevel@tonic-gate  * this permission notice appear in supporting documentation, and that
177c478bd9Sstevel@tonic-gate  * the name of M.I.T. not be used in advertising or publicity pertaining
187c478bd9Sstevel@tonic-gate  * to distribution of the software without specific, written prior
197c478bd9Sstevel@tonic-gate  * permission.  Furthermore if you modify this software you must label
207c478bd9Sstevel@tonic-gate  * your software as modified software and not distribute it in such a
217c478bd9Sstevel@tonic-gate  * fashion that it might be confused with the original M.I.T. software.
227c478bd9Sstevel@tonic-gate  * M.I.T. makes no representations about the suitability of
237c478bd9Sstevel@tonic-gate  * this software for any purpose.  It is provided "as is" without express
247c478bd9Sstevel@tonic-gate  * or implied warranty.
25*55fea89dSDan Cross  *
267c478bd9Sstevel@tonic-gate  *
277c478bd9Sstevel@tonic-gate  * Return the protocol addresses supported by this host.
28505d05c7Sgtb  * Exports from this file:
29505d05c7Sgtb  *   krb5int_foreach_localaddr (does callbacks)
30505d05c7Sgtb  *   krb5int_local_addresses (includes krb5.conf extra_addresses)
31505d05c7Sgtb  *   krb5_os_localaddr (doesn't)
327c478bd9Sstevel@tonic-gate  *
337c478bd9Sstevel@tonic-gate  * XNS support is untested, but "Should just work".  (Hah!)
347c478bd9Sstevel@tonic-gate  */
357c478bd9Sstevel@tonic-gate 
36505d05c7Sgtb #include "k5-int.h"
37505d05c7Sgtb 
38505d05c7Sgtb #if !defined(_WIN32)
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate /* needed for solaris, harmless elsewhere... */
417c478bd9Sstevel@tonic-gate #define BSD_COMP
427c478bd9Sstevel@tonic-gate #include <sys/ioctl.h>
437c478bd9Sstevel@tonic-gate #include <sys/time.h>
447c478bd9Sstevel@tonic-gate #include <errno.h>
457c478bd9Sstevel@tonic-gate #include <stddef.h>
467c478bd9Sstevel@tonic-gate #include <ctype.h>
477c478bd9Sstevel@tonic-gate 
48505d05c7Sgtb #if defined(TEST) || defined(DEBUG)
49505d05c7Sgtb # include "fake-addrinfo.h"
50505d05c7Sgtb #endif
51505d05c7Sgtb 
52505d05c7Sgtb #include "foreachaddr.h"
53505d05c7Sgtb 
54505d05c7Sgtb /* Note: foreach_localaddr is exported from the library through
55505d05c7Sgtb    krb5int_accessor, for the KDC to use.
56505d05c7Sgtb 
57505d05c7Sgtb    This function iterates over all the addresses it can find for the
58505d05c7Sgtb    local system, in one or two passes.  In each pass, and between the
59505d05c7Sgtb    two, it can invoke callback functions supplied by the caller.  The
60505d05c7Sgtb    two passes should operate on the same information, though not
61505d05c7Sgtb    necessarily in the same order each time.  Duplicate and local
62505d05c7Sgtb    addresses should be eliminated.  Storage passed to callback
63505d05c7Sgtb    functions should not be assumed to be valid after foreach_localaddr
64505d05c7Sgtb    returns.
65505d05c7Sgtb 
66505d05c7Sgtb    The int return value is an errno value (XXX or krb5_error_code
67505d05c7Sgtb    returned for a socket error) if something internal to
68505d05c7Sgtb    foreach_localaddr fails.  If one of the callback functions wants to
69505d05c7Sgtb    indicate an error, it should store something via the 'data' handle.
70505d05c7Sgtb    If any callback function returns a non-zero value,
71505d05c7Sgtb    foreach_localaddr will clean up and return immediately.
72505d05c7Sgtb 
73505d05c7Sgtb    Multiple definitions are provided below, dependent on various
74505d05c7Sgtb    system facilities for extracting the necessary information.  */
75505d05c7Sgtb 
76505d05c7Sgtb /* Now, on to the implementations, and heaps of debugging code.  */
77505d05c7Sgtb 
78505d05c7Sgtb #ifdef TEST
79505d05c7Sgtb # define Tprintf(X) printf X
80505d05c7Sgtb # define Tperror(X) perror(X)
81505d05c7Sgtb #else
82505d05c7Sgtb # define Tprintf(X) (void) X
83505d05c7Sgtb # define Tperror(X) (void)(X)
84505d05c7Sgtb #endif
85505d05c7Sgtb 
86505d05c7Sgtb /*
87505d05c7Sgtb  * The SIOCGIF* ioctls require a socket.
88505d05c7Sgtb  * It doesn't matter *what* kind of socket they use, but it has to be
89505d05c7Sgtb  * a socket.
90505d05c7Sgtb  *
91505d05c7Sgtb  * Of course, you can't just ask the kernel for a socket of arbitrary
92505d05c7Sgtb  * type; you have to ask for one with a valid type.
93505d05c7Sgtb  *
94505d05c7Sgtb  */
95505d05c7Sgtb #ifdef HAVE_NETINET_IN_H
96505d05c7Sgtb #include <netinet/in.h>
97505d05c7Sgtb #ifndef USE_AF
98505d05c7Sgtb #define USE_AF AF_INET
99505d05c7Sgtb #define USE_TYPE SOCK_DGRAM
100505d05c7Sgtb #define USE_PROTO 0
101505d05c7Sgtb #endif
102505d05c7Sgtb #endif
103505d05c7Sgtb 
104505d05c7Sgtb #ifdef KRB5_USE_NS
105505d05c7Sgtb #include <netns/ns.h>
106505d05c7Sgtb #ifndef USE_AF
107505d05c7Sgtb #define USE_AF AF_NS
108505d05c7Sgtb #define USE_TYPE SOCK_DGRAM
109505d05c7Sgtb #define USE_PROTO 0		/* guess */
110505d05c7Sgtb #endif
111505d05c7Sgtb #endif
112505d05c7Sgtb /*
113505d05c7Sgtb  * Add more address families here.
114505d05c7Sgtb  */
115505d05c7Sgtb 
116505d05c7Sgtb 
117505d05c7Sgtb #if defined(__linux__) && defined(KRB5_USE_INET6) && !defined(HAVE_IFADDRS_H)
118505d05c7Sgtb #define LINUX_IPV6_HACK
119505d05c7Sgtb #endif
120505d05c7Sgtb 
121505d05c7Sgtb #include <errno.h>
122505d05c7Sgtb 
123505d05c7Sgtb /*
124505d05c7Sgtb  * Return all the protocol addresses of this host.
125505d05c7Sgtb  *
126505d05c7Sgtb  * We could kludge up something to return all addresses, assuming that
127505d05c7Sgtb  * they're valid kerberos protocol addresses, but we wouldn't know the
128505d05c7Sgtb  * real size of the sockaddr or know which part of it was actually the
129505d05c7Sgtb  * host part.
130505d05c7Sgtb  *
131505d05c7Sgtb  * This uses the SIOCGIFCONF, SIOCGIFFLAGS, and SIOCGIFADDR ioctl's.
132505d05c7Sgtb  */
133505d05c7Sgtb 
134505d05c7Sgtb /*
135505d05c7Sgtb  * BSD 4.4 defines the size of an ifreq to be
136505d05c7Sgtb  * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
137*55fea89dSDan Cross  * However, under earlier systems, sa_len isn't present, so the size is
138505d05c7Sgtb  * just sizeof(struct ifreq).
139505d05c7Sgtb  */
140505d05c7Sgtb #ifdef HAVE_SA_LEN
141505d05c7Sgtb #ifndef max
142505d05c7Sgtb #define max(a,b) ((a) > (b) ? (a) : (b))
143505d05c7Sgtb #endif
144505d05c7Sgtb #define ifreq_size(i) max(sizeof(struct ifreq),\
145505d05c7Sgtb      sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
146505d05c7Sgtb #else
147505d05c7Sgtb #define ifreq_size(i) sizeof(struct ifreq)
148505d05c7Sgtb #endif /* HAVE_SA_LEN*/
149505d05c7Sgtb 
150505d05c7Sgtb #if defined(DEBUG) || defined(TEST)
151505d05c7Sgtb #include <netinet/in.h>
152505d05c7Sgtb #include <net/if.h>
153505d05c7Sgtb 
154505d05c7Sgtb #include "socket-utils.h"
155505d05c7Sgtb #include "fake-addrinfo.h"
156505d05c7Sgtb 
157505d05c7Sgtb void printaddr (struct sockaddr *);
158505d05c7Sgtb 
printaddr(struct sockaddr * sa)159505d05c7Sgtb void printaddr (struct sockaddr *sa)
160505d05c7Sgtb     /*@modifies fileSystem@*/
161505d05c7Sgtb {
162505d05c7Sgtb     char buf[NI_MAXHOST];
163505d05c7Sgtb     int err;
164505d05c7Sgtb 
165505d05c7Sgtb     printf ("%p ", (void *) sa);
166505d05c7Sgtb     err = getnameinfo (sa, socklen (sa), buf, sizeof (buf), 0, 0,
167505d05c7Sgtb 		       NI_NUMERICHOST);
168505d05c7Sgtb     if (err)
169505d05c7Sgtb 	printf ("<getnameinfo error %d: %s> family=%d",
170505d05c7Sgtb 		err, gai_strerror (err),
171505d05c7Sgtb 		sa->sa_family);
172505d05c7Sgtb     else
173505d05c7Sgtb 	printf ("%s", buf);
174505d05c7Sgtb }
175505d05c7Sgtb #endif
176505d05c7Sgtb 
177505d05c7Sgtb #ifdef HAVE_IFADDRS_H
178505d05c7Sgtb #include <ifaddrs.h>
179505d05c7Sgtb 
180505d05c7Sgtb #ifdef DEBUG
printifaddr(struct ifaddrs * ifp)181505d05c7Sgtb void printifaddr (struct ifaddrs *ifp)
182505d05c7Sgtb {
183505d05c7Sgtb     printf ("%p={\n", ifp);
184505d05c7Sgtb /*  printf ("\tnext=%p\n", ifp->ifa_next); */
185505d05c7Sgtb     printf ("\tname=%s\n", ifp->ifa_name);
186505d05c7Sgtb     printf ("\tflags=");
187505d05c7Sgtb     {
188505d05c7Sgtb 	int ch, flags = ifp->ifa_flags;
189505d05c7Sgtb 	printf ("%x", flags);
190505d05c7Sgtb 	ch = '<';
191505d05c7Sgtb #define X(F) if (flags & IFF_##F) { printf ("%c%s", ch, #F); flags &= ~IFF_##F; ch = ','; }
192505d05c7Sgtb 	X (UP); X (BROADCAST); X (DEBUG); X (LOOPBACK); X (POINTOPOINT);
193505d05c7Sgtb 	X (NOTRAILERS); X (RUNNING); X (NOARP); X (PROMISC); X (ALLMULTI);
194505d05c7Sgtb #ifdef IFF_OACTIVE
195505d05c7Sgtb 	X (OACTIVE);
196505d05c7Sgtb #endif
197505d05c7Sgtb #ifdef IFF_SIMPLE
198505d05c7Sgtb 	X (SIMPLEX);
199505d05c7Sgtb #endif
200505d05c7Sgtb 	X (MULTICAST);
201505d05c7Sgtb 	printf (">");
202505d05c7Sgtb #undef X
203505d05c7Sgtb     }
204505d05c7Sgtb     if (ifp->ifa_addr)
205505d05c7Sgtb 	printf ("\n\taddr="), printaddr (ifp->ifa_addr);
206505d05c7Sgtb     if (ifp->ifa_netmask)
207505d05c7Sgtb 	printf ("\n\tnetmask="), printaddr (ifp->ifa_netmask);
208505d05c7Sgtb     if (ifp->ifa_broadaddr)
209505d05c7Sgtb 	printf ("\n\tbroadaddr="), printaddr (ifp->ifa_broadaddr);
210505d05c7Sgtb     if (ifp->ifa_dstaddr)
211505d05c7Sgtb 	printf ("\n\tdstaddr="), printaddr (ifp->ifa_dstaddr);
212505d05c7Sgtb     if (ifp->ifa_data)
213505d05c7Sgtb 	printf ("\n\tdata=%p", ifp->ifa_data);
214505d05c7Sgtb     printf ("\n}\n");
215505d05c7Sgtb }
216505d05c7Sgtb #endif /* DEBUG */
217505d05c7Sgtb 
218505d05c7Sgtb #include <string.h>
219505d05c7Sgtb #include <stdlib.h>
220505d05c7Sgtb 
221505d05c7Sgtb static int
addr_eq(const struct sockaddr * s1,const struct sockaddr * s2)222505d05c7Sgtb addr_eq (const struct sockaddr *s1, const struct sockaddr *s2)
223505d05c7Sgtb {
224505d05c7Sgtb     if (s1->sa_family != s2->sa_family)
225505d05c7Sgtb 	return 0;
226505d05c7Sgtb #ifdef HAVE_SA_LEN
227505d05c7Sgtb     if (s1->sa_len != s2->sa_len)
228505d05c7Sgtb 	return 0;
229505d05c7Sgtb     return !memcmp (s1, s2, s1->sa_len);
230505d05c7Sgtb #else
231505d05c7Sgtb #define CMPTYPE(T,F) (!memcmp(&((const T*)s1)->F,&((const T*)s2)->F,sizeof(((const T*)s1)->F)))
232505d05c7Sgtb     switch (s1->sa_family) {
233505d05c7Sgtb     case AF_INET:
234505d05c7Sgtb 	return CMPTYPE (struct sockaddr_in, sin_addr);
235505d05c7Sgtb     case AF_INET6:
236505d05c7Sgtb 	return CMPTYPE (struct sockaddr_in6, sin6_addr);
237505d05c7Sgtb     default:
238505d05c7Sgtb 	/* Err on side of duplicate listings.  */
239505d05c7Sgtb 	return 0;
240505d05c7Sgtb     }
241505d05c7Sgtb #endif
242505d05c7Sgtb }
243505d05c7Sgtb #endif
244505d05c7Sgtb 
245505d05c7Sgtb #ifndef HAVE_IFADDRS_H
246505d05c7Sgtb /*@-usereleased@*/ /* lclint doesn't understand realloc */
247505d05c7Sgtb static /*@null@*/ void *
grow_or_free(void * ptr,size_t newsize)248505d05c7Sgtb grow_or_free (/*@only@*/ void *ptr, size_t newsize)
249505d05c7Sgtb      /*@*/
250505d05c7Sgtb {
251505d05c7Sgtb     void *newptr;
252505d05c7Sgtb     newptr = realloc (ptr, newsize);
253505d05c7Sgtb     if (newptr == NULL && newsize != 0) {
254505d05c7Sgtb 	free (ptr);		/* lclint complains but this is right */
255505d05c7Sgtb 	return NULL;
256505d05c7Sgtb     }
257505d05c7Sgtb     return newptr;
258505d05c7Sgtb }
259505d05c7Sgtb /*@=usereleased@*/
260505d05c7Sgtb 
261505d05c7Sgtb static int
get_ifconf(int s,size_t * lenp,char * buf)262505d05c7Sgtb get_ifconf (int s, size_t *lenp, /*@out@*/ char *buf)
263505d05c7Sgtb     /*@modifies *buf,*lenp@*/
264505d05c7Sgtb {
265505d05c7Sgtb     int ret;
266505d05c7Sgtb     struct ifconf ifc;
267505d05c7Sgtb 
268505d05c7Sgtb     /*@+matchanyintegral@*/
269505d05c7Sgtb     ifc.ifc_len = *lenp;
270505d05c7Sgtb     /*@=matchanyintegral@*/
271505d05c7Sgtb     ifc.ifc_buf = buf;
272505d05c7Sgtb     memset(buf, 0, *lenp);
273505d05c7Sgtb     /*@-moduncon@*/
274505d05c7Sgtb     ret = ioctl (s, SIOCGIFCONF, (char *)&ifc);
275505d05c7Sgtb     /*@=moduncon@*/
276505d05c7Sgtb     /*@+matchanyintegral@*/
277505d05c7Sgtb     *lenp = ifc.ifc_len;
278505d05c7Sgtb     /*@=matchanyintegral@*/
279505d05c7Sgtb     return ret;
280505d05c7Sgtb }
281505d05c7Sgtb 
282505d05c7Sgtb /* Solaris uses SIOCGLIFCONF to return struct lifconf which is just
283505d05c7Sgtb    an extended version of struct ifconf.
284505d05c7Sgtb 
285505d05c7Sgtb    HP-UX 11 also appears to have SIOCGLIFCONF, but uses struct
286505d05c7Sgtb    if_laddrconf, and struct if_laddrreq to be used with
287505d05c7Sgtb    SIOCGLIFADDR.  */
288505d05c7Sgtb #if defined(SIOCGLIFCONF) && defined(HAVE_STRUCT_LIFCONF)
289505d05c7Sgtb static int
get_lifconf(int af,int s,size_t * lenp,char * buf)290505d05c7Sgtb get_lifconf (int af, int s, size_t *lenp, /*@out@*/ char *buf)
291505d05c7Sgtb     /*@modifies *buf,*lenp@*/
292505d05c7Sgtb {
293505d05c7Sgtb     int ret;
294505d05c7Sgtb     struct lifconf lifc;
295505d05c7Sgtb 
296505d05c7Sgtb     lifc.lifc_family = af;
297505d05c7Sgtb     lifc.lifc_flags = 0;
298505d05c7Sgtb     /*@+matchanyintegral@*/
299505d05c7Sgtb     lifc.lifc_len = *lenp;
300505d05c7Sgtb     /*@=matchanyintegral@*/
301505d05c7Sgtb     lifc.lifc_buf = buf;
302505d05c7Sgtb     memset(buf, 0, *lenp);
303505d05c7Sgtb     /*@-moduncon@*/
304505d05c7Sgtb     ret = ioctl (s, SIOCGLIFCONF, (char *)&lifc);
305505d05c7Sgtb     if (ret)
306505d05c7Sgtb 	Tperror ("SIOCGLIFCONF");
307505d05c7Sgtb     /*@=moduncon@*/
308505d05c7Sgtb     /*@+matchanyintegral@*/
309505d05c7Sgtb     *lenp = lifc.lifc_len;
310505d05c7Sgtb     /*@=matchanyintegral@*/
311505d05c7Sgtb     return ret;
312505d05c7Sgtb }
313505d05c7Sgtb #endif
314505d05c7Sgtb #if defined(SIOCGLIFCONF) && defined(HAVE_STRUCT_IF_LADDRCONF) && 0
315505d05c7Sgtb /* I'm not sure if this is needed or if net/if.h will pull it in.  */
316505d05c7Sgtb /* #include <net/if6.h> */
317505d05c7Sgtb static int
get_if_laddrconf(int af,int s,size_t * lenp,char * buf)318505d05c7Sgtb get_if_laddrconf (int af, int s, size_t *lenp, /*@out@*/ char *buf)
319505d05c7Sgtb     /*@modifies *buf,*lenp@*/
320505d05c7Sgtb {
321505d05c7Sgtb     int ret;
322505d05c7Sgtb     struct if_laddrconf iflc;
323505d05c7Sgtb 
324505d05c7Sgtb     /*@+matchanyintegral@*/
325505d05c7Sgtb     iflc.iflc_len = *lenp;
326505d05c7Sgtb     /*@=matchanyintegral@*/
327505d05c7Sgtb     iflc.iflc_buf = buf;
328505d05c7Sgtb     memset(buf, 0, *lenp);
329505d05c7Sgtb     /*@-moduncon@*/
330505d05c7Sgtb     ret = ioctl (s, SIOCGLIFCONF, (char *)&iflc);
331505d05c7Sgtb     if (ret)
332505d05c7Sgtb 	Tperror ("SIOCGLIFCONF");
333505d05c7Sgtb     /*@=moduncon@*/
334505d05c7Sgtb     /*@+matchanyintegral@*/
335505d05c7Sgtb     *lenp = iflc.iflc_len;
336505d05c7Sgtb     /*@=matchanyintegral@*/
337505d05c7Sgtb     return ret;
338505d05c7Sgtb }
339505d05c7Sgtb #endif
340505d05c7Sgtb #endif /* ! HAVE_IFADDRS_H */
341505d05c7Sgtb 
342505d05c7Sgtb #ifdef LINUX_IPV6_HACK
343505d05c7Sgtb #include <stdio.h>
344505d05c7Sgtb /* Read IPv6 addresses out of /proc/net/if_inet6, since there isn't
345505d05c7Sgtb    (currently) any ioctl to return them.  */
346505d05c7Sgtb struct linux_ipv6_addr_list {
347505d05c7Sgtb     struct sockaddr_in6 addr;
348505d05c7Sgtb     struct linux_ipv6_addr_list *next;
349505d05c7Sgtb };
350505d05c7Sgtb static struct linux_ipv6_addr_list *
get_linux_ipv6_addrs()351505d05c7Sgtb get_linux_ipv6_addrs ()
352505d05c7Sgtb {
353505d05c7Sgtb     struct linux_ipv6_addr_list *lst = 0;
354505d05c7Sgtb     FILE *f;
355505d05c7Sgtb 
356505d05c7Sgtb     /* _PATH_PROCNET_IFINET6 */
357505d05c7Sgtb     f = fopen("/proc/net/if_inet6", "r");
358505d05c7Sgtb     if (f) {
359505d05c7Sgtb 	char ifname[21];
360505d05c7Sgtb 	unsigned int idx, pfxlen, scope, dadstat;
361505d05c7Sgtb 	struct in6_addr a6;
362505d05c7Sgtb 	struct linux_ipv6_addr_list *nw;
363505d05c7Sgtb 	int i;
364505d05c7Sgtb 	unsigned int addrbyte[16];
365505d05c7Sgtb 
366505d05c7Sgtb 	while (fscanf(f,
367505d05c7Sgtb 		      "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x"
368505d05c7Sgtb 		      " %2x %2x %2x %2x %20s\n",
369505d05c7Sgtb 		      &addrbyte[0], &addrbyte[1], &addrbyte[2], &addrbyte[3],
370505d05c7Sgtb 		      &addrbyte[4], &addrbyte[5], &addrbyte[6], &addrbyte[7],
371505d05c7Sgtb 		      &addrbyte[8], &addrbyte[9], &addrbyte[10], &addrbyte[11],
372505d05c7Sgtb 		      &addrbyte[12], &addrbyte[13], &addrbyte[14],
373505d05c7Sgtb 		      &addrbyte[15],
374505d05c7Sgtb 		      &idx, &pfxlen, &scope, &dadstat, ifname) != EOF) {
375505d05c7Sgtb 	    for (i = 0; i < 16; i++)
376505d05c7Sgtb 		a6.s6_addr[i] = addrbyte[i];
377505d05c7Sgtb 	    if (scope != 0)
378505d05c7Sgtb 		continue;
379505d05c7Sgtb #if 0 /* These symbol names are as used by ifconfig, but none of the
380505d05c7Sgtb 	 system header files export them.  Dig up the kernel versions
381505d05c7Sgtb 	 someday and see if they're exported.  */
382505d05c7Sgtb 	    switch (scope) {
383505d05c7Sgtb 	    case 0:
384505d05c7Sgtb 	    default:
385505d05c7Sgtb 		break;
386505d05c7Sgtb 	    case IPV6_ADDR_LINKLOCAL:
387505d05c7Sgtb 	    case IPV6_ADDR_SITELOCAL:
388505d05c7Sgtb 	    case IPV6_ADDR_COMPATv4:
389505d05c7Sgtb 	    case IPV6_ADDR_LOOPBACK:
390505d05c7Sgtb 		continue;
391505d05c7Sgtb 	    }
392505d05c7Sgtb #endif
393505d05c7Sgtb 	    nw = malloc (sizeof (struct linux_ipv6_addr_list));
394505d05c7Sgtb 	    if (nw == 0)
395505d05c7Sgtb 		continue;
396505d05c7Sgtb 	    memset (nw, 0, sizeof (*nw));
397505d05c7Sgtb 	    nw->addr.sin6_addr = a6;
398505d05c7Sgtb 	    nw->addr.sin6_family = AF_INET6;
399505d05c7Sgtb 	    /* Ignore other fields, we don't actually use them here.  */
400505d05c7Sgtb 	    nw->next = lst;
401505d05c7Sgtb 	    lst = nw;
402505d05c7Sgtb 	}
403505d05c7Sgtb 	fclose (f);
404505d05c7Sgtb     }
405505d05c7Sgtb     return lst;
406505d05c7Sgtb }
407505d05c7Sgtb #endif
408505d05c7Sgtb 
409505d05c7Sgtb /* Return value is errno if internal stuff failed, otherwise zero,
410505d05c7Sgtb    even in the case where a called function terminated the iteration.
411505d05c7Sgtb 
412505d05c7Sgtb    If one of the callback functions wants to pass back an error
413505d05c7Sgtb    indication, it should do it via some field pointed to by the DATA
414505d05c7Sgtb    argument.  */
415505d05c7Sgtb 
416505d05c7Sgtb #ifdef HAVE_IFADDRS_H
417505d05c7Sgtb 
418505d05c7Sgtb int
foreach_localaddr(void * data,int (* pass1fn)(void *,struct sockaddr *),int (* betweenfn)(void *),int (* pass2fn)(void *,struct sockaddr *))419505d05c7Sgtb foreach_localaddr (/*@null@*/ void *data,
420505d05c7Sgtb 		   int (*pass1fn) (/*@null@*/ void *, struct sockaddr *) /*@*/,
421505d05c7Sgtb 		   /*@null@*/ int (*betweenfn) (/*@null@*/ void *) /*@*/,
422505d05c7Sgtb 		   /*@null@*/ int (*pass2fn) (/*@null@*/ void *,
423505d05c7Sgtb 					      struct sockaddr *) /*@*/)
424505d05c7Sgtb #if defined(DEBUG) || defined(TEST)
425505d05c7Sgtb      /*@modifies fileSystem@*/
426505d05c7Sgtb #endif
427505d05c7Sgtb {
428505d05c7Sgtb     struct ifaddrs *ifp_head, *ifp, *ifp2;
429505d05c7Sgtb     int match;
430505d05c7Sgtb 
431505d05c7Sgtb     if (getifaddrs (&ifp_head) < 0)
432505d05c7Sgtb 	return errno;
433505d05c7Sgtb     for (ifp = ifp_head; ifp; ifp = ifp->ifa_next) {
434505d05c7Sgtb #ifdef DEBUG
435505d05c7Sgtb 	printifaddr (ifp);
436505d05c7Sgtb #endif
437505d05c7Sgtb 	if ((ifp->ifa_flags & IFF_UP) == 0)
438505d05c7Sgtb 	    continue;
439505d05c7Sgtb 	if (ifp->ifa_flags & IFF_LOOPBACK) {
440505d05c7Sgtb 	    /* Pretend it's not up, so the second pass will skip
441505d05c7Sgtb 	       it.  */
442505d05c7Sgtb 	    ifp->ifa_flags &= ~IFF_UP;
443505d05c7Sgtb 	    continue;
444505d05c7Sgtb 	}
445505d05c7Sgtb 	if (ifp->ifa_addr == NULL) {
446505d05c7Sgtb 	    /* Can't use an interface without an address.  Linux
447505d05c7Sgtb 	       apparently does this sometimes.  [RT ticket 1770 from
448505d05c7Sgtb 	       Maurice Massar, also Debian bug 206851, shows the
449505d05c7Sgtb 	       problem with a PPP link on a newer kernel than I'm
450505d05c7Sgtb 	       running.]
451505d05c7Sgtb 
452505d05c7Sgtb 	       Pretend it's not up, so the second pass will skip
453505d05c7Sgtb 	       it.  */
454505d05c7Sgtb 	    ifp->ifa_flags &= ~IFF_UP;
455505d05c7Sgtb 	    continue;
456505d05c7Sgtb 	}
457505d05c7Sgtb 	/* If this address is a duplicate, punt.  */
458505d05c7Sgtb 	match = 0;
459505d05c7Sgtb 	for (ifp2 = ifp_head; ifp2 && ifp2 != ifp; ifp2 = ifp2->ifa_next) {
460505d05c7Sgtb 	    if ((ifp2->ifa_flags & IFF_UP) == 0)
461505d05c7Sgtb 		continue;
462505d05c7Sgtb 	    if (ifp2->ifa_flags & IFF_LOOPBACK)
463505d05c7Sgtb 		continue;
464505d05c7Sgtb 	    if (addr_eq (ifp->ifa_addr, ifp2->ifa_addr)) {
465505d05c7Sgtb 		match = 1;
466505d05c7Sgtb 		ifp->ifa_flags &= ~IFF_UP;
467505d05c7Sgtb 		break;
468505d05c7Sgtb 	    }
469505d05c7Sgtb 	}
470505d05c7Sgtb 	if (match)
471505d05c7Sgtb 	    continue;
472505d05c7Sgtb 	if ((*pass1fn) (data, ifp->ifa_addr))
473505d05c7Sgtb 	    goto punt;
474505d05c7Sgtb     }
475505d05c7Sgtb     if (betweenfn && (*betweenfn)(data))
476505d05c7Sgtb 	goto punt;
477505d05c7Sgtb     if (pass2fn)
478505d05c7Sgtb 	for (ifp = ifp_head; ifp; ifp = ifp->ifa_next) {
479505d05c7Sgtb 	    if (ifp->ifa_flags & IFF_UP)
480505d05c7Sgtb 		if ((*pass2fn) (data, ifp->ifa_addr))
481505d05c7Sgtb 		    goto punt;
482505d05c7Sgtb 	}
483505d05c7Sgtb  punt:
484505d05c7Sgtb     freeifaddrs (ifp_head);
485505d05c7Sgtb     return 0;
486505d05c7Sgtb }
487505d05c7Sgtb 
488505d05c7Sgtb #elif defined (SIOCGLIFNUM) && defined(HAVE_STRUCT_LIFCONF) /* Solaris 8 and later; Sol 7? */
489505d05c7Sgtb 
490505d05c7Sgtb int
foreach_localaddr(void * data,int (* pass1fn)(void *,struct sockaddr *),int (* betweenfn)(void *),int (* pass2fn)(void *,struct sockaddr *))491505d05c7Sgtb foreach_localaddr (/*@null@*/ void *data,
492505d05c7Sgtb 		   int (*pass1fn) (/*@null@*/ void *, struct sockaddr *) /*@*/,
493505d05c7Sgtb 		   /*@null@*/ int (*betweenfn) (/*@null@*/ void *) /*@*/,
494505d05c7Sgtb 		   /*@null@*/ int (*pass2fn) (/*@null@*/ void *,
495505d05c7Sgtb 					      struct sockaddr *) /*@*/)
496505d05c7Sgtb #if defined(DEBUG) || defined(TEST)
497505d05c7Sgtb      /*@modifies fileSystem@*/
498505d05c7Sgtb #endif
499505d05c7Sgtb {
500505d05c7Sgtb     /* Okay, this is kind of odd.  We have to use each of the address
501505d05c7Sgtb        families we care about, because with an AF_INET socket, extra
502505d05c7Sgtb        interfaces like hme0:1 that have only AF_INET6 addresses will
503505d05c7Sgtb        cause errors.  Similarly, if hme0 has more AF_INET addresses
504505d05c7Sgtb        than AF_INET6 addresses, we won't be able to retrieve all of
505505d05c7Sgtb        the AF_INET addresses if we use an AF_INET6 socket.  Since
506505d05c7Sgtb        neither family is guaranteed to have the greater number of
507505d05c7Sgtb        addresses, we should use both.
508505d05c7Sgtb 
509505d05c7Sgtb        If it weren't for this little quirk, we could use one socket of
510505d05c7Sgtb        any type, and ask for addresses of all types.  At least, it
511505d05c7Sgtb        seems to work that way.  */
512505d05c7Sgtb 
513505d05c7Sgtb     static const int afs[] = { AF_INET, AF_NS, AF_INET6 };
514505d05c7Sgtb #define N_AFS (sizeof (afs) / sizeof (afs[0]))
515505d05c7Sgtb     struct {
516505d05c7Sgtb 	int af;
517505d05c7Sgtb 	int sock;
518505d05c7Sgtb 	void *buf;
519505d05c7Sgtb 	size_t buf_size;
520505d05c7Sgtb 	struct lifnum lifnum;
521505d05c7Sgtb     } afp[N_AFS];
522505d05c7Sgtb     int code, i, j;
523505d05c7Sgtb     int retval = 0, afidx;
524505d05c7Sgtb     krb5_error_code sock_err = 0;
525505d05c7Sgtb     struct lifreq *lifr, lifreq, *lifr2;
526505d05c7Sgtb 
527505d05c7Sgtb #define FOREACH_AF() for (afidx = 0; afidx < N_AFS; afidx++)
528505d05c7Sgtb #define P (afp[afidx])
529505d05c7Sgtb 
530505d05c7Sgtb     /* init */
531505d05c7Sgtb     FOREACH_AF () {
532505d05c7Sgtb 	P.af = afs[afidx];
533505d05c7Sgtb 	P.sock = -1;
534505d05c7Sgtb 	P.buf = 0;
535505d05c7Sgtb     }
536505d05c7Sgtb 
537505d05c7Sgtb     /* first pass: get raw data, discard uninteresting addresses, callback */
538505d05c7Sgtb     FOREACH_AF () {
539505d05c7Sgtb 	Tprintf (("trying af %d...\n", P.af));
540505d05c7Sgtb 	P.sock = socket (P.af, USE_TYPE, USE_PROTO);
541505d05c7Sgtb 	if (P.sock < 0) {
542505d05c7Sgtb 	    sock_err = SOCKET_ERROR;
543505d05c7Sgtb 	    Tperror ("socket");
544505d05c7Sgtb 	    continue;
545505d05c7Sgtb 	}
546505d05c7Sgtb 
547505d05c7Sgtb 	P.lifnum.lifn_family = P.af;
548505d05c7Sgtb 	P.lifnum.lifn_flags = 0;
549505d05c7Sgtb 	P.lifnum.lifn_count = 0;
550505d05c7Sgtb 	code = ioctl (P.sock, SIOCGLIFNUM, &P.lifnum);
551505d05c7Sgtb 	if (code) {
552505d05c7Sgtb 	    Tperror ("ioctl(SIOCGLIFNUM)");
553505d05c7Sgtb 	    retval = errno;
554505d05c7Sgtb 	    goto punt;
555505d05c7Sgtb 	}
556505d05c7Sgtb 
557505d05c7Sgtb 	P.buf_size = P.lifnum.lifn_count * sizeof (struct lifreq) * 2;
558505d05c7Sgtb 	P.buf = malloc (P.buf_size);
559505d05c7Sgtb 	if (P.buf == NULL) {
560505d05c7Sgtb 	    retval = errno;
561505d05c7Sgtb 	    goto punt;
562505d05c7Sgtb 	}
563505d05c7Sgtb 
564505d05c7Sgtb 	code = get_lifconf (P.af, P.sock, &P.buf_size, P.buf);
565505d05c7Sgtb 	if (code < 0) {
566505d05c7Sgtb 	    retval = errno;
567505d05c7Sgtb 	    goto punt;
568505d05c7Sgtb 	}
569505d05c7Sgtb 
570505d05c7Sgtb 	for (i = 0; i + sizeof(*lifr) <= P.buf_size; i+= sizeof (*lifr)) {
571505d05c7Sgtb 	    lifr = (struct lifreq *)((caddr_t) P.buf+i);
572505d05c7Sgtb 
573505d05c7Sgtb 	    strncpy(lifreq.lifr_name, lifr->lifr_name,
574505d05c7Sgtb 		    sizeof (lifreq.lifr_name));
575505d05c7Sgtb 	    Tprintf (("interface %s\n", lifreq.lifr_name));
576505d05c7Sgtb 	    /*@-moduncon@*/ /* ioctl unknown to lclint */
577505d05c7Sgtb 	    if (ioctl (P.sock, SIOCGLIFFLAGS, (char *)&lifreq) < 0) {
578505d05c7Sgtb 		Tperror ("ioctl(SIOCGLIFFLAGS)");
579505d05c7Sgtb 	    skip:
580505d05c7Sgtb 		/* mark for next pass */
581505d05c7Sgtb 		lifr->lifr_name[0] = '\0';
582505d05c7Sgtb 		continue;
583505d05c7Sgtb 	    }
584505d05c7Sgtb 	    /*@=moduncon@*/
585505d05c7Sgtb 
586505d05c7Sgtb #ifdef IFF_LOOPBACK
587505d05c7Sgtb 	    /* None of the current callers want loopback addresses.  */
588505d05c7Sgtb 	    if (lifreq.lifr_flags & IFF_LOOPBACK) {
589505d05c7Sgtb 		Tprintf (("  loopback\n"));
590505d05c7Sgtb 		goto skip;
591505d05c7Sgtb 	    }
592505d05c7Sgtb #endif
593505d05c7Sgtb 	    /* Ignore interfaces that are down.  */
594505d05c7Sgtb 	    if ((lifreq.lifr_flags & IFF_UP) == 0) {
595505d05c7Sgtb 		Tprintf (("  down\n"));
596505d05c7Sgtb 		goto skip;
597505d05c7Sgtb 	    }
598505d05c7Sgtb 
599505d05c7Sgtb 	    /* Make sure we didn't process this address already.  */
600505d05c7Sgtb 	    for (j = 0; j < i; j += sizeof (*lifr2)) {
601505d05c7Sgtb 		lifr2 = (struct lifreq *)((caddr_t) P.buf+j);
602505d05c7Sgtb 		if (lifr2->lifr_name[0] == '\0')
603505d05c7Sgtb 		    continue;
604505d05c7Sgtb 		if (lifr2->lifr_addr.ss_family == lifr->lifr_addr.ss_family
605505d05c7Sgtb 		    /* Compare address info.  If this isn't good enough --
606505d05c7Sgtb 		       i.e., if random padding bytes turn out to differ
607505d05c7Sgtb 		       when the addresses are the same -- then we'll have
608505d05c7Sgtb 		       to do it on a per address family basis.  */
609505d05c7Sgtb 		    && !memcmp (&lifr2->lifr_addr, &lifr->lifr_addr,
610505d05c7Sgtb 				sizeof (*lifr))) {
611505d05c7Sgtb 		    Tprintf (("  duplicate addr\n"));
612505d05c7Sgtb 		    goto skip;
613505d05c7Sgtb 		}
614505d05c7Sgtb 	    }
615505d05c7Sgtb 
616505d05c7Sgtb 	    /*@-moduncon@*/
617505d05c7Sgtb 	    if ((*pass1fn) (data, ss2sa (&lifr->lifr_addr)))
618505d05c7Sgtb 		goto punt;
619505d05c7Sgtb 	    /*@=moduncon@*/
620505d05c7Sgtb 	}
621505d05c7Sgtb     }
622505d05c7Sgtb 
623505d05c7Sgtb     /* Did we actually get any working sockets?  */
624505d05c7Sgtb     FOREACH_AF ()
625505d05c7Sgtb 	if (P.sock != -1)
626505d05c7Sgtb 	    goto have_working_socket;
627505d05c7Sgtb     retval = sock_err;
628505d05c7Sgtb     goto punt;
629505d05c7Sgtb have_working_socket:
630505d05c7Sgtb 
631505d05c7Sgtb     /*@-moduncon@*/
632505d05c7Sgtb     if (betweenfn != NULL && (*betweenfn)(data))
633505d05c7Sgtb 	goto punt;
634505d05c7Sgtb     /*@=moduncon@*/
635505d05c7Sgtb 
636505d05c7Sgtb     if (pass2fn)
637505d05c7Sgtb 	FOREACH_AF ()
638505d05c7Sgtb 	    if (P.sock >= 0) {
639505d05c7Sgtb 		for (i = 0; i + sizeof (*lifr) <= P.buf_size; i+= sizeof (*lifr)) {
640505d05c7Sgtb 		    lifr = (struct lifreq *)((caddr_t) P.buf+i);
641505d05c7Sgtb 
642505d05c7Sgtb 		    if (lifr->lifr_name[0] == '\0')
643505d05c7Sgtb 			/* Marked in first pass to be ignored.  */
644505d05c7Sgtb 			continue;
645505d05c7Sgtb 
646505d05c7Sgtb 		    /*@-moduncon@*/
647505d05c7Sgtb 		    if ((*pass2fn) (data, ss2sa (&lifr->lifr_addr)))
648505d05c7Sgtb 			goto punt;
649505d05c7Sgtb 		    /*@=moduncon@*/
650505d05c7Sgtb 		}
651505d05c7Sgtb 	    }
652505d05c7Sgtb punt:
653505d05c7Sgtb     FOREACH_AF () {
654505d05c7Sgtb 	/*@-moduncon@*/
655505d05c7Sgtb 	closesocket(P.sock);
656505d05c7Sgtb 	/*@=moduncon@*/
657505d05c7Sgtb 	free (P.buf);
658505d05c7Sgtb     }
659505d05c7Sgtb 
660505d05c7Sgtb     return retval;
661505d05c7Sgtb }
662505d05c7Sgtb 
663505d05c7Sgtb #elif defined (SIOCGLIFNUM) && defined(HAVE_STRUCT_IF_LADDRCONF) && 0 /* HP-UX 11 support being debugged */
664505d05c7Sgtb 
665505d05c7Sgtb int
foreach_localaddr(void * data,int (* pass1fn)(void *,struct sockaddr *),int (* betweenfn)(void *),int (* pass2fn)(void *,struct sockaddr *))666505d05c7Sgtb foreach_localaddr (/*@null@*/ void *data,
667505d05c7Sgtb 		   int (*pass1fn) (/*@null@*/ void *, struct sockaddr *) /*@*/,
668505d05c7Sgtb 		   /*@null@*/ int (*betweenfn) (/*@null@*/ void *) /*@*/,
669505d05c7Sgtb 		   /*@null@*/ int (*pass2fn) (/*@null@*/ void *,
670505d05c7Sgtb 					      struct sockaddr *) /*@*/)
671505d05c7Sgtb #if defined(DEBUG) || defined(TEST)
672505d05c7Sgtb      /*@modifies fileSystem@*/
673505d05c7Sgtb #endif
674505d05c7Sgtb {
675505d05c7Sgtb     /* Okay, this is kind of odd.  We have to use each of the address
676505d05c7Sgtb        families we care about, because with an AF_INET socket, extra
677505d05c7Sgtb        interfaces like hme0:1 that have only AF_INET6 addresses will
678505d05c7Sgtb        cause errors.  Similarly, if hme0 has more AF_INET addresses
679505d05c7Sgtb        than AF_INET6 addresses, we won't be able to retrieve all of
680505d05c7Sgtb        the AF_INET addresses if we use an AF_INET6 socket.  Since
681505d05c7Sgtb        neither family is guaranteed to have the greater number of
682505d05c7Sgtb        addresses, we should use both.
683505d05c7Sgtb 
684505d05c7Sgtb        If it weren't for this little quirk, we could use one socket of
685505d05c7Sgtb        any type, and ask for addresses of all types.  At least, it
686505d05c7Sgtb        seems to work that way.  */
687505d05c7Sgtb 
688505d05c7Sgtb     static const int afs[] = { AF_INET, AF_NS, AF_INET6 };
689505d05c7Sgtb #define N_AFS (sizeof (afs) / sizeof (afs[0]))
690505d05c7Sgtb     struct {
691505d05c7Sgtb 	int af;
692505d05c7Sgtb 	int sock;
693505d05c7Sgtb 	void *buf;
694505d05c7Sgtb 	size_t buf_size;
695505d05c7Sgtb 	int if_num;
696505d05c7Sgtb     } afp[N_AFS];
697505d05c7Sgtb     int code, i, j;
698505d05c7Sgtb     int retval = 0, afidx;
699505d05c7Sgtb     krb5_error_code sock_err = 0;
700505d05c7Sgtb     struct if_laddrreq *lifr, lifreq, *lifr2;
701505d05c7Sgtb 
702505d05c7Sgtb #define FOREACH_AF() for (afidx = 0; afidx < N_AFS; afidx++)
703505d05c7Sgtb #define P (afp[afidx])
704505d05c7Sgtb 
705505d05c7Sgtb     /* init */
706505d05c7Sgtb     FOREACH_AF () {
707505d05c7Sgtb 	P.af = afs[afidx];
708505d05c7Sgtb 	P.sock = -1;
709505d05c7Sgtb 	P.buf = 0;
710505d05c7Sgtb     }
711505d05c7Sgtb 
712505d05c7Sgtb     /* first pass: get raw data, discard uninteresting addresses, callback */
713505d05c7Sgtb     FOREACH_AF () {
714505d05c7Sgtb 	Tprintf (("trying af %d...\n", P.af));
715505d05c7Sgtb 	P.sock = socket (P.af, USE_TYPE, USE_PROTO);
716505d05c7Sgtb 	if (P.sock < 0) {
717505d05c7Sgtb 	    sock_err = SOCKET_ERROR;
718505d05c7Sgtb 	    Tperror ("socket");
719505d05c7Sgtb 	    continue;
720505d05c7Sgtb 	}
721505d05c7Sgtb 
722505d05c7Sgtb 	code = ioctl (P.sock, SIOCGLIFNUM, &P.if_num);
723505d05c7Sgtb 	if (code) {
724505d05c7Sgtb 	    Tperror ("ioctl(SIOCGLIFNUM)");
725505d05c7Sgtb 	    retval = errno;
726505d05c7Sgtb 	    goto punt;
727505d05c7Sgtb 	}
728505d05c7Sgtb 
729505d05c7Sgtb 	P.buf_size = P.if_num * sizeof (struct if_laddrreq) * 2;
730505d05c7Sgtb 	P.buf = malloc (P.buf_size);
731505d05c7Sgtb 	if (P.buf == NULL) {
732505d05c7Sgtb 	    retval = errno;
733505d05c7Sgtb 	    goto punt;
734505d05c7Sgtb 	}
735505d05c7Sgtb 
736505d05c7Sgtb 	code = get_if_laddrconf (P.af, P.sock, &P.buf_size, P.buf);
737505d05c7Sgtb 	if (code < 0) {
738505d05c7Sgtb 	    retval = errno;
739505d05c7Sgtb 	    goto punt;
740505d05c7Sgtb 	}
741505d05c7Sgtb 
742505d05c7Sgtb 	for (i = 0; i + sizeof(*lifr) <= P.buf_size; i+= sizeof (*lifr)) {
743505d05c7Sgtb 	    lifr = (struct if_laddrreq *)((caddr_t) P.buf+i);
744505d05c7Sgtb 
745505d05c7Sgtb 	    strncpy(lifreq.iflr_name, lifr->iflr_name,
746505d05c7Sgtb 		    sizeof (lifreq.iflr_name));
747505d05c7Sgtb 	    Tprintf (("interface %s\n", lifreq.iflr_name));
748505d05c7Sgtb 	    /*@-moduncon@*/ /* ioctl unknown to lclint */
749505d05c7Sgtb 	    if (ioctl (P.sock, SIOCGLIFFLAGS, (char *)&lifreq) < 0) {
750505d05c7Sgtb 		Tperror ("ioctl(SIOCGLIFFLAGS)");
751505d05c7Sgtb 	    skip:
752505d05c7Sgtb 		/* mark for next pass */
753505d05c7Sgtb 		lifr->iflr_name[0] = '\0';
754505d05c7Sgtb 		continue;
755505d05c7Sgtb 	    }
756505d05c7Sgtb 	    /*@=moduncon@*/
757505d05c7Sgtb 
758505d05c7Sgtb #ifdef IFF_LOOPBACK
759505d05c7Sgtb 	    /* None of the current callers want loopback addresses.  */
760505d05c7Sgtb 	    if (lifreq.iflr_flags & IFF_LOOPBACK) {
761505d05c7Sgtb 		Tprintf (("  loopback\n"));
762505d05c7Sgtb 		goto skip;
763505d05c7Sgtb 	    }
764505d05c7Sgtb #endif
765505d05c7Sgtb 	    /* Ignore interfaces that are down.  */
766505d05c7Sgtb 	    if ((lifreq.iflr_flags & IFF_UP) == 0) {
767505d05c7Sgtb 		Tprintf (("  down\n"));
768505d05c7Sgtb 		goto skip;
769505d05c7Sgtb 	    }
770505d05c7Sgtb 
771505d05c7Sgtb 	    /* Make sure we didn't process this address already.  */
772505d05c7Sgtb 	    for (j = 0; j < i; j += sizeof (*lifr2)) {
773505d05c7Sgtb 		lifr2 = (struct if_laddrreq *)((caddr_t) P.buf+j);
774505d05c7Sgtb 		if (lifr2->iflr_name[0] == '\0')
775505d05c7Sgtb 		    continue;
776505d05c7Sgtb 		if (lifr2->iflr_addr.sa_family == lifr->iflr_addr.sa_family
777505d05c7Sgtb 		    /* Compare address info.  If this isn't good enough --
778505d05c7Sgtb 		       i.e., if random padding bytes turn out to differ
779505d05c7Sgtb 		       when the addresses are the same -- then we'll have
780505d05c7Sgtb 		       to do it on a per address family basis.  */
781505d05c7Sgtb 		    && !memcmp (&lifr2->iflr_addr, &lifr->iflr_addr,
782505d05c7Sgtb 				sizeof (*lifr))) {
783505d05c7Sgtb 		    Tprintf (("  duplicate addr\n"));
784505d05c7Sgtb 		    goto skip;
785505d05c7Sgtb 		}
786505d05c7Sgtb 	    }
787505d05c7Sgtb 
788505d05c7Sgtb 	    /*@-moduncon@*/
789505d05c7Sgtb 	    if ((*pass1fn) (data, ss2sa (&lifr->iflr_addr)))
790505d05c7Sgtb 		goto punt;
791505d05c7Sgtb 	    /*@=moduncon@*/
792505d05c7Sgtb 	}
793505d05c7Sgtb     }
794505d05c7Sgtb 
795505d05c7Sgtb     /* Did we actually get any working sockets?  */
796505d05c7Sgtb     FOREACH_AF ()
797505d05c7Sgtb 	if (P.sock != -1)
798505d05c7Sgtb 	    goto have_working_socket;
799505d05c7Sgtb     retval = sock_err;
800505d05c7Sgtb     goto punt;
801505d05c7Sgtb have_working_socket:
802505d05c7Sgtb 
803505d05c7Sgtb     /*@-moduncon@*/
804505d05c7Sgtb     if (betweenfn != NULL && (*betweenfn)(data))
805505d05c7Sgtb 	goto punt;
806505d05c7Sgtb     /*@=moduncon@*/
807505d05c7Sgtb 
808505d05c7Sgtb     if (pass2fn)
809505d05c7Sgtb 	FOREACH_AF ()
810505d05c7Sgtb 	    if (P.sock >= 0) {
811505d05c7Sgtb 		for (i = 0; i + sizeof(*lifr) <= P.buf_size; i+= sizeof (*lifr)) {
812505d05c7Sgtb 		    lifr = (struct if_laddrreq *)((caddr_t) P.buf+i);
813505d05c7Sgtb 
814505d05c7Sgtb 		    if (lifr->iflr_name[0] == '\0')
815505d05c7Sgtb 			/* Marked in first pass to be ignored.  */
816505d05c7Sgtb 			continue;
817505d05c7Sgtb 
818505d05c7Sgtb 		    /*@-moduncon@*/
819505d05c7Sgtb 		    if ((*pass2fn) (data, ss2sa (&lifr->iflr_addr)))
820505d05c7Sgtb 			goto punt;
821505d05c7Sgtb 		    /*@=moduncon@*/
822505d05c7Sgtb 		}
823505d05c7Sgtb 	    }
824505d05c7Sgtb punt:
825505d05c7Sgtb     FOREACH_AF () {
826505d05c7Sgtb 	/*@-moduncon@*/
827505d05c7Sgtb 	closesocket(P.sock);
828505d05c7Sgtb 	/*@=moduncon@*/
829505d05c7Sgtb 	free (P.buf);
830505d05c7Sgtb     }
831505d05c7Sgtb 
832505d05c7Sgtb     return retval;
833505d05c7Sgtb }
834505d05c7Sgtb 
835505d05c7Sgtb #else /* not defined (SIOCGLIFNUM) */
836505d05c7Sgtb 
837505d05c7Sgtb #define SLOP (sizeof (struct ifreq) + 128)
838505d05c7Sgtb 
839505d05c7Sgtb static int
get_ifreq_array(char ** bufp,size_t * np,int s)840505d05c7Sgtb get_ifreq_array(char **bufp, size_t *np, int s)
841505d05c7Sgtb {
842505d05c7Sgtb     int code;
843505d05c7Sgtb     int est_if_count = 8;
844505d05c7Sgtb     size_t est_ifreq_size;
845505d05c7Sgtb     char *buf = 0;
846505d05c7Sgtb     size_t current_buf_size = 0, size, n;
847505d05c7Sgtb #ifdef SIOCGSIZIFCONF
848505d05c7Sgtb     int ifconfsize = -1;
849505d05c7Sgtb #endif
850505d05c7Sgtb #ifdef SIOCGIFNUM
851505d05c7Sgtb     int numifs = -1;
852505d05c7Sgtb #endif
853505d05c7Sgtb 
854505d05c7Sgtb     /* At least on NetBSD, an ifreq can hold an IPv4 address, but
855505d05c7Sgtb        isn't big enough for an IPv6 or ethernet address.  So add a
856505d05c7Sgtb        little more space.  */
857505d05c7Sgtb     est_ifreq_size = sizeof (struct ifreq) + 8;
858505d05c7Sgtb #ifdef SIOCGSIZIFCONF
859505d05c7Sgtb     code = ioctl (s, SIOCGSIZIFCONF, &ifconfsize);
860505d05c7Sgtb     if (!code) {
861505d05c7Sgtb 	current_buf_size = ifconfsize;
862505d05c7Sgtb 	est_if_count = ifconfsize / est_ifreq_size;
863505d05c7Sgtb     }
864505d05c7Sgtb #elif defined (SIOCGIFNUM)
865505d05c7Sgtb     code = ioctl (s, SIOCGIFNUM, &numifs);
866505d05c7Sgtb     if (!code && numifs > 0)
867505d05c7Sgtb 	est_if_count = numifs;
868505d05c7Sgtb #endif
869505d05c7Sgtb     if (current_buf_size == 0)
870505d05c7Sgtb 	current_buf_size = est_ifreq_size * est_if_count + SLOP;
871505d05c7Sgtb     buf = malloc (current_buf_size);
872505d05c7Sgtb     if (buf == NULL)
873505d05c7Sgtb 	return errno;
874505d05c7Sgtb 
875505d05c7Sgtb ask_again:
876505d05c7Sgtb     size = current_buf_size;
877505d05c7Sgtb     code = get_ifconf (s, &size, buf);
878505d05c7Sgtb     if (code < 0) {
879505d05c7Sgtb 	code = errno;
880505d05c7Sgtb 	free (buf);
881505d05c7Sgtb 	return code;
882505d05c7Sgtb     }
883505d05c7Sgtb     /* Test that the buffer was big enough that another ifreq could've
884505d05c7Sgtb        fit easily, if the OS wanted to provide one.  That seems to be
885505d05c7Sgtb        the only indication we get, complicated by the fact that the
886505d05c7Sgtb        associated address may make the required storage a little
887505d05c7Sgtb        bigger than the size of an ifreq.  */
888505d05c7Sgtb     if (current_buf_size - size < SLOP
889505d05c7Sgtb #ifdef SIOCGSIZIFCONF
890505d05c7Sgtb 	/* Unless we hear SIOCGSIZIFCONF is broken somewhere, let's
891505d05c7Sgtb 	   trust the value it returns.  */
892505d05c7Sgtb 	&& ifconfsize <= 0
893505d05c7Sgtb #elif defined (SIOCGIFNUM)
894505d05c7Sgtb 	&& numifs <= 0
895505d05c7Sgtb #endif
896505d05c7Sgtb 	/* And we need *some* sort of bounds.  */
897505d05c7Sgtb 	&& current_buf_size <= 100000
898505d05c7Sgtb 	) {
899505d05c7Sgtb 	size_t new_size;
900505d05c7Sgtb 
901505d05c7Sgtb 	est_if_count *= 2;
902505d05c7Sgtb 	new_size = est_ifreq_size * est_if_count + SLOP;
903505d05c7Sgtb 	buf = grow_or_free (buf, new_size);
904505d05c7Sgtb 	if (buf == 0)
905505d05c7Sgtb 	    return errno;
906505d05c7Sgtb 	current_buf_size = new_size;
907505d05c7Sgtb 	goto ask_again;
908505d05c7Sgtb     }
909505d05c7Sgtb 
910505d05c7Sgtb     n = size;
911505d05c7Sgtb     if (n > current_buf_size)
912505d05c7Sgtb 	n = current_buf_size;
913505d05c7Sgtb 
914505d05c7Sgtb     *bufp = buf;
915505d05c7Sgtb     *np = n;
916505d05c7Sgtb     return 0;
917505d05c7Sgtb }
918505d05c7Sgtb 
919505d05c7Sgtb int
foreach_localaddr(void * data,int (* pass1fn)(void *,struct sockaddr *),int (* betweenfn)(void *),int (* pass2fn)(void *,struct sockaddr *))920505d05c7Sgtb foreach_localaddr (/*@null@*/ void *data,
921505d05c7Sgtb 		   int (*pass1fn) (/*@null@*/ void *, struct sockaddr *) /*@*/,
922505d05c7Sgtb 		   /*@null@*/ int (*betweenfn) (/*@null@*/ void *) /*@*/,
923505d05c7Sgtb 		   /*@null@*/ int (*pass2fn) (/*@null@*/ void *,
924505d05c7Sgtb 					      struct sockaddr *) /*@*/)
925505d05c7Sgtb #if defined(DEBUG) || defined(TEST)
926505d05c7Sgtb      /*@modifies fileSystem@*/
927505d05c7Sgtb #endif
928505d05c7Sgtb {
929505d05c7Sgtb     struct ifreq *ifr, ifreq, *ifr2;
930505d05c7Sgtb     int s, code;
931505d05c7Sgtb     char *buf = 0;
932505d05c7Sgtb     size_t size, n, i, j;
933505d05c7Sgtb     int retval = 0;
934505d05c7Sgtb #ifdef LINUX_IPV6_HACK
935505d05c7Sgtb     struct linux_ipv6_addr_list *linux_ipv6_addrs = get_linux_ipv6_addrs ();
936505d05c7Sgtb     struct linux_ipv6_addr_list *lx_v6;
937505d05c7Sgtb #endif
938505d05c7Sgtb 
939505d05c7Sgtb     s = socket (USE_AF, USE_TYPE, USE_PROTO);
940505d05c7Sgtb     if (s < 0)
941505d05c7Sgtb 	return SOCKET_ERRNO;
942505d05c7Sgtb 
943505d05c7Sgtb     retval = get_ifreq_array(&buf, &n, s);
944505d05c7Sgtb     if (retval) {
945505d05c7Sgtb 	/*@-moduncon@*/ /* close() unknown to lclint */
946505d05c7Sgtb 	closesocket(s);
947505d05c7Sgtb 	/*@=moduncon@*/
948505d05c7Sgtb 	return retval;
949505d05c7Sgtb     }
950505d05c7Sgtb 
951505d05c7Sgtb     /* Note: Apparently some systems put the size (used or wanted?)
952505d05c7Sgtb        into the start of the buffer, just none that I'm actually
953505d05c7Sgtb        using.  Fix this when there's such a test system available.
954505d05c7Sgtb        The Samba mailing list archives mention that NTP looks for the
955505d05c7Sgtb        size on these systems: *-fujitsu-uxp* *-ncr-sysv4*
956505d05c7Sgtb        *-univel-sysv*.  */
957505d05c7Sgtb     for (i = 0; i + sizeof(struct ifreq) <= n; i+= ifreq_size(*ifr) ) {
958505d05c7Sgtb 	ifr = (struct ifreq *)((caddr_t) buf+i);
959505d05c7Sgtb 	/* In case ifreq_size is more than sizeof().  */
960505d05c7Sgtb 	if (i + ifreq_size(*ifr) > n)
961505d05c7Sgtb 	  break;
962505d05c7Sgtb 
963505d05c7Sgtb 	strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof (ifreq.ifr_name));
964505d05c7Sgtb 	Tprintf (("interface %s\n", ifreq.ifr_name));
965505d05c7Sgtb 	/*@-moduncon@*/ /* ioctl unknown to lclint */
966505d05c7Sgtb 	if (ioctl (s, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
967505d05c7Sgtb 	skip:
968505d05c7Sgtb 	    /* mark for next pass */
969505d05c7Sgtb 	    ifr->ifr_name[0] = '\0';
970505d05c7Sgtb 	    continue;
971505d05c7Sgtb 	}
972505d05c7Sgtb 	/*@=moduncon@*/
973505d05c7Sgtb 
974505d05c7Sgtb #ifdef IFF_LOOPBACK
975505d05c7Sgtb 	/* None of the current callers want loopback addresses.  */
976505d05c7Sgtb 	if (ifreq.ifr_flags & IFF_LOOPBACK) {
977505d05c7Sgtb 	    Tprintf (("  loopback\n"));
978505d05c7Sgtb 	    goto skip;
979505d05c7Sgtb 	}
980505d05c7Sgtb #endif
981505d05c7Sgtb 	/* Ignore interfaces that are down.  */
982505d05c7Sgtb 	if ((ifreq.ifr_flags & IFF_UP) == 0) {
983505d05c7Sgtb 	    Tprintf (("  down\n"));
984505d05c7Sgtb 	    goto skip;
985505d05c7Sgtb 	}
986505d05c7Sgtb 
987505d05c7Sgtb 	/* Make sure we didn't process this address already.  */
988505d05c7Sgtb 	for (j = 0; j < i; j += ifreq_size(*ifr2)) {
989505d05c7Sgtb 	    ifr2 = (struct ifreq *)((caddr_t) buf+j);
990505d05c7Sgtb 	    if (ifr2->ifr_name[0] == '\0')
991505d05c7Sgtb 		continue;
992505d05c7Sgtb 	    if (ifr2->ifr_addr.sa_family == ifr->ifr_addr.sa_family
993505d05c7Sgtb 		&& ifreq_size (*ifr) == ifreq_size (*ifr2)
994505d05c7Sgtb 		/* Compare address info.  If this isn't good enough --
995505d05c7Sgtb 		   i.e., if random padding bytes turn out to differ
996505d05c7Sgtb 		   when the addresses are the same -- then we'll have
997505d05c7Sgtb 		   to do it on a per address family basis.  */
998505d05c7Sgtb 		&& !memcmp (&ifr2->ifr_addr.sa_data, &ifr->ifr_addr.sa_data,
999505d05c7Sgtb 			    (ifreq_size (*ifr)
1000505d05c7Sgtb 			     - offsetof (struct ifreq, ifr_addr.sa_data)))) {
1001505d05c7Sgtb 		Tprintf (("  duplicate addr\n"));
1002505d05c7Sgtb 		goto skip;
1003505d05c7Sgtb 	    }
1004505d05c7Sgtb 	}
1005505d05c7Sgtb 
1006505d05c7Sgtb 	/*@-moduncon@*/
1007505d05c7Sgtb 	if ((*pass1fn) (data, &ifr->ifr_addr))
1008505d05c7Sgtb 	    goto punt;
1009505d05c7Sgtb 	/*@=moduncon@*/
1010505d05c7Sgtb     }
1011505d05c7Sgtb 
1012505d05c7Sgtb #ifdef LINUX_IPV6_HACK
1013505d05c7Sgtb     for (lx_v6 = linux_ipv6_addrs; lx_v6; lx_v6 = lx_v6->next)
1014505d05c7Sgtb 	if ((*pass1fn) (data, (struct sockaddr *) &lx_v6->addr))
1015505d05c7Sgtb 	    goto punt;
1016505d05c7Sgtb #endif
1017505d05c7Sgtb 
1018505d05c7Sgtb     /*@-moduncon@*/
1019505d05c7Sgtb     if (betweenfn != NULL && (*betweenfn)(data))
1020505d05c7Sgtb 	goto punt;
1021505d05c7Sgtb     /*@=moduncon@*/
1022505d05c7Sgtb 
1023505d05c7Sgtb     if (pass2fn) {
1024505d05c7Sgtb 	for (i = 0; i + sizeof(struct ifreq) <= n; i+= ifreq_size(*ifr) ) {
1025505d05c7Sgtb 	    ifr = (struct ifreq *)((caddr_t) buf+i);
1026505d05c7Sgtb 
1027505d05c7Sgtb 	    if (ifr->ifr_name[0] == '\0')
1028505d05c7Sgtb 		/* Marked in first pass to be ignored.  */
1029505d05c7Sgtb 		continue;
1030505d05c7Sgtb 
1031505d05c7Sgtb 	    /*@-moduncon@*/
1032505d05c7Sgtb 	    if ((*pass2fn) (data, &ifr->ifr_addr))
1033505d05c7Sgtb 		goto punt;
1034505d05c7Sgtb 	    /*@=moduncon@*/
1035505d05c7Sgtb 	}
1036505d05c7Sgtb #ifdef LINUX_IPV6_HACK
1037505d05c7Sgtb 	for (lx_v6 = linux_ipv6_addrs; lx_v6; lx_v6 = lx_v6->next)
1038505d05c7Sgtb 	    if ((*pass2fn) (data, (struct sockaddr *) &lx_v6->addr))
1039505d05c7Sgtb 		goto punt;
1040505d05c7Sgtb #endif
1041505d05c7Sgtb     }
1042505d05c7Sgtb  punt:
1043505d05c7Sgtb     /*@-moduncon@*/
1044505d05c7Sgtb     closesocket(s);
1045505d05c7Sgtb     /*@=moduncon@*/
1046505d05c7Sgtb     free (buf);
1047505d05c7Sgtb #ifdef LINUX_IPV6_HACK
1048505d05c7Sgtb     while (linux_ipv6_addrs) {
1049505d05c7Sgtb 	lx_v6 = linux_ipv6_addrs->next;
1050505d05c7Sgtb 	free (linux_ipv6_addrs);
1051505d05c7Sgtb 	linux_ipv6_addrs = lx_v6;
1052505d05c7Sgtb     }
1053505d05c7Sgtb #endif
1054505d05c7Sgtb 
1055505d05c7Sgtb     return retval;
1056505d05c7Sgtb }
1057505d05c7Sgtb 
1058505d05c7Sgtb #endif /* not HAVE_IFADDRS_H and not SIOCGLIFNUM */
1059505d05c7Sgtb 
10607c478bd9Sstevel@tonic-gate static krb5_error_code
10617c478bd9Sstevel@tonic-gate get_localaddrs (krb5_context context, krb5_address ***addr, int use_profile);
10627c478bd9Sstevel@tonic-gate 
1063505d05c7Sgtb #ifdef TEST
1064505d05c7Sgtb 
print_addr(void * dataptr,struct sockaddr * sa)1065505d05c7Sgtb static int print_addr (/*@unused@*/ void *dataptr, struct sockaddr *sa)
1066505d05c7Sgtb      /*@modifies fileSystem@*/
1067505d05c7Sgtb {
1068505d05c7Sgtb     char hostbuf[NI_MAXHOST];
1069505d05c7Sgtb     int err;
1070505d05c7Sgtb     socklen_t len;
1071505d05c7Sgtb 
1072505d05c7Sgtb     printf ("  --> family %2d ", sa->sa_family);
1073505d05c7Sgtb     len = socklen (sa);
1074505d05c7Sgtb     err = getnameinfo (sa, len, hostbuf, (socklen_t) sizeof (hostbuf),
1075505d05c7Sgtb 		       (char *) NULL, 0, NI_NUMERICHOST);
1076505d05c7Sgtb     if (err) {
1077505d05c7Sgtb 	int e = errno;
1078505d05c7Sgtb 	printf ("<getnameinfo error %d: %s>\n", err, gai_strerror (err));
1079505d05c7Sgtb 	if (err == EAI_SYSTEM)
1080505d05c7Sgtb 	    printf ("\t\t<errno is %d: %s>\n", e, strerror(e));
1081505d05c7Sgtb     } else
1082505d05c7Sgtb 	printf ("addr %s\n", hostbuf);
1083505d05c7Sgtb     return 0;
1084505d05c7Sgtb }
1085505d05c7Sgtb 
main()1086505d05c7Sgtb int main ()
1087505d05c7Sgtb {
1088505d05c7Sgtb     int r;
1089505d05c7Sgtb 
1090505d05c7Sgtb     (void) setvbuf (stdout, (char *)NULL, _IONBF, 0);
1091505d05c7Sgtb     r = foreach_localaddr (0, print_addr, NULL, NULL);
1092505d05c7Sgtb     printf ("return value = %d\n", r);
1093505d05c7Sgtb     return 0;
1094505d05c7Sgtb }
1095505d05c7Sgtb 
1096505d05c7Sgtb #else /* not TESTing */
1097505d05c7Sgtb 
10987c478bd9Sstevel@tonic-gate struct localaddr_data {
10997c478bd9Sstevel@tonic-gate     int count, mem_err, cur_idx, cur_size;
11007c478bd9Sstevel@tonic-gate     krb5_address **addr_temp;
11017c478bd9Sstevel@tonic-gate };
11027c478bd9Sstevel@tonic-gate 
11037c478bd9Sstevel@tonic-gate static int
count_addrs(void * P_data,struct sockaddr * a)11047c478bd9Sstevel@tonic-gate count_addrs (void *P_data, struct sockaddr *a)
11057c478bd9Sstevel@tonic-gate      /*@*/
11067c478bd9Sstevel@tonic-gate {
11077c478bd9Sstevel@tonic-gate     struct localaddr_data *data = P_data;
11087c478bd9Sstevel@tonic-gate     switch (a->sa_family) {
11097c478bd9Sstevel@tonic-gate     case AF_INET:
11107c478bd9Sstevel@tonic-gate #ifdef KRB5_USE_INET6
11117c478bd9Sstevel@tonic-gate     case AF_INET6:
11127c478bd9Sstevel@tonic-gate #endif
11137c478bd9Sstevel@tonic-gate #ifdef KRB5_USE_NS
11147c478bd9Sstevel@tonic-gate     case AF_XNS:
11157c478bd9Sstevel@tonic-gate #endif
11167c478bd9Sstevel@tonic-gate 	data->count++;
11177c478bd9Sstevel@tonic-gate 	break;
11187c478bd9Sstevel@tonic-gate     default:
11197c478bd9Sstevel@tonic-gate 	break;
11207c478bd9Sstevel@tonic-gate     }
11217c478bd9Sstevel@tonic-gate     return 0;
11227c478bd9Sstevel@tonic-gate }
11237c478bd9Sstevel@tonic-gate 
11247c478bd9Sstevel@tonic-gate static int
allocate(void * P_data)11257c478bd9Sstevel@tonic-gate allocate (void *P_data)
1126505d05c7Sgtb      /*@*/
11277c478bd9Sstevel@tonic-gate {
11287c478bd9Sstevel@tonic-gate     struct localaddr_data *data = P_data;
11297c478bd9Sstevel@tonic-gate     int i;
11307c478bd9Sstevel@tonic-gate     void *n;
11317c478bd9Sstevel@tonic-gate 
11327c478bd9Sstevel@tonic-gate     n = realloc (data->addr_temp,
11337c478bd9Sstevel@tonic-gate 		 (1 + data->count + data->cur_idx) * sizeof (krb5_address *));
11347c478bd9Sstevel@tonic-gate     if (n == 0) {
11357c478bd9Sstevel@tonic-gate 	data->mem_err++;
11367c478bd9Sstevel@tonic-gate 	return 1;
11377c478bd9Sstevel@tonic-gate     }
11387c478bd9Sstevel@tonic-gate     data->addr_temp = n;
11397c478bd9Sstevel@tonic-gate     data->cur_size = 1 + data->count + data->cur_idx;
11407c478bd9Sstevel@tonic-gate     for (i = data->cur_idx; i <= data->count + data->cur_idx; i++)
11417c478bd9Sstevel@tonic-gate 	data->addr_temp[i] = 0;
11427c478bd9Sstevel@tonic-gate     return 0;
11437c478bd9Sstevel@tonic-gate }
11447c478bd9Sstevel@tonic-gate 
1145505d05c7Sgtb static /*@null@*/ krb5_address *
make_addr(int type,size_t length,const void * contents)11467c478bd9Sstevel@tonic-gate make_addr (int type, size_t length, const void *contents)
1147505d05c7Sgtb     /*@*/
11487c478bd9Sstevel@tonic-gate {
11497c478bd9Sstevel@tonic-gate     krb5_address *a;
11507c478bd9Sstevel@tonic-gate     void *data;
11517c478bd9Sstevel@tonic-gate 
11527c478bd9Sstevel@tonic-gate     data = malloc (length);
11537c478bd9Sstevel@tonic-gate     if (data == NULL)
11547c478bd9Sstevel@tonic-gate 	return NULL;
11557c478bd9Sstevel@tonic-gate     a = malloc (sizeof (krb5_address));
11567c478bd9Sstevel@tonic-gate     if (a == NULL) {
11577c478bd9Sstevel@tonic-gate 	free (data);
11587c478bd9Sstevel@tonic-gate 	return NULL;
11597c478bd9Sstevel@tonic-gate     }
11607c478bd9Sstevel@tonic-gate     memcpy (data, contents, length);
11617c478bd9Sstevel@tonic-gate     a->magic = KV5M_ADDRESS;
11627c478bd9Sstevel@tonic-gate     a->addrtype = type;
11637c478bd9Sstevel@tonic-gate     a->length = length;
11647c478bd9Sstevel@tonic-gate     a->contents = data;
11657c478bd9Sstevel@tonic-gate     return a;
11667c478bd9Sstevel@tonic-gate }
11677c478bd9Sstevel@tonic-gate 
11687c478bd9Sstevel@tonic-gate static int
add_addr(void * P_data,struct sockaddr * a)11697c478bd9Sstevel@tonic-gate add_addr (void *P_data, struct sockaddr *a)
11707c478bd9Sstevel@tonic-gate      /*@modifies *P_data@*/
11717c478bd9Sstevel@tonic-gate {
11727c478bd9Sstevel@tonic-gate     struct localaddr_data *data = P_data;
1173505d05c7Sgtb     /*@null@*/ krb5_address *address = 0;
11747c478bd9Sstevel@tonic-gate 
11757c478bd9Sstevel@tonic-gate     switch (a->sa_family) {
11767c478bd9Sstevel@tonic-gate #ifdef HAVE_NETINET_IN_H
11777c478bd9Sstevel@tonic-gate     case AF_INET:
11787c478bd9Sstevel@tonic-gate 	address = make_addr (ADDRTYPE_INET, sizeof (struct in_addr),
11797c478bd9Sstevel@tonic-gate 			     &((const struct sockaddr_in *) a)->sin_addr);
11807c478bd9Sstevel@tonic-gate 	if (address == NULL)
11817c478bd9Sstevel@tonic-gate 	    data->mem_err++;
11827c478bd9Sstevel@tonic-gate 	break;
11837c478bd9Sstevel@tonic-gate 
11847c478bd9Sstevel@tonic-gate #ifdef KRB5_USE_INET6
11857c478bd9Sstevel@tonic-gate     case AF_INET6:
11867c478bd9Sstevel@tonic-gate     {
11877c478bd9Sstevel@tonic-gate 	const struct sockaddr_in6 *in = (const struct sockaddr_in6 *) a;
1188*55fea89dSDan Cross 
1189505d05c7Sgtb 	if (IN6_IS_ADDR_LINKLOCAL (&in->sin6_addr))
11907c478bd9Sstevel@tonic-gate 	    break;
11917c478bd9Sstevel@tonic-gate 
11927c478bd9Sstevel@tonic-gate 	address = make_addr (ADDRTYPE_INET6, sizeof (struct in6_addr),
11937c478bd9Sstevel@tonic-gate 			     &in->sin6_addr);
11947c478bd9Sstevel@tonic-gate 	if (address == NULL)
11957c478bd9Sstevel@tonic-gate 	    data->mem_err++;
11967c478bd9Sstevel@tonic-gate 	break;
11977c478bd9Sstevel@tonic-gate     }
11987c478bd9Sstevel@tonic-gate #endif /* KRB5_USE_INET6 */
11997c478bd9Sstevel@tonic-gate #endif /* netinet/in.h */
12007c478bd9Sstevel@tonic-gate 
12017c478bd9Sstevel@tonic-gate #ifdef KRB5_USE_NS
12027c478bd9Sstevel@tonic-gate     case AF_XNS:
12037c478bd9Sstevel@tonic-gate 	address = make_addr (ADDRTYPE_XNS, sizeof (struct ns_addr),
12047c478bd9Sstevel@tonic-gate 			     &((const struct sockaddr_ns *)a)->sns_addr);
12057c478bd9Sstevel@tonic-gate 	if (address == NULL)
12067c478bd9Sstevel@tonic-gate 	    data->mem_err++;
12077c478bd9Sstevel@tonic-gate 	break;
12087c478bd9Sstevel@tonic-gate #endif
12097c478bd9Sstevel@tonic-gate 
12107c478bd9Sstevel@tonic-gate #ifdef AF_LINK
12117c478bd9Sstevel@tonic-gate 	/* Some BSD-based systems (e.g. NetBSD 1.5) and AIX will
12127c478bd9Sstevel@tonic-gate 	   include the ethernet address, but we don't want that, at
12137c478bd9Sstevel@tonic-gate 	   least for now.  */
12147c478bd9Sstevel@tonic-gate     case AF_LINK:
12157c478bd9Sstevel@tonic-gate 	break;
12167c478bd9Sstevel@tonic-gate #endif
12177c478bd9Sstevel@tonic-gate     /*
12187c478bd9Sstevel@tonic-gate      * Add more address families here..
12197c478bd9Sstevel@tonic-gate      */
12207c478bd9Sstevel@tonic-gate     default:
12217c478bd9Sstevel@tonic-gate 	break;
12227c478bd9Sstevel@tonic-gate     }
12237c478bd9Sstevel@tonic-gate #ifdef __LCLINT__
12247c478bd9Sstevel@tonic-gate     /* Redundant but unconditional store un-confuses lclint.  */
12257c478bd9Sstevel@tonic-gate     data->addr_temp[data->cur_idx] = address;
12267c478bd9Sstevel@tonic-gate #endif
12277c478bd9Sstevel@tonic-gate     if (address) {
12287c478bd9Sstevel@tonic-gate 	data->addr_temp[data->cur_idx++] = address;
12297c478bd9Sstevel@tonic-gate     }
12307c478bd9Sstevel@tonic-gate 
12317c478bd9Sstevel@tonic-gate     return data->mem_err;
12327c478bd9Sstevel@tonic-gate }
12337c478bd9Sstevel@tonic-gate 
12347c478bd9Sstevel@tonic-gate static krb5_error_code
krb5_os_localaddr_profile(krb5_context context,struct localaddr_data * datap)12357c478bd9Sstevel@tonic-gate krb5_os_localaddr_profile (krb5_context context, struct localaddr_data *datap)
12367c478bd9Sstevel@tonic-gate {
12377c478bd9Sstevel@tonic-gate     krb5_error_code err;
1238505d05c7Sgtb     static const char *const profile_name[] = {
12397c478bd9Sstevel@tonic-gate 	"libdefaults", "extra_addresses", 0
12407c478bd9Sstevel@tonic-gate     };
12417c478bd9Sstevel@tonic-gate     char **values;
12427c478bd9Sstevel@tonic-gate     char **iter;
12437c478bd9Sstevel@tonic-gate     krb5_address **newaddrs;
12447c478bd9Sstevel@tonic-gate 
1245505d05c7Sgtb #ifdef DEBUG
1246505d05c7Sgtb     fprintf (stderr, "looking up extra_addresses foo\n");
1247505d05c7Sgtb #endif
1248505d05c7Sgtb 
12497c478bd9Sstevel@tonic-gate     err = profile_get_values (context->profile, profile_name, &values);
12507c478bd9Sstevel@tonic-gate     /* Ignore all errors for now?  */
12517c478bd9Sstevel@tonic-gate     if (err)
12527c478bd9Sstevel@tonic-gate 	return 0;
12537c478bd9Sstevel@tonic-gate 
12547c478bd9Sstevel@tonic-gate     for (iter = values; *iter; iter++) {
12557c478bd9Sstevel@tonic-gate 	char *cp = *iter, *next, *current;
12567c478bd9Sstevel@tonic-gate 	int i, count;
12577c478bd9Sstevel@tonic-gate 
1258505d05c7Sgtb #ifdef DEBUG
1259505d05c7Sgtb 	fprintf (stderr, "  found line: '%s'\n", cp);
1260505d05c7Sgtb #endif
1261505d05c7Sgtb 
12627c478bd9Sstevel@tonic-gate 	for (cp = *iter, next = 0; *cp; cp = next) {
12637c478bd9Sstevel@tonic-gate 	    while (isspace ((int) *cp) || *cp == ',')
12647c478bd9Sstevel@tonic-gate 		cp++;
12657c478bd9Sstevel@tonic-gate 	    if (*cp == 0)
12667c478bd9Sstevel@tonic-gate 		break;
12677c478bd9Sstevel@tonic-gate 	    /* Start of an address.  */
1268505d05c7Sgtb #ifdef DEBUG
1269505d05c7Sgtb 	    fprintf (stderr, "    addr found in '%s'\n", cp);
1270505d05c7Sgtb #endif
12717c478bd9Sstevel@tonic-gate 	    current = cp;
12727c478bd9Sstevel@tonic-gate 	    while (*cp != 0 && !isspace((int) *cp) && *cp != ',')
12737c478bd9Sstevel@tonic-gate 		cp++;
12747c478bd9Sstevel@tonic-gate 	    if (*cp != 0) {
12757c478bd9Sstevel@tonic-gate 		next = cp + 1;
12767c478bd9Sstevel@tonic-gate 		*cp = 0;
12777c478bd9Sstevel@tonic-gate 	    } else
12787c478bd9Sstevel@tonic-gate 		next = cp;
12797c478bd9Sstevel@tonic-gate 	    /* Got a single address, process it.  */
1280505d05c7Sgtb #ifdef DEBUG
1281505d05c7Sgtb 	    fprintf (stderr, "    processing '%s'\n", current);
1282505d05c7Sgtb #endif
12837c478bd9Sstevel@tonic-gate 	    newaddrs = 0;
12847c478bd9Sstevel@tonic-gate 	    err = krb5_os_hostaddr (context, current, &newaddrs);
12857c478bd9Sstevel@tonic-gate 	    if (err)
12867c478bd9Sstevel@tonic-gate 		continue;
12877c478bd9Sstevel@tonic-gate 	    for (i = 0; newaddrs[i]; i++) {
1288505d05c7Sgtb #ifdef DEBUG
1289505d05c7Sgtb 		fprintf (stderr, "    %d: family %d", i,
1290505d05c7Sgtb 			 newaddrs[i]->addrtype);
1291505d05c7Sgtb 		fprintf (stderr, "\n");
1292505d05c7Sgtb #endif
12937c478bd9Sstevel@tonic-gate 	    }
12947c478bd9Sstevel@tonic-gate 	    count = i;
1295505d05c7Sgtb #ifdef DEBUG
1296505d05c7Sgtb 	    fprintf (stderr, "    %d addresses\n", count);
1297505d05c7Sgtb #endif
12987c478bd9Sstevel@tonic-gate 	    if (datap->cur_idx + count >= datap->cur_size) {
12997c478bd9Sstevel@tonic-gate 		krb5_address **bigger;
13007c478bd9Sstevel@tonic-gate 		bigger = realloc (datap->addr_temp,
13017c478bd9Sstevel@tonic-gate 				  sizeof (krb5_address *) * (datap->cur_idx + count));
13027c478bd9Sstevel@tonic-gate 		if (bigger) {
13037c478bd9Sstevel@tonic-gate 		    datap->addr_temp = bigger;
13047c478bd9Sstevel@tonic-gate 		    datap->cur_size = datap->cur_idx + count;
13057c478bd9Sstevel@tonic-gate 		}
13067c478bd9Sstevel@tonic-gate 	    }
13077c478bd9Sstevel@tonic-gate 	    for (i = 0; i < count; i++) {
13087c478bd9Sstevel@tonic-gate 		if (datap->cur_idx < datap->cur_size)
13097c478bd9Sstevel@tonic-gate 		    datap->addr_temp[datap->cur_idx++] = newaddrs[i];
13107c478bd9Sstevel@tonic-gate 		else
13117c478bd9Sstevel@tonic-gate 		    free (newaddrs[i]->contents), free (newaddrs[i]);
13127c478bd9Sstevel@tonic-gate 	    }
13137c478bd9Sstevel@tonic-gate 	    free (newaddrs);
13147c478bd9Sstevel@tonic-gate 	}
13157c478bd9Sstevel@tonic-gate     }
13167c478bd9Sstevel@tonic-gate     return 0;
13177c478bd9Sstevel@tonic-gate }
13187c478bd9Sstevel@tonic-gate 
13197c478bd9Sstevel@tonic-gate krb5_error_code KRB5_CALLCONV
krb5_os_localaddr(krb5_context context,krb5_address *** addr)13207c478bd9Sstevel@tonic-gate krb5_os_localaddr(krb5_context context, krb5_address ***addr)
13217c478bd9Sstevel@tonic-gate {
13227c478bd9Sstevel@tonic-gate     return get_localaddrs(context, addr, 1);
13237c478bd9Sstevel@tonic-gate }
13247c478bd9Sstevel@tonic-gate 
13257c478bd9Sstevel@tonic-gate krb5_error_code
krb5int_local_addresses(krb5_context context,krb5_address *** addr)13267c478bd9Sstevel@tonic-gate krb5int_local_addresses(krb5_context context, krb5_address ***addr)
13277c478bd9Sstevel@tonic-gate {
13287c478bd9Sstevel@tonic-gate     return get_localaddrs(context, addr, 0);
13297c478bd9Sstevel@tonic-gate }
13307c478bd9Sstevel@tonic-gate 
13317c478bd9Sstevel@tonic-gate static krb5_error_code
get_localaddrs(krb5_context context,krb5_address *** addr,int use_profile)13327c478bd9Sstevel@tonic-gate get_localaddrs (krb5_context context, krb5_address ***addr, int use_profile)
13337c478bd9Sstevel@tonic-gate {
13347c478bd9Sstevel@tonic-gate     struct localaddr_data data = { 0 };
13357c478bd9Sstevel@tonic-gate     int r;
1336505d05c7Sgtb     krb5_error_code err;
13377c478bd9Sstevel@tonic-gate 
13387c478bd9Sstevel@tonic-gate     if (use_profile) {
1339505d05c7Sgtb 	err = krb5_os_localaddr_profile (context, &data);
13407c478bd9Sstevel@tonic-gate 	/* ignore err for now */
13417c478bd9Sstevel@tonic-gate     }
13427c478bd9Sstevel@tonic-gate 
13437c478bd9Sstevel@tonic-gate     r = foreach_localaddr (&data, count_addrs, allocate, add_addr);
13447c478bd9Sstevel@tonic-gate     if (r != 0) {
13457c478bd9Sstevel@tonic-gate 	int i;
13467c478bd9Sstevel@tonic-gate 	if (data.addr_temp) {
13477c478bd9Sstevel@tonic-gate 	    for (i = 0; i < data.count; i++)
13487c478bd9Sstevel@tonic-gate 		krb5_xfree (data.addr_temp[i]);
13497c478bd9Sstevel@tonic-gate 	    free (data.addr_temp);
13507c478bd9Sstevel@tonic-gate 	}
13517c478bd9Sstevel@tonic-gate 	if (data.mem_err)
13527c478bd9Sstevel@tonic-gate 	    return ENOMEM;
13537c478bd9Sstevel@tonic-gate 	else
13547c478bd9Sstevel@tonic-gate 	    return r;
13557c478bd9Sstevel@tonic-gate     }
13567c478bd9Sstevel@tonic-gate 
13577c478bd9Sstevel@tonic-gate     data.cur_idx++; /* null termination */
13587c478bd9Sstevel@tonic-gate     if (data.mem_err)
13597c478bd9Sstevel@tonic-gate 	return ENOMEM;
13607c478bd9Sstevel@tonic-gate     else if (data.cur_idx == data.count)
13617c478bd9Sstevel@tonic-gate 	*addr = data.addr_temp;
13627c478bd9Sstevel@tonic-gate     else {
13637c478bd9Sstevel@tonic-gate 	/* This can easily happen if we have IPv6 link-local
13647c478bd9Sstevel@tonic-gate 	   addresses.  Just shorten the array.  */
13657c478bd9Sstevel@tonic-gate 	*addr = (krb5_address **) realloc (data.addr_temp,
13667c478bd9Sstevel@tonic-gate 					   (sizeof (krb5_address *)
13677c478bd9Sstevel@tonic-gate 					    * data.cur_idx));
13687c478bd9Sstevel@tonic-gate 	if (*addr == 0)
13697c478bd9Sstevel@tonic-gate 	    /* Okay, shortening failed, but the original should still
13707c478bd9Sstevel@tonic-gate 	       be intact.  */
13717c478bd9Sstevel@tonic-gate 	    *addr = data.addr_temp;
13727c478bd9Sstevel@tonic-gate     }
13737c478bd9Sstevel@tonic-gate 
1374505d05c7Sgtb #ifdef DEBUG
1375505d05c7Sgtb     {
1376505d05c7Sgtb 	int j;
1377505d05c7Sgtb 	fprintf (stderr, "addresses:\n");
1378505d05c7Sgtb 	for (j = 0; addr[0][j]; j++) {
1379505d05c7Sgtb 	    struct sockaddr_storage ss;
1380505d05c7Sgtb 	    int err2;
1381505d05c7Sgtb 	    char namebuf[NI_MAXHOST];
1382505d05c7Sgtb 	    void *addrp = 0;
1383505d05c7Sgtb 
1384505d05c7Sgtb 	    fprintf (stderr, "%2d: ", j);
1385505d05c7Sgtb 	    fprintf (stderr, "addrtype %2d, length %2d", addr[0][j]->addrtype,
1386505d05c7Sgtb 		     addr[0][j]->length);
1387505d05c7Sgtb 	    memset (&ss, 0, sizeof (ss));
1388505d05c7Sgtb 	    switch (addr[0][j]->addrtype) {
1389505d05c7Sgtb 	    case ADDRTYPE_INET:
1390505d05c7Sgtb 	    {
1391505d05c7Sgtb 		struct sockaddr_in *sinp = ss2sin (&ss);
1392505d05c7Sgtb 		sinp->sin_family = AF_INET;
1393505d05c7Sgtb 		addrp = &sinp->sin_addr;
1394505d05c7Sgtb #ifdef HAVE_SA_LEN
1395505d05c7Sgtb 		sinp->sin_len = sizeof (struct sockaddr_in);
1396505d05c7Sgtb #endif
1397505d05c7Sgtb 		break;
1398505d05c7Sgtb 	    }
1399505d05c7Sgtb #ifdef KRB5_USE_INET6
1400505d05c7Sgtb 	    case ADDRTYPE_INET6:
1401505d05c7Sgtb 	    {
1402505d05c7Sgtb 		struct sockaddr_in6 *sin6p = ss2sin6 (&ss);
1403505d05c7Sgtb 		sin6p->sin6_family = AF_INET6;
1404505d05c7Sgtb 		addrp = &sin6p->sin6_addr;
1405505d05c7Sgtb #ifdef HAVE_SA_LEN
1406505d05c7Sgtb 		sin6p->sin6_len = sizeof (struct sockaddr_in6);
1407505d05c7Sgtb #endif
1408505d05c7Sgtb 		break;
1409505d05c7Sgtb 	    }
1410505d05c7Sgtb #endif
1411505d05c7Sgtb 	    default:
1412505d05c7Sgtb 		ss2sa(&ss)->sa_family = 0;
1413505d05c7Sgtb 		break;
1414505d05c7Sgtb 	    }
1415505d05c7Sgtb 	    if (addrp)
1416505d05c7Sgtb 		memcpy (addrp, addr[0][j]->contents, addr[0][j]->length);
1417505d05c7Sgtb 	    err2 = getnameinfo (ss2sa(&ss), socklen (ss2sa (&ss)),
1418505d05c7Sgtb 				namebuf, sizeof (namebuf), 0, 0,
1419505d05c7Sgtb 				NI_NUMERICHOST);
1420505d05c7Sgtb 	    if (err2 == 0)
1421505d05c7Sgtb 		fprintf (stderr, ": addr %s\n", namebuf);
1422505d05c7Sgtb 	    else
1423505d05c7Sgtb 		fprintf (stderr, ": getnameinfo error %d\n", err2);
1424505d05c7Sgtb 	}
1425505d05c7Sgtb     }
1426505d05c7Sgtb #endif
1427505d05c7Sgtb 
14287c478bd9Sstevel@tonic-gate     return 0;
14297c478bd9Sstevel@tonic-gate }
14307c478bd9Sstevel@tonic-gate 
1431505d05c7Sgtb #endif /* not TESTing */
1432505d05c7Sgtb 
1433505d05c7Sgtb #else /* Windows/Mac version */
1434505d05c7Sgtb 
1435505d05c7Sgtb /*
1436505d05c7Sgtb  * Hold on to your lunch!  Backup kludge method of obtaining your
1437505d05c7Sgtb  * local IP address, courtesy of Windows Socket Network Programming,
1438505d05c7Sgtb  * by Robert Quinn
1439505d05c7Sgtb  */
1440505d05c7Sgtb #if defined(_WIN32)
local_addr_fallback_kludge()1441505d05c7Sgtb static struct hostent *local_addr_fallback_kludge()
1442505d05c7Sgtb {
1443505d05c7Sgtb 	static struct hostent	host;
1444505d05c7Sgtb 	static SOCKADDR_IN	addr;
1445505d05c7Sgtb 	static char *		ip_ptrs[2];
1446505d05c7Sgtb 	SOCKET			sock;
1447505d05c7Sgtb 	int			size = sizeof(SOCKADDR);
1448505d05c7Sgtb 	int			err;
1449505d05c7Sgtb 
1450505d05c7Sgtb 	sock = socket(AF_INET, SOCK_DGRAM, 0);
1451505d05c7Sgtb 	if (sock == INVALID_SOCKET)
1452505d05c7Sgtb 		return NULL;
1453505d05c7Sgtb 
1454505d05c7Sgtb 	/* connect to arbitrary port and address (NOT loopback) */
1455505d05c7Sgtb 	addr.sin_family = AF_INET;
1456505d05c7Sgtb 	addr.sin_port = htons(IPPORT_ECHO);
1457505d05c7Sgtb 	addr.sin_addr.s_addr = inet_addr("204.137.220.51");
1458505d05c7Sgtb 
1459505d05c7Sgtb 	err = connect(sock, (LPSOCKADDR) &addr, sizeof(SOCKADDR));
1460505d05c7Sgtb 	if (err == SOCKET_ERROR)
1461505d05c7Sgtb 		return NULL;
1462505d05c7Sgtb 
1463505d05c7Sgtb 	err = getsockname(sock, (LPSOCKADDR) &addr, (int *) size);
1464505d05c7Sgtb 	if (err == SOCKET_ERROR)
1465505d05c7Sgtb 		return NULL;
1466505d05c7Sgtb 
1467505d05c7Sgtb 	closesocket(sock);
1468505d05c7Sgtb 
1469505d05c7Sgtb 	host.h_name = 0;
1470505d05c7Sgtb 	host.h_aliases = 0;
1471505d05c7Sgtb 	host.h_addrtype = AF_INET;
1472505d05c7Sgtb 	host.h_length = 4;
1473505d05c7Sgtb 	host.h_addr_list = ip_ptrs;
1474505d05c7Sgtb 	ip_ptrs[0] = (char *) &addr.sin_addr.s_addr;
1475505d05c7Sgtb 	ip_ptrs[1] = NULL;
1476505d05c7Sgtb 
1477505d05c7Sgtb 	return &host;
1478505d05c7Sgtb }
1479505d05c7Sgtb #endif
1480505d05c7Sgtb 
1481*55fea89dSDan Cross /* No ioctls in winsock so we just assume there is only one networking
1482*55fea89dSDan Cross  * card per machine, so gethostent is good enough.
1483505d05c7Sgtb  */
1484505d05c7Sgtb krb5_error_code KRB5_CALLCONV
krb5_os_localaddr(krb5_context context,krb5_address *** addr)1485505d05c7Sgtb krb5_os_localaddr (krb5_context context, krb5_address ***addr) {
1486505d05c7Sgtb     char host[64];                              /* Name of local machine */
1487505d05c7Sgtb     struct hostent *hostrec;
1488505d05c7Sgtb     int err, count, i;
1489505d05c7Sgtb     krb5_address ** paddr;
1490505d05c7Sgtb 
1491505d05c7Sgtb     *addr = 0;
1492505d05c7Sgtb     paddr = 0;
1493505d05c7Sgtb     err = 0;
1494*55fea89dSDan Cross 
1495505d05c7Sgtb     if (gethostname (host, sizeof(host))) {
1496505d05c7Sgtb         err = SOCKET_ERRNO;
1497505d05c7Sgtb     }
1498505d05c7Sgtb 
1499505d05c7Sgtb     if (!err) {
1500505d05c7Sgtb 	    hostrec = gethostbyname (host);
1501505d05c7Sgtb 	    if (hostrec == NULL) {
1502505d05c7Sgtb 		    err = SOCKET_ERRNO;
1503505d05c7Sgtb 	    }
1504505d05c7Sgtb     }
1505505d05c7Sgtb 
1506505d05c7Sgtb     if (err) {
1507505d05c7Sgtb 	    hostrec = local_addr_fallback_kludge();
1508505d05c7Sgtb 	    if (!hostrec)
1509505d05c7Sgtb 		    return err;
1510505d05c7Sgtb 		else
1511505d05c7Sgtb 			err = 0;  /* otherwise we will die at cleanup */
1512505d05c7Sgtb     }
1513505d05c7Sgtb 
1514505d05c7Sgtb     for (count = 0; hostrec->h_addr_list[count]; count++);
1515505d05c7Sgtb 
1516505d05c7Sgtb 
1517505d05c7Sgtb     paddr = (krb5_address **)malloc(sizeof(krb5_address *) * (count+1));
1518505d05c7Sgtb     if (!paddr) {
1519505d05c7Sgtb         err = ENOMEM;
1520505d05c7Sgtb         goto cleanup;
1521505d05c7Sgtb     }
1522505d05c7Sgtb 
1523505d05c7Sgtb     memset(paddr, 0, sizeof(krb5_address *) * (count+1));
1524505d05c7Sgtb 
1525505d05c7Sgtb     for (i = 0; i < count; i++)
1526505d05c7Sgtb     {
1527505d05c7Sgtb         paddr[i] = (krb5_address *)malloc(sizeof(krb5_address));
1528505d05c7Sgtb         if (paddr[i] == NULL) {
1529505d05c7Sgtb             err = ENOMEM;
1530505d05c7Sgtb             goto cleanup;
1531505d05c7Sgtb         }
1532505d05c7Sgtb 
1533505d05c7Sgtb         paddr[i]->magic = KV5M_ADDRESS;
1534505d05c7Sgtb         paddr[i]->addrtype = hostrec->h_addrtype;
1535505d05c7Sgtb         paddr[i]->length = hostrec->h_length;
1536505d05c7Sgtb         paddr[i]->contents = (unsigned char *)malloc(paddr[i]->length);
1537505d05c7Sgtb         if (!paddr[i]->contents) {
1538505d05c7Sgtb             err = ENOMEM;
1539505d05c7Sgtb             goto cleanup;
1540505d05c7Sgtb         }
1541505d05c7Sgtb         memcpy(paddr[i]->contents,
1542505d05c7Sgtb                hostrec->h_addr_list[i],
1543505d05c7Sgtb                paddr[i]->length);
1544505d05c7Sgtb     }
1545505d05c7Sgtb 
1546505d05c7Sgtb  cleanup:
1547505d05c7Sgtb     if (err) {
1548505d05c7Sgtb         if (paddr) {
1549505d05c7Sgtb             for (i = 0; i < count; i++)
1550505d05c7Sgtb             {
1551505d05c7Sgtb                 if (paddr[i]) {
1552505d05c7Sgtb                     if (paddr[i]->contents)
1553505d05c7Sgtb                         free(paddr[i]->contents);
1554505d05c7Sgtb                     free(paddr[i]);
1555505d05c7Sgtb                 }
1556505d05c7Sgtb             }
1557505d05c7Sgtb             free(paddr);
1558505d05c7Sgtb         }
1559505d05c7Sgtb     }
1560505d05c7Sgtb     else
1561505d05c7Sgtb         *addr = paddr;
1562505d05c7Sgtb 
1563505d05c7Sgtb     return(err);
1564505d05c7Sgtb }
1565505d05c7Sgtb #endif
1566