1 diff --git a/Makefile b/Makefile
2 index 4c87ea9..4e0ea10 100644
5 @@ -73,7 +73,8 @@ objs = cache.o rfc1035.o util.o option.o forward.o network.o \
6 dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
7 helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
8 dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
9 - domain.o dnssec.o blockdata.o tables.o loop.o inotify.o poll.o
10 + domain.o dnssec.o blockdata.o tables.o loop.o inotify.o poll.o \
13 hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
14 dns-protocol.h radv-protocol.h ip6addr.h
15 diff --git a/src/cache.c b/src/cache.c
16 index 178d654..6d0b131 100644
22 static struct crec *cache_head = NULL, *cache_tail = NULL, **hash_table = NULL;
24 +#if (defined HAVE_DHCP) || (defined HAVE_ISC_READER)
25 static struct crec *dhcp_spare = NULL;
27 static struct crec *new_chain = NULL;
28 @@ -222,6 +222,9 @@ static void cache_free(struct crec *crecp)
29 crecp->flags &= ~F_BIGNAME;
32 + if (crecp->flags & F_DHCP)
33 + free(crecp->name.namep);
36 cache_blockdata_free(crecp);
38 @@ -1151,7 +1154,7 @@ void cache_reload(void)
43 +#if (defined HAVE_DHCP) || (defined HAVE_ISC_READER)
44 struct in_addr a_record_from_hosts(char *name, time_t now)
46 struct crec *crecp = NULL;
47 @@ -1229,7 +1232,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
48 addrlen = sizeof(struct in6_addr);
53 inet_ntop(prot, host_address, daemon->addrbuff, ADDRSTRLEN);
55 while ((crec = cache_find_by_name(crec, host_name, 0, flags | F_CNAME)))
56 @@ -1294,7 +1297,11 @@ void cache_add_dhcp_entry(char *host_name, int prot,
59 crec->addr.addr = *host_address;
60 +#ifdef HAVE_ISC_READER
61 + crec->name.namep = strdup(host_name);
63 crec->name.namep = host_name;
65 crec->uid = next_uid();
68 diff --git a/src/dnsmasq.c b/src/dnsmasq.c
69 index 81254f6..ce2d1a7 100644
72 @@ -982,6 +982,11 @@ int main (int argc, char **argv)
74 poll_resolv(0, daemon->last_resolv != 0, now);
75 daemon->last_resolv = now;
77 +#ifdef HAVE_ISC_READER
78 + if (daemon->lease_file && !daemon->dhcp)
84 diff --git a/src/dnsmasq.h b/src/dnsmasq.h
85 index cf1a782..30437aa 100644
88 @@ -1519,3 +1519,7 @@ int poll_check(int fd, short event);
89 void poll_listen(int fd, short event);
90 int do_poll(int timeout);
93 +#ifdef HAVE_ISC_READER
94 +void load_dhcp(time_t now);
96 diff --git a/src/isc.c b/src/isc.c
98 index 0000000..5106442
102 +/* dnsmasq is Copyright (c) 2014 John Volpe, Simon Kelley and
105 + This program is free software; you can redistribute it and/or modify
106 + it under the terms of the GNU General Public License as published by
107 + the Free Software Foundation; version 2 dated June, 1991, or
108 + (at your option) version 3 dated 29 June, 2007.
110 + This program is distributed in the hope that it will be useful,
111 + but WITHOUT ANY WARRANTY; without even the implied warranty of
112 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
113 + GNU General Public License for more details.
115 + You should have received a copy of the GNU General Public License
116 + along with this program. If not, see <http://www.gnu.org/licenses/>.
118 + Code in this file is based on contributions by John Volpe and
119 + Simon Kelley. Updated for recent versions of dnsmasq by
123 +#include "dnsmasq.h"
125 +#ifdef HAVE_ISC_READER
128 +struct isc_dhcp_lease {
132 + struct in_addr addr;
133 + struct isc_dhcp_lease* next;
136 +static struct isc_dhcp_lease* dhcp_lease_new(const char* hostname) {
137 + struct isc_dhcp_lease* lease = whine_malloc(sizeof(*lease));
139 + lease->name = strdup(hostname);
140 + if (daemon->domain_suffix) {
141 + asprintf(&lease->fqdn, "%s.%s", hostname, daemon->domain_suffix);
143 + lease->expires = 0;
144 + lease->next = NULL;
149 +static void dhcp_lease_free(struct isc_dhcp_lease* lease) {
160 +static int next_token(char* token, int buffsize, FILE* fp) {
164 + while ((c = getc(fp)) != EOF) {
168 + } while (c != '\n' && c != EOF);
171 + if (c == ' ' || c == '\t' || c == '\n' || c == ';') {
174 + } else if ((c != '"') && (count < buffsize - 1)) {
181 + return count ? 1 : 0;
184 +static long get_utc_offset() {
185 + time_t t = time(NULL);
186 + struct tm* time_struct = localtime(&t);
188 + return time_struct->tm_gmtoff;
191 +static time_t parse_lease_time(const char* token_date, const char* token_time) {
192 + time_t time = (time_t)(-1);
193 + struct tm lease_time;
195 + if (sscanf(token_date, "%d/%d/%d", &lease_time.tm_year, &lease_time.tm_mon, &lease_time.tm_mday) == 3) {
196 + lease_time.tm_year -= 1900;
197 + lease_time.tm_mon -= 1;
199 + if (sscanf(token_time, "%d:%d:%d", &lease_time.tm_hour, &lease_time.tm_min, &lease_time.tm_sec) == 3) {
200 + time = mktime(&lease_time) + get_utc_offset();
207 +static struct isc_dhcp_lease* find_lease(const char* hostname, struct isc_dhcp_lease* leases) {
208 + struct isc_dhcp_lease* lease = leases;
211 + if (strcmp(hostname, lease->name) == 0) {
214 + lease = lease->next;
220 +static off_t lease_file_size = (off_t)0;
221 +static ino_t lease_file_inode = (ino_t)0;
223 +void load_dhcp(time_t now) {
224 + struct isc_dhcp_lease* leases = NULL;
226 + struct stat statbuf;
227 + if (stat(daemon->lease_file, &statbuf) == -1) {
231 + /* Do nothing if the lease file has not changed. */
232 + if ((statbuf.st_size <= lease_file_size) && (statbuf.st_ino == lease_file_inode))
235 + lease_file_size = statbuf.st_size;
236 + lease_file_inode = statbuf.st_ino;
238 + FILE* fp = fopen(daemon->lease_file, "r");
240 + my_syslog(LOG_ERR, _("failed to load %s:%s"), daemon->lease_file, strerror(errno));
244 + my_syslog(LOG_INFO, _("reading %s"), daemon->lease_file);
246 + char* hostname = daemon->namebuff;
247 + struct in_addr host_address;
248 + time_t time_starts = -1;
249 + time_t time_ends = -1;
252 + char token[MAXTOK];
253 + while ((next_token(token, MAXTOK, fp))) {
254 + if (strcmp(token, "lease") == 0) {
255 + hostname[0] = '\0';
257 + if (next_token(token, MAXTOK, fp) && ((host_address.s_addr = inet_addr(token)) != (in_addr_t)-1)) {
258 + if (next_token(token, MAXTOK, fp) && *token == '{') {
259 + while (next_token(token, MAXTOK, fp) && *token != '}') {
260 + if ((strcmp(token, "client-hostname") == 0) || (strcmp(token, "hostname") == 0)) {
261 + if (next_token(hostname, MAXDNAME, fp)) {
262 + if (!canonicalise(hostname, &nomem)) {
264 + my_syslog(LOG_ERR, _("bad name in %s"), daemon->lease_file);
267 + } else if ((strcmp(token, "starts") == 0) || (strcmp(token, "ends") == 0)) {
268 + char token_date[MAXTOK];
269 + char token_time[MAXTOK];
271 + int is_starts = strcmp(token, "starts") == 0;
273 + // Throw away the weekday and parse the date.
274 + if (next_token(token, MAXTOK, fp) && next_token(token_date, MAXTOK, fp) && next_token(token_time, MAXTOK, fp)) {
275 + time_t time = parse_lease_time(token_date, token_time);
278 + time_starts = time;
288 + if ((time_starts == -1) || (time_ends == -1))
291 + if (difftime(now, time_ends) > 0)
294 + char* dot = strchr(hostname, '.');
296 + if (!daemon->domain_suffix || hostname_isequal(dot + 1, daemon->domain_suffix)) {
297 + my_syslog(LOG_WARNING,
298 + _("Ignoring DHCP lease for %s because it has an illegal domain part"),
305 + // Search for an existing lease in the list
306 + // with the given host name and update the data
308 + struct isc_dhcp_lease* lease = find_lease(hostname, leases);
310 + // If no lease already exists, we create a new one
311 + // and append it to the list.
313 + lease = dhcp_lease_new(hostname);
315 + lease->next = leases;
319 + // Only update more recent leases.
320 + if (lease->expires > time_ends)
323 + lease->addr = host_address;
324 + lease->expires = time_ends;
332 + // Drop all entries.
333 + cache_unhash_dhcp();
336 + struct isc_dhcp_lease *lease = leases;
337 + leases = lease->next;
340 + cache_add_dhcp_entry(lease->fqdn, AF_INET, (struct all_addr*)&lease->addr.s_addr, lease->expires);
344 + cache_add_dhcp_entry(lease->name, AF_INET, (struct all_addr*)&lease->addr.s_addr, lease->expires);
348 + dhcp_lease_free(lease);
353 diff --git a/src/option.c b/src/option.c
354 index ecc2619..527c5aa 100644
357 @@ -1699,7 +1699,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
358 ret_err(_("bad MX target"));
362 +#if (defined HAVE_DHCP) || (defined HAVE_ISC_READER)
363 case 'l': /* --dhcp-leasefile */
364 daemon->lease_file = opt_string_alloc(arg);