Loading src/lib/encoding/.may_include +1 −0 Original line number Diff line number Diff line orconfig.h lib/cc/*.h lib/container/*.h lib/ctime/*.h lib/encoding/*.h lib/intmath/*.h Loading src/lib/encoding/include.am +2 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ src_lib_libtor_encoding_a_SOURCES = \ src/lib/encoding/confline.c \ src/lib/encoding/cstring.c \ src/lib/encoding/keyval.c \ src/lib/encoding/kvline.c \ src/lib/encoding/pem.c \ src/lib/encoding/time_fmt.c Loading @@ -22,5 +23,6 @@ noinst_HEADERS += \ src/lib/encoding/confline.h \ src/lib/encoding/cstring.h \ src/lib/encoding/keyval.h \ src/lib/encoding/kvline.h \ src/lib/encoding/pem.h \ src/lib/encoding/time_fmt.h src/lib/encoding/kvline.c 0 → 100644 +239 −0 Original line number Diff line number Diff line /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. * Copyright (c) 2007-2018, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file kvline.c * * \brief Manipulating lines of key-value pairs. **/ #include "orconfig.h" #include "lib/container/smartlist.h" #include "lib/encoding/confline.h" #include "lib/encoding/cstring.h" #include "lib/encoding/kvline.h" #include "lib/malloc/malloc.h" #include "lib/string/compat_ctype.h" #include "lib/string/printf.h" #include "lib/string/util_string.h" #include "lib/log/escape.h" #include "lib/log/util_bug.h" #include <stdbool.h> #include <stddef.h> #include <string.h> /** Return true iff we need to quote and escape the string <b>s</b> to encode * it. */ static bool needs_escape(const char *s, bool as_keyless_val) { if (as_keyless_val && *s == 0) return true; for (; *s; ++s) { if (*s >= 127 || TOR_ISSPACE(*s) || ! TOR_ISPRINT(*s) || *s == '\'' || *s == '\"') { return true; } } return false; } /** * Return true iff the key in <b>line</b> is not set. **/ static bool line_has_no_key(const config_line_t *line) { return line->key == NULL || strlen(line->key) == 0; } /** * Return true iff the all the lines in <b>line</b> can be encoded * using <b>flags</b>. **/ static bool kvline_can_encode_lines(const config_line_t *line, unsigned flags) { for ( ; line; line = line->next) { const bool keyless = line_has_no_key(line); if (keyless) { if (! (flags & KV_OMIT_KEYS)) { /* If KV_OMIT_KEYS is not set, we can't encode a line with no key. */ return false; } if (strchr(line->value, '=') && !( flags & KV_QUOTED)) { /* We can't have a keyless value with = without quoting it. */ return false; } } if (needs_escape(line->value, keyless) && ! (flags & KV_QUOTED)) { /* If KV_QUOTED is false, we can't encode a value that needs quotes. */ return false; } if (line->key && strlen(line->key) && (needs_escape(line->key, false) || strchr(line->key, '='))) { /* We can't handle keys that need quoting. */ return false; } } return true; } /** * Encode a linked list of lines in <b>line</b> as a series of 'Key=Value' * pairs, using the provided <b>flags</b> to encode it. Return a newly * allocated string on success, or NULL on failure. * * If KV_QUOTED is set in <b>flags</b>, then all values that contain * spaces or unusual characters are escaped and quoted. Otherwise, such * values are not allowed. * * If KV_OMIT_KEYS is set in <b>flags</b>, then pairs with empty keys are * allowed, and are encoded as 'Value'. Otherwise, such pairs are not * allowed. */ char * kvline_encode(const config_line_t *line, unsigned flags) { if (!kvline_can_encode_lines(line, flags)) return NULL; smartlist_t *elements = smartlist_new(); for (; line; line = line->next) { const char *k = ""; const char *eq = "="; const char *v = ""; const bool keyless = line_has_no_key(line); bool esc = needs_escape(line->value, keyless); char *tmp = NULL; if (! keyless) { k = line->key; } else { eq = ""; if (strchr(line->value, '=')) { esc = true; } } if (esc) { tmp = esc_for_log(line->value); v = tmp; } else { v = line->value; } smartlist_add_asprintf(elements, "%s%s%s", k, eq, v); tor_free(tmp); } char *result = smartlist_join_strings(elements, " ", 0, NULL); SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp)); smartlist_free(elements); return result; } /** * Decode a <b>line</b> containing a series of space-separated 'Key=Value' * pairs, using the provided <b>flags</b> to decode it. Return a newly * allocated list of pairs on success, or NULL on failure. * * If KV_QUOTED is set in <b>flags</b>, then (double-)quoted values are * allowed. Otherwise, such values are not allowed. * * If KV_OMIT_KEYS is set in <b>flags</b>, then values without keys are * allowed. Otherwise, such values are not allowed. */ config_line_t * kvline_parse(const char *line, unsigned flags) { const char *cp = line, *cplast = NULL; bool omit_keys = (flags & KV_OMIT_KEYS) != 0; bool quoted = (flags & KV_QUOTED) != 0; config_line_t *result = NULL; config_line_t **next_line = &result; char *key = NULL; char *val = NULL; while (*cp) { key = val = NULL; { size_t idx = strspn(cp, " \t\r\v\n"); cp += idx; } if (BUG(cp == cplast)) { /* If we didn't parse anything, this code is broken. */ goto err; // LCOV_EXCL_LINE } cplast = cp; if (! *cp) break; /* End of string; we're done. */ /* Possible formats are K=V, K="V", V, and "V", depending on flags. */ /* Find the key. */ if (*cp != '\"') { size_t idx = strcspn(cp, " \t\r\v\n="); if (cp[idx] == '=') { key = tor_memdup_nulterm(cp, idx); cp += idx + 1; } else { if (!omit_keys) goto err; } } if (*cp == '\"') { /* The type is "V". */ if (!quoted) goto err; size_t len=0; cp = unescape_string(cp, &val, &len); if (cp == NULL || len != strlen(val)) { // The string contains a NUL or is badly coded. goto err; } } else { size_t idx = strcspn(cp, " \t\r\v\n"); val = tor_memdup_nulterm(cp, idx); cp += idx; } if (key && strlen(key) == 0) { /* We don't allow empty keys. */ goto err; } *next_line = tor_malloc_zero(sizeof(config_line_t)); (*next_line)->key = key ? key : tor_strdup(""); (*next_line)->value = val; next_line = &(*next_line)->next; key = val = NULL; } if (!kvline_can_encode_lines(result, flags)) { goto err; } return result; err: tor_free(key); tor_free(val); config_free_lines(result); return NULL; } src/lib/encoding/kvline.h 0 → 100644 +24 −0 Original line number Diff line number Diff line /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. * Copyright (c) 2007-2018, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file kvline.h * * \brief Header for kvline.c **/ #ifndef TOR_KVLINE_H #define TOR_KVLINE_H struct config_line_t; #define KV_QUOTED (1u<<0) #define KV_OMIT_KEYS (1u<<1) struct config_line_t *kvline_parse(const char *line, unsigned flags); char *kvline_encode(const struct config_line_t *line, unsigned flags); #endif /* !defined(TOR_KVLINE_H) */ src/test/test_config.c +78 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ #include "lib/meminfo/meminfo.h" #include "lib/net/gethostname.h" #include "lib/encoding/confline.h" #include "lib/encoding/kvline.h" #ifdef HAVE_UNISTD_H #include <unistd.h> Loading Loading @@ -5813,6 +5814,82 @@ test_config_extended_fmt(void *arg) config_free_lines(lines); } static void test_config_kvline_parse(void *arg) { (void)arg; config_line_t *lines = NULL; char *enc = NULL; lines = kvline_parse("A=B CD=EF", 0); tt_assert(lines); tt_str_op(lines->key, OP_EQ, "A"); tt_str_op(lines->value, OP_EQ, "B"); tt_str_op(lines->next->key, OP_EQ, "CD"); tt_str_op(lines->next->value, OP_EQ, "EF"); enc = kvline_encode(lines, 0); tt_str_op(enc, OP_EQ, "A=B CD=EF"); tor_free(enc); enc = kvline_encode(lines, KV_QUOTED|KV_OMIT_KEYS); tt_str_op(enc, OP_EQ, "A=B CD=EF"); tor_free(enc); config_free_lines(lines); lines = kvline_parse("AB CDE=F", 0); tt_assert(! lines); lines = kvline_parse("AB CDE=F", KV_OMIT_KEYS); tt_assert(lines); tt_str_op(lines->key, OP_EQ, ""); tt_str_op(lines->value, OP_EQ, "AB"); tt_str_op(lines->next->key, OP_EQ, "CDE"); tt_str_op(lines->next->value, OP_EQ, "F"); tt_assert(lines); enc = kvline_encode(lines, 0); tt_assert(!enc); enc = kvline_encode(lines, KV_QUOTED|KV_OMIT_KEYS); tt_str_op(enc, OP_EQ, "AB CDE=F"); tor_free(enc); config_free_lines(lines); lines = kvline_parse("AB=C CDE=\"F G\"", 0); tt_assert(!lines); lines = kvline_parse("AB=C CDE=\"F G\" \"GHI\" ", KV_QUOTED|KV_OMIT_KEYS); tt_assert(lines); tt_str_op(lines->key, OP_EQ, "AB"); tt_str_op(lines->value, OP_EQ, "C"); tt_str_op(lines->next->key, OP_EQ, "CDE"); tt_str_op(lines->next->value, OP_EQ, "F G"); tt_str_op(lines->next->next->key, OP_EQ, ""); tt_str_op(lines->next->next->value, OP_EQ, "GHI"); enc = kvline_encode(lines, 0); tt_assert(!enc); enc = kvline_encode(lines, KV_QUOTED|KV_OMIT_KEYS); tt_str_op(enc, OP_EQ, "AB=C CDE=\"F G\" GHI"); tor_free(enc); config_free_lines(lines); lines = kvline_parse("A\"B=C CDE=\"F\" \"GHI\" ", KV_QUOTED|KV_OMIT_KEYS); tt_assert(! lines); lines = kvline_parse("AB=", KV_QUOTED); tt_assert(lines); tt_str_op(lines->key, OP_EQ, "AB"); tt_str_op(lines->value, OP_EQ, ""); config_free_lines(lines); lines = kvline_parse("AB=", 0); tt_assert(lines); tt_str_op(lines->key, OP_EQ, "AB"); tt_str_op(lines->value, OP_EQ, ""); done: config_free_lines(lines); tor_free(enc); } #define CONFIG_TEST(name, flags) \ { #name, test_config_ ## name, flags, NULL, NULL } Loading Loading @@ -5864,5 +5941,6 @@ struct testcase_t config_tests[] = { CONFIG_TEST(include_opened_file_list, 0), CONFIG_TEST(compute_max_mem_in_queues, 0), CONFIG_TEST(extended_fmt, 0), CONFIG_TEST(kvline_parse, 0), END_OF_TESTCASES }; Loading
src/lib/encoding/.may_include +1 −0 Original line number Diff line number Diff line orconfig.h lib/cc/*.h lib/container/*.h lib/ctime/*.h lib/encoding/*.h lib/intmath/*.h Loading
src/lib/encoding/include.am +2 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ src_lib_libtor_encoding_a_SOURCES = \ src/lib/encoding/confline.c \ src/lib/encoding/cstring.c \ src/lib/encoding/keyval.c \ src/lib/encoding/kvline.c \ src/lib/encoding/pem.c \ src/lib/encoding/time_fmt.c Loading @@ -22,5 +23,6 @@ noinst_HEADERS += \ src/lib/encoding/confline.h \ src/lib/encoding/cstring.h \ src/lib/encoding/keyval.h \ src/lib/encoding/kvline.h \ src/lib/encoding/pem.h \ src/lib/encoding/time_fmt.h
src/lib/encoding/kvline.c 0 → 100644 +239 −0 Original line number Diff line number Diff line /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. * Copyright (c) 2007-2018, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file kvline.c * * \brief Manipulating lines of key-value pairs. **/ #include "orconfig.h" #include "lib/container/smartlist.h" #include "lib/encoding/confline.h" #include "lib/encoding/cstring.h" #include "lib/encoding/kvline.h" #include "lib/malloc/malloc.h" #include "lib/string/compat_ctype.h" #include "lib/string/printf.h" #include "lib/string/util_string.h" #include "lib/log/escape.h" #include "lib/log/util_bug.h" #include <stdbool.h> #include <stddef.h> #include <string.h> /** Return true iff we need to quote and escape the string <b>s</b> to encode * it. */ static bool needs_escape(const char *s, bool as_keyless_val) { if (as_keyless_val && *s == 0) return true; for (; *s; ++s) { if (*s >= 127 || TOR_ISSPACE(*s) || ! TOR_ISPRINT(*s) || *s == '\'' || *s == '\"') { return true; } } return false; } /** * Return true iff the key in <b>line</b> is not set. **/ static bool line_has_no_key(const config_line_t *line) { return line->key == NULL || strlen(line->key) == 0; } /** * Return true iff the all the lines in <b>line</b> can be encoded * using <b>flags</b>. **/ static bool kvline_can_encode_lines(const config_line_t *line, unsigned flags) { for ( ; line; line = line->next) { const bool keyless = line_has_no_key(line); if (keyless) { if (! (flags & KV_OMIT_KEYS)) { /* If KV_OMIT_KEYS is not set, we can't encode a line with no key. */ return false; } if (strchr(line->value, '=') && !( flags & KV_QUOTED)) { /* We can't have a keyless value with = without quoting it. */ return false; } } if (needs_escape(line->value, keyless) && ! (flags & KV_QUOTED)) { /* If KV_QUOTED is false, we can't encode a value that needs quotes. */ return false; } if (line->key && strlen(line->key) && (needs_escape(line->key, false) || strchr(line->key, '='))) { /* We can't handle keys that need quoting. */ return false; } } return true; } /** * Encode a linked list of lines in <b>line</b> as a series of 'Key=Value' * pairs, using the provided <b>flags</b> to encode it. Return a newly * allocated string on success, or NULL on failure. * * If KV_QUOTED is set in <b>flags</b>, then all values that contain * spaces or unusual characters are escaped and quoted. Otherwise, such * values are not allowed. * * If KV_OMIT_KEYS is set in <b>flags</b>, then pairs with empty keys are * allowed, and are encoded as 'Value'. Otherwise, such pairs are not * allowed. */ char * kvline_encode(const config_line_t *line, unsigned flags) { if (!kvline_can_encode_lines(line, flags)) return NULL; smartlist_t *elements = smartlist_new(); for (; line; line = line->next) { const char *k = ""; const char *eq = "="; const char *v = ""; const bool keyless = line_has_no_key(line); bool esc = needs_escape(line->value, keyless); char *tmp = NULL; if (! keyless) { k = line->key; } else { eq = ""; if (strchr(line->value, '=')) { esc = true; } } if (esc) { tmp = esc_for_log(line->value); v = tmp; } else { v = line->value; } smartlist_add_asprintf(elements, "%s%s%s", k, eq, v); tor_free(tmp); } char *result = smartlist_join_strings(elements, " ", 0, NULL); SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp)); smartlist_free(elements); return result; } /** * Decode a <b>line</b> containing a series of space-separated 'Key=Value' * pairs, using the provided <b>flags</b> to decode it. Return a newly * allocated list of pairs on success, or NULL on failure. * * If KV_QUOTED is set in <b>flags</b>, then (double-)quoted values are * allowed. Otherwise, such values are not allowed. * * If KV_OMIT_KEYS is set in <b>flags</b>, then values without keys are * allowed. Otherwise, such values are not allowed. */ config_line_t * kvline_parse(const char *line, unsigned flags) { const char *cp = line, *cplast = NULL; bool omit_keys = (flags & KV_OMIT_KEYS) != 0; bool quoted = (flags & KV_QUOTED) != 0; config_line_t *result = NULL; config_line_t **next_line = &result; char *key = NULL; char *val = NULL; while (*cp) { key = val = NULL; { size_t idx = strspn(cp, " \t\r\v\n"); cp += idx; } if (BUG(cp == cplast)) { /* If we didn't parse anything, this code is broken. */ goto err; // LCOV_EXCL_LINE } cplast = cp; if (! *cp) break; /* End of string; we're done. */ /* Possible formats are K=V, K="V", V, and "V", depending on flags. */ /* Find the key. */ if (*cp != '\"') { size_t idx = strcspn(cp, " \t\r\v\n="); if (cp[idx] == '=') { key = tor_memdup_nulterm(cp, idx); cp += idx + 1; } else { if (!omit_keys) goto err; } } if (*cp == '\"') { /* The type is "V". */ if (!quoted) goto err; size_t len=0; cp = unescape_string(cp, &val, &len); if (cp == NULL || len != strlen(val)) { // The string contains a NUL or is badly coded. goto err; } } else { size_t idx = strcspn(cp, " \t\r\v\n"); val = tor_memdup_nulterm(cp, idx); cp += idx; } if (key && strlen(key) == 0) { /* We don't allow empty keys. */ goto err; } *next_line = tor_malloc_zero(sizeof(config_line_t)); (*next_line)->key = key ? key : tor_strdup(""); (*next_line)->value = val; next_line = &(*next_line)->next; key = val = NULL; } if (!kvline_can_encode_lines(result, flags)) { goto err; } return result; err: tor_free(key); tor_free(val); config_free_lines(result); return NULL; }
src/lib/encoding/kvline.h 0 → 100644 +24 −0 Original line number Diff line number Diff line /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. * Copyright (c) 2007-2018, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file kvline.h * * \brief Header for kvline.c **/ #ifndef TOR_KVLINE_H #define TOR_KVLINE_H struct config_line_t; #define KV_QUOTED (1u<<0) #define KV_OMIT_KEYS (1u<<1) struct config_line_t *kvline_parse(const char *line, unsigned flags); char *kvline_encode(const struct config_line_t *line, unsigned flags); #endif /* !defined(TOR_KVLINE_H) */
src/test/test_config.c +78 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ #include "lib/meminfo/meminfo.h" #include "lib/net/gethostname.h" #include "lib/encoding/confline.h" #include "lib/encoding/kvline.h" #ifdef HAVE_UNISTD_H #include <unistd.h> Loading Loading @@ -5813,6 +5814,82 @@ test_config_extended_fmt(void *arg) config_free_lines(lines); } static void test_config_kvline_parse(void *arg) { (void)arg; config_line_t *lines = NULL; char *enc = NULL; lines = kvline_parse("A=B CD=EF", 0); tt_assert(lines); tt_str_op(lines->key, OP_EQ, "A"); tt_str_op(lines->value, OP_EQ, "B"); tt_str_op(lines->next->key, OP_EQ, "CD"); tt_str_op(lines->next->value, OP_EQ, "EF"); enc = kvline_encode(lines, 0); tt_str_op(enc, OP_EQ, "A=B CD=EF"); tor_free(enc); enc = kvline_encode(lines, KV_QUOTED|KV_OMIT_KEYS); tt_str_op(enc, OP_EQ, "A=B CD=EF"); tor_free(enc); config_free_lines(lines); lines = kvline_parse("AB CDE=F", 0); tt_assert(! lines); lines = kvline_parse("AB CDE=F", KV_OMIT_KEYS); tt_assert(lines); tt_str_op(lines->key, OP_EQ, ""); tt_str_op(lines->value, OP_EQ, "AB"); tt_str_op(lines->next->key, OP_EQ, "CDE"); tt_str_op(lines->next->value, OP_EQ, "F"); tt_assert(lines); enc = kvline_encode(lines, 0); tt_assert(!enc); enc = kvline_encode(lines, KV_QUOTED|KV_OMIT_KEYS); tt_str_op(enc, OP_EQ, "AB CDE=F"); tor_free(enc); config_free_lines(lines); lines = kvline_parse("AB=C CDE=\"F G\"", 0); tt_assert(!lines); lines = kvline_parse("AB=C CDE=\"F G\" \"GHI\" ", KV_QUOTED|KV_OMIT_KEYS); tt_assert(lines); tt_str_op(lines->key, OP_EQ, "AB"); tt_str_op(lines->value, OP_EQ, "C"); tt_str_op(lines->next->key, OP_EQ, "CDE"); tt_str_op(lines->next->value, OP_EQ, "F G"); tt_str_op(lines->next->next->key, OP_EQ, ""); tt_str_op(lines->next->next->value, OP_EQ, "GHI"); enc = kvline_encode(lines, 0); tt_assert(!enc); enc = kvline_encode(lines, KV_QUOTED|KV_OMIT_KEYS); tt_str_op(enc, OP_EQ, "AB=C CDE=\"F G\" GHI"); tor_free(enc); config_free_lines(lines); lines = kvline_parse("A\"B=C CDE=\"F\" \"GHI\" ", KV_QUOTED|KV_OMIT_KEYS); tt_assert(! lines); lines = kvline_parse("AB=", KV_QUOTED); tt_assert(lines); tt_str_op(lines->key, OP_EQ, "AB"); tt_str_op(lines->value, OP_EQ, ""); config_free_lines(lines); lines = kvline_parse("AB=", 0); tt_assert(lines); tt_str_op(lines->key, OP_EQ, "AB"); tt_str_op(lines->value, OP_EQ, ""); done: config_free_lines(lines); tor_free(enc); } #define CONFIG_TEST(name, flags) \ { #name, test_config_ ## name, flags, NULL, NULL } Loading Loading @@ -5864,5 +5941,6 @@ struct testcase_t config_tests[] = { CONFIG_TEST(include_opened_file_list, 0), CONFIG_TEST(compute_max_mem_in_queues, 0), CONFIG_TEST(extended_fmt, 0), CONFIG_TEST(kvline_parse, 0), END_OF_TESTCASES };