ubox: Fix some memory leaks
[project/ubox.git] / validate / validate.c
index c3ae56e..0628407 100644 (file)
 #include <netinet/ether.h>
 #include <sys/stat.h>
 
+#include <sys/types.h>
+#include <regex.h>
+
+#include <uci.h>
+
 #include "libvalidate.h"
 
 enum dt_optype {
        OP_UNKNOWN,
-       OP_BOOL,
        OP_NUMBER,
        OP_STRING,
        OP_FUNCTION
@@ -49,12 +53,15 @@ struct dt_op {
 struct dt_state {
        int pos;
        int depth;
+       struct uci_context *ctx;
        const char *value;
+       enum dt_type valtype;
        struct dt_op stack[32];
 };
 
 struct dt_fun {
        const char *name;
+       enum dt_type valtype;
        bool (*call)(struct dt_state *s, int nargs);
 };
 
@@ -98,7 +105,7 @@ dt_test_string(const char *s, const char *end, const char *value)
                s++;
        }
 
-       return (*s == *value || (s > end && *value == 0));
+       return (*s == *value || (s >= end && *value == 0));
 }
 
 static bool
@@ -107,6 +114,10 @@ dt_step(struct dt_state *s);
 static bool
 dt_call(struct dt_state *s);
 
+#define dt_getint(n, v) \
+       ((n < nargs && s->stack[s->pos + n].type == OP_NUMBER) \
+               ? (v = s->stack[s->pos + n].value.number, 1) : 0)
+
 static bool
 dt_type_or(struct dt_state *s, int nargs)
 {
@@ -162,8 +173,10 @@ dt_type_list(struct dt_state *s, int nargs)
        char *p, *str = strdup(s->value);
        const char *value = s->value;
 
-       if (!str || !nargs)
+       if (!str || !nargs) {
+               free(str);
                return false;
+       }
 
        for (p = strtok(str, " \t"); p; p = strtok(NULL, " \t"))
        {
@@ -187,15 +200,13 @@ dt_type_list(struct dt_state *s, int nargs)
 static bool
 dt_type_min(struct dt_state *s, int nargs)
 {
-       int n;
+       int n, min;
        char *e;
 
-       if (nargs >= 1 && s->stack[s->pos].type == OP_NUMBER)
+       if (dt_getint(0, min))
        {
                n = strtol(s->value, &e, 0);
-
-               return (e > s->value && *e == 0 &&
-                       n >= s->stack[s->pos].value.number);
+               return (e > s->value && *e == 0 && n >= min);
        }
 
        return false;
@@ -204,15 +215,13 @@ dt_type_min(struct dt_state *s, int nargs)
 static bool
 dt_type_max(struct dt_state *s, int nargs)
 {
-       int n;
+       int n, max;
        char *e;
 
-       if (nargs >= 1 && s->stack[s->pos].type == OP_NUMBER)
+       if (dt_getint(0, max))
        {
                n = strtol(s->value, &e, 0);
-
-               return (e > s->value && *e == 0 &&
-                       n <= s->stack[s->pos].value.number);
+               return (e > s->value && *e == 0 && n <= max);
        }
 
        return false;
@@ -221,18 +230,13 @@ dt_type_max(struct dt_state *s, int nargs)
 static bool
 dt_type_range(struct dt_state *s, int nargs)
 {
-       int n;
+       int n, min, max;
        char *e;
 
-       if (nargs >= 2 &&
-           s->stack[s->pos].type == OP_NUMBER &&
-           s->stack[s->pos + 1].type == OP_NUMBER)
+       if (dt_getint(0, min) && dt_getint(1, max))
        {
                n = strtol(s->value, &e, 0);
-
-               return (e > s->value && *e == 0 &&
-                       n >= s->stack[s->pos].value.number &&
-                       n <= s->stack[s->pos + 1].value.number);
+               return (e > s->value && *e == 0 && n >= min && n <= max);
        }
 
        return false;
@@ -241,8 +245,10 @@ dt_type_range(struct dt_state *s, int nargs)
 static bool
 dt_type_minlen(struct dt_state *s, int nargs)
 {
-       if (nargs >= 1 && s->stack[s->pos].type == OP_NUMBER)
-               return (strlen(s->value) >= s->stack[s->pos].value.number);
+       int min;
+
+       if (dt_getint(0, min))
+               return (strlen(s->value) >= min);
 
        return false;
 }
@@ -250,8 +256,10 @@ dt_type_minlen(struct dt_state *s, int nargs)
 static bool
 dt_type_maxlen(struct dt_state *s, int nargs)
 {
-       if (nargs >= 1 && s->stack[s->pos].type == OP_NUMBER)
-               return (strlen(s->value) <= s->stack[s->pos].value.number);
+       int max;
+
+       if (dt_getint(0, max))
+               return (strlen(s->value) <= max);
 
        return false;
 }
@@ -259,11 +267,11 @@ dt_type_maxlen(struct dt_state *s, int nargs)
 static bool
 dt_type_rangelen(struct dt_state *s, int nargs)
 {
-       if (nargs >= 2 &&
-           s->stack[s->pos].type == OP_NUMBER &&
-           s->stack[s->pos + 1].type == OP_NUMBER)
-               return (strlen(s->value) >= s->stack[s->pos].value.number &&
-                               strlen(s->value) <= s->stack[s->pos + 1].value.number);
+       int min, max;
+       int len = strlen(s->value);
+
+       if (dt_getint(0, min) && dt_getint(1, max))
+               return (len >= min && len <= max);
 
        return false;
 }
@@ -272,8 +280,13 @@ static bool
 dt_type_int(struct dt_state *s, int nargs)
 {
        char *e;
+       int base = 0;
+
+       if (!isxdigit(*s->value) && *s->value != '-')
+               return false;
 
-       strtol(s->value, &e, 0);
+       dt_getint(0, base);
+       strtol(s->value, &e, base);
 
        return (e > s->value && *e == 0);
 }
@@ -281,12 +294,16 @@ dt_type_int(struct dt_state *s, int nargs)
 static bool
 dt_type_uint(struct dt_state *s, int nargs)
 {
-       int n;
        char *e;
+       int base = 0;
 
-       n = strtol(s->value, &e, 0);
+       if (!isxdigit(*s->value))
+               return false;
+
+       dt_getint(0, base);
+       strtoul(s->value, &e, base);
 
-       return (e > s->value && *e == 0 && n >= 0);
+       return (e > s->value && *e == 0);
 }
 
 static bool
@@ -315,8 +332,8 @@ dt_type_bool(struct dt_state *s, int nargs)
 {
        int i;
        const char *values[] = {
-               "0", "off", "false", "no",
-               "1", "on", "true", "yes"
+               "0", "off", "false", "no", "disabled",
+               "1", "on", "true", "yes", "enabled"
        };
 
        for (i = 0; i < sizeof(values) / sizeof(values[0]); i++)
@@ -329,6 +346,38 @@ dt_type_bool(struct dt_state *s, int nargs)
 static bool
 dt_type_string(struct dt_state *s, int nargs)
 {
+       int min, max;
+       int len = strlen(s->value);
+
+       if (dt_getint(0, min) && (len < min))
+               return false;
+
+       if (dt_getint(1, max) && (len > max))
+               return false;
+
+       return true;
+}
+
+static bool
+dt_type_hexstring(struct dt_state *s, int nargs)
+{
+       int min, max;
+       int len = strlen(s->value);
+       const char *p;
+
+       if (len % 2)
+               return false;
+
+       if (dt_getint(0, min) && (len < min))
+               return false;
+
+       if (dt_getint(1, max) && (len > max))
+               return false;
+
+       for (p = s->value; *p; p++)
+               if (!isxdigit(*p))
+                       return false;
+
        return true;
 }
 
@@ -683,49 +732,202 @@ dt_type_file(struct dt_state *s, int nargs)
        return (!stat(s->value, &st) && S_ISREG(st.st_mode));
 }
 
+static bool
+dt_type_regex(struct dt_state *s, int nargs)
+{
+       bool rv;
+       int relen;
+       regex_t pattern;
+       char *re = NULL;
+
+       if (nargs < 1 || s->stack[s->pos].type != OP_STRING)
+               return false;
+
+       relen = s->stack[s->pos].length;
+       re = alloca(relen + 3);
+
+       if (!re)
+               return false;
+
+       memset(re, 0, relen + 3);
+       memcpy(re + 1, s->stack[s->pos].value.string, relen);
+
+       re[0] = '^';
+       re[relen + 1] = '$';
+
+       if (regcomp(&pattern, re, REG_EXTENDED | REG_NOSUB))
+               return false;
+
+       rv = !regexec(&pattern, s->value, 0, NULL, 0);
+
+       regfree(&pattern);
+
+       return rv;
+}
+
+static void *
+dt_uci_lookup(struct dt_state *s, const char *pkg,
+              const char *sct, const char *opt, enum uci_type type)
+{
+       struct uci_ptr ptr = {
+               .package = pkg,
+               .section = sct,
+               .option  = opt
+       };
+
+       if (!s->ctx || uci_lookup_ptr(s->ctx, &ptr, NULL, false) ||
+           !(ptr.flags & UCI_LOOKUP_COMPLETE))
+               return NULL;
+
+       if (ptr.last->type != type)
+               return NULL;
+
+       switch (type)
+       {
+       case UCI_TYPE_PACKAGE:
+               return uci_to_package(ptr.last);
+
+       case UCI_TYPE_SECTION:
+               return uci_to_section(ptr.last);
+
+       case UCI_TYPE_OPTION:
+               return uci_to_option(ptr.last);
+
+       default:
+               return NULL;
+       }
+}
+
+static bool
+dt_uci_cmp(struct dt_state *s,
+           const char *pkg, const char *sct, const char *opt)
+{
+       struct uci_element *e;
+       struct uci_option *o = dt_uci_lookup(s, pkg, sct, opt, UCI_TYPE_OPTION);
+
+       if (!o)
+               return false;
+
+       switch (o->type)
+       {
+       case UCI_TYPE_STRING:
+               if (!strcmp(s->value, o->v.string))
+                       return true;
+               break;
+
+       case UCI_TYPE_LIST:
+               uci_foreach_element(&o->v.list, e)
+                       if (!strcmp(s->value, e->name))
+                               return true;
+               break;
+       }
+
+       return false;
+}
+
+static bool
+dt_type_uci(struct dt_state *s, int nargs)
+{
+       int i, len;
+       struct uci_element *e;
+       struct uci_package *p;
+       char *cso[3] = { };
+
+       if (!s->ctx)
+               return false;
+
+       for (i = 0; i < nargs && i < 3; i++)
+       {
+               if (s->stack[s->pos + i].type != OP_STRING)
+                       continue;
+
+               len = s->stack[s->pos + i].length;
+               cso[i] = alloca(len + 1);
+
+               if (!cso[i])
+                       continue;
+
+               memset(cso[i], 0, len + 1);
+               memcpy(cso[i], s->stack[s->pos + i].value.string, len);
+       }
+
+       if (!cso[0] || !cso[1] || (*cso[1] != '@' && !cso[2]))
+               return false;
+
+       if (*cso[1] != '@')
+               return dt_uci_cmp(s, cso[0], cso[1], cso[2]);
+
+       p = dt_uci_lookup(s, cso[0], NULL, NULL, UCI_TYPE_PACKAGE);
+
+       if (!p)
+               return false;
+
+       uci_foreach_element(&p->sections, e)
+       {
+               if (strcmp(uci_to_section(e)->type, cso[1] + 1))
+                       continue;
+
+               if (!cso[2])
+               {
+                       if (!strcmp(s->value, e->name))
+                               return true;
+               }
+               else
+               {
+                       if (dt_uci_cmp(s, cso[0], e->name, cso[2]))
+                               return true;
+               }
+       }
+
+       return false;
+}
+
 
 static struct dt_fun dt_types[] = {
-       { "or",                         dt_type_or                      },
-       { "and",                        dt_type_and                     },
-       { "not",                        dt_type_not                     },
-       { "neg",                        dt_type_neg                     },
-       { "list",                       dt_type_list            },
-       { "min",                        dt_type_min                     },
-       { "max",                        dt_type_max                     },
-       { "range",                      dt_type_range           },
-       { "minlength",          dt_type_minlen          },
-       { "maxlength",          dt_type_maxlen          },
-       { "rangelength",        dt_type_rangelen        },
-       { "integer",            dt_type_int                     },
-       { "uinteger",           dt_type_uint            },
-       { "float",                      dt_type_float           },
-       { "ufloat",                     dt_type_ufloat          },
-       { "bool",                       dt_type_bool            },
-       { "string",                     dt_type_string          },
-       { "ip4addr",            dt_type_ip4addr         },
-       { "ip6addr",            dt_type_ip6addr         },
-       { "ipaddr",                     dt_type_ipaddr          },
-       { "cidr4",                      dt_type_cidr4           },
-       { "cidr6",                      dt_type_cidr6           },
-       { "cidr",                       dt_type_cidr            },
-       { "netmask4",           dt_type_netmask4        },
-       { "netmask6",           dt_type_netmask6        },
-       { "ipmask4",            dt_type_ipmask4         },
-       { "ipmask6",            dt_type_ipmask6         },
-       { "ipmask",                     dt_type_ipmask          },
-       { "port",                       dt_type_port            },
-       { "portrange",          dt_type_portrange       },
-       { "macaddr",            dt_type_macaddr         },
-       { "uciname",        dt_type_uciname             },
-       { "wpakey",                     dt_type_wpakey          },
-       { "wepkey",                     dt_type_wepkey          },
-       { "hostname",           dt_type_hostname        },
-       { "host",                       dt_type_host            },
-       { "network",            dt_type_network         },
-       { "phonedigit",         dt_type_phonedigit      },
-       { "directory",          dt_type_directory       },
-       { "device",                     dt_type_device          },
-       { "file",                       dt_type_file            },
+       { "or",                 DT_INVALID,     dt_type_or              },
+       { "and",                DT_INVALID,     dt_type_and             },
+       { "not",                DT_INVALID,     dt_type_not             },
+       { "neg",                DT_INVALID,     dt_type_neg             },
+       { "list",               DT_INVALID,     dt_type_list            },
+       { "min",                DT_NUMBER,      dt_type_min             },
+       { "max",                DT_NUMBER,      dt_type_max             },
+       { "range",              DT_NUMBER,      dt_type_range           },
+       { "minlength",          DT_STRING,      dt_type_minlen          },
+       { "maxlength",          DT_STRING,      dt_type_maxlen          },
+       { "rangelength",        DT_STRING,      dt_type_rangelen        },
+       { "integer",            DT_NUMBER,      dt_type_int             },
+       { "uinteger",           DT_NUMBER,      dt_type_uint            },
+       { "float",              DT_NUMBER,      dt_type_float           },
+       { "ufloat",             DT_NUMBER,      dt_type_ufloat          },
+       { "bool",               DT_BOOL,        dt_type_bool            },
+       { "string",             DT_STRING,      dt_type_string          },
+       { "hexstring",          DT_STRING,      dt_type_hexstring       },
+       { "ip4addr",            DT_STRING,      dt_type_ip4addr         },
+       { "ip6addr",            DT_STRING,      dt_type_ip6addr         },
+       { "ipaddr",             DT_STRING,      dt_type_ipaddr          },
+       { "cidr4",              DT_STRING,      dt_type_cidr4           },
+       { "cidr6",              DT_STRING,      dt_type_cidr6           },
+       { "cidr",               DT_STRING,      dt_type_cidr            },
+       { "netmask4",           DT_STRING,      dt_type_netmask4        },
+       { "netmask6",           DT_STRING,      dt_type_netmask6        },
+       { "ipmask4",            DT_STRING,      dt_type_ipmask4         },
+       { "ipmask6",            DT_STRING,      dt_type_ipmask6         },
+       { "ipmask",             DT_STRING,      dt_type_ipmask          },
+       { "port",               DT_NUMBER,      dt_type_port            },
+       { "portrange",          DT_STRING,      dt_type_portrange       },
+       { "macaddr",            DT_STRING,      dt_type_macaddr         },
+       { "uciname",            DT_STRING,      dt_type_uciname         },
+       { "wpakey",             DT_STRING,      dt_type_wpakey          },
+       { "wepkey",             DT_STRING,      dt_type_wepkey          },
+       { "hostname",           DT_STRING,      dt_type_hostname        },
+       { "host",               DT_STRING,      dt_type_host            },
+       { "network",            DT_STRING,      dt_type_network         },
+       { "phonedigit",         DT_STRING,      dt_type_phonedigit      },
+       { "directory",          DT_STRING,      dt_type_directory       },
+       { "device",             DT_STRING,      dt_type_device          },
+       { "file",               DT_STRING,      dt_type_file            },
+       { "regex",              DT_STRING,      dt_type_regex           },
+       { "uci",                DT_STRING,      dt_type_uci             },
 
        { }
 };
@@ -795,7 +997,7 @@ dt_parse_atom(struct dt_state *s, const char *label, const char *end)
                        {
                                op->next = p + 1;
                                op->type = OP_STRING;
-                               op->length = (p - label) - 2;
+                               op->length = (p - label) - 1;
                                op->value.string = label + 1;
                                op->nextop = ++s->depth;
 
@@ -948,16 +1150,16 @@ dt_step(struct dt_state *s)
 
        switch (op->type)
        {
-       case OP_BOOL:
-               rv = op->value.boolean;
-               break;
-
        case OP_NUMBER:
                rv = dt_test_number(op->value.number, s->value);
+               if (rv)
+                       s->valtype = DT_NUMBER;
                break;
 
        case OP_STRING:
                rv = dt_test_string(op->value.string, op->value.string + op->length, s->value);
+               if (rv)
+                       s->valtype = DT_STRING;
                break;
 
        case OP_FUNCTION:
@@ -984,14 +1186,19 @@ dt_call(struct dt_state *s)
 
        rv = func->call(s, fptr->length);
 
+       if (rv && func->valtype)
+               s->valtype = func->valtype;
+
        s->pos = fptr->nextop;
 
        return rv;
 }
 
-bool
+enum dt_type
 dt_parse(const char *code, const char *value)
 {
+       enum dt_type rv = DT_INVALID;
+
        struct dt_state s = {
                .depth = 1,
                .stack = {
@@ -1009,7 +1216,14 @@ dt_parse(const char *code, const char *value)
        if (!dt_parse_list(&s, code, code + strlen(code)))
                return false;
 
+       s.ctx = uci_alloc_context();
        s.value = value;
 
-       return dt_call(&s);
+       if (dt_call(&s))
+               rv = s.valtype;
+
+       if (s.ctx)
+               uci_free_context(s.ctx);
+
+       return rv;
 }