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