diff options
Diffstat (limited to 'source3/lib/charcnv.c')
-rw-r--r-- | source3/lib/charcnv.c | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/source3/lib/charcnv.c b/source3/lib/charcnv.c new file mode 100644 index 0000000..5ec070c --- /dev/null +++ b/source3/lib/charcnv.c @@ -0,0 +1,535 @@ +/* + Unix SMB/CIFS implementation. + Character set conversion Extensions + Copyright (C) Igor Vergeichik <iverg@mail.ru> 2001 + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Simo Sorce 2001 + Copyright (C) Martin Pool 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +*/ +#include "includes.h" + +/** + * Destroy global objects allocated by init_iconv() + **/ +void gfree_charcnv(void) +{ + free_iconv_handle(); +} + +/** + * Copy a string from a char* unix src to a dos codepage string destination. + * + * @return the number of bytes occupied by the string in the destination. + * + * @param flags can include + * <dl> + * <dt>STR_TERMINATE</dt> <dd>means include the null termination</dd> + * <dt>STR_UPPER</dt> <dd>means uppercase in the destination</dd> + * </dl> + * + * @param dest_len the maximum length in bytes allowed in the + * destination. + **/ +size_t push_ascii(void *dest, const char *src, size_t dest_len, int flags) +{ + size_t src_len = 0; + char *tmpbuf = NULL; + size_t size = 0; + bool ret; + + /* No longer allow a length of -1. */ + if (dest_len == (size_t)-1) { + smb_panic("push_ascii - dest_len == -1"); + } + + if (flags & STR_UPPER) { + tmpbuf = SMB_STRDUP(src); + if (!tmpbuf) { + smb_panic("malloc fail"); + } + if (!strupper_m(tmpbuf)) { + if ((flags & (STR_TERMINATE|STR_TERMINATE_ASCII)) && + dest && + dest_len > 0) { + *(char *)dest = 0; + } + SAFE_FREE(tmpbuf); + return 0; + } + src = tmpbuf; + } + + src_len = strlen(src); + if (flags & (STR_TERMINATE | STR_TERMINATE_ASCII)) { + src_len++; + } + + ret = convert_string(CH_UNIX, CH_DOS, src, src_len, dest, dest_len, &size); + SAFE_FREE(tmpbuf); + if (ret == false) { + if ((flags & (STR_TERMINATE | STR_TERMINATE_ASCII)) && + dest_len > 0) { + ((char *)dest)[0] = '\0'; + } + return 0; + } + return size; +} + +/******************************************************************** + Push and malloc an ascii string. src and dest null terminated. +********************************************************************/ + +/** + * Copy a string from a dos codepage source to a unix char* destination. + * + * The resulting string in "dest" is always null terminated. + * + * @param flags can have: + * <dl> + * <dt>STR_TERMINATE</dt> + * <dd>STR_TERMINATE means the string in @p src + * is null terminated, and src_len is ignored.</dd> + * </dl> + * + * @param src_len is the length of the source area in bytes. + * @returns the number of bytes occupied by the string in @p src. + **/ +size_t pull_ascii(char *dest, const void *src, size_t dest_len, size_t src_len, int flags) +{ + bool ret; + size_t size = 0; + + if (dest_len == (size_t)-1) { + /* No longer allow dest_len of -1. */ + smb_panic("pull_ascii - invalid dest_len of -1"); + } + + if (flags & STR_TERMINATE) { + if (src_len == (size_t)-1) { + src_len = strlen((const char *)src) + 1; + } else { + size_t len = strnlen((const char *)src, src_len); + if (len < src_len) + len++; + src_len = len; + } + } + + ret = convert_string(CH_DOS, CH_UNIX, src, src_len, dest, dest_len, &size); + if (ret == false) { + size = 0; + dest_len = 0; + } + + if (dest_len && size) { + /* Did we already process the terminating zero ? */ + if (dest[MIN(size-1, dest_len-1)] != 0) { + dest[MIN(size, dest_len-1)] = 0; + } + } else { + dest[0] = 0; + } + + return src_len; +} + +/** + * Copy a string from a dos codepage source to a unix char* destination. + * Talloc version. + * + * The resulting string in "dest" is always null terminated. + * + * @param flags can have: + * <dl> + * <dt>STR_TERMINATE</dt> + * <dd>STR_TERMINATE means the string in @p src + * is null terminated, and src_len is ignored.</dd> + * </dl> + * + * @param src_len is the length of the source area in bytes. + * @returns the number of bytes occupied by the string in @p src. + **/ + +static size_t pull_ascii_base_talloc(TALLOC_CTX *ctx, + char **ppdest, + const void *src, + size_t src_len, + int flags) +{ + char *dest = NULL; + size_t dest_len; + + *ppdest = NULL; + + if (!src_len) { + return 0; + } + + if (src_len == (size_t)-1) { + smb_panic("src_len == -1 in pull_ascii_base_talloc"); + } + + if (flags & STR_TERMINATE) { + size_t len = strnlen((const char *)src, src_len); + if (len < src_len) + len++; + src_len = len; + /* Ensure we don't use an insane length from the client. */ + if (src_len >= 1024*1024) { + char *msg = talloc_asprintf(ctx, + "Bad src length (%u) in " + "pull_ascii_base_talloc", + (unsigned int)src_len); + smb_panic(msg); + } + } + + /* src_len != -1 here. */ + + if (!convert_string_talloc(ctx, CH_DOS, CH_UNIX, src, src_len, &dest, + &dest_len)) { + dest_len = 0; + } + + if (dest_len && dest) { + /* Did we already process the terminating zero ? */ + if (dest[dest_len-1] != 0) { + size_t size = talloc_get_size(dest); + /* Have we got space to append the '\0' ? */ + if (size <= dest_len) { + /* No, realloc. */ + dest = talloc_realloc(ctx, dest, char, + dest_len+1); + if (!dest) { + /* talloc fail. */ + dest_len = (size_t)-1; + return 0; + } + } + /* Yay - space ! */ + dest[dest_len] = '\0'; + dest_len++; + } + } else if (dest) { + dest[0] = 0; + } + + *ppdest = dest; + return src_len; +} + +/** + * Copy a string from a char* src to a unicode destination. + * + * @returns the number of bytes occupied by the string in the destination. + * + * @param flags can have: + * + * <dl> + * <dt>STR_TERMINATE <dd>means include the null termination. + * <dt>STR_UPPER <dd>means uppercase in the destination. + * <dt>STR_NOALIGN <dd>means don't do alignment. + * </dl> + * + * @param dest_len is the maximum length allowed in the + * destination. + **/ + +static size_t push_ucs2(const void *base_ptr, void *dest, const char *src, size_t dest_len, int flags) +{ + size_t len=0; + size_t src_len; + size_t size = 0; + bool ret; + + if (dest_len == (size_t)-1) { + /* No longer allow dest_len of -1. */ + smb_panic("push_ucs2 - invalid dest_len of -1"); + } + + if (flags & STR_TERMINATE) + src_len = (size_t)-1; + else + src_len = strlen(src); + + if (ucs2_align(base_ptr, dest, flags)) { + *(char *)dest = 0; + dest = (void *)((char *)dest + 1); + if (dest_len) + dest_len--; + len++; + } + + /* ucs2 is always a multiple of 2 bytes */ + dest_len &= ~1; + + ret = convert_string(CH_UNIX, CH_UTF16LE, src, src_len, dest, dest_len, &size); + if (ret == false) { + if ((flags & STR_TERMINATE) && + dest && + dest_len) { + *(char *)dest = 0; + } + return len; + } + + len += size; + + if (flags & STR_UPPER) { + smb_ucs2_t *dest_ucs2 = (smb_ucs2_t *)dest; + size_t i; + + /* We check for i < (size / 2) below as the dest string isn't null + terminated if STR_TERMINATE isn't set. */ + + for (i = 0; i < (size / 2) && i < (dest_len / 2) && dest_ucs2[i]; i++) { + smb_ucs2_t v = toupper_w(dest_ucs2[i]); + if (v != dest_ucs2[i]) { + dest_ucs2[i] = v; + } + } + } + + return len; +} + +/** + Copy a string from a ucs2 source to a unix char* destination. + Talloc version with a base pointer. + Uses malloc if TALLOC_CTX is NULL (this is a bad interface and + needs fixing. JRA). + Flags can have: + STR_TERMINATE means the string in src is null terminated. + STR_NOALIGN means don't try to align. + if STR_TERMINATE is set then src_len is ignored if it is -1. + src_len is the length of the source area in bytes + Return the number of bytes occupied by the string in src. + The resulting string in "dest" is always null terminated. +**/ + +static size_t pull_ucs2_base_talloc(TALLOC_CTX *ctx, + const void *base_ptr, + char **ppdest, + const void *src, + size_t src_len, + int flags) +{ + char *dest; + size_t dest_len; + size_t ucs2_align_len = 0; + + *ppdest = NULL; + +#ifdef DEVELOPER + /* Ensure we never use the braindead "malloc" varient. */ + if (ctx == NULL) { + smb_panic("NULL talloc CTX in pull_ucs2_base_talloc\n"); + } +#endif + + if (!src_len) { + return 0; + } + + if (src_len == (size_t)-1) { + /* no longer used anywhere, but worth checking */ + smb_panic("sec_len == -1 in pull_ucs2_base_talloc"); + } + + if (ucs2_align(base_ptr, src, flags)) { + src = (const void *)((const char *)src + 1); + src_len--; + ucs2_align_len = 1; + } + + if (flags & STR_TERMINATE) { + /* src_len -1 is the default for null terminated strings. */ + size_t len = strnlen_w((const smb_ucs2_t *)src, + src_len/2); + if (len < src_len/2) + len++; + src_len = len*2; + + /* Ensure we don't use an insane length from the client. */ + if (src_len >= 1024*1024) { + smb_panic("Bad src length in pull_ucs2_base_talloc\n"); + } + } + + /* ucs2 is always a multiple of 2 bytes */ + src_len &= ~1; + + if (!convert_string_talloc(ctx, CH_UTF16LE, CH_UNIX, src, src_len, + (void *)&dest, &dest_len)) { + dest_len = 0; + } + + if (dest_len) { + /* Did we already process the terminating zero ? */ + if (dest[dest_len-1] != 0) { + size_t size = talloc_get_size(dest); + /* Have we got space to append the '\0' ? */ + if (size <= dest_len) { + /* No, realloc. */ + dest = talloc_realloc(ctx, dest, char, + dest_len+1); + if (!dest) { + /* talloc fail. */ + dest_len = (size_t)-1; + return 0; + } + } + /* Yay - space ! */ + dest[dest_len] = '\0'; + dest_len++; + } + } else if (dest) { + dest[0] = 0; + } + + *ppdest = dest; + return src_len + ucs2_align_len; +} + +/** + Copy a string from a char* src to a unicode or ascii + dos codepage destination choosing unicode or ascii based on the + flags supplied + Return the number of bytes occupied by the string in the destination. + flags can have: + STR_TERMINATE means include the null termination. + STR_UPPER means uppercase in the destination. + STR_ASCII use ascii even with unicode packet. + STR_NOALIGN means don't do alignment. + dest_len is the maximum length allowed in the destination. If dest_len + is -1 then no maxiumum is used. +**/ + +size_t push_string_check_fn(void *dest, const char *src, + size_t dest_len, int flags) +{ + if (!(flags & STR_ASCII) && (flags & STR_UNICODE)) { + return push_ucs2(NULL, dest, src, dest_len, flags); + } + return push_ascii(dest, src, dest_len, flags); +} + + +/** + Copy a string from a char* src to a unicode or ascii + dos codepage destination choosing unicode or ascii based on the + flags in the SMB buffer starting at base_ptr. + Return the number of bytes occupied by the string in the destination. + flags can have: + STR_TERMINATE means include the null termination. + STR_UPPER means uppercase in the destination. + STR_ASCII use ascii even with unicode packet. + STR_NOALIGN means don't do alignment. + dest_len is the maximum length allowed in the destination. If dest_len + is -1 then no maxiumum is used. +**/ + +size_t push_string_base(const char *base, uint16_t flags2, + void *dest, const char *src, + size_t dest_len, int flags) +{ + + if (!(flags & STR_ASCII) && \ + ((flags & STR_UNICODE || \ + (flags2 & FLAGS2_UNICODE_STRINGS)))) { + return push_ucs2(base, dest, src, dest_len, flags); + } + return push_ascii(dest, src, dest_len, flags); +} + +/** + Copy a string from a unicode or ascii source (depending on + the packet flags) to a char* destination. + Variant that uses talloc. + Flags can have: + STR_TERMINATE means the string in src is null terminated. + STR_UNICODE means to force as unicode. + STR_ASCII use ascii even with unicode packet. + STR_NOALIGN means don't do alignment. + if STR_TERMINATE is set then src_len is ignored is it is -1 + src_len is the length of the source area in bytes. + Return the number of bytes occupied by the string in src. + The resulting string in "dest" is always null terminated. +**/ + +size_t pull_string_talloc(TALLOC_CTX *ctx, + const void *base_ptr, + uint16_t smb_flags2, + char **ppdest, + const void *src, + size_t src_len, + int flags) +{ + if ((base_ptr == NULL) && ((flags & (STR_ASCII|STR_UNICODE)) == 0)) { + smb_panic("No base ptr to get flg2 and neither ASCII nor " + "UNICODE defined"); + } + + if (!(flags & STR_ASCII) && \ + ((flags & STR_UNICODE || \ + (smb_flags2 & FLAGS2_UNICODE_STRINGS)))) { + return pull_ucs2_base_talloc(ctx, + base_ptr, + ppdest, + src, + src_len, + flags); + } + return pull_ascii_base_talloc(ctx, + ppdest, + src, + src_len, + flags); +} + +/******************************************************************* + Write a string in (little-endian) unicode format. src is in + the current DOS codepage. len is the length in bytes of the + string pointed to by dst. + + if null_terminate is True then null terminate the packet (adds 2 bytes) + + the return value is the length in bytes consumed by the string, including the + null termination if applied +********************************************************************/ + +size_t dos_PutUniCode(char *dst,const char *src, size_t len, bool null_terminate) +{ + int flags = null_terminate ? STR_UNICODE|STR_NOALIGN|STR_TERMINATE + : STR_UNICODE|STR_NOALIGN; + return push_ucs2(NULL, dst, src, len, flags); +} + + +/* Converts a string from internal samba format to unicode. Always terminates. + * Actually just a wrapper round push_ucs2_talloc(). + */ + +int rpcstr_push_talloc(TALLOC_CTX *ctx, smb_ucs2_t **dest, const char *src) +{ + size_t size; + if (push_ucs2_talloc(ctx, dest, src, &size)) + return size; + else + return -1; +} |