* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: entropy.c,v 1.65 2002/05/10 06:41:55 marka Exp $ */
+/* $Id: entropy.c,v 1.66 2002/05/30 04:21:15 marka Exp $ */
/*
* This is the system depenedent part of the ISC entropy API.
#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>
*/
#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
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 != 0) {
+ 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);
+}
+
/*
* Poll each source, trying to get data from it to stuff into the entropy
* pool.
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;
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) {
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);
(void)close(source->handle);
}
+static void
+destroyusocketsource(isc_entropyusocketsource_t *source) {
+ close(source->handle);
+}
+
/*
* Make a fd non-blocking
*/
isc_result_t
isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
int fd;
+ struct stat _stat;
+ isc_boolean_t is_usocket;
+ isc_boolean_t is_connected = ISC_FALSE;
isc_result_t ret;
isc_entropysource_t *source;
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 ((_stat.st_mode & S_IFSOCK) != 0
+#ifdef _SOCKET_IS_FIFO
+ || (_stat.st_mode & S_IFIFO) != 0
+#endif
+ ) {
+ fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ is_usocket = ISC_TRUE;
+ } else {
+ fd = open(fname, O_RDONLY | O_NONBLOCK, 0);
+ is_usocket = ISC_FALSE;
+ }
+
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;
* 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.