From 07049b3d25cbc397dd39c71d32422bfd7c39d814 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 10 Sep 2010 09:19:10 -0400 Subject: [PATCH 1/6] Support mutli-line torrc options via the usual backslash syntax --- changes/torrc_continuation | 5 +++++ src/common/util.c | 22 +++++++++++++++++++++- src/test/test_util.c | 20 ++++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 changes/torrc_continuation diff --git a/changes/torrc_continuation b/changes/torrc_continuation new file mode 100644 index 0000000000..f40e970c04 --- /dev/null +++ b/changes/torrc_continuation @@ -0,0 +1,5 @@ + o Minor features: + - Support line continuations in torrc. If a line ends with a + single backslash character, the newline is ignored, and the + configuration value is treated as continuing on the next line. + diff --git a/src/common/util.c b/src/common/util.c index 1d770458f7..6b9455ddd7 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -2285,6 +2285,7 @@ const char * parse_config_line_from_str(const char *line, char **key_out, char **value_out) { const char *key, *val, *cp; + int continuation = 0; tor_assert(key_out); tor_assert(value_out); @@ -2329,8 +2330,13 @@ parse_config_line_from_str(const char *line, char **key_out, char **value_out) if (*line && *line != '#' && *line != '\n') return NULL; } else { - while (*line && *line != '\n' && *line != '#') + while (*line && *line != '\n' && *line != '#') { + if (*line == '\\' && line[1] == '\n') { + continuation = 1; + ++line; + } ++line; + } if (*line == '\n') { cp = line++; } else { @@ -2340,7 +2346,21 @@ parse_config_line_from_str(const char *line, char **key_out, char **value_out) --cp; tor_assert(cp >= val); + *value_out = tor_strndup(val, cp-val); + if (continuation) { + char *v_out, *v_in; + v_out = v_in = *value_out; + while (*v_in) { + if (v_in[0] == '\\' && v_in[1] == '\n') { + v_in += 2; + } else { + *v_out++ = *v_in++; + } + } + *v_out = '\0'; + } + } if (*line == '#') { diff --git a/src/test/test_util.c b/src/test/test_util.c index 8a13597978..84bb5a67f4 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -100,6 +100,9 @@ test_util_config_line(void) "k4#a\n" "k5#abc\n" "k6 val #with comment\n" "kseven \"a quoted 'string\"\n" "k8 \"a \\x71uoted\\n\\\"str\\\\ing\\t\\001\\01\\1\\\"\"\n" + "k9 a line that\\\n spans two lines.\n\n" + "k10 more than\\\n one contin\\\nuation\n" + "k11 \\\ncontinuation at the start\n" , sizeof(buf)); str = buf; @@ -161,7 +164,24 @@ test_util_config_line(void) test_streq(k, "k8"); test_streq(v, "a quoted\n\"str\\ing\t\x01\x01\x01\""); tor_free(k); tor_free(v); + + str = parse_config_line_from_str(str, &k, &v); + test_streq(k, "k9"); + test_streq(v, "a line that spans two lines."); + tor_free(k); tor_free(v); + + str = parse_config_line_from_str(str, &k, &v); + test_streq(k, "k10"); + test_streq(v, "more than one continuation"); + tor_free(k); tor_free(v); + + str = parse_config_line_from_str(str, &k, &v); + test_streq(k, "k11"); + test_streq(v, "continuation at the start"); + tor_free(k); tor_free(v); + test_streq(str, ""); + done: tor_free(k); tor_free(v); From a05ef55b66684d3355b213f8df366c23d0128eca Mon Sep 17 00:00:00 2001 From: Sebastian Hahn Date: Sat, 11 Sep 2010 01:25:48 +0200 Subject: [PATCH 2/6] Allow comments for multi-line torrc options --- src/common/util.c | 21 +++++++++++++++------ src/or/circuitbuild.c | 3 ++- src/or/relay.c | 1 + src/test/test_util.c | 12 ++++++++++++ 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/common/util.c b/src/common/util.c index 6b9455ddd7..e47ac78d35 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -2309,9 +2309,10 @@ parse_config_line_from_str(const char *line, char **key_out, char **value_out) return line; } - /* Skip until the next space. */ + /* Skip until the next space or \ followed by newline. */ key = line; - while (*line && !TOR_ISSPACE(*line) && *line != '#') + while (*line && !TOR_ISSPACE(*line) && *line != '#' && + ! (line[0] == '\\' && line[1] == '\n')) ++line; *key_out = tor_strndup(key, line-key); @@ -2322,7 +2323,7 @@ parse_config_line_from_str(const char *line, char **key_out, char **value_out) val = line; /* Find the end of the line. */ - if (*line == '\"') { + if (*line == '\"') { // XXX No continuation here if (!(line = unescape_string(line, value_out, NULL))) return NULL; while (*line == ' ' || *line == '\t') @@ -2330,10 +2331,14 @@ parse_config_line_from_str(const char *line, char **key_out, char **value_out) if (*line && *line != '#' && *line != '\n') return NULL; } else { - while (*line && *line != '\n' && *line != '#') { + while (*line && *line != '\n' && (*line != '#' || continuation)) { if (*line == '\\' && line[1] == '\n') { continuation = 1; ++line; + } else if (*line == '#') { + do { + ++line; + } while (*line && *line != '\n'); } ++line; } @@ -2352,7 +2357,12 @@ parse_config_line_from_str(const char *line, char **key_out, char **value_out) char *v_out, *v_in; v_out = v_in = *value_out; while (*v_in) { - if (v_in[0] == '\\' && v_in[1] == '\n') { + if (*v_in == '#') { + do { + ++v_in; + } while (*v_in && *v_in != '\n'); + ++v_in; + } else if (v_in[0] == '\\' && v_in[1] == '\n') { v_in += 2; } else { *v_out++ = *v_in++; @@ -2360,7 +2370,6 @@ parse_config_line_from_str(const char *line, char **key_out, char **value_out) } *v_out = '\0'; } - } if (*line == '#') { diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 5567b246ab..ef1bab3206 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -1752,7 +1752,8 @@ circuit_deliver_create_cell(circuit_t *circ, uint8_t cell_type, cell.circ_id = circ->n_circ_id; memcpy(cell.payload, payload, ONIONSKIN_CHALLENGE_LEN); - append_cell_to_circuit_queue(circ, circ->n_conn, &cell, CELL_DIRECTION_OUT, 0); + append_cell_to_circuit_queue(circ, circ->n_conn, &cell, + CELL_DIRECTION_OUT, 0); if (CIRCUIT_IS_ORIGIN(circ)) { /* mark it so it gets better rate limiting treatment. */ diff --git a/src/or/relay.c b/src/or/relay.c index 0d51ea4060..0b0b7067a0 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -2446,3 +2446,4 @@ circuit_queue_streams_are_blocked(circuit_t *circ) return circ->streams_blocked_on_p_conn; } } + diff --git a/src/test/test_util.c b/src/test/test_util.c index 84bb5a67f4..49823fde70 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -103,6 +103,8 @@ test_util_config_line(void) "k9 a line that\\\n spans two lines.\n\n" "k10 more than\\\n one contin\\\nuation\n" "k11 \\\ncontinuation at the start\n" + "k12 line with a\\\n#comment\n embedded\n" + "k13\\\ncontinuation at the very start\n" , sizeof(buf)); str = buf; @@ -180,6 +182,16 @@ test_util_config_line(void) test_streq(v, "continuation at the start"); tor_free(k); tor_free(v); + str = parse_config_line_from_str(str, &k, &v); + test_streq(k, "k12"); + test_streq(v, "line with a embedded"); + tor_free(k); tor_free(v); + + str = parse_config_line_from_str(str, &k, &v); + test_streq(k, "k13"); + test_streq(v, "continuation at the very start"); + tor_free(k); tor_free(v); + test_streq(str, ""); done: From 1dab6cf4cbb35e27dd1d13f7deeb3e25f7dfcd5c Mon Sep 17 00:00:00 2001 From: Sebastian Hahn Date: Sat, 11 Sep 2010 01:41:33 +0200 Subject: [PATCH 3/6] Document multiline options in the manpage --- changes/torrc_continuation | 1 + doc/tor.1.txt | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/changes/torrc_continuation b/changes/torrc_continuation index f40e970c04..f63937e42a 100644 --- a/changes/torrc_continuation +++ b/changes/torrc_continuation @@ -2,4 +2,5 @@ - Support line continuations in torrc. If a line ends with a single backslash character, the newline is ignored, and the configuration value is treated as continuing on the next line. + Implements bug 1929. diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 3b7e30bdfb..8c581c407c 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -65,7 +65,10 @@ Other options can be specified either on the command-line (--option value), or in the configuration file (option value or option "value"). Options are case-insensitive. C-style escaped characters are allowed inside quoted values. Options on the command line take precedence over - options found in the configuration file. + options found in the configuration file, except indicated otherwise. To + split one configuration entry into multiple lines, use a single \ before + the end of the line. Comments can be used in such multiline entries, but + they must start at the beginning of a line. **BandwidthRate** __N__ **bytes**|**KB**|**MB**|**GB**:: A token bucket limits the average incoming bandwidth usage on this node to From 1d29ad891e848dcb48120331e0d2f78bd6777746 Mon Sep 17 00:00:00 2001 From: Sebastian Hahn Date: Thu, 23 Sep 2010 22:39:58 +0200 Subject: [PATCH 4/6] Add new torrc line continuation unit tests We want to make sure that we don't break old torrc files that might have used something like this made-up example: ContactInfo UberUser # /// Fake email! \\\ Log info file /home/nick.mathewson/projects/tor-info.log And we also want to support the following style of writing your torrc: ExcludeNodes \ # Node1337 is run by the Bavarian Illuminati Node1337, \ # The operator of Node99 looked at me funny Node99 The code already handles both cases, but the unit test should help prove it. --- src/test/test_util.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/test/test_util.c b/src/test/test_util.c index 49823fde70..d90927b35f 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -105,6 +105,10 @@ test_util_config_line(void) "k11 \\\ncontinuation at the start\n" "k12 line with a\\\n#comment\n embedded\n" "k13\\\ncontinuation at the very start\n" + "k14 a line that has a comment and # ends with a slash \\\n" + "k15 this should be the next new line\n" + "k16 a line that has a comment and # ends without a slash \n" + "k17 this should be the next new line\n" , sizeof(buf)); str = buf; @@ -192,6 +196,26 @@ test_util_config_line(void) test_streq(v, "continuation at the very start"); tor_free(k); tor_free(v); + str = parse_config_line_from_str(str, &k, &v); + test_streq(k, "k14"); + test_streq(v, "a line that has a comment and" ); + tor_free(k); tor_free(v); + + str = parse_config_line_from_str(str, &k, &v); + test_streq(k, "k15"); + test_streq(v, "this should be the next new line"); + tor_free(k); tor_free(v); + + str = parse_config_line_from_str(str, &k, &v); + test_streq(k, "k16"); + test_streq(v, "a line that has a comment and" ); + tor_free(k); tor_free(v); + + str = parse_config_line_from_str(str, &k, &v); + test_streq(k, "k17"); + test_streq(v, "this should be the next new line"); + tor_free(k); tor_free(v); + test_streq(str, ""); done: From 0a0cc4599fd743d124d6ee90671c7c8e205b86e8 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 23 Sep 2010 22:58:04 -0400 Subject: [PATCH 5/6] Tweak continuation-and-comment logic I think there was a read-off-the-end-of-the-buffer bug that I fixed. At least I added some good comments, I hope. --- changes/torrc_continuation | 2 +- src/common/util.c | 46 ++++++++++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/changes/torrc_continuation b/changes/torrc_continuation index f63937e42a..5b6e086e6f 100644 --- a/changes/torrc_continuation +++ b/changes/torrc_continuation @@ -2,5 +2,5 @@ - Support line continuations in torrc. If a line ends with a single backslash character, the newline is ignored, and the configuration value is treated as continuing on the next line. - Implements bug 1929. + Resolves bug 1929. diff --git a/src/common/util.c b/src/common/util.c index e47ac78d35..904fd82239 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -2284,6 +2284,36 @@ unescape_string(const char *s, char **result, size_t *size_out) const char * parse_config_line_from_str(const char *line, char **key_out, char **value_out) { + /* I believe the file format here is supposed to be: + FILE = (EMPTYLINE | LINE)* + + EMPTYLINE = SPACE* NL | COMMENT NL + SPACE = ' ' | '\r' | '\t' + COMMENT = '#' NOT-NL* + NOT-NL = Any character except '\n' + NL = '\n' + + LINE = SPACE* KEY SPACE* VALUES NL + KEY = KEYCHAR+ + KEYCHAR = Any character except ' ', '\r', '\n', '\t', '#', "\" + + VALUES = QUOTEDVALUE | NORMALVALUE + QUOTEDVALUE = QUOTE QVITEM* QUOTE EOLSPACE? + QUOTE = '"' + QVCHAR = KEYCHAR | ESC ('n' | 't' | 'r' | '"' | ESC |'\'' | OCTAL | HEX) + ESC = "\\" + OCTAL = ODIGIT (ODIGIT ODIGIT?)? + HEX = ('x' | 'X') HEXDIGIT HEXDIGIT + ODIGIT = '0' .. '7' + HEXDIGIT = '0'..'9' | 'a' .. 'f' | 'A' .. 'F' + EOLSPACE = SPACE* COMMENT? + + NORMALVALUE = (VALCHAR | ESC ESC_IGNORE | CONTINUATION)* EOLSPACE? + VALCHAR = Any character except ESC, '#', and '\n' + ESC_IGNORE = Any character except '#' or '\n' + CONTINUATION = ESC NL ( COMMENT NL )* + */ + const char *key, *val, *cp; int continuation = 0; @@ -2323,7 +2353,7 @@ parse_config_line_from_str(const char *line, char **key_out, char **value_out) val = line; /* Find the end of the line. */ - if (*line == '\"') { // XXX No continuation here + if (*line == '\"') { // XXX No continuation handling is done here if (!(line = unescape_string(line, value_out, NULL))) return NULL; while (*line == ' ' || *line == '\t') @@ -2331,27 +2361,34 @@ parse_config_line_from_str(const char *line, char **key_out, char **value_out) if (*line && *line != '#' && *line != '\n') return NULL; } else { + /* Look for the end of the line. */ while (*line && *line != '\n' && (*line != '#' || continuation)) { if (*line == '\\' && line[1] == '\n') { continuation = 1; - ++line; + line += 2; } else if (*line == '#') { do { ++line; } while (*line && *line != '\n'); + if (*line == '\n') + ++line; + } else { + ++line; } - ++line; } + if (*line == '\n') { cp = line++; } else { cp = line; } + /* Now back cp up to be the last nonspace character */ while (cp>val && TOR_ISSPACE(*(cp-1))) --cp; tor_assert(cp >= val); + /* Now copy out and decode the value. */ *value_out = tor_strndup(val, cp-val); if (continuation) { char *v_out, *v_in; @@ -2361,7 +2398,8 @@ parse_config_line_from_str(const char *line, char **key_out, char **value_out) do { ++v_in; } while (*v_in && *v_in != '\n'); - ++v_in; + if (*v_in == '\n') + ++v_in; } else if (v_in[0] == '\\' && v_in[1] == '\n') { v_in += 2; } else { From 851255170aaa419e9c8f2a7d1b3c7a124a9c2783 Mon Sep 17 00:00:00 2001 From: Sebastian Hahn Date: Fri, 24 Sep 2010 13:32:27 +0200 Subject: [PATCH 6/6] Note that the torrc format doesn't need nl at end --- src/common/util.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/common/util.c b/src/common/util.c index 904fd82239..abb2753c6e 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -2285,15 +2285,17 @@ const char * parse_config_line_from_str(const char *line, char **key_out, char **value_out) { /* I believe the file format here is supposed to be: - FILE = (EMPTYLINE | LINE)* + FILE = (EMPTYLINE | LINE)* (EMPTYLASTLINE | LASTLINE)? - EMPTYLINE = SPACE* NL | COMMENT NL + EMPTYLASTLINE = SPACE* | COMMENT + EMPTYLINE = EMPTYLASTLINE NL SPACE = ' ' | '\r' | '\t' COMMENT = '#' NOT-NL* NOT-NL = Any character except '\n' NL = '\n' - LINE = SPACE* KEY SPACE* VALUES NL + LASTLINE = SPACE* KEY SPACE* VALUES + LINE = LASTLINE NL KEY = KEYCHAR+ KEYCHAR = Any character except ' ', '\r', '\n', '\t', '#', "\"