Index: generic/tls.c ================================================================== --- generic/tls.c +++ generic/tls.c @@ -82,10 +82,11 @@ * Side effects: * Evaluates callback command * *------------------------------------------------------------------- */ + static int EvalCallback( Tcl_Interp *interp, /* Tcl interpreter */ State *statePtr, /* Client state for TLS socket */ Tcl_Obj *cmdPtr) /* Command to eval as a Tcl object */ @@ -136,10 +137,11 @@ * Side effects: * Calls callback (if defined) * *------------------------------------------------------------------- */ + static void InfoCallback( const SSL *ssl, /* SSL context */ int where, /* Source of info */ int ret) /* message enum */ @@ -212,10 +214,11 @@ * Side effects: * Calls callback (if defined) * *------------------------------------------------------------------- */ + #ifndef OPENSSL_NO_SSL_TRACE static void MessageCallback( int write_p, /* Message 0=received, 1=sent */ int version, /* TLS version */ @@ -361,10 +364,11 @@ * The err field of the currently operative State is set * to a string describing the SSL negotiation failure reason * *------------------------------------------------------------------- */ + static int VerifyCallback( int ok, /* Verify result */ X509_STORE_CTX *ctx) /* CTX context */ { @@ -430,10 +434,11 @@ * The err field of the currently operative State is set to a * string describing the SSL negotiation failure reason * *------------------------------------------------------------------- */ + void Tls_Error( State *statePtr, /* Client state for TLS socket */ const char *msg) /* Error message */ { @@ -487,10 +492,11 @@ * Side effects: * none * *------------------------------------------------------------------- */ + void KeyLogCallback( const SSL *ssl, /* Client state for TLS socket */ const char *line) /* Key data to be logged */ { char *str = getenv(SSLKEYLOGFILE); @@ -523,10 +529,11 @@ * Returns: * Password size in bytes or -1 for an error. * *------------------------------------------------------------------- */ + static int PasswordCallback( char *buf, /* Pointer to buffer to store password in */ int size, /* Buffer length in bytes */ int rwflag, /* Whether password is needed for read or write */ @@ -607,10 +614,11 @@ * 0 = error where session will be immediately removed from the internal cache. * 1 = success where app retains session in session cache, and must call SSL_SESSION_free() when done. * *------------------------------------------------------------------- */ + static int SessionCallback( SSL *ssl, /* SSL context */ SSL_SESSION *session) /* Session context */ { @@ -679,10 +687,11 @@ * SSL_TLSEXT_ERR_NOACK: ALPN protocol not selected, e.g., because no ALPN * protocols are configured for this connection. The connection continues. * *------------------------------------------------------------------- */ + static int ALPNCallback( SSL *ssl, /* SSL context */ const unsigned char **out, /* Return buffer to store selected protocol */ unsigned char *outlen, /* Return buffer size */ @@ -753,10 +762,11 @@ * SSL_TLSEXT_ERR_OK: NPN protocol selected. The connection continues. * SSL_TLSEXT_ERR_NOACK: NPN protocol not selected. The connection continues. * *------------------------------------------------------------------- */ + #ifdef USE_NPN static int NPNCallback( const SSL *ssl, /* SSL context */ const unsigned char **out, /* Return buffer to store selected protocol */ @@ -807,10 +817,11 @@ * SSL_TLSEXT_ERR_NOACK: SNI hostname is not accepted and not acknowledged, * e.g. if SNI has not been configured. The connection continues. * *------------------------------------------------------------------- */ + static int SNICallback( const SSL *ssl, /* SSL context */ int *alert, /* Returned alert message */ void *arg) /* Client state for TLS socket */ @@ -883,10 +894,11 @@ * SSL_CLIENT_HELLO_ERROR: failure, terminate connection. Set alert to error code. * SSL_CLIENT_HELLO_SUCCESS: success * *------------------------------------------------------------------- */ + static int HelloCallback( SSL *ssl, /* SSL context */ int *alert, /* Returned alert message */ void *arg) /* Client state for TLS socket */ @@ -983,10 +995,11 @@ * Side effects: * constructs and destroys SSL context (CTX) * *------------------------------------------------------------------- */ + static const char *protocols[] = { "ssl2", "ssl3", "tls1", "tls1.1", "tls1.2", "tls1.3", NULL }; enum protocol { TLS_SSL2, TLS_SSL3, TLS_TLS1, TLS_TLS1_1, TLS_TLS1_2, TLS_TLS1_3, TLS_NONE @@ -1250,11 +1263,11 @@ dprintf("Calling Tls_WaitForConnect"); ret = Tls_WaitForConnect(statePtr, &err, 1); dprintf("Tls_WaitForConnect returned: %i", ret); - if (ret < 0 && ((statePtr->flags & TLS_TCL_ASYNC) && (err == EAGAIN))) { + if (ret <= 0 && ((statePtr->flags & TLS_TCL_ASYNC) && (err == EAGAIN))) { dprintf("Async set and err = EAGAIN"); ret = 0; } else if (ret < 0) { long result; errStr = statePtr->err; @@ -1831,10 +1844,11 @@ * Side effects: * Loads CA certificates * *------------------------------------------------------------------- */ + static int TlsLoadClientCAFileFromMemory( Tcl_Interp *interp, /* Tcl interpreter */ SSL_CTX *ctx, /* CTX context */ Tcl_Obj *file) /* CA certificates filename */ @@ -2379,10 +2393,11 @@ * Side effects: * None. * *------------------------------------------------------------------- */ + static int StatusObjCmd( TCL_UNUSED(ClientData), /* Client data */ Tcl_Interp *interp, /* Tcl interpreter */ int objc, /* Arg count */ @@ -2785,10 +2800,11 @@ * Side effects: * None. * *------------------------------------------------------------------- */ + static int VersionObjCmd( TCL_UNUSED(ClientData), /* Client data */ Tcl_Interp *interp, /* Tcl interpreter */ TCL_UNUSED(int), /* objc - Arg count */ @@ -2815,10 +2831,11 @@ * Side effects: * None. * *------------------------------------------------------------------- */ + static int MiscObjCmd( TCL_UNUSED(ClientData), /* Client data */ Tcl_Interp *interp, /* Tcl interpreter */ int objc, /* Arg count */ @@ -3039,10 +3056,11 @@ * Side effects: * Frees all the state * *------------------------------------------------------------------- */ + void Tls_Free( tls_free_type *blockPtr) /* Client state for TLS socket */ { State *statePtr = (State *)blockPtr; @@ -3069,10 +3087,11 @@ * Side effects: * Frees all the state * *------------------------------------------------------------------- */ + void Tls_Clean( State *statePtr) /* Client state for TLS socket */ { dprintf("Called"); @@ -3227,10 +3246,11 @@ * Side effects: * Shutdown SSL library * *------------------------------------------------------* */ + void TlsLibShutdown( ClientData clientData) /* Not used */ { dprintf("Called"); @@ -3250,10 +3270,11 @@ * Side effects: * Initializes SSL library * *------------------------------------------------------* */ + static int TlsLibInit() { static int initialized = 0; dprintf("Called"); @@ -3357,11 +3378,12 @@ * Side effects: * Same as of 'Tls_Init' * *------------------------------------------------------------------- */ + DLLEXPORT int Tls_SafeInit( Tcl_Interp *interp) /* Tcl interpreter */ { dprintf("Called"); return Tls_Init(interp); } Index: generic/tlsBIO.c ================================================================== --- generic/tlsBIO.c +++ generic/tlsBIO.c @@ -6,16 +6,26 @@ * Copyright (C) 2024 Brian O'Hagan * */ /* + Normal tlsBIO.c tlsIO.c +------+ +---+ +---+ | |Tcl_WriteRaw<--BioOutput|SSL|BIO_write<--TlsOutputProc<--Write| | |socket| |BIO| |App| | |Tcl_ReadRaw --> BioInput| |BIO_Read -->TlsInputProc --> Read| | +------+ +---+ +---+ + + + Fast Path + tlsIO.c + +------+ +-----+ +-----+ + | |<-- write <--| SSL |BIO_write <-- TlsOutputProc <-- Write| | + |socket| | BIO | | App | + | |<-- read <--| |BIO_Read --> TlsInputProc --> Read| | + +------+ +-----+ +-----+ */ #include "tlsInt.h" #include Index: generic/tlsIO.c ================================================================== --- generic/tlsIO.c +++ generic/tlsIO.c @@ -15,16 +15,26 @@ * SSLtcl (Peter Antman) * */ /* + Normal tlsBIO.c tlsIO.c +------+ +---+ +---+ | |Tcl_WriteRaw<--BioOutput|SSL|BIO_write<--TlsOutputProc<--Write| | |socket| |BIO| |App| | |Tcl_ReadRaw --> BioInput| |BIO_Read -->TlsInputProc --> Read| | +------+ +---+ +---+ + + + Fast Path + tlsIO.c + +------+ +-----+ +-----+ + | |<-- write <--| SSL |BIO_write <-- TlsOutputProc <-- Write| | + |socket| | BIO | | App | + | |<-- read <--| |BIO_Read --> TlsInputProc --> Read| | + +------+ +-----+ +-----+ */ #include "tlsInt.h" #include @@ -218,14 +228,10 @@ bioShouldRetry = 1; statePtr->want |= TCL_READABLE; } else if (rc == SSL_ERROR_WANT_WRITE) { bioShouldRetry = 1; statePtr->want |= TCL_WRITABLE; - } else if (BIO_should_retry(statePtr->bio)) { - bioShouldRetry = 1; - } else if (rc == SSL_ERROR_SYSCALL && Tcl_GetErrno() == EAGAIN) { - bioShouldRetry = 1; } } if (bioShouldRetry) { dprintf("The I/O did not complete -- but we should try it again"); @@ -245,19 +251,19 @@ } switch (rc) { case SSL_ERROR_NONE: /* The TLS/SSL I/O operation completed successfully */ - dprintf("The connection is good"); + dprintf("SSL_ERROR_NONE"); *errorCodePtr = 0; break; case SSL_ERROR_SSL: /* A non-recoverable, fatal error in the SSL library occurred, usually a protocol error. This includes certificate validation errors. */ - dprintf("SSL_ERROR_SSL: Got permanent fatal SSL error, aborting immediately"); + dprintf("SSL_ERROR_SSL: Fatal SSL protocol error occurred"); if (SSL_get_verify_result(statePtr->ssl) != X509_V_OK) { Tls_Error(statePtr, X509_verify_cert_error_string(SSL_get_verify_result(statePtr->ssl))); } if (backingError != 0) { @@ -264,14 +270,44 @@ Tls_Error(statePtr, ERR_reason_error_string(backingError)); } statePtr->flags |= TLS_TCL_HANDSHAKE_FAILED; *errorCodePtr = ECONNABORTED; return -1; + + case SSL_ERROR_WANT_READ: + /* More data must be read from the underlying BIO layer in order to + complete the actual SSL_*() operation. */ + dprintf("SSL_ERROR_WANT_READ"); + BIO_set_retry_read(statePtr->bio); + *errorCodePtr = EAGAIN; + dprintf("ERR(SSL_ERROR_WANT_READ, EAGAIN)"); + statePtr->want |= TCL_READABLE; + return 0; + + case SSL_ERROR_WANT_WRITE: + /* There is data in the SSL buffer that must be written to the + underlying BIO in order to complete the SSL_*() operation. */ + dprintf("SSL_ERROR_WANT_WRITE"); + BIO_set_retry_write(statePtr->bio); + *errorCodePtr = EAGAIN; + dprintf("ERR(SSL_ERROR_WANT_WRITE, EAGAIN)"); + statePtr->want |= TCL_WRITABLE; + return 0; + + case SSL_ERROR_WANT_X509_LOOKUP: + /* The operation did not complete because an application callback + set by SSL_CTX_set_client_cert_cb() has asked to be called again. */ + dprintf("SSL_ERROR_WANT_X509_LOOKUP"); + BIO_set_retry_special(statePtr->bio); + BIO_set_retry_reason(statePtr->bio, BIO_RR_SSL_X509_LOOKUP); + *errorCodePtr = EAGAIN; + dprintf("ERR(SSL_ERROR_WANT_X509_LOOKUP, EAGAIN)"); + return 0; case SSL_ERROR_SYSCALL: /* Some non-recoverable, fatal I/O error occurred */ - dprintf("SSL_ERROR_SYSCALL"); + dprintf("SSL_ERROR_SYSCALL: Fatal I/O error occurred"); if (backingError == 0 && err == 0) { dprintf("EOF reached") *errorCodePtr = ECONNRESET; Tls_Error(statePtr, "(unexpected) EOF reached"); @@ -295,66 +331,36 @@ statePtr->flags |= TLS_TCL_HANDSHAKE_FAILED; return -1; case SSL_ERROR_ZERO_RETURN: - /* Peer has closed the connection by sending the close_notify alert. - Can't read, but can write. Need to return an EOF, so channel is - closed which will send an SSL_shutdown(). */ - dprintf("SSL_ERROR_ZERO_RETURN: Connect returned an invalid value..."); + /* Peer has cleanly closed the connection by sending the close_notify + alert. Can't read, but can write. Need to return an EOF, so the + channel is closed which will send an SSL_shutdown(). */ + dprintf("SSL_ERROR_ZERO_RETURN: Peer has closed the connection"); *errorCodePtr = ECONNRESET; Tls_Error(statePtr, "Peer has closed the connection for writing by sending the close_notify alert"); return -1; - case SSL_ERROR_WANT_READ: - /* More data must be read from the underlying BIO layer in order to - complete the actual SSL_*() operation. */ - dprintf("SSL_ERROR_WANT_READ"); - BIO_set_retry_read(statePtr->bio); - *errorCodePtr = EAGAIN; - dprintf("ERR(SSL_ERROR_WANT_READ, EAGAIN) "); - statePtr->want |= TCL_READABLE; - return 0; - - case SSL_ERROR_WANT_WRITE: - /* There is data in the SSL buffer that must be written to the - underlying BIO in order to complete the SSL_*() operation. */ - dprintf("SSL_ERROR_WANT_WRITE"); - BIO_set_retry_write(statePtr->bio); - *errorCodePtr = EAGAIN; - dprintf("ERR(SSL_ERROR_WANT_WRITE, EAGAIN) "); - statePtr->want |= TCL_WRITABLE; - return 0; - case SSL_ERROR_WANT_CONNECT: - /* Connect would have blocked. */ + /* The operation did not complete and connect would have blocked. + Retry again after connection is established. */ dprintf("SSL_ERROR_WANT_CONNECT"); BIO_set_retry_special(statePtr->bio); BIO_set_retry_reason(statePtr->bio, BIO_RR_CONNECT); *errorCodePtr = EAGAIN; - dprintf("ERR(SSL_ERROR_WANT_CONNECT, EAGAIN) "); + dprintf("ERR(SSL_ERROR_WANT_CONNECT, EAGAIN)"); return 0; case SSL_ERROR_WANT_ACCEPT: - /* Accept would have blocked */ + /* The operation did not complete and accept would have blocked. + Retry again after connection is established. */ dprintf("SSL_ERROR_WANT_ACCEPT"); BIO_set_retry_special(statePtr->bio); BIO_set_retry_reason(statePtr->bio, BIO_RR_ACCEPT); *errorCodePtr = EAGAIN; - dprintf("ERR(SSL_ERROR_WANT_ACCEPT, EAGAIN) "); - return 0; - - case SSL_ERROR_WANT_X509_LOOKUP: - /* Application callback set by SSL_CTX_set_client_cert_cb has asked - to be called again. The operation did not complete because an - application callback set by SSL_CTX_set_client_cert_cb() has - asked to be called again. */ - dprintf("SSL_ERROR_WANT_X509_LOOKUP"); - BIO_set_retry_special(statePtr->bio); - BIO_set_retry_reason(statePtr->bio, BIO_RR_SSL_X509_LOOKUP); - *errorCodePtr = EAGAIN; - dprintf("ERR(SSL_ERROR_WANT_X509_LOOKUP, EAGAIN) "); + dprintf("ERR(SSL_ERROR_WANT_ACCEPT, EAGAIN)"); return 0; case SSL_ERROR_WANT_ASYNC: /* Used with flag SSL_MODE_ASYNC, op didn't complete because an async engine is still processing data */ @@ -371,11 +377,11 @@ #endif default: /* The operation did not complete and should be retried later. */ dprintf("Operation did not complete, call function again later"); *errorCodePtr = EAGAIN; - dprintf("ERR(Other, EAGAIN) "); + dprintf("ERR(Other, EAGAIN)"); return 0; } dprintf("Removing the \"TLS_TCL_INIT\" flag since we have completed the handshake"); statePtr->flags &= ~TLS_TCL_INIT; @@ -434,19 +440,24 @@ dprintf("Calling Tls_WaitForConnect"); tlsConnect = Tls_WaitForConnect(statePtr, errorCodePtr, 0); if (tlsConnect < 0) { + /* Failure, so abort */ dprintf("Got an error waiting to connect (tlsConnect = %i, *errorCodePtr = %i)", tlsConnect, *errorCodePtr); bytesRead = -1; if (*errorCodePtr == ECONNRESET) { dprintf("Got connection reset"); /* Soft EOF */ *errorCodePtr = 0; bytesRead = 0; } + return bytesRead; + } else if (tlsConnect == 0) { + /* Try again */ + bytesRead = -1; return bytesRead; } } /* @@ -491,11 +502,10 @@ } if (BIO_should_io_special(statePtr->bio)) { int reason = BIO_get_retry_reason(statePtr->bio); dprintf("BIO has some special condition other than read or write: code=%d", reason); } - dprintf("BIO has pending data to write"); } switch (err) { case SSL_ERROR_NONE: /* I/O operation completed */ @@ -504,11 +514,11 @@ break; case SSL_ERROR_SSL: /* A non-recoverable, fatal error in the SSL library occurred, usually a protocol error. */ - dprintf("SSL error, indicating that the connection has been aborted"); + dprintf("SSL_ERROR_SSL: Fatal SSL protocol error occurred"); if (backingError != 0) { Tls_Error(statePtr, ERR_reason_error_string(backingError)); } else if (SSL_get_verify_result(statePtr->ssl) != X509_V_OK) { Tls_Error(statePtr, X509_verify_cert_error_string(SSL_get_verify_result(statePtr->ssl))); @@ -548,20 +558,20 @@ statePtr->want |= TCL_WRITABLE; BIO_set_retry_write(statePtr->bio); break; case SSL_ERROR_WANT_X509_LOOKUP: - /* Operation didn't complete since application callback set by - SSL_CTX_set_client_cert_cb() asked to be called again. */ + /* The operation did not complete because an application callback + set by SSL_CTX_set_client_cert_cb() has asked to be called again. */ dprintf("Got SSL_ERROR_WANT_X509_LOOKUP, mapping it to EAGAIN"); *errorCodePtr = EAGAIN; bytesRead = -1; break; case SSL_ERROR_SYSCALL: /* Some non-recoverable, fatal I/O error occurred */ - dprintf("SSL_ERROR_SYSCALL"); + dprintf("SSL_ERROR_SYSCALL: Fatal I/O error occurred"); if (backingError == 0 && bytesRead == 0) { /* Unexpected EOF from the peer for OpenSSL 1.1 */ dprintf("(Unexpected) EOF reached") *errorCodePtr = 0; @@ -582,14 +592,14 @@ Tls_Error(statePtr, ERR_reason_error_string(backingError)); } break; case SSL_ERROR_ZERO_RETURN: - /* Peer has closed the connection by sending the close_notify alert. - Can't read, but can write. Need to return an EOF, so channel is - closed which will send an SSL_shutdown(). */ - dprintf("Got SSL_ERROR_ZERO_RETURN, this means an EOF has been reached"); + /* Peer has cleanly closed the connection by sending the close_notify + alert. Can't read, but can write. Need to return an EOF, so the + channel is closed which will send an SSL_shutdown(). */ + dprintf("SSL_ERROR_ZERO_RETURN: Peer has closed the connection"); bytesRead = 0; *errorCodePtr = 0; Tls_Error(statePtr, "Peer has closed the connection for writing by sending the close_notify alert"); break; @@ -596,11 +606,11 @@ case SSL_ERROR_WANT_ASYNC: /* Used with flag SSL_MODE_ASYNC, operation didn't complete because an async engine is still processing data. */ dprintf("Got SSL_ERROR_WANT_ASYNC, mapping this to EAGAIN"); *errorCodePtr = EAGAIN; - bytesRead = -1; + bytesRead = 0; break; default: dprintf("Unknown error (err = %i), mapping to EOF", err); *errorCodePtr = 0; @@ -672,10 +682,14 @@ /* Soft EOF */ *errorCodePtr = 0; written = 0; } return written; + } else if (tlsConnect == 0) { + /* Try again */ + written = -1; + return written; } } if (toWrite == 0) { dprintf("zero-write"); @@ -734,11 +748,10 @@ } if (BIO_should_io_special(statePtr->bio)) { int reason = BIO_get_retry_reason(statePtr->bio); dprintf("BIO has some special condition other than read or write: code=%d", reason); } - dprintf("BIO has pending data to write"); } else { BIO_flush(statePtr->bio); } @@ -752,11 +765,11 @@ break; case SSL_ERROR_SSL: /* A non-recoverable, fatal error in the SSL library occurred, usually a protocol error */ - dprintf("SSL error, indicating that the connection has been aborted"); + dprintf("SSL_ERROR_SSL: Fatal SSL protocol error occurred"); if (backingError != 0) { Tls_Error(statePtr, ERR_reason_error_string(backingError)); } else if (SSL_get_verify_result(statePtr->ssl) != X509_V_OK) { Tls_Error(statePtr, X509_verify_cert_error_string(SSL_get_verify_result(statePtr->ssl))); @@ -767,39 +780,39 @@ written = -1; break; case SSL_ERROR_WANT_READ: /* Operation did not complete due to not enough data was available. - Retry again later. */ + Retry again later with same data. */ dprintf("Got SSL_ERROR_WANT_READ, mapping it to EAGAIN"); *errorCodePtr = EAGAIN; written = -1; statePtr->want |= TCL_READABLE; BIO_set_retry_read(statePtr->bio); break; case SSL_ERROR_WANT_WRITE: /* Operation did not complete due to unable to send all data to the - BIO. Retry later. */ + BIO. Retry later with same data. */ dprintf("Got SSL_ERROR_WANT_WRITE, mapping it to EAGAIN"); *errorCodePtr = EAGAIN; written = -1; statePtr->want |= TCL_WRITABLE; BIO_set_retry_write(statePtr->bio); break; case SSL_ERROR_WANT_X509_LOOKUP: - /* Operation didn't complete since application callback set by - SSL_CTX_set_client_cert_cb() asked to be called again. */ + /* The operation did not complete because an application callback + set by SSL_CTX_set_client_cert_cb() has asked to be called again. */ dprintf("Got SSL_ERROR_WANT_X509_LOOKUP, mapping it to EAGAIN"); *errorCodePtr = EAGAIN; written = -1; break; case SSL_ERROR_SYSCALL: /* Some non-recoverable, fatal I/O error occurred */ - dprintf("SSL_ERROR_SYSCALL"); + dprintf("SSL_ERROR_SYSCALL: Fatal I/O error occurred"); if (backingError == 0 && written == 0) { dprintf("EOF reached") *errorCodePtr = 0; written = 0; @@ -818,25 +831,25 @@ Tls_Error(statePtr, ERR_reason_error_string(backingError)); } break; case SSL_ERROR_ZERO_RETURN: - /* Peer has closed the connection by sending the close_notify alert. - Can't read, but can write. Need to return an EOF, so channel is - closed which will send an SSL_shutdown(). */ - dprintf("Got SSL_ERROR_ZERO_RETURN, this means an EOF has been reached"); + /* Peer has cleanly closed the connection by sending the close_notify + alert. Can't read, but can write. Need to return an EOF, so the + channel is closed which will send an SSL_shutdown(). */ + dprintf("SSL_ERROR_ZERO_RETURN: Peer has closed the connection"); *errorCodePtr = 0; written = 0; Tls_Error(statePtr, "Peer has closed the connection for writing by sending the close_notify alert"); break; case SSL_ERROR_WANT_ASYNC: - /* Used with flag SSL_MODE_ASYNC, op didn't complete because an - async engine is still processing data */ + /* Used with flag SSL_MODE_ASYNC, operation didn't complete because + an async engine is still processing data. */ dprintf("Got SSL_ERROR_WANT_ASYNC, mapping this to EAGAIN"); *errorCodePtr = EAGAIN; - written = -1; + written = 0; break; default: dprintf("unknown error: %d", err); Tls_Error(statePtr, "Unknown error"); @@ -1034,11 +1047,11 @@ TlsWatchProc( ClientData instanceData, /* Connection state info */ int mask) /* Events of interest; an OR-ed combination of * TCL_READABLE, TCL_WRITABLE and TCL_EXCEPTION. */ { - Tcl_Channel parent; + Tcl_Channel parent; State *statePtr = (State *) instanceData; Tcl_DriverWatchProc *watchProc; int pending = 0; dprintf("Called with mask 0x%02x", mask); @@ -1063,29 +1076,31 @@ return; } statePtr->watchMask = mask; - /* No channel handlers any more. We will be notified automatically about + /* + * No channel handlers any more. We will be notified automatically about * events on the channel below via a call to our 'TransformNotifyProc'. But * we have to pass the interest down now. We are allowed to add additional * 'interest' to the mask if we want to, but this transformation has no * such interest. It just passes the request down, unchanged. */ dprintf("Registering our interest in the lower channel (chan=%p)", (void *) parent); watchProc = Tcl_ChannelWatchProc(Tcl_GetChannelType(parent)); watchProc(Tcl_GetChannelInstanceData(parent), mask); - /* Do we have any pending events */ + /* Do we have any pending data */ pending = (statePtr->want || \ ((mask & TCL_READABLE) && ((Tcl_InputBuffered(statePtr->self) > 0) || (BIO_ctrl_pending(statePtr->bio) > 0))) || ((mask & TCL_WRITABLE) && ((Tcl_OutputBuffered(statePtr->self) > 0) || (BIO_ctrl_wpending(statePtr->bio) > 0)))); dprintf("IO Want=%d, input buffer=%d, output buffer=%d, BIO pending=%zd, BIO wpending=%zd, pending=%d", \ statePtr->want, Tcl_InputBuffered(statePtr->self), Tcl_OutputBuffered(statePtr->self), \ BIO_ctrl_pending(statePtr->bio), BIO_ctrl_wpending(statePtr->bio), pending); + /* Schedule next event if data is pending, otherwise cease events for now */ if (!(mask & TCL_READABLE) || pending == 0) { /* Remove timer, if any */ if (statePtr->timer != (Tcl_TimerToken) NULL) { dprintf("A timer was found, deleting it"); Tcl_DeleteTimerHandler(statePtr->timer); @@ -1184,11 +1199,11 @@ /* * Delete an existing timer. It was not fired, yet we are here, so the * channel below generated such an event and we don't have to. The renewal * of the interest after the execution of channel handlers will eventually - * cause us to recreate the timer (in WatchProc). + * cause us to recreate the timer (in TlsWatchProc). */ if (statePtr->timer != (Tcl_TimerToken) NULL) { Tcl_DeleteTimerHandler(statePtr->timer); statePtr->timer = (Tcl_TimerToken) NULL; }