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.71.18.7 2006/12/07 04:53:03 marka Exp $ */
20 /* \file unix/entropy.c
22 * This is the system dependent part of the ISC entropy API.
27 #include <sys/param.h> /* Openserver 5.0.6A and FD_SETSIZE */
28 #include <sys/types.h>
31 #include <sys/socket.h>
36 #include <isc/platform.h>
37 #include <isc/strerror.h>
39 #ifdef ISC_PLATFORM_NEEDSYSSELECTH
40 #include <sys/select.h>
43 #include "errno2result.h"
46 * There is only one variable in the entropy data structures that is not
47 * system independent, but pulling the structure that uses it into this file
48 * ultimately means pulling several other independent structures here also to
49 * resolve their interdependencies. Thus only the problem variable's type
52 #define FILESOURCE_HANDLE_TYPE int
57 isc_usocketsource_disconnected,
58 isc_usocketsource_connecting,
59 isc_usocketsource_connected,
60 isc_usocketsource_ndesired,
61 isc_usocketsource_wrote,
62 isc_usocketsource_reading
65 } isc_entropyusocketsource_t;
67 #include "../entropy.c"
70 get_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) {
71 isc_entropy_t *ent = source->ent;
72 unsigned char buf[128];
73 int fd = source->sources.file.handle;
80 desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
84 ndesired = ISC_MIN(desired, sizeof(buf));
85 n = read(fd, buf, ndesired);
87 if (errno == EAGAIN || errno == EINTR)
94 entropypool_adddata(ent, buf, n, n * 8);
102 source->sources.file.handle = -1;
103 source->bad = ISC_TRUE;
110 get_from_usocketsource(isc_entropysource_t *source, isc_uint32_t desired) {
111 isc_entropy_t *ent = source->ent;
112 unsigned char buf[128];
113 int fd = source->sources.usocket.handle;
114 ssize_t n = 0, ndesired;
116 size_t sz_to_recv = source->sources.usocket.sz_to_recv;
121 desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
124 while (desired > 0) {
125 ndesired = ISC_MIN(desired, sizeof(buf));
128 switch ( source->sources.usocket.status ) {
129 case isc_usocketsource_ndesired:
131 if ((n = sendto(fd, buf, 1, 0, NULL, 0)) < 0) {
132 if (errno == EWOULDBLOCK || errno == EINTR ||
138 source->sources.usocket.status =
139 isc_usocketsource_wrote;
142 case isc_usocketsource_connecting:
143 case isc_usocketsource_connected:
146 if ((n = sendto(fd, buf, 2, 0, NULL, 0)) < 0) {
147 if (errno == EWOULDBLOCK || errno == EINTR ||
153 source->sources.usocket.status =
154 isc_usocketsource_ndesired;
158 source->sources.usocket.status =
159 isc_usocketsource_wrote;
162 case isc_usocketsource_wrote:
163 if (recvfrom(fd, buf, 1, 0, NULL, NULL) != 1) {
164 if (errno == EAGAIN) {
166 * The problem of EAGAIN (try again
167 * later) is a major issue on HP-UX.
168 * Solaris actually tries the recvfrom
169 * call again, while HP-UX just dies.
170 * This code is an attempt to let the
171 * entropy pool fill back up (at least
172 * that's what I think the problem is.)
173 * We go to eagain_loop because if we
174 * just "break", then the "desired"
175 * amount gets borked.
180 if (errno == EWOULDBLOCK || errno == EINTR)
184 source->sources.usocket.status =
185 isc_usocketsource_reading;
187 source->sources.usocket.sz_to_recv = sz_to_recv;
188 if (sz_to_recv > sizeof(buf))
192 case isc_usocketsource_reading:
193 if (sz_to_recv != 0U) {
194 n = recv(fd, buf, sz_to_recv, 0);
196 if (errno == EWOULDBLOCK ||
209 if ((size_t)n != sz_to_recv)
210 source->sources.usocket.sz_to_recv -= n;
212 source->sources.usocket.status =
213 isc_usocketsource_connected;
218 entropypool_adddata(ent, buf, n, n * 8);
226 source->bad = ISC_TRUE;
227 source->sources.usocket.status = isc_usocketsource_disconnected;
228 source->sources.usocket.handle = -1;
235 * Poll each source, trying to get data from it to stuff into the entropy
239 fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
241 unsigned int remaining;
243 unsigned int nsource;
244 isc_entropysource_t *source;
246 REQUIRE(VALID_ENTROPY(ent));
251 * This logic is a little strange, so an explanation is in order.
253 * If needed is 0, it means we are being asked to "fill to whatever
254 * we think is best." This means that if we have at least a
255 * partially full pool (say, > 1/4th of the pool) we probably don't
256 * need to add anything.
258 * Also, we will check to see if the "pseudo" count is too high.
259 * If it is, try to mix in better data. Too high is currently
260 * defined as 1/4th of the pool.
262 * Next, if we are asked to add a specific bit of entropy, make
263 * certain that we will do so. Clamp how much we try to add to
264 * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
266 * Note that if we are in a blocking mode, we will only try to
267 * get as much data as we need, not as much as we might want
273 if ((ent->pool.entropy >= RND_POOLBITS / 4)
274 && (ent->pool.pseudo <= RND_POOLBITS / 4))
277 needed = THRESHOLD_BITS * 4;
279 needed = ISC_MAX(needed, THRESHOLD_BITS);
280 needed = ISC_MIN(needed, RND_POOLBITS);
284 * In any case, clamp how much we need to how much we can add.
286 needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
289 * But wait! If we're not yet initialized, we need at least
293 if (ent->initialized < THRESHOLD_BITS)
294 needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
297 * Poll each file source to see if we can read anything useful from
298 * it. XXXMLG When where are multiple sources, we should keep a
299 * record of which one we last used so we can start from it (or the
300 * next one) to avoid letting some sources build up entropy while
301 * others are always drained.
306 if (ent->nextsource == NULL) {
307 ent->nextsource = ISC_LIST_HEAD(ent->sources);
308 if (ent->nextsource == NULL)
311 source = ent->nextsource;
313 for (nsource = 0; nsource < ent->nsources; nsource++) {
321 switch ( source->type ) {
322 case ENTROPY_SOURCETYPE_FILE:
323 got = get_from_filesource(source, remaining);
326 case ENTROPY_SOURCETYPE_USOCKET:
327 got = get_from_usocketsource(source, remaining);
333 remaining -= ISC_MIN(remaining, got);
335 source = ISC_LIST_NEXT(source, link);
337 source = ISC_LIST_HEAD(ent->sources);
339 ent->nextsource = source;
341 if (blocking && remaining != 0) {
344 fds = wait_for_sources(ent);
350 * Here, if there are bits remaining to be had and we can block,
351 * check to see if we have a callback source. If so, call them.
353 source = ISC_LIST_HEAD(ent->sources);
354 while ((remaining != 0) && (source != NULL)) {
359 if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
360 got = get_from_callback(source, remaining, blocking);
363 remaining -= ISC_MIN(remaining, got);
368 source = ISC_LIST_NEXT(source, link);
372 * Mark as initialized if we've added enough data.
374 if (ent->initialized < THRESHOLD_BITS)
375 ent->initialized += added;
379 wait_for_sources(isc_entropy_t *ent) {
380 isc_entropysource_t *source;
390 source = ISC_LIST_HEAD(ent->sources);
391 while (source != NULL) {
392 if (source->type == ENTROPY_SOURCETYPE_FILE) {
393 fd = source->sources.file.handle;
395 maxfd = ISC_MAX(maxfd, fd);
399 if (source->type == ENTROPY_SOURCETYPE_USOCKET) {
400 fd = source->sources.usocket.handle;
402 switch (source->sources.usocket.status) {
403 case isc_usocketsource_disconnected:
405 case isc_usocketsource_connecting:
406 case isc_usocketsource_connected:
407 case isc_usocketsource_ndesired:
408 maxfd = ISC_MAX(maxfd, fd);
411 case isc_usocketsource_wrote:
412 case isc_usocketsource_reading:
413 maxfd = ISC_MAX(maxfd, fd);
419 source = ISC_LIST_NEXT(source, link);
425 cc = select(maxfd + 1, &reads, &writes, NULL, NULL);
433 destroyfilesource(isc_entropyfilesource_t *source) {
434 (void)close(source->handle);
438 destroyusocketsource(isc_entropyusocketsource_t *source) {
439 close(source->handle);
443 * Make a fd non-blocking
446 make_nonblock(int fd) {
449 char strbuf[ISC_STRERRORSIZE];
450 #ifdef USE_FIONBIO_IOCTL
453 ret = ioctl(fd, FIONBIO, (char *)&on);
455 flags = fcntl(fd, F_GETFL, 0);
456 flags |= PORT_NONBLOCK;
457 ret = fcntl(fd, F_SETFL, flags);
461 isc__strerror(errno, strbuf, sizeof(strbuf));
462 UNEXPECTED_ERROR(__FILE__, __LINE__,
463 #ifdef USE_FIONBIO_IOCTL
464 "ioctl(%d, FIONBIO, &on): %s", fd,
466 "fcntl(%d, F_SETFL, %d): %s", fd, flags,
470 return (ISC_R_UNEXPECTED);
473 return (ISC_R_SUCCESS);
477 isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
480 isc_boolean_t is_usocket = ISC_FALSE;
481 isc_boolean_t is_connected = ISC_FALSE;
483 isc_entropysource_t *source;
485 REQUIRE(VALID_ENTROPY(ent));
486 REQUIRE(fname != NULL);
490 if (stat(fname, &_stat) < 0) {
491 ret = isc__errno2result(errno);
495 * Solaris 2.5.1 does not have support for sockets (S_IFSOCK),
496 * but it does return type S_IFIFO (the OS believes that
497 * the socket is a fifo). This may be an issue if we tell
498 * the program to look at an actual FIFO as its source of
501 #if defined(S_ISSOCK)
502 if (S_ISSOCK(_stat.st_mode))
503 is_usocket = ISC_TRUE;
505 #if defined(S_ISFIFO) && defined(sun)
506 if (S_ISFIFO(_stat.st_mode))
507 is_usocket = ISC_TRUE;
510 fd = socket(PF_UNIX, SOCK_STREAM, 0);
512 fd = open(fname, O_RDONLY | PORT_NONBLOCK, 0);
515 ret = isc__errno2result(errno);
519 ret = make_nonblock(fd);
520 if (ret != ISC_R_SUCCESS)
524 struct sockaddr_un sname;
526 memset(&sname, 0, sizeof(sname));
527 sname.sun_family = AF_UNIX;
528 strncpy(sname.sun_path, fname, sizeof(sname.sun_path));
529 sname.sun_path[sizeof(sname.sun_path)-1] = '0';
530 #ifdef ISC_PLATFORM_HAVESALEN
531 #if !defined(SUN_LEN)
532 #define SUN_LEN(su) \
533 (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
535 sname.sun_len = SUN_LEN(&sname);
538 if (connect(fd, (struct sockaddr *) &sname,
539 sizeof(struct sockaddr_un)) < 0) {
540 if (errno != EINPROGRESS) {
541 ret = isc__errno2result(errno);
545 is_connected = ISC_TRUE;
548 source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
549 if (source == NULL) {
550 ret = ISC_R_NOMEMORY;
555 * From here down, no failures can occur.
557 source->magic = SOURCE_MAGIC;
560 source->bad = ISC_FALSE;
561 memset(source->name, 0, sizeof(source->name));
562 ISC_LINK_INIT(source, link);
564 source->sources.usocket.handle = fd;
566 source->sources.usocket.status =
567 isc_usocketsource_connected;
569 source->sources.usocket.status =
570 isc_usocketsource_connecting;
571 source->sources.usocket.sz_to_recv = 0;
572 source->type = ENTROPY_SOURCETYPE_USOCKET;
574 source->sources.file.handle = fd;
575 source->type = ENTROPY_SOURCETYPE_FILE;
579 * Hook it into the entropy system.
581 ISC_LIST_APPEND(ent->sources, source, link);
585 return (ISC_R_SUCCESS);