1 /*	$NetBSD: nameprep.c,v 1.4 2014/12/10 04:37:55 christos Exp $	*/
2 
3 #ifndef lint
4 static char *rcsid = "Id: nameprep.c,v 1.1 2003/06/04 00:25:56 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 <stdlib.h>
53 #include <string.h>
54 
55 #include <idn/result.h>
56 #include <idn/assert.h>
57 #include <idn/log.h>
58 #include <idn/logmacro.h>
59 #include <idn/debug.h>
60 #include <idn/nameprep.h>
61 
62 #define UCS_MAX		0x7fffffff
63 #define UNICODE_MAX	0x10ffff
64 
65 
66 /*
67  * Load NAMEPREP compiled tables.
68  */
69 #include "nameprepdata.c"
70 
71 /*
72  * Define mapping/checking functions for each version of the draft.
73  */
74 
75 #define VERSION rfc3491
76 #include "nameprep_template.c"
77 #undef VERSION
78 
79 typedef const char	*(*nameprep_mapproc)(unsigned long v);
80 typedef int		(*nameprep_checkproc)(unsigned long v);
81 typedef idn_biditype_t	(*nameprep_biditypeproc)(unsigned long v);
82 
83 static struct idn_nameprep {
84 	char *version;
85 	nameprep_mapproc map_proc;
86 	nameprep_checkproc prohibited_proc;
87 	nameprep_checkproc unassigned_proc;
88 	nameprep_biditypeproc biditype_proc;
89 } nameprep_versions[] = {
90 #define MAKE_NAMEPREP_HANDLE(version, id) \
91 	{ version, \
92 	  compose_sym2(nameprep_map_, id), \
93 	  compose_sym2(nameprep_prohibited_, id), \
94 	  compose_sym2(nameprep_unassigned_, id), \
95 	  compose_sym2(nameprep_biditype_, id), }
96 	MAKE_NAMEPREP_HANDLE("RFC3491", rfc3491),
97 	{ NULL, NULL, NULL },
98 };
99 
100 static idn_result_t	idn_nameprep_check(nameprep_checkproc proc,
101 					   const unsigned long *str,
102 					   const unsigned long **found);
103 
104 idn_result_t
105 idn_nameprep_create(const char *version, idn_nameprep_t *handlep) {
106 	idn_nameprep_t handle;
107 
108 	assert(handlep != NULL);
109 
110 	TRACE(("idn_nameprep_create(version=%-.50s)\n",
111 	       version == NULL ? "<NULL>" : version));
112 
113 	if (version == NULL)
114 		version = IDN_NAMEPREP_CURRENT;
115 
116 	/*
117 	 * Lookup table for the specified version.  Since the number of
118 	 * versions won't be large (I don't want see draft-23 or such :-),
119 	 * simple linear search is OK.
120 	 */
121 	for (handle = nameprep_versions; handle->version != NULL; handle++) {
122 		if (strcmp(handle->version, version) == 0) {
123 			*handlep = handle;
124 			return (idn_success);
125 		}
126 	}
127 	return (idn_notfound);
128 }
129 
130 void
131 idn_nameprep_destroy(idn_nameprep_t handle) {
132 	assert(handle != NULL);
133 
134 	TRACE(("idn_nameprep_destroy()\n"));
135 
136 	/* Nothing to do. */
137 }
138 
139 idn_result_t
140 idn_nameprep_map(idn_nameprep_t handle, const unsigned long *from,
141 		 unsigned long *to, size_t tolen) {
142 	assert(handle != NULL && from != NULL && to != NULL);
143 
144 	TRACE(("idn_nameprep_map(ctx=%s, from=\"%s\")\n",
145 	       handle->version, idn__debug_ucs4xstring(from, 50)));
146 
147 	while (*from != '\0') {
148 		unsigned long v = *from;
149 		const char *mapped;
150 
151 		if (v > UCS_MAX) {
152 			/* This cannot happen, but just in case.. */
153 			return (idn_invalid_codepoint);
154 		} else if (v > UNICODE_MAX) {
155 			/* No mapping is possible. */
156 			mapped = NULL;
157 		} else {
158 			/* Try mapping. */
159 			mapped = (*handle->map_proc)(v);
160 		}
161 
162 		if (mapped == NULL) {
163 			/* No mapping. Just copy verbatim. */
164 			if (tolen < 1)
165 				return (idn_buffer_overflow);
166 			*to++ = v;
167 			tolen--;
168 		} else {
169 			const unsigned char *mappeddata;
170 			size_t mappedlen;
171 
172 			mappeddata = (const unsigned char *)mapped + 1;
173 			mappedlen = *mapped;
174 
175 			if (tolen < (mappedlen + 3) / 4)
176 				return (idn_buffer_overflow);
177 			tolen -= (mappedlen + 3) / 4;
178 			while (mappedlen >= 4) {
179 				*to  = *mappeddata++;
180 				*to |= *mappeddata++ <<  8;
181 				*to |= *mappeddata++ << 16;
182 				*to |= *mappeddata++ << 24;
183 				mappedlen -= 4;
184 				to++;
185 			}
186 			if (mappedlen > 0) {
187 				*to  = *mappeddata++;
188 				*to |= (mappedlen >= 2) ?
189 				       *mappeddata++ <<  8: 0;
190 				*to |= (mappedlen >= 3) ?
191 				       *mappeddata++ << 16: 0;
192 				to++;
193 			}
194 		}
195 		from++;
196 	}
197 	if (tolen == 0)
198 		return (idn_buffer_overflow);
199 	*to = '\0';
200 	return (idn_success);
201 }
202 
203 idn_result_t
204 idn_nameprep_isprohibited(idn_nameprep_t handle, const unsigned long *str,
205 			  const unsigned long **found) {
206 	assert(handle != NULL && str != NULL && found != NULL);
207 
208 	TRACE(("idn_nameprep_isprohibited(ctx=%s, str=\"%s\")\n",
209 	       handle->version, idn__debug_ucs4xstring(str, 50)));
210 
211 	return (idn_nameprep_check(handle->prohibited_proc, str, found));
212 }
213 
214 idn_result_t
215 idn_nameprep_isunassigned(idn_nameprep_t handle, const unsigned long *str,
216 			  const unsigned long **found) {
217 	assert(handle != NULL && str != NULL && found != NULL);
218 
219 	TRACE(("idn_nameprep_isunassigned(handle->version, str=\"%s\")\n",
220 	       handle->version, idn__debug_ucs4xstring(str, 50)));
221 
222 	return (idn_nameprep_check(handle->unassigned_proc, str, found));
223 }
224 
225 static idn_result_t
226 idn_nameprep_check(nameprep_checkproc proc, const unsigned long *str,
227 		   const unsigned long **found) {
228 	unsigned long v;
229 
230 	while (*str != '\0') {
231 		v = *str;
232 
233 		if (v > UCS_MAX) {
234 			/* This cannot happen, but just in case.. */
235 			return (idn_invalid_codepoint);
236 		} else if (v > UNICODE_MAX) {
237 			/* It is invalid.. */
238 			*found = str;
239 			return (idn_success);
240 		} else if ((*proc)(v)) {
241 			*found = str;
242 			return (idn_success);
243 		}
244 		str++;
245 	}
246 	*found = NULL;
247 	return (idn_success);
248 }
249 
250 idn_result_t
251 idn_nameprep_isvalidbidi(idn_nameprep_t handle, const unsigned long *str,
252 			 const unsigned long **found) {
253 	unsigned long v;
254 	idn_biditype_t first_char;
255 	idn_biditype_t last_char;
256 	int found_r_al;
257 
258 	assert(handle != NULL && str != NULL && found != NULL);
259 
260 	TRACE(("idn_nameprep_isvalidbidi(ctx=%s, str=\"%s\")\n",
261 	       handle->version, idn__debug_ucs4xstring(str, 50)));
262 
263 	if (*str == '\0') {
264 		*found = NULL;
265 		return (idn_success);
266 	}
267 
268 	/*
269 	 * check first character's type and initialize variables.
270 	 */
271 	found_r_al = 0;
272 	if (*str > UCS_MAX) {
273 		/* This cannot happen, but just in case.. */
274 		return (idn_invalid_codepoint);
275 	} else if (*str > UNICODE_MAX) {
276 		/* It is invalid.. */
277 		*found = str;
278 		return (idn_success);
279 	}
280 	first_char = last_char = (*(handle->biditype_proc))(*str);
281 	if (first_char == idn_biditype_r_al) {
282 		found_r_al = 1;
283 	}
284 	str++;
285 
286 	/*
287 	 * see whether string is valid or not.
288 	 */
289 	while (*str != '\0') {
290 		v = *str;
291 
292 		if (v > UCS_MAX) {
293 			/* This cannot happen, but just in case.. */
294 			return (idn_invalid_codepoint);
295 		} else if (v > UNICODE_MAX) {
296 			/* It is invalid.. */
297 			*found = str;
298 			return (idn_success);
299 		} else {
300 			last_char = (*(handle->biditype_proc))(v);
301 			if (found_r_al && last_char == idn_biditype_l) {
302 				*found = str;
303 				return (idn_success);
304 			}
305 			if (first_char != idn_biditype_r_al && last_char == idn_biditype_r_al) {
306 				*found = str;
307 				return (idn_success);
308 			}
309 			if (last_char == idn_biditype_r_al) {
310 				found_r_al = 1;
311 			}
312 		}
313 		str++;
314 	}
315 
316 	if (found_r_al) {
317 		if (last_char != idn_biditype_r_al) {
318 			*found = str - 1;
319 			return (idn_success);
320 		}
321 	}
322 
323 	*found = NULL;
324 	return (idn_success);
325 }
326 
327 idn_result_t
328 idn_nameprep_createproc(const char *parameter, void **handlep) {
329 	return idn_nameprep_create(parameter, (idn_nameprep_t *)handlep);
330 }
331 
332 void
333 idn_nameprep_destroyproc(void *handle) {
334 	idn_nameprep_destroy((idn_nameprep_t)handle);
335 }
336 
337 idn_result_t
338 idn_nameprep_mapproc(void *handle, const unsigned long *from,
339 		      unsigned long *to, size_t tolen) {
340 	return idn_nameprep_map((idn_nameprep_t)handle, from, to, tolen);
341 }
342 
343 idn_result_t
344 idn_nameprep_prohibitproc(void *handle, const unsigned long *str,
345 			   const unsigned long **found) {
346 	return idn_nameprep_isprohibited((idn_nameprep_t)handle, str, found);
347 }
348 
349 idn_result_t
350 idn_nameprep_unassignedproc(void *handle, const unsigned long *str,
351 			     const unsigned long **found) {
352 	return idn_nameprep_isunassigned((idn_nameprep_t)handle, str, found);
353 }
354 
355 idn_result_t
356 idn_nameprep_bidiproc(void *handle, const unsigned long *str,
357 		      const unsigned long **found) {
358 	return idn_nameprep_isvalidbidi((idn_nameprep_t)handle, str, found);
359 }
360