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