root/sbin/ipf/libipf/parseipfexpr.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' value";
                        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) {
                        asprintf(&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);
}