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