]> git.ipfire.org Git - people/ms/dnsmasq.git/commitdiff
Add support to read ISC DHCP lease file.
authorMichael Tremer <michael.tremer@ipfire.org>
Sun, 27 Apr 2014 22:51:13 +0000 (00:51 +0200)
committerMichael Tremer <michael.tremer@ipfire.org>
Thu, 4 Feb 2016 23:37:54 +0000 (23:37 +0000)
Makefile
src/cache.c
src/dnsmasq.c
src/dnsmasq.h
src/isc.c [new file with mode: 0644]
src/option.c

index dd0513ba9adbb7f68c525be8e358ad1a7e51368d..67abb2f8d8805199d96bd6916ce26dd67f4f3480 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -74,7 +74,7 @@ objs = cache.o rfc1035.o util.o option.o forward.o network.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 tables.o loop.o inotify.o \
-       poll.o rrfilter.o edns0.o arp.o
+       poll.o rrfilter.o edns0.o arp.o isc.o
 
 hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
        dns-protocol.h radv-protocol.h ip6addr.h
index a9eaa65445ea9b21bc0e16072eef5ea9679813f3..9835d0538a2455acab54b49572daa9f76acc1689 100644 (file)
@@ -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;
@@ -217,6 +217,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
@@ -1131,7 +1134,7 @@ void cache_reload(void)
   
 } 
 
-#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;
@@ -1209,7 +1212,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)))
@@ -1274,7 +1277,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);
 
index 96aa7802aecd8b28db6e4d92d69d57384cd6b432..0a72f240d5374252730e1834ba75e71dc9b1eea8 100644 (file)
@@ -1013,6 +1013,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
        }
 #endif
 
index 549ef55439483664e1683d4fdf432983ee1b35dd..7bb205529c577e52ed35fa249504acd3d3fe1f91 100644 (file)
@@ -1503,6 +1503,10 @@ int detect_loop(char *query, int type);
 void inotify_dnsmasq_init();
 int inotify_check(time_t now);
 void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz);
+
+/* isc.c */
+#ifdef HAVE_ISC_READER
+void load_dhcp(time_t now);
 #endif
 
 /* poll.c */
diff --git a/src/isc.c b/src/isc.c
new file mode 100644 (file)
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
index ac35e7c8e720fd504ee31a8edf9e8892bc9af1e2..d5a9f1a30a260ae38918c660e44e4cc624115277 100644 (file)
@@ -1763,7 +1763,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;