xref: /freebsd/sbin/ipf/libipf/parseipfexpr.c (revision 963f5dc7a30624e95d72fb7f87b8892651164e46)
1 #include "ipf.h"
2 #include <ctype.h>
3 
4 
5 typedef 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 
13 static 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 
34 int *
35 parseipfexpr(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 				snprintf(error, sizeof(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 
275 parseerror:
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