/* * Copyright (C) 2008-2009 Tobias Brunner * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil * * 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 2 of the License, or (at your * option) any later version. See . * * 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. */ #include #include #include #include #include #include "chunk.h" #include "debug.h" /* required for chunk_hash */ #undef get16bits #if (defined(__GNUC__) && defined(__i386__)) #define get16bits(d) (*((const u_int16_t*)(d))) #endif #if !defined (get16bits) #define get16bits(d) ((((u_int32_t)(((const u_int8_t*)(d))[1])) << 8)\ + (u_int32_t)(((const u_int8_t*)(d))[0]) ) #endif /** * Empty chunk. */ chunk_t chunk_empty = { NULL, 0 }; /** * Described in header. */ chunk_t chunk_create_clone(u_char *ptr, chunk_t chunk) { chunk_t clone = chunk_empty; if (chunk.ptr && chunk.len > 0) { clone.ptr = ptr; clone.len = chunk.len; memcpy(clone.ptr, chunk.ptr, chunk.len); } return clone; } /** * Described in header. */ size_t chunk_length(const char* mode, ...) { va_list chunks; size_t length = 0; va_start(chunks, mode); while (TRUE) { switch (*mode++) { case 'm': case 'c': case 's': { chunk_t ch = va_arg(chunks, chunk_t); length += ch.len; continue; } default: break; } break; } va_end(chunks); return length; } /** * Described in header. */ chunk_t chunk_create_cat(u_char *ptr, const char* mode, ...) { va_list chunks; chunk_t construct = chunk_create(ptr, 0); va_start(chunks, mode); while (TRUE) { bool free_chunk = FALSE, clear_chunk = FALSE; chunk_t ch; switch (*mode++) { case 's': clear_chunk = TRUE; /* FALL */ case 'm': free_chunk = TRUE; /* FALL */ case 'c': ch = va_arg(chunks, chunk_t); memcpy(ptr, ch.ptr, ch.len); ptr += ch.len; construct.len += ch.len; if (clear_chunk) { chunk_clear(&ch); } else if (free_chunk) { free(ch.ptr); } continue; default: break; } break; } va_end(chunks); return construct; } /** * Described in header. */ void chunk_split(chunk_t chunk, const char *mode, ...) { va_list chunks; u_int len; chunk_t *ch; va_start(chunks, mode); while (TRUE) { if (*mode == '\0') { break; } len = va_arg(chunks, u_int); ch = va_arg(chunks, chunk_t*); /* a null chunk means skip len bytes */ if (ch == NULL) { chunk = chunk_skip(chunk, len); continue; } switch (*mode++) { case 'm': { ch->len = min(chunk.len, len); if (ch->len) { ch->ptr = chunk.ptr; } else { ch->ptr = NULL; } chunk = chunk_skip(chunk, ch->len); continue; } case 'a': { ch->len = min(chunk.len, len); if (ch->len) { ch->ptr = malloc(ch->len); memcpy(ch->ptr, chunk.ptr, ch->len); } else { ch->ptr = NULL; } chunk = chunk_skip(chunk, ch->len); continue; } case 'c': { ch->len = min(ch->len, chunk.len); ch->len = min(ch->len, len); if (ch->len) { memcpy(ch->ptr, chunk.ptr, ch->len); } else { ch->ptr = NULL; } chunk = chunk_skip(chunk, ch->len); continue; } default: break; } break; } va_end(chunks); } /** * Described in header. */ bool chunk_write(chunk_t chunk, char *path, char *label, mode_t mask, bool force) { mode_t oldmask; FILE *fd; bool good = FALSE; if (!force && access(path, F_OK) == 0) { DBG1(DBG_LIB, " %s file '%s' already exists", label, path); return FALSE; } oldmask = umask(mask); fd = fopen(path, "w"); if (fd) { if (fwrite(chunk.ptr, sizeof(u_char), chunk.len, fd) == chunk.len) { DBG1(DBG_LIB, " written %s file '%s' (%d bytes)", label, path, chunk.len); good = TRUE; } else { DBG1(DBG_LIB, " writing %s file '%s' failed: %s", label, path, strerror(errno)); } fclose(fd); } else { DBG1(DBG_LIB, " could not open %s file '%s': %s", label, path, strerror(errno)); } umask(oldmask); return good; } /** hex conversion digits */ static char hexdig_upper[] = "0123456789ABCDEF"; static char hexdig_lower[] = "0123456789abcdef"; /** * Described in header. */ chunk_t chunk_to_hex(chunk_t chunk, char *buf, bool uppercase) { int i, len; char *hexdig = hexdig_lower; if (uppercase) { hexdig = hexdig_upper; } len = chunk.len * 2; if (!buf) { buf = malloc(len + 1); } buf[len] = '\0'; for (i = 0; i < chunk.len; i++) { buf[i*2] = hexdig[(chunk.ptr[i] >> 4) & 0xF]; buf[i*2+1] = hexdig[(chunk.ptr[i] ) & 0xF]; } return chunk_create(buf, len); } /** * convert a signle hex character to its binary value */ static char hex2bin(char hex) { switch (hex) { case '0' ... '9': return hex - '0'; case 'A' ... 'F': return hex - 'A' + 10; case 'a' ... 'f': return hex - 'a' + 10; default: return 0; } } /** * Described in header. */ chunk_t chunk_from_hex(chunk_t hex, char *buf) { int i, len; u_char *ptr; bool odd = FALSE; /* subtract the number of optional ':' separation characters */ len = hex.len; ptr = hex.ptr; for (i = 0; i < hex.len; i++) { if (*ptr++ == ':') { len--; } } /* compute the number of binary bytes */ if (len % 2) { odd = TRUE; len++; } len /= 2; /* allocate buffer memory unless provided by caller */ if (!buf) { buf = malloc(len); } /* buffer is filled from the right */ memset(buf, 0, len); hex.ptr += hex.len; for (i = len - 1; i >= 0; i--) { /* skip separation characters */ if (*(--hex.ptr) == ':') { --hex.ptr; } buf[i] = hex2bin(*hex.ptr); if (i > 0 || !odd) { buf[i] |= hex2bin(*(--hex.ptr)) << 4; } } return chunk_create(buf, len); } /** base 64 conversion digits */ static char b64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /** * Described in header. */ chunk_t chunk_to_base64(chunk_t chunk, char *buf) { int i, len; char *pos; len = chunk.len + ((3 - chunk.len % 3) % 3); if (!buf) { buf = malloc(len * 4 / 3 + 1); } pos = buf; for (i = 0; i < len; i+=3) { *pos++ = b64digits[chunk.ptr[i] >> 2]; if (i+1 >= chunk.len) { *pos++ = b64digits[(chunk.ptr[i] & 0x03) << 4]; *pos++ = '='; *pos++ = '='; break; } *pos++ = b64digits[((chunk.ptr[i] & 0x03) << 4) | (chunk.ptr[i+1] >> 4)]; if (i+2 >= chunk.len) { *pos++ = b64digits[(chunk.ptr[i+1] & 0x0F) << 2]; *pos++ = '='; break; } *pos++ = b64digits[((chunk.ptr[i+1] & 0x0F) << 2) | (chunk.ptr[i+2] >> 6)]; *pos++ = b64digits[chunk.ptr[i+2] & 0x3F]; } *pos = '\0'; return chunk_create(buf, len * 4 / 3); } /** * convert a base 64 digit to its binary form (inversion of b64digits array) */ static int b642bin(char b64) { switch (b64) { case 'A' ... 'Z': return b64 - 'A'; case 'a' ... 'z': return ('Z' - 'A' + 1) + b64 - 'a'; case '0' ... '9': return ('Z' - 'A' + 1) + ('z' - 'a' + 1) + b64 - '0'; case '+': case '-': return 62; case '/': case '_': return 63; case '=': return 0; default: return -1; } } /** * Described in header. */ chunk_t chunk_from_base64(chunk_t base64, char *buf) { u_char *pos, byte[4]; int i, j, len, outlen; len = base64.len / 4 * 3; if (!buf) { buf = malloc(len); } pos = base64.ptr; outlen = 0; for (i = 0; i < len; i+=3) { outlen += 3; for (j = 0; j < 4; j++) { if (*pos == '=') { outlen--; } byte[j] = b642bin(*pos++); } buf[i] = (byte[0] << 2) | (byte[1] >> 4); buf[i+1] = (byte[1] << 4) | (byte[2] >> 2); buf[i+2] = (byte[2] << 6) | (byte[3]); } return chunk_create(buf, outlen); } /** base 32 conversion digits */ static char b32digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; /** * Described in header. */ chunk_t chunk_to_base32(chunk_t chunk, char *buf) { int i, len; char *pos; len = chunk.len + ((5 - chunk.len % 5) % 5); if (!buf) { buf = malloc(len * 8 / 5 + 1); } pos = buf; for (i = 0; i < len; i+=5) { *pos++ = b32digits[chunk.ptr[i] >> 3]; if (i+1 >= chunk.len) { *pos++ = b32digits[(chunk.ptr[i] & 0x07) << 2]; memset(pos, '=', 6); pos += 6; break; } *pos++ = b32digits[((chunk.ptr[i] & 0x07) << 2) | (chunk.ptr[i+1] >> 6)]; *pos++ = b32digits[(chunk.ptr[i+1] & 0x3E) >> 1]; if (i+2 >= chunk.len) { *pos++ = b32digits[(chunk.ptr[i+1] & 0x01) << 4]; memset(pos, '=', 4); pos += 4; break; } *pos++ = b32digits[((chunk.ptr[i+1] & 0x01) << 4) | (chunk.ptr[i+2] >> 4)]; if (i+3 >= chunk.len) { *pos++ = b32digits[(chunk.ptr[i+2] & 0x0F) << 1]; memset(pos, '=', 3); pos += 3; break; } *pos++ = b32digits[((chunk.ptr[i+2] & 0x0F) << 1) | (chunk.ptr[i+3] >> 7)]; *pos++ = b32digits[(chunk.ptr[i+3] & 0x7F) >> 2]; if (i+4 >= chunk.len) { *pos++ = b32digits[(chunk.ptr[i+3] & 0x03) << 3]; *pos++ = '='; break; } *pos++ = b32digits[((chunk.ptr[i+3] & 0x03) << 3) | (chunk.ptr[i+4] >> 5)]; *pos++ = b32digits[chunk.ptr[i+4] & 0x1F]; } *pos = '\0'; return chunk_create(buf, len * 8 / 5); } /** * Described in header. */ int chunk_compare(chunk_t a, chunk_t b) { int compare_len = a.len - b.len; int len = (compare_len < 0)? a.len : b.len; if (compare_len != 0 || len == 0) { return compare_len; } return memcmp(a.ptr, b.ptr, len); }; /** * Described in header. */ bool chunk_increment(chunk_t chunk) { int i; for (i = chunk.len - 1; i >= 0; i--) { if (++chunk.ptr[i] != 0) { return FALSE; } } return TRUE; } /** * Remove non-printable characters from a chunk. */ bool chunk_printable(chunk_t chunk, chunk_t *sane, char replace) { bool printable = TRUE; int i; if (sane) { *sane = chunk_clone(chunk); } for (i = 0; i < chunk.len; i++) { if (!isprint(chunk.ptr[i])) { if (sane) { sane->ptr[i] = replace; } printable = FALSE; } } return printable; } /** * Described in header. * * The implementation is based on Paul Hsieh's SuperFastHash: * http://www.azillionmonkeys.com/qed/hash.html */ u_int32_t chunk_hash_inc(chunk_t chunk, u_int32_t hash) { u_char *data = chunk.ptr; size_t len = chunk.len; u_int32_t tmp; int rem; if (!len || data == NULL) { return 0; } rem = len & 3; len >>= 2; /* Main loop */ for (; len > 0; --len) { hash += get16bits(data); tmp = (get16bits(data + 2) << 11) ^ hash; hash = (hash << 16) ^ tmp; data += 2 * sizeof(u_int16_t); hash += hash >> 11; } /* Handle end cases */ switch (rem) { case 3: { hash += get16bits(data); hash ^= hash << 16; hash ^= data[sizeof(u_int16_t)] << 18; hash += hash >> 11; break; } case 2: { hash += get16bits(data); hash ^= hash << 11; hash += hash >> 17; break; } case 1: { hash += *data; hash ^= hash << 10; hash += hash >> 1; break; } } /* Force "avalanching" of final 127 bits */ hash ^= hash << 3; hash += hash >> 5; hash ^= hash << 4; hash += hash >> 17; hash ^= hash << 25; hash += hash >> 6; return hash; } /** * Described in header. */ u_int32_t chunk_hash(chunk_t chunk) { return chunk_hash_inc(chunk, chunk.len); } /** * Described in header. */ int chunk_printf_hook(char *dst, size_t len, printf_hook_spec_t *spec, const void *const *args) { chunk_t *chunk = *((chunk_t**)(args[0])); bool first = TRUE; chunk_t copy = *chunk; int written = 0; if (!spec->hash) { const void *new_args[] = {&chunk->ptr, &chunk->len}; return mem_printf_hook(dst, len, spec, new_args); } while (copy.len > 0) { if (first) { first = FALSE; } else { written += print_in_hook(dst, len, ":"); } written += print_in_hook(dst, len, "%02x", *copy.ptr++); copy.len--; } return written; }