1/* dnsmasq is Copyright (c) 2000-2003 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. 6 7 This program is distributed in the hope that it will be useful, 8 but WITHOUT ANY WARRANTY; without even the implied warranty of 9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 GNU General Public License for more details. 11*/ 12 13/* Author's email: simon@thekelleys.org.uk */ 14 15#include "dnsmasq.h" 16 17static struct dhcp_lease *leases; 18static FILE *lease_file; 19static int dns_dirty, file_dirty, new_lease; 20static int leases_left; 21 22void lease_init(struct daemon *daemon, time_t now) 23{ 24 unsigned int e0, e1, e2, e3, e4, e5, a0, a1, a2, a3; 25 unsigned long ei; 26 time_t expires; 27 unsigned char hwaddr[ETHER_ADDR_LEN]; 28 struct in_addr addr; 29 struct dhcp_lease *lease; 30 int clid_len = 0; 31 int has_old = 0; 32 char *buff = daemon->dhcp_buff; 33 char *buff2 = daemon->dhcp_buff2; 34 35 leases = NULL; 36 leases_left = daemon->dhcp_max; 37 38 /* NOTE: need a+ mode to create file if it doesn't exist */ 39 if (!(lease_file = fopen(daemon->lease_file, "a+"))) 40 die("cannot open or create leases file: %s", NULL); 41 42 /* a+ mode lease pointer at end. */ 43 rewind(lease_file); 44 45 while (fscanf(lease_file, "%lu %x:%x:%x:%x:%x:%x %d.%d.%d.%d %257s %257s", 46 &ei, &e0, &e1, &e2, &e3, &e4, &e5, &a0, &a1, &a2, &a3, 47 buff, buff2) == 13) 48 { 49#ifdef HAVE_BROKEN_RTC 50 if (ei) 51 expires = (time_t)ei + now; 52 else 53 expires = (time_t)0; 54#else 55 /* strictly time_t is opaque, but this hack should work on all sane systems, 56 even when sizeof(time_t) == 8 */ 57 expires = (time_t)ei; 58 59 if (ei != 0 && difftime(now, expires) > 0) 60 { 61 has_old = 1; 62 continue; /* expired */ 63 } 64#endif 65 66 hwaddr[0] = e0; 67 hwaddr[1] = e1; 68 hwaddr[2] = e2; 69 hwaddr[3] = e3; 70 hwaddr[4] = e4; 71 hwaddr[5] = e5; 72 73 addr.s_addr = htonl((a0<<24) + (a1<<16) + (a2<<8) + a3); 74 75 /* decode hex in place */ 76 if (strcmp(buff2, "*") == 0) 77 clid_len = 0; 78 else 79 { 80 int s = (strlen(buff2)/3) + 1; 81 for (clid_len = 0; clid_len < s; clid_len++) 82 { 83 buff2[(clid_len*3)+2] = 0; 84 buff2[clid_len] = strtol(&buff2[clid_len*3], NULL, 16); 85 } 86 } 87 88 if (!(lease = lease_allocate(buff2, clid_len, addr))) 89 die ("too many stored leases", NULL); 90 91 lease->expires = expires; 92 memcpy(lease->hwaddr, hwaddr, ETHER_ADDR_LEN); 93 94 if (strcmp(buff, "*") != 0) 95 lease_set_hostname(lease, buff, daemon->domain_suffix); 96 } 97 98 dns_dirty = 1; 99 file_dirty = has_old; 100 new_lease = 0; 101 102 daemon->lease_fd = fileno(lease_file); 103} 104 105void lease_update_from_configs(struct dhcp_config *dhcp_configs, char *domain) 106{ 107 /* changes to the config may change current leases. */ 108 109 struct dhcp_lease *lease; 110 struct dhcp_config *config; 111 112 for (lease = leases; lease; lease = lease->next) 113 if ((config = find_config(dhcp_configs, NULL, lease->clid, lease->clid_len, lease->hwaddr, NULL)) && 114 (config->flags & CONFIG_NAME)) 115 lease_set_hostname(lease, config->hostname, domain); 116} 117 118void lease_update_file(int force, time_t now) 119{ 120 struct dhcp_lease *lease; 121 int i = force; /* avoid warning */ 122 unsigned long expires; 123 124#ifdef HAVE_BROKEN_RTC 125 if (force || new_lease) 126 { 127 lease_prune(NULL, now); 128#else 129 if (file_dirty) 130 { 131#endif 132 rewind(lease_file); 133 ftruncate(fileno(lease_file), 0); 134 135 for (lease = leases; lease; lease = lease->next) 136 { 137#ifdef HAVE_BROKEN_RTC 138 if (lease->expires) 139 expires = (unsigned long) difftime(lease->expires, now); 140 else 141 expires = 0; 142#else 143 expires = now; /* eliminate warning */ 144 expires = (unsigned long)lease->expires; 145#endif 146 fprintf(lease_file, "%lu %.2x:%.2x:%.2x:%.2x:%.2x:%.2x %s %s ", 147 expires, lease->hwaddr[0], lease->hwaddr[1], 148 lease->hwaddr[2], lease->hwaddr[3], lease->hwaddr[4], 149 lease->hwaddr[5], inet_ntoa(lease->addr), 150 lease->hostname && strlen(lease->hostname) != 0 ? lease->hostname : "*"); 151 152 if (lease->clid_len) 153 { 154 for (i = 0; i < lease->clid_len - 1; i++) 155 fprintf(lease_file, "%.2x:", lease->clid[i]); 156 fprintf(lease_file, "%.2x\n", lease->clid[i]); 157 } 158 else 159 fprintf(lease_file, "*\n"); 160 161 } 162 163 fflush(lease_file); 164 fsync(fileno(lease_file)); 165 file_dirty = 0; 166 new_lease = 0; 167 } 168} 169 170void lease_update_dns(void) 171{ 172 struct dhcp_lease *lease; 173 174 if (dns_dirty) 175 { 176 cache_unhash_dhcp(); 177 178 for (lease = leases; lease; lease = lease->next) 179 { 180 cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires); 181 cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires); 182 } 183 184 dns_dirty = 0; 185 } 186} 187 188void lease_prune(struct dhcp_lease *target, time_t now) 189{ 190 struct dhcp_lease *lease, *tmp, **up; 191 192 for (lease = leases, up = &leases; lease; lease = tmp) 193 { 194 tmp = lease->next; 195 if ((lease->expires != 0 && difftime(now, lease->expires) > 0) || lease == target) 196 { 197 file_dirty = 1; 198 199 *up = lease->next; /* unlink */ 200 if (lease->hostname) 201 { 202 free(lease->hostname); 203 dns_dirty = 1; 204 } 205 if (lease->fqdn) 206 free(lease->fqdn); 207 if (lease->clid) 208 free(lease->clid); 209 free(lease); 210 leases_left++; 211 } 212 else 213 up = &lease->next; 214 } 215} 216 217 218struct dhcp_lease *lease_find_by_client(unsigned char *clid, int clid_len) 219{ 220 struct dhcp_lease *lease; 221 222 if (clid_len) 223 { 224 for (lease = leases; lease; lease = lease->next) 225 if (lease->clid && clid_len == lease->clid_len && 226 memcmp(clid, lease->clid, clid_len) == 0) 227 return lease; 228 } 229 else 230 { 231 for (lease = leases; lease; lease = lease->next) 232 if (memcmp(clid, lease->hwaddr, ETHER_ADDR_LEN) == 0) 233 return lease; 234 } 235 236 return NULL; 237} 238 239struct dhcp_lease *lease_find_by_addr(struct in_addr addr) 240{ 241 struct dhcp_lease *lease; 242 243 for (lease = leases; lease; lease = lease->next) 244 if (lease->addr.s_addr == addr.s_addr) 245 return lease; 246 247 return NULL; 248} 249 250 251struct dhcp_lease *lease_allocate(unsigned char *clid, int clid_len, struct in_addr addr) 252{ 253 struct dhcp_lease *lease; 254 if (!leases_left || !(lease = malloc(sizeof(struct dhcp_lease)))) 255 return NULL; 256 257 lease->clid = NULL; 258 lease->clid_len = clid_len; 259 260 if (clid_len) 261 { 262 if (!(lease->clid = malloc(clid_len))) 263 { 264 free(lease); 265 return NULL; 266 } 267 memcpy(lease->clid, clid, clid_len); 268 } 269 270 lease->hostname = lease->fqdn = NULL; 271 lease->addr = addr; 272 memset(lease->hwaddr, 0, ETHER_ADDR_LEN); 273 lease->expires = 1; 274 275 lease->next = leases; 276 leases = lease; 277 278 file_dirty = 1; 279 new_lease = 1; 280 leases_left--; 281 282 return lease; 283} 284 285void lease_set_expires(struct dhcp_lease *lease, time_t exp) 286{ 287 if (exp != lease->expires) 288 file_dirty = dns_dirty = 1; 289 290 lease->expires = exp; 291} 292 293void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr) 294{ 295 if (memcmp(lease->hwaddr, hwaddr, ETHER_ADDR_LEN) != 0) 296 { 297 file_dirty = 1; 298 memcpy(lease->hwaddr, hwaddr, ETHER_ADDR_LEN); 299 } 300} 301 302void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix) 303{ 304 struct dhcp_lease *lease_tmp; 305 char *new_name = NULL, *new_fqdn = NULL; 306 307 if (lease->hostname && name && hostname_isequal(lease->hostname, name)) 308 return; 309 310 if (!name && !lease->hostname) 311 return; 312 313 /* If a machine turns up on a new net without dropping the old lease, 314 or two machines claim the same name, then we end up with two interfaces with 315 the same name. Check for that here and remove the name from the old lease. */ 316 317 if (name) 318 { 319 for (lease_tmp = leases; lease_tmp; lease_tmp = lease_tmp->next) 320 if (lease_tmp->hostname && hostname_isequal(lease_tmp->hostname, name)) 321 { 322 new_name = lease_tmp->hostname; 323 lease_tmp->hostname = NULL; 324 if (lease_tmp->fqdn) 325 { 326 new_fqdn = lease_tmp->fqdn; 327 lease_tmp->fqdn = NULL; 328 } 329 } 330 331 if (!new_name && (new_name = malloc(strlen(name) + 1))) 332 strcpy(new_name, name); 333 334 if (suffix && !new_fqdn && (new_fqdn = malloc(strlen(name) + strlen(suffix) + 2))) 335 { 336 strcpy(new_fqdn, name); 337 strcat(new_fqdn, "."); 338 strcat(new_fqdn, suffix); 339 } 340 } 341 342 if (lease->hostname) 343 free(lease->hostname); 344 if (lease->fqdn) 345 free(lease->fqdn); 346 347 lease->hostname = new_name; 348 lease->fqdn = new_fqdn; 349 350 file_dirty = dns_dirty = 1; 351} 352 353 354 355