Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : Character set conversion Extensions
4 : Copyright (C) Igor Vergeichik <iverg@mail.ru> 2001
5 : Copyright (C) Andrew Tridgell 2001-2011
6 : Copyright (C) Andrew Bartlett 2011
7 : Copyright (C) Simo Sorce 2001
8 : Copyright (C) Martin Pool 2003
9 :
10 : This program is free software; you can redistribute it and/or modify
11 : it under the terms of the GNU General Public License as published by
12 : the Free Software Foundation; either version 3 of the License, or
13 : (at your option) any later version.
14 :
15 : This program is distributed in the hope that it will be useful,
16 : but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : GNU General Public License for more details.
19 :
20 : You should have received a copy of the GNU General Public License
21 : along with this program. If not, see <http://www.gnu.org/licenses/>.
22 :
23 : */
24 : #include "replace.h"
25 : #include "system/iconv.h"
26 : #include "charset.h"
27 : #include "lib/util/debug.h"
28 : #include "lib/util/fault.h"
29 :
30 : /**
31 : * @file
32 : *
33 : * @brief Character-set conversion routines built on our iconv.
34 : *
35 : * @note Samba's internal character set (at least in the 3.0 series)
36 : * is always the same as the one for the Unix filesystem. It is
37 : * <b>not</b> necessarily UTF-8 and may be different on machines that
38 : * need i18n filenames to be compatible with Unix software. It does
39 : * have to be a superset of ASCII. All multibyte sequences must start
40 : * with a byte with the high bit set.
41 : *
42 : * @sa lib/iconv.c
43 : */
44 :
45 :
46 : /**
47 : * Convert string from one encoding to another, making error checking etc
48 : * Slow path version - uses (slow) iconv.
49 : *
50 : * @param src pointer to source string (multibyte or singlebyte)
51 : * @param srclen length of the source string in bytes
52 : * @param dest pointer to destination string (multibyte or singlebyte)
53 : * @param destlen maximal length allowed for string
54 : * @param converted size is the number of bytes occupied in the destination
55 : *
56 : * @returns false and sets errno on fail, true on success.
57 : *
58 : * Ensure the srclen contains the terminating zero.
59 : *
60 : **/
61 :
62 23366 : static bool convert_string_internal(struct smb_iconv_handle *ic,
63 : charset_t from, charset_t to,
64 : void const *src, size_t srclen,
65 : void *dest, size_t destlen, size_t *converted_size)
66 : {
67 : size_t i_len, o_len;
68 : size_t retval;
69 23366 : const char* inbuf = (const char*)src;
70 23366 : char* outbuf = (char*)dest;
71 : smb_iconv_t descriptor;
72 :
73 23366 : descriptor = get_conv_handle(ic, from, to);
74 :
75 23366 : if (srclen == (size_t)-1) {
76 8 : if (from == CH_UTF16LE || from == CH_UTF16BE) {
77 0 : srclen = (strlen_w((const smb_ucs2_t *)src)+1) * 2;
78 : } else {
79 8 : srclen = strlen((const char *)src)+1;
80 : }
81 : }
82 :
83 :
84 23366 : if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) {
85 0 : errno = EINVAL;
86 0 : return false;
87 : }
88 :
89 23366 : i_len=srclen;
90 23366 : o_len=destlen;
91 :
92 23366 : retval = smb_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len);
93 23366 : *converted_size = destlen-o_len;
94 :
95 23366 : return (retval != (size_t)-1);
96 : }
97 :
98 : /**
99 : * Convert string from one encoding to another, making error checking etc
100 : * Fast path version - handles ASCII first.
101 : *
102 : * @param src pointer to source string (multibyte or singlebyte)
103 : * @param srclen length of the source string in bytes, or -1 for nul terminated.
104 : * @param dest pointer to destination string (multibyte or singlebyte)
105 : * @param destlen maximal length allowed for string - *NEVER* -1.
106 : * @param converted size is the number of bytes occupied in the destination
107 : *
108 : * @returns false and sets errno on fail, true on success.
109 : *
110 : * Ensure the srclen contains the terminating zero.
111 : *
112 : * This function has been hand-tuned to provide a fast path.
113 : * Don't change unless you really know what you are doing. JRA.
114 : **/
115 :
116 4912425 : bool convert_string_error_handle(struct smb_iconv_handle *ic,
117 : charset_t from, charset_t to,
118 : void const *src, size_t srclen,
119 : void *dest, size_t destlen,
120 : size_t *converted_size)
121 : {
122 : /*
123 : * NB. We deliberately don't do a strlen here if srclen == -1.
124 : * This is very expensive over millions of calls and is taken
125 : * care of in the slow path in convert_string_internal. JRA.
126 : */
127 :
128 : #ifdef DEVELOPER
129 4912425 : SMB_ASSERT(destlen != (size_t)-1);
130 : #endif
131 :
132 4912425 : if (srclen == 0) {
133 276635 : *converted_size = 0;
134 276635 : return true;
135 : }
136 :
137 4635790 : if (from != CH_UTF16LE && from != CH_UTF16BE && to != CH_UTF16LE && to != CH_UTF16BE) {
138 1002616 : const unsigned char *p = (const unsigned char *)src;
139 1002616 : unsigned char *q = (unsigned char *)dest;
140 1002616 : size_t slen = srclen;
141 1002616 : size_t dlen = destlen;
142 1002616 : unsigned char lastp = '\0';
143 1002616 : size_t retval = 0;
144 :
145 : /* If all characters are ascii, fast path here. */
146 136870105 : while (slen && dlen) {
147 135142327 : if ((lastp = *p) <= 0x7f) {
148 135141771 : *q++ = *p++;
149 135141771 : if (slen != (size_t)-1) {
150 134969341 : slen--;
151 : }
152 135141771 : dlen--;
153 135141771 : retval++;
154 135141771 : if (!lastp)
155 42587 : break;
156 : } else {
157 : #ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
158 : goto general_case;
159 : #else
160 556 : bool ret = convert_string_internal(ic, from, to, p, slen, q, dlen, converted_size);
161 556 : *converted_size += retval;
162 556 : return ret;
163 : #endif
164 : }
165 : }
166 :
167 1002060 : *converted_size = retval;
168 :
169 1002060 : if (!dlen) {
170 : /* Even if we fast path we should note if we ran out of room. */
171 257369 : if (((slen != (size_t)-1) && slen) ||
172 453 : ((slen == (size_t)-1) && lastp)) {
173 4129 : errno = E2BIG;
174 4129 : return false;
175 : }
176 : }
177 997931 : return true;
178 3633174 : } else if (from == CH_UTF16LE && to != CH_UTF16LE) {
179 0 : const unsigned char *p = (const unsigned char *)src;
180 0 : unsigned char *q = (unsigned char *)dest;
181 0 : size_t retval = 0;
182 0 : size_t slen = srclen;
183 0 : size_t dlen = destlen;
184 0 : unsigned char lastp = '\0';
185 : #ifndef BROKEN_UNICODE_COMPOSE_CHARACTERS
186 : bool ret;
187 : #endif
188 :
189 0 : if (slen == (size_t)-1) {
190 0 : while (dlen &&
191 0 : ((lastp = *p) <= 0x7f) && (p[1] == 0)) {
192 0 : *q++ = *p;
193 0 : p += 2;
194 0 : dlen--;
195 0 : retval++;
196 0 : if (!lastp)
197 0 : break;
198 : }
199 0 : if (lastp != 0) goto slow_path;
200 : } else {
201 0 : while (slen >= 2 && dlen &&
202 0 : (*p <= 0x7f) && (p[1] == 0)) {
203 0 : *q++ = *p;
204 0 : slen -= 2;
205 0 : p += 2;
206 0 : dlen--;
207 0 : retval++;
208 : }
209 0 : if (slen != 0) goto slow_path;
210 : }
211 :
212 0 : *converted_size = retval;
213 :
214 0 : if (!dlen) {
215 : /* Even if we fast path we should note if we ran out of room. */
216 0 : if (((slen != (size_t)-1) && slen) ||
217 0 : ((slen == (size_t)-1) && lastp)) {
218 0 : errno = E2BIG;
219 0 : return false;
220 : }
221 : }
222 0 : return true;
223 :
224 0 : slow_path:
225 : /* come here when we hit a character we can't deal
226 : * with in the fast path
227 : */
228 : #ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
229 : goto general_case;
230 : #else
231 0 : ret = convert_string_internal(ic, from, to, p, slen, q, dlen, converted_size);
232 0 : *converted_size += retval;
233 0 : return ret;
234 : #endif
235 :
236 3633174 : } else if (from != CH_UTF16LE && from != CH_UTF16BE && to == CH_UTF16LE) {
237 3610533 : const unsigned char *p = (const unsigned char *)src;
238 3610533 : unsigned char *q = (unsigned char *)dest;
239 3610533 : size_t retval = 0;
240 3610533 : size_t slen = srclen;
241 3610533 : size_t dlen = destlen;
242 3610533 : unsigned char lastp = '\0';
243 :
244 : /* If all characters are ascii, fast path here. */
245 249241448 : while (slen && (dlen >= 1)) {
246 242342122 : if (dlen >=2 && (lastp = *p) <= 0x7F) {
247 242341953 : *q++ = *p++;
248 242341953 : *q++ = '\0';
249 242341953 : if (slen != (size_t)-1) {
250 242339829 : slen--;
251 : }
252 242341953 : dlen -= 2;
253 242341953 : retval += 2;
254 478838164 : if (!lastp)
255 162280 : break;
256 : } else {
257 : #ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
258 : goto general_case;
259 : #else
260 169 : bool ret = convert_string_internal(ic, from, to, p, slen, q, dlen, converted_size);
261 169 : *converted_size += retval;
262 169 : return ret;
263 : #endif
264 : }
265 : }
266 :
267 3610364 : *converted_size = retval;
268 :
269 3610364 : if (!dlen) {
270 : /* Even if we fast path we should note if we ran out of room. */
271 606252 : if (((slen != (size_t)-1) && slen) ||
272 0 : ((slen == (size_t)-1) && lastp)) {
273 0 : errno = E2BIG;
274 0 : return false;
275 : }
276 : }
277 3610364 : return true;
278 : }
279 :
280 : #ifdef BROKEN_UNICODE_COMPOSE_CHARACTERS
281 : general_case:
282 : #endif
283 22641 : return convert_string_internal(ic, from, to, src, srclen, dest, destlen, converted_size);
284 : }
285 :
286 4883929 : bool convert_string_handle(struct smb_iconv_handle *ic,
287 : charset_t from, charset_t to,
288 : void const *src, size_t srclen,
289 : void *dest, size_t destlen,
290 : size_t *converted_size)
291 : {
292 4883929 : bool ret = convert_string_error_handle(ic, from, to, src, srclen, dest, destlen, converted_size);
293 :
294 4883929 : if(ret==false) {
295 16 : const char *reason="unknown error";
296 16 : switch(errno) {
297 8 : case EINVAL:
298 8 : reason="Incomplete multibyte sequence";
299 8 : DBG_NOTICE("Conversion error: %s\n",
300 : reason);
301 8 : break;
302 0 : case E2BIG:
303 : {
304 0 : reason="No more room";
305 0 : if (from == CH_UNIX) {
306 0 : DBG_NOTICE("E2BIG: convert_string(%s,%s): srclen=%u destlen=%u error: %s\n",
307 : charset_name(ic, from), charset_name(ic, to),
308 : (unsigned int)srclen, (unsigned int)destlen, reason);
309 : } else {
310 0 : DBG_NOTICE("E2BIG: convert_string(%s,%s): srclen=%u destlen=%u error: %s\n",
311 : charset_name(ic, from), charset_name(ic, to),
312 : (unsigned int)srclen, (unsigned int)destlen, reason);
313 : }
314 0 : break;
315 : }
316 8 : case EILSEQ:
317 8 : reason="Illegal multibyte sequence";
318 8 : DBG_NOTICE("convert_string_internal: Conversion error: %s\n",
319 : reason);
320 8 : break;
321 0 : default:
322 0 : DBG_ERR("convert_string_internal: Conversion error: %s\n",
323 : reason);
324 0 : break;
325 : }
326 : /* smb_panic(reason); */
327 4427227 : }
328 4883929 : return ret;
329 : }
330 :
331 :
332 : /**
333 : * Convert between character sets, allocating a new buffer using talloc for the result.
334 : *
335 : * @param srclen length of source buffer.
336 : * @param dest always set at least to NULL
337 : * @parm converted_size set to the number of bytes occupied by the string in
338 : * the destination on success.
339 : * @note -1 is not accepted for srclen.
340 : *
341 : * @return true if new buffer was correctly allocated, and string was
342 : * converted.
343 : *
344 : * Ensure the srclen contains the terminating zero.
345 : *
346 : * I hate the goto's in this function. It's emberrassing.....
347 : * There has to be a cleaner way to do this. JRA.
348 : */
349 20125838 : bool convert_string_talloc_handle(TALLOC_CTX *ctx, struct smb_iconv_handle *ic,
350 : charset_t from, charset_t to,
351 : void const *src, size_t srclen, void *dst,
352 : size_t *converted_size)
353 :
354 : {
355 : size_t i_len, o_len, destlen;
356 : size_t retval;
357 20125838 : const char *inbuf = NULL;
358 20125838 : char *outbuf = NULL, *ob = NULL;
359 : smb_iconv_t descriptor;
360 20125838 : void **dest = (void **)dst;
361 :
362 20125838 : *dest = NULL;
363 20125838 : if (converted_size != NULL) {
364 20125838 : *converted_size = 0;
365 : }
366 :
367 20125838 : if (src == NULL || srclen == (size_t)-1) {
368 0 : errno = EINVAL;
369 0 : return false;
370 : }
371 :
372 20125838 : if (srclen == 0) {
373 : /* We really should treat this as an error, but
374 : there are too many callers that need this to
375 : return a NULL terminated string in the correct
376 : character set. */
377 5854 : if (to == CH_UTF16LE|| to == CH_UTF16BE || to == CH_UTF16MUNGED) {
378 999 : destlen = 2;
379 : } else {
380 4855 : destlen = 1;
381 : }
382 5854 : ob = talloc_zero_array(ctx, char, destlen);
383 5854 : if (ob == NULL) {
384 0 : DBG_ERR("Could not talloc destination buffer.\n");
385 0 : errno = ENOMEM;
386 0 : return false;
387 : }
388 5854 : if (converted_size != NULL) {
389 5854 : *converted_size = destlen;
390 : }
391 5854 : *dest = ob;
392 5854 : return true;
393 : }
394 :
395 20119984 : descriptor = get_conv_handle(ic, from, to);
396 :
397 20119984 : if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) {
398 0 : DEBUG(0,("convert_string_talloc: Conversion not supported.\n"));
399 0 : errno = EOPNOTSUPP;
400 0 : return false;
401 : }
402 :
403 20119984 : if (srclen >= (SIZE_MAX - 2) / 3) {
404 0 : DBG_ERR("convert_string_talloc: "
405 : "srclen is %zu, destlen would wrap!\n",
406 : srclen);
407 0 : errno = EOPNOTSUPP;
408 0 : return false;
409 : }
410 20119984 : destlen = srclen * 3;
411 :
412 : /* +2 is for ucs2 null termination. */
413 20119984 : ob = talloc_realloc(ctx, ob, char, destlen + 2);
414 :
415 20119984 : if (!ob) {
416 0 : DEBUG(0, ("convert_string_talloc: realloc failed!\n"));
417 0 : errno = ENOMEM;
418 0 : return false;
419 : }
420 20119984 : outbuf = ob;
421 20119984 : i_len = srclen;
422 20119984 : o_len = destlen;
423 20119984 : inbuf = (const char *)src;
424 :
425 20119984 : retval = smb_iconv(descriptor,
426 : &inbuf, &i_len,
427 : &outbuf, &o_len);
428 20119984 : if(retval == (size_t)-1) {
429 14 : const char *reason="unknown error";
430 14 : switch(errno) {
431 8 : case EINVAL:
432 8 : reason="Incomplete multibyte sequence";
433 8 : DBG_NOTICE("Conversion error: %s\n",
434 : reason);
435 8 : break;
436 0 : case E2BIG:
437 0 : reason = "output buffer is too small";
438 0 : DBG_ERR("Conversion error: %s\n",
439 : reason);
440 0 : break;
441 6 : case EILSEQ:
442 6 : reason="Illegal multibyte sequence";
443 6 : DBG_NOTICE("Conversion error: %s\n",
444 : reason);
445 6 : break;
446 0 : default:
447 0 : DBG_ERR("Conversion error: %s\n",
448 : reason);
449 0 : break;
450 : }
451 : /* smb_panic(reason); */
452 14 : TALLOC_FREE(ob);
453 14 : return false;
454 : }
455 :
456 20119970 : destlen = destlen - o_len;
457 : /* Don't shrink unless we're reclaiming a lot of
458 : * space. This is in the hot codepath and these
459 : * reallocs *cost*. JRA.
460 : */
461 20119970 : if (o_len > 1024) {
462 : /* We're shrinking here so we know the +2 is safe from wrap. */
463 320044 : ob = talloc_realloc(ctx,ob, char, destlen + 2);
464 : }
465 :
466 20119970 : if (destlen && !ob) {
467 0 : DEBUG(0, ("convert_string_talloc: out of memory!\n"));
468 0 : errno = ENOMEM;
469 0 : return false;
470 : }
471 :
472 20119970 : *dest = ob;
473 :
474 : /* Must ucs2 null terminate in the extra space we allocated. */
475 20119970 : ob[destlen] = '\0';
476 20119970 : ob[destlen+1] = '\0';
477 :
478 : /* Ensure we can never return a *converted_size of zero. */
479 20119970 : if (destlen == 0) {
480 : /* As we're now returning false on a bad smb_iconv call,
481 : this should never happen. But be safe anyway. */
482 0 : if (to == CH_UTF16LE|| to == CH_UTF16BE || to == CH_UTF16MUNGED) {
483 0 : destlen = 2;
484 : } else {
485 0 : destlen = 1;
486 : }
487 : }
488 :
489 20119970 : if (converted_size != NULL) {
490 20119970 : *converted_size = destlen;
491 : }
492 20119970 : return true;
493 : }
494 :
495 : /**
496 : * Convert string from one encoding to another, making error checking etc
497 : *
498 : * @param src pointer to source string (multibyte or singlebyte)
499 : * @param srclen length of the source string in bytes
500 : * @param dest pointer to destination string (multibyte or singlebyte)
501 : * @param destlen maximal length allowed for string
502 : * @param converted_size the number of bytes occupied in the destination
503 : *
504 : * @returns true on success, false on fail.
505 : **/
506 4883929 : _PUBLIC_ bool convert_string(charset_t from, charset_t to,
507 : void const *src, size_t srclen,
508 : void *dest, size_t destlen,
509 : size_t *converted_size)
510 : {
511 4883929 : return convert_string_handle(get_iconv_handle(), from, to,
512 : src, srclen,
513 : dest, destlen, converted_size);
514 : }
515 :
516 : /**
517 : * Convert string from one encoding to another, making error checking etc
518 : *
519 : * @param src pointer to source string (multibyte or singlebyte)
520 : * @param srclen length of the source string in bytes
521 : * @param dest pointer to destination string (multibyte or singlebyte)
522 : * @param destlen maximal length allowed for string
523 : * @param converted_size the number of bytes occupied in the destination
524 : *
525 : * @returns true on success, false on fail.
526 : **/
527 28496 : _PUBLIC_ bool convert_string_error(charset_t from, charset_t to,
528 : void const *src, size_t srclen,
529 : void *dest, size_t destlen,
530 : size_t *converted_size)
531 : {
532 28496 : return convert_string_error_handle(get_iconv_handle(), from, to,
533 : src, srclen,
534 : dest, destlen, converted_size);
535 : }
536 :
537 : /**
538 : * Convert between character sets, allocating a new buffer using talloc for the result.
539 : *
540 : * @param srclen length of source buffer.
541 : * @param dest always set at least to NULL
542 : * @param converted_size Size in bytes of the converted string
543 : * @note -1 is not accepted for srclen.
544 : *
545 : * @returns boolean indication whether the conversion succeeded
546 : **/
547 :
548 20125784 : _PUBLIC_ bool convert_string_talloc(TALLOC_CTX *ctx,
549 : charset_t from, charset_t to,
550 : void const *src, size_t srclen,
551 : void *dest, size_t *converted_size)
552 : {
553 20125784 : return convert_string_talloc_handle(ctx, get_iconv_handle(),
554 : from, to, src, srclen, dest,
555 : converted_size);
556 : }
|