xref: /minix3/external/bsd/bind/dist/contrib/idn/idnkit-1.0-src/lib/mapper.c (revision 00b67f09dd46474d133c95011a48590a8e8f94c7)
1 /*	$NetBSD: mapper.c,v 1.4 2014/12/10 04:37:55 christos Exp $	*/
2 
3 #ifndef lint
4 static char *rcsid = "Id: mapper.c,v 1.1 2003/06/04 00:25:55 marka Exp ";
5 #endif
6 
7 /*
8  * Copyright (c) 2001,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/mapper.h>
60 #include <idn/strhash.h>
61 #include <idn/debug.h>
62 #include <idn/util.h>
63 #include <idn/ucs4.h>
64 
65 /*
66  * Type for mapping scheme.
67  */
68 typedef struct {
69 	char *prefix;
70 	char *parameter;
71 	idn_mapper_createproc_t create;
72 	idn_mapper_destroyproc_t destroy;
73 	idn_mapper_mapproc_t map;
74 	void *context;
75 } map_scheme_t;
76 
77 /*
78  * Standard mapping schemes.
79  */
80 static const map_scheme_t nameprep_scheme = {
81 	"RFC3491",
82 	NULL,
83 	idn_nameprep_createproc,
84 	idn_nameprep_destroyproc,
85 	idn_nameprep_mapproc,
86 	NULL,
87 };
88 
89 static const map_scheme_t filemap_scheme = {
90 	"filemap",
91 	"",
92 	idn__filemapper_createproc,
93 	idn__filemapper_destroyproc,
94 	idn__filemapper_mapproc,
95 	NULL,
96 };
97 
98 static const map_scheme_t *standard_map_schemes[] = {
99 	&nameprep_scheme,
100 	&filemap_scheme,
101 	NULL,
102 };
103 
104 /*
105  * Hash table for mapping schemes.
106  */
107 static idn__strhash_t scheme_hash = NULL;
108 
109 /*
110  * Mapper object type.
111  */
112 struct idn_mapper {
113 	int nschemes;
114 	int scheme_size;
115 	map_scheme_t *schemes;
116 	int reference_count;
117 };
118 
119 #define MAPPER_INITIAL_SCHEME_SIZE	1
120 
121 idn_result_t
idn_mapper_initialize(void)122 idn_mapper_initialize(void) {
123 	idn_result_t r;
124 	map_scheme_t **scheme;
125 
126 	TRACE(("idn_mapper_initialize()\n"));
127 
128 	if (scheme_hash != NULL) {
129 		r = idn_success;	/* already initialized */
130 		goto ret;
131 	}
132 
133 	r = idn__strhash_create(&scheme_hash);
134 	if (r != idn_success)
135 		goto ret;
136 
137 	for (scheme = (map_scheme_t **)standard_map_schemes;
138 		*scheme != NULL; scheme++) {
139 		r = idn__strhash_put(scheme_hash, (*scheme)->prefix, *scheme);
140 		if (r != idn_success)
141 			goto ret;
142 	}
143 
144 	r = idn_success;
145 ret:
146 	if (r != idn_success && scheme_hash != NULL) {
147 		idn__strhash_destroy(scheme_hash, NULL);
148 		scheme_hash = NULL;
149 	}
150 	TRACE(("idn_mapper_initialize(): %s\n", idn_result_tostring(r)));
151 	return (r);
152 }
153 
154 idn_result_t
idn_mapper_create(idn_mapper_t * ctxp)155 idn_mapper_create(idn_mapper_t *ctxp) {
156 	idn_mapper_t ctx = NULL;
157 	idn_result_t r;
158 
159 	assert(scheme_hash != NULL);
160 	assert(ctxp != NULL);
161 
162 	TRACE(("idn_mapper_create()\n"));
163 
164 	ctx = (idn_mapper_t) malloc(sizeof(struct idn_mapper));
165 	if (ctx == NULL) {
166 		r = idn_nomemory;
167 		goto ret;
168 	}
169 
170 	ctx->schemes = (map_scheme_t *) malloc(sizeof(map_scheme_t)
171 		 * MAPPER_INITIAL_SCHEME_SIZE);
172 	if (ctx->schemes == NULL) {
173 		r = idn_nomemory;
174 		goto ret;
175 	}
176 
177 	ctx->nschemes = 0;
178 	ctx->scheme_size = MAPPER_INITIAL_SCHEME_SIZE;
179 	ctx->reference_count = 1;
180 	*ctxp = ctx;
181 	r = idn_success;
182 
183 ret:
184 	if (r != idn_success) {
185 		if (ctx != NULL)
186 			free(ctx->schemes);
187 		free(ctx);
188 	}
189 	TRACE(("idn_mapper_create(): %s\n", idn_result_tostring(r)));
190 	return (r);
191 }
192 
193 void
idn_mapper_destroy(idn_mapper_t ctx)194 idn_mapper_destroy(idn_mapper_t ctx) {
195 	int i;
196 
197 	assert(scheme_hash != NULL);
198 	assert(ctx != NULL);
199 
200 	TRACE(("idn_mapper_destroy()\n"));
201 
202 	ctx->reference_count--;
203 	if (ctx->reference_count <= 0) {
204 		TRACE(("idn_mapper_destroy(): the object is destroyed\n"));
205 		for (i = 0; i < ctx->nschemes; i++)
206 			ctx->schemes[i].destroy(ctx->schemes[i].context);
207 		free(ctx->schemes);
208 		free(ctx);
209 	} else {
210 		TRACE(("idn_mapper_destroy(): "
211 		       "update reference count (%d->%d)\n",
212 		       ctx->reference_count + 1, ctx->reference_count));
213 	}
214 }
215 
216 void
idn_mapper_incrref(idn_mapper_t ctx)217 idn_mapper_incrref(idn_mapper_t ctx) {
218 	assert(ctx != NULL && scheme_hash != NULL);
219 
220 	TRACE(("idn_mapper_incrref()\n"));
221 	TRACE(("idn_mapper_incrref: update reference count (%d->%d)\n",
222 		ctx->reference_count, ctx->reference_count + 1));
223 
224 	ctx->reference_count++;
225 }
226 
227 idn_result_t
idn_mapper_add(idn_mapper_t ctx,const char * scheme_name)228 idn_mapper_add(idn_mapper_t ctx, const char *scheme_name) {
229 	idn_result_t r;
230 	map_scheme_t *scheme;
231 	const char *scheme_prefix;
232 	const char *scheme_parameter;
233 	void *scheme_context = NULL;
234 	char static_buffer[128];	/* large enough */
235 	char *buffer = static_buffer;
236 
237 	assert(scheme_hash != NULL);
238 	assert(ctx != NULL);
239 
240 	TRACE(("idn_mapper_add(scheme_name=%s)\n",
241 		idn__debug_xstring(scheme_name, 50)));
242 
243 	/*
244 	 * Split `scheme_name' into `scheme_prefix' and `scheme_parameter'.
245 	 */
246 	scheme_parameter = strchr(scheme_name, ':');
247 	if (scheme_parameter == NULL) {
248 		scheme_prefix = scheme_name;
249 	} else {
250 		ptrdiff_t scheme_prefixlen;
251 
252 		scheme_prefixlen = scheme_parameter - scheme_name;
253 		if (scheme_prefixlen + 1 > sizeof(static_buffer)) {
254 			buffer = (char *) malloc(scheme_prefixlen + 1);
255 			if (buffer == NULL) {
256 				r = idn_nomemory;
257 				goto ret;
258 			}
259 		}
260 		memcpy(buffer, scheme_name, scheme_prefixlen);
261 		*(buffer + scheme_prefixlen) = '\0';
262 		scheme_prefix = buffer;
263 		scheme_parameter++;
264 	}
265 
266 	/*
267 	 * Find a scheme.
268 	 */
269 	if (idn__strhash_get(scheme_hash, scheme_prefix, (void **)&scheme)
270 		!= idn_success) {
271 		ERROR(("idn_mapper_add(): invalid scheme name \"%-.30s\"\n",
272 		       scheme_prefix));
273 		r = idn_invalid_name;
274 		goto ret;
275 	}
276 	if (scheme_parameter == NULL) {
277 		if (scheme->parameter != NULL)
278 			scheme_parameter = scheme->parameter;
279 		else
280 			scheme_parameter = scheme->prefix;
281 	}
282 
283 	/*
284 	 * Add the scheme.
285 	 */
286 	assert(ctx->nschemes <= ctx->scheme_size);
287 
288 	if (ctx->nschemes == ctx->scheme_size) {
289 		map_scheme_t *new_schemes;
290 
291 		new_schemes = (map_scheme_t *) realloc(ctx->schemes,
292 			sizeof(map_scheme_t) * ctx->scheme_size * 2);
293 		if (new_schemes == NULL) {
294 			r = idn_nomemory;
295 			goto ret;
296 		}
297 		ctx->schemes = new_schemes;
298 		ctx->scheme_size *= 2;
299 	}
300 
301 	r = scheme->create(scheme_parameter, &scheme_context);
302 	if (r != idn_success)
303 		goto ret;
304 
305 	memcpy(ctx->schemes + ctx->nschemes, scheme, sizeof(map_scheme_t));
306 	ctx->schemes[ctx->nschemes].context = scheme_context;
307 	ctx->nschemes++;
308 	r = idn_success;
309 ret:
310 	if (r != idn_success)
311 		free(scheme_context);
312 	if (buffer != static_buffer)
313 		free(buffer);
314 	TRACE(("idn_mapper_add(): %s\n", idn_result_tostring(r)));
315 	return (r);
316 }
317 
318 idn_result_t
idn_mapper_addall(idn_mapper_t ctx,const char ** scheme_names,int nschemes)319 idn_mapper_addall(idn_mapper_t ctx, const char **scheme_names, int nschemes) {
320 	idn_result_t r;
321 	int i;
322 
323 	assert(scheme_hash != NULL);
324 	assert(ctx != NULL && scheme_names != NULL);
325 
326 	TRACE(("idn_mapper_addall(nschemes=%d)\n", nschemes));
327 
328 	for (i = 0; i < nschemes; i++) {
329 		r = idn_mapper_add(ctx, (const char *)*scheme_names);
330 		if (r != idn_success)
331 			goto ret;
332 		scheme_names++;
333 	}
334 
335 	r = idn_success;
336 ret:
337 	TRACE(("idn_mapper_addall(): %s\n", idn_result_tostring(r)));
338 	return (r);
339 }
340 
341 idn_result_t
idn_mapper_map(idn_mapper_t ctx,const unsigned long * from,unsigned long * to,size_t tolen)342 idn_mapper_map(idn_mapper_t ctx, const unsigned long *from,
343 	       unsigned long *to, size_t tolen) {
344 	idn_result_t r;
345 	unsigned long *src, *dst;
346 	unsigned long *buffers[2] = {NULL, NULL};
347 	size_t buflen[2] = {0, 0};
348 	size_t dstlen;
349 	int idx;
350 	int i;
351 
352 	assert(scheme_hash != NULL);
353 	assert(ctx != NULL && from != NULL && to != NULL);
354 
355 	TRACE(("idn_mapper_map(from=\"%s\", tolen=%d)\n",
356 	       idn__debug_ucs4xstring(from, 50), (int)tolen));
357 
358 	if (ctx->nschemes <= 0) {
359 		if (tolen < idn_ucs4_strlen(from) + 1) {
360 			r = idn_buffer_overflow;
361 			goto ret;
362 		}
363 		idn_ucs4_strcpy(to, from);
364 		r = idn_success;
365 		goto ret;
366 	}
367 
368 	/*
369 	 * Map.
370 	 */
371 	src = (void *)from;
372 	dstlen = idn_ucs4_strlen(from) + 1;
373 
374 	i = 0;
375 	while (i < ctx->nschemes) {
376 		TRACE(("idn_mapper_map(): map %s\n", ctx->schemes[i].prefix));
377 
378 		/*
379 		 * Choose destination area to restore the result of a mapping.
380 		 */
381 		if (i + 1 == ctx->nschemes) {
382 			dst = to;
383 			dstlen = tolen;
384 
385 		} else {
386 			if (src == buffers[0])
387 				idx = 1;
388 			else
389 				idx = 0;
390 
391 			if (buflen[idx] < dstlen) {
392 				void *newbuf;
393 
394 				newbuf = realloc(buffers[idx],
395 						 sizeof(long) * dstlen);
396 				if (newbuf == NULL) {
397 					r = idn_nomemory;
398 					goto ret;
399 				}
400 				buffers[idx] = (unsigned long *)newbuf;
401 				buflen[idx] = dstlen;
402 			}
403 
404 			dst = buffers[idx];
405 			dstlen = buflen[idx];
406 		}
407 
408 		/*
409 		 * Perform i-th map scheme.
410 		 * If buffer size is not enough, we double it and try again.
411 		 */
412 		r = (ctx->schemes[i].map)(ctx->schemes[i].context, src, dst,
413 					  dstlen);
414 		if (r == idn_buffer_overflow && dst != to) {
415 			dstlen *= 2;
416 			continue;
417 		}
418 		if (r != idn_success)
419 			goto ret;
420 
421 		src = dst;
422 		i++;
423 	}
424 
425 	r = idn_success;
426 ret:
427 	free(buffers[0]);
428 	free(buffers[1]);
429 	if (r == idn_success) {
430 		TRACE(("idn_mapper_map(): success (to=\"%s\")\n",
431 		       idn__debug_ucs4xstring(to, 50)));
432 	} else {
433 		TRACE(("idn_mapper_map(): %s\n", idn_result_tostring(r)));
434 	}
435 	return (r);
436 }
437 
438 idn_result_t
idn_mapper_register(const char * prefix,idn_mapper_createproc_t create,idn_mapper_destroyproc_t destroy,idn_mapper_mapproc_t map)439 idn_mapper_register(const char *prefix,
440 		    idn_mapper_createproc_t create,
441 		    idn_mapper_destroyproc_t destroy,
442 		    idn_mapper_mapproc_t map) {
443 	idn_result_t r;
444 	map_scheme_t *scheme = NULL;
445 
446 	assert(scheme_hash != NULL);
447 	assert(prefix != NULL && create != NULL && destroy != NULL &&
448 		map != NULL);
449 
450 	TRACE(("idn_mapper_register(prefix=%s)\n", prefix));
451 
452 	scheme = (map_scheme_t *) malloc(sizeof(map_scheme_t));
453 	if (scheme == NULL) {
454 		r = idn_nomemory;
455 		goto ret;
456 	}
457 
458 	scheme->prefix = (char *) malloc(strlen(prefix) + 1);
459 	if (scheme->prefix == NULL) {
460 		r = idn_nomemory;
461 		goto ret;
462 	}
463 
464 	strcpy(scheme->prefix, prefix);
465 	scheme->parameter = NULL;
466 	scheme->create    = create;
467 	scheme->destroy   = destroy;
468 	scheme->map       = map;
469 
470 	r = idn__strhash_put(scheme_hash, prefix, scheme);
471 	if (r != idn_success)
472 		goto ret;
473 
474 	r = idn_success;
475 ret:
476 	if (r != idn_success) {
477 		if (scheme != NULL)
478 			free(scheme->prefix);
479 		free(scheme);
480 	}
481 
482 	TRACE(("idn_mapper_register(): %s\n", idn_result_tostring(r)));
483 	return (r);
484 }
485