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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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. */ 529 int 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 623