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.
diff --git a/src/option.c b/src/option.c
index d89389a..4627969 100644
--- a/src/option.c
+++ b/src/option.c
@@ -577,14 +577,15 @@
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 @@
}
#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 @@
p += sprintf(p, "%d.", (a >> 24) & 0xff);
break;
default:
+ free(serv->domain);
+ free(serv);
return NULL;
}
@@ -958,6 +963,97 @@
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 @@
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 @@
}
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 @@
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 @@
/* 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 @@
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 @@
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 @@
op += IN6ADDRSZ;
continue;
}
-
- ret_err(_("bad IPv6 address"));
+
+ goto_err(_("bad IPv6 address"));
}
new->len = op - new->val;
}
@@ -1320,7 +1410,7 @@
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 @@
{
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 @@
((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 @@
}
return 1;
+on_error:
+ dhcp_opt_free(new);
+ return 0;
}
#endif
@@ -1498,6 +1591,16 @@
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 @@
/* 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 @@
/* 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 @@
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 @@
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 @@
{
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 @@
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 @@
}
}
else
- ret_err(gen_err);
+ ret_err_free(gen_err, new);
}
else
{
@@ -2173,7 +2279,7 @@
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 @@
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 @@
#endif
}
else
- ret_err(gen_err);
+ ret_err_free(gen_err, new);
new->used = 0;
if (option == 'a')
@@ -2423,7 +2529,10 @@
{
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 @@
{
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 @@
{
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 @@
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 @@
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 @@
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 @@
#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 @@
}
}
- 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 @@
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 @@
}
#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 @@
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 @@
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 @@
}
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 @@
/* 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 @@
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 @@
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 @@
{
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 @@
}
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 @@
else
{
new->set = NULL;
- free(newtag);
+ dhcp_netid_free(newtag);
break;
}
}
@@ -3238,7 +3383,11 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
}
#endif
else
- ret_err(_("Bad dhcp-relay"));
+ {
+ free(new->interface);
+ ret_err_free(_("Bad dhcp-relay"), new);
+ }
break;
}
@@ -3749,8 +3872,11 @@
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 @@
(!(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 @@
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 @@
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 @@
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 @@
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 @@
!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 @@
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 @@
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 @@
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 @@
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 @@
{
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 @@
int len;
new->class = C_IN;
+ new->name = NULL;
if ((comma = split(arg)) && (algo = split(comma)))
{
@@ -4203,7 +4369,7 @@
!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 @@
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 @@
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 @@
}
else if (option == 'C')
{
- conffile_opt = 0; /* file must exist */
+ if (conffile)
+ free(conffile);
conffile = opt_string_alloc(arg);
}
else
@@ -4814,10 +4984,11 @@
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)