/*****************************************************************************/
/*
                               SesolaNet.c

SSL network connection establishment, read/write and other functions.

The SesolaNetRequest..() functions deal with SSL acceptance and SSL rundown of
requests to SSL services (i.e. via "https:").

The SesolaNetClient..() functions deal with initiation and rundown of an SSL
transaction with a remote SSL server.  This is sometimes refered to as HTTP to
SSL gatewaying, and should not be confised with the processing of peer
certificate which is also refered to as client processing sometimes.


VERSION HISTORY
---------------
29-JAN-2003  MGD  bugfix; error recovery in Sesola_read() and Sesola_write()
18-APR-2002  MGD  bugfix; service and client SSL contexts
26-FEB-2002  MGD  bugfix; SesolaNetRequestEnd() wait for blocking I/O
08-JAN-2002  MGD  rework SESOLA.C
*/
/*****************************************************************************/

#ifdef WASD_VMS_V6
#undef _VMS_V6_SOURCE
#define _VMS_V6_SOURCE
#undef __VMS_VER
#define __VMS_VER 60000000
#undef __CRTL_VER
#define __CRTL_VER 60000000
#endif

/* standard C header files */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* VMS related header files */
#include <iodef.h>
#include <ssdef.h>
#include <stsdef.h>

/* application header files */
#define SESOLA_REQUIRED
#include "Sesola.h"

#define WASD_MODULE "SESOLANET"

/***************************************/
#ifdef SESOLA  /* secure sockets layer */
/***************************************/

/******************/
/* global storage */
/******************/

BIO_METHOD Sesola_method =
{
   BIO_TYPE_FD,
   "WASD Sesola",
   Sesola_write,
   Sesola_read,
   Sesola_puts,
   Sesola_gets,
   Sesola_ctrl,
   Sesola_new,
   Sesola_free,
};

/********************/
/* external storage */
/********************/

#ifdef DBUG
extern BOOL Debug;
#else
#define Debug 0 
#endif

extern int  EfnWait,
            EfnNoWait,
            NetReadBufferSize,
            SesolaSSLversion;

extern char  *SesolaDefaultCertPtr,
             *SesolaDefaultCipherListPtr,
             *SesolaDefaultKeyPtr;

extern char  ErrorSanityCheck[],
             SoftwareID[],
             Utility[];

extern ACCOUNTING_STRUCT  *AccountingPtr;
extern WATCH_STRUCT  Watch;

/*****************************************************************************/
/*
Begin an SSL transaction for a request.  Create the Sesola structure used to
store HTTPd SSL-related used during the transaction, initialize the OpenSSL
structures required, then begin the OpenSSL accept functionality.
*/

SesolaNetRequestBegin (REQUEST_STRUCT *rqptr)

{
   int  value;
   SESOLA_STRUCT  *sesolaptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA)))
      WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA, "SesolaNetRequestBegin()");

   if (!((SESOLA_CONTEXT*)rqptr->ServicePtr->SSLserverPtr)->SslCtx)
   {
      ErrorNoticed (0, "SslCtx == NULL", FI_LI);
      if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_SESOLA))
         WatchThis (rqptr, FI_LI, WATCH_SESOLA, "BEGIN SSL context NULL");
      RequestEnd (rqptr);
      return;
   }

   /* note that this IS NOT HEAP memory, and must be explicitly VmFree()d */
   rqptr->rqNet.SesolaPtr = sesolaptr =
      (SESOLA_STRUCT*)VmGet (sizeof(SESOLA_STRUCT));

   sesolaptr->SslCtx =
     (SSL_CTX*)((SESOLA_CONTEXT*)rqptr->ServicePtr->SSLserverPtr)->SslCtx;

   /* set the Sesola structure request pointer */
   sesolaptr->RequestPtr = rqptr;

   sesolaptr->SslPtr = SSL_new (sesolaptr->SslCtx);
   if (!sesolaptr->SslPtr)
   {
      ErrorNoticed (0, "SSL_new() failed", FI_LI); 
      SesolaNetRequestFree (rqptr);
      if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_SESOLA))
         WatchThis (rqptr, FI_LI, WATCH_SESOLA, "BEGIN SSL_new() failed");
      RequestEnd (rqptr);
      return;
   }
   SSL_clear (sesolaptr->SslPtr);

   /* set the application data to point to the request structure */
   SSL_set_app_data (sesolaptr->SslPtr, rqptr);

   sesolaptr->BioPtr = BIO_new (BIO_s_Sesola());
   if (!sesolaptr->BioPtr)
   {
      ErrorNoticed (0, "BIO_new() failed", FI_LI); 
      SesolaNetRequestFree (rqptr);
      if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_SESOLA))
         WatchThis (rqptr, FI_LI, WATCH_SESOLA, "BEGIN BIO_new() failed");
      return;
   }
   /* set the "BIO_s_Sesola" pointer to the Sesola structure */
   sesolaptr->BioPtr->ptr = sesolaptr;

   /* set up the SSL filter */
   sesolaptr->BioSslPtr = BIO_new (BIO_f_ssl());
   if (!sesolaptr->BioSslPtr)
   {
      ErrorNoticed (0, "BIO_new() failed", FI_LI); 
      SesolaNetRequestFree (rqptr);
      if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_SESOLA))
         WatchThis (rqptr, FI_LI, WATCH_SESOLA, "BEGIN BIO_new() failed");
      return;
   }

   SSL_set_bio (sesolaptr->SslPtr, sesolaptr->BioPtr, sesolaptr->BioPtr);

   SSL_set_accept_state (sesolaptr->SslPtr);

#if WATCH_CAT
   if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_SESOLA))
   {
      WatchThis (rqptr, FI_LI, WATCH_SESOLA, "BEGIN");
      /* set the request pointer as the first item of SSL user data */
      SSL_set_ex_data (sesolaptr->SslPtr, 0, rqptr);
      /* set for SSL information and verification callback */
      SSL_CTX_set_info_callback (sesolaptr->SslPtr, &SesolaWatchInfoCallback);
      /* set for BIO callback with the request pointer as it's argument */
      BIO_set_callback (sesolaptr->BioPtr, &SesolaWatchBioCallback);
      BIO_set_callback_arg (sesolaptr->BioPtr, rqptr);
   }
#endif

   /* associate the request with the SSL structure */
   sesolaptr->RequestPtr = rqptr;
   sesolaptr->NetChannel = rqptr->rqClient.Channel;

   /* begin the SSL handshake */
   SesolaNetRequestAccept (rqptr);
}

/*****************************************************************************/
/*
Shutdown the SSL session with the client (via an SSL alert so that we don't
wait around for a response!)  Return true if RequestEnd() can continue the
request run-down, false if has to abort and wait for some more to happen!
*/

SesolaNetRequestEnd (REQUEST_STRUCT *rqptr)

{
   int  value;
   SESOLA_STRUCT  *sesolaptr;

   /*********/
   /* begin */
   /*********/

   sesolaptr = (SESOLA_STRUCT*)rqptr->rqNet.SesolaPtr;

   if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA)))
      WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA,
                 "SesolaNetRequestEnd() !SL !&B !&B",
                 sesolaptr->ShutdownState, sesolaptr->ReadInProgress,
                 sesolaptr->WriteInProgress);

   if (!sesolaptr->ShutdownState)
   {
      if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_SESOLA))
         WatchThis (rqptr, FI_LI, WATCH_SESOLA, "SHUTDOWN");

      /* assume we've received a shutdown indication */
      SSL_set_shutdown (sesolaptr->SslPtr, SSL_RECEIVED_SHUTDOWN);
   }

   while (++sesolaptr->ShutdownState <= 4)
   {
      sesolaptr->ReadSesolaFunction =
         sesolaptr->WriteSesolaFunction = &SesolaNetRequestEnd;
      if (SSL_shutdown (sesolaptr->SslPtr)) break;
      /* if non-blocking IO in progress just return and wait for delivery */
      if (sesolaptr->ReadInProgress || sesolaptr->WriteInProgress) return;
   }
   /* if non-blocking IO in progress just return and wait for delivery */
   if (sesolaptr->ReadInProgress || sesolaptr->WriteInProgress) return;

   if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_SESOLA))
      WatchThis (rqptr, FI_LI, WATCH_SESOLA, "END");

   SesolaNetRequestFree (rqptr);

   RequestEnd (rqptr);
}

/*****************************************************************************/
/*
Free the OpenSSL structures outside of any use of them (early mistake)!
*/

SesolaNetRequestFree (REQUEST_STRUCT *rqptr)

{
   int  value;
   SESOLA_STRUCT  *sesolaptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA)))
      WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA, "SesolaNetRequestFree()");

   sesolaptr = (SESOLA_STRUCT*)rqptr->rqNet.SesolaPtr;

   if (sesolaptr->SslPtr)
   {
      SSL_free (sesolaptr->SslPtr);
      sesolaptr->BioPtr = NULL;
   }
   if (sesolaptr->BioPtr)
   {
      BIO_free (sesolaptr->BioPtr);
      sesolaptr->BioPtr = NULL;
   }

   /* request has finished with the Sesola structure */
   VmFree (sesolaptr, FI_LI);
   rqptr->rqNet.SesolaPtr = NULL;
}

/*****************************************************************************/
/*
This establishes the connection with an SSL client by providing the server
"hello", certificate and key exchange, etc.  Due to the non-blocking I/O used
by WASD this function will be called multiple times to complete the OpenSSL
accept.
*/

SesolaNetRequestAccept (REQUEST_STRUCT *rqptr)

{
   int  status,
        value;
   SESOLA_STRUCT  *sesolaptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA)))
      WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA, "SesolaNetRequestAccept()");

   sesolaptr = (SESOLA_STRUCT*)rqptr->rqNet.SesolaPtr;

   sesolaptr->ReadSesolaFunction =
      sesolaptr->WriteSesolaFunction = &SesolaNetRequestAccept;

   if (sesolaptr->HTTPduringHandshake)
   {
      SesolaNetRequestFree (rqptr);
      RequestEnd (rqptr);
      return;
   }

   value = SSL_accept (sesolaptr->SslPtr);

   if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA)))
      WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA, "SSL_accept() !SL", value);

   if (sesolaptr->HTTPduringHandshake)
   {
      SesolaNetRequestFree (rqptr);
      RequestEnd (rqptr);
      return;
   }

   /* if non-blocking IO in progress just return and wait for delivery */
   if (sesolaptr->ReadInProgress || sesolaptr->WriteInProgress) return;

   if (value <= 0)
   {
      /*********/
      /* error */
      /*********/

      if (WATCHING(rqptr) && (WATCH_CATEGORY(WATCH_SESOLA)))
         SesolaWatchErrors (sesolaptr);

      SesolaNetRequestFree (rqptr);
      RequestEnd (rqptr);
      return;
   }

   if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_SESOLA))
      SesolaWatchSession (sesolaptr);

   sesolaptr->ReadSesolaFunction = sesolaptr->WriteSesolaFunction = NULL;

   rqptr->rqNet.ReadBufferPtr = VmGetHeap (rqptr, NetReadBufferSize);
   rqptr->rqNet.ReadBufferSize = NetReadBufferSize;

   NetRead (rqptr, &RequestGet,
            rqptr->rqNet.ReadBufferPtr, rqptr->rqNet.ReadBufferSize);
}

/*****************************************************************************/
/*
Begin an SSL transaction.  Create the Sesola structure used to store HTTPd
SSL-related used during the transaction, initialize the OpenSSL structures
required, then begin the OpenSSL connect functionality.
*/

SesolaNetClientBegin (PROXY_TASK *tkptr)

{
   int  value;
   REQUEST_STRUCT  *rqptr;
   SERVICE_STRUCT  *svptr;
   SESOLA_CONTEXT  *scptr;
   SESOLA_STRUCT  *sesolaptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHING(tkptr) && (WATCH_MODULE(WATCH_MOD_SESOLA)))
      WatchThis (WATCHTK(tkptr), FI_LI, WATCH_MOD_SESOLA,
                 "SesolaNetClientBegin()");

   /* get (any) associated request and the proxy service */
   rqptr = tkptr->RequestPtr;
   svptr = tkptr->ServicePtr;

   if (!((SESOLA_CONTEXT*)rqptr->ServicePtr->SSLclientPtr)->SslCtx)
   {
      ErrorNoticed (0, "SslCtx == NULL", FI_LI);
      if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_SESOLA))
         WatchThis (rqptr, FI_LI, WATCH_SESOLA, "BEGIN SSL context NULL");
      ProxyEnd (tkptr);
      return;
   }

   /* note that this IS NOT HEAP memory, and must be explicitly VmFree()d */
   tkptr->SesolaPtr = sesolaptr =
      (SESOLA_STRUCT*)VmGet (sizeof(SESOLA_STRUCT));

   /* set the Sesola structure proxy task pointer */
   sesolaptr->ProxyTaskPtr = tkptr;

   sesolaptr->SslPtr =
      SSL_new (((SESOLA_CONTEXT*)svptr->SSLclientPtr)->SslCtx);
   if (!sesolaptr->SslPtr)
   {
      if (WATCHING(tkptr) && WATCH_CATEGORY(WATCH_SESOLA))
         WatchThis (WATCHTK(tkptr), FI_LI, WATCH_SESOLA,
                    "BEGIN SSL_new() failed");
      ErrorNoticed (0, "SSL_new() failed", FI_LI); 
      if (rqptr)
      {
         rqptr->rqResponse.HttpStatus = 500;
         ErrorGeneral (rqptr, MsgFor(rqptr,MSG_GENERAL_INTERNAL), FI_LI);
      }
      SesolaNetClientFree (tkptr);
      ProxyEnd (tkptr);
      return;
   }
   SSL_clear (sesolaptr->SslPtr);

   scptr = (SESOLA_CONTEXT*)svptr->SSLclientPtr;

   if (scptr->CipherListPtr[0])
      SSL_set_cipher_list (sesolaptr->SslPtr, scptr->CipherListPtr);

   /* set the application data to point to the proxy task structure */
   SSL_set_app_data (sesolaptr->SslPtr, tkptr);

   sesolaptr->BioPtr = BIO_new (BIO_s_Sesola());
   if (!sesolaptr->BioPtr)
   {
      if (WATCHING(tkptr) && WATCH_CATEGORY(WATCH_SESOLA))
         WatchThis (WATCHTK(tkptr), FI_LI, WATCH_SESOLA,
                    "BEGIN BIO_new() failed");
      ErrorNoticed (0, "BIO_new() failed", FI_LI); 
      if (rqptr)
      {
         rqptr->rqResponse.HttpStatus = 500;
         ErrorGeneral (rqptr, MsgFor(rqptr,MSG_GENERAL_INTERNAL), FI_LI);
      }
      SesolaNetClientFree (tkptr);
      ProxyEnd (tkptr);
      return;
   }
   /* set the "BIO_s_Sesola" pointer to the Sesola structure */
   sesolaptr->BioPtr->ptr = sesolaptr;

   /* set up the SSL filter */
   sesolaptr->BioSslPtr = BIO_new (BIO_f_ssl());
   if (!sesolaptr->BioSslPtr)
   {
      if (WATCHING(tkptr) && WATCH_CATEGORY(WATCH_SESOLA))
         WatchThis (WATCHTK(tkptr), FI_LI, WATCH_SESOLA,
                    "BEGIN BIO_new() failed");
      ErrorNoticed (0, "BIO_new() failed", FI_LI); 
      if (rqptr)
      {
         rqptr->rqResponse.HttpStatus = 500;
         ErrorGeneral (rqptr, MsgFor(rqptr,MSG_GENERAL_INTERNAL), FI_LI);
      }
      SesolaNetClientFree (tkptr);
      ProxyEnd (tkptr);
      return;
   }

   SSL_set_bio (sesolaptr->SslPtr, sesolaptr->BioPtr, sesolaptr->BioPtr);

   SSL_set_connect_state (sesolaptr->SslPtr);

   if (scptr->VerifyCA)
      SSL_set_verify (sesolaptr->SslPtr, SSL_VERIFY_CLIENT_ONCE,
                      &SesolaCertVerifyCallback);

   /* provide the request pointer for the verify callback */
   SSL_set_ex_data (sesolaptr->SslPtr, 0, rqptr);

#if WATCH_CAT
   if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_SESOLA))
   {
      WatchThis (rqptr, FI_LI, WATCH_SESOLA, "BEGIN");
      /* set the ??? as the first item of SSL user data */
      SSL_set_ex_data (sesolaptr->SslPtr, 0, rqptr);
      /* set for SSL information and verification callback */
      SSL_CTX_set_info_callback (sesolaptr->SslPtr, &SesolaWatchInfoCallback);
      /* set for BIO callback with the request pointer as it's argument */
      BIO_set_callback (sesolaptr->BioPtr, &SesolaWatchBioCallback);
      BIO_set_callback_arg (sesolaptr->BioPtr, rqptr);
   }
#endif

   /* associate the proxy task with the SSL structure */
   sesolaptr->ProxyTaskPtr = tkptr;
   sesolaptr->NetChannel = tkptr->ProxyChannel;

   /* begin the SSL handshake */
   SesolaNetClientConnect (tkptr);
}

/*****************************************************************************/
/*
Shutdown the SSL session with the client (via an SSL alert so that we don't
wait around for a response!)  Return true if RequestEnd() can continue the
request run-down, false if has to abort and wait for some more to happen!
*/

SesolaNetClientEnd (PROXY_TASK *tkptr)

{
   int  value;
   SESOLA_STRUCT  *sesolaptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHING(tkptr) && (WATCH_MODULE(WATCH_MOD_SESOLA)))
      WatchThis (WATCHTK(tkptr), FI_LI, WATCH_MOD_SESOLA,
                 "SesolaNetClientEnd()");

   sesolaptr = (SESOLA_STRUCT*)tkptr->SesolaPtr;

   if (!sesolaptr->ShutdownState)
   {
      if (WATCHING(tkptr) && WATCH_CATEGORY(WATCH_SESOLA))
         WatchThis (WATCHTK(tkptr), FI_LI, WATCH_SESOLA, "SHUTDOWN");

      /* assume we've received a shutdown indication */
      SSL_set_shutdown (sesolaptr->SslPtr, SSL_RECEIVED_SHUTDOWN);
   }

   while (++sesolaptr->ShutdownState <= 4)
   {
      sesolaptr->ReadSesolaFunction =
         sesolaptr->WriteSesolaFunction = &SesolaNetClientEnd;
      if (SSL_shutdown (sesolaptr->SslPtr)) break;
      /* if non-blocking IO in progress just return and wait for delivery */
      if (sesolaptr->ReadInProgress || sesolaptr->WriteInProgress) return;
   }
   /* if non-blocking IO in progress just return and wait for delivery */
   if (sesolaptr->ReadInProgress || sesolaptr->WriteInProgress) return;

   if (WATCHING(tkptr) && WATCH_CATEGORY(WATCH_SESOLA))
      WatchThis (WATCHTK(tkptr), FI_LI, WATCH_SESOLA, "END");

   SesolaNetClientFree (tkptr);

   ProxyEnd (tkptr);
}

/*****************************************************************************/
/*
Free the OpenSSL structures outside of any use of them (early mistake)!
*/

SesolaNetClientFree (PROXY_TASK *tkptr)

{
   int  value;
   SESOLA_STRUCT  *sesolaptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHING(tkptr) && (WATCH_MODULE(WATCH_MOD_SESOLA)))
      WatchThis (WATCHTK(tkptr), FI_LI, WATCH_MOD_SESOLA,
                 "SesolaNetClientFree()");

   sesolaptr = (SESOLA_STRUCT*)tkptr->SesolaPtr;

   if (sesolaptr->SslPtr)
   {
      SSL_free (sesolaptr->SslPtr);
      sesolaptr->BioPtr = NULL;
   }
   if (sesolaptr->BioPtr)
   {
      BIO_free (sesolaptr->BioPtr);
      sesolaptr->BioPtr = NULL;
   }

   /* request has finished with the Sesola structure */
   VmFree (sesolaptr, FI_LI);
   tkptr->SesolaPtr = NULL;
}

/*****************************************************************************/
/*
This establishes the connection to an SSL server by providing the server
"hello", certificate and key exchange, etc.  Due to the non-blocking I/O used
by WASD this function will be called multiple times to complete the OpenSSL
connect. 
*/

SesolaNetClientConnect (PROXY_TASK *tkptr)

{
   int  status,
        value;
   SESOLA_STRUCT  *sesolaptr;
   REQUEST_STRUCT  *rqptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHING(tkptr) && (WATCH_MODULE(WATCH_MOD_SESOLA)))
      WatchThis (WATCHTK(tkptr), FI_LI, WATCH_MOD_SESOLA,
                 "SesolaNetClientConnect()");

   sesolaptr = (SESOLA_STRUCT*)tkptr->SesolaPtr;

   /* get (any) associated request and the proxy service */
   rqptr = tkptr->RequestPtr;

   sesolaptr->ReadSesolaFunction =
      sesolaptr->WriteSesolaFunction = &SesolaNetClientConnect;

   if (sesolaptr->HTTPduringHandshake)
   {
      if (rqptr)
      {
         /* or perhaps some more informative message? */
         rqptr->rqResponse.HttpStatus = 501;
         ErrorGeneral (rqptr, MsgFor(rqptr,MSG_HTTP_501), FI_LI);
      }
      SesolaNetClientFree (tkptr);
      ProxyEnd (tkptr);
      return;
   }

   value = SSL_connect (sesolaptr->SslPtr);

   if (WATCHING(tkptr) && (WATCH_MODULE(WATCH_MOD_SESOLA)))
      WatchThis (WATCHTK(tkptr), FI_LI, WATCH_MOD_SESOLA,
                 "SSL_connect() !SL", value);

   if (sesolaptr->HTTPduringHandshake)
   {
      if (rqptr)
      {
         rqptr->rqResponse.HttpStatus = 501;
         ErrorGeneral (rqptr, MsgFor(rqptr,MSG_HTTP_501), FI_LI);
      }
      SesolaNetClientFree (tkptr);
      ProxyEnd (tkptr);
      return;
   }

   /* if non-blocking IO in progress just return and wait for delivery */
   if (sesolaptr->ReadInProgress || sesolaptr->WriteInProgress) return;

   if (value <= 0)
   {
      /*********/
      /* error */
      /*********/

      if (WATCHING(rqptr) && (WATCH_CATEGORY(WATCH_SESOLA)))
         SesolaWatchErrors (sesolaptr);

      if (rqptr)
      {
         rqptr->rqResponse.HttpStatus = 502;
         if (sesolaptr->CertVerifyFailed)
            /* or perhaps some more formal message? */
            ErrorGeneral (rqptr, "Unknown server Certificate Authority.", FI_LI);
         else
            ErrorGeneral (rqptr, MsgFor(rqptr,MSG_HTTP_502), FI_LI);
      }
      SesolaNetClientFree (tkptr);
      ProxyEnd (tkptr);
      return;
   }

   if (WATCHING(tkptr) && WATCH_CATEGORY(WATCH_SESOLA))
      SesolaWatchSession (sesolaptr);

   sesolaptr->ReadSesolaFunction = sesolaptr->WriteSesolaFunction = NULL;

   ProxyWriteRequest (tkptr);
}

/*****************************************************************************/
/*
Read application data from the client via the network.  This function uses
SSL_read() to read a stream of encrypted data from the network then provide it
decrypted in the supplied buffer.  This function provides BLOCKING (non-AST)
and NON-BLOCKING I/O conforming to the functionality provided by NetRead(). The
network read I/O status block status and count fields are valid for the
high-level calling routine.
*/

SesolaNetRead
(
SESOLA_STRUCT *sesolaptr,
GENERAL_AST AstFunction,
char *DataPtr,
int DataSize
)
{
   int  value;
   REQUEST_STRUCT  *rqptr;

   /*********/
   /* begin */
   /*********/

#if WATCH_MOD

   /* get (any) associated request (WATCH purposes only) */
   if (!(rqptr = sesolaptr->RequestPtr))
      if (sesolaptr->ProxyTaskPtr)
         rqptr = sesolaptr->ProxyTaskPtr->RequestPtr;

   if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA)))
      WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA,
                 "SesolaNetRead() !&A !&X !UL",
                 AstFunction, DataPtr, DataSize);

#else /* WATCH_MOD */

   rqptr = NULL;

#endif /* WATCH_MOD */

   sesolaptr->ReadSesolaFunction = NULL;
   sesolaptr->ReadAstFunction = AstFunction;

   value = SSL_read (sesolaptr->SslPtr,
                     sesolaptr->ReadDataPtr = DataPtr,
                     sesolaptr->ReadDataSize = DataSize);

   if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA)))
      WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA,
                 "SSL_read() !SL", value);

   /* if non-blocking IO in progress just return and wait for delivery */
   if (sesolaptr->ReadInProgress) return (SS$_NORMAL);

   /* set the I/O status block count to reflect the decrypted data count */
   if (VMSok (sesolaptr->ReadIOsb.Status))
   {
      /* zero returned if received SSL shutdown */
      if (value > 0)
         sesolaptr->ReadIOsb.Count = value;
      else
      {
         sesolaptr->ReadIOsb.Status = SS$_ABORT;
         sesolaptr->ReadIOsb.Count = 0;
      }
   }
   else
      sesolaptr->ReadIOsb.Count = DataSize;

   if (!AstFunction) return (sesolaptr->ReadIOsb.Status);

   if (sesolaptr->RequestPtr)
   {
      memcpy (&sesolaptr->RequestPtr->rqNet.ReadIOsb,
              &sesolaptr->ReadIOsb,
              sizeof(sesolaptr->RequestPtr->rqNet.ReadIOsb));
      SysDclAst (AstFunction, sesolaptr->RequestPtr);
   }
   else
   if (sesolaptr->ProxyTaskPtr)
   {
      memcpy (&sesolaptr->ProxyTaskPtr->ProxyReadIOsb,
              &sesolaptr->ReadIOsb,
              sizeof(sesolaptr->ProxyTaskPtr->ProxyReadIOsb));
      SysDclAst (AstFunction, sesolaptr->ProxyTaskPtr);
   }
   else
      ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);

   return (SS$_NORMAL);
}

/*****************************************************************************/
/*
Implements a required function of a OpenSSL BIO_METHOD.  It provides blocking
and more importantly non-blocking reads of (encrypted) data from the network
connection.  With blocking IO it returns immediately
*/

int Sesola_read
(
BIO *bioptr,
char *DataPtr,
int DataSize
)
{
   int  status;
   SESOLA_STRUCT  *sesolaptr;
   REQUEST_STRUCT  *rqptr;

   /*********/
   /* begin */
   /*********/

   sesolaptr = (SESOLA_STRUCT*)bioptr->ptr;

#if WATCH_MOD

   /* get (any) associated request (WATCH purposes only) */
   if (!(rqptr = sesolaptr->RequestPtr))
      if (sesolaptr->ProxyTaskPtr)
         rqptr = sesolaptr->ProxyTaskPtr->RequestPtr;

   if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA)))
      WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA, 
                 "Sesola_read() !&X !UL !&B !&B !&B",
                 DataPtr, DataSize, sesolaptr->ReadCompleted,
                 sesolaptr->ReadInProgress, sesolaptr->WriteInProgress);

#else /* WATCH_MOD */

   rqptr = NULL;

#endif /* WATCH_MOD */

   if (sesolaptr->ReadInProgress)
   {
      /* asynchronous read in progress, retry next call */
      BIO_set_retry_read (bioptr);
      return (-1);
   }

   if (sesolaptr->ReadCompleted)
   {
      /* previous non-blocking read */
      sesolaptr->ReadCompleted = false;
      BIO_clear_retry_flags (bioptr);
      /* in case of an error give it zero (shutdown) */
      if (VMSok (sesolaptr->ReadIOsb.Status))
         return (sesolaptr->ReadIOsb.Count);
      else
         return (0);
   }

   if (sesolaptr->ReadAstFunction ||
       sesolaptr->ReadSesolaFunction)
   {
      /*******************/
      /* non-blocking IO */
      /*******************/

      /* indicate (in part to the AST) that non-blocking I/O in progress */
      sesolaptr->ReadInProgress = true;
      sesolaptr->ReadCompleted = false;
      BIO_set_retry_read (bioptr);

      if (sesolaptr->HTTPduringHandshake)
      {
         sesolaptr->ReadIOsb.Status = SS$_ABORT;
         sesolaptr->ReadIOsb.Count = 0;
         SysDclAst (&Sesola_read_ast, sesolaptr);
         return (-1);
      }

      status = sys$qio (EfnNoWait, sesolaptr->NetChannel,
                        IO$_READVBLK, &sesolaptr->ReadIOsb,
                        &Sesola_read_ast, sesolaptr,
                        sesolaptr->ReadWatchDataPtr = DataPtr, DataSize,
                        0, 0, 0, 0);

      /* return indicating non-blocking I/O */
      if (VMSok (status)) return (-1);

      if (status != SS$_EXQUOTA)
      {
         /* report the status via the AST */
         sesolaptr->ReadIOsb.Status = status;
         sesolaptr->ReadIOsb.Count = 0;
         SysDclAst (&Sesola_read_ast, sesolaptr);
         return (-1);
      }
   }
   else
   {
      /***************/
      /* blocking IO */
      /***************/

      sesolaptr->ReadInProgress = sesolaptr->ReadCompleted = false;
      BIO_clear_retry_flags (bioptr);

      if (sesolaptr->HTTPduringHandshake)
      {
         sesolaptr->ReadIOsb.Status = SS$_ABORT;
         sesolaptr->ReadIOsb.Count = 0;
         Sesola_read_ast (sesolaptr);
         return (0);
      }

      status = sys$qiow (EfnWait, sesolaptr->NetChannel,
                         IO$_READVBLK, &sesolaptr->ReadIOsb, 0, 0,
                         sesolaptr->ReadWatchDataPtr = DataPtr, DataSize,
                         0, 0, 0, 0);

      if (VMSnok (status))
      {
         /* report the status via the AST */
         sesolaptr->ReadIOsb.Status = status;
         sesolaptr->ReadIOsb.Count = 0;
      }

      Sesola_read_ast (sesolaptr);

      if (status != SS$_EXQUOTA) return (sesolaptr->ReadIOsb.Count);
   }

   /* with resource wait enabled the only quota not waited for is ASTLM */
   ErrorExitVmsStatus (status, "sys$qio()", FI_LI);
}

/*****************************************************************************/
/*
Post-process the write for both blocking and non-block I/O.  This function
contains code that attempts to check for HTTP transactions with an SSL service.
*/

Sesola_read_ast (SESOLA_STRUCT *sesolaptr)

{
   REQUEST_STRUCT  *rqptr;

   /*********/
   /* begin */
   /*********/

#if WATCH_MOD

   /* get (any) associated request (WATCH purposes only) */
   if (!(rqptr = sesolaptr->RequestPtr))
      if (sesolaptr->ProxyTaskPtr)
         rqptr = sesolaptr->ProxyTaskPtr->RequestPtr;

   if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA)))
      WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA, 
                 "Sesola_read_ast() !&F !&S !UL", &Sesola_read_ast,
                 sesolaptr->ReadIOsb.Status, sesolaptr->ReadIOsb.Count);

#else /* WATCH_MOD */

   rqptr = NULL;

#endif /* WATCH_MOD */

   if (rqptr && WATCHING(rqptr))
   {
      if (WATCH_CATEGORY(WATCH_NETWORK) ||
          WATCH_CATEGORY(WATCH_NETWORK_OCTETS))
      {
         if (VMSok(sesolaptr->ReadIOsb.Status))
         {
            WatchThis (rqptr, FI_LI, WATCH_NETWORK,
                       "READ !&S !UL bytes",
                       sesolaptr->ReadIOsb.Status,
                       sesolaptr->ReadIOsb.Count);
 
           if (WATCH_CATEGORY(WATCH_NETWORK_OCTETS))
               WatchDataDump (sesolaptr->ReadWatchDataPtr,
                              sesolaptr->ReadIOsb.Count);
         }
         else
            WatchThis (rqptr, FI_LI, WATCH_NETWORK,
                       "READ !&S %!-!&M",
                       sesolaptr->ReadIOsb.Status);
      }
   }

   if (VMSok (sesolaptr->ReadIOsb.Status))
   {
      if (sesolaptr->RequestPtr)
      {
         if (!sesolaptr->RequestPtr->BytesRawRx)
         {
            /* first read SSL_accept() handshake, check for HTTP request */
            if (!memcmp (sesolaptr->ReadWatchDataPtr, "GET ", 4) ||
                !memcmp (sesolaptr->ReadWatchDataPtr, "HEAD ", 5) ||
                !memcmp (sesolaptr->ReadWatchDataPtr, "POST ", 5) ||
                !memcmp (sesolaptr->ReadWatchDataPtr, "PUT ", 4))
            {
               sesolaptr->HTTPduringHandshake = true;
               SesolaNetThisIsSSL (sesolaptr);
            }
         }
         sesolaptr->RequestPtr->BytesRawRx += sesolaptr->ReadIOsb.Count;
      }
      else
      if (sesolaptr->ProxyTaskPtr)
      {
         if (!sesolaptr->ProxyTaskPtr->BytesRawRx)
         {
            /* first read SSL_connect() handshake, check for HTTP response */
            if (!memcmp (sesolaptr->ReadWatchDataPtr, "HTTP/", 5))
               sesolaptr->HTTPduringHandshake = true;
         }
         sesolaptr->ProxyTaskPtr->BytesRawRx += sesolaptr->ReadIOsb.Count;
      }
   }

   /* if blocking I/O then just return */
   if (!sesolaptr->ReadInProgress) return;

   sesolaptr->ReadInProgress = false;
   sesolaptr->ReadCompleted = true;

   /* if doing an application data read re-call with original parameters */
   if (!sesolaptr->ReadSesolaFunction)
      SesolaNetRead (sesolaptr, sesolaptr->ReadAstFunction,
                     sesolaptr->ReadDataPtr, sesolaptr->ReadDataSize);
   else
   /* otherwise call the specialized SESOLA SSL function */
   if (sesolaptr->RequestPtr)
      SysDclAst (sesolaptr->ReadSesolaFunction, sesolaptr->RequestPtr);
   else
   if (sesolaptr->ProxyTaskPtr)
      SysDclAst (sesolaptr->ReadSesolaFunction, sesolaptr->ProxyTaskPtr);
   else
      ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
}

/*****************************************************************************/
/*
Write application data to the client via the network.  This function uses
SSL_write() to write non-encrypted application data to the network in encrypted
form. This function provides BLOCKING (non-AST) and NON-BLOCKING I/O conforming
to the functionality provided by NetWrite(). The network write I/O status block
status and count fields are valid for the high-level calling routine.
*/

int SesolaNetWrite
(
SESOLA_STRUCT *sesolaptr,
GENERAL_AST AstFunction,
char *DataPtr,
int DataLength
)
{
   int  value;
   REQUEST_STRUCT  *rqptr;

   /*********/
   /* begin */
   /*********/

#if WATCH_MOD

   /* get (any) associated request (WATCH purposes only) */
   if (!(rqptr = sesolaptr->RequestPtr))
      if (sesolaptr->ProxyTaskPtr)
         rqptr = sesolaptr->ProxyTaskPtr->RequestPtr;

   if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA)))
      WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA,
                 "SesolaNetWrite() !&A !&X !UL",
                 AstFunction, DataPtr, DataLength);

#else /* WATCH_MOD */

   rqptr = NULL;

#endif /* WATCH_MOD */

   sesolaptr->WriteSesolaFunction = NULL;
   sesolaptr->WriteAstFunction = AstFunction;

   value = SSL_write (sesolaptr->SslPtr,
                      sesolaptr->WriteDataPtr = DataPtr,
                      sesolaptr->WriteDataLength = DataLength);

   if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA)))
      WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA,
                 "SSL_write() !SL", value);

   /* if non-blocking IO in progress just return and wait for delivery */
   if (sesolaptr->WriteInProgress) return (SS$_NORMAL);

   /* set the I/O status block count to reflect the decrypted data count */
   if (VMSok (sesolaptr->WriteIOsb.Status))
      sesolaptr->WriteIOsb.Count = value;
   else
      sesolaptr->WriteIOsb.Count = 0;

   if (!AstFunction) return (sesolaptr->WriteIOsb.Status);

   if (sesolaptr->RequestPtr)
   {
      memcpy (&sesolaptr->RequestPtr->rqNet.WriteIOsb,
              &sesolaptr->WriteIOsb,
              sizeof(sesolaptr->RequestPtr->rqNet.WriteIOsb));
      SysDclAst (AstFunction, sesolaptr->RequestPtr);
   }
   else
   if (sesolaptr->ProxyTaskPtr)
   {
      memcpy (&sesolaptr->ProxyTaskPtr->ProxyWriteIOsb,
              &sesolaptr->WriteIOsb,
              sizeof(sesolaptr->ProxyTaskPtr->ProxyWriteIOsb));
      SysDclAst (AstFunction, sesolaptr->ProxyTaskPtr);
   }
   else
      ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);

   return (SS$_NORMAL);
}

/*****************************************************************************/
/*
Implements a required function of a OpenSSL BIO_METHOD.
*/

int Sesola_write
(
BIO *bioptr,
char *DataPtr,
int DataLength
)
{
   int  status;
   SESOLA_STRUCT  *sesolaptr;
   REQUEST_STRUCT  *rqptr;

   /*********/
   /* begin */
   /*********/

   sesolaptr = (SESOLA_STRUCT*)bioptr->ptr;

#if WATCH_MOD

   /* get (any) associated request (WATCH purposes only) */
   if (!(rqptr = sesolaptr->RequestPtr))
      if (sesolaptr->ProxyTaskPtr)
         rqptr = sesolaptr->ProxyTaskPtr->RequestPtr;

   if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA)))
      WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA, 
                 "Sesola_write() !&X !UL !&B !&B !&B",
                 DataPtr, DataLength, sesolaptr->WriteCompleted,
                 sesolaptr->ReadInProgress, sesolaptr->WriteInProgress);

#else /* WATCH_MOD */

   rqptr = NULL;

#endif /* WATCH_MOD */

   if (sesolaptr->WriteInProgress)
   {
      /* asynchronous write in progress, retry next call */
      BIO_set_retry_write (bioptr);
      return (-1);
   }

   if (sesolaptr->WriteCompleted)
   {
      /* previous non-blocking write */
      sesolaptr->WriteCompleted = false;
      BIO_clear_retry_flags (bioptr);
      /* if an error give it what it wants, detected by SesolaNetWrite() */
      if (VMSok (sesolaptr->WriteIOsb.Status))
         return (sesolaptr->WriteIOsb.Count);
      else
         return (DataLength);
   }

   if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_NETWORK_OCTETS))
   {
      WatchThis (rqptr, FI_LI, WATCH_NETWORK,
                 "WRITE !UL bytes", DataLength);
      WatchDataDump (DataPtr, DataLength);
   }

   if (sesolaptr->WriteAstFunction ||
       sesolaptr->WriteSesolaFunction)
   {
      /*******************/
      /* non-blocking IO */
      /*******************/

      /* indicate (in part to the AST) that non-blocking I/O in progress */
      sesolaptr->WriteInProgress = true;
      sesolaptr->WriteCompleted = false;
      /* indicate to the calling routine this write should be retried */
      BIO_set_retry_write (bioptr);

      if (sesolaptr->HTTPduringHandshake)
      {
         sesolaptr->WriteIOsb.Status = SS$_ABORT;
         sesolaptr->WriteIOsb.Count = 0;
         SysDclAst (&Sesola_write_ast, sesolaptr);
         return (-1);
      }

      status = sys$qio (EfnNoWait, sesolaptr->NetChannel,
                        IO$_WRITEVBLK, &sesolaptr->WriteIOsb,
                        &Sesola_write_ast, sesolaptr,
                        DataPtr, DataLength, 0, 0, 0, 0);

      /* return indicating non-blocking I/O */
      if (VMSok (status)) return (-1);

      if (status != SS$_EXQUOTA)
      {
         /* report the status via the AST */
         sesolaptr->WriteIOsb.Status = status;
         sesolaptr->WriteIOsb.Count = 0;
         SysDclAst (&Sesola_write_ast, sesolaptr);
         return (-1);
      }
   }
   else
   {
      /***************/
      /* blocking IO */
      /***************/

      sesolaptr->WriteInProgress = sesolaptr->WriteCompleted = false;
      BIO_clear_retry_flags (bioptr);

      if (sesolaptr->HTTPduringHandshake)
      {
         sesolaptr->WriteIOsb.Status = SS$_ABORT;
         sesolaptr->WriteIOsb.Count = 0;
         Sesola_write_ast (sesolaptr);
         return (0);
      }

      status = sys$qiow (EfnWait, sesolaptr->NetChannel,
                         IO$_WRITEVBLK, &sesolaptr->WriteIOsb, 0, 0,
                         DataPtr, DataLength, 0, 0, 0, 0);

      if (VMSnok (status))
      {
         /* report the status via the AST */
         sesolaptr->WriteIOsb.Status = status;
         sesolaptr->WriteIOsb.Count = 0;
      }

      Sesola_write_ast (sesolaptr);

      /* return indicating blocking I/O */
      if (status != SS$_EXQUOTA) return (sesolaptr->WriteIOsb.Count);
   }

   /* with resource wait enabled the only quota not waited for is ASTLM */
   ErrorExitVmsStatus (status, "sys$qio()", FI_LI);
}

/*****************************************************************************/
/*
Post-process the write for both blocking and non-block I/O.
*/

Sesola_write_ast (SESOLA_STRUCT *sesolaptr)

{
   REQUEST_STRUCT  *rqptr;

   /*********/
   /* begin */
   /*********/

#if WATCH_MOD

   /* get (any) associated request (WATCH purposes only) */
   if (!(rqptr = sesolaptr->RequestPtr))
      if (sesolaptr->ProxyTaskPtr)
         rqptr = sesolaptr->ProxyTaskPtr->RequestPtr;

   if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA)))
      WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA, 
                 "Sesola_write_ast() !&F !&B !&S !UL", &Sesola_write_ast,
                 sesolaptr->WriteInProgress, sesolaptr->WriteIOsb.Status,
                 sesolaptr->WriteIOsb.Count);

#else /* WATCH_MOD */

   rqptr = NULL;

#endif /* WATCH_MOD */

   if (rqptr && WATCHING(rqptr))
   {
      if (WATCH_CATEGORY(WATCH_NETWORK))
      {
         if (VMSok (sesolaptr->WriteIOsb.Status))
            WatchThis (rqptr, FI_LI, WATCH_NETWORK,
                       "WRITE !&S !UL bytes",
                       sesolaptr->WriteIOsb.Status,
                       sesolaptr->WriteIOsb.Count);
         else
            WatchThis (rqptr, FI_LI, WATCH_NETWORK,
                       "WRITE !&S %!-!&M",
                       sesolaptr->WriteIOsb.Status);
      }
      if (WATCH_CATEGORY(WATCH_RESPONSE))
         if (VMSnok (sesolaptr->WriteIOsb.Status))
            WatchThis (rqptr, FI_LI, WATCH_RESPONSE,
                       "NETWORK !&S %!-!&M",
                       sesolaptr->WriteIOsb.Status);
   }

   if (VMSok (sesolaptr->WriteIOsb.Status))
   {
      if (sesolaptr->RequestPtr)
         sesolaptr->RequestPtr->BytesRawTx += sesolaptr->WriteIOsb.Count;
      else
      if (sesolaptr->ProxyTaskPtr)
         sesolaptr->ProxyTaskPtr->BytesRawTx += sesolaptr->WriteIOsb.Count;
   }

   /* if it's blocking I/O then just return */
   if (!sesolaptr->WriteInProgress) return;

   sesolaptr->WriteInProgress = false;
   sesolaptr->WriteCompleted = true;

   /* if doing an application data read re-call with original parameters */
   if (!sesolaptr->WriteSesolaFunction)
      SesolaNetWrite (sesolaptr, sesolaptr->WriteAstFunction,
                      sesolaptr->WriteDataPtr, sesolaptr->WriteDataLength);
   else
   /* otherwise call the specialized SESOLA SSL function */
   if (sesolaptr->RequestPtr)
      SysDclAst (sesolaptr->WriteSesolaFunction, sesolaptr->RequestPtr);
   else
   if (sesolaptr->ProxyTaskPtr)
      SysDclAst (sesolaptr->WriteSesolaFunction, sesolaptr->ProxyTaskPtr);
   else
      ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
}

/*****************************************************************************/
/*
The network (client) channel has been closed and zeroed, reflect that in the
sesola strcuture channel number.
*/

SesolaNetSocketHasBeenClosed (SESOLA_STRUCT *sesolaptr)

{
   REQUEST_STRUCT  *rqptr;

   /*********/
   /* begin */
   /*********/

#if WATCH_MOD

   /* get (any) associated request (WATCH purposes only) */
   if (!(rqptr = sesolaptr->RequestPtr))
      if (sesolaptr->ProxyTaskPtr)
         rqptr = sesolaptr->ProxyTaskPtr->RequestPtr;

   if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA)))
      WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA,
                 "SesolaNetSocketHasBeenClosed()");

#endif /* WATCH_MOD */

   sesolaptr->NetChannel = 0;
}

/*****************************************************************************/
/*
Just set the network channel to zero.
*/

SesolaNetThisIsSSL (SESOLA_STRUCT *sesolaptr)

{
   static char  ThisIsSSL [] =
"HTTP/1.0 501 This is an SSL service!\r\n\
Content-Type: text/html\r\n\
\r\n\
ERROR 501 - This is an SSL service!\n";

   REQUEST_STRUCT  *rqptr;
   IO_SB  IOsb;

   /*********/
   /* begin */
   /*********/

   rqptr = sesolaptr->RequestPtr;

   if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA)))
      WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA,
                 "SesolaNetThisIsSSL");

   sys$qiow (EfnWait, sesolaptr->NetChannel, IO$_WRITEVBLK, &IOsb, 0, 0,
             ThisIsSSL, sizeof(ThisIsSSL)-1, 0, 0, 0, 0);

   rqptr->BytesRawTx += sizeof(ThisIsSSL)-1;
   rqptr->rqResponse.HttpStatus = 501;
}

/*****************************************************************************/
/*
Implements a required function of a OpenSSL BIO_METHOD.
*/

BIO_METHOD *BIO_s_Sesola()
{
   if (WATCH_MODULE(WATCH_MOD_SESOLA))
      WatchThis (NULL, FI_LI, WATCH_MOD_SESOLA, "BIO_s_Sesola()");

   return (&Sesola_method);
}

/*****************************************************************************/
/*
Implements a required function of a OpenSSL BIO_METHOD.
*/

int Sesola_new (BIO *bioptr)

{
   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_SESOLA))
      WatchThis (NULL, FI_LI, WATCH_MOD_SESOLA, "Sesola_new()");

   bioptr->init = 1;
   bioptr->shutdown = 1;
   bioptr->num = 0;
   bioptr->flags = 0;
   /* the BIO pointer is set to the Sesola structure by the calling routine */
   return (1);
}

/*****************************************************************************/
/*
Implements a required function of a OpenSSL BIO_METHOD.
*/

int Sesola_free (BIO *bioptr)

{
   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_SESOLA))
      WatchThis (NULL, FI_LI, WATCH_MOD_SESOLA, "Sesola_free()");

   if (!bioptr) return (0);
   /* don't free anything, the BIO pointer is set to the Sesola structure */
   bioptr->ptr = NULL;
   return (1);
}
	
/*****************************************************************************/
/*
Implements a required function of a OpenSSL BIO_METHOD.
*/

long Sesola_ctrl
(
BIO *bioptr,
int Command,
long Number,
char *Pointer
)
{
   int  value;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_SESOLA))
      WatchThis (NULL, FI_LI, WATCH_MOD_SESOLA, 
                 "Sesola_ctrl() !UL !UL !&X", Command, Number, Pointer);

   value = 1;
   switch (Command)
   {
      case BIO_CTRL_RESET:
         if (Debug) fprintf (stdout, "BIO_CTRL_RESET\n");
         return (1);

      case BIO_CTRL_EOF:
         if (Debug) fprintf (stdout, "BIO_CTRL_EOF\n");
         return (1);

      case BIO_CTRL_SET:
         if (Debug) fprintf (stdout, "BIO_CTRL_SET\n");
         bioptr->num = Number;
         return (1);

      case BIO_CTRL_SET_CLOSE:
         if (Debug) fprintf (stdout, "BIO_CTRL_SET_CLOSE\n");
         return (1);

      case BIO_CTRL_FLUSH:
         if (Debug) fprintf (stdout, "BIO_CTRL_FLUSH\n");
         return (1);

      case BIO_CTRL_DUP:
         if (Debug) fprintf (stdout, "BIO_CTRL_DUP\n");
         return (1);

      case BIO_CTRL_GET_CLOSE:
         if (Debug) fprintf (stdout, "BIO_CTRL_GET_CLOSE\n");
         return (0);

      case BIO_CTRL_INFO:
         if (Debug) fprintf (stdout, "BIO_CTRL_INFO\n");
         return (0);

      case BIO_CTRL_GET:
         if (Debug) fprintf (stdout, "BIO_CTRL_GET\n");
         return (0);

      case BIO_CTRL_PENDING:
         if (Debug) fprintf (stdout, "BIO_CTRL_PENDING\n");
         return (0);

      case BIO_CTRL_POP:
         if (Debug) fprintf (stdout, "BIO_CTRL_POP\n");
         return (0);

      case BIO_CTRL_PUSH:
         if (Debug) fprintf (stdout, "BIO_CTRL_PUSH\n");
         return (0);

      case BIO_CTRL_WPENDING:
         if (Debug) fprintf (stdout, "BIO_CTRL_WPENDING\n");
         return (0);

      default:
         if (Debug) fprintf (stdout, "default:\n");
         return (0);
   }
}

/*****************************************************************************/
/*
Implements a required function of a OpenSSL BIO_METHOD.
*/

int Sesola_gets
(
BIO *bioptr,
char *StringPtr,
int StringSize
)
{
   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_SESOLA))
      WatchThis (NULL, FI_LI, WATCH_MOD_SESOLA, 
                 "Sesola_gets() !&X !UL", StringPtr, StringSize);
   return(0);
}

/*****************************************************************************/
/*
Implements a required function of a OpenSSL BIO_METHOD.
*/

int Sesola_puts
(
BIO *bioptr,
char *StringPtr
)
{
    int  Length;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "Sesola_puts() |%s|\n", StringPtr);

   if (WATCH_MODULE(WATCH_MOD_SESOLA))
      WatchThis (NULL, FI_LI, WATCH_MOD_SESOLA, "Sesola_puts() !&Z", StringPtr);

   if (!StringPtr) return (0);
   Length = strlen (StringPtr);
   if (Length == 0) return (0);
   return (Sesola_write (bioptr, StringPtr, Length));
}

/*****************************************************************************/
/*
For compilations without SSL these functions provide LINKage stubs for the
rest of the HTTPd modules, allowing for just recompiling the Sesola module to
integrate the SSL functionality.
*/

/*********************/
#else  /* not SESOLA */
/*********************/

#ifdef DBUG
extern BOOL Debug;
#else
#define Debug 0 
#endif

/* external storage */
extern char  ErrorSanityCheck[];
extern char  Utility[];

SesolaNetRequestBegin (REQUEST_STRUCT *rqptr)
{
   ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
}

SesolaNetRequestEnd (REQUEST_STRUCT *rqptr)
{
   ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
}

SesolaNetClientBegin (REQUEST_STRUCT *rqptr)
{
   ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
}

SesolaNetClientEnd (REQUEST_STRUCT *rqptr)
{
   ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
}

SesolaNetSocketHasBeenClosed (void *sesolaptr)
{
   ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
}

SesolaNetWrite
(
void *sesolaptr,
GENERAL_AST AstFunction,
char *DataPtr,
int DataLength
)
{
   ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
}

SesolaNetRead
(
void *sesolaptr,
GENERAL_AST AstFunction,
char *DataPtr,
int DataSize
)
{
   ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
}

Sesola_read_ast (void *sesolaptr)
{
   ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
}

Sesola_write_ast (void *sesolaptr)
{
   ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
}

/************************/
#endif  /* ifdef SESOLA */
/************************/

/*****************************************************************************/

