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