• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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