xref: /netbsd-src/external/bsd/ipf/dist/lib/parseipfexpr.c (revision 7332b28d50f80fd38e8fcb0326c77854faca9e91)
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