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