~cyphermox/ubuntu/precise/dnsmasq/dbus

« back to all changes in this revision

Viewing changes to src/lease.c

  • Committer: Bazaar Package Importer
  • Author(s): Simon Kelley
  • Date: 2005-05-04 13:25:23 UTC
  • mfrom: (0.2.1 upstream) (1.1.2 hoary)
  • Revision ID: james.westby@ubuntu.com-20050504132523-29x9nzdnkypp62nc
Tags: 2.22-2
Make the resolv.conf polling code resistant to 
backwards-moving system clocks. (closes: #306117) (closes: #300694)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* dnsmasq is Copyright (c) 2000-2005 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.
 
6
 
 
7
   This program is distributed in the hope that it will be useful,
 
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
10
   GNU General Public License for more details.
 
11
*/
 
12
 
 
13
/* Author's email: simon@thekelleys.org.uk */
 
14
 
 
15
#include "dnsmasq.h"
 
16
 
 
17
static struct dhcp_lease *leases;
 
18
static FILE *lease_file;
 
19
static int dns_dirty;
 
20
enum { no, yes, force } file_dirty;
 
21
static int leases_left;
 
22
 
 
23
void lease_init(struct daemon *daemon, time_t now)
 
24
{
 
25
  unsigned int a0, a1, a2, a3;
 
26
  unsigned long ei;
 
27
  time_t expires;
 
28
  unsigned char hwaddr[ETHER_ADDR_LEN];
 
29
  struct in_addr addr;
 
30
  struct dhcp_lease *lease;
 
31
  int clid_len = 0;
 
32
  int has_old = 0;
 
33
 
 
34
  leases = NULL;
 
35
  leases_left = daemon->dhcp_max;
 
36
 
 
37
  /* NOTE: need a+ mode to create file if it doesn't exist */
 
38
  if (!(lease_file = fopen(daemon->lease_file, "a+")))
 
39
    die("cannot open or create leases file: %s", NULL);
 
40
    
 
41
  /* a+ mode lease pointer at end. */
 
42
  rewind(lease_file);
 
43
 
 
44
  /* client-id max length is 255 which is 255*2 digits + 254 colons 
 
45
     borrow DNS packet buffer which is always larger than 1000 bytes */
 
46
  while (fscanf(lease_file, "%lu %40s %d.%d.%d.%d %255s %764s",
 
47
                &ei, daemon->dhcp_buff2, &a0, &a1, &a2, &a3, 
 
48
                daemon->dhcp_buff, daemon->packet) == 8)
 
49
    {
 
50
#ifdef HAVE_BROKEN_RTC
 
51
      if (ei)
 
52
        expires = (time_t)ei + now;
 
53
      else
 
54
        expires = (time_t)0;
 
55
#else 
 
56
      /* strictly time_t is opaque, but this hack should work on all sane systems,
 
57
         even when sizeof(time_t) == 8 */
 
58
      expires = (time_t)ei;
 
59
      
 
60
      if (ei != 0  && difftime(now, expires) > 0)
 
61
        {
 
62
          has_old = 1;
 
63
          continue; /* expired */
 
64
        }
 
65
#endif
 
66
 
 
67
      parse_hex(daemon->dhcp_buff2, hwaddr, ETHER_ADDR_LEN, NULL);
 
68
      addr.s_addr = htonl((a0<<24) + (a1<<16) + (a2<<8) + a3);
 
69
 
 
70
      /* decode hex in place */
 
71
      if (strcmp(daemon->packet, "*") == 0)
 
72
        clid_len = 0;
 
73
      else
 
74
        clid_len = parse_hex(daemon->packet, daemon->packet, 255, NULL);
 
75
      
 
76
      if (!(lease = lease_allocate(hwaddr, daemon->packet, clid_len, addr)))
 
77
        die ("too many stored leases", NULL);
 
78
      
 
79
      lease->expires = expires;
 
80
 
 
81
      if (strcmp(daemon->dhcp_buff, "*") !=  0)
 
82
          lease_set_hostname(lease, daemon->dhcp_buff, daemon->domain_suffix);
 
83
    }
 
84
  
 
85
  dns_dirty = 1;
 
86
  file_dirty = has_old ? yes: no;
 
87
 
 
88
  daemon->lease_fd = fileno(lease_file);
 
89
}
 
90
 
 
91
void lease_update_from_configs(struct dhcp_config *dhcp_configs, char *domain)
 
92
{
 
93
  /* changes to the config may change current leases. */
 
94
  
 
95
  struct dhcp_lease *lease;
 
96
  struct dhcp_config *config;
 
97
  
 
98
  for (lease = leases; lease; lease = lease->next)
 
99
    if ((config = find_config(dhcp_configs, NULL, lease->clid, lease->clid_len, lease->hwaddr, NULL)) && 
 
100
        (config->flags & CONFIG_NAME))
 
101
      lease_set_hostname(lease, config->hostname, domain);
 
102
}
 
103
 
 
104
void lease_update_file(int always, time_t now)
 
105
{
 
106
  struct dhcp_lease *lease;
 
107
  int i = always; /* avoid warning */
 
108
  unsigned long expires;
 
109
 
 
110
#ifdef HAVE_BROKEN_RTC
 
111
  if (always || file_dirty == force)
 
112
    {
 
113
      lease_prune(NULL, now);
 
114
#else
 
115
  if (file_dirty != no)
 
116
    {
 
117
#endif
 
118
      rewind(lease_file);
 
119
      ftruncate(fileno(lease_file), 0);
 
120
      
 
121
      for (lease = leases; lease; lease = lease->next)
 
122
        {
 
123
#ifdef HAVE_BROKEN_RTC
 
124
          if (lease->expires)
 
125
            expires = (unsigned long) difftime(lease->expires, now);
 
126
          else
 
127
            expires = 0;
 
128
#else
 
129
          expires = now; /* eliminate warning */
 
130
          expires = (unsigned long)lease->expires;
 
131
#endif
 
132
          fprintf(lease_file, "%lu %.2x:%.2x:%.2x:%.2x:%.2x:%.2x %s %s ", 
 
133
                  expires, lease->hwaddr[0], lease->hwaddr[1],
 
134
                  lease->hwaddr[2], lease->hwaddr[3], lease->hwaddr[4],
 
135
                  lease->hwaddr[5], inet_ntoa(lease->addr),
 
136
                  lease->hostname && strlen(lease->hostname) != 0 ? lease->hostname : "*");
 
137
          
 
138
          if (lease->clid && lease->clid_len != 0)
 
139
            {
 
140
              for (i = 0; i < lease->clid_len - 1; i++)
 
141
                fprintf(lease_file, "%.2x:", lease->clid[i]);
 
142
              fprintf(lease_file, "%.2x\n", lease->clid[i]);
 
143
            }
 
144
          else
 
145
            fprintf(lease_file, "*\n");
 
146
          
 
147
        }
 
148
 
 
149
      fflush(lease_file);
 
150
      fsync(fileno(lease_file));
 
151
      file_dirty = no;
 
152
    }
 
153
}
 
154
 
 
155
void lease_update_dns(struct daemon *daemon)
 
156
{
 
157
  struct dhcp_lease *lease;
 
158
  
 
159
  if (dns_dirty)
 
160
    {
 
161
      cache_unhash_dhcp();
 
162
      
 
163
      for (lease = leases; lease; lease = lease->next)
 
164
        {
 
165
          cache_add_dhcp_entry(daemon, lease->fqdn, &lease->addr, lease->expires);
 
166
          cache_add_dhcp_entry(daemon, lease->hostname, &lease->addr, lease->expires);
 
167
        }
 
168
      
 
169
      dns_dirty = 0;
 
170
    }
 
171
}
 
172
 
 
173
void lease_prune(struct dhcp_lease *target, time_t now)
 
174
{
 
175
  struct dhcp_lease *lease, *tmp, **up;
 
176
 
 
177
  for (lease = leases, up = &leases; lease; lease = tmp)
 
178
    {
 
179
      tmp = lease->next;
 
180
      if ((lease->expires != 0 && difftime(now, lease->expires) > 0) || lease == target)
 
181
        {
 
182
          file_dirty = yes;
 
183
 
 
184
          *up = lease->next; /* unlink */
 
185
          if (lease->hostname)
 
186
            {
 
187
              free(lease->hostname); 
 
188
              dns_dirty = 1;
 
189
            }
 
190
          if (lease->fqdn)
 
191
            free(lease->fqdn);
 
192
          if (lease->clid)
 
193
            free(lease->clid);
 
194
          free(lease);
 
195
          leases_left++;
 
196
        }
 
197
      else
 
198
        up = &lease->next;
 
199
    }
 
200
 
201
        
 
202
  
 
203
struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr,
 
204
                                        unsigned char *clid, int clid_len)
 
205
{
 
206
  struct dhcp_lease *lease;
 
207
 
 
208
  if (clid)
 
209
    for (lease = leases; lease; lease = lease->next)
 
210
      if (lease->clid && clid_len == lease->clid_len &&
 
211
          memcmp(clid, lease->clid, clid_len) == 0)
 
212
        return lease;
 
213
  
 
214
  for (lease = leases; lease; lease = lease->next)      
 
215
    if ((!lease->clid || !clid) && 
 
216
        memcmp(hwaddr, lease->hwaddr, ETHER_ADDR_LEN) == 0)
 
217
      return lease;
 
218
  
 
219
  return NULL;
 
220
}
 
221
 
 
222
struct dhcp_lease *lease_find_by_addr(struct in_addr addr)
 
223
{
 
224
  struct dhcp_lease *lease;
 
225
 
 
226
  for (lease = leases; lease; lease = lease->next)
 
227
    if (lease->addr.s_addr == addr.s_addr)
 
228
      return lease;
 
229
  
 
230
  return NULL;
 
231
}
 
232
 
 
233
 
 
234
struct dhcp_lease *lease_allocate(unsigned char *hwaddr, unsigned char *clid, 
 
235
                                  int clid_len, struct in_addr addr)
 
236
{
 
237
  struct dhcp_lease *lease;
 
238
  if (!leases_left || !(lease = malloc(sizeof(struct dhcp_lease))))
 
239
    return NULL;
 
240
 
 
241
  lease->clid = NULL;
 
242
  lease->hostname = lease->fqdn = NULL;  
 
243
  lease->addr = addr;
 
244
  memset(lease->hwaddr, 0, ETHER_ADDR_LEN);
 
245
  lease->expires = 1;
 
246
  
 
247
  if (!lease_set_hwaddr(lease, hwaddr, clid, clid_len))
 
248
    {
 
249
      free(lease);
 
250
      return NULL;
 
251
    }
 
252
 
 
253
  lease->next = leases;
 
254
  leases = lease;
 
255
  
 
256
  file_dirty = force;
 
257
  leases_left--;
 
258
 
 
259
  return lease;
 
260
}
 
261
 
 
262
void lease_set_expires(struct dhcp_lease *lease, time_t exp)
 
263
{
 
264
  if (exp != lease->expires)
 
265
    {
 
266
      file_dirty = yes;
 
267
      dns_dirty = 1;
 
268
    }
 
269
  lease->expires = exp;
 
270
}
 
271
 
 
272
int lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
 
273
                      unsigned char *clid, int clid_len)
 
274
{
 
275
  if (memcmp(lease->hwaddr, hwaddr, ETHER_ADDR_LEN) != 0)
 
276
    {
 
277
      file_dirty = force;
 
278
      memcpy(lease->hwaddr, hwaddr, ETHER_ADDR_LEN);
 
279
    }
 
280
 
 
281
  /* only update clid when one is available, stops packets
 
282
     without a clid removing the record. Lease init uses
 
283
     clid_len == 0 for no clid. */
 
284
  if (clid_len != 0 && clid)
 
285
    {
 
286
      if (!lease->clid)
 
287
        lease->clid_len = 0;
 
288
 
 
289
      if (lease->clid_len != clid_len)
 
290
        {
 
291
          file_dirty = force;
 
292
          if (lease->clid)
 
293
            free(lease->clid);
 
294
          if (!(lease->clid = malloc(clid_len)))
 
295
            return 0;
 
296
        }
 
297
      else if (memcmp(lease->clid, clid, clid_len) != 0)
 
298
        file_dirty = force;
 
299
 
 
300
      lease->clid_len = clid_len;
 
301
      memcpy(lease->clid, clid, clid_len);
 
302
    }
 
303
 
 
304
  return 1;
 
305
}
 
306
 
 
307
void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix)
 
308
{
 
309
  struct dhcp_lease *lease_tmp;
 
310
  char *new_name = NULL, *new_fqdn = NULL;
 
311
 
 
312
  if (lease->hostname && name && hostname_isequal(lease->hostname, name))
 
313
    return;
 
314
 
 
315
  if (!name && !lease->hostname)
 
316
    return;
 
317
 
 
318
  /* If a machine turns up on a new net without dropping the old lease,
 
319
     or two machines claim the same name, then we end up with two interfaces with
 
320
     the same name. Check for that here and remove the name from the old lease. */
 
321
  
 
322
  if (name)
 
323
    {
 
324
      for (lease_tmp = leases; lease_tmp; lease_tmp = lease_tmp->next)
 
325
        if (lease_tmp->hostname && hostname_isequal(lease_tmp->hostname, name))
 
326
          {
 
327
            new_name = lease_tmp->hostname;
 
328
            lease_tmp->hostname = NULL;
 
329
            if (lease_tmp->fqdn)
 
330
              {
 
331
                new_fqdn = lease_tmp->fqdn;
 
332
                lease_tmp->fqdn = NULL;
 
333
              }
 
334
          }
 
335
     
 
336
      if (!new_name && (new_name = malloc(strlen(name) + 1)))
 
337
        strcpy(new_name, name);
 
338
      
 
339
      if (suffix && !new_fqdn && (new_fqdn = malloc(strlen(name) + strlen(suffix) + 2)))
 
340
        {
 
341
          strcpy(new_fqdn, name);
 
342
          strcat(new_fqdn, ".");
 
343
          strcat(new_fqdn, suffix);
 
344
        }
 
345
    }
 
346
 
 
347
  if (lease->hostname)
 
348
    free(lease->hostname);
 
349
  if (lease->fqdn)
 
350
    free(lease->fqdn);
 
351
  
 
352
  lease->hostname = new_name;
 
353
  lease->fqdn = new_fqdn;
 
354
  
 
355
  file_dirty = force;
 
356
  dns_dirty = 1;
 
357
}
 
358
 
 
359
 
 
360