Bug Summary

File:src/usr.sbin/dhcpd/memory.c
Warning:line 323, column 1
Potential leak of memory pointed to by 'address_range'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name memory.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.sbin/dhcpd/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/dhcpd/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.sbin/dhcpd/memory.c
1/* $OpenBSD: memory.c,v 1.30 2020/11/10 16:42:17 krw Exp $ */
2
3/*
4 * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of The Internet Software Consortium nor the names
17 * of its contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
21 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
22 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * This software has been written for the Internet Software Consortium
35 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
36 * Enterprises. To learn more about the Internet Software Consortium,
37 * see ``http://www.vix.com/isc''. To learn more about Vixie
38 * Enterprises, see ``http://www.vix.com''.
39 */
40
41#include <sys/types.h>
42#include <sys/socket.h>
43
44#include <arpa/inet.h>
45
46#include <net/if.h>
47
48#include <netdb.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include <string.h>
52
53#include "dhcp.h"
54#include "tree.h"
55#include "dhcpd.h"
56#include "log.h"
57#include "sync.h"
58
59struct subnet *subnets;
60static struct shared_network *shared_networks;
61static struct hash_table *host_hw_addr_hash;
62static struct hash_table *host_uid_hash;
63static struct hash_table *lease_uid_hash;
64static struct hash_table *lease_ip_addr_hash;
65static struct hash_table *lease_hw_addr_hash;
66static struct lease *dangling_leases;
67
68static struct hash_table *vendor_class_hash;
69static struct hash_table *user_class_hash;
70
71extern int syncsend;
72
73void
74enter_host(struct host_decl *hd)
75{
76 struct host_decl *hp = NULL((void *)0), *np = NULL((void *)0);
77
78 hd->n_ipaddr = NULL((void *)0);
79 if (hd->interface.hlen) {
80 if (!host_hw_addr_hash)
81 host_hw_addr_hash = new_hash();
82 else
83 hp = (struct host_decl *)hash_lookup(host_hw_addr_hash,
84 hd->interface.haddr, hd->interface.hlen);
85
86 /*
87 * If there isn't already a host decl matching this
88 * address, add it to the hash table.
89 */
90 if (!hp)
91 add_hash(host_hw_addr_hash, hd->interface.haddr,
92 hd->interface.hlen, (unsigned char *)hd);
93 }
94
95 /*
96 * If there was already a host declaration for this hardware
97 * address, add this one to the end of the list.
98 */
99 if (hp) {
100 for (np = hp; np->n_ipaddr; np = np->n_ipaddr)
101 ;
102 np->n_ipaddr = hd;
103 }
104
105 if (hd->group->options[DHO_DHCP_CLIENT_IDENTIFIER61]) {
106 if (!tree_evaluate(
107 hd->group->options[DHO_DHCP_CLIENT_IDENTIFIER61]))
108 return;
109
110 /* If there's no uid hash, make one; otherwise, see if
111 there's already an entry in the hash for this host. */
112 if (!host_uid_hash) {
113 host_uid_hash = new_hash();
114 hp = NULL((void *)0);
115 } else
116 hp = (struct host_decl *)hash_lookup(host_uid_hash,
117 hd->group->options[DHO_DHCP_CLIENT_IDENTIFIER61]->value,
118 hd->group->options[DHO_DHCP_CLIENT_IDENTIFIER61]->len);
119
120 /*
121 * If there's already a host declaration for this
122 * client identifier, add this one to the end of the
123 * list. Otherwise, add it to the hash table.
124 */
125 if (hp) {
126 /* Don't link it in twice... */
127 if (!np) {
128 for (np = hp; np->n_ipaddr;
129 np = np->n_ipaddr)
130 ;
131 np->n_ipaddr = hd;
132 }
133 } else {
134 add_hash(host_uid_hash,
135 hd->group->options[DHO_DHCP_CLIENT_IDENTIFIER61]->value,
136 hd->group->options[DHO_DHCP_CLIENT_IDENTIFIER61]->len,
137 (unsigned char *)hd);
138 }
139 }
140}
141
142struct host_decl *
143find_hosts_by_haddr(int htype, unsigned char *haddr, int hlen)
144{
145 return (struct host_decl *)hash_lookup(host_hw_addr_hash,
146 haddr, hlen);
147}
148
149struct host_decl *
150find_hosts_by_uid(unsigned char *data, int len)
151{
152 return (struct host_decl *)hash_lookup(host_uid_hash, data, len);
153}
154
155/*
156 * More than one host_decl can be returned by find_hosts_by_haddr or
157 * find_hosts_by_uid, and each host_decl can have multiple addresses.
158 * Loop through the list of hosts, and then for each host, through the
159 * list of addresses, looking for an address that's in the same shared
160 * network as the one specified. Store the matching address through
161 * the addr pointer, update the host pointer to point at the host_decl
162 * that matched, and return the subnet that matched.
163 */
164struct subnet *
165find_host_for_network(struct host_decl **host, struct iaddr *addr,
166 struct shared_network *share)
167{
168 struct subnet *subnet;
169 struct iaddr ip_address;
170 struct host_decl *hp;
171 int i;
172
173 for (hp = *host; hp; hp = hp->n_ipaddr) {
174 if (!hp->fixed_addr || !tree_evaluate(hp->fixed_addr))
175 continue;
176 for (i = 0; i < hp->fixed_addr->len; i += 4) {
177 ip_address.len = 4;
178 memcpy(ip_address.iabuf, hp->fixed_addr->value + i, 4);
179 subnet = find_grouped_subnet(share, ip_address);
180 if (subnet) {
181 *addr = ip_address;
182 *host = hp;
183 return subnet;
184 }
185 }
186 }
187 return NULL((void *)0);
188}
189
190void
191new_address_range(struct iaddr low, struct iaddr high, struct subnet *subnet,
192 int dynamic)
193{
194 struct lease *address_range, *lp, *plp;
195 struct iaddr net;
196 int min, max, i;
197 char lowbuf[16], highbuf[16], netbuf[16];
198 struct shared_network *share = subnet->shared_network;
199 struct hostent *h;
200 struct in_addr ia;
201
202 /* All subnets should have attached shared network structures. */
203 if (!share) {
1
Assuming 'share' is non-null
2
Taking false branch
204 strlcpy(netbuf, piaddr(subnet->net), sizeof(netbuf));
205 fatalx("No shared network for network %s (%s)",
206 netbuf, piaddr(subnet->netmask));
207 }
208
209 /* Initialize the hash table if it hasn't been done yet. */
210 if (!lease_uid_hash)
3
Assuming 'lease_uid_hash' is non-null
4
Taking false branch
211 lease_uid_hash = new_hash();
212 if (!lease_ip_addr_hash)
5
Assuming 'lease_ip_addr_hash' is non-null
6
Taking false branch
213 lease_ip_addr_hash = new_hash();
214 if (!lease_hw_addr_hash)
7
Assuming 'lease_hw_addr_hash' is non-null
8
Taking false branch
215 lease_hw_addr_hash = new_hash();
216
217 /* Make sure that high and low addresses are in same subnet. */
218 net = subnet_number(low, subnet->netmask);
219 if (!addr_eq(net, subnet_number(high, subnet->netmask))) {
9
Assuming the condition is false
10
Taking false branch
220 strlcpy(lowbuf, piaddr(low), sizeof(lowbuf));
221 strlcpy(highbuf, piaddr(high), sizeof(highbuf));
222 strlcpy(netbuf, piaddr(subnet->netmask), sizeof(netbuf));
223 fatalx("Address range %s to %s, netmask %s spans %s!",
224 lowbuf, highbuf, netbuf, "multiple subnets");
225 }
226
227 /* Make sure that the addresses are on the correct subnet. */
228 if (!addr_eq(net, subnet->net)) {
11
Assuming the condition is false
12
Taking false branch
229 strlcpy(lowbuf, piaddr(low), sizeof(lowbuf));
230 strlcpy(highbuf, piaddr(high), sizeof(highbuf));
231 strlcpy(netbuf, piaddr(subnet->netmask), sizeof(netbuf));
232 fatalx("Address range %s to %s not on net %s/%s!",
233 lowbuf, highbuf, piaddr(subnet->net), netbuf);
234 }
235
236 /* Get the high and low host addresses... */
237 max = host_addr(high, subnet->netmask);
238 min = host_addr(low, subnet->netmask);
239
240 /* Allow range to be specified high-to-low as well as low-to-high. */
241 if (min > max) {
13
Assuming 'min' is <= 'max'
14
Taking false branch
242 max = min;
243 min = host_addr(high, subnet->netmask);
244 }
245
246 /* Get a lease structure for each address in the range. */
247 address_range = calloc(max - min + 1, sizeof(struct lease));
15
Memory is allocated
248 if (!address_range) {
16
Assuming 'address_range' is non-null
17
Taking false branch
249 strlcpy(lowbuf, piaddr(low), sizeof(lowbuf));
250 strlcpy(highbuf, piaddr(high), sizeof(highbuf));
251 fatalx("No memory for address range %s-%s.", lowbuf, highbuf);
252 }
253 memset(address_range, 0, (sizeof *address_range) * (max - min + 1));
254
255 /* Fill in the last lease if it hasn't been already... */
256 if (!share->last_lease)
18
Assuming field 'last_lease' is non-null
19
Taking false branch
257 share->last_lease = &address_range[0];
258
259 /* Fill out the lease structures with some minimal information. */
260 for (i = 0; i < max - min + 1; i++) {
20
Assuming the condition is false
21
Loop condition is false. Execution continues on line 296
261 address_range[i].ip_addr = ip_addr(subnet->net,
262 subnet->netmask, i + min);
263 address_range[i].starts = address_range[i].timestamp =
264 MIN_TIME0;
265 address_range[i].ends = MIN_TIME0;
266 address_range[i].subnet = subnet;
267 address_range[i].shared_network = share;
268 address_range[i].flags = dynamic ? DYNAMIC_BOOTP_OK4 : 0;
269
270 memcpy(&ia, address_range[i].ip_addr.iabuf, 4);
271
272 if (subnet->group->get_lease_hostnames) {
273 h = gethostbyaddr((char *)&ia, sizeof ia, AF_INET2);
274 if (!h)
275 log_warnx("No hostname for %s", inet_ntoa(ia));
276 else {
277 address_range[i].hostname = strdup(h->h_name);
278 if (address_range[i].hostname == NULL((void *)0))
279 fatalx("no memory for hostname %s.",
280 h->h_name);
281 }
282 }
283
284 /* Link this entry into the list. */
285 address_range[i].next = share->leases;
286 address_range[i].prev = NULL((void *)0);
287 share->leases = &address_range[i];
288 if (address_range[i].next)
289 address_range[i].next->prev = share->leases;
290 add_hash(lease_ip_addr_hash, address_range[i].ip_addr.iabuf,
291 address_range[i].ip_addr.len,
292 (unsigned char *)&address_range[i]);
293 }
294
295 /* Find out if any dangling leases are in range... */
296 plp = NULL((void *)0);
297 for (lp = dangling_leases; lp; lp = lp->next) {
22
Loop condition is false. Execution continues on line 193
298 struct iaddr lnet;
299 int lhost;
300
301 lnet = subnet_number(lp->ip_addr, subnet->netmask);
302 lhost = host_addr(lp->ip_addr, subnet->netmask);
303
304 /* If it's in range, fill in the real lease structure with
305 the dangling lease's values, and remove the lease from
306 the list of dangling leases. */
307 if (addr_eq(lnet, subnet->net) && lhost >= i && lhost <= max) {
308 if (plp) {
309 plp->next = lp->next;
310 } else {
311 dangling_leases = lp->next;
312 }
313 lp->next = NULL((void *)0);
314 address_range[lhost - i].hostname = lp->hostname;
315 address_range[lhost - i].client_hostname =
316 lp->client_hostname;
317 supersede_lease(&address_range[lhost - i], lp, 0);
318 free(lp);
319 return;
320 } else
321 plp = lp;
322 }
323}
23
Potential leak of memory pointed to by 'address_range'
324
325struct subnet *
326find_subnet(struct iaddr addr)
327{
328 struct subnet *rv;
329
330 for (rv = subnets; rv; rv = rv->next_subnet) {
331 if (addr_eq(subnet_number(addr, rv->netmask), rv->net))
332 return rv;
333 }
334 return NULL((void *)0);
335}
336
337struct subnet *
338find_grouped_subnet(struct shared_network *share, struct iaddr addr)
339{
340 struct subnet *rv;
341
342 for (rv = share->subnets; rv; rv = rv->next_sibling) {
343 if (addr_eq(subnet_number(addr, rv->netmask), rv->net))
344 return rv;
345 }
346 return NULL((void *)0);
347}
348
349int
350subnet_inner_than(struct subnet *subnet, struct subnet *scan, int warnp)
351{
352 if (addr_eq(subnet_number(subnet->net, scan->netmask), scan->net) ||
353 addr_eq(subnet_number(scan->net, subnet->netmask), subnet->net)) {
354 char n1buf[16];
355 int i, j;
356
357 for (i = 0; i < 32; i++)
358 if (subnet->netmask.iabuf[3 - (i >> 3)] &
359 (1 << (i & 7)))
360 break;
361 for (j = 0; j < 32; j++)
362 if (scan->netmask.iabuf[3 - (j >> 3)] &
363 (1 << (j & 7)))
364 break;
365 strlcpy(n1buf, piaddr(subnet->net), sizeof(n1buf));
366 if (warnp)
367 log_warnx("%ssubnet %s/%d conflicts with subnet %s/%d",
368 "Warning: ", n1buf, 32 - i,
369 piaddr(scan->net), 32 - j);
370 if (i < j)
371 return 1;
372 }
373 return 0;
374}
375
376/* Enter a new subnet into the subnet list. */
377void
378enter_subnet(struct subnet *subnet)
379{
380 struct subnet *scan, *prev = NULL((void *)0);
381
382 /* Check for duplicates... */
383 for (scan = subnets; scan; scan = scan->next_subnet) {
384 /*
385 * When we find a conflict, make sure that the
386 * subnet with the narrowest subnet mask comes
387 * first.
388 */
389 if (subnet_inner_than(subnet, scan, 1)) {
390 if (prev) {
391 prev->next_subnet = subnet;
392 } else
393 subnets = subnet;
394 subnet->next_subnet = scan;
395 return;
396 }
397 prev = scan;
398 }
399
400 /* XXX use the BSD radix tree code instead of a linked list. */
401 subnet->next_subnet = subnets;
402 subnets = subnet;
403}
404
405/* Enter a new shared network into the shared network list. */
406void
407enter_shared_network(struct shared_network *share)
408{
409 /* XXX Sort the nets into a balanced tree to make searching quicker. */
410 share->next = shared_networks;
411 shared_networks = share;
412}
413
414/*
415 * Enter a lease into the system. This is called by the parser each
416 * time it reads in a new lease. If the subnet for that lease has
417 * already been read in (usually the case), just update that lease;
418 * otherwise, allocate temporary storage for the lease and keep it around
419 * until we're done reading in the config file.
420 */
421void
422enter_lease(struct lease *lease)
423{
424 struct lease *comp = find_lease_by_ip_addr(lease->ip_addr);
425
426 /* If we don't have a place for this lease yet, save it for later. */
427 if (!comp) {
428 comp = calloc(1, sizeof(struct lease));
429 if (!comp)
430 fatalx("No memory for lease %s\n",
431 piaddr(lease->ip_addr));
432 *comp = *lease;
433 comp->next = dangling_leases;
434 comp->prev = NULL((void *)0);
435 dangling_leases = comp;
436 } else {
437 /* Record the hostname information in the lease. */
438 comp->hostname = lease->hostname;
439 comp->client_hostname = lease->client_hostname;
440 supersede_lease(comp, lease, 0);
441 }
442}
443
444static inline int
445hwaddrcmp(struct hardware *a, struct hardware *b)
446{
447 return ((a->htype != b->htype) || (a->hlen != b->hlen) ||
448 memcmp(a->haddr, b->haddr, b->hlen));
449}
450
451static inline int
452uidcmp(struct lease *a, struct lease *b)
453{
454 return (a->uid_len != b->uid_len || memcmp(a->uid, b->uid,
455 b->uid_len));
456}
457
458static inline int
459uid_or_hwaddr_cmp(struct lease *a, struct lease *b)
460{
461 if (a->uid && b->uid)
462 return uidcmp(a, b);
463 return hwaddrcmp(&a->hardware_addr, &b->hardware_addr);
464}
465
466/*
467 * Replace the data in an existing lease with the data in a new lease;
468 * adjust hash tables to suit, and insertion sort the lease into the
469 * list of leases by expiry time so that we can always find the oldest
470 * lease.
471 */
472int
473supersede_lease(struct lease *comp, struct lease *lease, int commit)
474{
475 int enter_uid = 0;
476 int enter_hwaddr = 0;
477 int do_pftable = 0;
478 struct lease *lp;
479
480 /* Static leases are not currently kept in the database... */
481 if (lease->flags & STATIC_LEASE1)
482 return 1;
483
484 /*
485 * If the existing lease hasn't expired and has a different
486 * unique identifier or, if it doesn't have a unique
487 * identifier, a different hardware address, then the two
488 * leases are in conflict. If the existing lease has a uid
489 * and the new one doesn't, but they both have the same
490 * hardware address, and dynamic bootp is allowed on this
491 * lease, then we allow that, in case a dynamic BOOTP lease is
492 * requested *after* a DHCP lease has been assigned.
493 */
494 if (!(lease->flags & ABANDONED_LEASE16) &&
495 comp->ends > cur_time && uid_or_hwaddr_cmp(comp, lease)) {
496 log_warnx("Lease conflict at %s", piaddr(comp->ip_addr));
497 return 0;
498 } else {
499 /* If there's a Unique ID, dissociate it from the hash
500 table and free it if necessary. */
501 if (comp->uid) {
502 uid_hash_delete(comp);
503 enter_uid = 1;
504 if (comp->uid != &comp->uid_buf[0]) {
505 if (comp->uid != lease->uid)
506 free(comp->uid);
507 comp->uid_max = 0;
508 comp->uid_len = 0;
509 }
510 comp->uid = NULL((void *)0);
511 } else
512 enter_uid = 1;
513
514 if (comp->hardware_addr.htype &&
515 hwaddrcmp(&comp->hardware_addr, &lease->hardware_addr)) {
516 hw_hash_delete(comp);
517 enter_hwaddr = 1;
518 do_pftable = 1;
519 } else if (!comp->hardware_addr.htype) {
520 enter_hwaddr = 1;
521 do_pftable = 1;
522 }
523
524 /* Copy the data files, but not the linkages. */
525 comp->starts = lease->starts;
526 if (lease->uid) {
527 if (lease->uid_len <= sizeof (lease->uid_buf)) {
528 memcpy(comp->uid_buf, lease->uid,
529 lease->uid_len);
530 comp->uid = &comp->uid_buf[0];
531 comp->uid_max = sizeof comp->uid_buf;
532 } else if (lease->uid != &lease->uid_buf[0]) {
533 comp->uid = lease->uid;
534 comp->uid_max = lease->uid_max;
535 lease->uid = NULL((void *)0);
536 lease->uid_max = 0;
537 } else {
538 fatalx("corrupt lease uid."); /* XXX */
539 }
540 } else {
541 comp->uid = NULL((void *)0);
542 comp->uid_max = 0;
543 }
544 comp->uid_len = lease->uid_len;
545 comp->host = lease->host;
546 comp->hardware_addr = lease->hardware_addr;
547 comp->flags = ((lease->flags & ~PERSISTENT_FLAGS(4)) |
548 (comp->flags & ~EPHEMERAL_FLAGS(2)));
549
550 /* Record the lease in the uid hash if necessary. */
551 if (enter_uid && lease->uid)
552 uid_hash_add(comp);
553
554 /* Record it in the hardware address hash if necessary. */
555 if (enter_hwaddr && lease->hardware_addr.htype)
556 hw_hash_add(comp);
557
558 /* Remove the lease from its current place in the
559 timeout sequence. */
560 if (comp->prev)
561 comp->prev->next = comp->next;
562 else
563 comp->shared_network->leases = comp->next;
564 if (comp->next)
565 comp->next->prev = comp->prev;
566 if (comp->shared_network->last_lease == comp)
567 comp->shared_network->last_lease = comp->prev;
568
569 /* Find the last insertion point... */
570 if (comp == comp->shared_network->insertion_point ||
571 !comp->shared_network->insertion_point)
572 lp = comp->shared_network->leases;
573 else
574 lp = comp->shared_network->insertion_point;
575
576 if (!lp) {
577 /* Nothing on the list yet? Just make comp the
578 head of the list. */
579 comp->shared_network->leases = comp;
580 comp->shared_network->last_lease = comp;
581 } else if (lp->ends > lease->ends) {
582 /* Skip down the list until we run out of list
583 or find a place for comp. */
584 while (lp->next && lp->ends > lease->ends) {
585 lp = lp->next;
586 }
587 if (lp->ends > lease->ends) {
588 /* If we ran out of list, put comp
589 at the end. */
590 lp->next = comp;
591 comp->prev = lp;
592 comp->next = NULL((void *)0);
593 comp->shared_network->last_lease = comp;
594 } else {
595 /* If we didn't, put it between lp and
596 the previous item on the list. */
597 if ((comp->prev = lp->prev))
598 comp->prev->next = comp;
599 comp->next = lp;
600 lp->prev = comp;
601 }
602 } else {
603 /* Skip up the list until we run out of list
604 or find a place for comp. */
605 while (lp->prev && lp->ends < lease->ends) {
606 lp = lp->prev;
607 }
608 if (lp->ends < lease->ends) {
609 /* If we ran out of list, put comp
610 at the beginning. */
611 lp->prev = comp;
612 comp->next = lp;
613 comp->prev = NULL((void *)0);
614 comp->shared_network->leases = comp;
615 } else {
616 /* If we didn't, put it between lp and
617 the next item on the list. */
618 if ((comp->next = lp->next))
619 comp->next->prev = comp;
620 comp->prev = lp;
621 lp->next = comp;
622 }
623 }
624 comp->shared_network->insertion_point = comp;
625 comp->ends = lease->ends;
626 }
627
628 pfmsg('L', lease); /* address is leased. remove from purgatory */
629 if (do_pftable) /* address changed hwaddr. remove from overload */
630 pfmsg('C', lease);
631
632 /* Return zero if we didn't commit the lease to permanent storage;
633 nonzero if we did. */
634 return commit && write_lease(comp) && commit_leases();
635}
636
637/* Release the specified lease and re-hash it as appropriate. */
638
639void
640release_lease(struct lease *lease)
641{
642 struct lease lt;
643
644 lt = *lease;
645 if (lt.ends > cur_time) {
646 lt.ends = cur_time;
647 supersede_lease(lease, &lt, 1);
648 log_info("Released lease for IP address %s",
649 piaddr(lease->ip_addr));
650 pfmsg('R', lease);
651 }
652}
653
654
655/*
656 * Abandon the specified lease for the specified time. sets it's
657 * particulars to zero, the end time appropriately and re-hash it as
658 * appropriate. abandons permanently if abtime is 0
659 */
660void
661abandon_lease(struct lease *lease, char *message)
662{
663 struct lease lt;
664 time_t abtime;
665
666 abtime = lease->subnet->group->default_lease_time;
667 lease->flags |= ABANDONED_LEASE16;
668 lt = *lease;
669 lt.ends = cur_time + abtime;
670 log_warnx("Abandoning IP address %s for %lld seconds: %s",
671 piaddr(lease->ip_addr), (long long)abtime, message);
672 lt.hardware_addr.htype = 0;
673 lt.hardware_addr.hlen = 0;
674 lt.uid = NULL((void *)0);
675 lt.uid_len = 0;
676 supersede_lease(lease, &lt, 1);
677
678 pfmsg('A', lease); /* address is abandoned. send to purgatory */
679 return;
680}
681
682/* Locate the lease associated with a given IP address... */
683struct lease *
684find_lease_by_ip_addr(struct iaddr addr)
685{
686 return (struct lease *)hash_lookup(lease_ip_addr_hash,
687 addr.iabuf, addr.len);
688}
689
690struct lease *
691find_lease_by_uid(unsigned char *uid, int len)
692{
693 return (struct lease *)hash_lookup(lease_uid_hash, uid, len);
694}
695
696struct lease *
697find_lease_by_hw_addr(unsigned char *hwaddr, int hwlen)
698{
699 return (struct lease *)hash_lookup(lease_hw_addr_hash, hwaddr, hwlen);
700}
701
702/* Add the specified lease to the uid hash. */
703void
704uid_hash_add(struct lease *lease)
705{
706 struct lease *head = find_lease_by_uid(lease->uid, lease->uid_len);
707 struct lease *scan;
708
709 /* If it's not in the hash, just add it. */
710 if (!head)
711 add_hash(lease_uid_hash, lease->uid,
712 lease->uid_len, (unsigned char *)lease);
713 else {
714 /* Otherwise, attach it to the end of the list. */
715 for (scan = head; scan->n_uid; scan = scan->n_uid)
716 ;
717 scan->n_uid = lease;
718 }
719}
720
721/* Delete the specified lease from the uid hash. */
722void
723uid_hash_delete(struct lease *lease)
724{
725 struct lease *head = find_lease_by_uid(lease->uid, lease->uid_len);
726 struct lease *scan;
727
728 /* If it's not in the hash, we have no work to do. */
729 if (!head) {
730 lease->n_uid = NULL((void *)0);
731 return;
732 }
733
734 /* If the lease we're freeing is at the head of the list,
735 remove the hash table entry and add a new one with the
736 next lease on the list (if there is one). */
737 if (head == lease) {
738 delete_hash_entry(lease_uid_hash, lease->uid, lease->uid_len);
739 if (lease->n_uid)
740 add_hash(lease_uid_hash, lease->n_uid->uid,
741 lease->n_uid->uid_len,
742 (unsigned char *)(lease->n_uid));
743 } else {
744 /* Otherwise, look for the lease in the list of leases
745 attached to the hash table entry, and remove it if
746 we find it. */
747 for (scan = head; scan->n_uid; scan = scan->n_uid) {
748 if (scan->n_uid == lease) {
749 scan->n_uid = scan->n_uid->n_uid;
750 break;
751 }
752 }
753 }
754 lease->n_uid = NULL((void *)0);
755}
756
757/* Add the specified lease to the hardware address hash. */
758void
759hw_hash_add(struct lease *lease)
760{
761 struct lease *head = find_lease_by_hw_addr(lease->hardware_addr.haddr,
762 lease->hardware_addr.hlen);
763 struct lease *scan;
764
765 /* If it's not in the hash, just add it. */
766 if (!head)
767 add_hash(lease_hw_addr_hash, lease->hardware_addr.haddr,
768 lease->hardware_addr.hlen, (unsigned char *)lease);
769 else {
770 /* Otherwise, attach it to the end of the list. */
771 for (scan = head; scan->n_hw; scan = scan->n_hw)
772 ;
773 scan->n_hw = lease;
774 }
775}
776
777/* Delete the specified lease from the hardware address hash. */
778void
779hw_hash_delete(struct lease *lease)
780{
781 struct lease *head = find_lease_by_hw_addr(lease->hardware_addr.haddr,
782 lease->hardware_addr.hlen);
783 struct lease *scan;
784
785 /* If it's not in the hash, we have no work to do. */
786 if (!head) {
787 lease->n_hw = NULL((void *)0);
788 return;
789 }
790
791 /* If the lease we're freeing is at the head of the list,
792 remove the hash table entry and add a new one with the
793 next lease on the list (if there is one). */
794 if (head == lease) {
795 delete_hash_entry(lease_hw_addr_hash,
796 lease->hardware_addr.haddr, lease->hardware_addr.hlen);
797 if (lease->n_hw)
798 add_hash(lease_hw_addr_hash,
799 lease->n_hw->hardware_addr.haddr,
800 lease->n_hw->hardware_addr.hlen,
801 (unsigned char *)(lease->n_hw));
802 } else {
803 /*
804 * Otherwise, look for the lease in the list of leases
805 * attached to the hash table entry, and remove it if
806 * we find it.
807 */
808 for (scan = head; scan->n_hw; scan = scan->n_hw) {
809 if (scan->n_hw == lease) {
810 scan->n_hw = scan->n_hw->n_hw;
811 break;
812 }
813 }
814 }
815 lease->n_hw = NULL((void *)0);
816}
817
818
819struct class *
820add_class(int type, char *name)
821{
822 struct class *class;
823 char *tname;
824
825 class = calloc(1, sizeof(*class));
826 tname = strdup(name);
827
828 if (!vendor_class_hash)
829 vendor_class_hash = new_hash();
830 if (!user_class_hash)
831 user_class_hash = new_hash();
832
833 if (!tname || !class || !vendor_class_hash || !user_class_hash) {
834 log_warnx("No memory for %s.", name);
835 free(class);
836 free(tname);
837 return NULL((void *)0);
838 }
839
840 class->name = tname;
841
842 if (type)
843 add_hash(user_class_hash, (unsigned char *)tname,
844 strlen(tname), (unsigned char *)class);
845 else
846 add_hash(vendor_class_hash, (unsigned char *)tname,
847 strlen(tname), (unsigned char *)class);
848
849 return class;
850}
851
852struct class *
853find_class(int type, unsigned char *name, int len)
854{
855 return (struct class *)hash_lookup(type ? user_class_hash :
856 vendor_class_hash, name, len);
857}
858
859struct group *
860clone_group(struct group *group, char *caller)
861{
862 struct group *g;
863
864 g = calloc(1, sizeof(struct group));
865 if (!g)
866 fatalx("%s: can't allocate new group", caller);
867 *g = *group;
868 return g;
869}
870
871/* Write all interesting leases to permanent storage. */
872
873void
874write_leases(void)
875{
876 struct lease *l;
877 struct shared_network *s;
878
879 for (s = shared_networks; s; s = s->next) {
880 for (l = s->leases; l; l = l->next) {
881 if (l->hardware_addr.hlen || l->uid_len ||
882 (l->flags & ABANDONED_LEASE16)) {
883 if (!write_lease(l))
884 fatalx("Can't rewrite lease database");
885 if (syncsend)
886 sync_lease(l);
887 }
888 }
889 }
890 if (!commit_leases())
891 fatal("Can't commit leases to new database");
892}