1/* dnsmasq is Copyright (c) 2000-2015 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_DNSSEC
20
21static struct blockdata *keyblock_free;
22static unsigned int blockdata_count, blockdata_hwm, blockdata_alloced;
23
24static void blockdata_expand(int n)
25{
26  struct blockdata *new = whine_malloc(n * sizeof(struct blockdata));
27
28  if (n > 0 && new)
29    {
30      int i;
31
32      new[n-1].next = keyblock_free;
33      keyblock_free = new;
34
35      for (i = 0; i < n - 1; i++)
36	new[i].next = &new[i+1];
37
38      blockdata_alloced += n;
39    }
40}
41
42/* Preallocate some blocks, proportional to cachesize, to reduce heap fragmentation. */
43void blockdata_init(void)
44{
45  keyblock_free = NULL;
46  blockdata_alloced = 0;
47  blockdata_count = 0;
48  blockdata_hwm = 0;
49
50  /* Note that daemon->cachesize is enforced to have non-zero size if OPT_DNSSEC_VALID is set */
51  if (option_bool(OPT_DNSSEC_VALID))
52    blockdata_expand((daemon->cachesize * 100) / sizeof(struct blockdata));
53}
54
55void blockdata_report(void)
56{
57  if (option_bool(OPT_DNSSEC_VALID))
58    my_syslog(LOG_INFO, _("DNSSEC memory in use %u, max %u, allocated %u"),
59	      blockdata_count * sizeof(struct blockdata),
60	      blockdata_hwm * sizeof(struct blockdata),
61	      blockdata_alloced * sizeof(struct blockdata));
62}
63
64struct blockdata *blockdata_alloc(char *data, size_t len)
65{
66  struct blockdata *block, *ret = NULL;
67  struct blockdata **prev = &ret;
68  size_t blen;
69
70  while (len > 0)
71    {
72      if (!keyblock_free)
73	blockdata_expand(50);
74
75      if (keyblock_free)
76	{
77	  block = keyblock_free;
78	  keyblock_free = block->next;
79	  blockdata_count++;
80	}
81      else
82	{
83	  /* failed to alloc, free partial chain */
84	  blockdata_free(ret);
85	  return NULL;
86	}
87
88      if (blockdata_hwm < blockdata_count)
89	blockdata_hwm = blockdata_count;
90
91      blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len;
92      memcpy(block->key, data, blen);
93      data += blen;
94      len -= blen;
95      *prev = block;
96      prev = &block->next;
97      block->next = NULL;
98    }
99
100  return ret;
101}
102
103void blockdata_free(struct blockdata *blocks)
104{
105  struct blockdata *tmp;
106
107  if (blocks)
108    {
109      for (tmp = blocks; tmp->next; tmp = tmp->next)
110	blockdata_count--;
111      tmp->next = keyblock_free;
112      keyblock_free = blocks;
113      blockdata_count--;
114    }
115}
116
117/* if data == NULL, return pointer to static block of sufficient size */
118void *blockdata_retrieve(struct blockdata *block, size_t len, void *data)
119{
120  size_t blen;
121  struct  blockdata *b;
122  void *new, *d;
123
124  static unsigned int buff_len = 0;
125  static unsigned char *buff = NULL;
126
127  if (!data)
128    {
129      if (len > buff_len)
130	{
131	  if (!(new = whine_malloc(len)))
132	    return NULL;
133	  if (buff)
134	    free(buff);
135	  buff = new;
136	}
137      data = buff;
138    }
139
140  for (d = data, b = block; len > 0 && b;  b = b->next)
141    {
142      blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len;
143      memcpy(d, b->key, blen);
144      d += blen;
145      len -= blen;
146    }
147
148  return data;
149}
150
151#endif
152