00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include <config.h>
00026
00027 #include <sys/param.h>
00028 #include <sys/types.h>
00029 #include <sys/time.h>
00030 #include <sys/stat.h>
00031 #include <sys/socket.h>
00032 #include <sys/un.h>
00033
00034 #ifdef HAVE_NANOSLEEP
00035 #include <time.h>
00036 #endif
00037 #include <unistd.h>
00038
00039 #include <isc/platform.h>
00040 #include <isc/strerror.h>
00041
00042 #ifdef ISC_PLATFORM_NEEDSYSSELECTH
00043 #include <sys/select.h>
00044 #endif
00045
00046 #include "errno2result.h"
00047
00048
00049
00050
00051
00052
00053
00054
00055 #define FILESOURCE_HANDLE_TYPE int
00056
00057 typedef struct {
00058 int handle;
00059 enum {
00060 isc_usocketsource_disconnected,
00061 isc_usocketsource_connecting,
00062 isc_usocketsource_connected,
00063 isc_usocketsource_ndesired,
00064 isc_usocketsource_wrote,
00065 isc_usocketsource_reading
00066 } status;
00067 size_t sz_to_recv;
00068 } isc_entropyusocketsource_t;
00069
00070 #include "../entropy.c"
00071
00072 static unsigned int
00073 get_from_filesource(isc_entropysource_t *source, isc_uint32_t desired) {
00074 isc_entropy_t *ent = source->ent;
00075 unsigned char buf[128];
00076 int fd = source->sources.file.handle;
00077 ssize_t n, ndesired;
00078 unsigned int added;
00079
00080 if (source->bad)
00081 return (0);
00082
00083 desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
00084
00085 added = 0;
00086 while (desired > 0) {
00087 ndesired = ISC_MIN(desired, sizeof(buf));
00088 n = read(fd, buf, ndesired);
00089 if (n < 0) {
00090 if (errno == EAGAIN || errno == EINTR)
00091 goto out;
00092 goto err;
00093 }
00094 if (n == 0)
00095 goto err;
00096
00097 entropypool_adddata(ent, buf, n, n * 8);
00098 added += n * 8;
00099 desired -= n;
00100 }
00101 goto out;
00102
00103 err:
00104 (void)close(fd);
00105 source->sources.file.handle = -1;
00106 source->bad = ISC_TRUE;
00107
00108 out:
00109 return (added);
00110 }
00111
00112 static unsigned int
00113 get_from_usocketsource(isc_entropysource_t *source, isc_uint32_t desired) {
00114 isc_entropy_t *ent = source->ent;
00115 unsigned char buf[128];
00116 int fd = source->sources.usocket.handle;
00117 ssize_t n = 0, ndesired;
00118 unsigned int added;
00119 size_t sz_to_recv = source->sources.usocket.sz_to_recv;
00120
00121 if (source->bad)
00122 return (0);
00123
00124 desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0);
00125
00126 added = 0;
00127 while (desired > 0) {
00128 ndesired = ISC_MIN(desired, sizeof(buf));
00129 eagain_loop:
00130
00131 switch ( source->sources.usocket.status ) {
00132 case isc_usocketsource_ndesired:
00133 buf[0] = ndesired;
00134 if ((n = sendto(fd, buf, 1, 0, NULL, 0)) < 0) {
00135 if (errno == EWOULDBLOCK || errno == EINTR ||
00136 errno == ECONNRESET)
00137 goto out;
00138 goto err;
00139 }
00140 INSIST(n == 1);
00141 source->sources.usocket.status =
00142 isc_usocketsource_wrote;
00143 goto eagain_loop;
00144
00145 case isc_usocketsource_connecting:
00146 case isc_usocketsource_connected:
00147 buf[0] = 1;
00148 buf[1] = ndesired;
00149 if ((n = sendto(fd, buf, 2, 0, NULL, 0)) < 0) {
00150 if (errno == EWOULDBLOCK || errno == EINTR ||
00151 errno == ECONNRESET)
00152 goto out;
00153 goto err;
00154 }
00155 if (n == 1) {
00156 source->sources.usocket.status =
00157 isc_usocketsource_ndesired;
00158 goto eagain_loop;
00159 }
00160 INSIST(n == 2);
00161 source->sources.usocket.status =
00162 isc_usocketsource_wrote;
00163
00164
00165 case isc_usocketsource_wrote:
00166 if (recvfrom(fd, buf, 1, 0, NULL, NULL) != 1) {
00167 if (errno == EAGAIN) {
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180 #ifdef HAVE_NANOSLEEP
00181 struct timespec ts;
00182
00183 ts.tv_sec = 0;
00184 ts.tv_nsec = 1000000;
00185 nanosleep(&ts, NULL);
00186 #else
00187 usleep(1000);
00188 #endif
00189 goto eagain_loop;
00190 }
00191 if (errno == EWOULDBLOCK || errno == EINTR)
00192 goto out;
00193 goto err;
00194 }
00195 source->sources.usocket.status =
00196 isc_usocketsource_reading;
00197 sz_to_recv = buf[0];
00198 source->sources.usocket.sz_to_recv = sz_to_recv;
00199 if (sz_to_recv > sizeof(buf))
00200 goto err;
00201
00202
00203 case isc_usocketsource_reading:
00204 if (sz_to_recv != 0U) {
00205 n = recv(fd, buf, sz_to_recv, 0);
00206 if (n < 0) {
00207 if (errno == EWOULDBLOCK ||
00208 errno == EINTR)
00209 goto out;
00210 goto err;
00211 }
00212 } else
00213 n = 0;
00214 break;
00215
00216 default:
00217 goto err;
00218 }
00219
00220 if ((size_t)n != sz_to_recv)
00221 source->sources.usocket.sz_to_recv -= n;
00222 else
00223 source->sources.usocket.status =
00224 isc_usocketsource_connected;
00225
00226 if (n == 0)
00227 goto out;
00228
00229 entropypool_adddata(ent, buf, n, n * 8);
00230 added += n * 8;
00231 desired -= n;
00232 }
00233 goto out;
00234
00235 err:
00236 close(fd);
00237 source->bad = ISC_TRUE;
00238 source->sources.usocket.status = isc_usocketsource_disconnected;
00239 source->sources.usocket.handle = -1;
00240
00241 out:
00242 return (added);
00243 }
00244
00245
00246
00247
00248
00249 static void
00250 fillpool(isc_entropy_t *ent, unsigned int desired, isc_boolean_t blocking) {
00251 unsigned int added;
00252 unsigned int remaining;
00253 unsigned int needed;
00254 unsigned int nsource;
00255 isc_entropysource_t *source;
00256
00257 REQUIRE(VALID_ENTROPY(ent));
00258
00259 needed = desired;
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281 if (needed == 0) {
00282 REQUIRE(!blocking);
00283
00284 if ((ent->pool.entropy >= RND_POOLBITS / 4)
00285 && (ent->pool.pseudo <= RND_POOLBITS / 4))
00286 return;
00287
00288 needed = THRESHOLD_BITS * 4;
00289 } else {
00290 needed = ISC_MAX(needed, THRESHOLD_BITS);
00291 needed = ISC_MIN(needed, RND_POOLBITS);
00292 }
00293
00294
00295
00296
00297 needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy);
00298
00299
00300
00301
00302
00303
00304 if (ent->initialized < THRESHOLD_BITS)
00305 needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized);
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315 added = 0;
00316 remaining = needed;
00317 if (ent->nextsource == NULL) {
00318 ent->nextsource = ISC_LIST_HEAD(ent->sources);
00319 if (ent->nextsource == NULL)
00320 return;
00321 }
00322 source = ent->nextsource;
00323 again_file:
00324 for (nsource = 0; nsource < ent->nsources; nsource++) {
00325 unsigned int got;
00326
00327 if (remaining == 0)
00328 break;
00329
00330 got = 0;
00331
00332 switch ( source->type ) {
00333 case ENTROPY_SOURCETYPE_FILE:
00334 got = get_from_filesource(source, remaining);
00335 break;
00336
00337 case ENTROPY_SOURCETYPE_USOCKET:
00338 got = get_from_usocketsource(source, remaining);
00339 break;
00340 }
00341
00342 added += got;
00343
00344 remaining -= ISC_MIN(remaining, got);
00345
00346 source = ISC_LIST_NEXT(source, link);
00347 if (source == NULL)
00348 source = ISC_LIST_HEAD(ent->sources);
00349 }
00350 ent->nextsource = source;
00351
00352 if (blocking && remaining != 0) {
00353 int fds;
00354
00355 fds = wait_for_sources(ent);
00356 if (fds > 0)
00357 goto again_file;
00358 }
00359
00360
00361
00362
00363
00364 source = ISC_LIST_HEAD(ent->sources);
00365 while ((remaining != 0) && (source != NULL)) {
00366 unsigned int got;
00367
00368 got = 0;
00369
00370 if (source->type == ENTROPY_SOURCETYPE_CALLBACK)
00371 got = get_from_callback(source, remaining, blocking);
00372
00373 added += got;
00374 remaining -= ISC_MIN(remaining, got);
00375
00376 if (added >= needed)
00377 break;
00378
00379 source = ISC_LIST_NEXT(source, link);
00380 }
00381
00382
00383
00384
00385 if (ent->initialized < THRESHOLD_BITS)
00386 ent->initialized += added;
00387 }
00388
00389 static int
00390 wait_for_sources(isc_entropy_t *ent) {
00391 isc_entropysource_t *source;
00392 int maxfd, fd;
00393 int cc;
00394 fd_set reads;
00395 fd_set writes;
00396
00397 maxfd = -1;
00398 FD_ZERO(&reads);
00399 FD_ZERO(&writes);
00400
00401 source = ISC_LIST_HEAD(ent->sources);
00402 while (source != NULL) {
00403 if (source->type == ENTROPY_SOURCETYPE_FILE) {
00404 fd = source->sources.file.handle;
00405 if (fd >= 0) {
00406 maxfd = ISC_MAX(maxfd, fd);
00407 FD_SET(fd, &reads);
00408 }
00409 }
00410 if (source->type == ENTROPY_SOURCETYPE_USOCKET) {
00411 fd = source->sources.usocket.handle;
00412 if (fd >= 0) {
00413 switch (source->sources.usocket.status) {
00414 case isc_usocketsource_disconnected:
00415 break;
00416 case isc_usocketsource_connecting:
00417 case isc_usocketsource_connected:
00418 case isc_usocketsource_ndesired:
00419 maxfd = ISC_MAX(maxfd, fd);
00420 FD_SET(fd, &writes);
00421 break;
00422 case isc_usocketsource_wrote:
00423 case isc_usocketsource_reading:
00424 maxfd = ISC_MAX(maxfd, fd);
00425 FD_SET(fd, &reads);
00426 break;
00427 }
00428 }
00429 }
00430 source = ISC_LIST_NEXT(source, link);
00431 }
00432
00433 if (maxfd < 0)
00434 return (-1);
00435
00436 cc = select(maxfd + 1, &reads, &writes, NULL, NULL);
00437 if (cc < 0)
00438 return (-1);
00439
00440 return (cc);
00441 }
00442
00443 static void
00444 destroyfilesource(isc_entropyfilesource_t *source) {
00445 (void)close(source->handle);
00446 }
00447
00448 static void
00449 destroyusocketsource(isc_entropyusocketsource_t *source) {
00450 close(source->handle);
00451 }
00452
00453
00454
00455
00456 static isc_result_t
00457 make_nonblock(int fd) {
00458 int ret;
00459 int flags;
00460 char strbuf[ISC_STRERRORSIZE];
00461 #ifdef USE_FIONBIO_IOCTL
00462 int on = 1;
00463
00464 ret = ioctl(fd, FIONBIO, (char *)&on);
00465 #else
00466 flags = fcntl(fd, F_GETFL, 0);
00467 flags |= PORT_NONBLOCK;
00468 ret = fcntl(fd, F_SETFL, flags);
00469 #endif
00470
00471 if (ret == -1) {
00472 isc__strerror(errno, strbuf, sizeof(strbuf));
00473 UNEXPECTED_ERROR(__FILE__, __LINE__,
00474 #ifdef USE_FIONBIO_IOCTL
00475 "ioctl(%d, FIONBIO, &on): %s", fd,
00476 #else
00477 "fcntl(%d, F_SETFL, %d): %s", fd, flags,
00478 #endif
00479 strbuf);
00480
00481 return (ISC_R_UNEXPECTED);
00482 }
00483
00484 return (ISC_R_SUCCESS);
00485 }
00486
00487 isc_result_t
00488 isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) {
00489 int fd;
00490 struct stat _stat;
00491 isc_boolean_t is_usocket = ISC_FALSE;
00492 isc_boolean_t is_connected = ISC_FALSE;
00493 isc_result_t ret;
00494 isc_entropysource_t *source;
00495
00496 REQUIRE(VALID_ENTROPY(ent));
00497 REQUIRE(fname != NULL);
00498
00499 LOCK(&ent->lock);
00500
00501 if (stat(fname, &_stat) < 0) {
00502 ret = isc__errno2result(errno);
00503 goto errout;
00504 }
00505
00506
00507
00508
00509
00510
00511
00512 #if defined(S_ISSOCK)
00513 if (S_ISSOCK(_stat.st_mode))
00514 is_usocket = ISC_TRUE;
00515 #endif
00516 #if defined(S_ISFIFO) && defined(sun)
00517 if (S_ISFIFO(_stat.st_mode))
00518 is_usocket = ISC_TRUE;
00519 #endif
00520 if (is_usocket)
00521 fd = socket(PF_UNIX, SOCK_STREAM, 0);
00522 else
00523 fd = open(fname, O_RDONLY | PORT_NONBLOCK, 0);
00524
00525 if (fd < 0) {
00526 ret = isc__errno2result(errno);
00527 goto errout;
00528 }
00529
00530 ret = make_nonblock(fd);
00531 if (ret != ISC_R_SUCCESS)
00532 goto closefd;
00533
00534 if (is_usocket) {
00535 struct sockaddr_un sname;
00536
00537 memset(&sname, 0, sizeof(sname));
00538 sname.sun_family = AF_UNIX;
00539 strlcpy(sname.sun_path, fname, sizeof(sname.sun_path));
00540 #ifdef ISC_PLATFORM_HAVESALEN
00541 #if !defined(SUN_LEN)
00542 #define SUN_LEN(su) \
00543 (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
00544 #endif
00545 sname.sun_len = SUN_LEN(&sname);
00546 #endif
00547
00548 if (connect(fd, (struct sockaddr *) &sname,
00549 sizeof(struct sockaddr_un)) < 0) {
00550 if (errno != EINPROGRESS) {
00551 ret = isc__errno2result(errno);
00552 goto closefd;
00553 }
00554 } else
00555 is_connected = ISC_TRUE;
00556 }
00557
00558 source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t));
00559 if (source == NULL) {
00560 ret = ISC_R_NOMEMORY;
00561 goto closefd;
00562 }
00563
00564
00565
00566
00567 source->magic = SOURCE_MAGIC;
00568 source->ent = ent;
00569 source->total = 0;
00570 source->bad = ISC_FALSE;
00571 memset(source->name, 0, sizeof(source->name));
00572 ISC_LINK_INIT(source, link);
00573 if (is_usocket) {
00574 source->sources.usocket.handle = fd;
00575 if (is_connected)
00576 source->sources.usocket.status =
00577 isc_usocketsource_connected;
00578 else
00579 source->sources.usocket.status =
00580 isc_usocketsource_connecting;
00581 source->sources.usocket.sz_to_recv = 0;
00582 source->type = ENTROPY_SOURCETYPE_USOCKET;
00583 } else {
00584 source->sources.file.handle = fd;
00585 source->type = ENTROPY_SOURCETYPE_FILE;
00586 }
00587
00588
00589
00590
00591 ISC_LIST_APPEND(ent->sources, source, link);
00592 ent->nsources++;
00593
00594 UNLOCK(&ent->lock);
00595 return (ISC_R_SUCCESS);
00596
00597 closefd:
00598 (void)close(fd);
00599
00600 errout:
00601 UNLOCK(&ent->lock);
00602
00603 return (ret);
00604 }