Blame SOURCES/dnsmasq-2.79-rh1602477.patch

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