/*
** (c) Copyright 2003-2005 Matt Messier and John Viega
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**
** Redistributions of source code must retain the above copyright notice,
** this list of conditions and the following disclaimer.
**
** Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
**
** Neither the name of the primary nor the names of the contributors may
** be used to endorse or promote products derived from this software
** without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "isafestr.h"
u_int32_t safestr_cookie = 0;
u_int64_t safestr_maxlength = ((u_int64_t)1 << (sizeof(u_int32_t) * 8)) - sizeof(small_isafestr_t);
safestr_malloc_t safestr_malloc_fn = safestr_default_malloc;
safestr_realloc_t safestr_realloc_fn = safestr_default_realloc;
safestr_free_t safestr_free_fn = safestr_default_free;
static xxl_assettype_t map_asset(u_int32_t);
static int compare_strings(isafestr_t, isafestr_t, unsigned char *, u_int32_t);
static void free_isafestr_asset(isafestr_t, void *);
static void free_void_asset(void *, void *);
static void bake_cookie(void);
static u_int32_t get_cookie(void);
static safestr_t * charlist(isafestr_t, const char *, unsigned int);
unsigned char safestr_casemap_none[256] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
};
unsigned char safestr_casemap_upper[256] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127,
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
};
unsigned char safestr_casemap_lower[256] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
};
static xxl_assettype_t
map_asset(u_int32_t flag)
{
switch ((flag & SAFESTR_ASSET_MASK))
{
case SAFESTR_ASSET_PERMANENT: return XXL_ASSET_PERMANENT;
case SAFESTR_ASSET_TEMPORARY: return XXL_ASSET_TEMPORARY;
case SAFESTR_ASSET_PROMOTE: return XXL_ASSET_PROMOTE;
case SAFESTR_ASSET_DEMOTE: return XXL_ASSET_DEMOTE;
}
return XXL_ASSET_PERMANENT;
}
static int
compare_strings(isafestr_t istr1, isafestr_t istr2, unsigned char *map, u_int32_t limit)
{
u_int32_t i, length;
unsigned char *s1, *s2;
if (map == safestr_casemap_none && limit == (u_int32_t)-1 && istr1->hdr.length == istr2->hdr.length)
return memcmp(istr1->str, istr2->str, istr1->hdr.length);
s1 = (unsigned char *)istr1->str;
s2 = (unsigned char *)istr2->str;
length = SAFESTR_MIN(istr1->hdr.length, istr2->hdr.length);
if (limit != (u_int32_t)-1 && limit < length)
length = limit;
for (i = 0; i < length; i++)
{
if (map[s1[i]] == map[s2[i]])
continue;
else if (map[s1[i]] < map[s2[i]])
return -1;
return 1;
}
if (istr1->hdr.length == istr2->hdr.length)
return 0;
if (limit == -1)
return (istr1->hdr.length < istr2->hdr.length ? -1 : 1);
if (istr1->hdr.length < istr2->hdr.length)
return (limit <= istr1->hdr.length ? 0 : -1);
return (limit <= istr2->hdr.length ? 0 : 1);
}
static void
free_isafestr_asset(isafestr_t str, void *arg)
{
if ((XXL_EXCEPTION_CODE() && !(str->hdr.flags & SAFESTR_ORIGINAL)) ||
(!XXL_EXCEPTION_CODE() && (str->hdr.flags & SAFESTR_RESIZED)))
{
safestr_memzero(str->str, str->hdr.size + 1);
str->hdr.size = str->hdr.length = str->hdr.flags = str->hdr.cookie = 0;
safestr_free_fn(str, __FILE__, __LINE__);
return;
}
str->hdr.flags &= ~(SAFESTR_ORIGINAL | SAFESTR_RESIZED);
DEC_REF_COUNT(str);
if (REF_COUNT(str) <= 0 ||
(REF_COUNT(str) == 1 && (str->hdr.flags & SAFESTR_TEMPORARY)))
{
if (str->hdr.cookie != safestr_cookie)
XXL_THROW_ERROR(SAFESTR_ERROR_BAD_ADDRESS, NULL);
#ifdef _DEBUG
if (getenv("SAFESTR_TRACE"))
{
fprintf(stderr, "Freeing string 0x%08X (\"%s\")\n",
(unsigned int)str->str, str->str);
}
#endif
safestr_memzero(str->str, str->hdr.size + 1);
str->hdr.size = str->hdr.length = str->hdr.flags = str->hdr.cookie = 0;
safestr_free_fn(str, __FILE__, __LINE__);
}
}
static void
free_void_asset(void *ptr, void *arg)
{
safestr_free_fn(ptr, __FILE__, __LINE__);
}
static void
bake_cookie(void)
{
#ifdef WIN32
HCRYPTPROV c;
if (!CryptAcquireContext(&c, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
XXL_THROW_ERROR(SAFESTR_ERROR_PRNG_FAILURE, NULL);
do
{
if (!CryptGenRandom(c, sizeof(safestr_cookie), (BYTE *)&safestr_cookie))
{
CryptReleaseContext(c, 0);
XXL_THROW_ERROR(SAFESTR_ERROR_PRNG_FAILURE, NULL);
}
} while (!safestr_cookie);
CryptReleaseContext(c, 0);
#else
int fd;
if ((fd = open("/dev/urandom", O_RDONLY)) == -1)
XXL_THROW_ERROR(SAFESTR_ERROR_PRNG_FAILURE, NULL);
do
{
read(fd, &safestr_cookie, sizeof(safestr_cookie));
} while (!safestr_cookie);
close(fd);
#endif
}
static u_int32_t
get_cookie(void)
{
if (!safestr_cookie)
bake_cookie();
return safestr_cookie;
}
static safestr_t *
charlist(isafestr_t str, const char *file, unsigned int lineno)
{
safestr_t *ret;
u_int32_t i, size;
isafestr_t istr;
/* Assets saved in this function will be saved within the context of the
* caller: safestr_split()
*/
size = sizeof(safestr_t) * (str->hdr.length + 1);
ret = (safestr_t *)safestr_malloc(size, XXL_ASSET_PROMOTE, file, lineno);
for (i = 0; i < str->hdr.length; i++)
{
ret[i] = safestr_do_alloc(1, (str->hdr.flags & SAFESTR_TRUSTED), file, lineno);
istr = (isafestr_t)((char *)ret[i] - sizeof(small_isafestr_t));
XXL_ASSET_SAVE(istr, free_isafestr_asset, NULL, XXL_ASSET_PROMOTE);
istr->hdr.length = 1;
istr->str[0] = str->str[i];
istr->str[1] = '\0';
}
ret[str->hdr.length] = NULL;
return ret;
}
/* ------------------------------------------------------------------------- */
void *
safestr_malloc(size_t size, xxl_assettype_t type, const char *file, unsigned int lineno)
{
void *ptr;
if (!(ptr = safestr_malloc_fn(size, file, lineno)))
XXL_THROW_ERROR(SAFESTR_ERROR_OUT_OF_MEMORY, NULL);
XXL_ASSET_SAVE(ptr, free_void_asset, NULL, type);
return ptr;
}
void *
safestr_realloc(void *old_ptr, size_t size, const char *file, unsigned int lineno)
{
void *new_ptr;
if (!(new_ptr = safestr_realloc_fn(old_ptr, size, file, lineno)))
XXL_THROW_ERROR(SAFESTR_ERROR_OUT_OF_MEMORY, NULL);
XXL_ASSET_UPDATE(old_ptr, new_ptr);
return new_ptr;
}
isafestr_t
safestr_get(safestr_t s, u_int32_t flags)
{
isafestr_t str;
if (!s)
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_PARAMETER, NULL);
str = (isafestr_t)((char *)s - sizeof(small_isafestr_t));
if (str->hdr.cookie != safestr_cookie)
XXL_THROW_ERROR(SAFESTR_ERROR_BAD_ADDRESS, NULL);
if ((flags & SAFESTR_GET_WRITABLE) && (str->hdr.flags & SAFESTR_IMMUTABLE))
XXL_THROW_ERROR(SAFESTR_ERROR_IMMUTABLE_STRING, s);
if ((flags & SAFESTR_GET_WRITABLE) && (REF_COUNT(str) > 1))
XXL_THROW_ERROR(SAFESTR_ERROR_IMMUTABLE_STRING, s);
if (REF_COUNT(str) == 0xFFFFFFFF)
XXL_THROW_ERROR(SAFESTR_ERROR_TOO_MANY_REFERENCES, s);
str->hdr.flags |= SAFESTR_ORIGINAL;
INC_REF_COUNT(str);
XXL_ASSET_SAVE(str, free_isafestr_asset, NULL, XXL_ASSET_TEMPORARY);
return str;
}
isafestr_t
safestr_resize(isafestr_t str, u_int32_t length)
{
u_int32_t size;
isafestr_t new_str;
/* neither length nor str->hdr.size consider null-terminator */
if (length <= str->hdr.size)
{
safestr_memzero(str->str + length, str->hdr.size - length);
str->hdr.length = length;
return str;
}
if (length + 1 > safestr_maxlength)
XXL_THROW_ERROR(SAFESTR_ERROR_STRING_TOO_LONG, NULL);
size = SAFESTR_ROUND(sizeof(small_isafestr_t) + length + 1);
new_str = (isafestr_t)safestr_malloc(size, XXL_ASSET_PERMANENT, __FILE__, __LINE__);
new_str->hdr.size = size - sizeof(small_isafestr_t) - 1;
new_str->hdr.length = length;
new_str->hdr.flags = str->hdr.flags & ~SAFESTR_ORIGINAL;
new_str->hdr.refs = str->hdr.refs;
new_str->hdr.cookie = str->hdr.cookie;
memcpy(new_str->str, str->str, str->hdr.length + 1);
str->hdr.flags |= SAFESTR_RESIZED;
XXL_ASSET_SAVE(new_str, free_isafestr_asset, NULL, XXL_ASSET_TEMPORARY);
return new_str;
}
safestr_t
safestr_complete(isafestr_t iold, isafestr_t inew)
{
u_int32_t newsize;
safestr_t new_safestr, old_safestr;
if (iold != inew)
{
newsize = inew->hdr.size + sizeof(small_isafestr_t) + 1;
old_safestr = (safestr_t)iold->str;
iold = (isafestr_t)safestr_realloc(iold, newsize, __FILE__, __LINE__);
new_safestr = (safestr_t)iold->str;
memcpy(iold, inew, newsize);
iold->hdr.flags &= ~(SAFESTR_ORIGINAL | SAFESTR_RESIZED);
inew->hdr.flags |= (SAFESTR_ORIGINAL | SAFESTR_RESIZED);
/* Now, the original exposed pointer may still be on the asset stack
* somewhere, so we need to update that as well (note that the call to
* safestr_realloc() takes care of the base isafestr_t pointer for us.
*/
XXL_ASSET_UPDATE(old_safestr, new_safestr);
}
return (safestr_t)iold->str;
}
/* ------------------------------------------------------------------------- */
safestr_t
safestr_do_alloc(u_int32_t length, u_int32_t flags, const char *file, unsigned int lineno)
{
u_int32_t size;
isafestr_t str;
xxl_assettype_t asset;
/* Is the length requested too big? */
if (length + 1 > safestr_maxlength)
XXL_THROW_ERROR(SAFESTR_ERROR_STRING_TOO_LONG, NULL);
size = SAFESTR_ROUND(sizeof(small_isafestr_t) + length + 1);
asset = map_asset(flags);
flags &= (SAFESTR_TEMPORARY | SAFESTR_IMMUTABLE | SAFESTR_TRUSTED);
str = (isafestr_t)safestr_malloc(size, XXL_ASSET_PERMANENT, file, lineno);
str->hdr.size = size - sizeof(small_isafestr_t) - 1;
str->hdr.length = 0;
str->hdr.flags = flags;
str->hdr.refs = 1;
str->hdr.cookie = get_cookie();
str->str[0] = 0;
XXL_ASSET_SAVE(str->str, safestr_cleanup_asset, NULL, asset);
return (safestr_t)str->str;
}
char
safestr_charat(safestr_t s, u_int32_t i)
{
char result;
isafestr_t str;
XXL_ASSET_BLOCK_BEGIN
{
str = safestr_get(s, SAFESTR_GET_READONLY);
if (i >= str->hdr.length)
XXL_THROW_ERROR(SAFESTR_ERROR_INDEX_OUT_OF_RANGE, NULL);
result = str->str[i];
}
XXL_ASSET_BLOCK_END;
return result;
}
void
safestr_cleanup_asset(void *ptr, void *arg)
{
isafestr_t istr;
istr = (isafestr_t)((char *)ptr - sizeof(small_isafestr_t));
if (istr->hdr.cookie != safestr_cookie)
XXL_THROW_ERROR(SAFESTR_ERROR_BAD_ADDRESS, NULL);
if (REF_COUNT(istr) == 0xFFFFFFFF)
XXL_THROW_ERROR(SAFESTR_ERROR_TOO_MANY_REFERENCES, istr);
istr->hdr.flags |= SAFESTR_ORIGINAL;
free_isafestr_asset(istr, NULL);
}
safestr_t
safestr_do_clone(safestr_t s, u_int32_t flags, const char *file, unsigned int lineno)
{
u_int32_t size;
isafestr_t clone, str;
xxl_assettype_t asset;
XXL_ASSET_BLOCK_BEGIN
{
str = safestr_get(s, SAFESTR_GET_READONLY);
/* Taint flag inheritance is independent of flags. Can't pass taint by
* calling _rw, because we have no need to check immutable.
*/
asset = map_asset(flags);
flags &= (SAFESTR_TEMPORARY | SAFESTR_IMMUTABLE);
flags |= (str->hdr.flags & SAFESTR_TRUSTED);
size = SAFESTR_ROUND(sizeof(small_isafestr_t) + str->hdr.length + 1);
clone = (isafestr_t)safestr_malloc(size, XXL_ASSET_PERMANENT, file, lineno);
clone->hdr.size = size - sizeof(small_isafestr_t) - 1;
clone->hdr.length = str->hdr.length;
clone->hdr.flags = flags;
clone->hdr.refs = 1;
clone->hdr.cookie = get_cookie();
memcpy(clone->str, str->str, str->hdr.length + 1);
}
XXL_ASSET_BLOCK_END;
XXL_ASSET_SAVE(clone->str, safestr_cleanup_asset, NULL, asset);
return (safestr_t)clone->str;
}
int
safestr_compare(safestr_t str1, safestr_t str2, u_int32_t flags, ...)
{
int result;
va_list ap;
u_int32_t length = (u_int32_t)-1;
isafestr_t istr1, istr2;
unsigned char *map;
XXL_ASSET_BLOCK_BEGIN
{
if (str1 == str2)
{
istr1 = safestr_get(str1, SAFESTR_GET_READONLY);
result = 0;
}
else
{
if (flags & SAFESTR_COMPARE_LIMIT)
{
va_start(ap, flags);
length = va_arg(ap, u_int32_t);
va_end(ap);
}
istr1 = safestr_get(str1, SAFESTR_GET_READONLY);
istr2 = safestr_get(str2, SAFESTR_GET_READONLY);
map = ((flags & SAFESTR_COMPARE_NOCASE) ? safestr_casemap_lower : safestr_casemap_none);
result = compare_strings(istr1, istr2, map, length);
}
}
XXL_ASSET_BLOCK_END;
return result;
}
void
safestr_concatenate(safestr_t *dst, safestr_t s, u_int32_t flags, ...)
{
va_list ap;
u_int32_t dst_length, src_length, tmp_length;
isafestr_t idst, iorig, isrc;
XXL_ASSET_BLOCK_BEGIN
{
isrc = safestr_get(s, SAFESTR_GET_READONLY);
idst = iorig = safestr_get(*dst, SAFESTR_GET_WRITABLE);
dst_length = idst->hdr.length;
src_length = isrc->hdr.length;
if (flags & SAFESTR_COPY_LIMIT)
{
/* SAFESTR_COPY_LIMIT specifies the maximum length of the resulting
* string, so we need to subtract the value from the current length
* to get the max.
*/
va_start(ap, flags);
tmp_length = va_arg(ap, u_int32_t);
va_end(ap);
src_length = (tmp_length < src_length ? 0 : tmp_length - src_length);
}
if (src_length > 0)
{
idst = safestr_resize(idst, dst_length + src_length);
memcpy(idst->str + dst_length, isrc->str, src_length);
*(idst->str + idst->hdr.length) = 0;
if (!(isrc->hdr.flags & SAFESTR_TRUSTED))
idst->hdr.flags &= ~SAFESTR_TRUSTED;
*dst = safestr_complete(iorig, idst);
}
}
XXL_ASSET_BLOCK_END;
}
safestr_t *
safestr_do_convertarray(char **arr, u_int32_t flags, const char *file, unsigned int lineno)
{
int c, i;
safestr_t *res;
isafestr_t str;
XXL_ASSET_BLOCK_BEGIN
{
for (c = 0; arr[c]; c++);
res = (safestr_t *)safestr_malloc(sizeof(safestr_t) * (c + 1), XXL_ASSET_PROMOTE, file, lineno);
for (i = 0; arr[i]; i++)
{
res[i] = safestr_do_create(arr[i], flags, file, lineno);
str = (isafestr_t)((char *)res[i] - sizeof(small_isafestr_t));
XXL_ASSET_SAVE(str, free_isafestr_asset, NULL, XXL_ASSET_PROMOTE);
}
res[i] = NULL;
}
XXL_ASSET_BLOCK_END;
return res;
}
safestr_t
safestr_do_create(char *s, u_int32_t flags, const char *file, unsigned int lineno)
{
safestr_t str;
u_int32_t length;
isafestr_t istr;
#ifndef WIN32
length = strlen(s);
#else
length = lstrlenA(s);
#endif
str = safestr_do_alloc(length, flags, file, lineno);
memcpy((char *)str, s, length + 1);
istr = (isafestr_t)((char *)str - sizeof(small_isafestr_t));
istr->hdr.length = length;
return str;
}
void
safestr_delete(safestr_t *s, u_int32_t pos, u_int32_t count)
{
isafestr_t str;
XXL_ASSET_BLOCK_BEGIN
{
str = safestr_get(*s, SAFESTR_GET_WRITABLE);
if (pos < str->hdr.length)
{
if (pos + count >= str->hdr.length)
{
str->hdr.length = pos;
*(str->str + str->hdr.length) = 0;
}
else
{
str->hdr.length -= count;
memmove(str->str + pos, str->str + pos + count,
str->hdr.length - pos + 1);
}
}
*s = (safestr_t)str->str;
}
XXL_ASSET_BLOCK_END;
}
void
safestr_duplicate(safestr_t *dst, safestr_t src, u_int32_t flags, ...)
{
va_list ap;
u_int32_t length;
isafestr_t idst, iorig, isrc;
XXL_ASSET_BLOCK_BEGIN
{
isrc = safestr_get(src, SAFESTR_GET_READONLY);
idst = iorig = safestr_get(*dst, SAFESTR_GET_WRITABLE);
if (!(flags & SAFESTR_COPY_LIMIT))
length = isrc->hdr.length;
else
{
va_start(ap, flags);
length = va_arg(ap, u_int32_t);
va_end(ap);
length = SAFESTR_MIN(length, isrc->hdr.length);
}
idst = safestr_resize(idst, length);
memcpy(idst->str, isrc->str, length);
*(idst->str + length) = 0;
if (!(isrc->hdr.flags & SAFESTR_TRUSTED))
idst->hdr.flags &= ~SAFESTR_TRUSTED;
*dst = safestr_complete(iorig, idst);
}
XXL_ASSET_BLOCK_END;
}
int
safestr_endswith(safestr_t str, safestr_t substr)
{
int match = 0;
isafestr_t istr, isubstr;
XXL_ASSET_BLOCK_BEGIN
{
istr = safestr_get(str, SAFESTR_GET_READONLY);
isubstr = safestr_get(substr, SAFESTR_GET_READONLY);
if (istr->hdr.length >= isubstr->hdr.length &&
!memcmp(istr->str + istr->hdr.length - isubstr->hdr.length, isubstr->str, isubstr->hdr.length))
{
match = 1;
}
}
XXL_ASSET_BLOCK_END;
return match;
}
int
safestr_equal(safestr_t str1, safestr_t str2, u_int32_t flags, ...)
{
int result;
va_list ap;
u_int32_t length = (u_int32_t)-1;
isafestr_t istr1, istr2;
unsigned char *map;
XXL_ASSET_BLOCK_BEGIN
{
if (str1 == str2)
{
istr1 = safestr_get(str1, SAFESTR_GET_READONLY);
result = 1;
}
else
{
if (flags & SAFESTR_COMPARE_LIMIT)
{
va_start(ap, flags);
length = va_arg(ap, u_int32_t);
va_end(ap);
}
istr1 = safestr_get(str1, SAFESTR_GET_READONLY);
istr2 = safestr_get(str2, SAFESTR_GET_READONLY);
map = ((flags & SAFESTR_COMPARE_NOCASE) ? safestr_casemap_lower : safestr_casemap_none);
result = (compare_strings(istr1, istr2, map, length) == 0);
}
}
XXL_ASSET_BLOCK_END;
return result;
}
void
safestr_free(safestr_t s)
{
isafestr_t str;
XXL_ASSET_BLOCK_BEGIN
{
str = safestr_get(s, SAFESTR_GET_READONLY);
DEC_REF_COUNT(str);
}
XXL_ASSET_BLOCK_END;
}
void
safestr_freelist(safestr_t *list)
{
u_int32_t i;
isafestr_t str;
for (i = 0; list[i]; i++)
{
XXL_ASSET_BLOCK_BEGIN
{
str = safestr_get(list[i], SAFESTR_GET_READONLY);
DEC_REF_COUNT(str);
}
XXL_ASSET_BLOCK_END;
}
safestr_free_fn(list, __FILE__, __LINE__);
}
void
safestr_insert(safestr_t *dst, u_int32_t pos, safestr_t s)
{
u_int32_t old_length;
isafestr_t idst, iorig, isrc;
XXL_ASSET_BLOCK_BEGIN
{
isrc = safestr_get(s, SAFESTR_GET_READONLY);
idst = iorig = safestr_get(*dst, SAFESTR_GET_WRITABLE);
if (pos > idst->hdr.length)
XXL_THROW_ERROR(SAFESTR_ERROR_INDEX_OUT_OF_RANGE, NULL);
old_length = idst->hdr.length;
idst = safestr_resize(idst, idst->hdr.length + isrc->hdr.length);
memmove(idst->str + pos + isrc->hdr.length, idst->str + pos, old_length - pos + 1);
memcpy(idst->str + pos, isrc->str, isrc->hdr.length);
if (!isrc->hdr.flags & SAFESTR_TRUSTED)
idst->hdr.flags &= ~SAFESTR_TRUSTED;
*dst = safestr_complete(iorig, idst);
}
XXL_ASSET_BLOCK_END;
}
safestr_t
safestr_do_join(safestr_t *s, safestr_t joiner, const char *file, unsigned int lineno)
{
char *outp;
safestr_t ret;
u_int32_t i, count, len, trust = SAFESTR_TRUSTED;
isafestr_t ijoiner, istr, *strs;
XXL_ASSET_BLOCK_BEGIN
{
ijoiner = safestr_get(joiner, SAFESTR_GET_READONLY);
trust &= ijoiner->hdr.flags;
for (count = 0; s[count]; count++);
if (!count)
ret = safestr_do_alloc(0, SAFESTR_TRUSTED, file, lineno);
else
{
strs = (isafestr_t *)safestr_malloc(sizeof(isafestr_t) * count, XXL_ASSET_TEMPORARY, __FILE__, __LINE__);
for (i = len = 0; i < count; i++)
{
strs[i] = safestr_get(s[i], SAFESTR_GET_READONLY);
len += strs[i]->hdr.length;
trust &= strs[i]->hdr.flags;
}
len += (ijoiner->hdr.length * (count - 1));
ret = safestr_do_alloc(len, trust, file, lineno);
istr = (isafestr_t)((char *)ret - sizeof(small_isafestr_t));
istr->hdr.length = len;
istr->str[len] = '\0';
memcpy(istr->str, strs[0]->str, strs[0]->hdr.length);
for (outp = istr->str + strs[0]->hdr.length, i = 1; i < count; i++)
{
memcpy(outp, ijoiner->str, ijoiner->hdr.length);
memcpy(outp + ijoiner->hdr.length, strs[i]->str, strs[i]->hdr.length);
outp += (ijoiner->hdr.length + strs[i]->hdr.length);
}
}
}
XXL_ASSET_BLOCK_END;
return ret;
}
u_int32_t
safestr_length(safestr_t s)
{
u_int32_t length;
isafestr_t str;
XXL_ASSET_BLOCK_BEGIN
{
str = safestr_get(s, SAFESTR_GET_READONLY);
length = str->hdr.length;
}
XXL_ASSET_BLOCK_END;
return length;
}
void
safestr_memzero(volatile void *str, u_int32_t len)
{
volatile char *buf;
for (buf = (volatile char *)str; len; buf[--len] = 0);
}
safestr_t
safestr_reference(safestr_t s)
{
isafestr_t str;
XXL_ASSET_BLOCK_BEGIN
{
str = safestr_get(s, SAFESTR_GET_READONLY);
INC_REF_COUNT(str);
}
XXL_ASSET_BLOCK_END;
return s;
}
void
safestr_replace(safestr_t *str, safestr_t oldp, safestr_t newp)
{
char *c;
u_int32_t new_len, str_len, zero_len = 0;
isafestr_t inewp, ioldp, iorig, istr;
XXL_ASSET_BLOCK_BEGIN
{
ioldp = safestr_get(oldp, SAFESTR_GET_READONLY);
istr = iorig = safestr_get(*str, SAFESTR_GET_WRITABLE);
if (istr->hdr.length >= ioldp->hdr.length)
{
inewp = safestr_get(newp, SAFESTR_GET_READONLY);
new_len = 0;
str_len = istr->hdr.length;
for (c = istr->str; c < istr->str + str_len - ioldp->hdr.length + 1; c++)
{
if (memcmp(c, ioldp->str, ioldp->hdr.length))
{
new_len++;
continue;
}
new_len += inewp->hdr.length;
c += (ioldp->hdr.length) - 1;
}
if (c < istr->str + str_len)
new_len += (ioldp->hdr.length - 1);
/* Note that we cannot use safestr_resize() here if the new length
* is less than the old length, because the string will be pre-
* maturely zeroed, resulting in a loss of data. Instead, we need
* to do the zero after we move stuff around.
*/
if (new_len > str_len)
istr = safestr_resize(istr, new_len);
else
{
zero_len = istr->hdr.length - new_len;
istr->hdr.length = new_len;
}
if (!(inewp->hdr.flags & SAFESTR_TRUSTED))
istr->hdr.flags &= ~SAFESTR_TRUSTED;
for (c = istr->str; c < istr->str + str_len - ioldp->hdr.length + 1; c++)
{
if (memcmp(c, ioldp->str, ioldp->hdr.length))
continue;
memmove(c + inewp->hdr.length, c + ioldp->hdr.length,
(str_len - (c - istr->str) - ioldp->hdr.length) + 1);
memcpy(c, inewp->str, inewp->hdr.length);
if (inewp->hdr.length > ioldp->hdr.length)
str_len += (inewp->hdr.length - ioldp->hdr.length);
else
str_len -= (ioldp->hdr.length - inewp->hdr.length);
c += (inewp->hdr.length - 1);
}
if (zero_len > 0)
safestr_memzero(istr->str + new_len, zero_len);
}
*str = safestr_complete(iorig, istr);
}
XXL_ASSET_BLOCK_END;
}
u_int32_t
safestr_search(safestr_t s, safestr_t sub, u_int32_t flags, ...)
{
int match;
va_list ap;
u_int32_t from = 0, fromchar = 0, i, n = 1, result, sublength = 0;
isafestr_t str, substr = NULL;
unsigned char findchar = 0, *map, *uptr, *usub;
XXL_ASSET_BLOCK_BEGIN
{
str = safestr_get(s, SAFESTR_GET_READONLY);
if (!(flags & SAFESTR_FIND_CHARACTER)) {
substr = safestr_get(sub, SAFESTR_GET_READONLY);
sublength = substr->hdr.length;
}
va_start(ap, flags);
if (flags & SAFESTR_FIND_FROMCHAR)
fromchar = va_arg(ap, u_int32_t);
if (flags & SAFESTR_FIND_FROMNTH)
from = va_arg(ap, u_int32_t);
if (flags & SAFESTR_FIND_NTH)
n = va_arg(ap, u_int32_t);
if (flags & SAFESTR_FIND_CHARACTER) {
findchar = (unsigned char)va_arg(ap, int);
sublength = 1;
}
va_end(ap);
n += from;
if (n < 1 || fromchar >= str->hdr.length || sublength > str->hdr.length)
result = SAFESTR_ERROR_NOTFOUND;
else
{
map = ((flags & SAFESTR_FIND_NOMATCHCASE) ? safestr_casemap_lower : safestr_casemap_none);
usub = ((flags & SAFESTR_FIND_CHARACTER) ? &findchar : (unsigned char *)substr->str);
if (flags & SAFESTR_FIND_REVERSE)
{
for (uptr = (unsigned char *)str->str + str->hdr.length - fromchar - sublength;
uptr >= (unsigned char *)str->str;
uptr--)
{
match = 1;
for (i = 0; i < sublength; i++)
{
if (map[uptr[i]] != map[usub[i]])
{
match = 0;
break;
}
}
if (match && !--n)
break;
}
result = (uptr < (unsigned char *)str->str ?
SAFESTR_ERROR_NOTFOUND : (u_int32_t)(uptr - (unsigned char *)str->str));
}
else
{
for (uptr = (unsigned char *)str->str + fromchar;
uptr <= (unsigned char *)str->str + str->hdr.length - sublength;
uptr++)
{
match = 1;
for (i = 0; i < sublength; i++)
{
if (map[uptr[i]] != map[usub[i]])
{
match = 0;
break;
}
}
if (match && !--n)
break;
}
result = (uptr > (unsigned char *)str->str + str->hdr.length - sublength ?
SAFESTR_ERROR_NOTFOUND : (u_int32_t)(uptr - (unsigned char *)str->str));
}
}
}
XXL_ASSET_BLOCK_END;
return result;
}
void
safestr_setcharat(safestr_t s, u_int32_t i, char c, int trust)
{
isafestr_t str;
XXL_ASSET_BLOCK_BEGIN
{
str = safestr_get(s, SAFESTR_GET_WRITABLE);
if (i >= str->hdr.length)
XXL_THROW_ERROR(SAFESTR_ERROR_INDEX_OUT_OF_RANGE, NULL);
if (!trust)
str->hdr.flags &= ~SAFESTR_TRUSTED;
str->str[i] = c;
}
XXL_ASSET_BLOCK_END;
}
void
safestr_setmemfns(safestr_malloc_t malloc_fn, safestr_realloc_t realloc_fn,
safestr_free_t free_fn)
{
safestr_malloc_fn = (malloc_fn ? malloc_fn : safestr_default_malloc);
safestr_realloc_fn = (realloc_fn ? realloc_fn : safestr_default_realloc);
safestr_free_fn = (free_fn ? free_fn : safestr_default_free);
}
safestr_t
safestr_do_slice(safestr_t s, u_int32_t start, u_int32_t end, const char *file, unsigned int lineno)
{
u_int32_t length = 0, size;
isafestr_t new_str, str;
XXL_ASSET_BLOCK_BEGIN
{
str = safestr_get(s, SAFESTR_GET_READONLY);
if (start < str->hdr.length)
{
length = end - start;
length = SAFESTR_MIN(str->hdr.length - start, length);
}
size = SAFESTR_ROUND(sizeof(small_isafestr_t) + length + 1);
new_str = (isafestr_t)safestr_malloc(size, XXL_ASSET_PERMANENT, file, lineno);
new_str->hdr.size = size - sizeof(small_isafestr_t) - 1;
new_str->hdr.length = length;
new_str->hdr.flags = (str->hdr.flags & SAFESTR_TRUSTED);
new_str->hdr.refs = 1;
new_str->hdr.cookie = get_cookie();
new_str->str[length] = 0;
memcpy(new_str->str, str->str + start, length);
}
XXL_ASSET_BLOCK_END;
return (safestr_t)new_str->str;
}
safestr_t *
safestr_do_split(safestr_t s, safestr_t sub, const char *file, unsigned int lineno)
{
char *c, *cursor;
safestr_t *ret;
u_int32_t count;
isafestr_t istr, str, substr;
XXL_ASSET_BLOCK_BEGIN
{
str = safestr_get(s, SAFESTR_GET_READONLY);
substr = safestr_get(sub, SAFESTR_GET_READONLY);
if (!substr->hdr.length)
ret = charlist(str, file, lineno);
else if (substr->hdr.length > str->hdr.length)
{
ret = (safestr_t *)safestr_malloc(sizeof(safestr_t) * 2, XXL_ASSET_PROMOTE, file, lineno);
ret[0] = safestr_do_clone(s, (str->hdr.flags & SAFESTR_TRUSTED), file, lineno);
ret[1] = NULL;
}
else
{
/* Figure out how many array elements are needed */
count = 1;
for (c = str->str; *(c + substr->hdr.length - 1); c++)
if (!memcmp(c, substr->str, substr->hdr.length))
{
count++;
c += (substr->hdr.length - 1);
}
ret = (safestr_t *)safestr_malloc(sizeof(safestr_t) * (count + 1), XXL_ASSET_PROMOTE, file, lineno);
count = 0;
for (c = cursor = str->str; *(c + substr->hdr.length - 1); c++)
{
if (!memcmp(c, substr->str, substr->hdr.length))
{
ret[count] = safestr_do_alloc(c - cursor, (str->hdr.flags & SAFESTR_TRUSTED), file, lineno);
istr = (isafestr_t)((char *)ret[count] - sizeof(small_isafestr_t));
XXL_ASSET_SAVE(istr, free_isafestr_asset, NULL, XXL_ASSET_PROMOTE);
istr->hdr.length = c - cursor;
memcpy(istr->str, cursor, istr->hdr.length);
istr->str[istr->hdr.length] = '\0';
cursor = c + substr->hdr.length;
c += (substr->hdr.length - 1);
count++;
}
}
if (*c)
c += (substr->hdr.length - 1);
ret[count] = safestr_do_alloc(c - cursor, (str->hdr.flags & SAFESTR_TRUSTED), file, lineno);
istr = (isafestr_t)((char *)ret[count] - sizeof(small_isafestr_t));
XXL_ASSET_SAVE(istr, free_isafestr_asset, NULL, XXL_ASSET_PROMOTE);
istr->hdr.length = c - cursor;
memcpy(istr->str, cursor, istr->hdr.length);
istr->str[istr->hdr.length] = '\0';
ret[++count] = NULL;
}
}
XXL_ASSET_BLOCK_END;
return ret;
}
int
safestr_startswith(safestr_t str, safestr_t substr)
{
int match = 0;
isafestr_t istr, isubstr;
XXL_ASSET_BLOCK_BEGIN
{
istr = safestr_get(str, SAFESTR_GET_READONLY);
isubstr = safestr_get(substr, SAFESTR_GET_READONLY);
if (istr->hdr.length >= isubstr->hdr.length &&
!memcmp(istr->str, isubstr->str, isubstr->hdr.length))
{
match = 1;
}
}
XXL_ASSET_BLOCK_END;
return match;
}
char *
safestr_do_strdup(char *s, const char *file, unsigned int lineno)
{
char *new_s;
u_int32_t len;
#ifndef WIN32
len = strlen(s) + 1;
#else
len = lstrlenA(s) + 1;
#endif
new_s = (char *)safestr_malloc(len, XXL_ASSET_PERMANENT, file, lineno);
memcpy(new_s, s, len);
return new_s;
}
void
safestr_truncate(safestr_t *s, u_int32_t length)
{
u_int32_t old_length;
isafestr_t iorig, istr;
XXL_ASSET_BLOCK_BEGIN
{
istr = iorig = safestr_get(*s, SAFESTR_GET_WRITABLE);
old_length = istr->hdr.length;
istr = safestr_resize(istr, length);
if (length > old_length)
memset(istr->str + old_length, ' ', length - old_length);
*(istr->str + istr->hdr.length) = 0;
*s = safestr_complete(iorig, istr);
}
XXL_ASSET_BLOCK_END;
}
/* ------------------------------------------------------------------------- */
void
safestr_trust(safestr_t s)
{
XXL_ASSET_BLOCK_BEGIN
{
safestr_get(s, SAFESTR_GET_READONLY)->hdr.flags |= SAFESTR_TRUSTED;
}
XXL_ASSET_BLOCK_END;
}
void
safestr_untrust(safestr_t s)
{
XXL_ASSET_BLOCK_BEGIN
{
safestr_get(s, SAFESTR_GET_READONLY)->hdr.flags &= ~SAFESTR_TRUSTED;
}
XXL_ASSET_BLOCK_END;
}
int
safestr_istrusted(safestr_t s)
{
u_int32_t trusted;
XXL_ASSET_BLOCK_BEGIN
{
trusted = safestr_get(s, SAFESTR_GET_READONLY)->hdr.flags & SAFESTR_TRUSTED;
}
XXL_ASSET_BLOCK_END;
return !!trusted;
}
void
safestr_makereadonly(safestr_t s)
{
XXL_ASSET_BLOCK_BEGIN
{
safestr_get(s, SAFESTR_GET_READONLY)->hdr.flags |= SAFESTR_IMMUTABLE;
}
XXL_ASSET_BLOCK_END;
}
void
safestr_makewritable(safestr_t s)
{
XXL_ASSET_BLOCK_BEGIN
{
safestr_get(s, SAFESTR_GET_READONLY)->hdr.flags &= ~SAFESTR_IMMUTABLE;
}
XXL_ASSET_BLOCK_END;
}
int
safestr_isreadonly(safestr_t s)
{
u_int32_t immutable;
XXL_ASSET_BLOCK_BEGIN
{
immutable = safestr_get(s, SAFESTR_GET_READONLY)->hdr.flags & SAFESTR_IMMUTABLE;
}
XXL_ASSET_BLOCK_END;
return !!immutable;
}
/* ------------------------------------------------------------------------- */
void *
safestr_default_malloc(size_t nbytes, const char *file, unsigned int lineno)
{
return malloc(nbytes);
}
void *
safestr_default_realloc(void *ptr, size_t nbytes, const char *file, unsigned int lineno)
{
return realloc(ptr, nbytes);
}
void
safestr_default_free(void *ptr, const char *file, unsigned int lineno)
{
free(ptr);
}
syntax highlighted by Code2HTML, v. 0.9.1