Add BIND 9.2.4rc7.
[dragonfly.git] / contrib / bind-9.2.4rc7 / lib / isc / unix / entropy.c
blobd94080ad1e79f0e9b4fbb8410dc7f42d2dd59c42
1 /*
2 * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000-2002 Internet Software Consortium.
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
18 /* $Id: entropy.c,v 1.60.2.4 2004/03/09 06:12:09 marka Exp $ */
21 * This is the system depenedent part of the ISC entropy API.
24 #include <config.h>
26 #include <sys/param.h> /* Openserver 5.0.6A and FD_SETSIZE */
27 #include <sys/types.h>
28 #include <sys/time.h>
30 #include <unistd.h>
32 #include <isc/platform.h>
33 #include <isc/strerror.h>
35 #ifdef ISC_PLATFORM_NEEDSYSSELECTH
36 #include <sys/select.h>
37 #endif
39 #include "errno2result.h"
42 * There is only one variable in the entropy data structures that is not
43 * system independent, but pulling the structure that uses it into this file
44 * ultimately means pulling several other independent structures here also to
45 * resolve their interdependencies. Thus only the problem variable's type
46 * is defined here.
48 #define FILESOURCE_HANDLE_TYPE int
50 #include "../entropy.c"
52 static unsigned int
53 get_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) {
54 isc_entropy_t *ent = source->ent;
55 unsigned char buf[128];
56 int fd = source->sources.file.handle;
57 ssize_t n, ndesired;
58 unsigned int added;
60 if (source->bad)
61 return (0);
63 desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
65 added = 0;
66 while (desired > 0) {
67 ndesired = ISC_MIN(desired, sizeof(buf));
68 n = read(fd, buf, ndesired);
69 if (n < 0) {
70 if (errno == EAGAIN || errno == EINTR)
71 goto out;
72 close(fd);
73 source->bad = ISC_TRUE;
74 goto out;
76 if (n == 0) {
77 close(fd);
78 source->bad = ISC_TRUE;
79 goto out;
82 entropypool_adddata(ent, buf, n, n * 8);
83 added += n * 8;
84 desired -= n;
87 out:
88 return (added);
92 * Poll each source, trying to get data from it to stuff into the entropy
93 * pool.
95 static void
96 fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
97 unsigned int added;
98 unsigned int remaining;
99 unsigned int needed;
100 unsigned int nsource;
101 isc_entropysource_t *source;
103 REQUIRE(VALID_ENTROPY(ent));
105 needed = desired;
108 * This logic is a little strange, so an explanation is in order.
110 * If needed is 0, it means we are being asked to "fill to whatever
111 * we think is best." This means that if we have at least a
112 * partially full pool (say, > 1/4th of the pool) we probably don't
113 * need to add anything.
115 * Also, we will check to see if the "pseudo" count is too high.
116 * If it is, try to mix in better data. Too high is currently
117 * defined as 1/4th of the pool.
119 * Next, if we are asked to add a specific bit of entropy, make
120 * certain that we will do so. Clamp how much we try to add to
121 * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
123 * Note that if we are in a blocking mode, we will only try to
124 * get as much data as we need, not as much as we might want
125 * to build up.
127 if (needed == 0) {
128 REQUIRE(!blocking);
130 if ((ent->pool.entropy >= RND_POOLBITS / 4)
131 && (ent->pool.pseudo <= RND_POOLBITS / 4))
132 return;
134 needed = THRESHOLD_BITS * 4;
135 } else {
136 needed = ISC_MAX(needed, THRESHOLD_BITS);
137 needed = ISC_MIN(needed, RND_POOLBITS);
141 * In any case, clamp how much we need to how much we can add.
143 needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
146 * But wait! If we're not yet initialized, we need at least
147 * THRESHOLD_BITS
148 * of randomness.
150 if (ent->initialized < THRESHOLD_BITS)
151 needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
154 * Poll each file source to see if we can read anything useful from
155 * it. XXXMLG When where are multiple sources, we should keep a
156 * record of which one we last used so we can start from it (or the
157 * next one) to avoid letting some sources build up entropy while
158 * others are always drained.
161 added = 0;
162 remaining = needed;
163 if (ent->nextsource == NULL) {
164 ent->nextsource = ISC_LIST_HEAD(ent->sources);
165 if (ent->nextsource == NULL)
166 return;
168 source = ent->nextsource;
169 again_file:
170 for (nsource = 0 ; nsource < ent->nsources ; nsource++) {
171 unsigned int got;
173 if (remaining == 0)
174 break;
176 got = 0;
178 if (source->type == ENTROPY_SOURCETYPE_FILE)
179 got = get_from_filesource(source, remaining);
181 added += got;
183 remaining -= ISC_MIN(remaining, got);
185 source = ISC_LIST_NEXT(source, link);
186 if (source == NULL)
187 source = ISC_LIST_HEAD(ent->sources);
189 ent->nextsource = source;
191 if (blocking && remaining != 0) {
192 int fds;
194 fds = wait_for_sources(ent);
195 if (fds > 0)
196 goto again_file;
200 * Here, if there are bits remaining to be had and we can block,
201 * check to see if we have a callback source. If so, call them.
203 source = ISC_LIST_HEAD(ent->sources);
204 while ((remaining != 0) && (source != NULL)) {
205 unsigned int got;
207 got = 0;
209 if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
210 got = get_from_callback(source, remaining, blocking);
212 added += got;
213 remaining -= ISC_MIN(remaining, got);
215 if (added >= needed)
216 break;
218 source = ISC_LIST_NEXT(source, link);
222 * Mark as initialized if we've added enough data.
224 if (ent->initialized < THRESHOLD_BITS)
225 ent->initialized += added;
228 static int
229 wait_for_sources(isc_entropy_t *ent) {
230 isc_entropysource_t *source;
231 int maxfd, fd;
232 int cc;
233 fd_set reads;
235 maxfd = -1;
236 FD_ZERO(&reads);
238 source = ISC_LIST_HEAD(ent->sources);
239 while (source != NULL) {
240 if (source->type == ENTROPY_SOURCETYPE_FILE) {
241 fd = source->sources.file.handle;
242 if (fd >= 0) {
243 maxfd = ISC_MAX(maxfd, fd);
244 FD_SET(fd, &reads);
247 source = ISC_LIST_NEXT(source, link);
250 if (maxfd < 0)
251 return (-1);
253 cc = select(maxfd + 1, &reads, NULL, NULL, NULL);
254 if (cc < 0)
255 return (-1);
257 return (cc);
260 static void
261 destroyfilesource(isc_entropyfilesource_t *source) {
262 close(source->handle);
266 * Make a fd non-blocking
268 static isc_result_t
269 make_nonblock(int fd) {
270 int ret;
271 int flags;
272 char strbuf[ISC_STRERRORSIZE];
274 flags = fcntl(fd, F_GETFL, 0);
275 flags |= O_NONBLOCK;
276 ret = fcntl(fd, F_SETFL, flags);
278 if (ret == -1) {
279 isc__strerror(errno, strbuf, sizeof(strbuf));
280 UNEXPECTED_ERROR(__FILE__, __LINE__,
281 "fcntl(%d, F_SETFL, %d): %s",
282 fd, flags, strbuf);
284 return (ISC_R_UNEXPECTED);
287 return (ISC_R_SUCCESS);
290 isc_result_t
291 isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
292 int fd;
293 isc_result_t ret;
294 isc_entropysource_t *source;
296 REQUIRE(VALID_ENTROPY(ent));
297 REQUIRE(fname != NULL);
299 LOCK(&ent->lock);
301 source = NULL;
303 fd = open(fname, O_RDONLY | O_NONBLOCK, 0);
304 if (fd < 0) {
305 ret = isc__errno2result(errno);
306 goto errout;
308 ret = make_nonblock(fd);
309 if (ret != ISC_R_SUCCESS)
310 goto closefd;
312 source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
313 if (source == NULL) {
314 ret = ISC_R_NOMEMORY;
315 goto closefd;
319 * From here down, no failures can occur.
321 source->magic = SOURCE_MAGIC;
322 source->type = ENTROPY_SOURCETYPE_FILE;
323 source->ent = ent;
324 source->total = 0;
325 source->bad = ISC_FALSE;
326 memset(source->name, 0, sizeof(source->name));
327 ISC_LINK_INIT(source, link);
328 source->sources.file.handle = fd;
331 * Hook it into the entropy system.
333 ISC_LIST_APPEND(ent->sources, source, link);
334 ent->nsources++;
336 UNLOCK(&ent->lock);
337 return (ISC_R_SUCCESS);
339 closefd:
340 close(fd);
342 errout:
343 if (source != NULL)
344 isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t));
346 UNLOCK(&ent->lock);
348 return (ret);