1290001Sglebius/*
2290001Sglebius * Copyright (C) 2004-2008  Internet Systems Consortium, Inc. ("ISC")
3290001Sglebius * Copyright (C) 2000-2003  Internet Software Consortium.
4290001Sglebius *
5290001Sglebius * Permission to use, copy, modify, and/or distribute this software for any
6290001Sglebius * purpose with or without fee is hereby granted, provided that the above
7290001Sglebius * copyright notice and this permission notice appear in all copies.
8290001Sglebius *
9290001Sglebius * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10290001Sglebius * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11290001Sglebius * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12290001Sglebius * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13290001Sglebius * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14290001Sglebius * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15290001Sglebius * PERFORMANCE OF THIS SOFTWARE.
16290001Sglebius */
17290001Sglebius
18290001Sglebius/* $Id: entropy.c,v 1.82 2008/12/01 23:47:45 tbox Exp $ */
19290001Sglebius
20290001Sglebius/* \file unix/entropy.c
21290001Sglebius * \brief
22290001Sglebius * This is the system dependent part of the ISC entropy API.
23290001Sglebius */
24290001Sglebius
25290001Sglebius#include <config.h>
26290001Sglebius
27290001Sglebius#include <sys/param.h>	/* Openserver 5.0.6A and FD_SETSIZE */
28290001Sglebius#include <sys/types.h>
29290001Sglebius#include <sys/time.h>
30290001Sglebius#include <sys/stat.h>
31290001Sglebius#include <sys/socket.h>
32290001Sglebius#include <sys/un.h>
33290001Sglebius
34290001Sglebius#ifdef HAVE_NANOSLEEP
35290001Sglebius#include <time.h>
36290001Sglebius#endif
37290001Sglebius#include <unistd.h>
38290001Sglebius
39290001Sglebius#include <isc/platform.h>
40290001Sglebius#include <isc/strerror.h>
41290001Sglebius
42290001Sglebius#ifdef ISC_PLATFORM_NEEDSYSSELECTH
43290001Sglebius#include <sys/select.h>
44290001Sglebius#endif
45290001Sglebius
46290001Sglebius#include "errno2result.h"
47290001Sglebius
48290001Sglebius/*%
49290001Sglebius * There is only one variable in the entropy data structures that is not
50290001Sglebius * system independent, but pulling the structure that uses it into this file
51290001Sglebius * ultimately means pulling several other independent structures here also to
52290001Sglebius * resolve their interdependencies.  Thus only the problem variable's type
53290001Sglebius * is defined here.
54290001Sglebius */
55290001Sglebius#define FILESOURCE_HANDLE_TYPE	int
56290001Sglebius
57290001Sglebiustypedef struct {
58290001Sglebius	int	handle;
59290001Sglebius	enum	{
60290001Sglebius		isc_usocketsource_disconnected,
61290001Sglebius		isc_usocketsource_connecting,
62290001Sglebius		isc_usocketsource_connected,
63290001Sglebius		isc_usocketsource_ndesired,
64290001Sglebius		isc_usocketsource_wrote,
65290001Sglebius		isc_usocketsource_reading
66290001Sglebius	} status;
67290001Sglebius	size_t	sz_to_recv;
68290001Sglebius} isc_entropyusocketsource_t;
69290001Sglebius
70290001Sglebius#include "../entropy.c"
71290001Sglebius
72290001Sglebiusstatic unsigned int
73290001Sglebiusget_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) {
74290001Sglebius	isc_entropy_t *ent = source->ent;
75290001Sglebius	unsigned char buf[128];
76290001Sglebius	int fd = source->sources.file.handle;
77290001Sglebius	ssize_t n, ndesired;
78290001Sglebius	unsigned int added;
79290001Sglebius
80290001Sglebius	if (source->bad)
81290001Sglebius		return (0);
82290001Sglebius
83290001Sglebius	desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
84290001Sglebius
85290001Sglebius	added = 0;
86290001Sglebius	while (desired > 0) {
87290001Sglebius		ndesired = ISC_MIN(desired, sizeof(buf));
88290001Sglebius		n = read(fd, buf, ndesired);
89290001Sglebius		if (n < 0) {
90290001Sglebius			if (errno == EAGAIN || errno == EINTR)
91290001Sglebius				goto out;
92290001Sglebius			goto err;
93290001Sglebius		}
94290001Sglebius		if (n == 0)
95290001Sglebius			goto err;
96290001Sglebius
97290001Sglebius		entropypool_adddata(ent, buf, n, n * 8);
98290001Sglebius		added += n * 8;
99290001Sglebius		desired -= n;
100290001Sglebius	}
101290001Sglebius	goto out;
102290001Sglebius
103290001Sglebius err:
104290001Sglebius	(void)close(fd);
105290001Sglebius	source->sources.file.handle = -1;
106290001Sglebius	source->bad = ISC_TRUE;
107290001Sglebius
108290001Sglebius out:
109290001Sglebius	return (added);
110290001Sglebius}
111290001Sglebius
112290001Sglebiusstatic unsigned int
113290001Sglebiusget_from_usocketsource(isc_entropysource_t *source, isc_uint32_t desired) {
114290001Sglebius	isc_entropy_t *ent = source->ent;
115290001Sglebius	unsigned char buf[128];
116290001Sglebius	int fd = source->sources.usocket.handle;
117290001Sglebius	ssize_t n = 0, ndesired;
118290001Sglebius	unsigned int added;
119290001Sglebius	size_t sz_to_recv = source->sources.usocket.sz_to_recv;
120290001Sglebius
121290001Sglebius	if (source->bad)
122290001Sglebius		return (0);
123290001Sglebius
124290001Sglebius	desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
125290001Sglebius
126290001Sglebius	added = 0;
127290001Sglebius	while (desired > 0) {
128290001Sglebius		ndesired = ISC_MIN(desired, sizeof(buf));
129290001Sglebius eagain_loop:
130290001Sglebius
131290001Sglebius		switch ( source->sources.usocket.status ) {
132290001Sglebius		case isc_usocketsource_ndesired:
133290001Sglebius			buf[0] = ndesired;
134290001Sglebius			if ((n = sendto(fd, buf, 1, 0, NULL, 0)) < 0) {
135290001Sglebius				if (errno == EWOULDBLOCK || errno == EINTR ||
136290001Sglebius				    errno == ECONNRESET)
137290001Sglebius					goto out;
138290001Sglebius				goto err;
139290001Sglebius			}
140290001Sglebius			INSIST(n == 1);
141290001Sglebius			source->sources.usocket.status =
142290001Sglebius						isc_usocketsource_wrote;
143290001Sglebius			goto eagain_loop;
144290001Sglebius
145290001Sglebius		case isc_usocketsource_connecting:
146290001Sglebius		case isc_usocketsource_connected:
147290001Sglebius			buf[0] = 1;
148290001Sglebius			buf[1] = ndesired;
149290001Sglebius			if ((n = sendto(fd, buf, 2, 0, NULL, 0)) < 0) {
150290001Sglebius				if (errno == EWOULDBLOCK || errno == EINTR ||
151290001Sglebius				    errno == ECONNRESET)
152290001Sglebius					goto out;
153290001Sglebius				goto err;
154290001Sglebius			}
155290001Sglebius			if (n == 1) {
156290001Sglebius				source->sources.usocket.status =
157290001Sglebius					isc_usocketsource_ndesired;
158290001Sglebius				goto eagain_loop;
159290001Sglebius			}
160290001Sglebius			INSIST(n == 2);
161290001Sglebius			source->sources.usocket.status =
162290001Sglebius						isc_usocketsource_wrote;
163290001Sglebius			/*FALLTHROUGH*/
164290001Sglebius
165290001Sglebius		case isc_usocketsource_wrote:
166290001Sglebius			if (recvfrom(fd, buf, 1, 0, NULL, NULL) != 1) {
167290001Sglebius				if (errno == EAGAIN) {
168290001Sglebius					/*
169290001Sglebius					 * The problem of EAGAIN (try again
170290001Sglebius					 * later) is a major issue on HP-UX.
171290001Sglebius					 * Solaris actually tries the recvfrom
172290001Sglebius					 * call again, while HP-UX just dies.
173290001Sglebius					 * This code is an attempt to let the
174290001Sglebius					 * entropy pool fill back up (at least
175290001Sglebius					 * that's what I think the problem is.)
176290001Sglebius					 * We go to eagain_loop because if we
177290001Sglebius					 * just "break", then the "desired"
178290001Sglebius					 * amount gets borked.
179290001Sglebius					 */
180290001Sglebius#ifdef HAVE_NANOSLEEP
181290001Sglebius					struct timespec ts;
182290001Sglebius
183290001Sglebius					ts.tv_sec = 0;
184290001Sglebius					ts.tv_nsec = 1000000;
185290001Sglebius					nanosleep(&ts, NULL);
186290001Sglebius#else
187290001Sglebius					usleep(1000);
188290001Sglebius#endif
189290001Sglebius					goto eagain_loop;
190290001Sglebius				}
191290001Sglebius				if (errno == EWOULDBLOCK || errno == EINTR)
192290001Sglebius					goto out;
193290001Sglebius				goto err;
194290001Sglebius			}
195290001Sglebius			source->sources.usocket.status =
196290001Sglebius					isc_usocketsource_reading;
197290001Sglebius			sz_to_recv = buf[0];
198290001Sglebius			source->sources.usocket.sz_to_recv = sz_to_recv;
199290001Sglebius			if (sz_to_recv > sizeof(buf))
200290001Sglebius				goto err;
201290001Sglebius			/*FALLTHROUGH*/
202290001Sglebius
203290001Sglebius		case isc_usocketsource_reading:
204290001Sglebius			if (sz_to_recv != 0U) {
205290001Sglebius				n = recv(fd, buf, sz_to_recv, 0);
206290001Sglebius				if (n < 0) {
207290001Sglebius					if (errno == EWOULDBLOCK ||
208290001Sglebius					    errno == EINTR)
209290001Sglebius						goto out;
210290001Sglebius					goto err;
211290001Sglebius				}
212290001Sglebius			} else
213290001Sglebius				n = 0;
214290001Sglebius			break;
215290001Sglebius
216290001Sglebius		default:
217290001Sglebius			goto err;
218290001Sglebius		}
219290001Sglebius
220290001Sglebius		if ((size_t)n != sz_to_recv)
221290001Sglebius			source->sources.usocket.sz_to_recv -= n;
222290001Sglebius		else
223290001Sglebius			source->sources.usocket.status =
224290001Sglebius				isc_usocketsource_connected;
225290001Sglebius
226290001Sglebius		if (n == 0)
227290001Sglebius			goto out;
228290001Sglebius
229290001Sglebius		entropypool_adddata(ent, buf, n, n * 8);
230290001Sglebius		added += n * 8;
231290001Sglebius		desired -= n;
232290001Sglebius	}
233290001Sglebius	goto out;
234290001Sglebius
235290001Sglebius err:
236290001Sglebius	close(fd);
237290001Sglebius	source->bad = ISC_TRUE;
238290001Sglebius	source->sources.usocket.status = isc_usocketsource_disconnected;
239290001Sglebius	source->sources.usocket.handle = -1;
240290001Sglebius
241290001Sglebius out:
242290001Sglebius	return (added);
243290001Sglebius}
244290001Sglebius
245290001Sglebius/*
246290001Sglebius * Poll each source, trying to get data from it to stuff into the entropy
247290001Sglebius * pool.
248290001Sglebius */
249290001Sglebiusstatic void
250290001Sglebiusfillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
251290001Sglebius	unsigned int added;
252290001Sglebius	unsigned int remaining;
253290001Sglebius	unsigned int needed;
254290001Sglebius	unsigned int nsource;
255290001Sglebius	isc_entropysource_t *source;
256290001Sglebius
257290001Sglebius	REQUIRE(VALID_ENTROPY(ent));
258290001Sglebius
259290001Sglebius	needed = desired;
260290001Sglebius
261290001Sglebius	/*
262290001Sglebius	 * This logic is a little strange, so an explanation is in order.
263290001Sglebius	 *
264290001Sglebius	 * If needed is 0, it means we are being asked to "fill to whatever
265290001Sglebius	 * we think is best."  This means that if we have at least a
266290001Sglebius	 * partially full pool (say, > 1/4th of the pool) we probably don't
267290001Sglebius	 * need to add anything.
268290001Sglebius	 *
269290001Sglebius	 * Also, we will check to see if the "pseudo" count is too high.
270290001Sglebius	 * If it is, try to mix in better data.  Too high is currently
271290001Sglebius	 * defined as 1/4th of the pool.
272290001Sglebius	 *
273290001Sglebius	 * Next, if we are asked to add a specific bit of entropy, make
274290001Sglebius	 * certain that we will do so.  Clamp how much we try to add to
275290001Sglebius	 * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
276290001Sglebius	 *
277290001Sglebius	 * Note that if we are in a blocking mode, we will only try to
278290001Sglebius	 * get as much data as we need, not as much as we might want
279290001Sglebius	 * to build up.
280290001Sglebius	 */
281290001Sglebius	if (needed == 0) {
282290001Sglebius		REQUIRE(!blocking);
283290001Sglebius
284290001Sglebius		if ((ent->pool.entropy >= RND_POOLBITS / 4)
285290001Sglebius		    && (ent->pool.pseudo <= RND_POOLBITS / 4))
286290001Sglebius			return;
287290001Sglebius
288290001Sglebius		needed = THRESHOLD_BITS * 4;
289290001Sglebius	} else {
290290001Sglebius		needed = ISC_MAX(needed, THRESHOLD_BITS);
291290001Sglebius		needed = ISC_MIN(needed, RND_POOLBITS);
292290001Sglebius	}
293290001Sglebius
294290001Sglebius	/*
295290001Sglebius	 * In any case, clamp how much we need to how much we can add.
296290001Sglebius	 */
297290001Sglebius	needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
298290001Sglebius
299290001Sglebius	/*
300290001Sglebius	 * But wait!  If we're not yet initialized, we need at least
301290001Sglebius	 *	THRESHOLD_BITS
302290001Sglebius	 * of randomness.
303290001Sglebius	 */
304290001Sglebius	if (ent->initialized < THRESHOLD_BITS)
305290001Sglebius		needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
306290001Sglebius
307290001Sglebius	/*
308290001Sglebius	 * Poll each file source to see if we can read anything useful from
309290001Sglebius	 * it.  XXXMLG When where are multiple sources, we should keep a
310290001Sglebius	 * record of which one we last used so we can start from it (or the
311290001Sglebius	 * next one) to avoid letting some sources build up entropy while
312290001Sglebius	 * others are always drained.
313290001Sglebius	 */
314290001Sglebius
315290001Sglebius	added = 0;
316290001Sglebius	remaining = needed;
317290001Sglebius	if (ent->nextsource == NULL) {
318290001Sglebius		ent->nextsource = ISC_LIST_HEAD(ent->sources);
319290001Sglebius		if (ent->nextsource == NULL)
320290001Sglebius			return;
321290001Sglebius	}
322290001Sglebius	source = ent->nextsource;
323290001Sglebius again_file:
324290001Sglebius	for (nsource = 0; nsource < ent->nsources; nsource++) {
325290001Sglebius		unsigned int got;
326290001Sglebius
327290001Sglebius		if (remaining == 0)
328290001Sglebius			break;
329290001Sglebius
330290001Sglebius		got = 0;
331290001Sglebius
332290001Sglebius		switch ( source->type ) {
333290001Sglebius		case ENTROPY_SOURCETYPE_FILE:
334290001Sglebius			got = get_from_filesource(source, remaining);
335290001Sglebius			break;
336290001Sglebius
337290001Sglebius		case ENTROPY_SOURCETYPE_USOCKET:
338290001Sglebius			got = get_from_usocketsource(source, remaining);
339290001Sglebius			break;
340290001Sglebius		}
341290001Sglebius
342290001Sglebius		added += got;
343290001Sglebius
344290001Sglebius		remaining -= ISC_MIN(remaining, got);
345290001Sglebius
346290001Sglebius		source = ISC_LIST_NEXT(source, link);
347290001Sglebius		if (source == NULL)
348290001Sglebius			source = ISC_LIST_HEAD(ent->sources);
349290001Sglebius	}
350290001Sglebius	ent->nextsource = source;
351290001Sglebius
352290001Sglebius	if (blocking && remaining != 0) {
353290001Sglebius		int fds;
354290001Sglebius
355290001Sglebius		fds = wait_for_sources(ent);
356290001Sglebius		if (fds > 0)
357290001Sglebius			goto again_file;
358290001Sglebius	}
359290001Sglebius
360290001Sglebius	/*
361290001Sglebius	 * Here, if there are bits remaining to be had and we can block,
362290001Sglebius	 * check to see if we have a callback source.  If so, call them.
363290001Sglebius	 */
364290001Sglebius	source = ISC_LIST_HEAD(ent->sources);
365290001Sglebius	while ((remaining != 0) && (source != NULL)) {
366290001Sglebius		unsigned int got;
367290001Sglebius
368290001Sglebius		got = 0;
369290001Sglebius
370290001Sglebius		if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
371290001Sglebius			got = get_from_callback(source, remaining, blocking);
372290001Sglebius
373290001Sglebius		added += got;
374290001Sglebius		remaining -= ISC_MIN(remaining, got);
375290001Sglebius
376290001Sglebius		if (added >= needed)
377290001Sglebius			break;
378290001Sglebius
379290001Sglebius		source = ISC_LIST_NEXT(source, link);
380290001Sglebius	}
381290001Sglebius
382290001Sglebius	/*
383290001Sglebius	 * Mark as initialized if we've added enough data.
384290001Sglebius	 */
385290001Sglebius	if (ent->initialized < THRESHOLD_BITS)
386290001Sglebius		ent->initialized += added;
387290001Sglebius}
388290001Sglebius
389290001Sglebiusstatic int
390290001Sglebiuswait_for_sources(isc_entropy_t *ent) {
391290001Sglebius	isc_entropysource_t *source;
392290001Sglebius	int maxfd, fd;
393290001Sglebius	int cc;
394290001Sglebius	fd_set reads;
395290001Sglebius	fd_set writes;
396290001Sglebius
397290001Sglebius	maxfd = -1;
398290001Sglebius	FD_ZERO(&reads);
399290001Sglebius	FD_ZERO(&writes);
400290001Sglebius
401290001Sglebius	source = ISC_LIST_HEAD(ent->sources);
402290001Sglebius	while (source != NULL) {
403290001Sglebius		if (source->type == ENTROPY_SOURCETYPE_FILE) {
404290001Sglebius			fd = source->sources.file.handle;
405290001Sglebius			if (fd >= 0) {
406290001Sglebius				maxfd = ISC_MAX(maxfd, fd);
407290001Sglebius				FD_SET(fd, &reads);
408290001Sglebius			}
409290001Sglebius		}
410290001Sglebius		if (source->type == ENTROPY_SOURCETYPE_USOCKET) {
411290001Sglebius			fd = source->sources.usocket.handle;
412290001Sglebius			if (fd >= 0) {
413290001Sglebius				switch (source->sources.usocket.status) {
414290001Sglebius				case isc_usocketsource_disconnected:
415290001Sglebius					break;
416290001Sglebius				case isc_usocketsource_connecting:
417290001Sglebius				case isc_usocketsource_connected:
418290001Sglebius				case isc_usocketsource_ndesired:
419290001Sglebius					maxfd = ISC_MAX(maxfd, fd);
420290001Sglebius					FD_SET(fd, &writes);
421290001Sglebius					break;
422290001Sglebius				case isc_usocketsource_wrote:
423290001Sglebius				case isc_usocketsource_reading:
424290001Sglebius					maxfd = ISC_MAX(maxfd, fd);
425290001Sglebius					FD_SET(fd, &reads);
426290001Sglebius					break;
427290001Sglebius				}
428290001Sglebius			}
429290001Sglebius		}
430290001Sglebius		source = ISC_LIST_NEXT(source, link);
431290001Sglebius	}
432290001Sglebius
433290001Sglebius	if (maxfd < 0)
434290001Sglebius		return (-1);
435290001Sglebius
436290001Sglebius	cc = select(maxfd + 1, &reads, &writes, NULL, NULL);
437290001Sglebius	if (cc < 0)
438290001Sglebius		return (-1);
439290001Sglebius
440290001Sglebius	return (cc);
441290001Sglebius}
442290001Sglebius
443290001Sglebiusstatic void
444290001Sglebiusdestroyfilesource(isc_entropyfilesource_t *source) {
445290001Sglebius	(void)close(source->handle);
446290001Sglebius}
447290001Sglebius
448290001Sglebiusstatic void
449290001Sglebiusdestroyusocketsource(isc_entropyusocketsource_t *source) {
450290001Sglebius	close(source->handle);
451290001Sglebius}
452290001Sglebius
453290001Sglebius/*
454290001Sglebius * Make a fd non-blocking
455290001Sglebius */
456290001Sglebiusstatic isc_result_t
457290001Sglebiusmake_nonblock(int fd) {
458290001Sglebius	int ret;
459290001Sglebius	int flags;
460290001Sglebius	char strbuf[ISC_STRERRORSIZE];
461290001Sglebius#ifdef USE_FIONBIO_IOCTL
462290001Sglebius	int on = 1;
463290001Sglebius
464290001Sglebius	ret = ioctl(fd, FIONBIO, (char *)&on);
465290001Sglebius#else
466290001Sglebius	flags = fcntl(fd, F_GETFL, 0);
467290001Sglebius	flags |= PORT_NONBLOCK;
468290001Sglebius	ret = fcntl(fd, F_SETFL, flags);
469290001Sglebius#endif
470290001Sglebius
471290001Sglebius	if (ret == -1) {
472290001Sglebius		isc__strerror(errno, strbuf, sizeof(strbuf));
473290001Sglebius		UNEXPECTED_ERROR(__FILE__, __LINE__,
474290001Sglebius#ifdef USE_FIONBIO_IOCTL
475290001Sglebius				 "ioctl(%d, FIONBIO, &on): %s", fd,
476290001Sglebius#else
477290001Sglebius				 "fcntl(%d, F_SETFL, %d): %s", fd, flags,
478290001Sglebius#endif
479290001Sglebius				 strbuf);
480290001Sglebius
481290001Sglebius		return (ISC_R_UNEXPECTED);
482290001Sglebius	}
483290001Sglebius
484290001Sglebius	return (ISC_R_SUCCESS);
485290001Sglebius}
486290001Sglebius
487290001Sglebiusisc_result_t
488290001Sglebiusisc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
489290001Sglebius	int fd;
490290001Sglebius	struct stat _stat;
491290001Sglebius	isc_boolean_t is_usocket = ISC_FALSE;
492290001Sglebius	isc_boolean_t is_connected = ISC_FALSE;
493290001Sglebius	isc_result_t ret;
494290001Sglebius	isc_entropysource_t *source;
495290001Sglebius
496290001Sglebius	REQUIRE(VALID_ENTROPY(ent));
497290001Sglebius	REQUIRE(fname != NULL);
498290001Sglebius
499290001Sglebius	LOCK(&ent->lock);
500290001Sglebius
501290001Sglebius	if (stat(fname, &_stat) < 0) {
502290001Sglebius		ret = isc__errno2result(errno);
503290001Sglebius		goto errout;
504290001Sglebius	}
505290001Sglebius	/*
506290001Sglebius	 * Solaris 2.5.1 does not have support for sockets (S_IFSOCK),
507290001Sglebius	 * but it does return type S_IFIFO (the OS believes that
508290001Sglebius	 * the socket is a fifo).  This may be an issue if we tell
509290001Sglebius	 * the program to look at an actual FIFO as its source of
510290001Sglebius	 * entropy.
511290001Sglebius	 */
512290001Sglebius#if defined(S_ISSOCK)
513290001Sglebius	if (S_ISSOCK(_stat.st_mode))
514290001Sglebius		is_usocket = ISC_TRUE;
515290001Sglebius#endif
516290001Sglebius#if defined(S_ISFIFO) && defined(sun)
517290001Sglebius	if (S_ISFIFO(_stat.st_mode))
518290001Sglebius		is_usocket = ISC_TRUE;
519290001Sglebius#endif
520290001Sglebius	if (is_usocket)
521290001Sglebius		fd = socket(PF_UNIX, SOCK_STREAM, 0);
522290001Sglebius	else
523290001Sglebius		fd = open(fname, O_RDONLY | PORT_NONBLOCK, 0);
524290001Sglebius
525290001Sglebius	if (fd < 0) {
526290001Sglebius		ret = isc__errno2result(errno);
527290001Sglebius		goto errout;
528290001Sglebius	}
529290001Sglebius
530290001Sglebius	ret = make_nonblock(fd);
531290001Sglebius	if (ret != ISC_R_SUCCESS)
532290001Sglebius		goto closefd;
533290001Sglebius
534290001Sglebius	if (is_usocket) {
535290001Sglebius		struct sockaddr_un sname;
536290001Sglebius
537290001Sglebius		memset(&sname, 0, sizeof(sname));
538290001Sglebius		sname.sun_family = AF_UNIX;
539290001Sglebius		strncpy(sname.sun_path, fname, sizeof(sname.sun_path));
540290001Sglebius		sname.sun_path[sizeof(sname.sun_path)-1] = '0';
541290001Sglebius#ifdef ISC_PLATFORM_HAVESALEN
542290001Sglebius#if !defined(SUN_LEN)
543290001Sglebius#define SUN_LEN(su) \
544290001Sglebius	(sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
545290001Sglebius#endif
546290001Sglebius		sname.sun_len = SUN_LEN(&sname);
547290001Sglebius#endif
548290001Sglebius
549290001Sglebius		if (connect(fd, (struct sockaddr *) &sname,
550290001Sglebius			    sizeof(struct sockaddr_un)) < 0) {
551290001Sglebius			if (errno != EINPROGRESS) {
552290001Sglebius				ret = isc__errno2result(errno);
553290001Sglebius				goto closefd;
554290001Sglebius			}
555290001Sglebius		} else
556290001Sglebius			is_connected = ISC_TRUE;
557290001Sglebius	}
558290001Sglebius
559290001Sglebius	source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
560290001Sglebius	if (source == NULL) {
561290001Sglebius		ret = ISC_R_NOMEMORY;
562290001Sglebius		goto closefd;
563290001Sglebius	}
564290001Sglebius
565290001Sglebius	/*
566290001Sglebius	 * From here down, no failures can occur.
567290001Sglebius	 */
568290001Sglebius	source->magic = SOURCE_MAGIC;
569290001Sglebius	source->ent = ent;
570290001Sglebius	source->total = 0;
571290001Sglebius	source->bad = ISC_FALSE;
572290001Sglebius	memset(source->name, 0, sizeof(source->name));
573290001Sglebius	ISC_LINK_INIT(source, link);
574290001Sglebius	if (is_usocket) {
575290001Sglebius		source->sources.usocket.handle = fd;
576290001Sglebius		if (is_connected)
577290001Sglebius			source->sources.usocket.status =
578290001Sglebius					isc_usocketsource_connected;
579290001Sglebius		else
580290001Sglebius			source->sources.usocket.status =
581290001Sglebius					isc_usocketsource_connecting;
582290001Sglebius		source->sources.usocket.sz_to_recv = 0;
583290001Sglebius		source->type = ENTROPY_SOURCETYPE_USOCKET;
584290001Sglebius	} else {
585290001Sglebius		source->sources.file.handle = fd;
586290001Sglebius		source->type = ENTROPY_SOURCETYPE_FILE;
587290001Sglebius	}
588290001Sglebius
589290001Sglebius	/*
590290001Sglebius	 * Hook it into the entropy system.
591290001Sglebius	 */
592290001Sglebius	ISC_LIST_APPEND(ent->sources, source, link);
593290001Sglebius	ent->nsources++;
594290001Sglebius
595290001Sglebius	UNLOCK(&ent->lock);
596290001Sglebius	return (ISC_R_SUCCESS);
597290001Sglebius
598290001Sglebius closefd:
599290001Sglebius	(void)close(fd);
600290001Sglebius
601290001Sglebius errout:
602290001Sglebius	UNLOCK(&ent->lock);
603290001Sglebius
604290001Sglebius	return (ret);
605290001Sglebius}
606