1 /* 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
21 static struct dhcp_lease *leases = NULL, *old_leases = NULL;
22 static int dns_dirty, file_dirty, leases_left;
23
lease_init(time_t now)24 void 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
lease_update_from_configs(void)145 void 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
ourprintf(int * errp,char * format,...)163 static 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
lease_update_file(time_t now)173 void 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
lease_update_dns(void)243 void 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
lease_prune(struct dhcp_lease * target,time_t now)264 void 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
lease_find_by_client(unsigned char * hwaddr,int hw_len,int hw_type,unsigned char * clid,int clid_len)292 struct 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
lease_find_by_addr(struct in_addr addr)314 struct 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
lease_allocate(struct in_addr addr)326 struct 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
lease_set_expires(struct dhcp_lease * lease,unsigned int len,time_t now)349 void 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
lease_set_hwaddr(struct dhcp_lease * lease,unsigned char * hwaddr,unsigned char * clid,int hw_len,int hw_type,int clid_len)377 void 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
kill_name(struct dhcp_lease * lease)414 static 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
lease_set_hostname(struct dhcp_lease * lease,char * name,int auth)436 void 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
lease_set_interface(struct dhcp_lease * lease,int interface)507 void 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
rerun_scripts(void)516 void 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. */
do_script_run(time_t now)529 int do_script_run(time_t now)
530 {
531 struct dhcp_lease *lease;
532
533 if (old_leases)
534 {
535 lease = old_leases;
536
537 /* If the lease still has an old_hostname, do the "old" action on that first */
538 if (lease->old_hostname)
539 {
540 #ifdef HAVE_SCRIPT
541 queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
542 #endif
543 free(lease->old_hostname);
544 lease->old_hostname = NULL;
545 return 1;
546 }
547 else
548 {
549 kill_name(lease);
550 #ifdef HAVE_SCRIPT
551 queue_script(ACTION_DEL, lease, lease->old_hostname, now);
552 #endif
553 old_leases = lease->next;
554
555 free(lease->old_hostname);
556 free(lease->clid);
557 free(lease->vendorclass);
558 free(lease->userclass);
559 free(lease->supplied_hostname);
560 free(lease);
561
562 return 1;
563 }
564 }
565
566 /* make sure we announce the loss of a hostname before its new location. */
567 for (lease = leases; lease; lease = lease->next)
568 if (lease->old_hostname)
569 {
570 #ifdef HAVE_SCRIPT
571 queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
572 #endif
573 free(lease->old_hostname);
574 lease->old_hostname = NULL;
575 return 1;
576 }
577
578 for (lease = leases; lease; lease = lease->next)
579 if (lease->new || lease->changed ||
580 (lease->aux_changed && (daemon->options & OPT_LEASE_RO)))
581 {
582 #ifdef HAVE_SCRIPT
583 queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease,
584 lease->fqdn ? lease->fqdn : lease->hostname, now);
585 #endif
586 lease->new = lease->changed = lease->aux_changed = 0;
587
588 /* these are used for the "add" call, then junked, since they're not in the database */
589 free(lease->vendorclass);
590 lease->vendorclass = NULL;
591
592 free(lease->userclass);
593 lease->userclass = NULL;
594
595 free(lease->supplied_hostname);
596 lease->supplied_hostname = NULL;
597
598 return 1;
599 }
600
601 return 0; /* nothing to do */
602 }
603
604 #endif
605
606
607
608
609