iptables: fix regression with unintended free in need_protomatch
[project/firewall3.git] / redirects.c
index ab95395ca6afda4f02227e4611e687faabb4673c..9a827b65a892cb9d8794ecdc3bd0a3189a903c0f 100644 (file)
@@ -61,6 +61,7 @@ const struct fw3_option fw3_redirect_opts[] = {
        FW3_OPT("reflection",          bool,      redirect,     reflection),
        FW3_OPT("reflection_src",      reflection_source,
                                                  redirect,     reflection_src),
+       FW3_LIST("reflection_zone",    device,    redirect,     reflection_zones),
 
        FW3_OPT("target",              target,    redirect,     target),
 
@@ -154,7 +155,7 @@ resolve_dest(struct uci_element *e, struct fw3_redirect *redir,
                        if (!compare_addr(addr, &redir->ip_redir))
                                continue;
 
-                       strncpy(redir->dest.name, zone->name, sizeof(redir->dest.name));
+                       snprintf(redir->dest.name, sizeof(redir->dest.name), "%s", zone->name);
                        redir->dest.set = true;
                        redir->_dest = zone;
 
@@ -254,14 +255,13 @@ check_redirect(struct fw3_state *state, struct fw3_redirect *redir, struct uci_e
        }
        else if (redir->ipset.set && state->disable_ipsets)
        {
-               warn_section("redirect", redir, e, "skipped due to disabled ipset support",
-                               redir->name);
+               warn_section("redirect", redir, e, "skipped due to disabled ipset support");
                return false;
        }
        else if (redir->ipset.set &&
                        !(redir->ipset.ptr = fw3_lookup_ipset(state, redir->ipset.name)))
        {
-               warn_section("redirect", redir, e, "refers to unknown ipset '%s'", redir->name,
+               warn_section("redirect", redir, e, "refers to unknown ipset '%s'",
                                redir->ipset.name);
                return false;
        }
@@ -367,6 +367,7 @@ fw3_alloc_redirect(struct fw3_state *state)
 
        INIT_LIST_HEAD(&redir->proto);
        INIT_LIST_HEAD(&redir->mac_src);
+       INIT_LIST_HEAD(&redir->reflection_zones);
 
        redir->enabled = true;
        redir->reflection = true;
@@ -457,14 +458,14 @@ append_chain_nat(struct fw3_ipt_rule *r, struct fw3_redirect *redir)
 static void
 set_redirect(struct fw3_ipt_rule *r, struct fw3_port *port)
 {
-       char buf[sizeof("65535-65535\0")];
+       char buf[sizeof("65535-65535")];
 
        fw3_ipt_rule_target(r, "REDIRECT");
 
        if (port && port->set)
        {
                if (port->port_min == port->port_max)
-                       sprintf(buf, "%u", port->port_min);
+                       snprintf(buf, sizeof(buf), "%u", port->port_min);
                else
                        snprintf(buf, sizeof(buf), "%u-%u", port->port_min, port->port_max);
 
@@ -476,22 +477,30 @@ static void
 set_snat_dnat(struct fw3_ipt_rule *r, enum fw3_flag target,
               struct fw3_address *addr, struct fw3_port *port)
 {
-       char buf[sizeof("255.255.255.255:65535-65535\0")];
-
-       buf[0] = '\0';
+       char buf[sizeof("255.255.255.255:65535-65535")] = {};
+       char ip[INET_ADDRSTRLEN], *p = buf;
+       size_t rem = sizeof(buf);
+       int len;
 
        if (addr && addr->set)
        {
-               inet_ntop(AF_INET, &addr->address.v4, buf, sizeof(buf));
+               inet_ntop(AF_INET, &addr->address.v4, ip, sizeof(ip));
+
+               len = snprintf(p, rem, "%s", ip);
+
+               if (len < 0 || len >= rem)
+                       return;
+
+               rem -= len;
+               p += len;
        }
 
        if (port && port->set)
        {
                if (port->port_min == port->port_max)
-                       sprintf(buf + strlen(buf), ":%u", port->port_min);
+                       snprintf(p, rem, ":%u", port->port_min);
                else
-                       sprintf(buf + strlen(buf), ":%u-%u",
-                               port->port_min, port->port_max);
+                       snprintf(p, rem, ":%u-%u", port->port_min, port->port_max);
        }
 
        if (target == FW3_FLAG_DNAT)
@@ -612,7 +621,7 @@ static void
 print_reflection(struct fw3_ipt_handle *h, struct fw3_state *state,
                  struct fw3_redirect *redir, int num,
                  struct fw3_protocol *proto, struct fw3_address *ra,
-                 struct fw3_address *ia, struct fw3_address *ea)
+                 struct fw3_address *ia, struct fw3_address *ea, struct fw3_device *rz)
 {
        struct fw3_ipt_rule *r;
 
@@ -625,7 +634,7 @@ print_reflection(struct fw3_ipt_handle *h, struct fw3_state *state,
                fw3_ipt_rule_time(r, &redir->time);
                set_comment(r, redir->name, num, "reflection");
                set_snat_dnat(r, FW3_FLAG_DNAT, &redir->ip_redir, &redir->port_redir);
-               fw3_ipt_rule_replace(r, "zone_%s_prerouting", redir->dest.name);
+               fw3_ipt_rule_replace(r, "zone_%s_prerouting", rz->name);
 
                r = fw3_ipt_rule_create(h, proto, NULL, NULL, ia, &redir->ip_redir);
                fw3_ipt_rule_sport_dport(r, NULL, &redir->port_redir);
@@ -633,7 +642,7 @@ print_reflection(struct fw3_ipt_handle *h, struct fw3_state *state,
                fw3_ipt_rule_time(r, &redir->time);
                set_comment(r, redir->name, num, "reflection");
                set_snat_dnat(r, FW3_FLAG_SNAT, ra, NULL);
-               fw3_ipt_rule_replace(r, "zone_%s_postrouting", redir->dest.name);
+               fw3_ipt_rule_replace(r, "zone_%s_postrouting", rz->name);
                break;
 
        default:
@@ -649,6 +658,8 @@ expand_redirect(struct fw3_ipt_handle *handle, struct fw3_state *state,
        struct fw3_address *ext_addr, *int_addr, ref_addr;
        struct fw3_protocol *proto;
        struct fw3_mac *mac;
+       struct fw3_device *reflection_zone;
+       struct fw3_zone *zone;
 
        if (redir->name)
                info("   * Redirect '%s'", redir->name);
@@ -705,43 +716,61 @@ expand_redirect(struct fw3_ipt_handle *handle, struct fw3_state *state,
                return;
 
        ext_addrs = fw3_resolve_zone_addresses(redir->_src, &redir->ip_dest);
-       int_addrs = fw3_resolve_zone_addresses(redir->_dest, NULL);
-
-       if (!ext_addrs || !int_addrs)
-               goto out;
+       if (!ext_addrs)
+               return;
 
        list_for_each_entry(ext_addr, ext_addrs, list)
        {
                if (!fw3_is_family(ext_addr, handle->family))
                        continue;
 
-               list_for_each_entry(int_addr, int_addrs, list)
+               for (reflection_zone = list_empty(&redir->reflection_zones)
+                      ? &redir->dest
+                      : list_first_entry(&redir->reflection_zones, struct fw3_device, list);
+                    list_empty(&redir->reflection_zones)
+                      ? (reflection_zone == &redir->dest)
+                      : (&reflection_zone->list != &redir->reflection_zones);
+                    reflection_zone = list_empty(&redir->reflection_zones)
+                      ? NULL
+                      : list_entry(reflection_zone->list.next, struct fw3_device, list))
                {
-                       if (!fw3_is_family(int_addr, handle->family))
+                       zone = fw3_lookup_zone(state, reflection_zone->name);
+
+                       if (!zone)
+                               continue;
+
+                       int_addrs = fw3_resolve_zone_addresses(zone, NULL);
+                       if (!int_addrs)
                                continue;
 
-                       fw3_foreach(proto, &redir->proto)
+                       list_for_each_entry(int_addr, int_addrs, list)
                        {
-                               if (!proto)
+                               if (!fw3_is_family(int_addr, handle->family))
                                        continue;
 
-                               if (redir->reflection_src == FW3_REFLECTION_INTERNAL)
-                                       ref_addr = *int_addr;
-                               else
-                                       ref_addr = *ext_addr;
+                               fw3_foreach(proto, &redir->proto)
+                               {
+                                       if (!proto)
+                                               continue;
+
+                                       if (redir->reflection_src == FW3_REFLECTION_INTERNAL)
+                                               ref_addr = *int_addr;
+                                       else
+                                               ref_addr = *ext_addr;
 
-                               ref_addr.mask.v4.s_addr = 0xFFFFFFFF;
-                               ext_addr->mask.v4.s_addr = 0xFFFFFFFF;
+                                       ref_addr.mask.v4.s_addr = 0xFFFFFFFF;
+                                       ext_addr->mask.v4.s_addr = 0xFFFFFFFF;
 
-                               print_reflection(handle, state, redir, num, proto,
-                                                                &ref_addr, int_addr, ext_addr);
+                                       print_reflection(handle, state, redir, num, proto,
+                                                        &ref_addr, int_addr, ext_addr, reflection_zone);
+                               }
                        }
+
+                       fw3_free_list(int_addrs);
                }
        }
 
-out:
        fw3_free_list(ext_addrs);
-       fw3_free_list(int_addrs);
 }
 
 void