Home | History | Annotate | Download | only in src
      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 {
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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. */
    529 int 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