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