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