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 @@ 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_addr addr, int msize) 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 *arg, int flags) 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, char *arg, int flags) } 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, char *arg, int flags) 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, char *arg, int flags) /* 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, char *arg, int flags) 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, char *arg, int flags) 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, char *arg, int flags) 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, char *arg, int flags) 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 *arg, int flags) { 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, char *arg, int flags) ((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, char *arg, int flags) } 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, char *errstr, char *gen_err, int comma /* 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, char *errstr, char *gen_err, int comma /* 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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma { 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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma } } else - ret_err(gen_err); + ret_err_free(gen_err, new); } else { @@ -2173,7 +2279,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma #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, char *errstr, char *gen_err, int comma { 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 *errstr, char *gen_err, int comma { 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, char *errstr, char *gen_err, int comma { 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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma #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, char *errstr, char *gen_err, int comma } } - 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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma } #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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma } 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, char *errstr, char *gen_err, int comma /* 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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma { 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, char *errstr, char *gen_err, int comma } 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, char *errstr, char *gen_err, int comma else { new->set = NULL; - free(newtag); + dhcp_netid_free(newtag); break; } } @@ -3238,7 +3383,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } 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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma } 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, char *errstr, char *gen_err, int comma 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, char *errstr, char *gen_err, int comma } #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, char *errstr, char *gen_err, int comma 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, char *compile_opts) 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, char *compile_opts) } 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, char *compile_opts) 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)