confline.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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-2017, The Tor Project, Inc. */
  5. /* See LICENSE for licensing information */
  6. #include "compat.h"
  7. #include "confline.h"
  8. #include "torlog.h"
  9. #include "util.h"
  10. /** Helper: allocate a new configuration option mapping 'key' to 'val',
  11. * append it to *<b>lst</b>. */
  12. void
  13. config_line_append(config_line_t **lst,
  14. const char *key,
  15. const char *val)
  16. {
  17. config_line_t *newline;
  18. newline = tor_malloc_zero(sizeof(config_line_t));
  19. newline->key = tor_strdup(key);
  20. newline->value = tor_strdup(val);
  21. newline->next = NULL;
  22. while (*lst)
  23. lst = &((*lst)->next);
  24. (*lst) = newline;
  25. }
  26. /** Return the line in <b>lines</b> whose key is exactly <b>key</b>, or NULL
  27. * if no such key exists. For handling commandline-only options only; other
  28. * options should be looked up in the appropriate data structure. */
  29. const config_line_t *
  30. config_line_find(const config_line_t *lines,
  31. const char *key)
  32. {
  33. const config_line_t *cl;
  34. for (cl = lines; cl; cl = cl->next) {
  35. if (!strcmp(cl->key, key))
  36. return cl;
  37. }
  38. return NULL;
  39. }
  40. /** Helper: parse the config string and strdup into key/value
  41. * strings. Set *result to the list, or NULL if parsing the string
  42. * failed. Return 0 on success, -1 on failure. Warn and ignore any
  43. * misformatted lines.
  44. *
  45. * If <b>extended</b> is set, then treat keys beginning with / and with + as
  46. * indicating "clear" and "append" respectively. */
  47. int
  48. config_get_lines(const char *string, config_line_t **result, int extended)
  49. {
  50. config_line_t *list = NULL, **next;
  51. char *k, *v;
  52. const char *parse_err;
  53. next = &list;
  54. do {
  55. k = v = NULL;
  56. string = parse_config_line_from_str_verbose(string, &k, &v, &parse_err);
  57. if (!string) {
  58. log_warn(LD_CONFIG, "Error while parsing configuration: %s",
  59. parse_err?parse_err:"<unknown>");
  60. config_free_lines(list);
  61. tor_free(k);
  62. tor_free(v);
  63. return -1;
  64. }
  65. if (k && v) {
  66. unsigned command = CONFIG_LINE_NORMAL;
  67. if (extended) {
  68. if (k[0] == '+') {
  69. char *k_new = tor_strdup(k+1);
  70. tor_free(k);
  71. k = k_new;
  72. command = CONFIG_LINE_APPEND;
  73. } else if (k[0] == '/') {
  74. char *k_new = tor_strdup(k+1);
  75. tor_free(k);
  76. k = k_new;
  77. tor_free(v);
  78. v = tor_strdup("");
  79. command = CONFIG_LINE_CLEAR;
  80. }
  81. }
  82. /* This list can get long, so we keep a pointer to the end of it
  83. * rather than using config_line_append over and over and getting
  84. * n^2 performance. */
  85. *next = tor_malloc_zero(sizeof(config_line_t));
  86. (*next)->key = k;
  87. (*next)->value = v;
  88. (*next)->next = NULL;
  89. (*next)->command = command;
  90. next = &((*next)->next);
  91. } else {
  92. tor_free(k);
  93. tor_free(v);
  94. }
  95. } while (*string);
  96. *result = list;
  97. return 0;
  98. }
  99. /**
  100. * Free all the configuration lines on the linked list <b>front</b>.
  101. */
  102. void
  103. config_free_lines(config_line_t *front)
  104. {
  105. config_line_t *tmp;
  106. while (front) {
  107. tmp = front;
  108. front = tmp->next;
  109. tor_free(tmp->key);
  110. tor_free(tmp->value);
  111. tor_free(tmp);
  112. }
  113. }
  114. /** Return a newly allocated deep copy of the lines in <b>inp</b>. */
  115. config_line_t *
  116. config_lines_dup(const config_line_t *inp)
  117. {
  118. return config_lines_dup_and_filter(inp, NULL);
  119. }
  120. /** Return a newly allocated deep copy of the lines in <b>inp</b>,
  121. * but only the ones that match <b>key</b>. */
  122. config_line_t *
  123. config_lines_dup_and_filter(const config_line_t *inp,
  124. const char *key)
  125. {
  126. config_line_t *result = NULL;
  127. config_line_t **next_out = &result;
  128. while (inp) {
  129. if (key && strcasecmpstart(inp->key, key)) {
  130. inp = inp->next;
  131. continue;
  132. }
  133. *next_out = tor_malloc_zero(sizeof(config_line_t));
  134. (*next_out)->key = tor_strdup(inp->key);
  135. (*next_out)->value = tor_strdup(inp->value);
  136. inp = inp->next;
  137. next_out = &((*next_out)->next);
  138. }
  139. (*next_out) = NULL;
  140. return result;
  141. }
  142. /** Return true iff a and b contain identical keys and values in identical
  143. * order. */
  144. int
  145. config_lines_eq(config_line_t *a, config_line_t *b)
  146. {
  147. while (a && b) {
  148. if (strcasecmp(a->key, b->key) || strcmp(a->value, b->value))
  149. return 0;
  150. a = a->next;
  151. b = b->next;
  152. }
  153. if (a || b)
  154. return 0;
  155. return 1;
  156. }
  157. /** Return the number of lines in <b>a</b> whose key is <b>key</b>. */
  158. int
  159. config_count_key(const config_line_t *a, const char *key)
  160. {
  161. int n = 0;
  162. while (a) {
  163. if (!strcasecmp(a->key, key)) {
  164. ++n;
  165. }
  166. a = a->next;
  167. }
  168. return n;
  169. }
  170. /** Given a string containing part of a configuration file or similar format,
  171. * advance past comments and whitespace and try to parse a single line. If we
  172. * parse a line successfully, set *<b>key_out</b> to a new string holding the
  173. * key portion and *<b>value_out</b> to a new string holding the value portion
  174. * of the line, and return a pointer to the start of the next line. If we run
  175. * out of data, return a pointer to the end of the string. If we encounter an
  176. * error, return NULL and set *<b>err_out</b> (if provided) to an error
  177. * message.
  178. */
  179. const char *
  180. parse_config_line_from_str_verbose(const char *line, char **key_out,
  181. char **value_out,
  182. const char **err_out)
  183. {
  184. /*
  185. See torrc_format.txt for a description of the (silly) format this parses.
  186. */
  187. const char *key, *val, *cp;
  188. int continuation = 0;
  189. tor_assert(key_out);
  190. tor_assert(value_out);
  191. *key_out = *value_out = NULL;
  192. key = val = NULL;
  193. /* Skip until the first keyword. */
  194. while (1) {
  195. while (TOR_ISSPACE(*line))
  196. ++line;
  197. if (*line == '#') {
  198. while (*line && *line != '\n')
  199. ++line;
  200. } else {
  201. break;
  202. }
  203. }
  204. if (!*line) { /* End of string? */
  205. *key_out = *value_out = NULL;
  206. return line;
  207. }
  208. /* Skip until the next space or \ followed by newline. */
  209. key = line;
  210. while (*line && !TOR_ISSPACE(*line) && *line != '#' &&
  211. ! (line[0] == '\\' && line[1] == '\n'))
  212. ++line;
  213. *key_out = tor_strndup(key, line-key);
  214. /* Skip until the value. */
  215. while (*line == ' ' || *line == '\t')
  216. ++line;
  217. val = line;
  218. /* Find the end of the line. */
  219. if (*line == '\"') { // XXX No continuation handling is done here
  220. if (!(line = unescape_string(line, value_out, NULL))) {
  221. if (err_out)
  222. *err_out = "Invalid escape sequence in quoted string";
  223. return NULL;
  224. }
  225. while (*line == ' ' || *line == '\t')
  226. ++line;
  227. if (*line == '\r' && *(++line) == '\n')
  228. ++line;
  229. if (*line && *line != '#' && *line != '\n') {
  230. if (err_out)
  231. *err_out = "Excess data after quoted string";
  232. return NULL;
  233. }
  234. } else {
  235. /* Look for the end of the line. */
  236. while (*line && *line != '\n' && (*line != '#' || continuation)) {
  237. if (*line == '\\' && line[1] == '\n') {
  238. continuation = 1;
  239. line += 2;
  240. } else if (*line == '#') {
  241. do {
  242. ++line;
  243. } while (*line && *line != '\n');
  244. if (*line == '\n')
  245. ++line;
  246. } else {
  247. ++line;
  248. }
  249. }
  250. if (*line == '\n') {
  251. cp = line++;
  252. } else {
  253. cp = line;
  254. }
  255. /* Now back cp up to be the last nonspace character */
  256. while (cp>val && TOR_ISSPACE(*(cp-1)))
  257. --cp;
  258. tor_assert(cp >= val);
  259. /* Now copy out and decode the value. */
  260. *value_out = tor_strndup(val, cp-val);
  261. if (continuation) {
  262. char *v_out, *v_in;
  263. v_out = v_in = *value_out;
  264. while (*v_in) {
  265. if (*v_in == '#') {
  266. do {
  267. ++v_in;
  268. } while (*v_in && *v_in != '\n');
  269. if (*v_in == '\n')
  270. ++v_in;
  271. } else if (v_in[0] == '\\' && v_in[1] == '\n') {
  272. v_in += 2;
  273. } else {
  274. *v_out++ = *v_in++;
  275. }
  276. }
  277. *v_out = '\0';
  278. }
  279. }
  280. if (*line == '#') {
  281. do {
  282. ++line;
  283. } while (*line && *line != '\n');
  284. }
  285. while (TOR_ISSPACE(*line)) ++line;
  286. return line;
  287. }