confline.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  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-2018, The Tor Project, Inc. */
  5. /* See LICENSE for licensing information */
  6. #include "common/compat.h"
  7. #include "common/confline.h"
  8. #include "common/torlog.h"
  9. #include "common/util.h"
  10. #include "common/container.h"
  11. static int config_get_lines_aux(const char *string, config_line_t **result,
  12. int extended, int allow_include,
  13. int *has_include, smartlist_t *opened_lst,
  14. int recursion_level, config_line_t **last);
  15. static smartlist_t *config_get_file_list(const char *path,
  16. smartlist_t *opened_files);
  17. static int config_get_included_config(const char *path, int recursion_level,
  18. int extended, config_line_t **config,
  19. config_line_t **config_last,
  20. smartlist_t *opened_lst);
  21. static int config_process_include(const char *path, int recursion_level,
  22. int extended, config_line_t **list,
  23. config_line_t **list_last,
  24. smartlist_t *opened_lst);
  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. /** Auxiliary function that does all the work of config_get_lines.
  74. * <b>recursion_level</b> is the count of how many nested %includes we have.
  75. * <b>opened_lst</b> will have a list of opened files if provided.
  76. * Returns the a pointer to the last element of the <b>result</b> in
  77. * <b>last</b>. */
  78. static int
  79. config_get_lines_aux(const char *string, config_line_t **result, int extended,
  80. int allow_include, int *has_include,
  81. smartlist_t *opened_lst, int recursion_level,
  82. config_line_t **last)
  83. {
  84. config_line_t *list = NULL, **next, *list_last = NULL;
  85. char *k, *v;
  86. const char *parse_err;
  87. int include_used = 0;
  88. if (recursion_level > MAX_INCLUDE_RECURSION_LEVEL) {
  89. log_warn(LD_CONFIG, "Error while parsing configuration: more than %d "
  90. "nested %%includes.", MAX_INCLUDE_RECURSION_LEVEL);
  91. return -1;
  92. }
  93. next = &list;
  94. do {
  95. k = v = NULL;
  96. string = parse_config_line_from_str_verbose(string, &k, &v, &parse_err);
  97. if (!string) {
  98. log_warn(LD_CONFIG, "Error while parsing configuration: %s",
  99. parse_err?parse_err:"<unknown>");
  100. config_free_lines(list);
  101. tor_free(k);
  102. tor_free(v);
  103. return -1;
  104. }
  105. if (k && v) {
  106. unsigned command = CONFIG_LINE_NORMAL;
  107. if (extended) {
  108. if (k[0] == '+') {
  109. char *k_new = tor_strdup(k+1);
  110. tor_free(k);
  111. k = k_new;
  112. command = CONFIG_LINE_APPEND;
  113. } else if (k[0] == '/') {
  114. char *k_new = tor_strdup(k+1);
  115. tor_free(k);
  116. k = k_new;
  117. tor_free(v);
  118. v = tor_strdup("");
  119. command = CONFIG_LINE_CLEAR;
  120. }
  121. }
  122. if (allow_include && !strcmp(k, "%include")) {
  123. tor_free(k);
  124. include_used = 1;
  125. config_line_t *include_list;
  126. if (config_process_include(v, recursion_level, extended, &include_list,
  127. &list_last, opened_lst) < 0) {
  128. log_warn(LD_CONFIG, "Error reading included configuration "
  129. "file or directory: \"%s\".", v);
  130. config_free_lines(list);
  131. tor_free(v);
  132. return -1;
  133. }
  134. *next = include_list;
  135. if (list_last)
  136. next = &list_last->next;
  137. tor_free(v);
  138. } else {
  139. /* This list can get long, so we keep a pointer to the end of it
  140. * rather than using config_line_append over and over and getting
  141. * n^2 performance. */
  142. *next = tor_malloc_zero(sizeof(**next));
  143. (*next)->key = k;
  144. (*next)->value = v;
  145. (*next)->next = NULL;
  146. (*next)->command = command;
  147. list_last = *next;
  148. next = &((*next)->next);
  149. }
  150. } else {
  151. tor_free(k);
  152. tor_free(v);
  153. }
  154. } while (*string);
  155. if (last) {
  156. *last = list_last;
  157. }
  158. if (has_include) {
  159. *has_include = include_used;
  160. }
  161. *result = list;
  162. return 0;
  163. }
  164. /** Helper: parse the config string and strdup into key/value
  165. * strings. Set *result to the list, or NULL if parsing the string
  166. * failed. Set *has_include to 1 if <b>result</b> has values from
  167. * %included files. <b>opened_lst</b> will have a list of opened files if
  168. * provided. Return 0 on success, -1 on failure. Warn and ignore any
  169. * misformatted lines.
  170. *
  171. * If <b>extended</b> is set, then treat keys beginning with / and with + as
  172. * indicating "clear" and "append" respectively. */
  173. int
  174. config_get_lines_include(const char *string, config_line_t **result,
  175. int extended, int *has_include,
  176. smartlist_t *opened_lst)
  177. {
  178. return config_get_lines_aux(string, result, extended, 1, has_include,
  179. opened_lst, 1, NULL);
  180. }
  181. /** Same as config_get_lines_include but does not allow %include */
  182. int
  183. config_get_lines(const char *string, config_line_t **result, int extended)
  184. {
  185. return config_get_lines_aux(string, result, extended, 0, NULL, NULL, 1,
  186. NULL);
  187. }
  188. /** Adds a list of configuration files present on <b>path</b> to
  189. * <b>file_list</b>. <b>path</b> can be a file or a directory. If it is a file,
  190. * only that file will be added to <b>file_list</b>. If it is a directory,
  191. * all paths for files on that directory root (no recursion) except for files
  192. * whose name starts with a dot will be added to <b>file_list</b>.
  193. * <b>opened_files</b> will have a list of files opened by this function
  194. * if provided. Return 0 on success, -1 on failure. Ignores empty files.
  195. */
  196. static smartlist_t *
  197. config_get_file_list(const char *path, smartlist_t *opened_files)
  198. {
  199. smartlist_t *file_list = smartlist_new();
  200. if (opened_files) {
  201. smartlist_add_strdup(opened_files, path);
  202. }
  203. file_status_t file_type = file_status(path);
  204. if (file_type == FN_FILE) {
  205. smartlist_add_strdup(file_list, path);
  206. return file_list;
  207. } else if (file_type == FN_DIR) {
  208. smartlist_t *all_files = tor_listdir(path);
  209. if (!all_files) {
  210. smartlist_free(file_list);
  211. return NULL;
  212. }
  213. smartlist_sort_strings(all_files);
  214. SMARTLIST_FOREACH_BEGIN(all_files, char *, f) {
  215. if (f[0] == '.') {
  216. tor_free(f);
  217. continue;
  218. }
  219. char *fullname;
  220. tor_asprintf(&fullname, "%s"PATH_SEPARATOR"%s", path, f);
  221. tor_free(f);
  222. if (opened_files) {
  223. smartlist_add_strdup(opened_files, fullname);
  224. }
  225. if (file_status(fullname) != FN_FILE) {
  226. tor_free(fullname);
  227. continue;
  228. }
  229. smartlist_add(file_list, fullname);
  230. } SMARTLIST_FOREACH_END(f);
  231. smartlist_free(all_files);
  232. return file_list;
  233. } else if (file_type == FN_EMPTY) {
  234. return file_list;
  235. } else {
  236. smartlist_free(file_list);
  237. return NULL;
  238. }
  239. }
  240. /** Creates a list of config lines present on included <b>path</b>.
  241. * Set <b>config</b> to the list and <b>config_last</b> to the last element of
  242. * <b>config</b>. <b>opened_lst</b> will have a list of opened files if
  243. * provided. Return 0 on success, -1 on failure. */
  244. static int
  245. config_get_included_config(const char *path, int recursion_level, int extended,
  246. config_line_t **config, config_line_t **config_last,
  247. smartlist_t *opened_lst)
  248. {
  249. char *included_conf = read_file_to_str(path, 0, NULL);
  250. if (!included_conf) {
  251. return -1;
  252. }
  253. if (config_get_lines_aux(included_conf, config, extended, 1, NULL,
  254. opened_lst, recursion_level+1, config_last) < 0) {
  255. tor_free(included_conf);
  256. return -1;
  257. }
  258. tor_free(included_conf);
  259. return 0;
  260. }
  261. /** Process an %include <b>path</b> in a config file. Set <b>list</b> to the
  262. * list of configuration settings obtained and <b>list_last</b> to the last
  263. * element of the same list. <b>opened_lst</b> will have a list of opened
  264. * files if provided. Return 0 on success, -1 on failure. */
  265. static int
  266. config_process_include(const char *path, int recursion_level, int extended,
  267. config_line_t **list, config_line_t **list_last,
  268. smartlist_t *opened_lst)
  269. {
  270. config_line_t *ret_list = NULL;
  271. config_line_t **next = &ret_list;
  272. smartlist_t *config_files = config_get_file_list(path, opened_lst);
  273. if (!config_files) {
  274. return -1;
  275. }
  276. int rv = -1;
  277. SMARTLIST_FOREACH_BEGIN(config_files, const char *, config_file) {
  278. config_line_t *included_config = NULL;
  279. if (config_get_included_config(config_file, recursion_level, extended,
  280. &included_config, list_last,
  281. opened_lst) < 0) {
  282. goto done;
  283. }
  284. *next = included_config;
  285. if (*list_last)
  286. next = &(*list_last)->next;
  287. } SMARTLIST_FOREACH_END(config_file);
  288. *list = ret_list;
  289. rv = 0;
  290. done:
  291. SMARTLIST_FOREACH(config_files, char *, f, tor_free(f));
  292. smartlist_free(config_files);
  293. return rv;
  294. }
  295. /**
  296. * Free all the configuration lines on the linked list <b>front</b>.
  297. */
  298. void
  299. config_free_lines_(config_line_t *front)
  300. {
  301. config_line_t *tmp;
  302. while (front) {
  303. tmp = front;
  304. front = tmp->next;
  305. tor_free(tmp->key);
  306. tor_free(tmp->value);
  307. tor_free(tmp);
  308. }
  309. }
  310. /** Return a newly allocated deep copy of the lines in <b>inp</b>. */
  311. config_line_t *
  312. config_lines_dup(const config_line_t *inp)
  313. {
  314. return config_lines_dup_and_filter(inp, NULL);
  315. }
  316. /** Return a newly allocated deep copy of the lines in <b>inp</b>,
  317. * but only the ones whose keys begin with <b>key</b> (case-insensitive).
  318. * If <b>key</b> is NULL, do not filter. */
  319. config_line_t *
  320. config_lines_dup_and_filter(const config_line_t *inp,
  321. const char *key)
  322. {
  323. config_line_t *result = NULL;
  324. config_line_t **next_out = &result;
  325. while (inp) {
  326. if (key && strcasecmpstart(inp->key, key)) {
  327. inp = inp->next;
  328. continue;
  329. }
  330. *next_out = tor_malloc_zero(sizeof(config_line_t));
  331. (*next_out)->key = tor_strdup(inp->key);
  332. (*next_out)->value = tor_strdup(inp->value);
  333. inp = inp->next;
  334. next_out = &((*next_out)->next);
  335. }
  336. (*next_out) = NULL;
  337. return result;
  338. }
  339. /** Return true iff a and b contain identical keys and values in identical
  340. * order. */
  341. int
  342. config_lines_eq(config_line_t *a, config_line_t *b)
  343. {
  344. while (a && b) {
  345. if (strcasecmp(a->key, b->key) || strcmp(a->value, b->value))
  346. return 0;
  347. a = a->next;
  348. b = b->next;
  349. }
  350. if (a || b)
  351. return 0;
  352. return 1;
  353. }
  354. /** Return the number of lines in <b>a</b> whose key is <b>key</b>. */
  355. int
  356. config_count_key(const config_line_t *a, const char *key)
  357. {
  358. int n = 0;
  359. while (a) {
  360. if (!strcasecmp(a->key, key)) {
  361. ++n;
  362. }
  363. a = a->next;
  364. }
  365. return n;
  366. }
  367. /** Given a string containing part of a configuration file or similar format,
  368. * advance past comments and whitespace and try to parse a single line. If we
  369. * parse a line successfully, set *<b>key_out</b> to a new string holding the
  370. * key portion and *<b>value_out</b> to a new string holding the value portion
  371. * of the line, and return a pointer to the start of the next line. If we run
  372. * out of data, return a pointer to the end of the string. If we encounter an
  373. * error, return NULL and set *<b>err_out</b> (if provided) to an error
  374. * message.
  375. */
  376. const char *
  377. parse_config_line_from_str_verbose(const char *line, char **key_out,
  378. char **value_out,
  379. const char **err_out)
  380. {
  381. /*
  382. See torrc_format.txt for a description of the (silly) format this parses.
  383. */
  384. const char *key, *val, *cp;
  385. int continuation = 0;
  386. tor_assert(key_out);
  387. tor_assert(value_out);
  388. *key_out = *value_out = NULL;
  389. key = val = NULL;
  390. /* Skip until the first keyword. */
  391. while (1) {
  392. while (TOR_ISSPACE(*line))
  393. ++line;
  394. if (*line == '#') {
  395. while (*line && *line != '\n')
  396. ++line;
  397. } else {
  398. break;
  399. }
  400. }
  401. if (!*line) { /* End of string? */
  402. *key_out = *value_out = NULL;
  403. return line;
  404. }
  405. /* Skip until the next space or \ followed by newline. */
  406. key = line;
  407. while (*line && !TOR_ISSPACE(*line) && *line != '#' &&
  408. ! (line[0] == '\\' && line[1] == '\n'))
  409. ++line;
  410. *key_out = tor_strndup(key, line-key);
  411. /* Skip until the value. */
  412. while (*line == ' ' || *line == '\t')
  413. ++line;
  414. val = line;
  415. /* Find the end of the line. */
  416. if (*line == '\"') { // XXX No continuation handling is done here
  417. if (!(line = unescape_string(line, value_out, NULL))) {
  418. if (err_out)
  419. *err_out = "Invalid escape sequence in quoted string";
  420. return NULL;
  421. }
  422. while (*line == ' ' || *line == '\t')
  423. ++line;
  424. if (*line == '\r' && *(++line) == '\n')
  425. ++line;
  426. if (*line && *line != '#' && *line != '\n') {
  427. if (err_out)
  428. *err_out = "Excess data after quoted string";
  429. return NULL;
  430. }
  431. } else {
  432. /* Look for the end of the line. */
  433. while (*line && *line != '\n' && (*line != '#' || continuation)) {
  434. if (*line == '\\' && line[1] == '\n') {
  435. continuation = 1;
  436. line += 2;
  437. } else if (*line == '#') {
  438. do {
  439. ++line;
  440. } while (*line && *line != '\n');
  441. if (*line == '\n')
  442. ++line;
  443. } else {
  444. ++line;
  445. }
  446. }
  447. if (*line == '\n') {
  448. cp = line++;
  449. } else {
  450. cp = line;
  451. }
  452. /* Now back cp up to be the last nonspace character */
  453. while (cp>val && TOR_ISSPACE(*(cp-1)))
  454. --cp;
  455. tor_assert(cp >= val);
  456. /* Now copy out and decode the value. */
  457. *value_out = tor_strndup(val, cp-val);
  458. if (continuation) {
  459. char *v_out, *v_in;
  460. v_out = v_in = *value_out;
  461. while (*v_in) {
  462. if (*v_in == '#') {
  463. do {
  464. ++v_in;
  465. } while (*v_in && *v_in != '\n');
  466. if (*v_in == '\n')
  467. ++v_in;
  468. } else if (v_in[0] == '\\' && v_in[1] == '\n') {
  469. v_in += 2;
  470. } else {
  471. *v_out++ = *v_in++;
  472. }
  473. }
  474. *v_out = '\0';
  475. }
  476. }
  477. if (*line == '#') {
  478. do {
  479. ++line;
  480. } while (*line && *line != '\n');
  481. }
  482. while (TOR_ISSPACE(*line)) ++line;
  483. return line;
  484. }