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