1 /*	$NetBSD: res.c,v 1.4 2014/12/10 04:37:55 christos Exp $	*/
2 
3 #ifndef lint
4 static char *rcsid = "Id: res.c,v 1.1 2003/06/04 00:26:10 marka Exp ";
5 #endif
6 
7 /*
8  * Copyright (c) 2000,2002 Japan Network Information Center.
9  * All rights reserved.
10  *
11  * By using this file, you agree to the terms and conditions set forth bellow.
12  *
13  * 			LICENSE TERMS AND CONDITIONS
14  *
15  * The following License Terms and Conditions apply, unless a different
16  * license is obtained from Japan Network Information Center ("JPNIC"),
17  * a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda,
18  * Chiyoda-ku, Tokyo 101-0047, Japan.
19  *
20  * 1. Use, Modification and Redistribution (including distribution of any
21  *    modified or derived work) in source and/or binary forms is permitted
22  *    under this License Terms and Conditions.
23  *
24  * 2. Redistribution of source code must retain the copyright notices as they
25  *    appear in each source code file, this License Terms and Conditions.
26  *
27  * 3. Redistribution in binary form must reproduce the Copyright Notice,
28  *    this License Terms and Conditions, in the documentation and/or other
29  *    materials provided with the distribution.  For the purposes of binary
30  *    distribution the "Copyright Notice" refers to the following language:
31  *    "Copyright (c) 2000-2002 Japan Network Information Center.  All rights reserved."
32  *
33  * 4. The name of JPNIC may not be used to endorse or promote products
34  *    derived from this Software without specific prior written approval of
35  *    JPNIC.
36  *
37  * 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC
38  *    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
39  *    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
40  *    PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL JPNIC BE LIABLE
41  *    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42  *    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
43  *    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
44  *    BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
45  *    WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
46  *    OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
47  *    ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
48  */
49 
50 #include <config.h>
51 
52 #include <stddef.h>
53 #include <stdlib.h>
54 #include <string.h>
55 
56 #include <idn/result.h>
57 #include <idn/assert.h>
58 #include <idn/logmacro.h>
59 #include <idn/converter.h>
60 #include <idn/normalizer.h>
61 #include <idn/checker.h>
62 #include <idn/mapper.h>
63 #include <idn/mapselector.h>
64 #include <idn/delimitermap.h>
65 #include <idn/resconf.h>
66 #include <idn/res.h>
67 #include <idn/util.h>
68 #include <idn/debug.h>
69 #include <idn/ucs4.h>
70 
71 #ifndef IDN_UTF8_ENCODING_NAME
72 #define IDN_UTF8_ENCODING_NAME "UTF-8"		/* by IANA */
73 #endif
74 
75 #ifndef WITHOUT_ICONV
76 #define ENCODE_MASK \
77 	(IDN_LOCALCONV | IDN_DELIMMAP | IDN_LOCALMAP | IDN_MAP | \
78 	 IDN_NORMALIZE | IDN_PROHCHECK | IDN_UNASCHECK | IDN_BIDICHECK | \
79 	 IDN_ASCCHECK | IDN_IDNCONV | IDN_LENCHECK | IDN_ENCODE_QUERY | \
80 	 IDN_UNDOIFERR)
81 #define DECODE_MASK \
82 	(IDN_DELIMMAP | IDN_MAP | IDN_NORMALIZE | IDN_PROHCHECK | \
83 	 IDN_UNASCHECK | IDN_BIDICHECK | IDN_IDNCONV | IDN_ASCCHECK | \
84 	 IDN_RTCHECK | IDN_LOCALCONV | IDN_DECODE_QUERY)
85 #else
86 #define ENCODE_MASK \
87 	(IDN_DELIMMAP | IDN_LOCALMAP | IDN_MAP | IDN_NORMALIZE | \
88 	 IDN_PROHCHECK | IDN_UNASCHECK | IDN_BIDICHECK | IDN_ASCCHECK | \
89 	 IDN_IDNCONV | IDN_LENCHECK | IDN_ENCODE_QUERY | IDN_UNDOIFERR)
90 #define DECODE_MASK \
91 	(IDN_DELIMMAP | IDN_MAP | IDN_NORMALIZE | IDN_PROHCHECK | \
92 	 IDN_UNASCHECK | IDN_BIDICHECK | IDN_IDNCONV | IDN_ASCCHECK | \
93 	 IDN_RTCHECK | IDN_DECODE_QUERY)
94 #endif
95 
96 #define MAX_LABEL_LENGTH	63
97 
98 /*
99  * label to convert.
100  */
101 typedef struct labellist * labellist_t;
102 struct labellist {
103 	unsigned long *name;
104 	size_t name_length;
105 	unsigned long *undo_name;
106 	labellist_t next;
107 	labellist_t previous;
108 	int dot_followed;
109 };
110 
111 typedef idn_result_t (*res_insnproc_t)(idn_resconf_t ctx,
112 				       labellist_t label);
113 
114 static void		idn_res_initialize(void);
115 static idn_result_t	copy_verbatim(const char *from, char *to,
116 				      size_t tolen);
117 static idn_result_t	labellist_create(const unsigned long *name,
118 					 labellist_t *labelp);
119 static void		labellist_destroy(labellist_t label);
120 static idn_result_t	labellist_setname(labellist_t label,
121 					  const unsigned long *name);
122 static const unsigned long *
123 			labellist_getname(labellist_t label);
124 static const unsigned long *
125 			labellist_gettldname(labellist_t label);
126 static idn_result_t	labellist_getnamelist(labellist_t label,
127 					      unsigned long *name,
128 					      size_t label_length);
129 static void		labellist_undo(labellist_t label);
130 static labellist_t	labellist_tail(labellist_t label);
131 static labellist_t	labellist_previous(labellist_t label);
132 
133 #ifndef WITHOUT_ICONV
134 static idn_result_t	label_localdecodecheck(idn_resconf_t ctx,
135 					       labellist_t label);
136 #endif
137 static idn_result_t	label_idnencode_ace(idn_resconf_t ctx,
138 					    labellist_t label);
139 static idn_result_t	label_idndecode(idn_resconf_t ctx, labellist_t label);
140 static idn_result_t	label_localmap(idn_resconf_t ctx, labellist_t label);
141 static idn_result_t	label_map(idn_resconf_t ctx, labellist_t label);
142 static idn_result_t	label_normalize(idn_resconf_t ctx, labellist_t label);
143 static idn_result_t	label_prohcheck(idn_resconf_t ctx, labellist_t label);
144 static idn_result_t	label_unascheck(idn_resconf_t ctx, labellist_t label);
145 static idn_result_t	label_bidicheck(idn_resconf_t ctx, labellist_t label);
146 static idn_result_t	label_asccheck(idn_resconf_t ctx, labellist_t label);
147 static idn_result_t	label_lencheck_ace(idn_resconf_t ctx,
148 					   labellist_t label);
149 static idn_result_t	label_lencheck_nonace(idn_resconf_t ctx,
150 					      labellist_t label);
151 static idn_result_t	label_rtcheck(idn_resconf_t ctx, idn_action_t actions,
152 				      labellist_t label,
153 				      const unsigned long *original_name);
154 
155 static int initialized;
156 static int enabled;
157 
158 void
159 idn_res_enable(int on_off) {
160 	if (!initialized) {
161 		idn_res_initialize();
162 	}
163 
164 	if (on_off == 0) {
165 		enabled = 0;
166 	} else {
167 		enabled = 1;
168 	}
169 }
170 
171 static void
172 idn_res_initialize(void) {
173 	if (!initialized) {
174 		char *value = getenv("IDN_DISABLE");
175 
176 		if (value == NULL) {
177 			enabled = 1;
178 		} else {
179 			enabled = 0;
180 		}
181 		initialized = 1;
182 	}
183 }
184 
185 idn_result_t
186 idn_res_encodename(idn_resconf_t ctx, idn_action_t actions, const char *from,
187 		    char *to, size_t tolen) {
188 	idn_converter_t local_converter = NULL;
189 	idn_converter_t idn_converter = NULL;
190 	idn_delimitermap_t delimiter_mapper;
191 	idn_result_t r;
192 	labellist_t labels = NULL, l;
193 	unsigned long *buffer = NULL;
194 	size_t buffer_length;
195 	int from_is_root;
196 	int idn_is_ace;
197 
198 	assert(ctx != NULL && from != NULL && to != NULL);
199 
200 	TRACE(("idn_res_encodename(actions=%s, from=\"%s\", tolen=%d)\n",
201 		idn__res_actionstostring(actions),
202 		idn__debug_xstring(from, 50), (int)tolen));
203 
204 	if (actions & ~ENCODE_MASK) {
205 		WARNING(("idn_res_encodename: invalid actions 0x%x\n",
206 			 actions));
207 		r = idn_invalid_action;
208 		goto ret;
209 	}
210 
211 	if (!initialized)
212 		idn_res_initialize();
213 	if (!enabled || actions == 0) {
214 		r = copy_verbatim(from, to, tolen);
215 		goto ret;
216 	} else if (tolen <= 0) {
217 		r = idn_buffer_overflow;
218 		goto ret;
219 	}
220 
221 	if (actions & IDN_ENCODE_QUERY) {
222 #ifndef WITHOUT_ICONV
223 		actions |= (IDN_LOCALCONV | IDN_DELIMMAP | IDN_LOCALMAP | \
224 			    IDN_MAP | IDN_NORMALIZE | IDN_PROHCHECK | \
225 			    IDN_BIDICHECK | IDN_IDNCONV | IDN_LENCHECK);
226 #else
227 		actions |= (IDN_DELIMMAP | IDN_LOCALMAP | IDN_MAP | \
228 			    IDN_NORMALIZE | IDN_PROHCHECK | IDN_BIDICHECK | \
229 			    IDN_IDNCONV | IDN_LENCHECK);
230 #endif
231 	}
232 
233 	/*
234 	 * Convert `from' to UCS4.
235 	 */
236 	local_converter = idn_resconf_getlocalconverter(ctx);
237 #ifndef WITHOUT_ICONV
238 	if (local_converter == NULL) {
239 		r = idn_invalid_name;
240 		goto ret;
241 	}
242 #endif
243 
244 	idn_converter = idn_resconf_getidnconverter(ctx);
245 	if (idn_converter != NULL &&
246 	    idn_converter_isasciicompatible(idn_converter))
247 		idn_is_ace = 1;
248 	else
249 		idn_is_ace = 0;
250 
251 	buffer_length = tolen * 2;
252 
253 	for (;;) {
254 		void *new_buffer;
255 
256 		new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length);
257 		if (new_buffer == NULL) {
258 			r = idn_nomemory;
259 			goto ret;
260 		}
261 		buffer = (unsigned long *)new_buffer;
262 
263 		if (actions & IDN_LOCALCONV) {
264 			r = idn_converter_convtoucs4(local_converter, from,
265 						     buffer, buffer_length);
266 		} else {
267 			r = idn_ucs4_utf8toucs4(from, buffer, buffer_length);
268 		}
269 		if (r == idn_success)
270 			break;
271 		else if (r != idn_buffer_overflow)
272 			goto ret;
273 
274 		buffer_length *= 2;
275 	}
276 
277 	if (*buffer == '\0') {
278 		if (tolen <= 0) {
279 			r = idn_buffer_overflow;
280 			goto ret;
281 		}
282 		*to = '\0';
283 		r = idn_success;
284 		goto ret;
285 	}
286 
287 	/*
288 	 * Delimiter map.
289 	 */
290 	if (actions & IDN_DELIMMAP) {
291 		TRACE(("res delimitermap(name=\"%s\")\n",
292 		       idn__debug_ucs4xstring(buffer, 50)));
293 
294 		delimiter_mapper = idn_resconf_getdelimitermap(ctx);
295 		if (delimiter_mapper != NULL) {
296 			r = idn_delimitermap_map(delimiter_mapper, buffer,
297 						 buffer, buffer_length);
298 			idn_delimitermap_destroy(delimiter_mapper);
299 			if (r != idn_success)
300 				goto ret;
301 		}
302 		TRACE(("res delimitermap(): success (name=\"%s\")\n",
303 		       idn__debug_ucs4xstring(buffer, 50)));
304 	}
305 
306 	from_is_root = (buffer[0] == '.' && buffer[1] == '\0');
307 
308 	/*
309 	 * Split the name into a list of labels.
310 	 */
311 	r = labellist_create(buffer, &labels);
312 	if (r != idn_success)
313 		goto ret;
314 
315 	/*
316 	 * Perform conversions and tests.
317 	 */
318 	for (l = labellist_tail(labels); l != NULL;
319 	     l = labellist_previous(l)) {
320 
321 		if (!idn__util_ucs4isasciirange(labellist_getname(l))) {
322 			if (actions & IDN_LOCALMAP) {
323 				r = label_localmap(ctx, l);
324 				if (r != idn_success)
325 					goto ret;
326 			}
327 		}
328 
329 		if (!idn__util_ucs4isasciirange(labellist_getname(l))) {
330 			if (actions & IDN_MAP) {
331 				r = label_map(ctx, l);
332 				if (r != idn_success)
333 					goto ret;
334 			}
335 			if (actions & IDN_NORMALIZE) {
336 				r = label_normalize(ctx, l);
337 				if (r != idn_success)
338 					goto ret;
339 			}
340 			if (actions & IDN_PROHCHECK) {
341 				r = label_prohcheck(ctx, l);
342 				if (r == idn_prohibited &&
343 				    (actions & IDN_UNDOIFERR)) {
344 					labellist_undo(l);
345 					continue;
346 				} else if (r != idn_success) {
347 					goto ret;
348 				}
349 			}
350 			if (actions & IDN_UNASCHECK) {
351 				r = label_unascheck(ctx, l);
352 				if (r == idn_prohibited &&
353 				    (actions & IDN_UNDOIFERR)) {
354 					labellist_undo(l);
355 					continue;
356 				} else if (r != idn_success) {
357 					goto ret;
358 				}
359 			}
360 			if (actions & IDN_BIDICHECK) {
361 				r = label_bidicheck(ctx, l);
362 				if (r == idn_prohibited &&
363 				    (actions & IDN_UNDOIFERR)) {
364 					labellist_undo(l);
365 					continue;
366 				} else if (r != idn_success) {
367 					goto ret;
368 				}
369 			}
370 		}
371 
372 		if (actions & IDN_ASCCHECK) {
373 			r = label_asccheck(ctx, l);
374 			if (r == idn_prohibited && (actions & IDN_UNDOIFERR)) {
375 				labellist_undo(l);
376 				continue;
377 			} else if (r != idn_success) {
378 				goto ret;
379 			}
380 		}
381 
382 		if (!idn__util_ucs4isasciirange(labellist_getname(l))) {
383 			if ((actions & IDN_IDNCONV) && idn_is_ace) {
384 				r = label_idnencode_ace(ctx, l);
385 				if (r != idn_success)
386 					goto ret;
387 			}
388 		}
389 
390 		if (!from_is_root && (actions & IDN_LENCHECK)) {
391 			if (idn_is_ace)
392 				r = label_lencheck_ace(ctx, l);
393 			else
394 				r = label_lencheck_nonace(ctx, l);
395 			if (r == idn_invalid_length &&
396 			    (actions & IDN_UNDOIFERR)) {
397 				labellist_undo(l);
398 				continue;
399 			} else if (r != idn_success) {
400 				goto ret;
401 			}
402 		}
403 	}
404 
405 	/*
406 	 * Concat a list of labels to a name.
407 	 */
408 	for (;;) {
409 		void *new_buffer;
410 
411 		new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length);
412 		if (new_buffer == NULL) {
413 			r = idn_nomemory;
414 			goto ret;
415 		}
416 		buffer = (unsigned long *)new_buffer;
417 
418 		r = labellist_getnamelist(labels, buffer, buffer_length);
419 		if (r == idn_success)
420 			break;
421 		else if (r != idn_buffer_overflow)
422 			goto ret;
423 
424 		buffer_length *= 2;
425 	}
426 
427 	if ((actions & IDN_IDNCONV) && idn_converter != NULL && !idn_is_ace) {
428 		r = idn_converter_convfromucs4(idn_converter, buffer, to,
429 					       tolen);
430 	} else {
431 		r = idn_ucs4_ucs4toutf8(buffer, to, tolen);
432 	}
433 
434 ret:
435 	if (r == idn_success) {
436 		TRACE(("idn_res_encodename(): success (to=\"%s\")\n",
437 		       idn__debug_xstring(to, 50)));
438 	} else {
439 		TRACE(("idn_res_encodename(): %s\n", idn_result_tostring(r)));
440 	}
441 	free(buffer);
442 	if (local_converter != NULL)
443 		idn_converter_destroy(local_converter);
444 	if (idn_converter != NULL)
445 		idn_converter_destroy(idn_converter);
446 	if (labels != NULL)
447 		labellist_destroy(labels);
448 	return (r);
449 }
450 
451 idn_result_t
452 idn_res_decodename(idn_resconf_t ctx, idn_action_t actions, const char *from,
453 		    char *to, size_t tolen) {
454 	idn_converter_t local_converter = NULL;
455 	idn_converter_t idn_converter = NULL;
456 	idn_delimitermap_t delimiter_mapper;
457 	idn_result_t r;
458 	labellist_t labels = NULL, l;
459 	unsigned long *buffer = NULL;
460 	unsigned long *saved_name = NULL;
461 	size_t buffer_length;
462 	int idn_is_ace;
463 
464 	assert(ctx != NULL && from != NULL && to != NULL);
465 
466 	TRACE(("idn_res_decodename(actions=%s, from=\"%s\", tolen=%d)\n",
467 		idn__res_actionstostring(actions),
468 		idn__debug_xstring(from, 50), (int)tolen));
469 
470 	if (actions & ~DECODE_MASK) {
471 		WARNING(("idn_res_decodename: invalid actions 0x%x\n",
472 			 actions));
473 		r = idn_invalid_action;
474 		goto ret;
475 	}
476 
477 	if (!initialized)
478 		idn_res_initialize();
479 	if (!enabled || actions == 0) {
480 		r = copy_verbatim(from, to, tolen);
481 		goto ret;
482 	} else if (tolen <= 0) {
483 		r = idn_buffer_overflow;
484 		goto ret;
485 	}
486 
487 	if (actions & IDN_DECODE_QUERY) {
488 #ifndef WITHOUT_ICONV
489 		actions |= (IDN_DELIMMAP | IDN_MAP | IDN_NORMALIZE | \
490 			    IDN_PROHCHECK | IDN_BIDICHECK | IDN_IDNCONV | \
491 			    IDN_RTCHECK | IDN_LOCALCONV);
492 #else
493 		actions |= (IDN_DELIMMAP | IDN_MAP | IDN_NORMALIZE | \
494 			    IDN_PROHCHECK | IDN_BIDICHECK | IDN_IDNCONV | \
495 			    IDN_RTCHECK);
496 #endif
497 	}
498 
499 	/*
500 	 * Convert `from' to UCS4.
501 	 */
502 	local_converter = idn_resconf_getlocalconverter(ctx);
503 #ifndef WITHOUT_ICONV
504 	if (local_converter == NULL) {
505 		r = idn_invalid_name;
506 		goto ret;
507 	}
508 #endif
509 
510 	idn_converter = idn_resconf_getidnconverter(ctx);
511 	if (idn_converter != NULL &&
512 	    idn_converter_isasciicompatible(idn_converter))
513 		idn_is_ace = 1;
514 	else
515 		idn_is_ace = 0;
516 
517 	buffer_length = tolen * 2;
518 
519 	TRACE(("res idndecode(name=\"%s\")\n", idn__debug_xstring(from, 50)));
520 
521 	for (;;) {
522 		void *new_buffer;
523 
524 		new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length);
525 		if (new_buffer == NULL) {
526 			r = idn_nomemory;
527 			goto ret;
528 		}
529 		buffer = (unsigned long *)new_buffer;
530 
531 		if ((actions & IDN_IDNCONV) &&
532 		     idn_converter != NULL && !idn_is_ace) {
533 			r = idn_converter_convtoucs4(idn_converter, from,
534 						     buffer, buffer_length);
535 		} else {
536 			r = idn_ucs4_utf8toucs4(from, buffer, buffer_length);
537 		}
538 		if (r == idn_success)
539 			break;
540 		else if (r != idn_buffer_overflow)
541 			goto ret;
542 
543 		buffer_length *= 2;
544 	}
545 
546 	if (*buffer == '\0') {
547 		if (tolen <= 0) {
548 			r = idn_buffer_overflow;
549 			goto ret;
550 		}
551 		*to = '\0';
552 		r = idn_success;
553 		goto ret;
554 	}
555 
556 	/*
557 	 * Delimiter map.
558 	 */
559 	if (actions & IDN_DELIMMAP) {
560 		TRACE(("res delimitermap(name=\"%s\")\n",
561 		       idn__debug_ucs4xstring(buffer, 50)));
562 
563 		delimiter_mapper = idn_resconf_getdelimitermap(ctx);
564 		if (delimiter_mapper != NULL) {
565 			r = idn_delimitermap_map(delimiter_mapper, buffer,
566 						 buffer, buffer_length);
567 			idn_delimitermap_destroy(delimiter_mapper);
568 			if (r != idn_success)
569 				goto ret;
570 		}
571 		TRACE(("res delimitermap(): success (name=\"%s\")\n",
572 		       idn__debug_ucs4xstring(buffer, 50)));
573 	}
574 
575 	/*
576 	 * Split the name into a list of labels.
577 	 */
578 	r = labellist_create(buffer, &labels);
579 	if (r != idn_success)
580 		goto ret;
581 
582 	/*
583 	 * Perform conversions and tests.
584 	 */
585 	for (l = labellist_tail(labels); l != NULL;
586 	     l = labellist_previous(l)) {
587 
588 		free(saved_name);
589 		saved_name = NULL;
590 
591 		if (!idn__util_ucs4isasciirange(labellist_getname(l))) {
592 			if (actions & IDN_MAP) {
593 				r = label_map(ctx, l);
594 				if (r != idn_success)
595 					goto ret;
596 			}
597 			if (actions & IDN_NORMALIZE) {
598 				r = label_normalize(ctx, l);
599 				if (r != idn_success)
600 					goto ret;
601 			}
602 			if (actions & IDN_PROHCHECK) {
603 				r = label_prohcheck(ctx, l);
604 				if (r == idn_prohibited) {
605 					labellist_undo(l);
606 					continue;
607 				} else if (r != idn_success) {
608 					goto ret;
609 				}
610 			}
611 			if (actions & IDN_UNASCHECK) {
612 				r = label_unascheck(ctx, l);
613 				if (r == idn_prohibited) {
614 					labellist_undo(l);
615 					continue;
616 				} else if (r != idn_success) {
617 					goto ret;
618 				}
619 			}
620 			if (actions & IDN_BIDICHECK) {
621 				r = label_bidicheck(ctx, l);
622 				if (r == idn_prohibited) {
623 					labellist_undo(l);
624 					continue;
625 				} else if (r != idn_success) {
626 					goto ret;
627 				}
628 			}
629 		}
630 
631 		if ((actions & IDN_IDNCONV) && idn_is_ace) {
632 			saved_name = idn_ucs4_strdup(labellist_getname(l));
633 			if (saved_name == NULL) {
634 				r = idn_nomemory;
635 				goto ret;
636 			}
637 			r = label_idndecode(ctx, l);
638 			if (r == idn_invalid_encoding) {
639 				labellist_undo(l);
640 				continue;
641 			} else if (r != idn_success) {
642 				goto ret;
643 			}
644 		}
645 		if ((actions & IDN_RTCHECK) && saved_name != NULL) {
646 			r = label_rtcheck(ctx, actions, l, saved_name);
647 			if (r == idn_invalid_encoding) {
648 				labellist_undo(l);
649 				continue;
650 			} else if (r != idn_success) {
651 				goto ret;
652 			}
653 		}
654 
655 #ifndef WITHOUT_ICONV
656 		if (actions & IDN_LOCALCONV) {
657 			r = label_localdecodecheck(ctx, l);
658 			if (r != idn_success)
659 				goto ret;
660 		}
661 #endif
662 	}
663 
664 	/*
665 	 * Concat a list of labels to a name.
666 	 */
667 	for (;;) {
668 		void *new_buffer;
669 
670 		new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length);
671 		if (new_buffer == NULL) {
672 			r = idn_nomemory;
673 			goto ret;
674 		}
675 		buffer = (unsigned long *)new_buffer;
676 
677 		r = labellist_getnamelist(labels, buffer, buffer_length);
678 		if (r == idn_success)
679 			break;
680 		else if (r != idn_buffer_overflow)
681 			goto ret;
682 
683 		buffer_length *= 2;
684 	}
685 
686 	if (actions & IDN_LOCALCONV) {
687 		r = idn_converter_convfromucs4(local_converter, buffer, to,
688 					       tolen);
689 	} else {
690 		r = idn_ucs4_ucs4toutf8(buffer, to, tolen);
691 	}
692 
693 ret:
694 	if (r == idn_success) {
695 		TRACE(("idn_res_decodename(): success (to=\"%s\")\n",
696 		       idn__debug_xstring(to, 50)));
697 	} else {
698 		TRACE(("idn_res_decodename(): %s\n", idn_result_tostring(r)));
699 	}
700 	free(saved_name);
701 	free(buffer);
702 	if (local_converter != NULL)
703 		idn_converter_destroy(local_converter);
704 	if (idn_converter != NULL)
705 		idn_converter_destroy(idn_converter);
706 	if (labels != NULL)
707 		labellist_destroy(labels);
708 	return (r);
709 }
710 
711 idn_result_t
712 idn_res_decodename2(idn_resconf_t ctx, idn_action_t actions, const char *from,
713 		    char *to, size_t tolen, const char *auxencoding) {
714 #ifdef WITHOUT_ICONV
715 	return idn_failure;
716 
717 #else /* WITHOUT_ICONV */
718 	idn_result_t r;
719 	idn_converter_t aux_converter = NULL;
720 	unsigned long *buffer_ucs4 = NULL;
721 	char *buffer_utf8 = NULL;
722 	size_t buffer_length;
723 
724 	assert(ctx != NULL && from != NULL && to != NULL);
725 
726 	TRACE(("idn_res_decodename2(actions=%s, from=\"%s\", tolen=%d, "
727 		"auxencoding=\"%s\")\n",
728 		idn__res_actionstostring(actions),
729 		idn__debug_xstring(from, 50), (int)tolen,
730 		(auxencoding != NULL) ? auxencoding : "<null>"));
731 
732 	if (!initialized)
733 		idn_res_initialize();
734 	if (!enabled || actions == 0) {
735 		r = copy_verbatim(from, to, tolen);
736 		goto ret;
737 	} else if (tolen <= 0) {
738 		r = idn_buffer_overflow;
739 		goto ret;
740 	}
741 
742 	if (auxencoding == NULL ||
743 	    strcmp(auxencoding, IDN_UTF8_ENCODING_NAME) == 0 ||
744 	    strcmp(auxencoding, "UTF-8") == 0) {
745 		return idn_res_decodename(ctx, actions, from, to, tolen);
746 	}
747 
748 	/*
749 	 * Convert `from' to UCS4.
750 	 */
751 	r = idn_resconf_setauxidnconvertername(ctx, auxencoding,
752 					       IDN_CONVERTER_DELAYEDOPEN);
753 	if (r != idn_success) {
754 		goto ret;
755 	}
756 
757 	aux_converter = idn_resconf_getauxidnconverter(ctx);
758 	if (aux_converter == NULL) {
759 		r = idn_failure;
760 		goto ret;
761 	}
762 
763 	buffer_length = tolen * 2;
764 	for (;;) {
765 		void *new_buffer;
766 
767 		new_buffer = realloc(buffer_ucs4,
768 				     sizeof(*buffer_ucs4) * buffer_length);
769 		if (new_buffer == NULL) {
770 			r = idn_nomemory;
771 			goto ret;
772 		}
773 		buffer_ucs4 = (unsigned long *)new_buffer;
774 
775 		r = idn_converter_convtoucs4(aux_converter, from,
776 					     buffer_ucs4,
777 					     buffer_length);
778 		if (r == idn_success)
779 			break;
780 		else if (r != idn_buffer_overflow)
781 			goto ret;
782 
783 		buffer_length *= 2;
784 	}
785 
786 	if (*buffer_ucs4 == '\0') {
787 		if (tolen <= 0) {
788 			r = idn_buffer_overflow;
789 			goto ret;
790 		}
791 		*to = '\0';
792 		r = idn_success;
793 		goto ret;
794 	}
795 
796 	/*
797 	 * Convert `buffer_ucs4' to UTF-8.
798 	 */
799 	buffer_length = tolen * 2;
800 	for (;;) {
801 		void *new_buffer;
802 
803 		new_buffer = realloc(buffer_utf8,
804 				     sizeof(*buffer_utf8) * buffer_length);
805 		if (new_buffer == NULL) {
806 			r = idn_nomemory;
807 			goto ret;
808 		}
809 		buffer_utf8 = (char *)new_buffer;
810 		r = idn_ucs4_ucs4toutf8(buffer_ucs4, buffer_utf8,
811 					buffer_length);
812 
813 		if (r == idn_success)
814 			break;
815 		else if (r != idn_buffer_overflow)
816 			goto ret;
817 
818 		buffer_length *= 2;
819 	}
820 
821 	if (*buffer_utf8 == '\0') {
822 		if (tolen <= 0) {
823 			r = idn_buffer_overflow;
824 			goto ret;
825 		}
826 		*to = '\0';
827 		r = idn_success;
828 		goto ret;
829 	}
830 
831 	r = idn_res_decodename(ctx, actions, buffer_utf8, to, tolen);
832 
833 ret:
834 	if (r == idn_success) {
835 		TRACE(("idn_res_decodename2(): success (to=\"%s\")\n",
836 		       idn__debug_xstring(to, 50)));
837 	} else {
838 		TRACE(("idn_res_decodename2(): %s\n", idn_result_tostring(r)));
839 	}
840 	free(buffer_ucs4);
841 	free(buffer_utf8);
842 	if (aux_converter != NULL)
843 		idn_converter_destroy(aux_converter);
844 
845 	return (r);
846 
847 #endif /* WITHOUT_ICONV */
848 }
849 
850 static idn_result_t
851 copy_verbatim(const char *from, char *to, size_t tolen) {
852 	size_t fromlen = strlen(from);
853 
854 	if (fromlen + 1 > tolen)
855 		return (idn_buffer_overflow);
856 	(void)memcpy(to, from, fromlen + 1);
857 	return (idn_success);
858 }
859 
860 static idn_result_t
861 labellist_create(const unsigned long *name, labellist_t *labelp) {
862 	size_t length, malloc_length;
863 	labellist_t head_label = NULL;
864 	labellist_t tail_label = NULL;
865 	labellist_t new_label = NULL;
866 	const unsigned long *endp = NULL;
867 	idn_result_t r;
868 
869 	while (*name != '\0') {
870 		for (endp = name; *endp != '.' && *endp != '\0'; endp++)
871 			;  /* nothing to be done */
872 		length = (endp - name) + 1;
873 		malloc_length = length + 15;  /* add 15 for margin */
874 
875 		new_label = (labellist_t)
876 			    malloc(sizeof(struct labellist));
877 		if (new_label == NULL) {
878 			r = idn_nomemory;
879 			goto ret;
880 		}
881 		if (head_label == NULL)
882 			head_label = new_label;
883 
884 		new_label->name = NULL;
885 		new_label->undo_name = NULL;
886 		new_label->name_length = malloc_length;
887 		new_label->next = NULL;
888 		new_label->previous = NULL;
889 		new_label->dot_followed = (*endp == '.');
890 
891 		new_label->name = (unsigned long *)
892 				  malloc(sizeof(long) * malloc_length);
893 		if (new_label->name == NULL) {
894 			r = idn_nomemory;
895 			goto ret;
896 		}
897 		memcpy(new_label->name, name, sizeof(long) * length);
898 		*(new_label->name + length - 1) = '\0';
899 
900 		new_label->undo_name = (unsigned long *)
901 				       malloc(sizeof(long) * malloc_length);
902 		if (new_label->undo_name == NULL) {
903 			r = idn_nomemory;
904 			goto ret;
905 		}
906 		memcpy(new_label->undo_name, name, sizeof(long) * length);
907 		*(new_label->undo_name + length - 1) = '\0';
908 
909 		if (tail_label != NULL) {
910 			tail_label->next = new_label;
911 			new_label->previous = tail_label;
912 		}
913 		tail_label = new_label;
914 
915 		if (*endp == '.')
916 			name = endp + 1;
917 		else
918 			name = endp;
919 	}
920 
921 	*labelp = head_label;
922 	r = idn_success;
923 
924 ret:
925 	if (r != idn_success) {
926 		if (new_label != NULL) {
927 			free(new_label->name);
928 			free(new_label->undo_name);
929 			free(new_label);
930 		}
931 		if (head_label != NULL)
932 			labellist_destroy(head_label);
933 	}
934 	return (r);
935 }
936 
937 
938 static void
939 labellist_destroy(labellist_t label) {
940 	labellist_t l, l_next;
941 
942 	for (l = label; l != NULL; l = l_next) {
943 		l_next = l->next;
944 		free(l->name);
945 		free(l->undo_name);
946 		free(l);
947 	}
948 }
949 
950 static idn_result_t
951 labellist_setname(labellist_t label, const unsigned long *name) {
952 	unsigned long *new_name;
953 	size_t length, new_length;
954 
955 	length = idn_ucs4_strlen(name) + 1;
956 	new_length = length + 15;  /* add 15 for margin */
957 
958 	if (label->name_length < new_length) {
959 		new_name = (unsigned long *)
960 			   realloc(label->name, sizeof(long) * new_length);
961 		if (new_name == NULL)
962 			return (idn_nomemory);
963 		label->name = new_name;
964 		label->name_length = new_length;
965 	}
966 	memcpy(label->name, name, sizeof(long) * length);
967 
968 	return (idn_success);
969 }
970 
971 static const unsigned long *
972 labellist_getname(labellist_t label) {
973 	return (label->name);
974 }
975 
976 static const unsigned long *
977 labellist_gettldname(labellist_t label) {
978 	labellist_t l;
979 
980 	if (label->previous == NULL && label->next == NULL &&
981 	    !label->dot_followed)
982 		return (idn_mapselector_getnotld());
983 
984 	for (l = label; l->next != NULL; l = l->next)
985 		;  /* nothing to be done */
986 
987 	return (l->name);
988 }
989 
990 static idn_result_t
991 labellist_getnamelist(labellist_t label, unsigned long *name,
992 			  size_t name_length) {
993 	static const unsigned long dot_string[] = {0x002e, 0x0000};  /* "." */
994 	size_t length;
995 	labellist_t l;
996 
997 	for (l = label, length = 0; l != NULL; l = l->next)
998 		length += idn_ucs4_strlen(l->name) + 1;  /* name + `.' */
999 	length++;  /* for NUL */
1000 
1001 	if (name_length < length)
1002 		return (idn_buffer_overflow);
1003 
1004 	*name = '\0';
1005 	for (l = label; l != NULL; l = l->next) {
1006 		idn_ucs4_strcat(name, l->name);
1007 		name += idn_ucs4_strlen(name);
1008 		if (l->dot_followed)
1009 			idn_ucs4_strcat(name, dot_string);
1010 	}
1011 	return (idn_success);
1012 }
1013 
1014 static void
1015 labellist_undo(labellist_t label) {
1016 	size_t length;
1017 
1018 	length = idn_ucs4_strlen(label->undo_name) + 1;
1019 	memcpy(label->name, label->undo_name, sizeof(long) * length);
1020 }
1021 
1022 static labellist_t
1023 labellist_tail(labellist_t label) {
1024 	labellist_t l;
1025 
1026 	if (label == NULL)
1027 		return (NULL);
1028 	for (l = label; l->next != NULL; l = l->next)
1029 		;  /* nothing to be done */
1030 	return (l);
1031 }
1032 
1033 static labellist_t
1034 labellist_previous(labellist_t label) {
1035 	return (label->previous);
1036 }
1037 
1038 #ifndef WITHOUT_ICONV
1039 
1040 static idn_result_t
1041 label_localdecodecheck(idn_resconf_t ctx, labellist_t label) {
1042 	idn_converter_t local_converter = NULL;
1043 	const unsigned long *from;
1044 	char *to = NULL;
1045 	size_t to_length;
1046 	idn_result_t r;
1047 
1048 	from = labellist_getname(label);
1049 	to_length = idn_ucs4_strlen(from) + 1 + 15;  /* 15 for margin */
1050 	TRACE(("res ucs4tolocal_check(label=\"%s\")\n",
1051 	       idn__debug_ucs4xstring(from, 50)));
1052 
1053 	local_converter = idn_resconf_getlocalconverter(ctx);
1054 	if (local_converter == NULL) {
1055 		r = idn_success;
1056 		goto ret;
1057 	}
1058 
1059 	for (;;) {
1060 		char *new_buffer;
1061 
1062 		new_buffer = (char *)realloc(to, to_length);
1063 		if (new_buffer == NULL) {
1064 			r = idn_nomemory;
1065 			goto ret;
1066 		}
1067 		to = new_buffer;
1068 		r = idn_converter_convfromucs4(local_converter, from, to,
1069 					       to_length);
1070 		if (r == idn_success)
1071 			break;
1072 		else if (r == idn_nomapping) {
1073 			r = label_idnencode_ace(ctx, label);
1074 			if (r != idn_success)
1075 				goto ret;
1076 			break;
1077 		} else if (r != idn_buffer_overflow) {
1078 			goto ret;
1079 		}
1080 		to_length *= 2;
1081 	}
1082 
1083 	r = idn_success;
1084 ret:
1085 	TRACE(("res ucs4tolocal_check(): %s\n", idn_result_tostring(r)));
1086 	if (local_converter != NULL)
1087 		idn_converter_destroy(local_converter);
1088 	free(to);
1089 	return (r);
1090 }
1091 
1092 #endif /* !WITHOUT_ICONV */
1093 
1094 static idn_result_t
1095 label_idndecode(idn_resconf_t ctx, labellist_t label) {
1096 	idn_converter_t idn_converter = NULL;
1097 	const unsigned long *from;
1098 	char *ascii_from = NULL;
1099 	unsigned long *to = NULL;
1100 	size_t from_length, to_length;
1101 	idn_result_t r;
1102 
1103 	from = labellist_getname(label);
1104 	from_length = idn_ucs4_strlen(from) + 1;
1105 	TRACE(("res idntoucs4(label=\"%s\")\n",
1106 	       idn__debug_ucs4xstring(from, 50)));
1107 
1108 	idn_converter = idn_resconf_getidnconverter(ctx);
1109 	if (idn_converter == NULL) {
1110 		r = idn_success;
1111 		goto ret;
1112 	}
1113 
1114 	for (;;) {
1115 		char *new_buffer;
1116 
1117 		new_buffer = (char *) realloc(ascii_from, from_length);
1118 		if (new_buffer == NULL) {
1119 			r = idn_nomemory;
1120 			goto ret;
1121 		}
1122 		ascii_from = new_buffer;
1123 		r = idn_ucs4_ucs4toutf8(from, ascii_from, from_length);
1124 		if (r == idn_success)
1125 			break;
1126 		else if (r != idn_buffer_overflow)
1127 			goto ret;
1128 		from_length *= 2;
1129 	}
1130 
1131 	to = NULL;
1132 	to_length = from_length;
1133 
1134 	for (;;) {
1135 		unsigned long *new_buffer;
1136 
1137 		new_buffer = (unsigned long *)
1138 			     realloc(to, sizeof(long) * to_length);
1139 		if (new_buffer == NULL) {
1140 			r = idn_nomemory;
1141 			goto ret;
1142 		}
1143 		to = new_buffer;
1144 		r = idn_converter_convtoucs4(idn_converter, ascii_from, to,
1145 					     to_length);
1146 		if (r == idn_success)
1147 			break;
1148 		else if (r != idn_buffer_overflow)
1149 			goto ret;
1150 		to_length *= 2;
1151 	}
1152 
1153 	r = labellist_setname(label, to);
1154 ret:
1155 	if (r == idn_success) {
1156 		TRACE(("res idntoucs4(): success (label=\"%s\")\n",
1157 		       idn__debug_ucs4xstring(labellist_getname(label),
1158 					      50)));
1159 	} else {
1160 		TRACE(("res idntoucs4(): %s\n", idn_result_tostring(r)));
1161 	}
1162 	if (idn_converter != NULL)
1163 		idn_converter_destroy(idn_converter);
1164 	free(to);
1165 	free(ascii_from);
1166 	return (r);
1167 }
1168 
1169 static idn_result_t
1170 label_idnencode_ace(idn_resconf_t ctx, labellist_t label) {
1171 	idn_converter_t idn_converter = NULL;
1172 	const unsigned long *from;
1173 	char *ascii_to = NULL;
1174 	unsigned long *to = NULL;
1175 	size_t to_length;
1176 	idn_result_t r;
1177 
1178 	from = labellist_getname(label);
1179 	TRACE(("res ucs4toidn(label=\"%s\")\n",
1180 	       idn__debug_ucs4xstring(from, 50)));
1181 
1182 	idn_converter = idn_resconf_getidnconverter(ctx);
1183 	if (idn_converter == NULL) {
1184 		r = idn_success;
1185 		goto ret;
1186 	}
1187 
1188 	ascii_to = NULL;
1189 	to_length = idn_ucs4_strlen(from) * 4 + 16;  /* add mergin */
1190 
1191 	for (;;) {
1192 		char *new_buffer;
1193 
1194 		new_buffer = (char *) realloc(ascii_to, to_length);
1195 		if (new_buffer == NULL) {
1196 			r = idn_nomemory;
1197 			goto ret;
1198 		}
1199 		ascii_to = new_buffer;
1200 		r = idn_converter_convfromucs4(idn_converter, from, ascii_to,
1201 					       to_length);
1202 		if (r == idn_success)
1203 			break;
1204 		else if (r != idn_buffer_overflow)
1205 			goto ret;
1206 		to_length *= 2;
1207 	}
1208 
1209 	for (;;) {
1210 		unsigned long *new_buffer;
1211 
1212 		new_buffer = (unsigned long *)
1213 			     realloc(to, sizeof(long) * to_length);
1214 		if (new_buffer == NULL) {
1215 			r = idn_nomemory;
1216 			goto ret;
1217 		}
1218 		to = new_buffer;
1219 		r = idn_ucs4_utf8toucs4(ascii_to, to, to_length);
1220 		if (r == idn_success)
1221 			break;
1222 		else if (r != idn_buffer_overflow)
1223 			goto ret;
1224 		to_length *= 2;
1225 	}
1226 
1227 	if (r != idn_success)
1228 		goto ret;
1229 
1230 	r = labellist_setname(label, to);
1231 ret:
1232 	if (r == idn_success) {
1233 		TRACE(("res ucs4toidn(): success (label=\"%s\")\n",
1234 		       idn__debug_ucs4xstring(labellist_getname(label),
1235 					      50)));
1236 	} else {
1237 		TRACE(("res ucs4toidn(): %s\n", idn_result_tostring(r)));
1238 	}
1239 	if (idn_converter != NULL)
1240 		idn_converter_destroy(idn_converter);
1241 	free(to);
1242 	free(ascii_to);
1243 	return (r);
1244 }
1245 
1246 static idn_result_t
1247 label_localmap(idn_resconf_t ctx, labellist_t label) {
1248 	const unsigned long *from;
1249 	const unsigned long *tld;
1250 	unsigned long *to = NULL;
1251 	size_t to_length;
1252 	idn_mapselector_t local_mapper;
1253 	idn_result_t r;
1254 
1255 	from = labellist_getname(label);
1256 	tld = labellist_gettldname(label);
1257 	TRACE(("res localmap(label=\"%s\", tld=\"%s\")\n",
1258 	       idn__debug_ucs4xstring(from, 50),
1259 	       idn__debug_ucs4xstring(tld, 50)));
1260 
1261 	local_mapper = idn_resconf_getlocalmapselector(ctx);
1262 	if (local_mapper == NULL) {
1263 		r = idn_success;
1264 		goto ret;
1265 	}
1266 
1267 	if (tld == from)
1268 		tld = idn_mapselector_getdefaulttld();
1269 	to_length = idn_ucs4_strlen(from) + 1 + 15;  /* 15 for margin */
1270 
1271 	for (;;) {
1272 		unsigned long *new_buffer;
1273 
1274 		new_buffer = (unsigned long *)
1275 			     realloc(to, sizeof(long) * to_length);
1276 		if (new_buffer == NULL) {
1277 			r = idn_nomemory;
1278 			goto ret;
1279 		}
1280 		to = new_buffer;
1281 		r = idn_mapselector_map2(local_mapper, from, tld, to,
1282 					 to_length);
1283 		if (r == idn_success)
1284 			break;
1285 		else if (r != idn_buffer_overflow)
1286 			goto ret;
1287 		to_length *= 2;
1288 	}
1289 
1290 	r = labellist_setname(label, to);
1291 ret:
1292 	if (r == idn_success) {
1293 		TRACE(("res localmap(): success (label=\"%s\")\n",
1294 		       idn__debug_ucs4xstring(labellist_getname(label),
1295 					      50)));
1296 	} else {
1297 		TRACE(("res localmap(): %s\n", idn_result_tostring(r)));
1298 	}
1299 	if (local_mapper != NULL)
1300 		idn_mapselector_destroy(local_mapper);
1301 	free(to);
1302 	return (r);
1303 }
1304 
1305 static idn_result_t
1306 label_map(idn_resconf_t ctx, labellist_t label) {
1307 	const unsigned long *from;
1308 	unsigned long *to = NULL;
1309 	size_t to_length;
1310 	idn_mapper_t mapper;
1311 	idn_result_t r;
1312 
1313 	from = labellist_getname(label);
1314 	TRACE(("res map(label=\"%s\")\n", idn__debug_ucs4xstring(from, 50)));
1315 
1316 	mapper = idn_resconf_getmapper(ctx);
1317 	if (mapper == NULL) {
1318 		r = idn_success;
1319 		goto ret;
1320 	}
1321 	to_length = idn_ucs4_strlen(from) + 1 + 15;  /* 15 for margin */
1322 
1323 	for (;;) {
1324 		unsigned long *new_buffer;
1325 
1326 		new_buffer = (unsigned long *)
1327 			     realloc(to, sizeof(long) * to_length);
1328 		if (new_buffer == NULL) {
1329 			r = idn_nomemory;
1330 			goto ret;
1331 		}
1332 		to = new_buffer;
1333 		r = idn_mapper_map(mapper, from, to, to_length);
1334 		if (r == idn_success)
1335 			break;
1336 		else if (r != idn_buffer_overflow)
1337 			goto ret;
1338 		to_length *= 2;
1339 	}
1340 
1341 	r = labellist_setname(label, to);
1342 ret:
1343 	if (r == idn_success) {
1344 		TRACE(("res map(): success (label=\"%s\")\n",
1345 		       idn__debug_ucs4xstring(labellist_getname(label),
1346 					      50)));
1347 	} else {
1348 		TRACE(("res map(): %s\n", idn_result_tostring(r)));
1349 	}
1350 	if (mapper != NULL)
1351 		idn_mapper_destroy(mapper);
1352 	free(to);
1353 	return (r);
1354 }
1355 
1356 static idn_result_t
1357 label_normalize(idn_resconf_t ctx, labellist_t label) {
1358 	const unsigned long *from;
1359 	unsigned long *to = NULL;
1360 	size_t to_length;
1361 	idn_normalizer_t normalizer;
1362 	idn_result_t r;
1363 
1364 	from = labellist_getname(label);
1365 	TRACE(("res normalzie(label=\"%s\")\n",
1366 	       idn__debug_ucs4xstring(from, 50)));
1367 
1368 	normalizer = idn_resconf_getnormalizer(ctx);
1369 	if (normalizer == NULL) {
1370 		r = idn_success;
1371 		goto ret;
1372 	}
1373 	to_length = idn_ucs4_strlen(from) + 1 + 15;  /* 15 for margin */
1374 
1375 	for (;;) {
1376 		unsigned long *new_buffer;
1377 
1378 		new_buffer = (unsigned long *)
1379 			     realloc(to, sizeof(long) * to_length);
1380 		if (new_buffer == NULL) {
1381 			r = idn_nomemory;
1382 			goto ret;
1383 		}
1384 		to = new_buffer;
1385 		r = idn_normalizer_normalize(normalizer, from, to, to_length);
1386 		if (r == idn_success)
1387 			break;
1388 		else if (r != idn_buffer_overflow)
1389 			goto ret;
1390 		to_length *= 2;
1391 	}
1392 
1393 	r = labellist_setname(label, to);
1394 ret:
1395 	if (r == idn_success) {
1396 		TRACE(("res normalize(): success (label=\"%s\")\n",
1397 		       idn__debug_ucs4xstring(labellist_getname(label),
1398 					      50)));
1399 	} else {
1400 		TRACE(("res normalize(): %s\n", idn_result_tostring(r)));
1401 	}
1402 	if (normalizer != NULL)
1403 		idn_normalizer_destroy(normalizer);
1404 	free(to);
1405 	return (r);
1406 }
1407 
1408 static idn_result_t
1409 label_prohcheck(idn_resconf_t ctx, labellist_t label) {
1410 	const unsigned long *name, *found;
1411 	idn_checker_t prohibit_checker;
1412 	idn_result_t r;
1413 
1414 	name = labellist_getname(label);
1415 	TRACE(("res prohcheck(label=\"%s\")\n",
1416 	       idn__debug_ucs4xstring(name, 50)));
1417 
1418 	prohibit_checker = idn_resconf_getprohibitchecker(ctx);
1419 	if (prohibit_checker == NULL) {
1420 		r = idn_success;
1421 		goto ret;
1422 	}
1423 
1424 	r = idn_checker_lookup(prohibit_checker, name, &found);
1425 	idn_checker_destroy(prohibit_checker);
1426 	if (r == idn_success && found != NULL)
1427 		r = idn_prohibited;
1428 
1429 ret:
1430 	TRACE(("res prohcheck(): %s\n", idn_result_tostring(r)));
1431 	return (r);
1432 }
1433 
1434 static idn_result_t
1435 label_unascheck(idn_resconf_t ctx, labellist_t label) {
1436 	const unsigned long *name, *found;
1437 	idn_checker_t unassigned_checker;
1438 	idn_result_t r;
1439 
1440 	name = labellist_getname(label);
1441 	TRACE(("res unascheck(label=\"%s\")\n",
1442 	       idn__debug_ucs4xstring(name, 50)));
1443 
1444 	unassigned_checker = idn_resconf_getunassignedchecker(ctx);
1445 	if (unassigned_checker == NULL) {
1446 		r = idn_success;
1447 		goto ret;
1448 	}
1449 
1450 	r = idn_checker_lookup(unassigned_checker, name, &found);
1451 	idn_checker_destroy(unassigned_checker);
1452 	if (r == idn_success && found != NULL)
1453 		r = idn_prohibited;
1454 
1455 ret:
1456 	TRACE(("res unascheck(): %s\n", idn_result_tostring(r)));
1457 	return (r);
1458 }
1459 
1460 static idn_result_t
1461 label_bidicheck(idn_resconf_t ctx, labellist_t label) {
1462 	const unsigned long *name, *found;
1463 	idn_checker_t bidi_checker;
1464 	idn_result_t r;
1465 
1466 	name = labellist_getname(label);
1467 	TRACE(("res bidicheck(label=\"%s\")\n",
1468 	       idn__debug_ucs4xstring(name, 50)));
1469 
1470 	bidi_checker = idn_resconf_getbidichecker(ctx);
1471 	if (bidi_checker == NULL) {
1472 		r = idn_success;
1473 		goto ret;
1474 	}
1475 
1476 	r = idn_checker_lookup(bidi_checker, name, &found);
1477 	idn_checker_destroy(bidi_checker);
1478 	if (r == idn_success && found != NULL)
1479 		r = idn_prohibited;
1480 
1481 ret:
1482 	TRACE(("res bidicheck(): %s\n", idn_result_tostring(r)));
1483 	return (r);
1484 }
1485 
1486 static idn_result_t
1487 label_asccheck(idn_resconf_t ctx, labellist_t label) {
1488 	const unsigned long *name, *n;
1489 	idn_result_t r;
1490 
1491 	name = labellist_getname(label);
1492 	TRACE(("res asccheck(label=\"%s\")\n",
1493 	       idn__debug_ucs4xstring(name, 50)));
1494 
1495 	if (*name == '-') {
1496 		r = idn_prohibited;
1497 		goto ret;
1498 	}
1499 
1500 	for (n = name; *n != '\0'; n++) {
1501 		if (*n <= '\177') {
1502 			if ((*n < '0' || *n > '9') &&
1503 			    (*n < 'A' || *n > 'Z') &&
1504 			    (*n < 'a' || *n > 'z') &&
1505 			    *n != '-') {
1506 				r  = idn_prohibited;
1507 				goto ret;
1508 			}
1509 		}
1510 	}
1511 
1512 	if (n > name && *(n - 1) == '-') {
1513 		r  = idn_prohibited;
1514 		goto ret;
1515 	}
1516 
1517 	r = idn_success;
1518 ret:
1519 	TRACE(("res asccheck(): %s\n", idn_result_tostring(r)));
1520 	return (r);
1521 }
1522 
1523 static idn_result_t
1524 label_lencheck_ace(idn_resconf_t ctx, labellist_t label) {
1525 	const unsigned long *name;
1526 	size_t name_length;
1527 	idn_result_t r;
1528 
1529 	name = labellist_getname(label);
1530 	name_length = idn_ucs4_strlen(name);
1531 	TRACE(("res lencheck(label=\"%s\")\n",
1532 	       idn__debug_ucs4xstring(name, 50)));
1533 
1534 	if (name_length == 0 || name_length > MAX_LABEL_LENGTH) {
1535 		r = idn_invalid_length;
1536 		goto ret;
1537 	}
1538 
1539 	r = idn_success;
1540 ret:
1541 	TRACE(("res lencheck(): %s\n", idn_result_tostring(r)));
1542 	return (r);
1543 }
1544 
1545 static idn_result_t
1546 label_lencheck_nonace(idn_resconf_t ctx, labellist_t label) {
1547 	idn_converter_t idn_converter;
1548 	const unsigned long *from;
1549 	size_t to_length;
1550 	idn_result_t r;
1551 	char *buffer = NULL;
1552 	size_t buffer_length;
1553 
1554 	from = labellist_getname(label);
1555 	TRACE(("res lencheck(label=\"%s\")\n",
1556 	       idn__debug_ucs4xstring(from, 50)));
1557 
1558 	buffer_length = idn_ucs4_strlen(from) * 4 + 16; /* 16 for margin */
1559 	idn_converter = idn_resconf_getidnconverter(ctx);
1560 
1561 	for (;;) {
1562 		void *new_buffer;
1563 
1564 		new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length);
1565 		if (new_buffer == NULL) {
1566 			r = idn_nomemory;
1567 			goto ret;
1568 		}
1569 		buffer = (char *)new_buffer;
1570 
1571 		if (idn_converter != NULL) {
1572 			r = idn_converter_convfromucs4(idn_converter, from,
1573 						       buffer, buffer_length);
1574 		} else {
1575 			r = idn_ucs4_ucs4toutf8(from, buffer, buffer_length);
1576 		}
1577 		if (r == idn_success)
1578 			break;
1579 		else if (r != idn_buffer_overflow)
1580 			goto ret;
1581 
1582 		buffer_length *= 2;
1583 	}
1584 
1585 	to_length = strlen(buffer);
1586 	if (to_length == 0 || to_length > MAX_LABEL_LENGTH) {
1587 		r = idn_invalid_length;
1588 		goto ret;
1589 	}
1590 
1591 	r = idn_success;
1592 ret:
1593 	TRACE(("res lencheck(): %s\n", idn_result_tostring(r)));
1594 	if (idn_converter != NULL)
1595 		idn_converter_destroy(idn_converter);
1596 	free(buffer);
1597 	return (r);
1598 }
1599 
1600 static idn_result_t
1601 label_rtcheck(idn_resconf_t ctx, idn_action_t actions, labellist_t label,
1602 	    const unsigned long *original_name) {
1603 	labellist_t rt_label = NULL;
1604 	const unsigned long *rt_name;
1605 	const unsigned long *cur_name;
1606 	idn_result_t r;
1607 
1608 	cur_name = labellist_getname(label);
1609 	TRACE(("res rtcheck(label=\"%s\", org_label=\"%s\")\n",
1610 		idn__debug_ucs4xstring(cur_name, 50),
1611 		idn__debug_ucs4xstring(original_name, 50)));
1612 
1613 	r = labellist_create(cur_name, &rt_label);
1614 	if (r != idn_success)
1615 		goto ret;
1616 	if (rt_label == NULL) {
1617 		if (*original_name == '\0')
1618 			r = idn_success;
1619 		else
1620 			r = idn_invalid_encoding;
1621 		goto ret;
1622 	}
1623 
1624 	if (!idn__util_ucs4isasciirange(labellist_getname(rt_label))) {
1625 		r = label_map(ctx, rt_label);
1626 		if (r != idn_success)
1627 			goto ret;
1628 		r = label_normalize(ctx, rt_label);
1629 		if (r != idn_success)
1630 			goto ret;
1631 		r = label_prohcheck(ctx, rt_label);
1632 		if (r != idn_success)
1633 			goto ret;
1634 		if (actions & IDN_UNASCHECK) {
1635 			r = label_unascheck(ctx, rt_label);
1636 			if (r != idn_success)
1637 				goto ret;
1638 		}
1639 		r = label_bidicheck(ctx, rt_label);
1640 		if (r != idn_success)
1641 			goto ret;
1642 	}
1643 
1644 	if (actions & IDN_ASCCHECK) {
1645 		r = label_asccheck(ctx, rt_label);
1646 		if (r != idn_success)
1647 			goto ret;
1648 	}
1649 	if (!idn__util_ucs4isasciirange(labellist_getname(rt_label))) {
1650 		r = label_idnencode_ace(ctx, rt_label);
1651 		if (r != idn_success)
1652 			goto ret;
1653 	}
1654 	r = label_lencheck_ace(ctx, rt_label);
1655 	if (r != idn_success)
1656 		goto ret;
1657 	rt_name = labellist_getname(rt_label);
1658 
1659 	if (idn_ucs4_strcasecmp(rt_name, original_name) != 0) {
1660 		TRACE(("res rtcheck(): round trip failed, org =\"%s\", rt=\"%s\"\n",
1661 		       idn__debug_ucs4xstring(original_name, 50),
1662 		       idn__debug_ucs4xstring(rt_name, 50)));
1663 		r = idn_invalid_encoding;
1664 		goto ret;
1665 	}
1666 
1667 	r  = idn_success;
1668 ret:
1669 	if (r != idn_nomemory && r != idn_success)
1670 		r = idn_invalid_encoding;
1671 	TRACE(("res rtcheck(): %s\n", idn_result_tostring(r)));
1672 	if (rt_label != NULL)
1673 		labellist_destroy(rt_label);
1674 	return (r);
1675 }
1676 
1677 const char *
1678 idn__res_actionstostring(idn_action_t actions) {
1679 	static char buf[100];
1680 
1681 	buf[0] = '\0';
1682 
1683 	if (actions == IDN_ENCODE_QUERY)
1684 		strcpy(buf, "encode-query");
1685 	else if (actions == IDN_DECODE_QUERY)
1686 		strcpy(buf, "decode-query");
1687 	else if (actions == IDN_ENCODE_APP)
1688 		strcpy(buf, "encode-app");
1689 	else if (actions == IDN_DECODE_APP)
1690 		strcpy(buf, "decode-app");
1691 	else if (actions == IDN_ENCODE_STORED)
1692 		strcpy(buf, "encode-stored");
1693 	else if (actions == IDN_DECODE_STORED)
1694 		strcpy(buf, "decode-stored");
1695 	else {
1696 		if (actions & IDN_LOCALCONV)
1697 			strcat(buf, "|localconv");
1698 		if (actions & IDN_DELIMMAP)
1699 			strcat(buf, "|delimmap");
1700 		if (actions & IDN_LOCALMAP)
1701 			strcat(buf, "|localmap");
1702 
1703 		if (actions & IDN_MAP)
1704 			strcat(buf, "|map");
1705 		if (actions & IDN_NORMALIZE)
1706 			strcat(buf, "|normalize");
1707 		if (actions & IDN_PROHCHECK)
1708 			strcat(buf, "|prohcheck");
1709 		if (actions & IDN_UNASCHECK)
1710 			strcat(buf, "|unascheck");
1711 		if (actions & IDN_BIDICHECK)
1712 			strcat(buf, "|bidicheck");
1713 
1714 		if (actions & IDN_IDNCONV)
1715 			strcat(buf, "|idnconv");
1716 		if (actions & IDN_ASCCHECK)
1717 			strcat(buf, "|asccheck");
1718 		if (actions & IDN_LENCHECK)
1719 			strcat(buf, "|lencheck");
1720 		if (actions & IDN_RTCHECK)
1721 			strcat(buf, "|rtcheck");
1722 	}
1723 
1724 	if (buf[0] == '|')
1725 		return (buf + 1);
1726 	else
1727 		return (buf);
1728 }
1729