xref: /freebsd/sbin/ipf/common/lexer.c (revision 315ee00f)
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 <syslog.h>
14 #ifdef	TEST_LEXER
15 # define	NO_YACC
16 union	{
17 	int		num;
18 	char		*str;
19 	struct in_addr	ipa;
20 	i6addr_t	ip6;
21 } yylval;
22 #endif
23 #include "lexer.h"
24 #include "y.tab.h"
25 
26 FILE *yyin;
27 
28 #define	ishex(c)	(ISDIGIT(c) || ((c) >= 'a' && (c) <= 'f') || \
29 			 ((c) >= 'A' && (c) <= 'F'))
30 #define	TOOLONG		-3
31 
32 extern int	string_start;
33 extern int	string_end;
34 extern char	*string_val;
35 extern int	pos;
36 extern int	yydebug;
37 
38 char		*yystr = NULL;
39 int		yytext[YYBUFSIZ+1];
40 char		yychars[YYBUFSIZ+1];
41 int		yylineNum = 1;
42 int		yypos = 0;
43 int		yylast = -1;
44 int		yydictfixed = 0;
45 int		yyexpectaddr = 0;
46 int		yybreakondot = 0;
47 int		yyvarnext = 0;
48 int		yytokentype = 0;
49 wordtab_t	*yywordtab = NULL;
50 int		yysavedepth = 0;
51 wordtab_t	*yysavewords[30];
52 
53 
54 static	wordtab_t	*yyfindkey(char *);
55 static	int		yygetc(int);
56 static	void		yyunputc(int);
57 static	int		yyswallow(int);
58 static	char		*yytexttostr(int, int);
59 static	void		yystrtotext(char *);
60 static	char		*yytexttochar(void);
61 
62 static int
63 yygetc(int docont)
64 {
65 	int c;
66 
67 	if (yypos < yylast) {
68 		c = yytext[yypos++];
69 		if (c == '\n')
70 			yylineNum++;
71 		return (c);
72 	}
73 
74 	if (yypos == YYBUFSIZ)
75 		return (TOOLONG);
76 
77 	if (pos >= string_start && pos <= string_end) {
78 		c = string_val[pos - string_start];
79 		yypos++;
80 	} else {
81 		c = fgetc(yyin);
82 		if (docont && (c == '\\')) {
83 			c = fgetc(yyin);
84 			if (c == '\n') {
85 				yylineNum++;
86 				c = fgetc(yyin);
87 			}
88 		}
89 	}
90 	if (c == '\n')
91 		yylineNum++;
92 	yytext[yypos++] = c;
93 	yylast = yypos;
94 	yytext[yypos] = '\0';
95 
96 	return (c);
97 }
98 
99 
100 static void
101 yyunputc(int c)
102 {
103 	if (c == '\n')
104 		yylineNum--;
105 	yytext[--yypos] = c;
106 }
107 
108 
109 static int
110 yyswallow(int last)
111 {
112 	int c;
113 
114 	while (((c = yygetc(0)) > '\0') && (c != last))
115 		;
116 
117 	if (c != EOF)
118 		yyunputc(c);
119 	if (c == last)
120 		return (0);
121 	return (-1);
122 }
123 
124 
125 static char *
126 yytexttochar(void)
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
138 yystrtotext(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 *
154 yytexttostr(int offset, int 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
178 yylex(void)
179 {
180 	static int prior = 0;
181 	static int priornum = 0;
182 	int c, n, isbuilding, rval, lnext, nokey = 0;
183 	char *name;
184 	int triedv6 = 0;
185 
186 	isbuilding = 0;
187 	lnext = 0;
188 	rval = 0;
189 
190 	if (yystr != NULL) {
191 		free(yystr);
192 		yystr = NULL;
193 	}
194 
195 nextchar:
196 	c = yygetc(0);
197 	if (yydebug > 1)
198 		printf("yygetc = (%x) %c [%*.*s]\n",
199 		       c, c, yypos, yypos, yytexttochar());
200 
201 	switch (c)
202 	{
203 	case '\n' :
204 		lnext = 0;
205 		nokey = 0;
206 	case '\t' :
207 	case '\r' :
208 	case ' ' :
209 		if (isbuilding == 1) {
210 			yyunputc(c);
211 			goto done;
212 		}
213 		if (yylast > yypos) {
214 			bcopy(yytext + yypos, yytext,
215 			      sizeof(yytext[0]) * (yylast - yypos + 1));
216 		}
217 		yylast -= yypos;
218 		if (yyexpectaddr == 2)
219 			yyexpectaddr = 0;
220 		yypos = 0;
221 		lnext = 0;
222 		nokey = 0;
223 		goto nextchar;
224 
225 	case '\\' :
226 		if (lnext == 0) {
227 			lnext = 1;
228 			if (yylast == yypos) {
229 				yylast--;
230 				yypos--;
231 			} else
232 				yypos--;
233 			if (yypos == 0)
234 				nokey = 1;
235 			goto nextchar;
236 		}
237 		break;
238 	}
239 
240 	if (lnext == 1) {
241 		lnext = 0;
242 		if ((isbuilding == 0) && !ISALNUM(c)) {
243 			prior = c;
244 			return (c);
245 		}
246 		goto nextchar;
247 	}
248 
249 	switch (c)
250 	{
251 	case '#' :
252 		if (isbuilding == 1) {
253 			yyunputc(c);
254 			goto done;
255 		}
256 		yyswallow('\n');
257 		rval = YY_COMMENT;
258 		goto done;
259 
260 	case '$' :
261 		if (isbuilding == 1) {
262 			yyunputc(c);
263 			goto done;
264 		}
265 		n = yygetc(0);
266 		if (n == '{') {
267 			if (yyswallow('}') == -1) {
268 				rval = -2;
269 				goto done;
270 			}
271 			(void) yygetc(0);
272 		} else {
273 			if (!ISALPHA(n)) {
274 				yyunputc(n);
275 				break;
276 			}
277 			do {
278 				n = yygetc(1);
279 			} while (ISALPHA(n) || ISDIGIT(n) || n == '_');
280 			yyunputc(n);
281 		}
282 
283 		name = yytexttostr(1, yypos);		/* skip $ */
284 
285 		if (name != NULL) {
286 			string_val = get_variable(name, NULL, yylineNum);
287 			free(name);
288 			if (string_val != NULL) {
289 				name = yytexttostr(yypos, yylast);
290 				if (name != NULL) {
291 					yypos = 0;
292 					yylast = 0;
293 					yystrtotext(string_val);
294 					yystrtotext(name);
295 					free(string_val);
296 					free(name);
297 					goto nextchar;
298 				}
299 				free(string_val);
300 			}
301 		}
302 		break;
303 
304 	case '\'':
305 	case '"' :
306 		if (isbuilding == 1) {
307 			goto done;
308 		}
309 		do {
310 			n = yygetc(1);
311 			if (n == EOF || n == TOOLONG) {
312 				rval = -2;
313 				goto done;
314 			}
315 			if (n == '\n') {
316 				yyunputc(' ');
317 				yypos++;
318 			}
319 		} while (n != c);
320 		rval = YY_STR;
321 		goto done;
322 		/* NOTREACHED */
323 
324 	case EOF :
325 		yylineNum = 1;
326 		yypos = 0;
327 		yylast = -1;
328 		yyexpectaddr = 0;
329 		yybreakondot = 0;
330 		yyvarnext = 0;
331 		yytokentype = 0;
332 		if (yydebug)
333 			fprintf(stderr, "reset at EOF\n");
334 		prior = 0;
335 		return (0);
336 	}
337 
338 	if (strchr("=,/;{}()@", c) != NULL) {
339 		if (isbuilding == 1) {
340 			yyunputc(c);
341 			goto done;
342 		}
343 		rval = c;
344 		goto done;
345 	} else if (c == '.') {
346 		if (isbuilding == 0) {
347 			rval = c;
348 			goto done;
349 		}
350 		if (yybreakondot != 0) {
351 			yyunputc(c);
352 			goto done;
353 		}
354 	}
355 
356 	switch (c)
357 	{
358 	case '-' :
359 		n = yygetc(0);
360 		if (n == '>') {
361 			isbuilding = 1;
362 			goto done;
363 		}
364 		yyunputc(n);
365 		if (yyexpectaddr) {
366 			if (isbuilding == 1)
367 				yyunputc(c);
368 			else
369 				rval = '-';
370 			goto done;
371 		}
372 		if (isbuilding == 1)
373 			break;
374 		rval = '-';
375 		goto done;
376 
377 	case '!' :
378 		if (isbuilding == 1) {
379 			yyunputc(c);
380 			goto done;
381 		}
382 		n = yygetc(0);
383 		if (n == '=') {
384 			rval = YY_CMP_NE;
385 			goto done;
386 		}
387 		yyunputc(n);
388 		rval = '!';
389 		goto done;
390 
391 	case '<' :
392 		if (yyexpectaddr)
393 			break;
394 		if (isbuilding == 1) {
395 			yyunputc(c);
396 			goto done;
397 		}
398 		n = yygetc(0);
399 		if (n == '=') {
400 			rval = YY_CMP_LE;
401 			goto done;
402 		}
403 		if (n == '>') {
404 			rval = YY_RANGE_OUT;
405 			goto done;
406 		}
407 		yyunputc(n);
408 		rval = YY_CMP_LT;
409 		goto done;
410 
411 	case '>' :
412 		if (yyexpectaddr)
413 			break;
414 		if (isbuilding == 1) {
415 			yyunputc(c);
416 			goto done;
417 		}
418 		n = yygetc(0);
419 		if (n == '=') {
420 			rval = YY_CMP_GE;
421 			goto done;
422 		}
423 		if (n == '<') {
424 			rval = YY_RANGE_IN;
425 			goto done;
426 		}
427 		yyunputc(n);
428 		rval = YY_CMP_GT;
429 		goto done;
430 	}
431 
432 	/*
433 	 * Now for the reason this is here...IPv6 address parsing.
434 	 * The longest string we can expect is of this form:
435 	 * 0000:0000:0000:0000:0000:0000:000.000.000.000
436 	 * not:
437 	 * 0000:0000:0000:0000:0000:0000:0000:0000
438 	 */
439 #ifdef	USE_INET6
440 	if (yyexpectaddr != 0 && isbuilding == 0 &&
441 	    (ishex(c) || isdigit(c) || c == ':')) {
442 		char ipv6buf[45 + 1], *s, oc;
443 		int start;
444 
445 buildipv6:
446 		start = yypos;
447 		s = ipv6buf;
448 		oc = c;
449 
450 		if (prior == YY_NUMBER && c == ':') {
451 			snprintf(s, sizeof(s), "%d", priornum);
452 			s += strlen(s);
453 		}
454 
455 		/*
456 		 * Perhaps we should implement stricter controls on what we
457 		 * swallow up here, but surely it would just be duplicating
458 		 * the code in inet_pton() anyway.
459 		 */
460 		do {
461 			*s++ = c;
462 			c = yygetc(1);
463 		} while ((ishex(c) || c == ':' || c == '.') &&
464 			 (s - ipv6buf < 46));
465 		yyunputc(c);
466 		*s = '\0';
467 
468 		if (inet_pton(AF_INET6, ipv6buf, &yylval.ip6) == 1) {
469 			rval = YY_IPV6;
470 			yyexpectaddr = 0;
471 			goto done;
472 		}
473 		yypos = start;
474 		c = oc;
475 	}
476 #endif
477 
478 	if ((c == ':') && (rval != YY_IPV6) && (triedv6 == 0)) {
479 #ifdef	USE_INET6
480 		yystr = yytexttostr(0, yypos - 1);
481 		if (yystr != NULL) {
482 			char *s;
483 
484 			for (s = yystr; *s && ishex(*s); s++)
485 				;
486 			if (!*s && *yystr) {
487 				isbuilding = 0;
488 				c = *yystr;
489 				free(yystr);
490 				triedv6 = 1;
491 				yypos = 1;
492 				goto buildipv6;
493 			}
494 			free(yystr);
495 		}
496 #endif
497 		if (isbuilding == 1) {
498 			yyunputc(c);
499 			goto done;
500 		}
501 		rval = ':';
502 		goto done;
503 	}
504 
505 	if (isbuilding == 0 && c == '0') {
506 		n = yygetc(0);
507 		if (n == 'x') {
508 			do {
509 				n = yygetc(1);
510 			} while (ishex(n));
511 			yyunputc(n);
512 			rval = YY_HEX;
513 			goto done;
514 		}
515 		yyunputc(n);
516 	}
517 
518 	/*
519 	 * No negative numbers with leading - sign..
520 	 */
521 	if (isbuilding == 0 && ISDIGIT(c)) {
522 		do {
523 			n = yygetc(1);
524 		} while (ISDIGIT(n));
525 		yyunputc(n);
526 		rval = YY_NUMBER;
527 		goto done;
528 	}
529 
530 	isbuilding = 1;
531 	goto nextchar;
532 
533 done:
534 	yystr = yytexttostr(0, yypos);
535 
536 	if (yydebug)
537 		printf("isbuilding %d yyvarnext %d nokey %d fixed %d addr %d\n",
538 		       isbuilding, yyvarnext, nokey, yydictfixed, yyexpectaddr);
539 	if (isbuilding == 1) {
540 		wordtab_t *w;
541 
542 		w = NULL;
543 		isbuilding = 0;
544 
545 		if ((yyvarnext == 0) && (nokey == 0)) {
546 			w = yyfindkey(yystr);
547 			if (w == NULL && yywordtab != NULL && !yydictfixed) {
548 				yyresetdict();
549 				w = yyfindkey(yystr);
550 			}
551 		} else
552 			yyvarnext = 0;
553 		if (w != NULL)
554 			rval = w->w_value;
555 		else
556 			rval = YY_STR;
557 	}
558 
559 	if (rval == YY_STR) {
560 		if (yysavedepth > 0 && !yydictfixed)
561 			yyresetdict();
562 		if (yyexpectaddr != 0)
563 			yyexpectaddr = 0;
564 	}
565 
566 	yytokentype = rval;
567 
568 	if (yydebug)
569 		printf("lexed(%s) %d,%d,%d [%d,%d,%d] => %d @%d\n",
570 		       yystr, isbuilding, yyexpectaddr, yysavedepth,
571 		       string_start, string_end, pos, rval, yysavedepth);
572 
573 	switch (rval)
574 	{
575 	case YY_NUMBER :
576 		sscanf(yystr, "%u", &yylval.num);
577 		break;
578 
579 	case YY_HEX :
580 		sscanf(yystr, "0x%x", (u_int *)&yylval.num);
581 		break;
582 
583 	case YY_STR :
584 		yylval.str = strdup(yystr);
585 		break;
586 
587 	default :
588 		break;
589 	}
590 
591 	if (yylast > 0) {
592 		bcopy(yytext + yypos, yytext,
593 		      sizeof(yytext[0]) * (yylast - yypos + 1));
594 		yylast -= yypos;
595 		yypos = 0;
596 	}
597 
598 	if (rval == YY_NUMBER)
599 		priornum = yylval.num;
600 	prior = rval;
601 	return (rval);
602 }
603 
604 
605 static wordtab_t *yyfindkey(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 *
620 yykeytostr(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 *
635 yysettab(wordtab_t *words)
636 {
637 	wordtab_t *save;
638 
639 	save = yywordtab;
640 	yywordtab = words;
641 	return (save);
642 }
643 
644 
645 void
646 yyerror(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
673 yysetfixeddict(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
692 yysetdict(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
709 yyresetdict(void)
710 {
711 	if (yydebug)
712 		printf("yyresetdict(%d)\n", yysavedepth);
713 	if (yysavedepth > 0) {
714 		yysettab(yysavewords[--yysavedepth]);
715 		if (yydebug)
716 			printf("yysavedepth-- => %d\n", yysavedepth);
717 	}
718 	yydictfixed = 0;
719 }
720 
721 
722 
723 #ifdef	TEST_LEXER
724 int
725 main(int argc, 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