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 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
lease_update_from_configs(void)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
ourprintf(int * errp,char * format,...)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
lease_update_file(time_t now)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
lease_update_dns(void)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
lease_prune(struct dhcp_lease * target,time_t now)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
lease_find_by_client(unsigned char * hwaddr,int hw_len,int hw_type,unsigned char * clid,int clid_len)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
lease_find_by_addr(struct in_addr addr)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
lease_allocate(struct in_addr addr)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
lease_set_expires(struct dhcp_lease * lease,unsigned int len,time_t now)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
lease_set_hwaddr(struct dhcp_lease * lease,unsigned char * hwaddr,unsigned char * clid,int hw_len,int hw_type,int clid_len)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
kill_name(struct dhcp_lease * lease)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
lease_set_hostname(struct dhcp_lease * lease,char * name,int auth)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
lease_set_interface(struct dhcp_lease * lease,int interface)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
rerun_scripts(void)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. */
do_script_run(time_t now)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