confline.c 8.5 KB

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