+From b3c0d11d8bcf810b8f50f695ea36c8183fa4791a Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 28 Apr 2014 00:51:13 +0200
+Subject: [PATCH] Add support to read ISC DHCP lease file.
+
+---
+ Makefile | 2 +-
+ src/cache.c | 13 +++-
+ src/dnsmasq.c | 5 ++
+ src/dnsmasq.h | 5 ++
+ src/isc.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ src/option.c | 2 +-
+ 6 files changed, 256 insertions(+), 5 deletions(-)
+ create mode 100644 src/isc.c
+
+diff --git a/Makefile b/Makefile
+index 292c8bd..5e0cdbe 100644
+--- a/Makefile
++++ b/Makefile
+@@ -69,7 +69,7 @@ objs = cache.o rfc1035.o util.o option.o forward.o network.o \
+ dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
+ helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
+ dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
+- domain.o dnssec.o blockdata.o
++ domain.o dnssec.o blockdata.o isc.o
+
+ hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
+ dns-protocol.h radv-protocol.h ip6addr.h
+diff --git a/src/cache.c b/src/cache.c
+index 5cec918..1f5657f 100644
+--- a/src/cache.c
++++ b/src/cache.c
+@@ -17,7 +17,7 @@
+ #include "dnsmasq.h"
+
+ static struct crec *cache_head = NULL, *cache_tail = NULL, **hash_table = NULL;
+-#ifdef HAVE_DHCP
++#if (defined HAVE_DHCP) || (defined HAVE_ISC_READER)
+ static struct crec *dhcp_spare = NULL;
+ #endif
+ static struct crec *new_chain = NULL;
+@@ -222,6 +222,9 @@ static void cache_free(struct crec *crecp)
+ crecp->flags &= ~F_BIGNAME;
+ }
+
++ if (crecp->flags & F_DHCP)
++ free(crecp->name.namep);
++
+ #ifdef HAVE_DNSSEC
+ cache_blockdata_free(crecp);
+ #endif
+@@ -1110,7 +1113,7 @@ void cache_reload(void)
+ total_size = read_hostsfile(ah->fname, ah->index, total_size, (struct crec **)daemon->packet, revhashsz);
+ }
+
+-#ifdef HAVE_DHCP
++#if (defined HAVE_DHCP) || (defined HAVE_ISC_READER)
+ struct in_addr a_record_from_hosts(char *name, time_t now)
+ {
+ struct crec *crecp = NULL;
+@@ -1188,7 +1191,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
+ addrlen = sizeof(struct in6_addr);
+ }
+ #endif
+-
++
+ inet_ntop(prot, host_address, daemon->addrbuff, ADDRSTRLEN);
+
+ while ((crec = cache_find_by_name(crec, host_name, 0, flags | F_CNAME)))
+@@ -1253,7 +1256,11 @@ void cache_add_dhcp_entry(char *host_name, int prot,
+ else
+ crec->ttd = ttd;
+ crec->addr.addr = *host_address;
++#ifdef HAVE_ISC_READER
++ crec->name.namep = strdup(host_name);
++#else
+ crec->name.namep = host_name;
++#endif
+ crec->uid = next_uid();
+ cache_hash(crec);
+
+diff --git a/src/dnsmasq.c b/src/dnsmasq.c
+index 1c96a0e..156ac9a 100644
+--- a/src/dnsmasq.c
++++ b/src/dnsmasq.c
+@@ -934,6 +934,11 @@ int main (int argc, char **argv)
+
+ poll_resolv(0, daemon->last_resolv != 0, now);
+ daemon->last_resolv = now;
++
++#ifdef HAVE_ISC_READER
++ if (daemon->lease_file && !daemon->dhcp)
++ load_dhcp(now);
++#endif
+ }
+
+ if (FD_ISSET(piperead, &rset))
+diff --git a/src/dnsmasq.h b/src/dnsmasq.h
+index 3032546..a40b2a9 100644
+--- a/src/dnsmasq.h
++++ b/src/dnsmasq.h
+@@ -1447,3 +1447,8 @@ void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force);
+ time_t periodic_slaac(time_t now, struct dhcp_lease *leases);
+ void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface, struct dhcp_lease *leases);
+ #endif
++
++/* isc.c */
++#ifdef HAVE_ISC_READER
++void load_dhcp(time_t now);
++#endif
+diff --git a/src/isc.c b/src/isc.c
+new file mode 100644
+index 0000000..565e4e2
+--- /dev/null
++++ b/src/isc.c
+@@ -0,0 +1,234 @@
++/* dnsmasq is Copyright (c) 2014 John Volpe, Simon Kelley and
++ Michael Tremer
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; version 2 dated June, 1991, or
++ (at your option) version 3 dated 29 June, 2007.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <http://www.gnu.org/licenses/>.
++
++ Code in this file is based on contributions by John Volpe and
++ Simon Kelley. Updated for recent versions of dnsmasq by
++ Michael Tremer.
++*/
++
++#include "dnsmasq.h"
++
++#ifdef HAVE_ISC_READER
++#define MAXTOK 50
++
++struct isc_dhcp_lease {
++ char* name;
++ char* fqdn;
++ time_t expires;
++ struct in_addr addr;
++ struct isc_dhcp_lease* next;
++};
++
++static struct isc_dhcp_lease* dhcp_lease_new() {
++ struct isc_dhcp_lease* lease = whine_malloc(sizeof(*lease));
++
++ lease->name = NULL;
++ lease->fqdn = NULL;
++ lease->next = NULL;
++
++ return lease;
++}
++
++static void dhcp_lease_free(struct isc_dhcp_lease* lease) {
++ if (!lease)
++ return;
++
++ if (lease->name)
++ free(lease->name);
++ if (lease->fqdn)
++ free(lease->fqdn);
++ free(lease);
++}
++
++static int next_token(char* token, int buffsize, FILE* fp) {
++ int c, count = 0;
++ char* cp = token;
++
++ while ((c = getc(fp)) != EOF) {
++ if (c == '#') {
++ do {
++ c = getc(fp);
++ } while (c != '\n' && c != EOF);
++ }
++
++ if (c == ' ' || c == '\t' || c == '\n' || c == ';') {
++ if (count)
++ break;
++ } else if ((c != '"') && (count < buffsize - 1)) {
++ *cp++ = c;
++ count++;
++ }
++ }
++
++ *cp = 0;
++ return count ? 1 : 0;
++}
++
++static time_t parse_lease_time(const char* token_date, const char* token_time) {
++ time_t time = (time_t)(-1);
++ struct tm lease_time;
++
++ static const int months[11] = {31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
++
++ if (sscanf(token_date, "%d/%d/%d", &lease_time.tm_year, &lease_time.tm_mon, &lease_time.tm_mday) == 3) {
++ if (sscanf(token_time, "%d:%d:%d", &lease_time.tm_hour, &lease_time.tm_min, &lease_time.tm_sec) == 3) {
++ /* There doesn't seem to be a universally available library function
++ which converts broken-down _GMT_ time to seconds-in-epoch.
++ The following was borrowed from ISC dhcpd sources, where
++ it is noted that it might not be entirely accurate for odd seconds.
++ Since we're trying to get the same answer as dhcpd, that's just
++ fine here. */
++ time = ((((((365 * (lease_time.tm_year - 1970) +
++ (lease_time.tm_year - 1969) / 4 +
++ (lease_time.tm_mon > 1 ? months[lease_time.tm_mon - 2] : 0) +
++ (lease_time.tm_mon > 2 && !((lease_time.tm_year - 1972) & 3 )) +
++ (lease_time.tm_mday - 1)) * 24) +
++ lease_time.tm_hour) * 60) +
++ lease_time.tm_mon) * 60) +
++ lease_time.tm_sec;
++ }
++ }
++
++ return time;
++}
++
++static off_t lease_file_size = (off_t)0;
++static ino_t lease_file_inode = (ino_t)0;
++
++void load_dhcp(time_t now) {
++ struct isc_dhcp_lease* leases = NULL;
++
++ struct stat statbuf;
++ if (stat(daemon->lease_file, &statbuf) == -1) {
++ return;
++ }
++
++ /* Do nothing if the lease file has not changed. */
++ if ((statbuf.st_size <= lease_file_size) && (statbuf.st_ino == lease_file_inode))
++ return;
++
++ lease_file_size = statbuf.st_size;
++ lease_file_inode = statbuf.st_ino;
++
++ FILE* fp = fopen(daemon->lease_file, "r");
++ if (!fp) {
++ my_syslog(LOG_ERR, _("failed to load %s:%s"), daemon->lease_file, strerror(errno));
++ return;
++ }
++
++ my_syslog(LOG_INFO, _("reading %s"), daemon->lease_file);
++
++ char* hostname = daemon->namebuff;
++ struct in_addr host_address;
++ time_t time_starts = -1;
++ time_t time_ends = -1;
++ int nomem;
++
++ char token[MAXTOK];
++ while ((next_token(token, MAXTOK, fp))) {
++ if (strcmp(token, "lease") == 0) {
++ hostname[0] = '\0';
++
++ if (next_token(token, MAXTOK, fp) && ((host_address.s_addr = inet_addr(token)) != (in_addr_t)-1)) {
++ if (next_token(token, MAXTOK, fp) && *token == '{') {
++ while (next_token(token, MAXTOK, fp) && *token != '}') {
++ if ((strcmp(token, "client-hostname") == 0) || (strcmp(token, "hostname") == 0)) {
++ if (next_token(hostname, MAXDNAME, fp)) {
++ if (!canonicalise(hostname, &nomem)) {
++ *hostname = 0;
++ my_syslog(LOG_ERR, _("bad name in %s"), daemon->lease_file);
++ }
++ }
++ } else if ((strcmp(token, "starts") == 0) || (strcmp(token, "ends") == 0)) {
++ char token_date[MAXTOK];
++ char token_time[MAXTOK];
++
++ int is_starts = strcmp(token, "starts") == 0;
++
++ // Throw away the weekday and parse the date.
++ if (next_token(token, MAXTOK, fp) && next_token(token_date, MAXTOK, fp) && next_token(token_time, MAXTOK, fp)) {
++ time_t time = parse_lease_time(token_date, token_time);
++
++ if (is_starts)
++ time_starts = time;
++ else
++ time_ends = time;
++ }
++ }
++ }
++
++ if (!*hostname)
++ continue;
++
++ if ((time_starts == -1) || (time_ends == -1))
++ continue;
++
++ if (difftime(now, time_ends) > 0)
++ continue;
++
++ char* dot = strchr(hostname, '.');
++ if (dot) {
++ if (!daemon->domain_suffix || hostname_isequal(dot + 1, daemon->domain_suffix)) {
++ my_syslog(LOG_WARNING,
++ _("Ignoring DHCP lease for %s because it has an illegal domain part"),
++ hostname);
++ continue;
++ }
++ *dot = 0;
++ }
++
++ struct isc_dhcp_lease* lease = dhcp_lease_new();
++ lease->name = strdup(hostname);
++ lease->addr = host_address;
++ lease->expires = time_ends;
++ lease->next = NULL;
++
++ if (daemon->domain_suffix) {
++ asprintf(&lease->fqdn, "%s.%s", hostname, daemon->domain_suffix);
++ }
++
++ if (!leases)
++ leases = lease;
++ else
++ leases->next = lease;
++ }
++ }
++ }
++ }
++
++ fclose(fp);
++
++ // Drop all entries.
++ cache_unhash_dhcp();
++
++ while (leases) {
++ struct isc_dhcp_lease *lease = leases;
++ leases = lease->next;
++
++ if (lease->fqdn) {
++ cache_add_dhcp_entry(lease->fqdn, AF_INET, (struct all_addr*)&lease->addr.s_addr, lease->expires);
++ }
++
++ if (lease->name) {
++ cache_add_dhcp_entry(lease->name, AF_INET, (struct all_addr*)&lease->addr.s_addr, lease->expires);
++ }
++
++ // Cleanup
++ dhcp_lease_free(lease);
++ }
++}
++
++#endif
+diff --git a/src/option.c b/src/option.c
+index daa728f..d16c982 100644
+--- a/src/option.c
++++ b/src/option.c
+@@ -1642,7 +1642,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
+ ret_err(_("bad MX target"));
+ break;
+
+-#ifdef HAVE_DHCP
++#if (defined HAVE_DHCP) || (defined HAVE_ISC_READER)
+ case 'l': /* --dhcp-leasefile */
+ daemon->lease_file = opt_string_alloc(arg);
+ break;
+--
+1.9.0
+