xref: /freebsd/sbin/ipf/libipf/parseipfexpr.c (revision c1f6704bf810bc28b2af6cb0d0e60cb8f5a9b44b)
141edb306SCy Schubert #include "ipf.h"
241edb306SCy Schubert #include <ctype.h>
341edb306SCy Schubert 
441edb306SCy Schubert 
541edb306SCy Schubert typedef struct ipfopentry {
641edb306SCy Schubert 	int	ipoe_cmd;
741edb306SCy Schubert 	int	ipoe_nbasearg;
841edb306SCy Schubert 	int	ipoe_maxarg;
941edb306SCy Schubert 	int	ipoe_argsize;
1041edb306SCy Schubert 	char	*ipoe_word;
1141edb306SCy Schubert } ipfopentry_t;
1241edb306SCy Schubert 
1341edb306SCy Schubert static ipfopentry_t opwords[17] = {
1441edb306SCy Schubert 	{ IPF_EXP_IP_ADDR, 2, 0, 1, "ip.addr" },
1541edb306SCy Schubert 	{ IPF_EXP_IP6_ADDR, 2, 0, 4, "ip6.addr" },
1641edb306SCy Schubert 	{ IPF_EXP_IP_PR, 1, 0, 1, "ip.p" },
1741edb306SCy Schubert 	{ IPF_EXP_IP_SRCADDR, 2, 0, 1, "ip.src" },
1841edb306SCy Schubert 	{ IPF_EXP_IP_DSTADDR, 2, 0, 1, "ip.dst" },
1941edb306SCy Schubert 	{ IPF_EXP_IP6_SRCADDR, 2, 0, 4, "ip6.src" },
2041edb306SCy Schubert 	{ IPF_EXP_IP6_DSTADDR, 2, 0, 4, "ip6.dst" },
2141edb306SCy Schubert 	{ IPF_EXP_TCP_PORT, 1, 0, 1, "tcp.port" },
2241edb306SCy Schubert 	{ IPF_EXP_TCP_DPORT, 1, 0, 1, "tcp.dport" },
2341edb306SCy Schubert 	{ IPF_EXP_TCP_SPORT, 1, 0, 1, "tcp.sport" },
2441edb306SCy Schubert 	{ IPF_EXP_TCP_FLAGS, 2, 0, 1, "tcp.flags" },
2541edb306SCy Schubert 	{ IPF_EXP_UDP_PORT, 1, 0, 1, "udp.port" },
2641edb306SCy Schubert 	{ IPF_EXP_UDP_DPORT, 1, 0, 1, "udp.dport" },
2741edb306SCy Schubert 	{ IPF_EXP_UDP_SPORT, 1, 0, 1, "udp.sport" },
2841edb306SCy Schubert 	{ IPF_EXP_TCP_STATE, 1, 0, 1, "tcp.state" },
2941edb306SCy Schubert 	{ IPF_EXP_IDLE_GT, 1, 1, 1, "idle-gt" },
3041edb306SCy Schubert 	{ -1, 0, 0, 0, NULL  }
3141edb306SCy Schubert };
3241edb306SCy Schubert 
3341edb306SCy Schubert 
3441edb306SCy Schubert int *
parseipfexpr(char * line,char ** errorptr)35efeb8bffSCy Schubert parseipfexpr(char *line, char **errorptr)
3641edb306SCy Schubert {
3741edb306SCy Schubert 	int not, items, asize, *oplist, osize, i;
3841edb306SCy Schubert 	char *temp, *arg, *s, *t, *ops, *error;
3941edb306SCy Schubert 	ipfopentry_t *e;
4041edb306SCy Schubert 	ipfexp_t *ipfe;
4141edb306SCy Schubert 
4241edb306SCy Schubert 	asize = 0;
4341edb306SCy Schubert 	error = NULL;
4441edb306SCy Schubert 	oplist = NULL;
4541edb306SCy Schubert 
4641edb306SCy Schubert 	temp = strdup(line);
4741edb306SCy Schubert 	if (temp == NULL) {
4841edb306SCy Schubert 		error = "strdup failed";
4941edb306SCy Schubert 		goto parseerror;
5041edb306SCy Schubert 	}
5141edb306SCy Schubert 
5241edb306SCy Schubert 	/*
5341edb306SCy Schubert 	 * Eliminate any white spaces to make parsing easier.
5441edb306SCy Schubert 	 */
5541edb306SCy Schubert 	for (s = temp; *s != '\0'; ) {
5641edb306SCy Schubert 		if (ISSPACE(*s))
5741edb306SCy Schubert 			strcpy(s, s + 1);
5841edb306SCy Schubert 		else
5941edb306SCy Schubert 			s++;
6041edb306SCy Schubert 	}
6141edb306SCy Schubert 
6241edb306SCy Schubert 	/*
6341edb306SCy Schubert 	 * Parse the string.
6441edb306SCy Schubert 	 * It should be sets of "ip.dst=1.2.3.4/32;" things.
6541edb306SCy Schubert 	 * There must be a "=" or "!=" and it must end in ";".
6641edb306SCy Schubert 	 */
6741edb306SCy Schubert 	if (temp[strlen(temp) - 1] != ';') {
6841edb306SCy Schubert 		error = "last character not ';'";
6941edb306SCy Schubert 		goto parseerror;
7041edb306SCy Schubert 	}
7141edb306SCy Schubert 
7241edb306SCy Schubert 	/*
7341edb306SCy Schubert 	 * Work through the list of complete operands present.
7441edb306SCy Schubert 	 */
7541edb306SCy Schubert 	for (ops = strtok(temp, ";"); ops != NULL; ops = strtok(NULL, ";")) {
7641edb306SCy Schubert 		arg = strchr(ops, '=');
7741edb306SCy Schubert 		if ((arg < ops + 2) || (arg == NULL)) {
78*c1f6704bSElyes Haouas 			error = "bad 'arg' value";
7941edb306SCy Schubert 			goto parseerror;
8041edb306SCy Schubert 		}
8141edb306SCy Schubert 
8241edb306SCy Schubert 		if (*(arg - 1) == '!') {
8341edb306SCy Schubert 			*(arg - 1) = '\0';
8441edb306SCy Schubert 			not = 1;
8541edb306SCy Schubert 		} else {
8641edb306SCy Schubert 			not = 0;
8741edb306SCy Schubert 		}
8841edb306SCy Schubert 		*arg++ = '\0';
8941edb306SCy Schubert 
9041edb306SCy Schubert 
9141edb306SCy Schubert 		for (e = opwords; e->ipoe_word; e++) {
9241edb306SCy Schubert 			if (strcmp(ops, e->ipoe_word) == 0)
9341edb306SCy Schubert 				break;
9441edb306SCy Schubert 		}
9541edb306SCy Schubert 		if (e->ipoe_word == NULL) {
964cd9d804SDag-Erling Smørgrav 			asprintf(&error, "keyword (%.10s) not found", ops);
9741edb306SCy Schubert 			goto parseerror;
9841edb306SCy Schubert 		}
9941edb306SCy Schubert 
10041edb306SCy Schubert 		/*
10141edb306SCy Schubert 		 * Count the number of commas so we know how big to
10241edb306SCy Schubert 		 * build the array
10341edb306SCy Schubert 		 */
10441edb306SCy Schubert 		for (s = arg, items = 1; *s != '\0'; s++)
10541edb306SCy Schubert 			if (*s == ',')
10641edb306SCy Schubert 				items++;
10741edb306SCy Schubert 
10841edb306SCy Schubert 		if ((e->ipoe_maxarg != 0) && (items > e->ipoe_maxarg)) {
10941edb306SCy Schubert 			error = "too many items";
11041edb306SCy Schubert 			goto parseerror;
11141edb306SCy Schubert 		}
11241edb306SCy Schubert 
11341edb306SCy Schubert 		/*
11441edb306SCy Schubert 		 * osize will mark the end of where we have filled up to
11541edb306SCy Schubert 		 * and is thus where we start putting new data.
11641edb306SCy Schubert 		 */
11741edb306SCy Schubert 		osize = asize;
11841edb306SCy Schubert 		asize += 4 + (items * e->ipoe_nbasearg * e->ipoe_argsize);
11941edb306SCy Schubert 		if (oplist == NULL)
12041edb306SCy Schubert 			oplist = calloc(asize + 2, sizeof(int));
12141edb306SCy Schubert 		else
12241edb306SCy Schubert 			oplist = reallocarray(oplist, asize + 2, sizeof(int));
12341edb306SCy Schubert 		if (oplist == NULL) {
12441edb306SCy Schubert 			error = "oplist alloc failed";
12541edb306SCy Schubert 			goto parseerror;
12641edb306SCy Schubert 		}
12741edb306SCy Schubert 		ipfe = (ipfexp_t *)(oplist + osize);
12841edb306SCy Schubert 		osize += 4;
12941edb306SCy Schubert 		ipfe->ipfe_cmd = e->ipoe_cmd;
13041edb306SCy Schubert 		ipfe->ipfe_not = not;
13141edb306SCy Schubert 		ipfe->ipfe_narg = items * e->ipoe_nbasearg;
13241edb306SCy Schubert 		ipfe->ipfe_size = items * e->ipoe_nbasearg * e->ipoe_argsize;
13341edb306SCy Schubert 		ipfe->ipfe_size += 4;
13441edb306SCy Schubert 
13541edb306SCy Schubert 		for (s = arg; (*s != '\0') && (osize < asize); s = t) {
13641edb306SCy Schubert 			/*
13741edb306SCy Schubert 			 * Look for the end of this arg or the ',' to say
13841edb306SCy Schubert 			 * there is another following.
13941edb306SCy Schubert 			 */
14041edb306SCy Schubert 			for (t = s; (*t != '\0') && (*t != ','); t++)
14141edb306SCy Schubert 				;
14241edb306SCy Schubert 			if (*t == ',')
14341edb306SCy Schubert 				*t++ = '\0';
14441edb306SCy Schubert 
14541edb306SCy Schubert 			if (!strcasecmp(ops, "ip.addr") ||
14641edb306SCy Schubert 			    !strcasecmp(ops, "ip.src") ||
14741edb306SCy Schubert 			    !strcasecmp(ops, "ip.dst")) {
14841edb306SCy Schubert 				i6addr_t mask, addr;
14941edb306SCy Schubert 				char *delim;
15041edb306SCy Schubert 
15141edb306SCy Schubert 				delim = strchr(s, '/');
15241edb306SCy Schubert 				if (delim != NULL) {
15341edb306SCy Schubert 					*delim++ = '\0';
15441edb306SCy Schubert 					if (genmask(AF_INET, delim,
15541edb306SCy Schubert 						    &mask) == -1) {
15641edb306SCy Schubert 						error = "genmask failed";
15741edb306SCy Schubert 						goto parseerror;
15841edb306SCy Schubert 					}
15941edb306SCy Schubert 				} else {
16041edb306SCy Schubert 					mask.in4.s_addr = 0xffffffff;
16141edb306SCy Schubert 				}
16241edb306SCy Schubert 				if (gethost(AF_INET, s, &addr) == -1) {
16341edb306SCy Schubert 					error = "gethost failed";
16441edb306SCy Schubert 					goto parseerror;
16541edb306SCy Schubert 				}
16641edb306SCy Schubert 
16741edb306SCy Schubert 				oplist[osize++] = addr.in4.s_addr;
16841edb306SCy Schubert 				oplist[osize++] = mask.in4.s_addr;
16941edb306SCy Schubert 
17041edb306SCy Schubert #ifdef USE_INET6
17141edb306SCy Schubert 			} else if (!strcasecmp(ops, "ip6.addr") ||
17241edb306SCy Schubert 			    !strcasecmp(ops, "ip6.src") ||
17341edb306SCy Schubert 			    !strcasecmp(ops, "ip6.dst")) {
17441edb306SCy Schubert 				i6addr_t mask, addr;
17541edb306SCy Schubert 				char *delim;
17641edb306SCy Schubert 
17741edb306SCy Schubert 				delim = strchr(s, '/');
17841edb306SCy Schubert 				if (delim != NULL) {
17941edb306SCy Schubert 					*delim++ = '\0';
18041edb306SCy Schubert 					if (genmask(AF_INET6, delim,
18141edb306SCy Schubert 						    &mask) == -1) {
18241edb306SCy Schubert 						error = "genmask failed";
18341edb306SCy Schubert 						goto parseerror;
18441edb306SCy Schubert 					}
18541edb306SCy Schubert 				} else {
18641edb306SCy Schubert 					mask.i6[0] = 0xffffffff;
18741edb306SCy Schubert 					mask.i6[1] = 0xffffffff;
18841edb306SCy Schubert 					mask.i6[2] = 0xffffffff;
18941edb306SCy Schubert 					mask.i6[3] = 0xffffffff;
19041edb306SCy Schubert 				}
19141edb306SCy Schubert 				if (gethost(AF_INET6, s, &addr) == -1) {
19241edb306SCy Schubert 					error = "gethost failed";
19341edb306SCy Schubert 					goto parseerror;
19441edb306SCy Schubert 				}
19541edb306SCy Schubert 
19641edb306SCy Schubert 				oplist[osize++] = addr.i6[0];
19741edb306SCy Schubert 				oplist[osize++] = addr.i6[1];
19841edb306SCy Schubert 				oplist[osize++] = addr.i6[2];
19941edb306SCy Schubert 				oplist[osize++] = addr.i6[3];
20041edb306SCy Schubert 				oplist[osize++] = mask.i6[0];
20141edb306SCy Schubert 				oplist[osize++] = mask.i6[1];
20241edb306SCy Schubert 				oplist[osize++] = mask.i6[2];
20341edb306SCy Schubert 				oplist[osize++] = mask.i6[3];
20441edb306SCy Schubert #endif
20541edb306SCy Schubert 
20641edb306SCy Schubert 			} else if (!strcasecmp(ops, "ip.p")) {
20741edb306SCy Schubert 				int p;
20841edb306SCy Schubert 
20941edb306SCy Schubert 				p = getproto(s);
21041edb306SCy Schubert 				if (p == -1)
21141edb306SCy Schubert 					goto parseerror;
21241edb306SCy Schubert 				oplist[osize++] = p;
21341edb306SCy Schubert 
21441edb306SCy Schubert 			} else if (!strcasecmp(ops, "tcp.flags")) {
21541edb306SCy Schubert 				u_32_t mask, flags;
21641edb306SCy Schubert 				char *delim;
21741edb306SCy Schubert 
21841edb306SCy Schubert 				delim = strchr(s, '/');
21941edb306SCy Schubert 				if (delim != NULL) {
22041edb306SCy Schubert 					*delim++ = '\0';
22141edb306SCy Schubert 					mask = tcpflags(delim);
22241edb306SCy Schubert 				} else {
22341edb306SCy Schubert 					mask = 0xff;
22441edb306SCy Schubert 				}
22541edb306SCy Schubert 				flags = tcpflags(s);
22641edb306SCy Schubert 
22741edb306SCy Schubert 				oplist[osize++] = flags;
22841edb306SCy Schubert 				oplist[osize++] = mask;
22941edb306SCy Schubert 
23041edb306SCy Schubert 
23141edb306SCy Schubert 			} else if (!strcasecmp(ops, "tcp.port") ||
23241edb306SCy Schubert 			    !strcasecmp(ops, "tcp.sport") ||
23341edb306SCy Schubert 			    !strcasecmp(ops, "tcp.dport") ||
23441edb306SCy Schubert 			    !strcasecmp(ops, "udp.port") ||
23541edb306SCy Schubert 			    !strcasecmp(ops, "udp.sport") ||
23641edb306SCy Schubert 			    !strcasecmp(ops, "udp.dport")) {
23741edb306SCy Schubert 				char proto[4];
23841edb306SCy Schubert 				u_short port;
23941edb306SCy Schubert 
24041edb306SCy Schubert 				strncpy(proto, ops, 3);
24141edb306SCy Schubert 				proto[3] = '\0';
24241edb306SCy Schubert 				if (getport(NULL, s, &port, proto) == -1)
24341edb306SCy Schubert 					goto parseerror;
24441edb306SCy Schubert 				oplist[osize++] = port;
24541edb306SCy Schubert 
24641edb306SCy Schubert 			} else if (!strcasecmp(ops, "tcp.state")) {
24741edb306SCy Schubert 				oplist[osize++] = atoi(s);
24841edb306SCy Schubert 
24941edb306SCy Schubert 			} else {
25041edb306SCy Schubert 				error = "unknown word";
25141edb306SCy Schubert 				goto parseerror;
25241edb306SCy Schubert 			}
25341edb306SCy Schubert 		}
25441edb306SCy Schubert 	}
25541edb306SCy Schubert 
25641edb306SCy Schubert 	free(temp);
25741edb306SCy Schubert 
25841edb306SCy Schubert 	if (errorptr != NULL)
25941edb306SCy Schubert 		*errorptr = NULL;
26041edb306SCy Schubert 
26141edb306SCy Schubert 	for (i = asize; i > 0; i--)
26241edb306SCy Schubert 		oplist[i] = oplist[i - 1];
26341edb306SCy Schubert 
26441edb306SCy Schubert 	oplist[0] = asize + 2;
26541edb306SCy Schubert 	oplist[asize + 1] = IPF_EXP_END;
26641edb306SCy Schubert 
2672582ae57SCy Schubert 	return (oplist);
26841edb306SCy Schubert 
26941edb306SCy Schubert parseerror:
27041edb306SCy Schubert 	if (errorptr != NULL)
27141edb306SCy Schubert 		*errorptr = error;
27241edb306SCy Schubert 	if (oplist != NULL)
27341edb306SCy Schubert 		free(oplist);
27441edb306SCy Schubert 	if (temp != NULL)
27541edb306SCy Schubert 		free(temp);
2762582ae57SCy Schubert 	return (NULL);
27741edb306SCy Schubert }
278