confline.c 8.1 KB

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