Source code upload
[framework/connectivity/dnsmasq.git] / src / lease.c
1 /* dnsmasq is Copyright (c) 2000-2011 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 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   daemon->dhcp_buff3 = safe_malloc(256);
37  
38   leases_left = daemon->dhcp_max;
39
40   if (option_bool(OPT_LEASE_RO))
41     {
42       /* run "<lease_change_script> init" once to get the
43          initial state of the database. If leasefile-ro is
44          set without a script, we just do without any 
45          lease database. */
46 #ifdef HAVE_SCRIPT
47       if (daemon->lease_change_command)
48         {
49           strcpy(daemon->dhcp_buff, daemon->lease_change_command);
50           strcat(daemon->dhcp_buff, " init");
51           leasestream = popen(daemon->dhcp_buff, "r");
52         }
53       else
54 #endif
55         {
56           file_dirty = dns_dirty = 0;
57           return;
58         }
59
60     }
61   else
62     {
63       /* NOTE: need a+ mode to create file if it doesn't exist */
64       leasestream = daemon->lease_stream = fopen(daemon->lease_file, "a+");
65       
66       if (!leasestream)
67         die(_("cannot open or create lease file %s: %s"), daemon->lease_file, EC_FILE);
68       
69       /* a+ mode leaves pointer at end. */
70       rewind(leasestream);
71     }
72   
73   /* client-id max length is 255 which is 255*2 digits + 254 colons 
74      borrow DNS packet buffer which is always larger than 1000 bytes */
75   if (leasestream)
76     while (fscanf(leasestream, "%lu %255s %16s %255s %764s",
77                   &ei, daemon->dhcp_buff2, daemon->namebuff, 
78                   daemon->dhcp_buff, daemon->packet) == 5)
79       {
80         hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type);
81         /* For backwards compatibility, no explict MAC address type means ether. */
82         if (hw_type == 0 && hw_len != 0)
83           hw_type = ARPHRD_ETHER;
84         
85         addr.s_addr = inet_addr(daemon->namebuff);
86         
87         /* decode hex in place */
88         clid_len = 0;
89         if (strcmp(daemon->packet, "*") != 0)
90           clid_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL);
91         
92         if (!(lease = lease_allocate(addr)))
93           die (_("too many stored leases"), NULL, EC_MISC);
94         
95 #ifdef HAVE_BROKEN_RTC
96         if (ei != 0)
97           lease->expires = (time_t)ei + now;
98         else
99           lease->expires = (time_t)0;
100         lease->length = ei;
101 #else
102         /* strictly time_t is opaque, but this hack should work on all sane systems,
103            even when sizeof(time_t) == 8 */
104         lease->expires = (time_t)ei;
105 #endif
106         
107         lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet, hw_len, hw_type, clid_len);
108         
109         if (strcmp(daemon->dhcp_buff, "*") !=  0)
110           lease_set_hostname(lease, daemon->dhcp_buff, 0);
111
112         /* set these correctly: the "old" events are generated later from
113            the startup synthesised SIGHUP. */
114         lease->new = lease->changed = 0;
115       }
116   
117 #ifdef HAVE_SCRIPT
118   if (!daemon->lease_stream)
119     {
120       int rc = 0;
121
122       /* shell returns 127 for "command not found", 126 for bad permissions. */
123       if (!leasestream || (rc = pclose(leasestream)) == -1 || WEXITSTATUS(rc) == 127 || WEXITSTATUS(rc) == 126)
124         {
125           if (WEXITSTATUS(rc) == 127)
126             errno = ENOENT;
127           else if (WEXITSTATUS(rc) == 126)
128             errno = EACCES;
129           die(_("cannot run lease-init script %s: %s"), daemon->lease_change_command, EC_FILE);
130         }
131       
132       if (WEXITSTATUS(rc) != 0)
133         {
134           sprintf(daemon->dhcp_buff, "%d", WEXITSTATUS(rc));
135           die(_("lease-init script returned exit code %s"), daemon->dhcp_buff, WEXITSTATUS(rc) + EC_INIT_OFFSET);
136         }
137     }
138 #endif
139
140   /* Some leases may have expired */
141   file_dirty = 0;
142   lease_prune(NULL, now);
143   dns_dirty = 1;
144 }
145
146 void lease_update_from_configs(void)
147 {
148   /* changes to the config may change current leases. */
149   
150   struct dhcp_lease *lease;
151   struct dhcp_config *config;
152   char *name;
153
154   for (lease = leases; lease; lease = lease->next)
155     if ((config = find_config(daemon->dhcp_conf, NULL, lease->clid, lease->clid_len, 
156                               lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL)) && 
157         (config->flags & CONFIG_NAME) &&
158         (!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr))
159       lease_set_hostname(lease, config->hostname, 1);
160     else if ((name = host_from_dns(lease->addr)))
161       lease_set_hostname(lease, name, 1); /* updates auth flag only */
162 }
163
164 static void ourprintf(int *errp, char *format, ...)
165 {
166   va_list ap;
167   
168   va_start(ap, format);
169   if (!(*errp) && vfprintf(daemon->lease_stream, format, ap) < 0)
170     *errp = errno;
171   va_end(ap);
172 }
173
174 void lease_update_file(time_t now)
175 {
176   struct dhcp_lease *lease;
177   time_t next_event;
178   int i, err = 0;
179
180   if (file_dirty != 0 && daemon->lease_stream)
181     {
182       errno = 0;
183       rewind(daemon->lease_stream);
184       if (errno != 0 || ftruncate(fileno(daemon->lease_stream), 0) != 0)
185         err = errno;
186       
187       for (lease = leases; lease; lease = lease->next)
188         {
189 #ifdef HAVE_BROKEN_RTC
190           ourprintf(&err, "%u ", lease->length);
191 #else
192           ourprintf(&err, "%lu ", (unsigned long)lease->expires);
193 #endif
194           if (lease->hwaddr_type != ARPHRD_ETHER || lease->hwaddr_len == 0) 
195             ourprintf(&err, "%.2x-", lease->hwaddr_type);
196           for (i = 0; i < lease->hwaddr_len; i++)
197             {
198               ourprintf(&err, "%.2x", lease->hwaddr[i]);
199               if (i != lease->hwaddr_len - 1)
200                 ourprintf(&err, ":");
201             }
202
203           ourprintf(&err, " %s ", inet_ntoa(lease->addr));
204           ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*");
205                   
206           if (lease->clid && lease->clid_len != 0)
207             {
208               for (i = 0; i < lease->clid_len - 1; i++)
209                 ourprintf(&err, "%.2x:", lease->clid[i]);
210               ourprintf(&err, "%.2x\n", lease->clid[i]);
211             }
212           else
213             ourprintf(&err, "*\n");       
214         }
215       
216       if (fflush(daemon->lease_stream) != 0 ||
217           fsync(fileno(daemon->lease_stream)) < 0)
218         err = errno;
219       
220       if (!err)
221         file_dirty = 0;
222     }
223   
224   /* Set alarm for when the first lease expires + slop. */
225   for (next_event = 0, lease = leases; lease; lease = lease->next)
226     if (lease->expires != 0 &&
227         (next_event == 0 || difftime(next_event, lease->expires + 10) > 0.0))
228       next_event = lease->expires + 10;
229    
230   if (err)
231     {
232       if (next_event == 0 || difftime(next_event, LEASE_RETRY + now) > 0.0)
233         next_event = LEASE_RETRY + now;
234       
235       my_syslog(MS_DHCP | LOG_ERR, _("failed to write %s: %s (retry in %us)"), 
236                 daemon->lease_file, strerror(err),
237                 (unsigned int)difftime(next_event, now));
238     }
239
240   if (next_event != 0)
241     alarm((unsigned)difftime(next_event, now)); 
242 }
243
244 void lease_update_dns(void)
245 {
246   struct dhcp_lease *lease;
247   
248   if (daemon->port != 0 && dns_dirty)
249     {
250       cache_unhash_dhcp();
251       
252       for (lease = leases; lease; lease = lease->next)
253         {
254           if (lease->fqdn)
255             cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires);
256              
257           if (!option_bool(OPT_DHCP_FQDN) && lease->hostname)
258             cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires);
259         }
260       
261       dns_dirty = 0;
262     }
263 }
264
265 void lease_prune(struct dhcp_lease *target, time_t now)
266 {
267   struct dhcp_lease *lease, *tmp, **up;
268
269   for (lease = leases, up = &leases; lease; lease = tmp)
270     {
271       tmp = lease->next;
272       if ((lease->expires != 0 && difftime(now, lease->expires) > 0) || lease == target)
273         {
274           file_dirty = 1;
275           if (lease->hostname)
276             dns_dirty = 1;
277           
278           *up = lease->next; /* unlink */
279           
280           /* Put on old_leases list 'till we
281              can run the script */
282           lease->next = old_leases;
283           old_leases = lease;
284           
285           leases_left++;
286         }
287       else
288         up = &lease->next;
289     }
290
291         
292   
293 struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type,
294                                         unsigned char *clid, int clid_len)
295 {
296   struct dhcp_lease *lease;
297
298   if (clid)
299     for (lease = leases; lease; lease = lease->next)
300       if (lease->clid && clid_len == lease->clid_len &&
301           memcmp(clid, lease->clid, clid_len) == 0)
302         return lease;
303   
304   for (lease = leases; lease; lease = lease->next)      
305     if ((!lease->clid || !clid) && 
306         hw_len != 0 && 
307         lease->hwaddr_len == hw_len &&
308         lease->hwaddr_type == hw_type &&
309         memcmp(hwaddr, lease->hwaddr, hw_len) == 0)
310       return lease;
311   
312   return NULL;
313 }
314
315 struct dhcp_lease *lease_find_by_addr(struct in_addr addr)
316 {
317   struct dhcp_lease *lease;
318
319   for (lease = leases; lease; lease = lease->next)
320     if (lease->addr.s_addr == addr.s_addr)
321       return lease;
322   
323   return NULL;
324 }
325
326
327 struct dhcp_lease *lease_allocate(struct in_addr addr)
328 {
329   struct dhcp_lease *lease;
330   if (!leases_left || !(lease = whine_malloc(sizeof(struct dhcp_lease))))
331     return NULL;
332
333   memset(lease, 0, sizeof(struct dhcp_lease));
334   lease->new = 1;
335   lease->addr = addr;
336   lease->hwaddr_len = 256; /* illegal value */
337   lease->expires = 1;
338 #ifdef HAVE_BROKEN_RTC
339   lease->length = 0xffffffff; /* illegal value */
340 #endif
341   lease->next = leases;
342   leases = lease;
343   
344   file_dirty = 1;
345   leases_left--;
346
347   return lease;
348 }
349
350 void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
351 {
352   time_t exp = now + (time_t)len;
353   
354   if (len == 0xffffffff)
355     {
356       exp = 0;
357       len = 0;
358     }
359   
360   if (exp != lease->expires)
361     {
362       dns_dirty = 1;
363       lease->expires = exp;
364 #ifndef HAVE_BROKEN_RTC
365       lease->aux_changed = file_dirty = 1;
366 #endif
367     }
368   
369 #ifdef HAVE_BROKEN_RTC
370   if (len != lease->length)
371     {
372       lease->length = len;
373       lease->aux_changed = file_dirty = 1; 
374     }
375 #endif
376
377
378 void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
379                       unsigned char *clid, int hw_len, int hw_type, int clid_len)
380 {
381   if (hw_len != lease->hwaddr_len ||
382       hw_type != lease->hwaddr_type || 
383       (hw_len != 0 && memcmp(lease->hwaddr, hwaddr, hw_len) != 0))
384     {
385       memcpy(lease->hwaddr, hwaddr, hw_len);
386       lease->hwaddr_len = hw_len;
387       lease->hwaddr_type = hw_type;
388       lease->changed = file_dirty = 1; /* run script on change */
389     }
390
391   /* only update clid when one is available, stops packets
392      without a clid removing the record. Lease init uses
393      clid_len == 0 for no clid. */
394   if (clid_len != 0 && clid)
395     {
396       if (!lease->clid)
397         lease->clid_len = 0;
398
399       if (lease->clid_len != clid_len)
400         {
401           lease->aux_changed = file_dirty = 1;
402           free(lease->clid);
403           if (!(lease->clid = whine_malloc(clid_len)))
404             return;
405         }
406       else if (memcmp(lease->clid, clid, clid_len) != 0)
407         lease->aux_changed = file_dirty = 1;
408           
409       lease->clid_len = clid_len;
410       memcpy(lease->clid, clid, clid_len);
411     }
412
413 }
414
415 static void kill_name(struct dhcp_lease *lease)
416 {
417   /* run script to say we lost our old name */
418   
419   /* this shouldn't happen unless updates are very quick and the
420      script very slow, we just avoid a memory leak if it does. */
421   free(lease->old_hostname);
422   
423   /* If we know the fqdn, pass that. The helper will derive the
424      unqualified name from it, free the unqulaified name here. */
425
426   if (lease->fqdn)
427     {
428       lease->old_hostname = lease->fqdn;
429       free(lease->hostname);
430     }
431   else
432     lease->old_hostname = lease->hostname;
433
434   lease->hostname = lease->fqdn = NULL;
435 }
436
437 void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth)
438 {
439   struct dhcp_lease *lease_tmp;
440   char *new_name = NULL, *new_fqdn = NULL;
441   
442   if (lease->hostname && name && hostname_isequal(lease->hostname, name))
443     {
444       lease->auth_name = auth;
445       return;
446     }
447   
448   if (!name && !lease->hostname)
449     return;
450
451   /* If a machine turns up on a new net without dropping the old lease,
452      or two machines claim the same name, then we end up with two interfaces with
453      the same name. Check for that here and remove the name from the old lease.
454      Don't allow a name from the client to override a name from dnsmasq config. */
455   
456   if (name)
457     {
458       if ((new_name = whine_malloc(strlen(name) + 1)))
459         {
460           char *suffix = get_domain(lease->addr);
461           strcpy(new_name, name);
462           if (suffix && (new_fqdn = whine_malloc(strlen(new_name) + strlen(suffix) + 2)))
463             {
464               strcpy(new_fqdn, name);
465               strcat(new_fqdn, ".");
466               strcat(new_fqdn, suffix);
467             }
468         }
469           
470       /* Depending on mode, we check either unqualified name or FQDN. */
471       for (lease_tmp = leases; lease_tmp; lease_tmp = lease_tmp->next)
472         {
473           if (option_bool(OPT_DHCP_FQDN))
474             {
475               if (!new_fqdn || !lease_tmp->fqdn || !hostname_isequal(lease_tmp->fqdn, new_fqdn) )
476                 continue;
477             }
478           else
479             {
480               if (!new_name || !lease_tmp->hostname || !hostname_isequal(lease_tmp->hostname, new_name) )
481                 continue; 
482             }
483           
484           if (lease_tmp->auth_name && !auth)
485             {
486               free(new_name);
487               free(new_fqdn);
488               return;
489             }
490         
491           kill_name(lease_tmp);
492           break;
493         }
494     }
495
496   if (lease->hostname)
497     kill_name(lease);
498
499   lease->hostname = new_name;
500   lease->fqdn = new_fqdn;
501   lease->auth_name = auth;
502   
503   file_dirty = 1;
504   dns_dirty = 1; 
505   lease->changed = 1; /* run script on change */
506 }
507
508 void lease_set_interface(struct dhcp_lease *lease, int interface)
509 {
510   if (lease->last_interface == interface)
511     return;
512
513   lease->last_interface = interface;
514   lease->changed = 1;
515 }
516
517 void rerun_scripts(void)
518 {
519   struct dhcp_lease *lease;
520   
521   for (lease = leases; lease; lease = lease->next)
522     lease->changed = 1;
523 }
524
525 /* deleted leases get transferred to the old_leases list.
526    remove them here, after calling the lease change
527    script. Also run the lease change script on new/modified leases.
528
529    Return zero if nothing to do. */
530 int do_script_run(time_t now)
531 {
532   struct dhcp_lease *lease;
533
534 #ifdef HAVE_DBUS
535   /* If we're going to be sending DBus signals, but the connection is not yet up,
536      delay everything until it is. */
537   if (option_bool(OPT_DBUS) && !daemon->dbus)
538     return 0;
539 #endif
540
541   if (old_leases)
542     {
543       lease = old_leases;
544                   
545       /* If the lease still has an old_hostname, do the "old" action on that first */
546       if (lease->old_hostname)
547         {
548 #ifdef HAVE_SCRIPT
549           queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
550 #endif
551           free(lease->old_hostname);
552           lease->old_hostname = NULL;
553           return 1;
554         }
555       else 
556         {
557           kill_name(lease);
558 #ifdef HAVE_SCRIPT
559           queue_script(ACTION_DEL, lease, lease->old_hostname, now);
560 #endif
561 #ifdef HAVE_DBUS
562           emit_dbus_signal(ACTION_DEL, lease, lease->old_hostname);
563 #endif
564           old_leases = lease->next;
565           
566           free(lease->old_hostname); 
567           free(lease->clid);
568           free(lease->extradata);
569           free(lease);
570             
571           return 1; 
572         }
573     }
574   
575   /* make sure we announce the loss of a hostname before its new location. */
576   for (lease = leases; lease; lease = lease->next)
577     if (lease->old_hostname)
578       { 
579 #ifdef HAVE_SCRIPT
580         queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
581 #endif
582         free(lease->old_hostname);
583         lease->old_hostname = NULL;
584         return 1;
585       }
586   
587   for (lease = leases; lease; lease = lease->next)
588     if (lease->new || lease->changed || 
589         (lease->aux_changed && option_bool(OPT_LEASE_RO)))
590       {
591 #ifdef HAVE_SCRIPT
592         queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease, 
593                      lease->fqdn ? lease->fqdn : lease->hostname, now);
594 #endif
595 #ifdef HAVE_DBUS
596         emit_dbus_signal(lease->new ? ACTION_ADD : ACTION_OLD, lease,
597                          lease->fqdn ? lease->fqdn : lease->hostname);
598 #endif
599         lease->new = lease->changed = lease->aux_changed = 0;
600         
601         /* this is used for the "add" call, then junked, since they're not in the database */
602         free(lease->extradata);
603         lease->extradata = NULL;
604         
605         return 1;
606       }
607
608   return 0; /* nothing to do */
609 }
610
611 #endif
612           
613
614       
615