From af06e6a288789c2fdb673b2448bc9f11a7d8bb4b Mon Sep 17 00:00:00 2001 From: Brian Rectanus Date: Tue, 8 Sep 2009 14:52:09 -0700 Subject: [PATCH] Added byte extraction util. --- src/Makefile.am | 1 + src/eidps.c | 2 + src/util-byte.c | 542 ++++++++++++++++++++++++++++++++++++++++++++++++ src/util-byte.h | 177 ++++++++++++++++ 4 files changed, 722 insertions(+) create mode 100644 src/util-byte.c create mode 100644 src/util-byte.h diff --git a/src/Makefile.am b/src/Makefile.am index a79e32d370..20ad52eec9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -80,6 +80,7 @@ util-pool.c util-pool.h \ util-time.c util-time.h \ util-var.c util-var.h \ util-var-name.c util-var-name.h \ +util-byte.c util-byte.h \ tm-modules.c tm-modules.h \ tm-queues.c tm-queues.h \ tm-queuehandlers.c tm-queuehandlers.h \ diff --git a/src/eidps.c b/src/eidps.c index 501df48fce..9334800ee1 100644 --- a/src/eidps.c +++ b/src/eidps.c @@ -29,6 +29,7 @@ #include "util-bloomfilter.h" #include "util-bloomfilter-counting.h" #include "util-pool.h" +#include "util-byte.h" #include "detect-parse.h" #include "detect-engine.h" @@ -388,6 +389,7 @@ int main(int argc, char **argv) BloomFilterRegisterTests(); BloomFilterCountingRegisterTests(); PoolRegisterTests(); + ByteRegisterTests(); MpmRegisterTests(); FlowBitRegisterTests(); PerfRegisterTests(); diff --git a/src/util-byte.c b/src/util-byte.c new file mode 100644 index 0000000000..ba65775a9a --- /dev/null +++ b/src/util-byte.c @@ -0,0 +1,542 @@ +#include +#include +#include +#include +#include + +#include "util-byte.h" +#include "util-unittest.h" + +int ByteExtract(uint64_t *res, int e, uint16_t len, const uint8_t *bytes) +{ + uint64_t b = 0; + int i; + + if ((e != BYTE_BIG_ENDIAN) && (e != BYTE_LITTLE_ENDIAN)) { + /** \todo Need standard return values */ + return -1; + } + + *res = 0; + + /* Go through each byte and merge it into the result in the correct order */ + /** \todo Probably a more efficient way to do this. */ + for (i = 0; i < len; i++) { + + if (e == BYTE_LITTLE_ENDIAN) { + b = bytes[i]; + } + else { + b = bytes[len - i - 1]; + } + + *res |= (b << ((i & 7) << 3)); + + //printf("ByteExtractUint64: %016" PRIx64 "/%016" PRIx64 "\n", (b << ((i & 7) << 3)), *res); + } + + return 0; +} + +inline int ByteExtractUint64(uint64_t *res, int e, uint16_t len, const uint8_t *bytes) +{ + uint64_t i64; + int ret; + + /* Uint64 is limited to 8 bytes */ + if ((len < 0) || (len > 8)) { + /** \todo Need standard return values */ + return -1; + } + + ret = ByteExtract(&i64, e, len, bytes); + if (ret) { + return ret; + } + + *res = (uint64_t)i64; + + return ret; +} + +inline int ByteExtractUint32(uint32_t *res, int e, uint16_t len, const uint8_t *bytes) +{ + uint64_t i64; + int ret; + + /* Uint32 is limited to 4 bytes */ + if ((len < 0) || (len > 4)) { + /** \todo Need standard return values */ + return -1; + } + + ret = ByteExtract(&i64, e, len, bytes); + if (ret) { + return ret; + } + + *res = (uint32_t)i64; + + return ret; +} + +inline int ByteExtractUint16(uint16_t *res, int e, uint16_t len, const uint8_t *bytes) +{ + uint64_t i64; + int ret; + + /* Uint16 is limited to 2 bytes */ + if ((len < 0) || (len > 2)) { + /** \todo Need standard return values */ + return -1; + } + + ret = ByteExtract(&i64, e, len, bytes); + if (ret) { + return ret; + } + + *res = (uint16_t)i64; + + return ret; +} + +int ByteExtractString(uint64_t *res, int base, uint16_t len, const char *str) +{ + const char *ptr = str; + char *endptr = NULL; + + /* 23 - This is the largest string (octal, with a zero prefix) that + * will not overflow uint64_t. The only way this length + * could be over 23 and still not overflow is if it were zero + * prefixed and we only support 1 byte of zero prefix for octal. + * + * "01777777777777777777777" = 0xffffffffffffffff + */ + char strbuf[24]; + +//printf("ByteExtractString(%p,%d,%d,\"%s\")", res, base, len, str); + if (len > 23) { + printf("ByteExtractString: len too large (23 max)\n"); + return -1; + } + + if (len) { + /* Extract out the string so it can be null terminated */ + memcpy(strbuf, str, len); + strbuf[len] = '\0'; + ptr = strbuf; + } + + errno = 0; + *res = strtoull(ptr, &endptr, base); + + if (errno == ERANGE) { + printf("ByteExtractString: Numeric value out of range.\n"); + return -1; + } else if (endptr == str) { + printf("ByteExtractString: Invalid numeric value.\n"); + return -1; + } + /* This will interfere with some rules that do not know the length + * in advance and instead are just using the max. + */ +#if 0 + else if (len && *endptr != '\0') { + printf("ByteExtractString: Extra characters following numeric value.\n"); + return -1; + } +#endif + + //printf("ByteExtractString: Extracted base %d: 0x%" PRIx64 "\n", base, *res); + + return 0; +} + +inline int ByteExtractStringUint64(uint64_t *res, int base, uint16_t len, const char *str) +{ + return ByteExtractString(res, base, len, str); +} + +inline int ByteExtractStringUint32(uint32_t *res, int base, uint16_t len, const char *str) +{ + uint64_t i64; + int ret; + + ret = ByteExtractString(&i64, base, len, str); + if (ret != 0) { + return ret; + } + + *res = (uint32_t)i64; + + if ((uint64_t)(*res) != i64) { + printf("ByteExtractStringUint32: Numeric value out of range (%" PRIx64 " != %" PRIx64 ").", (uint64_t)(*res), i64); + return -1; + } + + return 0; +} + +inline int ByteExtractStringUint16(uint16_t *res, int base, uint16_t len, const char *str) +{ + uint64_t i64; + int ret; + + ret = ByteExtractString(&i64, base, len, str); + if (ret != 0) { + return ret; + } + + *res = (uint16_t)i64; + + if ((uint64_t)(*res) != i64) { + printf("ByteExtractStringUint16: Numeric value out of range (%" PRIx64 " != %" PRIx64 ").", (uint64_t)(*res), i64); + return -1; + } + + return 0; +} + +inline int ByteExtractStringUint8(uint8_t *res, int base, uint16_t len, const char *str) +{ + uint64_t i64; + int ret; + + ret = ByteExtractString(&i64, base, len, str); + if (ret != 0) { + return ret; + } + + *res = (uint8_t)i64; + + if ((uint64_t)(*res) != i64) { + printf("ByteExtractStringUint8: Numeric value out of range (%" PRIx64 " != %" PRIx64 ").", (uint64_t)(*res), i64); + return -1; + } + + return 0; +} + +int ByteExtractStringSigned(int64_t *res, int base, uint16_t len, const char *str) +{ + const char *ptr = str; + char *endptr; + + /* 23 - This is the largest string (octal, with a zero prefix) that + * will not overflow int64_t. The only way this length + * could be over 23 and still not overflow is if it were zero + * prefixed and we only support 1 byte of zero prefix for octal. + * + * "-0777777777777777777777" = 0xffffffffffffffff + */ + char strbuf[24]; + + if (len > 23) { + printf("ByteExtractStringSigned: len too large (23 max)\n"); + return -1; + } + + if (len) { + /* Extract out the string so it can be null terminated */ + memcpy(strbuf, str, len); + strbuf[len] = '\0'; + ptr = strbuf; + } + + errno = 0; + *res = strtoll(ptr, &endptr, base); + + if (errno == ERANGE) { + printf("ByteExtractStringSigned: Numeric value out of range.\n"); + return -1; + } else if (endptr == str) { + printf("ByteExtractStringSigned: Invalid numeric value.\n"); + return -1; + } + /* This will interfere with some rules that do not know the length + * in advance and instead are just using the max. + */ +#if 0 + else if (len && *endptr != '\0') { + printf("ByteExtractStringSigned: Extra characters following numeric value.\n"); + return -1; + } +#endif + + //printf("ByteExtractStringSigned: Extracted base %d: 0x%" PRIx64 "\n", base, *res); + + return 0; +} + +inline int ByteExtractStringInt64(int64_t *res, int base, uint16_t len, const char *str) +{ + return ByteExtractStringSigned(res, base, len, str); +} + +inline int ByteExtractStringInt32(int32_t *res, int base, uint16_t len, const char *str) +{ + int64_t i64; + int ret; + + ret = ByteExtractStringSigned(&i64, base, len, str); + if (ret != 0) { + return ret; + } + + *res = (int32_t)i64; + + if ((int64_t)(*res) != i64) { + printf("ByteExtractStringUint32: Numeric value out of range (%" PRIx64 " != %" PRIx64 ").", (int64_t)(*res), i64); + return -1; + } + + return 0; +} + +inline int ByteExtractStringInt16(int16_t *res, int base, uint16_t len, const char *str) +{ + int64_t i64; + int ret; + + ret = ByteExtractStringSigned(&i64, base, len, str); + if (ret != 0) { + return ret; + } + + *res = (int16_t)i64; + + if ((int64_t)(*res) != i64) { + printf("ByteExtractStringInt16: Numeric value out of range (%" PRIx64 " != %" PRIx64 ").", (int64_t)(*res), i64); + return -1; + } + + return 0; +} + +inline int ByteExtractStringInt8(int8_t *res, int base, uint16_t len, const char *str) +{ + int64_t i64; + int ret; + + ret = ByteExtractStringSigned(&i64, base, len, str); + if (ret != 0) { + return ret; + } + + *res = (int8_t)i64; + + if ((int64_t)(*res) != i64) { + printf("ByteExtractStringInt8: Numeric value out of range (%" PRIx64 " != %" PRIx64 ").", (int64_t)(*res), i64); + return -1; + } + + return 0; +} + +/* UNITTESTS */ +#ifdef UNITTESTS + +static int ByteTest01 (void) { + uint16_t val = 0x0102; + uint16_t i16 = 0xbfbf; + uint8_t bytes[2] = { 0x02, 0x01 }; + int ret = ByteExtractUint16(&i16, BYTE_LITTLE_ENDIAN, sizeof(bytes), bytes); + + if ((ret == 0) && (i16 == val)) { + return 1; + } + + return 0; +} + +static int ByteTest02 (void) { + uint16_t val = 0x0102; + uint16_t i16 = 0xbfbf; + uint8_t bytes[2] = { 0x01, 0x02 }; + int ret = ByteExtractUint16(&i16, BYTE_BIG_ENDIAN, sizeof(bytes), bytes); + + if ((ret == 0) && (i16 == val)) { + return 1; + } + + return 0; +} + +static int ByteTest03 (void) { + uint32_t val = 0x01020304; + uint32_t i32 = 0xbfbfbfbf; + uint8_t bytes[4] = { 0x04, 0x03, 0x02, 0x01 }; + int ret = ByteExtractUint32(&i32, BYTE_LITTLE_ENDIAN, sizeof(bytes), bytes); + + if ((ret == 0) && (i32 == val)) { + return 1; + } + + return 0; +} + +static int ByteTest04 (void) { + uint32_t val = 0x01020304; + uint32_t i32 = 0xbfbfbfbf; + uint8_t bytes[4] = { 0x01, 0x02, 0x03, 0x04 }; + int ret = ByteExtractUint32(&i32, BYTE_BIG_ENDIAN, sizeof(bytes), bytes); + + if ((ret == 0) && (i32 == val)) { + return 1; + } + + return 0; +} + +static int ByteTest05 (void) { + uint64_t val = 0x0102030405060708; + uint64_t i64 = 0xbfbfbfbfbfbfbfbf; + uint8_t bytes[8] = { 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 }; + int ret = ByteExtractUint64(&i64, BYTE_LITTLE_ENDIAN, sizeof(bytes), bytes); + + if ((ret == 0) && (i64 == val)) { + return 1; + } + + return 0; +} + +static int ByteTest06 (void) { + uint64_t val = 0x0102030405060708; + uint64_t i64 = 0xbfbfbfbfbfbfbfbf; + uint8_t bytes[8] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; + int ret = ByteExtractUint64(&i64, BYTE_BIG_ENDIAN, sizeof(bytes), bytes); + + if ((ret == 0) && (i64 == val)) { + return 1; + } + + return 0; +} + +static int ByteTest07 (void) { + const char *str = "1234567890"; + uint64_t val = 1234567890; + uint64_t i64 = 0xbfbfbfbfbfbfbfbf; + int ret = ByteExtractStringUint64(&i64, 10, strlen(str), str); + + if ((ret == 0) && (i64 == val)) { + return 1; + } + + return 0; +} + +static int ByteTest08 (void) { + const char *str = "1234567890"; + uint32_t val = 1234567890; + uint32_t i32 = 0xbfbfbfbf; + int ret = ByteExtractStringUint32(&i32, 10, strlen(str), str); + + if ((ret == 0) && (i32 == val)) { + return 1; + } + + return 0; +} + +static int ByteTest09 (void) { + const char *str = "12345"; + uint16_t val = 12345; + uint16_t i16 = 0xbfbf; + int ret = ByteExtractStringUint16(&i16, 10, strlen(str), str); + + if ((ret == 0) && (i16 == val)) { + return 1; + } + + return 0; +} + +static int ByteTest10 (void) { + const char *str = "123"; + uint8_t val = 123; + uint8_t i8 = 0xbf; + int ret = ByteExtractStringUint8(&i8, 10, strlen(str), str); + + if ((ret == 0) && (i8 == val)) { + return 1; + } + + return 0; +} + +static int ByteTest11 (void) { + const char *str = "-1234567890"; + int64_t val = -1234567890; + int64_t i64 = 0xbfbfbfbfbfbfbfbf; + int ret = ByteExtractStringInt64(&i64, 10, strlen(str), str); + + if ((ret == 0) && (i64 == val)) { + return 1; + } + + return 0; +} + +static int ByteTest12 (void) { + const char *str = "-1234567890"; + int32_t val = -1234567890; + int32_t i32 = 0xbfbfbfbf; + int ret = ByteExtractStringInt32(&i32, 10, strlen(str), str); + + if ((ret == 0) && (i32 == val)) { + return 1; + } + + return 0; +} + +static int ByteTest13 (void) { + const char *str = "-12345"; + int16_t val = -12345; + int16_t i16 = 0xbfbf; + int ret = ByteExtractStringInt16(&i16, 10, strlen(str), str); + + if ((ret == 0) && (i16 == val)) { + return 1; + } + + return 0; +} + +static int ByteTest14 (void) { + const char *str = "-123"; + int8_t val = -123; + int8_t i8 = 0xbf; + int ret = ByteExtractStringInt8(&i8, 10, strlen(str), str); + + if ((ret == 0) && (i8 == val)) { + return 1; + } + + return 0; +} +#endif /* UNITTESTS */ + +void ByteRegisterTests(void) { +#ifdef UNITTESTS + UtRegisterTest("ByteTest01", ByteTest01, 1); + UtRegisterTest("ByteTest02", ByteTest02, 1); + UtRegisterTest("ByteTest03", ByteTest03, 1); + UtRegisterTest("ByteTest04", ByteTest04, 1); + UtRegisterTest("ByteTest05", ByteTest05, 1); + UtRegisterTest("ByteTest06", ByteTest06, 1); + UtRegisterTest("ByteTest07", ByteTest07, 1); + UtRegisterTest("ByteTest08", ByteTest08, 1); + UtRegisterTest("ByteTest09", ByteTest09, 1); + UtRegisterTest("ByteTest10", ByteTest10, 1); + UtRegisterTest("ByteTest11", ByteTest11, 1); + UtRegisterTest("ByteTest12", ByteTest12, 1); + UtRegisterTest("ByteTest13", ByteTest13, 1); + UtRegisterTest("ByteTest14", ByteTest14, 1); +#endif /* UNITTESTS */ +} + + diff --git a/src/util-byte.h b/src/util-byte.h new file mode 100644 index 0000000000..982dfc306f --- /dev/null +++ b/src/util-byte.h @@ -0,0 +1,177 @@ +/** Copyright (c) 2009 Open Information Security Foundation + * + * \author Brian Rectanus + */ + +#ifndef __UTIL_BYTE_H__ +#define __UTIL_BYTE_H__ + +#include + +#define BYTE_BIG_ENDIAN 0 +#define BYTE_LITTLE_ENDIAN 1 + +/** + * Extract bytes from a byte string and convert to a unint64_t. + * + * \param res Stores result + * \param e Endianness (BYTE_BIG_ENDIAN or BYTE_LITTLE_ENDIAN) + * \param len Number of bytes to extract (8 max) + * \param bytes Data to extract from + * + * \return 0 On success + */ +int ByteExtractUint64(uint64_t *res, int e, uint16_t len, const uint8_t *bytes); + +/** + * Extract bytes from a byte string and convert to a unint32_t. + * + * \param res Stores result + * \param e Endianness (BYTE_BIG_ENDIAN or BYTE_LITTLE_ENDIAN) + * \param len Number of bytes to extract (8 max) + * \param bytes Data to extract from + * + * \return 0 On success + */ +inline int ByteExtractUint32(uint32_t *res, int e, uint16_t len, const uint8_t *bytes); + +/** + * Extract bytes from a byte string and convert to a unint16_t. + * + * \param res Stores result + * \param e Endianness (BYTE_BIG_ENDIAN or BYTE_LITTLE_ENDIAN) + * \param len Number of bytes to extract (8 max) + * \param bytes Data to extract from + * + * \return 0 On success + */ +inline int ByteExtractUint16(uint16_t *res, int e, uint16_t len, const uint8_t *bytes); + +/** + * Extract unsigned integer value from a string. + * + * \param res Stores result + * \param base Base of the number to extract + * \param len Number of bytes to extract (23 max or 0 for unbounded) + * \param str String to extract from + * + * \return 0 On success + */ +inline int ByteExtractString(uint64_t *res, int base, uint16_t len, const char *str); + +/** + * Extract unsigned integer value from a string as uint64_t. + * + * \param res Stores result + * \param base Base of the number to extract + * \param len Number of bytes to extract (23 max or 0 for unbounded) + * \param len Number of bytes to extract (23 max) + * \param str String to extract from + * + * \return 0 On success + */ +inline int ByteExtractStringUint64(uint64_t *res, int base, uint16_t len, const char *str); + +/** + * Extract unsigned integer value from a string as uint32_t. + * + * \param res Stores result + * \param base Base of the number to extract + * \param len Number of bytes to extract (23 max or 0 for unbounded) + * \param str String to extract from + * + * \return 0 On success + */ +inline int ByteExtractStringUint32(uint32_t *res, int base, uint16_t len, const char *str); + +/** + * Extract unsigned integer value from a string as uint16_t. + * + * \param res Stores result + * \param base Base of the number to extract + * \param len Number of bytes to extract (23 max or 0 for unbounded) + * \param str String to extract from + * + * \return 0 On success + */ +inline int ByteExtractStringUint16(uint16_t *res, int base, uint16_t len, const char *str); + +/** + * Extract unsigned integer value from a string as uint8_t. + * + * \param res Stores result + * \param base Base of the number to extract + * \param len Number of bytes to extract (23 max or 0 for unbounded) + * \param str String to extract from + * + * \return 0 On success + */ +inline int ByteExtractStringUint8(uint8_t *res, int base, uint16_t len, const char *str); + +/** + * Extract signed integer value from a string. + * + * \param res Stores result + * \param base Base of the number to extract + * \param len Number of bytes to extract (23 max or 0 for unbounded) + * \param str String to extract from + * + * \return 0 On success + */ +inline int ByteExtractStringSigned(int64_t *res, int base, uint16_t len, const char *str); + +/** + * Extract signed integer value from a string as uint64_t. + * + * \param res Stores result + * \param base Base of the number to extract + * \param len Number of bytes to extract (23 max or 0 for unbounded) + * \param str String to extract from + * + * \return 0 On success + */ +inline int ByteExtractStringInt64(int64_t *res, int base, uint16_t len, const char *str); + +/** + * Extract signed integer value from a string as uint32_t. + * + * \param res Stores result + * \param base Base of the number to extract + * \param len Number of bytes to extract (23 max or 0 for unbounded) + * \param str String to extract from + * + * \return 0 On success + */ +inline int ByteExtractStringInt32(int32_t *res, int base, uint16_t len, const char *str); + +/** + * Extract signed integer value from a string as uint16_t. + * + * \param res Stores result + * \param base Base of the number to extract + * \param len Number of bytes to extract (23 max or 0 for unbounded) + * \param str String to extract from + * + * \return 0 On success + */ +inline int ByteExtractStringInt16(int16_t *res, int base, uint16_t len, const char *str); + +/** + * Extract signed integer value from a string as uint8_t. + * + * \param res Stores result + * \param base Base of the number to extract + * \param len Number of bytes to extract (23 max or 0 for unbounded) + * \param str String to extract from + * + * \return 0 On success + */ +inline int ByteExtractStringInt8(int8_t *res, int base, uint16_t len, const char *str); + +#ifdef UNITTESTS +void ByteRegisterTests(void); +#endif /* UNITTESTS */ + + +#endif /* __UTIL_BYTE_H__ */ +