@@ -39,10 +39,18 @@ static int BioFree _ANSI_ARGS_((BIO *h)); BIO *BIO_new_tcl(State *statePtr, int flags) { BIO *bio; static BIO_METHOD *BioMethods = NULL; +#ifdef TCLTLS_SSL_USE_FASTPATH + Tcl_Channel parentChannel; + const Tcl_ChannelType *parentChannelType; + void *parentChannelFdIn_p, *parentChannelFdOut_p; + int parentChannelFdIn, parentChannelFdOut, parentChannelFd; + int validParentChannelFd; + int tclGetChannelHandleRet; +#endif dprintf("BIO_new_tcl() called"); if (BioMethods == NULL) { BioMethods = BIO_meth_new(BIO_TYPE_TCL, "tcl"); @@ -52,82 +60,155 @@ BIO_meth_set_ctrl(BioMethods, BioCtrl); BIO_meth_set_create(BioMethods, BioNew); BIO_meth_set_destroy(BioMethods, BioFree); } - bio = BIO_new(BioMethods); + if (statePtr == NULL) { + dprintf("Asked to setup a NULL state, just creating the initial configuration"); + + return(NULL); + } + +#ifdef TCLTLS_SSL_USE_FASTPATH + /* + * If the channel can be mapped back to a file descriptor, just use the file descriptor + * with the SSL library since it will likely be optimized for this. + */ + parentChannel = Tls_GetParent(statePtr, 0); + parentChannelType = Tcl_GetChannelType(parentChannel); + + validParentChannelFd = 0; + if (strcmp(parentChannelType->typeName, "tcp") == 0) { + tclGetChannelHandleRet = Tcl_GetChannelHandle(parentChannel, TCL_READABLE, (ClientData) &parentChannelFdIn_p); + if (tclGetChannelHandleRet == TCL_OK) { + tclGetChannelHandleRet = Tcl_GetChannelHandle(parentChannel, TCL_WRITABLE, (ClientData) &parentChannelFdOut_p); + if (tclGetChannelHandleRet == TCL_OK) { + parentChannelFdIn = PTR2INT(parentChannelFdIn_p); + parentChannelFdOut = PTR2INT(parentChannelFdOut_p); + if (parentChannelFdIn == parentChannelFdOut) { + parentChannelFd = parentChannelFdIn; + validParentChannelFd = 1; + } + } + } + } + + if (validParentChannelFd) { + dprintf("We found a shortcut, this channel is backed by a socket: %i", parentChannelFdIn); + bio = BIO_new_socket(parentChannelFd, flags); + statePtr->flags |= TLS_TCL_FASTPATH; + return(bio); + } + + dprintf("Falling back to Tcl I/O for this channel"); +#endif + bio = BIO_new(BioMethods); BIO_set_data(bio, statePtr); - BIO_set_init(bio, 1); BIO_set_shutdown(bio, flags); + BIO_set_init(bio, 1); return(bio); } static int BioWrite(BIO *bio, CONST char *buf, int bufLen) { Tcl_Channel chan; int ret; + int tclEofChan, tclErrno; - chan = Tls_GetParent((State *) BIO_get_data(bio)); + chan = Tls_GetParent((State *) BIO_get_data(bio), 0); - dprintf("BioWrite(%p, , %d) [%p]", (void *) bio, bufLen, (void *) chan); + dprintf("[chan=%p] BioWrite(%p, , %d)", (void *)chan, (void *) bio, bufLen); ret = Tcl_WriteRaw(chan, buf, bufLen); - dprintf("[%p] BioWrite(%d) -> %d [%d.%d]", (void *) chan, bufLen, ret, Tcl_Eof(chan), Tcl_GetErrno()); + tclEofChan = Tcl_Eof(chan); + tclErrno = Tcl_GetErrno(); + + dprintf("[chan=%p] BioWrite(%d) -> %d [tclEof=%d; tclErrno=%d]", (void *) chan, bufLen, ret, tclEofChan, Tcl_GetErrno()); BIO_clear_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY); - if (ret == 0) { - if (!Tcl_Eof(chan)) { - BIO_set_retry_write(bio); - ret = -1; - } - } - - if (BIO_should_read(bio)) { + if (tclEofChan && ret <= 0) { + dprintf("Got %i from Tcl_WriteRaw, and EOF is set; ret = -1", ret); + Tcl_SetErrno(ECONNRESET); + ret = -1; + } else if (ret == 0) { + dprintf("Got 0 from Tcl_WriteRaw, and EOF is not set; ret = 0"); + dprintf("Setting retry read flag"); BIO_set_retry_read(bio); + } else if (ret < 0) { + dprintf("We got some kind of I/O error"); + + if (tclErrno == EAGAIN) { + dprintf("It's EAGAIN"); + } else { + dprintf("It's an unepxected error: %s/%i", Tcl_ErrnoMsg(tclErrno), tclErrno); + } + } else { + dprintf("Successfully wrote some data"); + } + + if (ret != -1 || (ret == -1 && tclErrno == EAGAIN)) { + if (BIO_should_read(bio)) { + dprintf("Setting should retry read flag"); + + BIO_set_retry_read(bio); + } } return(ret); } static int BioRead(BIO *bio, char *buf, int bufLen) { Tcl_Channel chan; int ret = 0; - int tclEofChan; + int tclEofChan, tclErrno; - chan = Tls_GetParent((State *) BIO_get_data(bio)); + chan = Tls_GetParent((State *) BIO_get_data(bio), 0); - dprintf("BioRead(%p, , %d) [%p]", (void *) bio, bufLen, (void *) chan); + dprintf("[chan=%p] BioRead(%p, , %d)", (void *) chan, (void *) bio, bufLen); if (buf == NULL) { return 0; } ret = Tcl_ReadRaw(chan, buf, bufLen); tclEofChan = Tcl_Eof(chan); + tclErrno = Tcl_GetErrno(); - dprintf("[%p] BioRead(%d) -> %d [tclEof=%d; tclErrno=%d]", (void *) chan, bufLen, ret, tclEofChan, Tcl_GetErrno()); + dprintf("[chan=%p] BioRead(%d) -> %d [tclEof=%d; tclErrno=%d]", (void *) chan, bufLen, ret, tclEofChan, tclErrno); BIO_clear_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY); - if (ret == 0) { - if (!tclEofChan) { - dprintf("Got 0 from Tcl_Read or Tcl_ReadRaw, and EOF is not set -- ret == -1 now"); - BIO_set_retry_read(bio); - ret = -1; + if (tclEofChan && ret <= 0) { + dprintf("Got %i from Tcl_Read or Tcl_ReadRaw, and EOF is set; ret = -1", ret); + Tcl_SetErrno(ECONNRESET); + ret = -1; + } else if (ret == 0) { + dprintf("Got 0 from Tcl_Read or Tcl_ReadRaw, and EOF is not set; ret = 0"); + dprintf("Setting retry read flag"); + BIO_set_retry_read(bio); + } else if (ret < 0) { + dprintf("We got some kind of I/O error"); + + if (tclErrno == EAGAIN) { + dprintf("It's EAGAIN"); } else { - dprintf("Got 0 from Tcl_Read or Tcl_ReadRaw, and EOF is set"); + dprintf("It's an unepxected error: %s/%i", Tcl_ErrnoMsg(tclErrno), tclErrno); } } else { - dprintf("Got non-zero from Tcl_Read or Tcl_ReadRaw; ret == %i", ret); + dprintf("Successfully read some data"); } - if (BIO_should_write(bio)) { - BIO_set_retry_write(bio); + if (ret != -1 || (ret == -1 && tclErrno == EAGAIN)) { + if (BIO_should_write(bio)) { + dprintf("Setting should retry write flag"); + + BIO_set_retry_write(bio); + } } dprintf("BioRead(%p, , %d) [%p] returning %i", (void *) bio, bufLen, (void *) chan, ret); return(ret); @@ -141,11 +222,11 @@ static long BioCtrl(BIO *bio, int cmd, long num, void *ptr) { Tcl_Channel chan; long ret = 1; - chan = Tls_GetParent((State *) BIO_get_data(bio)); + chan = Tls_GetParent((State *) BIO_get_data(bio), 0); dprintf("BioCtrl(%p, 0x%x, 0x%x, %p)", (void *) bio, (unsigned int) cmd, (unsigned int) num, (void *) ptr); switch (cmd) { case BIO_CTRL_RESET: @@ -198,11 +279,11 @@ ret = ((Tcl_WriteRaw(chan, "", 0) >= 0) ? 1 : -1); dprintf("BIO_CTRL_FLUSH returning value %li", ret); break; default: dprintf("Got unknown control command (%i)", cmd); - ret = 0; + ret = -2; break; } return(ret); }