aboutsummaryrefslogtreecommitdiff
path: root/src/lib/encoding
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/encoding')
-rw-r--r--src/lib/encoding/.may_include1
-rw-r--r--src/lib/encoding/binascii.c26
-rw-r--r--src/lib/encoding/binascii.h5
-rw-r--r--src/lib/encoding/confline.c51
-rw-r--r--src/lib/encoding/confline.h7
-rw-r--r--src/lib/encoding/cstring.c2
-rw-r--r--src/lib/encoding/cstring.h2
-rw-r--r--src/lib/encoding/include.am6
-rw-r--r--src/lib/encoding/keyval.c2
-rw-r--r--src/lib/encoding/keyval.h4
-rw-r--r--src/lib/encoding/kvline.c297
-rw-r--r--src/lib/encoding/kvline.h27
-rw-r--r--src/lib/encoding/lib_encoding.md6
-rw-r--r--src/lib/encoding/pem.c4
-rw-r--r--src/lib/encoding/pem.h4
-rw-r--r--src/lib/encoding/qstring.c90
-rw-r--r--src/lib/encoding/qstring.h18
-rw-r--r--src/lib/encoding/time_fmt.c37
-rw-r--r--src/lib/encoding/time_fmt.h8
19 files changed, 569 insertions, 28 deletions
diff --git a/src/lib/encoding/.may_include b/src/lib/encoding/.may_include
index 7c2ef36929..c9bf4b1786 100644
--- a/src/lib/encoding/.may_include
+++ b/src/lib/encoding/.may_include
@@ -1,5 +1,6 @@
orconfig.h
lib/cc/*.h
+lib/container/*.h
lib/ctime/*.h
lib/encoding/*.h
lib/intmath/*.h
diff --git a/src/lib/encoding/binascii.c b/src/lib/encoding/binascii.c
index bd063440d6..5f68da183f 100644
--- a/src/lib/encoding/binascii.c
+++ b/src/lib/encoding/binascii.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001, Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -84,7 +84,7 @@ base32_encode(char *dest, size_t destlen, const char *src, size_t srclen)
}
/** Implements base32 decoding as in RFC 4648.
- * Returns 0 if successful, -1 otherwise.
+ * Return the number of bytes decoded if successful; -1 otherwise.
*/
int
base32_decode(char *dest, size_t destlen, const char *src, size_t srclen)
@@ -147,7 +147,7 @@ base32_decode(char *dest, size_t destlen, const char *src, size_t srclen)
memset(tmp, 0, srclen); /* on the heap, this should be safe */
tor_free(tmp);
tmp = NULL;
- return 0;
+ return i;
}
#define BASE64_OPENSSL_LINELEN 64
@@ -179,6 +179,18 @@ base64_encode_size(size_t srclen, int flags)
return enclen;
}
+/** Return an upper bound on the number of bytes that might be needed to hold
+ * the data from decoding the base64 string <b>srclen</b>. This is only an
+ * upper bound, since some part of the base64 string might be padding or
+ * space. */
+size_t
+base64_decode_maxsize(size_t srclen)
+{
+ tor_assert(srclen < INT_MAX / 3);
+
+ return CEIL_DIV(srclen * 3, 4);
+}
+
/** Internal table mapping 6 bit values to the Base64 alphabet. */
static const char base64_encode_table[64] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
@@ -263,7 +275,7 @@ base64_encode(char *dest, size_t destlen, const char *src, size_t srclen,
}
switch (n_idx) {
case 0:
- /* 0 leftover bits, no pading to add. */
+ /* 0 leftover bits, no padding to add. */
break;
case 1:
/* 8 leftover bits, pad to 12 bits, write the 2 6-bit values followed
@@ -309,8 +321,10 @@ base64_encode(char *dest, size_t destlen, const char *src, size_t srclen,
return (int) enclen;
}
-/** As base64_encode, but do not add any internal spaces or external padding
- * to the output stream. */
+/** As base64_encode, but do not add any internal spaces, and remove external
+ * padding from the output stream.
+ * dest must be at least base64_encode_size(srclen, 0), including space for
+ * the removed external padding. */
int
base64_encode_nopad(char *dest, size_t destlen,
const uint8_t *src, size_t srclen)
diff --git a/src/lib/encoding/binascii.h b/src/lib/encoding/binascii.h
index 7e3cc04f09..9cb03bab62 100644
--- a/src/lib/encoding/binascii.h
+++ b/src/lib/encoding/binascii.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001, Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -42,6 +42,7 @@ const char *hex_str(const char *from, size_t fromlen);
#define BASE64_ENCODE_MULTILINE 1
size_t base64_encode_size(size_t srclen, int flags);
+size_t base64_decode_maxsize(size_t srclen);
int base64_encode(char *dest, size_t destlen, const char *src, size_t srclen,
int flags);
int base64_decode(char *dest, size_t destlen, const char *src, size_t srclen);
@@ -57,4 +58,4 @@ size_t base32_encoded_size(size_t srclen);
void base16_encode(char *dest, size_t destlen, const char *src, size_t srclen);
int base16_decode(char *dest, size_t destlen, const char *src, size_t srclen);
-#endif /* !defined(TOR_UTIL_FORMAT_H) */
+#endif /* !defined(TOR_BINASCII_H) */
diff --git a/src/lib/encoding/confline.c b/src/lib/encoding/confline.c
index 8110f3dd9c..613e4a00c6 100644
--- a/src/lib/encoding/confline.c
+++ b/src/lib/encoding/confline.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -82,6 +82,19 @@ config_line_find(const config_line_t *lines,
return NULL;
}
+/** As config_line_find(), but perform a case-insensitive comparison. */
+const config_line_t *
+config_line_find_case(const config_line_t *lines,
+ const char *key)
+{
+ const config_line_t *cl;
+ for (cl = lines; cl; cl = cl->next) {
+ if (!strcasecmp(cl->key, key))
+ return cl;
+ }
+ return NULL;
+}
+
/** Auxiliary function that does all the work of config_get_lines.
* <b>recursion_level</b> is the count of how many nested %includes we have.
* <b>opened_lst</b> will have a list of opened files if provided.
@@ -138,6 +151,8 @@ config_get_lines_aux(const char *string, config_line_t **result, int extended,
if (allow_include && !strcmp(k, "%include") && handle_include) {
tor_free(k);
include_used = 1;
+ log_notice(LD_CONFIG, "Processing configuration path \"%s\" at "
+ "recursion level %d.", v, recursion_level);
config_line_t *include_list;
if (handle_include(v, recursion_level, extended, &include_list,
@@ -148,9 +163,6 @@ config_get_lines_aux(const char *string, config_line_t **result, int extended,
tor_free(v);
return -1;
}
- log_notice(LD_CONFIG, "Included configuration file or "
- "directory at recursion level %d: \"%s\".",
- recursion_level, v);
*next = include_list;
if (list_last)
next = &list_last->next;
@@ -240,10 +252,39 @@ config_lines_dup_and_filter(const config_line_t *inp,
return result;
}
+/**
+ * Given a linelist <b>inp</b> beginning with the key <b>header</b>, find the
+ * next line with that key, and remove that instance and all following lines
+ * from the list. Return the lines that were removed. Operate
+ * case-insensitively.
+ *
+ * For example, if the header is "H", and <b>inp</b> contains "H, A, B, H, C,
+ * H, D", this function will alter <b>inp</b> to contain only "H, A, B", and
+ * return the elements "H, C, H, D" as a separate list.
+ **/
+config_line_t *
+config_lines_partition(config_line_t *inp, const char *header)
+{
+ if (BUG(inp == NULL))
+ return NULL;
+ if (BUG(strcasecmp(inp->key, header)))
+ return NULL;
+
+ /* Advance ptr until it points to the link to the next segment of this
+ list. */
+ config_line_t **ptr = &inp->next;
+ while (*ptr && strcasecmp((*ptr)->key, header)) {
+ ptr = &(*ptr)->next;
+ }
+ config_line_t *remainder = *ptr;
+ *ptr = NULL;
+ return remainder;
+}
+
/** Return true iff a and b contain identical keys and values in identical
* order. */
int
-config_lines_eq(config_line_t *a, config_line_t *b)
+config_lines_eq(const config_line_t *a, const config_line_t *b)
{
while (a && b) {
if (strcasecmp(a->key, b->key) || strcmp(a->value, b->value))
diff --git a/src/lib/encoding/confline.h b/src/lib/encoding/confline.h
index 3d9ae8a662..ce0d6c6e17 100644
--- a/src/lib/encoding/confline.h
+++ b/src/lib/encoding/confline.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -48,7 +48,10 @@ config_line_t *config_lines_dup_and_filter(const config_line_t *inp,
const char *key);
const config_line_t *config_line_find(const config_line_t *lines,
const char *key);
-int config_lines_eq(config_line_t *a, config_line_t *b);
+const config_line_t *config_line_find_case(const config_line_t *lines,
+ const char *key);
+config_line_t *config_lines_partition(config_line_t *inp, const char *header);
+int config_lines_eq(const config_line_t *a, const config_line_t *b);
int config_count_key(const config_line_t *a, const char *key);
void config_free_lines_(config_line_t *front);
#define config_free_lines(front) \
diff --git a/src/lib/encoding/cstring.c b/src/lib/encoding/cstring.c
index 29d3714126..54c330fca3 100644
--- a/src/lib/encoding/cstring.c
+++ b/src/lib/encoding/cstring.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/lib/encoding/cstring.h b/src/lib/encoding/cstring.h
index 904a2c9c1c..2a3f6d0fc4 100644
--- a/src/lib/encoding/cstring.h
+++ b/src/lib/encoding/cstring.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/lib/encoding/include.am b/src/lib/encoding/include.am
index 2d2aa3988a..48d0120bfc 100644
--- a/src/lib/encoding/include.am
+++ b/src/lib/encoding/include.am
@@ -4,12 +4,15 @@ if UNITTESTS_ENABLED
noinst_LIBRARIES += src/lib/libtor-encoding-testing.a
endif
+# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_encoding_a_SOURCES = \
src/lib/encoding/binascii.c \
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/qstring.c \
src/lib/encoding/time_fmt.c
src_lib_libtor_encoding_testing_a_SOURCES = \
@@ -17,10 +20,13 @@ src_lib_libtor_encoding_testing_a_SOURCES = \
src_lib_libtor_encoding_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_lib_libtor_encoding_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
src/lib/encoding/binascii.h \
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/qstring.h \
src/lib/encoding/time_fmt.h
diff --git a/src/lib/encoding/keyval.c b/src/lib/encoding/keyval.c
index c5da5a0bfc..0eb1219d43 100644
--- a/src/lib/encoding/keyval.c
+++ b/src/lib/encoding/keyval.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2003, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/lib/encoding/keyval.h b/src/lib/encoding/keyval.h
index cd327b7a82..b4966b01de 100644
--- a/src/lib/encoding/keyval.h
+++ b/src/lib/encoding/keyval.h
@@ -1,6 +1,6 @@
/* Copyright (c) 2003, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -14,4 +14,4 @@
int string_is_key_value(int severity, const char *string);
-#endif
+#endif /* !defined(TOR_KEYVAL_H) */
diff --git a/src/lib/encoding/kvline.c b/src/lib/encoding/kvline.c
new file mode 100644
index 0000000000..5b220605d6
--- /dev/null
+++ b/src/lib/encoding/kvline.c
@@ -0,0 +1,297 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, 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/encoding/qstring.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.
+ *
+ * kvline_can_encode_lines() also uses this (with
+ * <b>as_keyless_val</b> true) to check whether a key would require
+ * quoting.
+ */
+static bool
+needs_escape(const char *s, bool as_keyless_val)
+{
+ if (as_keyless_val && *s == 0)
+ return true;
+ /* Keyless values containing '=' need to be escaped. */
+ if (as_keyless_val && strchr(s, '='))
+ 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 value in <b>line</b> is not set.
+ **/
+static bool
+line_has_no_val(const config_line_t *line)
+{
+ return line->value == NULL || strlen(line->value) == 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 && ! (flags & KV_OMIT_KEYS)) {
+ /* If KV_OMIT_KEYS is not set, we can't encode a line with no key. */
+ return false;
+ }
+
+ if (needs_escape(line->value, keyless) && ! (flags & (KV_QUOTED|KV_RAW))) {
+ /* If both KV_QUOTED and KV_RAW are false, we can't encode a
+ value that needs quotes. */
+ return false;
+ }
+ if (!keyless && needs_escape(line->key, true)) {
+ /* 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. Mutually exclusive with KV_RAW.
+ *
+ * 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.
+ *
+ * If KV_OMIT_VALS is set in <b>flags</b>, then an empty value is
+ * encoded as 'Key', not as 'Key=' or 'Key=""'. Mutually exclusive with
+ * KV_OMIT_KEYS.
+ *
+ * If KV_RAW is set in <b>flags</b>, then don't apply any quoting to
+ * the value, and assume that the caller has adequately quoted it.
+ * (The control protocol has some quirks that make this necessary.)
+ * Mutually exclusive with KV_QUOTED.
+ *
+ * KV_QUOTED_QSTRING is not supported.
+ */
+char *
+kvline_encode(const config_line_t *line,
+ unsigned flags)
+{
+ tor_assert(! (flags & KV_QUOTED_QSTRING));
+
+ tor_assert((flags & (KV_OMIT_KEYS|KV_OMIT_VALS)) !=
+ (KV_OMIT_KEYS|KV_OMIT_VALS));
+ tor_assert((flags & (KV_QUOTED|KV_RAW)) != (KV_QUOTED|KV_RAW));
+
+ 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 ((flags & KV_OMIT_VALS) && line_has_no_val(line)) {
+ eq = "";
+ v = "";
+ } else if (!(flags & KV_RAW) && 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 and handled as C strings. 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.
+ *
+ * If KV_OMIT_VALS is set in <b>flags</b>, then keys without values are
+ * allowed. Otherwise, such keys are not allowed. Mutually exclusive with
+ * KV_OMIT_KEYS.
+ *
+ * If KV_QUOTED_QSTRING is set in <b>flags</b>, then double-quoted values
+ * are allowed and handled as QuotedStrings per qstring.c. Do not add
+ * new users of this flag.
+ *
+ * KV_RAW is not supported.
+ */
+config_line_t *
+kvline_parse(const char *line, unsigned flags)
+{
+ tor_assert((flags & (KV_OMIT_KEYS|KV_OMIT_VALS)) !=
+ (KV_OMIT_KEYS|KV_OMIT_VALS));
+ tor_assert(!(flags & KV_RAW));
+
+ const char *cp = line, *cplast = NULL;
+ const bool omit_keys = (flags & KV_OMIT_KEYS) != 0;
+ const bool omit_vals = (flags & KV_OMIT_VALS) != 0;
+ const bool quoted = (flags & (KV_QUOTED|KV_QUOTED_QSTRING)) != 0;
+ const bool c_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;
+ /* skip all spaces */
+ {
+ size_t idx = strspn(cp, " \t\r\v\n");
+ cp += idx;
+ }
+ if (BUG(cp == cplast)) {
+ /* If we didn't parse anything since the last loop, 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", K, V, and "V", depending on flags. */
+
+ /* Find where the key ends */
+ 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_vals) {
+ key = tor_memdup_nulterm(cp, idx);
+ cp += idx;
+ goto commit;
+ } else {
+ if (!omit_keys)
+ goto err;
+ }
+ }
+
+ if (*cp == '\"') {
+ /* The type is "V". */
+ if (!quoted)
+ goto err;
+ size_t len=0;
+ if (c_quoted) {
+ cp = unescape_string(cp, &val, &len);
+ } else {
+ cp = decode_qstring(cp, strlen(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;
+ }
+
+ commit:
+ 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 ? val : tor_strdup("");
+ next_line = &(*next_line)->next;
+ key = val = NULL;
+ }
+
+ if (! (flags & KV_QUOTED_QSTRING)) {
+ if (!kvline_can_encode_lines(result, flags)) {
+ goto err;
+ }
+ }
+ return result;
+
+ err:
+ tor_free(key);
+ tor_free(val);
+ config_free_lines(result);
+ return NULL;
+}
diff --git a/src/lib/encoding/kvline.h b/src/lib/encoding/kvline.h
new file mode 100644
index 0000000000..34c52908e3
--- /dev/null
+++ b/src/lib/encoding/kvline.h
@@ -0,0 +1,27 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, 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)
+#define KV_OMIT_VALS (1u<<2)
+#define KV_QUOTED_QSTRING (1u<<3)
+#define KV_RAW (1u<<4)
+
+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) */
diff --git a/src/lib/encoding/lib_encoding.md b/src/lib/encoding/lib_encoding.md
new file mode 100644
index 0000000000..66dd9d8caf
--- /dev/null
+++ b/src/lib/encoding/lib_encoding.md
@@ -0,0 +1,6 @@
+@dir /lib/encoding
+@brief lib/encoding: Encoding data in various forms, types, and transformations
+
+Here we have time formats (timefmt.c), quoted strings (qstring.c), C strings
+(string.c) base-16/32/64 (binascii.c), and more.
+
diff --git a/src/lib/encoding/pem.c b/src/lib/encoding/pem.c
index 51f37d0840..6c9f10e085 100644
--- a/src/lib/encoding/pem.c
+++ b/src/lib/encoding/pem.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001, Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -42,7 +42,7 @@ pem_encoded_size(size_t src_len, const char *objtype)
/**
* PEM-encode the <b>srclen</b>-byte object at <b>src</b> into the
- * <b>destlen<\b>-byte buffer at <b>dest</b>, tagging it with <b>objtype</b>.
+ * <b>destlen</b>-byte buffer at <b>dest</b>, tagging it with <b>objtype</b>.
* Return 0 on success and -1 on failure.
*/
int
diff --git a/src/lib/encoding/pem.h b/src/lib/encoding/pem.h
index 0bbb06a794..027c31c315 100644
--- a/src/lib/encoding/pem.h
+++ b/src/lib/encoding/pem.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001, Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -23,4 +23,4 @@ int pem_encode(char *dest, size_t destlen, const uint8_t *src, size_t srclen,
int pem_decode(uint8_t *dest, size_t destlen, const char *src, size_t srclen,
const char *objtype);
-#endif
+#endif /* !defined(TOR_PEM_H) */
diff --git a/src/lib/encoding/qstring.c b/src/lib/encoding/qstring.c
new file mode 100644
index 0000000000..5a34924eab
--- /dev/null
+++ b/src/lib/encoding/qstring.c
@@ -0,0 +1,90 @@
+/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file qstring.c
+ * \brief Implement QuotedString parsing.
+ *
+ * Note that this is only used for controller authentication; do not
+ * create new users for this. Instead, prefer the cstring.c functions.
+ **/
+
+#include "orconfig.h"
+#include "lib/encoding/qstring.h"
+#include "lib/malloc/malloc.h"
+#include "lib/log/util_bug.h"
+
+/** If the first <b>in_len_max</b> characters in <b>start</b> contain a
+ * QuotedString, return the length of that
+ * string (as encoded, including quotes). Otherwise return -1. */
+static inline int
+get_qstring_length(const char *start, size_t in_len_max,
+ int *chars_out)
+{
+ const char *cp, *end;
+ int chars = 0;
+
+ if (*start != '\"')
+ return -1;
+
+ cp = start+1;
+ end = start+in_len_max;
+
+ /* Calculate length. */
+ while (1) {
+ if (cp >= end) {
+ return -1; /* Too long. */
+ } else if (*cp == '\\') {
+ if (++cp == end)
+ return -1; /* Can't escape EOS. */
+ ++cp;
+ ++chars;
+ } else if (*cp == '\"') {
+ break;
+ } else {
+ ++cp;
+ ++chars;
+ }
+ }
+ if (chars_out)
+ *chars_out = chars;
+ return (int)(cp - start+1);
+}
+
+/** Given a pointer to a string starting at <b>start</b> containing
+ * <b>in_len_max</b> characters, decode a string beginning with one double
+ * quote, containing any number of non-quote characters or characters escaped
+ * with a backslash, and ending with a final double quote. Place the resulting
+ * string (unquoted, unescaped) into a newly allocated string in *<b>out</b>;
+ * store its length in <b>out_len</b>. On success, return a pointer to the
+ * character immediately following the escaped string. On failure, return
+ * NULL. */
+const char *
+decode_qstring(const char *start, size_t in_len_max,
+ char **out, size_t *out_len)
+{
+ const char *cp, *end;
+ char *outp;
+ int len, n_chars = 0;
+
+ len = get_qstring_length(start, in_len_max, &n_chars);
+ if (len<0)
+ return NULL;
+
+ end = start+len-1; /* Index of last quote. */
+ tor_assert(*end == '\"');
+ outp = *out = tor_malloc(len+1);
+ *out_len = n_chars;
+
+ cp = start+1;
+ while (cp < end) {
+ if (*cp == '\\')
+ ++cp;
+ *outp++ = *cp++;
+ }
+ *outp = '\0';
+ tor_assert((outp - *out) == (int)*out_len);
+
+ return end+1;
+}
diff --git a/src/lib/encoding/qstring.h b/src/lib/encoding/qstring.h
new file mode 100644
index 0000000000..f19a7dad87
--- /dev/null
+++ b/src/lib/encoding/qstring.h
@@ -0,0 +1,18 @@
+/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file qstring.h
+ * \brief Header for qstring.c
+ */
+
+#ifndef TOR_ENCODING_QSTRING_H
+#define TOR_ENCODING_QSTRING_H
+
+#include <stddef.h>
+
+const char *decode_qstring(const char *start, size_t in_len_max,
+ char **out, size_t *out_len);
+
+#endif /* !defined(TOR_ENCODING_QSTRING_H) */
diff --git a/src/lib/encoding/time_fmt.c b/src/lib/encoding/time_fmt.c
index 40543d41e0..5e58d36698 100644
--- a/src/lib/encoding/time_fmt.c
+++ b/src/lib/encoding/time_fmt.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001, Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -13,6 +13,7 @@
* and handles a larger variety of types. It converts between different time
* formats, and encodes and decodes them from strings.
**/
+#define TIME_FMT_PRIVATE
#include "lib/encoding/time_fmt.h"
#include "lib/log/log.h"
@@ -25,6 +26,7 @@
#include <string.h>
#include <time.h>
+#include <errno.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
@@ -92,8 +94,8 @@ static const int days_per_month[] =
/** Compute a time_t given a struct tm. The result is given in UTC, and
* does not account for leap seconds. Return 0 on success, -1 on failure.
*/
-int
-tor_timegm(const struct tm *tm, time_t *time_out)
+ATTR_UNUSED STATIC int
+tor_timegm_impl(const struct tm *tm, time_t *time_out)
{
/* This is a pretty ironclad timegm implementation, snarfed from Python2.2.
* It's way more brute-force than fiddling with tzset().
@@ -162,6 +164,35 @@ tor_timegm(const struct tm *tm, time_t *time_out)
return 0;
}
+/** Compute a time_t given a struct tm. The result here should be an inverse
+ * of the system's gmtime() function. Return 0 on success, -1 on failure.
+ */
+int
+tor_timegm(const struct tm *tm, time_t *time_out)
+{
+#ifdef HAVE_TIMEGM
+ /* If the system gives us a timegm(), use it: if the system's time_t
+ * includes leap seconds, then we can hope that its timegm() knows too.
+ *
+ * https://k5wiki.kerberos.org/wiki/Leap_second_handling says the in
+ * general we can rely on any system with leap seconds also having a
+ * timegm implementation. Let's hope it's right!
+ * */
+ time_t result = timegm((struct tm *) tm);
+ if (result == -1) {
+ log_warn(LD_BUG, "timegm() could not convert time: %s", strerror(errno));
+ *time_out = 0;
+ return -1;
+ } else {
+ *time_out = result;
+ return 0;
+ }
+#else
+ /* The system doesn't have timegm; we'll have to use our own. */
+ return tor_timegm_impl(tm, time_out);
+#endif
+}
+
/* strftime is locale-specific, so we need to replace those parts */
/** A c-locale array of 3-letter names of weekdays, starting with Sun. */
diff --git a/src/lib/encoding/time_fmt.h b/src/lib/encoding/time_fmt.h
index 0ddeca57fc..4adccb5990 100644
--- a/src/lib/encoding/time_fmt.h
+++ b/src/lib/encoding/time_fmt.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001, Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -18,6 +18,8 @@
#include <sys/types.h>
#endif
+#include "lib/testsupport/testsupport.h"
+
struct tm;
struct timeval;
@@ -41,4 +43,8 @@ int parse_iso_time_nospace(const char *cp, time_t *t);
int parse_http_time(const char *buf, struct tm *tm);
int format_time_interval(char *out, size_t out_len, long interval);
+#ifdef TIME_FMT_PRIVATE
+STATIC int tor_timegm_impl(const struct tm *tm, time_t *time_out);
#endif
+
+#endif /* !defined(TOR_TIME_FMT_H) */