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     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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. */
    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