This is an automated email from the ASF dual-hosted git repository. bcall pushed a commit to branch 8.1.x in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/8.1.x by this push: new a6ce406 Add hooks for outbound TLS start and close. a6ce406 is described below commit a6ce406126d510d431baa80b3a09ebd46a792cd4 Author: Susan Hinrichs <shinr...@apache.org> AuthorDate: Thu Oct 4 23:33:00 2018 +0000 Add hooks for outbound TLS start and close. (cherry picked from commit 8e309ff9be0931f69503b3b71806e55cf8fcfddd) --- .../api/functions/TSNetInvokingGet.en.rst | 37 ++++++ doc/developer-guide/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 + tests/gold_tests/tls_hooks/tls_hooks10.test.py | 7 -- tests/gold_tests/tls_hooks/tls_hooks12.test.py | 15 --- .../{tls_hooks10.test.py => tls_hooks13.test.py} | 24 ++-- .../{tls_hooks10.test.py => tls_hooks14.test.py} | 26 ++-- .../{tls_hooks10.test.py => tls_hooks15.test.py} | 24 ++-- tests/tools/plugins/ssl_hook_test.cc | 134 ++++++++++++++++++++- 20 files changed, 398 insertions(+), 119 deletions(-) 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 0000000..876196e --- /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 <ts/ts.h>` + +.. 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 852b672..dd09fbc 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 1fa1a20..bb56447 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 862ac31..60a9bae 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 3db5637..4fbe2d1 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 66505fd..7afc515 100644 --- a/iocore/net/P_SSLNetVConnection.h +++ b/iocore/net/P_SSLNetVConnection.h @@ -261,6 +261,15 @@ public: } 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 @@ private: 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 d801550..5687d48 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<char *>(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 0ed5672..9c24fe8 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 e460f82..7492a3a 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 372b855..d055317 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<TSAction>(netProcessor.connect_re(reinterpret_cast<INKContInternal *>(contp), server_addr, &opt)); } +TSCont +TSNetInvokingContGet(TSVConn conn) +{ + NetVConnection *vc = reinterpret_cast<NetVConnection *>(conn); + UnixNetVConnection *net_vc = dynamic_cast<UnixNetVConnection *>(vc); + TSCont ret = nullptr; + if (net_vc) { + const Action *action = net_vc->get_action(); + ret = reinterpret_cast<TSCont>(action->continuation); + } + return ret; +} + +TSHttpTxn +TSNetInvokingTxnGet(TSVConn conn) +{ + TSCont cont = TSNetInvokingContGet(conn); + TSHttpTxn ret = nullptr; + if (cont) { + Continuation *contobj = reinterpret_cast<Continuation *>(cont); + HttpSM *sm = dynamic_cast<HttpSM *>(contobj); + if (sm) { + ret = reinterpret_cast<TSHttpTxn>(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 2769d49..f17e761 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 0000000..c03962e --- /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 0000000..21c5d61 --- /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 0000000..d13e43d --- /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 59cccf5..b4aa252 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 = 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 -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 252f92d..ea66c33 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 = 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 -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_hooks10.test.py b/tests/gold_tests/tls_hooks/tls_hooks13.test.py similarity index 74% copy from tests/gold_tests/tls_hooks/tls_hooks10.test.py copy to tests/gold_tests/tls_hooks/tls_hooks13.test.py index 59cccf5..9388221 100644 --- a/tests/gold_tests/tls_hooks/tls_hooks10.test.py +++ b/tests/gold_tests/tls_hooks/tls_hooks13.test.py @@ -1,5 +1,5 @@ ''' -Test one delayed cert callback and two immediate cert callbacks +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 @@ -24,13 +24,10 @@ 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"), - Condition.HasOpenSSLVersion("1.0.2") - ) +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") +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": ""} @@ -56,27 +53,20 @@ ts.Disk.ssl_multicert_config.AddLine( ) ts.Disk.remap_config.AddLine( - 'map https://example.com:4443 http://127.0.0.1:{0}'.format(server.Variables.Port) + '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, '-cert=1 -i=2') +Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 'ssl_hook_test.cc'), ts, '-out_start=1 -out_close=2') -tr = Test.AddTestRun("Test a combination of delayed and immediate cert hooks") +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 -tr.Processes.Default.Streams.stdout = "gold/preaccept-1.gold" -ts.Streams.stderr = "gold/ts-cert-1-im-2.gold" +ts.Streams.stderr = "gold/ts-out-start-close-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_hooks10.test.py b/tests/gold_tests/tls_hooks/tls_hooks14.test.py similarity index 74% copy from tests/gold_tests/tls_hooks/tls_hooks10.test.py copy to tests/gold_tests/tls_hooks/tls_hooks14.test.py index 59cccf5..b411f65 100644 --- a/tests/gold_tests/tls_hooks/tls_hooks10.test.py +++ b/tests/gold_tests/tls_hooks/tls_hooks14.test.py @@ -1,5 +1,5 @@ ''' -Test one delayed cert callback and two immediate cert callbacks +Test two outbound start delayed hooks ''' # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file @@ -24,13 +24,10 @@ 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"), - Condition.HasOpenSSLVersion("1.0.2") - ) +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") +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": ""} @@ -56,27 +53,20 @@ ts.Disk.ssl_multicert_config.AddLine( ) ts.Disk.remap_config.AddLine( - 'map https://example.com:4443 http://127.0.0.1:{0}'.format(server.Variables.Port) + '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, '-cert=1 -i=2') - -tr = Test.AddTestRun("Test a combination of delayed and immediate cert hooks") +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 -tr.Processes.Default.Streams.stdout = "gold/preaccept-1.gold" -ts.Streams.stderr = "gold/ts-cert-1-im-2.gold" +ts.Streams.stderr = "gold/ts-out-delay-start-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_hooks10.test.py b/tests/gold_tests/tls_hooks/tls_hooks15.test.py similarity index 74% copy from tests/gold_tests/tls_hooks/tls_hooks10.test.py copy to tests/gold_tests/tls_hooks/tls_hooks15.test.py index 59cccf5..ed6e687 100644 --- a/tests/gold_tests/tls_hooks/tls_hooks10.test.py +++ b/tests/gold_tests/tls_hooks/tls_hooks15.test.py @@ -1,5 +1,5 @@ ''' -Test one delayed cert callback and two immediate cert callbacks +Test one delayed preaccept callback ''' # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file @@ -24,13 +24,10 @@ 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"), - Condition.HasOpenSSLVersion("1.0.2") - ) +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") +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": ""} @@ -56,27 +53,20 @@ ts.Disk.ssl_multicert_config.AddLine( ) ts.Disk.remap_config.AddLine( - 'map https://example.com:4443 http://127.0.0.1:{0}'.format(server.Variables.Port) + '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, '-cert=1 -i=2') +Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 'ssl_hook_test.cc'), ts, '-close=2 -out_close=1') -tr = Test.AddTestRun("Test a combination of delayed and immediate cert hooks") +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 -tr.Processes.Default.Streams.stdout = "gold/preaccept-1.gold" -ts.Streams.stderr = "gold/ts-cert-1-im-2.gold" +ts.Streams.stderr = "gold/ts-close-out-close.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/tools/plugins/ssl_hook_test.cc b/tests/tools/plugins/ssl_hook_test.cc index 7321b9c..f7fde1c 100644 --- a/tests/tools/plugins/ssl_hook_test.cc +++ b/tests/tools/plugins/ssl_hook_test.cc @@ -30,6 +30,7 @@ #include <getopt.h> #include <openssl/ssl.h> #include <strings.h> +#include <cstring> #define PN "ssl_hook_test" #define PCP "[" PN " Plugin] " @@ -78,6 +79,70 @@ CB_Pre_Accept_Delay(TSCont cont, TSEvent event, void *edata) } int +CB_out_start(TSCont cont, TSEvent event, void *edata) +{ + TSVConn ssl_vc = reinterpret_cast<TSVConn>(edata); + + int count = reinterpret_cast<intptr_t>(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<TSVConn>(edata); + + int count = reinterpret_cast<intptr_t>(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<TSVConn>(edata); + + int count = reinterpret_cast<intptr_t>(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<TSVConn>(edata); + + int count = reinterpret_cast<intptr_t>(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) { TSVConn ssl_vc = reinterpret_cast<TSVConn>(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; }