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
21static struct dhcp_lease *leases = NULL, *old_leases = NULL;
22static int dns_dirty, file_dirty, leases_left;
23
24void 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
145void 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
163static 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
173void 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
243void 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
264void 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
292struct 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
314struct 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
326struct 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
349void 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
377void 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
414static 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
436void 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
507void 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
516void 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. */
529int 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