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.
26 #include <sys/param.h> /* Openserver 5.0.6A and FD_SETSIZE */
27 #include <sys/types.h>
30 #include <sys/socket.h>
35 #include <isc/platform.h>
36 #include <isc/strerror.h>
38 #ifdef ISC_PLATFORM_NEEDSYSSELECTH
39 #include <sys/select.h>
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
51 #define FILESOURCE_HANDLE_TYPE int
56 isc_usocketsource_disconnected
,
57 isc_usocketsource_connecting
,
58 isc_usocketsource_connected
,
59 isc_usocketsource_ndesired
,
60 isc_usocketsource_wrote
,
61 isc_usocketsource_reading
64 } isc_entropyusocketsource_t
;
66 #include "../entropy.c"
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
;
79 desired
= desired
/ 8 + (((desired
& 0x07) > 0) ? 1 : 0);
83 ndesired
= ISC_MIN(desired
, sizeof(buf
));
84 n
= read(fd
, buf
, ndesired
);
86 if (errno
== EAGAIN
|| errno
== EINTR
)
93 entropypool_adddata(ent
, buf
, n
, n
* 8);
101 source
->sources
.file
.handle
= -1;
102 source
->bad
= ISC_TRUE
;
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
;
115 size_t sz_to_recv
= source
->sources
.usocket
.sz_to_recv
;
120 desired
= desired
/ 8 + (((desired
& 0x07) > 0) ? 1 : 0);
123 while (desired
> 0) {
124 ndesired
= ISC_MIN(desired
, sizeof(buf
));
127 switch ( source
->sources
.usocket
.status
) {
128 case isc_usocketsource_ndesired
:
130 if ((n
= sendto(fd
, buf
, 1, 0, NULL
, 0)) < 0) {
131 if (errno
== EWOULDBLOCK
|| errno
== EINTR
||
137 source
->sources
.usocket
.status
=
138 isc_usocketsource_wrote
;
141 case isc_usocketsource_connecting
:
142 case isc_usocketsource_connected
:
145 if ((n
= sendto(fd
, buf
, 2, 0, NULL
, 0)) < 0) {
146 if (errno
== EWOULDBLOCK
|| errno
== EINTR
||
152 source
->sources
.usocket
.status
=
153 isc_usocketsource_ndesired
;
157 source
->sources
.usocket
.status
=
158 isc_usocketsource_wrote
;
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.
179 if (errno
== EWOULDBLOCK
|| errno
== EINTR
)
183 source
->sources
.usocket
.status
=
184 isc_usocketsource_reading
;
186 source
->sources
.usocket
.sz_to_recv
= sz_to_recv
;
187 if (sz_to_recv
> sizeof(buf
))
191 case isc_usocketsource_reading
:
192 if (sz_to_recv
!= 0U) {
193 n
= recv(fd
, buf
, sz_to_recv
, 0);
195 if (errno
== EWOULDBLOCK
||
208 if ((size_t)n
!= sz_to_recv
)
209 source
->sources
.usocket
.sz_to_recv
-= n
;
211 source
->sources
.usocket
.status
=
212 isc_usocketsource_connected
;
217 entropypool_adddata(ent
, buf
, n
, n
* 8);
225 source
->bad
= ISC_TRUE
;
226 source
->sources
.usocket
.status
= isc_usocketsource_disconnected
;
227 source
->sources
.usocket
.handle
= -1;
234 * Poll each source, trying to get data from it to stuff into the entropy
238 fillpool(isc_entropy_t
*ent
, unsigned int desired
, isc_boolean_t blocking
) {
240 unsigned int remaining
;
242 unsigned int nsource
;
243 isc_entropysource_t
*source
;
245 REQUIRE(VALID_ENTROPY(ent
));
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
272 if ((ent
->pool
.entropy
>= RND_POOLBITS
/ 4)
273 && (ent
->pool
.pseudo
<= RND_POOLBITS
/ 4))
276 needed
= THRESHOLD_BITS
* 4;
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
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.
305 if (ent
->nextsource
== NULL
) {
306 ent
->nextsource
= ISC_LIST_HEAD(ent
->sources
);
307 if (ent
->nextsource
== NULL
)
310 source
= ent
->nextsource
;
312 for (nsource
= 0; nsource
< ent
->nsources
; nsource
++) {
320 switch ( source
->type
) {
321 case ENTROPY_SOURCETYPE_FILE
:
322 got
= get_from_filesource(source
, remaining
);
325 case ENTROPY_SOURCETYPE_USOCKET
:
326 got
= get_from_usocketsource(source
, remaining
);
332 remaining
-= ISC_MIN(remaining
, got
);
334 source
= ISC_LIST_NEXT(source
, link
);
336 source
= ISC_LIST_HEAD(ent
->sources
);
338 ent
->nextsource
= source
;
340 if (blocking
&& remaining
!= 0) {
343 fds
= wait_for_sources(ent
);
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
)) {
358 if (source
->type
== ENTROPY_SOURCETYPE_CALLBACK
)
359 got
= get_from_callback(source
, remaining
, blocking
);
362 remaining
-= ISC_MIN(remaining
, got
);
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
;
378 wait_for_sources(isc_entropy_t
*ent
) {
379 isc_entropysource_t
*source
;
389 source
= ISC_LIST_HEAD(ent
->sources
);
390 while (source
!= NULL
) {
391 if (source
->type
== ENTROPY_SOURCETYPE_FILE
) {
392 fd
= source
->sources
.file
.handle
;
394 maxfd
= ISC_MAX(maxfd
, fd
);
398 if (source
->type
== ENTROPY_SOURCETYPE_USOCKET
) {
399 fd
= source
->sources
.usocket
.handle
;
401 switch (source
->sources
.usocket
.status
) {
402 case isc_usocketsource_disconnected
:
404 case isc_usocketsource_connecting
:
405 case isc_usocketsource_connected
:
406 case isc_usocketsource_ndesired
:
407 maxfd
= ISC_MAX(maxfd
, fd
);
410 case isc_usocketsource_wrote
:
411 case isc_usocketsource_reading
:
412 maxfd
= ISC_MAX(maxfd
, fd
);
418 source
= ISC_LIST_NEXT(source
, link
);
424 cc
= select(maxfd
+ 1, &reads
, &writes
, NULL
, NULL
);
432 destroyfilesource(isc_entropyfilesource_t
*source
) {
433 (void)close(source
->handle
);
437 destroyusocketsource(isc_entropyusocketsource_t
*source
) {
438 close(source
->handle
);
442 * Make a fd non-blocking
445 make_nonblock(int fd
) {
448 char strbuf
[ISC_STRERRORSIZE
];
449 #ifdef USE_FIONBIO_IOCTL
452 ret
= ioctl(fd
, FIONBIO
, (char *)&on
);
454 flags
= fcntl(fd
, F_GETFL
, 0);
455 flags
|= PORT_NONBLOCK
;
456 ret
= fcntl(fd
, F_SETFL
, flags
);
460 isc__strerror(errno
, strbuf
, sizeof(strbuf
));
461 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
462 #ifdef USE_FIONBIO_IOCTL
463 "ioctl(%d, FIONBIO, &on): %s", fd
,
465 "fcntl(%d, F_SETFL, %d): %s", fd
, flags
,
469 return (ISC_R_UNEXPECTED
);
472 return (ISC_R_SUCCESS
);
476 isc_entropy_createfilesource(isc_entropy_t
*ent
, const char *fname
) {
479 isc_boolean_t is_usocket
= ISC_FALSE
;
480 isc_boolean_t is_connected
= ISC_FALSE
;
482 isc_entropysource_t
*source
;
484 REQUIRE(VALID_ENTROPY(ent
));
485 REQUIRE(fname
!= NULL
);
491 if (stat(fname
, &_stat
) < 0) {
492 ret
= isc__errno2result(errno
);
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
502 #if defined(S_ISSOCK)
503 if (S_ISSOCK(_stat
.st_mode
))
504 is_usocket
= ISC_TRUE
;
506 #if defined(S_ISFIFO) && defined(sun)
507 if (S_ISFIFO(_stat
.st_mode
))
508 is_usocket
= ISC_TRUE
;
511 fd
= socket(PF_UNIX
, SOCK_STREAM
, 0);
513 fd
= open(fname
, O_RDONLY
| PORT_NONBLOCK
, 0);
516 ret
= isc__errno2result(errno
);
520 ret
= make_nonblock(fd
);
521 if (ret
!= ISC_R_SUCCESS
)
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))
536 sname
.sun_len
= SUN_LEN(&sname
);
539 if (connect(fd
, (struct sockaddr
*) &sname
,
540 sizeof(struct sockaddr_un
)) < 0) {
541 if (errno
!= EINPROGRESS
) {
542 ret
= isc__errno2result(errno
);
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
;
556 * From here down, no failures can occur.
558 source
->magic
= SOURCE_MAGIC
;
561 source
->bad
= ISC_FALSE
;
562 memset(source
->name
, 0, sizeof(source
->name
));
563 ISC_LINK_INIT(source
, link
);
565 source
->sources
.usocket
.handle
= fd
;
567 source
->sources
.usocket
.status
=
568 isc_usocketsource_connected
;
570 source
->sources
.usocket
.status
=
571 isc_usocketsource_connecting
;
572 source
->sources
.usocket
.sz_to_recv
= 0;
573 source
->type
= ENTROPY_SOURCETYPE_USOCKET
;
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
);
586 return (ISC_R_SUCCESS
);
593 isc_mem_put(ent
->mctx
, source
, sizeof(isc_entropysource_t
));