1 /*
2  * Copyright (C) 2001,2002,2003,2004,2005,2006 by the Massachusetts Institute of Technology,
3  * Cambridge, MA, USA.  All Rights Reserved.
4  *
5  * This software is being provided to you, the LICENSEE, by the
6  * Massachusetts Institute of Technology (M.I.T.) under the following
7  * license.  By obtaining, using and/or copying this software, you agree
8  * that you have read, understood, and will comply with these terms and
9  * conditions:
10  *
11  * Export of this software from the United States of America may
12  * require a specific license from the United States Government.
13  * It is the responsibility of any person or organization contemplating
14  * export to obtain such a license before exporting.
15  *
16  * WITHIN THAT CONSTRAINT, permission to use, copy, modify and distribute
17  * this software and its documentation for any purpose and without fee or
18  * royalty is hereby granted, provided that you agree to comply with the
19  * following copyright notice and statements, including the disclaimer, and
20  * that the same appear on ALL copies of the software and documentation,
21  * including modifications that you make for internal use or for
22  * distribution:
23  *
24  * THIS SOFTWARE IS PROVIDED "AS IS", AND M.I.T. MAKES NO REPRESENTATIONS
25  * OR WARRANTIES, EXPRESS OR IMPLIED.  By way of example, but not
26  * limitation, M.I.T. MAKES NO REPRESENTATIONS OR WARRANTIES OF
27  * MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF
28  * THE LICENSED SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY
29  * PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
30  *
31  * The name of the Massachusetts Institute of Technology or M.I.T. may NOT
32  * be used in advertising or publicity pertaining to distribution of the
33  * software.  Title to copyright in this software and any associated
34  * documentation shall at all times remain with M.I.T., and USER agrees to
35  * preserve same.
36  *
37  * Furthermore if you modify this software you must label
38  * your software as modified software and not distribute it in such a
39  * fashion that it might be confused with the original M.I.T. software.
40  */
41 
42 /* Approach overview:
43 
44    If a system version is available but buggy, save handles to it,
45    redefine the names to refer to static functions defined here, and
46    in those functions, call the system versions and fix up the
47    returned data.  Use the native data structures and flag values.
48 
49    If no system version exists, use gethostby* and fake it.  Define
50    the data structures and flag values locally.
51 
52 
53    On Mac OS X, getaddrinfo results aren't cached (though
54    gethostbyname results are), so we need to build a cache here.  Now
55    things are getting really messy.  Because the cache is in use, we
56    use getservbyname, and throw away thread safety.  (Not that the
57    cache is thread safe, but when we get locking support, that'll be
58    dealt with.)  This code needs tearing down and rebuilding, soon.
59 
60 
61    Note that recent Windows developers' code has an interesting hack:
62    When you include the right header files, with the right set of
63    macros indicating system versions, you'll get an inline function
64    that looks for getaddrinfo (or whatever) in the system library, and
65    calls it if it's there.  If it's not there, it fakes it with
66    gethostby* calls.
67 
68    We're taking a simpler approach: A system provides these routines or
69    it does not.
70 
71    Someday, we may want to take into account different versions (say,
72    different revs of GNU libc) where some are broken in one way, and
73    some work or are broken in another way.  Cross that bridge when we
74    come to it.  */
75 
76 /* To do, maybe:
77 
78    + For AIX 4.3.3, using the RFC 2133 definition: Implement
79      AI_NUMERICHOST.  It's not defined in the header file.
80 
81      For certain (old?) versions of GNU libc, AI_NUMERICHOST is
82      defined but not implemented.
83 
84    + Use gethostbyname2, inet_aton and other IPv6 or thread-safe
85      functions if available.  But, see
86      http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=135182 for one
87      gethostbyname2 problem on Linux.  And besides, if a platform is
88      supporting IPv6 at all, they really should be doing getaddrinfo
89      by now.
90 
91    + inet_ntop, inet_pton
92 
93    + Conditionally export/import the function definitions, so a
94      library can have a single copy instead of multiple.
95 
96    + Upgrade host requirements to include working implementations of
97      these functions, and throw all this away.  Pleeease?  :-)  */
98 
99 #include "port-sockets.h"
100 #include "socket-utils.h"
101 #include "k5-platform.h"
102 #include "k5-thread.h"
103 #include "supp-int.h"
104 
105 #include <stdio.h>		/* for sprintf */
106 #include <errno.h>
107 
108 #define IMPLEMENT_FAKE_GETADDRINFO
109 #include "fake-addrinfo.h"
110 
111 #ifdef S_SPLINT_S
112 /*@-incondefs@*/
113 extern int
114 getaddrinfo (/*@in@*/ /*@null@*/ const char *,
115 	     /*@in@*/ /*@null@*/ const char *,
116 	     /*@in@*/ /*@null@*/ const struct addrinfo *,
117 	     /*@out@*/ struct addrinfo **)
118     ;
119 extern void
120 freeaddrinfo (/*@only@*/ /*@out@*/ struct addrinfo *)
121     ;
122 extern int
123 getnameinfo (const struct sockaddr *addr, socklen_t addrsz,
124 	     /*@out@*/ /*@null@*/ char *h, socklen_t hsz,
125 	     /*@out@*/ /*@null@*/ char *s, socklen_t ssz,
126 	     int flags)
127     /*@requires (maxSet(h)+1) >= hsz /\ (maxSet(s)+1) >= ssz @*/
128     /* too hard: maxRead(addr) >= (addrsz-1) */
129     /*@modifies *h, *s@*/;
130 extern /*@dependent@*/ char *gai_strerror (int code) /*@*/;
131 /*@=incondefs@*/
132 #endif
133 
134 
135 #include "cache-addrinfo.h"
136 
137 #if (defined (__linux__) && defined(HAVE_GETADDRINFO)) || defined (_AIX)
138 /* See comments below.  */
139 #  define WRAP_GETADDRINFO
140 #endif
141 
142 #if defined (__linux__) && defined(HAVE_GETADDRINFO)
143 # define COPY_FIRST_CANONNAME
144 #endif
145 
146 #ifdef _AIX
147 # define NUMERIC_SERVICE_BROKEN
148 # define COPY_FIRST_CANONNAME
149 #endif
150 
151 
152 #ifdef COPY_FIRST_CANONNAME
153 # include <string.h>
154 #endif
155 
156 #ifdef NUMERIC_SERVICE_BROKEN
157 # include <ctype.h>		/* isdigit */
158 # include <stdlib.h>		/* strtoul */
159 #endif
160 
161 
162 /* Do we actually have *any* systems we care about that don't provide
163    either getaddrinfo or one of these two flavors of
164    gethostbyname_r?  */
165 #if !defined(HAVE_GETHOSTBYNAME_R) || defined(THREADSAFE_GETHOSTBYNAME)
166 typedef struct hostent *GET_HOST_TMP;
167 #define GET_HOST_BY_NAME(NAME, HP, ERR, TMP) \
168     { TMP = gethostbyname (NAME); (ERR) = h_errno; (HP) = TMP; }
169 #define GET_HOST_BY_ADDR(ADDR, ADDRLEN, FAMILY, HP, ERR, TMP) \
170     { TMP = gethostbyaddr ((ADDR), (ADDRLEN), (FAMILY)); (ERR) = h_errno; (HP) = TMP; }
171 #else
172 #ifdef _AIX /* XXX should have a feature test! */
173 typedef struct {
174     struct hostent ent;
175     struct hostent_data data;
176 } GET_HOST_TMP;
177 #define GET_HOST_BY_NAME(NAME, HP, ERR, TMP) \
178     {								\
179 	(HP) = (gethostbyname_r((NAME), &TMP.ent, &TMP.data)	\
180 		? 0						\
181 		: &TMP.ent);					\
182 	(ERR) = h_errno;					\
183     }
184 /*
185 #define GET_HOST_BY_ADDR(ADDR, ADDRLEN, FAMILY, HP, ERR) \
186     {									\
187 	struct hostent my_h_ent;					\
188 	struct hostent_data my_h_ent_data;				\
189 	(HP) = (gethostbyaddr_r((ADDR), (ADDRLEN), (FAMILY), &my_h_ent,	\
190 				&my_h_ent_data)				\
191 		? 0							\
192 		: &my_h_ent);						\
193 	(ERR) = my_h_err;						\
194     }
195 */
196 #else
197 #ifdef GETHOSTBYNAME_R_RETURNS_INT
198 typedef struct {
199     struct hostent ent;
200     char buf[8192];
201 } GET_HOST_TMP;
202 #define GET_HOST_BY_NAME(NAME, HP, ERR, TMP) \
203     {									\
204 	struct hostent *my_hp = NULL;					\
205 	int my_h_err, my_ret;						\
206 	my_ret = gethostbyname_r((NAME), &TMP.ent,			\
207 				 TMP.buf, sizeof (TMP.buf), &my_hp,	\
208 				 &my_h_err);				\
209 	(HP) = (((my_ret != 0) || (my_hp != &TMP.ent))			\
210 		? 0							\
211 		: &TMP.ent);						\
212 	(ERR) = my_h_err;						\
213     }
214 #define GET_HOST_BY_ADDR(ADDR, ADDRLEN, FAMILY, HP, ERR, TMP) \
215     {									\
216 	struct hostent *my_hp;						\
217 	int my_h_err, my_ret;						\
218 	my_ret = gethostbyaddr_r((ADDR), (ADDRLEN), (FAMILY), &TMP.ent,	\
219 				 TMP.buf, sizeof (TMP.buf), &my_hp,	\
220 				 &my_h_err);				\
221 	(HP) = (((my_ret != 0) || (my_hp != &TMP.ent))			\
222 		? 0							\
223 		: &TMP.ent);						\
224 	(ERR) = my_h_err;						\
225     }
226 #else
227 typedef struct {
228     struct hostent ent;
229     char buf[8192];
230 } GET_HOST_TMP;
231 #define GET_HOST_BY_NAME(NAME, HP, ERR, TMP) \
232     {									\
233 	int my_h_err;							\
234 	(HP) = gethostbyname_r((NAME), &TMP.ent,			\
235 			       TMP.buf, sizeof (TMP.buf), &my_h_err);	\
236 	(ERR) = my_h_err;						\
237     }
238 #define GET_HOST_BY_ADDR(ADDR, ADDRLEN, FAMILY, HP, ERR, TMP) \
239     {									\
240 	int my_h_err;							\
241 	(HP) = gethostbyaddr_r((ADDR), (ADDRLEN), (FAMILY), &TMP.ent,	\
242 			       TMP.buf, sizeof (TMP.buf), &my_h_err);	\
243 	(ERR) = my_h_err;						\
244     }
245 #endif /* returns int? */
246 #endif /* _AIX */
247 #endif
248 
249 /* Now do the same for getservby* functions.  */
250 #ifndef HAVE_GETSERVBYNAME_R
251 typedef struct servent *GET_SERV_TMP;
252 #define GET_SERV_BY_NAME(NAME, PROTO, SP, ERR, TMP) \
253     (TMP = getservbyname (NAME, PROTO), (SP) = TMP, (ERR) = (SP) ? 0 : -1)
254 #define GET_SERV_BY_PORT(PORT, PROTO, SP, ERR, TMP) \
255     (TMP = getservbyport (PORT, PROTO), (SP) = TMP, (ERR) = (SP) ? 0 : -1)
256 #else
257 #ifdef GETSERVBYNAME_R_RETURNS_INT
258 typedef struct {
259     struct servent ent;
260     char buf[8192];
261 } GET_SERV_TMP;
262 #define GET_SERV_BY_NAME(NAME, PROTO, SP, ERR, TMP) \
263     {									\
264 	struct servent *my_sp;						\
265 	int my_s_err;							\
266 	(SP) = (getservbyname_r((NAME), (PROTO), &TMP.ent,		\
267 				TMP.buf, sizeof (TMP.buf), &my_sp,	\
268 				&my_s_err)				\
269 		? 0							\
270 		: &TMP.ent);						\
271 	(ERR) = my_s_err;						\
272     }
273 #define GET_SERV_BY_PORT(PORT, PROTO, SP, ERR, TMP) \
274     {									\
275 	struct servent *my_sp;						\
276 	int my_s_err;							\
277 	(SP) = (getservbyport_r((PORT), (PROTO), &TMP.ent,		\
278 				TMP.buf, sizeof (TMP.buf), &my_sp,	\
279 				&my_s_err)				\
280 		? 0							\
281 		: &TMP.ent);						\
282 	(ERR) = my_s_err;						\
283     }
284 #else
285 /* returns ptr -- IRIX? */
286 typedef struct {
287     struct servent ent;
288     char buf[8192];
289 } GET_SERV_TMP;
290 #define GET_SERV_BY_NAME(NAME, PROTO, SP, ERR, TMP) \
291     {									\
292 	(SP) = getservbyname_r((NAME), (PROTO), &TMP.ent,		\
293 			       TMP.buf, sizeof (TMP.buf));		\
294 	(ERR) = (SP) == NULL;						\
295     }
296 
297 #define GET_SERV_BY_PORT(PORT, PROTO, SP, ERR, TMP) \
298     {									\
299 	struct servent *my_sp;						\
300 	my_sp = getservbyport_r((PORT), (PROTO), &TMP.ent,		\
301 				TMP.buf, sizeof (TMP.buf));		\
302 	(SP) = my_sp;							\
303 	(ERR) = my_sp == 0;						\
304 	(ERR) = (ERR);	/* avoid "unused" warning */			\
305     }
306 #endif
307 #endif
308 
309 #if defined(WRAP_GETADDRINFO) || defined(FAI_CACHE)
310 static inline int
311 system_getaddrinfo (const char *name, const char *serv,
312 		    const struct addrinfo *hint,
313 		    struct addrinfo **res)
314 {
315     return getaddrinfo(name, serv, hint, res);
316 }
317 
318 static inline void
319 system_freeaddrinfo (struct addrinfo *ai)
320 {
321     freeaddrinfo(ai);
322 }
323 
324 /* Note: Implementations written to RFC 2133 use size_t, while RFC
325    2553 implementations use socklen_t, for the second parameter.
326 
327    Mac OS X (10.2) and AIX 4.3.3 appear to be in the RFC 2133 camp,
328    but we don't have an autoconf test for that right now.  */
329 static inline int
330 system_getnameinfo (const struct sockaddr *sa, socklen_t salen,
331 		    char *host, size_t hostlen, char *serv, size_t servlen,
332 		    int flags)
333 {
334     return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
335 }
336 #endif
337 
338 #if !defined (HAVE_GETADDRINFO) || defined(WRAP_GETADDRINFO) || defined(FAI_CACHE)
339 
340 #undef  getaddrinfo
341 #define getaddrinfo	my_fake_getaddrinfo
342 #undef  freeaddrinfo
343 #define freeaddrinfo	my_fake_freeaddrinfo
344 
345 #endif
346 
347 #if !defined (HAVE_GETADDRINFO)
348 
349 #undef  gai_strerror
350 #define gai_strerror	my_fake_gai_strerror
351 
352 #endif /* ! HAVE_GETADDRINFO */
353 
354 #if (!defined (HAVE_GETADDRINFO) || defined (WRAP_GETADDRINFO)) && defined(DEBUG_ADDRINFO)
355 /* Some debug routines.  */
356 
357 static const char *protoname (int p, char *buf) {
358 #define X(N) if (p == IPPROTO_ ## N) return #N
359 
360     X(TCP);
361     X(UDP);
362     X(ICMP);
363     X(IPV6);
364 #ifdef IPPROTO_GRE
365     X(GRE);
366 #endif
367     X(NONE);
368     X(RAW);
369 #ifdef IPPROTO_COMP
370     X(COMP);
371 #endif
372 #ifdef IPPROTO_IGMP
373     X(IGMP);
374 #endif
375 
376     sprintf(buf, " %-2d", p);
377     return buf;
378 }
379 
380 static const char *socktypename (int t, char *buf) {
381     switch (t) {
382     case SOCK_DGRAM: return "DGRAM";
383     case SOCK_STREAM: return "STREAM";
384     case SOCK_RAW: return "RAW";
385     case SOCK_RDM: return "RDM";
386     case SOCK_SEQPACKET: return "SEQPACKET";
387     }
388     sprintf(buf, " %-2d", t);
389     return buf;
390 }
391 
392 static const char *familyname (int f, char *buf) {
393     switch (f) {
394     default:
395 	sprintf(buf, "AF %d", f);
396 	return buf;
397     case AF_INET: return "AF_INET";
398     case AF_INET6: return "AF_INET6";
399 #ifdef AF_UNIX
400     case AF_UNIX: return "AF_UNIX";
401 #endif
402     }
403 }
404 
405 static void debug_dump_getaddrinfo_args (const char *name, const char *serv,
406 					 const struct addrinfo *hint)
407 {
408     const char *sep;
409     fprintf(stderr,
410 	    "getaddrinfo(hostname %s, service %s,\n"
411 	    "            hints { ",
412 	    name ? name : "(null)", serv ? serv : "(null)");
413     if (hint) {
414 	char buf[30];
415 	sep = "";
416 #define Z(FLAG) if (hint->ai_flags & AI_##FLAG) fprintf(stderr, "%s%s", sep, #FLAG), sep = "|"
417 	Z(CANONNAME);
418 	Z(PASSIVE);
419 #ifdef AI_NUMERICHOST
420 	Z(NUMERICHOST);
421 #endif
422 	if (sep[0] == 0)
423 	    fprintf(stderr, "no-flags");
424 	if (hint->ai_family)
425 	    fprintf(stderr, " %s", familyname(hint->ai_family, buf));
426 	if (hint->ai_socktype)
427 	    fprintf(stderr, " SOCK_%s", socktypename(hint->ai_socktype, buf));
428 	if (hint->ai_protocol)
429 	    fprintf(stderr, " IPPROTO_%s", protoname(hint->ai_protocol, buf));
430     } else
431 	fprintf(stderr, "(null)");
432     fprintf(stderr, " }):\n");
433 }
434 
435 static void debug_dump_error (int err)
436 {
437     fprintf(stderr, "error %d: %s\n", err, gai_strerror(err));
438 }
439 
440 static void debug_dump_addrinfos (const struct addrinfo *ai)
441 {
442     int count = 0;
443     char buf[10];
444     fprintf(stderr, "addrinfos returned:\n");
445     while (ai) {
446 	fprintf(stderr, "%p...", ai);
447 	fprintf(stderr, " socktype=%s", socktypename(ai->ai_socktype, buf));
448 	fprintf(stderr, " ai_family=%s", familyname(ai->ai_family, buf));
449 	if (ai->ai_family != ai->ai_addr->sa_family)
450 	    fprintf(stderr, " sa_family=%s",
451 		    familyname(ai->ai_addr->sa_family, buf));
452 	fprintf(stderr, "\n");
453 	ai = ai->ai_next;
454 	count++;
455     }
456     fprintf(stderr, "end addrinfos returned (%d)\n");
457 }
458 
459 #endif
460 
461 #if !defined (HAVE_GETADDRINFO) || defined (WRAP_GETADDRINFO)
462 
463 static
464 int getaddrinfo (const char *name, const char *serv,
465 		 const struct addrinfo *hint, struct addrinfo **result);
466 
467 static
468 void freeaddrinfo (struct addrinfo *ai);
469 
470 #endif
471 
472 #if !defined (HAVE_GETADDRINFO)
473 
474 #define HAVE_FAKE_GETADDRINFO /* was not originally HAVE_GETADDRINFO */
475 #define HAVE_GETADDRINFO
476 #define NEED_FAKE_GETNAMEINFO
477 #undef  HAVE_GETNAMEINFO
478 #define HAVE_GETNAMEINFO 1
479 
480 #undef  getnameinfo
481 #define getnameinfo	my_fake_getnameinfo
482 
483 static
484 char *gai_strerror (int code);
485 
486 #endif
487 
488 #if !defined (HAVE_GETADDRINFO)
489 static
490 int getnameinfo (const struct sockaddr *addr, socklen_t len,
491 		 char *host, socklen_t hostlen,
492 		 char *service, socklen_t servicelen,
493 		 int flags);
494 #endif
495 
496 /* Fudge things on older gai implementations.  */
497 /* AIX 4.3.3 is based on RFC 2133; no AI_NUMERICHOST.  */
498 #ifndef AI_NUMERICHOST
499 # define AI_NUMERICHOST 0
500 #endif
501 /* Partial RFC 2553 implementations may not have AI_ADDRCONFIG and
502    friends, which RFC 3493 says are now part of the getaddrinfo
503    interface, and we'll want to use.  */
504 #ifndef AI_ADDRCONFIG
505 # define AI_ADDRCONFIG 0
506 #endif
507 #ifndef AI_V4MAPPED
508 # define AI_V4MAPPED 0
509 #endif
510 #ifndef AI_ALL
511 # define AI_ALL 0
512 #endif
513 #ifndef AI_DEFAULT
514 # define AI_DEFAULT (AI_ADDRCONFIG|AI_V4MAPPED)
515 #endif
516 
517 #if defined(HAVE_FAKE_GETADDRINFO) || defined(FAI_CACHE)
518 #define NEED_FAKE_GETADDRINFO
519 #endif
520 
521 #if defined(NEED_FAKE_GETADDRINFO) || defined(WRAP_GETADDRINFO)
522 #include <stdlib.h>
523 #endif
524 
525 #ifdef NEED_FAKE_GETADDRINFO
526 #include <string.h> /* for strspn */
527 
528 static inline int translate_h_errno (int h);
529 
530 static inline int fai_add_entry (struct addrinfo **result, void *addr,
531 				 int port, const struct addrinfo *template)
532 {
533     struct addrinfo *n = malloc (sizeof (struct addrinfo));
534     if (n == 0)
535 	return EAI_MEMORY;
536     if (template->ai_family != AF_INET
537 #ifdef KRB5_USE_INET6
538 	&& template->ai_family != AF_INET6
539 #endif
540 	)
541 	return EAI_FAMILY;
542     *n = *template;
543     if (template->ai_family == AF_INET) {
544 	struct sockaddr_in *sin4;
545 	sin4 = malloc (sizeof (struct sockaddr_in));
546 	if (sin4 == 0)
547 	    return EAI_MEMORY;
548         memset (sin4, 0, sizeof (struct sockaddr_in)); /* for sin_zero */
549 	n->ai_addr = (struct sockaddr *) sin4;
550 	sin4->sin_family = AF_INET;
551 	sin4->sin_addr = *(struct in_addr *)addr;
552 	sin4->sin_port = port;
553 #ifdef HAVE_SA_LEN
554 	sin4->sin_len = sizeof (struct sockaddr_in);
555 #endif
556     }
557 #ifdef KRB5_USE_INET6
558     if (template->ai_family == AF_INET6) {
559 	struct sockaddr_in6 *sin6;
560 	sin6 = malloc (sizeof (struct sockaddr_in6));
561 	if (sin6 == 0)
562 	    return EAI_MEMORY;
563         memset (sin6, 0, sizeof (struct sockaddr_in6)); /* for sin_zero */
564 	n->ai_addr = (struct sockaddr *) sin6;
565 	sin6->sin6_family = AF_INET6;
566 	sin6->sin6_addr = *(struct in6_addr *)addr;
567 	sin6->sin6_port = port;
568 #ifdef HAVE_SA_LEN
569 	sin6->sin6_len = sizeof (struct sockaddr_in6);
570 #endif
571     }
572 #endif
573     n->ai_next = *result;
574     *result = n;
575     return 0;
576 }
577 
578 #ifdef FAI_CACHE
579 /* fake addrinfo cache entries */
580 #define CACHE_ENTRY_LIFETIME	15 /* seconds */
581 
582 static void plant_face (const char *name, struct face *entry)
583 {
584     entry->name = strdup(name);
585     if (entry->name == NULL)
586 	/* @@ Wastes memory.  */
587 	return;
588     k5_mutex_assert_locked(&krb5int_fac.lock);
589     entry->next = krb5int_fac.data;
590     entry->expiration = time(0) + CACHE_ENTRY_LIFETIME;
591     krb5int_fac.data = entry;
592 #ifdef DEBUG_ADDRINFO
593     printf("added cache entry '%s' at %p: %d ipv4, %d ipv6; expire %d\n",
594 	   name, entry, entry->naddrs4, entry->naddrs6, entry->expiration);
595 #endif
596 }
597 
598 static int find_face (const char *name, struct face **entry)
599 {
600     struct face *fp, **fpp;
601     time_t now = time(0);
602 
603     /* First, scan for expired entries and free them.
604        (Future improvement: Integrate these two loops.)  */
605 #ifdef DEBUG_ADDRINFO
606     printf("scanning cache at %d for '%s'...\n", now, name);
607 #endif
608     k5_mutex_assert_locked(&krb5int_fac.lock);
609     for (fpp = &krb5int_fac.data; *fpp; ) {
610 	fp = *fpp;
611 #ifdef DEBUG_ADDRINFO
612 	printf("  checking expiration time of @%p: %d\n",
613 	       fp, fp->expiration);
614 #endif
615 	if (fp->expiration < now) {
616 #ifdef DEBUG_ADDRINFO
617 	    printf("\texpiring cache entry\n");
618 #endif
619 	    free(fp->name);
620 	    free(fp->canonname);
621 	    free(fp->addrs4);
622 	    free(fp->addrs6);
623 	    *fpp = fp->next;
624 	    free(fp);
625 	    /* Stay at this point in the list, and check again.  */
626 	} else
627 	    /* Move forward.  */
628 	    fpp = &(*fpp)->next;
629     }
630 
631     for (fp = krb5int_fac.data; fp; fp = fp->next) {
632 #ifdef DEBUG_ADDRINFO
633 	printf("  comparing entry @%p\n", fp);
634 #endif
635 	if (!strcasecmp(fp->name, name)) {
636 #ifdef DEBUG_ADDRINFO
637 	    printf("\tMATCH!\n");
638 #endif
639 	    *entry = fp;
640 	    return 1;
641 	}
642     }
643     return 0;
644 }
645 
646 #endif
647 
648 static int krb5int_lock_fac(void), krb5int_unlock_fac(void);
649 
650 static inline int fai_add_hosts_by_name (const char *name,
651 					 struct addrinfo *template,
652 					 int portnum, int flags,
653 					 struct addrinfo **result)
654 {
655 #ifdef FAI_CACHE
656 
657     struct face *ce;
658     int i, r, err;
659 
660     err = krb5int_lock_fac();
661     if (err) {
662 	errno = err;
663 	return EAI_SYSTEM;
664     }
665     if (!find_face(name, &ce)) {
666 	struct addrinfo myhints = { 0 }, *ai, *ai2;
667 	int i4, i6, aierr;
668 
669 #ifdef DEBUG_ADDRINFO
670 	printf("looking up new data for '%s'...\n", name);
671 #endif
672 	myhints.ai_socktype = SOCK_STREAM;
673 	myhints.ai_flags = AI_CANONNAME;
674 	/* Don't set ai_family -- we want to cache all address types,
675 	   because the next lookup may not use the same constraints as
676 	   the current one.  We *could* cache them separately, so that
677 	   we never have to look up an IPv6 address if we are always
678 	   asked for IPv4 only, but let's deal with that later, if we
679 	   have to.  */
680 	/* Try NULL for the service for now.
681 
682 	   It would be nice to use the requested service name, and not
683 	   have to patch things up, but then we'd be doing multiple
684 	   queries for the same host when we get different services.
685 	   We were using "telnet" for a little more confidence that
686 	   getaddrinfo would heed the hints to only give us stream
687 	   socket types (with no socket type and null service name, we
688 	   might get stream *and* dgram *and* raw, for each address,
689 	   or only raw).  The RFC 3493 description of ai_socktype
690 	   sometimes associates it with the specified service,
691 	   sometimes not.
692 
693 	   But on Mac OS X (10.3, 10.4) they've "extended" getaddrinfo
694 	   to make SRV RR queries.  (Please, somebody, show me
695 	   something in the specs that actually supports this?  RFC
696 	   3493 says nothing about it, but it does say getaddrinfo is
697 	   the new way to look up hostnames.  RFC 2782 says SRV
698 	   records should *not* be used unless the application
699 	   protocol spec says to do so.  The Telnet spec does not say
700 	   to do it.)  And then they complain when our code
701 	   "unexpectedly" seems to use this "extension" in cases where
702 	   they don't want it to be used.
703 
704 	   Fortunately, it appears that if we specify ai_socktype as
705 	   SOCK_STREAM and use a null service name, we only get one
706 	   copy of each address on all the platforms I've tried,
707 	   although it may not have ai_socktype filled in properly.
708 	   So, we'll fudge it with that for now.  */
709 	aierr = system_getaddrinfo(name, NULL, &myhints, &ai);
710 	if (aierr) {
711 	    krb5int_unlock_fac();
712 	    return aierr;
713 	}
714 	ce = malloc(sizeof(struct face));
715 	memset(ce, 0, sizeof(*ce));
716 	ce->expiration = time(0) + 30;
717 	for (ai2 = ai; ai2; ai2 = ai2->ai_next) {
718 #ifdef DEBUG_ADDRINFO
719 	    printf("  found an address in family %d...\n", ai2->ai_family);
720 #endif
721 	    switch (ai2->ai_family) {
722 	    case AF_INET:
723 		ce->naddrs4++;
724 		break;
725 	    case AF_INET6:
726 		ce->naddrs6++;
727 		break;
728 	    default:
729 		break;
730 	    }
731 	}
732 	ce->addrs4 = calloc(ce->naddrs4, sizeof(*ce->addrs4));
733 	if (ce->addrs4 == NULL && ce->naddrs4 != 0) {
734 	    krb5int_unlock_fac();
735 	    system_freeaddrinfo(ai);
736 	    return EAI_MEMORY;
737 	}
738 	ce->addrs6 = calloc(ce->naddrs6, sizeof(*ce->addrs6));
739 	if (ce->addrs6 == NULL && ce->naddrs6 != 0) {
740 	    krb5int_unlock_fac();
741 	    free(ce->addrs4);
742 	    system_freeaddrinfo(ai);
743 	    return EAI_MEMORY;
744 	}
745 	for (ai2 = ai, i4 = i6 = 0; ai2; ai2 = ai2->ai_next) {
746 	    switch (ai2->ai_family) {
747 	    case AF_INET:
748 		ce->addrs4[i4++] = ((struct sockaddr_in *)ai2->ai_addr)->sin_addr;
749 		break;
750 	    case AF_INET6:
751 		ce->addrs6[i6++] = ((struct sockaddr_in6 *)ai2->ai_addr)->sin6_addr;
752 		break;
753 	    default:
754 		break;
755 	    }
756 	}
757 	ce->canonname = ai->ai_canonname ? strdup(ai->ai_canonname) : 0;
758 	system_freeaddrinfo(ai);
759 	plant_face(name, ce);
760     }
761     template->ai_family = AF_INET6;
762     template->ai_addrlen = sizeof(struct sockaddr_in6);
763     for (i = 0; i < ce->naddrs6; i++) {
764 	r = fai_add_entry (result, &ce->addrs6[i], portnum, template);
765 	if (r) {
766 	    krb5int_unlock_fac();
767 	    return r;
768 	}
769     }
770     template->ai_family = AF_INET;
771     template->ai_addrlen = sizeof(struct sockaddr_in);
772     for (i = 0; i < ce->naddrs4; i++) {
773 	r = fai_add_entry (result, &ce->addrs4[i], portnum, template);
774 	if (r) {
775 	    krb5int_unlock_fac();
776 	    return r;
777 	}
778     }
779     if (*result && (flags & AI_CANONNAME))
780 	(*result)->ai_canonname = (ce->canonname
781 				   ? strdup(ce->canonname)
782 				   : NULL);
783     krb5int_unlock_fac();
784     return 0;
785 
786 #else
787 
788     struct hostent *hp;
789     int i, r;
790     int herr;
791     GET_HOST_TMP htmp;
792 
793     GET_HOST_BY_NAME (name, hp, herr, htmp);
794     if (hp == 0)
795 	return translate_h_errno (herr);
796     for (i = 0; hp->h_addr_list[i]; i++) {
797 	r = fai_add_entry (result, hp->h_addr_list[i], portnum, template);
798 	if (r)
799 	    return r;
800     }
801     if (*result && (flags & AI_CANONNAME))
802 	(*result)->ai_canonname = strdup (hp->h_name);
803     return 0;
804 
805 #endif
806 }
807 
808 static inline void
809 fake_freeaddrinfo (struct addrinfo *ai)
810 {
811     struct addrinfo *next;
812     while (ai) {
813 	next = ai->ai_next;
814 	if (ai->ai_canonname)
815 	  free (ai->ai_canonname);
816 	if (ai->ai_addr)
817 	  free (ai->ai_addr);
818 	free (ai);
819 	ai = next;
820     }
821 }
822 
823 static inline int
824 fake_getaddrinfo (const char *name, const char *serv,
825 		  const struct addrinfo *hint, struct addrinfo **result)
826 {
827     struct addrinfo *res = 0;
828     int ret;
829     int port = 0, socktype;
830     int flags;
831     struct addrinfo template;
832 
833 #ifdef DEBUG_ADDRINFO
834     debug_dump_getaddrinfo_args(name, serv, hint);
835 #endif
836 
837     if (hint != 0) {
838 	if (hint->ai_family != 0 && hint->ai_family != AF_INET)
839 	    return EAI_NODATA;
840 	socktype = hint->ai_socktype;
841 	flags = hint->ai_flags;
842     } else {
843 	socktype = 0;
844 	flags = 0;
845     }
846 
847     if (serv) {
848 	size_t numlen = strspn (serv, "0123456789");
849 	if (serv[numlen] == '\0') {
850 	    /* pure numeric */
851 	    unsigned long p = strtoul (serv, 0, 10);
852 	    if (p == 0 || p > 65535)
853 		return EAI_NONAME;
854 	    port = htons (p);
855 	} else {
856 	    struct servent *sp;
857 	    int try_dgram_too = 0, s_err;
858 	    GET_SERV_TMP stmp;
859 
860 	    if (socktype == 0) {
861 		try_dgram_too = 1;
862 		socktype = SOCK_STREAM;
863 	    }
864 	try_service_lookup:
865 	    GET_SERV_BY_NAME(serv, socktype == SOCK_STREAM ? "tcp" : "udp",
866 			     sp, s_err, stmp);
867 	    if (sp == 0) {
868 		if (try_dgram_too) {
869 		    socktype = SOCK_DGRAM;
870 		    goto try_service_lookup;
871 		}
872 		return EAI_SERVICE;
873 	    }
874 	    port = sp->s_port;
875 	}
876     }
877 
878     if (name == 0) {
879 	name = (flags & AI_PASSIVE) ? "0.0.0.0" : "127.0.0.1";
880 	flags |= AI_NUMERICHOST;
881     }
882 
883     template.ai_family = AF_INET;
884     template.ai_addrlen = sizeof (struct sockaddr_in);
885     template.ai_socktype = socktype;
886     template.ai_protocol = 0;
887     template.ai_flags = 0;
888     template.ai_canonname = 0;
889     template.ai_next = 0;
890     template.ai_addr = 0;
891 
892     /* If NUMERICHOST is set, parse a numeric address.
893        If it's not set, don't accept such names.  */
894     if (flags & AI_NUMERICHOST) {
895 	struct in_addr addr4;
896 #if 0
897 	ret = inet_aton (name, &addr4);
898 	if (ret)
899 	    return EAI_NONAME;
900 #else
901 	addr4.s_addr = inet_addr (name);
902 	if (addr4.s_addr == 0xffffffff || addr4.s_addr == -1)
903 	    /* 255.255.255.255 or parse error, both bad */
904 	    return EAI_NONAME;
905 #endif
906 	ret = fai_add_entry (&res, &addr4, port, &template);
907     } else {
908 	ret = fai_add_hosts_by_name (name, &template, port, flags,
909 				     &res);
910     }
911 
912     if (ret && ret != NO_ADDRESS) {
913 	fake_freeaddrinfo (res);
914 	return ret;
915     }
916     if (res == 0)
917 	return NO_ADDRESS;
918     *result = res;
919     return 0;
920 }
921 
922 #ifdef NEED_FAKE_GETNAMEINFO
923 static inline int
924 fake_getnameinfo (const struct sockaddr *sa, socklen_t len,
925 		  char *host, socklen_t hostlen,
926 		  char *service, socklen_t servicelen,
927 		  int flags)
928 {
929     struct hostent *hp;
930     const struct sockaddr_in *sinp;
931     struct servent *sp;
932     size_t hlen, slen;
933 
934     if (sa->sa_family != AF_INET) {
935 	return EAI_FAMILY;
936     }
937     sinp = (const struct sockaddr_in *) sa;
938 
939     hlen = hostlen;
940     if (hostlen < 0 || hlen != hostlen) {
941 	errno = EINVAL;
942 	return EAI_SYSTEM;
943     }
944     slen = servicelen;
945     if (servicelen < 0 || slen != servicelen) {
946 	errno = EINVAL;
947 	return EAI_SYSTEM;
948     }
949 
950     if (host) {
951 	if (flags & NI_NUMERICHOST) {
952 #if (defined(__GNUC__) && defined(__mips__)) || 1 /* thread safety always */
953 	    /* The inet_ntoa call, passing a struct, fails on IRIX 6.5
954 	       using gcc 2.95; we get back "0.0.0.0".  Since this in a
955 	       configuration still important at Athena, here's the
956 	       workaround, which also happens to be thread-safe....  */
957 	    const unsigned char *uc;
958 	    char tmpbuf[20];
959 	numeric_host:
960 	    uc = (const unsigned char *) &sinp->sin_addr;
961 	    sprintf(tmpbuf, "%d.%d.%d.%d", uc[0], uc[1], uc[2], uc[3]);
962 	    strncpy(host, tmpbuf, hlen);
963 #else
964 	    char *p;
965 	numeric_host:
966 	    p = inet_ntoa (sinp->sin_addr);
967 	    strncpy (host, p, hlen);
968 #endif
969 	} else {
970 	    int herr;
971 	    GET_HOST_TMP htmp;
972 
973 	    GET_HOST_BY_ADDR((const char *) &sinp->sin_addr,
974 			     sizeof (struct in_addr),
975 			     sa->sa_family, hp, herr, htmp);
976 	    if (hp == 0) {
977 		if (herr == NO_ADDRESS && !(flags & NI_NAMEREQD)) /* ??? */
978 		    goto numeric_host;
979 		return translate_h_errno (herr);
980 	    }
981 	    /* According to the Open Group spec, getnameinfo can
982 	       silently truncate, but must still return a
983 	       null-terminated string.  */
984 	    strncpy (host, hp->h_name, hlen);
985 	}
986 	host[hostlen-1] = 0;
987     }
988 
989     if (service) {
990 	if (flags & NI_NUMERICSERV) {
991 	    char numbuf[10];
992 	    int port;
993 	numeric_service:
994 	    port = ntohs (sinp->sin_port);
995 	    if (port < 0 || port > 65535)
996 		return EAI_FAIL;
997 	    sprintf (numbuf, "%d", port);
998 	    strncpy (service, numbuf, slen);
999 	} else {
1000 	    int serr;
1001 	    GET_SERV_TMP stmp;
1002 
1003 	    GET_SERV_BY_PORT(sinp->sin_port,
1004 			     (flags & NI_DGRAM) ? "udp" : "tcp",
1005 			     sp, serr, stmp);
1006 	    if (sp == 0)
1007 		goto numeric_service;
1008 	    strncpy (service, sp->s_name, slen);
1009 	}
1010 	service[servicelen-1] = 0;
1011     }
1012 
1013     return 0;
1014 }
1015 #endif
1016 
1017 #if defined(HAVE_FAKE_GETADDRINFO) || defined(NEED_FAKE_GETNAMEINFO)
1018 
1019 static inline
1020 char *gai_strerror (int code)
1021 {
1022     switch (code) {
1023     case EAI_ADDRFAMILY: return "address family for nodename not supported";
1024     case EAI_AGAIN:	return "temporary failure in name resolution";
1025     case EAI_BADFLAGS:	return "bad flags to getaddrinfo/getnameinfo";
1026     case EAI_FAIL:	return "non-recoverable failure in name resolution";
1027     case EAI_FAMILY:	return "ai_family not supported";
1028     case EAI_MEMORY:	return "out of memory";
1029     case EAI_NODATA:	return "no address associated with hostname";
1030     case EAI_NONAME:	return "name does not exist";
1031     case EAI_SERVICE:	return "service name not supported for specified socket type";
1032     case EAI_SOCKTYPE:	return "ai_socktype not supported";
1033     case EAI_SYSTEM:	return strerror (errno);
1034     default:		return "bogus getaddrinfo error?";
1035     }
1036 }
1037 #endif
1038 
1039 static inline int translate_h_errno (int h)
1040 {
1041     switch (h) {
1042     case 0:
1043 	return 0;
1044 #ifdef NETDB_INTERNAL
1045     case NETDB_INTERNAL:
1046 	if (errno == ENOMEM)
1047 	    return EAI_MEMORY;
1048 	return EAI_SYSTEM;
1049 #endif
1050     case HOST_NOT_FOUND:
1051 	return EAI_NONAME;
1052     case TRY_AGAIN:
1053 	return EAI_AGAIN;
1054     case NO_RECOVERY:
1055 	return EAI_FAIL;
1056     case NO_DATA:
1057 #if NO_DATA != NO_ADDRESS
1058     case NO_ADDRESS:
1059 #endif
1060 	return EAI_NODATA;
1061     default:
1062 	return EAI_SYSTEM;
1063     }
1064 }
1065 
1066 #if defined(HAVE_FAKE_GETADDRINFO) || defined(FAI_CACHE)
1067 static inline
1068 int getaddrinfo (const char *name, const char *serv,
1069 		 const struct addrinfo *hint, struct addrinfo **result)
1070 {
1071     return fake_getaddrinfo(name, serv, hint, result);
1072 }
1073 
1074 static inline
1075 void freeaddrinfo (struct addrinfo *ai)
1076 {
1077     fake_freeaddrinfo(ai);
1078 }
1079 
1080 #ifdef NEED_FAKE_GETNAMEINFO
1081 static inline
1082 int getnameinfo (const struct sockaddr *sa, socklen_t len,
1083 		 char *host, socklen_t hostlen,
1084 		 char *service, socklen_t servicelen,
1085 		 int flags)
1086 {
1087     return fake_getnameinfo(sa, len, host, hostlen, service, servicelen,
1088 			    flags);
1089 }
1090 #endif /* NEED_FAKE_GETNAMEINFO */
1091 #endif /* HAVE_FAKE_GETADDRINFO */
1092 #endif /* NEED_FAKE_GETADDRINFO */
1093 
1094 
1095 #ifdef WRAP_GETADDRINFO
1096 
1097 static inline
1098 int
1099 getaddrinfo (const char *name, const char *serv, const struct addrinfo *hint,
1100 	     struct addrinfo **result)
1101 {
1102     int aierr;
1103 #if defined(_AIX) || defined(COPY_FIRST_CANONNAME)
1104     struct addrinfo *ai;
1105 #endif
1106 #ifdef NUMERIC_SERVICE_BROKEN
1107     int service_is_numeric = 0;
1108     int service_port = 0;
1109     int socket_type = 0;
1110 #endif
1111 
1112 #ifdef DEBUG_ADDRINFO
1113     debug_dump_getaddrinfo_args(name, serv, hint);
1114 #endif
1115 
1116 #ifdef NUMERIC_SERVICE_BROKEN
1117     /* AIX 4.3.3 is broken.  (Or perhaps out of date?)
1118 
1119        If a numeric service is provided, and it doesn't correspond to
1120        a known service name for tcp or udp (as appropriate), an error
1121        code (for "host not found") is returned.  If the port maps to a
1122        known service for both udp and tcp, all is well.  */
1123     if (serv && serv[0] && isdigit(serv[0])) {
1124 	unsigned long lport;
1125 	char *end;
1126 	lport = strtoul(serv, &end, 10);
1127 	if (!*end) {
1128 	    if (lport > 65535)
1129 		return EAI_SOCKTYPE;
1130 	    service_is_numeric = 1;
1131 	    service_port = htons(lport);
1132 #ifdef AI_NUMERICSERV
1133 	    if (hint && hint->ai_flags & AI_NUMERICSERV)
1134 		serv = "9";
1135 	    else
1136 #endif
1137 		serv = "discard";	/* defined for both udp and tcp */
1138 	    if (hint)
1139 		socket_type = hint->ai_socktype;
1140 	}
1141     }
1142 #endif
1143 
1144     aierr = system_getaddrinfo (name, serv, hint, result);
1145     if (aierr || *result == 0) {
1146 #ifdef DEBUG_ADDRINFO
1147 	debug_dump_error(aierr);
1148 #endif
1149 	return aierr;
1150     }
1151 
1152     /* Linux libc version 6 (libc-2.2.4.so on Debian) is broken.
1153 
1154        RFC 2553 says that when AI_CANONNAME is set, the ai_canonname
1155        flag of the first returned structure has the canonical name of
1156        the host.  Instead, GNU libc sets ai_canonname in each returned
1157        structure to the name that the corresponding address maps to,
1158        if any, or a printable numeric form.
1159 
1160        RFC 2553 bis and the new Open Group spec say that field will be
1161        the canonical name if it can be determined, otherwise, the
1162        provided hostname or a copy of it.
1163 
1164        IMNSHO, "canonical name" means CNAME processing and not PTR
1165        processing, but I can see arguing it.  Using the numeric form
1166        when that's not the form provided is just wrong.  So, let's fix
1167        it.
1168 
1169        The glibc 2.2.5 sources indicate that the canonical name is
1170        *not* allocated separately, it's just some extra storage tacked
1171        on the end of the addrinfo structure.  So, let's try this
1172        approach: If getaddrinfo sets ai_canonname, we'll replace the
1173        *first* one with allocated storage, and free up that pointer in
1174        freeaddrinfo if it's set; the other ai_canonname fields will be
1175        left untouched.  And we'll just pray that the application code
1176        won't mess around with the list structure; if we start doing
1177        that, we'll have to start replacing and freeing all of the
1178        ai_canonname fields.
1179 
1180        Ref: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=133668 .
1181 
1182        Since it's dependent on the target hostname, it's hard to check
1183        for at configure time.  Always do it on Linux for now.  When
1184        they get around to fixing it, add a compile-time or run-time
1185        check for the glibc version in use.
1186 
1187        Some Windows documentation says that even when AI_CANONNAME is
1188        set, the returned ai_canonname field can be null.  The NetBSD
1189        1.5 implementation also does this, if the input hostname is a
1190        numeric host address string.  That case isn't handled well at
1191        the moment.
1192 
1193        Libc version 5 didn't have getaddrinfo at all.  */
1194 
1195 #ifdef COPY_FIRST_CANONNAME
1196     /*
1197      * This code must *always* return an error, return a null
1198      * ai_canonname, or return an ai_canonname allocated here using
1199      * malloc, so that freeaddrinfo can always free a non-null
1200      * ai_canonname.  Note that it really doesn't matter if the
1201      * AI_CANONNAME flag was set.
1202      */
1203     ai = *result;
1204     if (ai->ai_canonname) {
1205 	struct hostent *hp;
1206 	const char *name2 = 0;
1207 	int i, herr;
1208 	GET_HOST_TMP htmp;
1209 
1210 	/*
1211 	 * Current versions of GET_HOST_BY_NAME will fail if the
1212 	 * target hostname has IPv6 addresses only.  Make sure it
1213 	 * fails fairly cleanly.
1214 	 */
1215 	GET_HOST_BY_NAME (name, hp, herr, htmp);
1216 	if (hp == 0) {
1217 	    /*
1218 	     * This case probably means it's an IPv6-only name.  If
1219 	     * ai_canonname is a numeric address, get rid of it.
1220 	     */
1221 	    if (ai->ai_canonname && strchr(ai->ai_canonname, ':'))
1222 		ai->ai_canonname = 0;
1223 	    name2 = ai->ai_canonname ? ai->ai_canonname : name;
1224 	} else {
1225 	    /* Sometimes gethostbyname will be directed to /etc/hosts
1226 	       first, and sometimes that file will have entries with
1227 	       the unqualified name first.  So take the first entry
1228 	       that looks like it could be a FQDN.  */
1229 	    for (i = 0; hp->h_aliases[i]; i++) {
1230 		if (strchr(hp->h_aliases[i], '.') != 0) {
1231 		    name2 = hp->h_aliases[i];
1232 		    break;
1233 		}
1234 	    }
1235 	    /* Give up, just use the first name (h_name ==
1236 	       h_aliases[0] on all systems I've seen).  */
1237 	    if (hp->h_aliases[i] == 0)
1238 		name2 = hp->h_name;
1239 	}
1240 
1241 	ai->ai_canonname = strdup(name2);
1242 	if (name2 != 0 && ai->ai_canonname == 0) {
1243 	    system_freeaddrinfo(ai);
1244 	    *result = 0;
1245 #ifdef DEBUG_ADDRINFO
1246 	    debug_dump_error(EAI_MEMORY);
1247 #endif
1248 	    return EAI_MEMORY;
1249 	}
1250 	/* Zap the remaining ai_canonname fields glibc fills in, in
1251 	   case the application messes around with the list
1252 	   structure.  */
1253 	while ((ai = ai->ai_next) != NULL)
1254 	    ai->ai_canonname = 0;
1255     }
1256 #endif
1257 
1258 #ifdef NUMERIC_SERVICE_BROKEN
1259     if (service_port != 0) {
1260 	for (ai = *result; ai; ai = ai->ai_next) {
1261 	    if (socket_type != 0 && ai->ai_socktype == 0)
1262 		/* Is this check actually needed?  */
1263 		ai->ai_socktype = socket_type;
1264 	    switch (ai->ai_family) {
1265 	    case AF_INET:
1266 		((struct sockaddr_in *)ai->ai_addr)->sin_port = service_port;
1267 		break;
1268 	    case AF_INET6:
1269 		((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = service_port;
1270 		break;
1271 	    }
1272 	}
1273     }
1274 #endif
1275 
1276 #ifdef _AIX
1277     for (ai = *result; ai; ai = ai->ai_next) {
1278 	/* AIX 4.3.3 libc is broken.  It doesn't set the family or len
1279 	   fields of the sockaddr structures.  Usually, sa_family is
1280 	   zero, but I've seen it set to 1 in some cases also (maybe
1281 	   just leftover from previous contents of the memory
1282 	   block?).  So, always override what libc returned.  */
1283 	ai->ai_addr->sa_family = ai->ai_family;
1284 #ifdef HAVE_SA_LEN /* always true on AIX, actually */
1285 	ai->ai_addr->sa_len = ai->ai_addrlen;
1286 #endif
1287     }
1288 #endif
1289 
1290     /* Not dealt with currently:
1291 
1292        - Some versions of GNU libc can lose some IPv4 addresses in
1293 	 certain cases when multiple IPv4 and IPv6 addresses are
1294 	 available.  */
1295 
1296 #ifdef DEBUG_ADDRINFO
1297     debug_dump_addrinfos(*result);
1298 #endif
1299 
1300     return 0;
1301 }
1302 
1303 static inline
1304 void freeaddrinfo (struct addrinfo *ai)
1305 {
1306 #ifdef COPY_FIRST_CANONNAME
1307     if (ai) {
1308       free(ai->ai_canonname);
1309 	ai->ai_canonname = 0;
1310 	system_freeaddrinfo(ai);
1311     }
1312 #else
1313     system_freeaddrinfo(ai);
1314 #endif
1315 }
1316 #endif /* WRAP_GETADDRINFO */
1317 
1318 static int krb5int_lock_fac (void)
1319 {
1320     int err;
1321     err = krb5int_call_thread_support_init();
1322     if (err)
1323 	return err;
1324     return k5_mutex_lock(&krb5int_fac.lock);
1325 }
1326 
1327 static int krb5int_unlock_fac (void)
1328 {
1329     return k5_mutex_unlock(&krb5int_fac.lock);
1330 }
1331 
1332 /* Some systems don't define in6addr_any.  */
1333 const struct in6_addr krb5int_in6addr_any = IN6ADDR_ANY_INIT;
1334 
1335 int krb5int_getaddrinfo (const char *node, const char *service,
1336 			 const struct addrinfo *hints,
1337 			 struct addrinfo **aip)
1338 {
1339     return getaddrinfo(node, service, hints, aip);
1340 }
1341 
1342 void krb5int_freeaddrinfo (struct addrinfo *ai)
1343 {
1344     freeaddrinfo(ai);
1345 }
1346 
1347 const char *krb5int_gai_strerror(int err)
1348 {
1349     return gai_strerror(err);
1350 }
1351 
1352 int krb5int_getnameinfo (const struct sockaddr *sa, socklen_t salen,
1353 			 char *hbuf, size_t hbuflen,
1354 			 char *sbuf, size_t sbuflen,
1355 			 int flags)
1356 {
1357     return getnameinfo(sa, salen, hbuf, hbuflen, sbuf, sbuflen, flags);
1358 }
1359