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