confline.c 8.1 KB

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