diff options
Diffstat (limited to 'usr.sbin/bind/lib/isc/unix/entropy.c')
| -rw-r--r-- | usr.sbin/bind/lib/isc/unix/entropy.c | 288 |
1 files changed, 264 insertions, 24 deletions
diff --git a/usr.sbin/bind/lib/isc/unix/entropy.c b/usr.sbin/bind/lib/isc/unix/entropy.c index 005de6810d7..8c73be6f14b 100644 --- a/usr.sbin/bind/lib/isc/unix/entropy.c +++ b/usr.sbin/bind/lib/isc/unix/entropy.c @@ -1,21 +1,21 @@ /* - * Copyright (C) 2000-2002 Internet Software Consortium. + * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2000-2003 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * - * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM - * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL - * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. */ -/* $ISC: entropy.c,v 1.60.2.3 2002/08/05 06:57:16 marka Exp $ */ +/* $ISC: entropy.c,v 1.60.2.3.8.9 2004/03/16 05:02:31 marka Exp $ */ /* * This is the system depenedent part of the ISC entropy API. @@ -26,6 +26,9 @@ #include <sys/param.h> /* Openserver 5.0.6A and FD_SETSIZE */ #include <sys/types.h> #include <sys/time.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> #include <unistd.h> @@ -47,6 +50,19 @@ */ #define FILESOURCE_HANDLE_TYPE int +typedef struct { + int handle; + enum { + isc_usocketsource_disconnected, + isc_usocketsource_connecting, + isc_usocketsource_connected, + isc_usocketsource_ndesired, + isc_usocketsource_wrote, + isc_usocketsource_reading + } status; + size_t sz_to_recv; +} isc_entropyusocketsource_t; + #include "../entropy.c" static unsigned int @@ -69,20 +85,146 @@ get_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) { if (n < 0) { if (errno == EAGAIN || errno == EINTR) goto out; - close(fd); - source->bad = ISC_TRUE; - goto out; + goto err; } - if (n == 0) { - close(fd); - source->bad = ISC_TRUE; - goto out; + if (n == 0) + goto err; + + entropypool_adddata(ent, buf, n, n * 8); + added += n * 8; + desired -= n; + } + goto out; + + err: + (void)close(fd); + source->sources.file.handle = -1; + source->bad = ISC_TRUE; + + out: + return (added); +} + +static unsigned int +get_from_usocketsource(isc_entropysource_t *source, isc_uint32_t desired) { + isc_entropy_t *ent = source->ent; + unsigned char buf[128]; + int fd = source->sources.usocket.handle; + ssize_t n = 0, ndesired; + unsigned int added; + size_t sz_to_recv = source->sources.usocket.sz_to_recv; + + if (source->bad) + return (0); + + desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0); + + added = 0; + while (desired > 0) { + ndesired = ISC_MIN(desired, sizeof(buf)); + eagain_loop: + + switch ( source->sources.usocket.status ) { + case isc_usocketsource_ndesired: + buf[0] = ndesired; + if ((n = send(fd, buf, 1, 0)) < 0) { + if (errno == EWOULDBLOCK || errno == EINTR || + errno == ECONNRESET) + goto out; + goto err; + } + INSIST(n == 1); + source->sources.usocket.status = + isc_usocketsource_wrote; + goto eagain_loop; + + case isc_usocketsource_connecting: + case isc_usocketsource_connected: + buf[0] = 1; + buf[1] = ndesired; + if ((n = send(fd, buf, 2, 0)) < 0) { + if (errno == EWOULDBLOCK || errno == EINTR || + errno == ECONNRESET) + goto out; + goto err; + } + if (n == 1) { + source->sources.usocket.status = + isc_usocketsource_ndesired; + goto eagain_loop; + } + INSIST(n == 2); + source->sources.usocket.status = + isc_usocketsource_wrote; + /*FALLTHROUGH*/ + + case isc_usocketsource_wrote: + if (recv(fd, buf, 1, 0) != 1) { + if (errno == EAGAIN) { + /* + * The problem of EAGAIN (try again + * later) is a major issue on HP-UX. + * Solaris actually tries the recv + * call again, while HP-UX just dies. + * This code is an attempt to let the + * entropy pool fill back up (at least + * that's what I think the problem is.) + * We go to eagain_loop because if we + * just "break", then the "desired" + * amount gets borked. + */ + usleep(1000); + goto eagain_loop; + } + if (errno == EWOULDBLOCK || errno == EINTR) + goto out; + goto err; + } + source->sources.usocket.status = + isc_usocketsource_reading; + sz_to_recv = buf[0]; + source->sources.usocket.sz_to_recv = sz_to_recv; + if (sz_to_recv > sizeof(buf)) + goto err; + /*FALLTHROUGH*/ + + case isc_usocketsource_reading: + if (sz_to_recv != 0U) { + n = recv(fd, buf, sz_to_recv, 0); + if (n < 0) { + if (errno == EWOULDBLOCK || + errno == EINTR) + goto out; + goto err; + } + } else + n = 0; + break; + + default: + goto err; } + if ((size_t)n != sz_to_recv) + source->sources.usocket.sz_to_recv -= n; + else + source->sources.usocket.status = + isc_usocketsource_connected; + + if (n == 0) + goto out; + entropypool_adddata(ent, buf, n, n * 8); added += n * 8; desired -= n; } + goto out; + + err: + close(fd); + source->bad = ISC_TRUE; + source->sources.usocket.status = isc_usocketsource_disconnected; + source->sources.usocket.handle = -1; out: return (added); @@ -167,7 +309,7 @@ fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) { } source = ent->nextsource; again_file: - for (nsource = 0 ; nsource < ent->nsources ; nsource++) { + for (nsource = 0; nsource < ent->nsources; nsource++) { unsigned int got; if (remaining == 0) @@ -175,8 +317,15 @@ fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) { got = 0; - if (source->type == ENTROPY_SOURCETYPE_FILE) + switch ( source->type ) { + case ENTROPY_SOURCETYPE_FILE: got = get_from_filesource(source, remaining); + break; + + case ENTROPY_SOURCETYPE_USOCKET: + got = get_from_usocketsource(source, remaining); + break; + } added += got; @@ -231,9 +380,11 @@ wait_for_sources(isc_entropy_t *ent) { int maxfd, fd; int cc; fd_set reads; + fd_set writes; maxfd = -1; FD_ZERO(&reads); + FD_ZERO(&writes); source = ISC_LIST_HEAD(ent->sources); while (source != NULL) { @@ -244,13 +395,33 @@ wait_for_sources(isc_entropy_t *ent) { FD_SET(fd, &reads); } } + if (source->type == ENTROPY_SOURCETYPE_USOCKET) { + fd = source->sources.usocket.handle; + if (fd >= 0) { + switch (source->sources.usocket.status) { + case isc_usocketsource_disconnected: + break; + case isc_usocketsource_connecting: + case isc_usocketsource_connected: + case isc_usocketsource_ndesired: + maxfd = ISC_MAX(maxfd, fd); + FD_SET(fd, &writes); + break; + case isc_usocketsource_wrote: + case isc_usocketsource_reading: + maxfd = ISC_MAX(maxfd, fd); + FD_SET(fd, &reads); + break; + } + } + } source = ISC_LIST_NEXT(source, link); } if (maxfd < 0) return (-1); - cc = select(maxfd + 1, &reads, NULL, NULL, NULL); + cc = select(maxfd + 1, &reads, &writes, NULL, NULL); if (cc < 0) return (-1); @@ -259,6 +430,11 @@ wait_for_sources(isc_entropy_t *ent) { static void destroyfilesource(isc_entropyfilesource_t *source) { + (void)close(source->handle); +} + +static void +destroyusocketsource(isc_entropyusocketsource_t *source) { close(source->handle); } @@ -290,6 +466,9 @@ make_nonblock(int fd) { isc_result_t isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) { int fd; + struct stat _stat; + isc_boolean_t is_usocket = ISC_FALSE; + isc_boolean_t is_connected = ISC_FALSE; isc_result_t ret; isc_entropysource_t *source; @@ -300,15 +479,64 @@ isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) { source = NULL; - fd = open(fname, O_RDONLY | O_NONBLOCK, 0); + if (stat(fname, &_stat) < 0) { + ret = isc__errno2result(errno); + goto errout; + } + /* + * Solaris 2.5.1 does not have support for sockets (S_IFSOCK), + * but it does return type S_IFIFO (the OS believes that + * the socket is a fifo). This may be an issue if we tell + * the program to look at an actual FIFO as its source of + * entropy. + */ +#if defined(S_ISSOCK) + if (S_ISSOCK(_stat.st_mode)) + is_usocket = ISC_TRUE; +#endif +#if defined(S_ISFIFO) + if (S_ISFIFO(_stat.st_mode)) + is_usocket = ISC_TRUE; +#endif + if (is_usocket) + fd = socket(PF_UNIX, SOCK_STREAM, 0); + else + fd = open(fname, O_RDONLY | O_NONBLOCK, 0); + if (fd < 0) { ret = isc__errno2result(errno); goto errout; } + ret = make_nonblock(fd); if (ret != ISC_R_SUCCESS) goto closefd; + if (is_usocket) { + struct sockaddr_un sname; + + memset(&sname, 0, sizeof(sname)); + sname.sun_family = AF_UNIX; + strncpy(sname.sun_path, fname, sizeof(sname.sun_path)); + sname.sun_path[sizeof(sname.sun_path)-1] = '0'; +#ifdef ISC_PLATFORM_HAVESALEN +#if !defined(SUN_LEN) +#define SUN_LEN(su) \ + (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) +#endif + sname.sun_len = SUN_LEN(&sname); +#endif + + if (connect(fd, (struct sockaddr *) &sname, + sizeof(struct sockaddr_un)) < 0) { + if (errno != EINPROGRESS) { + ret = isc__errno2result(errno); + goto closefd; + } + } else + is_connected = ISC_TRUE; + } + source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t)); if (source == NULL) { ret = ISC_R_NOMEMORY; @@ -319,13 +547,25 @@ isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) { * From here down, no failures can occur. */ source->magic = SOURCE_MAGIC; - source->type = ENTROPY_SOURCETYPE_FILE; source->ent = ent; source->total = 0; source->bad = ISC_FALSE; memset(source->name, 0, sizeof(source->name)); ISC_LINK_INIT(source, link); - source->sources.file.handle = fd; + if (is_usocket) { + source->sources.usocket.handle = fd; + if (is_connected) + source->sources.usocket.status = + isc_usocketsource_connected; + else + source->sources.usocket.status = + isc_usocketsource_connecting; + source->sources.usocket.sz_to_recv = 0; + source->type = ENTROPY_SOURCETYPE_USOCKET; + } else { + source->sources.file.handle = fd; + source->type = ENTROPY_SOURCETYPE_FILE; + } /* * Hook it into the entropy system. @@ -337,7 +577,7 @@ isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) { return (ISC_R_SUCCESS); closefd: - close(fd); + (void)close(fd); errout: if (source != NULL) |
