minix/external/bsd/bind/dist/contrib/idn/idnkit-1.0-src/lib/res.c
David van Moolenbroek 00b67f09dd Import NetBSD named(8)
Also known as ISC bind.  This import adds utilities such as host(1),
dig(1), and nslookup(1), as well as many other tools and libraries.

Change-Id: I035ca46e64f1965d57019e773f4ff0ef035e4aa3
2017-03-21 22:00:06 +00:00

1729 lines
41 KiB
C

/* $NetBSD: res.c,v 1.4 2014/12/10 04:37:55 christos Exp $ */
#ifndef lint
static char *rcsid = "Id: res.c,v 1.1 2003/06/04 00:26:10 marka Exp ";
#endif
/*
* Copyright (c) 2000,2002 Japan Network Information Center.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set forth bellow.
*
* LICENSE TERMS AND CONDITIONS
*
* The following License Terms and Conditions apply, unless a different
* license is obtained from Japan Network Information Center ("JPNIC"),
* a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda,
* Chiyoda-ku, Tokyo 101-0047, Japan.
*
* 1. Use, Modification and Redistribution (including distribution of any
* modified or derived work) in source and/or binary forms is permitted
* under this License Terms and Conditions.
*
* 2. Redistribution of source code must retain the copyright notices as they
* appear in each source code file, this License Terms and Conditions.
*
* 3. Redistribution in binary form must reproduce the Copyright Notice,
* this License Terms and Conditions, in the documentation and/or other
* materials provided with the distribution. For the purposes of binary
* distribution the "Copyright Notice" refers to the following language:
* "Copyright (c) 2000-2002 Japan Network Information Center. All rights reserved."
*
* 4. The name of JPNIC may not be used to endorse or promote products
* derived from this Software without specific prior written approval of
* JPNIC.
*
* 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JPNIC BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*/
#include <config.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <idn/result.h>
#include <idn/assert.h>
#include <idn/logmacro.h>
#include <idn/converter.h>
#include <idn/normalizer.h>
#include <idn/checker.h>
#include <idn/mapper.h>
#include <idn/mapselector.h>
#include <idn/delimitermap.h>
#include <idn/resconf.h>
#include <idn/res.h>
#include <idn/util.h>
#include <idn/debug.h>
#include <idn/ucs4.h>
#ifndef IDN_UTF8_ENCODING_NAME
#define IDN_UTF8_ENCODING_NAME "UTF-8" /* by IANA */
#endif
#ifndef WITHOUT_ICONV
#define ENCODE_MASK \
(IDN_LOCALCONV | IDN_DELIMMAP | IDN_LOCALMAP | IDN_MAP | \
IDN_NORMALIZE | IDN_PROHCHECK | IDN_UNASCHECK | IDN_BIDICHECK | \
IDN_ASCCHECK | IDN_IDNCONV | IDN_LENCHECK | IDN_ENCODE_QUERY | \
IDN_UNDOIFERR)
#define DECODE_MASK \
(IDN_DELIMMAP | IDN_MAP | IDN_NORMALIZE | IDN_PROHCHECK | \
IDN_UNASCHECK | IDN_BIDICHECK | IDN_IDNCONV | IDN_ASCCHECK | \
IDN_RTCHECK | IDN_LOCALCONV | IDN_DECODE_QUERY)
#else
#define ENCODE_MASK \
(IDN_DELIMMAP | IDN_LOCALMAP | IDN_MAP | IDN_NORMALIZE | \
IDN_PROHCHECK | IDN_UNASCHECK | IDN_BIDICHECK | IDN_ASCCHECK | \
IDN_IDNCONV | IDN_LENCHECK | IDN_ENCODE_QUERY | IDN_UNDOIFERR)
#define DECODE_MASK \
(IDN_DELIMMAP | IDN_MAP | IDN_NORMALIZE | IDN_PROHCHECK | \
IDN_UNASCHECK | IDN_BIDICHECK | IDN_IDNCONV | IDN_ASCCHECK | \
IDN_RTCHECK | IDN_DECODE_QUERY)
#endif
#define MAX_LABEL_LENGTH 63
/*
* label to convert.
*/
typedef struct labellist * labellist_t;
struct labellist {
unsigned long *name;
size_t name_length;
unsigned long *undo_name;
labellist_t next;
labellist_t previous;
int dot_followed;
};
typedef idn_result_t (*res_insnproc_t)(idn_resconf_t ctx,
labellist_t label);
static void idn_res_initialize(void);
static idn_result_t copy_verbatim(const char *from, char *to,
size_t tolen);
static idn_result_t labellist_create(const unsigned long *name,
labellist_t *labelp);
static void labellist_destroy(labellist_t label);
static idn_result_t labellist_setname(labellist_t label,
const unsigned long *name);
static const unsigned long *
labellist_getname(labellist_t label);
static const unsigned long *
labellist_gettldname(labellist_t label);
static idn_result_t labellist_getnamelist(labellist_t label,
unsigned long *name,
size_t label_length);
static void labellist_undo(labellist_t label);
static labellist_t labellist_tail(labellist_t label);
static labellist_t labellist_previous(labellist_t label);
#ifndef WITHOUT_ICONV
static idn_result_t label_localdecodecheck(idn_resconf_t ctx,
labellist_t label);
#endif
static idn_result_t label_idnencode_ace(idn_resconf_t ctx,
labellist_t label);
static idn_result_t label_idndecode(idn_resconf_t ctx, labellist_t label);
static idn_result_t label_localmap(idn_resconf_t ctx, labellist_t label);
static idn_result_t label_map(idn_resconf_t ctx, labellist_t label);
static idn_result_t label_normalize(idn_resconf_t ctx, labellist_t label);
static idn_result_t label_prohcheck(idn_resconf_t ctx, labellist_t label);
static idn_result_t label_unascheck(idn_resconf_t ctx, labellist_t label);
static idn_result_t label_bidicheck(idn_resconf_t ctx, labellist_t label);
static idn_result_t label_asccheck(idn_resconf_t ctx, labellist_t label);
static idn_result_t label_lencheck_ace(idn_resconf_t ctx,
labellist_t label);
static idn_result_t label_lencheck_nonace(idn_resconf_t ctx,
labellist_t label);
static idn_result_t label_rtcheck(idn_resconf_t ctx, idn_action_t actions,
labellist_t label,
const unsigned long *original_name);
static int initialized;
static int enabled;
void
idn_res_enable(int on_off) {
if (!initialized) {
idn_res_initialize();
}
if (on_off == 0) {
enabled = 0;
} else {
enabled = 1;
}
}
static void
idn_res_initialize(void) {
if (!initialized) {
char *value = getenv("IDN_DISABLE");
if (value == NULL) {
enabled = 1;
} else {
enabled = 0;
}
initialized = 1;
}
}
idn_result_t
idn_res_encodename(idn_resconf_t ctx, idn_action_t actions, const char *from,
char *to, size_t tolen) {
idn_converter_t local_converter = NULL;
idn_converter_t idn_converter = NULL;
idn_delimitermap_t delimiter_mapper;
idn_result_t r;
labellist_t labels = NULL, l;
unsigned long *buffer = NULL;
size_t buffer_length;
int from_is_root;
int idn_is_ace;
assert(ctx != NULL && from != NULL && to != NULL);
TRACE(("idn_res_encodename(actions=%s, from=\"%s\", tolen=%d)\n",
idn__res_actionstostring(actions),
idn__debug_xstring(from, 50), (int)tolen));
if (actions & ~ENCODE_MASK) {
WARNING(("idn_res_encodename: invalid actions 0x%x\n",
actions));
r = idn_invalid_action;
goto ret;
}
if (!initialized)
idn_res_initialize();
if (!enabled || actions == 0) {
r = copy_verbatim(from, to, tolen);
goto ret;
} else if (tolen <= 0) {
r = idn_buffer_overflow;
goto ret;
}
if (actions & IDN_ENCODE_QUERY) {
#ifndef WITHOUT_ICONV
actions |= (IDN_LOCALCONV | IDN_DELIMMAP | IDN_LOCALMAP | \
IDN_MAP | IDN_NORMALIZE | IDN_PROHCHECK | \
IDN_BIDICHECK | IDN_IDNCONV | IDN_LENCHECK);
#else
actions |= (IDN_DELIMMAP | IDN_LOCALMAP | IDN_MAP | \
IDN_NORMALIZE | IDN_PROHCHECK | IDN_BIDICHECK | \
IDN_IDNCONV | IDN_LENCHECK);
#endif
}
/*
* Convert `from' to UCS4.
*/
local_converter = idn_resconf_getlocalconverter(ctx);
#ifndef WITHOUT_ICONV
if (local_converter == NULL) {
r = idn_invalid_name;
goto ret;
}
#endif
idn_converter = idn_resconf_getidnconverter(ctx);
if (idn_converter != NULL &&
idn_converter_isasciicompatible(idn_converter))
idn_is_ace = 1;
else
idn_is_ace = 0;
buffer_length = tolen * 2;
for (;;) {
void *new_buffer;
new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length);
if (new_buffer == NULL) {
r = idn_nomemory;
goto ret;
}
buffer = (unsigned long *)new_buffer;
if (actions & IDN_LOCALCONV) {
r = idn_converter_convtoucs4(local_converter, from,
buffer, buffer_length);
} else {
r = idn_ucs4_utf8toucs4(from, buffer, buffer_length);
}
if (r == idn_success)
break;
else if (r != idn_buffer_overflow)
goto ret;
buffer_length *= 2;
}
if (*buffer == '\0') {
if (tolen <= 0) {
r = idn_buffer_overflow;
goto ret;
}
*to = '\0';
r = idn_success;
goto ret;
}
/*
* Delimiter map.
*/
if (actions & IDN_DELIMMAP) {
TRACE(("res delimitermap(name=\"%s\")\n",
idn__debug_ucs4xstring(buffer, 50)));
delimiter_mapper = idn_resconf_getdelimitermap(ctx);
if (delimiter_mapper != NULL) {
r = idn_delimitermap_map(delimiter_mapper, buffer,
buffer, buffer_length);
idn_delimitermap_destroy(delimiter_mapper);
if (r != idn_success)
goto ret;
}
TRACE(("res delimitermap(): success (name=\"%s\")\n",
idn__debug_ucs4xstring(buffer, 50)));
}
from_is_root = (buffer[0] == '.' && buffer[1] == '\0');
/*
* Split the name into a list of labels.
*/
r = labellist_create(buffer, &labels);
if (r != idn_success)
goto ret;
/*
* Perform conversions and tests.
*/
for (l = labellist_tail(labels); l != NULL;
l = labellist_previous(l)) {
if (!idn__util_ucs4isasciirange(labellist_getname(l))) {
if (actions & IDN_LOCALMAP) {
r = label_localmap(ctx, l);
if (r != idn_success)
goto ret;
}
}
if (!idn__util_ucs4isasciirange(labellist_getname(l))) {
if (actions & IDN_MAP) {
r = label_map(ctx, l);
if (r != idn_success)
goto ret;
}
if (actions & IDN_NORMALIZE) {
r = label_normalize(ctx, l);
if (r != idn_success)
goto ret;
}
if (actions & IDN_PROHCHECK) {
r = label_prohcheck(ctx, l);
if (r == idn_prohibited &&
(actions & IDN_UNDOIFERR)) {
labellist_undo(l);
continue;
} else if (r != idn_success) {
goto ret;
}
}
if (actions & IDN_UNASCHECK) {
r = label_unascheck(ctx, l);
if (r == idn_prohibited &&
(actions & IDN_UNDOIFERR)) {
labellist_undo(l);
continue;
} else if (r != idn_success) {
goto ret;
}
}
if (actions & IDN_BIDICHECK) {
r = label_bidicheck(ctx, l);
if (r == idn_prohibited &&
(actions & IDN_UNDOIFERR)) {
labellist_undo(l);
continue;
} else if (r != idn_success) {
goto ret;
}
}
}
if (actions & IDN_ASCCHECK) {
r = label_asccheck(ctx, l);
if (r == idn_prohibited && (actions & IDN_UNDOIFERR)) {
labellist_undo(l);
continue;
} else if (r != idn_success) {
goto ret;
}
}
if (!idn__util_ucs4isasciirange(labellist_getname(l))) {
if ((actions & IDN_IDNCONV) && idn_is_ace) {
r = label_idnencode_ace(ctx, l);
if (r != idn_success)
goto ret;
}
}
if (!from_is_root && (actions & IDN_LENCHECK)) {
if (idn_is_ace)
r = label_lencheck_ace(ctx, l);
else
r = label_lencheck_nonace(ctx, l);
if (r == idn_invalid_length &&
(actions & IDN_UNDOIFERR)) {
labellist_undo(l);
continue;
} else if (r != idn_success) {
goto ret;
}
}
}
/*
* Concat a list of labels to a name.
*/
for (;;) {
void *new_buffer;
new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length);
if (new_buffer == NULL) {
r = idn_nomemory;
goto ret;
}
buffer = (unsigned long *)new_buffer;
r = labellist_getnamelist(labels, buffer, buffer_length);
if (r == idn_success)
break;
else if (r != idn_buffer_overflow)
goto ret;
buffer_length *= 2;
}
if ((actions & IDN_IDNCONV) && idn_converter != NULL && !idn_is_ace) {
r = idn_converter_convfromucs4(idn_converter, buffer, to,
tolen);
} else {
r = idn_ucs4_ucs4toutf8(buffer, to, tolen);
}
ret:
if (r == idn_success) {
TRACE(("idn_res_encodename(): success (to=\"%s\")\n",
idn__debug_xstring(to, 50)));
} else {
TRACE(("idn_res_encodename(): %s\n", idn_result_tostring(r)));
}
free(buffer);
if (local_converter != NULL)
idn_converter_destroy(local_converter);
if (idn_converter != NULL)
idn_converter_destroy(idn_converter);
if (labels != NULL)
labellist_destroy(labels);
return (r);
}
idn_result_t
idn_res_decodename(idn_resconf_t ctx, idn_action_t actions, const char *from,
char *to, size_t tolen) {
idn_converter_t local_converter = NULL;
idn_converter_t idn_converter = NULL;
idn_delimitermap_t delimiter_mapper;
idn_result_t r;
labellist_t labels = NULL, l;
unsigned long *buffer = NULL;
unsigned long *saved_name = NULL;
size_t buffer_length;
int idn_is_ace;
assert(ctx != NULL && from != NULL && to != NULL);
TRACE(("idn_res_decodename(actions=%s, from=\"%s\", tolen=%d)\n",
idn__res_actionstostring(actions),
idn__debug_xstring(from, 50), (int)tolen));
if (actions & ~DECODE_MASK) {
WARNING(("idn_res_decodename: invalid actions 0x%x\n",
actions));
r = idn_invalid_action;
goto ret;
}
if (!initialized)
idn_res_initialize();
if (!enabled || actions == 0) {
r = copy_verbatim(from, to, tolen);
goto ret;
} else if (tolen <= 0) {
r = idn_buffer_overflow;
goto ret;
}
if (actions & IDN_DECODE_QUERY) {
#ifndef WITHOUT_ICONV
actions |= (IDN_DELIMMAP | IDN_MAP | IDN_NORMALIZE | \
IDN_PROHCHECK | IDN_BIDICHECK | IDN_IDNCONV | \
IDN_RTCHECK | IDN_LOCALCONV);
#else
actions |= (IDN_DELIMMAP | IDN_MAP | IDN_NORMALIZE | \
IDN_PROHCHECK | IDN_BIDICHECK | IDN_IDNCONV | \
IDN_RTCHECK);
#endif
}
/*
* Convert `from' to UCS4.
*/
local_converter = idn_resconf_getlocalconverter(ctx);
#ifndef WITHOUT_ICONV
if (local_converter == NULL) {
r = idn_invalid_name;
goto ret;
}
#endif
idn_converter = idn_resconf_getidnconverter(ctx);
if (idn_converter != NULL &&
idn_converter_isasciicompatible(idn_converter))
idn_is_ace = 1;
else
idn_is_ace = 0;
buffer_length = tolen * 2;
TRACE(("res idndecode(name=\"%s\")\n", idn__debug_xstring(from, 50)));
for (;;) {
void *new_buffer;
new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length);
if (new_buffer == NULL) {
r = idn_nomemory;
goto ret;
}
buffer = (unsigned long *)new_buffer;
if ((actions & IDN_IDNCONV) &&
idn_converter != NULL && !idn_is_ace) {
r = idn_converter_convtoucs4(idn_converter, from,
buffer, buffer_length);
} else {
r = idn_ucs4_utf8toucs4(from, buffer, buffer_length);
}
if (r == idn_success)
break;
else if (r != idn_buffer_overflow)
goto ret;
buffer_length *= 2;
}
if (*buffer == '\0') {
if (tolen <= 0) {
r = idn_buffer_overflow;
goto ret;
}
*to = '\0';
r = idn_success;
goto ret;
}
/*
* Delimiter map.
*/
if (actions & IDN_DELIMMAP) {
TRACE(("res delimitermap(name=\"%s\")\n",
idn__debug_ucs4xstring(buffer, 50)));
delimiter_mapper = idn_resconf_getdelimitermap(ctx);
if (delimiter_mapper != NULL) {
r = idn_delimitermap_map(delimiter_mapper, buffer,
buffer, buffer_length);
idn_delimitermap_destroy(delimiter_mapper);
if (r != idn_success)
goto ret;
}
TRACE(("res delimitermap(): success (name=\"%s\")\n",
idn__debug_ucs4xstring(buffer, 50)));
}
/*
* Split the name into a list of labels.
*/
r = labellist_create(buffer, &labels);
if (r != idn_success)
goto ret;
/*
* Perform conversions and tests.
*/
for (l = labellist_tail(labels); l != NULL;
l = labellist_previous(l)) {
free(saved_name);
saved_name = NULL;
if (!idn__util_ucs4isasciirange(labellist_getname(l))) {
if (actions & IDN_MAP) {
r = label_map(ctx, l);
if (r != idn_success)
goto ret;
}
if (actions & IDN_NORMALIZE) {
r = label_normalize(ctx, l);
if (r != idn_success)
goto ret;
}
if (actions & IDN_PROHCHECK) {
r = label_prohcheck(ctx, l);
if (r == idn_prohibited) {
labellist_undo(l);
continue;
} else if (r != idn_success) {
goto ret;
}
}
if (actions & IDN_UNASCHECK) {
r = label_unascheck(ctx, l);
if (r == idn_prohibited) {
labellist_undo(l);
continue;
} else if (r != idn_success) {
goto ret;
}
}
if (actions & IDN_BIDICHECK) {
r = label_bidicheck(ctx, l);
if (r == idn_prohibited) {
labellist_undo(l);
continue;
} else if (r != idn_success) {
goto ret;
}
}
}
if ((actions & IDN_IDNCONV) && idn_is_ace) {
saved_name = idn_ucs4_strdup(labellist_getname(l));
if (saved_name == NULL) {
r = idn_nomemory;
goto ret;
}
r = label_idndecode(ctx, l);
if (r == idn_invalid_encoding) {
labellist_undo(l);
continue;
} else if (r != idn_success) {
goto ret;
}
}
if ((actions & IDN_RTCHECK) && saved_name != NULL) {
r = label_rtcheck(ctx, actions, l, saved_name);
if (r == idn_invalid_encoding) {
labellist_undo(l);
continue;
} else if (r != idn_success) {
goto ret;
}
}
#ifndef WITHOUT_ICONV
if (actions & IDN_LOCALCONV) {
r = label_localdecodecheck(ctx, l);
if (r != idn_success)
goto ret;
}
#endif
}
/*
* Concat a list of labels to a name.
*/
for (;;) {
void *new_buffer;
new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length);
if (new_buffer == NULL) {
r = idn_nomemory;
goto ret;
}
buffer = (unsigned long *)new_buffer;
r = labellist_getnamelist(labels, buffer, buffer_length);
if (r == idn_success)
break;
else if (r != idn_buffer_overflow)
goto ret;
buffer_length *= 2;
}
if (actions & IDN_LOCALCONV) {
r = idn_converter_convfromucs4(local_converter, buffer, to,
tolen);
} else {
r = idn_ucs4_ucs4toutf8(buffer, to, tolen);
}
ret:
if (r == idn_success) {
TRACE(("idn_res_decodename(): success (to=\"%s\")\n",
idn__debug_xstring(to, 50)));
} else {
TRACE(("idn_res_decodename(): %s\n", idn_result_tostring(r)));
}
free(saved_name);
free(buffer);
if (local_converter != NULL)
idn_converter_destroy(local_converter);
if (idn_converter != NULL)
idn_converter_destroy(idn_converter);
if (labels != NULL)
labellist_destroy(labels);
return (r);
}
idn_result_t
idn_res_decodename2(idn_resconf_t ctx, idn_action_t actions, const char *from,
char *to, size_t tolen, const char *auxencoding) {
#ifdef WITHOUT_ICONV
return idn_failure;
#else /* WITHOUT_ICONV */
idn_result_t r;
idn_converter_t aux_converter = NULL;
unsigned long *buffer_ucs4 = NULL;
char *buffer_utf8 = NULL;
size_t buffer_length;
assert(ctx != NULL && from != NULL && to != NULL);
TRACE(("idn_res_decodename2(actions=%s, from=\"%s\", tolen=%d, "
"auxencoding=\"%s\")\n",
idn__res_actionstostring(actions),
idn__debug_xstring(from, 50), (int)tolen,
(auxencoding != NULL) ? auxencoding : "<null>"));
if (!initialized)
idn_res_initialize();
if (!enabled || actions == 0) {
r = copy_verbatim(from, to, tolen);
goto ret;
} else if (tolen <= 0) {
r = idn_buffer_overflow;
goto ret;
}
if (auxencoding == NULL ||
strcmp(auxencoding, IDN_UTF8_ENCODING_NAME) == 0 ||
strcmp(auxencoding, "UTF-8") == 0) {
return idn_res_decodename(ctx, actions, from, to, tolen);
}
/*
* Convert `from' to UCS4.
*/
r = idn_resconf_setauxidnconvertername(ctx, auxencoding,
IDN_CONVERTER_DELAYEDOPEN);
if (r != idn_success) {
goto ret;
}
aux_converter = idn_resconf_getauxidnconverter(ctx);
if (aux_converter == NULL) {
r = idn_failure;
goto ret;
}
buffer_length = tolen * 2;
for (;;) {
void *new_buffer;
new_buffer = realloc(buffer_ucs4,
sizeof(*buffer_ucs4) * buffer_length);
if (new_buffer == NULL) {
r = idn_nomemory;
goto ret;
}
buffer_ucs4 = (unsigned long *)new_buffer;
r = idn_converter_convtoucs4(aux_converter, from,
buffer_ucs4,
buffer_length);
if (r == idn_success)
break;
else if (r != idn_buffer_overflow)
goto ret;
buffer_length *= 2;
}
if (*buffer_ucs4 == '\0') {
if (tolen <= 0) {
r = idn_buffer_overflow;
goto ret;
}
*to = '\0';
r = idn_success;
goto ret;
}
/*
* Convert `buffer_ucs4' to UTF-8.
*/
buffer_length = tolen * 2;
for (;;) {
void *new_buffer;
new_buffer = realloc(buffer_utf8,
sizeof(*buffer_utf8) * buffer_length);
if (new_buffer == NULL) {
r = idn_nomemory;
goto ret;
}
buffer_utf8 = (char *)new_buffer;
r = idn_ucs4_ucs4toutf8(buffer_ucs4, buffer_utf8,
buffer_length);
if (r == idn_success)
break;
else if (r != idn_buffer_overflow)
goto ret;
buffer_length *= 2;
}
if (*buffer_utf8 == '\0') {
if (tolen <= 0) {
r = idn_buffer_overflow;
goto ret;
}
*to = '\0';
r = idn_success;
goto ret;
}
r = idn_res_decodename(ctx, actions, buffer_utf8, to, tolen);
ret:
if (r == idn_success) {
TRACE(("idn_res_decodename2(): success (to=\"%s\")\n",
idn__debug_xstring(to, 50)));
} else {
TRACE(("idn_res_decodename2(): %s\n", idn_result_tostring(r)));
}
free(buffer_ucs4);
free(buffer_utf8);
if (aux_converter != NULL)
idn_converter_destroy(aux_converter);
return (r);
#endif /* WITHOUT_ICONV */
}
static idn_result_t
copy_verbatim(const char *from, char *to, size_t tolen) {
size_t fromlen = strlen(from);
if (fromlen + 1 > tolen)
return (idn_buffer_overflow);
(void)memcpy(to, from, fromlen + 1);
return (idn_success);
}
static idn_result_t
labellist_create(const unsigned long *name, labellist_t *labelp) {
size_t length, malloc_length;
labellist_t head_label = NULL;
labellist_t tail_label = NULL;
labellist_t new_label = NULL;
const unsigned long *endp = NULL;
idn_result_t r;
while (*name != '\0') {
for (endp = name; *endp != '.' && *endp != '\0'; endp++)
; /* nothing to be done */
length = (endp - name) + 1;
malloc_length = length + 15; /* add 15 for margin */
new_label = (labellist_t)
malloc(sizeof(struct labellist));
if (new_label == NULL) {
r = idn_nomemory;
goto ret;
}
if (head_label == NULL)
head_label = new_label;
new_label->name = NULL;
new_label->undo_name = NULL;
new_label->name_length = malloc_length;
new_label->next = NULL;
new_label->previous = NULL;
new_label->dot_followed = (*endp == '.');
new_label->name = (unsigned long *)
malloc(sizeof(long) * malloc_length);
if (new_label->name == NULL) {
r = idn_nomemory;
goto ret;
}
memcpy(new_label->name, name, sizeof(long) * length);
*(new_label->name + length - 1) = '\0';
new_label->undo_name = (unsigned long *)
malloc(sizeof(long) * malloc_length);
if (new_label->undo_name == NULL) {
r = idn_nomemory;
goto ret;
}
memcpy(new_label->undo_name, name, sizeof(long) * length);
*(new_label->undo_name + length - 1) = '\0';
if (tail_label != NULL) {
tail_label->next = new_label;
new_label->previous = tail_label;
}
tail_label = new_label;
if (*endp == '.')
name = endp + 1;
else
name = endp;
}
*labelp = head_label;
r = idn_success;
ret:
if (r != idn_success) {
if (new_label != NULL) {
free(new_label->name);
free(new_label->undo_name);
free(new_label);
}
if (head_label != NULL)
labellist_destroy(head_label);
}
return (r);
}
static void
labellist_destroy(labellist_t label) {
labellist_t l, l_next;
for (l = label; l != NULL; l = l_next) {
l_next = l->next;
free(l->name);
free(l->undo_name);
free(l);
}
}
static idn_result_t
labellist_setname(labellist_t label, const unsigned long *name) {
unsigned long *new_name;
size_t length, new_length;
length = idn_ucs4_strlen(name) + 1;
new_length = length + 15; /* add 15 for margin */
if (label->name_length < new_length) {
new_name = (unsigned long *)
realloc(label->name, sizeof(long) * new_length);
if (new_name == NULL)
return (idn_nomemory);
label->name = new_name;
label->name_length = new_length;
}
memcpy(label->name, name, sizeof(long) * length);
return (idn_success);
}
static const unsigned long *
labellist_getname(labellist_t label) {
return (label->name);
}
static const unsigned long *
labellist_gettldname(labellist_t label) {
labellist_t l;
if (label->previous == NULL && label->next == NULL &&
!label->dot_followed)
return (idn_mapselector_getnotld());
for (l = label; l->next != NULL; l = l->next)
; /* nothing to be done */
return (l->name);
}
static idn_result_t
labellist_getnamelist(labellist_t label, unsigned long *name,
size_t name_length) {
static const unsigned long dot_string[] = {0x002e, 0x0000}; /* "." */
size_t length;
labellist_t l;
for (l = label, length = 0; l != NULL; l = l->next)
length += idn_ucs4_strlen(l->name) + 1; /* name + `.' */
length++; /* for NUL */
if (name_length < length)
return (idn_buffer_overflow);
*name = '\0';
for (l = label; l != NULL; l = l->next) {
idn_ucs4_strcat(name, l->name);
name += idn_ucs4_strlen(name);
if (l->dot_followed)
idn_ucs4_strcat(name, dot_string);
}
return (idn_success);
}
static void
labellist_undo(labellist_t label) {
size_t length;
length = idn_ucs4_strlen(label->undo_name) + 1;
memcpy(label->name, label->undo_name, sizeof(long) * length);
}
static labellist_t
labellist_tail(labellist_t label) {
labellist_t l;
if (label == NULL)
return (NULL);
for (l = label; l->next != NULL; l = l->next)
; /* nothing to be done */
return (l);
}
static labellist_t
labellist_previous(labellist_t label) {
return (label->previous);
}
#ifndef WITHOUT_ICONV
static idn_result_t
label_localdecodecheck(idn_resconf_t ctx, labellist_t label) {
idn_converter_t local_converter = NULL;
const unsigned long *from;
char *to = NULL;
size_t to_length;
idn_result_t r;
from = labellist_getname(label);
to_length = idn_ucs4_strlen(from) + 1 + 15; /* 15 for margin */
TRACE(("res ucs4tolocal_check(label=\"%s\")\n",
idn__debug_ucs4xstring(from, 50)));
local_converter = idn_resconf_getlocalconverter(ctx);
if (local_converter == NULL) {
r = idn_success;
goto ret;
}
for (;;) {
char *new_buffer;
new_buffer = (char *)realloc(to, to_length);
if (new_buffer == NULL) {
r = idn_nomemory;
goto ret;
}
to = new_buffer;
r = idn_converter_convfromucs4(local_converter, from, to,
to_length);
if (r == idn_success)
break;
else if (r == idn_nomapping) {
r = label_idnencode_ace(ctx, label);
if (r != idn_success)
goto ret;
break;
} else if (r != idn_buffer_overflow) {
goto ret;
}
to_length *= 2;
}
r = idn_success;
ret:
TRACE(("res ucs4tolocal_check(): %s\n", idn_result_tostring(r)));
if (local_converter != NULL)
idn_converter_destroy(local_converter);
free(to);
return (r);
}
#endif /* !WITHOUT_ICONV */
static idn_result_t
label_idndecode(idn_resconf_t ctx, labellist_t label) {
idn_converter_t idn_converter = NULL;
const unsigned long *from;
char *ascii_from = NULL;
unsigned long *to = NULL;
size_t from_length, to_length;
idn_result_t r;
from = labellist_getname(label);
from_length = idn_ucs4_strlen(from) + 1;
TRACE(("res idntoucs4(label=\"%s\")\n",
idn__debug_ucs4xstring(from, 50)));
idn_converter = idn_resconf_getidnconverter(ctx);
if (idn_converter == NULL) {
r = idn_success;
goto ret;
}
for (;;) {
char *new_buffer;
new_buffer = (char *) realloc(ascii_from, from_length);
if (new_buffer == NULL) {
r = idn_nomemory;
goto ret;
}
ascii_from = new_buffer;
r = idn_ucs4_ucs4toutf8(from, ascii_from, from_length);
if (r == idn_success)
break;
else if (r != idn_buffer_overflow)
goto ret;
from_length *= 2;
}
to = NULL;
to_length = from_length;
for (;;) {
unsigned long *new_buffer;
new_buffer = (unsigned long *)
realloc(to, sizeof(long) * to_length);
if (new_buffer == NULL) {
r = idn_nomemory;
goto ret;
}
to = new_buffer;
r = idn_converter_convtoucs4(idn_converter, ascii_from, to,
to_length);
if (r == idn_success)
break;
else if (r != idn_buffer_overflow)
goto ret;
to_length *= 2;
}
r = labellist_setname(label, to);
ret:
if (r == idn_success) {
TRACE(("res idntoucs4(): success (label=\"%s\")\n",
idn__debug_ucs4xstring(labellist_getname(label),
50)));
} else {
TRACE(("res idntoucs4(): %s\n", idn_result_tostring(r)));
}
if (idn_converter != NULL)
idn_converter_destroy(idn_converter);
free(to);
free(ascii_from);
return (r);
}
static idn_result_t
label_idnencode_ace(idn_resconf_t ctx, labellist_t label) {
idn_converter_t idn_converter = NULL;
const unsigned long *from;
char *ascii_to = NULL;
unsigned long *to = NULL;
size_t to_length;
idn_result_t r;
from = labellist_getname(label);
TRACE(("res ucs4toidn(label=\"%s\")\n",
idn__debug_ucs4xstring(from, 50)));
idn_converter = idn_resconf_getidnconverter(ctx);
if (idn_converter == NULL) {
r = idn_success;
goto ret;
}
ascii_to = NULL;
to_length = idn_ucs4_strlen(from) * 4 + 16; /* add mergin */
for (;;) {
char *new_buffer;
new_buffer = (char *) realloc(ascii_to, to_length);
if (new_buffer == NULL) {
r = idn_nomemory;
goto ret;
}
ascii_to = new_buffer;
r = idn_converter_convfromucs4(idn_converter, from, ascii_to,
to_length);
if (r == idn_success)
break;
else if (r != idn_buffer_overflow)
goto ret;
to_length *= 2;
}
for (;;) {
unsigned long *new_buffer;
new_buffer = (unsigned long *)
realloc(to, sizeof(long) * to_length);
if (new_buffer == NULL) {
r = idn_nomemory;
goto ret;
}
to = new_buffer;
r = idn_ucs4_utf8toucs4(ascii_to, to, to_length);
if (r == idn_success)
break;
else if (r != idn_buffer_overflow)
goto ret;
to_length *= 2;
}
if (r != idn_success)
goto ret;
r = labellist_setname(label, to);
ret:
if (r == idn_success) {
TRACE(("res ucs4toidn(): success (label=\"%s\")\n",
idn__debug_ucs4xstring(labellist_getname(label),
50)));
} else {
TRACE(("res ucs4toidn(): %s\n", idn_result_tostring(r)));
}
if (idn_converter != NULL)
idn_converter_destroy(idn_converter);
free(to);
free(ascii_to);
return (r);
}
static idn_result_t
label_localmap(idn_resconf_t ctx, labellist_t label) {
const unsigned long *from;
const unsigned long *tld;
unsigned long *to = NULL;
size_t to_length;
idn_mapselector_t local_mapper;
idn_result_t r;
from = labellist_getname(label);
tld = labellist_gettldname(label);
TRACE(("res localmap(label=\"%s\", tld=\"%s\")\n",
idn__debug_ucs4xstring(from, 50),
idn__debug_ucs4xstring(tld, 50)));
local_mapper = idn_resconf_getlocalmapselector(ctx);
if (local_mapper == NULL) {
r = idn_success;
goto ret;
}
if (tld == from)
tld = idn_mapselector_getdefaulttld();
to_length = idn_ucs4_strlen(from) + 1 + 15; /* 15 for margin */
for (;;) {
unsigned long *new_buffer;
new_buffer = (unsigned long *)
realloc(to, sizeof(long) * to_length);
if (new_buffer == NULL) {
r = idn_nomemory;
goto ret;
}
to = new_buffer;
r = idn_mapselector_map2(local_mapper, from, tld, to,
to_length);
if (r == idn_success)
break;
else if (r != idn_buffer_overflow)
goto ret;
to_length *= 2;
}
r = labellist_setname(label, to);
ret:
if (r == idn_success) {
TRACE(("res localmap(): success (label=\"%s\")\n",
idn__debug_ucs4xstring(labellist_getname(label),
50)));
} else {
TRACE(("res localmap(): %s\n", idn_result_tostring(r)));
}
if (local_mapper != NULL)
idn_mapselector_destroy(local_mapper);
free(to);
return (r);
}
static idn_result_t
label_map(idn_resconf_t ctx, labellist_t label) {
const unsigned long *from;
unsigned long *to = NULL;
size_t to_length;
idn_mapper_t mapper;
idn_result_t r;
from = labellist_getname(label);
TRACE(("res map(label=\"%s\")\n", idn__debug_ucs4xstring(from, 50)));
mapper = idn_resconf_getmapper(ctx);
if (mapper == NULL) {
r = idn_success;
goto ret;
}
to_length = idn_ucs4_strlen(from) + 1 + 15; /* 15 for margin */
for (;;) {
unsigned long *new_buffer;
new_buffer = (unsigned long *)
realloc(to, sizeof(long) * to_length);
if (new_buffer == NULL) {
r = idn_nomemory;
goto ret;
}
to = new_buffer;
r = idn_mapper_map(mapper, from, to, to_length);
if (r == idn_success)
break;
else if (r != idn_buffer_overflow)
goto ret;
to_length *= 2;
}
r = labellist_setname(label, to);
ret:
if (r == idn_success) {
TRACE(("res map(): success (label=\"%s\")\n",
idn__debug_ucs4xstring(labellist_getname(label),
50)));
} else {
TRACE(("res map(): %s\n", idn_result_tostring(r)));
}
if (mapper != NULL)
idn_mapper_destroy(mapper);
free(to);
return (r);
}
static idn_result_t
label_normalize(idn_resconf_t ctx, labellist_t label) {
const unsigned long *from;
unsigned long *to = NULL;
size_t to_length;
idn_normalizer_t normalizer;
idn_result_t r;
from = labellist_getname(label);
TRACE(("res normalzie(label=\"%s\")\n",
idn__debug_ucs4xstring(from, 50)));
normalizer = idn_resconf_getnormalizer(ctx);
if (normalizer == NULL) {
r = idn_success;
goto ret;
}
to_length = idn_ucs4_strlen(from) + 1 + 15; /* 15 for margin */
for (;;) {
unsigned long *new_buffer;
new_buffer = (unsigned long *)
realloc(to, sizeof(long) * to_length);
if (new_buffer == NULL) {
r = idn_nomemory;
goto ret;
}
to = new_buffer;
r = idn_normalizer_normalize(normalizer, from, to, to_length);
if (r == idn_success)
break;
else if (r != idn_buffer_overflow)
goto ret;
to_length *= 2;
}
r = labellist_setname(label, to);
ret:
if (r == idn_success) {
TRACE(("res normalize(): success (label=\"%s\")\n",
idn__debug_ucs4xstring(labellist_getname(label),
50)));
} else {
TRACE(("res normalize(): %s\n", idn_result_tostring(r)));
}
if (normalizer != NULL)
idn_normalizer_destroy(normalizer);
free(to);
return (r);
}
static idn_result_t
label_prohcheck(idn_resconf_t ctx, labellist_t label) {
const unsigned long *name, *found;
idn_checker_t prohibit_checker;
idn_result_t r;
name = labellist_getname(label);
TRACE(("res prohcheck(label=\"%s\")\n",
idn__debug_ucs4xstring(name, 50)));
prohibit_checker = idn_resconf_getprohibitchecker(ctx);
if (prohibit_checker == NULL) {
r = idn_success;
goto ret;
}
r = idn_checker_lookup(prohibit_checker, name, &found);
idn_checker_destroy(prohibit_checker);
if (r == idn_success && found != NULL)
r = idn_prohibited;
ret:
TRACE(("res prohcheck(): %s\n", idn_result_tostring(r)));
return (r);
}
static idn_result_t
label_unascheck(idn_resconf_t ctx, labellist_t label) {
const unsigned long *name, *found;
idn_checker_t unassigned_checker;
idn_result_t r;
name = labellist_getname(label);
TRACE(("res unascheck(label=\"%s\")\n",
idn__debug_ucs4xstring(name, 50)));
unassigned_checker = idn_resconf_getunassignedchecker(ctx);
if (unassigned_checker == NULL) {
r = idn_success;
goto ret;
}
r = idn_checker_lookup(unassigned_checker, name, &found);
idn_checker_destroy(unassigned_checker);
if (r == idn_success && found != NULL)
r = idn_prohibited;
ret:
TRACE(("res unascheck(): %s\n", idn_result_tostring(r)));
return (r);
}
static idn_result_t
label_bidicheck(idn_resconf_t ctx, labellist_t label) {
const unsigned long *name, *found;
idn_checker_t bidi_checker;
idn_result_t r;
name = labellist_getname(label);
TRACE(("res bidicheck(label=\"%s\")\n",
idn__debug_ucs4xstring(name, 50)));
bidi_checker = idn_resconf_getbidichecker(ctx);
if (bidi_checker == NULL) {
r = idn_success;
goto ret;
}
r = idn_checker_lookup(bidi_checker, name, &found);
idn_checker_destroy(bidi_checker);
if (r == idn_success && found != NULL)
r = idn_prohibited;
ret:
TRACE(("res bidicheck(): %s\n", idn_result_tostring(r)));
return (r);
}
static idn_result_t
label_asccheck(idn_resconf_t ctx, labellist_t label) {
const unsigned long *name, *n;
idn_result_t r;
name = labellist_getname(label);
TRACE(("res asccheck(label=\"%s\")\n",
idn__debug_ucs4xstring(name, 50)));
if (*name == '-') {
r = idn_prohibited;
goto ret;
}
for (n = name; *n != '\0'; n++) {
if (*n <= '\177') {
if ((*n < '0' || *n > '9') &&
(*n < 'A' || *n > 'Z') &&
(*n < 'a' || *n > 'z') &&
*n != '-') {
r = idn_prohibited;
goto ret;
}
}
}
if (n > name && *(n - 1) == '-') {
r = idn_prohibited;
goto ret;
}
r = idn_success;
ret:
TRACE(("res asccheck(): %s\n", idn_result_tostring(r)));
return (r);
}
static idn_result_t
label_lencheck_ace(idn_resconf_t ctx, labellist_t label) {
const unsigned long *name;
size_t name_length;
idn_result_t r;
name = labellist_getname(label);
name_length = idn_ucs4_strlen(name);
TRACE(("res lencheck(label=\"%s\")\n",
idn__debug_ucs4xstring(name, 50)));
if (name_length == 0 || name_length > MAX_LABEL_LENGTH) {
r = idn_invalid_length;
goto ret;
}
r = idn_success;
ret:
TRACE(("res lencheck(): %s\n", idn_result_tostring(r)));
return (r);
}
static idn_result_t
label_lencheck_nonace(idn_resconf_t ctx, labellist_t label) {
idn_converter_t idn_converter;
const unsigned long *from;
size_t to_length;
idn_result_t r;
char *buffer = NULL;
size_t buffer_length;
from = labellist_getname(label);
TRACE(("res lencheck(label=\"%s\")\n",
idn__debug_ucs4xstring(from, 50)));
buffer_length = idn_ucs4_strlen(from) * 4 + 16; /* 16 for margin */
idn_converter = idn_resconf_getidnconverter(ctx);
for (;;) {
void *new_buffer;
new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length);
if (new_buffer == NULL) {
r = idn_nomemory;
goto ret;
}
buffer = (char *)new_buffer;
if (idn_converter != NULL) {
r = idn_converter_convfromucs4(idn_converter, from,
buffer, buffer_length);
} else {
r = idn_ucs4_ucs4toutf8(from, buffer, buffer_length);
}
if (r == idn_success)
break;
else if (r != idn_buffer_overflow)
goto ret;
buffer_length *= 2;
}
to_length = strlen(buffer);
if (to_length == 0 || to_length > MAX_LABEL_LENGTH) {
r = idn_invalid_length;
goto ret;
}
r = idn_success;
ret:
TRACE(("res lencheck(): %s\n", idn_result_tostring(r)));
if (idn_converter != NULL)
idn_converter_destroy(idn_converter);
free(buffer);
return (r);
}
static idn_result_t
label_rtcheck(idn_resconf_t ctx, idn_action_t actions, labellist_t label,
const unsigned long *original_name) {
labellist_t rt_label = NULL;
const unsigned long *rt_name;
const unsigned long *cur_name;
idn_result_t r;
cur_name = labellist_getname(label);
TRACE(("res rtcheck(label=\"%s\", org_label=\"%s\")\n",
idn__debug_ucs4xstring(cur_name, 50),
idn__debug_ucs4xstring(original_name, 50)));
r = labellist_create(cur_name, &rt_label);
if (r != idn_success)
goto ret;
if (rt_label == NULL) {
if (*original_name == '\0')
r = idn_success;
else
r = idn_invalid_encoding;
goto ret;
}
if (!idn__util_ucs4isasciirange(labellist_getname(rt_label))) {
r = label_map(ctx, rt_label);
if (r != idn_success)
goto ret;
r = label_normalize(ctx, rt_label);
if (r != idn_success)
goto ret;
r = label_prohcheck(ctx, rt_label);
if (r != idn_success)
goto ret;
if (actions & IDN_UNASCHECK) {
r = label_unascheck(ctx, rt_label);
if (r != idn_success)
goto ret;
}
r = label_bidicheck(ctx, rt_label);
if (r != idn_success)
goto ret;
}
if (actions & IDN_ASCCHECK) {
r = label_asccheck(ctx, rt_label);
if (r != idn_success)
goto ret;
}
if (!idn__util_ucs4isasciirange(labellist_getname(rt_label))) {
r = label_idnencode_ace(ctx, rt_label);
if (r != idn_success)
goto ret;
}
r = label_lencheck_ace(ctx, rt_label);
if (r != idn_success)
goto ret;
rt_name = labellist_getname(rt_label);
if (idn_ucs4_strcasecmp(rt_name, original_name) != 0) {
TRACE(("res rtcheck(): round trip failed, org =\"%s\", rt=\"%s\"\n",
idn__debug_ucs4xstring(original_name, 50),
idn__debug_ucs4xstring(rt_name, 50)));
r = idn_invalid_encoding;
goto ret;
}
r = idn_success;
ret:
if (r != idn_nomemory && r != idn_success)
r = idn_invalid_encoding;
TRACE(("res rtcheck(): %s\n", idn_result_tostring(r)));
if (rt_label != NULL)
labellist_destroy(rt_label);
return (r);
}
const char *
idn__res_actionstostring(idn_action_t actions) {
static char buf[100];
buf[0] = '\0';
if (actions == IDN_ENCODE_QUERY)
strcpy(buf, "encode-query");
else if (actions == IDN_DECODE_QUERY)
strcpy(buf, "decode-query");
else if (actions == IDN_ENCODE_APP)
strcpy(buf, "encode-app");
else if (actions == IDN_DECODE_APP)
strcpy(buf, "decode-app");
else if (actions == IDN_ENCODE_STORED)
strcpy(buf, "encode-stored");
else if (actions == IDN_DECODE_STORED)
strcpy(buf, "decode-stored");
else {
if (actions & IDN_LOCALCONV)
strcat(buf, "|localconv");
if (actions & IDN_DELIMMAP)
strcat(buf, "|delimmap");
if (actions & IDN_LOCALMAP)
strcat(buf, "|localmap");
if (actions & IDN_MAP)
strcat(buf, "|map");
if (actions & IDN_NORMALIZE)
strcat(buf, "|normalize");
if (actions & IDN_PROHCHECK)
strcat(buf, "|prohcheck");
if (actions & IDN_UNASCHECK)
strcat(buf, "|unascheck");
if (actions & IDN_BIDICHECK)
strcat(buf, "|bidicheck");
if (actions & IDN_IDNCONV)
strcat(buf, "|idnconv");
if (actions & IDN_ASCCHECK)
strcat(buf, "|asccheck");
if (actions & IDN_LENCHECK)
strcat(buf, "|lencheck");
if (actions & IDN_RTCHECK)
strcat(buf, "|rtcheck");
}
if (buf[0] == '|')
return (buf + 1);
else
return (buf);
}