/src/dnsmasq/src/blockdata.c
Line | Count | Source |
1 | | /* dnsmasq is Copyright (c) 2000-2025 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 | | static struct blockdata *keyblock_free; |
20 | | static unsigned int blockdata_count, blockdata_hwm, blockdata_alloced; |
21 | | |
22 | | static void add_blocks(int n) |
23 | 0 | { |
24 | 0 | struct blockdata *new = whine_malloc(n * sizeof(struct blockdata)); |
25 | | |
26 | 0 | if (new) |
27 | 0 | { |
28 | 0 | int i; |
29 | | |
30 | 0 | new[n-1].next = keyblock_free; |
31 | 0 | keyblock_free = new; |
32 | |
|
33 | 0 | for (i = 0; i < n - 1; i++) |
34 | 0 | new[i].next = &new[i+1]; |
35 | |
|
36 | 0 | blockdata_alloced += n; |
37 | 0 | } |
38 | 0 | } |
39 | | |
40 | | /* Preallocate some blocks, proportional to cachesize, to reduce heap fragmentation. */ |
41 | | void blockdata_init(void) |
42 | 0 | { |
43 | 0 | keyblock_free = NULL; |
44 | 0 | blockdata_alloced = 0; |
45 | 0 | blockdata_count = 0; |
46 | 0 | blockdata_hwm = 0; |
47 | | |
48 | | /* Note that daemon->cachesize is enforced to have non-zero size if OPT_DNSSEC_VALID is set */ |
49 | 0 | if (option_bool(OPT_DNSSEC_VALID)) |
50 | 0 | add_blocks(daemon->cachesize); |
51 | 0 | } |
52 | | |
53 | | void blockdata_report(void) |
54 | 0 | { |
55 | 0 | my_syslog(LOG_INFO, _("pool memory in use %zu, max %zu, allocated %zu"), |
56 | 0 | blockdata_count * sizeof(struct blockdata), |
57 | 0 | blockdata_hwm * sizeof(struct blockdata), |
58 | 0 | blockdata_alloced * sizeof(struct blockdata)); |
59 | 0 | } |
60 | | |
61 | | static struct blockdata *new_block(void) |
62 | 0 | { |
63 | 0 | struct blockdata *block; |
64 | |
|
65 | 0 | if (!keyblock_free) |
66 | 0 | add_blocks(50); |
67 | | |
68 | 0 | if (keyblock_free) |
69 | 0 | { |
70 | 0 | block = keyblock_free; |
71 | 0 | keyblock_free = block->next; |
72 | 0 | blockdata_count++; |
73 | 0 | if (blockdata_hwm < blockdata_count) |
74 | 0 | blockdata_hwm = blockdata_count; |
75 | 0 | block->next = NULL; |
76 | 0 | return block; |
77 | 0 | } |
78 | | |
79 | 0 | return NULL; |
80 | 0 | } |
81 | | |
82 | | static struct blockdata *blockdata_alloc_real(int fd, char *data, size_t len) |
83 | 0 | { |
84 | 0 | struct blockdata *block, *ret = NULL; |
85 | 0 | struct blockdata **prev = &ret; |
86 | 0 | size_t blen; |
87 | |
|
88 | 0 | do |
89 | 0 | { |
90 | 0 | if (!(block = new_block())) |
91 | 0 | { |
92 | | /* failed to alloc, free partial chain */ |
93 | 0 | blockdata_free(ret); |
94 | 0 | return NULL; |
95 | 0 | } |
96 | | |
97 | 0 | if ((blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len) > 0) |
98 | 0 | { |
99 | 0 | if (data) |
100 | 0 | { |
101 | 0 | memcpy(block->key, data, blen); |
102 | 0 | data += blen; |
103 | 0 | } |
104 | 0 | else if (!read_write(fd, block->key, blen, RW_READ)) |
105 | 0 | { |
106 | | /* failed read free partial chain */ |
107 | 0 | blockdata_free(ret); |
108 | 0 | return NULL; |
109 | 0 | } |
110 | 0 | } |
111 | | |
112 | 0 | len -= blen; |
113 | 0 | *prev = block; |
114 | 0 | prev = &block->next; |
115 | 0 | } while (len != 0); |
116 | | |
117 | 0 | return ret; |
118 | 0 | } |
119 | | |
120 | | struct blockdata *blockdata_alloc(char *data, size_t len) |
121 | 0 | { |
122 | 0 | return blockdata_alloc_real(0, data, len); |
123 | 0 | } |
124 | | |
125 | | /* Add data to the end of the block. |
126 | | newlen is length of new data, NOT total new length. |
127 | | Use blockdata_alloc(NULL, 0) to make empty block to add to. */ |
128 | | int blockdata_expand(struct blockdata *block, size_t oldlen, char *data, size_t newlen) |
129 | 0 | { |
130 | 0 | struct blockdata *b; |
131 | | |
132 | | /* find size of current final block */ |
133 | 0 | for (b = block; oldlen > KEYBLOCK_LEN && b; b = b->next, oldlen -= KEYBLOCK_LEN); |
134 | | |
135 | | /* chain to short for length, something is broken */ |
136 | 0 | if (oldlen > KEYBLOCK_LEN) |
137 | 0 | { |
138 | 0 | blockdata_free(block); |
139 | 0 | return 0; |
140 | 0 | } |
141 | | |
142 | 0 | while (1) |
143 | 0 | { |
144 | 0 | struct blockdata *new; |
145 | 0 | size_t blocksize = KEYBLOCK_LEN - oldlen; |
146 | 0 | size_t size = (newlen <= blocksize) ? newlen : blocksize; |
147 | | |
148 | 0 | if (size != 0) |
149 | 0 | { |
150 | 0 | memcpy(&b->key[oldlen], data, size); |
151 | 0 | data += size; |
152 | 0 | newlen -= size; |
153 | 0 | } |
154 | | |
155 | | /* full blocks from now on. */ |
156 | 0 | oldlen = 0; |
157 | |
|
158 | 0 | if (newlen == 0) |
159 | 0 | break; |
160 | | |
161 | 0 | if ((new = new_block())) |
162 | 0 | { |
163 | 0 | b->next = new; |
164 | 0 | b = new; |
165 | 0 | } |
166 | 0 | else |
167 | 0 | { |
168 | | /* failed to alloc, free partial chain */ |
169 | 0 | blockdata_free(block); |
170 | 0 | return 0; |
171 | 0 | } |
172 | 0 | } |
173 | | |
174 | 0 | return 1; |
175 | 0 | } |
176 | | |
177 | | void blockdata_free(struct blockdata *blocks) |
178 | 0 | { |
179 | 0 | struct blockdata *tmp; |
180 | | |
181 | 0 | if (blocks) |
182 | 0 | { |
183 | 0 | for (tmp = blocks; tmp->next; tmp = tmp->next) |
184 | 0 | blockdata_count--; |
185 | 0 | tmp->next = keyblock_free; |
186 | 0 | keyblock_free = blocks; |
187 | 0 | blockdata_count--; |
188 | 0 | } |
189 | 0 | } |
190 | | |
191 | | /* if data == NULL, return pointer to static block of sufficient size */ |
192 | | void *blockdata_retrieve(struct blockdata *block, size_t len, void *data) |
193 | 0 | { |
194 | 0 | size_t blen; |
195 | 0 | struct blockdata *b; |
196 | 0 | uint8_t *new, *d; |
197 | | |
198 | 0 | static unsigned int buff_len = 0; |
199 | 0 | static unsigned char *buff = NULL; |
200 | | |
201 | 0 | if (!data) |
202 | 0 | { |
203 | 0 | if (len > buff_len) |
204 | 0 | { |
205 | 0 | if (!(new = whine_malloc(len))) |
206 | 0 | return NULL; |
207 | 0 | if (buff) |
208 | 0 | free(buff); |
209 | 0 | buff = new; |
210 | 0 | } |
211 | 0 | data = buff; |
212 | 0 | } |
213 | | |
214 | 0 | for (d = data, b = block; len > 0 && b; b = b->next) |
215 | 0 | { |
216 | 0 | blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len; |
217 | 0 | memcpy(d, b->key, blen); |
218 | 0 | d += blen; |
219 | 0 | len -= blen; |
220 | 0 | } |
221 | |
|
222 | 0 | return data; |
223 | 0 | } |
224 | | |
225 | | |
226 | | void blockdata_write(struct blockdata *block, size_t len, int fd) |
227 | 0 | { |
228 | 0 | for (; len > 0 && block; block = block->next) |
229 | 0 | { |
230 | 0 | size_t blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len; |
231 | 0 | read_write(fd, block->key, blen, RW_WRITE); |
232 | 0 | len -= blen; |
233 | 0 | } |
234 | 0 | } |
235 | | |
236 | | struct blockdata *blockdata_read(int fd, size_t len) |
237 | 0 | { |
238 | | return blockdata_alloc_real(fd, NULL, len); |
239 | 0 | } |