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