diff --git a/SOURCES/dnsmasq-2.76-rh1728698-1.patch b/SOURCES/dnsmasq-2.76-rh1728698-1.patch new file mode 100644 index 0000000..2546301 --- /dev/null +++ b/SOURCES/dnsmasq-2.76-rh1728698-1.patch @@ -0,0 +1,52 @@ +From cae343c1f3bea9d1ca2e71d3709d3f02b799f94d Mon Sep 17 00:00:00 2001 +From: Petr Mensik +Date: Thu, 4 Jul 2019 20:28:08 +0200 +Subject: [PATCH 1/5] Log listening on new interfaces + +Log in debug mode listening on interfaces. They can be dynamically +found, include interface number, since it is checked on TCP connections. +Print also addresses found on them. +--- + src/network.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/src/network.c b/src/network.c +index d75f560..fd90288 100644 +--- a/src/network.c ++++ b/src/network.c +@@ -662,6 +662,13 @@ int enumerate_interfaces(int reset) + else + { + *up = l->next; ++ if (l->iface->done) ++ { ++ iface = l->iface; ++ (void)prettyprint_addr(&iface->addr, daemon->addrbuff); ++ my_syslog(LOG_DEBUG, _("stopped listening on %s(#%d): %s"), ++ iface->name, iface->index, daemon->addrbuff); ++ } + + /* In case it ever returns */ + l->iface->done = 0; +@@ -978,6 +985,9 @@ void create_bound_listeners(int dienow) + new->next = daemon->listeners; + daemon->listeners = new; + iface->done = 1; ++ (void)prettyprint_addr(&iface->addr, daemon->addrbuff); ++ my_syslog(LOG_DEBUG, _("listening on %s(#%d): %s"), ++ iface->name, iface->index, daemon->addrbuff); + } + + /* Check for --listen-address options that haven't been used because there's +@@ -997,6 +1007,8 @@ void create_bound_listeners(int dienow) + { + new->next = daemon->listeners; + daemon->listeners = new; ++ (void)prettyprint_addr(&if_tmp->addr, daemon->addrbuff); ++ my_syslog(LOG_DEBUG, _("listening on %s"), daemon->addrbuff); + } + } + +-- +2.20.1 + diff --git a/SOURCES/dnsmasq-2.76-rh1728698-3.patch b/SOURCES/dnsmasq-2.76-rh1728698-3.patch new file mode 100644 index 0000000..cdb690d --- /dev/null +++ b/SOURCES/dnsmasq-2.76-rh1728698-3.patch @@ -0,0 +1,74 @@ +From 527029312cbe37c0285240943ad02352d64d403d Mon Sep 17 00:00:00 2001 +From: Petr Mensik +Date: Tue, 9 Jul 2019 14:05:59 +0200 +Subject: [PATCH 3/5] Cleanup interfaces no longer available + +Clean addresses and interfaces not found after enumerate. Free unused +records to speed up checking active interfaces and reduce used memory. +--- + src/network.c | 32 ++++++++++++++++++++++++++++++-- + 1 file changed, 30 insertions(+), 2 deletions(-) + +diff --git a/src/network.c b/src/network.c +index f247811..d6d4b01 100644 +--- a/src/network.c ++++ b/src/network.c +@@ -553,7 +553,30 @@ static int iface_allowed_v4(struct in_addr local, int if_index, char *label, + + return iface_allowed((struct iface_param *)vparam, if_index, label, &addr, netmask, prefix, 0); + } +- ++ ++/* ++ * Clean old interfaces no longer found. ++ */ ++static void clean_interfaces() ++{ ++ struct irec *iface; ++ struct irec **up = &daemon->interfaces; ++ ++ for (iface = *up; iface; iface = *up) ++ { ++ if (!iface->found && !iface->done) ++ { ++ *up = iface->next; ++ free(iface->name); ++ free(iface); ++ } ++ else ++ { ++ up = &iface->next; ++ } ++ } ++} ++ + int enumerate_interfaces(int reset) + { + static struct addrlist *spare = NULL; +@@ -653,6 +676,7 @@ int enumerate_interfaces(int reset) + in OPT_CLEVERBIND mode, that at listener will just disappear after + a call to enumerate_interfaces, this is checked OK on all calls. */ + struct listener *l, *tmp, **up; ++ int freed = 0; + + for (up = &daemon->listeners, l = daemon->listeners; l; l = tmp) + { +@@ -682,10 +706,14 @@ int enumerate_interfaces(int reset) + close(l->tftpfd); + + free(l); ++ freed = 1; + } + } ++ ++ if (freed) ++ clean_interfaces(); + } +- ++ + errno = errsave; + spare = param.spare; + +-- +2.20.1 + diff --git a/SOURCES/dnsmasq-2.76-rh1752569.patch b/SOURCES/dnsmasq-2.76-rh1752569.patch new file mode 100644 index 0000000..b46fcd3 --- /dev/null +++ b/SOURCES/dnsmasq-2.76-rh1752569.patch @@ -0,0 +1,75 @@ +From 3d27384fc5f2a437b7bce128c8ba62e8d6e12df7 Mon Sep 17 00:00:00 2001 +From: Brian Haley +Date: Wed, 28 Aug 2019 16:13:23 -0400 +Subject: [PATCH] Change dhcp_release to use default address when no IP subnet + matches + +Currently, dhcp_release will only send a 'fake' release +when the address given is in the same subnet as an IP +on the interface that was given. + +This doesn't work in an environment where dnsmasq is +managing leases for remote subnets via a DHCP relay, as +running dhcp_release locally will just cause it to +silently exit without doing anything, leaving the lease +in the database. + +Change it to use the default IP on the interface, as the +dnsmasq source code at src/dhcp.c does, if no matching subnet +IP is found, as a fall-back. This fixes an issue we are +seeing in certain Openstack deployments where we are using +dnsmasq to provision baremetal systems in a datacenter. + +While using Dbus might have seemed like an obvious solution, +because of our extensive use of network namespaces (which +Dbus doesn't support), this seemed like a better solution +than creating system.d policy files for each dnsmasq we +might spawn and using --enable-dbus=$id in order to isolate +messages to specific dnsmasq instances. + +Signed-off-by: Brian Haley +(cherry picked from commit d9f882bea2806799bf3d1f73937f5e72d0bfc650) +--- + contrib/lease-tools/dhcp_release.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/contrib/lease-tools/dhcp_release.c b/contrib/lease-tools/dhcp_release.c +index a51f04b..1dd8d32 100644 +--- a/contrib/lease-tools/dhcp_release.c ++++ b/contrib/lease-tools/dhcp_release.c +@@ -178,7 +178,7 @@ static int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask) + return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr); + } + +-static struct in_addr find_interface(struct in_addr client, int fd, unsigned int index) ++static struct in_addr find_interface(struct in_addr client, int fd, unsigned int index, int ifrfd, struct ifreq *ifr) + { + struct sockaddr_nl addr; + struct nlmsghdr *h; +@@ -218,7 +218,13 @@ static struct in_addr find_interface(struct in_addr client, int fd, unsigned int + + for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) + if (h->nlmsg_type == NLMSG_DONE) +- exit(0); ++ { ++ /* No match found, return first address as src/dhcp.c code does */ ++ ifr->ifr_addr.sa_family = AF_INET; ++ if (ioctl(ifrfd, SIOCGIFADDR, ifr) != -1) ++ return ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr; ++ exit(0); ++ } + else if (h->nlmsg_type == RTM_NEWADDR) + { + struct ifaddrmsg *ifa = NLMSG_DATA(h); +@@ -284,7 +290,7 @@ int main(int argc, char **argv) + } + + lease.s_addr = inet_addr(argv[2]); +- server = find_interface(lease, nl, if_nametoindex(argv[1])); ++ server = find_interface(lease, nl, if_nametoindex(argv[1]), fd, &ifr); + + memset(&packet, 0, sizeof(packet)); + +-- +2.20.1 + diff --git a/SOURCES/dnsmasq-2.79-rh1602477-2.patch b/SOURCES/dnsmasq-2.79-rh1602477-2.patch new file mode 100644 index 0000000..8421b76 --- /dev/null +++ b/SOURCES/dnsmasq-2.79-rh1602477-2.patch @@ -0,0 +1,49 @@ +From dcb4fa04548ab2364f662b735be86e275bd50745 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= +Date: Fri, 19 Jul 2019 14:00:08 +0200 +Subject: [PATCH] Remove warnings in coverity + +Change in dnsmasq should never occur, because ent_pw would not change. +But keep Coverity happy and prevent logic error. Second change avoids +warning from compiler. +--- + src/dnsmasq.c | 9 ++++++++- + src/option.c | 2 +- + 2 files changed, 9 insertions(+), 2 deletions(-) + +diff --git a/src/dnsmasq.c b/src/dnsmasq.c +index ce44809..2984f55 100644 +--- a/src/dnsmasq.c ++++ b/src/dnsmasq.c +@@ -608,7 +608,14 @@ int main (int argc, char **argv) + + if (ent_pw && ent_pw->pw_uid != 0) + { +-#if defined(HAVE_LINUX_NETWORK) ++#if defined(HAVE_LINUX_NETWORK) ++ if (!hdr || !data) ++ { ++ /* Just failsafe for logic errors */ ++ send_event(err_pipe[1], EVENT_CAP_ERR, ENOMEM, NULL); ++ _exit(0); ++ } ++ + /* On linux, we keep CAP_NETADMIN (for ARP-injection) and + CAP_NET_RAW (for icmp) if we're doing dhcp. If we have yet to bind + ports because of DAD, or we're doing it dynamically, +diff --git a/src/option.c b/src/option.c +index 9768efb..b12183b 100644 +--- a/src/option.c ++++ b/src/option.c +@@ -4255,7 +4255,7 @@ err: + struct name_list *nl; + if (!canon) + { +- struct name_list *tmp = new->names, *next; ++ struct name_list *tmp, *next; + for (tmp = new->names; tmp; tmp = next) + { + next = tmp->next; +-- +2.20.1 + diff --git a/SOURCES/dnsmasq-2.79-rh1602477.patch b/SOURCES/dnsmasq-2.79-rh1602477.patch new file mode 100644 index 0000000..c896db1 --- /dev/null +++ b/SOURCES/dnsmasq-2.79-rh1602477.patch @@ -0,0 +1,1686 @@ +From 8e11d702921e51a5eb00b9ee12925cae69039c22 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= +Date: Thu, 18 Jul 2019 18:49:30 +0200 +Subject: [PATCH] Fix issues detected by Coverity +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Squashed commit of the following: + +commit 6a5fa6c9207961a662f8debbe9172500c752e3ac +Author: Kevin Darbyshire-Bryant +Date: Mon Dec 10 10:34:35 2018 +0000 + + build failure on master with NO_DHCPv6 and fix.... + + Hi Simon, + + master has a build error when building without HAVE_DHCPv6 + + option.c: In function 'dhcp_context_free': + option.c:1042:15: error: 'struct dhcp_context' has no member named 'template_interface' + free(ctx->template_interface); + + Sadly, need to put in a little conditional compilation ifdef'erey + + Simplest patch in the world attached + + Cheers, + + Kevin D-B + + 012C ACB2 28C6 C53E 9775 9123 B3A2 389B 9DE2 334A + + From 061eb8599636bb360e0b7fa5986935b86db39497 Mon Sep 17 00:00:00 2001 + From: Kevin Darbyshire-Bryant + Date: Mon, 10 Dec 2018 10:07:33 +0000 + Subject: [PATCH] option: fix non DHCPv6 build error + + option.c: In function 'dhcp_context_free': + option.c:1042:15: error: 'struct dhcp_context' has no member named 'template_interface' + free(ctx->template_interface); + ^~ + + Signed-off-by: Kevin Darbyshire-Bryant + (cherry picked from commit b683cf37f9f3dd3dc5d95d621ee75850d559b2e4) + +commit a4be120618a5d8517d23e687003cab53e7db11c9 +Author: Petr Menšík +Date: Sun Dec 16 21:25:29 2018 +0000 + + Fix option parsing errors introduced in 59e470381f84f2fdf0640c7bc67827f3f0c64784 + + Thanks to Kevin Darbyshire-Bryant for spotting this. + + (cherry picked from commit 137e9f878fafb38369eab7d9dfe84e4228ff5f89) + +commit 3e0752faa67ffd25893ebbcbe6a5788699a2e1c9 +Author: Petr Menšík +Date: Fri Nov 2 22:39:39 2018 +0000 + + Free config file values on parsing errors. + + This time I have a little bit more controversal patches. But I think + still useful. They fixes memory leaks that might occur in some cases. + Most dnsmasq errors is fatal, so it does not matter. But some are not. + Some parts are reloaded on SIGHUP signal, so it might leak more than once. + + Some example when it changes the failures. Use dhcp-options file with + this content: + + tag:error,vendor:redhat + option:ntp-server,1.2.3.4.5 + option6:ntp-server,[:::] + + Is not fatal and dnsmasq will start. On each reload command, it would + leak some memory. I validated it using valgrind --leak-check=full + dnsmasq -d. This patch fixes it. It introduces something that might be + considered constructor and destructor of selected structures. + + (cherry picked from commit 59e470381f84f2fdf0640c7bc67827f3f0c64784) + +commit c2a44c21dddffff95346c931feda696704ea73ca +Author: Petr Menšík +Date: Wed Oct 24 22:30:18 2018 +0100 + + Do not rely on dead code elimination, use array instead. + Make options bits derived from size and count. Use size of option bits + and last supported bit in computation. No new change would be required + when new options are added. Just change OPT_LAST constant. + + (cherry picked from commit 24b87607c1353e94689e8a2190571ab3f3b36f31) + +commit 5b9f199a1b16b7aa41cf544e9312c93e893206b3 +Author: Petr Menšík +Date: Fri Aug 17 10:20:05 2018 +0200 + + Minor improvements in lease-tools + + Limit max interface name to fit into buffer. + Make sure pointer have to be always positive. + Close socket after received reply. + + (cherry picked from commit 2b38e3823b12ab13f86c3a44648de436daadb1f6) + +commit d30a8f4c46a1b446b7d9932d022a09f1ee6b4554 +Author: Petr Menšík +Date: Thu Aug 16 15:48:15 2018 +0200 + + Close socket after received reply + +commit 6e5dedbb8aa3d27c9477558e66f9d260414340a3 +Author: Petr Menšík +Date: Wed Aug 15 19:41:07 2018 +0200 + + Mark die function as never returning + + Improves static analysis output and reduces false positives. + +commit 3d7e9ba115d3c229d678814103dbf3401738dcf5 +Author: Petr Menšík +Date: Wed Aug 15 18:17:00 2018 +0200 + + Fix lengths of interface names + + Use helper function similar to copy correctly limited names into + buffers. +--- + contrib/lease-tools/dhcp_lease_time.c | 2 +- + contrib/lease-tools/dhcp_release.c | 3 +- + contrib/lease-tools/dhcp_release6.c | 5 +- + src/bpf.c | 2 +- + src/dhcp.c | 9 +- + src/dnsmasq.h | 20 +- + src/log.c | 2 +- + src/network.c | 12 +- + src/option.c | 548 +++++++++++++++++--------- + src/rfc2131.c | 10 +- + src/tftp.c | 2 +- + src/util.c | 12 +- + 12 files changed, 408 insertions(+), 219 deletions(-) + +diff --git a/contrib/lease-tools/dhcp_lease_time.c b/contrib/lease-tools/dhcp_lease_time.c +index f9d7a85..697d627 100644 +--- a/contrib/lease-tools/dhcp_lease_time.c ++++ b/contrib/lease-tools/dhcp_lease_time.c +@@ -83,7 +83,7 @@ static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt + if (p >= end - 2) + return NULL; /* malformed packet */ + opt_len = option_len(p); +- if (p >= end - (2 + opt_len)) ++ if (end - p >= (2 + opt_len)) + return NULL; /* malformed packet */ + if (*p == opt && opt_len >= minsize) + return p; +diff --git a/contrib/lease-tools/dhcp_release.c b/contrib/lease-tools/dhcp_release.c +index 201fcd3..59883d4 100644 +--- a/contrib/lease-tools/dhcp_release.c ++++ b/contrib/lease-tools/dhcp_release.c +@@ -270,7 +270,8 @@ int main(int argc, char **argv) + + /* This voodoo fakes up a packet coming from the correct interface, which really matters for + a DHCP server */ +- strcpy(ifr.ifr_name, argv[1]); ++ strncpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name)-1); ++ ifr.ifr_name[sizeof(ifr.ifr_name)-1] = '\0'; + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == -1) + { + perror("cannot setup interface"); +diff --git a/contrib/lease-tools/dhcp_release6.c b/contrib/lease-tools/dhcp_release6.c +index 7f79fa7..d680222 100644 +--- a/contrib/lease-tools/dhcp_release6.c ++++ b/contrib/lease-tools/dhcp_release6.c +@@ -376,9 +376,12 @@ int send_release_packet(const char* iface, struct dhcp6_packet* packet) + sleep(1); + continue; + } ++ ++ close(sock); + return result; + } +- ++ ++ close(sock); + fprintf(stderr, "Response timed out\n"); + return -1; + } +diff --git a/src/bpf.c b/src/bpf.c +index 49a11bf..ff66d6d 100644 +--- a/src/bpf.c ++++ b/src/bpf.c +@@ -169,7 +169,7 @@ int iface_enumerate(int family, void *parm, int (*callback)()) + struct in6_ifreq ifr6; + + memset(&ifr6, 0, sizeof(ifr6)); +- strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name)); ++ safe_strncpy(ifr6.ifr_name, addrs->ifa_name, sizeof(ifr6.ifr_name)); + + ifr6.ifr_addr = *((struct sockaddr_in6 *) addrs->ifa_addr); + if (fd != -1 && ioctl(fd, SIOCGIFAFLAG_IN6, &ifr6) != -1) +diff --git a/src/dhcp.c b/src/dhcp.c +index 5a8daec..26f3287 100644 +--- a/src/dhcp.c ++++ b/src/dhcp.c +@@ -232,7 +232,7 @@ void dhcp_packet(time_t now, int pxe_fd) + + #ifdef HAVE_LINUX_NETWORK + /* ARP fiddling uses original interface even if we pretend to use a different one. */ +- strncpy(arp_req.arp_dev, ifr.ifr_name, 16); ++ safe_strncpy(arp_req.arp_dev, ifr.ifr_name, sizeof(arp_req.arp_dev)); + #endif + + /* If the interface on which the DHCP request was received is an +@@ -255,7 +255,7 @@ void dhcp_packet(time_t now, int pxe_fd) + } + else + { +- strncpy(ifr.ifr_name, bridge->iface, IF_NAMESIZE); ++ safe_strncpy(ifr.ifr_name, bridge->iface, sizeof(ifr.ifr_name)); + break; + } + } +@@ -279,7 +279,7 @@ void dhcp_packet(time_t now, int pxe_fd) + is_relay_reply = 1; + iov.iov_len = sz; + #ifdef HAVE_LINUX_NETWORK +- strncpy(arp_req.arp_dev, ifr.ifr_name, 16); ++ safe_strncpy(arp_req.arp_dev, ifr.ifr_name, sizeof(arp_req.arp_dev)); + #endif + } + else +@@ -988,8 +988,7 @@ char *host_from_dns(struct in_addr addr) + if (!legal_hostname(hostname)) + return NULL; + +- strncpy(daemon->dhcp_buff, hostname, 256); +- daemon->dhcp_buff[255] = 0; ++ safe_strncpy(daemon->dhcp_buff, hostname, 256); + strip_hostname(daemon->dhcp_buff); + + return daemon->dhcp_buff; +diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 6773b69..6b18bb7 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -42,6 +42,12 @@ + # define __EXTENSIONS__ + #endif + ++#if (defined(__GNUC__) && __GNUC__ >= 3) || defined(__clang__) ++#define ATTRIBUTE_NORETURN __attribute__ ((noreturn)) ++#else ++#define ATTRIBUTE_NORETURN ++#endif ++ + /* get these before config.h for IPv6 stuff... */ + #include + #include +@@ -190,9 +196,6 @@ struct event_desc { + #define EC_MISC 5 + #define EC_INIT_OFFSET 10 + +-/* Trust the compiler dead-code eliminator.... */ +-#define option_bool(x) (((x) < 32) ? daemon->options & (1u << (x)) : daemon->options2 & (1u << ((x) - 32))) +- + #define OPT_BOGUSPRIV 0 + #define OPT_FILTER 1 + #define OPT_LOG 2 +@@ -252,6 +255,12 @@ struct event_desc { + #define OPT_TFTP_APREF_MAC 56 + #define OPT_LAST 57 + ++#define OPTION_BITS (sizeof(unsigned int)*8) ++#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) ) ++#define option_var(x) (daemon->options[(x) / OPTION_BITS]) ++#define option_val(x) ((1u) << ((x) % OPTION_BITS)) ++#define option_bool(x) (option_var(x) & option_val(x)) ++ + /* extra flags for my_syslog, we use a couple of facilities since they are known + not to occupy the same bits as priorities, no matter how syslog.h is set up. */ + #define MS_TFTP LOG_USER +@@ -947,7 +956,7 @@ extern struct daemon { + config file arguments. All set (including defaults) + in option.c */ + +- unsigned int options, options2; ++ unsigned int options[OPTION_SIZE]; + struct resolvc default_resolv, *resolv_files; + time_t last_resolv; + char *servers_file; +@@ -1205,6 +1214,7 @@ int legal_hostname(char *name); + char *canonicalise(char *in, int *nomem); + unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit); + void *safe_malloc(size_t size); ++void safe_strncpy(char *dest, const char *src, size_t size); + void safe_pipe(int *fd, int read_noblock); + void *whine_malloc(size_t size); + int sa_len(union mysockaddr *addr); +@@ -1233,7 +1243,7 @@ int wildcard_match(const char* wildcard, const char* match); + int wildcard_matchn(const char* wildcard, const char* match, int num); + + /* log.c */ +-void die(char *message, char *arg1, int exit_code); ++void die(char *message, char *arg1, int exit_code) ATTRIBUTE_NORETURN; + int log_start(struct passwd *ent_pw, int errfd); + int log_reopen(char *log_file); + +diff --git a/src/log.c b/src/log.c +index dae8a75..d0d4780 100644 +--- a/src/log.c ++++ b/src/log.c +@@ -232,7 +232,7 @@ static void log_write(void) + logaddr.sun_len = sizeof(logaddr) - sizeof(logaddr.sun_path) + strlen(_PATH_LOG) + 1; + #endif + logaddr.sun_family = AF_UNIX; +- strncpy(logaddr.sun_path, _PATH_LOG, sizeof(logaddr.sun_path)); ++ safe_strncpy(logaddr.sun_path, _PATH_LOG, sizeof(logaddr.sun_path)); + + /* Got connection back? try again. */ + if (connect(log_fd, (struct sockaddr *)&logaddr, sizeof(logaddr)) != -1) +diff --git a/src/network.c b/src/network.c +index 0381513..efb7b03 100644 +--- a/src/network.c ++++ b/src/network.c +@@ -29,7 +29,7 @@ int indextoname(int fd, int index, char *name) + if (ioctl(fd, SIOCGIFNAME, &ifr) == -1) + return 0; + +- strncpy(name, ifr.ifr_name, IF_NAMESIZE); ++ safe_strncpy(name, ifr.ifr_name, IF_NAMESIZE); + + return 1; + } +@@ -82,12 +82,12 @@ int indextoname(int fd, int index, char *name) + for (i = lifc.lifc_len / sizeof(struct lifreq); i; i--, lifrp++) + { + struct lifreq lifr; +- strncpy(lifr.lifr_name, lifrp->lifr_name, IF_NAMESIZE); ++ safe_strncpy(lifr.lifr_name, lifrp->lifr_name, IF_NAMESIZE); + if (ioctl(fd, SIOCGLIFINDEX, &lifr) < 0) + return 0; + + if (lifr.lifr_index == index) { +- strncpy(name, lifr.lifr_name, IF_NAMESIZE); ++ safe_strncpy(name, lifr.lifr_name, IF_NAMESIZE); + return 1; + } + } +@@ -188,7 +188,7 @@ int loopback_exception(int fd, int family, struct all_addr *addr, char *name) + struct ifreq ifr; + struct irec *iface; + +- strncpy(ifr.ifr_name, name, IF_NAMESIZE); ++ safe_strncpy(ifr.ifr_name, name, IF_NAMESIZE); + if (ioctl(fd, SIOCGIFFLAGS, &ifr) != -1 && + ifr.ifr_flags & IFF_LOOPBACK) + { +@@ -1284,7 +1284,7 @@ static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname) + return NULL; + } + +- strcpy(sfd->interface, intname); ++ safe_strncpy(sfd->interface, intname, sizeof(sfd->interface)); + sfd->source_addr = *addr; + sfd->next = daemon->sfds; + sfd->ifindex = ifindex; +@@ -1452,7 +1452,7 @@ void add_update_server(int flags, + serv->flags |= SERV_HAS_DOMAIN; + + if (interface) +- strcpy(serv->interface, interface); ++ safe_strncpy(serv->interface, interface, sizeof(serv->interface)); + if (addr) + serv->addr = *addr; + if (source_addr) +diff --git a/src/option.c b/src/option.c +index d358d99..9768efb 100644 +--- a/src/option.c ++++ b/src/option.c +@@ -559,14 +559,15 @@ static void *opt_malloc(size_t size) + return ret; + } + +-static char *opt_string_alloc(char *cp) ++static char *opt_string_alloc(const char *cp) + { + char *ret = NULL; ++ size_t len; + +- if (cp && strlen(cp) != 0) ++ if (cp && (len = strlen(cp)) != 0) + { +- ret = opt_malloc(strlen(cp)+1); +- strcpy(ret, cp); ++ ret = opt_malloc(len+1); ++ memcpy(ret, cp, len+1); + + /* restore hidden metachars */ + unhide_metas(ret); +@@ -741,6 +742,8 @@ static void do_usage(void) + } + + #define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0) ++#define ret_err_free(x,m) do { strcpy(errstr, (x)); free((m)); return 0; } while (0) ++#define goto_err(x) do { strcpy(errstr, (x)); goto on_error; } while (0) + + static char *parse_mysockaddr(char *arg, union mysockaddr *addr) + { +@@ -792,7 +795,7 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a + if (interface_opt) + { + #if defined(SO_BINDTODEVICE) +- strncpy(interface, interface_opt, IF_NAMESIZE - 1); ++ safe_strncpy(interface, interface_opt, IF_NAMESIZE); + #else + return _("interface binding not supported"); + #endif +@@ -821,7 +824,7 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a + return _("interface can only be specified once"); + + source_addr->in.sin_addr.s_addr = INADDR_ANY; +- strncpy(interface, source, IF_NAMESIZE - 1); ++ safe_strncpy(interface, source, IF_NAMESIZE); + #else + return _("interface binding not supported"); + #endif +@@ -856,7 +859,7 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a + return _("interface can only be specified once"); + + source_addr->in6.sin6_addr = in6addr_any; +- strncpy(interface, source, IF_NAMESIZE - 1); ++ safe_strncpy(interface, source, IF_NAMESIZE); + #else + return _("interface binding not supported"); + #endif +@@ -894,6 +897,8 @@ static struct server *add_rev4(struct in_addr addr, int msize) + p += sprintf(p, "%d.", (a >> 24) & 0xff); + break; + default: ++ free(serv->domain); ++ free(serv); + return NULL; + } + +@@ -948,6 +953,99 @@ static char *set_prefix(char *arg) + return arg; + } + ++static struct dhcp_netid * ++dhcp_netid_create(const char *net, struct dhcp_netid *next) ++{ ++ struct dhcp_netid *tt; ++ tt = opt_malloc(sizeof (struct dhcp_netid)); ++ tt->net = opt_string_alloc(net); ++ tt->next = next; ++ return tt; ++} ++ ++static void dhcp_netid_free(struct dhcp_netid *nid) ++{ ++ while (nid) ++ { ++ struct dhcp_netid *tmp = nid; ++ nid = nid->next; ++ free(tmp->net); ++ free(tmp); ++ } ++} ++ ++/* Parse one or more tag:s before parameters. ++ * Moves arg to the end of tags. */ ++static struct dhcp_netid * dhcp_tags(char **arg) ++{ ++ struct dhcp_netid *id = NULL; ++ ++ while (is_tag_prefix(*arg)) ++ { ++ char *comma = split(*arg); ++ id = dhcp_netid_create((*arg)+4, id); ++ *arg = comma; ++ }; ++ if (!*arg) ++ { ++ dhcp_netid_free(id); ++ id = NULL; ++ } ++ return id; ++} ++ ++static void dhcp_netid_list_free(struct dhcp_netid_list *netid) ++{ ++ while (netid) ++ { ++ struct dhcp_netid_list *tmplist = netid; ++ netid = netid->next; ++ dhcp_netid_free(tmplist->list); ++ free(tmplist); ++ } ++} ++ ++static void dhcp_config_free(struct dhcp_config *config) ++{ ++ if (config) ++ { ++ struct hwaddr_config *hwaddr = config->hwaddr; ++ while (hwaddr) ++ { ++ struct hwaddr_config *tmp = hwaddr; ++ hwaddr = hwaddr->next; ++ free(tmp); ++ } ++ dhcp_netid_list_free(config->netid); ++ if (config->flags & CONFIG_CLID) ++ free(config->clid); ++ free(config); ++ } ++} ++ ++static void dhcp_context_free(struct dhcp_context *ctx) ++{ ++ if (ctx) ++ { ++ dhcp_netid_free(ctx->filter); ++ free(ctx->netid.net); ++#ifdef HAVE_DHCP6 ++ free(ctx->template_interface); ++#endif ++ free(ctx); ++ } ++} ++ ++static void dhcp_opt_free(struct dhcp_opt *opt) ++{ ++ if (opt->flags & DHOPT_VENDOR) ++ free(opt->u.vendor_class); ++ dhcp_netid_free(opt->netid); ++ free(opt->val); ++ free(opt); ++} ++ ++ + /* This is too insanely large to keep in-line in the switch */ + static int parse_dhcp_opt(char *errstr, char *arg, int flags) + { +@@ -955,7 +1053,6 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) + char lenchar = 0, *cp; + int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots; + char *comma = NULL; +- struct dhcp_netid *np = NULL; + u16 opt_len = 0; + int is6 = 0; + int option_ok = 0; +@@ -1042,14 +1139,9 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) + } + else + { +- new->netid = opt_malloc(sizeof (struct dhcp_netid)); + /* allow optional "net:" or "tag:" for consistency */ +- if (is_tag_prefix(arg)) +- new->netid->net = opt_string_alloc(arg+4); +- else +- new->netid->net = opt_string_alloc(set_prefix(arg)); +- new->netid->next = np; +- np = new->netid; ++ const char *name = (is_tag_prefix(arg)) ? arg+4 : set_prefix(arg); ++ new->netid = dhcp_netid_create(name, new->netid); + } + + arg = comma; +@@ -1059,7 +1151,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) + if (is6) + { + if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE)) +- ret_err(_("unsupported encapsulation for IPv6 option")); ++ goto_err(_("unsupported encapsulation for IPv6 option")); + + if (opt_len == 0 && + !(new->flags & DHOPT_RFC3925)) +@@ -1073,7 +1165,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) + + /* option may be missing with rfc3925 match */ + if (!option_ok) +- ret_err(_("bad dhcp-option")); ++ goto_err(_("bad dhcp-option")); + + if (comma) + { +@@ -1141,10 +1233,10 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) + is_string = is_dec = is_hex = 0; + + if (!is6 && (!is_addr || dots == 0)) +- ret_err(_("bad IP address")); ++ goto_err(_("bad IP address")); + + if (is6 && !is_addr6) +- ret_err(_("bad IPv6 address")); ++ goto_err(_("bad IPv6 address")); + } + /* or names */ + else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING)) +@@ -1237,7 +1329,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) + comma = split(cp); + slash = split_chr(cp, '/'); + if (!inet_pton(AF_INET, cp, &in)) +- ret_err(_("bad IPv4 address")); ++ goto_err(_("bad IPv4 address")); + if (!slash) + { + memcpy(op, &in, INADDRSZ); +@@ -1282,8 +1374,8 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) + op += IN6ADDRSZ; + continue; + } +- +- ret_err(_("bad IPv6 address")); ++ ++ goto_err(_("bad IPv6 address")); + } + new->len = op - new->val; + } +@@ -1310,7 +1402,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) + if (strcmp (arg, ".") != 0) + { + if (!(dom = canonicalise_opt(arg))) +- ret_err(_("bad domain in dhcp-option")); ++ goto_err(_("bad domain in dhcp-option")); + + domlen = strlen(dom) + 2; + } +@@ -1404,7 +1496,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) + { + char *dom = canonicalise_opt(arg); + if (!dom) +- ret_err(_("bad domain in dhcp-option")); ++ goto_err(_("bad domain in dhcp-option")); + + newp = opt_malloc(len + strlen(dom) + 2); + +@@ -1442,14 +1534,14 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) + ((new->len > 255) || + (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) || + (new->len > 250 && (new->flags & DHOPT_RFC3925)))) +- ret_err(_("dhcp-option too long")); ++ goto_err(_("dhcp-option too long")); + + if (flags == DHOPT_MATCH) + { + if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) || + !new->netid || + new->netid->next) +- ret_err(_("illegal dhcp-match")); ++ goto_err(_("illegal dhcp-match")); + + if (is6) + { +@@ -1474,24 +1566,31 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) + } + + return 1; ++on_error: ++ dhcp_opt_free(new); ++ return 0; + } + + #endif + + void set_option_bool(unsigned int opt) + { +- if (opt < 32) +- daemon->options |= 1u << opt; +- else +- daemon->options2 |= 1u << (opt - 32); ++ option_var(opt) |= option_val(opt); + } + + void reset_option_bool(unsigned int opt) + { +- if (opt < 32) +- daemon->options &= ~(1u << opt); +- else +- daemon->options2 &= ~(1u << (opt - 32)); ++ option_var(opt) &= ~(option_val(opt)); ++} ++ ++static void server_list_free(struct server *list) ++{ ++ while (list) ++ { ++ struct server *tmp = list; ++ list = list->next; ++ free(tmp); ++ } + } + + static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only) +@@ -1675,13 +1774,13 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + /* has subnet+len */ + err = parse_mysockaddr(arg, &new->addr); + if (err) +- ret_err(err); ++ ret_err_free(err, new); + if (!atoi_check(end, &new->mask)) +- ret_err(gen_err); ++ ret_err_free(gen_err, new); + new->addr_used = 1; + } + else if (!atoi_check(arg, &new->mask)) +- ret_err(gen_err); ++ ret_err_free(gen_err, new); + + daemon->add_subnet4 = new; + +@@ -1693,15 +1792,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + /* has subnet+len */ + err = parse_mysockaddr(comma, &new->addr); + if (err) +- ret_err(err); ++ ret_err_free(err, new); + if (!atoi_check(end, &new->mask)) +- ret_err(gen_err); ++ ret_err_free(gen_err, new); + new->addr_used = 1; + } + else + { + if (!atoi_check(comma, &new->mask)) +- ret_err(gen_err); ++ ret_err_free(gen_err, new); + } + + daemon->add_subnet6 = new; +@@ -1908,7 +2007,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + new->addr.sa.sa_family = AF_INET6; + #endif + else +- ret_err(gen_err); ++ { ++ free(new->name); ++ ret_err_free(gen_err, new); ++ } + } + } + new->next = daemon->authinterface; +@@ -2080,7 +2182,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + + arg = split(netpart); + if (!atoi_check(netpart, &msize)) +- ret_err(gen_err); ++ ret_err_free(gen_err, new); + else if (inet_pton(AF_INET, comma, &new->start)) + { + int mask = (1 << (32 - msize)) - 1; +@@ -2093,18 +2195,18 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + { + if (!(new->prefix = canonicalise_opt(arg)) || + strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN) +- ret_err(_("bad prefix")); ++ ret_err_free(_("bad prefix"), new); + } + else if (strcmp(arg, "local") != 0 || + (msize != 8 && msize != 16 && msize != 24)) +- ret_err(gen_err); ++ ret_err_free(gen_err, new); + else + { + /* generate the equivalent of + local=/xxx.yyy.zzz.in-addr.arpa/ */ + struct server *serv = add_rev4(new->start, msize); + if (!serv) +- ret_err(_("bad prefix")); ++ ret_err_free(_("bad prefix"), new); + + serv->flags |= SERV_NO_ADDR; + +@@ -2134,17 +2236,17 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + setaddr6part(&new->end6, addrpart | mask); + + if (msize < 64) +- ret_err(gen_err); ++ ret_err_free(gen_err, new); + else if (arg) + { + if (option != 's') + { + if (!(new->prefix = canonicalise_opt(arg)) || + strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN) +- ret_err(_("bad prefix")); ++ ret_err_free(_("bad prefix"), new); + } + else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0)) +- ret_err(gen_err); ++ ret_err_free(gen_err, new); + else + { + /* generate the equivalent of +@@ -2164,7 +2266,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + } + #endif + else +- ret_err(gen_err); ++ ret_err_free(gen_err, new); + } + else + { +@@ -2178,7 +2280,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + if (!arg) + new->end.s_addr = new->start.s_addr; + else if (!inet_pton(AF_INET, arg, &new->end)) +- ret_err(gen_err); ++ ret_err_free(gen_err, new); + } + #ifdef HAVE_IPV6 + else if (inet_pton(AF_INET6, comma, &new->start6)) +@@ -2187,17 +2289,17 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + if (!arg) + memcpy(&new->end6, &new->start6, IN6ADDRSZ); + else if (!inet_pton(AF_INET6, arg, &new->end6)) +- ret_err(gen_err); ++ ret_err_free(gen_err, new); + } + #endif + else +- ret_err(gen_err); ++ ret_err_free(gen_err, new); + + if (option != 's' && prefstr) + { + if (!(new->prefix = canonicalise_opt(prefstr)) || + strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN) +- ret_err(_("bad prefix")); ++ ret_err_free(_("bad prefix"), new); + } + } + +@@ -2359,7 +2461,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + } + #endif + else +- ret_err(gen_err); ++ ret_err_free(gen_err, new); + + new->used = 0; + if (option == 'a') +@@ -2430,7 +2532,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + { + newlist->flags |= SERV_LITERAL_ADDRESS; + if (!(newlist->flags & SERV_TYPE)) +- ret_err(gen_err); ++ { ++ server_list_free(newlist); ++ ret_err(gen_err); ++ } + } + else if (option == LOPT_NO_REBIND) + newlist->flags |= SERV_NO_REBIND; +@@ -2451,7 +2556,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + { + char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags); + if (err) +- ret_err(err); ++ { ++ server_list_free(newlist); ++ ret_err(err); ++ } + } + + serv = newlist; +@@ -2801,21 +2909,19 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + { + if (is_tag_prefix(arg)) + { +- struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid)); +- tt->net = opt_string_alloc(arg+4); +- tt->next = new->filter; + /* ignore empty tag */ +- if (tt->net) +- new->filter = tt; ++ if (arg[4]) ++ new->filter = dhcp_netid_create(arg+4, new->filter); + } + else + { + if (new->netid.net) +- ret_err(_("only one tag allowed")); +- else if (strstr(arg, "set:") == arg) +- new->netid.net = opt_string_alloc(arg+4); ++ { ++ dhcp_context_free(new); ++ ret_err(_("only one tag allowed")); ++ } + else +- new->netid.net = opt_string_alloc(arg); ++ new->netid.net = opt_string_alloc(set_prefix(arg)); + } + arg = comma; + } +@@ -2831,7 +2937,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + break; + + if (k < 2) +- ret_err(_("bad dhcp-range")); ++ { ++ dhcp_context_free(new); ++ ret_err(_("bad dhcp-range")); ++ } + + if (inet_pton(AF_INET, a[0], &new->start)) + { +@@ -2843,7 +2952,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + else if (strcmp(a[1], "proxy") == 0) + new->flags |= CONTEXT_PROXY; + else if (!inet_pton(AF_INET, a[1], &new->end)) +- ret_err(_("bad dhcp-range")); ++ { ++ dhcp_context_free(new); ++ ret_err(_("bad dhcp-range")); ++ } + + if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr)) + { +@@ -2858,7 +2970,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + new->flags |= CONTEXT_NETMASK; + leasepos = 3; + if (!is_same_net(new->start, new->end, new->netmask)) +- ret_err(_("inconsistent DHCP range")); ++ { ++ dhcp_context_free(new); ++ ret_err(_("inconsistent DHCP range")); ++ } + + + if (k >= 4 && strchr(a[3], '.') && +@@ -2872,6 +2987,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + #ifdef HAVE_DHCP6 + else if (inet_pton(AF_INET6, a[0], &new->start6)) + { ++ const char *err = NULL; ++ + new->flags |= CONTEXT_V6; + new->prefix = 64; /* default */ + new->end6 = new->start6; +@@ -2917,19 +3034,24 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + } + } + +- if (new->prefix != 64) ++ if (new->prefix > 64) + { + if (new->flags & CONTEXT_RA) +- ret_err(_("prefix length must be exactly 64 for RA subnets")); ++ err=(_("prefix length must be exactly 64 for RA subnets")); + else if (new->flags & CONTEXT_TEMPLATE) +- ret_err(_("prefix length must be exactly 64 for subnet constructors")); ++ err=(_("prefix length must be exactly 64 for subnet constructors")); + } +- +- if (new->prefix < 64) +- ret_err(_("prefix length must be at least 64")); ++ else if (new->prefix < 64) ++ err=(_("prefix length must be at least 64")); + +- if (!is_same_net6(&new->start6, &new->end6, new->prefix)) +- ret_err(_("inconsistent DHCPv6 range")); ++ if (!err && !is_same_net6(&new->start6, &new->end6, new->prefix)) ++ err=(_("inconsistent DHCPv6 range")); ++ ++ if (err) ++ { ++ dhcp_context_free(new); ++ ret_err(err); ++ } + + /* dhcp-range=:: enables DHCP stateless on any interface */ + if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE)) +@@ -2940,7 +3062,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + struct in6_addr zero; + memset(&zero, 0, sizeof(zero)); + if (!is_same_net6(&zero, &new->start6, new->prefix)) +- ret_err(_("prefix must be zero with \"constructor:\" argument")); ++ { ++ dhcp_context_free(new); ++ ret_err(_("prefix must be zero with \"constructor:\" argument")); ++ } + } + + if (addr6part(&new->start6) > addr6part(&new->end6)) +@@ -2952,12 +3077,18 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + } + #endif + else +- ret_err(_("bad dhcp-range")); ++ { ++ dhcp_context_free(new); ++ ret_err(_("bad dhcp-range")); ++ } + + if (leasepos < k) + { + if (leasepos != k-1) +- ret_err(_("bad dhcp-range")); ++ { ++ dhcp_context_free(new); ++ ret_err(_("bad dhcp-range")); ++ } + + if (strcmp(a[leasepos], "infinite") == 0) + new->lease_time = 0xffffffff; +@@ -2996,7 +3127,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + break; + + if (*cp || (leasepos+1 < k)) +- ret_err(_("bad dhcp-range")); ++ ret_err_free(_("bad dhcp-range"), new); + + new->lease_time = atoi(a[leasepos]) * fac; + /* Leases of a minute or less confuse +@@ -3023,6 +3154,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0; + new->hwaddr = NULL; + new->netid = NULL; ++ new->clid = NULL; + + if ((a[0] = arg)) + for (k = 1; k < 7; k++) +@@ -3053,7 +3185,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + } + + if (len == -1) +- ret_err(_("bad hex constant")); ++ { ++ dhcp_config_free(new); ++ ret_err(_("bad hex constant")); ++ } + else if ((new->clid = opt_malloc(len))) + { + new->flags |= CONFIG_CLID; +@@ -3065,17 +3200,17 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + /* dhcp-host has strange backwards-compat needs. */ + else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg) + { +- struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid)); + struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list)); +- newtag->net = opt_malloc(strlen(arg + 4) + 1); + newlist->next = new->netid; + new->netid = newlist; +- newlist->list = newtag; +- strcpy(newtag->net, arg+4); +- unhide_metas(newtag->net); ++ newlist->list = dhcp_netid_create(arg+4, NULL); + } + else if (strstr(arg, "tag:") == arg) +- ret_err(_("cannot match tags in --dhcp-host")); ++ { ++ ++ dhcp_config_free(new); ++ ret_err(_("cannot match tags in --dhcp-host")); ++ } + #ifdef HAVE_DHCP6 + else if (arg[0] == '[' && arg[strlen(arg)-1] == ']') + { +@@ -3083,7 +3218,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + arg++; + + if (!inet_pton(AF_INET6, arg, &new->addr6)) +- ret_err(_("bad IPv6 address")); ++ { ++ dhcp_config_free(new); ++ ret_err(_("bad IPv6 address")); ++ } + + for (i= 0; i < 8; i++) + if (new->addr6.s6_addr[i] != 0) +@@ -3101,10 +3239,13 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config)); + if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX, + &newhw->wildcard_mask, &newhw->hwaddr_type)) == -1) +- ret_err(_("bad hex constant")); ++ { ++ free(newhw); ++ dhcp_config_free(new); ++ ret_err(_("bad hex constant")); ++ } + else + { +- + newhw->next = new->hwaddr; + new->hwaddr = newhw; + } +@@ -3181,7 +3322,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + { + if (!(new->hostname = canonicalise_opt(a[j])) || + !legal_hostname(new->hostname)) +- ret_err(_("bad DHCP host name")); ++ { ++ dhcp_config_free(new); ++ ret_err(_("bad DHCP host name")); ++ } + + new->flags |= CONFIG_NAME; + new->domain = strip_hostname(new->hostname); +@@ -3234,10 +3378,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + } + else + { +- struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid)); +- newtag->net = opt_malloc(len - 3); +- strcpy(newtag->net, arg+4); +- unhide_metas(newtag->net); ++ struct dhcp_netid *newtag = dhcp_netid_create(arg+4, NULL); + + if (strstr(arg, "set:") == arg) + { +@@ -3254,7 +3395,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + else + { + new->set = NULL; +- free(newtag); ++ dhcp_netid_free(newtag); + break; + } + } +@@ -3263,7 +3404,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + } + + if (!new->set) +- ret_err(_("bad tag-if")); ++ { ++ dhcp_netid_free(new->tag); ++ dhcp_netid_list_free(new->set); ++ ret_err_free(_("bad tag-if"), new); ++ } + + break; + } +@@ -3280,19 +3425,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + + case 'M': /* --dhcp-boot */ + { +- struct dhcp_netid *id = NULL; +- while (is_tag_prefix(arg)) +- { +- struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid)); +- newid->next = id; +- id = newid; +- comma = split(arg); +- newid->net = opt_string_alloc(arg+4); +- arg = comma; +- }; ++ struct dhcp_netid *id = dhcp_tags(&arg); + + if (!arg) +- ret_err(gen_err); ++ { ++ ret_err(gen_err); ++ } + else + { + char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL; +@@ -3338,19 +3476,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + + case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */ + { +- struct dhcp_netid *id = NULL; +- while (is_tag_prefix(arg)) +- { +- struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid)); +- newid->next = id; +- id = newid; +- comma = split(arg); +- newid->net = opt_string_alloc(arg+4); +- arg = comma; +- }; ++ struct dhcp_netid *id = dhcp_tags(&arg); + + if (!arg) +- ret_err(gen_err); ++ { ++ ret_err(gen_err); ++ } + else + { + struct delay_config *new; +@@ -3375,19 +3506,13 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + + new->netid = NULL; + new->opt = 10; /* PXE_MENU_PROMPT */ +- +- while (is_tag_prefix(arg)) +- { +- struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid)); +- comma = split(arg); +- nn->next = new->netid; +- new->netid = nn; +- nn->net = opt_string_alloc(arg+4); +- arg = comma; +- } ++ new->netid = dhcp_tags(&arg); + + if (!arg) +- ret_err(gen_err); ++ { ++ dhcp_opt_free(new); ++ ret_err(gen_err); ++ } + else + { + comma = split(arg); +@@ -3423,17 +3548,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + new->netid = NULL; + new->sname = NULL; + new->server.s_addr = 0; ++ new->netid = dhcp_tags(&arg); + +- while (is_tag_prefix(arg)) +- { +- struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid)); +- comma = split(arg); +- nn->next = new->netid; +- new->netid = nn; +- nn->net = opt_string_alloc(arg+4); +- arg = comma; +- } +- + if (arg && (comma = split(arg))) + { + for (i = 0; CSA[i]; i++) +@@ -3510,7 +3626,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + unhide_metas(comma); + new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type); + if (new->hwaddr_len == -1) +- ret_err(gen_err); ++ { ++ free(new->netid.net); ++ ret_err_free(gen_err, new); ++ } + else + { + new->next = daemon->dhcp_macs; +@@ -3527,7 +3646,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + + if (!(comma = split(arg)) || + !atoi_check16(comma, &new->class)) +- ret_err(gen_err); ++ ret_err_free(gen_err, new); + + new->tag.net = opt_string_alloc(set_prefix(arg)); + new->next = daemon->prefix_classes; +@@ -3549,7 +3668,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor)); + + if (!(comma = split(arg))) +- ret_err(gen_err); ++ ret_err_free(gen_err, new); + + new->netid.net = opt_string_alloc(set_prefix(arg)); + /* check for hex string - must digits may include : must not have nothing else, +@@ -3559,7 +3678,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + if ((comma = split(arg))) + { + if (option != 'U' || strstr(arg, "enterprise:") != arg) +- ret_err(gen_err); ++ { ++ free(new->netid.net); ++ ret_err_free(gen_err, new); ++ } + else + new->enterprise = atoi(arg+11); + } +@@ -3661,14 +3783,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + } + + while (arg) { +- struct dhcp_netid *member = opt_malloc(sizeof(struct dhcp_netid)); + comma = split(arg); +- member->next = list; +- list = member; +- if (is_tag_prefix(arg)) +- member->net = opt_string_alloc(arg+4); +- else +- member->net = opt_string_alloc(arg); ++ list = dhcp_netid_create(is_tag_prefix(arg) ? arg+4 :arg, list); + arg = comma; + } + +@@ -3682,7 +3798,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + struct addr_list *new = opt_malloc(sizeof(struct addr_list)); + comma = split(arg); + if (!(inet_pton(AF_INET, arg, &new->addr) > 0)) +- ret_err(_("bad dhcp-proxy address")); ++ ret_err_free(_("bad dhcp-proxy address"), new); + new->next = daemon->override_relays; + daemon->override_relays = new; + arg = comma; +@@ -3708,7 +3824,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + } + #endif + else +- ret_err(_("Bad dhcp-relay")); ++ { ++ free(new->interface); ++ ret_err_free(_("Bad dhcp-relay"), new); ++ } + + break; + } +@@ -3748,8 +3867,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + arg = split(comma); + if (!atoi_check(comma, &new->interval) || + (arg && !atoi_check(arg, &new->lifetime))) ++ { + err: +- ret_err(_("bad RA-params")); ++ free(new->name); ++ ret_err_free(_("bad RA-params"), new); ++ } + + new->next = daemon->ra_interfaces; + daemon->ra_interfaces = new; +@@ -3800,7 +3922,7 @@ err: + (!(inet_pton(AF_INET, dash, &new->end) > 0) || + !is_same_net(new->in, new->end, new->mask) || + ntohl(new->in.s_addr) > ntohl(new->end.s_addr))) +- ret_err(_("invalid alias range")); ++ ret_err_free(_("invalid alias range"), new); + + break; + } +@@ -3835,7 +3957,7 @@ err: + new->family = AF_INET6; + #endif + else +- ret_err(gen_err); ++ ret_err_free(gen_err, new); + } + new->intr = opt_string_alloc(comma); + break; +@@ -3867,11 +3989,19 @@ err: + alias = canonicalise_opt(arg); + + if (!alias || !target) +- ret_err(_("bad CNAME")); ++ { ++ free(target); ++ free(alias); ++ ret_err(_("bad CNAME")); ++ } + + for (new = daemon->cnames; new; new = new->next) + if (hostname_isequal(new->alias, alias)) +- ret_err(_("duplicate CNAME")); ++ { ++ free(target); ++ free(alias); ++ ret_err(_("duplicate CNAME")); ++ } + new = opt_malloc(sizeof(struct cname)); + new->next = daemon->cnames; + daemon->cnames = new; +@@ -3894,7 +4024,11 @@ err: + + if (!(dom = canonicalise_opt(arg)) || + (comma && !(target = canonicalise_opt(comma)))) +- ret_err(_("bad PTR record")); ++ { ++ free(dom); ++ free(target); ++ ret_err(_("bad PTR record")); ++ } + else + { + new = opt_malloc(sizeof(struct ptr_record)); +@@ -3912,7 +4046,7 @@ err: + int k = 0; + struct naptr *new; + int order, pref; +- char *name, *replace = NULL; ++ char *name=NULL, *replace = NULL; + + if ((a[0] = arg)) + for (k = 1; k < 7; k++) +@@ -3925,7 +4059,11 @@ err: + !atoi_check16(a[1], &order) || + !atoi_check16(a[2], &pref) || + (k == 7 && !(replace = canonicalise_opt(a[6])))) +- ret_err(_("bad NAPTR record")); ++ { ++ free(name); ++ free(replace); ++ ret_err(_("bad NAPTR record")); ++ } + else + { + new = opt_malloc(sizeof(struct naptr)); +@@ -3947,22 +4085,26 @@ err: + struct txt_record *new; + size_t len = 0; + char *data; +- int val; ++ int class; + + comma = split(arg); + data = split(comma); + + new = opt_malloc(sizeof(struct txt_record)); +- new->next = daemon->rr; +- daemon->rr = new; ++ new->name = NULL; + +- if (!atoi_check(comma, &val) || ++ if (!atoi_check(comma, &class) || + !(new->name = canonicalise_opt(arg)) || + (data && (len = parse_hex(data, (unsigned char *)data, -1, NULL, NULL)) == -1U)) +- ret_err(_("bad RR record")); +- +- new->class = val; ++ { ++ free(new->name); ++ ret_err_free(_("bad RR record"), new); ++ } ++ + new->len = 0; ++ new->class = class; ++ new->next = daemon->rr; ++ daemon->rr = new; + + if (data) + { +@@ -3983,14 +4125,14 @@ err: + comma = split(arg); + + new = opt_malloc(sizeof(struct txt_record)); +- new->next = daemon->txt; +- daemon->txt = new; + new->class = C_IN; + new->stat = 0; + + if (!(new->name = canonicalise_opt(arg))) +- ret_err(_("bad TXT record")); ++ ret_err_free(_("bad TXT record"), new); + ++ new->next = daemon->txt; ++ daemon->txt = new; + len = comma ? strlen(comma) : 0; + len += (len/255) + 1; /* room for extra counts */ + new->txt = p = opt_malloc(len); +@@ -4037,24 +4179,32 @@ err: + arg = comma; + comma = split(arg); + if (!(target = canonicalise_opt(arg))) +- ret_err(_("bad SRV target")); ++ ret_err_free(_("bad SRV target"), name); + + if (comma) + { + arg = comma; + comma = split(arg); + if (!atoi_check16(arg, &port)) +- ret_err(_("invalid port number")); ++ { ++ free(name); ++ ret_err_free(_("invalid port number"), target); ++ } + + if (comma) + { + arg = comma; + comma = split(arg); + if (!atoi_check16(arg, &priority)) +- ret_err(_("invalid priority")); +- ++ { ++ free(name); ++ ret_err_free(_("invalid priority"), target); ++ } + if (comma && !atoi_check16(comma, &weight)) +- ret_err(_("invalid weight")); ++ { ++ free(name); ++ ret_err_free(_("invalid weight"), target); ++ } + } + } + } +@@ -4073,13 +4223,15 @@ err: + + case LOPT_HOST_REC: /* --host-record */ + { +- struct host_record *new = opt_malloc(sizeof(struct host_record)); +- memset(new, 0, sizeof(struct host_record)); +- new->ttl = -1; ++ struct host_record *new; + + if (!arg || !(comma = split(arg))) + ret_err(_("Bad host-record")); + ++ new = opt_malloc(sizeof(struct host_record)); ++ memset(new, 0, sizeof(struct host_record)); ++ new->ttl = -1; ++ + while (arg) + { + struct all_addr addr; +@@ -4100,10 +4252,19 @@ err: + { + int nomem; + char *canon = canonicalise(arg, &nomem); +- struct name_list *nl = opt_malloc(sizeof(struct name_list)); ++ struct name_list *nl; + if (!canon) +- ret_err(_("Bad name in host-record")); ++ { ++ struct name_list *tmp = new->names, *next; ++ for (tmp = new->names; tmp; tmp = next) ++ { ++ next = tmp->next; ++ free(tmp); ++ } ++ ret_err_free(_("Bad name in host-record"), new); ++ } + ++ nl = opt_malloc(sizeof(struct name_list)); + nl->name = canon; + /* keep order, so that PTR record goes to first name */ + nl->next = NULL; +@@ -4143,6 +4304,7 @@ err: + int len; + + new->class = C_IN; ++ new->name = NULL; + + if ((comma = split(arg)) && (algo = split(comma))) + { +@@ -4167,7 +4329,7 @@ err: + !atoi_check8(algo, &new->algo) || + !atoi_check8(digest, &new->digest_type) || + !(new->name = canonicalise_opt(arg))) +- ret_err(_("bad trust anchor")); ++ ret_err_free(_("bad trust anchor"), new); + + /* Upper bound on length */ + len = (2*strlen(keyhex))+1; +@@ -4181,7 +4343,10 @@ err: + else + cp++; + if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1) +- ret_err(_("bad HEX in trust anchor")); ++ { ++ free(new->name); ++ ret_err_free(_("bad HEX in trust anchor"), new); ++ } + + new->next = daemon->ds; + daemon->ds = new; +@@ -4650,8 +4815,8 @@ void read_opts(int argc, char **argv, char *compile_opts) + size_t argbuf_size = MAXDNAME; + char *argbuf = opt_malloc(argbuf_size); + char *buff = opt_malloc(MAXDNAME); +- int option, conffile_opt = '7', testmode = 0; +- char *arg, *conffile = CONFFILE; ++ int option, testmode = 0; ++ char *arg, *conffile = NULL; + + opterr = 0; + +@@ -4725,8 +4890,7 @@ void read_opts(int argc, char **argv, char *compile_opts) + argbuf_size = strlen(optarg) + 1; + argbuf = opt_malloc(argbuf_size); + } +- strncpy(argbuf, optarg, argbuf_size); +- argbuf[argbuf_size-1] = 0; ++ safe_strncpy(argbuf, optarg, argbuf_size); + arg = argbuf; + } + else +@@ -4761,7 +4925,8 @@ void read_opts(int argc, char **argv, char *compile_opts) + } + else if (option == 'C') + { +- conffile_opt = 0; /* file must exist */ ++ if (conffile) ++ free(conffile); + conffile = opt_string_alloc(arg); + } + else +@@ -4779,10 +4944,11 @@ void read_opts(int argc, char **argv, char *compile_opts) + + if (conffile) + { +- one_file(conffile, conffile_opt); +- if (conffile_opt == 0) +- free(conffile); ++ one_file(conffile, 0); ++ free(conffile); + } ++ else ++ one_file(CONFFILE, '7'); + + /* port might not be known when the address is parsed - fill in here */ + if (daemon->servers) +diff --git a/src/rfc2131.c b/src/rfc2131.c +index c08a8ab..997575a 100644 +--- a/src/rfc2131.c ++++ b/src/rfc2131.c +@@ -917,7 +917,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, + mess->siaddr = a_record_from_hosts(boot->tftp_sname, now); + + if (boot->file) +- strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1); ++ safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file)); + } + + option_put(mess, end, OPTION_MESSAGE_TYPE, 1, +@@ -2296,7 +2296,7 @@ static void do_options(struct dhcp_context *context, + in_list(req_options, OPTION_SNAME)) + option_put_string(mess, end, OPTION_SNAME, boot->sname, 1); + else +- strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1); ++ safe_strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)); + } + + if (boot->file) +@@ -2306,7 +2306,7 @@ static void do_options(struct dhcp_context *context, + in_list(req_options, OPTION_FILENAME)) + option_put_string(mess, end, OPTION_FILENAME, boot->file, 1); + else +- strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1); ++ safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file)); + } + + if (boot->next_server.s_addr) +@@ -2323,14 +2323,14 @@ static void do_options(struct dhcp_context *context, + if ((!req_options || !in_list(req_options, OPTION_FILENAME)) && + (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE)) + { +- strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1); ++ safe_strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)); + done_file = 1; + } + + if ((!req_options || !in_list(req_options, OPTION_SNAME)) && + (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE)) + { +- strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1); ++ safe_strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)); + done_server = 1; + } + +diff --git a/src/tftp.c b/src/tftp.c +index bccca69..f2eccbc 100644 +--- a/src/tftp.c ++++ b/src/tftp.c +@@ -234,7 +234,7 @@ void tftp_request(struct listener *listen, time_t now) + #endif + } + +- strncpy(ifr.ifr_name, name, IF_NAMESIZE); ++ safe_strncpy(ifr.ifr_name, name, IF_NAMESIZE); + if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1) + { + mtu = ifr.ifr_mtu; +diff --git a/src/util.c b/src/util.c +index 532bc16..e003e3c 100644 +--- a/src/util.c ++++ b/src/util.c +@@ -281,7 +281,17 @@ void *safe_malloc(size_t size) + die(_("could not get memory"), NULL, EC_NOMEM); + + return ret; +-} ++} ++ ++/* can be replaced by (void)strlcpy() on some platforms */ ++void safe_strncpy(char *dest, const char *src, size_t size) ++{ ++ if (size) ++ { ++ dest[size-1] = '\0'; ++ strncpy(dest, src, size-1); ++ } ++} + + void safe_pipe(int *fd, int read_noblock) + { +-- +2.20.1 + diff --git a/SOURCES/dnsmasq-2.79-rh1700916.patch b/SOURCES/dnsmasq-2.79-rh1700916.patch new file mode 100644 index 0000000..0511e24 --- /dev/null +++ b/SOURCES/dnsmasq-2.79-rh1700916.patch @@ -0,0 +1,95 @@ +From 10642f9fb350e118d88e995b8dfa2badc7be1c30 Mon Sep 17 00:00:00 2001 +From: Petr Mensik +Date: Wed, 11 Dec 2019 13:41:57 +0100 +Subject: [PATCH] Restore ability to answer non-recursive requests + +Instead, check only local configured entries are answered without +rdbit set. All cached replies are still denied, but locally configured +names are available with both recursion and without it. +--- + src/rfc1035.c | 27 ++++++++++++++------------- + 1 file changed, 14 insertions(+), 13 deletions(-) + +diff --git a/src/rfc1035.c b/src/rfc1035.c +index 6b3bb27..6a7c154 100644 +--- a/src/rfc1035.c ++++ b/src/rfc1035.c +@@ -1262,7 +1262,11 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now) + else + return daemon->max_ttl; + } +- ++ ++static int cache_validated(const struct crec *crecp) ++{ ++ return (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)); ++} + + /* return zero if we can't answer from cache, or packet size if we can */ + size_t answer_request(struct dns_header *header, char *limit, size_t qlen, +@@ -1281,6 +1285,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1; + struct mx_srv_record *rec; + size_t len; ++ int rd_bit; + // Make sure we do not underflow here too. + if (qlen > (limit - ((char *)header))) return 0; + +@@ -1290,10 +1295,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + OPCODE(header) != QUERY ) + return 0; + +- /* always servfail queries with RD unset, to avoid cache snooping. */ +- if (!(header->hb3 & HB3_RD)) +- return setup_reply(header, qlen, NULL, F_SERVFAIL, 0); +- ++ rd_bit = (header->hb3 & HB3_RD); ++ + /* Don't return AD set if checking disabled. */ + if (header->hb4 & HB4_CD) + sec_data = 0; +@@ -1458,9 +1461,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + /* Don't use cache when DNSSEC data required, unless we know that + the zone is unsigned, which implies that we're doing + validation. */ +- if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || +- !do_bit || +- (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))) ++ if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || ++ (rd_bit && (!do_bit || cache_validated(crecp)) )) + { + do + { +@@ -1657,8 +1659,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + + /* If the client asked for DNSSEC don't use cached data. */ + if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || +- !do_bit || +- (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))) ++ (rd_bit && (!do_bit || cache_validated(crecp)) )) + do + { + /* don't answer wildcard queries with data not from /etc/hosts +@@ -1741,8 +1742,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + if (qtype == T_CNAME || qtype == T_ANY) + { + if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME | (dryrun ? F_NO_RR : 0))) && +- (qtype == T_CNAME || (crecp->flags & F_CONFIG)) && +- ((crecp->flags & F_CONFIG) || !do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))) ++ ((qtype == T_CNAME && rd_bit) || (crecp->flags & F_CONFIG)) && ++ ((crecp->flags & F_CONFIG) || (!do_bit || cache_validated(crecp)))) + { + if (!(crecp->flags & F_DNSSECOK)) + sec_data = 0; +@@ -1780,7 +1781,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + } + } + +- if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) && ++ if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) && + cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP | F_NO_RR)) + { + ans = 1; +-- +2.21.0 + diff --git a/SOURCES/dnsmasq-2.79-rh1728698-2.patch b/SOURCES/dnsmasq-2.79-rh1728698-2.patch new file mode 100644 index 0000000..9c335e5 --- /dev/null +++ b/SOURCES/dnsmasq-2.79-rh1728698-2.patch @@ -0,0 +1,48 @@ +From 7e3250d52921b5f75bdbe0b794514bb78a209969 Mon Sep 17 00:00:00 2001 +From: Petr Mensik +Date: Wed, 3 Jul 2019 17:02:16 +0200 +Subject: [PATCH 2/5] Compare address and interface index for allowed interface + +If interface is recreated with the same address but different index, it +would not change any other parameter. + +Test also address family on incoming TCP queries. +--- + src/dnsmasq.c | 3 ++- + src/network.c | 3 ++- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/src/dnsmasq.c b/src/dnsmasq.c +index f3d2671..7812be8 100644 +--- a/src/dnsmasq.c ++++ b/src/dnsmasq.c +@@ -1667,7 +1667,8 @@ static void check_dns_listeners(time_t now) + #endif + + for (iface = daemon->interfaces; iface; iface = iface->next) +- if (iface->index == if_index) ++ if (iface->index == if_index && ++ iface->addr.sa.sa_family == tcp_addr.sa.sa_family) + break; + + if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name)) +diff --git a/src/network.c b/src/network.c +index fd90288..f247811 100644 +--- a/src/network.c ++++ b/src/network.c +@@ -404,10 +404,11 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label, + /* check whether the interface IP has been added already + we call this routine multiple times. */ + for (iface = daemon->interfaces; iface; iface = iface->next) +- if (sockaddr_isequal(&iface->addr, addr)) ++ if (sockaddr_isequal(&iface->addr, addr) && iface->index == if_index) + { + iface->dad = !!(iface_flags & IFACE_TENTATIVE); + iface->found = 1; /* for garbage collection */ ++ iface->netmask = netmask; + return 1; + } + +-- +2.20.1 + diff --git a/SOURCES/dnsmasq-2.79-rh1728698-4.patch b/SOURCES/dnsmasq-2.79-rh1728698-4.patch new file mode 100644 index 0000000..86d7e02 --- /dev/null +++ b/SOURCES/dnsmasq-2.79-rh1728698-4.patch @@ -0,0 +1,188 @@ +From 11ab42e63f9089c4c14a391f30175d4c2d071e99 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= +Date: Mon, 15 Jul 2019 17:13:12 +0200 +Subject: [PATCH 4/5] Handle listening on duplicate addresses + +Save listening address into listener. Use it to find existing listeners +before creating new one. If it exist, increase just used counter. +Release only listeners not already used. + +Duplicates family in listener. +--- + src/dnsmasq.h | 3 +- + src/network.c | 115 ++++++++++++++++++++++++++++++++++++-------------- + 2 files changed, 85 insertions(+), 33 deletions(-) + +diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 89d138a..3b3f6ef 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -552,7 +552,8 @@ struct irec { + }; + + struct listener { +- int fd, tcpfd, tftpfd, family; ++ int fd, tcpfd, tftpfd, family, used; ++ union mysockaddr addr; + struct irec *iface; /* only sometimes valid for non-wildcard */ + struct listener *next; + }; +diff --git a/src/network.c b/src/network.c +index d6d4b01..4bbd810 100644 +--- a/src/network.c ++++ b/src/network.c +@@ -577,6 +577,56 @@ static void clean_interfaces() + } + } + ++/** Release listener if no other interface needs it. ++ * ++ * @return 1 if released, 0 if still required ++ */ ++static int release_listener(struct listener *l) ++{ ++ if (l->used > 1) ++ { ++ struct irec *iface; ++ for (iface = daemon->interfaces; iface; iface = iface->next) ++ if (iface->done && sockaddr_isequal(&l->addr, &iface->addr)) ++ { ++ if (iface->found) ++ { ++ /* update listener to point to active interface instead */ ++ if (!l->iface->found) ++ l->iface = iface; ++ } ++ else ++ { ++ l->used--; ++ iface->done = 0; ++ } ++ } ++ ++ /* Someone is still using this listener, skip its deletion */ ++ if (l->used > 0) ++ return 0; ++ } ++ ++ if (l->iface->done) ++ { ++ (void)prettyprint_addr(&l->iface->addr, daemon->addrbuff); ++ my_syslog(LOG_DEBUG, _("stopped listening on %s(#%d): %s"), ++ l->iface->name, l->iface->index, daemon->addrbuff); ++ /* In case it ever returns */ ++ l->iface->done = 0; ++ } ++ ++ if (l->fd != -1) ++ close(l->fd); ++ if (l->tcpfd != -1) ++ close(l->tcpfd); ++ if (l->tftpfd != -1) ++ close(l->tftpfd); ++ ++ free(l); ++ return 1; ++} ++ + int enumerate_interfaces(int reset) + { + static struct addrlist *spare = NULL; +@@ -684,29 +734,10 @@ int enumerate_interfaces(int reset) + + if (!l->iface || l->iface->found) + up = &l->next; +- else ++ else if (release_listener(l)) + { +- *up = l->next; +- if (l->iface->done) +- { +- iface = l->iface; +- (void)prettyprint_addr(&iface->addr, daemon->addrbuff); +- my_syslog(LOG_DEBUG, _("stopped listening on %s(#%d): %s"), +- iface->name, iface->index, daemon->addrbuff); +- } +- +- /* In case it ever returns */ +- l->iface->done = 0; +- +- if (l->fd != -1) +- close(l->fd); +- if (l->tcpfd != -1) +- close(l->tcpfd); +- if (l->tftpfd != -1) +- close(l->tftpfd); +- +- free(l); +- freed = 1; ++ *up = tmp; ++ freed = 1; + } + } + +@@ -959,7 +990,9 @@ static struct listener *create_listeners(union mysockaddr *addr, int do_tftp, in + l->family = addr->sa.sa_family; + l->fd = fd; + l->tcpfd = tcpfd; +- l->tftpfd = tftpfd; ++ l->tftpfd = tftpfd; ++ l->addr = *addr; ++ l->used = 1; + l->iface = NULL; + } + +@@ -1000,23 +1033,41 @@ void create_wildcard_listeners(void) + daemon->listeners = l; + } + ++static struct listener *find_listener(union mysockaddr *addr) ++{ ++ struct listener *l; ++ for (l = daemon->listeners; l; l = l->next) ++ if (sockaddr_isequal(&l->addr, addr)) ++ return l; ++ return NULL; ++} ++ + void create_bound_listeners(int dienow) + { + struct listener *new; + struct irec *iface; + struct iname *if_tmp; ++ struct listener *existing; + + for (iface = daemon->interfaces; iface; iface = iface->next) +- if (!iface->done && !iface->dad && iface->found && +- (new = create_listeners(&iface->addr, iface->tftp_ok, dienow))) ++ if (!iface->done && !iface->dad && iface->found) + { +- new->iface = iface; +- new->next = daemon->listeners; +- daemon->listeners = new; +- iface->done = 1; +- (void)prettyprint_addr(&iface->addr, daemon->addrbuff); +- my_syslog(LOG_DEBUG, _("listening on %s(#%d): %s"), +- iface->name, iface->index, daemon->addrbuff); ++ existing = find_listener(&iface->addr); ++ if (existing) ++ { ++ iface->done = 1; ++ existing->used++; /* increase usage counter */ ++ } ++ else if ((new = create_listeners(&iface->addr, iface->tftp_ok, dienow))) ++ { ++ new->iface = iface; ++ new->next = daemon->listeners; ++ daemon->listeners = new; ++ iface->done = 1; ++ (void)prettyprint_addr(&iface->addr, daemon->addrbuff); ++ my_syslog(LOG_DEBUG, _("listening on %s(#%d): %s"), ++ iface->name, iface->index, daemon->addrbuff); ++ } + } + + /* Check for --listen-address options that haven't been used because there's +-- +2.20.1 + diff --git a/SOURCES/dnsmasq-2.79-rh1746411.patch b/SOURCES/dnsmasq-2.79-rh1746411.patch new file mode 100644 index 0000000..74922c5 --- /dev/null +++ b/SOURCES/dnsmasq-2.79-rh1746411.patch @@ -0,0 +1,22 @@ +From: Simon Kelley +Date: Wed, 14 Aug 2019 20:52:50 +0000 (+0100) +Subject: Fix breakage of dhcp_lease_time utility. +X-Git-Url: http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commitdiff_plain;h=225accd235a09413ca253e710d7d691a3475c523 + +Fix breakage of dhcp_lease_time utility. +--- + +diff --git a/contrib/lease-tools/dhcp_lease_time.c b/contrib/lease-tools/dhcp_lease_time.c +index 697d627..91edbfa 100644 +--- a/contrib/lease-tools/dhcp_lease_time.c ++++ b/contrib/lease-tools/dhcp_lease_time.c +@@ -83,7 +83,7 @@ static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt + if (p >= end - 2) + return NULL; /* malformed packet */ + opt_len = option_len(p); +- if (end - p >= (2 + opt_len)) ++ if (end - p < (2 + opt_len)) + return NULL; /* malformed packet */ + if (*p == opt && opt_len >= minsize) + return p; + diff --git a/SOURCES/dnsmasq-2.79-rh1749092-fail.patch b/SOURCES/dnsmasq-2.79-rh1749092-fail.patch new file mode 100644 index 0000000..3550d61 --- /dev/null +++ b/SOURCES/dnsmasq-2.79-rh1749092-fail.patch @@ -0,0 +1,34 @@ +From 8fda4b4620ca2b23152ca805d14c7cde1083fe31 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= +Date: Tue, 1 Oct 2019 16:08:28 +0200 +Subject: [PATCH] Report error on dhcp_release + +If no IPv4 address is present on given interface, the tool would not +send any request. It would not report any error at the same time. Report +error if request send failed. + +Signed-off-by: Petr Mensik +--- + contrib/lease-tools/dhcp_release.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/contrib/lease-tools/dhcp_release.c b/contrib/lease-tools/dhcp_release.c +index c866cd9..30e77c6 100644 +--- a/contrib/lease-tools/dhcp_release.c ++++ b/contrib/lease-tools/dhcp_release.c +@@ -223,7 +223,11 @@ static struct in_addr find_interface(struct in_addr client, int fd, unsigned int + ifr->ifr_addr.sa_family = AF_INET; + if (ioctl(ifrfd, SIOCGIFADDR, ifr) != -1) + return ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr; +- exit(0); ++ else ++ { ++ fprintf(stderr, "error: local IPv4 address not found\n"); ++ exit(1); ++ } + } + else if (h->nlmsg_type == RTM_NEWADDR) + { +-- +2.20.1 + diff --git a/SPECS/dnsmasq.spec b/SPECS/dnsmasq.spec index 2a433d2..74fb635 100644 --- a/SPECS/dnsmasq.spec +++ b/SPECS/dnsmasq.spec @@ -13,7 +13,7 @@ Name: dnsmasq Version: 2.79 -Release: 4%{?extraversion:.%{extraversion}}%{?dist} +Release: 9%{?extraversion:.%{extraversion}}%{?dist} Summary: A lightweight DHCP/caching DNS server License: GPLv2 or GPLv3 @@ -26,6 +26,19 @@ Source2: dnsmasq-systemd-sysusers.conf Patch1: dnsmasq-2.77-underflow.patch Patch3: dnsmasq-2.78-fips.patch Patch4: dnsmasq-2.80-dnssec.patch +Patch5: dnsmasq-2.79-rh1602477.patch +# Few changes not yet in upstream +Patch6: dnsmasq-2.79-rh1602477-2.patch +# commit 60ac10d8d86e6f95ab0f06abe6c42596adcedcb8 +Patch7: dnsmasq-2.76-rh1752569.patch +# Report failure when no release would be sent +Patch8: dnsmasq-2.79-rh1749092-fail.patch +Patch9: dnsmasq-2.76-rh1728698-1.patch +Patch10: dnsmasq-2.79-rh1728698-2.patch +Patch11: dnsmasq-2.76-rh1728698-3.patch +Patch12: dnsmasq-2.79-rh1728698-4.patch +Patch13: dnsmasq-2.79-rh1746411.patch +Patch14: dnsmasq-2.79-rh1700916.patch # This is workaround to nettle bug #1549190 # https://bugzilla.redhat.com/show_bug.cgi?id=1549190 @@ -63,6 +76,16 @@ server's leases. %patch1 -p1 -b .underflow %patch3 -p1 -b .fips %patch4 -p1 -b .dnssec +%patch5 -p1 -b .rh1602477 +%patch6 -p1 -b .rh1602477-2 +%patch7 -p1 -b .rh1752569 +%patch8 -p1 -b .rh1752569 +%patch9 -p1 -b .rh1728698-1 +%patch10 -p1 -b .rh1728698-2 +%patch11 -p1 -b .rh1728698-3 +%patch12 -p1 -b .rh1728698-4 +%patch13 -p1 -b .rh1746411 +%patch14 -p1 -b .rh1700916 # use /var/lib/dnsmasq instead of /var/lib/misc for file in dnsmasq.conf.example man/dnsmasq.8 man/es/dnsmasq.8 src/config.h; do @@ -163,6 +186,21 @@ install -Dpm 644 %{SOURCE2} %{buildroot}%{_sysusersdir}/dnsmasq.conf %{_mandir}/man1/dhcp_* %changelog +* Tue Dec 10 2019 Tomas Korbar - 2.79-9 +- Fix replies to non-recursive queries (#1700916) + +* Mon Dec 09 2019 Tomas Korbar - 2.79-8 +- Fix dhcp_lease_time (#1746411) + +* Mon Dec 09 2019 Tomas Korbar - 2.79-7 +- Fix TCP queries after interface recreation (#1728698) + +* Mon Sep 30 2019 Petr Menšík - 2.79-6 +- Send dhcp_release even for addresses not on local network (#1749092) + +* Thu Jul 18 2019 Petr Menšík - 2.79-5 +- Fix Coverity detected issues (#1602477) + * Thu Jul 26 2018 Zbigniew Jędrzejewski-Szmek - 2.79-4 - Fix %%pre scriptlet (#1548050)