Java程序辅导

C C++ Java Python Processing编程在线培训 程序编写 软件开发 视频讲解

客服在线QQ:2653320439 微信:ittutor Email:itutor@qq.com
wx: cjtutor
QQ: 2653320439
/*****************************************************************************/ /* 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 --------------- 02-FEB-2015 MGD kludge; SesolaNetAccept() SSL3_ST_SR_CLNT_HELLO_C 29-JAN-2015 MGD bugfix; SesolaNetClientBegin() ambiguous WatchThis(SNI) 11-NOV-2014 MGD SesolaNetAccept() collect version, cipher, cached 04-OCT-2013 MGD SesolaNetThisIsSSL() support [ServiceNonSSLRedirect] 03-AUG-2013 MGD SesolaNetClientBegin() include SNI before connect 28-APR-2012 MGD bugfix; SesolaNetAccept() initialise value=0 bugfix; SesolaNetRead() SSL state not SSL_ST_OK bugfix; SesolaNetWrite() SSL state not SSL_ST_OK 04-SEP-2010 MGD Sesola_read_ast() extend HTTP methods check 18-JUL-2010 MGD bugfix; SSL_set_info_callback() not SSL_CTX_set..() 25-APR-2008 MGD increase sanity check error count from 255 to 8192 12-APR-2006 MGD bugfix; SSL_shutdown() problem reported by JPP introduce SesolaNetReadAst() and SesolaNetWriteAst() to defer reset of AST function address used to indicate AST-in-progress in other parts of the code 10-JUN-2005 MGD make EXQUOTA (particularly ASTLM) a little more obvious 14-APR-2005 MGD bugfix; SesolaNetClientShutdown() remove SSL_shutdown() (revealed by ->https: tunnelling shutdown) 21-DEC-2004 MGD bugfix; obscure in Sesesol_read() and Sesola_Write() when WATCHing via SSL due to undereferenced NULL 'rqptr' 17-DEC-2004 MGD bugfix; Sesola_read_ast() and Sesola_write_ast() zero I/O status block count on error status 14-DEC-2004 MGD remove BIO_set_retry_..() BIO_clear_retry_..(), add some sanity checking around reads and writes 17-NOV-2004 MGD bugfix; SesolaNetRead() and SesolaNetWrite() if no I/O and hence no defered AST reset defered AST function pointer (thanks to jpp@esme.fr for isolating this during BETA) 27-OCT-2004 MGD SesolaNetInProgress() raw I/O in progress? (for ProxyEnd()) 21-AUG-2004 MGD significant refinements to SSL processing 22-JUL-2004 MGD changes to SSL shutdown, SesolaNetShutdown() to allow persistent connections over SSL for HTTP/1.1 (and general SSL performance improvements - long overdue) 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_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #define __VMS_VER 70000000 #undef __CRTL_VER #define __CRTL_VER 70000000 #endif /* standard C header files */ #include #include #include #include /* VMS related header files */ #include #include #include /* 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 */ /********************/ extern int EfnWait, EfnNoWait, NetReadBufferSize, SesolaSNI, SesolaSSLversion; extern char *SesolaDefaultCertPtr, *SesolaDefaultCipherListPtr, *SesolaDefaultKeyPtr; extern char ErrorSanityCheck[], SoftwareID[]; extern ACCOUNTING_STRUCT *AccountingPtr; extern WATCH_STRUCT Watch; extern int (*Sesola_SSL_get_servername)(SSL*, int); /*****************************************************************************/ /* 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. */ SesolaNetBegin (REQUEST_STRUCT *rqptr) { int value; SESOLA_STRUCT *sesolaptr; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA))) WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA, "SesolaNetBegin()"); if (!((SESOLA_CONTEXT*)rqptr->ServicePtr->SSLserverPtr)->SslCtx) { ErrorNoticed (rqptr, 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 (rqptr, 0, "SSL_new() failed", FI_LI); if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_SESOLA)) WatchThis (rqptr, FI_LI, WATCH_SESOLA, "BEGIN SSL_new() failed"); SesolaNetFree (rqptr); 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 (rqptr, 0, "BIO_new() failed", FI_LI); if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_SESOLA)) WatchThis (rqptr, FI_LI, WATCH_SESOLA, "BEGIN BIO_new() failed"); SesolaNetFree (rqptr); RequestEnd (rqptr); 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 (rqptr, 0, "BIO_new() failed", FI_LI); if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_SESOLA)) WatchThis (rqptr, FI_LI, WATCH_SESOLA, "BEGIN BIO_new() failed"); SesolaNetFree (rqptr); RequestEnd (rqptr); 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_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 */ SesolaNetAccept (sesolaptr); } /*****************************************************************************/ /* 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. */ SesolaNetAccept (SESOLA_STRUCT *sesolaptr) { int value = 0; char *cptr; SSL *SslPtr; SSL_CIPHER *CipherPtr; REQUEST_STRUCT *rqptr; /*********/ /* begin */ /*********/ rqptr = sesolaptr->RequestPtr; if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA))) WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA, "SesolaNetAccept() !&F !UL", &SesolaNetAccept, sesolaptr->SslAcceptCount); sesolaptr->SslStateFunction = &SesolaNetAccept; /* just a sanity check on the SSL_accept() activity */ if (sesolaptr->SslAcceptCount++ < SESOLA_SSL_ACCEPT_MAX) { value = SSL_accept (sesolaptr->SslPtr); if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA))) WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA, "SSL_accept() !SL !SL", value, SSL_get_error(sesolaptr->SslPtr,value)); #if 1 /**********/ /* KLUDGE */ /**********/ /* OpenSSL v1.0.2 ACCVIOs in s3_srvr.c ssl3_get_client_hello() at ssl3_choose_cipher() when SSLv3 is not enabled and SSLv3 client! OpenSSL v1.0.1k s3_srvr.c ssl3_get_client_hello() is fine!! Can't see how this is a WASD issue (but not impossible). The kludge just works around it by short-circuiting the code path to ssl3_choose_cipher() when seeing an SSLv3 handshake (presumably) errant SSL3_ST_SR_CLNT_HELLO_C (which far as I can make out is only used when (re)negotiating a server certificate). Check with: $ openssl s_client -ssl3 -host -port 443 */ if (SSL_version(sesolaptr->SslPtr) == SSL3_VERSION && SSL_state(sesolaptr->SslPtr) == SSL3_ST_SR_CLNT_HELLO_C && SSL_get_options(sesolaptr->SslPtr) & SSL_OP_NO_SSLv3) { if (WATCHING(rqptr) && (WATCH_CATEGORY(WATCH_SESOLA))) WatchThis (rqptr, FI_LI, WATCH_SESOLA, "KLUDGE: SSL3_ST_SR_CLNT_HELLO_C"); SesolaNetShutdown (sesolaptr); return; } /* if non-blocking IO in progress just return and wait for delivery */ if (sesolaptr->ReadInProgress || sesolaptr->WriteInProgress) return; } #endif sesolaptr->SslStateFunction = NULL; /* this shouldn't have happened! */ if (sesolaptr->ReadInProgress || sesolaptr->WriteInProgress) { /* so knock the socket on the head */ if (sesolaptr->NetChannel) NetCloseSocket (rqptr); return; } if (value <= 0) { if (WATCHING(rqptr) && (WATCH_CATEGORY(WATCH_SESOLA))) { WatchThis (rqptr, FI_LI, WATCH_SESOLA, "BEGIN failed"); SesolaWatchErrors (sesolaptr); } SesolaNetFree (rqptr); RequestEnd (rqptr); return; } SslPtr = sesolaptr->SslPtr; /* user-defined logging purposes */ rqptr->rqNet.SesolaVersionPtr = (char*)SSL_get_version (SslPtr); /* user-defined logging purposes */ if (CipherPtr = SSL_get_current_cipher (SslPtr)) rqptr->rqNet.SesolaCipherPtr = (char*)SSL_CIPHER_get_name (CipherPtr); else rqptr->rqNet.SesolaCipherPtr = "(unknown)"; /* user-defined logging purposes */ rqptr->rqNet.SesolaSessionReused = SslPtr->hit; if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_SESOLA)) SesolaWatchSession (sesolaptr); /* read the (now encrypted) request */ rqptr->rqNet.ReadBufferPtr = VmGetHeap (rqptr, NetReadBufferSize); rqptr->rqNet.ReadBufferSize = NetReadBufferSize; NetRead (rqptr, &RequestGet, rqptr->rqNet.ReadBufferPtr, rqptr->rqNet.ReadBufferSize); } /*****************************************************************************/ /* Shutdown the SSL session with the client (elegantly if we can!) */ SesolaNetShutdown (SESOLA_STRUCT *sesolaptr) { int value; REQUEST_STRUCT *rqptr; /*********/ /* begin */ /*********/ rqptr = sesolaptr->RequestPtr; if (!rqptr->rqNet.SesolaPtr) { /* interim sanity check whilst investigating the 10-APR-2006 issue */ ErrorNoticed (rqptr, SS$_BUGCHECK, ErrorSanityCheck, FI_LI); WatchNoticed (rqptr); return; } if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA))) WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA, "SesolaNetShutdown() !&F !UL count:!UL read:!&B write:!&B", &SesolaNetShutdown, sesolaptr->NetChannel, sesolaptr->SslShutdownCount, sesolaptr->ReadInProgress, sesolaptr->WriteInProgress); /* everything is now going to be coming here! */ sesolaptr->SslStateFunction = &SesolaNetShutdown; if (sesolaptr->NetChannel) { /* only provide explicit shutdown if still connected! */ if (!sesolaptr->SslShutdownCount++) { if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_SESOLA)) WatchThis (rqptr, FI_LI, WATCH_SESOLA, "SHUTDOWN"); /* not going to wait for the client to respond */ SSL_set_shutdown (sesolaptr->SslPtr, SSL_RECEIVED_SHUTDOWN); value = SSL_shutdown (sesolaptr->SslPtr); if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA))) WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA, "SSL_shutdown() !SL !SL", value, SSL_get_error(sesolaptr->SslPtr,value)); } else if (sesolaptr->SslShutdownCount > SESOLA_SSL_SHUTDOWN_MAX) { /* sanity check, so knock the socket on the head */ if (sesolaptr->NetChannel) NetCloseSocket (rqptr); } } /* intercept any outstanding I/O */ if (sesolaptr->ReadInProgress || sesolaptr->WriteInProgress) return; if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_SESOLA)) WatchThis (rqptr, FI_LI, WATCH_SESOLA, "END"); SesolaNetFree (rqptr); RequestConnection (rqptr); } /*****************************************************************************/ /* Free the OpenSSL structures outside of any use of them (early mistake)! */ SesolaNetFree (REQUEST_STRUCT *rqptr) { int value; SESOLA_STRUCT *sesolaptr; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA))) WatchThis (rqptr, FI_LI, WATCH_MOD_SESOLA, "SesolaNetFree()"); 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; } /*****************************************************************************/ /* 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. Note that this is proxy functionality and so proxy request rundown functions are used. */ 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 (!svptr->SSLclientPtr) { if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_SESOLA)) WatchThis (rqptr, FI_LI, WATCH_SESOLA, "CLIENT SSL not configured"); ProxyEnd (tkptr); return; } if (!((SESOLA_CONTEXT*)svptr->SSLclientPtr)->SslCtx) { ErrorNoticed (rqptr, 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 (rqptr, 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 (rqptr, 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 (rqptr, 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); /* Server Name Indication (SNI) */ if (SesolaSNI && Sesola_SSL_get_servername) { /* if the SSL_get_servername() exists then this control should work */ value = SSL_set_tlsext_host_name (sesolaptr->SslPtr, tkptr->RequestHostName); if (WATCHING(tkptr) && WATCH_CATEGORY(WATCH_SESOLA)) WatchThis (WATCHTK(tkptr), FI_LI, WATCH_SESOLA, "SNI !AZ!AZ", tkptr->RequestHostName, value ? "" : " FAILED"); } 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_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 (sesolaptr); } /*****************************************************************************/ /* 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 (SESOLA_STRUCT *sesolaptr) { int status, value; PROXY_TASK *tkptr; REQUEST_STRUCT *rqptr; /*********/ /* begin */ /*********/ /* get the associate proxy task and request pointers */ tkptr = sesolaptr->ProxyTaskPtr; rqptr = tkptr->RequestPtr; if (WATCHING(tkptr) && (WATCH_MODULE(WATCH_MOD_SESOLA))) WatchThis (WATCHTK(tkptr), FI_LI, WATCH_MOD_SESOLA, "SesolaNetClientConnect() !&F !UL", &SesolaNetClientConnect, sesolaptr->SslConnectCount); sesolaptr->SslStateFunction = &SesolaNetClientConnect; /* just a sanity check on the SSL_connect() activity */ if (sesolaptr->SslConnectCount++ < SESOLA_SSL_CONNECT_MAX) { if (sesolaptr->HTTPduringHandshake) { 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 non-blocking IO in progress just return and wait for delivery */ if (sesolaptr->ReadInProgress || sesolaptr->WriteInProgress) return; if (sesolaptr->HTTPduringHandshake) { rqptr->rqResponse.HttpStatus = 501; ErrorGeneral (rqptr, MsgFor(rqptr,MSG_HTTP_501), FI_LI); SesolaNetClientFree (tkptr); ProxyEnd (tkptr); return; } } /* can't free structures while non-blocking I/O still outstanding */ if (sesolaptr->ReadInProgress || sesolaptr->WriteInProgress) { /* so knock the socket on the head */ if (sesolaptr->NetChannel) ProxyNetCloseSocket (tkptr); 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->SslStateFunction = NULL; if (tkptr->ProxyTunnel) ProxyTunnelBegin (tkptr); else ProxyWriteRequest (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! */ SesolaNetClientShutdown (SESOLA_STRUCT *sesolaptr) { int value; PROXY_TASK *tkptr; /*********/ /* begin */ /*********/ tkptr = sesolaptr->ProxyTaskPtr; if (!tkptr->SesolaPtr) { /* interim sanity check whilst investigating the 10-APR-2006 issue */ ErrorNoticed (tkptr->RequestPtr, SS$_BUGCHECK, "SesolaNetClientShutdown()", FI_LI); WatchNoticed (tkptr->RequestPtr); return; } if (WATCHING(tkptr) && (WATCH_MODULE(WATCH_MOD_SESOLA))) WatchThis (WATCHTK(tkptr), FI_LI, WATCH_MOD_SESOLA, "SesolaNetClientShutdown() !&F !UL !UL !&B !&B", &SesolaNetClientShutdown, sesolaptr->NetChannel, sesolaptr->SslShutdownCount, sesolaptr->ReadInProgress, sesolaptr->WriteInProgress); /* everything is now going to be coming here! */ sesolaptr->SslStateFunction = &SesolaNetClientShutdown; /* only provide explicit shutdown if still connected! */ if (sesolaptr->NetChannel) { /* just a sanity check on the SSL_shutdown() activity */ if (!sesolaptr->SslShutdownCount++) { if (WATCHING(tkptr) && WATCH_CATEGORY(WATCH_SESOLA)) WatchThis (WATCHTK(tkptr), FI_LI, WATCH_SESOLA, "SHUTDOWN"); /* not going to wait for the server to respond */ SSL_set_shutdown (sesolaptr->SslPtr, SSL_RECEIVED_SHUTDOWN); } else if (sesolaptr->SslShutdownCount > SESOLA_SSL_SHUTDOWN_MAX) { /* sanity check, so knock the socket on the head */ if (sesolaptr->NetChannel) ProxyNetCloseSocket (tkptr); } } /* intercept any outstanding I/O */ 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; } /*****************************************************************************/ /* 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 (!(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); 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 !SL", value, SSL_get_error(sesolaptr->SslPtr,value)); /* if non-blocking IO in progress just return and wait for delivery */ if (sesolaptr->ReadInProgress) return (SS$_NORMAL); if (SSL_state (sesolaptr->SslPtr) != SSL_ST_OK) { /* currently in some SSL state other than OK */ sesolaptr->ReadIOsb.Status = SS$_ABORT; sesolaptr->ReadIOsb.Count = 0; } else if (VMSok (sesolaptr->ReadIOsb.Status)) { /* zero returned if received SSL shutdown */ if (value == 0) sesolaptr->ReadIOsb.Status = SS$_ABORT; /* set the I/O status block count to decrypted data count */ sesolaptr->ReadIOsb.Count = value; } else sesolaptr->ReadIOsb.Count = 0; if (AstFunction) SysDclAst (SesolaNetReadAst, sesolaptr); return (sesolaptr->ReadIOsb.Status); } /*****************************************************************************/ /* Deliver the AST defering the NULLing of the 'ReadAstFunction' so that the AST continues to be outstanding until it is actually delivered here. */ SesolaNetReadAst (SESOLA_STRUCT *sesolaptr) { GENERAL_AST AstFunction; REQUEST_STRUCT *rqptr; /*********/ /* begin */ /*********/ 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, "SesolaNetReadAst() !&F !&S !UL", &SesolaNetReadAst, sesolaptr->ReadIOsb.Status, sesolaptr->ReadIOsb.Count); AstFunction = sesolaptr->ReadAstFunction; sesolaptr->ReadAstFunction = NULL; if (sesolaptr->RequestPtr) { memcpy (&sesolaptr->RequestPtr->rqNet.ReadIOsb, &sesolaptr->ReadIOsb, sizeof(sesolaptr->RequestPtr->rqNet.ReadIOsb)); (*AstFunction)(sesolaptr->RequestPtr); } else if (sesolaptr->ProxyTaskPtr) { memcpy (&sesolaptr->ProxyTaskPtr->ProxyReadIOsb, &sesolaptr->ReadIOsb, sizeof(sesolaptr->ProxyTaskPtr->ProxyReadIOsb)); (*AstFunction)(sesolaptr->ProxyTaskPtr); } else ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } /*****************************************************************************/ /* 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 (!(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); if (sesolaptr->ReadInProgress) { /* asynchronous read in progress, retry next call */ return (-1); } if (sesolaptr->ReadCompleted) { /* previous non-blocking read */ sesolaptr->ReadCompleted = false; /* in case of an error give it zero (shutdown) */ if (VMSok (sesolaptr->ReadIOsb.Status)) return (sesolaptr->ReadIOsb.Count); else return (0); } if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_NETWORK_OCTETS)) WatchThis (rqptr, FI_LI, WATCH_NETWORK, "READ !UL bytes max (!&?non-blocking\rblocking\r)", DataSize, sesolaptr->ReadAstFunction || sesolaptr->SslStateFunction); if (sesolaptr->ReadAstFunction || sesolaptr->SslStateFunction) { /*******************/ /* non-blocking IO */ /*******************/ /* indicate (in part to the AST) that non-blocking I/O in progress */ sesolaptr->ReadInProgress = true; sesolaptr->ReadCompleted = false; 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->ReadRawDataPtr = 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 */ ErrorNoticed (rqptr, status, "sys$qio", FI_LI); sesolaptr->ReadIOsb.Status = status; sesolaptr->ReadIOsb.Count = 0; SysDclAst (&Sesola_read_ast, sesolaptr); return (-1); } } else { /***************/ /* blocking IO */ /***************/ sesolaptr->ReadInProgress = sesolaptr->ReadCompleted = false; 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->ReadRawDataPtr = 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 */ sys$canexh(0); /* make the message a little more meaningful */ sys$exit (SS$_EXASTLM); } /*****************************************************************************/ /* 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 (!(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); 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 (!&?non-blocking\rblocking\r)", sesolaptr->ReadIOsb.Status, sesolaptr->ReadIOsb.Count, sesolaptr->WriteAstFunction || sesolaptr->SslStateFunction); if (WATCH_CATEGORY(WATCH_NETWORK_OCTETS)) WatchDataDump (sesolaptr->ReadRawDataPtr, sesolaptr->ReadIOsb.Count); } else WatchThis (rqptr, FI_LI, WATCH_NETWORK, "READ !&S (!&?non-blocking\rblocking\r)", sesolaptr->ReadIOsb.Status, sesolaptr->WriteAstFunction || sesolaptr->SslStateFunction); } } if (VMSok (sesolaptr->ReadIOsb.Status)) { if (!sesolaptr->ReadIOsb.Count) { WatchNoticed (rqptr); ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } sesolaptr->Sesola_read_ErrorCount = 0; if (sesolaptr->RequestPtr) { if (!sesolaptr->RequestPtr->BytesRawRx[0]) { /* first read SSL_accept() handshake, check for HTTP request */ if (SAME4 (sesolaptr->ReadRawDataPtr, 'GET ') || SAME4 (sesolaptr->ReadRawDataPtr, 'HEAD') || SAME4 (sesolaptr->ReadRawDataPtr, 'POST') || SAME4 (sesolaptr->ReadRawDataPtr, 'CONN') || SAME4 (sesolaptr->ReadRawDataPtr, 'COPY') || SAME4 (sesolaptr->ReadRawDataPtr, 'DELE') || SAME4 (sesolaptr->ReadRawDataPtr, 'LOCK') || SAME4 (sesolaptr->ReadRawDataPtr, 'MKCO') || SAME4 (sesolaptr->ReadRawDataPtr, 'MOVE') || SAME4 (sesolaptr->ReadRawDataPtr, 'OPTI') || SAME4 (sesolaptr->ReadRawDataPtr, 'PROP') || SAME4 (sesolaptr->ReadRawDataPtr, 'PUT ') || SAME4 (sesolaptr->ReadRawDataPtr, 'TRAC') || SAME4 (sesolaptr->ReadRawDataPtr, 'UNLO')) { sesolaptr->HTTPduringHandshake = true; SesolaNetThisIsSSL (sesolaptr); return; } } ADD_LONG_QUAD (sesolaptr->ReadIOsb.Count, sesolaptr->RequestPtr->BytesRawRx); } else if (sesolaptr->ProxyTaskPtr) { if (QUAD_ZERO(sesolaptr->ProxyTaskPtr->BytesRawRx)) { /* first read SSL_connect() handshake, check for HTTP response */ if (MATCH5 (sesolaptr->ReadRawDataPtr, "HTTP/")) sesolaptr->HTTPduringHandshake = true; } ADD_LONG_QUAD (sesolaptr->ReadIOsb.Count, sesolaptr->ProxyTaskPtr->BytesRawRx); } } else { if (sesolaptr->Sesola_read_ErrorCount++ > 8192) { WatchNoticed (rqptr); ErrorExitVmsStatus (sesolaptr->ReadIOsb.Status, ErrorSanityCheck, FI_LI); } if (sesolaptr->ReadIOsb.Status != SS$_LINKDISCON && sesolaptr->ReadIOsb.Status != SS$_CONNECFAIL && sesolaptr->ReadIOsb.Status != SS$_ABORT && sesolaptr->ReadIOsb.Status != SS$_CANCEL && sesolaptr->ReadIOsb.Status != SS$_TIMEOUT && sesolaptr->ReadIOsb.Status != SS$_IVCHAN) ErrorNoticed (rqptr, sesolaptr->ReadIOsb.Status, NULL, FI_LI); sesolaptr->ReadIOsb.Count = 0; } /* if blocking I/O then just return */ if (!sesolaptr->ReadInProgress) return; sesolaptr->ReadInProgress = false; sesolaptr->ReadCompleted = true; if (sesolaptr->SslStateFunction) SysDclAst (sesolaptr->SslStateFunction, sesolaptr); else if (sesolaptr->ReadAstFunction) SesolaNetRead (sesolaptr, sesolaptr->ReadAstFunction, sesolaptr->ReadDataPtr, sesolaptr->ReadDataSize); } /*****************************************************************************/ /* 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 (!(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); 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 !SL", value, SSL_get_error(sesolaptr->SslPtr,value)); /* if non-blocking IO in progress just return and wait for delivery */ if (sesolaptr->WriteInProgress) return (SS$_NORMAL); if (SSL_state (sesolaptr->SslPtr) != SSL_ST_OK) { /* currently in some SSL state other than OK */ sesolaptr->WriteIOsb.Status = SS$_ABORT; sesolaptr->WriteIOsb.Count = 0; } else if (VMSok (sesolaptr->WriteIOsb.Status)) { /* zero returned if received SSL shutdown */ if (value == 0) sesolaptr->WriteIOsb.Status = SS$_ABORT; /* set the I/O status block count to reflect the decrypted data count */ sesolaptr->WriteIOsb.Count = value; } else sesolaptr->WriteIOsb.Count = 0; if (AstFunction) SysDclAst (SesolaNetWriteAst, sesolaptr); return (sesolaptr->WriteIOsb.Status); } /*****************************************************************************/ /* Deliver the AST defering the NULLing of the 'WriteAstFunction' so that the AST continues to be outstanding until it is actually delivered here. */ int SesolaNetWriteAst (SESOLA_STRUCT *sesolaptr) { GENERAL_AST AstFunction; REQUEST_STRUCT *rqptr; /*********/ /* begin */ /*********/ 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, "SesolaNetWriteAst() !&F !&S !UL", &SesolaNetWriteAst, sesolaptr->WriteIOsb.Status, sesolaptr->WriteIOsb.Count); AstFunction = sesolaptr->WriteAstFunction; sesolaptr->WriteAstFunction = NULL; if (sesolaptr->RequestPtr) { memcpy (&sesolaptr->RequestPtr->rqNet.WriteIOsb, &sesolaptr->WriteIOsb, sizeof(sesolaptr->RequestPtr->rqNet.WriteIOsb)); (*AstFunction)(sesolaptr->RequestPtr); } else if (sesolaptr->ProxyTaskPtr) { memcpy (&sesolaptr->ProxyTaskPtr->ProxyWriteIOsb, &sesolaptr->WriteIOsb, sizeof(sesolaptr->ProxyTaskPtr->ProxyWriteIOsb)); (*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 (!(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); if (sesolaptr->WriteInProgress) { /* asynchronous write in progress, retry next call */ return (-1); } if (sesolaptr->WriteCompleted) { /* previous non-blocking write */ sesolaptr->WriteCompleted = false; /* if an error return zero, detected by SesolaNetWrite() */ if (VMSok (sesolaptr->WriteIOsb.Status)) return (sesolaptr->WriteIOsb.Count); else return (0); } if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_NETWORK_OCTETS)) { WatchThis (rqptr, FI_LI, WATCH_NETWORK, "WRITE !UL bytes (!&?non-blocking\rblocking\r)", DataLength, sesolaptr->WriteAstFunction || sesolaptr->SslStateFunction); WatchDataDump (DataPtr, DataLength); } if (sesolaptr->WriteAstFunction || sesolaptr->SslStateFunction) { /*******************/ /* non-blocking IO */ /*******************/ /* indicate (in part to the AST) that non-blocking I/O in progress */ sesolaptr->WriteInProgress = true; sesolaptr->WriteCompleted = false; 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 */ if (status != SS$_IVCHAN) ErrorNoticed (rqptr, status, "sys$qio", FI_LI); sesolaptr->WriteIOsb.Status = status; sesolaptr->WriteIOsb.Count = 0; SysDclAst (&Sesola_write_ast, sesolaptr); return (-1); } } else { /***************/ /* blocking IO */ /***************/ sesolaptr->WriteInProgress = sesolaptr->WriteCompleted = false; 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 */ sys$canexh(0); /* make the message a little more meaningful */ sys$exit (SS$_EXASTLM); } /*****************************************************************************/ /* Post-process the write for both blocking and non-block I/O. */ Sesola_write_ast (SESOLA_STRUCT *sesolaptr) { REQUEST_STRUCT *rqptr; /*********/ /* begin */ /*********/ 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); 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", sesolaptr->WriteIOsb.Status); } if (WATCH_CATEGORY(WATCH_RESPONSE)) if (VMSnok (sesolaptr->WriteIOsb.Status)) WatchThis (rqptr, FI_LI, WATCH_RESPONSE, "NETWORK !&S", sesolaptr->WriteIOsb.Status); } if (VMSok (sesolaptr->WriteIOsb.Status)) { if (!sesolaptr->WriteIOsb.Count) { WatchNoticed (rqptr); ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } sesolaptr->Sesola_write_ErrorCount = 0; if (sesolaptr->RequestPtr) ADD_LONG_QUAD (sesolaptr->WriteIOsb.Count, sesolaptr->RequestPtr->BytesRawTx) else if (sesolaptr->ProxyTaskPtr) ADD_LONG_QUAD (sesolaptr->WriteIOsb.Count, sesolaptr->ProxyTaskPtr->BytesRawTx) } else { if (sesolaptr->Sesola_write_ErrorCount++ > 8192) { WatchNoticed (rqptr); ErrorExitVmsStatus (sesolaptr->WriteIOsb.Status, ErrorSanityCheck, FI_LI); } if (sesolaptr->WriteIOsb.Status != SS$_LINKDISCON && sesolaptr->WriteIOsb.Status != SS$_CONNECFAIL && sesolaptr->WriteIOsb.Status != SS$_ABORT && sesolaptr->WriteIOsb.Status != SS$_CANCEL && sesolaptr->WriteIOsb.Status != SS$_TIMEOUT && sesolaptr->WriteIOsb.Status != SS$_IVCHAN) ErrorNoticed (rqptr, sesolaptr->WriteIOsb.Status, NULL, FI_LI); sesolaptr->WriteIOsb.Count = 0; } /* if 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->SslStateFunction) SysDclAst (sesolaptr->SslStateFunction, sesolaptr); else if (sesolaptr->WriteAstFunction) SesolaNetWrite (sesolaptr, sesolaptr->WriteAstFunction, sesolaptr->WriteDataPtr, sesolaptr->WriteDataLength); } /*****************************************************************************/ /* Any outstanding network (raw) I/O? Return true if there is. */ BOOL SesolaNetInProgress (SESOLA_STRUCT *sesolaptr) { /*********/ /* begin */ /*********/ #if WATCH_MOD REQUEST_STRUCT *rqptr; if (!sesolaptr) return (false); /* 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, "SesolaNetInProgress() state:!&B read:!&B write:!&B", sesolaptr->SslStateFunction, sesolaptr->ReadAstFunction, sesolaptr->WriteAstFunction); #endif /* WATCH_MOD */ if (!sesolaptr) return (false); if (sesolaptr->SslStateFunction) return (true); if (sesolaptr->ReadAstFunction) return (true); if (sesolaptr->WriteAstFunction) return (true); return (false); } /*****************************************************************************/ /* 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; } /*****************************************************************************/ /* Plain-text HTTP has been detected arriving on an SSL service. Return either an error message or redirect response (if configured). The redirect parameter can comprise an optional leading HTTP response code 301, 302, or 307 (default), followed by an optional host name or IP address, and optional port number (port must have leading colon if only parameter), and even an optional URI. The minimum parameter is a single colon, which redirects to the same service name on port 80. If the parameter contains a slash (i.e. contains a URI) then it is used as-is. If it does not contain a slash (i.e. contains no URI) the URI from the request is appended to the redirect host. A 307 is used to indicate to the agent that a non-GET should not be turned into one! Something that apparently commonly (incorrectly) happens with 302s. */ SesolaNetThisIsSSL (SESOLA_STRUCT *sesolaptr) { static char ThisIsSSLFao [] = "HTTP/1.0 400 This is an SSL service!!\r\n\ Server: !AZ\r\n\ Content-Type: text/html\r\n\ \r\n\ 400 error - This is an SSL service!!\n"; static char RedirectFao [] = "HTTP/1.0 !UL Redirection\r\n\ Server: !AZ\r\n\ Location: http://!AZ!AZ!AZ\r\n\ Content-Type: text/html\r\n\ \r\n\ !UL redirection - http://!AZ!AZ!AZ\n"; int code, status; unsigned short slen; char *aptr, *cptr, *sptr, *uptr, *zptr; char NonSslRedirect [256], ResponseBuffer [4096]; 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()"); /* no longer the case */ sesolaptr->ReadInProgress = false; sesolaptr->ReadCompleted = true; for (cptr = rqptr->ServicePtr->NonSslRedirect; *cptr == ' '; cptr++); if (*cptr) { /************/ /* redirect */ /************/ if (!rqptr->rqNet.ReadBufferSize) { /* first call, allocate a network buffer and fudge */ rqptr->rqNet.ReadBufferPtr = VmGetHeap (rqptr, NetReadBufferSize); rqptr->rqNet.ReadBufferSize = NetReadBufferSize; cptr = sesolaptr->ReadRawDataPtr; zptr = (sptr = rqptr->rqNet.ReadBufferPtr) + rqptr->rqNet.ReadBufferSize; while (sptr < zptr && sesolaptr->ReadIOsb.Count--) *sptr++ = *cptr++; cptr = &sesolaptr->ReadIOsb; zptr = (sptr = &rqptr->rqNet.ReadIOsb) + sizeof(rqptr->rqNet.ReadIOsb); while (sptr < zptr) *sptr++ = *cptr++; } if (VMSnok (rqptr->rqNet.ReadIOsb.Status)) { /* network read failed */ SysDclAst (sesolaptr->SslStateFunction, sesolaptr); return; } /* look for a (request) line terminator */ zptr = (cptr = rqptr->rqNet.ReadBufferPtr) + rqptr->rqNet.ReadBufferSize; while (cptr < zptr && *cptr && *cptr != '\r' && *cptr != '\n') cptr++; if (cptr >= zptr) { /* exhausted buffer space without getting a request line */ SysDclAst (sesolaptr->ReadAstFunction, sesolaptr); return; } if (!*cptr) { /* not seen a line terminator, read more */ slen = rqptr->rqNet.ReadBufferPtr + rqptr->rqNet.ReadBufferSize; slen -= cptr - rqptr->rqNet.ReadBufferPtr; NetReadRaw (rqptr, SesolaNetThisIsSSLAst, cptr, slen); return; } /*********************/ /* have request line */ /*********************/ if (WATCHING(rqptr) && (WATCH_MODULE(WATCH_MOD_SESOLA))) WatchData (rqptr->rqNet.ReadBufferPtr, -1); /* buffer the directive */ for (cptr = rqptr->ServicePtr->NonSslRedirect; *cptr == ' '; cptr++); zptr = (sptr = NonSslRedirect) + sizeof(NonSslRedirect)-1; while (*cptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; cptr = NonSslRedirect; if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_RESPONSE)) WatchThis (rqptr, FI_LI, WATCH_RESPONSE, "REDIRECT !AZ", cptr); /* optional HTTP response code - default is 307 */ if (isdigit(*cptr)) { code = atoi(cptr); if (code != 301 && code != 302 && code != 303) code = 307; while (*cptr && (isdigit(*cptr) || ISLWS(*cptr))) cptr++; } else code = 307; if (*cptr == ':') { /* default to current service name */ sptr = rqptr->ServicePtr->ServerHostName; /* if no trailing number then no port - default will be 80 */ if (!isdigit(*(cptr+1))) cptr++; } else sptr = ""; /* if no directive URI then append the request URI */ for (uptr = cptr; *uptr && *uptr != '/'; uptr++); if (!*uptr) { uptr = rqptr->rqNet.ReadBufferPtr; /* span across the method and space to the URI */ while (*uptr && !ISLWS(*uptr)) uptr++; while (*uptr && ISLWS(*uptr)) uptr++; /* find the end of the URI */ for (aptr = uptr; *uptr && !ISLWS(*aptr); aptr++); *aptr = '\0'; if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_RESPONSE)) WatchThis (rqptr, FI_LI, WATCH_RESPONSE, "URI !AZ", uptr); } else uptr = ""; status = FaoToBuffer (ResponseBuffer, sizeof(ResponseBuffer)-1, &slen, RedirectFao, code, SoftwareID, sptr, cptr, uptr, code, sptr, cptr, uptr); rqptr->rqResponse.HttpStatus = code; } else { /***********/ /* message */ /***********/ status = FaoToBuffer (ResponseBuffer, sizeof(ResponseBuffer)-1, &slen, ThisIsSSLFao, SoftwareID); rqptr->rqResponse.HttpStatus = 400; } if (VMSnok(status)) ErrorNoticed (rqptr, status, "FaoToBuffer()", FI_LI); /*******************/ /* write to client */ /*******************/ if (WATCHING(rqptr) && (WATCH_CATEGORY(WATCH_RESPONSE_HEADER) || WATCH_CATEGORY(WATCH_RESPONSE_BODY))) WatchData (ResponseBuffer, slen); sys$qiow (EfnWait, sesolaptr->NetChannel, IO$_WRITEVBLK, &IOsb, 0, 0, ResponseBuffer, slen, 0, 0, 0, 0); /* update status code accumulator */ InstanceMutexLock (INSTANCE_MUTEX_HTTPD); RequestHttpStatusCode (rqptr); InstanceMutexUnLock (INSTANCE_MUTEX_HTTPD); SysDclAst (sesolaptr->SslStateFunction, sesolaptr); } /*****************************************************************************/ /* Transmogrify an AST with request pointer to one with SSL pointer. */ SesolaNetThisIsSSLAst (REQUEST_STRUCT *rqptr) { /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_SESOLA)) WatchThis (NULL, FI_LI, WATCH_MOD_SESOLA, "SesolaNetThisIsSSLAst()"); SesolaNetThisIsSSL (rqptr->rqNet.SesolaPtr); } /*****************************************************************************/ /* 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: return (1); case BIO_CTRL_EOF: return (1); case BIO_CTRL_SET: bioptr->num = Number; return (1); case BIO_CTRL_SET_CLOSE: return (1); case BIO_CTRL_FLUSH: return (1); case BIO_CTRL_DUP: return (1); case BIO_CTRL_GET_CLOSE: return (0); case BIO_CTRL_INFO: return (0); case BIO_CTRL_GET: return (0); case BIO_CTRL_PENDING: return (0); case BIO_CTRL_POP: return (0); case BIO_CTRL_PUSH: return (0); case BIO_CTRL_WPENDING: return (0); default: 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 (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 */ /*********************/ /* external storage */ extern char ErrorSanityCheck[]; SesolaNetBegin (REQUEST_STRUCT *rqptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } SesolaNetRequestEnd (REQUEST_STRUCT *rqptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } SesolaNetClientBegin (PROXY_TASK *tkptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } SesolaNetClientShutdown (void *sesolaptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } SesolaNetInProgress (void *sesolaptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } SesolaNetShutdown (void *sesolaptr) { 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); } SesolaNetWriteAst (void *sesolaptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } SesolaNetRead ( void *sesolaptr, GENERAL_AST AstFunction, char *DataPtr, int DataSize ) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } SesolaNetReadAst (void *sesolaptr) { 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); } SesolaNetAccept (void *sesolaptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } SesolaNetClientConnect (void *sesolaptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } /************************/ #endif /* ifdef SESOLA */ /************************/ /*****************************************************************************/