bind - Removed version tag from contrib directory and updated README.DRAGONFLY.
[dragonfly.git] / contrib / bind / lib / isc / unix / entropy.c
1 /*
2  * Copyright (C) 2004-2007, 2009  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000-2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and/or 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.
8  *
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.
16  */
17
18 /* $Id: entropy.c,v 1.80.128.2 2009/02/16 23:46:44 tbox Exp $ */
19
20 /* \file unix/entropy.c
21  * \brief
22  * This is the system dependent part of the ISC entropy API.
23  */
24
25 #include <config.h>
26
27 #include <sys/param.h>  /* Openserver 5.0.6A and FD_SETSIZE */
28 #include <sys/types.h>
29 #include <sys/time.h>
30 #include <sys/stat.h>
31 #include <sys/socket.h>
32 #include <sys/un.h>
33
34 #ifdef HAVE_NANOSLEEP
35 #include <time.h>
36 #endif
37 #include <unistd.h>
38
39 #include <isc/platform.h>
40 #include <isc/strerror.h>
41
42 #ifdef ISC_PLATFORM_NEEDSYSSELECTH
43 #include <sys/select.h>
44 #endif
45
46 #include "errno2result.h"
47
48 /*%
49  * There is only one variable in the entropy data structures that is not
50  * system independent, but pulling the structure that uses it into this file
51  * ultimately means pulling several other independent structures here also to
52  * resolve their interdependencies.  Thus only the problem variable's type
53  * is defined here.
54  */
55 #define FILESOURCE_HANDLE_TYPE  int
56
57 typedef struct {
58         int     handle;
59         enum    {
60                 isc_usocketsource_disconnected,
61                 isc_usocketsource_connecting,
62                 isc_usocketsource_connected,
63                 isc_usocketsource_ndesired,
64                 isc_usocketsource_wrote,
65                 isc_usocketsource_reading
66         } status;
67         size_t  sz_to_recv;
68 } isc_entropyusocketsource_t;
69
70 #include "../entropy.c"
71
72 static unsigned int
73 get_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) {
74         isc_entropy_t *ent = source->ent;
75         unsigned char buf[128];
76         int fd = source->sources.file.handle;
77         ssize_t n, ndesired;
78         unsigned int added;
79
80         if (source->bad)
81                 return (0);
82
83         desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
84
85         added = 0;
86         while (desired > 0) {
87                 ndesired = ISC_MIN(desired, sizeof(buf));
88                 n = read(fd, buf, ndesired);
89                 if (n < 0) {
90                         if (errno == EAGAIN || errno == EINTR)
91                                 goto out;
92                         goto err;
93                 }
94                 if (n == 0)
95                         goto err;
96
97                 entropypool_adddata(ent, buf, n, n * 8);
98                 added += n * 8;
99                 desired -= n;
100         }
101         goto out;
102
103  err:
104         (void)close(fd);
105         source->sources.file.handle = -1;
106         source->bad = ISC_TRUE;
107
108  out:
109         return (added);
110 }
111
112 static unsigned int
113 get_from_usocketsource(isc_entropysource_t *source, isc_uint32_t desired) {
114         isc_entropy_t *ent = source->ent;
115         unsigned char buf[128];
116         int fd = source->sources.usocket.handle;
117         ssize_t n = 0, ndesired;
118         unsigned int added;
119         size_t sz_to_recv = source->sources.usocket.sz_to_recv;
120
121         if (source->bad)
122                 return (0);
123
124         desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
125
126         added = 0;
127         while (desired > 0) {
128                 ndesired = ISC_MIN(desired, sizeof(buf));
129  eagain_loop:
130
131                 switch ( source->sources.usocket.status ) {
132                 case isc_usocketsource_ndesired:
133                         buf[0] = ndesired;
134                         if ((n = sendto(fd, buf, 1, 0, NULL, 0)) < 0) {
135                                 if (errno == EWOULDBLOCK || errno == EINTR ||
136                                     errno == ECONNRESET)
137                                         goto out;
138                                 goto err;
139                         }
140                         INSIST(n == 1);
141                         source->sources.usocket.status =
142                                                 isc_usocketsource_wrote;
143                         goto eagain_loop;
144
145                 case isc_usocketsource_connecting:
146                 case isc_usocketsource_connected:
147                         buf[0] = 1;
148                         buf[1] = ndesired;
149                         if ((n = sendto(fd, buf, 2, 0, NULL, 0)) < 0) {
150                                 if (errno == EWOULDBLOCK || errno == EINTR ||
151                                     errno == ECONNRESET)
152                                         goto out;
153                                 goto err;
154                         }
155                         if (n == 1) {
156                                 source->sources.usocket.status =
157                                         isc_usocketsource_ndesired;
158                                 goto eagain_loop;
159                         }
160                         INSIST(n == 2);
161                         source->sources.usocket.status =
162                                                 isc_usocketsource_wrote;
163                         /*FALLTHROUGH*/
164
165                 case isc_usocketsource_wrote:
166                         if (recvfrom(fd, buf, 1, 0, NULL, NULL) != 1) {
167                                 if (errno == EAGAIN) {
168                                         /*
169                                          * The problem of EAGAIN (try again
170                                          * later) is a major issue on HP-UX.
171                                          * Solaris actually tries the recvfrom
172                                          * call again, while HP-UX just dies.
173                                          * This code is an attempt to let the
174                                          * entropy pool fill back up (at least
175                                          * that's what I think the problem is.)
176                                          * We go to eagain_loop because if we
177                                          * just "break", then the "desired"
178                                          * amount gets borked.
179                                          */
180 #ifdef HAVE_NANOSLEEP
181                                         struct timespec ts;
182
183                                         ts.tv_sec = 0;
184                                         ts.tv_nsec = 1000000;
185                                         nanosleep(&ts, NULL);
186 #else
187                                         usleep(1000);
188 #endif
189                                         goto eagain_loop;
190                                 }
191                                 if (errno == EWOULDBLOCK || errno == EINTR)
192                                         goto out;
193                                 goto err;
194                         }
195                         source->sources.usocket.status =
196                                         isc_usocketsource_reading;
197                         sz_to_recv = buf[0];
198                         source->sources.usocket.sz_to_recv = sz_to_recv;
199                         if (sz_to_recv > sizeof(buf))
200                                 goto err;
201                         /*FALLTHROUGH*/
202
203                 case isc_usocketsource_reading:
204                         if (sz_to_recv != 0U) {
205                                 n = recv(fd, buf, sz_to_recv, 0);
206                                 if (n < 0) {
207                                         if (errno == EWOULDBLOCK ||
208                                             errno == EINTR)
209                                                 goto out;
210                                         goto err;
211                                 }
212                         } else
213                                 n = 0;
214                         break;
215
216                 default:
217                         goto err;
218                 }
219
220                 if ((size_t)n != sz_to_recv)
221                         source->sources.usocket.sz_to_recv -= n;
222                 else
223                         source->sources.usocket.status =
224                                 isc_usocketsource_connected;
225
226                 if (n == 0)
227                         goto out;
228
229                 entropypool_adddata(ent, buf, n, n * 8);
230                 added += n * 8;
231                 desired -= n;
232         }
233         goto out;
234
235  err:
236         close(fd);
237         source->bad = ISC_TRUE;
238         source->sources.usocket.status = isc_usocketsource_disconnected;
239         source->sources.usocket.handle = -1;
240
241  out:
242         return (added);
243 }
244
245 /*
246  * Poll each source, trying to get data from it to stuff into the entropy
247  * pool.
248  */
249 static void
250 fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
251         unsigned int added;
252         unsigned int remaining;
253         unsigned int needed;
254         unsigned int nsource;
255         isc_entropysource_t *source;
256
257         REQUIRE(VALID_ENTROPY(ent));
258
259         needed = desired;
260
261         /*
262          * This logic is a little strange, so an explanation is in order.
263          *
264          * If needed is 0, it means we are being asked to "fill to whatever
265          * we think is best."  This means that if we have at least a
266          * partially full pool (say, > 1/4th of the pool) we probably don't
267          * need to add anything.
268          *
269          * Also, we will check to see if the "pseudo" count is too high.
270          * If it is, try to mix in better data.  Too high is currently
271          * defined as 1/4th of the pool.
272          *
273          * Next, if we are asked to add a specific bit of entropy, make
274          * certain that we will do so.  Clamp how much we try to add to
275          * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
276          *
277          * Note that if we are in a blocking mode, we will only try to
278          * get as much data as we need, not as much as we might want
279          * to build up.
280          */
281         if (needed == 0) {
282                 REQUIRE(!blocking);
283
284                 if ((ent->pool.entropy >= RND_POOLBITS / 4)
285                     && (ent->pool.pseudo <= RND_POOLBITS / 4))
286                         return;
287
288                 needed = THRESHOLD_BITS * 4;
289         } else {
290                 needed = ISC_MAX(needed, THRESHOLD_BITS);
291                 needed = ISC_MIN(needed, RND_POOLBITS);
292         }
293
294         /*
295          * In any case, clamp how much we need to how much we can add.
296          */
297         needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
298
299         /*
300          * But wait!  If we're not yet initialized, we need at least
301          *      THRESHOLD_BITS
302          * of randomness.
303          */
304         if (ent->initialized < THRESHOLD_BITS)
305                 needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
306
307         /*
308          * Poll each file source to see if we can read anything useful from
309          * it.  XXXMLG When where are multiple sources, we should keep a
310          * record of which one we last used so we can start from it (or the
311          * next one) to avoid letting some sources build up entropy while
312          * others are always drained.
313          */
314
315         added = 0;
316         remaining = needed;
317         if (ent->nextsource == NULL) {
318                 ent->nextsource = ISC_LIST_HEAD(ent->sources);
319                 if (ent->nextsource == NULL)
320                         return;
321         }
322         source = ent->nextsource;
323  again_file:
324         for (nsource = 0; nsource < ent->nsources; nsource++) {
325                 unsigned int got;
326
327                 if (remaining == 0)
328                         break;
329
330                 got = 0;
331
332                 switch ( source->type ) {
333                 case ENTROPY_SOURCETYPE_FILE:
334                         got = get_from_filesource(source, remaining);
335                         break;
336
337                 case ENTROPY_SOURCETYPE_USOCKET:
338                         got = get_from_usocketsource(source, remaining);
339                         break;
340                 }
341
342                 added += got;
343
344                 remaining -= ISC_MIN(remaining, got);
345
346                 source = ISC_LIST_NEXT(source, link);
347                 if (source == NULL)
348                         source = ISC_LIST_HEAD(ent->sources);
349         }
350         ent->nextsource = source;
351
352         if (blocking && remaining != 0) {
353                 int fds;
354
355                 fds = wait_for_sources(ent);
356                 if (fds > 0)
357                         goto again_file;
358         }
359
360         /*
361          * Here, if there are bits remaining to be had and we can block,
362          * check to see if we have a callback source.  If so, call them.
363          */
364         source = ISC_LIST_HEAD(ent->sources);
365         while ((remaining != 0) && (source != NULL)) {
366                 unsigned int got;
367
368                 got = 0;
369
370                 if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
371                         got = get_from_callback(source, remaining, blocking);
372
373                 added += got;
374                 remaining -= ISC_MIN(remaining, got);
375
376                 if (added >= needed)
377                         break;
378
379                 source = ISC_LIST_NEXT(source, link);
380         }
381
382         /*
383          * Mark as initialized if we've added enough data.
384          */
385         if (ent->initialized < THRESHOLD_BITS)
386                 ent->initialized += added;
387 }
388
389 static int
390 wait_for_sources(isc_entropy_t *ent) {
391         isc_entropysource_t *source;
392         int maxfd, fd;
393         int cc;
394         fd_set reads;
395         fd_set writes;
396
397         maxfd = -1;
398         FD_ZERO(&reads);
399         FD_ZERO(&writes);
400
401         source = ISC_LIST_HEAD(ent->sources);
402         while (source != NULL) {
403                 if (source->type == ENTROPY_SOURCETYPE_FILE) {
404                         fd = source->sources.file.handle;
405                         if (fd >= 0) {
406                                 maxfd = ISC_MAX(maxfd, fd);
407                                 FD_SET(fd, &reads);
408                         }
409                 }
410                 if (source->type == ENTROPY_SOURCETYPE_USOCKET) {
411                         fd = source->sources.usocket.handle;
412                         if (fd >= 0) {
413                                 switch (source->sources.usocket.status) {
414                                 case isc_usocketsource_disconnected:
415                                         break;
416                                 case isc_usocketsource_connecting:
417                                 case isc_usocketsource_connected:
418                                 case isc_usocketsource_ndesired:
419                                         maxfd = ISC_MAX(maxfd, fd);
420                                         FD_SET(fd, &writes);
421                                         break;
422                                 case isc_usocketsource_wrote:
423                                 case isc_usocketsource_reading:
424                                         maxfd = ISC_MAX(maxfd, fd);
425                                         FD_SET(fd, &reads);
426                                         break;
427                                 }
428                         }
429                 }
430                 source = ISC_LIST_NEXT(source, link);
431         }
432
433         if (maxfd < 0)
434                 return (-1);
435
436         cc = select(maxfd + 1, &reads, &writes, NULL, NULL);
437         if (cc < 0)
438                 return (-1);
439
440         return (cc);
441 }
442
443 static void
444 destroyfilesource(isc_entropyfilesource_t *source) {
445         (void)close(source->handle);
446 }
447
448 static void
449 destroyusocketsource(isc_entropyusocketsource_t *source) {
450         close(source->handle);
451 }
452
453 /*
454  * Make a fd non-blocking
455  */
456 static isc_result_t
457 make_nonblock(int fd) {
458         int ret;
459         int flags;
460         char strbuf[ISC_STRERRORSIZE];
461 #ifdef USE_FIONBIO_IOCTL
462         int on = 1;
463
464         ret = ioctl(fd, FIONBIO, (char *)&on);
465 #else
466         flags = fcntl(fd, F_GETFL, 0);
467         flags |= PORT_NONBLOCK;
468         ret = fcntl(fd, F_SETFL, flags);
469 #endif
470
471         if (ret == -1) {
472                 isc__strerror(errno, strbuf, sizeof(strbuf));
473                 UNEXPECTED_ERROR(__FILE__, __LINE__,
474 #ifdef USE_FIONBIO_IOCTL
475                                  "ioctl(%d, FIONBIO, &on): %s", fd,
476 #else
477                                  "fcntl(%d, F_SETFL, %d): %s", fd, flags,
478 #endif
479                                  strbuf);
480
481                 return (ISC_R_UNEXPECTED);
482         }
483
484         return (ISC_R_SUCCESS);
485 }
486
487 isc_result_t
488 isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
489         int fd;
490         struct stat _stat;
491         isc_boolean_t is_usocket = ISC_FALSE;
492         isc_boolean_t is_connected = ISC_FALSE;
493         isc_result_t ret;
494         isc_entropysource_t *source;
495
496         REQUIRE(VALID_ENTROPY(ent));
497         REQUIRE(fname != NULL);
498
499         LOCK(&ent->lock);
500
501         if (stat(fname, &_stat) < 0) {
502                 ret = isc__errno2result(errno);
503                 goto errout;
504         }
505         /*
506          * Solaris 2.5.1 does not have support for sockets (S_IFSOCK),
507          * but it does return type S_IFIFO (the OS believes that
508          * the socket is a fifo).  This may be an issue if we tell
509          * the program to look at an actual FIFO as its source of
510          * entropy.
511          */
512 #if defined(S_ISSOCK)
513         if (S_ISSOCK(_stat.st_mode))
514                 is_usocket = ISC_TRUE;
515 #endif
516 #if defined(S_ISFIFO) && defined(sun)
517         if (S_ISFIFO(_stat.st_mode))
518                 is_usocket = ISC_TRUE;
519 #endif
520         if (is_usocket)
521                 fd = socket(PF_UNIX, SOCK_STREAM, 0);
522         else
523                 fd = open(fname, O_RDONLY | PORT_NONBLOCK, 0);
524
525         if (fd < 0) {
526                 ret = isc__errno2result(errno);
527                 goto errout;
528         }
529
530         ret = make_nonblock(fd);
531         if (ret != ISC_R_SUCCESS)
532                 goto closefd;
533
534         if (is_usocket) {
535                 struct sockaddr_un sname;
536
537                 memset(&sname, 0, sizeof(sname));
538                 sname.sun_family = AF_UNIX;
539                 strncpy(sname.sun_path, fname, sizeof(sname.sun_path));
540                 sname.sun_path[sizeof(sname.sun_path)-1] = '0';
541 #ifdef ISC_PLATFORM_HAVESALEN
542 #if !defined(SUN_LEN)
543 #define SUN_LEN(su) \
544         (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
545 #endif
546                 sname.sun_len = SUN_LEN(&sname);
547 #endif
548
549                 if (connect(fd, (struct sockaddr *) &sname,
550                             sizeof(struct sockaddr_un)) < 0) {
551                         if (errno != EINPROGRESS) {
552                                 ret = isc__errno2result(errno);
553                                 goto closefd;
554                         }
555                 } else
556                         is_connected = ISC_TRUE;
557         }
558
559         source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
560         if (source == NULL) {
561                 ret = ISC_R_NOMEMORY;
562                 goto closefd;
563         }
564
565         /*
566          * From here down, no failures can occur.
567          */
568         source->magic = SOURCE_MAGIC;
569         source->ent = ent;
570         source->total = 0;
571         source->bad = ISC_FALSE;
572         memset(source->name, 0, sizeof(source->name));
573         ISC_LINK_INIT(source, link);
574         if (is_usocket) {
575                 source->sources.usocket.handle = fd;
576                 if (is_connected)
577                         source->sources.usocket.status =
578                                         isc_usocketsource_connected;
579                 else
580                         source->sources.usocket.status =
581                                         isc_usocketsource_connecting;
582                 source->sources.usocket.sz_to_recv = 0;
583                 source->type = ENTROPY_SOURCETYPE_USOCKET;
584         } else {
585                 source->sources.file.handle = fd;
586                 source->type = ENTROPY_SOURCETYPE_FILE;
587         }
588
589         /*
590          * Hook it into the entropy system.
591          */
592         ISC_LIST_APPEND(ent->sources, source, link);
593         ent->nsources++;
594
595         UNLOCK(&ent->lock);
596         return (ISC_R_SUCCESS);
597
598  closefd:
599         (void)close(fd);
600
601  errout:
602         UNLOCK(&ent->lock);
603
604         return (ret);
605 }