#include "types.h"
#include "mpool.h"
#include "tktree.h"
#include "clstree.h"
#include "sha1.h"
#include "listen.h"
#include "ssl_conf.h"
#include "rovm.h"

#include "thread.h"
#include "thread_mutex.h"
#include "thread_cond.h"

#include "mpm_worker_fdqueue.h"
#include "mpm_worker_pod.h"
#include "mpm_worker.h"

#include "connection.h"
#include "request.h"
#include "proc_rc.h"

#include "log.h"
#include "common.h"
#include "ticket.h"

/*  _________________________________________________________________
**
**  Support for better seeding of SSL library's RNG
**  _________________________________________________________________
*/

#define BUFSIZE 8192

static int 
ssl_rand_feedfp (rc_pool_t *p, apr_file_t *fp, int nReq)
{
  apr_size_t nDone;
  unsigned char caBuf[BUFSIZE];
  apr_size_t nBuf;
  apr_size_t nRead;
  apr_size_t nTodo;
  
  nDone = 0;
  nRead = BUFSIZE;
  nTodo = nReq;
  while (1) 
    {
      if (nReq > 0)
        nRead = (nTodo < BUFSIZE ? nTodo : BUFSIZE);
      nBuf = nRead;
      if (apr_file_read (fp, caBuf, &nBuf) != APR_SUCCESS)
        break;
      RAND_seed (caBuf, nBuf);
      nDone += nBuf;
      if (nReq > 0) 
        {
          nTodo -= nBuf;
          if (nTodo <= 0)
            break;
        }
    }
  return nDone;
}

static int 
ssl_rand_choosenum(int l, int h)
{
  int i;
  char buf[50];
  
  apr_snprintf (buf, sizeof(buf), "%.0f",
                (((double)(rand()%RAND_MAX)/RAND_MAX)*(h-l)));
  i = atoi (buf)+1;
  if (i < l) i = l;
  if (i > h) i = h;
  return i;
}

int 
ssl_rand_seed (struct rovm *r, rc_pool_t *p, ssl_rsctx_t nCtx, char *prefix)
{
  SSLModConfigRec *mc;
  rc_array_header_t *apRandSeed;
  ssl_randseed_t *pRandSeeds;
  ssl_randseed_t *pRandSeed;
  unsigned char stackdata[256];
  int nReq, nDone;
  apr_file_t *fp;
  int i, n, l;
  
  mc = myModConfig (r);
  nReq  = 0;
  nDone = 0;
  apRandSeed = mc->aRandSeed;
  pRandSeeds = (ssl_randseed_t *)apRandSeed->elts;
  for (i = 0; i < apRandSeed->nelts; i++) 
    {
      pRandSeed = &pRandSeeds[i];
      if (pRandSeed->nCtx == nCtx) 
        {
          nReq += pRandSeed->nBytes;
          if (pRandSeed->nSrc == SSL_RSSRC_FILE) 
            {
              /*
               * seed in contents of an external file
               */
              if (apr_file_open(&fp, pRandSeed->cpPath,
                                APR_READ, APR_OS_DEFAULT, p) != APR_SUCCESS)
                continue;
              nDone += ssl_rand_feedfp (p, fp, pRandSeed->nBytes);
              apr_file_close (fp);
            }
          else if (pRandSeed->nSrc == SSL_RSSRC_EXEC) 
            {
              const char *cmd = pRandSeed->cpPath;
              const char **argv = apr_palloc(p, sizeof(char *) * 3);
              /*
               * seed in contents generated by an external program
               */
              argv[0] = cmd;
              argv[1] = apr_itoa(p, pRandSeed->nBytes);
              argv[2] = NULL;
              
              if ((fp = ssl_util_ppopen (r, p, cmd, argv)) == NULL)
                continue;
              nDone += ssl_rand_feedfp (p, fp, pRandSeed->nBytes);
              ssl_util_ppclose (r, p, fp);
            }
#ifdef HAVE_SSL_RAND_EGD
          else if (pRandSeed->nSrc == SSL_RSSRC_EGD) 
            {
              /*
               * seed in contents provided by the external
               * Entropy Gathering Daemon (EGD)
               */
              if ((n = RAND_egd (pRandSeed->cpPath)) == -1)
                continue;
              nDone += n;
            }
#endif
          else if (pRandSeed->nSrc == SSL_RSSRC_BUILTIN) 
            {
              struct 
              {
                time_t t;
                pid_t pid;
              } my_seed;
              
              /*
               * seed in the current time (usually just 4 bytes)
               */
              my_seed.t = time(NULL);
              
              /*
               * seed in the current process id (usually just 4 bytes)
               */
              my_seed.pid = mc->pid;
              
              l = sizeof(my_seed);
              RAND_seed ((unsigned char *)&my_seed, l);
              nDone += l;
              
              /*
               * seed in some current state of the run-time stack (128 bytes)
               */
              n = ssl_rand_choosenum (0, sizeof(stackdata)-128-1);
              RAND_seed (stackdata+n, 128);
              nDone += 128;
            }
        }
    }
  rovm_log (NULL, ROVMLOG_INFO, ROVMLOG_MARK, 
            "%sSeeding PRNG with %d bytes of entropy", prefix, nDone);
  
  if (RAND_status() == 0)
    rovm_log (NULL, ROVMLOG_INFO, ROVMLOG_MARK, 
              "%sPRNG still contains insufficient entropy!", prefix);
  
  return nDone;
}
