dnsmasq: bump to v2.81rc1
[openwrt/staging/rmilecki.git] / package / network / services / dnsmasq / patches / 0011-Free-config-file-values-on-parsing-errors.patch
diff --git a/package/network/services/dnsmasq/patches/0011-Free-config-file-values-on-parsing-errors.patch b/package/network/services/dnsmasq/patches/0011-Free-config-file-values-on-parsing-errors.patch
deleted file mode 100644 (file)
index 0b53b30..0000000
+++ /dev/null
@@ -1,1199 +0,0 @@
-From 59e470381f84f2fdf0640c7bc67827f3f0c64784 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
-Date: Fri, 2 Nov 2018 22:39:39 +0000
-Subject: [PATCH 11/32] Free config file values on parsing errors.
-
-This time I have a little bit more controversal patches. But I think
-still useful. They fixes memory leaks that might occur in some cases.
-Most dnsmasq errors is fatal, so it does not matter. But some are not.
-Some parts are reloaded on SIGHUP signal, so it might leak more than once.
-
-Some example when it changes the failures. Use dhcp-options file with
-this content:
-
-tag:error,vendor:redhat
-option:ntp-server,1.2.3.4.5
-option6:ntp-server,[:::]
-
-Is not fatal and dnsmasq will start. On each reload command, it would
-leak some memory. I validated it using valgrind --leak-check=full
-dnsmasq -d. This patch fixes it. It introduces something that might be
-considered constructor and destructor of selected structures.
-
-Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
----
- src/option.c | 533 ++++++++++++++++++++++++++++++++++-----------------
- 1 file changed, 352 insertions(+), 181 deletions(-)
-
---- a/src/option.c
-+++ b/src/option.c
-@@ -577,14 +577,15 @@ static void *opt_malloc(size_t size)
-   return ret;
- }
--static char *opt_string_alloc(char *cp)
-+static char *opt_string_alloc(const char *cp)
- {
-   char *ret = NULL;
-+  size_t len;
-   
--  if (cp && strlen(cp) != 0)
-+  if (cp && (len = strlen(cp)) != 0)
-     {
--      ret = opt_malloc(strlen(cp)+1);
--      strcpy(ret, cp); 
-+      ret = opt_malloc(len+1);
-+      memcpy(ret, cp, len+1); 
-       
-       /* restore hidden metachars */
-       unhide_metas(ret);
-@@ -759,6 +760,8 @@ static void do_usage(void)
- }
- #define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
-+#define ret_err_free(x,m) do { strcpy(errstr, (x)); free((m)); return 0; } while (0)
-+#define goto_err(x) do { strcpy(errstr, (x)); goto on_error; } while (0)
- static char *parse_mysockaddr(char *arg, union mysockaddr *addr) 
- {
-@@ -904,6 +907,8 @@ static struct server *add_rev4(struct in
-       p += sprintf(p, "%d.", (a >> 24) & 0xff);
-       break;
-     default:
-+      free(serv->domain);
-+      free(serv);
-       return NULL;
-     }
-@@ -958,6 +963,97 @@ static char *set_prefix(char *arg)
-    return arg;
- }
-+static struct dhcp_netid *
-+dhcp_netid_create(const char *net, struct dhcp_netid *next)
-+{
-+  struct dhcp_netid *tt;
-+  tt = opt_malloc(sizeof (struct dhcp_netid));
-+  tt->net = opt_string_alloc(net);
-+  tt->next = next;
-+  return tt;
-+}
-+
-+static void dhcp_netid_free(struct dhcp_netid *nid)
-+{
-+  while (nid)
-+    {
-+      struct dhcp_netid *tmp = nid;
-+      nid = nid->next;
-+      free(tmp->net);
-+      free(tmp);
-+    }
-+}
-+
-+/* Parse one or more tag:s before parameters.
-+ * Moves arg to the end of tags. */
-+static struct dhcp_netid * dhcp_tags(char **arg)
-+{
-+  struct dhcp_netid *id = NULL;
-+
-+  while (is_tag_prefix(*arg))
-+    {
-+      char *comma = split(*arg);
-+      id = dhcp_netid_create((*arg)+4, id);
-+      *arg = comma;
-+    };
-+  if (!*arg)
-+    {
-+      dhcp_netid_free(id);
-+      id = NULL;
-+    }
-+  return id;
-+}
-+
-+static void dhcp_netid_list_free(struct dhcp_netid_list *netid)
-+{
-+  while (netid)
-+    {
-+      struct dhcp_netid_list *tmplist = netid;
-+      netid = netid->next;
-+      dhcp_netid_free(tmplist->list);
-+      free(tmplist);
-+    }
-+}
-+
-+static void dhcp_config_free(struct dhcp_config *config)
-+{
-+  if (config)
-+    {
-+      struct hwaddr_config *hwaddr = config->hwaddr;
-+      while (hwaddr)
-+        {
-+        struct hwaddr_config *tmp = hwaddr;
-+          hwaddr = hwaddr->next;
-+        free(tmp);
-+        }
-+      dhcp_netid_list_free(config->netid);
-+      if (config->flags & CONFIG_CLID)
-+        free(config->clid);
-+      free(config);
-+    }
-+}
-+
-+static void dhcp_context_free(struct dhcp_context *ctx)
-+{
-+  if (ctx)
-+    {
-+      dhcp_netid_free(ctx->filter);
-+      free(ctx->netid.net);
-+      free(ctx->template_interface);
-+      free(ctx);
-+    }
-+}
-+
-+static void dhcp_opt_free(struct dhcp_opt *opt)
-+{
-+  if (opt->flags & DHOPT_VENDOR)
-+    free(opt->u.vendor_class);
-+  dhcp_netid_free(opt->netid);
-+  free(opt->val);
-+  free(opt);
-+}
-+
-+
- /* This is too insanely large to keep in-line in the switch */
- static int parse_dhcp_opt(char *errstr, char *arg, int flags)
- {
-@@ -965,7 +1061,6 @@ static int parse_dhcp_opt(char *errstr,
-   char lenchar = 0, *cp;
-   int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
-   char *comma = NULL;
--  struct dhcp_netid *np = NULL;
-   u16 opt_len = 0;
-   int is6 = 0;
-   int option_ok = 0;
-@@ -1052,14 +1147,9 @@ static int parse_dhcp_opt(char *errstr,
-       }
-       else
-       {
--        new->netid = opt_malloc(sizeof (struct dhcp_netid));
-         /* allow optional "net:" or "tag:" for consistency */
--        if (is_tag_prefix(arg))
--          new->netid->net = opt_string_alloc(arg+4);
--        else
--          new->netid->net = opt_string_alloc(set_prefix(arg));
--        new->netid->next = np;
--        np = new->netid;
-+        const char *name = (is_tag_prefix(arg)) ? arg+4 : set_prefix(arg);
-+        new->netid = dhcp_netid_create(name, new->netid);
-       }
-       
-       arg = comma; 
-@@ -1069,7 +1159,7 @@ static int parse_dhcp_opt(char *errstr,
-   if (is6)
-     {
-       if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
--      ret_err(_("unsupported encapsulation for IPv6 option"));
-+      goto_err(_("unsupported encapsulation for IPv6 option"));
-       
-       if (opt_len == 0 &&
-         !(new->flags & DHOPT_RFC3925))
-@@ -1083,7 +1173,7 @@ static int parse_dhcp_opt(char *errstr,
-   
-   /* option may be missing with rfc3925 match */
-   if (!option_ok)
--    ret_err(_("bad dhcp-option"));
-+    goto_err(_("bad dhcp-option"));
-   
-   if (comma)
-     {
-@@ -1151,10 +1241,10 @@ static int parse_dhcp_opt(char *errstr,
-         is_string = is_dec = is_hex = 0;
-         
-         if (!is6 && (!is_addr || dots == 0))
--          ret_err(_("bad IP address"));
-+          goto_err(_("bad IP address"));
-          if (is6 && !is_addr6)
--           ret_err(_("bad IPv6 address"));
-+           goto_err(_("bad IPv6 address"));
-       }
-       /* or names */
-       else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
-@@ -1247,7 +1337,7 @@ static int parse_dhcp_opt(char *errstr,
-             comma = split(cp);
-             slash = split_chr(cp, '/');
-             if (!inet_pton(AF_INET, cp, &in))
--              ret_err(_("bad IPv4 address"));
-+              goto_err(_("bad IPv4 address"));
-             if (!slash)
-               {
-                 memcpy(op, &in, INADDRSZ);
-@@ -1292,8 +1382,8 @@ static int parse_dhcp_opt(char *errstr,
-                 op += IN6ADDRSZ;
-                 continue;
-               }
--        
--            ret_err(_("bad IPv6 address"));
-+
-+            goto_err(_("bad IPv6 address"));
-           } 
-         new->len = op - new->val;
-       }
-@@ -1320,7 +1410,7 @@ static int parse_dhcp_opt(char *errstr,
-                 if (strcmp (arg, ".") != 0)
-                   {
-                     if (!(dom = canonicalise_opt(arg)))
--                      ret_err(_("bad domain in dhcp-option"));
-+                      goto_err(_("bad domain in dhcp-option"));
-                       
-                     domlen = strlen(dom) + 2;
-                   }
-@@ -1414,7 +1504,7 @@ static int parse_dhcp_opt(char *errstr,
-               {
-                 char *dom = canonicalise_opt(arg);
-                 if (!dom)
--                  ret_err(_("bad domain in dhcp-option"));
-+                  goto_err(_("bad domain in dhcp-option"));
-                                 
-                 newp = opt_malloc(len + strlen(dom) + 2);
-                 
-@@ -1452,14 +1542,14 @@ static int parse_dhcp_opt(char *errstr,
-       ((new->len > 255) || 
-       (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
-        (new->len > 250 && (new->flags & DHOPT_RFC3925))))
--    ret_err(_("dhcp-option too long"));
-+    goto_err(_("dhcp-option too long"));
-   
-   if (flags == DHOPT_MATCH)
-     {
-       if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
-         !new->netid ||
-         new->netid->next)
--      ret_err(_("illegal dhcp-match"));
-+      goto_err(_("illegal dhcp-match"));
-        
-       if (is6)
-       {
-@@ -1484,6 +1574,9 @@ static int parse_dhcp_opt(char *errstr,
-     }
-     
-   return 1;
-+on_error:
-+  dhcp_opt_free(new);
-+  return 0;
- }
- #endif
-@@ -1498,6 +1591,16 @@ void reset_option_bool(unsigned int opt)
-   option_var(opt) &= ~(option_val(opt));
- }
-+static void server_list_free(struct server *list)
-+{
-+  while (list)
-+    {
-+      struct server *tmp = list;
-+      list = list->next;
-+      free(tmp);
-+    }
-+}
-+
- static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only)
- {      
-   int i;
-@@ -1679,13 +1782,13 @@ static int one_opt(int option, char *arg
-             /* has subnet+len */
-             err = parse_mysockaddr(arg, &new->addr);
-             if (err)
--              ret_err(err);
-+              ret_err_free(err, new);
-             if (!atoi_check(end, &new->mask))
--              ret_err(gen_err);
-+              ret_err_free(gen_err, new);
-             new->addr_used = 1;
-           } 
-         else if (!atoi_check(arg, &new->mask))
--          ret_err(gen_err);
-+          ret_err_free(gen_err, new);
-           
-           daemon->add_subnet4 = new;
-@@ -1697,15 +1800,15 @@ static int one_opt(int option, char *arg
-                 /* has subnet+len */
-                   err = parse_mysockaddr(comma, &new->addr);
-                   if (err)
--                    ret_err(err);
-+                    ret_err_free(err, new);
-                   if (!atoi_check(end, &new->mask))
--                    ret_err(gen_err);
-+                    ret_err_free(gen_err, new);
-                   new->addr_used = 1;
-                 }
-               else
-                 {
-                   if (!atoi_check(comma, &new->mask))
--                    ret_err(gen_err);
-+                    ret_err_free(gen_err, new);
-                 }
-           
-             daemon->add_subnet6 = new;
-@@ -1912,7 +2015,10 @@ static int one_opt(int option, char *arg
-                 else if (strcmp(fam, "6") == 0)
-                   new->addr.sa.sa_family = AF_INET6;
-                 else
--                  ret_err(gen_err);
-+                {
-+                  free(new->name);
-+                  ret_err_free(gen_err, new);
-+                }
-               } 
-           }
-         new->next = daemon->authinterface;
-@@ -2077,7 +2183,7 @@ static int one_opt(int option, char *arg
-                     arg = split(netpart);
-                     if (!atoi_check(netpart, &msize))
--                      ret_err(gen_err);
-+                      ret_err_free(gen_err, new);
-                     else if (inet_pton(AF_INET, comma, &new->start))
-                       {
-                         int mask = (1 << (32 - msize)) - 1;
-@@ -2090,18 +2196,18 @@ static int one_opt(int option, char *arg
-                               {
-                                 if (!(new->prefix = canonicalise_opt(arg)) ||
-                                     strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
--                                  ret_err(_("bad prefix"));
-+                                  ret_err_free(_("bad prefix"), new);
-                               }
-                             else if (strcmp(arg, "local") != 0 ||
-                                      (msize != 8 && msize != 16 && msize != 24))
--                              ret_err(gen_err);
-+                              ret_err_free(gen_err, new);
-                             else
-                               {
-                                  /* generate the equivalent of
-                                     local=/xxx.yyy.zzz.in-addr.arpa/ */
-                                 struct server *serv = add_rev4(new->start, msize);
-                                 if (!serv)
--                                  ret_err(_("bad prefix"));
-+                                  ret_err_free(_("bad prefix"), new);
-                                 serv->flags |= SERV_NO_ADDR;
-@@ -2130,17 +2236,17 @@ static int one_opt(int option, char *arg
-                         setaddr6part(&new->end6, addrpart | mask);
-                         
-                         if (msize < 64)
--                          ret_err(gen_err);
-+                          ret_err_free(gen_err, new);
-                         else if (arg)
-                           {
-                             if (option != 's')
-                               {
-                                 if (!(new->prefix = canonicalise_opt(arg)) ||
-                                     strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
--                                  ret_err(_("bad prefix"));
-+                                  ret_err_free(_("bad prefix"), new);
-                               }       
-                             else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
--                              ret_err(gen_err);
-+                              ret_err_free(gen_err, new);
-                             else 
-                               {
-                                 /* generate the equivalent of
-@@ -2159,7 +2265,7 @@ static int one_opt(int option, char *arg
-                           }
-                       }
-                     else
--                      ret_err(gen_err);
-+                      ret_err_free(gen_err, new);
-                   }
-                 else
-                   {
-@@ -2173,7 +2279,7 @@ static int one_opt(int option, char *arg
-                         if (!arg)
-                           new->end.s_addr = new->start.s_addr;
-                         else if (!inet_pton(AF_INET, arg, &new->end))
--                          ret_err(gen_err);
-+                          ret_err_free(gen_err, new);
-                       }
-                     else if (inet_pton(AF_INET6, comma, &new->start6))
-                       {
-@@ -2181,16 +2287,16 @@ static int one_opt(int option, char *arg
-                         if (!arg)
-                           memcpy(&new->end6, &new->start6, IN6ADDRSZ);
-                         else if (!inet_pton(AF_INET6, arg, &new->end6))
--                          ret_err(gen_err);
-+                          ret_err_free(gen_err, new);
-                       }
-                     else 
--                      ret_err(gen_err);
-+                      ret_err_free(gen_err, new);
-                     if (option != 's' && prefstr)
-                       {
-                         if (!(new->prefix = canonicalise_opt(prefstr)) ||
-                             strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
--                          ret_err(_("bad prefix"));
-+                          ret_err_free(_("bad prefix"), new);
-                       }
-                   }
-@@ -2352,7 +2458,7 @@ static int one_opt(int option, char *arg
- #endif
-         }
-       else
--        ret_err(gen_err);
-+        ret_err_free(gen_err, new);
-       new->used = 0;
-       if (option == 'a')
-@@ -2423,7 +2529,10 @@ static int one_opt(int option, char *arg
-         {
-           newlist->flags |= SERV_LITERAL_ADDRESS;
-           if (!(newlist->flags & SERV_TYPE))
--            ret_err(gen_err);
-+            {
-+              server_list_free(newlist);
-+              ret_err(gen_err);
-+            }
-         }
-       else if (option == LOPT_NO_REBIND)
-         newlist->flags |= SERV_NO_REBIND;
-@@ -2440,7 +2549,10 @@ static int one_opt(int option, char *arg
-         {
-           char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
-           if (err)
--            ret_err(err);
-+            {
-+              server_list_free(newlist);
-+              ret_err(err);
-+            }
-         }
-       
-       serv = newlist;
-@@ -2776,21 +2888,19 @@ static int one_opt(int option, char *arg
-             {
-               if (is_tag_prefix(arg))
-                 {
--                  struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid));
--                  tt->net = opt_string_alloc(arg+4);
--                  tt->next = new->filter;
-                   /* ignore empty tag */
--                  if (tt->net)
--                    new->filter = tt;
-+                  if (arg[4])
-+                    new->filter = dhcp_netid_create(arg+4, new->filter);
-                 }
-               else
-                 {
-                   if (new->netid.net)
--                    ret_err(_("only one tag allowed"));
--                  else if (strstr(arg, "set:") == arg)
--                    new->netid.net = opt_string_alloc(arg+4);
-+                    {
-+                      dhcp_context_free(new);
-+                      ret_err(_("only one tag allowed"));
-+                    }
-                   else
--                    new->netid.net = opt_string_alloc(arg);
-+                    new->netid.net = opt_string_alloc(set_prefix(arg));
-                 }
-               arg = comma;
-             }
-@@ -2806,7 +2916,10 @@ static int one_opt(int option, char *arg
-           break;
-       
-       if (k < 2)
--        ret_err(_("bad dhcp-range"));
-+        {
-+          dhcp_context_free(new);
-+          ret_err(_("bad dhcp-range"));
-+        }
-       
-       if (inet_pton(AF_INET, a[0], &new->start))
-         {
-@@ -2818,7 +2931,10 @@ static int one_opt(int option, char *arg
-           else if (strcmp(a[1], "proxy") == 0)
-             new->flags |= CONTEXT_PROXY;
-           else if (!inet_pton(AF_INET, a[1], &new->end))
--            ret_err(_("bad dhcp-range"));
-+            {
-+              dhcp_context_free(new);
-+              ret_err(_("bad dhcp-range"));
-+            }
-           
-           if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
-             {
-@@ -2833,7 +2949,10 @@ static int one_opt(int option, char *arg
-               new->flags |= CONTEXT_NETMASK;
-               leasepos = 3;
-               if (!is_same_net(new->start, new->end, new->netmask))
--                ret_err(_("inconsistent DHCP range"));
-+                {
-+                  dhcp_context_free(new);
-+                  ret_err(_("inconsistent DHCP range"));
-+                }
-               
-           
-               if (k >= 4 && strchr(a[3], '.') &&  
-@@ -2847,6 +2966,8 @@ static int one_opt(int option, char *arg
- #ifdef HAVE_DHCP6
-       else if (inet_pton(AF_INET6, a[0], &new->start6))
-         {
-+          const char *err = NULL;
-+
-           new->flags |= CONTEXT_V6; 
-           new->prefix = 64; /* default */
-           new->end6 = new->start6;
-@@ -2892,19 +3013,24 @@ static int one_opt(int option, char *arg
-                 }
-             }
-           
--          if (new->prefix != 64)
-+          if (new->prefix > 64)
-             {
-               if (new->flags & CONTEXT_RA)
--                ret_err(_("prefix length must be exactly 64 for RA subnets"));
-+                err=(_("prefix length must be exactly 64 for RA subnets"));
-               else if (new->flags & CONTEXT_TEMPLATE)
--                ret_err(_("prefix length must be exactly 64 for subnet constructors"));
-+                err=(_("prefix length must be exactly 64 for subnet constructors"));
-             }
--
--          if (new->prefix < 64)
--            ret_err(_("prefix length must be at least 64"));
-+          else if (new->prefix < 64)
-+            err=(_("prefix length must be at least 64"));
-           
--          if (!is_same_net6(&new->start6, &new->end6, new->prefix))
--            ret_err(_("inconsistent DHCPv6 range"));
-+          if (!err && !is_same_net6(&new->start6, &new->end6, new->prefix))
-+            err=(_("inconsistent DHCPv6 range"));
-+
-+          if (err)
-+            {
-+              dhcp_context_free(new);
-+              ret_err(err);
-+            }
-           /* dhcp-range=:: enables DHCP stateless on any interface */
-           if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
-@@ -2915,7 +3041,10 @@ static int one_opt(int option, char *arg
-               struct in6_addr zero;
-               memset(&zero, 0, sizeof(zero));
-               if (!is_same_net6(&zero, &new->start6, new->prefix))
--                ret_err(_("prefix must be zero with \"constructor:\" argument"));
-+                {
-+                  dhcp_context_free(new);
-+                  ret_err(_("prefix must be zero with \"constructor:\" argument"));
-+                }
-             }
-           
-           if (addr6part(&new->start6) > addr6part(&new->end6))
-@@ -2927,12 +3056,18 @@ static int one_opt(int option, char *arg
-         }
- #endif
-       else
--        ret_err(_("bad dhcp-range"));
-+        {
-+          dhcp_context_free(new);
-+          ret_err(_("bad dhcp-range"));
-+        }
-       
-       if (leasepos < k)
-         {
-           if (leasepos != k-1)
--            ret_err(_("bad dhcp-range"));
-+            {
-+              dhcp_context_free(new);
-+              ret_err(_("bad dhcp-range"));
-+            }
-           
-           if (strcmp(a[leasepos], "infinite") == 0)
-             new->lease_time = 0xffffffff;
-@@ -2971,7 +3106,7 @@ static int one_opt(int option, char *arg
-                       break;
-                   if (*cp || (leasepos+1 < k))
--                    ret_err(_("bad dhcp-range"));
-+                    ret_err_free(_("bad dhcp-range"), new);
-                   
-                   new->lease_time = atoi(a[leasepos]) * fac;
-                   /* Leases of a minute or less confuse
-@@ -2998,6 +3133,7 @@ static int one_opt(int option, char *arg
-       new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
-       new->hwaddr = NULL;
-       new->netid = NULL;
-+      new->clid = NULL;
-       if ((a[0] = arg))
-         for (k = 1; k < 7; k++)
-@@ -3028,7 +3164,10 @@ static int one_opt(int option, char *arg
-                       }
-                     if (len == -1)
--                      ret_err(_("bad hex constant"));
-+                      {
-+                        dhcp_config_free(new);
-+                        ret_err(_("bad hex constant"));
-+                      }
-                     else if ((new->clid = opt_malloc(len)))
-                       {
-                         new->flags |= CONFIG_CLID;
-@@ -3040,17 +3179,17 @@ static int one_opt(int option, char *arg
-             /* dhcp-host has strange backwards-compat needs. */
-             else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
-               {
--                struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
-                 struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
--                newtag->net = opt_malloc(strlen(arg + 4) + 1);
-                 newlist->next = new->netid;
-                 new->netid = newlist;
--                newlist->list = newtag;
--                strcpy(newtag->net, arg+4);
--                unhide_metas(newtag->net);
-+                newlist->list = dhcp_netid_create(arg+4, NULL);
-               }
-             else if (strstr(arg, "tag:") == arg)
--              ret_err(_("cannot match tags in --dhcp-host"));
-+              {
-+                
-+                dhcp_config_free(new);
-+                ret_err(_("cannot match tags in --dhcp-host"));
-+              }
- #ifdef HAVE_DHCP6
-             else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
-               {
-@@ -3058,7 +3197,10 @@ static int one_opt(int option, char *arg
-                 arg++;
-                 
-                 if (!inet_pton(AF_INET6, arg, &new->addr6))
--                  ret_err(_("bad IPv6 address"));
-+                  {
-+                    dhcp_config_free(new);
-+                    ret_err(_("bad IPv6 address"));
-+                  }
-                 for (i= 0; i < 8; i++)
-                   if (new->addr6.s6_addr[i] != 0)
-@@ -3076,10 +3218,13 @@ static int one_opt(int option, char *arg
-                 struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
-                 if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX, 
-                                                    &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
--                  ret_err(_("bad hex constant"));
-+                  {
-+                    free(newhw);
-+                    dhcp_config_free(new);
-+                    ret_err(_("bad hex constant"));
-+                  }
-                 else
-                   {
--                    
-                     newhw->next = new->hwaddr;
-                     new->hwaddr = newhw;
-                   }               
-@@ -3156,7 +3301,10 @@ static int one_opt(int option, char *arg
-                   {
-                     if (!(new->hostname = canonicalise_opt(a[j])) ||
-                         !legal_hostname(new->hostname))
--                      ret_err(_("bad DHCP host name"));
-+                      {
-+                        dhcp_config_free(new);
-+                        ret_err(_("bad DHCP host name"));
-+                      }
-                    
-                     new->flags |= CONFIG_NAME;
-                     new->domain = strip_hostname(new->hostname);                      
-@@ -3209,10 +3357,7 @@ static int one_opt(int option, char *arg
-             }
-           else
-             {
--              struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
--              newtag->net = opt_malloc(len - 3);
--              strcpy(newtag->net, arg+4);
--              unhide_metas(newtag->net);
-+              struct dhcp_netid *newtag = dhcp_netid_create(arg+4, NULL);
-               if (strstr(arg, "set:") == arg)
-                 {
-@@ -3229,7 +3374,7 @@ static int one_opt(int option, char *arg
-               else 
-                 {
-                   new->set = NULL;
--                  free(newtag);
-+                  dhcp_netid_free(newtag);
-                   break;
-                 }
-             }
-@@ -3238,7 +3383,11 @@ static int one_opt(int option, char *arg
-         }
-       if (!new->set)
--        ret_err(_("bad tag-if"));
-+        {
-+          dhcp_netid_free(new->tag);
-+          dhcp_netid_list_free(new->set);
-+          ret_err_free(_("bad tag-if"), new);
-+        }
-         
-       break;
-       }
-@@ -3281,19 +3430,12 @@ static int one_opt(int option, char *arg
-       
-     case 'M': /* --dhcp-boot */
-       {
--      struct dhcp_netid *id = NULL;
--      while (is_tag_prefix(arg))
--        {
--          struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
--          newid->next = id;
--          id = newid;
--          comma = split(arg);
--          newid->net = opt_string_alloc(arg+4);
--          arg = comma;
--        };
-+      struct dhcp_netid *id = dhcp_tags(&arg);
-       
--      if (!arg)
--        ret_err(gen_err);
-+      if (!id)
-+        {
-+          ret_err(gen_err);
-+        }
-       else 
-         {
-           char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
-@@ -3339,19 +3481,12 @@ static int one_opt(int option, char *arg
-     case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
-       {
--      struct dhcp_netid *id = NULL;
--      while (is_tag_prefix(arg))
--        {
--          struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
--          newid->next = id;
--          id = newid;
--          comma = split(arg);
--          newid->net = opt_string_alloc(arg+4);
--          arg = comma;
--        };
-+      struct dhcp_netid *id = dhcp_tags(&arg);
-       
--      if (!arg)
--        ret_err(gen_err);
-+      if (!id)
-+        {
-+          ret_err(gen_err);
-+        }
-       else
-         {
-           struct delay_config *new;
-@@ -3376,19 +3511,13 @@ static int one_opt(int option, char *arg
-        
-        new->netid = NULL;
-        new->opt = 10; /* PXE_MENU_PROMPT */
--
--       while (is_tag_prefix(arg))
--        {
--           struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
--           comma = split(arg);
--           nn->next = new->netid;
--           new->netid = nn;
--           nn->net = opt_string_alloc(arg+4);
--           arg = comma;
--         }
-+       new->netid = dhcp_tags(&arg);
-        
--       if (!arg)
--         ret_err(gen_err);
-+       if (!new->netid)
-+         {
-+           dhcp_opt_free(new);
-+           ret_err(gen_err);
-+         }
-        else
-          {
-            comma = split(arg);
-@@ -3424,17 +3553,8 @@ static int one_opt(int option, char *arg
-        new->netid = NULL;
-        new->sname = NULL;
-        new->server.s_addr = 0;
-+       new->netid = dhcp_tags(&arg);
--       while (is_tag_prefix(arg))
--         {
--           struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
--           comma = split(arg);
--           nn->next = new->netid;
--           new->netid = nn;
--           nn->net = opt_string_alloc(arg+4);
--           arg = comma;
--         }
--       
-        if (arg && (comma = split(arg)))
-          {
-            for (i = 0; CSA[i]; i++)
-@@ -3511,7 +3631,10 @@ static int one_opt(int option, char *arg
-           unhide_metas(comma);
-           new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
-           if (new->hwaddr_len == -1)
--            ret_err(gen_err);
-+            {
-+              free(new->netid.net);
-+              ret_err_free(gen_err, new);
-+            }
-           else
-             {
-               new->next = daemon->dhcp_macs;
-@@ -3528,7 +3651,7 @@ static int one_opt(int option, char *arg
-       
-       if (!(comma = split(arg)) ||
-           !atoi_check16(comma, &new->class))
--        ret_err(gen_err);
-+        ret_err_free(gen_err, new);
-       
-       new->tag.net = opt_string_alloc(set_prefix(arg));
-       new->next = daemon->prefix_classes;
-@@ -3550,7 +3673,7 @@ static int one_opt(int option, char *arg
-        struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
-        
-        if (!(comma = split(arg)))
--         ret_err(gen_err);
-+         ret_err_free(gen_err, new);
-       
-        new->netid.net = opt_string_alloc(set_prefix(arg));
-        /* check for hex string - must digits may include : must not have nothing else, 
-@@ -3560,7 +3683,10 @@ static int one_opt(int option, char *arg
-        if ((comma = split(arg)))
-          {
-            if (option  != 'U' || strstr(arg, "enterprise:") != arg)
--             ret_err(gen_err);
-+             {
-+               free(new->netid.net);
-+               ret_err_free(gen_err, new);
-+             }
-            else
-              new->enterprise = atoi(arg+11);
-          }
-@@ -3662,14 +3788,8 @@ static int one_opt(int option, char *arg
-         }
-       
-       while (arg) {
--        struct dhcp_netid *member = opt_malloc(sizeof(struct dhcp_netid));
-         comma = split(arg);
--        member->next = list;
--        list = member;
--        if (is_tag_prefix(arg))
--          member->net = opt_string_alloc(arg+4);
--        else
--          member->net = opt_string_alloc(arg);
-+        list = dhcp_netid_create(is_tag_prefix(arg) ? arg+4 :arg, list);
-         arg = comma;
-       }
-       
-@@ -3683,7 +3803,7 @@ static int one_opt(int option, char *arg
-       struct addr_list *new = opt_malloc(sizeof(struct addr_list));
-       comma = split(arg);
-       if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
--        ret_err(_("bad dhcp-proxy address"));
-+        ret_err_free(_("bad dhcp-proxy address"), new);
-       new->next = daemon->override_relays;
-       daemon->override_relays = new;
-       arg = comma;
-@@ -3709,7 +3829,10 @@ static int one_opt(int option, char *arg
-         }
- #endif
-       else
--        ret_err(_("Bad dhcp-relay"));
-+        {
-+          free(new->interface);
-+          ret_err_free(_("Bad dhcp-relay"), new);
-+        }
-       
-       break;
-       }
-@@ -3749,8 +3872,11 @@ static int one_opt(int option, char *arg
-          arg = split(comma);
-          if (!atoi_check(comma, &new->interval) || 
-             (arg && !atoi_check(arg, &new->lifetime)))
-+             {
- err:
--          ret_err(_("bad RA-params"));
-+             free(new->name);
-+             ret_err_free(_("bad RA-params"), new);
-+             }
-         
-         new->next = daemon->ra_interfaces;
-         daemon->ra_interfaces = new;
-@@ -3799,7 +3925,7 @@ err:
-           (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
-            !is_same_net(new->in, new->end, new->mask) ||
-            ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
--        ret_err(_("invalid alias range"));
-+        ret_err_free(_("invalid alias range"), new);
-       
-       break;
-       }
-@@ -3832,7 +3958,7 @@ err:
-           else if (strcmp(arg, "6") == 0)
-             new->family = AF_INET6;
-           else
--            ret_err(gen_err);
-+            ret_err_free(gen_err, new);
-         } 
-       new->intr = opt_string_alloc(comma);
-       break;
-@@ -3864,11 +3990,19 @@ err:
-           alias = canonicalise_opt(arg);
-           if (!alias || !target)
--            ret_err(_("bad CNAME"));
-+            {
-+              free(target);
-+              free(alias);
-+              ret_err(_("bad CNAME"));
-+            }
-           
-           for (new = daemon->cnames; new; new = new->next)
-             if (hostname_isequal(new->alias, alias))
--              ret_err(_("duplicate CNAME"));
-+              {
-+                free(target);
-+                free(alias);
-+                ret_err(_("duplicate CNAME"));
-+              }
-           new = opt_malloc(sizeof(struct cname));
-           new->next = daemon->cnames;
-           daemon->cnames = new;
-@@ -3891,7 +4025,11 @@ err:
-       
-       if (!(dom = canonicalise_opt(arg)) ||
-           (comma && !(target = canonicalise_opt(comma))))
--        ret_err(_("bad PTR record"));
-+        {
-+          free(dom);
-+          free(target);
-+          ret_err(_("bad PTR record"));
-+        }
-       else
-         {
-           new = opt_malloc(sizeof(struct ptr_record));
-@@ -3909,7 +4047,7 @@ err:
-       int k = 0;
-       struct naptr *new;
-       int order, pref;
--      char *name, *replace = NULL;
-+      char *name=NULL, *replace = NULL;
-       if ((a[0] = arg))
-         for (k = 1; k < 7; k++)
-@@ -3922,7 +4060,11 @@ err:
-           !atoi_check16(a[1], &order) || 
-           !atoi_check16(a[2], &pref) ||
-           (k == 7 && !(replace = canonicalise_opt(a[6]))))
--        ret_err(_("bad NAPTR record"));
-+          {
-+          free(name);
-+          free(replace);
-+          ret_err(_("bad NAPTR record"));
-+          }
-       else
-         {
-           new = opt_malloc(sizeof(struct naptr));
-@@ -3944,22 +4086,26 @@ err:
-               struct txt_record *new;
-       size_t len = 0;
-       char *data;
--      int val;
-+      int class;
-       comma = split(arg);
-       data = split(comma);
-               
-       new = opt_malloc(sizeof(struct txt_record));
--      new->next = daemon->rr;
--      daemon->rr = new;
-+      new->name = NULL;
-       
--      if (!atoi_check(comma, &val) || 
-+      if (!atoi_check(comma, &class) || 
-           !(new->name = canonicalise_opt(arg)) ||
-           (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
--        ret_err(_("bad RR record"));
--              
--      new->class = val;
-+          {
-+            free(new->name);
-+          ret_err_free(_("bad RR record"), new);
-+          }
-+
-       new->len = 0;
-+      new->class = class;
-+      new->next = daemon->rr;
-+      daemon->rr = new;
-       
-       if (data)
-         {
-@@ -4011,14 +4157,14 @@ err:
-       comma = split(arg);
-               
-       new = opt_malloc(sizeof(struct txt_record));
--      new->next = daemon->txt;
--      daemon->txt = new;
-       new->class = C_IN;
-       new->stat = 0;
-       if (!(new->name = canonicalise_opt(arg)))
--        ret_err(_("bad TXT record"));
-+        ret_err_free(_("bad TXT record"), new);
-       
-+      new->next = daemon->txt;
-+      daemon->txt = new;
-       len = comma ? strlen(comma) : 0;
-       len += (len/255) + 1; /* room for extra counts */
-       new->txt = p = opt_malloc(len);
-@@ -4065,24 +4211,32 @@ err:
-           arg = comma;
-           comma = split(arg);
-           if (!(target = canonicalise_opt(arg)))
--            ret_err(_("bad SRV target"));
-+            ret_err_free(_("bad SRV target"), name);
-               
-           if (comma)
-             {
-               arg = comma;
-               comma = split(arg);
-               if (!atoi_check16(arg, &port))
--                ret_err(_("invalid port number"));
-+                  {
-+                    free(name);
-+                  ret_err_free(_("invalid port number"), target);
-+                  }
-               
-               if (comma)
-                 {
-                   arg = comma;
-                   comma = split(arg);
-                   if (!atoi_check16(arg, &priority))
--                    ret_err(_("invalid priority"));
--                      
-+                      {
-+                        free(name);
-+                      ret_err_free(_("invalid priority"), target);
-+                    }
-                   if (comma && !atoi_check16(comma, &weight))
--                    ret_err(_("invalid weight"));
-+                      {
-+                        free(name);
-+                      ret_err_free(_("invalid weight"), target);
-+                      }
-                 }
-             }
-         }
-@@ -4101,13 +4255,15 @@ err:
-       
-     case LOPT_HOST_REC: /* --host-record */
-       {
--      struct host_record *new = opt_malloc(sizeof(struct host_record));
--      memset(new, 0, sizeof(struct host_record));
--      new->ttl = -1;
-+      struct host_record *new;
-       if (!arg || !(comma = split(arg)))
-         ret_err(_("Bad host-record"));
-       
-+      new = opt_malloc(sizeof(struct host_record));
-+      memset(new, 0, sizeof(struct host_record));
-+      new->ttl = -1;
-+
-       while (arg)
-         {
-           struct all_addr addr;
-@@ -4126,10 +4282,19 @@ err:
-             {
-               int nomem;
-               char *canon = canonicalise(arg, &nomem);
--              struct name_list *nl = opt_malloc(sizeof(struct name_list));
-+              struct name_list *nl;
-               if (!canon)
--                ret_err(_("Bad name in host-record"));
-+                  {
-+                  struct name_list *tmp = new->names, *next;
-+                  for (tmp = new->names; tmp; tmp = next)
-+                    {
-+                      next = tmp->next;
-+                      free(tmp);
-+                    }
-+                  ret_err_free(_("Bad name in host-record"), new);
-+                  }
-+              nl = opt_malloc(sizeof(struct name_list));
-               nl->name = canon;
-               /* keep order, so that PTR record goes to first name */
-               nl->next = NULL;
-@@ -4179,6 +4344,7 @@ err:
-       int len;
-       
-       new->class = C_IN;
-+      new->name = NULL;
-       if ((comma = split(arg)) && (algo = split(comma)))
-         {
-@@ -4203,7 +4369,7 @@ err:
-           !atoi_check8(algo, &new->algo) ||
-           !atoi_check8(digest, &new->digest_type) ||
-           !(new->name = canonicalise_opt(arg)))
--        ret_err(_("bad trust anchor"));
-+        ret_err_free(_("bad trust anchor"), new);
-           
-       /* Upper bound on length */
-       len = (2*strlen(keyhex))+1;
-@@ -4217,7 +4383,10 @@ err:
-         else
-           cp++;
-       if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
--        ret_err(_("bad HEX in trust anchor"));
-+        {
-+          free(new->name);
-+          ret_err_free(_("bad HEX in trust anchor"), new);
-+        }
-       
-       new->next = daemon->ds;
-       daemon->ds = new;
-@@ -4686,8 +4855,8 @@ void read_opts(int argc, char **argv, ch
-   size_t argbuf_size = MAXDNAME;
-   char *argbuf = opt_malloc(argbuf_size);
-   char *buff = opt_malloc(MAXDNAME);
--  int option, conffile_opt = '7', testmode = 0;
--  char *arg, *conffile = CONFFILE;
-+  int option, testmode = 0;
-+  char *arg, *conffile = NULL;
-       
-   opterr = 0;
-@@ -4796,7 +4965,8 @@ void read_opts(int argc, char **argv, ch
-         }
-       else if (option == 'C')
-       {
--        conffile_opt = 0; /* file must exist */
-+          if (conffile)
-+            free(conffile);
-         conffile = opt_string_alloc(arg);
-       }
-       else
-@@ -4814,10 +4984,11 @@ void read_opts(int argc, char **argv, ch
-   if (conffile)
-     {
--      one_file(conffile, conffile_opt);
--      if (conffile_opt == 0)
--      free(conffile);
-+      one_file(conffile, 0);
-+      free(conffile);
-     }
-+  else
-+    one_file(CONFFILE, '7');
-   /* port might not be known when the address is parsed - fill in here */
-   if (daemon->servers)