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 */
10 * \brief Functions to manipulate a linked list of key-value pairs, of the
11 * type used in Tor's configuration files.
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.
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"
23 #include "lib/string/compat_ctype.h"
24 #include "lib/string/compat_string.h"
25 #include "lib/string/util_string.h"
29 /** Helper: allocate a new configuration option mapping 'key' to 'val',
30 * append it to *<b>lst</b>. */
32 config_line_append(config_line_t
**lst
,
38 config_line_t
*newline
;
40 newline
= tor_malloc_zero(sizeof(config_line_t
));
41 newline
->key
= tor_strdup(key
);
42 newline
->value
= tor_strdup(val
);
45 lst
= &((*lst
)->next
);
50 /** Helper: allocate a new configuration option mapping 'key' to 'val',
51 * and prepend it to *<b>lst</b> */
53 config_line_prepend(config_line_t
**lst
,
59 config_line_t
*newline
;
61 newline
= tor_malloc_zero(sizeof(config_line_t
));
62 newline
->key
= tor_strdup(key
);
63 newline
->value
= tor_strdup(val
);
68 /** Return the first line in <b>lines</b> whose key is exactly <b>key</b>, or
69 * NULL if no such key exists.
71 * (In options parsing, this is for handling commandline-only options only;
72 * other options should be looked up in the appropriate data structure.) */
74 config_line_find(const config_line_t
*lines
,
77 const config_line_t
*cl
;
78 for (cl
= lines
; cl
; cl
= cl
->next
) {
79 if (!strcmp(cl
->key
, key
))
85 /** As config_line_find(), but perform a case-insensitive comparison. */
87 config_line_find_case(const config_line_t
*lines
,
90 const config_line_t
*cl
;
91 for (cl
= lines
; cl
; cl
= cl
->next
) {
92 if (!strcasecmp(cl
->key
, key
))
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
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
)
110 config_line_t
*list
= NULL
, **next
, *list_last
= NULL
;
112 const char *parse_err
;
113 int include_used
= 0;
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
);
124 string
= parse_config_line_from_str_verbose(string
, &k
, &v
, &parse_err
);
126 log_warn(LD_CONFIG
, "Error while parsing configuration: %s",
127 parse_err
?parse_err
:"<unknown>");
128 config_free_lines(list
);
134 unsigned command
= CONFIG_LINE_NORMAL
;
137 char *k_new
= tor_strdup(k
+1);
140 command
= CONFIG_LINE_APPEND
;
141 } else if (k
[0] == '/') {
142 char *k_new
= tor_strdup(k
+1);
147 command
= CONFIG_LINE_CLEAR
;
151 if (allow_include
&& !strcmp(k
, "%include") && handle_include
) {
154 log_notice(LD_CONFIG
, "Processing configuration path \"%s\" at "
155 "recursion level %d.", v
, recursion_level
);
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
);
166 *next
= include_list
;
168 next
= &list_last
->next
;
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
));
177 (*next
)->next
= NULL
;
178 (*next
)->command
= command
;
180 next
= &((*next
)->next
);
192 *has_include
= include_used
;
198 /** Same as config_get_lines_include but does not allow %include */
200 config_get_lines(const char *string
, config_line_t
**result
, int extended
)
202 return config_get_lines_aux(string
, result
, extended
, 0, NULL
, NULL
, 1,
207 * Free all the configuration lines on the linked list <b>front</b>.
210 config_free_lines_(config_line_t
*front
)
219 tor_free(tmp
->value
);
224 /** Return a newly allocated deep copy of the lines in <b>inp</b>. */
226 config_lines_dup(const config_line_t
*inp
)
228 return config_lines_dup_and_filter(inp
, NULL
);
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. */
235 config_lines_dup_and_filter(const config_line_t
*inp
,
238 config_line_t
*result
= NULL
;
239 config_line_t
**next_out
= &result
;
241 if (key
&& strcasecmpstart(inp
->key
, key
)) {
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
);
249 next_out
= &((*next_out
)->next
);
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.
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.
266 config_lines_partition(config_line_t
*inp
, const char *header
)
268 if (BUG(inp
== NULL
))
270 if (BUG(strcasecmp(inp
->key
, header
)))
273 /* Advance ptr until it points to the link to the next segment of this
275 config_line_t
**ptr
= &inp
->next
;
276 while (*ptr
&& strcasecmp((*ptr
)->key
, header
)) {
279 config_line_t
*remainder
= *ptr
;
284 /** Return true iff a and b contain identical keys and values in identical
287 config_lines_eq(const config_line_t
*a
, const config_line_t
*b
)
290 if (strcasecmp(a
->key
, b
->key
) || strcmp(a
->value
, b
->value
))
300 /** Return the number of lines in <b>a</b> whose key is <b>key</b>. */
302 config_count_key(const config_line_t
*a
, const char *key
)
306 if (!strcasecmp(a
->key
, key
)) {
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
324 parse_config_line_from_str_verbose(const char *line
, char **key_out
,
326 const char **err_out
)
329 See torrc_format.txt for a description of the (silly) format this parses.
331 const char *key
, *val
, *cp
;
332 int continuation
= 0;
335 tor_assert(value_out
);
337 *key_out
= *value_out
= NULL
;
339 /* Skip until the first keyword. */
341 while (TOR_ISSPACE(*line
))
344 while (*line
&& *line
!= '\n')
351 if (!*line
) { /* End of string? */
352 *key_out
= *value_out
= NULL
;
356 /* Skip until the next space or \ followed by newline. */
358 while (*line
&& !TOR_ISSPACE(*line
) && *line
!= '#' &&
359 ! (line
[0] == '\\' && line
[1] == '\n'))
361 *key_out
= tor_strndup(key
, line
-key
);
363 /* Skip until the value. */
364 while (*line
== ' ' || *line
== '\t')
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
))) {
373 *err_out
= "Invalid escape sequence in quoted string";
376 while (*line
== ' ' || *line
== '\t')
378 if (*line
== '\r' && *(++line
) == '\n')
380 if (*line
&& *line
!= '#' && *line
!= '\n') {
382 *err_out
= "Excess data after quoted string";
386 /* Look for the end of the line. */
387 while (*line
&& *line
!= '\n' && (*line
!= '#' || continuation
)) {
388 if (*line
== '\\' && line
[1] == '\n') {
391 } else if (*line
== '#') {
394 } while (*line
&& *line
!= '\n');
407 /* Now back cp up to be the last nonspace character */
408 while (cp
>val
&& TOR_ISSPACE(*(cp
-1)))
411 tor_assert(cp
>= val
);
413 /* Now copy out and decode the value. */
414 *value_out
= tor_strndup(val
, cp
-val
);
417 v_out
= v_in
= *value_out
;
422 } while (*v_in
&& *v_in
!= '\n');
425 } else if (v_in
[0] == '\\' && v_in
[1] == '\n') {
438 } while (*line
&& *line
!= '\n');
440 while (TOR_ISSPACE(*line
)) ++line
;