4 * Copyright (C) 2004-2007, 2009 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 2000-2003 Internet Software Consortium.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
20 /* Id: entropy.c,v 1.80.332.2 2009/02/16 23:47:15 tbox Exp */
22 /* \file unix/entropy.c
24 * This is the system dependent part of the ISC entropy API.
29 #include <sys/param.h> /* Openserver 5.0.6A and FD_SETSIZE */
30 #include <sys/types.h>
33 #include <sys/socket.h>
41 #include <isc/platform.h>
42 #include <isc/strerror.h>
44 #ifdef ISC_PLATFORM_NEEDSYSSELECTH
45 #include <sys/select.h>
48 #include "errno2result.h"
51 * There is only one variable in the entropy data structures that is not
52 * system independent, but pulling the structure that uses it into this file
53 * ultimately means pulling several other independent structures here also to
54 * resolve their interdependencies. Thus only the problem variable's type
57 #define FILESOURCE_HANDLE_TYPE int
62 isc_usocketsource_disconnected
,
63 isc_usocketsource_connecting
,
64 isc_usocketsource_connected
,
65 isc_usocketsource_ndesired
,
66 isc_usocketsource_wrote
,
67 isc_usocketsource_reading
70 } isc_entropyusocketsource_t
;
72 #include "../entropy.c"
75 get_from_filesource(isc_entropysource_t
*source
, isc_uint32_t desired
) {
76 isc_entropy_t
*ent
= source
->ent
;
77 unsigned char buf
[128];
78 int fd
= source
->sources
.file
.handle
;
85 desired
= desired
/ 8 + (((desired
& 0x07) > 0) ? 1 : 0);
89 ndesired
= ISC_MIN(desired
, sizeof(buf
));
90 n
= read(fd
, buf
, ndesired
);
92 if (errno
== EAGAIN
|| errno
== EINTR
)
99 entropypool_adddata(ent
, buf
, n
, n
* 8);
107 source
->sources
.file
.handle
= -1;
108 source
->bad
= ISC_TRUE
;
115 get_from_usocketsource(isc_entropysource_t
*source
, isc_uint32_t desired
) {
116 isc_entropy_t
*ent
= source
->ent
;
117 unsigned char buf
[128];
118 int fd
= source
->sources
.usocket
.handle
;
119 ssize_t n
= 0, ndesired
;
121 size_t sz_to_recv
= source
->sources
.usocket
.sz_to_recv
;
126 desired
= desired
/ 8 + (((desired
& 0x07) > 0) ? 1 : 0);
129 while (desired
> 0) {
130 ndesired
= ISC_MIN(desired
, sizeof(buf
));
133 switch ( source
->sources
.usocket
.status
) {
134 case isc_usocketsource_ndesired
:
136 if ((n
= sendto(fd
, buf
, 1, 0, NULL
, 0)) < 0) {
137 if (errno
== EWOULDBLOCK
|| errno
== EINTR
||
143 source
->sources
.usocket
.status
=
144 isc_usocketsource_wrote
;
147 case isc_usocketsource_connecting
:
148 case isc_usocketsource_connected
:
151 if ((n
= sendto(fd
, buf
, 2, 0, NULL
, 0)) < 0) {
152 if (errno
== EWOULDBLOCK
|| errno
== EINTR
||
158 source
->sources
.usocket
.status
=
159 isc_usocketsource_ndesired
;
163 source
->sources
.usocket
.status
=
164 isc_usocketsource_wrote
;
167 case isc_usocketsource_wrote
:
168 if (recvfrom(fd
, buf
, 1, 0, NULL
, NULL
) != 1) {
169 if (errno
== EAGAIN
) {
171 * The problem of EAGAIN (try again
172 * later) is a major issue on HP-UX.
173 * Solaris actually tries the recvfrom
174 * call again, while HP-UX just dies.
175 * This code is an attempt to let the
176 * entropy pool fill back up (at least
177 * that's what I think the problem is.)
178 * We go to eagain_loop because if we
179 * just "break", then the "desired"
180 * amount gets borked.
182 #ifdef HAVE_NANOSLEEP
186 ts
.tv_nsec
= 1000000;
187 nanosleep(&ts
, NULL
);
193 if (errno
== EWOULDBLOCK
|| errno
== EINTR
)
197 source
->sources
.usocket
.status
=
198 isc_usocketsource_reading
;
200 source
->sources
.usocket
.sz_to_recv
= sz_to_recv
;
201 if (sz_to_recv
> sizeof(buf
))
205 case isc_usocketsource_reading
:
206 if (sz_to_recv
!= 0U) {
207 n
= recv(fd
, buf
, sz_to_recv
, 0);
209 if (errno
== EWOULDBLOCK
||
222 if ((size_t)n
!= sz_to_recv
)
223 source
->sources
.usocket
.sz_to_recv
-= n
;
225 source
->sources
.usocket
.status
=
226 isc_usocketsource_connected
;
231 entropypool_adddata(ent
, buf
, n
, n
* 8);
239 source
->bad
= ISC_TRUE
;
240 source
->sources
.usocket
.status
= isc_usocketsource_disconnected
;
241 source
->sources
.usocket
.handle
= -1;
248 * Poll each source, trying to get data from it to stuff into the entropy
252 fillpool(isc_entropy_t
*ent
, unsigned int desired
, isc_boolean_t blocking
) {
254 unsigned int remaining
;
256 unsigned int nsource
;
257 isc_entropysource_t
*source
;
259 REQUIRE(VALID_ENTROPY(ent
));
264 * This logic is a little strange, so an explanation is in order.
266 * If needed is 0, it means we are being asked to "fill to whatever
267 * we think is best." This means that if we have at least a
268 * partially full pool (say, > 1/4th of the pool) we probably don't
269 * need to add anything.
271 * Also, we will check to see if the "pseudo" count is too high.
272 * If it is, try to mix in better data. Too high is currently
273 * defined as 1/4th of the pool.
275 * Next, if we are asked to add a specific bit of entropy, make
276 * certain that we will do so. Clamp how much we try to add to
277 * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
279 * Note that if we are in a blocking mode, we will only try to
280 * get as much data as we need, not as much as we might want
286 if ((ent
->pool
.entropy
>= RND_POOLBITS
/ 4)
287 && (ent
->pool
.pseudo
<= RND_POOLBITS
/ 4))
290 needed
= THRESHOLD_BITS
* 4;
292 needed
= ISC_MAX(needed
, THRESHOLD_BITS
);
293 needed
= ISC_MIN(needed
, RND_POOLBITS
);
297 * In any case, clamp how much we need to how much we can add.
299 needed
= ISC_MIN(needed
, RND_POOLBITS
- ent
->pool
.entropy
);
302 * But wait! If we're not yet initialized, we need at least
306 if (ent
->initialized
< THRESHOLD_BITS
)
307 needed
= ISC_MAX(needed
, THRESHOLD_BITS
- ent
->initialized
);
310 * Poll each file source to see if we can read anything useful from
311 * it. XXXMLG When where are multiple sources, we should keep a
312 * record of which one we last used so we can start from it (or the
313 * next one) to avoid letting some sources build up entropy while
314 * others are always drained.
319 if (ent
->nextsource
== NULL
) {
320 ent
->nextsource
= ISC_LIST_HEAD(ent
->sources
);
321 if (ent
->nextsource
== NULL
)
324 source
= ent
->nextsource
;
326 for (nsource
= 0; nsource
< ent
->nsources
; nsource
++) {
334 switch ( source
->type
) {
335 case ENTROPY_SOURCETYPE_FILE
:
336 got
= get_from_filesource(source
, remaining
);
339 case ENTROPY_SOURCETYPE_USOCKET
:
340 got
= get_from_usocketsource(source
, remaining
);
346 remaining
-= ISC_MIN(remaining
, got
);
348 source
= ISC_LIST_NEXT(source
, link
);
350 source
= ISC_LIST_HEAD(ent
->sources
);
352 ent
->nextsource
= source
;
354 if (blocking
&& remaining
!= 0) {
357 fds
= wait_for_sources(ent
);
363 * Here, if there are bits remaining to be had and we can block,
364 * check to see if we have a callback source. If so, call them.
366 source
= ISC_LIST_HEAD(ent
->sources
);
367 while ((remaining
!= 0) && (source
!= NULL
)) {
372 if (source
->type
== ENTROPY_SOURCETYPE_CALLBACK
)
373 got
= get_from_callback(source
, remaining
, blocking
);
376 remaining
-= ISC_MIN(remaining
, got
);
381 source
= ISC_LIST_NEXT(source
, link
);
385 * Mark as initialized if we've added enough data.
387 if (ent
->initialized
< THRESHOLD_BITS
)
388 ent
->initialized
+= added
;
392 wait_for_sources(isc_entropy_t
*ent
) {
393 isc_entropysource_t
*source
;
403 source
= ISC_LIST_HEAD(ent
->sources
);
404 while (source
!= NULL
) {
405 if (source
->type
== ENTROPY_SOURCETYPE_FILE
) {
406 fd
= source
->sources
.file
.handle
;
408 maxfd
= ISC_MAX(maxfd
, fd
);
412 if (source
->type
== ENTROPY_SOURCETYPE_USOCKET
) {
413 fd
= source
->sources
.usocket
.handle
;
415 switch (source
->sources
.usocket
.status
) {
416 case isc_usocketsource_disconnected
:
418 case isc_usocketsource_connecting
:
419 case isc_usocketsource_connected
:
420 case isc_usocketsource_ndesired
:
421 maxfd
= ISC_MAX(maxfd
, fd
);
424 case isc_usocketsource_wrote
:
425 case isc_usocketsource_reading
:
426 maxfd
= ISC_MAX(maxfd
, fd
);
432 source
= ISC_LIST_NEXT(source
, link
);
438 cc
= select(maxfd
+ 1, &reads
, &writes
, NULL
, NULL
);
446 destroyfilesource(isc_entropyfilesource_t
*source
) {
447 (void)close(source
->handle
);
451 destroyusocketsource(isc_entropyusocketsource_t
*source
) {
452 close(source
->handle
);
456 * Make a fd non-blocking
459 make_nonblock(int fd
) {
462 char strbuf
[ISC_STRERRORSIZE
];
463 #ifdef USE_FIONBIO_IOCTL
466 ret
= ioctl(fd
, FIONBIO
, (char *)&on
);
468 flags
= fcntl(fd
, F_GETFL
, 0);
469 flags
|= PORT_NONBLOCK
;
470 ret
= fcntl(fd
, F_SETFL
, flags
);
474 isc__strerror(errno
, strbuf
, sizeof(strbuf
));
475 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
476 #ifdef USE_FIONBIO_IOCTL
477 "ioctl(%d, FIONBIO, &on): %s", fd
,
479 "fcntl(%d, F_SETFL, %d): %s", fd
, flags
,
483 return (ISC_R_UNEXPECTED
);
486 return (ISC_R_SUCCESS
);
490 isc_entropy_createfilesource(isc_entropy_t
*ent
, const char *fname
) {
493 isc_boolean_t is_usocket
= ISC_FALSE
;
494 isc_boolean_t is_connected
= ISC_FALSE
;
496 isc_entropysource_t
*source
;
498 REQUIRE(VALID_ENTROPY(ent
));
499 REQUIRE(fname
!= NULL
);
503 if (stat(fname
, &_stat
) < 0) {
504 ret
= isc__errno2result(errno
);
508 * Solaris 2.5.1 does not have support for sockets (S_IFSOCK),
509 * but it does return type S_IFIFO (the OS believes that
510 * the socket is a fifo). This may be an issue if we tell
511 * the program to look at an actual FIFO as its source of
514 #if defined(S_ISSOCK)
515 if (S_ISSOCK(_stat
.st_mode
))
516 is_usocket
= ISC_TRUE
;
518 #if defined(S_ISFIFO) && defined(sun)
519 if (S_ISFIFO(_stat
.st_mode
))
520 is_usocket
= ISC_TRUE
;
523 fd
= socket(PF_UNIX
, SOCK_STREAM
, 0);
525 fd
= open(fname
, O_RDONLY
| PORT_NONBLOCK
, 0);
528 ret
= isc__errno2result(errno
);
532 ret
= make_nonblock(fd
);
533 if (ret
!= ISC_R_SUCCESS
)
537 struct sockaddr_un sname
;
539 memset(&sname
, 0, sizeof(sname
));
540 sname
.sun_family
= AF_UNIX
;
541 strncpy(sname
.sun_path
, fname
, sizeof(sname
.sun_path
));
542 sname
.sun_path
[sizeof(sname
.sun_path
)-1] = '0';
543 #ifdef ISC_PLATFORM_HAVESALEN
544 #if !defined(SUN_LEN)
545 #define SUN_LEN(su) \
546 (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
548 sname
.sun_len
= SUN_LEN(&sname
);
551 if (connect(fd
, (struct sockaddr
*) &sname
,
552 sizeof(struct sockaddr_un
)) < 0) {
553 if (errno
!= EINPROGRESS
) {
554 ret
= isc__errno2result(errno
);
558 is_connected
= ISC_TRUE
;
561 source
= isc_mem_get(ent
->mctx
, sizeof(isc_entropysource_t
));
562 if (source
== NULL
) {
563 ret
= ISC_R_NOMEMORY
;
568 * From here down, no failures can occur.
570 source
->magic
= SOURCE_MAGIC
;
573 source
->bad
= ISC_FALSE
;
574 memset(source
->name
, 0, sizeof(source
->name
));
575 ISC_LINK_INIT(source
, link
);
577 source
->sources
.usocket
.handle
= fd
;
579 source
->sources
.usocket
.status
=
580 isc_usocketsource_connected
;
582 source
->sources
.usocket
.status
=
583 isc_usocketsource_connecting
;
584 source
->sources
.usocket
.sz_to_recv
= 0;
585 source
->type
= ENTROPY_SOURCETYPE_USOCKET
;
587 source
->sources
.file
.handle
= fd
;
588 source
->type
= ENTROPY_SOURCETYPE_FILE
;
592 * Hook it into the entropy system.
594 ISC_LIST_APPEND(ent
->sources
, source
, link
);
598 return (ISC_R_SUCCESS
);