Tor  0.4.7.0-alpha-dev
confline.c
Go to the documentation of this file.
1 /* Copyright (c) 2001 Matej Pfajfar.
2  * Copyright (c) 2001-2004, Roger Dingledine.
3  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
4  * Copyright (c) 2007-2021, The Tor Project, Inc. */
5 /* See LICENSE for licensing information */
6 
7 /**
8  * \file confline.c
9  *
10  * \brief Functions to manipulate a linked list of key-value pairs, of the
11  * type used in Tor's configuration files.
12  *
13  * Tor uses the config_line_t type and its associated serialized format for
14  * human-readable key-value pairs in many places, including its configuration,
15  * its state files, its consensus cache, and so on.
16  **/
17 
18 #include "lib/encoding/confline.h"
19 #include "lib/encoding/cstring.h"
20 #include "lib/log/log.h"
21 #include "lib/log/util_bug.h"
22 #include "lib/malloc/malloc.h"
25 #include "lib/string/util_string.h"
26 
27 #include <string.h>
28 
29 /** Helper: allocate a new configuration option mapping 'key' to 'val',
30  * append it to *<b>lst</b>. */
31 void
33  const char *key,
34  const char *val)
35 {
36  tor_assert(lst);
37 
38  config_line_t *newline;
39 
40  newline = tor_malloc_zero(sizeof(config_line_t));
41  newline->key = tor_strdup(key);
42  newline->value = tor_strdup(val);
43  newline->next = NULL;
44  while (*lst)
45  lst = &((*lst)->next);
46 
47  (*lst) = newline;
48 }
49 
50 /** Helper: allocate a new configuration option mapping 'key' to 'val',
51  * and prepend it to *<b>lst</b> */
52 void
54  const char *key,
55  const char *val)
56 {
57  tor_assert(lst);
58 
59  config_line_t *newline;
60 
61  newline = tor_malloc_zero(sizeof(config_line_t));
62  newline->key = tor_strdup(key);
63  newline->value = tor_strdup(val);
64  newline->next = *lst;
65  *lst = newline;
66 }
67 
68 /** Return the first line in <b>lines</b> whose key is exactly <b>key</b>, or
69  * NULL if no such key exists.
70  *
71  * (In options parsing, this is for handling commandline-only options only;
72  * other options should be looked up in the appropriate data structure.) */
73 const config_line_t *
75  const char *key)
76 {
77  const config_line_t *cl;
78  for (cl = lines; cl; cl = cl->next) {
79  if (!strcmp(cl->key, key))
80  return cl;
81  }
82  return NULL;
83 }
84 
85 /** As config_line_find(), but perform a case-insensitive comparison. */
86 const config_line_t *
88  const char *key)
89 {
90  const config_line_t *cl;
91  for (cl = lines; cl; cl = cl->next) {
92  if (!strcasecmp(cl->key, key))
93  return cl;
94  }
95  return NULL;
96 }
97 
98 /** Auxiliary function that does all the work of config_get_lines.
99  * <b>recursion_level</b> is the count of how many nested %includes we have.
100  * <b>opened_lst</b> will have a list of opened files if provided.
101  * Returns the a pointer to the last element of the <b>result</b> in
102  * <b>last</b>. */
103 int
104 config_get_lines_aux(const char *string, config_line_t **result, int extended,
105  int allow_include, int *has_include,
106  struct smartlist_t *opened_lst, int recursion_level,
107  config_line_t **last,
108  include_handler_fn handle_include)
109 {
110  config_line_t *list = NULL, **next, *list_last = NULL;
111  char *k, *v;
112  const char *parse_err;
113  int include_used = 0;
114 
115  if (recursion_level > MAX_INCLUDE_RECURSION_LEVEL) {
116  log_warn(LD_CONFIG, "Error while parsing configuration: more than %d "
117  "nested %%includes.", MAX_INCLUDE_RECURSION_LEVEL);
118  return -1;
119  }
120 
121  next = &list;
122  do {
123  k = v = NULL;
124  string = parse_config_line_from_str_verbose(string, &k, &v, &parse_err);
125  if (!string) {
126  log_warn(LD_CONFIG, "Error while parsing configuration: %s",
127  parse_err?parse_err:"<unknown>");
128  config_free_lines(list);
129  tor_free(k);
130  tor_free(v);
131  return -1;
132  }
133  if (k && v) {
134  unsigned command = CONFIG_LINE_NORMAL;
135  if (extended) {
136  if (k[0] == '+') {
137  char *k_new = tor_strdup(k+1);
138  tor_free(k);
139  k = k_new;
141  } else if (k[0] == '/') {
142  char *k_new = tor_strdup(k+1);
143  tor_free(k);
144  k = k_new;
145  tor_free(v);
146  v = tor_strdup("");
147  command = CONFIG_LINE_CLEAR;
148  }
149  }
150 
151  if (allow_include && !strcmp(k, "%include") && handle_include) {
152  tor_free(k);
153  include_used = 1;
154  log_notice(LD_CONFIG, "Processing configuration path \"%s\" at "
155  "recursion level %d.", v, recursion_level);
156 
157  config_line_t *include_list;
158  if (handle_include(v, recursion_level, extended, &include_list,
159  &list_last, opened_lst) < 0) {
160  log_warn(LD_CONFIG, "Error reading included configuration "
161  "file or directory: \"%s\".", v);
162  config_free_lines(list);
163  tor_free(v);
164  return -1;
165  }
166  *next = include_list;
167  if (list_last)
168  next = &list_last->next;
169  tor_free(v);
170  } else {
171  /* This list can get long, so we keep a pointer to the end of it
172  * rather than using config_line_append over and over and getting
173  * n^2 performance. */
174  *next = tor_malloc_zero(sizeof(**next));
175  (*next)->key = k;
176  (*next)->value = v;
177  (*next)->next = NULL;
178  (*next)->command = command;
179  list_last = *next;
180  next = &((*next)->next);
181  }
182  } else {
183  tor_free(k);
184  tor_free(v);
185  }
186  } while (*string);
187 
188  if (last) {
189  *last = list_last;
190  }
191  if (has_include) {
192  *has_include = include_used;
193  }
194  *result = list;
195  return 0;
196 }
197 
198 /** Same as config_get_lines_include but does not allow %include */
199 int
200 config_get_lines(const char *string, config_line_t **result, int extended)
201 {
202  return config_get_lines_aux(string, result, extended, 0, NULL, NULL, 1,
203  NULL, NULL);
204 }
205 
206 /**
207  * Free all the configuration lines on the linked list <b>front</b>.
208  */
209 void
211 {
212  config_line_t *tmp;
213 
214  while (front) {
215  tmp = front;
216  front = tmp->next;
217 
218  tor_free(tmp->key);
219  tor_free(tmp->value);
220  tor_free(tmp);
221  }
222 }
223 
224 /** Return a newly allocated deep copy of the lines in <b>inp</b>. */
227 {
228  return config_lines_dup_and_filter(inp, NULL);
229 }
230 
231 /** Return a newly allocated deep copy of the lines in <b>inp</b>,
232  * but only the ones whose keys begin with <b>key</b> (case-insensitive).
233  * If <b>key</b> is NULL, do not filter. */
236  const char *key)
237 {
238  config_line_t *result = NULL;
239  config_line_t **next_out = &result;
240  while (inp) {
241  if (key && strcasecmpstart(inp->key, key)) {
242  inp = inp->next;
243  continue;
244  }
245  *next_out = tor_malloc_zero(sizeof(config_line_t));
246  (*next_out)->key = tor_strdup(inp->key);
247  (*next_out)->value = tor_strdup(inp->value);
248  inp = inp->next;
249  next_out = &((*next_out)->next);
250  }
251  (*next_out) = NULL;
252  return result;
253 }
254 
255 /**
256  * Given a linelist <b>inp</b> beginning with the key <b>header</b>, find the
257  * next line with that key, and remove that instance and all following lines
258  * from the list. Return the lines that were removed. Operate
259  * case-insensitively.
260  *
261  * For example, if the header is "H", and <b>inp</b> contains "H, A, B, H, C,
262  * H, D", this function will alter <b>inp</b> to contain only "H, A, B", and
263  * return the elements "H, C, H, D" as a separate list.
264  **/
266 config_lines_partition(config_line_t *inp, const char *header)
267 {
268  if (BUG(inp == NULL))
269  return NULL;
270  if (BUG(strcasecmp(inp->key, header)))
271  return NULL;
272 
273  /* Advance ptr until it points to the link to the next segment of this
274  list. */
275  config_line_t **ptr = &inp->next;
276  while (*ptr && strcasecmp((*ptr)->key, header)) {
277  ptr = &(*ptr)->next;
278  }
279  config_line_t *remainder = *ptr;
280  *ptr = NULL;
281  return remainder;
282 }
283 
284 /** Return true iff a and b contain identical keys and values in identical
285  * order. */
286 int
288 {
289  while (a && b) {
290  if (strcasecmp(a->key, b->key) || strcmp(a->value, b->value))
291  return 0;
292  a = a->next;
293  b = b->next;
294  }
295  if (a || b)
296  return 0;
297  return 1;
298 }
299 
300 /** Return the number of lines in <b>a</b> whose key is <b>key</b>. */
301 int
302 config_count_key(const config_line_t *a, const char *key)
303 {
304  int n = 0;
305  while (a) {
306  if (!strcasecmp(a->key, key)) {
307  ++n;
308  }
309  a = a->next;
310  }
311  return n;
312 }
313 
314 /** Given a string containing part of a configuration file or similar format,
315  * advance past comments and whitespace and try to parse a single line. If we
316  * parse a line successfully, set *<b>key_out</b> to a new string holding the
317  * key portion and *<b>value_out</b> to a new string holding the value portion
318  * of the line, and return a pointer to the start of the next line. If we run
319  * out of data, return a pointer to the end of the string. If we encounter an
320  * error, return NULL and set *<b>err_out</b> (if provided) to an error
321  * message.
322  */
323 const char *
324 parse_config_line_from_str_verbose(const char *line, char **key_out,
325  char **value_out,
326  const char **err_out)
327 {
328  /*
329  See torrc_format.txt for a description of the (silly) format this parses.
330  */
331  const char *key, *val, *cp;
332  int continuation = 0;
333 
334  tor_assert(key_out);
335  tor_assert(value_out);
336 
337  *key_out = *value_out = NULL;
338  key = val = NULL;
339  /* Skip until the first keyword. */
340  while (1) {
341  while (TOR_ISSPACE(*line))
342  ++line;
343  if (*line == '#') {
344  while (*line && *line != '\n')
345  ++line;
346  } else {
347  break;
348  }
349  }
350 
351  if (!*line) { /* End of string? */
352  *key_out = *value_out = NULL;
353  return line;
354  }
355 
356  /* Skip until the next space or \ followed by newline. */
357  key = line;
358  while (*line && !TOR_ISSPACE(*line) && *line != '#' &&
359  ! (line[0] == '\\' && line[1] == '\n'))
360  ++line;
361  *key_out = tor_strndup(key, line-key);
362 
363  /* Skip until the value. */
364  while (*line == ' ' || *line == '\t')
365  ++line;
366 
367  val = line;
368 
369  /* Find the end of the line. */
370  if (*line == '\"') { // XXX No continuation handling is done here
371  if (!(line = unescape_string(line, value_out, NULL))) {
372  if (err_out)
373  *err_out = "Invalid escape sequence in quoted string";
374  return NULL;
375  }
376  while (*line == ' ' || *line == '\t')
377  ++line;
378  if (*line == '\r' && *(++line) == '\n')
379  ++line;
380  if (*line && *line != '#' && *line != '\n') {
381  if (err_out)
382  *err_out = "Excess data after quoted string";
383  return NULL;
384  }
385  } else {
386  /* Look for the end of the line. */
387  while (*line && *line != '\n' && (*line != '#' || continuation)) {
388  if (*line == '\\' && line[1] == '\n') {
389  continuation = 1;
390  line += 2;
391  } else if (*line == '#') {
392  do {
393  ++line;
394  } while (*line && *line != '\n');
395  if (*line == '\n')
396  ++line;
397  } else {
398  ++line;
399  }
400  }
401 
402  if (*line == '\n') {
403  cp = line++;
404  } else {
405  cp = line;
406  }
407  /* Now back cp up to be the last nonspace character */
408  while (cp>val && TOR_ISSPACE(*(cp-1)))
409  --cp;
410 
411  tor_assert(cp >= val);
412 
413  /* Now copy out and decode the value. */
414  *value_out = tor_strndup(val, cp-val);
415  if (continuation) {
416  char *v_out, *v_in;
417  v_out = v_in = *value_out;
418  while (*v_in) {
419  if (*v_in == '#') {
420  do {
421  ++v_in;
422  } while (*v_in && *v_in != '\n');
423  if (*v_in == '\n')
424  ++v_in;
425  } else if (v_in[0] == '\\' && v_in[1] == '\n') {
426  v_in += 2;
427  } else {
428  *v_out++ = *v_in++;
429  }
430  }
431  *v_out = '\0';
432  }
433  }
434 
435  if (*line == '#') {
436  do {
437  ++line;
438  } while (*line && *line != '\n');
439  }
440  while (TOR_ISSPACE(*line)) ++line;
441 
442  return line;
443 }
Locale-independent character-type inspection (header)
Header for compat_string.c.
tor_cmdline_mode_t command
Definition: config.c:2440
void config_line_prepend(config_line_t **lst, const char *key, const char *val)
Definition: confline.c:53
const config_line_t * config_line_find(const config_line_t *lines, const char *key)
Definition: confline.c:74
config_line_t * config_lines_dup_and_filter(const config_line_t *inp, const char *key)
Definition: confline.c:235
config_line_t * config_lines_partition(config_line_t *inp, const char *header)
Definition: confline.c:266
int config_get_lines_aux(const char *string, config_line_t **result, int extended, int allow_include, int *has_include, struct smartlist_t *opened_lst, int recursion_level, config_line_t **last, include_handler_fn handle_include)
Definition: confline.c:104
void config_free_lines_(config_line_t *front)
Definition: confline.c:210
const char * parse_config_line_from_str_verbose(const char *line, char **key_out, char **value_out, const char **err_out)
Definition: confline.c:324
int config_count_key(const config_line_t *a, const char *key)
Definition: confline.c:302
config_line_t * config_lines_dup(const config_line_t *inp)
Definition: confline.c:226
void config_line_append(config_line_t **lst, const char *key, const char *val)
Definition: confline.c:32
int config_get_lines(const char *string, config_line_t **result, int extended)
Definition: confline.c:200
const config_line_t * config_line_find_case(const config_line_t *lines, const char *key)
Definition: confline.c:87
int config_lines_eq(const config_line_t *a, const config_line_t *b)
Definition: confline.c:287
Header for confline.c.
#define CONFIG_LINE_APPEND
Definition: confline.h:22
#define CONFIG_LINE_NORMAL
Definition: confline.h:19
const char * unescape_string(const char *s, char **result, size_t *size_out)
Definition: cstring.c:30
Header for cstring.c.
Headers for log.c.
#define LD_CONFIG
Definition: log.h:68
Headers for util_malloc.c.
#define tor_free(p)
Definition: malloc.h:52
Macros to manage assertions, fatal and non-fatal.
#define tor_assert(expr)
Definition: util_bug.h:102
int strcasecmpstart(const char *s1, const char *s2)
Definition: util_string.c:225
Header for util_string.c.