confline.c 11 KB

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