dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / gss_mechs / mech_krb5 / krb5 / os / localaddr.c
blob73b4690d651b0595439fab75c247bab4799d37d0
1 /*
2 * lib/krb5/os/localaddr.c
4 * Copyright 1990,1991,2000,2001,2002,2004 by the Massachusetts Institute of Technology.
5 * All Rights Reserved.
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.
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.
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)
33 * XNS support is untested, but "Should just work". (Hah!)
36 #include "k5-int.h"
38 #if !defined(_WIN32)
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>
48 #if defined(TEST) || defined(DEBUG)
49 # include "fake-addrinfo.h"
50 #endif
52 #include "foreachaddr.h"
54 /* Note: foreach_localaddr is exported from the library through
55 krb5int_accessor, for the KDC to use.
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.
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.
73 Multiple definitions are provided below, dependent on various
74 system facilities for extracting the necessary information. */
76 /* Now, on to the implementations, and heaps of debugging code. */
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
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.
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.
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
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
113 * Add more address families here.
117 #if defined(__linux__) && defined(KRB5_USE_INET6) && !defined(HAVE_IFADDRS_H)
118 #define LINUX_IPV6_HACK
119 #endif
121 #include <errno.h>
124 * Return all the protocol addresses of this host.
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.
131 * This uses the SIOCGIFCONF, SIOCGIFFLAGS, and SIOCGIFADDR ioctl's.
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).
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*/
150 #if defined(DEBUG) || defined(TEST)
151 #include <netinet/in.h>
152 #include <net/if.h>
154 #include "socket-utils.h"
155 #include "fake-addrinfo.h"
157 void printaddr (struct sockaddr *);
159 void printaddr (struct sockaddr *sa)
160 /*@modifies fileSystem@*/
162 char buf[NI_MAXHOST];
163 int err;
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);
175 #endif
177 #ifdef HAVE_IFADDRS_H
178 #include <ifaddrs.h>
180 #ifdef DEBUG
181 void printifaddr (struct ifaddrs *ifp)
183 printf ("%p={\n", ifp);
184 /* printf ("\tnext=%p\n", ifp->ifa_next); */
185 printf ("\tname=%s\n", ifp->ifa_name);
186 printf ("\tflags=");
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
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");
216 #endif /* DEBUG */
218 #include <string.h>
219 #include <stdlib.h>
221 static int
222 addr_eq (const struct sockaddr *s1, const struct sockaddr *s2)
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;
241 #endif
243 #endif
245 #ifndef HAVE_IFADDRS_H
246 /*@-usereleased@*/ /* lclint doesn't understand realloc */
247 static /*@null@*/ void *
248 grow_or_free (/*@only@*/ void *ptr, size_t newsize)
249 /*@*/
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;
257 return newptr;
259 /*@=usereleased@*/
261 static int
262 get_ifconf (int s, size_t *lenp, /*@out@*/ char *buf)
263 /*@modifies *buf,*lenp@*/
265 int ret;
266 struct ifconf ifc;
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;
282 /* Solaris uses SIOCGLIFCONF to return struct lifconf which is just
283 an extended version of struct ifconf.
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
290 get_lifconf (int af, int s, size_t *lenp, /*@out@*/ char *buf)
291 /*@modifies *buf,*lenp@*/
293 int ret;
294 struct lifconf lifc;
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;
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
318 get_if_laddrconf (int af, int s, size_t *lenp, /*@out@*/ char *buf)
319 /*@modifies *buf,*lenp@*/
321 int ret;
322 struct if_laddrconf iflc;
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;
339 #endif
340 #endif /* ! HAVE_IFADDRS_H */
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;
350 static struct linux_ipv6_addr_list *
351 get_linux_ipv6_addrs ()
353 struct linux_ipv6_addr_list *lst = 0;
354 FILE *f;
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];
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;
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;
403 fclose (f);
405 return lst;
407 #endif
409 /* Return value is errno if internal stuff failed, otherwise zero,
410 even in the case where a called function terminated the iteration.
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. */
416 #ifdef HAVE_IFADDRS_H
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
428 struct ifaddrs *ifp_head, *ifp, *ifp2;
429 int match;
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;
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.]
452 Pretend it's not up, so the second pass will skip
453 it. */
454 ifp->ifa_flags &= ~IFF_UP;
455 continue;
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;
470 if (match)
471 continue;
472 if ((*pass1fn) (data, ifp->ifa_addr))
473 goto punt;
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;
483 punt:
484 freeifaddrs (ifp_head);
485 return 0;
488 #elif defined (SIOCGLIFNUM) && defined(HAVE_STRUCT_LIFCONF) /* Solaris 8 and later; Sol 7? */
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
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.
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. */
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;
527 #define FOREACH_AF() for (afidx = 0; afidx < N_AFS; afidx++)
528 #define P (afp[afidx])
530 /* init */
531 FOREACH_AF () {
532 P.af = afs[afidx];
533 P.sock = -1;
534 P.buf = 0;
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;
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;
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;
564 code = get_lifconf (P.af, P.sock, &P.buf_size, P.buf);
565 if (code < 0) {
566 retval = errno;
567 goto punt;
570 for (i = 0; i + sizeof(*lifr) <= P.buf_size; i+= sizeof (*lifr)) {
571 lifr = (struct lifreq *)((caddr_t) P.buf+i);
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;
584 /*@=moduncon@*/
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;
592 #endif
593 /* Ignore interfaces that are down. */
594 if ((lifreq.lifr_flags & IFF_UP) == 0) {
595 Tprintf ((" down\n"));
596 goto skip;
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;
616 /*@-moduncon@*/
617 if ((*pass1fn) (data, ss2sa (&lifr->lifr_addr)))
618 goto punt;
619 /*@=moduncon@*/
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:
631 /*@-moduncon@*/
632 if (betweenfn != NULL && (*betweenfn)(data))
633 goto punt;
634 /*@=moduncon@*/
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);
642 if (lifr->lifr_name[0] == '\0')
643 /* Marked in first pass to be ignored. */
644 continue;
646 /*@-moduncon@*/
647 if ((*pass2fn) (data, ss2sa (&lifr->lifr_addr)))
648 goto punt;
649 /*@=moduncon@*/
652 punt:
653 FOREACH_AF () {
654 /*@-moduncon@*/
655 closesocket(P.sock);
656 /*@=moduncon@*/
657 free (P.buf);
660 return retval;
663 #elif defined (SIOCGLIFNUM) && defined(HAVE_STRUCT_IF_LADDRCONF) && 0 /* HP-UX 11 support being debugged */
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
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.
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. */
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;
702 #define FOREACH_AF() for (afidx = 0; afidx < N_AFS; afidx++)
703 #define P (afp[afidx])
705 /* init */
706 FOREACH_AF () {
707 P.af = afs[afidx];
708 P.sock = -1;
709 P.buf = 0;
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;
722 code = ioctl (P.sock, SIOCGLIFNUM, &P.if_num);
723 if (code) {
724 Tperror ("ioctl(SIOCGLIFNUM)");
725 retval = errno;
726 goto punt;
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;
736 code = get_if_laddrconf (P.af, P.sock, &P.buf_size, P.buf);
737 if (code < 0) {
738 retval = errno;
739 goto punt;
742 for (i = 0; i + sizeof(*lifr) <= P.buf_size; i+= sizeof (*lifr)) {
743 lifr = (struct if_laddrreq *)((caddr_t) P.buf+i);
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;
756 /*@=moduncon@*/
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;
764 #endif
765 /* Ignore interfaces that are down. */
766 if ((lifreq.iflr_flags & IFF_UP) == 0) {
767 Tprintf ((" down\n"));
768 goto skip;
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;
788 /*@-moduncon@*/
789 if ((*pass1fn) (data, ss2sa (&lifr->iflr_addr)))
790 goto punt;
791 /*@=moduncon@*/
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:
803 /*@-moduncon@*/
804 if (betweenfn != NULL && (*betweenfn)(data))
805 goto punt;
806 /*@=moduncon@*/
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);
814 if (lifr->iflr_name[0] == '\0')
815 /* Marked in first pass to be ignored. */
816 continue;
818 /*@-moduncon@*/
819 if ((*pass2fn) (data, ss2sa (&lifr->iflr_addr)))
820 goto punt;
821 /*@=moduncon@*/
824 punt:
825 FOREACH_AF () {
826 /*@-moduncon@*/
827 closesocket(P.sock);
828 /*@=moduncon@*/
829 free (P.buf);
832 return retval;
835 #else /* not defined (SIOCGLIFNUM) */
837 #define SLOP (sizeof (struct ifreq) + 128)
839 static int
840 get_ifreq_array(char **bufp, size_t *np, int s)
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
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;
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;
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;
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
899 size_t new_size;
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;
910 n = size;
911 if (n > current_buf_size)
912 n = current_buf_size;
914 *bufp = buf;
915 *np = n;
916 return 0;
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
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
939 s = socket (USE_AF, USE_TYPE, USE_PROTO);
940 if (s < 0)
941 return SOCKET_ERRNO;
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;
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;
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;
972 /*@=moduncon@*/
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;
980 #endif
981 /* Ignore interfaces that are down. */
982 if ((ifreq.ifr_flags & IFF_UP) == 0) {
983 Tprintf ((" down\n"));
984 goto skip;
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;
1006 /*@-moduncon@*/
1007 if ((*pass1fn) (data, &ifr->ifr_addr))
1008 goto punt;
1009 /*@=moduncon@*/
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
1018 /*@-moduncon@*/
1019 if (betweenfn != NULL && (*betweenfn)(data))
1020 goto punt;
1021 /*@=moduncon@*/
1023 if (pass2fn) {
1024 for (i = 0; i + sizeof(struct ifreq) <= n; i+= ifreq_size(*ifr) ) {
1025 ifr = (struct ifreq *)((caddr_t) buf+i);
1027 if (ifr->ifr_name[0] == '\0')
1028 /* Marked in first pass to be ignored. */
1029 continue;
1031 /*@-moduncon@*/
1032 if ((*pass2fn) (data, &ifr->ifr_addr))
1033 goto punt;
1034 /*@=moduncon@*/
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
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;
1053 #endif
1055 return retval;
1058 #endif /* not HAVE_IFADDRS_H and not SIOCGLIFNUM */
1060 static krb5_error_code
1061 get_localaddrs (krb5_context context, krb5_address ***addr, int use_profile);
1063 #ifdef TEST
1065 static int print_addr (/*@unused@*/ void *dataptr, struct sockaddr *sa)
1066 /*@modifies fileSystem@*/
1068 char hostbuf[NI_MAXHOST];
1069 int err;
1070 socklen_t len;
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;
1086 int main ()
1088 int r;
1090 (void) setvbuf (stdout, NULL, _IONBF, 0);
1091 r = foreach_localaddr (0, print_addr, NULL, NULL);
1092 printf ("return value = %d\n", r);
1093 return 0;
1096 #else /* not TESTing */
1098 struct localaddr_data {
1099 int count, mem_err, cur_idx, cur_size;
1100 krb5_address **addr_temp;
1103 static int
1104 count_addrs (void *P_data, struct sockaddr *a)
1105 /*@*/
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;
1121 return 0;
1124 static int
1125 allocate (void *P_data)
1126 /*@*/
1128 struct localaddr_data *data = P_data;
1129 int i;
1130 void *n;
1132 n = reallocarray(data->addr_temp, (1 + data->count + data->cur_idx),
1133 sizeof(krb5_address *));
1134 if (n == 0) {
1135 data->mem_err++;
1136 return 1;
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;
1145 static /*@null@*/ krb5_address *
1146 make_addr (int type, size_t length, const void *contents)
1147 /*@*/
1149 krb5_address *a;
1150 void *data;
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;
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;
1168 static int
1169 add_addr (void *P_data, struct sockaddr *a)
1170 /*@modifies *P_data@*/
1172 struct localaddr_data *data = P_data;
1173 /*@null@*/ krb5_address *address = 0;
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;
1184 #ifdef KRB5_USE_INET6
1185 case AF_INET6:
1187 const struct sockaddr_in6 *in = (const struct sockaddr_in6 *) a;
1189 if (IN6_IS_ADDR_LINKLOCAL (&in->sin6_addr))
1190 break;
1192 address = make_addr (ADDRTYPE_INET6, sizeof (struct in6_addr),
1193 &in->sin6_addr);
1194 if (address == NULL)
1195 data->mem_err++;
1196 break;
1198 #endif /* KRB5_USE_INET6 */
1199 #endif /* netinet/in.h */
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
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
1218 * Add more address families here..
1220 default:
1221 break;
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;
1231 return data->mem_err;
1234 static krb5_error_code
1235 krb5_os_localaddr_profile (krb5_context context, struct localaddr_data *datap)
1237 krb5_error_code err;
1238 static const char *const profile_name[] = {
1239 "libdefaults", "extra_addresses", 0
1241 char **values;
1242 char **iter;
1243 krb5_address **newaddrs;
1245 #ifdef DEBUG
1246 fprintf (stderr, "looking up extra_addresses foo\n");
1247 #endif
1249 err = profile_get_values (context->profile, profile_name, &values);
1250 /* Ignore all errors for now? */
1251 if (err)
1252 return 0;
1254 for (iter = values; *iter; iter++) {
1255 char *cp = *iter, *next, *current;
1256 int i, count;
1258 #ifdef DEBUG
1259 fprintf (stderr, " found line: '%s'\n", cp);
1260 #endif
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
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 = reallocarray(datap->addr_temp,
1301 (datap->cur_idx + count),
1302 sizeof(krb5_address *));
1303 if (bigger) {
1304 datap->addr_temp = bigger;
1305 datap->cur_size = datap->cur_idx + count;
1308 for (i = 0; i < count; i++) {
1309 if (datap->cur_idx < datap->cur_size)
1310 datap->addr_temp[datap->cur_idx++] = newaddrs[i];
1311 else
1312 free (newaddrs[i]->contents), free (newaddrs[i]);
1314 free (newaddrs);
1317 return 0;
1320 krb5_error_code KRB5_CALLCONV
1321 krb5_os_localaddr(krb5_context context, krb5_address ***addr)
1323 return get_localaddrs(context, addr, 1);
1326 krb5_error_code
1327 krb5int_local_addresses(krb5_context context, krb5_address ***addr)
1329 return get_localaddrs(context, addr, 0);
1332 static krb5_error_code
1333 get_localaddrs (krb5_context context, krb5_address ***addr, int use_profile)
1335 struct localaddr_data data = { 0 };
1336 int r;
1337 krb5_error_code err;
1339 if (use_profile) {
1340 err = krb5_os_localaddr_profile (context, &data);
1341 /* ignore err for now */
1344 r = foreach_localaddr (&data, count_addrs, allocate, add_addr);
1345 if (r != 0) {
1346 int i;
1347 if (data.addr_temp) {
1348 for (i = 0; i < data.count; i++)
1349 krb5_xfree (data.addr_temp[i]);
1350 free (data.addr_temp);
1352 if (data.mem_err)
1353 return ENOMEM;
1354 else
1355 return r;
1358 data.cur_idx++; /* null termination */
1359 if (data.mem_err)
1360 return ENOMEM;
1361 else if (data.cur_idx == data.count)
1362 *addr = data.addr_temp;
1363 else {
1364 /* This can easily happen if we have IPv6 link-local
1365 addresses. Just shorten the array. */
1366 *addr = (krb5_address **) realloc (data.addr_temp,
1367 (sizeof (krb5_address *)
1368 * data.cur_idx));
1369 if (*addr == 0)
1370 /* Okay, shortening failed, but the original should still
1371 be intact. */
1372 *addr = data.addr_temp;
1375 #ifdef DEBUG
1377 int j;
1378 fprintf (stderr, "addresses:\n");
1379 for (j = 0; addr[0][j]; j++) {
1380 struct sockaddr_storage ss;
1381 int err2;
1382 char namebuf[NI_MAXHOST];
1383 void *addrp = 0;
1385 fprintf (stderr, "%2d: ", j);
1386 fprintf (stderr, "addrtype %2d, length %2d", addr[0][j]->addrtype,
1387 addr[0][j]->length);
1388 memset (&ss, 0, sizeof (ss));
1389 switch (addr[0][j]->addrtype) {
1390 case ADDRTYPE_INET:
1392 struct sockaddr_in *sinp = ss2sin (&ss);
1393 sinp->sin_family = AF_INET;
1394 addrp = &sinp->sin_addr;
1395 #ifdef HAVE_SA_LEN
1396 sinp->sin_len = sizeof (struct sockaddr_in);
1397 #endif
1398 break;
1400 #ifdef KRB5_USE_INET6
1401 case ADDRTYPE_INET6:
1403 struct sockaddr_in6 *sin6p = ss2sin6 (&ss);
1404 sin6p->sin6_family = AF_INET6;
1405 addrp = &sin6p->sin6_addr;
1406 #ifdef HAVE_SA_LEN
1407 sin6p->sin6_len = sizeof (struct sockaddr_in6);
1408 #endif
1409 break;
1411 #endif
1412 default:
1413 ss2sa(&ss)->sa_family = 0;
1414 break;
1416 if (addrp)
1417 memcpy (addrp, addr[0][j]->contents, addr[0][j]->length);
1418 err2 = getnameinfo (ss2sa(&ss), socklen (ss2sa (&ss)),
1419 namebuf, sizeof (namebuf), 0, 0,
1420 NI_NUMERICHOST);
1421 if (err2 == 0)
1422 fprintf (stderr, ": addr %s\n", namebuf);
1423 else
1424 fprintf (stderr, ": getnameinfo error %d\n", err2);
1427 #endif
1429 return 0;
1432 #endif /* not TESTing */
1434 #else /* Windows/Mac version */
1437 * Hold on to your lunch! Backup kludge method of obtaining your
1438 * local IP address, courtesy of Windows Socket Network Programming,
1439 * by Robert Quinn
1441 #if defined(_WIN32)
1442 static struct hostent *local_addr_fallback_kludge()
1444 static struct hostent host;
1445 static SOCKADDR_IN addr;
1446 static char * ip_ptrs[2];
1447 SOCKET sock;
1448 int size = sizeof(SOCKADDR);
1449 int err;
1451 sock = socket(AF_INET, SOCK_DGRAM, 0);
1452 if (sock == INVALID_SOCKET)
1453 return NULL;
1455 /* connect to arbitrary port and address (NOT loopback) */
1456 addr.sin_family = AF_INET;
1457 addr.sin_port = htons(IPPORT_ECHO);
1458 addr.sin_addr.s_addr = inet_addr("204.137.220.51");
1460 err = connect(sock, (LPSOCKADDR) &addr, sizeof(SOCKADDR));
1461 if (err == SOCKET_ERROR)
1462 return NULL;
1464 err = getsockname(sock, (LPSOCKADDR) &addr, (int *) size);
1465 if (err == SOCKET_ERROR)
1466 return NULL;
1468 closesocket(sock);
1470 host.h_name = 0;
1471 host.h_aliases = 0;
1472 host.h_addrtype = AF_INET;
1473 host.h_length = 4;
1474 host.h_addr_list = ip_ptrs;
1475 ip_ptrs[0] = (char *) &addr.sin_addr.s_addr;
1476 ip_ptrs[1] = NULL;
1478 return &host;
1480 #endif
1482 /* No ioctls in winsock so we just assume there is only one networking
1483 * card per machine, so gethostent is good enough.
1485 krb5_error_code KRB5_CALLCONV
1486 krb5_os_localaddr (krb5_context context, krb5_address ***addr) {
1487 char host[64]; /* Name of local machine */
1488 struct hostent *hostrec;
1489 int err, count, i;
1490 krb5_address ** paddr;
1492 *addr = 0;
1493 paddr = 0;
1494 err = 0;
1496 if (gethostname (host, sizeof(host))) {
1497 err = SOCKET_ERRNO;
1500 if (!err) {
1501 hostrec = gethostbyname (host);
1502 if (hostrec == NULL) {
1503 err = SOCKET_ERRNO;
1507 if (err) {
1508 hostrec = local_addr_fallback_kludge();
1509 if (!hostrec)
1510 return err;
1511 else
1512 err = 0; /* otherwise we will die at cleanup */
1515 for (count = 0; hostrec->h_addr_list[count]; count++);
1518 paddr = (krb5_address **)malloc(sizeof(krb5_address *) * (count+1));
1519 if (!paddr) {
1520 err = ENOMEM;
1521 goto cleanup;
1524 memset(paddr, 0, sizeof(krb5_address *) * (count+1));
1526 for (i = 0; i < count; i++)
1528 paddr[i] = (krb5_address *)malloc(sizeof(krb5_address));
1529 if (paddr[i] == NULL) {
1530 err = ENOMEM;
1531 goto cleanup;
1534 paddr[i]->magic = KV5M_ADDRESS;
1535 paddr[i]->addrtype = hostrec->h_addrtype;
1536 paddr[i]->length = hostrec->h_length;
1537 paddr[i]->contents = (unsigned char *)malloc(paddr[i]->length);
1538 if (!paddr[i]->contents) {
1539 err = ENOMEM;
1540 goto cleanup;
1542 memcpy(paddr[i]->contents,
1543 hostrec->h_addr_list[i],
1544 paddr[i]->length);
1547 cleanup:
1548 if (err) {
1549 if (paddr) {
1550 for (i = 0; i < count; i++)
1552 if (paddr[i]) {
1553 free(paddr[i]->contents);
1554 free(paddr[i]);
1557 free(paddr);
1560 else
1561 *addr = paddr;
1563 return(err);
1565 #endif