44bc301921
Reported by: jrtc27
Fixes: 2582ae5740
MFC after: 1 month
282 lines
6.2 KiB
C
282 lines
6.2 KiB
C
#include "ipf.h"
|
|
#include <ctype.h>
|
|
|
|
|
|
typedef struct ipfopentry {
|
|
int ipoe_cmd;
|
|
int ipoe_nbasearg;
|
|
int ipoe_maxarg;
|
|
int ipoe_argsize;
|
|
char *ipoe_word;
|
|
} ipfopentry_t;
|
|
|
|
static ipfopentry_t opwords[17] = {
|
|
{ IPF_EXP_IP_ADDR, 2, 0, 1, "ip.addr" },
|
|
{ IPF_EXP_IP6_ADDR, 2, 0, 4, "ip6.addr" },
|
|
{ IPF_EXP_IP_PR, 1, 0, 1, "ip.p" },
|
|
{ IPF_EXP_IP_SRCADDR, 2, 0, 1, "ip.src" },
|
|
{ IPF_EXP_IP_DSTADDR, 2, 0, 1, "ip.dst" },
|
|
{ IPF_EXP_IP6_SRCADDR, 2, 0, 4, "ip6.src" },
|
|
{ IPF_EXP_IP6_DSTADDR, 2, 0, 4, "ip6.dst" },
|
|
{ IPF_EXP_TCP_PORT, 1, 0, 1, "tcp.port" },
|
|
{ IPF_EXP_TCP_DPORT, 1, 0, 1, "tcp.dport" },
|
|
{ IPF_EXP_TCP_SPORT, 1, 0, 1, "tcp.sport" },
|
|
{ IPF_EXP_TCP_FLAGS, 2, 0, 1, "tcp.flags" },
|
|
{ IPF_EXP_UDP_PORT, 1, 0, 1, "udp.port" },
|
|
{ IPF_EXP_UDP_DPORT, 1, 0, 1, "udp.dport" },
|
|
{ IPF_EXP_UDP_SPORT, 1, 0, 1, "udp.sport" },
|
|
{ IPF_EXP_TCP_STATE, 1, 0, 1, "tcp.state" },
|
|
{ IPF_EXP_IDLE_GT, 1, 1, 1, "idle-gt" },
|
|
{ -1, 0, 0, 0, NULL }
|
|
};
|
|
|
|
|
|
int *
|
|
parseipfexpr(char *line, char **errorptr)
|
|
{
|
|
int not, items, asize, *oplist, osize, i;
|
|
char *temp, *arg, *s, *t, *ops, *error;
|
|
ipfopentry_t *e;
|
|
ipfexp_t *ipfe;
|
|
|
|
asize = 0;
|
|
error = NULL;
|
|
oplist = NULL;
|
|
|
|
temp = strdup(line);
|
|
if (temp == NULL) {
|
|
error = "strdup failed";
|
|
goto parseerror;
|
|
}
|
|
|
|
/*
|
|
* Eliminate any white spaces to make parsing easier.
|
|
*/
|
|
for (s = temp; *s != '\0'; ) {
|
|
if (ISSPACE(*s))
|
|
strcpy(s, s + 1);
|
|
else
|
|
s++;
|
|
}
|
|
|
|
/*
|
|
* Parse the string.
|
|
* It should be sets of "ip.dst=1.2.3.4/32;" things.
|
|
* There must be a "=" or "!=" and it must end in ";".
|
|
*/
|
|
if (temp[strlen(temp) - 1] != ';') {
|
|
error = "last character not ';'";
|
|
goto parseerror;
|
|
}
|
|
|
|
/*
|
|
* Work through the list of complete operands present.
|
|
*/
|
|
for (ops = strtok(temp, ";"); ops != NULL; ops = strtok(NULL, ";")) {
|
|
arg = strchr(ops, '=');
|
|
if ((arg < ops + 2) || (arg == NULL)) {
|
|
error = "bad 'arg' vlaue";
|
|
goto parseerror;
|
|
}
|
|
|
|
if (*(arg - 1) == '!') {
|
|
*(arg - 1) = '\0';
|
|
not = 1;
|
|
} else {
|
|
not = 0;
|
|
}
|
|
*arg++ = '\0';
|
|
|
|
|
|
for (e = opwords; e->ipoe_word; e++) {
|
|
if (strcmp(ops, e->ipoe_word) == 0)
|
|
break;
|
|
}
|
|
if (e->ipoe_word == NULL) {
|
|
error = malloc(32);
|
|
if (error != NULL) {
|
|
snprintf(error, sizeof(error), "keyword (%.10s) not found",
|
|
ops);
|
|
}
|
|
goto parseerror;
|
|
}
|
|
|
|
/*
|
|
* Count the number of commas so we know how big to
|
|
* build the array
|
|
*/
|
|
for (s = arg, items = 1; *s != '\0'; s++)
|
|
if (*s == ',')
|
|
items++;
|
|
|
|
if ((e->ipoe_maxarg != 0) && (items > e->ipoe_maxarg)) {
|
|
error = "too many items";
|
|
goto parseerror;
|
|
}
|
|
|
|
/*
|
|
* osize will mark the end of where we have filled up to
|
|
* and is thus where we start putting new data.
|
|
*/
|
|
osize = asize;
|
|
asize += 4 + (items * e->ipoe_nbasearg * e->ipoe_argsize);
|
|
if (oplist == NULL)
|
|
oplist = calloc(asize + 2, sizeof(int));
|
|
else
|
|
oplist = reallocarray(oplist, asize + 2, sizeof(int));
|
|
if (oplist == NULL) {
|
|
error = "oplist alloc failed";
|
|
goto parseerror;
|
|
}
|
|
ipfe = (ipfexp_t *)(oplist + osize);
|
|
osize += 4;
|
|
ipfe->ipfe_cmd = e->ipoe_cmd;
|
|
ipfe->ipfe_not = not;
|
|
ipfe->ipfe_narg = items * e->ipoe_nbasearg;
|
|
ipfe->ipfe_size = items * e->ipoe_nbasearg * e->ipoe_argsize;
|
|
ipfe->ipfe_size += 4;
|
|
|
|
for (s = arg; (*s != '\0') && (osize < asize); s = t) {
|
|
/*
|
|
* Look for the end of this arg or the ',' to say
|
|
* there is another following.
|
|
*/
|
|
for (t = s; (*t != '\0') && (*t != ','); t++)
|
|
;
|
|
if (*t == ',')
|
|
*t++ = '\0';
|
|
|
|
if (!strcasecmp(ops, "ip.addr") ||
|
|
!strcasecmp(ops, "ip.src") ||
|
|
!strcasecmp(ops, "ip.dst")) {
|
|
i6addr_t mask, addr;
|
|
char *delim;
|
|
|
|
delim = strchr(s, '/');
|
|
if (delim != NULL) {
|
|
*delim++ = '\0';
|
|
if (genmask(AF_INET, delim,
|
|
&mask) == -1) {
|
|
error = "genmask failed";
|
|
goto parseerror;
|
|
}
|
|
} else {
|
|
mask.in4.s_addr = 0xffffffff;
|
|
}
|
|
if (gethost(AF_INET, s, &addr) == -1) {
|
|
error = "gethost failed";
|
|
goto parseerror;
|
|
}
|
|
|
|
oplist[osize++] = addr.in4.s_addr;
|
|
oplist[osize++] = mask.in4.s_addr;
|
|
|
|
#ifdef USE_INET6
|
|
} else if (!strcasecmp(ops, "ip6.addr") ||
|
|
!strcasecmp(ops, "ip6.src") ||
|
|
!strcasecmp(ops, "ip6.dst")) {
|
|
i6addr_t mask, addr;
|
|
char *delim;
|
|
|
|
delim = strchr(s, '/');
|
|
if (delim != NULL) {
|
|
*delim++ = '\0';
|
|
if (genmask(AF_INET6, delim,
|
|
&mask) == -1) {
|
|
error = "genmask failed";
|
|
goto parseerror;
|
|
}
|
|
} else {
|
|
mask.i6[0] = 0xffffffff;
|
|
mask.i6[1] = 0xffffffff;
|
|
mask.i6[2] = 0xffffffff;
|
|
mask.i6[3] = 0xffffffff;
|
|
}
|
|
if (gethost(AF_INET6, s, &addr) == -1) {
|
|
error = "gethost failed";
|
|
goto parseerror;
|
|
}
|
|
|
|
oplist[osize++] = addr.i6[0];
|
|
oplist[osize++] = addr.i6[1];
|
|
oplist[osize++] = addr.i6[2];
|
|
oplist[osize++] = addr.i6[3];
|
|
oplist[osize++] = mask.i6[0];
|
|
oplist[osize++] = mask.i6[1];
|
|
oplist[osize++] = mask.i6[2];
|
|
oplist[osize++] = mask.i6[3];
|
|
#endif
|
|
|
|
} else if (!strcasecmp(ops, "ip.p")) {
|
|
int p;
|
|
|
|
p = getproto(s);
|
|
if (p == -1)
|
|
goto parseerror;
|
|
oplist[osize++] = p;
|
|
|
|
} else if (!strcasecmp(ops, "tcp.flags")) {
|
|
u_32_t mask, flags;
|
|
char *delim;
|
|
|
|
delim = strchr(s, '/');
|
|
if (delim != NULL) {
|
|
*delim++ = '\0';
|
|
mask = tcpflags(delim);
|
|
} else {
|
|
mask = 0xff;
|
|
}
|
|
flags = tcpflags(s);
|
|
|
|
oplist[osize++] = flags;
|
|
oplist[osize++] = mask;
|
|
|
|
|
|
} else if (!strcasecmp(ops, "tcp.port") ||
|
|
!strcasecmp(ops, "tcp.sport") ||
|
|
!strcasecmp(ops, "tcp.dport") ||
|
|
!strcasecmp(ops, "udp.port") ||
|
|
!strcasecmp(ops, "udp.sport") ||
|
|
!strcasecmp(ops, "udp.dport")) {
|
|
char proto[4];
|
|
u_short port;
|
|
|
|
strncpy(proto, ops, 3);
|
|
proto[3] = '\0';
|
|
if (getport(NULL, s, &port, proto) == -1)
|
|
goto parseerror;
|
|
oplist[osize++] = port;
|
|
|
|
} else if (!strcasecmp(ops, "tcp.state")) {
|
|
oplist[osize++] = atoi(s);
|
|
|
|
} else {
|
|
error = "unknown word";
|
|
goto parseerror;
|
|
}
|
|
}
|
|
}
|
|
|
|
free(temp);
|
|
|
|
if (errorptr != NULL)
|
|
*errorptr = NULL;
|
|
|
|
for (i = asize; i > 0; i--)
|
|
oplist[i] = oplist[i - 1];
|
|
|
|
oplist[0] = asize + 2;
|
|
oplist[asize + 1] = IPF_EXP_END;
|
|
|
|
return (oplist);
|
|
|
|
parseerror:
|
|
if (errorptr != NULL)
|
|
*errorptr = error;
|
|
if (oplist != NULL)
|
|
free(oplist);
|
|
if (temp != NULL)
|
|
free(temp);
|
|
return (NULL);
|
|
}
|