]>
Commit | Line | Data |
---|---|---|
45e65f1d MT |
1 | From b3c0d11d8bcf810b8f50f695ea36c8183fa4791a Mon Sep 17 00:00:00 2001 |
2 | From: Michael Tremer <michael.tremer@ipfire.org> | |
3 | Date: Mon, 28 Apr 2014 00:51:13 +0200 | |
4 | Subject: [PATCH] Add support to read ISC DHCP lease file. | |
5 | ||
6 | --- | |
7 | Makefile | 2 +- | |
8 | src/cache.c | 13 +++- | |
9 | src/dnsmasq.c | 5 ++ | |
10 | src/dnsmasq.h | 5 ++ | |
11 | src/isc.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | |
12 | src/option.c | 2 +- | |
13 | 6 files changed, 256 insertions(+), 5 deletions(-) | |
14 | create mode 100644 src/isc.c | |
15 | ||
16 | diff --git a/Makefile b/Makefile | |
17 | index 292c8bd..5e0cdbe 100644 | |
18 | --- a/Makefile | |
19 | +++ b/Makefile | |
20 | @@ -69,7 +69,7 @@ objs = cache.o rfc1035.o util.o option.o forward.o network.o \ | |
21 | dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \ | |
22 | helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \ | |
23 | dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \ | |
24 | - domain.o dnssec.o blockdata.o | |
25 | + domain.o dnssec.o blockdata.o isc.o | |
26 | ||
27 | hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \ | |
28 | dns-protocol.h radv-protocol.h ip6addr.h | |
29 | diff --git a/src/cache.c b/src/cache.c | |
30 | index 5cec918..1f5657f 100644 | |
31 | --- a/src/cache.c | |
32 | +++ b/src/cache.c | |
33 | @@ -17,7 +17,7 @@ | |
34 | #include "dnsmasq.h" | |
35 | ||
36 | static struct crec *cache_head = NULL, *cache_tail = NULL, **hash_table = NULL; | |
37 | -#ifdef HAVE_DHCP | |
38 | +#if (defined HAVE_DHCP) || (defined HAVE_ISC_READER) | |
39 | static struct crec *dhcp_spare = NULL; | |
40 | #endif | |
41 | static struct crec *new_chain = NULL; | |
42 | @@ -222,6 +222,9 @@ static void cache_free(struct crec *crecp) | |
43 | crecp->flags &= ~F_BIGNAME; | |
44 | } | |
45 | ||
46 | + if (crecp->flags & F_DHCP) | |
47 | + free(crecp->name.namep); | |
48 | + | |
49 | #ifdef HAVE_DNSSEC | |
50 | cache_blockdata_free(crecp); | |
51 | #endif | |
52 | @@ -1110,7 +1113,7 @@ void cache_reload(void) | |
53 | total_size = read_hostsfile(ah->fname, ah->index, total_size, (struct crec **)daemon->packet, revhashsz); | |
54 | } | |
55 | ||
56 | -#ifdef HAVE_DHCP | |
57 | +#if (defined HAVE_DHCP) || (defined HAVE_ISC_READER) | |
58 | struct in_addr a_record_from_hosts(char *name, time_t now) | |
59 | { | |
60 | struct crec *crecp = NULL; | |
61 | @@ -1188,7 +1191,7 @@ void cache_add_dhcp_entry(char *host_name, int prot, | |
62 | addrlen = sizeof(struct in6_addr); | |
63 | } | |
64 | #endif | |
65 | - | |
66 | + | |
67 | inet_ntop(prot, host_address, daemon->addrbuff, ADDRSTRLEN); | |
68 | ||
69 | while ((crec = cache_find_by_name(crec, host_name, 0, flags | F_CNAME))) | |
70 | @@ -1253,7 +1256,11 @@ void cache_add_dhcp_entry(char *host_name, int prot, | |
71 | else | |
72 | crec->ttd = ttd; | |
73 | crec->addr.addr = *host_address; | |
74 | +#ifdef HAVE_ISC_READER | |
75 | + crec->name.namep = strdup(host_name); | |
76 | +#else | |
77 | crec->name.namep = host_name; | |
78 | +#endif | |
79 | crec->uid = next_uid(); | |
80 | cache_hash(crec); | |
81 | ||
82 | diff --git a/src/dnsmasq.c b/src/dnsmasq.c | |
83 | index 1c96a0e..156ac9a 100644 | |
84 | --- a/src/dnsmasq.c | |
85 | +++ b/src/dnsmasq.c | |
86 | @@ -934,6 +934,11 @@ int main (int argc, char **argv) | |
87 | ||
88 | poll_resolv(0, daemon->last_resolv != 0, now); | |
89 | daemon->last_resolv = now; | |
90 | + | |
91 | +#ifdef HAVE_ISC_READER | |
92 | + if (daemon->lease_file && !daemon->dhcp) | |
93 | + load_dhcp(now); | |
94 | +#endif | |
95 | } | |
96 | ||
97 | if (FD_ISSET(piperead, &rset)) | |
98 | diff --git a/src/dnsmasq.h b/src/dnsmasq.h | |
99 | index 3032546..a40b2a9 100644 | |
100 | --- a/src/dnsmasq.h | |
101 | +++ b/src/dnsmasq.h | |
102 | @@ -1447,3 +1447,8 @@ void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force); | |
103 | time_t periodic_slaac(time_t now, struct dhcp_lease *leases); | |
104 | void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface, struct dhcp_lease *leases); | |
105 | #endif | |
106 | + | |
107 | +/* isc.c */ | |
108 | +#ifdef HAVE_ISC_READER | |
109 | +void load_dhcp(time_t now); | |
110 | +#endif | |
111 | diff --git a/src/isc.c b/src/isc.c | |
112 | new file mode 100644 | |
113 | index 0000000..565e4e2 | |
114 | --- /dev/null | |
115 | +++ b/src/isc.c | |
116 | @@ -0,0 +1,234 @@ | |
117 | +/* dnsmasq is Copyright (c) 2014 John Volpe, Simon Kelley and | |
118 | + Michael Tremer | |
119 | + | |
120 | + This program is free software; you can redistribute it and/or modify | |
121 | + it under the terms of the GNU General Public License as published by | |
122 | + the Free Software Foundation; version 2 dated June, 1991, or | |
123 | + (at your option) version 3 dated 29 June, 2007. | |
124 | + | |
125 | + This program is distributed in the hope that it will be useful, | |
126 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
127 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
128 | + GNU General Public License for more details. | |
129 | + | |
130 | + You should have received a copy of the GNU General Public License | |
131 | + along with this program. If not, see <http://www.gnu.org/licenses/>. | |
132 | + | |
133 | + Code in this file is based on contributions by John Volpe and | |
134 | + Simon Kelley. Updated for recent versions of dnsmasq by | |
135 | + Michael Tremer. | |
136 | +*/ | |
137 | + | |
138 | +#include "dnsmasq.h" | |
139 | + | |
140 | +#ifdef HAVE_ISC_READER | |
141 | +#define MAXTOK 50 | |
142 | + | |
143 | +struct isc_dhcp_lease { | |
144 | + char* name; | |
145 | + char* fqdn; | |
146 | + time_t expires; | |
147 | + struct in_addr addr; | |
148 | + struct isc_dhcp_lease* next; | |
149 | +}; | |
150 | + | |
151 | +static struct isc_dhcp_lease* dhcp_lease_new() { | |
152 | + struct isc_dhcp_lease* lease = whine_malloc(sizeof(*lease)); | |
153 | + | |
154 | + lease->name = NULL; | |
155 | + lease->fqdn = NULL; | |
156 | + lease->next = NULL; | |
157 | + | |
158 | + return lease; | |
159 | +} | |
160 | + | |
161 | +static void dhcp_lease_free(struct isc_dhcp_lease* lease) { | |
162 | + if (!lease) | |
163 | + return; | |
164 | + | |
165 | + if (lease->name) | |
166 | + free(lease->name); | |
167 | + if (lease->fqdn) | |
168 | + free(lease->fqdn); | |
169 | + free(lease); | |
170 | +} | |
171 | + | |
172 | +static int next_token(char* token, int buffsize, FILE* fp) { | |
173 | + int c, count = 0; | |
174 | + char* cp = token; | |
175 | + | |
176 | + while ((c = getc(fp)) != EOF) { | |
177 | + if (c == '#') { | |
178 | + do { | |
179 | + c = getc(fp); | |
180 | + } while (c != '\n' && c != EOF); | |
181 | + } | |
182 | + | |
183 | + if (c == ' ' || c == '\t' || c == '\n' || c == ';') { | |
184 | + if (count) | |
185 | + break; | |
186 | + } else if ((c != '"') && (count < buffsize - 1)) { | |
187 | + *cp++ = c; | |
188 | + count++; | |
189 | + } | |
190 | + } | |
191 | + | |
192 | + *cp = 0; | |
193 | + return count ? 1 : 0; | |
194 | +} | |
195 | + | |
196 | +static time_t parse_lease_time(const char* token_date, const char* token_time) { | |
197 | + time_t time = (time_t)(-1); | |
198 | + struct tm lease_time; | |
199 | + | |
200 | + static const int months[11] = {31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; | |
201 | + | |
202 | + if (sscanf(token_date, "%d/%d/%d", &lease_time.tm_year, &lease_time.tm_mon, &lease_time.tm_mday) == 3) { | |
203 | + if (sscanf(token_time, "%d:%d:%d", &lease_time.tm_hour, &lease_time.tm_min, &lease_time.tm_sec) == 3) { | |
204 | + /* There doesn't seem to be a universally available library function | |
205 | + which converts broken-down _GMT_ time to seconds-in-epoch. | |
206 | + The following was borrowed from ISC dhcpd sources, where | |
207 | + it is noted that it might not be entirely accurate for odd seconds. | |
208 | + Since we're trying to get the same answer as dhcpd, that's just | |
209 | + fine here. */ | |
210 | + time = ((((((365 * (lease_time.tm_year - 1970) + | |
211 | + (lease_time.tm_year - 1969) / 4 + | |
212 | + (lease_time.tm_mon > 1 ? months[lease_time.tm_mon - 2] : 0) + | |
213 | + (lease_time.tm_mon > 2 && !((lease_time.tm_year - 1972) & 3 )) + | |
214 | + (lease_time.tm_mday - 1)) * 24) + | |
215 | + lease_time.tm_hour) * 60) + | |
216 | + lease_time.tm_mon) * 60) + | |
217 | + lease_time.tm_sec; | |
218 | + } | |
219 | + } | |
220 | + | |
221 | + return time; | |
222 | +} | |
223 | + | |
224 | +static off_t lease_file_size = (off_t)0; | |
225 | +static ino_t lease_file_inode = (ino_t)0; | |
226 | + | |
227 | +void load_dhcp(time_t now) { | |
228 | + struct isc_dhcp_lease* leases = NULL; | |
229 | + | |
230 | + struct stat statbuf; | |
231 | + if (stat(daemon->lease_file, &statbuf) == -1) { | |
232 | + return; | |
233 | + } | |
234 | + | |
235 | + /* Do nothing if the lease file has not changed. */ | |
236 | + if ((statbuf.st_size <= lease_file_size) && (statbuf.st_ino == lease_file_inode)) | |
237 | + return; | |
238 | + | |
239 | + lease_file_size = statbuf.st_size; | |
240 | + lease_file_inode = statbuf.st_ino; | |
241 | + | |
242 | + FILE* fp = fopen(daemon->lease_file, "r"); | |
243 | + if (!fp) { | |
244 | + my_syslog(LOG_ERR, _("failed to load %s:%s"), daemon->lease_file, strerror(errno)); | |
245 | + return; | |
246 | + } | |
247 | + | |
248 | + my_syslog(LOG_INFO, _("reading %s"), daemon->lease_file); | |
249 | + | |
250 | + char* hostname = daemon->namebuff; | |
251 | + struct in_addr host_address; | |
252 | + time_t time_starts = -1; | |
253 | + time_t time_ends = -1; | |
254 | + int nomem; | |
255 | + | |
256 | + char token[MAXTOK]; | |
257 | + while ((next_token(token, MAXTOK, fp))) { | |
258 | + if (strcmp(token, "lease") == 0) { | |
259 | + hostname[0] = '\0'; | |
260 | + | |
261 | + if (next_token(token, MAXTOK, fp) && ((host_address.s_addr = inet_addr(token)) != (in_addr_t)-1)) { | |
262 | + if (next_token(token, MAXTOK, fp) && *token == '{') { | |
263 | + while (next_token(token, MAXTOK, fp) && *token != '}') { | |
264 | + if ((strcmp(token, "client-hostname") == 0) || (strcmp(token, "hostname") == 0)) { | |
265 | + if (next_token(hostname, MAXDNAME, fp)) { | |
266 | + if (!canonicalise(hostname, &nomem)) { | |
267 | + *hostname = 0; | |
268 | + my_syslog(LOG_ERR, _("bad name in %s"), daemon->lease_file); | |
269 | + } | |
270 | + } | |
271 | + } else if ((strcmp(token, "starts") == 0) || (strcmp(token, "ends") == 0)) { | |
272 | + char token_date[MAXTOK]; | |
273 | + char token_time[MAXTOK]; | |
274 | + | |
275 | + int is_starts = strcmp(token, "starts") == 0; | |
276 | + | |
277 | + // Throw away the weekday and parse the date. | |
278 | + if (next_token(token, MAXTOK, fp) && next_token(token_date, MAXTOK, fp) && next_token(token_time, MAXTOK, fp)) { | |
279 | + time_t time = parse_lease_time(token_date, token_time); | |
280 | + | |
281 | + if (is_starts) | |
282 | + time_starts = time; | |
283 | + else | |
284 | + time_ends = time; | |
285 | + } | |
286 | + } | |
287 | + } | |
288 | + | |
289 | + if (!*hostname) | |
290 | + continue; | |
291 | + | |
292 | + if ((time_starts == -1) || (time_ends == -1)) | |
293 | + continue; | |
294 | + | |
295 | + if (difftime(now, time_ends) > 0) | |
296 | + continue; | |
297 | + | |
298 | + char* dot = strchr(hostname, '.'); | |
299 | + if (dot) { | |
300 | + if (!daemon->domain_suffix || hostname_isequal(dot + 1, daemon->domain_suffix)) { | |
301 | + my_syslog(LOG_WARNING, | |
302 | + _("Ignoring DHCP lease for %s because it has an illegal domain part"), | |
303 | + hostname); | |
304 | + continue; | |
305 | + } | |
306 | + *dot = 0; | |
307 | + } | |
308 | + | |
309 | + struct isc_dhcp_lease* lease = dhcp_lease_new(); | |
310 | + lease->name = strdup(hostname); | |
311 | + lease->addr = host_address; | |
312 | + lease->expires = time_ends; | |
313 | + lease->next = NULL; | |
314 | + | |
315 | + if (daemon->domain_suffix) { | |
316 | + asprintf(&lease->fqdn, "%s.%s", hostname, daemon->domain_suffix); | |
317 | + } | |
318 | + | |
319 | + if (!leases) | |
320 | + leases = lease; | |
321 | + else | |
322 | + leases->next = lease; | |
323 | + } | |
324 | + } | |
325 | + } | |
326 | + } | |
327 | + | |
328 | + fclose(fp); | |
329 | + | |
330 | + // Drop all entries. | |
331 | + cache_unhash_dhcp(); | |
332 | + | |
333 | + while (leases) { | |
334 | + struct isc_dhcp_lease *lease = leases; | |
335 | + leases = lease->next; | |
336 | + | |
337 | + if (lease->fqdn) { | |
338 | + cache_add_dhcp_entry(lease->fqdn, AF_INET, (struct all_addr*)&lease->addr.s_addr, lease->expires); | |
339 | + } | |
340 | + | |
341 | + if (lease->name) { | |
342 | + cache_add_dhcp_entry(lease->name, AF_INET, (struct all_addr*)&lease->addr.s_addr, lease->expires); | |
343 | + } | |
344 | + | |
345 | + // Cleanup | |
346 | + dhcp_lease_free(lease); | |
347 | + } | |
348 | +} | |
349 | + | |
350 | +#endif | |
351 | diff --git a/src/option.c b/src/option.c | |
352 | index daa728f..d16c982 100644 | |
353 | --- a/src/option.c | |
354 | +++ b/src/option.c | |
355 | @@ -1642,7 +1642,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma | |
356 | ret_err(_("bad MX target")); | |
357 | break; | |
358 | ||
359 | -#ifdef HAVE_DHCP | |
360 | +#if (defined HAVE_DHCP) || (defined HAVE_ISC_READER) | |
361 | case 'l': /* --dhcp-leasefile */ | |
362 | daemon->lease_file = opt_string_alloc(arg); | |
363 | break; | |
364 | -- | |
365 | 1.9.0 | |
366 |