summaryrefslogtreecommitdiff
path: root/deps/ntlmclient/unicode_iconv.c
diff options
context:
space:
mode:
Diffstat (limited to 'deps/ntlmclient/unicode_iconv.c')
-rw-r--r--deps/ntlmclient/unicode_iconv.c201
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);
+}