parser.y 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. /*
  2. * Copyright (c) 2019 Markus Hennecke. All rights reserved.
  3. * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
  4. * Copyright (c) 2001 Markus Friedl. All rights reserved.
  5. * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
  6. * Copyright (c) 2001 Theo de Raadt. All rights reserved.
  7. *
  8. * Permission to use, copy, modify, and distribute this software for any
  9. * purpose with or without fee is hereby granted, provided that the above
  10. * copyright notice and this permission notice appear in all copies.
  11. *
  12. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  13. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  14. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  15. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  16. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  17. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  18. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  19. */
  20. %{
  21. #include <ctype.h>
  22. #include <stdio.h>
  23. #include <stdarg.h>
  24. #include <stdlib.h>
  25. #include <limits.h>
  26. #include "dnsbl.h"
  27. #include "log.h"
  28. int yyerror(const char *, ...);
  29. int yylex(void);
  30. int lookup(char *);
  31. int kw_cmp(const void *, const void *);
  32. int lgetc(int);
  33. int lungetc(int);
  34. int findeol(void);
  35. FILE *file;
  36. int lineno;
  37. struct config *cfg;
  38. typedef struct {
  39. char *string;
  40. int lineno;
  41. } YYSTYPE;
  42. %}
  43. %token SET AUTOWHITELIST OFF ON
  44. %token LIST HAS DNS
  45. %token SPAMD LOGFILE IS STRING
  46. %token ERROR
  47. %token <string> STRING
  48. %%
  49. grammar : /* empty */
  50. | grammar '\n'
  51. | grammar setting '\n'
  52. | grammar dnsbl '\n'
  53. | grammar logfile '\n'
  54. ;
  55. logfile : SPAMD LOGFILE IS STRING {
  56. if (cfg->logfile) {
  57. yyerror("multiple \"logfile\" entries");
  58. } else {
  59. cfg->logfile = strdup($4);
  60. }
  61. if (cfg->logfile == NULL)
  62. fatal(NULL);
  63. }
  64. ;
  65. dnsbl : LIST STRING HAS DNS STRING {
  66. config_add_spamlist(cfg, $2, $5);
  67. }
  68. ;
  69. setting : SET AUTOWHITELIST OFF {
  70. cfg->autowhitelist = 0;
  71. }
  72. | SET AUTOWHITELIST ON {
  73. cfg->autowhitelist = 1;
  74. }
  75. ;
  76. %%
  77. int
  78. yyerror(const char *fmt, ...)
  79. {
  80. va_list ap;
  81. char *msg;
  82. va_start(ap, fmt);
  83. if (vasprintf(&msg, fmt, ap) == -1)
  84. fatalx("yyerror vasprintf");
  85. va_end(ap);
  86. log_warnx("ERROR: %s in line %d", msg, lineno);
  87. return 0;
  88. }
  89. struct keywords {
  90. const char *k_name;
  91. int k_val;
  92. };
  93. int
  94. kw_cmp(const void *k, const void *e)
  95. {
  96. return (strcmp(k, ((const struct keywords *)e)->k_name));
  97. }
  98. int
  99. lookup(char *s)
  100. {
  101. /* must be sorted */
  102. static const struct keywords keywords[] = {
  103. { "autowhitelist", AUTOWHITELIST },
  104. { "dns", DNS },
  105. { "has", HAS },
  106. { "is", IS },
  107. { "list", LIST },
  108. { "logfile", LOGFILE },
  109. { "off", OFF },
  110. { "on", ON },
  111. { "set", SET },
  112. { "spamd", SPAMD },
  113. };
  114. const struct keywords *p;
  115. p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
  116. sizeof(keywords[0]), kw_cmp);
  117. if (p)
  118. return (p->k_val);
  119. else
  120. return (STRING);
  121. }
  122. #define MAXPUSHBACK 128
  123. u_char *parsebuf;
  124. int parseindex;
  125. u_char pushback_buffer[MAXPUSHBACK];
  126. int pushback_index = 0;
  127. int
  128. lgetc(int quotec)
  129. {
  130. int c, next;
  131. if (parsebuf) {
  132. /* Read character from the parsebuffer instead of input. */
  133. if (parseindex >= 0) {
  134. c = parsebuf[parseindex++];
  135. if (c != '\0')
  136. return (c);
  137. parsebuf = NULL;
  138. } else
  139. parseindex++;
  140. }
  141. if (pushback_index)
  142. return (pushback_buffer[--pushback_index]);
  143. if (quotec) {
  144. if ((c = getc(file)) == EOF) {
  145. yyerror("reached end of file while parsing "
  146. "quoted string");
  147. return (EOF);
  148. }
  149. return (c);
  150. }
  151. while ((c = getc(file)) == '\\') {
  152. next = getc(file);
  153. if (next != '\n') {
  154. c = next;
  155. break;
  156. }
  157. yylval.lineno = lineno;
  158. lineno++;
  159. }
  160. return (c);
  161. }
  162. int
  163. lungetc(int c)
  164. {
  165. if (c == EOF)
  166. return (EOF);
  167. if (parsebuf) {
  168. parseindex--;
  169. if (parseindex >= 0)
  170. return (c);
  171. }
  172. if (pushback_index < MAXPUSHBACK-1)
  173. return (pushback_buffer[pushback_index++] = c);
  174. else
  175. return (EOF);
  176. }
  177. int
  178. findeol(void)
  179. {
  180. int c;
  181. parsebuf = NULL;
  182. /* skip to either EOF or the first real EOL */
  183. while (1) {
  184. if (pushback_index)
  185. c = pushback_buffer[--pushback_index];
  186. else
  187. c = lgetc(0);
  188. if (c == '\n') {
  189. lineno++;
  190. break;
  191. }
  192. if (c == EOF)
  193. break;
  194. }
  195. return (ERROR);
  196. }
  197. int
  198. yylex(void)
  199. {
  200. u_char buf[8096];
  201. u_char *p;
  202. int quotec, next, c;
  203. int token;
  204. p = buf;
  205. while ((c = lgetc(0)) == ' ' || c == '\t')
  206. ; /* nothing */
  207. yylval.lineno = lineno;
  208. if (c == '#')
  209. while ((c = lgetc(0)) != '\n' && c != EOF)
  210. ; /* nothing */
  211. switch (c) {
  212. case '\'':
  213. case '"':
  214. quotec = c;
  215. while (1) {
  216. if ((c = lgetc(quotec)) == EOF)
  217. return (0);
  218. if (c == '\n') {
  219. lineno++;
  220. continue;
  221. } else if (c == '\\') {
  222. if ((next = lgetc(quotec)) == EOF)
  223. return (0);
  224. if (next == quotec || c == ' ' || c == '\t')
  225. c = next;
  226. else if (next == '\n') {
  227. lineno++;
  228. continue;
  229. } else
  230. lungetc(next);
  231. } else if (c == quotec) {
  232. *p = '\0';
  233. break;
  234. } else if (c == '\0') {
  235. yyerror("syntax error");
  236. return (findeol());
  237. }
  238. if (p + 1 >= buf + sizeof(buf) - 1) {
  239. yyerror("string too long");
  240. return (findeol());
  241. }
  242. *p++ = c;
  243. }
  244. yylval.string = strdup(buf);
  245. if (yylval.string == NULL)
  246. fatal("yylex: strdup");
  247. return (STRING);
  248. }
  249. #define allowed_in_string(x) (isalnum(x) || ispunct(x))
  250. if (isalnum(c) || c == ':' || c == '_' || c == '*') {
  251. do {
  252. *p++ = c;
  253. if ((unsigned)(p-buf) >= sizeof(buf)) {
  254. yyerror("string too long");
  255. return (findeol());
  256. }
  257. } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
  258. lungetc(c);
  259. *p = '\0';
  260. if ((token = lookup(buf)) == STRING)
  261. if ((yylval.string = strdup(buf)) == NULL)
  262. fatal("yylex: strdup");
  263. return (token);
  264. }
  265. if (c == '\n') {
  266. yylval.lineno = lineno;
  267. lineno++;
  268. }
  269. if (c == EOF)
  270. return (0);
  271. return (c);
  272. }
  273. bool
  274. config_parse(struct config *c, const char *filename)
  275. {
  276. log_debug("Parsing config file %s", filename);
  277. cfg = c;
  278. file = fopen(filename, "r");
  279. if (file == NULL) {
  280. log_warn("parse_config %s", filename);
  281. return false;
  282. }
  283. cfg->config_file = strdup(filename);
  284. if (cfg->config_file == NULL)
  285. fatal("config_parse");
  286. lineno = 1;
  287. yyparse();
  288. fclose(file);
  289. return config_check(c);
  290. }