xref: /freebsd/sbin/ipf/common/lexer.c (revision b985c9ca)
1 
2 /*
3  * Copyright (C) 2012 by Darren Reed.
4  *
5  * See the IPFILTER.LICENCE file for details on licencing.
6  */
7 #include <ctype.h>
8 #include "ipf.h"
9 #ifdef	IPFILTER_SCAN
10 # include "netinet/ip_scan.h"
11 #endif
12 #include <sys/ioctl.h>
13 #include <sys/param.h>
14 #include <syslog.h>
15 #ifdef	TEST_LEXER
16 # define	NO_YACC
17 union	{
18 	int		num;
19 	char		*str;
20 	struct in_addr	ipa;
21 	i6addr_t	ip6;
22 } yylval;
23 #endif
24 #include "lexer.h"
25 #include "y.tab.h"
26 
27 FILE *yyin;
28 
29 #define	ishex(c)	(ISDIGIT(c) || ((c) >= 'a' && (c) <= 'f') || \
30 			 ((c) >= 'A' && (c) <= 'F'))
31 #define	TOOLONG		-3
32 
33 extern int	string_start;
34 extern int	string_end;
35 extern char	*string_val;
36 extern int	pos;
37 extern int	yydebug;
38 
39 char		*yystr = NULL;
40 int		yytext[YYBUFSIZ+1];
41 char		yychars[YYBUFSIZ+1];
42 int		yylineNum = 1;
43 int		yypos = 0;
44 int		yylast = -1;
45 int		yydictfixed = 0;
46 int		yyexpectaddr = 0;
47 int		yybreakondot = 0;
48 int		yyvarnext = 0;
49 int		yytokentype = 0;
50 wordtab_t	*yywordtab = NULL;
51 int		yysavedepth = 0;
52 wordtab_t	*yysavewords[30];
53 
54 
55 static	wordtab_t	*yyfindkey(char *);
56 static	int		yygetc(int);
57 static	void		yyunputc(int);
58 static	int		yyswallow(int);
59 static	char		*yytexttostr(int, int);
60 static	void		yystrtotext(char *);
61 static	char		*yytexttochar(void);
62 
63 static int
64 yygetc(int docont)
65 {
66 	int c;
67 
68 	if (yypos < yylast) {
69 		c = yytext[yypos++];
70 		if (c == '\n')
71 			yylineNum++;
72 		return (c);
73 	}
74 
75 	if (yypos == YYBUFSIZ)
76 		return (TOOLONG);
77 
78 	if (pos >= string_start && pos <= string_end) {
79 		c = string_val[pos - string_start];
80 		yypos++;
81 	} else {
82 		c = fgetc(yyin);
83 		if (docont && (c == '\\')) {
84 			c = fgetc(yyin);
85 			if (c == '\n') {
86 				yylineNum++;
87 				c = fgetc(yyin);
88 			}
89 		}
90 	}
91 	if (c == '\n')
92 		yylineNum++;
93 	yytext[yypos++] = c;
94 	yylast = yypos;
95 	yytext[yypos] = '\0';
96 
97 	return (c);
98 }
99 
100 
101 static void
102 yyunputc(int c)
103 {
104 	if (c == '\n')
105 		yylineNum--;
106 	yytext[--yypos] = c;
107 }
108 
109 
110 static int
111 yyswallow(int last)
112 {
113 	int c;
114 
115 	while (((c = yygetc(0)) > '\0') && (c != last))
116 		;
117 
118 	if (c != EOF)
119 		yyunputc(c);
120 	if (c == last)
121 		return (0);
122 	return (-1);
123 }
124 
125 
126 static char *
127 yytexttochar(void)
128 {
129 	int i;
130 
131 	for (i = 0; i < yypos; i++)
132 		yychars[i] = (char)(yytext[i] & 0xff);
133 	yychars[i] = '\0';
134 	return (yychars);
135 }
136 
137 
138 static void
139 yystrtotext(char *str)
140 {
141 	int len;
142 	char *s;
143 
144 	len = strlen(str);
145 	if (len > YYBUFSIZ)
146 		len = YYBUFSIZ;
147 
148 	for (s = str; *s != '\0' && len > 0; s++, len--)
149 		yytext[yylast++] = *s;
150 	yytext[yylast] = '\0';
151 }
152 
153 
154 static char *
155 yytexttostr(int offset, int max)
156 {
157 	char *str;
158 	int i;
159 
160 	if ((yytext[offset] == '\'' || yytext[offset] == '"') &&
161 	    (yytext[offset] == yytext[offset + max - 1])) {
162 		offset++;
163 		max--;
164 	}
165 
166 	if (max > yylast)
167 		max = yylast;
168 	str = malloc(max + 1);
169 	if (str != NULL) {
170 		for (i = offset; i < max; i++)
171 			str[i - offset] = (char)(yytext[i] & 0xff);
172 		str[i - offset] = '\0';
173 	}
174 	return (str);
175 }
176 
177 
178 int
179 yylex(void)
180 {
181 	static int prior = 0;
182 	static int priornum = 0;
183 	int c, n, isbuilding, rval, lnext, nokey = 0;
184 	char *name;
185 	int triedv6 = 0;
186 
187 	isbuilding = 0;
188 	lnext = 0;
189 	rval = 0;
190 
191 	if (yystr != NULL) {
192 		free(yystr);
193 		yystr = NULL;
194 	}
195 
196 nextchar:
197 	c = yygetc(0);
198 	if (yydebug > 1)
199 		printf("yygetc = (%x) %c [%*.*s]\n",
200 		       c, c, yypos, yypos, yytexttochar());
201 
202 	switch (c)
203 	{
204 	case '\n' :
205 		lnext = 0;
206 		nokey = 0;
207 	case '\t' :
208 	case '\r' :
209 	case ' ' :
210 		if (isbuilding == 1) {
211 			yyunputc(c);
212 			goto done;
213 		}
214 		if (yylast > yypos) {
215 			bcopy(yytext + yypos, yytext,
216 			      sizeof(yytext[0]) * (yylast - yypos + 1));
217 		}
218 		yylast -= yypos;
219 		if (yyexpectaddr == 2)
220 			yyexpectaddr = 0;
221 		yypos = 0;
222 		lnext = 0;
223 		nokey = 0;
224 		goto nextchar;
225 
226 	case '\\' :
227 		if (lnext == 0) {
228 			lnext = 1;
229 			if (yylast == yypos) {
230 				yylast--;
231 				yypos--;
232 			} else
233 				yypos--;
234 			if (yypos == 0)
235 				nokey = 1;
236 			goto nextchar;
237 		}
238 		break;
239 	}
240 
241 	if (lnext == 1) {
242 		lnext = 0;
243 		if ((isbuilding == 0) && !ISALNUM(c)) {
244 			prior = c;
245 			return (c);
246 		}
247 		goto nextchar;
248 	}
249 
250 	switch (c)
251 	{
252 	case '#' :
253 		if (isbuilding == 1) {
254 			yyunputc(c);
255 			goto done;
256 		}
257 		yyswallow('\n');
258 		rval = YY_COMMENT;
259 		goto done;
260 
261 	case '$' :
262 		if (isbuilding == 1) {
263 			yyunputc(c);
264 			goto done;
265 		}
266 		n = yygetc(0);
267 		if (n == '{') {
268 			if (yyswallow('}') == -1) {
269 				rval = -2;
270 				goto done;
271 			}
272 			(void) yygetc(0);
273 		} else {
274 			if (!ISALPHA(n)) {
275 				yyunputc(n);
276 				break;
277 			}
278 			do {
279 				n = yygetc(1);
280 			} while (ISALPHA(n) || ISDIGIT(n) || n == '_');
281 			yyunputc(n);
282 		}
283 
284 		name = yytexttostr(1, yypos);		/* skip $ */
285 
286 		if (name != NULL) {
287 			string_val = get_variable(name, NULL, yylineNum);
288 			free(name);
289 			if (string_val != NULL) {
290 				name = yytexttostr(yypos, yylast);
291 				if (name != NULL) {
292 					yypos = 0;
293 					yylast = 0;
294 					yystrtotext(string_val);
295 					yystrtotext(name);
296 					free(string_val);
297 					free(name);
298 					goto nextchar;
299 				}
300 				free(string_val);
301 			}
302 		}
303 		break;
304 
305 	case '\'':
306 	case '"' :
307 		if (isbuilding == 1) {
308 			goto done;
309 		}
310 		do {
311 			n = yygetc(1);
312 			if (n == EOF || n == TOOLONG) {
313 				rval = -2;
314 				goto done;
315 			}
316 			if (n == '\n') {
317 				yyunputc(' ');
318 				yypos++;
319 			}
320 		} while (n != c);
321 		rval = YY_STR;
322 		goto done;
323 		/* NOTREACHED */
324 
325 	case EOF :
326 		yylineNum = 1;
327 		yypos = 0;
328 		yylast = -1;
329 		yyexpectaddr = 0;
330 		yybreakondot = 0;
331 		yyvarnext = 0;
332 		yytokentype = 0;
333 		if (yydebug)
334 			fprintf(stderr, "reset at EOF\n");
335 		prior = 0;
336 		return (0);
337 	}
338 
339 	if (strchr("=,/;{}()@", c) != NULL) {
340 		if (isbuilding == 1) {
341 			yyunputc(c);
342 			goto done;
343 		}
344 		rval = c;
345 		goto done;
346 	} else if (c == '.') {
347 		if (isbuilding == 0) {
348 			rval = c;
349 			goto done;
350 		}
351 		if (yybreakondot != 0) {
352 			yyunputc(c);
353 			goto done;
354 		}
355 	}
356 
357 	switch (c)
358 	{
359 	case '-' :
360 		n = yygetc(0);
361 		if (n == '>') {
362 			isbuilding = 1;
363 			goto done;
364 		}
365 		yyunputc(n);
366 		if (yyexpectaddr) {
367 			if (isbuilding == 1)
368 				yyunputc(c);
369 			else
370 				rval = '-';
371 			goto done;
372 		}
373 		if (isbuilding == 1)
374 			break;
375 		rval = '-';
376 		goto done;
377 
378 	case '!' :
379 		if (isbuilding == 1) {
380 			yyunputc(c);
381 			goto done;
382 		}
383 		n = yygetc(0);
384 		if (n == '=') {
385 			rval = YY_CMP_NE;
386 			goto done;
387 		}
388 		yyunputc(n);
389 		rval = '!';
390 		goto done;
391 
392 	case '<' :
393 		if (yyexpectaddr)
394 			break;
395 		if (isbuilding == 1) {
396 			yyunputc(c);
397 			goto done;
398 		}
399 		n = yygetc(0);
400 		if (n == '=') {
401 			rval = YY_CMP_LE;
402 			goto done;
403 		}
404 		if (n == '>') {
405 			rval = YY_RANGE_OUT;
406 			goto done;
407 		}
408 		yyunputc(n);
409 		rval = YY_CMP_LT;
410 		goto done;
411 
412 	case '>' :
413 		if (yyexpectaddr)
414 			break;
415 		if (isbuilding == 1) {
416 			yyunputc(c);
417 			goto done;
418 		}
419 		n = yygetc(0);
420 		if (n == '=') {
421 			rval = YY_CMP_GE;
422 			goto done;
423 		}
424 		if (n == '<') {
425 			rval = YY_RANGE_IN;
426 			goto done;
427 		}
428 		yyunputc(n);
429 		rval = YY_CMP_GT;
430 		goto done;
431 	}
432 
433 	/*
434 	 * Now for the reason this is here...IPv6 address parsing.
435 	 * The longest string we can expect is of this form:
436 	 * 0000:0000:0000:0000:0000:0000:000.000.000.000
437 	 * not:
438 	 * 0000:0000:0000:0000:0000:0000:0000:0000
439 	 */
440 #ifdef	USE_INET6
441 	if (yyexpectaddr != 0 && isbuilding == 0 &&
442 	    (ishex(c) || isdigit(c) || c == ':')) {
443 		char ipv6buf[45 + 1], *s, oc;
444 		int start;
445 
446 buildipv6:
447 		start = yypos;
448 		s = ipv6buf;
449 		oc = c;
450 
451 		if (prior == YY_NUMBER && c == ':') {
452 			snprintf(s, sizeof(s), "%d", priornum);
453 			s += strlen(s);
454 		}
455 
456 		/*
457 		 * Perhaps we should implement stricter controls on what we
458 		 * swallow up here, but surely it would just be duplicating
459 		 * the code in inet_pton() anyway.
460 		 */
461 		do {
462 			*s++ = c;
463 			c = yygetc(1);
464 		} while ((ishex(c) || c == ':' || c == '.') &&
465 			 (s - ipv6buf < 46));
466 		yyunputc(c);
467 		*s = '\0';
468 
469 		if (inet_pton(AF_INET6, ipv6buf, &yylval.ip6) == 1) {
470 			rval = YY_IPV6;
471 			yyexpectaddr = 0;
472 			goto done;
473 		}
474 		yypos = start;
475 		c = oc;
476 	}
477 #endif
478 
479 	if ((c == ':') && (rval != YY_IPV6) && (triedv6 == 0)) {
480 #ifdef	USE_INET6
481 		yystr = yytexttostr(0, yypos - 1);
482 		if (yystr != NULL) {
483 			char *s;
484 
485 			for (s = yystr; *s && ishex(*s); s++)
486 				;
487 			if (!*s && *yystr) {
488 				isbuilding = 0;
489 				c = *yystr;
490 				free(yystr);
491 				triedv6 = 1;
492 				yypos = 1;
493 				goto buildipv6;
494 			}
495 			free(yystr);
496 		}
497 #endif
498 		if (isbuilding == 1) {
499 			yyunputc(c);
500 			goto done;
501 		}
502 		rval = ':';
503 		goto done;
504 	}
505 
506 	if (isbuilding == 0 && c == '0') {
507 		n = yygetc(0);
508 		if (n == 'x') {
509 			do {
510 				n = yygetc(1);
511 			} while (ishex(n));
512 			yyunputc(n);
513 			rval = YY_HEX;
514 			goto done;
515 		}
516 		yyunputc(n);
517 	}
518 
519 	/*
520 	 * No negative numbers with leading - sign..
521 	 */
522 	if (isbuilding == 0 && ISDIGIT(c)) {
523 		do {
524 			n = yygetc(1);
525 		} while (ISDIGIT(n));
526 		yyunputc(n);
527 		rval = YY_NUMBER;
528 		goto done;
529 	}
530 
531 	isbuilding = 1;
532 	goto nextchar;
533 
534 done:
535 	yystr = yytexttostr(0, yypos);
536 
537 	if (yydebug)
538 		printf("isbuilding %d yyvarnext %d nokey %d fixed %d addr %d\n",
539 		       isbuilding, yyvarnext, nokey, yydictfixed, yyexpectaddr);
540 	if (isbuilding == 1) {
541 		wordtab_t *w;
542 
543 		w = NULL;
544 		isbuilding = 0;
545 
546 		if ((yyvarnext == 0) && (nokey == 0)) {
547 			w = yyfindkey(yystr);
548 			if (w == NULL && yywordtab != NULL && !yydictfixed) {
549 				yyresetdict();
550 				w = yyfindkey(yystr);
551 			}
552 		} else
553 			yyvarnext = 0;
554 		if (w != NULL)
555 			rval = w->w_value;
556 		else
557 			rval = YY_STR;
558 	}
559 
560 	if (rval == YY_STR) {
561 		if (yysavedepth > 0 && !yydictfixed)
562 			yyresetdict();
563 		if (yyexpectaddr != 0)
564 			yyexpectaddr = 0;
565 	}
566 
567 	yytokentype = rval;
568 
569 	if (yydebug)
570 		printf("lexed(%s) %d,%d,%d [%d,%d,%d] => %d @%d\n",
571 		       yystr, isbuilding, yyexpectaddr, yysavedepth,
572 		       string_start, string_end, pos, rval, yysavedepth);
573 
574 	switch (rval)
575 	{
576 	case YY_NUMBER :
577 		sscanf(yystr, "%u", &yylval.num);
578 		break;
579 
580 	case YY_HEX :
581 		sscanf(yystr, "0x%x", (u_int *)&yylval.num);
582 		break;
583 
584 	case YY_STR :
585 		yylval.str = strdup(yystr);
586 		break;
587 
588 	default :
589 		break;
590 	}
591 
592 	if (yylast > 0) {
593 		bcopy(yytext + yypos, yytext,
594 		      sizeof(yytext[0]) * (yylast - yypos + 1));
595 		yylast -= yypos;
596 		yypos = 0;
597 	}
598 
599 	if (rval == YY_NUMBER)
600 		priornum = yylval.num;
601 	prior = rval;
602 	return (rval);
603 }
604 
605 
606 static wordtab_t *yyfindkey(char *key)
607 {
608 	wordtab_t *w;
609 
610 	if (yywordtab == NULL)
611 		return (NULL);
612 
613 	for (w = yywordtab; w->w_word != 0; w++)
614 		if (strcasecmp(key, w->w_word) == 0)
615 			return (w);
616 	return (NULL);
617 }
618 
619 
620 char *
621 yykeytostr(int num)
622 {
623 	wordtab_t *w;
624 
625 	if (yywordtab == NULL)
626 		return ("<unknown>");
627 
628 	for (w = yywordtab; w->w_word; w++)
629 		if (w->w_value == num)
630 			return (w->w_word);
631 	return ("<unknown>");
632 }
633 
634 
635 wordtab_t *
636 yysettab(wordtab_t *words)
637 {
638 	wordtab_t *save;
639 
640 	save = yywordtab;
641 	yywordtab = words;
642 	return (save);
643 }
644 
645 
646 void
647 yyerror(char *msg)
648 {
649 	char *txt, letter[2];
650 	int freetxt = 0;
651 
652 	if (yytokentype < 256) {
653 		letter[0] = yytokentype;
654 		letter[1] = '\0';
655 		txt =  letter;
656 	} else if (yytokentype == YY_STR || yytokentype == YY_HEX ||
657 		   yytokentype == YY_NUMBER) {
658 		if (yystr == NULL) {
659 			txt = yytexttostr(yypos, YYBUFSIZ);
660 			freetxt = 1;
661 		} else
662 			txt = yystr;
663 	} else {
664 		txt = yykeytostr(yytokentype);
665 	}
666 	fprintf(stderr, "%s error at \"%s\", line %d\n", msg, txt, yylineNum);
667 	if (freetxt == 1)
668 		free(txt);
669 	exit(1);
670 }
671 
672 
673 void
674 yysetfixeddict(wordtab_t *newdict)
675 {
676 	if (yydebug)
677 		printf("yysetfixeddict(%lx)\n", (u_long)newdict);
678 
679 	if (yysavedepth == nitems(yysavewords)) {
680 		fprintf(stderr, "%d: at maximum dictionary depth\n",
681 			yylineNum);
682 		return;
683 	}
684 
685 	yysavewords[yysavedepth++] = yysettab(newdict);
686 	if (yydebug)
687 		printf("yysavedepth++ => %d\n", yysavedepth);
688 	yydictfixed = 1;
689 }
690 
691 
692 void
693 yysetdict(wordtab_t *newdict)
694 {
695 	if (yydebug)
696 		printf("yysetdict(%lx)\n", (u_long)newdict);
697 
698 	if (yysavedepth == nitems(yysavewords)) {
699 		fprintf(stderr, "%d: at maximum dictionary depth\n",
700 			yylineNum);
701 		return;
702 	}
703 
704 	yysavewords[yysavedepth++] = yysettab(newdict);
705 	if (yydebug)
706 		printf("yysavedepth++ => %d\n", yysavedepth);
707 }
708 
709 void
710 yyresetdict(void)
711 {
712 	if (yydebug)
713 		printf("yyresetdict(%d)\n", yysavedepth);
714 	if (yysavedepth > 0) {
715 		yysettab(yysavewords[--yysavedepth]);
716 		if (yydebug)
717 			printf("yysavedepth-- => %d\n", yysavedepth);
718 	}
719 	yydictfixed = 0;
720 }
721 
722 
723 
724 #ifdef	TEST_LEXER
725 int
726 main(int argc, char *argv[])
727 {
728 	int n;
729 
730 	yyin = stdin;
731 
732 	while ((n = yylex()) != 0)
733 		printf("%d.n = %d [%s] %d %d\n",
734 			yylineNum, n, yystr, yypos, yylast);
735 }
736 #endif
737