]>
Commit | Line | Data |
---|---|---|
28866e95 | 1 | /* dnsmasq is Copyright (c) 2000-2011 Simon Kelley |
9e4abcb5 SK |
2 | |
3 | This program is free software; you can redistribute it and/or modify | |
4 | it under the terms of the GNU General Public License as published by | |
824af85b SK |
5 | the Free Software Foundation; version 2 dated June, 1991, or |
6 | (at your option) version 3 dated 29 June, 2007. | |
7 | ||
9e4abcb5 SK |
8 | This program is distributed in the hope that it will be useful, |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | GNU General Public License for more details. | |
824af85b | 12 | |
73a08a24 SK |
13 | You should have received a copy of the GNU General Public License |
14 | along with this program. If not, see <http://www.gnu.org/licenses/>. | |
9e4abcb5 SK |
15 | */ |
16 | ||
9e4abcb5 SK |
17 | #include "dnsmasq.h" |
18 | ||
7622fc06 SK |
19 | #ifdef HAVE_DHCP |
20 | ||
5aabfc78 | 21 | static struct dhcp_lease *leases = NULL, *old_leases = NULL; |
5e9e0efb | 22 | static int dns_dirty, file_dirty, leases_left; |
9e4abcb5 | 23 | |
5aabfc78 | 24 | void lease_init(time_t now) |
9e4abcb5 | 25 | { |
9e4abcb5 | 26 | unsigned long ei; |
c72daea8 | 27 | struct all_addr addr; |
9e4abcb5 | 28 | struct dhcp_lease *lease; |
5aabfc78 | 29 | int clid_len, hw_len, hw_type; |
208b65c5 | 30 | FILE *leasestream; |
c72daea8 SK |
31 | #ifdef HAVE_DHCP6 |
32 | int v6pass = 0; | |
33 | #endif | |
5e9e0efb | 34 | |
8ef5ada2 | 35 | /* These each hold a DHCP option max size 255 |
5aabfc78 SK |
36 | and get a terminating zero added */ |
37 | daemon->dhcp_buff = safe_malloc(256); | |
38 | daemon->dhcp_buff2 = safe_malloc(256); | |
8ef5ada2 SK |
39 | daemon->dhcp_buff3 = safe_malloc(256); |
40 | ||
3be34541 | 41 | leases_left = daemon->dhcp_max; |
44a2a316 | 42 | |
28866e95 | 43 | if (option_bool(OPT_LEASE_RO)) |
208b65c5 SK |
44 | { |
45 | /* run "<lease_change_script> init" once to get the | |
46 | initial state of the database. If leasefile-ro is | |
47 | set without a script, we just do without any | |
48 | lease database. */ | |
1f15b81d SK |
49 | #ifdef HAVE_SCRIPT |
50 | if (daemon->lease_change_command) | |
208b65c5 | 51 | { |
1f15b81d SK |
52 | strcpy(daemon->dhcp_buff, daemon->lease_change_command); |
53 | strcat(daemon->dhcp_buff, " init"); | |
54 | leasestream = popen(daemon->dhcp_buff, "r"); | |
208b65c5 | 55 | } |
1f15b81d SK |
56 | else |
57 | #endif | |
58 | { | |
59 | file_dirty = dns_dirty = 0; | |
60 | return; | |
61 | } | |
62 | ||
208b65c5 SK |
63 | } |
64 | else | |
65 | { | |
66 | /* NOTE: need a+ mode to create file if it doesn't exist */ | |
67 | leasestream = daemon->lease_stream = fopen(daemon->lease_file, "a+"); | |
68 | ||
69 | if (!leasestream) | |
5aabfc78 | 70 | die(_("cannot open or create lease file %s: %s"), daemon->lease_file, EC_FILE); |
208b65c5 | 71 | |
7622fc06 | 72 | /* a+ mode leaves pointer at end. */ |
208b65c5 SK |
73 | rewind(leasestream); |
74 | } | |
7cebd20f | 75 | |
c72daea8 SK |
76 | #ifdef HAVE_DHCP6 |
77 | again: | |
78 | #endif | |
79 | ||
0a852541 SK |
80 | /* client-id max length is 255 which is 255*2 digits + 254 colons |
81 | borrow DNS packet buffer which is always larger than 1000 bytes */ | |
208b65c5 | 82 | if (leasestream) |
c72daea8 | 83 | while (fscanf(leasestream, "%lu %255s %64s %255s %764s", |
208b65c5 SK |
84 | &ei, daemon->dhcp_buff2, daemon->namebuff, |
85 | daemon->dhcp_buff, daemon->packet) == 5) | |
86 | { | |
c72daea8 SK |
87 | #ifdef HAVE_DHCP6 |
88 | if (!v6pass) | |
89 | #endif | |
90 | { | |
91 | hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type); | |
92 | /* For backwards compatibility, no explict MAC address type means ether. */ | |
93 | if (hw_type == 0 && hw_len != 0) | |
94 | hw_type = ARPHRD_ETHER; | |
95 | } | |
96 | ||
97 | #ifdef HAVE_DHCP6 | |
98 | if (v6pass) | |
99 | inet_pton(AF_INET6, daemon->namebuff, &addr.addr.addr6); | |
100 | else | |
101 | #endif | |
102 | inet_pton(AF_INET, daemon->namebuff, &addr.addr.addr4); | |
103 | ||
208b65c5 SK |
104 | clid_len = 0; |
105 | if (strcmp(daemon->packet, "*") != 0) | |
106 | clid_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL); | |
107 | ||
c72daea8 SK |
108 | #ifdef HAVE_DHCP6 |
109 | if (v6pass) | |
110 | lease = lease_allocate6(&addr.addr.addr6); | |
111 | else | |
112 | #endif | |
113 | lease = lease_allocate4(addr.addr.addr4); | |
114 | ||
115 | if (!lease) | |
5aabfc78 SK |
116 | die (_("too many stored leases"), NULL, EC_MISC); |
117 | ||
5e9e0efb | 118 | #ifdef HAVE_BROKEN_RTC |
208b65c5 SK |
119 | if (ei != 0) |
120 | lease->expires = (time_t)ei + now; | |
121 | else | |
122 | lease->expires = (time_t)0; | |
123 | lease->length = ei; | |
5e9e0efb | 124 | #else |
208b65c5 SK |
125 | /* strictly time_t is opaque, but this hack should work on all sane systems, |
126 | even when sizeof(time_t) == 8 */ | |
127 | lease->expires = (time_t)ei; | |
5e9e0efb | 128 | #endif |
c72daea8 SK |
129 | |
130 | #ifdef HAVE_DHCP6 | |
131 | if (!v6pass) | |
132 | #endif | |
133 | lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet, hw_len, hw_type, clid_len); | |
208b65c5 SK |
134 | |
135 | if (strcmp(daemon->dhcp_buff, "*") != 0) | |
1f15b81d | 136 | lease_set_hostname(lease, daemon->dhcp_buff, 0); |
5aabfc78 SK |
137 | |
138 | /* set these correctly: the "old" events are generated later from | |
139 | the startup synthesised SIGHUP. */ | |
140 | lease->new = lease->changed = 0; | |
208b65c5 | 141 | } |
c72daea8 SK |
142 | |
143 | #ifdef HAVE_DHCP6 | |
144 | if (!v6pass) | |
145 | { | |
146 | if (fscanf(leasestream, "duid %255s", daemon->dhcp_buff) == 1) | |
147 | { | |
148 | daemon->duid_len = parse_hex(daemon->dhcp_buff, (unsigned char *)daemon->dhcp_buff, 130, NULL, NULL); | |
149 | daemon->duid = safe_malloc(daemon->duid_len); | |
150 | memcpy(daemon->duid, daemon->dhcp_buff, daemon->duid_len ); | |
151 | v6pass = 1; | |
152 | goto again; | |
153 | } | |
154 | ||
155 | /* If we're not doing DHCPv6, and there are not v6 leases, don't add the DUID to the database */ | |
156 | if (daemon->dhcp6) | |
157 | make_duid(now); | |
158 | } | |
159 | #endif | |
208b65c5 | 160 | |
1f15b81d | 161 | #ifdef HAVE_SCRIPT |
208b65c5 SK |
162 | if (!daemon->lease_stream) |
163 | { | |
164 | int rc = 0; | |
9e4abcb5 | 165 | |
208b65c5 SK |
166 | /* shell returns 127 for "command not found", 126 for bad permissions. */ |
167 | if (!leasestream || (rc = pclose(leasestream)) == -1 || WEXITSTATUS(rc) == 127 || WEXITSTATUS(rc) == 126) | |
168 | { | |
169 | if (WEXITSTATUS(rc) == 127) | |
170 | errno = ENOENT; | |
171 | else if (WEXITSTATUS(rc) == 126) | |
172 | errno = EACCES; | |
5aabfc78 | 173 | die(_("cannot run lease-init script %s: %s"), daemon->lease_change_command, EC_FILE); |
208b65c5 SK |
174 | } |
175 | ||
176 | if (WEXITSTATUS(rc) != 0) | |
177 | { | |
178 | sprintf(daemon->dhcp_buff, "%d", WEXITSTATUS(rc)); | |
5aabfc78 | 179 | die(_("lease-init script returned exit code %s"), daemon->dhcp_buff, WEXITSTATUS(rc) + EC_INIT_OFFSET); |
208b65c5 | 180 | } |
9e4abcb5 | 181 | } |
1f15b81d | 182 | #endif |
7cebd20f SK |
183 | |
184 | /* Some leases may have expired */ | |
185 | file_dirty = 0; | |
186 | lease_prune(NULL, now); | |
187 | dns_dirty = 1; | |
44a2a316 SK |
188 | } |
189 | ||
5aabfc78 | 190 | void lease_update_from_configs(void) |
44a2a316 SK |
191 | { |
192 | /* changes to the config may change current leases. */ | |
193 | ||
194 | struct dhcp_lease *lease; | |
195 | struct dhcp_config *config; | |
b8187c80 SK |
196 | char *name; |
197 | ||
9e4abcb5 | 198 | for (lease = leases; lease; lease = lease->next) |
cdeda28f SK |
199 | if ((config = find_config(daemon->dhcp_conf, NULL, lease->clid, lease->clid_len, |
200 | lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL)) && | |
b8187c80 SK |
201 | (config->flags & CONFIG_NAME) && |
202 | (!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr)) | |
9009d746 | 203 | lease_set_hostname(lease, config->hostname, 1); |
5aabfc78 | 204 | else if ((name = host_from_dns(lease->addr))) |
9009d746 | 205 | lease_set_hostname(lease, name, 1); /* updates auth flag only */ |
9e4abcb5 SK |
206 | } |
207 | ||
5aabfc78 | 208 | static void ourprintf(int *errp, char *format, ...) |
9e4abcb5 | 209 | { |
7cebd20f | 210 | va_list ap; |
5e9e0efb | 211 | |
7cebd20f SK |
212 | va_start(ap, format); |
213 | if (!(*errp) && vfprintf(daemon->lease_stream, format, ap) < 0) | |
214 | *errp = errno; | |
215 | va_end(ap); | |
216 | } | |
217 | ||
5aabfc78 | 218 | void lease_update_file(time_t now) |
7cebd20f SK |
219 | { |
220 | struct dhcp_lease *lease; | |
221 | time_t next_event; | |
222 | int i, err = 0; | |
223 | ||
208b65c5 | 224 | if (file_dirty != 0 && daemon->lease_stream) |
9e4abcb5 | 225 | { |
cdeda28f SK |
226 | errno = 0; |
227 | rewind(daemon->lease_stream); | |
228 | if (errno != 0 || ftruncate(fileno(daemon->lease_stream), 0) != 0) | |
7cebd20f | 229 | err = errno; |
9e4abcb5 SK |
230 | |
231 | for (lease = leases; lease; lease = lease->next) | |
232 | { | |
c72daea8 SK |
233 | |
234 | #ifdef HAVE_DHCP6 | |
235 | if (lease->is_ipv6) | |
236 | continue; | |
237 | #endif | |
238 | ||
44a2a316 | 239 | #ifdef HAVE_BROKEN_RTC |
5aabfc78 | 240 | ourprintf(&err, "%u ", lease->length); |
44a2a316 | 241 | #else |
5aabfc78 | 242 | ourprintf(&err, "%lu ", (unsigned long)lease->expires); |
5e9e0efb | 243 | #endif |
c72daea8 | 244 | |
7cebd20f | 245 | if (lease->hwaddr_type != ARPHRD_ETHER || lease->hwaddr_len == 0) |
5aabfc78 | 246 | ourprintf(&err, "%.2x-", lease->hwaddr_type); |
5e9e0efb SK |
247 | for (i = 0; i < lease->hwaddr_len; i++) |
248 | { | |
5aabfc78 | 249 | ourprintf(&err, "%.2x", lease->hwaddr[i]); |
7cebd20f | 250 | if (i != lease->hwaddr_len - 1) |
5aabfc78 | 251 | ourprintf(&err, ":"); |
5e9e0efb | 252 | } |
c72daea8 SK |
253 | |
254 | inet_ntop(AF_INET, &lease->addr, daemon->addrbuff, ADDRSTRLEN); | |
5aabfc78 | 255 | |
c72daea8 | 256 | ourprintf(&err, " %s ", daemon->addrbuff); |
1f15b81d SK |
257 | ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*"); |
258 | ||
0a852541 | 259 | if (lease->clid && lease->clid_len != 0) |
9e4abcb5 SK |
260 | { |
261 | for (i = 0; i < lease->clid_len - 1; i++) | |
5aabfc78 SK |
262 | ourprintf(&err, "%.2x:", lease->clid[i]); |
263 | ourprintf(&err, "%.2x\n", lease->clid[i]); | |
9e4abcb5 SK |
264 | } |
265 | else | |
5aabfc78 | 266 | ourprintf(&err, "*\n"); |
9e4abcb5 | 267 | } |
7cebd20f | 268 | |
c72daea8 SK |
269 | #ifdef HAVE_DHCP6 |
270 | if (daemon->duid) | |
271 | { | |
272 | ourprintf(&err, "duid "); | |
273 | for (i = 0; i < daemon->duid_len - 1; i++) | |
274 | ourprintf(&err, "%.2x:", daemon->duid[i]); | |
275 | ourprintf(&err, "%.2x\n", daemon->duid[i]); | |
276 | ||
277 | for (lease = leases; lease; lease = lease->next) | |
278 | { | |
279 | ||
280 | if (!lease->is_ipv6) | |
281 | continue; | |
282 | ||
283 | #ifdef HAVE_BROKEN_RTC | |
284 | ourprintf(&err, "%u ", lease->length); | |
285 | #else | |
286 | ourprintf(&err, "%lu ", (unsigned long)lease->expires); | |
287 | #endif | |
288 | ||
289 | inet_ntop(AF_INET6, lease->hwaddr, daemon->addrbuff, ADDRSTRLEN); | |
290 | ||
291 | ourprintf(&err, "* %s ", daemon->addrbuff); | |
292 | ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*"); | |
293 | ||
294 | if (lease->clid && lease->clid_len != 0) | |
295 | { | |
296 | for (i = 0; i < lease->clid_len - 1; i++) | |
297 | ourprintf(&err, "%.2x:", lease->clid[i]); | |
298 | ourprintf(&err, "%.2x\n", lease->clid[i]); | |
299 | } | |
300 | else | |
301 | ourprintf(&err, "*\n"); | |
302 | } | |
303 | } | |
304 | #endif | |
305 | ||
7cebd20f SK |
306 | if (fflush(daemon->lease_stream) != 0 || |
307 | fsync(fileno(daemon->lease_stream)) < 0) | |
308 | err = errno; | |
309 | ||
310 | if (!err) | |
311 | file_dirty = 0; | |
312 | } | |
313 | ||
314 | /* Set alarm for when the first lease expires + slop. */ | |
315 | for (next_event = 0, lease = leases; lease; lease = lease->next) | |
316 | if (lease->expires != 0 && | |
317 | (next_event == 0 || difftime(next_event, lease->expires + 10) > 0.0)) | |
318 | next_event = lease->expires + 10; | |
319 | ||
320 | if (err) | |
321 | { | |
322 | if (next_event == 0 || difftime(next_event, LEASE_RETRY + now) > 0.0) | |
323 | next_event = LEASE_RETRY + now; | |
324 | ||
7622fc06 | 325 | my_syslog(MS_DHCP | LOG_ERR, _("failed to write %s: %s (retry in %us)"), |
f2621c7f SK |
326 | daemon->lease_file, strerror(err), |
327 | (unsigned int)difftime(next_event, now)); | |
9e4abcb5 | 328 | } |
7cebd20f SK |
329 | |
330 | if (next_event != 0) | |
331 | alarm((unsigned)difftime(next_event, now)); | |
44a2a316 | 332 | } |
9e4abcb5 | 333 | |
5aabfc78 | 334 | void lease_update_dns(void) |
44a2a316 SK |
335 | { |
336 | struct dhcp_lease *lease; | |
337 | ||
824af85b | 338 | if (daemon->port != 0 && dns_dirty) |
9e4abcb5 SK |
339 | { |
340 | cache_unhash_dhcp(); | |
44a2a316 | 341 | |
9e4abcb5 SK |
342 | for (lease = leases; lease; lease = lease->next) |
343 | { | |
9009d746 SK |
344 | if (lease->fqdn) |
345 | cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires); | |
346 | ||
28866e95 | 347 | if (!option_bool(OPT_DHCP_FQDN) && lease->hostname) |
9009d746 | 348 | cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires); |
9e4abcb5 SK |
349 | } |
350 | ||
351 | dns_dirty = 0; | |
352 | } | |
353 | } | |
354 | ||
355 | void lease_prune(struct dhcp_lease *target, time_t now) | |
356 | { | |
357 | struct dhcp_lease *lease, *tmp, **up; | |
358 | ||
359 | for (lease = leases, up = &leases; lease; lease = tmp) | |
360 | { | |
361 | tmp = lease->next; | |
362 | if ((lease->expires != 0 && difftime(now, lease->expires) > 0) || lease == target) | |
363 | { | |
5e9e0efb | 364 | file_dirty = 1; |
9e4abcb5 | 365 | if (lease->hostname) |
7cebd20f SK |
366 | dns_dirty = 1; |
367 | ||
c72daea8 | 368 | *up = lease->next; /* unlink */ |
7cebd20f SK |
369 | |
370 | /* Put on old_leases list 'till we | |
371 | can run the script */ | |
372 | lease->next = old_leases; | |
373 | old_leases = lease; | |
374 | ||
44a2a316 | 375 | leases_left++; |
9e4abcb5 SK |
376 | } |
377 | else | |
378 | up = &lease->next; | |
379 | } | |
380 | } | |
381 | ||
382 | ||
cdeda28f | 383 | struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type, |
0a852541 | 384 | unsigned char *clid, int clid_len) |
9e4abcb5 | 385 | { |
9e4abcb5 SK |
386 | struct dhcp_lease *lease; |
387 | ||
0a852541 SK |
388 | if (clid) |
389 | for (lease = leases; lease; lease = lease->next) | |
c72daea8 SK |
390 | { |
391 | #ifdef HAVE_DHCP6 | |
392 | if (lease->is_ipv6) | |
393 | continue; | |
394 | #endif | |
395 | if (lease->clid && clid_len == lease->clid_len && | |
396 | memcmp(clid, lease->clid, clid_len) == 0) | |
397 | return lease; | |
398 | } | |
0a852541 SK |
399 | |
400 | for (lease = leases; lease; lease = lease->next) | |
c72daea8 SK |
401 | { |
402 | #ifdef HAVE_DHCP6 | |
403 | if (lease->is_ipv6) | |
404 | continue; | |
405 | #endif | |
406 | if ((!lease->clid || !clid) && | |
407 | hw_len != 0 && | |
408 | lease->hwaddr_len == hw_len && | |
409 | lease->hwaddr_type == hw_type && | |
410 | memcmp(hwaddr, lease->hwaddr, hw_len) == 0) | |
411 | return lease; | |
412 | } | |
413 | ||
9e4abcb5 SK |
414 | return NULL; |
415 | } | |
416 | ||
417 | struct dhcp_lease *lease_find_by_addr(struct in_addr addr) | |
418 | { | |
419 | struct dhcp_lease *lease; | |
420 | ||
421 | for (lease = leases; lease; lease = lease->next) | |
c72daea8 SK |
422 | { |
423 | #ifdef HAVE_DHCP6 | |
424 | if (lease->is_ipv6) | |
425 | continue; | |
426 | #endif | |
427 | if (lease->addr.s_addr == addr.s_addr) | |
428 | return lease; | |
429 | } | |
430 | ||
9e4abcb5 SK |
431 | return NULL; |
432 | } | |
433 | ||
7de060b0 SK |
434 | /* Find largest assigned address in context */ |
435 | struct in_addr lease_find_max_addr(struct dhcp_context *context) | |
436 | { | |
437 | struct dhcp_lease *lease; | |
438 | struct in_addr addr = context->start; | |
439 | ||
440 | if (!(context->flags & (CONTEXT_STATIC | CONTEXT_PROXY))) | |
441 | for (lease = leases; lease; lease = lease->next) | |
c72daea8 SK |
442 | { |
443 | #ifdef HAVE_DHCP6 | |
444 | if (lease->is_ipv6) | |
445 | continue; | |
446 | #endif | |
447 | if (((unsigned)ntohl(lease->addr.s_addr)) > ((unsigned)ntohl(context->start.s_addr)) && | |
448 | ((unsigned)ntohl(lease->addr.s_addr)) <= ((unsigned)ntohl(context->end.s_addr)) && | |
449 | ((unsigned)ntohl(lease->addr.s_addr)) > ((unsigned)ntohl(addr.s_addr))) | |
450 | addr = lease->addr; | |
451 | } | |
7de060b0 SK |
452 | |
453 | return addr; | |
454 | } | |
9e4abcb5 | 455 | |
c72daea8 | 456 | static struct dhcp_lease *lease_allocate(void) |
9e4abcb5 SK |
457 | { |
458 | struct dhcp_lease *lease; | |
5aabfc78 | 459 | if (!leases_left || !(lease = whine_malloc(sizeof(struct dhcp_lease)))) |
9e4abcb5 SK |
460 | return NULL; |
461 | ||
7cebd20f SK |
462 | memset(lease, 0, sizeof(struct dhcp_lease)); |
463 | lease->new = 1; | |
9e4abcb5 | 464 | lease->expires = 1; |
5e9e0efb SK |
465 | #ifdef HAVE_BROKEN_RTC |
466 | lease->length = 0xffffffff; /* illegal value */ | |
467 | #endif | |
9e4abcb5 SK |
468 | lease->next = leases; |
469 | leases = lease; | |
470 | ||
5e9e0efb | 471 | file_dirty = 1; |
44a2a316 | 472 | leases_left--; |
9e4abcb5 SK |
473 | |
474 | return lease; | |
475 | } | |
476 | ||
c72daea8 SK |
477 | struct dhcp_lease *lease_allocate4(struct in_addr addr) |
478 | { | |
479 | struct dhcp_lease *lease = lease_allocate(); | |
480 | lease->addr = addr; | |
481 | lease->hwaddr_len = 256; /* illegal value */ | |
482 | ||
483 | return lease; | |
484 | } | |
485 | ||
486 | #ifdef HAVE_DHCP6 | |
487 | struct dhcp_lease *lease_allocate6(struct in6_addr *addrp) | |
488 | { | |
489 | struct dhcp_lease *lease = lease_allocate(); | |
490 | memcpy(lease->hwaddr, addrp, sizeof(*addrp)) ; | |
491 | lease->is_ipv6 = 1; | |
492 | ||
493 | return lease; | |
494 | } | |
495 | #endif | |
496 | ||
5e9e0efb | 497 | void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now) |
9e4abcb5 | 498 | { |
5e9e0efb SK |
499 | time_t exp = now + (time_t)len; |
500 | ||
501 | if (len == 0xffffffff) | |
502 | { | |
503 | exp = 0; | |
504 | len = 0; | |
505 | } | |
506 | ||
9e4abcb5 | 507 | if (exp != lease->expires) |
0a852541 | 508 | { |
0a852541 | 509 | dns_dirty = 1; |
5e9e0efb SK |
510 | lease->expires = exp; |
511 | #ifndef HAVE_BROKEN_RTC | |
208b65c5 | 512 | lease->aux_changed = file_dirty = 1; |
5e9e0efb | 513 | #endif |
0a852541 | 514 | } |
5e9e0efb SK |
515 | |
516 | #ifdef HAVE_BROKEN_RTC | |
517 | if (len != lease->length) | |
518 | { | |
519 | lease->length = len; | |
208b65c5 | 520 | lease->aux_changed = file_dirty = 1; |
5e9e0efb SK |
521 | } |
522 | #endif | |
523 | } | |
9e4abcb5 | 524 | |
7cebd20f | 525 | void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr, |
cdeda28f | 526 | unsigned char *clid, int hw_len, int hw_type, int clid_len) |
9e4abcb5 | 527 | { |
cdeda28f SK |
528 | if (hw_len != lease->hwaddr_len || |
529 | hw_type != lease->hwaddr_type || | |
7cebd20f | 530 | (hw_len != 0 && memcmp(lease->hwaddr, hwaddr, hw_len) != 0)) |
9e4abcb5 | 531 | { |
cdeda28f SK |
532 | memcpy(lease->hwaddr, hwaddr, hw_len); |
533 | lease->hwaddr_len = hw_len; | |
534 | lease->hwaddr_type = hw_type; | |
208b65c5 | 535 | lease->changed = file_dirty = 1; /* run script on change */ |
9e4abcb5 | 536 | } |
0a852541 SK |
537 | |
538 | /* only update clid when one is available, stops packets | |
539 | without a clid removing the record. Lease init uses | |
540 | clid_len == 0 for no clid. */ | |
541 | if (clid_len != 0 && clid) | |
542 | { | |
543 | if (!lease->clid) | |
544 | lease->clid_len = 0; | |
545 | ||
546 | if (lease->clid_len != clid_len) | |
547 | { | |
208b65c5 | 548 | lease->aux_changed = file_dirty = 1; |
5aabfc78 SK |
549 | free(lease->clid); |
550 | if (!(lease->clid = whine_malloc(clid_len))) | |
7cebd20f | 551 | return; |
0a852541 SK |
552 | } |
553 | else if (memcmp(lease->clid, clid, clid_len) != 0) | |
208b65c5 SK |
554 | lease->aux_changed = file_dirty = 1; |
555 | ||
0a852541 SK |
556 | lease->clid_len = clid_len; |
557 | memcpy(lease->clid, clid, clid_len); | |
558 | } | |
208b65c5 | 559 | |
9e4abcb5 SK |
560 | } |
561 | ||
9009d746 SK |
562 | static void kill_name(struct dhcp_lease *lease) |
563 | { | |
564 | /* run script to say we lost our old name */ | |
565 | ||
566 | /* this shouldn't happen unless updates are very quick and the | |
567 | script very slow, we just avoid a memory leak if it does. */ | |
568 | free(lease->old_hostname); | |
569 | ||
570 | /* If we know the fqdn, pass that. The helper will derive the | |
571 | unqualified name from it, free the unqulaified name here. */ | |
572 | ||
573 | if (lease->fqdn) | |
574 | { | |
575 | lease->old_hostname = lease->fqdn; | |
576 | free(lease->hostname); | |
577 | } | |
578 | else | |
579 | lease->old_hostname = lease->hostname; | |
580 | ||
581 | lease->hostname = lease->fqdn = NULL; | |
582 | } | |
583 | ||
584 | void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth) | |
9e4abcb5 SK |
585 | { |
586 | struct dhcp_lease *lease_tmp; | |
587 | char *new_name = NULL, *new_fqdn = NULL; | |
9009d746 | 588 | |
a222641c | 589 | if (lease->hostname && name && hostname_isequal(lease->hostname, name)) |
b8187c80 SK |
590 | { |
591 | lease->auth_name = auth; | |
592 | return; | |
593 | } | |
7cebd20f | 594 | |
9e4abcb5 SK |
595 | if (!name && !lease->hostname) |
596 | return; | |
597 | ||
598 | /* If a machine turns up on a new net without dropping the old lease, | |
599 | or two machines claim the same name, then we end up with two interfaces with | |
b8187c80 SK |
600 | the same name. Check for that here and remove the name from the old lease. |
601 | Don't allow a name from the client to override a name from dnsmasq config. */ | |
9e4abcb5 SK |
602 | |
603 | if (name) | |
604 | { | |
9009d746 SK |
605 | if ((new_name = whine_malloc(strlen(name) + 1))) |
606 | { | |
607 | char *suffix = get_domain(lease->addr); | |
608 | strcpy(new_name, name); | |
609 | if (suffix && (new_fqdn = whine_malloc(strlen(new_name) + strlen(suffix) + 2))) | |
610 | { | |
611 | strcpy(new_fqdn, name); | |
612 | strcat(new_fqdn, "."); | |
613 | strcat(new_fqdn, suffix); | |
614 | } | |
615 | } | |
616 | ||
617 | /* Depending on mode, we check either unqualified name or FQDN. */ | |
9e4abcb5 | 618 | for (lease_tmp = leases; lease_tmp; lease_tmp = lease_tmp->next) |
9e4abcb5 | 619 | { |
28866e95 | 620 | if (option_bool(OPT_DHCP_FQDN)) |
9009d746 SK |
621 | { |
622 | if (!new_fqdn || !lease_tmp->fqdn || !hostname_isequal(lease_tmp->fqdn, new_fqdn) ) | |
623 | continue; | |
624 | } | |
625 | else | |
626 | { | |
627 | if (!new_name || !lease_tmp->hostname || !hostname_isequal(lease_tmp->hostname, new_name) ) | |
628 | continue; | |
629 | } | |
630 | ||
631 | if (lease_tmp->auth_name && !auth) | |
632 | { | |
633 | free(new_name); | |
634 | free(new_fqdn); | |
635 | return; | |
636 | } | |
637 | ||
638 | kill_name(lease_tmp); | |
639 | break; | |
9e4abcb5 SK |
640 | } |
641 | } | |
642 | ||
643 | if (lease->hostname) | |
9009d746 | 644 | kill_name(lease); |
1697269c | 645 | |
9e4abcb5 SK |
646 | lease->hostname = new_name; |
647 | lease->fqdn = new_fqdn; | |
b8187c80 | 648 | lease->auth_name = auth; |
9e4abcb5 | 649 | |
5e9e0efb | 650 | file_dirty = 1; |
7cebd20f | 651 | dns_dirty = 1; |
208b65c5 | 652 | lease->changed = 1; /* run script on change */ |
7cebd20f SK |
653 | } |
654 | ||
824af85b SK |
655 | void lease_set_interface(struct dhcp_lease *lease, int interface) |
656 | { | |
657 | if (lease->last_interface == interface) | |
658 | return; | |
659 | ||
660 | lease->last_interface = interface; | |
661 | lease->changed = 1; | |
662 | } | |
663 | ||
5aabfc78 SK |
664 | void rerun_scripts(void) |
665 | { | |
666 | struct dhcp_lease *lease; | |
667 | ||
668 | for (lease = leases; lease; lease = lease->next) | |
669 | lease->changed = 1; | |
670 | } | |
671 | ||
7cebd20f SK |
672 | /* deleted leases get transferred to the old_leases list. |
673 | remove them here, after calling the lease change | |
1697269c SK |
674 | script. Also run the lease change script on new/modified leases. |
675 | ||
676 | Return zero if nothing to do. */ | |
5aabfc78 | 677 | int do_script_run(time_t now) |
7cebd20f SK |
678 | { |
679 | struct dhcp_lease *lease; | |
9e4abcb5 | 680 | |
9009d746 SK |
681 | #ifdef HAVE_DBUS |
682 | /* If we're going to be sending DBus signals, but the connection is not yet up, | |
683 | delay everything until it is. */ | |
28866e95 | 684 | if (option_bool(OPT_DBUS) && !daemon->dbus) |
9009d746 SK |
685 | return 0; |
686 | #endif | |
687 | ||
1697269c | 688 | if (old_leases) |
7cebd20f | 689 | { |
7cebd20f | 690 | lease = old_leases; |
1697269c SK |
691 | |
692 | /* If the lease still has an old_hostname, do the "old" action on that first */ | |
693 | if (lease->old_hostname) | |
694 | { | |
1f15b81d | 695 | #ifdef HAVE_SCRIPT |
5aabfc78 SK |
696 | queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now); |
697 | #endif | |
1697269c SK |
698 | free(lease->old_hostname); |
699 | lease->old_hostname = NULL; | |
700 | return 1; | |
701 | } | |
702 | else | |
703 | { | |
9009d746 | 704 | kill_name(lease); |
1f15b81d | 705 | #ifdef HAVE_SCRIPT |
9009d746 | 706 | queue_script(ACTION_DEL, lease, lease->old_hostname, now); |
1f15b81d SK |
707 | #endif |
708 | #ifdef HAVE_DBUS | |
709 | emit_dbus_signal(ACTION_DEL, lease, lease->old_hostname); | |
5aabfc78 | 710 | #endif |
1697269c SK |
711 | old_leases = lease->next; |
712 | ||
9009d746 | 713 | free(lease->old_hostname); |
5aabfc78 | 714 | free(lease->clid); |
316e2730 | 715 | free(lease->extradata); |
1697269c SK |
716 | free(lease); |
717 | ||
718 | return 1; | |
719 | } | |
7cebd20f | 720 | } |
1697269c SK |
721 | |
722 | /* make sure we announce the loss of a hostname before its new location. */ | |
723 | for (lease = leases; lease; lease = lease->next) | |
724 | if (lease->old_hostname) | |
725 | { | |
1f15b81d | 726 | #ifdef HAVE_SCRIPT |
5aabfc78 SK |
727 | queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now); |
728 | #endif | |
1697269c SK |
729 | free(lease->old_hostname); |
730 | lease->old_hostname = NULL; | |
731 | return 1; | |
732 | } | |
733 | ||
7cebd20f | 734 | for (lease = leases; lease; lease = lease->next) |
208b65c5 | 735 | if (lease->new || lease->changed || |
28866e95 | 736 | (lease->aux_changed && option_bool(OPT_LEASE_RO))) |
7cebd20f | 737 | { |
1f15b81d | 738 | #ifdef HAVE_SCRIPT |
9009d746 SK |
739 | queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease, |
740 | lease->fqdn ? lease->fqdn : lease->hostname, now); | |
1f15b81d SK |
741 | #endif |
742 | #ifdef HAVE_DBUS | |
743 | emit_dbus_signal(lease->new ? ACTION_ADD : ACTION_OLD, lease, | |
744 | lease->fqdn ? lease->fqdn : lease->hostname); | |
5aabfc78 | 745 | #endif |
208b65c5 | 746 | lease->new = lease->changed = lease->aux_changed = 0; |
1697269c | 747 | |
316e2730 SK |
748 | /* this is used for the "add" call, then junked, since they're not in the database */ |
749 | free(lease->extradata); | |
750 | lease->extradata = NULL; | |
1697269c SK |
751 | |
752 | return 1; | |
7cebd20f | 753 | } |
1697269c SK |
754 | |
755 | return 0; /* nothing to do */ | |
7cebd20f | 756 | } |
7622fc06 SK |
757 | |
758 | #endif | |
7cebd20f SK |
759 | |
760 | ||
761 | ||
9e4abcb5 | 762 |