/*****************************************************************************/ /* SesolaNetIo.c Handles the a/synchronous $QIO with the network for SSL/TLS encrypted traffic. Is the SSL/TLS equivalent of NETIO.C Reading and writing data size now have no architectural limit. While the OpenSSL BIO infrastructure could always perform it, WASD now reads and writes exceeding 65535 bytes per call. It's handled internally. With the potential for data size/length greater than a single QIO the IO status block is deprecated in favour of |ioptr->Read/WriteCount| and |ioptr->Read/WriteStatus|. VERSION HISTORY --------------- 02-SEP-2021 MGD bugfix; SesolaNetIoRead() /bytes = value/ 18-APR-2020 MGD NetIoQioMaxSeg() tune QIO to TCP MSS 27-FEB-2020 MGD Sesola_netio_.._ex() seems to be the OpenSSL 1.1.n approach 18-JUN-2019 MGD Sesola_netio_read_ast() 0 status TCP/IP Services? Sesola_netio_write_ast() 0 status TCP/IP Services? 04-FEB-2019 MGD Sesola_netio_read() and Sesola_netio_write() if connection broken (channel zero) return zero (SSL shutdown) 17-MAR-2017 MGD bugfix; SesolaNetIoRead() SSL_read() in-progress 05-JUN-2016 MGD SesolaNetIoRead() renegotiate application data buffer 11-AUG-2015 MGD restructure of network I/O abstractions */ /*****************************************************************************/ #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 "SESOLANETIO" /***************************************/ #ifdef SESOLA /* secure sockets layer */ /***************************************/ /******************/ /* global storage */ /******************/ /********************/ /* external storage */ /********************/ extern int EfnWait, EfnNoWait; extern char ErrorSanityCheck[]; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* 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 SesolaNetIoWrite ( NETIO_STRUCT *ioptr, void *DataPtr, int DataLength ) { int bytes, value; SESOLA_STRUCT *sesolaptr; /*********/ /* begin */ /*********/ if (WATCHMOD (ioptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(ioptr), WATCH_MOD_SESOLA, "SesolaNetIoWrite() !&X !&X !UL", ioptr, DataPtr, DataLength); sesolaptr = ioptr->SesolaPtr; if (DataPtr) { /* primary call not AST recall */ if (ioptr->VmsStatus) { /* deliver explicitly set status */ ioptr->WriteCount = 0; ioptr->WriteStatus = ioptr->VmsStatus; if (ioptr->WriteAstFunction) SysDclAst (SesolaNetIoWriteAst, ioptr); return (ioptr->WriteStatus); } sesolaptr->WritePtr = DataPtr; sesolaptr->WriteLength = DataLength; sesolaptr->WriteCount = ioptr->WriteCount = 0; sesolaptr->WriteIOsb.Count = 0; sesolaptr->WriteIOsb.Status = 0; if (WATCHING (ioptr, WATCH_NETWORK_OCTETS)) { WatchThis (WATCHITM(ioptr), WATCH_NETWORK, "WRITE !UL bytes PLAIN (!&?non-blocking\rblocking\r)", DataLength, SESOLA_NETIO_WRITE_IN_PROGRESS(sesolaptr)); WatchDataDump (DataPtr, DataLength); } } else { /* AST recall */ DataPtr = sesolaptr->WritePtr; DataLength = sesolaptr->WriteLength; } if (!sesolaptr->WriteLength) ioptr->VmsStatus = SS$_BUGCHECK; ERR_clear_error (); bytes = 0; #if SESOLA_SINCE_110 value = SSL_write_ex (sesolaptr->SslPtr, DataPtr, DataLength, &bytes); #else value = SSL_write (sesolaptr->SslPtr, DataPtr, DataLength); #endif if (WATCHMOD (ioptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(ioptr), WATCH_MOD_SESOLA, "SSL_write() !SL !SL !SL !&B", value, SSL_get_error(sesolaptr->SslPtr,value), bytes, sesolaptr->WriteInProgress); /* if non-blocking I/O in progress just return and wait for delivery */ if (sesolaptr->WriteInProgress) return (SS$_NORMAL); if (ioptr->VmsStatus) sesolaptr->WriteIOsb.Status = ioptr->VmsStatus; ioptr->WriteCount = 0; if (VMSok (sesolaptr->WriteIOsb.Status)) { if (SSL_is_init_finished(sesolaptr->SslPtr)) { if (value > 0) { #if SESOLA_SINCE_110 sesolaptr->WriteCount = bytes; ioptr->WriteCount = bytes; #else sesolaptr->WriteCount = value; ioptr->WriteCount = value; #endif ioptr->WriteStatus = SS$_NORMAL; } else ioptr->WriteStatus = sesolaptr->WriteIOsb.Status; } else ioptr->WriteStatus = SS$_ABORT; } else ioptr->WriteStatus = sesolaptr->WriteIOsb.Status; if (WATCHMOD (ioptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(ioptr), WATCH_MOD_SESOLA, "!&S !UL", ioptr->WriteStatus, ioptr->WriteCount); /* update the stats of the underlying network I/O structure */ ioptr->BytesRawTx64 += sesolaptr->BytesTallyTx64; ioptr->BytesTallyTx64 += sesolaptr->BytesTallyTx64; ioptr->BlocksRawTx64 += sesolaptr->BlocksTallyTx64; ioptr->BlocksTallyTx64 += sesolaptr->BlocksTallyTx64; /* reset the intermediate accumulators used to update the stats */ sesolaptr->BytesTallyTx64 = 0; sesolaptr->BlocksTallyTx64 = 0; /* decouple the AST delivery */ if (ioptr->WriteAstFunction) SysDclAst (SesolaNetIoWriteAst, ioptr); return (ioptr->WriteStatus); } /*****************************************************************************/ /* This function decouples SesolaNetIoWrite() from the AST delivery where it might be recalled to make another write. */ void SesolaNetIoWriteAst (NETIO_STRUCT *ioptr) { void *AstParam; VOID_AST AstFunction; /*********/ /* begin */ /*********/ if (WATCHMOD (ioptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(ioptr), WATCH_MOD_SESOLA, "SesolaNetIoWriteAst()"); AstParam = ioptr->WriteAstParam; AstFunction = ioptr->WriteAstFunction; ioptr->WriteAstFunction = ioptr->WriteAstParam = NULL; if (AstFunction) AstFunction (AstParam); } /*****************************************************************************/ /* Implements a required function of a OpenSSL BIO_METHOD. */ #if SESOLA_BEFORE_110 int Sesola_netio_write ( BIO *bioptr, char *DataPtr, int DataLength ) { int bytes = 0; Sesola_netio_write_ex (bioptr, DataPtr, DataLength, &bytes); return (bytes); } #endif int Sesola_netio_write_ex ( BIO *bioptr, char *DataPtr, int DataLength, int *BytesPtr ) { int status; NETIO_STRUCT *ioptr; SESOLA_STRUCT *sesolaptr; /*********/ /* begin */ /*********/ #if SESOLA_SINCE_110 sesolaptr = (SESOLA_STRUCT*)BIO_get_data(bioptr); #else sesolaptr = (SESOLA_STRUCT*)bioptr->ptr; #endif ioptr = sesolaptr->NetIoPtr; if (WATCHMOD (ioptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(ioptr), WATCH_MOD_SESOLA, "Sesola_netio_write_ex() !&X !&X !UL !&X !&B !&B", ioptr, DataPtr, DataLength, BytesPtr, sesolaptr->WriteInProgress, sesolaptr->WriteInProgress); if (sesolaptr->WriteIsComplete) { /* previous non-blocking write is now complete */ sesolaptr->WriteInProgress = sesolaptr->WriteIsComplete = false; if (VMSok (sesolaptr->WriteIOsb.Status)) { *BytesPtr = sesolaptr->WriteIOsb.Count; return (1); } return (0); } /* if asynchronous write in progress, retry next call */ if (sesolaptr->WriteInProgress) return (0); /* in case of connection dropped */ if (!ioptr->Channel) { BIO_clear_retry_flags (sesolaptr->BioPtr); return (0); } /* no empty writes! */ if (!DataLength) { BIO_clear_retry_flags (sesolaptr->BioPtr); return (0); } if (WATCHING (ioptr, WATCH_NETWORK_OCTETS)) { WatchThis (WATCHITM(ioptr), WATCH_NETWORK, "WRITE !UL bytes CIPHER (!&?non-blocking\rblocking\r)", DataLength, SESOLA_NETIO_WRITE_IN_PROGRESS(sesolaptr)); WatchDataDump (DataPtr, DataLength); } if (DataLength > ioptr->QioMaxSeg) DataLength = ioptr->QioMaxSeg; if (SESOLA_NETIO_WRITE_IN_PROGRESS(sesolaptr)) { /*******************/ /* non-blocking IO */ /*******************/ BIO_set_retry_write (bioptr); sesolaptr->WriteInProgress = true; sesolaptr->WriteIsComplete = false; status = sys$qio (EfnNoWait, ioptr->Channel, IO$_WRITEVBLK, &sesolaptr->WriteIOsb, &Sesola_netio_write_ast, sesolaptr, sesolaptr->WriteRawPtr = DataPtr, DataLength, 0, 0, 0, 0); if (VMSok (status)) return (0); if (status != SS$_EXQUOTA) { /* report the status via the AST */ if (status != SS$_IVCHAN) ErrorNoticed (NULL, status, "sys$qio", FI_LI); sesolaptr->WriteIOsb.Status = status; sesolaptr->WriteIOsb.Count = 0; SysDclAst (&Sesola_netio_write_ast, sesolaptr); return (0); } } else { /***************/ /* blocking IO */ /***************/ sesolaptr->WriteInProgress = false; sesolaptr->WriteIsComplete = false; status = sys$qiow (EfnWait, ioptr->Channel, IO$_WRITEVBLK, &sesolaptr->WriteIOsb, 0, 0, sesolaptr->WriteRawPtr = DataPtr, DataLength, 0, 0, 0, 0); if (VMSok (status)) *BytesPtr = sesolaptr->WriteIOsb.Count; else { /* report the status via the AST */ sesolaptr->WriteIOsb.Status = status; sesolaptr->WriteIOsb.Count = 0; } Sesola_netio_write_ast (sesolaptr); if (status != SS$_EXQUOTA) return (1); } /* 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. */ void Sesola_netio_write_ast (SESOLA_STRUCT *sesolaptr) { NETIO_STRUCT *ioptr; /*********/ /* begin */ /*********/ ioptr = sesolaptr->NetIoPtr; if (WATCHMOD (ioptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(ioptr), WATCH_MOD_SESOLA, "Sesola_netio_write_ast() !&X !&F !&S !&B !&S !UL", ioptr, &Sesola_netio_write_ast, ioptr->VmsStatus, sesolaptr->WriteInProgress, sesolaptr->WriteIOsb.Status, sesolaptr->WriteIOsb.Count); if (ioptr->VmsStatus) { /* deliver explicitly set status */ ioptr->WriteIOsb.Status = ioptr->VmsStatus; ioptr->WriteIOsb.Count = 0; } if (WATCHING (ioptr, WATCH_THIS)) { if (WATCH_CATEGORY(WATCH_NETWORK)) WatchThis (WATCHITM(ioptr), WATCH_NETWORK, "WRITE !&S !UL bytes CIPHER (!&?non-blocking\rblocking\r)", sesolaptr->WriteIOsb.Status, sesolaptr->WriteIOsb.Count, SESOLA_NETIO_WRITE_IN_PROGRESS(sesolaptr)); if (WATCH_CATEGORY(WATCH_RESPONSE)) if (VMSnok (sesolaptr->WriteIOsb.Status)) WatchThis (WATCHITM(ioptr), WATCH_RESPONSE, "NETWORK !&S (!&?non-blocking\rblocking\r)", sesolaptr->WriteIOsb.Status, SESOLA_NETIO_WRITE_IN_PROGRESS(sesolaptr)); } if (VMSok (sesolaptr->WriteIOsb.Status)) { sesolaptr->Sesola_write_ErrorCount = 0; sesolaptr->BytesTallyTx64 += sesolaptr->WriteIOsb.Count; } else { if (sesolaptr->Sesola_write_ErrorCount++ > 8192) ErrorExitVmsStatus (sesolaptr->WriteIOsb.Status, ErrorSanityCheck, FI_LI); ioptr->WriteErrorCount++; /* just note the first error status */ if (!ioptr->WriteErrorStatus) ioptr->WriteErrorStatus = sesolaptr->WriteIOsb.Status; if (!(sesolaptr->WriteIOsb.Status == SS$_ABORT || sesolaptr->WriteIOsb.Status == SS$_CANCEL || sesolaptr->WriteIOsb.Status == SS$_CONNECFAIL || sesolaptr->WriteIOsb.Status == SS$_LINKDISCON || sesolaptr->WriteIOsb.Status == SS$_TIMEOUT || sesolaptr->WriteIOsb.Status == SS$_IVCHAN || sesolaptr->WriteIOsb.Status == 0)) /* TCP/IP Services? */ ErrorNoticed (NULL, sesolaptr->WriteIOsb.Status, NULL, FI_LI); sesolaptr->WriteIOsb.Count = 0; } sesolaptr->BlocksTallyTx64++; BIO_clear_retry_flags (sesolaptr->BioPtr); /* if blocking I/O then just return */ if (!sesolaptr->WriteInProgress) return; sesolaptr->WriteInProgress = false; sesolaptr->WriteIsComplete = true; if (sesolaptr->SslStateFunction) SysDclAst (sesolaptr->SslStateFunction, sesolaptr); else SesolaNetIoWrite (ioptr, NULL, 0); } /*****************************************************************************/ /* 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. */ SesolaNetIoRead ( NETIO_STRUCT *ioptr, void *DataPtr, int DataSize ) { int bytes, error, value; SESOLA_STRUCT *sesolaptr; /*********/ /* begin */ /*********/ if (WATCHMOD (ioptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(ioptr), WATCH_MOD_SESOLA, "SesolaNetIoRead() !&X !&X !UL", ioptr, DataPtr, DataSize); sesolaptr = ioptr->SesolaPtr; if (DataPtr) { /* primary call not AST recall */ if (ioptr->VmsStatus) { /* deliver explicitly set status */ ioptr->ReadStatus = ioptr->VmsStatus; ioptr->ReadCount = 0; if (ioptr->ReadAstFunction) SysDclAst (SesolaNetIoReadAst, ioptr); return (ioptr->ReadStatus); } sesolaptr->ReadPtr = DataPtr; sesolaptr->ReadSize = DataSize; sesolaptr->ReadCount = 0; sesolaptr->ReadIOsb.Count = 0; sesolaptr->ReadIOsb.Status = 0; if (WATCHING (ioptr, WATCH_NETWORK)) WatchThis (WATCHITM(ioptr), WATCH_NETWORK, "!&?FILL\rREAD\r !UL/!UL bytes (!&?non-blocking\rblocking\r)", sesolaptr->ReadSize & NETIO_DATA_FILL_BUF, sesolaptr->ReadCount, sesolaptr->ReadSize & ~NETIO_DATA_FILL_BUF, SESOLA_NETIO_READ_IN_PROGRESS(sesolaptr)); } if (!sesolaptr->ReadSize) ioptr->VmsStatus = SS$_BUGCHECK; for (;;) { DataSize = (sesolaptr->ReadSize & ~NETIO_DATA_FILL_BUF); if (ioptr->VmsStatus) ioptr->ReadStatus = ioptr->VmsStatus; else if (VMSnok (sesolaptr->ReadIOsb.Status)) ioptr->ReadStatus = sesolaptr->ReadIOsb.Status; else if (sesolaptr->ReadCount >= DataSize) ioptr->ReadStatus = SS$_BUGCHECK; if (ioptr->ReadStatus) if (VMSnok (ioptr->ReadStatus)) { /* break if an issue around the read */ ioptr->ReadCount = 0; break; } DataSize -= sesolaptr->ReadCount; (uchar*)DataPtr = (uchar*)sesolaptr->ReadPtr + sesolaptr->ReadCount; if (sesolaptr->VerifyPeerDataSize && sesolaptr->VerifyPeerDataCount > 0) { /* application data buffered at time of TLS/SSL renegotiate */ if (sesolaptr->VerifyPeerDataCount > DataSize) value = DataSize; else value = sesolaptr->VerifyPeerDataCount; memcpy (DataPtr, sesolaptr->VerifyPeerReadPtr, bytes = value); if (WATCHING (ioptr, WATCH_SESOLA)) WatchThis (WATCHITM(ioptr), WATCH_SESOLA, "BUFFERED client data !UL/!UL !UL", sesolaptr->VerifyPeerDataCount, sesolaptr->VerifyPeerDataSize, value); sesolaptr->VerifyPeerDataCount -= value; sesolaptr->VerifyPeerReadPtr += value; if (!sesolaptr->VerifyPeerDataCount) { /* reset the buffer freeing the global memory */ VmFree (sesolaptr->VerifyPeerDataPtr, FI_LI); sesolaptr->VerifyPeerDataPtr = sesolaptr->VerifyPeerReadPtr = NULL; sesolaptr->VerifyPeerDataSize = 0; } } else { /* OpenSSL (TLS?) maximum of 16kB will be read at a time */ ERR_clear_error (); bytes = 0; #if SESOLA_SINCE_110 value = SSL_read_ex (sesolaptr->SslPtr, DataPtr, DataSize, &bytes); #else value = SSL_read (sesolaptr->SslPtr, DataPtr, DataSize); #endif /* if non-blocking IO in progress just return and wait for delivery */ if (sesolaptr->ReadInProgress) { if (WATCHMOD (ioptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(ioptr), WATCH_MOD_SESOLA, "SSL_read() in-progress"); return (SS$_NORMAL); } } error = SSL_get_error (sesolaptr->SslPtr, value); if (value > 0) { #if SESOLA_SINCE_110 sesolaptr->ReadCount += bytes; ioptr->ReadCount += bytes; #else sesolaptr->ReadCount += value; ioptr->ReadCount += value; #endif ioptr->ReadStatus = SS$_NORMAL; } else if (value <= 0) { ioptr->ReadCount = 0; if (error == SSL_ERROR_ZERO_RETURN || error == SSL_ERROR_SYSCALL || error == SSL_ERROR_SSL) ioptr->ReadStatus = SS$_ABORT; else ioptr->ReadStatus = sesolaptr->ReadIOsb.Status; } if (WATCHMOD (ioptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(ioptr), WATCH_MOD_SESOLA, "SSL_read() !SL !SL !SL !SL/!SL !&S", value, error, bytes, sesolaptr->ReadCount, sesolaptr->ReadSize & ~NETIO_DATA_FILL_BUF, ioptr->ReadStatus); if (VMSnok (ioptr->ReadStatus)) { /* deliver explicitly set status */ if (ioptr->ReadAstFunction) SysDclAst (SesolaNetIoReadAst, ioptr); return (ioptr->ReadStatus); } /* just the one read if not filling a buffer */ if (!(sesolaptr->ReadSize & NETIO_DATA_FILL_BUF)) break; } if (WATCHING (ioptr, WATCH_THIS) && (WATCH_CATEGORY(WATCH_NETWORK) || WATCH_CATEGORY(WATCH_NETWORK_OCTETS))) { WatchThis (WATCHITM(ioptr), WATCH_NETWORK, "READ !&S !UL bytes PLAIN (!&?non-blocking\rblocking\r)", ioptr->ReadStatus, ioptr->ReadCount, SESOLA_NETIO_READ_IN_PROGRESS(sesolaptr)); if (WATCH_CATEGORY(WATCH_NETWORK_OCTETS)) WatchDataDump (ioptr->ReadPtr, ioptr->ReadCount); } /* update the stats of the underlying network I/O structure */ ioptr->BytesRawRx64 += sesolaptr->BytesTallyRx64; ioptr->BytesTallyRx64 += sesolaptr->BytesTallyRx64; ioptr->BlocksRawRx64 += sesolaptr->BlocksTallyRx64; ioptr->BlocksTallyRx64 += sesolaptr->BlocksTallyRx64; /* reset the intermediate accumulators used to update the stats */ sesolaptr->BytesTallyRx64 = 0; sesolaptr->BlocksTallyRx64 = 0; /* decouple the AST delivery */ if (ioptr->ReadAstFunction) SysDclAst (SesolaNetIoReadAst, ioptr); return (ioptr->ReadStatus); } /*****************************************************************************/ /* This function decouples SesolaNetIoRead() from the AST delivery where it might be recalled to make another read. */ void SesolaNetIoReadAst (NETIO_STRUCT *ioptr) { void *AstParam; VOID_AST AstFunction; /*********/ /* begin */ /*********/ if (WATCHMOD (ioptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(ioptr), WATCH_MOD_SESOLA, "SesolaNetIoReadAst()"); AstParam = ioptr->ReadAstParam; AstFunction = ioptr->ReadAstFunction; ioptr->ReadAstFunction = ioptr->ReadAstParam = NULL; if (AstFunction) AstFunction (AstParam); } /*****************************************************************************/ /* 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. */ #if SESOLA_BEFORE_110 int Sesola_netio_read ( BIO *bioptr, char *DataPtr, int DataSize ) { int bytes = 0; Sesola_netio_read_ex (bioptr, DataPtr, DataSize, &bytes); return (bytes); } #endif int Sesola_netio_read_ex ( BIO *bioptr, char *DataPtr, int DataSize, int *BytesPtr ) { int status; NETIO_STRUCT *ioptr; SESOLA_STRUCT *sesolaptr; /*********/ /* begin */ /*********/ #if SESOLA_SINCE_110 sesolaptr = (SESOLA_STRUCT*)BIO_get_data(bioptr); #else sesolaptr = (SESOLA_STRUCT*)bioptr->ptr; #endif ioptr = sesolaptr->NetIoPtr; if (WATCHMOD (ioptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(ioptr), WATCH_MOD_SESOLA, "Sesola_netio_read() !&X !&X !&X !UL !&B !&B", ioptr, DataPtr, DataSize, BytesPtr, sesolaptr->ReadInProgress, sesolaptr->ReadIsComplete); if (sesolaptr->ReadIsComplete) { /* previous non-blocking read is now complete */ sesolaptr->ReadInProgress = sesolaptr->ReadIsComplete = false; if (VMSok (sesolaptr->ReadIOsb.Status)) { *BytesPtr = sesolaptr->ReadIOsb.Count; return (sesolaptr->ReadIOsb.Count); } return (0); } /* if non-blocking read (already) in progress, retry next call */ if (sesolaptr->ReadInProgress) return (-1); /* in case of connection dropped */ if (!ioptr->Channel) { BIO_clear_retry_flags (sesolaptr->BioPtr); return (0); } /* no null buffers! */ if (!DataSize) { BIO_clear_retry_flags (sesolaptr->BioPtr); return (0); } if (WATCHING (ioptr, WATCH_NETWORK_OCTETS)) WatchThis (WATCHITM(ioptr), WATCH_NETWORK, "READ !UL bytes max CIPHER (!&?non-blocking\rblocking\r)", DataSize, SESOLA_NETIO_READ_IN_PROGRESS(sesolaptr)); if (DataSize > ioptr->QioMaxSeg) DataSize = ioptr->QioMaxSeg; if (SESOLA_NETIO_READ_IN_PROGRESS(sesolaptr)) { /*******************/ /* non-blocking IO */ /*******************/ BIO_set_retry_read (bioptr); sesolaptr->ReadInProgress = true; sesolaptr->ReadIsComplete = false; status = sys$qio (EfnNoWait, ioptr->Channel, IO$_READVBLK, &sesolaptr->ReadIOsb, &Sesola_netio_read_ast, sesolaptr, sesolaptr->ReadRawPtr = 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 (NULL, status, "sys$qio", FI_LI); sesolaptr->ReadIOsb.Status = status; sesolaptr->ReadIOsb.Count = 0; SysDclAst (&Sesola_netio_read_ast, sesolaptr); return (-1); } } else { /***************/ /* blocking IO */ /***************/ sesolaptr->ReadInProgress = false; sesolaptr->ReadIsComplete = false; /* see HTTP detection in Sesola_netio_read_ast() */ if (sesolaptr->ReadIOsb.Status == SS$_BADESCAPE) return (0); status = sys$qiow (EfnWait, ioptr->Channel, IO$_READVBLK, &sesolaptr->ReadIOsb, 0, 0, sesolaptr->ReadRawPtr = DataPtr, DataSize, 0, 0, 0, 0); if (VMSok (status)) *BytesPtr = sesolaptr->ReadIOsb.Count; else { /* report the status via the AST */ sesolaptr->ReadIOsb.Status = status; sesolaptr->ReadIOsb.Count = 0; } Sesola_netio_read_ast (sesolaptr); if (status != SS$_EXQUOTA) return (1); } /* 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 read for both blocking and non-block I/O. This function contains code that attempts to check for HTTP transactions with an SSL service. */ void Sesola_netio_read_ast (SESOLA_STRUCT *sesolaptr) { char *cptr; NETIO_STRUCT *ioptr; /*********/ /* begin */ /*********/ ioptr = sesolaptr->NetIoPtr; if (WATCHMOD (ioptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(ioptr), WATCH_MOD_SESOLA, "Sesola_netio_read_ast() !&X !&F !&S !&S !UL", ioptr, &Sesola_netio_read_ast, ioptr->VmsStatus, sesolaptr->ReadIOsb.Status, sesolaptr->ReadIOsb.Count); if (ioptr->VmsStatus) { /* deliver explicitly set status */ ioptr->ReadIOsb.Status = ioptr->VmsStatus; ioptr->ReadIOsb.Count = 0; } if (WATCHING (ioptr, WATCH_THIS) && (WATCH_CATEGORY(WATCH_NETWORK) || WATCH_CATEGORY(WATCH_NETWORK_OCTETS))) { WatchThis (WATCHITM(ioptr), WATCH_NETWORK, "READ !&S !UL bytes CIPHER (!&?non-blocking\rblocking\r)", sesolaptr->ReadIOsb.Status, sesolaptr->ReadIOsb.Count, SESOLA_NETIO_READ_IN_PROGRESS(sesolaptr)); if (WATCH_CATEGORY(WATCH_NETWORK_OCTETS)) WatchDataDump (sesolaptr->ReadRawPtr, sesolaptr->ReadIOsb.Count); } if (VMSok (sesolaptr->ReadIOsb.Status)) { if (!sesolaptr->ReadIOsb.Count) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); sesolaptr->Sesola_read_ErrorCount = 0; /* if first read then check for what might be HTTP */ if (!ioptr->BytesRawRx64) { /* first SSL/TLS octet should be 0x16 then major/minor (e.g. 0x0301) */ cptr = sesolaptr->ReadRawPtr; if (*cptr >= 'A' && *cptr <= 'Z' && (SAME4 (cptr, 'GET ') || SAME4 (cptr, 'HEAD') || SAME4 (cptr, 'POST') || SAME4 (cptr, 'CONN') || SAME4 (cptr, 'COPY') || SAME4 (cptr, 'DELE') || SAME4 (cptr, 'LOCK') || SAME4 (cptr, 'MKCO') || SAME4 (cptr, 'MOVE') || SAME4 (cptr, 'OPTI') || SAME4 (cptr, 'PRI ') || /* i.e. HTTP/2 preface */ SAME4 (cptr, 'PROP') || SAME4 (cptr, 'PUT ') || SAME4 (cptr, 'TRAC') || SAME4 (cptr, 'UNLO') || MATCH5 (cptr, "HTTP/"))) { /* ordinarily this status never would occur with network I/O */ sesolaptr->ReadIOsb.Status = SS$_BADESCAPE; sesolaptr->HTTPduringHandshake = true; } else if (SAME4 (cptr, 'SSH-')) { /* ordinarily this status never would occur with network I/O */ sesolaptr->ReadIOsb.Status = SS$_BADESCAPE; sesolaptr->SSHduringHandshake = true; /* store the fifth character from the handshake (version digit) */ ioptr->SSHversionDigit = cptr[4]; } } sesolaptr->BlocksTallyRx64++; sesolaptr->BytesTallyRx64 += sesolaptr->ReadIOsb.Count; } if (VMSnok (sesolaptr->ReadIOsb.Status)) { if (sesolaptr->Sesola_read_ErrorCount++ > 8192) ErrorExitVmsStatus (sesolaptr->ReadIOsb.Status, ErrorSanityCheck, FI_LI); ioptr->ReadErrorCount++; /* just note the first error status */ if (!ioptr->ReadErrorStatus) ioptr->ReadErrorStatus = sesolaptr->ReadIOsb.Status; if (!(sesolaptr->ReadIOsb.Status == SS$_ABORT || sesolaptr->ReadIOsb.Status == SS$_BADESCAPE || /* see above */ sesolaptr->ReadIOsb.Status == SS$_CANCEL || sesolaptr->ReadIOsb.Status == SS$_CONNECFAIL || sesolaptr->ReadIOsb.Status == SS$_IVCHAN || sesolaptr->ReadIOsb.Status == SS$_LINKDISCON || sesolaptr->ReadIOsb.Status == SS$_TIMEOUT || sesolaptr->ReadIOsb.Status == SS$_UNREACHABLE || /* MultiNet? */ sesolaptr->ReadIOsb.Status == 0)) /* TCP/IP Services? */ ErrorNoticed (NULL, sesolaptr->ReadIOsb.Status, NULL, FI_LI); sesolaptr->ReadIOsb.Count = 0; } BIO_clear_retry_flags (sesolaptr->BioPtr); /* if blocking I/O then just return */ if (!sesolaptr->ReadInProgress) return; sesolaptr->ReadInProgress = false; sesolaptr->ReadIsComplete = true; if (sesolaptr->SslStateFunction) SysDclAst (sesolaptr->SslStateFunction, sesolaptr); else SesolaNetIoRead (ioptr, NULL, 0); } /****************************************************************************/ /* Ensure any memory that may have ben allocated (see SesolaClientRequestData()) but not fully consumed (see SesolaNetIoRead()) during client certiicate renegotiation is freed at the conclusion of each request. */ void SesolaNetIoReset (NETIO_STRUCT *ioptr) { SESOLA_STRUCT *sesolaptr; /*********/ /* begin */ /*********/ sesolaptr = ioptr->SesolaPtr; if (WATCHMOD (sesolaptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(sesolaptr), WATCH_MOD_SESOLA, "SesolaNetIoReset()"); if (sesolaptr->VerifyPeerDataSize) { VmFree (sesolaptr->VerifyPeerDataPtr, FI_LI); sesolaptr->VerifyPeerDataPtr = sesolaptr->VerifyPeerReadPtr = NULL; sesolaptr->VerifyPeerDataCount = sesolaptr->VerifyPeerDataSize = 0; } } /****************************************************************************/ /* Return true if there is the merest whiff of I/O going on. */ BOOL SesolaNetIoInProgress (NETIO_STRUCT *ioptr) { SESOLA_STRUCT *sesolaptr; /*********/ /* begin */ /*********/ sesolaptr = ioptr->SesolaPtr; if (WATCHMOD (sesolaptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(sesolaptr), WATCH_MOD_SESOLA, "SesolaNetIoInProgress() !&B !&B !&A", sesolaptr->WriteInProgress, sesolaptr->ReadInProgress, sesolaptr->SslStateFunction); return (sesolaptr->WriteInProgress || sesolaptr->ReadInProgress || sesolaptr->SslStateFunction); } /****************************************************************************/ /* Explicitly set the status to SS$_CANCEL and cancel any actual I/O. */ void SesolaNetIoCancel (NETIO_STRUCT *ioptr) { SESOLA_STRUCT *sesolaptr; /*********/ /* begin */ /*********/ sesolaptr = ioptr->SesolaPtr; if (WATCHMOD (sesolaptr, WATCH_MOD_SESOLA)) WatchThis (WATCHITM(sesolaptr), WATCH_MOD_SESOLA, "SesolaNetIoCancel()"); sesolaptr->NetIoPtr->VmsStatus = SS$_CANCEL; if (sesolaptr->WriteInProgress || sesolaptr->ReadInProgress) sys$cancel (sesolaptr->NetIoPtr->Channel); } /*****************************************************************************/ /* 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[]; BOOL SesolaNetIoCancel (void *sesolaptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } int SesolaNetIoWrite ( NETIO_STRUCT *ioptr, void *DataPtr, int DataLength ) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } void SesolaNetIoWriteAst (void *sesolaptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } int SesolaNetIoRead ( NETIO_STRUCT *ioptr, void *DataPtr, int DataSize ) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } void SesolaNetIoReadAst (void *sesolaptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } void Sesola_netio_read_ast (void *sesolaptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } void Sesola_netio_write_ast (void *sesolaptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } void SesolaNetIoReset (void *sesolaptr) { ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } BOOL SesolaNetIoInProgress (NETIO_STRUCT *ioptr) { return (false); } /************************/ #endif /* ifdef SESOLA */ /************************/ /*****************************************************************************/