diff options
Diffstat (limited to 'deps/ntlmclient/unicode_iconv.c')
-rw-r--r-- | deps/ntlmclient/unicode_iconv.c | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/deps/ntlmclient/unicode_iconv.c b/deps/ntlmclient/unicode_iconv.c new file mode 100644 index 000000000..d1fe07e26 --- /dev/null +++ b/deps/ntlmclient/unicode_iconv.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) Edward Thomson. All rights reserved. + * + * This file is part of ntlmclient, distributed under the MIT license. + * For full terms and copyright information, and for third-party + * copyright information, see the included LICENSE.txt file. + */ + +#include <locale.h> +#include <iconv.h> +#include <string.h> +#include <errno.h> + +#include "ntlmclient.h" +#include "unicode.h" +#include "ntlm.h" +#include "compat.h" + +struct ntlm_unicode_ctx { + ntlm_client *ntlm; + iconv_t utf8_to_16; + iconv_t utf16_to_8; +}; + +ntlm_unicode_ctx *ntlm_unicode_ctx_init(ntlm_client *ntlm) +{ + ntlm_unicode_ctx *ctx; + + if ((ctx = calloc(1, sizeof(ntlm_unicode_ctx))) == NULL) + return NULL; + + ctx->ntlm = ntlm; + ctx->utf8_to_16 = (iconv_t)-1; + ctx->utf16_to_8 = (iconv_t)-1; + + return ctx; +} + +typedef enum { + unicode_iconv_utf8_to_16, + unicode_iconv_utf16_to_8 +} unicode_iconv_encoding_direction; + +static inline bool unicode_iconv_init(ntlm_unicode_ctx *ctx) +{ + if (ctx->utf8_to_16 != (iconv_t)-1 || ctx->utf16_to_8 != (iconv_t)-1) + return true; + + if ((ctx->utf8_to_16 = iconv_open("UTF-16LE", "UTF-8")) == (iconv_t)-1 || + (ctx->utf16_to_8 = iconv_open("UTF-8", "UTF-16LE")) == (iconv_t)-1) { + if (errno == EINVAL) + ntlm_client_set_errmsg(ctx->ntlm, + "iconv does not support UTF8 <-> UTF16 conversion"); + else + ntlm_client_set_errmsg(ctx->ntlm, strerror(errno)); + + return false; + } + + return true; +} + +static inline bool unicode_iconv_encoding_convert( + char **converted, + size_t *converted_len, + ntlm_unicode_ctx *ctx, + const char *string, + size_t string_len, + unicode_iconv_encoding_direction direction) +{ + char *in_start, *out_start, *out, *new_out; + size_t in_start_len, out_start_len, out_size, nul_size, ret, written = 0; + iconv_t converter; + + *converted = NULL; + *converted_len = 0; + + if (!unicode_iconv_init(ctx)) + return false; + + /* + * When translating UTF8 to UTF16, these strings are only used + * internally, and we obey the given length, so we can simply + * use a buffer that is 2x the size. When translating from UTF16 + * to UTF8, we may need to return to callers, so we need to NUL + * terminate and expect an extra byte for UTF8, two for UTF16. + */ + if (direction == unicode_iconv_utf8_to_16) { + converter = ctx->utf8_to_16; + out_size = (string_len * 2) + 2; + nul_size = 2; + } else { + converter = ctx->utf16_to_8; + out_size = (string_len / 2) + 1; + nul_size = 1; + } + + /* Round to the nearest multiple of 8 */ + out_size = (out_size + 7) & ~7; + + if ((out = malloc(out_size)) == NULL) { + ntlm_client_set_errmsg(ctx->ntlm, "out of memory"); + return false; + } + + in_start = (char *)string; + in_start_len = string_len; + + while (true) { + out_start = out + written; + out_start_len = (out_size - nul_size) - written; + + ret = iconv(converter, &in_start, &in_start_len, &out_start, &out_start_len); + written = (out_size - nul_size) - out_start_len; + + if (ret == 0) + break; + + if (ret == (size_t)-1 && errno != E2BIG) { + ntlm_client_set_errmsg(ctx->ntlm, strerror(errno)); + goto on_error; + } + + /* Grow buffer size by 1.5 (rounded up to a multiple of 8) */ + out_size = ((((out_size << 1) - (out_size >> 1)) + 7) & ~7); + + if (out_size > NTLM_UNICODE_MAX_LEN) { + ntlm_client_set_errmsg(ctx->ntlm, + "unicode conversion too large"); + goto on_error; + } + + if ((new_out = realloc(out, out_size)) == NULL) { + ntlm_client_set_errmsg(ctx->ntlm, "out of memory"); + goto on_error; + } + + out = new_out; + } + + if (in_start_len != 0) { + ntlm_client_set_errmsg(ctx->ntlm, + "invalid unicode string; trailing data remains"); + goto on_error; + } + + /* NUL terminate */ + out[written] = '\0'; + + if (direction == unicode_iconv_utf8_to_16) + out[written + 1] = '\0'; + + *converted = out; + + if (converted_len) + *converted_len = written; + + return true; + +on_error: + free(out); + return false; +} + +bool ntlm_unicode_utf8_to_16( + char **converted, + size_t *converted_len, + ntlm_unicode_ctx *ctx, + const char *string, + size_t string_len) +{ + return unicode_iconv_encoding_convert( + converted, converted_len, ctx, string, string_len, + unicode_iconv_utf8_to_16); +} + +bool ntlm_unicode_utf16_to_8( + char **converted, + size_t *converted_len, + ntlm_unicode_ctx *ctx, + const char *string, + size_t string_len) +{ + return unicode_iconv_encoding_convert( + converted, converted_len, ctx, string, string_len, + unicode_iconv_utf16_to_8); +} + +void ntlm_unicode_ctx_free(ntlm_unicode_ctx *ctx) +{ + if (!ctx) + return; + + if (ctx->utf16_to_8 != (iconv_t)-1) + iconv_close(ctx->utf16_to_8); + + if (ctx->utf8_to_16 != (iconv_t)-1) + iconv_close(ctx->utf8_to_16); + + free(ctx); +} |