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