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