aboutsummaryrefslogtreecommitdiffstats
path: root/package/network/services/dnsmasq/patches/0011-Free-config-file-values-on-parsing-errors.patch
diff options
context:
space:
mode:
authorKevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>2018-10-25 10:20:25 +0100
committerKevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>2018-12-10 10:38:49 +0000
commit8c0f6a010a5b7011acbbf2ac5b345d9cf1c14b1f (patch)
treee2bf800c82360eb9f657a40b9db90cbb43918cd0 /package/network/services/dnsmasq/patches/0011-Free-config-file-values-on-parsing-errors.patch
parent18e02fa20c949e17ff77d821e464a44640ca4c52 (diff)
downloadupstream-8c0f6a010a5b7011acbbf2ac5b345d9cf1c14b1f.tar.gz
upstream-8c0f6a010a5b7011acbbf2ac5b345d9cf1c14b1f.tar.bz2
upstream-8c0f6a010a5b7011acbbf2ac5b345d9cf1c14b1f.zip
dnsmasq: follow upstream dnsmasq pre-v2.81 v2
Backport upstream commits. Most interesting 122392e which changes how SERVFAIL is handled especially in event of genuine server down/failure scenarios with multiple servers. a799ca0 also interesting in that answered received via TCP are now cached, DNSSEC typically using TCP meant until now answers weren't cached, hence reducing performance. 59e4703 Free config file values on parsing errors. 48d12f1 Remove the NO_FORK compile-time option, and support for uclinux. 122392e Revert 68f6312d4bae30b78daafcd6f51dc441b8685b1e 3a5a84c Fix Makefile lines generating UBUS linker config. 24b8760 Do not rely on dead code elimination, use array instead. Make options bits derived from size and count. Use size of option bits and last supported bit in computation. No new change would be required when new options are added. Just change OPT_LAST constant. 6f7812d Fix spurious AD flags in some DNS replies from local config. cbb5b17 Fix logging in cf5984367bc6a949e3803a576512c5a7bc48ebab cf59843 Don't forward *.bind/*.server queries upstream ee87504 Remove ability to compile without IPv6 support. a220545 Ensure that AD bit is reset on answers from --address=/<domain>/<address>. a799ca0 Impove cache behaviour for TCP connections. Along with an additional patch to fix compilation without DHCPv6, sent upstream. I've been running this for aaaages without obvious issue hence brave step of opening to wider openwrt community. Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
Diffstat (limited to 'package/network/services/dnsmasq/patches/0011-Free-config-file-values-on-parsing-errors.patch')
-rw-r--r--package/network/services/dnsmasq/patches/0011-Free-config-file-values-on-parsing-errors.patch1199
1 files changed, 1199 insertions, 0 deletions
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
new file mode 100644
index 0000000000..3ba971fe72
--- /dev/null
+++ b/package/network/services/dnsmasq/patches/0011-Free-config-file-values-on-parsing-errors.patch
@@ -0,0 +1,1199 @@
+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/11] 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)