See ChangeLog: Mon Jul 17 16:35:47 CEST 2000 Werner Koch
[gnupg.git] / cipher / rndegd.c
index 6cdc4dd..f6870cd 100644 (file)
@@ -1,5 +1,5 @@
 /* rndegd.c  - interface to the EGD
- *     Copyright (C) 1999 Free Software Foundation, Inc.
+ *     Copyright (C) 1999, 2000 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include <sys/socket.h>
 #include <sys/un.h>
 #include "types.h"
-#include "util.h"
+#include "g10lib.h"
+#ifndef IS_MODULE
 #include "ttyio.h"
-#include "dynload.h"
-
-#ifdef IS_MODULE
-  #define _(a) (a)
-#else
-  #include "i18n.h"
 #endif
+#include "dynload.h"
+#include "cipher.h"
 
 #ifndef offsetof
 #define offsetof(type, member) ((size_t) &((type *)0)->member)
 #endif
 
+
+/* FIXME: this is duplicated code from util/fileutil
+ * I don't think that this code should go into libgcrypt anyway.
+ */
+char *
+my_make_filename( const char *first_part, ... )
+{
+    va_list arg_ptr ;
+    size_t n;
+    const char *s;
+    char *name, *home, *p;
+
+    va_start( arg_ptr, first_part ) ;
+    n = strlen(first_part)+1;
+    while( (s=va_arg(arg_ptr, const char *)) )
+       n += strlen(s) + 1;
+    va_end(arg_ptr);
+
+    home = NULL;
+    if( *first_part == '~' && first_part[1] == '/'
+                          && (home = getenv("HOME")) && *home )
+       n += strlen(home);
+
+    name = m_alloc(n);
+    p = home ? stpcpy(stpcpy(name,home), first_part+1)
+            : stpcpy(name, first_part);
+    va_start( arg_ptr, first_part ) ;
+    while( (s=va_arg(arg_ptr, const char *)) )
+       p = stpcpy(stpcpy(p,"/"), s);
+    va_end(arg_ptr);
+
+    return name;
+}
+
+
+
+
+
 static int
 do_write( int fd, void *buf, size_t nbytes )
 {
     size_t nleft = nbytes;
-    ssize_t nwritten;
+    int nwritten;
 
     while( nleft > 0 ) {
        nwritten = write( fd, buf, nleft);
@@ -64,18 +99,57 @@ do_write( int fd, void *buf, size_t nbytes )
     return 0;
 }
 
+static int
+do_read( int fd, void *buf, size_t nbytes )
+{
+    int n, nread = 0;
+
+    do {
+       do {
+           n = read(fd, (char*)buf + nread, nbytes );
+       } while( n == -1 && errno == EINTR );
+       if( n == -1 )
+           return -1;
+       nread += n;
+    } while( nread < nbytes );
+    return nbytes;
+}
+
 
+
+/****************
+ * Note: we always use the highest level.
+ * TO boost the performance we may want to add some
+ * additional code for level 1
+ *
+ * Using a level of 0 should never block and better add nothing
+ * to the pool.  So this is just a dummy for EGD.
+ */
 static int
 gather_random( void (*add)(const void*, size_t, int), int requester,
                                          size_t length, int level )
 {
     static int fd = -1;
     int n;
-    int warn=0;
     byte buffer[256+2];
+    int nbytes;
+    int do_restart = 0;
 
+    if( !length )
+       return 0;
+    if( !level )
+       return 0;
+
+  restart:
+    if( do_restart ) {
+       if( fd != -1 ) {
+           close( fd );
+           fd = -1;
+       }
+    }
     if( fd == -1 ) {
-       const char *name = "/tmp/entropy";
+       #warning Fixme: make the filename configurable
+       char *name = my_make_filename( "~/.gnupg-test", "entropy", NULL );
        struct sockaddr_un addr;
        int addr_len;
 
@@ -92,73 +166,59 @@ gather_random( void (*add)(const void*, size_t, int), int requester,
        if( connect( fd, (struct sockaddr*)&addr, addr_len) == -1 )
            g10_log_fatal("can't connect to `%s': %s\n",
                                                    name, strerror(errno) );
+       g10_free(name);
     }
+    do_restart = 0;
 
+    nbytes = length < 255? length : 255;
+    /* first time we do it with a non blocking request */
+    buffer[0] = 1; /* non blocking */
+    buffer[1] = nbytes;
+    if( do_write( fd, buffer, 2 ) == -1 )
+       g10_log_fatal("can't write to the EGD: %s\n", strerror(errno) );
+    n = do_read( fd, buffer, 1 );
+    if( n == -1 ) {
+       g10_log_error("read error on EGD: %s\n", strerror(errno));
+       do_restart = 1;
+       goto restart;
+    }
+    n = buffer[0];
+    if( n ) {
+       n = do_read( fd, buffer, n );
+       if( n == -1 ) {
+           g10_log_error("read error on EGD: %s\n", strerror(errno));
+           do_restart = 1;
+           goto restart;
+       }
+       (*add)( buffer, n, requester );
+       length -= n;
+    }
 
+    if( length ) {
+      #ifdef IS_MODULE
+       fprintf( stderr,
+      #else
+       tty_printf(
+      #endif
+        _("Please wait, entropy is being gathered. Do some work if it would\n"
+          "keep you from getting bored, because it will improve the quality\n"
+          "of the entropy.\n") );
+    }
     while( length ) {
-       fd_set rfds;
-       struct timeval tv;
-       int rc;
-       int nbytes;
-       int cmd;
-
        nbytes = length < 255? length : 255;
-       /* send request */
-       cmd = level >= 2 ? 2 : 1;
-       buffer[0] = cmd;
+
+       buffer[0] = 2; /* blocking */
        buffer[1] = nbytes;
        if( do_write( fd, buffer, 2 ) == -1 )
            g10_log_fatal("can't write to the EGD: %s\n", strerror(errno) );
-       /* wait on reply */
-       FD_ZERO(&rfds);
-       FD_SET(fd, &rfds);
-       tv.tv_sec = 3;
-       tv.tv_usec = 0;
-       if( !(rc=select(fd+1, &rfds, NULL, NULL, &tv)) ) {
-           if( !warn )
-             #ifdef IS_MODULE
-               fprintf( stderr,
-             #else
-               tty_printf(
-             #endif
-                           _(
-"\n"
-"Not enough random bytes available.  Please do some other work to give\n"
-"the OS a chance to collect more entropy! (Need %d more bytes)\n"), length );
-           warn = 1;
-           continue;
-       }
-       else if( rc == -1 ) {
-           g10_log_error("select error on EGD: %s\n", strerror(errno));
-           continue;
-       }
-
-       /* collect reply */
-       do {
-           n = read(fd, buffer, nbytes+2 );
-       } while( n == -1 && errno == EINTR );
-       /* process reply */
-       if( n == -1 )
+       n = do_read( fd, buffer, nbytes );
+       if( n == -1 ) {
            g10_log_error("read error on EGD: %s\n", strerror(errno));
-       else if( cmd == 2 && n != nbytes  ) {
-           g10_log_error("bad EGD reply: too short %d/%d\n", nbytes, n );
-       }
-       else if( cmd == 2 ) {
-           (*add)( buffer, n, requester );
-           length -= n;
-       }
-       else if( !n )
-           g10_log_error("bad EGD reply: too short\n");
-       else if( buffer[0] != n-1 )
-           g10_log_error("bad EGD reply: count mismatch %d/%d\n",
-                                                     n-1, buffer[0] );
-       else if( n==1 )
-           g10_log_info("no data from EGD\n");
-       else {
-           n -= 1;
-           (*add)( buffer+1, n, requester );
-           length -= n;
+           do_restart = 1;
+           goto restart;
        }
+       (*add)( buffer, n, requester );
+       length -= n;
     }
     memset(buffer, 0, sizeof(buffer) );