r17499@catbus: nickm | 2008-01-07 13:39:46 -0500

Bugfix on fix for 557: Make values containing special characters work right with getconf, setconf, and saveconf.  Document this in control-spec.txt


svn:r13056
This commit is contained in:
Nick Mathewson 2008-01-07 18:54:55 +00:00
parent a63eb68fe1
commit dd35fe59c4
4 changed files with 106 additions and 12 deletions

View File

@ -36,6 +36,9 @@ Changes in version 0.2.0.16-alpha - 2008-01-??
o Minor features (controller): o Minor features (controller):
- Get NS events working again. (Patch from tup) - Get NS events working again. (Patch from tup)
- The GETCONF command now escapes and quotes configuration values
that don't otherwise fit into the torrc file.
- The SETCONF command now handles quoted values correctly.
o Minor features (other): o Minor features (other):
- Add hidden services and DNSPorts to the list of things that make - Add hidden services and DNSPorts to the list of things that make
@ -43,6 +46,7 @@ Changes in version 0.2.0.16-alpha - 2008-01-??
no ports from a fatal error to a warning; we might change it no ports from a fatal error to a warning; we might change it
back if this turns out to confuse anybody. (Fixes bug 579.) back if this turns out to confuse anybody. (Fixes bug 579.)
Changes in version 0.2.0.15-alpha - 2007-12-25 Changes in version 0.2.0.15-alpha - 2007-12-25
o Major bugfixes: o Major bugfixes:
- Fix several remotely triggerable asserts based on DirPort requests - Fix several remotely triggerable asserts based on DirPort requests

View File

@ -139,7 +139,8 @@ $Id$
Change the value of one or more configuration variables. The syntax is: Change the value of one or more configuration variables. The syntax is:
"SETCONF" 1*(SP keyword ["=" String]) CRLF "SETCONF" 1*(SP keyword ["=" value]) CRLF
value = String / QuotedString
Tor behaves as though it had just read each of the key-value pairs Tor behaves as though it had just read each of the key-value pairs
from its configuration file. Keywords with no corresponding values have from its configuration file. Keywords with no corresponding values have
@ -184,6 +185,10 @@ $Id$
empty string, Tor may reply with a reply line of the form: empty string, Tor may reply with a reply line of the form:
250 keyword 250 keyword
Value may be a raw value or a quoted string. Tor will try to use
unquoted values except when the value could be misinterpreted through
not being quoted.
If some of the listed keywords can't be found, Tor replies with a If some of the listed keywords can't be found, Tor replies with a
"552 unknown configuration keyword" message. "552 unknown configuration keyword" message.

View File

@ -627,7 +627,8 @@ static int parse_log_severity_range(const char *range, int *min_out,
static int validate_data_directory(or_options_t *options); static int validate_data_directory(or_options_t *options);
static int write_configuration_file(const char *fname, or_options_t *options); static int write_configuration_file(const char *fname, or_options_t *options);
static config_line_t *get_assigned_option(config_format_t *fmt, static config_line_t *get_assigned_option(config_format_t *fmt,
or_options_t *options, const char *key); or_options_t *options, const char *key,
int escape_val);
static void config_init(config_format_t *fmt, void *options); static void config_init(config_format_t *fmt, void *options);
static int or_state_validate(or_state_t *old_options, or_state_t *options, static int or_state_validate(or_state_t *old_options, or_state_t *options,
int from_setconf, char **msg); int from_setconf, char **msg);
@ -1686,7 +1687,33 @@ option_get_canonical_name(const char *key)
config_line_t * config_line_t *
option_get_assignment(or_options_t *options, const char *key) option_get_assignment(or_options_t *options, const char *key)
{ {
return get_assigned_option(&options_format, options, key); return get_assigned_option(&options_format, options, key, 1);
}
/** Return true iff value needs to be quoted and escaped to be used in
* a configuration file. */
static int
config_value_needs_escape(const char *value)
{
if (*value == '\"')
return 1;
while (*value) {
switch (*value)
{
case '\r':
case '\n':
case '#':
/* Note: quotes and backspaces need special handling when we are using
* quotes, not otherwise, so they don't trigger escaping on their
* own. */
return 1;
default:
if (!TOR_ISPRINT(*value))
return 1;
}
++value;
}
return 0;
} }
/** Return a newly allocated deep copy of the lines in <b>inp</b>. */ /** Return a newly allocated deep copy of the lines in <b>inp</b>. */
@ -1707,10 +1734,12 @@ config_lines_dup(const config_line_t *inp)
} }
/** Return newly allocated line or lines corresponding to <b>key</b> in the /** Return newly allocated line or lines corresponding to <b>key</b> in the
* configuration <b>options</b>. Return NULL if no such key exists. */ * configuration <b>options</b>. If <b>escape_val</b> is true and a
* value needs to be quoted before it's put in a config file, quote and
* escape that value. Return NULL if no such key exists. */
static config_line_t * static config_line_t *
get_assigned_option(config_format_t *fmt, or_options_t *options, get_assigned_option(config_format_t *fmt, or_options_t *options,
const char *key) const char *key, int escape_val)
/* XXXX argument is options, but fmt is provided. Inconsistent. */ /* XXXX argument is options, but fmt is provided. Inconsistent. */
{ {
config_var_t *var; config_var_t *var;
@ -1749,6 +1778,7 @@ get_assigned_option(config_format_t *fmt, or_options_t *options,
tor_free(result->key); tor_free(result->key);
tor_free(result); tor_free(result);
} }
escape_val = 0; /* Can't need escape. */
break; break;
case CONFIG_TYPE_INTERVAL: case CONFIG_TYPE_INTERVAL:
case CONFIG_TYPE_UINT: case CONFIG_TYPE_UINT:
@ -1756,18 +1786,22 @@ get_assigned_option(config_format_t *fmt, or_options_t *options,
* needs to be an int. Not, say, a uint16_t or char. */ * needs to be an int. Not, say, a uint16_t or char. */
tor_snprintf(buf, sizeof(buf), "%d", *(int*)value); tor_snprintf(buf, sizeof(buf), "%d", *(int*)value);
result->value = tor_strdup(buf); result->value = tor_strdup(buf);
escape_val = 0; /* Can't need escape. */
break; break;
case CONFIG_TYPE_MEMUNIT: case CONFIG_TYPE_MEMUNIT:
tor_snprintf(buf, sizeof(buf), U64_FORMAT, tor_snprintf(buf, sizeof(buf), U64_FORMAT,
U64_PRINTF_ARG(*(uint64_t*)value)); U64_PRINTF_ARG(*(uint64_t*)value));
result->value = tor_strdup(buf); result->value = tor_strdup(buf);
escape_val = 0; /* Can't need escape. */
break; break;
case CONFIG_TYPE_DOUBLE: case CONFIG_TYPE_DOUBLE:
tor_snprintf(buf, sizeof(buf), "%f", *(double*)value); tor_snprintf(buf, sizeof(buf), "%f", *(double*)value);
result->value = tor_strdup(buf); result->value = tor_strdup(buf);
escape_val = 0; /* Can't need escape. */
break; break;
case CONFIG_TYPE_BOOL: case CONFIG_TYPE_BOOL:
result->value = tor_strdup(*(int*)value ? "1" : "0"); result->value = tor_strdup(*(int*)value ? "1" : "0");
escape_val = 0; /* Can't need escape. */
break; break;
case CONFIG_TYPE_CSV: case CONFIG_TYPE_CSV:
if (*(smartlist_t**)value) if (*(smartlist_t**)value)
@ -1793,7 +1827,8 @@ get_assigned_option(config_format_t *fmt, or_options_t *options,
case CONFIG_TYPE_LINELIST_V: case CONFIG_TYPE_LINELIST_V:
tor_free(result->key); tor_free(result->key);
tor_free(result); tor_free(result);
return config_lines_dup(*(const config_line_t**)value); result = config_lines_dup(*(const config_line_t**)value);
break;
default: default:
tor_free(result->key); tor_free(result->key);
tor_free(result); tor_free(result);
@ -1802,6 +1837,17 @@ get_assigned_option(config_format_t *fmt, or_options_t *options,
return NULL; return NULL;
} }
if (escape_val) {
config_line_t *line;
for (line = result; line; line = line->next) {
if (line->value && config_value_needs_escape(line->value)) {
char *newval = esc_for_log(line->value);
tor_free(line->value);
line->value = newval;
}
}
}
return result; return result;
} }
@ -2319,8 +2365,8 @@ option_is_same(config_format_t *fmt,
CHECK(fmt, o1); CHECK(fmt, o1);
CHECK(fmt, o2); CHECK(fmt, o2);
c1 = get_assigned_option(fmt, o1, name); c1 = get_assigned_option(fmt, o1, name, 0);
c2 = get_assigned_option(fmt, o2, name); c2 = get_assigned_option(fmt, o2, name, 0);
r = config_lines_eq(c1, c2); r = config_lines_eq(c1, c2);
config_free_lines(c1); config_free_lines(c1);
config_free_lines(c2); config_free_lines(c2);
@ -2341,7 +2387,7 @@ options_dup(config_format_t *fmt, or_options_t *old)
continue; continue;
if (fmt->vars[i].type == CONFIG_TYPE_OBSOLETE) if (fmt->vars[i].type == CONFIG_TYPE_OBSOLETE)
continue; continue;
line = get_assigned_option(fmt, old, fmt->vars[i].name); line = get_assigned_option(fmt, old, fmt->vars[i].name, 0);
if (line) { if (line) {
char *msg = NULL; char *msg = NULL;
if (config_assign(fmt, newopts, line, 0, 0, &msg) < 0) { if (config_assign(fmt, newopts, line, 0, 0, &msg) < 0) {
@ -2430,7 +2476,7 @@ config_dump(config_format_t *fmt, void *options, int minimal,
comment_option = 1; comment_option = 1;
desc = config_find_description(fmt, fmt->vars[i].name); desc = config_find_description(fmt, fmt->vars[i].name);
line = assigned = get_assigned_option(fmt, options, fmt->vars[i].name); line = assigned = get_assigned_option(fmt, options, fmt->vars[i].name, 1);
if (line && desc) { if (line && desc) {
/* Only dump the description if there's something to describe. */ /* Only dump the description if there's something to describe. */

View File

@ -364,6 +364,44 @@ read_escaped_data(const char *data, size_t len, char **out)
return outp - *out; return outp - *out;
} }
/** DOCDOC */
static const char *
extract_escaped_string(const char *start, size_t in_len_max,
char **out, size_t *out_len)
{
const char *cp, *end;
size_t len=0;
if (*start != '\"')
return NULL;
cp = start+1;
end = start+in_len_max;
/* Calculate length. */
while (1) {
if (cp >= end)
return NULL;
else if (*cp == '\\') {
if (++cp == end)
return NULL; /* Can't escape EOS. */
++cp;
++len;
} else if (*cp == '\"') {
break;
} else {
++cp;
++len;
}
}
end = cp;
*out_len = end-start+1;
*out = tor_strndup(start, *out_len);
return end+1;
}
/** Given a pointer to a string starting at <b>start</b> containing /** 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 * <b>in_len_max</b> characters, decode a string beginning with one double
* quote, containing any number of non-quote characters or characters escaped * quote, containing any number of non-quote characters or characters escaped
@ -372,6 +410,7 @@ read_escaped_data(const char *data, size_t len, char **out)
* store its length in <b>out_len</b>. On success, return a pointer to the * store its length in <b>out_len</b>. On success, return a pointer to the
* character immediately following the escaped string. On failure, return * character immediately following the escaped string. On failure, return
* NULL. */ * NULL. */
/* XXXX020 fold into extract_escaped_string */
static const char * static const char *
get_escaped_string(const char *start, size_t in_len_max, get_escaped_string(const char *start, size_t in_len_max,
char **out, size_t *out_len) char **out, size_t *out_len)
@ -659,7 +698,7 @@ control_setconf_helper(control_connection_t *conn, uint32_t len, char *body,
val = tor_strndup(val_start, body-val_start); val = tor_strndup(val_start, body-val_start);
val_len = strlen(val); val_len = strlen(val);
} else { } else {
body = (char*)get_escaped_string(body, (len - (body-start)), body = (char*)extract_escaped_string(body, (len - (body-start)),
&val, &val_len); &val, &val_len);
if (!body) { if (!body) {
connection_write_str_to_buf("551 Couldn't parse string\r\n", conn); connection_write_str_to_buf("551 Couldn't parse string\r\n", conn);