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 return sa2sin(sa)->sin_addr.s_addr == htonl(INADDR_LOOPBACK);
186 case AF_INET6:
187 return IN6_IS_ADDR_LOOPBACK(&sa2sin6(sa)->sin6_addr);
188 default:
189 return 0;
190 }
191 }
192 #endif
193
194 #ifdef HAVE_IFADDRS_H
195 #include <ifaddrs.h>
196
197 #ifdef DEBUG
198 void
printifaddr(struct ifaddrs * ifp)199 printifaddr(struct ifaddrs *ifp)
200 {
201 printf ("%p={\n", ifp);
202 /* printf ("\tnext=%p\n", ifp->ifa_next); */
203 printf ("\tname=%s\n", ifp->ifa_name);
204 printf ("\tflags=");
205 {
206 int ch, flags = ifp->ifa_flags;
207 printf ("%x", flags);
208 ch = '<';
209 #define X(F) if (flags & IFF_##F) { printf ("%c%s", ch, #F); flags &= ~IFF_##F; ch = ','; }
210 X (UP); X (BROADCAST); X (DEBUG); X (LOOPBACK); X (POINTOPOINT);
211 X (NOTRAILERS); X (RUNNING); X (NOARP); X (PROMISC); X (ALLMULTI);
212 #ifdef IFF_OACTIVE
213 X (OACTIVE);
214 #endif
215 #ifdef IFF_SIMPLE
216 X (SIMPLEX);
217 #endif
218 X (MULTICAST);
219 printf (">");
220 #undef X
221 }
222 if (ifp->ifa_addr)
223 printf ("\n\taddr="), printaddr (ifp->ifa_addr);
224 if (ifp->ifa_netmask)
225 printf ("\n\tnetmask="), printaddr (ifp->ifa_netmask);
226 if (ifp->ifa_broadaddr)
227 printf ("\n\tbroadaddr="), printaddr (ifp->ifa_broadaddr);
228 if (ifp->ifa_dstaddr)
229 printf ("\n\tdstaddr="), printaddr (ifp->ifa_dstaddr);
230 if (ifp->ifa_data)
231 printf ("\n\tdata=%p", ifp->ifa_data);
232 printf ("\n}\n");
233 }
234 #endif /* DEBUG */
235
236 #include <string.h>
237 #include <stdlib.h>
238
239 static int
addr_eq(struct sockaddr * s1,struct sockaddr * s2)240 addr_eq (struct sockaddr *s1, struct sockaddr *s2)
241 {
242 if (s1->sa_family != s2->sa_family)
243 return 0;
244 switch (s1->sa_family) {
245 case AF_INET:
246 return !memcmp(&sa2sin(s1)->sin_addr, &sa2sin(s2)->sin_addr,
247 sizeof(sa2sin(s1)->sin_addr));
248 case AF_INET6:
249 return !memcmp(&sa2sin6(s1)->sin6_addr, &sa2sin6(s2)->sin6_addr,
250 sizeof(sa2sin6(s1)->sin6_addr));
251 default:
252 /* Err on side of duplicate listings. */
253 return 0;
254 }
255 }
256 #endif
257
258 #ifndef HAVE_IFADDRS_H
259 /*@-usereleased@*/ /* lclint doesn't understand realloc */
260 static /*@null@*/ void *
grow_or_free(void * ptr,size_t newsize)261 grow_or_free (/*@only@*/ void *ptr, size_t newsize)
262 /*@*/
263 {
264 void *newptr;
265 newptr = realloc (ptr, newsize);
266 if (newptr == NULL && newsize != 0) {
267 free (ptr); /* lclint complains but this is right */
268 return NULL;
269 }
270 return newptr;
271 }
272 /*@=usereleased@*/
273
274 static int
get_ifconf(int s,size_t * lenp,char * buf)275 get_ifconf (int s, size_t *lenp, /*@out@*/ char *buf)
276 /*@modifies *buf,*lenp@*/
277 {
278 int ret;
279 struct ifconf ifc;
280
281 /*@+matchanyintegral@*/
282 ifc.ifc_len = *lenp;
283 /*@=matchanyintegral@*/
284 ifc.ifc_buf = buf;
285 memset(buf, 0, *lenp);
286 /*@-moduncon@*/
287 ret = ioctl (s, SIOCGIFCONF, (char *)&ifc);
288 /*@=moduncon@*/
289 /*@+matchanyintegral@*/
290 *lenp = ifc.ifc_len;
291 /*@=matchanyintegral@*/
292 return ret;
293 }
294
295 /* Solaris uses SIOCGLIFCONF to return struct lifconf which is just
296 an extended version of struct ifconf.
297
298 HP-UX 11 also appears to have SIOCGLIFCONF, but uses struct
299 if_laddrconf, and struct if_laddrreq to be used with
300 SIOCGLIFADDR. */
301 #if defined(SIOCGLIFCONF) && defined(HAVE_STRUCT_LIFCONF)
302 static int
get_lifconf(int af,int s,size_t * lenp,char * buf)303 get_lifconf (int af, int s, size_t *lenp, /*@out@*/ char *buf)
304 /*@modifies *buf,*lenp@*/
305 {
306 int ret;
307 struct lifconf lifc;
308
309 lifc.lifc_family = af;
310 lifc.lifc_flags = 0;
311 /*@+matchanyintegral@*/
312 lifc.lifc_len = *lenp;
313 /*@=matchanyintegral@*/
314 lifc.lifc_buf = buf;
315 memset(buf, 0, *lenp);
316 /*@-moduncon@*/
317 ret = ioctl (s, SIOCGLIFCONF, (char *)&lifc);
318 if (ret)
319 Tperror ("SIOCGLIFCONF");
320 /*@=moduncon@*/
321 /*@+matchanyintegral@*/
322 *lenp = lifc.lifc_len;
323 /*@=matchanyintegral@*/
324 return ret;
325 }
326 #endif
327 #if defined(SIOCGLIFCONF) && defined(HAVE_STRUCT_IF_LADDRCONF) && 0
328 /* I'm not sure if this is needed or if net/if.h will pull it in. */
329 /* #include <net/if6.h> */
330 static int
get_if_laddrconf(int af,int s,size_t * lenp,char * buf)331 get_if_laddrconf (int af, int s, size_t *lenp, /*@out@*/ char *buf)
332 /*@modifies *buf,*lenp@*/
333 {
334 int ret;
335 struct if_laddrconf iflc;
336
337 /*@+matchanyintegral@*/
338 iflc.iflc_len = *lenp;
339 /*@=matchanyintegral@*/
340 iflc.iflc_buf = buf;
341 memset(buf, 0, *lenp);
342 /*@-moduncon@*/
343 ret = ioctl (s, SIOCGLIFCONF, (char *)&iflc);
344 if (ret)
345 Tperror ("SIOCGLIFCONF");
346 /*@=moduncon@*/
347 /*@+matchanyintegral@*/
348 *lenp = iflc.iflc_len;
349 /*@=matchanyintegral@*/
350 return ret;
351 }
352 #endif
353 #endif /* ! HAVE_IFADDRS_H */
354
355 #ifdef LINUX_IPV6_HACK
356 #include <stdio.h>
357 /* Read IPv6 addresses out of /proc/net/if_inet6, since there isn't
358 (currently) any ioctl to return them. */
359 struct linux_ipv6_addr_list {
360 struct sockaddr_in6 addr;
361 struct linux_ipv6_addr_list *next;
362 };
363 static struct linux_ipv6_addr_list *
get_linux_ipv6_addrs(void)364 get_linux_ipv6_addrs (void)
365 {
366 struct linux_ipv6_addr_list *lst = 0;
367 FILE *f;
368
369 /* _PATH_PROCNET_IFINET6 */
370 f = fopen("/proc/net/if_inet6", "r");
371 if (f) {
372 char ifname[21];
373 unsigned int idx, pfxlen, scope, dadstat;
374 struct in6_addr a6;
375 struct linux_ipv6_addr_list *nw;
376 int i;
377 unsigned int addrbyte[16];
378
379 set_cloexec_file(f);
380 while (fscanf(f,
381 "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x"
382 " %2x %2x %2x %2x %20s\n",
383 &addrbyte[0], &addrbyte[1], &addrbyte[2], &addrbyte[3],
384 &addrbyte[4], &addrbyte[5], &addrbyte[6], &addrbyte[7],
385 &addrbyte[8], &addrbyte[9], &addrbyte[10], &addrbyte[11],
386 &addrbyte[12], &addrbyte[13], &addrbyte[14],
387 &addrbyte[15],
388 &idx, &pfxlen, &scope, &dadstat, ifname) != EOF) {
389 for (i = 0; i < 16; i++)
390 a6.s6_addr[i] = addrbyte[i];
391 if (scope != 0)
392 continue;
393 nw = calloc (1, sizeof (struct linux_ipv6_addr_list));
394 if (nw == 0)
395 continue;
396 nw->addr.sin6_addr = a6;
397 nw->addr.sin6_family = AF_INET6;
398 /* Ignore other fields, we don't actually use them here. */
399 nw->next = lst;
400 lst = nw;
401 }
402 fclose (f);
403 }
404 return lst;
405 }
406 #endif
407
408 /* Return value is errno if internal stuff failed, otherwise zero,
409 even in the case where a called function terminated the iteration.
410
411 If one of the callback functions wants to pass back an error
412 indication, it should do it via some field pointed to by the DATA
413 argument. */
414
415 #ifdef HAVE_IFADDRS_H
416
417 int
foreach_localaddr(void * data,int (* pass1fn)(void *,struct sockaddr *),int (* betweenfn)(void *),int (* pass2fn)(void *,struct sockaddr *))418 foreach_localaddr (/*@null@*/ void *data,
419 int (*pass1fn) (/*@null@*/ void *, struct sockaddr *) /*@*/,
420 /*@null@*/ int (*betweenfn) (/*@null@*/ void *) /*@*/,
421 /*@null@*/ int (*pass2fn) (/*@null@*/ void *,
422 struct sockaddr *) /*@*/)
423 #if defined(DEBUG) || defined(TEST)
424 /*@modifies fileSystem@*/
425 #endif
426 {
427 struct ifaddrs *ifp_head, *ifp, *ifp2;
428 int match;
429
430 if (getifaddrs (&ifp_head) < 0)
431 return errno;
432 for (ifp = ifp_head; ifp; ifp = ifp->ifa_next) {
433 #ifdef DEBUG
434 printifaddr (ifp);
435 #endif
436 if ((ifp->ifa_flags & IFF_UP) == 0)
437 continue;
438 if (ifp->ifa_addr == NULL) {
439 /* Can't use an interface without an address. Linux
440 apparently does this sometimes. [RT ticket 1770 from
441 Maurice Massar, also Debian bug 206851, shows the
442 problem with a PPP link on a newer kernel than I'm
443 running.]
444
445 Pretend it's not up, so the second pass will skip
446 it. */
447 ifp->ifa_flags &= ~IFF_UP;
448 continue;
449 }
450 #if 0
451 if (is_loopback_address(ifp->ifa_addr)) {
452 /* Pretend it's not up, so the second pass will skip
453 it. */
454 ifp->ifa_flags &= ~IFF_UP;
455 continue;
456 }
457 #endif
458 /* If this address is a duplicate, punt. */
459 match = 0;
460 for (ifp2 = ifp_head; ifp2 && ifp2 != ifp; ifp2 = ifp2->ifa_next) {
461 if ((ifp2->ifa_flags & IFF_UP) == 0)
462 continue;
463 if (addr_eq (ifp->ifa_addr, ifp2->ifa_addr)) {
464 match = 1;
465 ifp->ifa_flags &= ~IFF_UP;
466 break;
467 }
468 }
469 if (match)
470 continue;
471 if ((*pass1fn) (data, ifp->ifa_addr))
472 goto punt;
473 }
474 if (betweenfn && (*betweenfn)(data))
475 goto punt;
476 if (pass2fn)
477 for (ifp = ifp_head; ifp; ifp = ifp->ifa_next) {
478 if (ifp->ifa_flags & IFF_UP)
479 if ((*pass2fn) (data, ifp->ifa_addr))
480 goto punt;
481 }
482 punt:
483 freeifaddrs (ifp_head);
484 return 0;
485 }
486
487 #elif defined (SIOCGLIFNUM) && defined(HAVE_STRUCT_LIFCONF) /* Solaris 8 and later; Sol 7? */
488
489 int
foreach_localaddr(void * data,int (* pass1fn)(void *,struct sockaddr *),int (* betweenfn)(void *),int (* pass2fn)(void *,struct sockaddr *))490 foreach_localaddr (/*@null@*/ void *data,
491 int (*pass1fn) (/*@null@*/ void *, struct sockaddr *) /*@*/,
492 /*@null@*/ int (*betweenfn) (/*@null@*/ void *) /*@*/,
493 /*@null@*/ int (*pass2fn) (/*@null@*/ void *,
494 struct sockaddr *) /*@*/)
495 #if defined(DEBUG) || defined(TEST)
496 /*@modifies fileSystem@*/
497 #endif
498 {
499 /* Okay, this is kind of odd. We have to use each of the address
500 families we care about, because with an AF_INET socket, extra
501 interfaces like hme0:1 that have only AF_INET6 addresses will
502 cause errors. Similarly, if hme0 has more AF_INET addresses
503 than AF_INET6 addresses, we won't be able to retrieve all of
504 the AF_INET addresses if we use an AF_INET6 socket. Since
505 neither family is guaranteed to have the greater number of
506 addresses, we should use both.
507
508 If it weren't for this little quirk, we could use one socket of
509 any type, and ask for addresses of all types. At least, it
510 seems to work that way. */
511
512 static const int afs[] = { AF_INET, AF_NS, AF_INET6 };
513 #define N_AFS (sizeof (afs) / sizeof (afs[0]))
514 struct {
515 int af;
516 int sock;
517 void *buf;
518 size_t buf_size;
519 struct lifnum lifnum;
520 } afp[N_AFS];
521 int code, i, j;
522 int retval = 0, afidx;
523 krb5_error_code sock_err = 0;
524 struct lifreq *lifr, lifreq, *lifr2;
525
526 #define FOREACH_AF() for (afidx = 0; afidx < N_AFS; afidx++)
527 #define P (afp[afidx])
528
529 /* init */
530 FOREACH_AF () {
531 P.af = afs[afidx];
532 P.sock = -1;
533 P.buf = 0;
534 }
535
536 /* first pass: get raw data, discard uninteresting addresses, callback */
537 FOREACH_AF () {
538 Tprintf (("trying af %d...\n", P.af));
539 P.sock = socket (P.af, USE_TYPE, USE_PROTO);
540 if (P.sock < 0) {
541 sock_err = SOCKET_ERROR;
542 Tperror ("socket");
543 continue;
544 }
545 set_cloexec_fd(P.sock);
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 = ENOMEM;
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 #if 0
587 /* None of the current callers want loopback addresses. */
588 if (is_loopback_address((struct sockaddr *)&lifr->lifr_addr)) {
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 set_cloexec_fd(P.sock);
722
723 code = ioctl (P.sock, SIOCGLIFNUM, &P.if_num);
724 if (code) {
725 Tperror ("ioctl(SIOCGLIFNUM)");
726 retval = errno;
727 goto punt;
728 }
729
730 P.buf_size = P.if_num * sizeof (struct if_laddrreq) * 2;
731 P.buf = malloc (P.buf_size);
732 if (P.buf == NULL) {
733 retval = ENOMEM;
734 goto punt;
735 }
736
737 code = get_if_laddrconf (P.af, P.sock, &P.buf_size, P.buf);
738 if (code < 0) {
739 retval = errno;
740 goto punt;
741 }
742
743 for (i = 0; i + sizeof(*lifr) <= P.buf_size; i+= sizeof (*lifr)) {
744 lifr = (struct if_laddrreq *)((caddr_t) P.buf+i);
745
746 strncpy(lifreq.iflr_name, lifr->iflr_name,
747 sizeof (lifreq.iflr_name));
748 Tprintf (("interface %s\n", lifreq.iflr_name));
749 /*@-moduncon@*/ /* ioctl unknown to lclint */
750 if (ioctl (P.sock, SIOCGLIFFLAGS, (char *)&lifreq) < 0) {
751 Tperror ("ioctl(SIOCGLIFFLAGS)");
752 skip:
753 /* mark for next pass */
754 lifr->iflr_name[0] = '\0';
755 continue;
756 }
757 /*@=moduncon@*/
758
759 #if 0
760 /* None of the current callers want loopback addresses. */
761 if (is_loopback_address(&lifr->iflr_addr)) {
762 Tprintf ((" loopback\n"));
763 goto skip;
764 }
765 #endif
766 /* Ignore interfaces that are down. */
767 if ((lifreq.iflr_flags & IFF_UP) == 0) {
768 Tprintf ((" down\n"));
769 goto skip;
770 }
771
772 /* Make sure we didn't process this address already. */
773 for (j = 0; j < i; j += sizeof (*lifr2)) {
774 lifr2 = (struct if_laddrreq *)((caddr_t) P.buf+j);
775 if (lifr2->iflr_name[0] == '\0')
776 continue;
777 if (lifr2->iflr_addr.sa_family == lifr->iflr_addr.sa_family
778 /* Compare address info. If this isn't good enough --
779 i.e., if random padding bytes turn out to differ
780 when the addresses are the same -- then we'll have
781 to do it on a per address family basis. */
782 && !memcmp (&lifr2->iflr_addr, &lifr->iflr_addr,
783 sizeof (*lifr))) {
784 Tprintf ((" duplicate addr\n"));
785 goto skip;
786 }
787 }
788
789 /*@-moduncon@*/
790 if ((*pass1fn) (data, ss2sa (&lifr->iflr_addr)))
791 goto punt;
792 /*@=moduncon@*/
793 }
794 }
795
796 /* Did we actually get any working sockets? */
797 FOREACH_AF ()
798 if (P.sock != -1)
799 goto have_working_socket;
800 retval = sock_err;
801 goto punt;
802 have_working_socket:
803
804 /*@-moduncon@*/
805 if (betweenfn != NULL && (*betweenfn)(data))
806 goto punt;
807 /*@=moduncon@*/
808
809 if (pass2fn)
810 FOREACH_AF ()
811 if (P.sock >= 0) {
812 for (i = 0; i + sizeof(*lifr) <= P.buf_size; i+= sizeof (*lifr)) {
813 lifr = (struct if_laddrreq *)((caddr_t) P.buf+i);
814
815 if (lifr->iflr_name[0] == '\0')
816 /* Marked in first pass to be ignored. */
817 continue;
818
819 /*@-moduncon@*/
820 if ((*pass2fn) (data, ss2sa (&lifr->iflr_addr)))
821 goto punt;
822 /*@=moduncon@*/
823 }
824 }
825 punt:
826 FOREACH_AF () {
827 /*@-moduncon@*/
828 closesocket(P.sock);
829 /*@=moduncon@*/
830 free (P.buf);
831 }
832
833 return retval;
834 }
835
836 #else /* not defined (SIOCGLIFNUM) */
837
838 #define SLOP (sizeof (struct ifreq) + 128)
839
840 static int
get_ifreq_array(char ** bufp,size_t * np,int s)841 get_ifreq_array(char **bufp, size_t *np, int s)
842 {
843 int code;
844 int est_if_count = 8;
845 size_t est_ifreq_size;
846 char *buf = 0;
847 size_t current_buf_size = 0, size, n;
848 #ifdef SIOCGSIZIFCONF
849 int ifconfsize = -1;
850 #endif
851 #ifdef SIOCGIFNUM
852 int numifs = -1;
853 #endif
854
855 *bufp = NULL;
856 *np = 0;
857
858 /* At least on NetBSD, an ifreq can hold an IPv4 address, but
859 isn't big enough for an IPv6 or ethernet address. So add a
860 little more space. */
861 est_ifreq_size = sizeof (struct ifreq) + 8;
862 #ifdef SIOCGSIZIFCONF
863 code = ioctl (s, SIOCGSIZIFCONF, &ifconfsize);
864 if (!code) {
865 current_buf_size = ifconfsize;
866 est_if_count = ifconfsize / est_ifreq_size;
867 }
868 #elif defined (SIOCGIFNUM)
869 code = ioctl (s, SIOCGIFNUM, &numifs);
870 if (!code && numifs > 0)
871 est_if_count = numifs;
872 #endif
873 if (current_buf_size == 0)
874 current_buf_size = est_ifreq_size * est_if_count + SLOP;
875 buf = malloc (current_buf_size);
876 if (buf == NULL)
877 return ENOMEM;
878
879 ask_again:
880 size = current_buf_size;
881 code = get_ifconf (s, &size, buf);
882 if (code < 0) {
883 code = errno;
884 free (buf);
885 return code;
886 }
887 /* Test that the buffer was big enough that another ifreq could've
888 fit easily, if the OS wanted to provide one. That seems to be
889 the only indication we get, complicated by the fact that the
890 associated address may make the required storage a little
891 bigger than the size of an ifreq. */
892 if (current_buf_size - size < SLOP
893 #ifdef SIOCGSIZIFCONF
894 /* Unless we hear SIOCGSIZIFCONF is broken somewhere, let's
895 trust the value it returns. */
896 && ifconfsize <= 0
897 #elif defined (SIOCGIFNUM)
898 && numifs <= 0
899 #endif
900 /* And we need *some* sort of bounds. */
901 && current_buf_size <= 100000
902 ) {
903 size_t new_size;
904
905 est_if_count *= 2;
906 new_size = est_ifreq_size * est_if_count + SLOP;
907 buf = grow_or_free (buf, new_size);
908 if (buf == 0)
909 return ENOMEM;
910 current_buf_size = new_size;
911 goto ask_again;
912 }
913
914 n = size;
915 if (n > current_buf_size)
916 n = current_buf_size;
917
918 *bufp = buf;
919 *np = n;
920 return 0;
921 }
922
923 int
foreach_localaddr(void * data,int (* pass1fn)(void *,struct sockaddr *),int (* betweenfn)(void *),int (* pass2fn)(void *,struct sockaddr *))924 foreach_localaddr (/*@null@*/ void *data,
925 int (*pass1fn) (/*@null@*/ void *, struct sockaddr *) /*@*/,
926 /*@null@*/ int (*betweenfn) (/*@null@*/ void *) /*@*/,
927 /*@null@*/ int (*pass2fn) (/*@null@*/ void *,
928 struct sockaddr *) /*@*/)
929 #if defined(DEBUG) || defined(TEST)
930 /*@modifies fileSystem@*/
931 #endif
932 {
933 struct ifreq *ifr, ifreq, *ifr2;
934 int s;
935 char *buf = 0;
936 size_t n, i, j;
937 int retval = 0;
938 #ifdef LINUX_IPV6_HACK
939 struct linux_ipv6_addr_list *linux_ipv6_addrs = get_linux_ipv6_addrs ();
940 struct linux_ipv6_addr_list *lx_v6;
941 #endif
942
943 s = socket (USE_AF, USE_TYPE, USE_PROTO);
944 if (s < 0)
945 return SOCKET_ERRNO;
946 set_cloexec_fd(s);
947
948 retval = get_ifreq_array(&buf, &n, s);
949 if (retval) {
950 /*@-moduncon@*/ /* close() unknown to lclint */
951 closesocket(s);
952 /*@=moduncon@*/
953 return retval;
954 }
955
956 /* Note: Apparently some systems put the size (used or wanted?)
957 into the start of the buffer, just none that I'm actually
958 using. Fix this when there's such a test system available.
959 The Samba mailing list archives mention that NTP looks for the
960 size on these systems: *-fujitsu-uxp* *-ncr-sysv4*
961 *-univel-sysv*. */
962 for (i = 0; i + sizeof(struct ifreq) <= n; i+= ifreq_size(*ifr) ) {
963 ifr = (struct ifreq *)((caddr_t) buf+i);
964 /* In case ifreq_size is more than sizeof(). */
965 if (i + ifreq_size(*ifr) > n)
966 break;
967
968 strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof (ifreq.ifr_name));
969 Tprintf (("interface %s\n", ifreq.ifr_name));
970 /*@-moduncon@*/ /* ioctl unknown to lclint */
971 if (ioctl (s, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
972 skip:
973 /* mark for next pass */
974 ifr->ifr_name[0] = '\0';
975 continue;
976 }
977 /*@=moduncon@*/
978
979 #if 0
980 /* None of the current callers want loopback addresses. */
981 if (is_loopback_address(&ifreq.ifr_addr)) {
982 Tprintf ((" loopback\n"));
983 goto skip;
984 }
985 #endif
986 /* Ignore interfaces that are down. */
987 if ((ifreq.ifr_flags & IFF_UP) == 0) {
988 Tprintf ((" down\n"));
989 goto skip;
990 }
991
992 /* Make sure we didn't process this address already. */
993 for (j = 0; j < i; j += ifreq_size(*ifr2)) {
994 ifr2 = (struct ifreq *)((caddr_t) buf+j);
995 if (ifr2->ifr_name[0] == '\0')
996 continue;
997 if (ifr2->ifr_addr.sa_family == ifr->ifr_addr.sa_family
998 && ifreq_size (*ifr) == ifreq_size (*ifr2)
999 /* Compare address info. If this isn't good enough --
1000 i.e., if random padding bytes turn out to differ
1001 when the addresses are the same -- then we'll have
1002 to do it on a per address family basis. */
1003 && !memcmp (&ifr2->ifr_addr.sa_data, &ifr->ifr_addr.sa_data,
1004 (ifreq_size (*ifr)
1005 - offsetof (struct ifreq, ifr_addr.sa_data)))) {
1006 Tprintf ((" duplicate addr\n"));
1007 goto skip;
1008 }
1009 }
1010
1011 /*@-moduncon@*/
1012 if ((*pass1fn) (data, &ifr->ifr_addr))
1013 goto punt;
1014 /*@=moduncon@*/
1015 }
1016
1017 #ifdef LINUX_IPV6_HACK
1018 for (lx_v6 = linux_ipv6_addrs; lx_v6; lx_v6 = lx_v6->next)
1019 if ((*pass1fn) (data, (struct sockaddr *) &lx_v6->addr))
1020 goto punt;
1021 #endif
1022
1023 /*@-moduncon@*/
1024 if (betweenfn != NULL && (*betweenfn)(data))
1025 goto punt;
1026 /*@=moduncon@*/
1027
1028 if (pass2fn) {
1029 for (i = 0; i + sizeof(struct ifreq) <= n; i+= ifreq_size(*ifr) ) {
1030 ifr = (struct ifreq *)((caddr_t) buf+i);
1031
1032 if (ifr->ifr_name[0] == '\0')
1033 /* Marked in first pass to be ignored. */
1034 continue;
1035
1036 /*@-moduncon@*/
1037 if ((*pass2fn) (data, &ifr->ifr_addr))
1038 goto punt;
1039 /*@=moduncon@*/
1040 }
1041 #ifdef LINUX_IPV6_HACK
1042 for (lx_v6 = linux_ipv6_addrs; lx_v6; lx_v6 = lx_v6->next)
1043 if ((*pass2fn) (data, (struct sockaddr *) &lx_v6->addr))
1044 goto punt;
1045 #endif
1046 }
1047 punt:
1048 /*@-moduncon@*/
1049 closesocket(s);
1050 /*@=moduncon@*/
1051 free (buf);
1052 #ifdef LINUX_IPV6_HACK
1053 while (linux_ipv6_addrs) {
1054 lx_v6 = linux_ipv6_addrs->next;
1055 free (linux_ipv6_addrs);
1056 linux_ipv6_addrs = lx_v6;
1057 }
1058 #endif
1059
1060 return retval;
1061 }
1062
1063 #endif /* not HAVE_IFADDRS_H and not SIOCGLIFNUM */
1064
1065 static krb5_error_code
1066 get_localaddrs (krb5_context context, krb5_address ***addr, int use_profile);
1067
1068 #ifdef TEST
1069
print_addr(void * dataptr,struct sockaddr * sa)1070 static int print_addr (/*@unused@*/ void *dataptr, struct sockaddr *sa)
1071 /*@modifies fileSystem@*/
1072 {
1073 char hostbuf[NI_MAXHOST];
1074 int err;
1075 socklen_t len;
1076
1077 printf (" --> family %2d ", sa->sa_family);
1078 len = sa_socklen (sa);
1079 err = getnameinfo (sa, len, hostbuf, (socklen_t) sizeof (hostbuf),
1080 (char *) NULL, 0, NI_NUMERICHOST);
1081 if (err) {
1082 int e = errno;
1083 printf ("<getnameinfo error %d: %s>\n", err, gai_strerror (err));
1084 if (err == EAI_SYSTEM)
1085 printf ("\t\t<errno is %d: %s>\n", e, strerror(e));
1086 } else
1087 printf ("addr %s\n", hostbuf);
1088 return 0;
1089 }
1090
main(void)1091 int main (void)
1092 {
1093 int r;
1094
1095 (void) setvbuf (stdout, (char *)NULL, _IONBF, 0);
1096 r = foreach_localaddr (0, print_addr, NULL, NULL);
1097 printf ("return value = %d\n", r);
1098 return 0;
1099 }
1100
1101 #else /* not TESTing */
1102
1103 struct localaddr_data {
1104 size_t count, cur_idx, cur_size;
1105 int mem_err;
1106 krb5_address **addr_temp;
1107 };
1108
1109 static int
count_addrs(void * P_data,struct sockaddr * a)1110 count_addrs (void *P_data, struct sockaddr *a)
1111 /*@*/
1112 {
1113 struct localaddr_data *data = P_data;
1114 switch (a->sa_family) {
1115 case AF_INET:
1116 case AF_INET6:
1117 #ifdef KRB5_USE_NS
1118 case AF_XNS:
1119 #endif
1120 data->count++;
1121 break;
1122 default:
1123 break;
1124 }
1125 return 0;
1126 }
1127
1128 static int
allocate(void * P_data)1129 allocate (void *P_data)
1130 /*@*/
1131 {
1132 struct localaddr_data *data = P_data;
1133 size_t i;
1134 void *n;
1135
1136 n = realloc (data->addr_temp,
1137 (1 + data->count + data->cur_idx) * sizeof (krb5_address *));
1138 if (n == 0) {
1139 data->mem_err++;
1140 return 1;
1141 }
1142 data->addr_temp = n;
1143 data->cur_size = 1 + data->count + data->cur_idx;
1144 for (i = data->cur_idx; i <= data->count + data->cur_idx; i++)
1145 data->addr_temp[i] = 0;
1146 return 0;
1147 }
1148
1149 static /*@null@*/ krb5_address *
make_addr(int type,size_t length,const void * contents)1150 make_addr (int type, size_t length, const void *contents)
1151 /*@*/
1152 {
1153 krb5_address *a;
1154 void *data;
1155
1156 data = malloc (length);
1157 if (data == NULL)
1158 return NULL;
1159 a = malloc (sizeof (krb5_address));
1160 if (a == NULL) {
1161 free (data);
1162 return NULL;
1163 }
1164 memcpy (data, contents, length);
1165 a->magic = KV5M_ADDRESS;
1166 a->addrtype = type;
1167 a->length = length;
1168 a->contents = data;
1169 return a;
1170 }
1171
1172 static int
add_addr(void * P_data,struct sockaddr * a)1173 add_addr (void *P_data, struct sockaddr *a)
1174 /*@modifies *P_data@*/
1175 {
1176 struct localaddr_data *data = P_data;
1177 /*@null@*/ krb5_address *address = 0;
1178
1179 switch (a->sa_family) {
1180 #ifdef HAVE_NETINET_IN_H
1181 case AF_INET:
1182 address = make_addr (ADDRTYPE_INET, sizeof (struct in_addr),
1183 &sa2sin(a)->sin_addr);
1184 if (address == NULL)
1185 data->mem_err++;
1186 break;
1187
1188 case AF_INET6:
1189 {
1190 const struct sockaddr_in6 *in = sa2sin6(a);
1191
1192 if (IN6_IS_ADDR_LINKLOCAL (&in->sin6_addr))
1193 break;
1194
1195 address = make_addr (ADDRTYPE_INET6, sizeof (struct in6_addr),
1196 &in->sin6_addr);
1197 if (address == NULL)
1198 data->mem_err++;
1199 break;
1200 }
1201 #endif /* netinet/in.h */
1202
1203 #ifdef KRB5_USE_NS
1204 case AF_XNS:
1205 address = make_addr (ADDRTYPE_XNS, sizeof (struct ns_addr),
1206 &((const struct sockaddr_ns *)a)->sns_addr);
1207 if (address == NULL)
1208 data->mem_err++;
1209 break;
1210 #endif
1211
1212 #ifdef AF_LINK
1213 /* Some BSD-based systems (e.g. NetBSD 1.5) and AIX will
1214 include the ethernet address, but we don't want that, at
1215 least for now. */
1216 case AF_LINK:
1217 break;
1218 #endif
1219 /*
1220 * Add more address families here..
1221 */
1222 default:
1223 break;
1224 }
1225 #ifdef __LCLINT__
1226 /* Redundant but unconditional store un-confuses lclint. */
1227 data->addr_temp[data->cur_idx] = address;
1228 #endif
1229 if (address) {
1230 data->addr_temp[data->cur_idx++] = address;
1231 }
1232
1233 return data->mem_err;
1234 }
1235
1236 static krb5_error_code
krb5_os_localaddr_profile(krb5_context context,struct localaddr_data * datap)1237 krb5_os_localaddr_profile (krb5_context context, struct localaddr_data *datap)
1238 {
1239 krb5_error_code err;
1240 static const char *const profile_name[] = {
1241 KRB5_CONF_LIBDEFAULTS, KRB5_CONF_EXTRA_ADDRESSES, 0
1242 };
1243 char **values;
1244 char **iter;
1245 krb5_address **newaddrs;
1246
1247 #ifdef DEBUG
1248 fprintf (stderr, "looking up extra_addresses foo\n");
1249 #endif
1250
1251 err = profile_get_values (context->profile, profile_name, &values);
1252 /* Ignore all errors for now? */
1253 if (err)
1254 return 0;
1255
1256 for (iter = values; *iter; iter++) {
1257 char *cp = *iter, *next, *current;
1258 size_t i, count;
1259
1260 #ifdef DEBUG
1261 fprintf (stderr, " found line: '%s'\n", cp);
1262 #endif
1263
1264 for (cp = *iter, next = 0; *cp; cp = next) {
1265 while (isspace ((int) *cp) || *cp == ',')
1266 cp++;
1267 if (*cp == 0)
1268 break;
1269 /* Start of an address. */
1270 #ifdef DEBUG
1271 fprintf (stderr, " addr found in '%s'\n", cp);
1272 #endif
1273 current = cp;
1274 while (*cp != 0 && !isspace((int) *cp) && *cp != ',')
1275 cp++;
1276 if (*cp != 0) {
1277 next = cp + 1;
1278 *cp = 0;
1279 } else
1280 next = cp;
1281 /* Got a single address, process it. */
1282 #ifdef DEBUG
1283 fprintf (stderr, " processing '%s'\n", current);
1284 #endif
1285 newaddrs = 0;
1286 err = k5_os_hostaddr (context, current, &newaddrs);
1287 if (err)
1288 continue;
1289 for (i = 0; newaddrs[i]; i++) {
1290 #ifdef DEBUG
1291 fprintf (stderr, " %d: family %d", i,
1292 newaddrs[i]->addrtype);
1293 fprintf (stderr, "\n");
1294 #endif
1295 }
1296 count = i;
1297 #ifdef DEBUG
1298 fprintf (stderr, " %d addresses\n", count);
1299 #endif
1300 if (datap->cur_idx + count >= datap->cur_size) {
1301 krb5_address **bigger;
1302 bigger = realloc (datap->addr_temp,
1303 sizeof (krb5_address *) * (datap->cur_idx + count));
1304 if (bigger) {
1305 datap->addr_temp = bigger;
1306 datap->cur_size = datap->cur_idx + count;
1307 }
1308 }
1309 for (i = 0; i < count; i++) {
1310 if (datap->cur_idx < datap->cur_size)
1311 datap->addr_temp[datap->cur_idx++] = newaddrs[i];
1312 else
1313 free (newaddrs[i]->contents), free (newaddrs[i]);
1314 }
1315 free (newaddrs);
1316 }
1317 }
1318 return 0;
1319 }
1320
1321 krb5_error_code KRB5_CALLCONV
krb5_os_localaddr(krb5_context context,krb5_address *** addr)1322 krb5_os_localaddr(krb5_context context, krb5_address ***addr)
1323 {
1324 return get_localaddrs(context, addr, 1);
1325 }
1326
1327 static krb5_error_code
get_localaddrs(krb5_context context,krb5_address *** addr,int use_profile)1328 get_localaddrs (krb5_context context, krb5_address ***addr, int use_profile)
1329 {
1330 struct localaddr_data data = { 0 };
1331 int r;
1332
1333 /* Ignore errors for now. */
1334 if (use_profile)
1335 (void)krb5_os_localaddr_profile (context, &data);
1336
1337 r = foreach_localaddr (&data, count_addrs, allocate, add_addr);
1338 if (r != 0) {
1339 size_t i;
1340 if (data.addr_temp) {
1341 for (i = 0; i < data.count; i++)
1342 free (data.addr_temp[i]);
1343 free (data.addr_temp);
1344 }
1345 if (data.mem_err)
1346 return ENOMEM;
1347 else
1348 return r;
1349 }
1350
1351 data.cur_idx++; /* null termination */
1352 if (data.mem_err)
1353 return ENOMEM;
1354 else if (data.cur_idx == data.count)
1355 *addr = data.addr_temp;
1356 else {
1357 /* This can easily happen if we have IPv6 link-local
1358 addresses. Just shorten the array. */
1359 *addr = (krb5_address **) realloc (data.addr_temp,
1360 (sizeof (krb5_address *)
1361 * data.cur_idx));
1362 if (*addr == 0)
1363 /* Okay, shortening failed, but the original should still
1364 be intact. */
1365 *addr = data.addr_temp;
1366 }
1367
1368 #ifdef DEBUG
1369 {
1370 size_t j;
1371 fprintf (stderr, "addresses:\n");
1372 for (j = 0; addr[0][j]; j++) {
1373 struct sockaddr_storage ss;
1374 int err2;
1375 char namebuf[NI_MAXHOST];
1376 void *addrp = 0;
1377
1378 fprintf (stderr, "%2d: ", j);
1379 fprintf (stderr, "addrtype %2d, length %2d", addr[0][j]->addrtype,
1380 addr[0][j]->length);
1381 memset (&ss, 0, sizeof (ss));
1382 switch (addr[0][j]->addrtype) {
1383 case ADDRTYPE_INET:
1384 {
1385 struct sockaddr_in *sinp = ss2sin (&ss);
1386 sinp->sin_family = AF_INET;
1387 addrp = &sinp->sin_addr;
1388 break;
1389 }
1390 case ADDRTYPE_INET6:
1391 {
1392 struct sockaddr_in6 *sin6p = ss2sin6 (&ss);
1393 sin6p->sin6_family = AF_INET6;
1394 addrp = &sin6p->sin6_addr;
1395 break;
1396 }
1397 default:
1398 ss2sa(&ss)->sa_family = 0;
1399 break;
1400 }
1401 if (addrp)
1402 memcpy (addrp, addr[0][j]->contents, addr[0][j]->length);
1403 err2 = getnameinfo (ss2sa(&ss), sa_socklen (ss2sa (&ss)),
1404 namebuf, sizeof (namebuf), 0, 0,
1405 NI_NUMERICHOST);
1406 if (err2 == 0)
1407 fprintf (stderr, ": addr %s\n", namebuf);
1408 else
1409 fprintf (stderr, ": getnameinfo error %d\n", err2);
1410 }
1411 }
1412 #endif
1413
1414 return 0;
1415 }
1416
1417 #endif /* not TESTing */
1418
1419 #else /* Windows/Mac version */
1420
1421 /*
1422 * Hold on to your lunch! Backup kludge method of obtaining your
1423 * local IP address, courtesy of Windows Socket Network Programming,
1424 * by Robert Quinn
1425 */
1426 #if defined(_WIN32)
local_addr_fallback_kludge(void)1427 static struct hostent *local_addr_fallback_kludge(void)
1428 {
1429 static struct hostent host;
1430 static SOCKADDR_IN addr;
1431 static char * ip_ptrs[2];
1432 SOCKET sock;
1433 int size = sizeof(SOCKADDR);
1434 int err;
1435
1436 sock = socket(AF_INET, SOCK_DGRAM, 0);
1437 if (sock == INVALID_SOCKET)
1438 return NULL;
1439 set_cloexec_fd(sock);
1440
1441 /* connect to arbitrary port and address (NOT loopback) */
1442 addr.sin_family = AF_INET;
1443 addr.sin_port = htons(IPPORT_ECHO);
1444 addr.sin_addr.s_addr = inet_addr("204.137.220.51");
1445
1446 err = connect(sock, (LPSOCKADDR) &addr, sizeof(SOCKADDR));
1447 if (err == SOCKET_ERROR)
1448 return NULL;
1449
1450 err = getsockname(sock, (LPSOCKADDR) &addr, &size);
1451 if (err == SOCKET_ERROR)
1452 return NULL;
1453
1454 closesocket(sock);
1455
1456 host.h_name = 0;
1457 host.h_aliases = 0;
1458 host.h_addrtype = AF_INET;
1459 host.h_length = 4;
1460 host.h_addr_list = ip_ptrs;
1461 ip_ptrs[0] = (char *) &addr.sin_addr.s_addr;
1462 ip_ptrs[1] = NULL;
1463
1464 return &host;
1465 }
1466 #endif
1467
1468 /* No ioctls in winsock so we just assume there is only one networking
1469 * card per machine, so gethostent is good enough.
1470 */
1471 krb5_error_code KRB5_CALLCONV
krb5_os_localaddr(krb5_context context,krb5_address *** addr)1472 krb5_os_localaddr (krb5_context context, krb5_address ***addr) {
1473 char host[64]; /* Name of local machine */
1474 struct hostent *hostrec;
1475 size_t count, i;
1476 int err;
1477 krb5_address ** paddr;
1478
1479 *addr = 0;
1480 paddr = 0;
1481 err = 0;
1482
1483 if (gethostname (host, sizeof(host))) {
1484 err = SOCKET_ERRNO;
1485 }
1486
1487 if (!err) {
1488 hostrec = gethostbyname (host);
1489 if (hostrec == NULL) {
1490 err = SOCKET_ERRNO;
1491 }
1492 }
1493
1494 if (err) {
1495 hostrec = local_addr_fallback_kludge();
1496 if (!hostrec)
1497 return err;
1498 else
1499 err = 0; /* otherwise we will die at cleanup */
1500 }
1501
1502 for (count = 0; hostrec->h_addr_list[count]; count++);
1503
1504
1505 paddr = (krb5_address **)calloc(count+1, sizeof(krb5_address *));
1506 if (!paddr) {
1507 err = ENOMEM;
1508 goto cleanup;
1509 }
1510
1511 for (i = 0; i < count; i++) {
1512 paddr[i] = (krb5_address *)malloc(sizeof(krb5_address));
1513 if (paddr[i] == NULL) {
1514 err = ENOMEM;
1515 goto cleanup;
1516 }
1517
1518 paddr[i]->magic = KV5M_ADDRESS;
1519 paddr[i]->addrtype = hostrec->h_addrtype;
1520 paddr[i]->length = hostrec->h_length;
1521 paddr[i]->contents = (unsigned char *)malloc(paddr[i]->length);
1522 if (!paddr[i]->contents) {
1523 err = ENOMEM;
1524 goto cleanup;
1525 }
1526 memcpy(paddr[i]->contents,
1527 hostrec->h_addr_list[i],
1528 paddr[i]->length);
1529 }
1530
1531 cleanup:
1532 if (err) {
1533 if (paddr) {
1534 for (i = 0; i < count; i++)
1535 {
1536 if (paddr[i]) {
1537 if (paddr[i]->contents)
1538 free(paddr[i]->contents);
1539 free(paddr[i]);
1540 }
1541 }
1542 free(paddr);
1543 }
1544 }
1545 else
1546 *addr = paddr;
1547
1548 return(err);
1549 }
1550 #endif
1551