1 /*	$NetBSD: resolver.c,v 1.4 2014/12/10 04:37:56 christos Exp $	*/
2 
3 #ifndef lint
4 static char *rcsid = "Id: resolver.c,v 1.1 2003/06/04 00:27:12 marka Exp ";
5 #endif
6 
7 /*
8  * Copyright (c) 2001 Japan Network Information Center.  All rights reserved.
9  *
10  * By using this file, you agree to the terms and conditions set forth bellow.
11  *
12  * 			LICENSE TERMS AND CONDITIONS
13  *
14  * The following License Terms and Conditions apply, unless a different
15  * license is obtained from Japan Network Information Center ("JPNIC"),
16  * a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda,
17  * Chiyoda-ku, Tokyo 101-0047, Japan.
18  *
19  * 1. Use, Modification and Redistribution (including distribution of any
20  *    modified or derived work) in source and/or binary forms is permitted
21  *    under this License Terms and Conditions.
22  *
23  * 2. Redistribution of source code must retain the copyright notices as they
24  *    appear in each source code file, this License Terms and Conditions.
25  *
26  * 3. Redistribution in binary form must reproduce the Copyright Notice,
27  *    this License Terms and Conditions, in the documentation and/or other
28  *    materials provided with the distribution.  For the purposes of binary
29  *    distribution the "Copyright Notice" refers to the following language:
30  *    "Copyright (c) 2000-2002 Japan Network Information Center.  All rights reserved."
31  *
32  * 4. The name of JPNIC may not be used to endorse or promote products
33  *    derived from this Software without specific prior written approval of
34  *    JPNIC.
35  *
36  * 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC
37  *    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
38  *    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
39  *    PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL JPNIC BE LIABLE
40  *    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
41  *    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
42  *    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
43  *    BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
44  *    WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45  *    OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
46  *    ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
47  */
48 
49 #include <config.h>
50 
51 #include <stdio.h>
52 #include <stddef.h>
53 #include <stdarg.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <sys/types.h>
57 #include <sys/socket.h>
58 #include <netdb.h>
59 #include <errno.h>
60 
61 #include <idn/api.h>
62 #include <idn/log.h>
63 #include <idn/logmacro.h>
64 #include <idn/debug.h>
65 
66 #ifdef FOR_RUNIDN
67 /*
68  * This file is specially compiled for runidn.
69  * runidn replaces existing resolver functions dynamically with ones
70  * with IDN processing (encoding conversion and normalization).
71  * So entry names must be same as the system's one.
72  */
73 #include "stub.h"
74 
75 #define ENTRY(name) name
76 #define REAL(name) idn_stub_ ## name
77 #else
78 /*
79  * For normal use.  All the entry names are prefixed with "idn_resolver_".
80  * <idn/resolver.h> has bunch of #defines to substitute the standard
81  * name resolver functions with ones provided here.
82  */
83 #include "resolver.h"
84 #undef  gethostbyname
85 #undef  gethostbyname2
86 #undef  gethostbyaddr
87 #undef  gethostbyname_r
88 #undef  gethostbyname2_r
89 #undef  gethostbyaddr_r
90 #undef  getipnodebyname
91 #undef  getipnodebyaddr
92 #undef  getaddrinfo
93 #undef  getnameinfo
94 
95 #define ENTRY(name) idn_resolver_ ## name
96 #define REAL(name) name
97 #endif
98 
99 #define IDN_NAME_SIZE		512
100 
101 #define IDN_HOSTBUF_SIZE	2048
102 typedef union {
103 	char *dummy_for_alignment;
104 	char data[IDN_HOSTBUF_SIZE];
105 } hostbuf_t;
106 
107 typedef struct obj_lock {
108 	void *key;
109 	struct obj_lock *next;
110 } obj_lock_t;
111 
112 #define OBJLOCKHASH_SIZE	127
113 static obj_lock_t *obj_lock_hash[OBJLOCKHASH_SIZE];
114 
115 /*
116  * This variable is to prevent IDN processing occuring more than once for
117  * a single name resolution.  This will happen if some resolver function
118  * is implemented using another function (e.g. gethostbyname() implemented
119  * using gethostbyname2()).
120  * No, using the static variable is not a correct thing to do for a multi-
121  * threading environment, but I don't think of a better solution..
122  */
123 static int idn_isprocessing = 0;
124 
125 static int		obj_hash(void *key);
126 static int		obj_islocked(void *key);
127 static void		obj_lock(void *key);
128 static void		obj_unlock(void *key);
129 static struct hostent	*copy_decode_hostent_static(struct hostent *hp,
130 						    struct hostent *newhp,
131 						    char *buf, size_t buflen,
132 						    int *errp);
133 static char		*decode_name_dynamic(const char *name);
134 static struct hostent	*copy_decode_hostent_dynamic(struct hostent *hp,
135 						     int *errp);
136 static void		free_copied_hostent(struct hostent *hp);
137 #ifdef HAVE_GETADDRINFO
138 static struct addrinfo	*copy_decode_addrinfo_dynamic(struct addrinfo *aip);
139 #endif
140 #ifdef HAVE_FREEADDRINFO
141 static void		free_copied_addrinfo(struct addrinfo *aip);
142 #endif
143 
144 /*
145  * Object locking facility.
146  */
147 
148 static int
obj_hash(void * key)149 obj_hash(void *key) {
150 	/*
151 	 * Hash function for obj_*.
152 	 * 'key' is supposed to be an address.
153 	 */
154 	unsigned long v = (unsigned long)key;
155 
156 	return ((v >> 3) % OBJLOCKHASH_SIZE);
157 }
158 
159 static int
obj_islocked(void * key)160 obj_islocked(void *key)
161 {
162 	/*
163 	 * Check if the object specified by 'key' is locked.
164 	 * Return 1 if so, 0 otherwise.
165 	 */
166 	int h = obj_hash(key);
167 	obj_lock_t *olp = obj_lock_hash[h];
168 
169 	while (olp != NULL) {
170 		if (olp->key == key)
171 			return (1);
172 		olp = olp->next;
173 	}
174 	return (0);
175 }
176 
177 static void
obj_lock(void * key)178 obj_lock(void *key)
179 {
180 	/*
181 	 * Lock an object specified by 'key'.
182 	 */
183 	int h = obj_hash(key);
184 	obj_lock_t *olp;
185 
186 	olp = malloc(sizeof(obj_lock_t));
187 	if (olp != NULL) {
188 		olp->key = key;
189 		olp->next = obj_lock_hash[h];
190 		obj_lock_hash[h] = olp;
191 	}
192 }
193 
194 static void
obj_unlock(void * key)195 obj_unlock(void *key)
196 {
197 	/*
198 	 * Unlock an object specified by 'key'.
199 	 */
200 	int h = obj_hash(key);
201 	obj_lock_t *olp, *olp0;
202 
203 	olp = obj_lock_hash[h];
204 	olp0 = NULL;
205 	while (olp != NULL) {
206 		if (olp->key == key) {
207 			if (olp0 == NULL)
208 				obj_lock_hash[h] = olp->next;
209 			else
210 				olp0->next = olp->next;
211 			free(olp);
212 			return;
213 		}
214 		olp0 = olp;
215 		olp = olp->next;
216 	}
217 }
218 
219 static struct hostent *
copy_decode_hostent_static(struct hostent * hp,struct hostent * newhp,char * buf,size_t buflen,int * errp)220 copy_decode_hostent_static(struct hostent *hp, struct hostent *newhp,
221 			   char *buf, size_t buflen, int *errp)
222 {
223 	/*
224 	 * Copy "struct hostent" data referenced by 'hp' to 'newhp'.
225 	 * It's a deep-copy, meaning all the data referenced by 'hp' are
226 	 * also copied.  They are copied into 'buf', whose length is 'buflen'.
227 	 * The domain names ('hp->h_name' and 'hp->h_aliases') are
228 	 * decoded from ACE to the local encoding before they are copied.
229 	 * If 'buf' is too small to hold all the data, NULL will be
230 	 * returned and '*errp' is set to NO_RECOVERY.
231 	 */
232 	int naliases = 0;
233 	int naddrs = 0;
234 
235 	if (hp == NULL)
236 		return (NULL);
237 
238 	*newhp = *hp;
239 
240 	if (hp->h_aliases != NULL) {
241 		/*
242 		 * Allocate aliase table in 'buf'.
243 		 */
244 		size_t sz;
245 
246 		while (hp->h_aliases[naliases] != NULL)
247 			naliases++;
248 
249 		newhp->h_aliases = (char **)buf;
250 		sz = sizeof(char *) * (naliases + 1);
251 
252 		if (buflen < sz)
253 			goto overflow;
254 
255 		buf += sz;
256 		buflen -= sz;
257 	}
258 
259 	if (hp->h_addr_list != NULL) {
260 		/*
261 		 * Allocate address table in 'buf'.
262 		 */
263 		size_t sz;
264 		int i;
265 
266 		while (hp->h_addr_list[naddrs] != NULL)
267 			naddrs++;
268 
269 		newhp->h_addr_list = (char **)buf;
270 		sz = sizeof(char *) * (naddrs + 1);
271 
272 		if (buflen < sz)
273 			goto overflow;
274 
275 		buf += sz;
276 		buflen -= sz;
277 
278 		/*
279 		 * Copy the addresses.
280 		 */
281 		sz = hp->h_length * naddrs;
282 		if (buflen < sz)
283 			goto overflow;
284 
285 		for (i = 0; i < naddrs; i++) {
286 			newhp->h_addr_list[i] = buf;
287 			memcpy(buf, hp->h_addr_list[i], hp->h_length);
288 			buf += hp->h_length;
289 		}
290 		newhp->h_addr_list[naddrs] = NULL;
291 
292 		buf += sz;
293 		buflen -= sz;
294 	}
295 
296 	if (hp->h_name != NULL) {
297 		/*
298 		 * Decode the name in h_name.
299 		 */
300 		idn_result_t r;
301 		size_t slen;
302 
303 		idn_enable(1);
304 		idn_nameinit(1);
305 		r = idn_decodename(IDN_DECODE_APP, hp->h_name,
306 				   buf, buflen);
307 		switch (r) {
308 		case idn_success:
309 			newhp->h_name = buf;
310 			break;
311 		default:
312 			/* Copy hp->h_name verbatim. */
313 			if (strlen(hp->h_name) + 1 <= buflen) {
314 				newhp->h_name = buf;
315 				strcpy(buf, hp->h_name);
316 				break;
317 			}
318 			/* falllthrough */
319 		case idn_buffer_overflow:
320 			goto overflow;
321 		}
322 
323 		slen = strlen(buf) + 1;
324 		buf += slen;
325 		buflen -= slen;
326 	}
327 
328 	if (hp->h_aliases != NULL) {
329 		/*
330 		 * Decode the names in h_aliases.
331 		 */
332 		char **aliases = hp->h_aliases;
333 		char **newaliases = newhp->h_aliases;
334 		int i;
335 
336 		for (i = 0; i < naliases; i++) {
337 			idn_result_t r;
338 			size_t slen;
339 
340 			idn_enable(1);
341 			idn_nameinit(1);
342 			r = idn_decodename(IDN_DECODE_APP, aliases[i],
343 					   buf, buflen);
344 
345 			switch (r) {
346 			case idn_success:
347 				newaliases[i] = buf;
348 				break;
349 			default:
350 				/* Copy hp->h_name verbatim. */
351 				if (strlen(aliases[i]) + 1 <= buflen) {
352 					newaliases[i] = buf;
353 					strcpy(buf, aliases[i]);
354 					break;
355 				}
356 				/* falllthrough */
357 			case idn_buffer_overflow:
358 				goto overflow;
359 			}
360 
361 			slen = strlen(buf) + 1;
362 			buf += slen;
363 			buflen -= slen;
364 		}
365 		newaliases[naliases] = NULL;
366 	}
367 
368 	return (newhp);
369 
370  overflow:
371 	*errp = NO_RECOVERY;
372 	return (NULL);
373 }
374 
375 static char *
decode_name_dynamic(const char * name)376 decode_name_dynamic(const char *name) {
377 	idn_result_t r;
378 	char buf[IDN_NAME_SIZE];
379 	char *s;
380 
381 	idn_enable(1);
382 	idn_nameinit(1);
383 	r = idn_decodename(IDN_DECODE_APP, name, buf, sizeof(buf));
384 	if (r == idn_success) {
385 		name = buf;
386 	}
387 	s = malloc(strlen(name) + 1);
388 	if (s == NULL)
389 		return (NULL);
390 	else
391 		return (strcpy(s, name));
392 }
393 
394 static struct hostent *
copy_decode_hostent_dynamic(struct hostent * hp,int * errp)395 copy_decode_hostent_dynamic(struct hostent *hp, int *errp) {
396 	/*
397 	 * Make a deep-copy of the data referenced by 'hp', and return
398 	 * a pointer to the copied data.
399 	 * All the data are dynamically allocated using malloc().
400 	 * The domain names ('hp->h_name' and 'hp->h_aliases') are
401 	 * decoded from ACE to the local encoding before they are copied.
402 	 * If malloc() fails, NULL will be returned and '*errp' is set to
403 	 * NO_RECOVERY.
404 	 */
405 	struct hostent *newhp;
406 	char **pp;
407 	size_t alloc_size;
408 	int naliases = 0;
409 	int naddrs = 0;
410 	int i;
411 
412 	if (hp == NULL)
413 		return (NULL);
414 
415 	if (hp->h_aliases != NULL) {
416 		while (hp->h_aliases[naliases] != NULL)
417 			naliases++;
418 	}
419 
420 	if (hp->h_addr_list != NULL) {
421 		while (hp->h_addr_list[naddrs] != NULL)
422 			naddrs++;
423 	}
424 
425 	alloc_size = sizeof(struct hostent) +
426 		sizeof(char *) * (naliases + 1) +
427 		sizeof(char *) * (naddrs + 1) +
428 		hp->h_length * naddrs;
429 
430 	if ((newhp = malloc(alloc_size)) == NULL) {
431 		return (hp);
432 	}
433 
434 	memset(newhp, 0, alloc_size);
435 
436 	pp = (char **)(newhp + 1);
437 
438 	if (hp->h_name != NULL) {
439 		newhp->h_name = decode_name_dynamic(hp->h_name);
440 		if (newhp->h_name == NULL)
441 			goto alloc_fail;
442 	}
443 
444 	newhp->h_addrtype = hp->h_addrtype;
445 	newhp->h_length = hp->h_length;
446 
447 	if (hp->h_aliases != NULL) {
448 		newhp->h_aliases = pp;
449 		for (i = 0; i < naliases; i++) {
450 			newhp->h_aliases[i] =
451 				decode_name_dynamic(hp->h_aliases[i]);
452 			if (newhp->h_aliases[i] == NULL)
453 				goto alloc_fail;
454 		}
455 		newhp->h_aliases[naliases] = NULL;
456 		pp += naliases + 1;
457 	}
458 
459 	if (hp->h_addr_list != NULL) {
460 		char *p;
461 
462 		newhp->h_addr_list = pp;
463 		pp += naddrs + 1;
464 		p = (char *)pp;
465 
466 		for (i = 0; i < naddrs; i++) {
467 			newhp->h_addr_list[i] = p;
468 			memcpy(p, hp->h_addr_list[i], hp->h_length);
469 			p += hp->h_length;
470 		}
471 		newhp->h_addr_list[naddrs] = NULL;
472 	}
473 
474 	return (newhp);
475 
476  alloc_fail:
477 	free_copied_hostent(hp);
478 	*errp = NO_RECOVERY;
479 	return (NULL);
480 }
481 
482 static void
free_copied_hostent(struct hostent * hp)483 free_copied_hostent(struct hostent *hp) {
484 	/*
485 	 * Free all the memory allocated by copy_decode_hostent_dynamic().
486 	 */
487 	if (hp->h_name != NULL)
488 		free(hp->h_name);
489 	if (hp->h_aliases != NULL) {
490 		char **pp = hp->h_aliases;
491 		while (*pp != NULL)
492 			free(*pp++);
493 	}
494 	free(hp);
495 }
496 
497 #ifdef HAVE_GETNAMEINFO
498 static struct addrinfo *
copy_decode_addrinfo_dynamic(struct addrinfo * aip)499 copy_decode_addrinfo_dynamic(struct addrinfo *aip) {
500 	struct addrinfo *newaip;
501 
502 	if (aip == NULL)
503 		return (NULL);
504 
505 	newaip = malloc(sizeof(struct addrinfo) + aip->ai_addrlen);
506 	if (newaip == NULL)
507 		return (NULL);
508 
509 	*newaip = *aip;
510 	newaip->ai_addr = (struct sockaddr *)(newaip + 1);
511 	memcpy(newaip->ai_addr, aip->ai_addr, aip->ai_addrlen);
512 
513 	if (newaip->ai_canonname != NULL)
514 		newaip->ai_canonname = decode_name_dynamic(aip->ai_canonname);
515 
516 	newaip->ai_next = copy_decode_addrinfo_dynamic(aip->ai_next);
517 	return (newaip);
518 }
519 #endif
520 
521 #ifdef HAVE_FREEADDRINFO
522 static void
free_copied_addrinfo(struct addrinfo * aip)523 free_copied_addrinfo(struct addrinfo *aip) {
524 	while (aip != NULL) {
525 		struct addrinfo *next = aip->ai_next;
526 
527 		if (aip->ai_canonname != NULL)
528 			free(aip->ai_canonname);
529 		free(aip);
530 		aip = next;
531 	}
532 }
533 #endif
534 
535 #ifdef HAVE_GETHOSTBYNAME
536 struct hostent *
ENTRY(gethostbyname)537 ENTRY(gethostbyname)(const char *name) {
538 	static hostbuf_t buf;
539 	static struct hostent he;
540 	idn_result_t r;
541 	struct hostent *hp;
542 
543 	if (idn_isprocessing)
544 		return (REAL(gethostbyname)(name));
545 
546 	TRACE(("gethostbyname(name=%s)\n", idn__debug_xstring(name, 60)));
547 
548 	idn_isprocessing = 1;
549 	idn_enable(1);
550 	idn_nameinit(1);
551 	r = idn_encodename(IDN_ENCODE_APP, name, buf.data, sizeof(buf));
552 	if (r == idn_success)
553 		name = buf.data;
554 
555 	hp = copy_decode_hostent_static(REAL(gethostbyname)(name),
556 					&he, buf.data, sizeof(buf),
557 					&h_errno);
558 	idn_isprocessing = 0;
559 	return (hp);
560 }
561 #endif
562 
563 #ifdef HAVE_GETHOSTBYNAME2
564 struct hostent *
ENTRY(gethostbyname2)565 ENTRY(gethostbyname2)(const char *name, int af) {
566 	static hostbuf_t buf;
567 	static struct hostent he;
568 	idn_result_t r;
569 	struct hostent *hp;
570 
571 	if (idn_isprocessing)
572 		return (REAL(gethostbyname2)(name, af));
573 
574 	TRACE(("gethostbyname2(name=%s)\n", idn__debug_xstring(name, 60), af));
575 
576 	idn_isprocessing = 1;
577 	idn_enable(1);
578 	idn_nameinit(1);
579 	r = idn_encodename(IDN_ENCODE_APP, name, buf.data, sizeof(buf));
580 	if (r == idn_success)
581 		name = buf.data;
582 
583 	hp = copy_decode_hostent_static(REAL(gethostbyname2)(name, af),
584 					&he, buf.data, sizeof(buf),
585 					&h_errno);
586 	idn_isprocessing = 0;
587 	return (hp);
588 }
589 #endif
590 
591 #ifdef HAVE_GETHOSTBYADDR
592 struct hostent *
ENTRY(gethostbyaddr)593 ENTRY(gethostbyaddr)(GHBA_ADDR_T addr, GHBA_ADDRLEN_T len, int type) {
594 	static hostbuf_t buf;
595 	static struct hostent he;
596 	struct hostent *hp;
597 
598 	if (idn_isprocessing)
599 		return (REAL(gethostbyaddr)(addr, len, type));
600 
601 	TRACE(("gethostbyaddr()\n"));
602 
603 	idn_isprocessing = 1;
604 	hp = copy_decode_hostent_static(REAL(gethostbyaddr)(addr, len, type),
605 					&he, buf.data, sizeof(buf),
606 					&h_errno);
607 	idn_isprocessing = 0;
608 	return (hp);
609 }
610 #endif
611 
612 #ifdef GETHOST_R_GLIBC_FLAVOR
613 
614 #ifdef HAVE_GETHOSTBYNAME_R
615 int
ENTRY(gethostbyname_r)616 ENTRY(gethostbyname_r)(const char *name, struct hostent *result,
617 		       char *buffer, size_t buflen,
618 		       struct hostent **rp, int *errp)
619 {
620 	char namebuf[IDN_NAME_SIZE];
621 	char *data;
622 	size_t datalen;
623 	idn_result_t r;
624 	struct hostent he;
625 	hostbuf_t buf;
626 	int n;
627 
628 	if (idn_isprocessing)
629 		return (REAL(gethostbyname_r)(name, result, buffer,
630 					      buflen, rp, errp));
631 
632 	TRACE(("gethostbyname_r(name=%s,buflen=%d)\n",
633 	       idn__debug_xstring(name, 60), buflen));
634 
635 	if (buflen <= sizeof(buf)) {
636 		data = buf.data;
637 		datalen = sizeof(buf);
638 	} else {
639 		data = malloc(buflen);
640 		datalen = buflen;
641 		if (data == NULL) {
642 			*errp = NO_RECOVERY;
643 			return (ENOMEM);
644 		}
645 	}
646 
647 	idn_isprocessing = 1;
648 	idn_enable(1);
649 	idn_nameinit(1);
650 	r = idn_encodename(IDN_ENCODE_APP, name, namebuf, sizeof(namebuf));
651 	if (r == idn_success)
652 		name = namebuf;
653 
654 	*errp = 0;
655 	n = REAL(gethostbyname_r)(name, &he, data, datalen, rp, errp);
656 
657 	if (n == 0 && *rp != NULL)
658 		*rp = copy_decode_hostent_static(*rp, result, buffer, buflen,
659 						 errp);
660 	idn_isprocessing = 0;
661 
662 	if (data != buf.data)
663 		free(data);
664 
665 	if (*errp != 0)
666 		n = EINVAL;	/* XXX */
667 
668 	return (n);
669 }
670 #endif
671 
672 #ifdef HAVE_GETHOSTBYNAME2_R
673 int
ENTRY(gethostbyname2_r)674 ENTRY(gethostbyname2_r)(const char *name, int af, struct hostent *result,
675 			char *buffer, size_t buflen,
676 			struct hostent **rp, int *errp)
677 {
678 	char namebuf[IDN_NAME_SIZE];
679 	char *data;
680 	size_t datalen;
681 	idn_result_t r;
682 	struct hostent he;
683 	hostbuf_t buf;
684 	int n;
685 
686 	if (idn_isprocessing)
687 		return (REAL(gethostbyname2_r)(name, af, result, buffer,
688 					       buflen, rp, errp));
689 
690 	TRACE(("gethostbyname2_r(name=%s,buflen=%d)\n",
691 	       idn__debug_xstring(name, 60), buflen));
692 
693 	if (buflen <= sizeof(buf)) {
694 		data = buf.data;
695 		datalen = sizeof(buf);
696 	} else {
697 		data = malloc(buflen);
698 		datalen = buflen;
699 		if (data == NULL) {
700 			*errp = NO_RECOVERY;
701 			return (ENOMEM);
702 		}
703 	}
704 
705 	idn_isprocessing = 1;
706 	idn_enable(1);
707 	idn_nameinit(1);
708 	r = idn_encodename(IDN_ENCODE_APP, name, namebuf, sizeof(namebuf));
709 	if (r == idn_success)
710 		name = namebuf;
711 
712 	n = REAL(gethostbyname2_r)(name, af, &he, data, datalen, rp, errp);
713 
714 	if (n == 0 && *rp != NULL)
715 		*rp = copy_decode_hostent_static(*rp, result, buffer, buflen,
716 						 errp);
717 	idn_isprocessing = 0;
718 
719 	if (data != buf.data)
720 		free(data);
721 
722 	if (*errp != 0)
723 		n = EINVAL;	/* XXX */
724 
725 	return (n);
726 }
727 #endif
728 
729 #ifdef HAVE_GETHOSTBYADDR_R
730 int
ENTRY(gethostbyaddr_r)731 ENTRY(gethostbyaddr_r)(GHBA_ADDR_T addr, GHBA_ADDRLEN_T len, int type,
732 		       struct hostent *result,
733 		       char *buffer, size_t buflen,
734 		       struct hostent **rp, int *errp)
735 {
736 	char *data;
737 	size_t datalen;
738 	struct hostent he;
739 	hostbuf_t buf;
740 	int n;
741 
742 	if (idn_isprocessing) {
743 		return (REAL(gethostbyaddr_r)(addr, len, type, result,
744 					      buffer, buflen, rp, errp));
745 	}
746 
747 	TRACE(("gethostbyaddr_r(buflen=%d)\n", buflen));
748 
749 	if (buflen <= sizeof(buf)) {
750 		data = buf.data;
751 		datalen = sizeof(buf);
752 	} else {
753 		data = malloc(buflen);
754 		datalen = buflen;
755 		if (data == NULL) {
756 			*errp = NO_RECOVERY;
757 			return (ENOMEM);
758 		}
759 	}
760 
761 	idn_isprocessing = 1;
762 	n = REAL(gethostbyaddr_r)(addr, len, type, &he,
763 				   data, datalen, rp, errp);
764 
765 	if (n == 0 && *rp != NULL)
766 		*rp = copy_decode_hostent_static(*rp, result, buffer, buflen,
767 						 errp);
768 	idn_isprocessing = 0;
769 
770 	if (data != buf.data)
771 		free(data);
772 
773 	if (*errp != 0)
774 		n = EINVAL;	/* XXX */
775 
776 	return (0);
777 }
778 #endif
779 
780 #else /* GETHOST_R_GLIBC_FLAVOR */
781 
782 #ifdef HAVE_GETHOSTBYNAME_R
783 struct hostent *
ENTRY(gethostbyname_r)784 ENTRY(gethostbyname_r)(const char *name, struct hostent *result,
785 		       char *buffer, int buflen, int *errp)
786 {
787 	char namebuf[IDN_NAME_SIZE];
788 	char *data;
789 	size_t datalen;
790 	idn_result_t r;
791 	struct hostent *hp, he;
792 	hostbuf_t buf;
793 
794 	if (idn_isprocessing)
795 		return (REAL(gethostbyname_r)(name, result, buffer,
796 					      buflen, errp));
797 
798 	TRACE(("gethostbyname_r(name=%s,buflen=%d)\n",
799 	       idn__debug_xstring(name, 60), buflen));
800 
801 	if (buflen <= sizeof(buf)) {
802 		data = buf.data;
803 		datalen = sizeof(buf);
804 	} else {
805 		data = malloc(buflen);
806 		datalen = buflen;
807 		if (data == NULL) {
808 			*errp = NO_RECOVERY;
809 			return (NULL);
810 		}
811 	}
812 
813 	idn_isprocessing = 1;
814 	idn_enable(1);
815 	idn_nameinit(1);
816 	r = idn_encodename(IDN_ENCODE_APP, name, namebuf, sizeof(namebuf));
817 	if (r == idn_success)
818 		name = namebuf;
819 
820 	hp = REAL(gethostbyname_r)(name, &he, data, datalen, errp);
821 
822 	if (hp != NULL)
823 		hp = copy_decode_hostent_static(hp, result, buffer, buflen,
824 						errp);
825 	idn_isprocessing = 0;
826 
827 	if (data != buf.data)
828 		free(data);
829 
830 	return (hp);
831 }
832 #endif
833 
834 #ifdef HAVE_GETHOSTBYADDR_R
835 struct hostent *
ENTRY(gethostbyaddr_r)836 ENTRY(gethostbyaddr_r)(GHBA_ADDR_T addr, GHBA_ADDRLEN_T len, int type,
837 		       struct hostent *result,
838 		       char *buffer, int buflen, int *errp)
839 {
840 	char *data;
841 	size_t datalen;
842 	struct hostent *hp, he;
843 	hostbuf_t buf;
844 
845 	if (idn_isprocessing) {
846 		return (REAL(gethostbyaddr_r)(addr, len, type, result,
847 					      buffer, buflen, errp));
848 	}
849 
850 	TRACE(("gethostbyaddr_r(buflen=%d)\n", buflen));
851 
852 	if (buflen <= sizeof(buf)) {
853 		data = buf.data;
854 		datalen = sizeof(buf);
855 	} else {
856 		data = malloc(buflen);
857 		datalen = buflen;
858 		if (data == NULL) {
859 			*errp = NO_RECOVERY;
860 			return (NULL);
861 		}
862 	}
863 
864 	idn_isprocessing = 1;
865 	hp = REAL(gethostbyaddr_r)(addr, len, type, &he, data, datalen, errp);
866 
867 	if (hp != NULL)
868 		hp = copy_decode_hostent_static(hp, result, buffer, buflen,
869 						errp);
870 	idn_isprocessing = 0;
871 
872 	if (data != buf.data)
873 		free(data);
874 
875 	return (hp);
876 }
877 #endif
878 
879 #endif /* GETHOST_R_GLIBC_FLAVOR */
880 
881 #ifdef HAVE_GETIPNODEBYNAME
882 struct hostent *
ENTRY(getipnodebyname)883 ENTRY(getipnodebyname)(const char *name, int af, int flags, int *errp) {
884 	char namebuf[IDN_NAME_SIZE];
885 	idn_result_t r;
886 	struct hostent *hp;
887 
888 	if (idn_isprocessing)
889 		return (REAL(getipnodebyname)(name, af, flags, errp));
890 
891 	TRACE(("getipnodebyname(name=%s)\n", idn__debug_xstring(name, 60), af));
892 
893 	idn_isprocessing = 1;
894 	idn_enable(1);
895 	idn_nameinit(1);
896 	r = idn_encodename(IDN_ENCODE_APP, name, namebuf, sizeof(namebuf));
897 	if (r == idn_success)
898 		name = namebuf;
899 
900 	hp = REAL(getipnodebyname)(name, af, flags, errp);
901 	if (hp != NULL) {
902 		struct hostent *newhp = copy_decode_hostent_dynamic(hp, errp);
903 		if (newhp != hp) {
904 			REAL(freehostent)(hp);
905 			obj_lock(newhp);
906 			hp = newhp;
907 		}
908 	}
909 	idn_isprocessing = 0;
910 	return (hp);
911 }
912 #endif
913 
914 #ifdef HAVE_GETIPNODEBYADDR
915 struct hostent *
ENTRY(getipnodebyaddr)916 ENTRY(getipnodebyaddr)(const void *src, size_t len, int af, int *errp) {
917 	struct hostent *hp;
918 
919 	if (idn_isprocessing)
920 		return (REAL(getipnodebyaddr)(src, len, af, errp));
921 
922 	TRACE(("getipnodebyaddr()\n"));
923 
924 	idn_isprocessing = 1;
925 	hp = REAL(getipnodebyaddr)(src, len, af, errp);
926 	if (hp != NULL) {
927 		struct hostent *newhp = copy_decode_hostent_dynamic(hp, errp);
928 		if (newhp != hp) {
929 			REAL(freehostent)(hp);
930 			obj_lock(newhp);
931 			hp = newhp;
932 		}
933 	}
934 	idn_isprocessing = 0;
935 	return (hp);
936 }
937 #endif
938 
939 #ifdef HAVE_FREEHOSTENT
940 void
ENTRY(freehostent)941 ENTRY(freehostent)(struct hostent *hp) {
942 	TRACE(("freehostent(hp=%p)\n", (void *)hp));
943 
944 	if (obj_islocked(hp)) {
945 		/*
946 		 * We allocated the data.
947 		 */
948 		obj_unlock(hp);
949 		free_copied_hostent(hp);
950 	} else {
951 		/*
952 		 * It was allocated the original getipnodeby*().
953 		 */
954 		REAL(freehostent)(hp);
955 	}
956 }
957 #endif
958 
959 #ifdef HAVE_GETADDRINFO
960 int
ENTRY(getaddrinfo)961 ENTRY(getaddrinfo)(const char *nodename, const char *servname,
962 		   const struct addrinfo *hints, struct addrinfo **res)
963 {
964 	char namebuf[IDN_NAME_SIZE];
965 	idn_result_t r;
966 	struct addrinfo *aip;
967 	int err;
968 
969 	if (nodename == NULL || idn_isprocessing)
970 		return (REAL(getaddrinfo)(nodename, servname, hints, res));
971 
972 	TRACE(("getaddrinfo(nodename=%s)\n", idn__debug_xstring(nodename, 60)));
973 
974 	idn_isprocessing = 1;
975 	idn_enable(1);
976 	idn_nameinit(1);
977 	r = idn_encodename(IDN_ENCODE_APP, nodename,
978 			   namebuf, sizeof(namebuf));
979 	if (r == idn_success)
980 		nodename = namebuf;
981 
982 	err = REAL(getaddrinfo)(nodename, servname, hints, &aip);
983 	if (err == 0 && aip != NULL) {
984 		*res = copy_decode_addrinfo_dynamic(aip);
985 		if (*res == NULL)
986 			err = EAI_FAIL;
987 		else
988 			obj_lock(*res);
989 		if (aip != NULL)
990 			REAL(freeaddrinfo)(aip);
991 	}
992 	idn_isprocessing = 0;
993 	return (err);
994 }
995 #endif
996 
997 #ifdef HAVE_FREEADDRINFO
998 void
ENTRY(freeaddrinfo)999 ENTRY(freeaddrinfo)(struct addrinfo *aip) {
1000 	TRACE(("freeaddrinfo(aip=%p)\n", (void *)aip));
1001 
1002 	if (obj_islocked(aip)) {
1003 		/*
1004 		 * We allocated the data.
1005 		 */
1006 		obj_unlock(aip);
1007 		free_copied_addrinfo(aip);
1008 	} else {
1009 		/*
1010 		 * It was allocated the original getaddrinfo().
1011 		 */
1012 		REAL(freeaddrinfo)(aip);
1013 	}
1014 }
1015 #endif
1016 
1017 #ifdef HAVE_GETNAMEINFO
1018 int
ENTRY(getnameinfo)1019 ENTRY(getnameinfo)(const struct sockaddr *sa, GNI_SALEN_T salen,
1020 		   char *host, GNI_HOSTLEN_T hostlen, char *serv,
1021 		   GNI_SERVLEN_T servlen, GNI_FLAGS_T flags)
1022 {
1023 	char name[IDN_NAME_SIZE];
1024 	size_t namelen = sizeof(name);
1025 	int code;
1026 	idn_result_t r;
1027 
1028 	if (host == NULL || hostlen == 0 || idn_isprocessing) {
1029 		return (REAL(getnameinfo)(sa, salen, host, hostlen,
1030 					  serv, servlen, flags));
1031 	}
1032 
1033 	TRACE(("getnameinfo(hostlen=%u)\n", hostlen));
1034 
1035 	idn_isprocessing = 1;
1036 	code = REAL(getnameinfo)(sa, salen, name, namelen,
1037 				 serv, servlen, flags);
1038 	if (code == 0 && name[0] != '\0') {
1039 		idn_enable(1);
1040 		idn_nameinit(1);
1041 		r = idn_decodename(IDN_DECODE_APP, name, host, hostlen);
1042 		switch (r) {
1043 		case idn_success:
1044 			code = 0;
1045 			break;
1046 		case idn_buffer_overflow:
1047 		case idn_nomemory:
1048 			code = EAI_MEMORY;
1049 			break;
1050 		default:
1051 			code = EAI_FAIL;
1052 			break;
1053 		}
1054 	}
1055 	idn_isprocessing = 0;
1056 	return (code);
1057 }
1058 #endif
1059