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