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