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