diff options
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.patch | 1199 |
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) |