blob: c021d15ab4c8b7e3682beb4d86ff187863c72d3f [file] [log] [blame]
San Mehatffd68722010-01-20 09:56:15 -08001/* dnsmasq is Copyright (c) 2000-2009 Simon Kelley
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
5 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
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.
12
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/>.
15*/
16
17#include "dnsmasq.h"
18
19#ifdef HAVE_DHCP
20
21static struct dhcp_lease *leases = NULL, *old_leases = NULL;
22static int dns_dirty, file_dirty, leases_left;
23
24void lease_init(time_t now)
25{
26 unsigned long ei;
27 struct in_addr addr;
28 struct dhcp_lease *lease;
29 int clid_len, hw_len, hw_type;
30 FILE *leasestream;
31
32 /* These two each hold a DHCP option max size 255
33 and get a terminating zero added */
34 daemon->dhcp_buff = safe_malloc(256);
35 daemon->dhcp_buff2 = safe_malloc(256);
36
37 leases_left = daemon->dhcp_max;
38
39 if (daemon->options & OPT_LEASE_RO)
40 {
41 /* run "<lease_change_script> init" once to get the
42 initial state of the database. If leasefile-ro is
43 set without a script, we just do without any
44 lease database. */
45#ifdef HAVE_SCRIPT
46 if (daemon->lease_change_command)
47 {
48 strcpy(daemon->dhcp_buff, daemon->lease_change_command);
49 strcat(daemon->dhcp_buff, " init");
50 leasestream = popen(daemon->dhcp_buff, "r");
51 }
52 else
53#endif
54 {
55 file_dirty = dns_dirty = 0;
56 return;
57 }
58
59 }
60 else
61 {
62 /* NOTE: need a+ mode to create file if it doesn't exist */
63 leasestream = daemon->lease_stream = fopen(daemon->lease_file, "a+");
64
65 if (!leasestream)
66 die(_("cannot open or create lease file %s: %s"), daemon->lease_file, EC_FILE);
67
68 /* a+ mode leaves pointer at end. */
69 rewind(leasestream);
70 }
71
72 /* client-id max length is 255 which is 255*2 digits + 254 colons
73 borrow DNS packet buffer which is always larger than 1000 bytes */
74 if (leasestream)
75 while (fscanf(leasestream, "%lu %255s %16s %255s %764s",
76 &ei, daemon->dhcp_buff2, daemon->namebuff,
77 daemon->dhcp_buff, daemon->packet) == 5)
78 {
79 hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type);
80 /* For backwards compatibility, no explict MAC address type means ether. */
81 if (hw_type == 0 && hw_len != 0)
82 hw_type = ARPHRD_ETHER;
83
84 addr.s_addr = inet_addr(daemon->namebuff);
85
86 /* decode hex in place */
87 clid_len = 0;
88 if (strcmp(daemon->packet, "*") != 0)
89 clid_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL);
90
91 if (!(lease = lease_allocate(addr)))
92 die (_("too many stored leases"), NULL, EC_MISC);
93
94#ifdef HAVE_BROKEN_RTC
95 if (ei != 0)
96 lease->expires = (time_t)ei + now;
97 else
98 lease->expires = (time_t)0;
99 lease->length = ei;
100#else
101 /* strictly time_t is opaque, but this hack should work on all sane systems,
102 even when sizeof(time_t) == 8 */
103 lease->expires = (time_t)ei;
104#endif
105
106 lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet, hw_len, hw_type, clid_len);
107
108 if (strcmp(daemon->dhcp_buff, "*") != 0)
109 lease_set_hostname(lease, daemon->dhcp_buff, 0);
110
111 /* set these correctly: the "old" events are generated later from
112 the startup synthesised SIGHUP. */
113 lease->new = lease->changed = 0;
114 }
115
116#ifdef HAVE_SCRIPT
117 if (!daemon->lease_stream)
118 {
119 int rc = 0;
120
121 /* shell returns 127 for "command not found", 126 for bad permissions. */
122 if (!leasestream || (rc = pclose(leasestream)) == -1 || WEXITSTATUS(rc) == 127 || WEXITSTATUS(rc) == 126)
123 {
124 if (WEXITSTATUS(rc) == 127)
125 errno = ENOENT;
126 else if (WEXITSTATUS(rc) == 126)
127 errno = EACCES;
128 die(_("cannot run lease-init script %s: %s"), daemon->lease_change_command, EC_FILE);
129 }
130
131 if (WEXITSTATUS(rc) != 0)
132 {
133 sprintf(daemon->dhcp_buff, "%d", WEXITSTATUS(rc));
134 die(_("lease-init script returned exit code %s"), daemon->dhcp_buff, WEXITSTATUS(rc) + EC_INIT_OFFSET);
135 }
136 }
137#endif
138
139 /* Some leases may have expired */
140 file_dirty = 0;
141 lease_prune(NULL, now);
142 dns_dirty = 1;
143}
144
145void lease_update_from_configs(void)
146{
147 /* changes to the config may change current leases. */
148
149 struct dhcp_lease *lease;
150 struct dhcp_config *config;
151 char *name;
152
153 for (lease = leases; lease; lease = lease->next)
154 if ((config = find_config(daemon->dhcp_conf, NULL, lease->clid, lease->clid_len,
155 lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL)) &&
156 (config->flags & CONFIG_NAME) &&
157 (!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr))
158 lease_set_hostname(lease, config->hostname, 1);
159 else if ((name = host_from_dns(lease->addr)))
160 lease_set_hostname(lease, name, 1); /* updates auth flag only */
161}
162
163static void ourprintf(int *errp, char *format, ...)
164{
165 va_list ap;
166
167 va_start(ap, format);
168 if (!(*errp) && vfprintf(daemon->lease_stream, format, ap) < 0)
169 *errp = errno;
170 va_end(ap);
171}
172
173void lease_update_file(time_t now)
174{
175 struct dhcp_lease *lease;
176 time_t next_event;
177 int i, err = 0;
178
179 if (file_dirty != 0 && daemon->lease_stream)
180 {
181 errno = 0;
182 rewind(daemon->lease_stream);
183 if (errno != 0 || ftruncate(fileno(daemon->lease_stream), 0) != 0)
184 err = errno;
185
186 for (lease = leases; lease; lease = lease->next)
187 {
188#ifdef HAVE_BROKEN_RTC
189 ourprintf(&err, "%u ", lease->length);
190#else
191 ourprintf(&err, "%lu ", (unsigned long)lease->expires);
192#endif
193 if (lease->hwaddr_type != ARPHRD_ETHER || lease->hwaddr_len == 0)
194 ourprintf(&err, "%.2x-", lease->hwaddr_type);
195 for (i = 0; i < lease->hwaddr_len; i++)
196 {
197 ourprintf(&err, "%.2x", lease->hwaddr[i]);
198 if (i != lease->hwaddr_len - 1)
199 ourprintf(&err, ":");
200 }
201
202 ourprintf(&err, " %s ", inet_ntoa(lease->addr));
203 ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*");
204
205 if (lease->clid && lease->clid_len != 0)
206 {
207 for (i = 0; i < lease->clid_len - 1; i++)
208 ourprintf(&err, "%.2x:", lease->clid[i]);
209 ourprintf(&err, "%.2x\n", lease->clid[i]);
210 }
211 else
212 ourprintf(&err, "*\n");
213 }
214
215 if (fflush(daemon->lease_stream) != 0 ||
216 fsync(fileno(daemon->lease_stream)) < 0)
217 err = errno;
218
219 if (!err)
220 file_dirty = 0;
221 }
222
223 /* Set alarm for when the first lease expires + slop. */
224 for (next_event = 0, lease = leases; lease; lease = lease->next)
225 if (lease->expires != 0 &&
226 (next_event == 0 || difftime(next_event, lease->expires + 10) > 0.0))
227 next_event = lease->expires + 10;
228
229 if (err)
230 {
231 if (next_event == 0 || difftime(next_event, LEASE_RETRY + now) > 0.0)
232 next_event = LEASE_RETRY + now;
233
234 my_syslog(MS_DHCP | LOG_ERR, _("failed to write %s: %s (retry in %us)"),
235 daemon->lease_file, strerror(err),
236 (unsigned int)difftime(next_event, now));
237 }
238
239 if (next_event != 0)
240 alarm((unsigned)difftime(next_event, now));
241}
242
243void lease_update_dns(void)
244{
245 struct dhcp_lease *lease;
246
247 if (daemon->port != 0 && dns_dirty)
248 {
249 cache_unhash_dhcp();
250
251 for (lease = leases; lease; lease = lease->next)
252 {
253 if (lease->fqdn)
254 cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires);
255
256 if (!(daemon->options & OPT_DHCP_FQDN) && lease->hostname)
257 cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires);
258 }
259
260 dns_dirty = 0;
261 }
262}
263
264void lease_prune(struct dhcp_lease *target, time_t now)
265{
266 struct dhcp_lease *lease, *tmp, **up;
267
268 for (lease = leases, up = &leases; lease; lease = tmp)
269 {
270 tmp = lease->next;
271 if ((lease->expires != 0 && difftime(now, lease->expires) > 0) || lease == target)
272 {
273 file_dirty = 1;
274 if (lease->hostname)
275 dns_dirty = 1;
276
277 *up = lease->next; /* unlink */
278
279 /* Put on old_leases list 'till we
280 can run the script */
281 lease->next = old_leases;
282 old_leases = lease;
283
284 leases_left++;
285 }
286 else
287 up = &lease->next;
288 }
289}
290
291
292struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type,
293 unsigned char *clid, int clid_len)
294{
295 struct dhcp_lease *lease;
296
297 if (clid)
298 for (lease = leases; lease; lease = lease->next)
299 if (lease->clid && clid_len == lease->clid_len &&
300 memcmp(clid, lease->clid, clid_len) == 0)
301 return lease;
302
303 for (lease = leases; lease; lease = lease->next)
304 if ((!lease->clid || !clid) &&
305 hw_len != 0 &&
306 lease->hwaddr_len == hw_len &&
307 lease->hwaddr_type == hw_type &&
308 memcmp(hwaddr, lease->hwaddr, hw_len) == 0)
309 return lease;
310
311 return NULL;
312}
313
314struct dhcp_lease *lease_find_by_addr(struct in_addr addr)
315{
316 struct dhcp_lease *lease;
317
318 for (lease = leases; lease; lease = lease->next)
319 if (lease->addr.s_addr == addr.s_addr)
320 return lease;
321
322 return NULL;
323}
324
325
326struct dhcp_lease *lease_allocate(struct in_addr addr)
327{
328 struct dhcp_lease *lease;
329 if (!leases_left || !(lease = whine_malloc(sizeof(struct dhcp_lease))))
330 return NULL;
331
332 memset(lease, 0, sizeof(struct dhcp_lease));
333 lease->new = 1;
334 lease->addr = addr;
335 lease->hwaddr_len = 256; /* illegal value */
336 lease->expires = 1;
337#ifdef HAVE_BROKEN_RTC
338 lease->length = 0xffffffff; /* illegal value */
339#endif
340 lease->next = leases;
341 leases = lease;
342
343 file_dirty = 1;
344 leases_left--;
345
346 return lease;
347}
348
349void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
350{
351 time_t exp = now + (time_t)len;
352
353 if (len == 0xffffffff)
354 {
355 exp = 0;
356 len = 0;
357 }
358
359 if (exp != lease->expires)
360 {
361 dns_dirty = 1;
362 lease->expires = exp;
363#ifndef HAVE_BROKEN_RTC
364 lease->aux_changed = file_dirty = 1;
365#endif
366 }
367
368#ifdef HAVE_BROKEN_RTC
369 if (len != lease->length)
370 {
371 lease->length = len;
372 lease->aux_changed = file_dirty = 1;
373 }
374#endif
375}
376
377void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
378 unsigned char *clid, int hw_len, int hw_type, int clid_len)
379{
380 if (hw_len != lease->hwaddr_len ||
381 hw_type != lease->hwaddr_type ||
382 (hw_len != 0 && memcmp(lease->hwaddr, hwaddr, hw_len) != 0))
383 {
384 memcpy(lease->hwaddr, hwaddr, hw_len);
385 lease->hwaddr_len = hw_len;
386 lease->hwaddr_type = hw_type;
387 lease->changed = file_dirty = 1; /* run script on change */
388 }
389
390 /* only update clid when one is available, stops packets
391 without a clid removing the record. Lease init uses
392 clid_len == 0 for no clid. */
393 if (clid_len != 0 && clid)
394 {
395 if (!lease->clid)
396 lease->clid_len = 0;
397
398 if (lease->clid_len != clid_len)
399 {
400 lease->aux_changed = file_dirty = 1;
401 free(lease->clid);
402 if (!(lease->clid = whine_malloc(clid_len)))
403 return;
404 }
405 else if (memcmp(lease->clid, clid, clid_len) != 0)
406 lease->aux_changed = file_dirty = 1;
407
408 lease->clid_len = clid_len;
409 memcpy(lease->clid, clid, clid_len);
410 }
411
412}
413
414static void kill_name(struct dhcp_lease *lease)
415{
416 /* run script to say we lost our old name */
417
418 /* this shouldn't happen unless updates are very quick and the
419 script very slow, we just avoid a memory leak if it does. */
420 free(lease->old_hostname);
421
422 /* If we know the fqdn, pass that. The helper will derive the
423 unqualified name from it, free the unqulaified name here. */
424
425 if (lease->fqdn)
426 {
427 lease->old_hostname = lease->fqdn;
428 free(lease->hostname);
429 }
430 else
431 lease->old_hostname = lease->hostname;
432
433 lease->hostname = lease->fqdn = NULL;
434}
435
436void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth)
437{
438 struct dhcp_lease *lease_tmp;
439 char *new_name = NULL, *new_fqdn = NULL;
440
441 if (lease->hostname && name && hostname_isequal(lease->hostname, name))
442 {
443 lease->auth_name = auth;
444 return;
445 }
446
447 if (!name && !lease->hostname)
448 return;
449
450 /* If a machine turns up on a new net without dropping the old lease,
451 or two machines claim the same name, then we end up with two interfaces with
452 the same name. Check for that here and remove the name from the old lease.
453 Don't allow a name from the client to override a name from dnsmasq config. */
454
455 if (name)
456 {
457 if ((new_name = whine_malloc(strlen(name) + 1)))
458 {
459 char *suffix = get_domain(lease->addr);
460 strcpy(new_name, name);
461 if (suffix && (new_fqdn = whine_malloc(strlen(new_name) + strlen(suffix) + 2)))
462 {
463 strcpy(new_fqdn, name);
464 strcat(new_fqdn, ".");
465 strcat(new_fqdn, suffix);
466 }
467 }
468
469 /* Depending on mode, we check either unqualified name or FQDN. */
470 for (lease_tmp = leases; lease_tmp; lease_tmp = lease_tmp->next)
471 {
472 if (daemon->options & OPT_DHCP_FQDN)
473 {
474 if (!new_fqdn || !lease_tmp->fqdn || !hostname_isequal(lease_tmp->fqdn, new_fqdn) )
475 continue;
476 }
477 else
478 {
479 if (!new_name || !lease_tmp->hostname || !hostname_isequal(lease_tmp->hostname, new_name) )
480 continue;
481 }
482
483 if (lease_tmp->auth_name && !auth)
484 {
485 free(new_name);
486 free(new_fqdn);
487 return;
488 }
489
490 kill_name(lease_tmp);
491 break;
492 }
493 }
494
495 if (lease->hostname)
496 kill_name(lease);
497
498 lease->hostname = new_name;
499 lease->fqdn = new_fqdn;
500 lease->auth_name = auth;
501
502 file_dirty = 1;
503 dns_dirty = 1;
504 lease->changed = 1; /* run script on change */
505}
506
507void lease_set_interface(struct dhcp_lease *lease, int interface)
508{
509 if (lease->last_interface == interface)
510 return;
511
512 lease->last_interface = interface;
513 lease->changed = 1;
514}
515
516void rerun_scripts(void)
517{
518 struct dhcp_lease *lease;
519
520 for (lease = leases; lease; lease = lease->next)
521 lease->changed = 1;
522}
523
524/* deleted leases get transferred to the old_leases list.
525 remove them here, after calling the lease change
526 script. Also run the lease change script on new/modified leases.
527
528 Return zero if nothing to do. */
529int do_script_run(time_t now)
530{
531 struct dhcp_lease *lease;
532
533#ifdef HAVE_DBUS
534 /* If we're going to be sending DBus signals, but the connection is not yet up,
535 delay everything until it is. */
536 if ((daemon->options & OPT_DBUS) && !daemon->dbus)
537 return 0;
538#endif
539
540 if (old_leases)
541 {
542 lease = old_leases;
543
544 /* If the lease still has an old_hostname, do the "old" action on that first */
545 if (lease->old_hostname)
546 {
547#ifdef HAVE_SCRIPT
548 queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
549#endif
550 free(lease->old_hostname);
551 lease->old_hostname = NULL;
552 return 1;
553 }
554 else
555 {
556 kill_name(lease);
557#ifdef HAVE_SCRIPT
558 queue_script(ACTION_DEL, lease, lease->old_hostname, now);
559#endif
560#ifdef HAVE_DBUS
561 emit_dbus_signal(ACTION_DEL, lease, lease->old_hostname);
562#endif
563 old_leases = lease->next;
564
565 free(lease->old_hostname);
566 free(lease->clid);
567 free(lease->vendorclass);
568 free(lease->userclass);
569 free(lease->supplied_hostname);
570 free(lease);
571
572 return 1;
573 }
574 }
575
576 /* make sure we announce the loss of a hostname before its new location. */
577 for (lease = leases; lease; lease = lease->next)
578 if (lease->old_hostname)
579 {
580#ifdef HAVE_SCRIPT
581 queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
582#endif
583 free(lease->old_hostname);
584 lease->old_hostname = NULL;
585 return 1;
586 }
587
588 for (lease = leases; lease; lease = lease->next)
589 if (lease->new || lease->changed ||
590 (lease->aux_changed && (daemon->options & OPT_LEASE_RO)))
591 {
592#ifdef HAVE_SCRIPT
593 queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease,
594 lease->fqdn ? lease->fqdn : lease->hostname, now);
595#endif
596#ifdef HAVE_DBUS
597 emit_dbus_signal(lease->new ? ACTION_ADD : ACTION_OLD, lease,
598 lease->fqdn ? lease->fqdn : lease->hostname);
599#endif
600 lease->new = lease->changed = lease->aux_changed = 0;
601
602 /* these are used for the "add" call, then junked, since they're not in the database */
603 free(lease->vendorclass);
604 lease->vendorclass = NULL;
605
606 free(lease->userclass);
607 lease->userclass = NULL;
608
609 free(lease->supplied_hostname);
610 lease->supplied_hostname = NULL;
611
612 return 1;
613 }
614
615 return 0; /* nothing to do */
616}
617
618#endif
619
620
621
622