From aace80c6ef4a44a6f2f07471d4479373a8db3f3f Mon Sep 17 00:00:00 2001 From: Susan Hinrichs Date: Thu, 4 Oct 2018 23:33:00 +0000 Subject: [PATCH] Add hooks for outbound TLS start and close. (cherry picked from commit 8e309ff9be0931f69503b3b71806e55cf8fcfddd) --- .../api/functions/TSNetInvokingGet.en.rst | 37 +++++ .../api/types/TSHttpHookID.en.rst | 4 + .../hooks-and-transactions/ssl-hooks.en.rst | 30 +++- include/ts/apidefs.h.in | 11 +- include/ts/ts.h | 10 ++ iocore/net/P_SSLNetVConnection.h | 11 ++ iocore/net/SSLNetVConnection.cc | 132 ++++++++++++----- proxy/InkAPIInternal.h | 2 + proxy/http/HttpDebugNames.cc | 4 + src/traffic_server/InkAPI.cc | 28 ++++ src/traffic_server/InkAPITest.cc | 6 +- .../tls_hooks/gold/ts-close-out-close.gold | 3 + .../tls_hooks/gold/ts-out-delay-start-2.gold | 5 + .../tls_hooks/gold/ts-out-start-close-2.gold | 4 + .../gold_tests/tls_hooks/tls_hooks10.test.py | 7 - .../gold_tests/tls_hooks/tls_hooks12.test.py | 15 -- .../gold_tests/tls_hooks/tls_hooks13.test.py | 72 ++++++++++ .../gold_tests/tls_hooks/tls_hooks14.test.py | 72 ++++++++++ .../gold_tests/tls_hooks/tls_hooks15.test.py | 72 ++++++++++ tests/tools/plugins/ssl_hook_test.cc | 134 +++++++++++++++++- 20 files changed, 592 insertions(+), 67 deletions(-) create mode 100644 doc/developer-guide/api/functions/TSNetInvokingGet.en.rst create mode 100644 tests/gold_tests/tls_hooks/gold/ts-close-out-close.gold create mode 100644 tests/gold_tests/tls_hooks/gold/ts-out-delay-start-2.gold create mode 100644 tests/gold_tests/tls_hooks/gold/ts-out-start-close-2.gold create mode 100644 tests/gold_tests/tls_hooks/tls_hooks13.test.py create mode 100644 tests/gold_tests/tls_hooks/tls_hooks14.test.py create mode 100644 tests/gold_tests/tls_hooks/tls_hooks15.test.py diff --git a/doc/developer-guide/api/functions/TSNetInvokingGet.en.rst b/doc/developer-guide/api/functions/TSNetInvokingGet.en.rst new file mode 100644 index 00000000000..876196e4099 --- /dev/null +++ b/doc/developer-guide/api/functions/TSNetInvokingGet.en.rst @@ -0,0 +1,37 @@ +.. Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed + with this work for additional information regarding copyright + ownership. The ASF licenses this file to you under the Apache + License, Version 2.0 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + +.. include:: ../../../common.defs + +.. default-domain:: c + +TSNetInvokingContGet +******************** + +Synopsis +======== + +`#include ` + +.. function:: TSCont TSNetInvokingContGet(TSVConn conn) + +.. function:: TSHttpTxn TSNetInvokingTxnGet(TSVConn conn) + +Description +=========== + +The TSNetInvokingContGet and TSNetInvokingTxnGet returns the continuation or transaction that +started the connection request associated with the conn parameter. diff --git a/doc/developer-guide/api/types/TSHttpHookID.en.rst b/doc/developer-guide/api/types/TSHttpHookID.en.rst index 852b6725016..dd09fbcc6fb 100644 --- a/doc/developer-guide/api/types/TSHttpHookID.en.rst +++ b/doc/developer-guide/api/types/TSHttpHookID.en.rst @@ -72,8 +72,12 @@ Enumeration Members .. c:macro:: TSHttpHookID TS_VCONN_START_HOOK +.. c:macro:: TSHttpHookID TS_VCONN_OUTBOUND_START_HOOK + .. c:macro:: TSHttpHookID TS_VCONN_CLOSE_HOOK +.. c:macro:: TSHttpHookID TS_VCONN_OUTBOUND_CLOSE_HOOK + .. c:macro:: TSHttpHookID TS_SSL_SNI_HOOK .. c:macro:: TSHttpHookID TS_SSL_CERT_HOOK diff --git a/doc/developer-guide/plugins/hooks-and-transactions/ssl-hooks.en.rst b/doc/developer-guide/plugins/hooks-and-transactions/ssl-hooks.en.rst index 1fa1a20e391..bb564470dfe 100644 --- a/doc/developer-guide/plugins/hooks-and-transactions/ssl-hooks.en.rst +++ b/doc/developer-guide/plugins/hooks-and-transactions/ssl-hooks.en.rst @@ -58,7 +58,7 @@ callback or from another piece of code. TS_VCONN_CLOSE_HOOK ------------------------ -This hook is invoked after the SSL handshake is done and when the IO is closing. The TSVConnArgs should be cleaned up here. +This hook is invoked after the SSL handshake is done and when the IO is closing. The TSVConnArgs should be cleaned up here. A callback at this point must reenable. TS_SSL_SERVERNAME_HOOK ---------------------- @@ -107,11 +107,27 @@ Processing will continue regardless of whether the hook callback executes :c:func:`TSSslVConnReenable()` since the openssl implementation does not allow for pausing processing during the certificate verify callback. +TS_VCONN_OUTBOUND_START_HOOK +---------------------------- + +This hook is invoked after ATS has connected to the upstream server and before the SSL handshake has started. This gives the plugin the option of +overriding the default SSL connection options on the SSL object. + +In theory this hook could apply and be useful for non-SSL connections as well, but at this point this hook is only called in the SSL sequence. + +The TLS handshake processing will not proceed until :c:func:`TSSslVConnReenable()` is called either from within the hook +callback or from another piece of code. + +TS_VCONN_OUTBOUND_CLOSE_HOOK +----------------------------- + +This hook is invoked after the SSL handshake is done and right before the outbound connection closes. A callback at this point must reenable. + TLS Hook State Diagram ---------------------- .. graphviz:: - :alt: TLS Hook State Diagram + :alt: TLS Inbound Hook State Diagram digraph tls_hook_state_diagram{ HANDSHAKE_HOOKS_PRE -> TS_VCONN_START_HOOK; @@ -144,4 +160,14 @@ TLS Hook State Diagram HANDSHAKE_HOOKS_DONE [shape=box]; } +.. graphviz:: + :alt: TLS Outbound Hook State Diagram + + digraph tls_hook_state_diagram{ + HANDSHAKE_HOOKS_OUTBOUND_PRE -> HANDSHAKE_HOOKS_OUTBOUND_PRE_INVOKE; + HANDSHAKE_HOOKS_PRE_INVOKE -> TSSslVConnReenable; + TSSslVConnReenable -> HANDSHAKE_HOOKS_OUTBOUND_PRE; + HANDSHAKE_HOOKS_OUTBOUND_PRE -> HANDSHAKE_HOOKS_DONE; + HANDSHAKE_HOOKS_DONE -> HANDSHAKE_HOOKS_OUTBOUND_CLOSE; + } diff --git a/include/ts/apidefs.h.in b/include/ts/apidefs.h.in index 862ac31ab7d..60a9baeb336 100644 --- a/include/ts/apidefs.h.in +++ b/include/ts/apidefs.h.in @@ -293,8 +293,10 @@ typedef enum { TS_SSL_VERIFY_SERVER_HOOK = TS_SSL_SERVER_VERIFY_HOOK, TS_SSL_VERIFY_CLIENT_HOOK, TS_SSL_SESSION_HOOK, - TS_SSL_LAST_HOOK = TS_SSL_SESSION_HOOK, - TS_HTTP_REQUEST_BUFFER_READ_COMPLETE_HOOK = 24, + TS_VCONN_OUTBOUND_START_HOOK, + TS_VCONN_OUTBOUND_CLOSE_HOOK, + TS_SSL_LAST_HOOK = TS_VCONN_OUTBOUND_CLOSE_HOOK, + TS_HTTP_REQUEST_BUFFER_READ_COMPLETE_HOOK, TS_HTTP_LAST_HOOK } TSHttpHookID; @@ -462,9 +464,10 @@ typedef enum { TS_EVENT_INTERNAL_60202 = 60202, TS_EVENT_SSL_CERT = 60203, TS_EVENT_SSL_SERVERNAME = 60204, - TS_EVENT_SSL_SERVER_VERIFY_HOOK = 60205, TS_EVENT_SSL_VERIFY_SERVER = 60205, - TS_EVENT_SSL_VERIFY_CLIENT = 60206 + TS_EVENT_SSL_VERIFY_CLIENT = 60206, + TS_EVENT_VCONN_OUTBOUND_START = 60207, + TS_EVENT_VCONN_OUTBOUND_CLOSE = 60208 } TSEvent; #define TS_EVENT_HTTP_READ_REQUEST_PRE_REMAP TS_EVENT_HTTP_PRE_REMAP /* backwards compat */ diff --git a/include/ts/ts.h b/include/ts/ts.h index 3db56376fb2..4fbe2d10e91 100644 --- a/include/ts/ts.h +++ b/include/ts/ts.h @@ -1742,6 +1742,16 @@ tsapi TSAction TSNetConnect( struct sockaddr const *to /**< Address to which to connect. */ ); +/** + * Retrieves the continuation associated with creating the TSVConn + */ +tsapi TSCont TSNetInvokingContGet(TSVConn conn); + +/** + * Retrieves the transaction associated with creating the TSVConn + */ +tsapi TSHttpTxn TSNetInvokingTxnGet(TSVConn conn); + tsapi TSAction TSNetAccept(TSCont contp, int port, int domain, int accept_threads); /** diff --git a/iocore/net/P_SSLNetVConnection.h b/iocore/net/P_SSLNetVConnection.h index 66505fd799e..7afc515c67b 100644 --- a/iocore/net/P_SSLNetVConnection.h +++ b/iocore/net/P_SSLNetVConnection.h @@ -261,6 +261,15 @@ class SSLNetVConnection : public UnixNetVConnection } break; + case HANDSHAKE_HOOKS_OUTBOUND_PRE: + case HANDSHAKE_HOOKS_OUTBOUND_PRE_INVOKE: + if (eventId == TS_EVENT_VCONN_OUTBOUND_START) { + if (curHook) { + retval = true; + } + } + break; + case HANDSHAKE_HOOKS_DONE: retval = true; break; @@ -343,6 +352,8 @@ class SSLNetVConnection : public UnixNetVConnection HANDSHAKE_HOOKS_CERT_INVOKE, HANDSHAKE_HOOKS_CLIENT_CERT, HANDSHAKE_HOOKS_CLIENT_CERT_INVOKE, + HANDSHAKE_HOOKS_OUTBOUND_PRE, + HANDSHAKE_HOOKS_OUTBOUND_PRE_INVOKE, HANDSHAKE_HOOKS_DONE } sslHandshakeHookState = HANDSHAKE_HOOKS_PRE; diff --git a/iocore/net/SSLNetVConnection.cc b/iocore/net/SSLNetVConnection.cc index d8015503672..5687d481b1e 100644 --- a/iocore/net/SSLNetVConnection.cc +++ b/iocore/net/SSLNetVConnection.cc @@ -878,35 +878,42 @@ SSLNetVConnection::SSLNetVConnection() {} void SSLNetVConnection::do_io_close(int lerrno) { - if (this->ssl != nullptr && sslHandShakeComplete) { - callHooks(TS_EVENT_VCONN_CLOSE); - int shutdown_mode = SSL_get_shutdown(ssl); - Debug("ssl-shutdown", "previous shutdown state 0x%x", shutdown_mode); - int new_shutdown_mode = shutdown_mode | SSL_RECEIVED_SHUTDOWN; - - if (new_shutdown_mode != shutdown_mode) { - // We do not need to sit around and wait for the client's close-notify if - // they have not already sent it. We will still be standards compliant - Debug("ssl-shutdown", "new SSL_set_shutdown 0x%x", new_shutdown_mode); - SSL_set_shutdown(ssl, new_shutdown_mode); + if (this->ssl != nullptr) { + if (get_context() == NET_VCONNECTION_OUT) { + callHooks(TS_EVENT_VCONN_OUTBOUND_CLOSE); + } else { + callHooks(TS_EVENT_VCONN_CLOSE); } - // If the peer has already sent a FIN, don't bother with the shutdown - // They will just send us a RST for our troubles - // This test is not foolproof. The client's fin could be on the wire - // at the same time we send the close-notify. If so, the client will likely - // send RST anyway - char c; - ssize_t x = recv(this->con.fd, &c, 1, MSG_PEEK); - // x < 0 means error. x == 0 means fin sent - bool do_shutdown = (x > 0); - if (x < 0) { - do_shutdown = (errno == EAGAIN || errno == EWOULDBLOCK); - } - if (do_shutdown) { - // Send the close-notify - int ret = SSL_shutdown(ssl); - Debug("ssl-shutdown", "SSL_shutdown %s", (ret) ? "success" : "failed"); + if (sslHandShakeComplete) { + int shutdown_mode = SSL_get_shutdown(ssl); + Debug("ssl-shutdown", "previous shutdown state 0x%x", shutdown_mode); + int new_shutdown_mode = shutdown_mode | SSL_RECEIVED_SHUTDOWN; + + if (new_shutdown_mode != shutdown_mode) { + // We do not need to sit around and wait for the client's close-notify if + // they have not already sent it. We will still be standards compliant + Debug("ssl-shutdown", "new SSL_set_shutdown 0x%x", new_shutdown_mode); + SSL_set_shutdown(ssl, new_shutdown_mode); + } + + // If the peer has already sent a FIN, don't bother with the shutdown + // They will just send us a RST for our troubles + // This test is not foolproof. The client's fin could be on the wire + // at the same time we send the close-notify. If so, the client will likely + // send RST anyway + char c; + ssize_t x = recv(this->con.fd, &c, 1, MSG_PEEK); + // x < 0 means error. x == 0 means fin sent + bool do_shutdown = (x > 0); + if (x < 0) { + do_shutdown = (errno == EAGAIN || errno == EWOULDBLOCK); + } + if (do_shutdown) { + // Send the close-notify + int ret = SSL_shutdown(ssl); + Debug("ssl-shutdown", "SSL_shutdown %s", (ret) ? "success" : "failed"); + } } } // Go on and do the unix socket cleanups @@ -1366,6 +1373,33 @@ SSLNetVConnection::sslClientHandShakeEvent(int &err) ink_assert(SSLNetVCAccess(ssl) == this); + // Initialize properly for a client connection + if (sslHandshakeHookState == HANDSHAKE_HOOKS_PRE) { + sslHandshakeHookState = HANDSHAKE_HOOKS_OUTBOUND_PRE; + } + + // Do outbound hook processing here + // Continue on if we are in the invoked state. The hook has not yet reenabled + if (sslHandshakeHookState == HANDSHAKE_HOOKS_OUTBOUND_PRE_INVOKE) { + return SSL_WAIT_FOR_HOOK; + } + + // Go do the preaccept hooks + if (sslHandshakeHookState == HANDSHAKE_HOOKS_OUTBOUND_PRE) { + if (!curHook) { + Debug("ssl", "Initialize outbound connect curHook from NULL"); + curHook = ssl_hooks->get(TS_VCONN_OUTBOUND_START_INTERNAL_HOOK); + } else { + curHook = curHook->next(); + } + // If no more hooks, carry on + if (nullptr != curHook) { + sslHandshakeHookState = HANDSHAKE_HOOKS_OUTBOUND_PRE_INVOKE; + ContWrapper::wrap(nh->mutex.get(), curHook->m_cont, TS_EVENT_VCONN_OUTBOUND_START, this); + return SSL_WAIT_FOR_HOOK; + } + } + ssl_error = SSLConnect(ssl); switch (ssl_error) { case SSL_ERROR_NONE: @@ -1521,6 +1555,9 @@ SSLNetVConnection::reenable(NetHandler *nh) case HANDSHAKE_HOOKS_PRE_INVOKE: sslHandshakeHookState = HANDSHAKE_HOOKS_PRE; break; + case HANDSHAKE_HOOKS_OUTBOUND_PRE_INVOKE: + sslHandshakeHookState = HANDSHAKE_HOOKS_OUTBOUND_PRE; + break; case HANDSHAKE_HOOKS_CERT_INVOKE: sslHandshakeHookState = HANDSHAKE_HOOKS_CERT; break; @@ -1553,6 +1590,16 @@ SSLNetVConnection::reenable(NetHandler *nh) Debug("ssl", "Reenable preaccept"); sslHandshakeHookState = HANDSHAKE_HOOKS_PRE_INVOKE; ContWrapper::wrap(nh->mutex.get(), curHook->m_cont, TS_EVENT_VCONN_START, this); + } else if (sslHandshakeHookState == HANDSHAKE_HOOKS_OUTBOUND_PRE) { + Debug("ssl", "Reenable outbound connect"); + sslHandshakeHookState = HANDSHAKE_HOOKS_OUTBOUND_PRE_INVOKE; + ContWrapper::wrap(nh->mutex.get(), curHook->m_cont, TS_EVENT_VCONN_OUTBOUND_START, this); + } else if (sslHandshakeHookState == HANDSHAKE_HOOKS_DONE) { + if (this->get_context() == NET_VCONNECTION_OUT) { + ContWrapper::wrap(nh->mutex.get(), curHook->m_cont, TS_EVENT_VCONN_OUTBOUND_CLOSE, this); + } else { + ContWrapper::wrap(nh->mutex.get(), curHook->m_cont, TS_EVENT_VCONN_CLOSE, this); + } } return; } else { @@ -1569,6 +1616,13 @@ SSLNetVConnection::reenable(NetHandler *nh) case HANDSHAKE_HOOKS_CERT_INVOKE: sslHandshakeHookState = HANDSHAKE_HOOKS_CLIENT_CERT; break; + case HANDSHAKE_HOOKS_OUTBOUND_PRE: + case HANDSHAKE_HOOKS_OUTBOUND_PRE_INVOKE: + this->write.triggered = true; + this->write.enabled = true; + this->writeReschedule(nh); + sslHandshakeHookState = HANDSHAKE_HOOKS_DONE; + break; case HANDSHAKE_HOOKS_CLIENT_CERT: case HANDSHAKE_HOOKS_CLIENT_CERT_INVOKE: sslHandshakeHookState = HANDSHAKE_HOOKS_DONE; @@ -1600,7 +1654,7 @@ SSLNetVConnection::callHooks(TSEvent eventId) { // Only dealing with the SNI/CERT hook so far. ink_assert(eventId == TS_EVENT_SSL_CERT || eventId == TS_EVENT_SSL_SERVERNAME || eventId == TS_EVENT_SSL_VERIFY_SERVER || - eventId == TS_EVENT_SSL_VERIFY_CLIENT || eventId == TS_EVENT_VCONN_CLOSE); + eventId == TS_EVENT_SSL_VERIFY_CLIENT || eventId == TS_EVENT_VCONN_CLOSE || eventId == TS_EVENT_VCONN_OUTBOUND_CLOSE); Debug("ssl", "callHooks sslHandshakeHookState=%d", this->sslHandshakeHookState); // Move state if it is appropriate @@ -1663,8 +1717,21 @@ SSLNetVConnection::callHooks(TSEvent eventId) } // fallthrough case HANDSHAKE_HOOKS_DONE: + case HANDSHAKE_HOOKS_OUTBOUND_PRE: if (eventId == TS_EVENT_VCONN_CLOSE) { - curHook = ssl_hooks->get(TS_VCONN_CLOSE_INTERNAL_HOOK); + sslHandshakeHookState = HANDSHAKE_HOOKS_DONE; + if (curHook == nullptr) { + curHook = ssl_hooks->get(TS_VCONN_CLOSE_INTERNAL_HOOK); + } else { + curHook = curHook->next(); + } + } else if (eventId == TS_EVENT_VCONN_OUTBOUND_CLOSE) { + sslHandshakeHookState = HANDSHAKE_HOOKS_DONE; + if (curHook == nullptr) { + curHook = ssl_hooks->get(TS_VCONN_OUTBOUND_CLOSE_INTERNAL_HOOK); + } else { + curHook = curHook->next(); + } } break; default: @@ -1675,13 +1742,15 @@ SSLNetVConnection::callHooks(TSEvent eventId) Debug("ssl", "callHooks iterated to curHook=%p", curHook); + bool reenabled = true; + this->serverName = const_cast(SSL_get_servername(this->ssl, TLSEXT_NAMETYPE_host_name)); if (this->serverName) { auto *hs = TunnelMap.find(this->serverName); if (hs != nullptr) { this->SNIMapping = true; this->attributes = HttpProxyPort::TRANSPORT_BLIND_TUNNEL; - return EVENT_DONE; + return reenabled; } } @@ -1692,10 +1761,9 @@ SSLNetVConnection::callHooks(TSEvent eventId) // we get out of this callback, and then will shuffle // over the buffered handshake packets to the O.S. // sslHandShakeComplete = 1; - return EVENT_DONE; + return reenabled; } - bool reenabled = true; if (curHook != nullptr) { curHook->invoke(eventId, this); reenabled = diff --git a/proxy/InkAPIInternal.h b/proxy/InkAPIInternal.h index 0ed567212b7..9c24fe854ef 100644 --- a/proxy/InkAPIInternal.h +++ b/proxy/InkAPIInternal.h @@ -282,6 +282,8 @@ typedef enum { TS_SSL_VERIFY_SERVER_INTERNAL_HOOK, TS_SSL_VERIFY_CLIENT_INTERNAL_HOOK, TS_SSL_SESSION_INTERNAL_HOOK, + TS_VCONN_OUTBOUND_START_INTERNAL_HOOK, + TS_VCONN_OUTBOUND_CLOSE_INTERNAL_HOOK, TS_SSL_INTERNAL_LAST_HOOK } TSSslHookInternalID; diff --git a/proxy/http/HttpDebugNames.cc b/proxy/http/HttpDebugNames.cc index e460f821b70..7492a3ad064 100644 --- a/proxy/http/HttpDebugNames.cc +++ b/proxy/http/HttpDebugNames.cc @@ -470,6 +470,10 @@ HttpDebugNames::get_api_hook_name(TSHttpHookID t) return "TS_SSL_VERIFY_CLIENT_HOOK"; case TS_SSL_SESSION_HOOK: return "TS_SSL_SESSION_HOOK"; + case TS_VCONN_OUTBOUND_START_HOOK: + return "TS_VCONN_OUTBOUND_START_HOOK"; + case TS_VCONN_OUTBOUND_CLOSE_HOOK: + return "TS_VCONN_OUTBOUND_CLOSE_HOOK"; } return "unknown hook"; diff --git a/src/traffic_server/InkAPI.cc b/src/traffic_server/InkAPI.cc index 372b855864f..d055317404f 100644 --- a/src/traffic_server/InkAPI.cc +++ b/src/traffic_server/InkAPI.cc @@ -6806,6 +6806,34 @@ TSNetConnectTransparent(TSCont contp, sockaddr const *client_addr, sockaddr cons return reinterpret_cast(netProcessor.connect_re(reinterpret_cast(contp), server_addr, &opt)); } +TSCont +TSNetInvokingContGet(TSVConn conn) +{ + NetVConnection *vc = reinterpret_cast(conn); + UnixNetVConnection *net_vc = dynamic_cast(vc); + TSCont ret = nullptr; + if (net_vc) { + const Action *action = net_vc->get_action(); + ret = reinterpret_cast(action->continuation); + } + return ret; +} + +TSHttpTxn +TSNetInvokingTxnGet(TSVConn conn) +{ + TSCont cont = TSNetInvokingContGet(conn); + TSHttpTxn ret = nullptr; + if (cont) { + Continuation *contobj = reinterpret_cast(cont); + HttpSM *sm = dynamic_cast(contobj); + if (sm) { + ret = reinterpret_cast(sm); + } + } + return ret; +} + TSAction TSNetAccept(TSCont contp, int port, int domain, int accept_threads) { diff --git a/src/traffic_server/InkAPITest.cc b/src/traffic_server/InkAPITest.cc index 2769d496b6b..f17e7612afc 100644 --- a/src/traffic_server/InkAPITest.cc +++ b/src/traffic_server/InkAPITest.cc @@ -6625,8 +6625,10 @@ typedef enum { ORIG_TS_SSL_VERIFY_SERVER_HOOK, ORIG_TS_SSL_VERIFY_CLIENT_HOOK, ORIG_TS_SSL_SESSION_HOOK, - ORIG_TS_SSL_LAST_HOOK = ORIG_TS_SSL_SESSION_HOOK, - ORIG_TS_HTTP_REQUEST_BUFFER_READ_COMPLETE_HOOK = 24, + ORIG_TS_VCONN_OUTBOUND_START_HOOK, + ORIG_TS_VCONN_OUTBOUND_CLOSE_HOOK, + ORIG_TS_SSL_LAST_HOOK = ORIG_TS_VCONN_OUTBOUND_CLOSE_HOOK, + ORIG_TS_HTTP_REQUEST_BUFFER_READ_COMPLETE_HOOK, ORIG_TS_HTTP_LAST_HOOK } ORIG_TSHttpHookID; diff --git a/tests/gold_tests/tls_hooks/gold/ts-close-out-close.gold b/tests/gold_tests/tls_hooks/gold/ts-close-out-close.gold new file mode 100644 index 00000000000..c03962ead36 --- /dev/null +++ b/tests/gold_tests/tls_hooks/gold/ts-close-out-close.gold @@ -0,0 +1,3 @@ +`` DIAG: (ssl_hook_test) Outbound close callback `` - event is good +`` DIAG: (ssl_hook_test) Close callback 0 `` - event is good +`` DIAG: (ssl_hook_test) Close callback 1 `` - event is good diff --git a/tests/gold_tests/tls_hooks/gold/ts-out-delay-start-2.gold b/tests/gold_tests/tls_hooks/gold/ts-out-delay-start-2.gold new file mode 100644 index 00000000000..21c5d61073c --- /dev/null +++ b/tests/gold_tests/tls_hooks/gold/ts-out-delay-start-2.gold @@ -0,0 +1,5 @@ +`` DIAG: (ssl_hook_test) Setup callbacks pa=0 sni=0 cert=0 cert_imm=0 pa_delay=0 +`` DIAG: (ssl_hook_test) Outbound delay start callback 0 `` +`` DIAG: (ssl_hook_test) Callback reenable ssl_vc=`` +`` DIAG: (ssl_hook_test) Outbound delay start callback 1 `` +`` DIAG: (ssl_hook_test) Callback reenable ssl_vc=`` diff --git a/tests/gold_tests/tls_hooks/gold/ts-out-start-close-2.gold b/tests/gold_tests/tls_hooks/gold/ts-out-start-close-2.gold new file mode 100644 index 00000000000..d13e43d6fe0 --- /dev/null +++ b/tests/gold_tests/tls_hooks/gold/ts-out-start-close-2.gold @@ -0,0 +1,4 @@ +`` DIAG: (ssl_hook_test) Outbound start callback 0 `` +`` DIAG: (ssl_hook_test) Outbound close callback 0 `` +`` DIAG: (ssl_hook_test) Outbound close callback 1 `` + diff --git a/tests/gold_tests/tls_hooks/tls_hooks10.test.py b/tests/gold_tests/tls_hooks/tls_hooks10.test.py index 59cccf5dbb6..b4aa25293e6 100644 --- a/tests/gold_tests/tls_hooks/tls_hooks10.test.py +++ b/tests/gold_tests/tls_hooks/tls_hooks10.test.py @@ -68,15 +68,8 @@ tr.StillRunningAfter = server tr.Processes.Default.Command = 'curl -k -H \'host:example.com:{0}\' https://127.0.0.1:{0}'.format(ts.Variables.ssl_port) tr.Processes.Default.ReturnCode = 0 -tr.Processes.Default.Streams.stdout = "gold/preaccept-1.gold" ts.Streams.stderr = "gold/ts-cert-1-im-2.gold" -certstring0 = "Cert callback 0" -certstring1 = "Cert callback 1" -ts.Streams.All = Testers.ContainsExpression( - "\A(?:(?!{0}).)*{0}.*{0}(?!.*{0}).*\Z".format(certstring0), "Cert message appears twicd", reflags=re.S | re.M) -ts.Streams.All = Testers.ContainsExpression( - "\A(?:(?!{0}).)*{0}(?!.*{0}).*\Z".format(certstring1), "Cert message appears only once", reflags=re.S | re.M) tr.Processes.Default.TimeOut = 5 tr.TimeOut = 5 diff --git a/tests/gold_tests/tls_hooks/tls_hooks12.test.py b/tests/gold_tests/tls_hooks/tls_hooks12.test.py index 252f92d67fb..ea66c336f7c 100644 --- a/tests/gold_tests/tls_hooks/tls_hooks12.test.py +++ b/tests/gold_tests/tls_hooks/tls_hooks12.test.py @@ -65,23 +65,8 @@ tr.StillRunningAfter = server tr.Processes.Default.Command = 'curl -k -H \'host:example.com:{0}\' https://127.0.0.1:{0}'.format(ts.Variables.ssl_port) tr.Processes.Default.ReturnCode = 0 -tr.Processes.Default.Streams.stdout = "gold/preaccept-1.gold" ts.Streams.stderr = "gold/ts-preaccept-delayed-1-immdate-2.gold" -# Not going to check for number of times the message appears. With the current test framework -# a probing TCP connection is made to test that the port is listening. The entire preaccept hook -# sequence may appear on that probe. Or it may not. If we move away from the probe connection -# we can check for the right number of each message. -#preacceptstring0 = "Pre accept delay callback 0" -# ts.Streams.All = Testers.ContainsExpression( -# "\A(?:(?!{0}).)*{0}.*({0})?(?!.*{0}).*\Z".format(preacceptstring0), "Pre accept message appears only once or twice", reflags=re.S | re.M) -#preacceptstring1 = "Pre accept callback 0" -# ts.Streams.All = Testers.ContainsExpression( -# "\A(?:(?!{0}).)*{0}.*({0})?(?!.*{0}).*\Z".format(preacceptstring1), "Pre accept message appears only once or twice", reflags=re.S | re.M) -#preacceptstring2 = "Pre accept callback 1" -# ts.Streams.All = Testers.ContainsExpression( -# "\A(?:(?!{0}).)*{0}.*({0})?(?!.*{0}).*\Z".format(preacceptstring2), "Pre accept message appears only once or twice", reflags=re.S | re.M) - tr.Processes.Default.TimeOut = 5 tr.TimeOut = 5 diff --git a/tests/gold_tests/tls_hooks/tls_hooks13.test.py b/tests/gold_tests/tls_hooks/tls_hooks13.test.py new file mode 100644 index 00000000000..9388221bce6 --- /dev/null +++ b/tests/gold_tests/tls_hooks/tls_hooks13.test.py @@ -0,0 +1,72 @@ +''' +Test one outbound start and two outbound close +''' +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import re + +Test.Summary = ''' +Test different combinations of TLS handshake hooks to ensure they are applied consistently. +''' + +Test.SkipUnless(Condition.HasProgram("grep", "grep needs to be installed on system for this test to work")) + +ts = Test.MakeATSProcess("ts", select_ports=False) +server = Test.MakeOriginServer("server", ssl=True) +request_header = {"headers": "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +# desired response form the origin server +response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +server.addResponse("sessionlog.json", request_header, response_header) + +ts.addSSLfile("ssl/server.pem") +ts.addSSLfile("ssl/server.key") + +ts.Variables.ssl_port = 4443 +ts.Disk.records_config.update({ + 'proxy.config.diags.debug.enabled': 1, + 'proxy.config.diags.debug.tags': 'ssl_hook_test', + 'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir), + 'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir), + # enable ssl port + 'proxy.config.http.server_ports': '{0}:ssl'.format(ts.Variables.ssl_port), + 'proxy.config.ssl.client.verify.server': 0, + 'proxy.config.ssl.server.cipher_suite': 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2', +}) + +ts.Disk.ssl_multicert_config.AddLine( + 'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key' +) + +ts.Disk.remap_config.AddLine( + 'map https://example.com:4443 https://127.0.0.1:{0}'.format(server.Variables.Port) +) + +Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 'ssl_hook_test.cc'), ts, '-out_start=1 -out_close=2') + +tr = Test.AddTestRun("Test outbound start and close") +tr.Processes.Default.StartBefore(server) +tr.Processes.Default.StartBefore(Test.Processes.ts, ready=When.PortOpen(ts.Variables.ssl_port)) +tr.StillRunningAfter = ts +tr.StillRunningAfter = server +tr.Processes.Default.Command = 'curl -k -H \'host:example.com:{0}\' https://127.0.0.1:{0}'.format(ts.Variables.ssl_port) +tr.Processes.Default.ReturnCode = 0 + +ts.Streams.stderr = "gold/ts-out-start-close-2.gold" + +tr.Processes.Default.TimeOut = 5 +tr.TimeOut = 5 diff --git a/tests/gold_tests/tls_hooks/tls_hooks14.test.py b/tests/gold_tests/tls_hooks/tls_hooks14.test.py new file mode 100644 index 00000000000..b411f65c6fe --- /dev/null +++ b/tests/gold_tests/tls_hooks/tls_hooks14.test.py @@ -0,0 +1,72 @@ +''' +Test two outbound start delayed hooks +''' +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import re + +Test.Summary = ''' +Test different combinations of TLS handshake hooks to ensure they are applied consistently. +''' + +Test.SkipUnless(Condition.HasProgram("grep", "grep needs to be installed on system for this test to work")) + +ts = Test.MakeATSProcess("ts", select_ports=False) +server = Test.MakeOriginServer("server", ssl=True) +request_header = {"headers": "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +# desired response form the origin server +response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +server.addResponse("sessionlog.json", request_header, response_header) + +ts.addSSLfile("ssl/server.pem") +ts.addSSLfile("ssl/server.key") + +ts.Variables.ssl_port = 4443 +ts.Disk.records_config.update({ + 'proxy.config.diags.debug.enabled': 1, + 'proxy.config.diags.debug.tags': 'ssl_hook_test', + 'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir), + 'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir), + # enable ssl port + 'proxy.config.http.server_ports': '{0}:ssl'.format(ts.Variables.ssl_port), + 'proxy.config.ssl.client.verify.server': 0, + 'proxy.config.ssl.server.cipher_suite': 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2', +}) + +ts.Disk.ssl_multicert_config.AddLine( + 'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key' +) + +ts.Disk.remap_config.AddLine( + 'map https://example.com:4443 https://127.0.0.1:{0}'.format(server.Variables.Port) +) + +Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 'ssl_hook_test.cc'), ts, '-out_start_delay=2') + +tr = Test.AddTestRun("Test outbound delay start") +tr.Processes.Default.StartBefore(server) +tr.Processes.Default.StartBefore(Test.Processes.ts, ready=When.PortOpen(ts.Variables.ssl_port)) +tr.StillRunningAfter = ts +tr.StillRunningAfter = server +tr.Processes.Default.Command = 'curl -k -H \'host:example.com:{0}\' https://127.0.0.1:{0}'.format(ts.Variables.ssl_port) +tr.Processes.Default.ReturnCode = 0 + +ts.Streams.stderr = "gold/ts-out-delay-start-2.gold" + +tr.Processes.Default.TimeOut = 5 +tr.TimeOut = 5 diff --git a/tests/gold_tests/tls_hooks/tls_hooks15.test.py b/tests/gold_tests/tls_hooks/tls_hooks15.test.py new file mode 100644 index 00000000000..ed6e687cda1 --- /dev/null +++ b/tests/gold_tests/tls_hooks/tls_hooks15.test.py @@ -0,0 +1,72 @@ +''' +Test one delayed preaccept callback +''' +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import re + +Test.Summary = ''' +Test different combinations of TLS handshake hooks to ensure they are applied consistently. +''' + +Test.SkipUnless(Condition.HasProgram("grep", "grep needs to be installed on system for this test to work")) + +ts = Test.MakeATSProcess("ts", select_ports=False) +server = Test.MakeOriginServer("server", ssl=True) +request_header = {"headers": "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +# desired response form the origin server +response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +server.addResponse("sessionlog.json", request_header, response_header) + +ts.addSSLfile("ssl/server.pem") +ts.addSSLfile("ssl/server.key") + +ts.Variables.ssl_port = 4443 +ts.Disk.records_config.update({ + 'proxy.config.diags.debug.enabled': 1, + 'proxy.config.diags.debug.tags': 'ssl_hook_test', + 'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir), + 'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir), + # enable ssl port + 'proxy.config.http.server_ports': '{0}:ssl'.format(ts.Variables.ssl_port), + 'proxy.config.ssl.client.verify.server': 0, + 'proxy.config.ssl.server.cipher_suite': 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2', +}) + +ts.Disk.ssl_multicert_config.AddLine( + 'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key' +) + +ts.Disk.remap_config.AddLine( + 'map https://example.com:4443 https://127.0.0.1:{0}'.format(server.Variables.Port) +) + +Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 'ssl_hook_test.cc'), ts, '-close=2 -out_close=1') + +tr = Test.AddTestRun("Test one delayed preaccept hook") +tr.Processes.Default.StartBefore(server) +tr.Processes.Default.StartBefore(Test.Processes.ts, ready=When.PortOpen(ts.Variables.ssl_port)) +tr.StillRunningAfter = ts +tr.StillRunningAfter = server +tr.Processes.Default.Command = 'curl -k -H \'host:example.com:{0}\' https://127.0.0.1:{0}'.format(ts.Variables.ssl_port) +tr.Processes.Default.ReturnCode = 0 + +ts.Streams.stderr = "gold/ts-close-out-close.gold" + +tr.Processes.Default.TimeOut = 5 +tr.TimeOut = 5 diff --git a/tests/tools/plugins/ssl_hook_test.cc b/tests/tools/plugins/ssl_hook_test.cc index 7321b9c6829..f7fde1c3915 100644 --- a/tests/tools/plugins/ssl_hook_test.cc +++ b/tests/tools/plugins/ssl_hook_test.cc @@ -30,6 +30,7 @@ #include #include #include +#include #define PN "ssl_hook_test" #define PCP "[" PN " Plugin] " @@ -77,6 +78,70 @@ CB_Pre_Accept_Delay(TSCont cont, TSEvent event, void *edata) return TS_SUCCESS; } +int +CB_out_start(TSCont cont, TSEvent event, void *edata) +{ + TSVConn ssl_vc = reinterpret_cast(edata); + + int count = reinterpret_cast(TSContDataGet(cont)); + + TSDebug(PN, "Outbound start callback %d %p - event is %s", count, ssl_vc, + event == TS_EVENT_VCONN_OUTBOUND_START ? "good" : "bad"); + + // All done, reactivate things + TSVConnReenable(ssl_vc); + return TS_SUCCESS; +} + +int +CB_out_start_delay(TSCont cont, TSEvent event, void *edata) +{ + TSVConn ssl_vc = reinterpret_cast(edata); + + int count = reinterpret_cast(TSContDataGet(cont)); + + TSDebug(PN, "Outbound delay start callback %d %p - event is %s", count, ssl_vc, + event == TS_EVENT_VCONN_OUTBOUND_START ? "good" : "bad"); + + TSCont cb = TSContCreate(&ReenableSSL, TSMutexCreate()); + + TSContDataSet(cb, ssl_vc); + + // Schedule to reenable in a bit + TSContSchedule(cb, 2000, TS_THREAD_POOL_NET); + + return TS_SUCCESS; +} + +int +CB_close(TSCont cont, TSEvent event, void *edata) +{ + TSVConn ssl_vc = reinterpret_cast(edata); + + int count = reinterpret_cast(TSContDataGet(cont)); + + TSDebug(PN, "Close callback %d %p - event is %s", count, ssl_vc, event == TS_EVENT_VCONN_CLOSE ? "good" : "bad"); + + // All done, reactivate things + TSVConnReenable(ssl_vc); + return TS_SUCCESS; +} + +int +CB_out_close(TSCont cont, TSEvent event, void *edata) +{ + TSVConn ssl_vc = reinterpret_cast(edata); + + int count = reinterpret_cast(TSContDataGet(cont)); + + TSDebug(PN, "Outbound close callback %d %p - event is %s", count, ssl_vc, + event == TS_EVENT_VCONN_OUTBOUND_CLOSE ? "good" : "bad"); + + // All done, reactivate things + TSVConnReenable(ssl_vc); + return TS_SUCCESS; +} + int CB_SNI(TSCont cont, TSEvent event, void *edata) { @@ -125,7 +190,8 @@ CB_Cert(TSCont cont, TSEvent event, void *edata) void parse_callbacks(int argc, const char *argv[], int &preaccept_count, int &sni_count, int &cert_count, int &cert_count_immediate, - int &preaccept_count_delay) + int &preaccept_count_delay, int &close_count, int &out_start_count, int &out_start_delay_count, + int &out_close_count) { int i = 0; const char *ptr; @@ -147,7 +213,11 @@ parse_callbacks(int argc, const char *argv[], int &preaccept_count, int &sni_cou case 'c': ptr = index(argv[i], '='); if (ptr) { - cert_count = atoi(ptr + 1); + if (strncmp(argv[i] + 1, "close", strlen("close")) == 0) { + close_count = atoi(ptr + i); + } else { + cert_count = atoi(ptr + 1); + } } break; case 'd': @@ -162,6 +232,17 @@ parse_callbacks(int argc, const char *argv[], int &preaccept_count, int &sni_cou cert_count_immediate = atoi(ptr + 1); } break; + case 'o': + ptr = index(argv[i], '='); + if (ptr) { + if (strncmp(argv[i] + 1, "out_start_delay", strlen("out_start_delay")) == 0) { + out_start_delay_count = atoi(ptr + 1); + } else if (strncmp(argv[i] + 1, "out_start", strlen("out_start")) == 0) { + out_start_count = atoi(ptr + 1); + } else if (strncmp(argv[i] + 1, "out_close", strlen("out_close")) == 0) { + out_close_count = atoi(ptr + 1); + } + } } } } @@ -169,7 +250,7 @@ parse_callbacks(int argc, const char *argv[], int &preaccept_count, int &sni_cou void setup_callbacks(TSHttpTxn txn, int preaccept_count, int sni_count, int cert_count, int cert_count_immediate, - int preaccept_count_delay) + int preaccept_count_delay, int close_count, int out_start_count, int out_start_delay_count, int out_close_count) { TSCont cb = nullptr; // pre-accept callback continuation int i; @@ -222,6 +303,43 @@ setup_callbacks(TSHttpTxn txn, int preaccept_count, int sni_count, int cert_coun } } + for (i = 0; i < close_count; i++) { + cb = TSContCreate(&CB_close, TSMutexCreate()); + TSContDataSet(cb, (void *)(intptr_t)i); + if (txn) { + TSHttpTxnHookAdd(txn, TS_VCONN_CLOSE_HOOK, cb); + } else { + TSHttpHookAdd(TS_VCONN_CLOSE_HOOK, cb); + } + } + for (i = 0; i < out_start_count; i++) { + cb = TSContCreate(&CB_out_start, TSMutexCreate()); + TSContDataSet(cb, (void *)(intptr_t)i); + if (txn) { + TSHttpTxnHookAdd(txn, TS_VCONN_OUTBOUND_START_HOOK, cb); + } else { + TSHttpHookAdd(TS_VCONN_OUTBOUND_START_HOOK, cb); + } + } + for (i = 0; i < out_start_delay_count; i++) { + cb = TSContCreate(&CB_out_start_delay, TSMutexCreate()); + TSContDataSet(cb, (void *)(intptr_t)i); + if (txn) { + TSHttpTxnHookAdd(txn, TS_VCONN_OUTBOUND_START_HOOK, cb); + } else { + TSHttpHookAdd(TS_VCONN_OUTBOUND_START_HOOK, cb); + } + } + for (i = 0; i < out_close_count; i++) { + cb = TSContCreate(&CB_out_close, TSMutexCreate()); + TSContDataSet(cb, (void *)(intptr_t)i); + if (txn) { + TSHttpTxnHookAdd(txn, TS_VCONN_OUTBOUND_CLOSE_HOOK, cb); + } else { + TSHttpHookAdd(TS_VCONN_OUTBOUND_CLOSE_HOOK, cb); + } + } + return; } @@ -242,7 +360,13 @@ TSPluginInit(int argc, const char *argv[]) int cert_count = 0; int cert_count_immediate = 0; int preaccept_count_delay = 0; - parse_callbacks(argc, argv, preaccept_count, sni_count, cert_count, cert_count_immediate, preaccept_count_delay); - setup_callbacks(nullptr, preaccept_count, sni_count, cert_count, cert_count_immediate, preaccept_count_delay); + int close_count = 0; + int out_start_count = 0; + int out_start_delay_count = 0; + int out_close_count = 0; + parse_callbacks(argc, argv, preaccept_count, sni_count, cert_count, cert_count_immediate, preaccept_count_delay, close_count, + out_start_count, out_start_delay_count, out_close_count); + setup_callbacks(nullptr, preaccept_count, sni_count, cert_count, cert_count_immediate, preaccept_count_delay, close_count, + out_start_count, out_start_delay_count, out_close_count); return; }