/*******************************************************************************
* SSL.C 
* libramesys ssl handling functions
********************************************************************************
* COPYRIGHT (C) 2003 Andy Hobbs
********************************************************************************
* These library provides ssl functionality
********************************************************************************
* $Author: andy $
* $Revision: 1.1.8.1 $
* $Log: ssl.c,v $
* Revision 1.1.8.1  2004/10/14 09:45:12  andy
*
* Revision 1.1  2004/04/01 14:37:39  andy
* First cut of code
*
*******************************************************************************/

#include <lib.h>  /* NOTE Change this tou your own includes!! */

/*********< PROTOTYPES >*******************************************************/
extern flag          sslGlobalInit              (void);
extern flag          sslThreadInit              (void);
extern flag          sslThreadCleanUp           (void);
static void          sslThreadsLockingCallback  (int mode, int type, char * file, int line);
static unsigned long sslThreadsThreadId         (void);

/*********< GLOBALS >**********************************************************/

static pthread_mutex_t *ssl_lock_cs;
static long *ssl_lock_count;

static int initSSL = NO; // Global flag to indicate if the library has been initialised

/*******************************************************************************
* SSLGLOBALINIT() : Perform global ssl initialisation
*******************************************************************************/

extern flag sslGlobalInit(void)
{
   flag retval = SUCCESS;
   /**************************
   * Running on linux we don't 
   * need to initialise the
   * random seed as we have a
   * /dev/urandom
   ***************************/
   if (initSSL == NO)
   {
      initSSL = YES;

#ifdef HAVE_ENGINE_LOAD_BUILTIN_ENGINES
      ENGINE_load_builtin_engines();
#endif

      SSL_load_error_strings(); // Use nice error strings

      SSLeay_add_ssl_algorithms(); // Other ssl set up stuff
   }  

   return retval;
}

/*******************************************************************************
* SSLGLOBALCLEANUP() : Perform global ssl clean up
*******************************************************************************/

extern flag sslGlobalCleanUp(void)
{
   flag retval = SUCCESS;

   if(initSSL == YES) {

      ERR_free_strings(); // Free the SSL error strings

      EVP_cleanup(); // EVP_cleanup() removes all ciphers and digests from the table

#ifdef HAVE_ENGINE_cleanup
      ENGINE_cleanup();
#endif

      CRYPTO_cleanup_all_ex_data();

      initSSL = NO; 
   }

   return retval;
}

/*******************************************************************************
* SSLTHREADINIT() : Perform thread ssl initialisation required for multi-threading
*
* Returns:
*  Currently always returns SUCCESS
*******************************************************************************/

extern flag sslThreadInit(void)
{
   flag retval = SUCCESS;

   int i;

   sslGlobalInit();

   // Create the number of mutexes required
   ssl_lock_cs = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
   ssl_lock_count = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));

   // Initialise each mutex
   for (i = 0; i < CRYPTO_num_locks(); i++)
   {
      ssl_lock_count[i] = 0;
      pthread_mutex_init(&(ssl_lock_cs[i]), NULL);
   }

   // Register the call backs with the OpenSSL library
   CRYPTO_set_id_callback((unsigned long (*)()) sslThreadsThreadId);
   CRYPTO_set_locking_callback((void (*)()) sslThreadsLockingCallback);

   return retval;
}

/*******************************************************************************
* SSLTHREADCLEANUP() : Perform global ssl clean up required for multi-threading
*
* Returns:
*  Currently always returns SUCCESS
*******************************************************************************/

extern flag sslThreadCleanUp(void)
{
   flag retval = SUCCESS;
   int i;

   // Clear the locking callback
   CRYPTO_set_locking_callback(NULL);

   // Destroy all the mutexes
   for (i = 0; i < CRYPTO_num_locks(); i++)
   {
      pthread_mutex_destroy(&(ssl_lock_cs[i]));
   }

   // Free up the mutexes
   OPENSSL_free(ssl_lock_cs);
   OPENSSL_free(ssl_lock_count);

   sslGlobalCleanUp();

   return retval;
}

/*******************************************************************************
* SSLTHREADSLOCKINGCALLBACK() : callback for openssl library to mutex lock
*
* Params:
*  mode - the locking mode - CRYPTO_LOCK for lock other for UNLOCK
*  type - index into array of mutex for which mutex to lock
*  file - not used - filename of calling source
*  line - not used - line number of calling source
*******************************************************************************/

static void sslThreadsLockingCallback(int mode, int type, char * file, int line)
{
   // Are we locking or unlocking?
   if (mode & CRYPTO_LOCK)
   {
      // Lock the appropriate mutex
      pthread_mutex_lock(&(ssl_lock_cs[type]));
      ssl_lock_count[type]++;
   }
   else
   {
      pthread_mutex_unlock(&(ssl_lock_cs[type]));
   }
}

/*******************************************************************************
* SSLTHREADSTHREADID() : callback for openssl library to return thread ID
*
* Returns: ID of calling thread
*******************************************************************************/

static unsigned long sslThreadsThreadId(void)
{
   unsigned long retval;

   retval = (unsigned long) pthread_self();
   return retval;
}

