acpi.4: Add some missing references.
[dragonfly.git] / contrib / bind-9.3 / lib / isc / unix / entropy.c
blobd52849aa35b846c2b64204da1c08b8def56e8a1c
1 /*
2 * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000-2003 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.3.8.14 2006/03/02 23:29:17 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>
29 #include <sys/stat.h>
30 #include <sys/socket.h>
31 #include <sys/un.h>
33 #include <unistd.h>
35 #include <isc/platform.h>
36 #include <isc/strerror.h>
38 #ifdef ISC_PLATFORM_NEEDSYSSELECTH
39 #include <sys/select.h>
40 #endif
42 #include "errno2result.h"
45 * There is only one variable in the entropy data structures that is not
46 * system independent, but pulling the structure that uses it into this file
47 * ultimately means pulling several other independent structures here also to
48 * resolve their interdependencies. Thus only the problem variable's type
49 * is defined here.
51 #define FILESOURCE_HANDLE_TYPE int
53 typedef struct {
54 int handle;
55 enum {
56 isc_usocketsource_disconnected,
57 isc_usocketsource_connecting,
58 isc_usocketsource_connected,
59 isc_usocketsource_ndesired,
60 isc_usocketsource_wrote,
61 isc_usocketsource_reading
62 } status;
63 size_t sz_to_recv;
64 } isc_entropyusocketsource_t;
66 #include "../entropy.c"
68 static unsigned int
69 get_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) {
70 isc_entropy_t *ent = source->ent;
71 unsigned char buf[128];
72 int fd = source->sources.file.handle;
73 ssize_t n, ndesired;
74 unsigned int added;
76 if (source->bad)
77 return (0);
79 desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
81 added = 0;
82 while (desired > 0) {
83 ndesired = ISC_MIN(desired, sizeof(buf));
84 n = read(fd, buf, ndesired);
85 if (n < 0) {
86 if (errno == EAGAIN || errno == EINTR)
87 goto out;
88 goto err;
90 if (n == 0)
91 goto err;
93 entropypool_adddata(ent, buf, n, n * 8);
94 added += n * 8;
95 desired -= n;
97 goto out;
99 err:
100 (void)close(fd);
101 source->sources.file.handle = -1;
102 source->bad = ISC_TRUE;
104 out:
105 return (added);
108 static unsigned int
109 get_from_usocketsource(isc_entropysource_t *source, isc_uint32_t desired) {
110 isc_entropy_t *ent = source->ent;
111 unsigned char buf[128];
112 int fd = source->sources.usocket.handle;
113 ssize_t n = 0, ndesired;
114 unsigned int added;
115 size_t sz_to_recv = source->sources.usocket.sz_to_recv;
117 if (source->bad)
118 return (0);
120 desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
122 added = 0;
123 while (desired > 0) {
124 ndesired = ISC_MIN(desired, sizeof(buf));
125 eagain_loop:
127 switch ( source->sources.usocket.status ) {
128 case isc_usocketsource_ndesired:
129 buf[0] = ndesired;
130 if ((n = sendto(fd, buf, 1, 0, NULL, 0)) < 0) {
131 if (errno == EWOULDBLOCK || errno == EINTR ||
132 errno == ECONNRESET)
133 goto out;
134 goto err;
136 INSIST(n == 1);
137 source->sources.usocket.status =
138 isc_usocketsource_wrote;
139 goto eagain_loop;
141 case isc_usocketsource_connecting:
142 case isc_usocketsource_connected:
143 buf[0] = 1;
144 buf[1] = ndesired;
145 if ((n = sendto(fd, buf, 2, 0, NULL, 0)) < 0) {
146 if (errno == EWOULDBLOCK || errno == EINTR ||
147 errno == ECONNRESET)
148 goto out;
149 goto err;
151 if (n == 1) {
152 source->sources.usocket.status =
153 isc_usocketsource_ndesired;
154 goto eagain_loop;
156 INSIST(n == 2);
157 source->sources.usocket.status =
158 isc_usocketsource_wrote;
159 /*FALLTHROUGH*/
161 case isc_usocketsource_wrote:
162 if (recvfrom(fd, buf, 1, 0, NULL, NULL) != 1) {
163 if (errno == EAGAIN) {
165 * The problem of EAGAIN (try again
166 * later) is a major issue on HP-UX.
167 * Solaris actually tries the recvfrom
168 * call again, while HP-UX just dies.
169 * This code is an attempt to let the
170 * entropy pool fill back up (at least
171 * that's what I think the problem is.)
172 * We go to eagain_loop because if we
173 * just "break", then the "desired"
174 * amount gets borked.
176 usleep(1000);
177 goto eagain_loop;
179 if (errno == EWOULDBLOCK || errno == EINTR)
180 goto out;
181 goto err;
183 source->sources.usocket.status =
184 isc_usocketsource_reading;
185 sz_to_recv = buf[0];
186 source->sources.usocket.sz_to_recv = sz_to_recv;
187 if (sz_to_recv > sizeof(buf))
188 goto err;
189 /*FALLTHROUGH*/
191 case isc_usocketsource_reading:
192 if (sz_to_recv != 0U) {
193 n = recv(fd, buf, sz_to_recv, 0);
194 if (n < 0) {
195 if (errno == EWOULDBLOCK ||
196 errno == EINTR)
197 goto out;
198 goto err;
200 } else
201 n = 0;
202 break;
204 default:
205 goto err;
208 if ((size_t)n != sz_to_recv)
209 source->sources.usocket.sz_to_recv -= n;
210 else
211 source->sources.usocket.status =
212 isc_usocketsource_connected;
214 if (n == 0)
215 goto out;
217 entropypool_adddata(ent, buf, n, n * 8);
218 added += n * 8;
219 desired -= n;
221 goto out;
223 err:
224 close(fd);
225 source->bad = ISC_TRUE;
226 source->sources.usocket.status = isc_usocketsource_disconnected;
227 source->sources.usocket.handle = -1;
229 out:
230 return (added);
234 * Poll each source, trying to get data from it to stuff into the entropy
235 * pool.
237 static void
238 fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
239 unsigned int added;
240 unsigned int remaining;
241 unsigned int needed;
242 unsigned int nsource;
243 isc_entropysource_t *source;
245 REQUIRE(VALID_ENTROPY(ent));
247 needed = desired;
250 * This logic is a little strange, so an explanation is in order.
252 * If needed is 0, it means we are being asked to "fill to whatever
253 * we think is best." This means that if we have at least a
254 * partially full pool (say, > 1/4th of the pool) we probably don't
255 * need to add anything.
257 * Also, we will check to see if the "pseudo" count is too high.
258 * If it is, try to mix in better data. Too high is currently
259 * defined as 1/4th of the pool.
261 * Next, if we are asked to add a specific bit of entropy, make
262 * certain that we will do so. Clamp how much we try to add to
263 * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
265 * Note that if we are in a blocking mode, we will only try to
266 * get as much data as we need, not as much as we might want
267 * to build up.
269 if (needed == 0) {
270 REQUIRE(!blocking);
272 if ((ent->pool.entropy >= RND_POOLBITS / 4)
273 && (ent->pool.pseudo <= RND_POOLBITS / 4))
274 return;
276 needed = THRESHOLD_BITS * 4;
277 } else {
278 needed = ISC_MAX(needed, THRESHOLD_BITS);
279 needed = ISC_MIN(needed, RND_POOLBITS);
283 * In any case, clamp how much we need to how much we can add.
285 needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
288 * But wait! If we're not yet initialized, we need at least
289 * THRESHOLD_BITS
290 * of randomness.
292 if (ent->initialized < THRESHOLD_BITS)
293 needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
296 * Poll each file source to see if we can read anything useful from
297 * it. XXXMLG When where are multiple sources, we should keep a
298 * record of which one we last used so we can start from it (or the
299 * next one) to avoid letting some sources build up entropy while
300 * others are always drained.
303 added = 0;
304 remaining = needed;
305 if (ent->nextsource == NULL) {
306 ent->nextsource = ISC_LIST_HEAD(ent->sources);
307 if (ent->nextsource == NULL)
308 return;
310 source = ent->nextsource;
311 again_file:
312 for (nsource = 0; nsource < ent->nsources; nsource++) {
313 unsigned int got;
315 if (remaining == 0)
316 break;
318 got = 0;
320 switch ( source->type ) {
321 case ENTROPY_SOURCETYPE_FILE:
322 got = get_from_filesource(source, remaining);
323 break;
325 case ENTROPY_SOURCETYPE_USOCKET:
326 got = get_from_usocketsource(source, remaining);
327 break;
330 added += got;
332 remaining -= ISC_MIN(remaining, got);
334 source = ISC_LIST_NEXT(source, link);
335 if (source == NULL)
336 source = ISC_LIST_HEAD(ent->sources);
338 ent->nextsource = source;
340 if (blocking && remaining != 0) {
341 int fds;
343 fds = wait_for_sources(ent);
344 if (fds > 0)
345 goto again_file;
349 * Here, if there are bits remaining to be had and we can block,
350 * check to see if we have a callback source. If so, call them.
352 source = ISC_LIST_HEAD(ent->sources);
353 while ((remaining != 0) && (source != NULL)) {
354 unsigned int got;
356 got = 0;
358 if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
359 got = get_from_callback(source, remaining, blocking);
361 added += got;
362 remaining -= ISC_MIN(remaining, got);
364 if (added >= needed)
365 break;
367 source = ISC_LIST_NEXT(source, link);
371 * Mark as initialized if we've added enough data.
373 if (ent->initialized < THRESHOLD_BITS)
374 ent->initialized += added;
377 static int
378 wait_for_sources(isc_entropy_t *ent) {
379 isc_entropysource_t *source;
380 int maxfd, fd;
381 int cc;
382 fd_set reads;
383 fd_set writes;
385 maxfd = -1;
386 FD_ZERO(&reads);
387 FD_ZERO(&writes);
389 source = ISC_LIST_HEAD(ent->sources);
390 while (source != NULL) {
391 if (source->type == ENTROPY_SOURCETYPE_FILE) {
392 fd = source->sources.file.handle;
393 if (fd >= 0) {
394 maxfd = ISC_MAX(maxfd, fd);
395 FD_SET(fd, &reads);
398 if (source->type == ENTROPY_SOURCETYPE_USOCKET) {
399 fd = source->sources.usocket.handle;
400 if (fd >= 0) {
401 switch (source->sources.usocket.status) {
402 case isc_usocketsource_disconnected:
403 break;
404 case isc_usocketsource_connecting:
405 case isc_usocketsource_connected:
406 case isc_usocketsource_ndesired:
407 maxfd = ISC_MAX(maxfd, fd);
408 FD_SET(fd, &writes);
409 break;
410 case isc_usocketsource_wrote:
411 case isc_usocketsource_reading:
412 maxfd = ISC_MAX(maxfd, fd);
413 FD_SET(fd, &reads);
414 break;
418 source = ISC_LIST_NEXT(source, link);
421 if (maxfd < 0)
422 return (-1);
424 cc = select(maxfd + 1, &reads, &writes, NULL, NULL);
425 if (cc < 0)
426 return (-1);
428 return (cc);
431 static void
432 destroyfilesource(isc_entropyfilesource_t *source) {
433 (void)close(source->handle);
436 static void
437 destroyusocketsource(isc_entropyusocketsource_t *source) {
438 close(source->handle);
442 * Make a fd non-blocking
444 static isc_result_t
445 make_nonblock(int fd) {
446 int ret;
447 int flags;
448 char strbuf[ISC_STRERRORSIZE];
449 #ifdef USE_FIONBIO_IOCTL
450 int on = 1;
452 ret = ioctl(fd, FIONBIO, (char *)&on);
453 #else
454 flags = fcntl(fd, F_GETFL, 0);
455 flags |= PORT_NONBLOCK;
456 ret = fcntl(fd, F_SETFL, flags);
457 #endif
459 if (ret == -1) {
460 isc__strerror(errno, strbuf, sizeof(strbuf));
461 UNEXPECTED_ERROR(__FILE__, __LINE__,
462 #ifdef USE_FIONBIO_IOCTL
463 "ioctl(%d, FIONBIO, &on): %s", fd,
464 #else
465 "fcntl(%d, F_SETFL, %d): %s", fd, flags,
466 #endif
467 strbuf);
469 return (ISC_R_UNEXPECTED);
472 return (ISC_R_SUCCESS);
475 isc_result_t
476 isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
477 int fd;
478 struct stat _stat;
479 isc_boolean_t is_usocket = ISC_FALSE;
480 isc_boolean_t is_connected = ISC_FALSE;
481 isc_result_t ret;
482 isc_entropysource_t *source;
484 REQUIRE(VALID_ENTROPY(ent));
485 REQUIRE(fname != NULL);
487 LOCK(&ent->lock);
489 source = NULL;
491 if (stat(fname, &_stat) < 0) {
492 ret = isc__errno2result(errno);
493 goto errout;
496 * Solaris 2.5.1 does not have support for sockets (S_IFSOCK),
497 * but it does return type S_IFIFO (the OS believes that
498 * the socket is a fifo). This may be an issue if we tell
499 * the program to look at an actual FIFO as its source of
500 * entropy.
502 #if defined(S_ISSOCK)
503 if (S_ISSOCK(_stat.st_mode))
504 is_usocket = ISC_TRUE;
505 #endif
506 #if defined(S_ISFIFO) && defined(sun)
507 if (S_ISFIFO(_stat.st_mode))
508 is_usocket = ISC_TRUE;
509 #endif
510 if (is_usocket)
511 fd = socket(PF_UNIX, SOCK_STREAM, 0);
512 else
513 fd = open(fname, O_RDONLY | PORT_NONBLOCK, 0);
515 if (fd < 0) {
516 ret = isc__errno2result(errno);
517 goto errout;
520 ret = make_nonblock(fd);
521 if (ret != ISC_R_SUCCESS)
522 goto closefd;
524 if (is_usocket) {
525 struct sockaddr_un sname;
527 memset(&sname, 0, sizeof(sname));
528 sname.sun_family = AF_UNIX;
529 strncpy(sname.sun_path, fname, sizeof(sname.sun_path));
530 sname.sun_path[sizeof(sname.sun_path)-1] = '0';
531 #ifdef ISC_PLATFORM_HAVESALEN
532 #if !defined(SUN_LEN)
533 #define SUN_LEN(su) \
534 (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
535 #endif
536 sname.sun_len = SUN_LEN(&sname);
537 #endif
539 if (connect(fd, (struct sockaddr *) &sname,
540 sizeof(struct sockaddr_un)) < 0) {
541 if (errno != EINPROGRESS) {
542 ret = isc__errno2result(errno);
543 goto closefd;
545 } else
546 is_connected = ISC_TRUE;
549 source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
550 if (source == NULL) {
551 ret = ISC_R_NOMEMORY;
552 goto closefd;
556 * From here down, no failures can occur.
558 source->magic = SOURCE_MAGIC;
559 source->ent = ent;
560 source->total = 0;
561 source->bad = ISC_FALSE;
562 memset(source->name, 0, sizeof(source->name));
563 ISC_LINK_INIT(source, link);
564 if (is_usocket) {
565 source->sources.usocket.handle = fd;
566 if (is_connected)
567 source->sources.usocket.status =
568 isc_usocketsource_connected;
569 else
570 source->sources.usocket.status =
571 isc_usocketsource_connecting;
572 source->sources.usocket.sz_to_recv = 0;
573 source->type = ENTROPY_SOURCETYPE_USOCKET;
574 } else {
575 source->sources.file.handle = fd;
576 source->type = ENTROPY_SOURCETYPE_FILE;
580 * Hook it into the entropy system.
582 ISC_LIST_APPEND(ent->sources, source, link);
583 ent->nsources++;
585 UNLOCK(&ent->lock);
586 return (ISC_R_SUCCESS);
588 closefd:
589 (void)close(fd);
591 errout:
592 if (source != NULL)
593 isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t));
595 UNLOCK(&ent->lock);
597 return (ret);