cmake: Find uci.h
[project/firewall3.git] / options.c
index 1dbdca8..407931b 100644 (file)
--- a/options.c
+++ b/options.c
@@ -1,7 +1,7 @@
 /*
  * firewall3 - 3rd OpenWrt UCI firewall implementation
  *
- *   Copyright (C) 2013-2014 Jo-Philipp Wich <jow@openwrt.org>
+ *   Copyright (C) 2013-2014 Jo-Philipp Wich <jo@mein.io>
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -244,8 +244,8 @@ fw3_parse_address(void *ptr, const char *val, bool is_list)
        struct fw3_address addr = { };
        struct in_addr v4;
        struct in6_addr v6;
-       char *p, *s, *e;
-       int i, m = -1;
+       char *p = NULL, *m = NULL, *s, *e;
+       int bits = -1;
 
        if (*val == '!')
        {
@@ -258,99 +258,114 @@ fw3_parse_address(void *ptr, const char *val, bool is_list)
        if (!s)
                return false;
 
-       if ((p = strchr(s, '/')) != NULL)
-       {
+       if ((m = strchr(s, '/')) != NULL)
+               *m++ = 0;
+       else if ((p = strchr(s, '-')) != NULL)
                *p++ = 0;
-               m = strtoul(p, &e, 10);
 
-               if ((e == p) || (*e != 0))
+       if (inet_pton(AF_INET6, s, &v6))
+       {
+               addr.family = FW3_FAMILY_V6;
+               addr.address.v6 = v6;
+
+               if (m)
                {
-                       if (strchr(s, ':') || !inet_pton(AF_INET, p, &v4))
+                       if (!inet_pton(AF_INET6, m, &v6))
                        {
-                               free(s);
-                               return false;
-                       }
+                               bits = strtol(m, &e, 10);
 
-                       for (i = 0, m = 32; !(v4.s_addr & 1) && (i < 32); i++)
-                       {
-                               m--;
-                               v4.s_addr >>= 1;
+                               if ((*e != 0) || !fw3_bitlen2netmask(addr.family, bits, &v6))
+                                       goto fail;
                        }
-               }
-       }
-       else if ((p = strchr(s, '-')) != NULL)
-       {
-               *p++ = 0;
 
-               if (inet_pton(AF_INET6, p, &v6))
-               {
-                       addr.family = FW3_FAMILY_V6;
-                       addr.address2.v6 = v6;
-                       addr.range = true;
+                       addr.mask.v6 = v6;
                }
-               else if (inet_pton(AF_INET, p, &v4))
+               else if (p)
                {
-                       addr.family = FW3_FAMILY_V4;
-                       addr.address2.v4 = v4;
+                       if (!inet_pton(AF_INET6, p, &addr.mask.v6))
+                               goto fail;
+
                        addr.range = true;
                }
                else
                {
-                       free(s);
-                       return false;
+                       memset(addr.mask.v6.s6_addr, 0xFF, 16);
                }
        }
-
-       if (inet_pton(AF_INET6, s, &v6))
-       {
-               addr.family = FW3_FAMILY_V6;
-               addr.address.v6 = v6;
-               addr.mask = (m >= 0) ? m : 128;
-       }
        else if (inet_pton(AF_INET, s, &v4))
        {
                addr.family = FW3_FAMILY_V4;
                addr.address.v4 = v4;
-               addr.mask = (m >= 0) ? m : 32;
+
+               if (m)
+               {
+                       if (!inet_pton(AF_INET, m, &v4))
+                       {
+                               bits = strtol(m, &e, 10);
+
+                               if ((*e != 0) || !fw3_bitlen2netmask(addr.family, bits, &v4))
+                                       goto fail;
+                       }
+
+                       addr.mask.v4 = v4;
+               }
+               else if (p)
+               {
+                       if (!inet_pton(AF_INET, p, &addr.mask.v4))
+                               goto fail;
+
+                       addr.range = true;
+               }
+               else
+               {
+                       addr.mask.v4.s_addr = 0xFFFFFFFF;
+               }
        }
        else
        {
-               free(s);
-               return false;
+               goto fail;
        }
 
        free(s);
        addr.set = true;
        put_value(ptr, &addr, sizeof(addr), is_list);
        return true;
+
+fail:
+       free(s);
+       return false;
 }
 
 bool
 fw3_parse_network(void *ptr, const char *val, bool is_list)
 {
        struct fw3_device dev = { };
-       struct fw3_address *addr;
-       struct list_head *addr_list;
+       struct fw3_address *addr, *tmp;
+       LIST_HEAD(addr_list);
 
        if (!fw3_parse_address(ptr, val, is_list))
        {
                if (!fw3_parse_device(&dev, val, false))
                        return false;
 
-               addr_list = fw3_ubus_address(dev.name);
-
-               if (addr_list)
+               fw3_ubus_address(&addr_list, dev.name);
+               list_for_each_entry(addr, &addr_list, list)
                {
-                       list_for_each_entry(addr, addr_list, list)
-                       {
-                               addr->invert = dev.invert;
-                               addr->resolved = true;
+                       addr->invert = dev.invert;
+                       addr->resolved = true;
+               }
 
-                               if (!put_value(ptr, addr, sizeof(*addr), is_list))
-                                       break;
-                       }
+               if (is_list)
+               {
+                       list_splice_tail(&addr_list, ptr);
+               }
+               else if (!list_empty(&addr_list))
+               {
+                       memcpy(ptr, list_first_entry(&addr_list, typeof(*addr), list),
+                              sizeof(*addr));
 
-                       fw3_free_list(addr_list);
+                       list_for_each_entry_safe(addr, tmp, &addr_list, list)
+                               free(addr);
                }
        }
 
@@ -427,7 +442,7 @@ fw3_parse_port(void *ptr, const char *val, bool is_list)
 bool
 fw3_parse_family(void *ptr, const char *val, bool is_list)
 {
-       if (!strcmp(val, "any"))
+       if (!strcmp(val, "any") || !strcmp(val, "*"))
                *((enum fw3_family *)ptr) = FW3_FAMILY_ANY;
        else if (!strcmp(val, "inet") || strrchr(val, '4'))
                *((enum fw3_family *)ptr) = FW3_FAMILY_V4;
@@ -528,7 +543,7 @@ fw3_parse_protocol(void *ptr, const char *val, bool is_list)
                while (isspace(*++val));
        }
 
-       if (!strcmp(val, "all"))
+       if (!strcmp(val, "all") || !strcmp(val, "any") || !strcmp(val, "*"))
        {
                proto.any = true;
                put_value(ptr, &proto, sizeof(proto), is_list);
@@ -964,8 +979,114 @@ fw3_parse_options(void *s, const struct fw3_option *opts,
 }
 
 
+bool
+fw3_parse_blob_options(void *s, const struct fw3_option *opts,
+                       struct blob_attr *a, const char *name)
+{
+       char *p, *v, buf[16];
+       bool known;
+       unsigned rem, erem;
+       struct blob_attr *o, *e;
+       const struct fw3_option *opt;
+       struct list_head *dest;
+       bool valid = true;
+
+       blobmsg_for_each_attr(o, a, rem)
+       {
+               known = false;
+
+               for (opt = opts; opt->name; opt++)
+               {
+                       if (!opt->parse)
+                               continue;
+
+                       if (strcmp(opt->name, blobmsg_name(o)))
+                               continue;
+
+                       if (blobmsg_type(o) == BLOBMSG_TYPE_ARRAY)
+                       {
+                               if (!opt->elem_size)
+                               {
+                                       fprintf(stderr, "%s: '%s' must not be a list\n",
+                                               name, opt->name);
+
+                                       valid = false;
+                               }
+                               else
+                               {
+                                       dest = (struct list_head *)((char *)s + opt->offset);
+
+                                       blobmsg_for_each_attr(e, o, erem)
+                                       {
+                                               if (blobmsg_type(e) == BLOBMSG_TYPE_INT32) {
+                                                       snprintf(buf, sizeof(buf), "%d", blobmsg_get_u32(e));
+                                                       v = buf;
+                                               } else {
+                                                       v = blobmsg_get_string(e);
+                                               }
+
+                                               if (!opt->parse(dest, v, true))
+                                               {
+                                                       fprintf(stderr, "%s: '%s' has invalid value '%s'\n",
+                                                               name, opt->name, v);
+                                                       valid = false;
+                                                       continue;
+                                               }
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               if (blobmsg_type(o) == BLOBMSG_TYPE_INT32) {
+                                       snprintf(buf, sizeof(buf), "%d", blobmsg_get_u32(o));
+                                       v = buf;
+                               } else {
+                                       v = blobmsg_get_string(o);
+                               }
+
+                               if (!v)
+                                       continue;
+
+                               if (!opt->elem_size)
+                               {
+                                       if (!opt->parse((char *)s + opt->offset, v, false))
+                                       {
+                                               fprintf(stderr, "%s: '%s' has invalid value '%s'\n",
+                                                       name, opt->name, v);
+                                               valid = false;
+                                       }
+                               }
+                               else
+                               {
+                                       dest = (struct list_head *)((char *)s + opt->offset);
+
+                                       for (p = strtok(v, " \t"); p != NULL; p = strtok(NULL, " \t"))
+                                       {
+                                               if (!opt->parse(dest, p, true))
+                                               {
+                                                       fprintf(stderr, "%s: '%s' has invalid value '%s'\n",
+                                                               name, opt->name, p);
+                                                       valid = false;
+                                                       continue;
+                                               }
+                                       }
+                               }
+                       }
+
+                       known = true;
+                       break;
+               }
+
+               if (!known && strcmp(blobmsg_name(o), "type"))
+                       fprintf(stderr, "%s: '%s' is unknown\n", name, blobmsg_name(o));
+       }
+
+       return valid;
+}
+
+
 const char *
-fw3_address_to_string(struct fw3_address *address, bool allow_invert)
+fw3_address_to_string(struct fw3_address *address, bool allow_invert, bool as_cidr)
 {
        char *p, ip[INET6_ADDRSTRLEN];
        static char buf[INET6_ADDRSTRLEN * 2 + 2];
@@ -983,13 +1104,21 @@ fw3_address_to_string(struct fw3_address *address, bool allow_invert)
        if (address->range)
        {
                inet_ntop(address->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
-                         &address->address2.v4, ip, sizeof(ip));
+                         &address->mask.v4, ip, sizeof(ip));
 
                p += sprintf(p, "-%s", ip);
        }
+       else if (!as_cidr)
+       {
+               inet_ntop(address->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
+                         &address->mask.v4, ip, sizeof(ip));
+
+               p += sprintf(p, "/%s", ip);
+       }
        else
        {
-               p += sprintf(p, "/%u", address->mask);
+               p += sprintf(p, "/%u", fw3_netmask2bitlen(address->family,
+                                                         &address->mask.v6));
        }
 
        return buf;