Sync usage with man page.
[netbsd-mini2440.git] / external / bsd / ntp / dist / lib / isc / unix / entropy.c
blob64ce91356589a6e3c02fdadd4a695824cea51ea8
1 /* $NetBSD$ */
3 /*
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
23 * \brief
24 * This is the system dependent part of the ISC entropy API.
27 #include <config.h>
29 #include <sys/param.h> /* Openserver 5.0.6A and FD_SETSIZE */
30 #include <sys/types.h>
31 #include <sys/time.h>
32 #include <sys/stat.h>
33 #include <sys/socket.h>
34 #include <sys/un.h>
36 #ifdef HAVE_NANOSLEEP
37 #include <time.h>
38 #endif
39 #include <unistd.h>
41 #include <isc/platform.h>
42 #include <isc/strerror.h>
44 #ifdef ISC_PLATFORM_NEEDSYSSELECTH
45 #include <sys/select.h>
46 #endif
48 #include "errno2result.h"
50 /*%
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
55 * is defined here.
57 #define FILESOURCE_HANDLE_TYPE int
59 typedef struct {
60 int handle;
61 enum {
62 isc_usocketsource_disconnected,
63 isc_usocketsource_connecting,
64 isc_usocketsource_connected,
65 isc_usocketsource_ndesired,
66 isc_usocketsource_wrote,
67 isc_usocketsource_reading
68 } status;
69 size_t sz_to_recv;
70 } isc_entropyusocketsource_t;
72 #include "../entropy.c"
74 static unsigned int
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;
79 ssize_t n, ndesired;
80 unsigned int added;
82 if (source->bad)
83 return (0);
85 desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
87 added = 0;
88 while (desired > 0) {
89 ndesired = ISC_MIN(desired, sizeof(buf));
90 n = read(fd, buf, ndesired);
91 if (n < 0) {
92 if (errno == EAGAIN || errno == EINTR)
93 goto out;
94 goto err;
96 if (n == 0)
97 goto err;
99 entropypool_adddata(ent, buf, n, n * 8);
100 added += n * 8;
101 desired -= n;
103 goto out;
105 err:
106 (void)close(fd);
107 source->sources.file.handle = -1;
108 source->bad = ISC_TRUE;
110 out:
111 return (added);
114 static unsigned int
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;
120 unsigned int added;
121 size_t sz_to_recv = source->sources.usocket.sz_to_recv;
123 if (source->bad)
124 return (0);
126 desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
128 added = 0;
129 while (desired > 0) {
130 ndesired = ISC_MIN(desired, sizeof(buf));
131 eagain_loop:
133 switch ( source->sources.usocket.status ) {
134 case isc_usocketsource_ndesired:
135 buf[0] = ndesired;
136 if ((n = sendto(fd, buf, 1, 0, NULL, 0)) < 0) {
137 if (errno == EWOULDBLOCK || errno == EINTR ||
138 errno == ECONNRESET)
139 goto out;
140 goto err;
142 INSIST(n == 1);
143 source->sources.usocket.status =
144 isc_usocketsource_wrote;
145 goto eagain_loop;
147 case isc_usocketsource_connecting:
148 case isc_usocketsource_connected:
149 buf[0] = 1;
150 buf[1] = ndesired;
151 if ((n = sendto(fd, buf, 2, 0, NULL, 0)) < 0) {
152 if (errno == EWOULDBLOCK || errno == EINTR ||
153 errno == ECONNRESET)
154 goto out;
155 goto err;
157 if (n == 1) {
158 source->sources.usocket.status =
159 isc_usocketsource_ndesired;
160 goto eagain_loop;
162 INSIST(n == 2);
163 source->sources.usocket.status =
164 isc_usocketsource_wrote;
165 /*FALLTHROUGH*/
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
183 struct timespec ts;
185 ts.tv_sec = 0;
186 ts.tv_nsec = 1000000;
187 nanosleep(&ts, NULL);
188 #else
189 usleep(1000);
190 #endif
191 goto eagain_loop;
193 if (errno == EWOULDBLOCK || errno == EINTR)
194 goto out;
195 goto err;
197 source->sources.usocket.status =
198 isc_usocketsource_reading;
199 sz_to_recv = buf[0];
200 source->sources.usocket.sz_to_recv = sz_to_recv;
201 if (sz_to_recv > sizeof(buf))
202 goto err;
203 /*FALLTHROUGH*/
205 case isc_usocketsource_reading:
206 if (sz_to_recv != 0U) {
207 n = recv(fd, buf, sz_to_recv, 0);
208 if (n < 0) {
209 if (errno == EWOULDBLOCK ||
210 errno == EINTR)
211 goto out;
212 goto err;
214 } else
215 n = 0;
216 break;
218 default:
219 goto err;
222 if ((size_t)n != sz_to_recv)
223 source->sources.usocket.sz_to_recv -= n;
224 else
225 source->sources.usocket.status =
226 isc_usocketsource_connected;
228 if (n == 0)
229 goto out;
231 entropypool_adddata(ent, buf, n, n * 8);
232 added += n * 8;
233 desired -= n;
235 goto out;
237 err:
238 close(fd);
239 source->bad = ISC_TRUE;
240 source->sources.usocket.status = isc_usocketsource_disconnected;
241 source->sources.usocket.handle = -1;
243 out:
244 return (added);
248 * Poll each source, trying to get data from it to stuff into the entropy
249 * pool.
251 static void
252 fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
253 unsigned int added;
254 unsigned int remaining;
255 unsigned int needed;
256 unsigned int nsource;
257 isc_entropysource_t *source;
259 REQUIRE(VALID_ENTROPY(ent));
261 needed = desired;
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
281 * to build up.
283 if (needed == 0) {
284 REQUIRE(!blocking);
286 if ((ent->pool.entropy >= RND_POOLBITS / 4)
287 && (ent->pool.pseudo <= RND_POOLBITS / 4))
288 return;
290 needed = THRESHOLD_BITS * 4;
291 } else {
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
303 * THRESHOLD_BITS
304 * of randomness.
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.
317 added = 0;
318 remaining = needed;
319 if (ent->nextsource == NULL) {
320 ent->nextsource = ISC_LIST_HEAD(ent->sources);
321 if (ent->nextsource == NULL)
322 return;
324 source = ent->nextsource;
325 again_file:
326 for (nsource = 0; nsource < ent->nsources; nsource++) {
327 unsigned int got;
329 if (remaining == 0)
330 break;
332 got = 0;
334 switch ( source->type ) {
335 case ENTROPY_SOURCETYPE_FILE:
336 got = get_from_filesource(source, remaining);
337 break;
339 case ENTROPY_SOURCETYPE_USOCKET:
340 got = get_from_usocketsource(source, remaining);
341 break;
344 added += got;
346 remaining -= ISC_MIN(remaining, got);
348 source = ISC_LIST_NEXT(source, link);
349 if (source == NULL)
350 source = ISC_LIST_HEAD(ent->sources);
352 ent->nextsource = source;
354 if (blocking && remaining != 0) {
355 int fds;
357 fds = wait_for_sources(ent);
358 if (fds > 0)
359 goto again_file;
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)) {
368 unsigned int got;
370 got = 0;
372 if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
373 got = get_from_callback(source, remaining, blocking);
375 added += got;
376 remaining -= ISC_MIN(remaining, got);
378 if (added >= needed)
379 break;
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;
391 static int
392 wait_for_sources(isc_entropy_t *ent) {
393 isc_entropysource_t *source;
394 int maxfd, fd;
395 int cc;
396 fd_set reads;
397 fd_set writes;
399 maxfd = -1;
400 FD_ZERO(&reads);
401 FD_ZERO(&writes);
403 source = ISC_LIST_HEAD(ent->sources);
404 while (source != NULL) {
405 if (source->type == ENTROPY_SOURCETYPE_FILE) {
406 fd = source->sources.file.handle;
407 if (fd >= 0) {
408 maxfd = ISC_MAX(maxfd, fd);
409 FD_SET(fd, &reads);
412 if (source->type == ENTROPY_SOURCETYPE_USOCKET) {
413 fd = source->sources.usocket.handle;
414 if (fd >= 0) {
415 switch (source->sources.usocket.status) {
416 case isc_usocketsource_disconnected:
417 break;
418 case isc_usocketsource_connecting:
419 case isc_usocketsource_connected:
420 case isc_usocketsource_ndesired:
421 maxfd = ISC_MAX(maxfd, fd);
422 FD_SET(fd, &writes);
423 break;
424 case isc_usocketsource_wrote:
425 case isc_usocketsource_reading:
426 maxfd = ISC_MAX(maxfd, fd);
427 FD_SET(fd, &reads);
428 break;
432 source = ISC_LIST_NEXT(source, link);
435 if (maxfd < 0)
436 return (-1);
438 cc = select(maxfd + 1, &reads, &writes, NULL, NULL);
439 if (cc < 0)
440 return (-1);
442 return (cc);
445 static void
446 destroyfilesource(isc_entropyfilesource_t *source) {
447 (void)close(source->handle);
450 static void
451 destroyusocketsource(isc_entropyusocketsource_t *source) {
452 close(source->handle);
456 * Make a fd non-blocking
458 static isc_result_t
459 make_nonblock(int fd) {
460 int ret;
461 int flags;
462 char strbuf[ISC_STRERRORSIZE];
463 #ifdef USE_FIONBIO_IOCTL
464 int on = 1;
466 ret = ioctl(fd, FIONBIO, (char *)&on);
467 #else
468 flags = fcntl(fd, F_GETFL, 0);
469 flags |= PORT_NONBLOCK;
470 ret = fcntl(fd, F_SETFL, flags);
471 #endif
473 if (ret == -1) {
474 isc__strerror(errno, strbuf, sizeof(strbuf));
475 UNEXPECTED_ERROR(__FILE__, __LINE__,
476 #ifdef USE_FIONBIO_IOCTL
477 "ioctl(%d, FIONBIO, &on): %s", fd,
478 #else
479 "fcntl(%d, F_SETFL, %d): %s", fd, flags,
480 #endif
481 strbuf);
483 return (ISC_R_UNEXPECTED);
486 return (ISC_R_SUCCESS);
489 isc_result_t
490 isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
491 int fd;
492 struct stat _stat;
493 isc_boolean_t is_usocket = ISC_FALSE;
494 isc_boolean_t is_connected = ISC_FALSE;
495 isc_result_t ret;
496 isc_entropysource_t *source;
498 REQUIRE(VALID_ENTROPY(ent));
499 REQUIRE(fname != NULL);
501 LOCK(&ent->lock);
503 if (stat(fname, &_stat) < 0) {
504 ret = isc__errno2result(errno);
505 goto errout;
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
512 * entropy.
514 #if defined(S_ISSOCK)
515 if (S_ISSOCK(_stat.st_mode))
516 is_usocket = ISC_TRUE;
517 #endif
518 #if defined(S_ISFIFO) && defined(sun)
519 if (S_ISFIFO(_stat.st_mode))
520 is_usocket = ISC_TRUE;
521 #endif
522 if (is_usocket)
523 fd = socket(PF_UNIX, SOCK_STREAM, 0);
524 else
525 fd = open(fname, O_RDONLY | PORT_NONBLOCK, 0);
527 if (fd < 0) {
528 ret = isc__errno2result(errno);
529 goto errout;
532 ret = make_nonblock(fd);
533 if (ret != ISC_R_SUCCESS)
534 goto closefd;
536 if (is_usocket) {
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))
547 #endif
548 sname.sun_len = SUN_LEN(&sname);
549 #endif
551 if (connect(fd, (struct sockaddr *) &sname,
552 sizeof(struct sockaddr_un)) < 0) {
553 if (errno != EINPROGRESS) {
554 ret = isc__errno2result(errno);
555 goto closefd;
557 } else
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;
564 goto closefd;
568 * From here down, no failures can occur.
570 source->magic = SOURCE_MAGIC;
571 source->ent = ent;
572 source->total = 0;
573 source->bad = ISC_FALSE;
574 memset(source->name, 0, sizeof(source->name));
575 ISC_LINK_INIT(source, link);
576 if (is_usocket) {
577 source->sources.usocket.handle = fd;
578 if (is_connected)
579 source->sources.usocket.status =
580 isc_usocketsource_connected;
581 else
582 source->sources.usocket.status =
583 isc_usocketsource_connecting;
584 source->sources.usocket.sz_to_recv = 0;
585 source->type = ENTROPY_SOURCETYPE_USOCKET;
586 } else {
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);
595 ent->nsources++;
597 UNLOCK(&ent->lock);
598 return (ISC_R_SUCCESS);
600 closefd:
601 (void)close(fd);
603 errout:
604 UNLOCK(&ent->lock);
606 return (ret);