1/*	$FreeBSD$	*/
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
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];
41char		yychars[YYBUFSIZ+1];
42int		yylineNum = 1;
43int		yypos = 0;
44int		yylast = -1;
45int		yyexpectaddr = 0;
46int		yybreakondot = 0;
47int		yyvarnext = 0;
48int		yytokentype = 0;
49wordtab_t	*yywordtab = NULL;
50int		yysavedepth = 0;
51wordtab_t	*yysavewords[30];
52
53
54static	wordtab_t	*yyfindkey __P((char *));
55static	int		yygetc __P((int));
56static	void		yyunputc __P((int));
57static	int		yyswallow __P((int));
58static	char		*yytexttostr __P((int, int));
59static	void		yystrtotext __P((char *));
60static	char		*yytexttochar __P((void));
61
62static int yygetc(docont)
63int 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
100static void yyunputc(c)
101int c;
102{
103	if (c == '\n')
104		yylineNum--;
105	yytext[--yypos] = c;
106}
107
108
109static int yyswallow(last)
110int 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
125static 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
136static void yystrtotext(str)
137char *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
152static char *yytexttostr(offset, max)
153int 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
176int 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
190nextchar:
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
491done:
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
555static wordtab_t *yyfindkey(key)
556char *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
570char *yykeytostr(num)
571int 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
585wordtab_t *yysettab(words)
586wordtab_t *words;
587{
588	wordtab_t *save;
589
590	save = yywordtab;
591	yywordtab = words;
592	return save;
593}
594
595
596void yyerror(msg)
597char *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
623void yysetdict(newdict)
624wordtab_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
637void 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
651int main(argc, argv)
652int argc;
653char *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