From 0d7773b63fa14d5de132d8503afb786a5ee4a799 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 7 Aug 2012 13:30:13 +0300 Subject: [PATCH] Add routines for encoding/decoding printf escaping mechanism This can be used to build ASCII strings from binary data that is more likely to use ASCII (i.e., text format is more natural option than hexdump, but there is possibility of some non-ASCII characters). Signed-hostap: Jouni Malinen --- src/utils/common.c | 129 ++++++++++++++++++++++++++++++++++++++++++++ src/utils/common.h | 3 ++ tests/.gitignore | 1 + tests/Makefile | 8 ++- tests/test-printf.c | 83 ++++++++++++++++++++++++++++ 5 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 tests/test-printf.c diff --git a/src/utils/common.c b/src/utils/common.c index 99861aeba..c814e89a7 100644 --- a/src/utils/common.c +++ b/src/utils/common.c @@ -344,6 +344,135 @@ TCHAR * wpa_strdup_tchar(const char *str) #endif /* CONFIG_NATIVE_WINDOWS */ +void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len) +{ + char *end = txt + maxlen; + size_t i; + + for (i = 0; i < len; i++) { + if (txt + 4 > end) + break; + + switch (data[i]) { + case '\"': + *txt++ = '\\'; + *txt++ = '\"'; + break; + case '\\': + *txt++ = '\\'; + *txt++ = '\\'; + break; + case '\e': + *txt++ = '\\'; + *txt++ = 'e'; + break; + case '\n': + *txt++ = '\\'; + *txt++ = 'n'; + break; + case '\r': + *txt++ = '\\'; + *txt++ = 'r'; + break; + case '\t': + *txt++ = '\\'; + *txt++ = 't'; + break; + default: + if (data[i] >= 32 && data[i] <= 127) { + *txt++ = data[i]; + } else { + txt += os_snprintf(txt, end - txt, "\\x%02x", + data[i]); + } + break; + } + } + + *txt = '\0'; +} + + +size_t printf_decode(u8 *buf, size_t maxlen, const char *str) +{ + const char *pos = str; + size_t len = 0; + int val; + + while (*pos) { + if (len == maxlen) + break; + switch (*pos) { + case '\\': + pos++; + switch (*pos) { + case '\\': + buf[len++] = '\\'; + pos++; + break; + case '"': + buf[len++] = '"'; + pos++; + break; + case 'n': + buf[len++] = '\n'; + pos++; + break; + case 'r': + buf[len++] = '\r'; + pos++; + break; + case 't': + buf[len++] = '\t'; + pos++; + break; + case 'e': + buf[len++] = '\e'; + pos++; + break; + case 'x': + pos++; + val = hex2byte(pos); + if (val < 0) { + val = hex2num(*pos); + if (val < 0) + break; + buf[len++] = val; + pos++; + } else { + buf[len++] = val; + pos += 2; + } + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + val = *pos++ - '0'; + if (*pos >= '0' && *pos <= '7') + val = val * 8 + (*pos++ - '0'); + if (*pos >= '0' && *pos <= '7') + val = val * 8 + (*pos++ - '0'); + buf[len++] = val; + break; + default: + break; + } + break; + default: + buf[len++] = *pos++; + break; + } + } + + return len; +} + + /** * wpa_ssid_txt - Convert SSID to a printable string * @ssid: SSID (32-octet string) diff --git a/src/utils/common.h b/src/utils/common.h index 7f115efe8..fbc61191e 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -441,6 +441,9 @@ TCHAR * wpa_strdup_tchar(const char *str); #define wpa_strdup_tchar(s) strdup((s)) #endif /* CONFIG_NATIVE_WINDOWS */ +void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len); +size_t printf_decode(u8 *buf, size_t maxlen, const char *str); + const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len); static inline int is_zero_ether_addr(const u8 *a) diff --git a/tests/.gitignore b/tests/.gitignore index 39c7447fd..bef38cfa9 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -7,6 +7,7 @@ test-md4 test-md5 test-milenage test-ms_funcs +test-printf test-rc4 test-sha1 test-sha256 diff --git a/tests/Makefile b/tests/Makefile index 0774337e7..d49633084 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,4 +1,6 @@ -TESTS=test-base64 test-md4 test-md5 test-milenage test-ms_funcs test-sha1 \ +TESTS=test-base64 test-md4 test-md5 test-milenage test-ms_funcs \ + test-printf \ + test-sha1 \ test-sha256 test-aes test-asn1 test-x509 test-x509v3 test-list test-rc4 all: $(TESTS) @@ -63,6 +65,9 @@ test-milenage: test-milenage.o $(LIBS) test-ms_funcs: test-ms_funcs.o $(LIBS) $(LDO) $(LDFLAGS) -o $@ $^ +test-printf: test-printf.o $(LIBS) + $(LDO) $(LDFLAGS) -o $@ $^ + test-rc4: test-rc4.o $(LIBS) $(LDO) $(LDFLAGS) -o $@ $^ @@ -85,6 +90,7 @@ run-tests: $(TESTS) ./test-md4 ./test-md5 ./test-milenage + ./test-printf ./test-sha1 ./test-sha256 @echo diff --git a/tests/test-printf.c b/tests/test-printf.c new file mode 100644 index 000000000..a8a2e258e --- /dev/null +++ b/tests/test-printf.c @@ -0,0 +1,83 @@ +/* + * printf format routines - test program + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include "utils/os.h" +#include "utils/common.h" + + +struct test_data { + u8 *data; + size_t len; + char *encoded; +}; + +static const struct test_data tests[] = { + { (u8 *) "abcde", 5, "abcde" }, + { (u8 *) "a\0b\nc\ed\re\tf", 11, "a\\0b\\nc\\ed\\re\\tf" }, + { (u8 *) "\x00\x31\x00\x32\x00\x39", 6, "\\x001\\0002\\09" }, + { (u8 *) "\n\n\n", 3, "\n\12\x0a" }, + { (u8 *) "\303\245\303\244\303\266\303\205\303\204\303\226", 12, + "\\xc3\\xa5\xc3\\xa4\\xc3\\xb6\\xc3\\x85\\xc3\\x84\\xc3\\x96" }, + { (u8 *) "\303\245\303\244\303\266\303\205\303\204\303\226", 12, + "\\303\\245\\303\\244\\303\\266\\303\\205\\303\\204\\303\\226" }, + { (u8 *) "\xe5\xe4\xf6\xc5\xc4\xd6", 6, + "\\xe5\\xe4\\xf6\\xc5\\xc4\\xd6" }, + { NULL, 0, NULL } +}; + + +static void print_hex(const u8 *data, size_t len) +{ + size_t i; + for (i = 0; i < len; i++) + printf(" %02x", data[i]); +} + + +int main(int argc, char *argv[]) +{ + int i; + size_t binlen; + char buf[100]; + u8 bin[100]; + int errors = 0; + + for (i = 0; tests[i].data; i++) { + const struct test_data *test = &tests[i]; + printf("%d:", i); + print_hex(test->data, test->len); + printf_encode(buf, sizeof(buf), test->data, test->len); + printf(" -> \"%s\"\n", buf); + + binlen = printf_decode(bin, sizeof(bin), buf); + if (binlen != test->len || + os_memcmp(bin, test->data, binlen) != 0) { + printf("Error in decoding#1:"); + print_hex(bin, binlen); + printf("\n"); + errors++; + } + + binlen = printf_decode(bin, sizeof(bin), test->encoded); + if (binlen != test->len || + os_memcmp(bin, test->data, binlen) != 0) { + printf("Error in decoding#2:"); + print_hex(bin, binlen); + printf("\n"); + errors++; + } + } + + if (errors) { + printf("%d test(s) failed\n", errors); + return -1; + } + + return 0; +}