confline.c (12249B)
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" 23 #include "lib/string/compat_ctype.h" 24 #include "lib/string/compat_string.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 32 config_line_append(config_line_t **lst, 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 53 config_line_prepend(config_line_t **lst, 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 * 74 config_line_find(const config_line_t *lines, 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 * 87 config_line_find_case(const config_line_t *lines, 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; 140 command = CONFIG_LINE_APPEND; 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 210 config_free_lines_(config_line_t *front) 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>. */ 225 config_line_t * 226 config_lines_dup(const config_line_t *inp) 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. */ 234 config_line_t * 235 config_lines_dup_and_filter(const config_line_t *inp, 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 **/ 265 config_line_t * 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 287 config_lines_eq(const config_line_t *a, const config_line_t *b) 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 }