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