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