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