Blob Blame History Raw
From 8e11d702921e51a5eb00b9ee12925cae69039c22 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
Date: Thu, 18 Jul 2019 18:49:30 +0200
Subject: [PATCH] Fix issues detected by Coverity
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Squashed commit of the following:

commit 6a5fa6c9207961a662f8debbe9172500c752e3ac
Author: Kevin Darbyshire-Bryant <kevin@darbyshire-bryant.me.uk>
Date:   Mon Dec 10 10:34:35 2018 +0000

    build failure on master with NO_DHCPv6 and fix....

    Hi Simon,

    master has a build error when building without HAVE_DHCPv6

    option.c: In function 'dhcp_context_free':
    option.c:1042:15: error: 'struct dhcp_context' has no member named 'template_interface'
           free(ctx->template_interface);

    Sadly, need to put in a little conditional compilation ifdef'erey

    Simplest patch in the world attached

    Cheers,

    Kevin D-B

    012C ACB2 28C6 C53E 9775  9123 B3A2 389B 9DE2 334A

    From 061eb8599636bb360e0b7fa5986935b86db39497 Mon Sep 17 00:00:00 2001
    From: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
    Date: Mon, 10 Dec 2018 10:07:33 +0000
    Subject: [PATCH] option: fix non DHCPv6 build error

    option.c: In function 'dhcp_context_free':
    option.c:1042:15: error: 'struct dhcp_context' has no member named 'template_interface'
           free(ctx->template_interface);
    		^~

    Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
    (cherry picked from commit b683cf37f9f3dd3dc5d95d621ee75850d559b2e4)

commit a4be120618a5d8517d23e687003cab53e7db11c9
Author: Petr Menšík <pemensik@redhat.com>
Date:   Sun Dec 16 21:25:29 2018 +0000

    Fix option parsing errors introduced in 59e470381f84f2fdf0640c7bc67827f3f0c64784

    Thanks to Kevin Darbyshire-Bryant for spotting this.

    (cherry picked from commit 137e9f878fafb38369eab7d9dfe84e4228ff5f89)

commit 3e0752faa67ffd25893ebbcbe6a5788699a2e1c9
Author: Petr Menšík <pemensik@redhat.com>
Date:   Fri Nov 2 22:39:39 2018 +0000

    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.

    (cherry picked from commit 59e470381f84f2fdf0640c7bc67827f3f0c64784)

commit c2a44c21dddffff95346c931feda696704ea73ca
Author: Petr Menšík <pemensik@redhat.com>
Date:   Wed Oct 24 22:30:18 2018 +0100

    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.

    (cherry picked from commit 24b87607c1353e94689e8a2190571ab3f3b36f31)

commit 5b9f199a1b16b7aa41cf544e9312c93e893206b3
Author: Petr Menšík <pemensik@redhat.com>
Date:   Fri Aug 17 10:20:05 2018 +0200

    Minor improvements in lease-tools

    Limit max interface name to fit into buffer.
    Make sure pointer have to be always positive.
    Close socket after received reply.

    (cherry picked from commit 2b38e3823b12ab13f86c3a44648de436daadb1f6)

commit d30a8f4c46a1b446b7d9932d022a09f1ee6b4554
Author: Petr Menšík <pemensik@redhat.com>
Date:   Thu Aug 16 15:48:15 2018 +0200

    Close socket after received reply

commit 6e5dedbb8aa3d27c9477558e66f9d260414340a3
Author: Petr Menšík <pemensik@redhat.com>
Date:   Wed Aug 15 19:41:07 2018 +0200

    Mark die function as never returning

    Improves static analysis output and reduces false positives.

commit 3d7e9ba115d3c229d678814103dbf3401738dcf5
Author: Petr Menšík <pemensik@redhat.com>
Date:   Wed Aug 15 18:17:00 2018 +0200

    Fix lengths of interface names

    Use helper function similar to copy correctly limited names into
    buffers.
---
 contrib/lease-tools/dhcp_lease_time.c |   2 +-
 contrib/lease-tools/dhcp_release.c    |   3 +-
 contrib/lease-tools/dhcp_release6.c   |   5 +-
 src/bpf.c                             |   2 +-
 src/dhcp.c                            |   9 +-
 src/dnsmasq.h                         |  20 +-
 src/log.c                             |   2 +-
 src/network.c                         |  12 +-
 src/option.c                          | 548 +++++++++++++++++---------
 src/rfc2131.c                         |  10 +-
 src/tftp.c                            |   2 +-
 src/util.c                            |  12 +-
 12 files changed, 408 insertions(+), 219 deletions(-)

diff --git a/contrib/lease-tools/dhcp_lease_time.c b/contrib/lease-tools/dhcp_lease_time.c
index f9d7a85..697d627 100644
--- a/contrib/lease-tools/dhcp_lease_time.c
+++ b/contrib/lease-tools/dhcp_lease_time.c
@@ -83,7 +83,7 @@ static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt
           if (p >= end - 2)
             return NULL; /* malformed packet */
           opt_len = option_len(p);
-          if (p >= end - (2 + opt_len))
+          if (end - p >= (2 + opt_len))
             return NULL; /* malformed packet */
           if (*p == opt && opt_len >= minsize)
             return p;
diff --git a/contrib/lease-tools/dhcp_release.c b/contrib/lease-tools/dhcp_release.c
index 201fcd3..59883d4 100644
--- a/contrib/lease-tools/dhcp_release.c
+++ b/contrib/lease-tools/dhcp_release.c
@@ -270,7 +270,8 @@ int main(int argc, char **argv)
   
   /* This voodoo fakes up a packet coming from the correct interface, which really matters for 
      a DHCP server */
-  strcpy(ifr.ifr_name, argv[1]);
+  strncpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name)-1);
+  ifr.ifr_name[sizeof(ifr.ifr_name)-1] = '\0';
   if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == -1)
     {
       perror("cannot setup interface");
diff --git a/contrib/lease-tools/dhcp_release6.c b/contrib/lease-tools/dhcp_release6.c
index 7f79fa7..d680222 100644
--- a/contrib/lease-tools/dhcp_release6.c
+++ b/contrib/lease-tools/dhcp_release6.c
@@ -376,9 +376,12 @@ int send_release_packet(const char* iface, struct dhcp6_packet* packet)
             sleep(1);
             continue;
 	  }
+
+        close(sock);
         return result;
       }
-    
+
+    close(sock);
     fprintf(stderr, "Response timed out\n");
     return -1;   
 }
diff --git a/src/bpf.c b/src/bpf.c
index 49a11bf..ff66d6d 100644
--- a/src/bpf.c
+++ b/src/bpf.c
@@ -169,7 +169,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
 	      struct in6_ifreq ifr6;
 
 	      memset(&ifr6, 0, sizeof(ifr6));
-	      strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name));
+	      safe_strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name));
 	      
 	      ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
 	      if (fd != -1 && ioctl(fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)
diff --git a/src/dhcp.c b/src/dhcp.c
index 5a8daec..26f3287 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -232,7 +232,7 @@ void dhcp_packet(time_t now, int pxe_fd)
   
 #ifdef HAVE_LINUX_NETWORK
   /* ARP fiddling uses original interface even if we pretend to use a different one. */
-  strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
+  safe_strncpy(arp_req.arp_dev, ifr.ifr_name, sizeof(arp_req.arp_dev));
 #endif 
 
   /* If the interface on which the DHCP request was received is an
@@ -255,7 +255,7 @@ void dhcp_packet(time_t now, int pxe_fd)
 	      }
 	    else 
 	      {
-		strncpy(ifr.ifr_name,  bridge->iface, IF_NAMESIZE);
+		safe_strncpy(ifr.ifr_name,  bridge->iface, sizeof(ifr.ifr_name));
 		break;
 	      }
 	  }
@@ -279,7 +279,7 @@ void dhcp_packet(time_t now, int pxe_fd)
       is_relay_reply = 1; 
       iov.iov_len = sz;
 #ifdef HAVE_LINUX_NETWORK
-      strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
+      safe_strncpy(arp_req.arp_dev, ifr.ifr_name, sizeof(arp_req.arp_dev));
 #endif 
     }
   else
@@ -988,8 +988,7 @@ char *host_from_dns(struct in_addr addr)
       if (!legal_hostname(hostname))
 	return NULL;
       
-      strncpy(daemon->dhcp_buff, hostname, 256);
-      daemon->dhcp_buff[255] = 0;
+      safe_strncpy(daemon->dhcp_buff, hostname, 256);
       strip_hostname(daemon->dhcp_buff);
 
       return daemon->dhcp_buff;
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 6773b69..6b18bb7 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -42,6 +42,12 @@
 #  define __EXTENSIONS__
 #endif
 
+#if (defined(__GNUC__) && __GNUC__ >= 3) || defined(__clang__)
+#define ATTRIBUTE_NORETURN __attribute__ ((noreturn))
+#else
+#define ATTRIBUTE_NORETURN
+#endif
+
 /* get these before config.h  for IPv6 stuff... */
 #include <sys/types.h> 
 #include <sys/socket.h>
@@ -190,9 +196,6 @@ struct event_desc {
 #define EC_MISC        5
 #define EC_INIT_OFFSET 10
 
-/* Trust the compiler dead-code eliminator.... */
-#define option_bool(x) (((x) < 32) ? daemon->options & (1u << (x)) : daemon->options2 & (1u << ((x) - 32)))
-
 #define OPT_BOGUSPRIV      0
 #define OPT_FILTER         1
 #define OPT_LOG            2
@@ -252,6 +255,12 @@ struct event_desc {
 #define OPT_TFTP_APREF_MAC 56
 #define OPT_LAST           57
 
+#define OPTION_BITS (sizeof(unsigned int)*8)
+#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
+#define option_var(x) (daemon->options[(x) / OPTION_BITS])
+#define option_val(x) ((1u) << ((x) % OPTION_BITS))
+#define option_bool(x) (option_var(x) & option_val(x))
+
 /* extra flags for my_syslog, we use a couple of facilities since they are known 
    not to occupy the same bits as priorities, no matter how syslog.h is set up. */
 #define MS_TFTP   LOG_USER
@@ -947,7 +956,7 @@ extern struct daemon {
      config file arguments. All set (including defaults)
      in option.c */
 
-  unsigned int options, options2;
+  unsigned int options[OPTION_SIZE];
   struct resolvc default_resolv, *resolv_files;
   time_t last_resolv;
   char *servers_file;
@@ -1205,6 +1214,7 @@ int legal_hostname(char *name);
 char *canonicalise(char *in, int *nomem);
 unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit);
 void *safe_malloc(size_t size);
+void safe_strncpy(char *dest, const char *src, size_t size);
 void safe_pipe(int *fd, int read_noblock);
 void *whine_malloc(size_t size);
 int sa_len(union mysockaddr *addr);
@@ -1233,7 +1243,7 @@ int wildcard_match(const char* wildcard, const char* match);
 int wildcard_matchn(const char* wildcard, const char* match, int num);
 
 /* log.c */
-void die(char *message, char *arg1, int exit_code);
+void die(char *message, char *arg1, int exit_code) ATTRIBUTE_NORETURN;
 int log_start(struct passwd *ent_pw, int errfd);
 int log_reopen(char *log_file);
 
diff --git a/src/log.c b/src/log.c
index dae8a75..d0d4780 100644
--- a/src/log.c
+++ b/src/log.c
@@ -232,7 +232,7 @@ static void log_write(void)
 	      logaddr.sun_len = sizeof(logaddr) - sizeof(logaddr.sun_path) + strlen(_PATH_LOG) + 1; 
 #endif
 	      logaddr.sun_family = AF_UNIX;
-	      strncpy(logaddr.sun_path, _PATH_LOG, sizeof(logaddr.sun_path));
+	      safe_strncpy(logaddr.sun_path, _PATH_LOG, sizeof(logaddr.sun_path));
 	      
 	      /* Got connection back? try again. */
 	      if (connect(log_fd, (struct sockaddr *)&logaddr, sizeof(logaddr)) != -1)
diff --git a/src/network.c b/src/network.c
index 0381513..efb7b03 100644
--- a/src/network.c
+++ b/src/network.c
@@ -29,7 +29,7 @@ int indextoname(int fd, int index, char *name)
   if (ioctl(fd, SIOCGIFNAME, &ifr) == -1)
     return 0;
 
-  strncpy(name, ifr.ifr_name, IF_NAMESIZE);
+  safe_strncpy(name, ifr.ifr_name, IF_NAMESIZE);
 
   return 1;
 }
@@ -82,12 +82,12 @@ int indextoname(int fd, int index, char *name)
   for (i = lifc.lifc_len / sizeof(struct lifreq); i; i--, lifrp++) 
     {
       struct lifreq lifr;
-      strncpy(lifr.lifr_name, lifrp->lifr_name, IF_NAMESIZE);
+      safe_strncpy(lifr.lifr_name, lifrp->lifr_name, IF_NAMESIZE);
       if (ioctl(fd, SIOCGLIFINDEX, &lifr) < 0) 
 	return 0;
       
       if (lifr.lifr_index == index) {
-	strncpy(name, lifr.lifr_name, IF_NAMESIZE);
+	safe_strncpy(name, lifr.lifr_name, IF_NAMESIZE);
 	return 1;
       }
     }
@@ -188,7 +188,7 @@ int loopback_exception(int fd, int family, struct all_addr *addr, char *name)
   struct ifreq ifr;
   struct irec *iface;
 
-  strncpy(ifr.ifr_name, name, IF_NAMESIZE);
+  safe_strncpy(ifr.ifr_name, name, IF_NAMESIZE);
   if (ioctl(fd, SIOCGIFFLAGS, &ifr) != -1 &&
       ifr.ifr_flags & IFF_LOOPBACK)
     {
@@ -1284,7 +1284,7 @@ static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname)
       return NULL;
     }
 
-  strcpy(sfd->interface, intname); 
+  safe_strncpy(sfd->interface, intname, sizeof(sfd->interface)); 
   sfd->source_addr = *addr;
   sfd->next = daemon->sfds;
   sfd->ifindex = ifindex;
@@ -1452,7 +1452,7 @@ void add_update_server(int flags,
 	serv->flags |= SERV_HAS_DOMAIN;
       
       if (interface)
-	strcpy(serv->interface, interface);      
+	safe_strncpy(serv->interface, interface, sizeof(serv->interface));
       if (addr)
 	serv->addr = *addr;
       if (source_addr)
diff --git a/src/option.c b/src/option.c
index d358d99..9768efb 100644
--- a/src/option.c
+++ b/src/option.c
@@ -559,14 +559,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);
@@ -741,6 +742,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) 
 {
@@ -792,7 +795,7 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
     if (interface_opt)
       {
 #if defined(SO_BINDTODEVICE)
-	strncpy(interface, interface_opt, IF_NAMESIZE - 1);
+	safe_strncpy(interface, interface_opt, IF_NAMESIZE);
 #else
 	return _("interface binding not supported");
 #endif
@@ -821,7 +824,7 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
 		return _("interface can only be specified once");
 	      
 	      source_addr->in.sin_addr.s_addr = INADDR_ANY;
-	      strncpy(interface, source, IF_NAMESIZE - 1);
+	      safe_strncpy(interface, source, IF_NAMESIZE);
 #else
 	      return _("interface binding not supported");
 #endif
@@ -856,7 +859,7 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
 		return _("interface can only be specified once");
 	      
 	      source_addr->in6.sin6_addr = in6addr_any;
-	      strncpy(interface, source, IF_NAMESIZE - 1);
+	      safe_strncpy(interface, source, IF_NAMESIZE);
 #else
 	      return _("interface binding not supported");
 #endif
@@ -894,6 +897,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;
     }
 
@@ -948,6 +953,99 @@ 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);
+#ifdef HAVE_DHCP6
+      free(ctx->template_interface);
+#endif
+      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)
 {
@@ -955,7 +1053,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;
@@ -1042,14 +1139,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; 
@@ -1059,7 +1151,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))
@@ -1073,7 +1165,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)
     {
@@ -1141,10 +1233,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))
@@ -1237,7 +1329,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);
@@ -1282,8 +1374,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;
 	}
@@ -1310,7 +1402,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;
 		    }
@@ -1404,7 +1496,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);
 		  
@@ -1442,14 +1534,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)
 	{
@@ -1474,24 +1566,31 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
     }
     
   return 1;
+on_error:
+  dhcp_opt_free(new);
+  return 0;
 }
 
 #endif
 
 void set_option_bool(unsigned int opt)
 {
-  if (opt < 32)
-    daemon->options |= 1u << opt;
-  else
-    daemon->options2 |= 1u << (opt - 32);
+  option_var(opt) |= option_val(opt);
 }
 
 void reset_option_bool(unsigned int opt)
 {
-  if (opt < 32)
-    daemon->options &= ~(1u << opt);
-  else
-    daemon->options2 &= ~(1u << (opt - 32));
+  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)
@@ -1675,13 +1774,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;
 
@@ -1693,15 +1792,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;
@@ -1908,7 +2007,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
 		  new->addr.sa.sa_family = AF_INET6;
 #endif
 		else
-		  ret_err(gen_err);
+		  {
+		    free(new->name);
+		    ret_err_free(gen_err, new);
+		  }
 	      } 
 	  }
 	new->next = daemon->authinterface;
@@ -2080,7 +2182,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;
@@ -2093,18 +2195,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;
 
@@ -2134,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
@@ -2164,7 +2266,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);
 		    }
 		  else
 		    {
@@ -2178,7 +2280,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);
 			}
 #ifdef HAVE_IPV6
 		      else if (inet_pton(AF_INET6, comma, &new->start6))
@@ -2187,17 +2289,17 @@ 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);
 			}
 #endif
 		      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);
 			}
 		    }
 
@@ -2359,7 +2461,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')
@@ -2430,7 +2532,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;
@@ -2451,7 +2556,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;
@@ -2801,21 +2909,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;
 	      }
@@ -2831,7 +2937,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))
 	  {
@@ -2843,7 +2952,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))
 	      {
@@ -2858,7 +2970,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], '.') &&  
@@ -2872,6 +2987,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;
@@ -2917,19 +3034,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))
@@ -2940,7 +3062,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))
@@ -2952,12 +3077,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;
@@ -2996,7 +3127,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
@@ -3023,6 +3154,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++)
@@ -3053,7 +3185,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;
@@ -3065,17 +3200,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] == ']')
 		{
@@ -3083,7 +3218,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)
@@ -3101,10 +3239,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;
 		    }		    
@@ -3181,7 +3322,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);			
@@ -3234,10 +3378,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)
 		  {
@@ -3254,7 +3395,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;
 		  }
 	      }
@@ -3263,7 +3404,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;
       }
@@ -3280,19 +3425,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);
+	  {
+	    ret_err(gen_err);
+	  }
 	else 
 	  {
 	    char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
@@ -3338,19 +3476,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);
+	  {
+	    ret_err(gen_err);
+	  }
 	else
 	  {
 	    struct delay_config *new;
@@ -3375,19 +3506,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);
+	   {
+	     dhcp_opt_free(new);
+	     ret_err(gen_err);
+	   }
 	 else
 	   {
 	     comma = split(arg);
@@ -3423,17 +3548,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++)
@@ -3510,7 +3626,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;
@@ -3527,7 +3646,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;
@@ -3549,7 +3668,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, 
@@ -3559,7 +3678,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);
 	   }
@@ -3661,14 +3783,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;
 	}
 	
@@ -3682,7 +3798,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;
@@ -3708,7 +3824,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;
       }
@@ -3748,8 +3867,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;
@@ -3800,7 +3922,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;
       }
@@ -3835,7 +3957,7 @@ err:
 	      new->family = AF_INET6;
 #endif
 	    else
-	      ret_err(gen_err);
+	      ret_err_free(gen_err, new);
 	  } 
 	new->intr = opt_string_alloc(comma);
 	break;
@@ -3867,11 +3989,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;
@@ -3894,7 +4024,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));
@@ -3912,7 +4046,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++)
@@ -3925,7 +4059,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));
@@ -3947,22 +4085,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)
 	  {
@@ -3983,14 +4125,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);
@@ -4037,24 +4179,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);
+                      }
 		  }
 	      }
 	  }
@@ -4073,13 +4223,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;
@@ -4100,10 +4252,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;
@@ -4143,6 +4304,7 @@ err:
 	int len;
 	
 	new->class = C_IN;
+	new->name = NULL;
 
 	if ((comma = split(arg)) && (algo = split(comma)))
 	  {
@@ -4167,7 +4329,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;
@@ -4181,7 +4343,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;
@@ -4650,8 +4815,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;
 
@@ -4725,8 +4890,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
 	      argbuf_size = strlen(optarg) + 1;
 	      argbuf = opt_malloc(argbuf_size);
 	    }
-	  strncpy(argbuf, optarg, argbuf_size);
-	  argbuf[argbuf_size-1] = 0;
+	  safe_strncpy(argbuf, optarg, argbuf_size);
 	  arg = argbuf;
 	}
       else
@@ -4761,7 +4925,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
@@ -4779,10 +4944,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)
diff --git a/src/rfc2131.c b/src/rfc2131.c
index c08a8ab..997575a 100644
--- a/src/rfc2131.c
+++ b/src/rfc2131.c
@@ -917,7 +917,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
 			mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
 		      
 		      if (boot->file)
-			strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
+			safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
 		    }
 		  
 		  option_put(mess, end, OPTION_MESSAGE_TYPE, 1, 
@@ -2296,7 +2296,7 @@ static void do_options(struct dhcp_context *context,
 	      in_list(req_options, OPTION_SNAME))
 	    option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
 	  else
-	    strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1);
+	    safe_strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname));
 	}
       
       if (boot->file)
@@ -2306,7 +2306,7 @@ static void do_options(struct dhcp_context *context,
 	      in_list(req_options, OPTION_FILENAME))
 	    option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
 	  else
-	    strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
+	    safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
 	}
       
       if (boot->next_server.s_addr) 
@@ -2323,14 +2323,14 @@ static void do_options(struct dhcp_context *context,
       if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
 	  (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
 	{
-	  strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
+	  safe_strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file));
 	  done_file = 1;
 	}
       
       if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
 	  (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
 	{
-	  strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
+	  safe_strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname));
 	  done_server = 1;
 	}
       
diff --git a/src/tftp.c b/src/tftp.c
index bccca69..f2eccbc 100644
--- a/src/tftp.c
+++ b/src/tftp.c
@@ -234,7 +234,7 @@ void tftp_request(struct listener *listen, time_t now)
 #endif
 	}
 
-      strncpy(ifr.ifr_name, name, IF_NAMESIZE);
+      safe_strncpy(ifr.ifr_name, name, IF_NAMESIZE);
       if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
 	{
 	  mtu = ifr.ifr_mtu;  
diff --git a/src/util.c b/src/util.c
index 532bc16..e003e3c 100644
--- a/src/util.c
+++ b/src/util.c
@@ -281,7 +281,17 @@ void *safe_malloc(size_t size)
     die(_("could not get memory"), NULL, EC_NOMEM);
       
   return ret;
-}    
+}
+
+/* can be replaced by (void)strlcpy() on some platforms */
+void safe_strncpy(char *dest, const char *src, size_t size)
+{
+  if (size)
+    {
+      dest[size-1] = '\0';
+      strncpy(dest, src, size-1);
+    }
+}
 
 void safe_pipe(int *fd, int read_noblock)
 {
-- 
2.20.1