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