xref: /netbsd-src/external/bsd/ipf/dist/lib/parseipfexpr.c (revision 7332b28d50f80fd38e8fcb0326c77854faca9e91)
1*7332b28dSmsaitoh /*	$NetBSD: parseipfexpr.c,v 1.2 2021/12/05 08:19:57 msaitoh Exp $	*/
2bc4097aaSchristos 
3bc4097aaSchristos #include "ipf.h"
4bc4097aaSchristos #include <ctype.h>
5bc4097aaSchristos 
6bc4097aaSchristos 
7bc4097aaSchristos typedef struct ipfopentry {
8bc4097aaSchristos 	int	ipoe_cmd;
9bc4097aaSchristos 	int	ipoe_nbasearg;
10bc4097aaSchristos 	int	ipoe_maxarg;
11c9d5dc6cSdarrenr 	int	ipoe_argsize;
12bc4097aaSchristos 	char	*ipoe_word;
13bc4097aaSchristos } ipfopentry_t;
14bc4097aaSchristos 
15bc4097aaSchristos static ipfopentry_t opwords[17] = {
16c9d5dc6cSdarrenr 	{ IPF_EXP_IP_ADDR, 2, 0, 1, "ip.addr" },
17c9d5dc6cSdarrenr 	{ IPF_EXP_IP6_ADDR, 2, 0, 4, "ip6.addr" },
18c9d5dc6cSdarrenr 	{ IPF_EXP_IP_PR, 1, 0, 1, "ip.p" },
19c9d5dc6cSdarrenr 	{ IPF_EXP_IP_SRCADDR, 2, 0, 1, "ip.src" },
20c9d5dc6cSdarrenr 	{ IPF_EXP_IP_DSTADDR, 2, 0, 1, "ip.dst" },
21c9d5dc6cSdarrenr 	{ IPF_EXP_IP6_SRCADDR, 2, 0, 4, "ip6.src" },
22c9d5dc6cSdarrenr 	{ IPF_EXP_IP6_DSTADDR, 2, 0, 4, "ip6.dst" },
23c9d5dc6cSdarrenr 	{ IPF_EXP_TCP_PORT, 1, 0, 1, "tcp.port" },
24c9d5dc6cSdarrenr 	{ IPF_EXP_TCP_DPORT, 1, 0, 1, "tcp.dport" },
25c9d5dc6cSdarrenr 	{ IPF_EXP_TCP_SPORT, 1, 0, 1, "tcp.sport" },
26c9d5dc6cSdarrenr 	{ IPF_EXP_TCP_FLAGS, 2, 0, 1, "tcp.flags" },
27c9d5dc6cSdarrenr 	{ IPF_EXP_UDP_PORT, 1, 0, 1, "udp.port" },
28c9d5dc6cSdarrenr 	{ IPF_EXP_UDP_DPORT, 1, 0, 1, "udp.dport" },
29c9d5dc6cSdarrenr 	{ IPF_EXP_UDP_SPORT, 1, 0, 1, "udp.sport" },
30c9d5dc6cSdarrenr 	{ IPF_EXP_TCP_STATE, 1, 0, 1, "tcp.state" },
31c9d5dc6cSdarrenr 	{ IPF_EXP_IDLE_GT, 1, 1, 1, "idle-gt" },
32c9d5dc6cSdarrenr 	{ -1, 0, 0, 0, NULL  }
33bc4097aaSchristos };
34bc4097aaSchristos 
35bc4097aaSchristos 
36c9d5dc6cSdarrenr int *
parseipfexpr(line,errorptr)37c9d5dc6cSdarrenr parseipfexpr(line, errorptr)
38bc4097aaSchristos 	char *line;
39bc4097aaSchristos 	char **errorptr;
40bc4097aaSchristos {
41bc4097aaSchristos 	int not, items, asize, *oplist, osize, i;
42bc4097aaSchristos 	char *temp, *arg, *s, *t, *ops, *error;
43bc4097aaSchristos 	ipfopentry_t *e;
44bc4097aaSchristos 	ipfexp_t *ipfe;
45bc4097aaSchristos 
46bc4097aaSchristos 	asize = 0;
47bc4097aaSchristos 	error = NULL;
48bc4097aaSchristos 	oplist = NULL;
49bc4097aaSchristos 
50bc4097aaSchristos 	temp = strdup(line);
51bc4097aaSchristos 	if (temp == NULL) {
52bc4097aaSchristos 		error = "strdup failed";
53bc4097aaSchristos 		goto parseerror;
54bc4097aaSchristos 	}
55bc4097aaSchristos 
56bc4097aaSchristos 	/*
57bc4097aaSchristos 	 * Eliminate any white spaces to make parsing easier.
58bc4097aaSchristos 	 */
59bc4097aaSchristos 	for (s = temp; *s != '\0'; ) {
60bc4097aaSchristos 		if (ISSPACE(*s))
61bc4097aaSchristos 			strcpy(s, s + 1);
62bc4097aaSchristos 		else
63bc4097aaSchristos 			s++;
64bc4097aaSchristos 	}
65bc4097aaSchristos 
66bc4097aaSchristos 	/*
67bc4097aaSchristos 	 * Parse the string.
68bc4097aaSchristos 	 * It should be sets of "ip.dst=1.2.3.4/32;" things.
69bc4097aaSchristos 	 * There must be a "=" or "!=" and it must end in ";".
70bc4097aaSchristos 	 */
71bc4097aaSchristos 	if (temp[strlen(temp) - 1] != ';') {
72bc4097aaSchristos 		error = "last character not ';'";
73bc4097aaSchristos 		goto parseerror;
74bc4097aaSchristos 	}
75bc4097aaSchristos 
76bc4097aaSchristos 	/*
77bc4097aaSchristos 	 * Work through the list of complete operands present.
78bc4097aaSchristos 	 */
79bc4097aaSchristos 	for (ops = strtok(temp, ";"); ops != NULL; ops = strtok(NULL, ";")) {
80bc4097aaSchristos 		arg = strchr(ops, '=');
81bc4097aaSchristos 		if ((arg < ops + 2) || (arg == NULL)) {
82*7332b28dSmsaitoh 			error = "bad 'arg' value";
83bc4097aaSchristos 			goto parseerror;
84bc4097aaSchristos 		}
85bc4097aaSchristos 
86bc4097aaSchristos 		if (*(arg - 1) == '!') {
87bc4097aaSchristos 			*(arg - 1) = '\0';
88bc4097aaSchristos 			not = 1;
89bc4097aaSchristos 		} else {
90bc4097aaSchristos 			not = 0;
91bc4097aaSchristos 		}
92bc4097aaSchristos 		*arg++ = '\0';
93bc4097aaSchristos 
94bc4097aaSchristos 
95bc4097aaSchristos 		for (e = opwords; e->ipoe_word; e++) {
96bc4097aaSchristos 			if (strcmp(ops, e->ipoe_word) == 0)
97bc4097aaSchristos 				break;
98bc4097aaSchristos 		}
99bc4097aaSchristos 		if (e->ipoe_word == NULL) {
100bc4097aaSchristos 			error = malloc(32);
101bc4097aaSchristos 			if (error != NULL) {
102bc4097aaSchristos 				sprintf(error, "keyword (%.10s) not found",
103bc4097aaSchristos 					ops);
104bc4097aaSchristos 			}
105bc4097aaSchristos 			goto parseerror;
106bc4097aaSchristos 		}
107bc4097aaSchristos 
108bc4097aaSchristos 		/*
109bc4097aaSchristos 		 * Count the number of commas so we know how big to
110bc4097aaSchristos 		 * build the array
111bc4097aaSchristos 		 */
112bc4097aaSchristos 		for (s = arg, items = 1; *s != '\0'; s++)
113bc4097aaSchristos 			if (*s == ',')
114bc4097aaSchristos 				items++;
115bc4097aaSchristos 
116bc4097aaSchristos 		if ((e->ipoe_maxarg != 0) && (items > e->ipoe_maxarg)) {
117bc4097aaSchristos 			error = "too many items";
118bc4097aaSchristos 			goto parseerror;
119bc4097aaSchristos 		}
120bc4097aaSchristos 
121bc4097aaSchristos 		/*
122bc4097aaSchristos 		 * osize will mark the end of where we have filled up to
123bc4097aaSchristos 		 * and is thus where we start putting new data.
124bc4097aaSchristos 		 */
125bc4097aaSchristos 		osize = asize;
126c9d5dc6cSdarrenr 		asize += 4 + (items * e->ipoe_nbasearg * e->ipoe_argsize);
127bc4097aaSchristos 		if (oplist == NULL)
128bc4097aaSchristos 			oplist = calloc(1, sizeof(int) * (asize + 2));
129bc4097aaSchristos 		else
130bc4097aaSchristos 			oplist = realloc(oplist, sizeof(int) * (asize + 2));
131bc4097aaSchristos 		if (oplist == NULL) {
132bc4097aaSchristos 			error = "oplist alloc failed";
133bc4097aaSchristos 			goto parseerror;
134bc4097aaSchristos 		}
135bc4097aaSchristos 		ipfe = (ipfexp_t *)(oplist + osize);
136c9d5dc6cSdarrenr 		osize += 4;
137bc4097aaSchristos 		ipfe->ipfe_cmd = e->ipoe_cmd;
138bc4097aaSchristos 		ipfe->ipfe_not = not;
139bc4097aaSchristos 		ipfe->ipfe_narg = items * e->ipoe_nbasearg;
140c9d5dc6cSdarrenr 		ipfe->ipfe_size = items * e->ipoe_nbasearg * e->ipoe_argsize;
141c9d5dc6cSdarrenr 		ipfe->ipfe_size += 4;
142bc4097aaSchristos 
143bc4097aaSchristos 		for (s = arg; (*s != '\0') && (osize < asize); s = t) {
144bc4097aaSchristos 			/*
145bc4097aaSchristos 			 * Look for the end of this arg or the ',' to say
146bc4097aaSchristos 			 * there is another following.
147bc4097aaSchristos 			 */
148bc4097aaSchristos 			for (t = s; (*t != '\0') && (*t != ','); t++)
149bc4097aaSchristos 				;
150bc4097aaSchristos 			if (*t == ',')
151bc4097aaSchristos 				*t++ = '\0';
152bc4097aaSchristos 
153bc4097aaSchristos 			if (!strcasecmp(ops, "ip.addr") ||
154bc4097aaSchristos 			    !strcasecmp(ops, "ip.src") ||
155bc4097aaSchristos 			    !strcasecmp(ops, "ip.dst")) {
156bc4097aaSchristos 				i6addr_t mask, addr;
157bc4097aaSchristos 				char *delim;
158bc4097aaSchristos 
159bc4097aaSchristos 				delim = strchr(s, '/');
160bc4097aaSchristos 				if (delim != NULL) {
161bc4097aaSchristos 					*delim++ = '\0';
162bc4097aaSchristos 					if (genmask(AF_INET, delim,
163bc4097aaSchristos 						    &mask) == -1) {
164bc4097aaSchristos 						error = "genmask failed";
165bc4097aaSchristos 						goto parseerror;
166bc4097aaSchristos 					}
167bc4097aaSchristos 				} else {
168bc4097aaSchristos 					mask.in4.s_addr = 0xffffffff;
169bc4097aaSchristos 				}
170bc4097aaSchristos 				if (gethost(AF_INET, s, &addr) == -1) {
171bc4097aaSchristos 					error = "gethost failed";
172bc4097aaSchristos 					goto parseerror;
173bc4097aaSchristos 				}
174bc4097aaSchristos 
175bc4097aaSchristos 				oplist[osize++] = addr.in4.s_addr;
176bc4097aaSchristos 				oplist[osize++] = mask.in4.s_addr;
177bc4097aaSchristos 
178bc4097aaSchristos #ifdef USE_INET6
179bc4097aaSchristos 			} else if (!strcasecmp(ops, "ip6.addr") ||
180bc4097aaSchristos 			    !strcasecmp(ops, "ip6.src") ||
181bc4097aaSchristos 			    !strcasecmp(ops, "ip6.dst")) {
182bc4097aaSchristos 				i6addr_t mask, addr;
183bc4097aaSchristos 				char *delim;
184bc4097aaSchristos 
185bc4097aaSchristos 				delim = strchr(s, '/');
186bc4097aaSchristos 				if (delim != NULL) {
187bc4097aaSchristos 					*delim++ = '\0';
188bc4097aaSchristos 					if (genmask(AF_INET6, delim,
189bc4097aaSchristos 						    &mask) == -1) {
190bc4097aaSchristos 						error = "genmask failed";
191bc4097aaSchristos 						goto parseerror;
192bc4097aaSchristos 					}
193bc4097aaSchristos 				} else {
194bc4097aaSchristos 					mask.i6[0] = 0xffffffff;
195bc4097aaSchristos 					mask.i6[1] = 0xffffffff;
196bc4097aaSchristos 					mask.i6[2] = 0xffffffff;
197bc4097aaSchristos 					mask.i6[3] = 0xffffffff;
198bc4097aaSchristos 				}
199bc4097aaSchristos 				if (gethost(AF_INET6, s, &addr) == -1) {
200bc4097aaSchristos 					error = "gethost failed";
201bc4097aaSchristos 					goto parseerror;
202bc4097aaSchristos 				}
203bc4097aaSchristos 
204bc4097aaSchristos 				oplist[osize++] = addr.i6[0];
205bc4097aaSchristos 				oplist[osize++] = addr.i6[1];
206bc4097aaSchristos 				oplist[osize++] = addr.i6[2];
207bc4097aaSchristos 				oplist[osize++] = addr.i6[3];
208bc4097aaSchristos 				oplist[osize++] = mask.i6[0];
209bc4097aaSchristos 				oplist[osize++] = mask.i6[1];
210bc4097aaSchristos 				oplist[osize++] = mask.i6[2];
211bc4097aaSchristos 				oplist[osize++] = mask.i6[3];
212bc4097aaSchristos #endif
213bc4097aaSchristos 
214bc4097aaSchristos 			} else if (!strcasecmp(ops, "ip.p")) {
215bc4097aaSchristos 				int p;
216bc4097aaSchristos 
217bc4097aaSchristos 				p = getproto(s);
218bc4097aaSchristos 				if (p == -1)
219bc4097aaSchristos 					goto parseerror;
220bc4097aaSchristos 				oplist[osize++] = p;
221bc4097aaSchristos 
222bc4097aaSchristos 			} else if (!strcasecmp(ops, "tcp.flags")) {
223bc4097aaSchristos 				u_32_t mask, flags;
224bc4097aaSchristos 				char *delim;
225bc4097aaSchristos 
226bc4097aaSchristos 				delim = strchr(s, '/');
227bc4097aaSchristos 				if (delim != NULL) {
228bc4097aaSchristos 					*delim++ = '\0';
229bc4097aaSchristos 					mask = tcpflags(delim);
230bc4097aaSchristos 				} else {
231bc4097aaSchristos 					mask = 0xff;
232bc4097aaSchristos 				}
233bc4097aaSchristos 				flags = tcpflags(s);
234bc4097aaSchristos 
235bc4097aaSchristos 				oplist[osize++] = flags;
236bc4097aaSchristos 				oplist[osize++] = mask;
237bc4097aaSchristos 
238bc4097aaSchristos 
239bc4097aaSchristos 			} else if (!strcasecmp(ops, "tcp.port") ||
240bc4097aaSchristos 			    !strcasecmp(ops, "tcp.sport") ||
241bc4097aaSchristos 			    !strcasecmp(ops, "tcp.dport") ||
242bc4097aaSchristos 			    !strcasecmp(ops, "udp.port") ||
243bc4097aaSchristos 			    !strcasecmp(ops, "udp.sport") ||
244bc4097aaSchristos 			    !strcasecmp(ops, "udp.dport")) {
245bc4097aaSchristos 				char proto[4];
246bc4097aaSchristos 				u_short port;
247bc4097aaSchristos 
248bc4097aaSchristos 				strncpy(proto, ops, 3);
249bc4097aaSchristos 				proto[3] = '\0';
250bc4097aaSchristos 				if (getport(NULL, s, &port, proto) == -1)
251bc4097aaSchristos 					goto parseerror;
252bc4097aaSchristos 				oplist[osize++] = port;
253bc4097aaSchristos 
254bc4097aaSchristos 			} else if (!strcasecmp(ops, "tcp.state")) {
255bc4097aaSchristos 				oplist[osize++] = atoi(s);
256bc4097aaSchristos 
257bc4097aaSchristos 			} else {
258bc4097aaSchristos 				error = "unknown word";
259bc4097aaSchristos 				goto parseerror;
260bc4097aaSchristos 			}
261bc4097aaSchristos 		}
262bc4097aaSchristos 	}
263bc4097aaSchristos 
264bc4097aaSchristos 	free(temp);
265bc4097aaSchristos 
266bc4097aaSchristos 	if (errorptr != NULL)
267bc4097aaSchristos 		*errorptr = NULL;
268bc4097aaSchristos 
269bc4097aaSchristos 	for (i = asize; i > 0; i--)
270bc4097aaSchristos 		oplist[i] = oplist[i - 1];
271bc4097aaSchristos 
272bc4097aaSchristos 	oplist[0] = asize + 2;
273bc4097aaSchristos 	oplist[asize + 1] = IPF_EXP_END;
274bc4097aaSchristos 
275bc4097aaSchristos 	return oplist;
276bc4097aaSchristos 
277bc4097aaSchristos parseerror:
278bc4097aaSchristos 	if (errorptr != NULL)
279bc4097aaSchristos 		*errorptr = error;
280bc4097aaSchristos 	if (oplist != NULL)
281bc4097aaSchristos 		free(oplist);
282bc4097aaSchristos 	if (temp != NULL)
283bc4097aaSchristos 		free(temp);
284bc4097aaSchristos 	return NULL;
285bc4097aaSchristos }
286