1254219Scy#include "ipf.h"
2254219Scy#include <ctype.h>
3254219Scy
4254219Scy
5254219Scytypedef struct ipfopentry {
6254219Scy	int	ipoe_cmd;
7254219Scy	int	ipoe_nbasearg;
8254219Scy	int	ipoe_maxarg;
9254219Scy	int	ipoe_argsize;
10254219Scy	char	*ipoe_word;
11254219Scy} ipfopentry_t;
12254219Scy
13254219Scystatic ipfopentry_t opwords[17] = {
14254219Scy	{ IPF_EXP_IP_ADDR, 2, 0, 1, "ip.addr" },
15254219Scy	{ IPF_EXP_IP6_ADDR, 2, 0, 4, "ip6.addr" },
16254219Scy	{ IPF_EXP_IP_PR, 1, 0, 1, "ip.p" },
17254219Scy	{ IPF_EXP_IP_SRCADDR, 2, 0, 1, "ip.src" },
18254219Scy	{ IPF_EXP_IP_DSTADDR, 2, 0, 1, "ip.dst" },
19254219Scy	{ IPF_EXP_IP6_SRCADDR, 2, 0, 4, "ip6.src" },
20254219Scy	{ IPF_EXP_IP6_DSTADDR, 2, 0, 4, "ip6.dst" },
21254219Scy	{ IPF_EXP_TCP_PORT, 1, 0, 1, "tcp.port" },
22254219Scy	{ IPF_EXP_TCP_DPORT, 1, 0, 1, "tcp.dport" },
23254219Scy	{ IPF_EXP_TCP_SPORT, 1, 0, 1, "tcp.sport" },
24254219Scy	{ IPF_EXP_TCP_FLAGS, 2, 0, 1, "tcp.flags" },
25254219Scy	{ IPF_EXP_UDP_PORT, 1, 0, 1, "udp.port" },
26254219Scy	{ IPF_EXP_UDP_DPORT, 1, 0, 1, "udp.dport" },
27254219Scy	{ IPF_EXP_UDP_SPORT, 1, 0, 1, "udp.sport" },
28254219Scy	{ IPF_EXP_TCP_STATE, 1, 0, 1, "tcp.state" },
29254219Scy	{ IPF_EXP_IDLE_GT, 1, 1, 1, "idle-gt" },
30254219Scy	{ -1, 0, 0, 0, NULL  }
31254219Scy};
32254219Scy
33254219Scy
34254219Scyint *
35254219Scyparseipfexpr(line, errorptr)
36254219Scy	char *line;
37254219Scy	char **errorptr;
38254219Scy{
39254219Scy	int not, items, asize, *oplist, osize, i;
40254219Scy	char *temp, *arg, *s, *t, *ops, *error;
41254219Scy	ipfopentry_t *e;
42254219Scy	ipfexp_t *ipfe;
43254219Scy
44254219Scy	asize = 0;
45254219Scy	error = NULL;
46254219Scy	oplist = NULL;
47254219Scy
48254219Scy	temp = strdup(line);
49254219Scy	if (temp == NULL) {
50254219Scy		error = "strdup failed";
51254219Scy		goto parseerror;
52254219Scy	}
53254219Scy
54254219Scy	/*
55254219Scy	 * Eliminate any white spaces to make parsing easier.
56254219Scy	 */
57254219Scy	for (s = temp; *s != '\0'; ) {
58254219Scy		if (ISSPACE(*s))
59254219Scy			strcpy(s, s + 1);
60254219Scy		else
61254219Scy			s++;
62254219Scy	}
63254219Scy
64254219Scy	/*
65254219Scy	 * Parse the string.
66254219Scy	 * It should be sets of "ip.dst=1.2.3.4/32;" things.
67254219Scy	 * There must be a "=" or "!=" and it must end in ";".
68254219Scy	 */
69254219Scy	if (temp[strlen(temp) - 1] != ';') {
70254219Scy		error = "last character not ';'";
71254219Scy		goto parseerror;
72254219Scy	}
73254219Scy
74254219Scy	/*
75254219Scy	 * Work through the list of complete operands present.
76254219Scy	 */
77254219Scy	for (ops = strtok(temp, ";"); ops != NULL; ops = strtok(NULL, ";")) {
78254219Scy		arg = strchr(ops, '=');
79254219Scy		if ((arg < ops + 2) || (arg == NULL)) {
80254219Scy			error = "bad 'arg' vlaue";
81254219Scy			goto parseerror;
82254219Scy		}
83254219Scy
84254219Scy		if (*(arg - 1) == '!') {
85254219Scy			*(arg - 1) = '\0';
86254219Scy			not = 1;
87254219Scy		} else {
88254219Scy			not = 0;
89254219Scy		}
90254219Scy		*arg++ = '\0';
91254219Scy
92254219Scy
93254219Scy		for (e = opwords; e->ipoe_word; e++) {
94254219Scy			if (strcmp(ops, e->ipoe_word) == 0)
95254219Scy				break;
96254219Scy		}
97254219Scy		if (e->ipoe_word == NULL) {
98254219Scy			error = malloc(32);
99254219Scy			if (error != NULL) {
100254219Scy				sprintf(error, "keyword (%.10s) not found",
101254219Scy					ops);
102254219Scy			}
103254219Scy			goto parseerror;
104254219Scy		}
105254219Scy
106254219Scy		/*
107254219Scy		 * Count the number of commas so we know how big to
108254219Scy		 * build the array
109254219Scy		 */
110254219Scy		for (s = arg, items = 1; *s != '\0'; s++)
111254219Scy			if (*s == ',')
112254219Scy				items++;
113254219Scy
114254219Scy		if ((e->ipoe_maxarg != 0) && (items > e->ipoe_maxarg)) {
115254219Scy			error = "too many items";
116254219Scy			goto parseerror;
117254219Scy		}
118254219Scy
119254219Scy		/*
120254219Scy		 * osize will mark the end of where we have filled up to
121254219Scy		 * and is thus where we start putting new data.
122254219Scy		 */
123254219Scy		osize = asize;
124254219Scy		asize += 4 + (items * e->ipoe_nbasearg * e->ipoe_argsize);
125254219Scy		if (oplist == NULL)
126254219Scy			oplist = calloc(1, sizeof(int) * (asize + 2));
127254219Scy		else
128254219Scy			oplist = realloc(oplist, sizeof(int) * (asize + 2));
129254219Scy		if (oplist == NULL) {
130254219Scy			error = "oplist alloc failed";
131254219Scy			goto parseerror;
132254219Scy		}
133254219Scy		ipfe = (ipfexp_t *)(oplist + osize);
134254219Scy		osize += 4;
135254219Scy		ipfe->ipfe_cmd = e->ipoe_cmd;
136254219Scy		ipfe->ipfe_not = not;
137254219Scy		ipfe->ipfe_narg = items * e->ipoe_nbasearg;
138254219Scy		ipfe->ipfe_size = items * e->ipoe_nbasearg * e->ipoe_argsize;
139254219Scy		ipfe->ipfe_size += 4;
140254219Scy
141254219Scy		for (s = arg; (*s != '\0') && (osize < asize); s = t) {
142254219Scy			/*
143254219Scy			 * Look for the end of this arg or the ',' to say
144254219Scy			 * there is another following.
145254219Scy			 */
146254219Scy			for (t = s; (*t != '\0') && (*t != ','); t++)
147254219Scy				;
148254219Scy			if (*t == ',')
149254219Scy				*t++ = '\0';
150254219Scy
151254219Scy			if (!strcasecmp(ops, "ip.addr") ||
152254219Scy			    !strcasecmp(ops, "ip.src") ||
153254219Scy			    !strcasecmp(ops, "ip.dst")) {
154254219Scy				i6addr_t mask, addr;
155254219Scy				char *delim;
156254219Scy
157254219Scy				delim = strchr(s, '/');
158254219Scy				if (delim != NULL) {
159254219Scy					*delim++ = '\0';
160254219Scy					if (genmask(AF_INET, delim,
161254219Scy						    &mask) == -1) {
162254219Scy						error = "genmask failed";
163254219Scy						goto parseerror;
164254219Scy					}
165254219Scy				} else {
166254219Scy					mask.in4.s_addr = 0xffffffff;
167254219Scy				}
168254219Scy				if (gethost(AF_INET, s, &addr) == -1) {
169254219Scy					error = "gethost failed";
170254219Scy					goto parseerror;
171254219Scy				}
172254219Scy
173254219Scy				oplist[osize++] = addr.in4.s_addr;
174254219Scy				oplist[osize++] = mask.in4.s_addr;
175254219Scy
176254219Scy#ifdef USE_INET6
177254219Scy			} else if (!strcasecmp(ops, "ip6.addr") ||
178254219Scy			    !strcasecmp(ops, "ip6.src") ||
179254219Scy			    !strcasecmp(ops, "ip6.dst")) {
180254219Scy				i6addr_t mask, addr;
181254219Scy				char *delim;
182254219Scy
183254219Scy				delim = strchr(s, '/');
184254219Scy				if (delim != NULL) {
185254219Scy					*delim++ = '\0';
186254219Scy					if (genmask(AF_INET6, delim,
187254219Scy						    &mask) == -1) {
188254219Scy						error = "genmask failed";
189254219Scy						goto parseerror;
190254219Scy					}
191254219Scy				} else {
192254219Scy					mask.i6[0] = 0xffffffff;
193254219Scy					mask.i6[1] = 0xffffffff;
194254219Scy					mask.i6[2] = 0xffffffff;
195254219Scy					mask.i6[3] = 0xffffffff;
196254219Scy				}
197254219Scy				if (gethost(AF_INET6, s, &addr) == -1) {
198254219Scy					error = "gethost failed";
199254219Scy					goto parseerror;
200254219Scy				}
201254219Scy
202254219Scy				oplist[osize++] = addr.i6[0];
203254219Scy				oplist[osize++] = addr.i6[1];
204254219Scy				oplist[osize++] = addr.i6[2];
205254219Scy				oplist[osize++] = addr.i6[3];
206254219Scy				oplist[osize++] = mask.i6[0];
207254219Scy				oplist[osize++] = mask.i6[1];
208254219Scy				oplist[osize++] = mask.i6[2];
209254219Scy				oplist[osize++] = mask.i6[3];
210254219Scy#endif
211254219Scy
212254219Scy			} else if (!strcasecmp(ops, "ip.p")) {
213254219Scy				int p;
214254219Scy
215254219Scy				p = getproto(s);
216254219Scy				if (p == -1)
217254219Scy					goto parseerror;
218254219Scy				oplist[osize++] = p;
219254219Scy
220254219Scy			} else if (!strcasecmp(ops, "tcp.flags")) {
221254219Scy				u_32_t mask, flags;
222254219Scy				char *delim;
223254219Scy
224254219Scy				delim = strchr(s, '/');
225254219Scy				if (delim != NULL) {
226254219Scy					*delim++ = '\0';
227254219Scy					mask = tcpflags(delim);
228254219Scy				} else {
229254219Scy					mask = 0xff;
230254219Scy				}
231254219Scy				flags = tcpflags(s);
232254219Scy
233254219Scy				oplist[osize++] = flags;
234254219Scy				oplist[osize++] = mask;
235254219Scy
236254219Scy
237254219Scy			} else if (!strcasecmp(ops, "tcp.port") ||
238254219Scy			    !strcasecmp(ops, "tcp.sport") ||
239254219Scy			    !strcasecmp(ops, "tcp.dport") ||
240254219Scy			    !strcasecmp(ops, "udp.port") ||
241254219Scy			    !strcasecmp(ops, "udp.sport") ||
242254219Scy			    !strcasecmp(ops, "udp.dport")) {
243254219Scy				char proto[4];
244254219Scy				u_short port;
245254219Scy
246254219Scy				strncpy(proto, ops, 3);
247254219Scy				proto[3] = '\0';
248254219Scy				if (getport(NULL, s, &port, proto) == -1)
249254219Scy					goto parseerror;
250254219Scy				oplist[osize++] = port;
251254219Scy
252254219Scy			} else if (!strcasecmp(ops, "tcp.state")) {
253254219Scy				oplist[osize++] = atoi(s);
254254219Scy
255254219Scy			} else {
256254219Scy				error = "unknown word";
257254219Scy				goto parseerror;
258254219Scy			}
259254219Scy		}
260254219Scy	}
261254219Scy
262254219Scy	free(temp);
263254219Scy
264254219Scy	if (errorptr != NULL)
265254219Scy		*errorptr = NULL;
266254219Scy
267254219Scy	for (i = asize; i > 0; i--)
268254219Scy		oplist[i] = oplist[i - 1];
269254219Scy
270254219Scy	oplist[0] = asize + 2;
271254219Scy	oplist[asize + 1] = IPF_EXP_END;
272254219Scy
273254219Scy	return oplist;
274254219Scy
275254219Scyparseerror:
276254219Scy	if (errorptr != NULL)
277254219Scy		*errorptr = error;
278254219Scy	if (oplist != NULL)
279254219Scy		free(oplist);
280254219Scy	if (temp != NULL)
281254219Scy		free(temp);
282254219Scy	return NULL;
283254219Scy}
284