Add BIND 9.2.4rc7.
[dragonfly.git] / contrib / bind-9.2.4rc7 / lib / isc / unix / entropy.c
1 /*
2  * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000-2002  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.4 2004/03/09 06:12:09 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
30 #include <unistd.h>
31
32 #include <isc/platform.h>
33 #include <isc/strerror.h>
34
35 #ifdef ISC_PLATFORM_NEEDSYSSELECTH
36 #include <sys/select.h>
37 #endif
38
39 #include "errno2result.h"
40
41 /*
42  * There is only one variable in the entropy data structures that is not
43  * system independent, but pulling the structure that uses it into this file
44  * ultimately means pulling several other independent structures here also to
45  * resolve their interdependencies.  Thus only the problem variable's type
46  * is defined here.
47  */
48 #define FILESOURCE_HANDLE_TYPE  int
49
50 #include "../entropy.c"
51
52 static unsigned int
53 get_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) {
54         isc_entropy_t *ent = source->ent;
55         unsigned char buf[128];
56         int fd = source->sources.file.handle;
57         ssize_t n, ndesired;
58         unsigned int added;
59
60         if (source->bad)
61                 return (0);
62
63         desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
64
65         added = 0;
66         while (desired > 0) {
67                 ndesired = ISC_MIN(desired, sizeof(buf));
68                 n = read(fd, buf, ndesired);
69                 if (n < 0) {
70                         if (errno == EAGAIN || errno == EINTR)
71                                 goto out;
72                         close(fd);
73                         source->bad = ISC_TRUE;
74                         goto out;
75                 }
76                 if (n == 0) {
77                         close(fd);
78                         source->bad = ISC_TRUE;
79                         goto out;
80                 }
81
82                 entropypool_adddata(ent, buf, n, n * 8);
83                 added += n * 8;
84                 desired -= n;
85         }
86
87  out:
88         return (added);
89 }
90
91 /*
92  * Poll each source, trying to get data from it to stuff into the entropy
93  * pool.
94  */
95 static void
96 fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
97         unsigned int added;
98         unsigned int remaining;
99         unsigned int needed;
100         unsigned int nsource;
101         isc_entropysource_t *source;
102
103         REQUIRE(VALID_ENTROPY(ent));
104
105         needed = desired;
106
107         /*
108          * This logic is a little strange, so an explanation is in order.
109          *
110          * If needed is 0, it means we are being asked to "fill to whatever
111          * we think is best."  This means that if we have at least a
112          * partially full pool (say, > 1/4th of the pool) we probably don't
113          * need to add anything.
114          *
115          * Also, we will check to see if the "pseudo" count is too high.
116          * If it is, try to mix in better data.  Too high is currently
117          * defined as 1/4th of the pool.
118          *
119          * Next, if we are asked to add a specific bit of entropy, make
120          * certain that we will do so.  Clamp how much we try to add to
121          * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy).
122          *
123          * Note that if we are in a blocking mode, we will only try to
124          * get as much data as we need, not as much as we might want
125          * to build up.
126          */
127         if (needed == 0) {
128                 REQUIRE(!blocking);
129
130                 if ((ent->pool.entropy >= RND_POOLBITS / 4)
131                     && (ent->pool.pseudo <= RND_POOLBITS / 4))
132                         return;
133
134                 needed = THRESHOLD_BITS * 4;
135         } else {
136                 needed = ISC_MAX(needed, THRESHOLD_BITS);
137                 needed = ISC_MIN(needed, RND_POOLBITS);
138         }
139
140         /*
141          * In any case, clamp how much we need to how much we can add.
142          */
143         needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
144
145         /*
146          * But wait!  If we're not yet initialized, we need at least
147          *      THRESHOLD_BITS
148          * of randomness.
149          */
150         if (ent->initialized < THRESHOLD_BITS)
151                 needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
152
153         /*
154          * Poll each file source to see if we can read anything useful from
155          * it.  XXXMLG When where are multiple sources, we should keep a
156          * record of which one we last used so we can start from it (or the
157          * next one) to avoid letting some sources build up entropy while
158          * others are always drained.
159          */
160
161         added = 0;
162         remaining = needed;
163         if (ent->nextsource == NULL) {
164                 ent->nextsource = ISC_LIST_HEAD(ent->sources);
165                 if (ent->nextsource == NULL)
166                         return;
167         }
168         source = ent->nextsource;
169  again_file:
170         for (nsource = 0 ; nsource < ent->nsources ; nsource++) {
171                 unsigned int got;
172
173                 if (remaining == 0)
174                         break;
175
176                 got = 0;
177
178                 if (source->type == ENTROPY_SOURCETYPE_FILE)
179                         got = get_from_filesource(source, remaining);
180
181                 added += got;
182
183                 remaining -= ISC_MIN(remaining, got);
184
185                 source = ISC_LIST_NEXT(source, link);
186                 if (source == NULL)
187                         source = ISC_LIST_HEAD(ent->sources);
188         }
189         ent->nextsource = source;
190
191         if (blocking && remaining != 0) {
192                 int fds;
193
194                 fds = wait_for_sources(ent);
195                 if (fds > 0)
196                         goto again_file;
197         }
198
199         /*
200          * Here, if there are bits remaining to be had and we can block,
201          * check to see if we have a callback source.  If so, call them.
202          */
203         source = ISC_LIST_HEAD(ent->sources);
204         while ((remaining != 0) && (source != NULL)) {
205                 unsigned int got;
206
207                 got = 0;
208
209                 if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
210                         got = get_from_callback(source, remaining, blocking);
211
212                 added += got;
213                 remaining -= ISC_MIN(remaining, got);
214
215                 if (added >= needed)
216                         break;
217
218                 source = ISC_LIST_NEXT(source, link);
219         }
220
221         /*
222          * Mark as initialized if we've added enough data.
223          */
224         if (ent->initialized < THRESHOLD_BITS)
225                 ent->initialized += added;
226 }
227
228 static int
229 wait_for_sources(isc_entropy_t *ent) {
230         isc_entropysource_t *source;
231         int maxfd, fd;
232         int cc;
233         fd_set reads;
234
235         maxfd = -1;
236         FD_ZERO(&reads);
237
238         source = ISC_LIST_HEAD(ent->sources);
239         while (source != NULL) {
240                 if (source->type == ENTROPY_SOURCETYPE_FILE) {
241                         fd = source->sources.file.handle;
242                         if (fd >= 0) {
243                                 maxfd = ISC_MAX(maxfd, fd);
244                                 FD_SET(fd, &reads);
245                         }
246                 }
247                 source = ISC_LIST_NEXT(source, link);
248         }
249
250         if (maxfd < 0)
251                 return (-1);
252
253         cc = select(maxfd + 1, &reads, NULL, NULL, NULL);
254         if (cc < 0)
255                 return (-1);
256
257         return (cc);
258 }
259
260 static void
261 destroyfilesource(isc_entropyfilesource_t *source) {
262         close(source->handle);
263 }
264
265 /*
266  * Make a fd non-blocking
267  */
268 static isc_result_t
269 make_nonblock(int fd) {
270         int ret;
271         int flags;
272         char strbuf[ISC_STRERRORSIZE];
273
274         flags = fcntl(fd, F_GETFL, 0);
275         flags |= O_NONBLOCK;
276         ret = fcntl(fd, F_SETFL, flags);
277
278         if (ret == -1) {
279                 isc__strerror(errno, strbuf, sizeof(strbuf));
280                 UNEXPECTED_ERROR(__FILE__, __LINE__,
281                                  "fcntl(%d, F_SETFL, %d): %s",
282                                  fd, flags, strbuf);
283
284                 return (ISC_R_UNEXPECTED);
285         }
286
287         return (ISC_R_SUCCESS);
288 }
289
290 isc_result_t
291 isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
292         int fd;
293         isc_result_t ret;
294         isc_entropysource_t *source;
295
296         REQUIRE(VALID_ENTROPY(ent));
297         REQUIRE(fname != NULL);
298
299         LOCK(&ent->lock);
300
301         source = NULL;
302
303         fd = open(fname, O_RDONLY | O_NONBLOCK, 0);
304         if (fd < 0) {
305                 ret = isc__errno2result(errno);
306                 goto errout;
307         }
308         ret = make_nonblock(fd);
309         if (ret != ISC_R_SUCCESS)
310                 goto closefd;
311
312         source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
313         if (source == NULL) {
314                 ret = ISC_R_NOMEMORY;
315                 goto closefd;
316         }
317
318         /*
319          * From here down, no failures can occur.
320          */
321         source->magic = SOURCE_MAGIC;
322         source->type = ENTROPY_SOURCETYPE_FILE;
323         source->ent = ent;
324         source->total = 0;
325         source->bad = ISC_FALSE;
326         memset(source->name, 0, sizeof(source->name));
327         ISC_LINK_INIT(source, link);
328         source->sources.file.handle = fd;
329
330         /*
331          * Hook it into the entropy system.
332          */
333         ISC_LIST_APPEND(ent->sources, source, link);
334         ent->nsources++;
335
336         UNLOCK(&ent->lock);
337         return (ISC_R_SUCCESS);
338
339  closefd:
340         close(fd);
341
342  errout:
343         if (source != NULL)
344                 isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t));
345
346         UNLOCK(&ent->lock);
347
348         return (ret);
349 }