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