/*****************************************************************************/ /* 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 --------------- 25-JUL-2024 MGD WATCH cipher octets only when [x]SSL is checked 30-JUN-2024 MGD SesolaNetIoPerMinute() allows socket read size to be set bugfix; Sesola_netio_read_ex() ->TcpMaxQio to ->TcpMaxSeg subtly broke (very) large reads, back to v12.0.0 strategy 02-SEP-2021 MGD bugfix; SesolaNetIoRead() /bytes = value/ 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 */ /******************/ static int SesolaNetIoReadSize = -1; /********************/ /* external storage */ /********************/ extern int EfnWait, EfnNoWait; extern char ErrorSanityCheck[]; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* Called at startup and then every minute. Check logical name and if present set the socket read size to be that value. */ void SesolaNetIoPerMinute () { char *sptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE (WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaNetIoPerMinute()"); /* check for logical name first-up then every minute */ if (sptr = SysTrnLnm (WASD_SSL_READ_SIZE)) SesolaNetIoReadSize = abs(atoi(sptr)); else SesolaNetIoReadSize = -1; } /*****************************************************************************/ /* 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); } if (Watch.TriggerTxCount) { if (!ioptr->WatchTriggerTxPlus) { if (WatchTrigger (DataPtr, DataLength, Watch.TriggerTx)) { ioptr->WatchTriggerTx = true; if (Watch.TriggerPlus) ioptr->WatchTriggerTxPlus = true; } } if (ioptr->WatchTriggerTx || ioptr->WatchTriggerTxPlus) { WatchSetTrigger (ioptr); ioptr->WatchTriggerTx = false; } } } else { /* AST recall */ DataPtr = sesolaptr->WritePtr; DataLength = sesolaptr->WriteLength; } if (!sesolaptr->WriteLength) ioptr->VmsStatus = SS$_BUGCHECK; ERR_clear_error (); bytes = 0; value = SSL_write_ex (sesolaptr->SslPtr, DataPtr, DataLength, &bytes); 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) { sesolaptr->WriteCount = bytes; ioptr->WriteCount = bytes; 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. */ int Sesola_netio_write_ex ( BIO *bioptr, char *DataPtr, int DataLength, int *BytesPtr ) { int status; NETIO_STRUCT *ioptr; SESOLA_STRUCT *sesolaptr; /*********/ /* begin */ /*********/ sesolaptr = (SESOLA_STRUCT*)BIO_get_data(bioptr); 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)); if (WATCHING (ioptr, WATCH_SESOLA)) WatchDataDump (DataPtr, DataLength); } if (DataLength > ioptr->TcpMaxQio) DataLength = ioptr->TcpMaxQio; 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; value = SSL_read_ex (sesolaptr->SslPtr, DataPtr, DataSize, &bytes); /* 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) { sesolaptr->ReadCount += bytes; ioptr->ReadCount += bytes; 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); } if (Watch.TriggerRxCount) { if (!ioptr->WatchTriggerRxPlus) { if (WatchTrigger (ioptr->ReadPtr, ioptr->ReadCount, Watch.TriggerRx)) { ioptr->WatchTriggerRx = true; if (Watch.TriggerPlus) ioptr->WatchTriggerRxPlus = true; } } if (ioptr->WatchTriggerRx || ioptr->WatchTriggerRxPlus) { WatchSetTrigger (ioptr); ioptr->WatchTriggerRx = false; } } /* 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. */ int Sesola_netio_read_ex ( BIO *bioptr, char *DataPtr, int DataSize, int *BytesPtr ) { int status; NETIO_STRUCT *ioptr; SESOLA_STRUCT *sesolaptr; /*********/ /* begin */ /*********/ sesolaptr = (SESOLA_STRUCT*)BIO_get_data(bioptr); 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 (SesolaNetIoReadSize >= 0) { if (SesolaNetIoReadSize > 0 && DataSize > SesolaNetIoReadSize) DataSize = SesolaNetIoReadSize; else if (DataSize > ioptr->TcpMaxSeg) DataSize = ioptr->TcpMaxSeg; if (WATCHING (ioptr, WATCH_NETWORK)) WatchThis (WATCHITM(ioptr), WATCH_NETWORK, "WASD_SSL_READ_SIZE !UL (!UL)", SesolaNetIoReadSize, DataSize); } else if (DataSize > ioptr->TcpMaxSeg) DataSize = ioptr->TcpMaxSeg; 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)) if (WATCH_CATEGORY(WATCH_SESOLA)) 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[]; extern WATCH_STRUCT Watch; void SesolaNetIoPerMinute () { if (WATCH_MODULE (WATCH_MOD_SESOLA)) WatchThis (WATCHALL, WATCH_MOD_SESOLA, "SesolaNetIoPerMinute()"); } 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 */ /************************/ /*****************************************************************************/