2bab6f
From 8e11d702921e51a5eb00b9ee12925cae69039c22 Mon Sep 17 00:00:00 2001
2bab6f
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
2bab6f
Date: Thu, 18 Jul 2019 18:49:30 +0200
2bab6f
Subject: [PATCH] Fix issues detected by Coverity
2bab6f
MIME-Version: 1.0
2bab6f
Content-Type: text/plain; charset=UTF-8
2bab6f
Content-Transfer-Encoding: 8bit
2bab6f
2bab6f
Squashed commit of the following:
2bab6f
2bab6f
commit 6a5fa6c9207961a662f8debbe9172500c752e3ac
2bab6f
Author: Kevin Darbyshire-Bryant <kevin@darbyshire-bryant.me.uk>
2bab6f
Date:   Mon Dec 10 10:34:35 2018 +0000
2bab6f
2bab6f
    build failure on master with NO_DHCPv6 and fix....
2bab6f
2bab6f
    Hi Simon,
2bab6f
2bab6f
    master has a build error when building without HAVE_DHCPv6
2bab6f
2bab6f
    option.c: In function 'dhcp_context_free':
2bab6f
    option.c:1042:15: error: 'struct dhcp_context' has no member named 'template_interface'
2bab6f
           free(ctx->template_interface);
2bab6f
2bab6f
    Sadly, need to put in a little conditional compilation ifdef'erey
2bab6f
2bab6f
    Simplest patch in the world attached
2bab6f
2bab6f
    Cheers,
2bab6f
2bab6f
    Kevin D-B
2bab6f
2bab6f
    012C ACB2 28C6 C53E 9775  9123 B3A2 389B 9DE2 334A
2bab6f
2bab6f
    From 061eb8599636bb360e0b7fa5986935b86db39497 Mon Sep 17 00:00:00 2001
2bab6f
    From: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
2bab6f
    Date: Mon, 10 Dec 2018 10:07:33 +0000
2bab6f
    Subject: [PATCH] option: fix non DHCPv6 build error
2bab6f
2bab6f
    option.c: In function 'dhcp_context_free':
2bab6f
    option.c:1042:15: error: 'struct dhcp_context' has no member named 'template_interface'
2bab6f
           free(ctx->template_interface);
2bab6f
    		^~
2bab6f
2bab6f
    Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
2bab6f
    (cherry picked from commit b683cf37f9f3dd3dc5d95d621ee75850d559b2e4)
2bab6f
2bab6f
commit a4be120618a5d8517d23e687003cab53e7db11c9
2bab6f
Author: Petr Menšík <pemensik@redhat.com>
2bab6f
Date:   Sun Dec 16 21:25:29 2018 +0000
2bab6f
2bab6f
    Fix option parsing errors introduced in 59e470381f84f2fdf0640c7bc67827f3f0c64784
2bab6f
2bab6f
    Thanks to Kevin Darbyshire-Bryant for spotting this.
2bab6f
2bab6f
    (cherry picked from commit 137e9f878fafb38369eab7d9dfe84e4228ff5f89)
2bab6f
2bab6f
commit 3e0752faa67ffd25893ebbcbe6a5788699a2e1c9
2bab6f
Author: Petr Menšík <pemensik@redhat.com>
2bab6f
Date:   Fri Nov 2 22:39:39 2018 +0000
2bab6f
2bab6f
    Free config file values on parsing errors.
2bab6f
2bab6f
    This time I have a little bit more controversal patches. But I think
2bab6f
    still useful. They fixes memory leaks that might occur in some cases.
2bab6f
    Most dnsmasq errors is fatal, so it does not matter. But some are not.
2bab6f
    Some parts are reloaded on SIGHUP signal, so it might leak more than once.
2bab6f
2bab6f
    Some example when it changes the failures. Use dhcp-options file with
2bab6f
    this content:
2bab6f
2bab6f
    tag:error,vendor:redhat
2bab6f
    option:ntp-server,1.2.3.4.5
2bab6f
    option6:ntp-server,[:::]
2bab6f
2bab6f
    Is not fatal and dnsmasq will start. On each reload command, it would
2bab6f
    leak some memory. I validated it using valgrind --leak-check=full
2bab6f
    dnsmasq -d. This patch fixes it. It introduces something that might be
2bab6f
    considered constructor and destructor of selected structures.
2bab6f
2bab6f
    (cherry picked from commit 59e470381f84f2fdf0640c7bc67827f3f0c64784)
2bab6f
2bab6f
commit c2a44c21dddffff95346c931feda696704ea73ca
2bab6f
Author: Petr Menšík <pemensik@redhat.com>
2bab6f
Date:   Wed Oct 24 22:30:18 2018 +0100
2bab6f
2bab6f
    Do not rely on dead code elimination, use array instead.
2bab6f
    Make options bits derived from size and count. Use size of option bits
2bab6f
    and last supported bit in computation. No new change would be required
2bab6f
    when new options are added. Just change OPT_LAST constant.
2bab6f
2bab6f
    (cherry picked from commit 24b87607c1353e94689e8a2190571ab3f3b36f31)
2bab6f
2bab6f
commit 5b9f199a1b16b7aa41cf544e9312c93e893206b3
2bab6f
Author: Petr Menšík <pemensik@redhat.com>
2bab6f
Date:   Fri Aug 17 10:20:05 2018 +0200
2bab6f
2bab6f
    Minor improvements in lease-tools
2bab6f
2bab6f
    Limit max interface name to fit into buffer.
2bab6f
    Make sure pointer have to be always positive.
2bab6f
    Close socket after received reply.
2bab6f
2bab6f
    (cherry picked from commit 2b38e3823b12ab13f86c3a44648de436daadb1f6)
2bab6f
2bab6f
commit d30a8f4c46a1b446b7d9932d022a09f1ee6b4554
2bab6f
Author: Petr Menšík <pemensik@redhat.com>
2bab6f
Date:   Thu Aug 16 15:48:15 2018 +0200
2bab6f
2bab6f
    Close socket after received reply
2bab6f
2bab6f
commit 6e5dedbb8aa3d27c9477558e66f9d260414340a3
2bab6f
Author: Petr Menšík <pemensik@redhat.com>
2bab6f
Date:   Wed Aug 15 19:41:07 2018 +0200
2bab6f
2bab6f
    Mark die function as never returning
2bab6f
2bab6f
    Improves static analysis output and reduces false positives.
2bab6f
2bab6f
commit 3d7e9ba115d3c229d678814103dbf3401738dcf5
2bab6f
Author: Petr Menšík <pemensik@redhat.com>
2bab6f
Date:   Wed Aug 15 18:17:00 2018 +0200
2bab6f
2bab6f
    Fix lengths of interface names
2bab6f
2bab6f
    Use helper function similar to copy correctly limited names into
2bab6f
    buffers.
2bab6f
---
2bab6f
 contrib/lease-tools/dhcp_lease_time.c |   2 +-
2bab6f
 contrib/lease-tools/dhcp_release.c    |   3 +-
2bab6f
 contrib/lease-tools/dhcp_release6.c   |   5 +-
2bab6f
 src/bpf.c                             |   2 +-
2bab6f
 src/dhcp.c                            |   9 +-
2bab6f
 src/dnsmasq.h                         |  20 +-
2bab6f
 src/log.c                             |   2 +-
2bab6f
 src/network.c                         |  12 +-
2bab6f
 src/option.c                          | 548 +++++++++++++++++---------
2bab6f
 src/rfc2131.c                         |  10 +-
2bab6f
 src/tftp.c                            |   2 +-
2bab6f
 src/util.c                            |  12 +-
2bab6f
 12 files changed, 408 insertions(+), 219 deletions(-)
2bab6f
2bab6f
diff --git a/contrib/lease-tools/dhcp_lease_time.c b/contrib/lease-tools/dhcp_lease_time.c
2bab6f
index f9d7a85..697d627 100644
2bab6f
--- a/contrib/lease-tools/dhcp_lease_time.c
2bab6f
+++ b/contrib/lease-tools/dhcp_lease_time.c
2bab6f
@@ -83,7 +83,7 @@ static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt
2bab6f
           if (p >= end - 2)
2bab6f
             return NULL; /* malformed packet */
2bab6f
           opt_len = option_len(p);
2bab6f
-          if (p >= end - (2 + opt_len))
2bab6f
+          if (end - p >= (2 + opt_len))
2bab6f
             return NULL; /* malformed packet */
2bab6f
           if (*p == opt && opt_len >= minsize)
2bab6f
             return p;
2bab6f
diff --git a/contrib/lease-tools/dhcp_release.c b/contrib/lease-tools/dhcp_release.c
2bab6f
index 201fcd3..59883d4 100644
2bab6f
--- a/contrib/lease-tools/dhcp_release.c
2bab6f
+++ b/contrib/lease-tools/dhcp_release.c
2bab6f
@@ -270,7 +270,8 @@ int main(int argc, char **argv)
2bab6f
   
2bab6f
   /* This voodoo fakes up a packet coming from the correct interface, which really matters for 
2bab6f
      a DHCP server */
2bab6f
-  strcpy(ifr.ifr_name, argv[1]);
2bab6f
+  strncpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name)-1);
2bab6f
+  ifr.ifr_name[sizeof(ifr.ifr_name)-1] = '\0';
2bab6f
   if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == -1)
2bab6f
     {
2bab6f
       perror("cannot setup interface");
2bab6f
diff --git a/contrib/lease-tools/dhcp_release6.c b/contrib/lease-tools/dhcp_release6.c
2bab6f
index 7f79fa7..d680222 100644
2bab6f
--- a/contrib/lease-tools/dhcp_release6.c
2bab6f
+++ b/contrib/lease-tools/dhcp_release6.c
2bab6f
@@ -376,9 +376,12 @@ int send_release_packet(const char* iface, struct dhcp6_packet* packet)
2bab6f
             sleep(1);
2bab6f
             continue;
2bab6f
 	  }
2bab6f
+
2bab6f
+        close(sock);
2bab6f
         return result;
2bab6f
       }
2bab6f
-    
2bab6f
+
2bab6f
+    close(sock);
2bab6f
     fprintf(stderr, "Response timed out\n");
2bab6f
     return -1;   
2bab6f
 }
2bab6f
diff --git a/src/bpf.c b/src/bpf.c
2bab6f
index 49a11bf..ff66d6d 100644
2bab6f
--- a/src/bpf.c
2bab6f
+++ b/src/bpf.c
2bab6f
@@ -169,7 +169,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
2bab6f
 	      struct in6_ifreq ifr6;
2bab6f
 
2bab6f
 	      memset(&ifr6, 0, sizeof(ifr6));
2bab6f
-	      strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name));
2bab6f
+	      safe_strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name));
2bab6f
 	      
2bab6f
 	      ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr);
2bab6f
 	      if (fd != -1 && ioctl(fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)
2bab6f
diff --git a/src/dhcp.c b/src/dhcp.c
2bab6f
index 5a8daec..26f3287 100644
2bab6f
--- a/src/dhcp.c
2bab6f
+++ b/src/dhcp.c
2bab6f
@@ -232,7 +232,7 @@ void dhcp_packet(time_t now, int pxe_fd)
2bab6f
   
2bab6f
 #ifdef HAVE_LINUX_NETWORK
2bab6f
   /* ARP fiddling uses original interface even if we pretend to use a different one. */
2bab6f
-  strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
2bab6f
+  safe_strncpy(arp_req.arp_dev, ifr.ifr_name, sizeof(arp_req.arp_dev));
2bab6f
 #endif 
2bab6f
 
2bab6f
   /* If the interface on which the DHCP request was received is an
2bab6f
@@ -255,7 +255,7 @@ void dhcp_packet(time_t now, int pxe_fd)
2bab6f
 	      }
2bab6f
 	    else 
2bab6f
 	      {
2bab6f
-		strncpy(ifr.ifr_name,  bridge->iface, IF_NAMESIZE);
2bab6f
+		safe_strncpy(ifr.ifr_name,  bridge->iface, sizeof(ifr.ifr_name));
2bab6f
 		break;
2bab6f
 	      }
2bab6f
 	  }
2bab6f
@@ -279,7 +279,7 @@ void dhcp_packet(time_t now, int pxe_fd)
2bab6f
       is_relay_reply = 1; 
2bab6f
       iov.iov_len = sz;
2bab6f
 #ifdef HAVE_LINUX_NETWORK
2bab6f
-      strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
2bab6f
+      safe_strncpy(arp_req.arp_dev, ifr.ifr_name, sizeof(arp_req.arp_dev));
2bab6f
 #endif 
2bab6f
     }
2bab6f
   else
2bab6f
@@ -988,8 +988,7 @@ char *host_from_dns(struct in_addr addr)
2bab6f
       if (!legal_hostname(hostname))
2bab6f
 	return NULL;
2bab6f
       
2bab6f
-      strncpy(daemon->dhcp_buff, hostname, 256);
2bab6f
-      daemon->dhcp_buff[255] = 0;
2bab6f
+      safe_strncpy(daemon->dhcp_buff, hostname, 256);
2bab6f
       strip_hostname(daemon->dhcp_buff);
2bab6f
 
2bab6f
       return daemon->dhcp_buff;
2bab6f
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
2bab6f
index 6773b69..6b18bb7 100644
2bab6f
--- a/src/dnsmasq.h
2bab6f
+++ b/src/dnsmasq.h
2bab6f
@@ -42,6 +42,12 @@
2bab6f
 #  define __EXTENSIONS__
2bab6f
 #endif
2bab6f
 
2bab6f
+#if (defined(__GNUC__) && __GNUC__ >= 3) || defined(__clang__)
2bab6f
+#define ATTRIBUTE_NORETURN __attribute__ ((noreturn))
2bab6f
+#else
2bab6f
+#define ATTRIBUTE_NORETURN
2bab6f
+#endif
2bab6f
+
2bab6f
 /* get these before config.h  for IPv6 stuff... */
2bab6f
 #include <sys/types.h> 
2bab6f
 #include <sys/socket.h>
2bab6f
@@ -190,9 +196,6 @@ struct event_desc {
2bab6f
 #define EC_MISC        5
2bab6f
 #define EC_INIT_OFFSET 10
2bab6f
 
2bab6f
-/* Trust the compiler dead-code eliminator.... */
2bab6f
-#define option_bool(x) (((x) < 32) ? daemon->options & (1u << (x)) : daemon->options2 & (1u << ((x) - 32)))
2bab6f
-
2bab6f
 #define OPT_BOGUSPRIV      0
2bab6f
 #define OPT_FILTER         1
2bab6f
 #define OPT_LOG            2
2bab6f
@@ -252,6 +255,12 @@ struct event_desc {
2bab6f
 #define OPT_TFTP_APREF_MAC 56
2bab6f
 #define OPT_LAST           57
2bab6f
 
2bab6f
+#define OPTION_BITS (sizeof(unsigned int)*8)
2bab6f
+#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
2bab6f
+#define option_var(x) (daemon->options[(x) / OPTION_BITS])
2bab6f
+#define option_val(x) ((1u) << ((x) % OPTION_BITS))
2bab6f
+#define option_bool(x) (option_var(x) & option_val(x))
2bab6f
+
2bab6f
 /* extra flags for my_syslog, we use a couple of facilities since they are known 
2bab6f
    not to occupy the same bits as priorities, no matter how syslog.h is set up. */
2bab6f
 #define MS_TFTP   LOG_USER
2bab6f
@@ -947,7 +956,7 @@ extern struct daemon {
2bab6f
      config file arguments. All set (including defaults)
2bab6f
      in option.c */
2bab6f
 
2bab6f
-  unsigned int options, options2;
2bab6f
+  unsigned int options[OPTION_SIZE];
2bab6f
   struct resolvc default_resolv, *resolv_files;
2bab6f
   time_t last_resolv;
2bab6f
   char *servers_file;
2bab6f
@@ -1205,6 +1214,7 @@ int legal_hostname(char *name);
2bab6f
 char *canonicalise(char *in, int *nomem);
2bab6f
 unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit);
2bab6f
 void *safe_malloc(size_t size);
2bab6f
+void safe_strncpy(char *dest, const char *src, size_t size);
2bab6f
 void safe_pipe(int *fd, int read_noblock);
2bab6f
 void *whine_malloc(size_t size);
2bab6f
 int sa_len(union mysockaddr *addr);
2bab6f
@@ -1233,7 +1243,7 @@ int wildcard_match(const char* wildcard, const char* match);
2bab6f
 int wildcard_matchn(const char* wildcard, const char* match, int num);
2bab6f
 
2bab6f
 /* log.c */
2bab6f
-void die(char *message, char *arg1, int exit_code);
2bab6f
+void die(char *message, char *arg1, int exit_code) ATTRIBUTE_NORETURN;
2bab6f
 int log_start(struct passwd *ent_pw, int errfd);
2bab6f
 int log_reopen(char *log_file);
2bab6f
 
2bab6f
diff --git a/src/log.c b/src/log.c
2bab6f
index dae8a75..d0d4780 100644
2bab6f
--- a/src/log.c
2bab6f
+++ b/src/log.c
2bab6f
@@ -232,7 +232,7 @@ static void log_write(void)
2bab6f
 	      logaddr.sun_len = sizeof(logaddr) - sizeof(logaddr.sun_path) + strlen(_PATH_LOG) + 1; 
2bab6f
 #endif
2bab6f
 	      logaddr.sun_family = AF_UNIX;
2bab6f
-	      strncpy(logaddr.sun_path, _PATH_LOG, sizeof(logaddr.sun_path));
2bab6f
+	      safe_strncpy(logaddr.sun_path, _PATH_LOG, sizeof(logaddr.sun_path));
2bab6f
 	      
2bab6f
 	      /* Got connection back? try again. */
2bab6f
 	      if (connect(log_fd, (struct sockaddr *)&logaddr, sizeof(logaddr)) != -1)
2bab6f
diff --git a/src/network.c b/src/network.c
2bab6f
index 0381513..efb7b03 100644
2bab6f
--- a/src/network.c
2bab6f
+++ b/src/network.c
2bab6f
@@ -29,7 +29,7 @@ int indextoname(int fd, int index, char *name)
2bab6f
   if (ioctl(fd, SIOCGIFNAME, &ifr) == -1)
2bab6f
     return 0;
2bab6f
 
2bab6f
-  strncpy(name, ifr.ifr_name, IF_NAMESIZE);
2bab6f
+  safe_strncpy(name, ifr.ifr_name, IF_NAMESIZE);
2bab6f
 
2bab6f
   return 1;
2bab6f
 }
2bab6f
@@ -82,12 +82,12 @@ int indextoname(int fd, int index, char *name)
2bab6f
   for (i = lifc.lifc_len / sizeof(struct lifreq); i; i--, lifrp++) 
2bab6f
     {
2bab6f
       struct lifreq lifr;
2bab6f
-      strncpy(lifr.lifr_name, lifrp->lifr_name, IF_NAMESIZE);
2bab6f
+      safe_strncpy(lifr.lifr_name, lifrp->lifr_name, IF_NAMESIZE);
2bab6f
       if (ioctl(fd, SIOCGLIFINDEX, &lifr) < 0) 
2bab6f
 	return 0;
2bab6f
       
2bab6f
       if (lifr.lifr_index == index) {
2bab6f
-	strncpy(name, lifr.lifr_name, IF_NAMESIZE);
2bab6f
+	safe_strncpy(name, lifr.lifr_name, IF_NAMESIZE);
2bab6f
 	return 1;
2bab6f
       }
2bab6f
     }
2bab6f
@@ -188,7 +188,7 @@ int loopback_exception(int fd, int family, struct all_addr *addr, char *name)
2bab6f
   struct ifreq ifr;
2bab6f
   struct irec *iface;
2bab6f
 
2bab6f
-  strncpy(ifr.ifr_name, name, IF_NAMESIZE);
2bab6f
+  safe_strncpy(ifr.ifr_name, name, IF_NAMESIZE);
2bab6f
   if (ioctl(fd, SIOCGIFFLAGS, &ifr) != -1 &&
2bab6f
       ifr.ifr_flags & IFF_LOOPBACK)
2bab6f
     {
2bab6f
@@ -1284,7 +1284,7 @@ static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname)
2bab6f
       return NULL;
2bab6f
     }
2bab6f
 
2bab6f
-  strcpy(sfd->interface, intname); 
2bab6f
+  safe_strncpy(sfd->interface, intname, sizeof(sfd->interface)); 
2bab6f
   sfd->source_addr = *addr;
2bab6f
   sfd->next = daemon->sfds;
2bab6f
   sfd->ifindex = ifindex;
2bab6f
@@ -1452,7 +1452,7 @@ void add_update_server(int flags,
2bab6f
 	serv->flags |= SERV_HAS_DOMAIN;
2bab6f
       
2bab6f
       if (interface)
2bab6f
-	strcpy(serv->interface, interface);      
2bab6f
+	safe_strncpy(serv->interface, interface, sizeof(serv->interface));
2bab6f
       if (addr)
2bab6f
 	serv->addr = *addr;
2bab6f
       if (source_addr)
2bab6f
diff --git a/src/option.c b/src/option.c
2bab6f
index d358d99..9768efb 100644
2bab6f
--- a/src/option.c
2bab6f
+++ b/src/option.c
2bab6f
@@ -559,14 +559,15 @@ static void *opt_malloc(size_t size)
2bab6f
   return ret;
2bab6f
 }
2bab6f
 
2bab6f
-static char *opt_string_alloc(char *cp)
2bab6f
+static char *opt_string_alloc(const char *cp)
2bab6f
 {
2bab6f
   char *ret = NULL;
2bab6f
+  size_t len;
2bab6f
   
2bab6f
-  if (cp && strlen(cp) != 0)
2bab6f
+  if (cp && (len = strlen(cp)) != 0)
2bab6f
     {
2bab6f
-      ret = opt_malloc(strlen(cp)+1);
2bab6f
-      strcpy(ret, cp); 
2bab6f
+      ret = opt_malloc(len+1);
2bab6f
+      memcpy(ret, cp, len+1); 
2bab6f
       
2bab6f
       /* restore hidden metachars */
2bab6f
       unhide_metas(ret);
2bab6f
@@ -741,6 +742,8 @@ static void do_usage(void)
2bab6f
 }
2bab6f
 
2bab6f
 #define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
2bab6f
+#define ret_err_free(x,m) do { strcpy(errstr, (x)); free((m)); return 0; } while (0)
2bab6f
+#define goto_err(x) do { strcpy(errstr, (x)); goto on_error; } while (0)
2bab6f
 
2bab6f
 static char *parse_mysockaddr(char *arg, union mysockaddr *addr) 
2bab6f
 {
2bab6f
@@ -792,7 +795,7 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
2bab6f
     if (interface_opt)
2bab6f
       {
2bab6f
 #if defined(SO_BINDTODEVICE)
2bab6f
-	strncpy(interface, interface_opt, IF_NAMESIZE - 1);
2bab6f
+	safe_strncpy(interface, interface_opt, IF_NAMESIZE);
2bab6f
 #else
2bab6f
 	return _("interface binding not supported");
2bab6f
 #endif
2bab6f
@@ -821,7 +824,7 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
2bab6f
 		return _("interface can only be specified once");
2bab6f
 	      
2bab6f
 	      source_addr->in.sin_addr.s_addr = INADDR_ANY;
2bab6f
-	      strncpy(interface, source, IF_NAMESIZE - 1);
2bab6f
+	      safe_strncpy(interface, source, IF_NAMESIZE);
2bab6f
 #else
2bab6f
 	      return _("interface binding not supported");
2bab6f
 #endif
2bab6f
@@ -856,7 +859,7 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
2bab6f
 		return _("interface can only be specified once");
2bab6f
 	      
2bab6f
 	      source_addr->in6.sin6_addr = in6addr_any;
2bab6f
-	      strncpy(interface, source, IF_NAMESIZE - 1);
2bab6f
+	      safe_strncpy(interface, source, IF_NAMESIZE);
2bab6f
 #else
2bab6f
 	      return _("interface binding not supported");
2bab6f
 #endif
2bab6f
@@ -894,6 +897,8 @@ static struct server *add_rev4(struct in_addr addr, int msize)
2bab6f
       p += sprintf(p, "%d.", (a >> 24) & 0xff);
2bab6f
       break;
2bab6f
     default:
2bab6f
+      free(serv->domain);
2bab6f
+      free(serv);
2bab6f
       return NULL;
2bab6f
     }
2bab6f
 
2bab6f
@@ -948,6 +953,99 @@ static char *set_prefix(char *arg)
2bab6f
    return arg;
2bab6f
 }
2bab6f
 
2bab6f
+static struct dhcp_netid *
2bab6f
+dhcp_netid_create(const char *net, struct dhcp_netid *next)
2bab6f
+{
2bab6f
+  struct dhcp_netid *tt;
2bab6f
+  tt = opt_malloc(sizeof (struct dhcp_netid));
2bab6f
+  tt->net = opt_string_alloc(net);
2bab6f
+  tt->next = next;
2bab6f
+  return tt;
2bab6f
+}
2bab6f
+
2bab6f
+static void dhcp_netid_free(struct dhcp_netid *nid)
2bab6f
+{
2bab6f
+  while (nid)
2bab6f
+    {
2bab6f
+      struct dhcp_netid *tmp = nid;
2bab6f
+      nid = nid->next;
2bab6f
+      free(tmp->net);
2bab6f
+      free(tmp);
2bab6f
+    }
2bab6f
+}
2bab6f
+
2bab6f
+/* Parse one or more tag:s before parameters.
2bab6f
+ * Moves arg to the end of tags. */
2bab6f
+static struct dhcp_netid * dhcp_tags(char **arg)
2bab6f
+{
2bab6f
+  struct dhcp_netid *id = NULL;
2bab6f
+
2bab6f
+  while (is_tag_prefix(*arg))
2bab6f
+    {
2bab6f
+      char *comma = split(*arg);
2bab6f
+      id = dhcp_netid_create((*arg)+4, id);
2bab6f
+      *arg = comma;
2bab6f
+    };
2bab6f
+  if (!*arg)
2bab6f
+    {
2bab6f
+      dhcp_netid_free(id);
2bab6f
+      id = NULL;
2bab6f
+    }
2bab6f
+  return id;
2bab6f
+}
2bab6f
+
2bab6f
+static void dhcp_netid_list_free(struct dhcp_netid_list *netid)
2bab6f
+{
2bab6f
+  while (netid)
2bab6f
+    {
2bab6f
+      struct dhcp_netid_list *tmplist = netid;
2bab6f
+      netid = netid->next;
2bab6f
+      dhcp_netid_free(tmplist->list);
2bab6f
+      free(tmplist);
2bab6f
+    }
2bab6f
+}
2bab6f
+
2bab6f
+static void dhcp_config_free(struct dhcp_config *config)
2bab6f
+{
2bab6f
+  if (config)
2bab6f
+    {
2bab6f
+      struct hwaddr_config *hwaddr = config->hwaddr;
2bab6f
+      while (hwaddr)
2bab6f
+        {
2bab6f
+	  struct hwaddr_config *tmp = hwaddr;
2bab6f
+          hwaddr = hwaddr->next;
2bab6f
+	  free(tmp);
2bab6f
+        }
2bab6f
+      dhcp_netid_list_free(config->netid);
2bab6f
+      if (config->flags & CONFIG_CLID)
2bab6f
+        free(config->clid);
2bab6f
+      free(config);
2bab6f
+    }
2bab6f
+}
2bab6f
+
2bab6f
+static void dhcp_context_free(struct dhcp_context *ctx)
2bab6f
+{
2bab6f
+  if (ctx)
2bab6f
+    {
2bab6f
+      dhcp_netid_free(ctx->filter);
2bab6f
+      free(ctx->netid.net);
2bab6f
+#ifdef HAVE_DHCP6
2bab6f
+      free(ctx->template_interface);
2bab6f
+#endif
2bab6f
+      free(ctx);
2bab6f
+    }
2bab6f
+}
2bab6f
+
2bab6f
+static void dhcp_opt_free(struct dhcp_opt *opt)
2bab6f
+{
2bab6f
+  if (opt->flags & DHOPT_VENDOR)
2bab6f
+    free(opt->u.vendor_class);
2bab6f
+  dhcp_netid_free(opt->netid);
2bab6f
+  free(opt->val);
2bab6f
+  free(opt);
2bab6f
+}
2bab6f
+
2bab6f
+
2bab6f
 /* This is too insanely large to keep in-line in the switch */
2bab6f
 static int parse_dhcp_opt(char *errstr, char *arg, int flags)
2bab6f
 {
2bab6f
@@ -955,7 +1053,6 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
2bab6f
   char lenchar = 0, *cp;
2bab6f
   int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots;
2bab6f
   char *comma = NULL;
2bab6f
-  struct dhcp_netid *np = NULL;
2bab6f
   u16 opt_len = 0;
2bab6f
   int is6 = 0;
2bab6f
   int option_ok = 0;
2bab6f
@@ -1042,14 +1139,9 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
2bab6f
 	}
2bab6f
       else
2bab6f
 	{
2bab6f
-	  new->netid = opt_malloc(sizeof (struct dhcp_netid));
2bab6f
 	  /* allow optional "net:" or "tag:" for consistency */
2bab6f
-	  if (is_tag_prefix(arg))
2bab6f
-	    new->netid->net = opt_string_alloc(arg+4);
2bab6f
-	  else
2bab6f
-	    new->netid->net = opt_string_alloc(set_prefix(arg));
2bab6f
-	  new->netid->next = np;
2bab6f
-	  np = new->netid;
2bab6f
+	  const char *name = (is_tag_prefix(arg)) ? arg+4 : set_prefix(arg);
2bab6f
+	  new->netid = dhcp_netid_create(name, new->netid);
2bab6f
 	}
2bab6f
       
2bab6f
       arg = comma; 
2bab6f
@@ -1059,7 +1151,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
2bab6f
   if (is6)
2bab6f
     {
2bab6f
       if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))
2bab6f
-	ret_err(_("unsupported encapsulation for IPv6 option"));
2bab6f
+	goto_err(_("unsupported encapsulation for IPv6 option"));
2bab6f
       
2bab6f
       if (opt_len == 0 &&
2bab6f
 	  !(new->flags & DHOPT_RFC3925))
2bab6f
@@ -1073,7 +1165,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
2bab6f
   
2bab6f
   /* option may be missing with rfc3925 match */
2bab6f
   if (!option_ok)
2bab6f
-    ret_err(_("bad dhcp-option"));
2bab6f
+    goto_err(_("bad dhcp-option"));
2bab6f
   
2bab6f
   if (comma)
2bab6f
     {
2bab6f
@@ -1141,10 +1233,10 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
2bab6f
 	  is_string = is_dec = is_hex = 0;
2bab6f
 	  
2bab6f
 	  if (!is6 && (!is_addr || dots == 0))
2bab6f
-	    ret_err(_("bad IP address"));
2bab6f
+	    goto_err(_("bad IP address"));
2bab6f
 
2bab6f
 	   if (is6 && !is_addr6)
2bab6f
-	     ret_err(_("bad IPv6 address"));
2bab6f
+	     goto_err(_("bad IPv6 address"));
2bab6f
 	}
2bab6f
       /* or names */
2bab6f
       else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING))
2bab6f
@@ -1237,7 +1329,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
2bab6f
 	      comma = split(cp);
2bab6f
 	      slash = split_chr(cp, '/');
2bab6f
 	      if (!inet_pton(AF_INET, cp, &in))
2bab6f
-		ret_err(_("bad IPv4 address"));
2bab6f
+		goto_err(_("bad IPv4 address"));
2bab6f
 	      if (!slash)
2bab6f
 		{
2bab6f
 		  memcpy(op, &in, INADDRSZ);
2bab6f
@@ -1282,8 +1374,8 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
2bab6f
 		  op += IN6ADDRSZ;
2bab6f
 		  continue;
2bab6f
 		}
2bab6f
-	  
2bab6f
-	      ret_err(_("bad IPv6 address"));
2bab6f
+
2bab6f
+	      goto_err(_("bad IPv6 address"));
2bab6f
 	    } 
2bab6f
 	  new->len = op - new->val;
2bab6f
 	}
2bab6f
@@ -1310,7 +1402,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
2bab6f
 		  if (strcmp (arg, ".") != 0)
2bab6f
 		    {
2bab6f
 		      if (!(dom = canonicalise_opt(arg)))
2bab6f
-			ret_err(_("bad domain in dhcp-option"));
2bab6f
+			goto_err(_("bad domain in dhcp-option"));
2bab6f
 			
2bab6f
 		      domlen = strlen(dom) + 2;
2bab6f
 		    }
2bab6f
@@ -1404,7 +1496,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
2bab6f
 		{
2bab6f
 		  char *dom = canonicalise_opt(arg);
2bab6f
 		  if (!dom)
2bab6f
-		    ret_err(_("bad domain in dhcp-option"));
2bab6f
+		    goto_err(_("bad domain in dhcp-option"));
2bab6f
 		    		  
2bab6f
 		  newp = opt_malloc(len + strlen(dom) + 2);
2bab6f
 		  
2bab6f
@@ -1442,14 +1534,14 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
2bab6f
       ((new->len > 255) || 
2bab6f
       (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) ||
2bab6f
        (new->len > 250 && (new->flags & DHOPT_RFC3925))))
2bab6f
-    ret_err(_("dhcp-option too long"));
2bab6f
+    goto_err(_("dhcp-option too long"));
2bab6f
   
2bab6f
   if (flags == DHOPT_MATCH)
2bab6f
     {
2bab6f
       if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
2bab6f
 	  !new->netid ||
2bab6f
 	  new->netid->next)
2bab6f
-	ret_err(_("illegal dhcp-match"));
2bab6f
+	goto_err(_("illegal dhcp-match"));
2bab6f
        
2bab6f
       if (is6)
2bab6f
 	{
2bab6f
@@ -1474,24 +1566,31 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
2bab6f
     }
2bab6f
     
2bab6f
   return 1;
2bab6f
+on_error:
2bab6f
+  dhcp_opt_free(new);
2bab6f
+  return 0;
2bab6f
 }
2bab6f
 
2bab6f
 #endif
2bab6f
 
2bab6f
 void set_option_bool(unsigned int opt)
2bab6f
 {
2bab6f
-  if (opt < 32)
2bab6f
-    daemon->options |= 1u << opt;
2bab6f
-  else
2bab6f
-    daemon->options2 |= 1u << (opt - 32);
2bab6f
+  option_var(opt) |= option_val(opt);
2bab6f
 }
2bab6f
 
2bab6f
 void reset_option_bool(unsigned int opt)
2bab6f
 {
2bab6f
-  if (opt < 32)
2bab6f
-    daemon->options &= ~(1u << opt);
2bab6f
-  else
2bab6f
-    daemon->options2 &= ~(1u << (opt - 32));
2bab6f
+  option_var(opt) &= ~(option_val(opt));
2bab6f
+}
2bab6f
+
2bab6f
+static void server_list_free(struct server *list)
2bab6f
+{
2bab6f
+  while (list)
2bab6f
+    {
2bab6f
+      struct server *tmp = list;
2bab6f
+      list = list->next;
2bab6f
+      free(tmp);
2bab6f
+    }
2bab6f
 }
2bab6f
 
2bab6f
 static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only)
2bab6f
@@ -1675,13 +1774,13 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	      /* has subnet+len */
2bab6f
 	      err = parse_mysockaddr(arg, &new->addr);
2bab6f
 	      if (err)
2bab6f
-		ret_err(err);
2bab6f
+		ret_err_free(err, new);
2bab6f
 	      if (!atoi_check(end, &new->mask))
2bab6f
-		ret_err(gen_err);
2bab6f
+		ret_err_free(gen_err, new);
2bab6f
 	      new->addr_used = 1;
2bab6f
 	    } 
2bab6f
 	  else if (!atoi_check(arg, &new->mask))
2bab6f
-	    ret_err(gen_err);
2bab6f
+	    ret_err_free(gen_err, new);
2bab6f
 	    
2bab6f
           daemon->add_subnet4 = new;
2bab6f
 
2bab6f
@@ -1693,15 +1792,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 		  /* has subnet+len */
2bab6f
                   err = parse_mysockaddr(comma, &new->addr);
2bab6f
                   if (err)
2bab6f
-                    ret_err(err);
2bab6f
+                    ret_err_free(err, new);
2bab6f
                   if (!atoi_check(end, &new->mask))
2bab6f
-                    ret_err(gen_err);
2bab6f
+                    ret_err_free(gen_err, new);
2bab6f
                   new->addr_used = 1;
2bab6f
                 }
2bab6f
               else
2bab6f
                 {
2bab6f
                   if (!atoi_check(comma, &new->mask))
2bab6f
-                    ret_err(gen_err);
2bab6f
+                    ret_err_free(gen_err, new);
2bab6f
                 }
2bab6f
           
2bab6f
 	      daemon->add_subnet6 = new;
2bab6f
@@ -1908,7 +2007,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 		  new->addr.sa.sa_family = AF_INET6;
2bab6f
 #endif
2bab6f
 		else
2bab6f
-		  ret_err(gen_err);
2bab6f
+		  {
2bab6f
+		    free(new->name);
2bab6f
+		    ret_err_free(gen_err, new);
2bab6f
+		  }
2bab6f
 	      } 
2bab6f
 	  }
2bab6f
 	new->next = daemon->authinterface;
2bab6f
@@ -2080,7 +2182,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 
2bab6f
 		      arg = split(netpart);
2bab6f
 		      if (!atoi_check(netpart, &msize))
2bab6f
-			ret_err(gen_err);
2bab6f
+			ret_err_free(gen_err, new);
2bab6f
 		      else if (inet_pton(AF_INET, comma, &new->start))
2bab6f
 			{
2bab6f
 			  int mask = (1 << (32 - msize)) - 1;
2bab6f
@@ -2093,18 +2195,18 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 				{
2bab6f
 				  if (!(new->prefix = canonicalise_opt(arg)) ||
2bab6f
 				      strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
2bab6f
-				    ret_err(_("bad prefix"));
2bab6f
+				    ret_err_free(_("bad prefix"), new);
2bab6f
 				}
2bab6f
 			      else if (strcmp(arg, "local") != 0 ||
2bab6f
 				       (msize != 8 && msize != 16 && msize != 24))
2bab6f
-				ret_err(gen_err);
2bab6f
+				ret_err_free(gen_err, new);
2bab6f
 			      else
2bab6f
 				{
2bab6f
 				   /* generate the equivalent of
2bab6f
 				      local=/xxx.yyy.zzz.in-addr.arpa/ */
2bab6f
 				  struct server *serv = add_rev4(new->start, msize);
2bab6f
 				  if (!serv)
2bab6f
-				    ret_err(_("bad prefix"));
2bab6f
+				    ret_err_free(_("bad prefix"), new);
2bab6f
 
2bab6f
 				  serv->flags |= SERV_NO_ADDR;
2bab6f
 
2bab6f
@@ -2134,17 +2236,17 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 			  setaddr6part(&new->end6, addrpart | mask);
2bab6f
 			  
2bab6f
 			  if (msize < 64)
2bab6f
-			    ret_err(gen_err);
2bab6f
+			    ret_err_free(gen_err, new);
2bab6f
 			  else if (arg)
2bab6f
 			    {
2bab6f
 			      if (option != 's')
2bab6f
 				{
2bab6f
 				  if (!(new->prefix = canonicalise_opt(arg)) ||
2bab6f
 				      strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
2bab6f
-				    ret_err(_("bad prefix"));
2bab6f
+				    ret_err_free(_("bad prefix"), new);
2bab6f
 				}	
2bab6f
 			      else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
2bab6f
-				ret_err(gen_err);
2bab6f
+				ret_err_free(gen_err, new);
2bab6f
 			      else 
2bab6f
 				{
2bab6f
 				  /* generate the equivalent of
2bab6f
@@ -2164,7 +2266,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 			}
2bab6f
 #endif
2bab6f
 		      else
2bab6f
-			ret_err(gen_err);
2bab6f
+			ret_err_free(gen_err, new);
2bab6f
 		    }
2bab6f
 		  else
2bab6f
 		    {
2bab6f
@@ -2178,7 +2280,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 			  if (!arg)
2bab6f
 			    new->end.s_addr = new->start.s_addr;
2bab6f
 			  else if (!inet_pton(AF_INET, arg, &new->end))
2bab6f
-			    ret_err(gen_err);
2bab6f
+			    ret_err_free(gen_err, new);
2bab6f
 			}
2bab6f
 #ifdef HAVE_IPV6
2bab6f
 		      else if (inet_pton(AF_INET6, comma, &new->start6))
2bab6f
@@ -2187,17 +2289,17 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 			  if (!arg)
2bab6f
 			    memcpy(&new->end6, &new->start6, IN6ADDRSZ);
2bab6f
 			  else if (!inet_pton(AF_INET6, arg, &new->end6))
2bab6f
-			    ret_err(gen_err);
2bab6f
+			    ret_err_free(gen_err, new);
2bab6f
 			}
2bab6f
 #endif
2bab6f
 		      else 
2bab6f
-			ret_err(gen_err);
2bab6f
+			ret_err_free(gen_err, new);
2bab6f
 
2bab6f
 		      if (option != 's' && prefstr)
2bab6f
 			{
2bab6f
 			  if (!(new->prefix = canonicalise_opt(prefstr)) ||
2bab6f
 			      strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
2bab6f
-			    ret_err(_("bad prefix"));
2bab6f
+			    ret_err_free(_("bad prefix"), new);
2bab6f
 			}
2bab6f
 		    }
2bab6f
 
2bab6f
@@ -2359,7 +2461,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	  }
2bab6f
 #endif
2bab6f
 	else
2bab6f
-	  ret_err(gen_err);
2bab6f
+	  ret_err_free(gen_err, new);
2bab6f
 
2bab6f
 	new->used = 0;
2bab6f
 	if (option == 'a')
2bab6f
@@ -2430,7 +2532,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	  {
2bab6f
 	    newlist->flags |= SERV_LITERAL_ADDRESS;
2bab6f
 	    if (!(newlist->flags & SERV_TYPE))
2bab6f
-	      ret_err(gen_err);
2bab6f
+	      {
2bab6f
+	        server_list_free(newlist);
2bab6f
+	        ret_err(gen_err);
2bab6f
+	      }
2bab6f
 	  }
2bab6f
 	else if (option == LOPT_NO_REBIND)
2bab6f
 	  newlist->flags |= SERV_NO_REBIND;
2bab6f
@@ -2451,7 +2556,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	  {
2bab6f
 	    char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
2bab6f
 	    if (err)
2bab6f
-	      ret_err(err);
2bab6f
+	      {
2bab6f
+	        server_list_free(newlist);
2bab6f
+	        ret_err(err);
2bab6f
+	      }
2bab6f
 	  }
2bab6f
 	
2bab6f
 	serv = newlist;
2bab6f
@@ -2801,21 +2909,19 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	      {
2bab6f
 		if (is_tag_prefix(arg))
2bab6f
 		  {
2bab6f
-		    struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid));
2bab6f
-		    tt->net = opt_string_alloc(arg+4);
2bab6f
-		    tt->next = new->filter;
2bab6f
 		    /* ignore empty tag */
2bab6f
-		    if (tt->net)
2bab6f
-		      new->filter = tt;
2bab6f
+		    if (arg[4])
2bab6f
+		      new->filter = dhcp_netid_create(arg+4, new->filter);
2bab6f
 		  }
2bab6f
 		else
2bab6f
 		  {
2bab6f
 		    if (new->netid.net)
2bab6f
-		      ret_err(_("only one tag allowed"));
2bab6f
-		    else if (strstr(arg, "set:") == arg)
2bab6f
-		      new->netid.net = opt_string_alloc(arg+4);
2bab6f
+		      {
2bab6f
+			dhcp_context_free(new);
2bab6f
+			ret_err(_("only one tag allowed"));
2bab6f
+		      }
2bab6f
 		    else
2bab6f
-		      new->netid.net = opt_string_alloc(arg);
2bab6f
+		      new->netid.net = opt_string_alloc(set_prefix(arg));
2bab6f
 		  }
2bab6f
 		arg = comma;
2bab6f
 	      }
2bab6f
@@ -2831,7 +2937,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	    break;
2bab6f
 	
2bab6f
 	if (k < 2)
2bab6f
-	  ret_err(_("bad dhcp-range"));
2bab6f
+	  {
2bab6f
+	    dhcp_context_free(new);
2bab6f
+	    ret_err(_("bad dhcp-range"));
2bab6f
+	  }
2bab6f
 	
2bab6f
 	if (inet_pton(AF_INET, a[0], &new->start))
2bab6f
 	  {
2bab6f
@@ -2843,7 +2952,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	    else if (strcmp(a[1], "proxy") == 0)
2bab6f
 	      new->flags |= CONTEXT_PROXY;
2bab6f
 	    else if (!inet_pton(AF_INET, a[1], &new->end))
2bab6f
-	      ret_err(_("bad dhcp-range"));
2bab6f
+	      {
2bab6f
+		dhcp_context_free(new);
2bab6f
+		ret_err(_("bad dhcp-range"));
2bab6f
+	      }
2bab6f
 	    
2bab6f
 	    if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
2bab6f
 	      {
2bab6f
@@ -2858,7 +2970,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 		new->flags |= CONTEXT_NETMASK;
2bab6f
 		leasepos = 3;
2bab6f
 		if (!is_same_net(new->start, new->end, new->netmask))
2bab6f
-		  ret_err(_("inconsistent DHCP range"));
2bab6f
+		  {
2bab6f
+		    dhcp_context_free(new);
2bab6f
+		    ret_err(_("inconsistent DHCP range"));
2bab6f
+		  }
2bab6f
 		
2bab6f
 	    
2bab6f
 		if (k >= 4 && strchr(a[3], '.') &&  
2bab6f
@@ -2872,6 +2987,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 #ifdef HAVE_DHCP6
2bab6f
 	else if (inet_pton(AF_INET6, a[0], &new->start6))
2bab6f
 	  {
2bab6f
+	    const char *err = NULL;
2bab6f
+
2bab6f
 	    new->flags |= CONTEXT_V6; 
2bab6f
 	    new->prefix = 64; /* default */
2bab6f
 	    new->end6 = new->start6;
2bab6f
@@ -2917,19 +3034,24 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 		  }
2bab6f
 	      }
2bab6f
 	    
2bab6f
-	    if (new->prefix != 64)
2bab6f
+	    if (new->prefix > 64)
2bab6f
 	      {
2bab6f
 		if (new->flags & CONTEXT_RA)
2bab6f
-		  ret_err(_("prefix length must be exactly 64 for RA subnets"));
2bab6f
+		  err=(_("prefix length must be exactly 64 for RA subnets"));
2bab6f
 		else if (new->flags & CONTEXT_TEMPLATE)
2bab6f
-		  ret_err(_("prefix length must be exactly 64 for subnet constructors"));
2bab6f
+		  err=(_("prefix length must be exactly 64 for subnet constructors"));
2bab6f
 	      }
2bab6f
-
2bab6f
-	    if (new->prefix < 64)
2bab6f
-	      ret_err(_("prefix length must be at least 64"));
2bab6f
+	    else if (new->prefix < 64)
2bab6f
+	      err=(_("prefix length must be at least 64"));
2bab6f
 	    
2bab6f
-	    if (!is_same_net6(&new->start6, &new->end6, new->prefix))
2bab6f
-	      ret_err(_("inconsistent DHCPv6 range"));
2bab6f
+	    if (!err && !is_same_net6(&new->start6, &new->end6, new->prefix))
2bab6f
+	      err=(_("inconsistent DHCPv6 range"));
2bab6f
+
2bab6f
+	    if (err)
2bab6f
+	      {
2bab6f
+		dhcp_context_free(new);
2bab6f
+		ret_err(err);
2bab6f
+	      }
2bab6f
 
2bab6f
 	    /* dhcp-range=:: enables DHCP stateless on any interface */
2bab6f
 	    if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE))
2bab6f
@@ -2940,7 +3062,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 		struct in6_addr zero;
2bab6f
 		memset(&zero, 0, sizeof(zero));
2bab6f
 		if (!is_same_net6(&zero, &new->start6, new->prefix))
2bab6f
-		  ret_err(_("prefix must be zero with \"constructor:\" argument"));
2bab6f
+		  {
2bab6f
+		    dhcp_context_free(new);
2bab6f
+		    ret_err(_("prefix must be zero with \"constructor:\" argument"));
2bab6f
+		  }
2bab6f
 	      }
2bab6f
 	    
2bab6f
 	    if (addr6part(&new->start6) > addr6part(&new->end6))
2bab6f
@@ -2952,12 +3077,18 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	  }
2bab6f
 #endif
2bab6f
 	else
2bab6f
-	  ret_err(_("bad dhcp-range"));
2bab6f
+	  {
2bab6f
+	    dhcp_context_free(new);
2bab6f
+	    ret_err(_("bad dhcp-range"));
2bab6f
+	  }
2bab6f
 	
2bab6f
 	if (leasepos < k)
2bab6f
 	  {
2bab6f
 	    if (leasepos != k-1)
2bab6f
-	      ret_err(_("bad dhcp-range"));
2bab6f
+	      {
2bab6f
+		dhcp_context_free(new);
2bab6f
+		ret_err(_("bad dhcp-range"));
2bab6f
+	      }
2bab6f
 	    
2bab6f
 	    if (strcmp(a[leasepos], "infinite") == 0)
2bab6f
 	      new->lease_time = 0xffffffff;
2bab6f
@@ -2996,7 +3127,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 			break;
2bab6f
 
2bab6f
 		    if (*cp || (leasepos+1 < k))
2bab6f
-		      ret_err(_("bad dhcp-range"));
2bab6f
+		      ret_err_free(_("bad dhcp-range"), new);
2bab6f
 		    
2bab6f
 		    new->lease_time = atoi(a[leasepos]) * fac;
2bab6f
 		    /* Leases of a minute or less confuse
2bab6f
@@ -3023,6 +3154,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
2bab6f
 	new->hwaddr = NULL;
2bab6f
 	new->netid = NULL;
2bab6f
+	new->clid = NULL;
2bab6f
 
2bab6f
 	if ((a[0] = arg))
2bab6f
 	  for (k = 1; k < 7; k++)
2bab6f
@@ -3053,7 +3185,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 			}
2bab6f
 
2bab6f
 		      if (len == -1)
2bab6f
-			ret_err(_("bad hex constant"));
2bab6f
+			{
2bab6f
+			  dhcp_config_free(new);
2bab6f
+			  ret_err(_("bad hex constant"));
2bab6f
+			}
2bab6f
 		      else if ((new->clid = opt_malloc(len)))
2bab6f
 			{
2bab6f
 			  new->flags |= CONFIG_CLID;
2bab6f
@@ -3065,17 +3200,17 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	      /* dhcp-host has strange backwards-compat needs. */
2bab6f
 	      else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
2bab6f
 		{
2bab6f
-		  struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
2bab6f
 		  struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
2bab6f
-		  newtag->net = opt_malloc(strlen(arg + 4) + 1);
2bab6f
 		  newlist->next = new->netid;
2bab6f
 		  new->netid = newlist;
2bab6f
-		  newlist->list = newtag;
2bab6f
-		  strcpy(newtag->net, arg+4);
2bab6f
-		  unhide_metas(newtag->net);
2bab6f
+		  newlist->list = dhcp_netid_create(arg+4, NULL);
2bab6f
 		}
2bab6f
 	      else if (strstr(arg, "tag:") == arg)
2bab6f
-		ret_err(_("cannot match tags in --dhcp-host"));
2bab6f
+		{
2bab6f
+		  
2bab6f
+		  dhcp_config_free(new);
2bab6f
+		  ret_err(_("cannot match tags in --dhcp-host"));
2bab6f
+		}
2bab6f
 #ifdef HAVE_DHCP6
2bab6f
 	      else if (arg[0] == '[' && arg[strlen(arg)-1] == ']')
2bab6f
 		{
2bab6f
@@ -3083,7 +3218,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 		  arg++;
2bab6f
 		  
2bab6f
 		  if (!inet_pton(AF_INET6, arg, &new->addr6))
2bab6f
-		    ret_err(_("bad IPv6 address"));
2bab6f
+		    {
2bab6f
+		      dhcp_config_free(new);
2bab6f
+		      ret_err(_("bad IPv6 address"));
2bab6f
+		    }
2bab6f
 
2bab6f
 		  for (i= 0; i < 8; i++)
2bab6f
 		    if (new->addr6.s6_addr[i] != 0)
2bab6f
@@ -3101,10 +3239,13 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 		  struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
2bab6f
 		  if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX, 
2bab6f
 						     &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1)
2bab6f
-		    ret_err(_("bad hex constant"));
2bab6f
+		    {
2bab6f
+		      free(newhw);
2bab6f
+		      dhcp_config_free(new);
2bab6f
+		      ret_err(_("bad hex constant"));
2bab6f
+		    }
2bab6f
 		  else
2bab6f
 		    {
2bab6f
-		      
2bab6f
 		      newhw->next = new->hwaddr;
2bab6f
 		      new->hwaddr = newhw;
2bab6f
 		    }		    
2bab6f
@@ -3181,7 +3322,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 		    {
2bab6f
 		      if (!(new->hostname = canonicalise_opt(a[j])) ||
2bab6f
 			  !legal_hostname(new->hostname))
2bab6f
-			ret_err(_("bad DHCP host name"));
2bab6f
+			{
2bab6f
+			  dhcp_config_free(new);
2bab6f
+			  ret_err(_("bad DHCP host name"));
2bab6f
+			}
2bab6f
 		     
2bab6f
 		      new->flags |= CONFIG_NAME;
2bab6f
 		      new->domain = strip_hostname(new->hostname);			
2bab6f
@@ -3234,10 +3378,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	      }
2bab6f
 	    else
2bab6f
 	      {
2bab6f
-		struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
2bab6f
-		newtag->net = opt_malloc(len - 3);
2bab6f
-		strcpy(newtag->net, arg+4);
2bab6f
-		unhide_metas(newtag->net);
2bab6f
+		struct dhcp_netid *newtag = dhcp_netid_create(arg+4, NULL);
2bab6f
 
2bab6f
 		if (strstr(arg, "set:") == arg)
2bab6f
 		  {
2bab6f
@@ -3254,7 +3395,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 		else 
2bab6f
 		  {
2bab6f
 		    new->set = NULL;
2bab6f
-		    free(newtag);
2bab6f
+		    dhcp_netid_free(newtag);
2bab6f
 		    break;
2bab6f
 		  }
2bab6f
 	      }
2bab6f
@@ -3263,7 +3404,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	  }
2bab6f
 
2bab6f
 	if (!new->set)
2bab6f
-	  ret_err(_("bad tag-if"));
2bab6f
+	  {
2bab6f
+	    dhcp_netid_free(new->tag);
2bab6f
+	    dhcp_netid_list_free(new->set);
2bab6f
+	    ret_err_free(_("bad tag-if"), new);
2bab6f
+	  }
2bab6f
 	  
2bab6f
 	break;
2bab6f
       }
2bab6f
@@ -3280,19 +3425,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
      
2bab6f
     case 'M': /* --dhcp-boot */
2bab6f
       {
2bab6f
-	struct dhcp_netid *id = NULL;
2bab6f
-	while (is_tag_prefix(arg))
2bab6f
-	  {
2bab6f
-	    struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
2bab6f
-	    newid->next = id;
2bab6f
-	    id = newid;
2bab6f
-	    comma = split(arg);
2bab6f
-	    newid->net = opt_string_alloc(arg+4);
2bab6f
-	    arg = comma;
2bab6f
-	  };
2bab6f
+	struct dhcp_netid *id = dhcp_tags(&arg;;
2bab6f
 	
2bab6f
 	if (!arg)
2bab6f
-	  ret_err(gen_err);
2bab6f
+	  {
2bab6f
+	    ret_err(gen_err);
2bab6f
+	  }
2bab6f
 	else 
2bab6f
 	  {
2bab6f
 	    char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
2bab6f
@@ -3338,19 +3476,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 
2bab6f
     case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */
2bab6f
       {
2bab6f
-	struct dhcp_netid *id = NULL;
2bab6f
-	while (is_tag_prefix(arg))
2bab6f
-	  {
2bab6f
-	    struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
2bab6f
-	    newid->next = id;
2bab6f
-	    id = newid;
2bab6f
-	    comma = split(arg);
2bab6f
-	    newid->net = opt_string_alloc(arg+4);
2bab6f
-	    arg = comma;
2bab6f
-	  };
2bab6f
+	struct dhcp_netid *id = dhcp_tags(&arg;;
2bab6f
 	
2bab6f
 	if (!arg)
2bab6f
-	  ret_err(gen_err);
2bab6f
+	  {
2bab6f
+	    ret_err(gen_err);
2bab6f
+	  }
2bab6f
 	else
2bab6f
 	  {
2bab6f
 	    struct delay_config *new;
2bab6f
@@ -3375,19 +3506,13 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	 
2bab6f
 	 new->netid = NULL;
2bab6f
 	 new->opt = 10; /* PXE_MENU_PROMPT */
2bab6f
-
2bab6f
-	 while (is_tag_prefix(arg))
2bab6f
-	  {
2bab6f
-	     struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
2bab6f
-	     comma = split(arg);
2bab6f
-	     nn->next = new->netid;
2bab6f
-	     new->netid = nn;
2bab6f
-	     nn->net = opt_string_alloc(arg+4);
2bab6f
-	     arg = comma;
2bab6f
-	   }
2bab6f
+	 new->netid = dhcp_tags(&arg;;
2bab6f
 	 
2bab6f
 	 if (!arg)
2bab6f
-	   ret_err(gen_err);
2bab6f
+	   {
2bab6f
+	     dhcp_opt_free(new);
2bab6f
+	     ret_err(gen_err);
2bab6f
+	   }
2bab6f
 	 else
2bab6f
 	   {
2bab6f
 	     comma = split(arg);
2bab6f
@@ -3423,17 +3548,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	 new->netid = NULL;
2bab6f
 	 new->sname = NULL;
2bab6f
 	 new->server.s_addr = 0;
2bab6f
+	 new->netid = dhcp_tags(&arg;;
2bab6f
 
2bab6f
-	 while (is_tag_prefix(arg))
2bab6f
-	   {
2bab6f
-	     struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
2bab6f
-	     comma = split(arg);
2bab6f
-	     nn->next = new->netid;
2bab6f
-	     new->netid = nn;
2bab6f
-	     nn->net = opt_string_alloc(arg+4);
2bab6f
-	     arg = comma;
2bab6f
-	   }
2bab6f
-       
2bab6f
 	 if (arg && (comma = split(arg)))
2bab6f
 	   {
2bab6f
 	     for (i = 0; CSA[i]; i++)
2bab6f
@@ -3510,7 +3626,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	    unhide_metas(comma);
2bab6f
 	    new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
2bab6f
 	    if (new->hwaddr_len == -1)
2bab6f
-	      ret_err(gen_err);
2bab6f
+	      {
2bab6f
+		free(new->netid.net);
2bab6f
+		ret_err_free(gen_err, new);
2bab6f
+	      }
2bab6f
 	    else
2bab6f
 	      {
2bab6f
 		new->next = daemon->dhcp_macs;
2bab6f
@@ -3527,7 +3646,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	
2bab6f
 	if (!(comma = split(arg)) ||
2bab6f
 	    !atoi_check16(comma, &new->class))
2bab6f
-	  ret_err(gen_err);
2bab6f
+	  ret_err_free(gen_err, new);
2bab6f
 	
2bab6f
 	new->tag.net = opt_string_alloc(set_prefix(arg));
2bab6f
 	new->next = daemon->prefix_classes;
2bab6f
@@ -3549,7 +3668,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	 struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
2bab6f
 	 
2bab6f
 	 if (!(comma = split(arg)))
2bab6f
-	   ret_err(gen_err);
2bab6f
+	   ret_err_free(gen_err, new);
2bab6f
 	
2bab6f
 	 new->netid.net = opt_string_alloc(set_prefix(arg));
2bab6f
 	 /* check for hex string - must digits may include : must not have nothing else, 
2bab6f
@@ -3559,7 +3678,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	 if ((comma = split(arg)))
2bab6f
 	   {
2bab6f
 	     if (option  != 'U' || strstr(arg, "enterprise:") != arg)
2bab6f
-	       ret_err(gen_err);
2bab6f
+	       {
2bab6f
+	         free(new->netid.net);
2bab6f
+	         ret_err_free(gen_err, new);
2bab6f
+	       }
2bab6f
 	     else
2bab6f
 	       new->enterprise = atoi(arg+11);
2bab6f
 	   }
2bab6f
@@ -3661,14 +3783,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	  }
2bab6f
 	
2bab6f
 	while (arg) {
2bab6f
-	  struct dhcp_netid *member = opt_malloc(sizeof(struct dhcp_netid));
2bab6f
 	  comma = split(arg);
2bab6f
-	  member->next = list;
2bab6f
-	  list = member;
2bab6f
-	  if (is_tag_prefix(arg))
2bab6f
-	    member->net = opt_string_alloc(arg+4);
2bab6f
-	  else
2bab6f
-	    member->net = opt_string_alloc(arg);
2bab6f
+	  list = dhcp_netid_create(is_tag_prefix(arg) ? arg+4 :arg, list);
2bab6f
 	  arg = comma;
2bab6f
 	}
2bab6f
 	
2bab6f
@@ -3682,7 +3798,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	struct addr_list *new = opt_malloc(sizeof(struct addr_list));
2bab6f
 	comma = split(arg);
2bab6f
 	if (!(inet_pton(AF_INET, arg, &new->addr) > 0))
2bab6f
-	  ret_err(_("bad dhcp-proxy address"));
2bab6f
+	  ret_err_free(_("bad dhcp-proxy address"), new);
2bab6f
 	new->next = daemon->override_relays;
2bab6f
 	daemon->override_relays = new;
2bab6f
 	arg = comma;
2bab6f
@@ -3708,7 +3824,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	  }
2bab6f
 #endif
2bab6f
 	else
2bab6f
-	  ret_err(_("Bad dhcp-relay"));
2bab6f
+	  {
2bab6f
+	    free(new->interface);
2bab6f
+	    ret_err_free(_("Bad dhcp-relay"), new);
2bab6f
+	  }
2bab6f
 	
2bab6f
 	break;
2bab6f
       }
2bab6f
@@ -3748,8 +3867,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
2bab6f
 	   arg = split(comma);
2bab6f
 	   if (!atoi_check(comma, &new->interval) || 
2bab6f
 	      (arg && !atoi_check(arg, &new->lifetime)))
2bab6f
+             {
2bab6f
 err:
2bab6f
-	    ret_err(_("bad RA-params"));
2bab6f
+	       free(new->name);
2bab6f
+	       ret_err_free(_("bad RA-params"), new);
2bab6f
+             }
2bab6f
 	  
2bab6f
 	  new->next = daemon->ra_interfaces;
2bab6f
 	  daemon->ra_interfaces = new;
2bab6f
@@ -3800,7 +3922,7 @@ err:
2bab6f
 	    (!(inet_pton(AF_INET, dash, &new->end) > 0) ||
2bab6f
 	     !is_same_net(new->in, new->end, new->mask) ||
2bab6f
 	     ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
2bab6f
-	  ret_err(_("invalid alias range"));
2bab6f
+	  ret_err_free(_("invalid alias range"), new);
2bab6f
 	
2bab6f
 	break;
2bab6f
       }
2bab6f
@@ -3835,7 +3957,7 @@ err:
2bab6f
 	      new->family = AF_INET6;
2bab6f
 #endif
2bab6f
 	    else
2bab6f
-	      ret_err(gen_err);
2bab6f
+	      ret_err_free(gen_err, new);
2bab6f
 	  } 
2bab6f
 	new->intr = opt_string_alloc(comma);
2bab6f
 	break;
2bab6f
@@ -3867,11 +3989,19 @@ err:
2bab6f
 	    alias = canonicalise_opt(arg);
2bab6f
 
2bab6f
 	    if (!alias || !target)
2bab6f
-	      ret_err(_("bad CNAME"));
2bab6f
+	      {
2bab6f
+		free(target);
2bab6f
+		free(alias);
2bab6f
+		ret_err(_("bad CNAME"));
2bab6f
+	      }
2bab6f
 	    
2bab6f
 	    for (new = daemon->cnames; new; new = new->next)
2bab6f
 	      if (hostname_isequal(new->alias, alias))
2bab6f
-		ret_err(_("duplicate CNAME"));
2bab6f
+		{
2bab6f
+		  free(target);
2bab6f
+		  free(alias);
2bab6f
+		  ret_err(_("duplicate CNAME"));
2bab6f
+		}
2bab6f
 	    new = opt_malloc(sizeof(struct cname));
2bab6f
 	    new->next = daemon->cnames;
2bab6f
 	    daemon->cnames = new;
2bab6f
@@ -3894,7 +4024,11 @@ err:
2bab6f
 	
2bab6f
 	if (!(dom = canonicalise_opt(arg)) ||
2bab6f
 	    (comma && !(target = canonicalise_opt(comma))))
2bab6f
-	  ret_err(_("bad PTR record"));
2bab6f
+	  {
2bab6f
+	    free(dom);
2bab6f
+	    free(target);
2bab6f
+	    ret_err(_("bad PTR record"));
2bab6f
+	  }
2bab6f
 	else
2bab6f
 	  {
2bab6f
 	    new = opt_malloc(sizeof(struct ptr_record));
2bab6f
@@ -3912,7 +4046,7 @@ err:
2bab6f
 	int k = 0;
2bab6f
 	struct naptr *new;
2bab6f
 	int order, pref;
2bab6f
-	char *name, *replace = NULL;
2bab6f
+	char *name=NULL, *replace = NULL;
2bab6f
 
2bab6f
 	if ((a[0] = arg))
2bab6f
 	  for (k = 1; k < 7; k++)
2bab6f
@@ -3925,7 +4059,11 @@ err:
2bab6f
 	    !atoi_check16(a[1], &order) || 
2bab6f
 	    !atoi_check16(a[2], &pref) ||
2bab6f
 	    (k == 7 && !(replace = canonicalise_opt(a[6]))))
2bab6f
-	  ret_err(_("bad NAPTR record"));
2bab6f
+          {
2bab6f
+	    free(name);
2bab6f
+	    free(replace);
2bab6f
+	    ret_err(_("bad NAPTR record"));
2bab6f
+          }
2bab6f
 	else
2bab6f
 	  {
2bab6f
 	    new = opt_malloc(sizeof(struct naptr));
2bab6f
@@ -3947,22 +4085,26 @@ err:
2bab6f
        	struct txt_record *new;
2bab6f
 	size_t len = 0;
2bab6f
 	char *data;
2bab6f
-	int val;
2bab6f
+	int class;
2bab6f
 
2bab6f
 	comma = split(arg);
2bab6f
 	data = split(comma);
2bab6f
 		
2bab6f
 	new = opt_malloc(sizeof(struct txt_record));
2bab6f
-	new->next = daemon->rr;
2bab6f
-	daemon->rr = new;
2bab6f
+	new->name = NULL;
2bab6f
 	
2bab6f
-	if (!atoi_check(comma, &val) || 
2bab6f
+	if (!atoi_check(comma, &class) || 
2bab6f
 	    !(new->name = canonicalise_opt(arg)) ||
2bab6f
 	    (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U))
2bab6f
-	  ret_err(_("bad RR record"));
2bab6f
-	   	
2bab6f
-	new->class = val;
2bab6f
+          {
2bab6f
+            free(new->name);
2bab6f
+	    ret_err_free(_("bad RR record"), new);
2bab6f
+          }
2bab6f
+
2bab6f
 	new->len = 0;
2bab6f
+	new->class = class;
2bab6f
+	new->next = daemon->rr;
2bab6f
+	daemon->rr = new;
2bab6f
 	
2bab6f
 	if (data)
2bab6f
 	  {
2bab6f
@@ -3983,14 +4125,14 @@ err:
2bab6f
 	comma = split(arg);
2bab6f
 		
2bab6f
 	new = opt_malloc(sizeof(struct txt_record));
2bab6f
-	new->next = daemon->txt;
2bab6f
-	daemon->txt = new;
2bab6f
 	new->class = C_IN;
2bab6f
 	new->stat = 0;
2bab6f
 
2bab6f
 	if (!(new->name = canonicalise_opt(arg)))
2bab6f
-	  ret_err(_("bad TXT record"));
2bab6f
+	  ret_err_free(_("bad TXT record"), new);
2bab6f
 	
2bab6f
+	new->next = daemon->txt;
2bab6f
+	daemon->txt = new;
2bab6f
 	len = comma ? strlen(comma) : 0;
2bab6f
 	len += (len/255) + 1; /* room for extra counts */
2bab6f
 	new->txt = p = opt_malloc(len);
2bab6f
@@ -4037,24 +4179,32 @@ err:
2bab6f
 	    arg = comma;
2bab6f
 	    comma = split(arg);
2bab6f
 	    if (!(target = canonicalise_opt(arg)))
2bab6f
-	      ret_err(_("bad SRV target"));
2bab6f
+	      ret_err_free(_("bad SRV target"), name);
2bab6f
 		
2bab6f
 	    if (comma)
2bab6f
 	      {
2bab6f
 		arg = comma;
2bab6f
 		comma = split(arg);
2bab6f
 		if (!atoi_check16(arg, &port))
2bab6f
-		  ret_err(_("invalid port number"));
2bab6f
+                  {
2bab6f
+                    free(name);
2bab6f
+		    ret_err_free(_("invalid port number"), target);
2bab6f
+                  }
2bab6f
 		
2bab6f
 		if (comma)
2bab6f
 		  {
2bab6f
 		    arg = comma;
2bab6f
 		    comma = split(arg);
2bab6f
 		    if (!atoi_check16(arg, &priority))
2bab6f
-		      ret_err(_("invalid priority"));
2bab6f
-			
2bab6f
+                      {
2bab6f
+                        free(name);
2bab6f
+		        ret_err_free(_("invalid priority"), target);
2bab6f
+		      }
2bab6f
 		    if (comma && !atoi_check16(comma, &weight))
2bab6f
-		      ret_err(_("invalid weight"));
2bab6f
+                      {
2bab6f
+                        free(name);
2bab6f
+		        ret_err_free(_("invalid weight"), target);
2bab6f
+                      }
2bab6f
 		  }
2bab6f
 	      }
2bab6f
 	  }
2bab6f
@@ -4073,13 +4223,15 @@ err:
2bab6f
       
2bab6f
     case LOPT_HOST_REC: /* --host-record */
2bab6f
       {
2bab6f
-	struct host_record *new = opt_malloc(sizeof(struct host_record));
2bab6f
-	memset(new, 0, sizeof(struct host_record));
2bab6f
-	new->ttl = -1;
2bab6f
+	struct host_record *new;
2bab6f
 
2bab6f
 	if (!arg || !(comma = split(arg)))
2bab6f
 	  ret_err(_("Bad host-record"));
2bab6f
 	
2bab6f
+	new = opt_malloc(sizeof(struct host_record));
2bab6f
+	memset(new, 0, sizeof(struct host_record));
2bab6f
+	new->ttl = -1;
2bab6f
+
2bab6f
 	while (arg)
2bab6f
 	  {
2bab6f
 	    struct all_addr addr;
2bab6f
@@ -4100,10 +4252,19 @@ err:
2bab6f
 	      {
2bab6f
 		int nomem;
2bab6f
 		char *canon = canonicalise(arg, &nomem);
2bab6f
-		struct name_list *nl = opt_malloc(sizeof(struct name_list));
2bab6f
+		struct name_list *nl;
2bab6f
 		if (!canon)
2bab6f
-		  ret_err(_("Bad name in host-record"));
2bab6f
+                  {
2bab6f
+		    struct name_list *tmp = new->names, *next;
2bab6f
+		    for (tmp = new->names; tmp; tmp = next)
2bab6f
+		      {
2bab6f
+			next = tmp->next;
2bab6f
+			free(tmp);
2bab6f
+		      }
2bab6f
+		    ret_err_free(_("Bad name in host-record"), new);
2bab6f
+                  }
2bab6f
 
2bab6f
+		nl = opt_malloc(sizeof(struct name_list));
2bab6f
 		nl->name = canon;
2bab6f
 		/* keep order, so that PTR record goes to first name */
2bab6f
 		nl->next = NULL;
2bab6f
@@ -4143,6 +4304,7 @@ err:
2bab6f
 	int len;
2bab6f
 	
2bab6f
 	new->class = C_IN;
2bab6f
+	new->name = NULL;
2bab6f
 
2bab6f
 	if ((comma = split(arg)) && (algo = split(comma)))
2bab6f
 	  {
2bab6f
@@ -4167,7 +4329,7 @@ err:
2bab6f
 	    !atoi_check8(algo, &new->algo) ||
2bab6f
 	    !atoi_check8(digest, &new->digest_type) ||
2bab6f
 	    !(new->name = canonicalise_opt(arg)))
2bab6f
-	  ret_err(_("bad trust anchor"));
2bab6f
+	  ret_err_free(_("bad trust anchor"), new);
2bab6f
 	    
2bab6f
 	/* Upper bound on length */
2bab6f
 	len = (2*strlen(keyhex))+1;
2bab6f
@@ -4181,7 +4343,10 @@ err:
2bab6f
 	  else
2bab6f
 	    cp++;
2bab6f
 	if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
2bab6f
-	  ret_err(_("bad HEX in trust anchor"));
2bab6f
+	  {
2bab6f
+	    free(new->name);
2bab6f
+	    ret_err_free(_("bad HEX in trust anchor"), new);
2bab6f
+	  }
2bab6f
 	
2bab6f
 	new->next = daemon->ds;
2bab6f
 	daemon->ds = new;
2bab6f
@@ -4650,8 +4815,8 @@ void read_opts(int argc, char **argv, char *compile_opts)
2bab6f
   size_t argbuf_size = MAXDNAME;
2bab6f
   char *argbuf = opt_malloc(argbuf_size);
2bab6f
   char *buff = opt_malloc(MAXDNAME);
2bab6f
-  int option, conffile_opt = '7', testmode = 0;
2bab6f
-  char *arg, *conffile = CONFFILE;
2bab6f
+  int option, testmode = 0;
2bab6f
+  char *arg, *conffile = NULL;
2bab6f
       
2bab6f
   opterr = 0;
2bab6f
 
2bab6f
@@ -4725,8 +4890,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
2bab6f
 	      argbuf_size = strlen(optarg) + 1;
2bab6f
 	      argbuf = opt_malloc(argbuf_size);
2bab6f
 	    }
2bab6f
-	  strncpy(argbuf, optarg, argbuf_size);
2bab6f
-	  argbuf[argbuf_size-1] = 0;
2bab6f
+	  safe_strncpy(argbuf, optarg, argbuf_size);
2bab6f
 	  arg = argbuf;
2bab6f
 	}
2bab6f
       else
2bab6f
@@ -4761,7 +4925,8 @@ void read_opts(int argc, char **argv, char *compile_opts)
2bab6f
         }
2bab6f
       else if (option == 'C')
2bab6f
 	{
2bab6f
-	  conffile_opt = 0; /* file must exist */
2bab6f
+          if (conffile)
2bab6f
+            free(conffile);
2bab6f
 	  conffile = opt_string_alloc(arg);
2bab6f
 	}
2bab6f
       else
2bab6f
@@ -4779,10 +4944,11 @@ void read_opts(int argc, char **argv, char *compile_opts)
2bab6f
 
2bab6f
   if (conffile)
2bab6f
     {
2bab6f
-      one_file(conffile, conffile_opt);
2bab6f
-      if (conffile_opt == 0)
2bab6f
-	free(conffile);
2bab6f
+      one_file(conffile, 0);
2bab6f
+      free(conffile);
2bab6f
     }
2bab6f
+  else
2bab6f
+    one_file(CONFFILE, '7');
2bab6f
 
2bab6f
   /* port might not be known when the address is parsed - fill in here */
2bab6f
   if (daemon->servers)
2bab6f
diff --git a/src/rfc2131.c b/src/rfc2131.c
2bab6f
index c08a8ab..997575a 100644
2bab6f
--- a/src/rfc2131.c
2bab6f
+++ b/src/rfc2131.c
2bab6f
@@ -917,7 +917,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
2bab6f
 			mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
2bab6f
 		      
2bab6f
 		      if (boot->file)
2bab6f
-			strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
2bab6f
+			safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
2bab6f
 		    }
2bab6f
 		  
2bab6f
 		  option_put(mess, end, OPTION_MESSAGE_TYPE, 1, 
2bab6f
@@ -2296,7 +2296,7 @@ static void do_options(struct dhcp_context *context,
2bab6f
 	      in_list(req_options, OPTION_SNAME))
2bab6f
 	    option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
2bab6f
 	  else
2bab6f
-	    strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1);
2bab6f
+	    safe_strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname));
2bab6f
 	}
2bab6f
       
2bab6f
       if (boot->file)
2bab6f
@@ -2306,7 +2306,7 @@ static void do_options(struct dhcp_context *context,
2bab6f
 	      in_list(req_options, OPTION_FILENAME))
2bab6f
 	    option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
2bab6f
 	  else
2bab6f
-	    strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
2bab6f
+	    safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
2bab6f
 	}
2bab6f
       
2bab6f
       if (boot->next_server.s_addr) 
2bab6f
@@ -2323,14 +2323,14 @@ static void do_options(struct dhcp_context *context,
2bab6f
       if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
2bab6f
 	  (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
2bab6f
 	{
2bab6f
-	  strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
2bab6f
+	  safe_strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file));
2bab6f
 	  done_file = 1;
2bab6f
 	}
2bab6f
       
2bab6f
       if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
2bab6f
 	  (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
2bab6f
 	{
2bab6f
-	  strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
2bab6f
+	  safe_strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname));
2bab6f
 	  done_server = 1;
2bab6f
 	}
2bab6f
       
2bab6f
diff --git a/src/tftp.c b/src/tftp.c
2bab6f
index bccca69..f2eccbc 100644
2bab6f
--- a/src/tftp.c
2bab6f
+++ b/src/tftp.c
2bab6f
@@ -234,7 +234,7 @@ void tftp_request(struct listener *listen, time_t now)
2bab6f
 #endif
2bab6f
 	}
2bab6f
 
2bab6f
-      strncpy(ifr.ifr_name, name, IF_NAMESIZE);
2bab6f
+      safe_strncpy(ifr.ifr_name, name, IF_NAMESIZE);
2bab6f
       if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
2bab6f
 	{
2bab6f
 	  mtu = ifr.ifr_mtu;  
2bab6f
diff --git a/src/util.c b/src/util.c
2bab6f
index 532bc16..e003e3c 100644
2bab6f
--- a/src/util.c
2bab6f
+++ b/src/util.c
2bab6f
@@ -281,7 +281,17 @@ void *safe_malloc(size_t size)
2bab6f
     die(_("could not get memory"), NULL, EC_NOMEM);
2bab6f
       
2bab6f
   return ret;
2bab6f
-}    
2bab6f
+}
2bab6f
+
2bab6f
+/* can be replaced by (void)strlcpy() on some platforms */
2bab6f
+void safe_strncpy(char *dest, const char *src, size_t size)
2bab6f
+{
2bab6f
+  if (size)
2bab6f
+    {
2bab6f
+      dest[size-1] = '\0';
2bab6f
+      strncpy(dest, src, size-1);
2bab6f
+    }
2bab6f
+}
2bab6f
 
2bab6f
 void safe_pipe(int *fd, int read_noblock)
2bab6f
 {
2bab6f
-- 
2bab6f
2.20.1
2bab6f