Tor 0.4.9.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
20#include "lib/log/log.h"
21#include "lib/log/util_bug.h"
22#include "lib/malloc/malloc.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>. */
31void
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> */
52void
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.) */
73const 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. */
86const 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>. */
103int
104config_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 */
199int
200config_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 */
209void
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 **/
266config_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. */
286int
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>. */
301int
302config_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 */
323const char *
324parse_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:2468
void config_line_prepend(config_line_t **lst, const char *key, const char *val)
Definition: confline.c:53
config_line_t * config_lines_dup_and_filter(const config_line_t *inp, const char *key)
Definition: confline.c:235
const config_line_t * config_line_find_case(const config_line_t *lines, const char *key)
Definition: confline.c:87
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
config_line_t * config_lines_partition(config_line_t *inp, const char *header)
Definition: confline.c:266
void config_free_lines_(config_line_t *front)
Definition: confline.c:210
int config_count_key(const config_line_t *a, const char *key)
Definition: confline.c:302
const config_line_t * config_line_find(const config_line_t *lines, const char *key)
Definition: confline.c:74
void config_line_append(config_line_t **lst, const char *key, const char *val)
Definition: confline.c:32
config_line_t * config_lines_dup(const config_line_t *inp)
Definition: confline.c:226
int config_get_lines(const char *string, config_line_t **result, int extended)
Definition: confline.c:200
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_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:56
Macros to manage assertions, fatal and non-fatal.
#define tor_assert(expr)
Definition: util_bug.h:103
int strcasecmpstart(const char *s1, const char *s2)
Definition: util_string.c:227
Header for util_string.c.