You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by zw...@apache.org on 2017/07/26 23:40:49 UTC
[trafficserver] branch 7.1.x updated: Rework SSL handshake hooks
and add tls_hooks tests.
This is an automated email from the ASF dual-hosted git repository.
zwoop pushed a commit to branch 7.1.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/7.1.x by this push:
new 3c2ab23 Rework SSL handshake hooks and add tls_hooks tests.
3c2ab23 is described below
commit 3c2ab2338ebbb0ddde28c685eb3b4d0ea54cfa14
Author: Susan Hinrichs <sh...@spellhotel.corp.ne1.yahoo.com>
AuthorDate: Wed Jul 5 16:23:17 2017 +0000
Rework SSL handshake hooks and add tls_hooks tests.
(cherry picked from commit f68361e53d059b5773b055e0186b0224b1b7db51)
Conflicts:
iocore/net/P_SSLNetVConnection.h
iocore/net/SSLNetVConnection.cc
---
iocore/net/P_SSLNetVConnection.h | 52 ++++-
iocore/net/SSLNetVConnection.cc | 225 +++++++++++--------
.../autest-site/trafficserver_plugins.test.ext | 4 +-
tests/gold_tests/tls_hooks/gold/cert-1.gold | 0
tests/gold_tests/tls_hooks/gold/preaccept-1.gold | 0
tests/gold_tests/tls_hooks/gold/sni-1.gold | 0
.../gold_tests/tls_hooks/gold/ts-cert-1-im-2.gold | 5 +
tests/gold_tests/tls_hooks/gold/ts-cert-1.gold | 3 +
tests/gold_tests/tls_hooks/gold/ts-cert-2.gold | 5 +
tests/gold_tests/tls_hooks/gold/ts-cert-im-1.gold | 2 +
.../gold_tests/tls_hooks/gold/ts-preaccept-1.gold | 3 +
.../gold_tests/tls_hooks/gold/ts-preaccept-2.gold | 4 +
.../gold/ts-preaccept-delayed-1-immdate-2.gold | 5 +
.../tls_hooks/gold/ts-preaccept-delayed-1.gold | 3 +
.../tls_hooks/gold/ts-preaccept1-sni1-cert1.gold | 5 +
tests/gold_tests/tls_hooks/gold/ts-sni-1.gold | 3 +
tests/gold_tests/tls_hooks/gold/ts-sni-2.gold | 4 +
tests/gold_tests/tls_hooks/ssl/server.key | 15 ++
tests/gold_tests/tls_hooks/ssl/server.pem | 32 +++
tests/gold_tests/tls_hooks/tls_hooks.test.py | 78 +++++++
tests/gold_tests/tls_hooks/tls_hooks10.test.py | 79 +++++++
tests/gold_tests/tls_hooks/tls_hooks11.test.py | 76 +++++++
tests/gold_tests/tls_hooks/tls_hooks12.test.py | 87 ++++++++
tests/gold_tests/tls_hooks/tls_hooks2.test.py | 77 +++++++
tests/gold_tests/tls_hooks/tls_hooks3.test.py | 77 +++++++
tests/gold_tests/tls_hooks/tls_hooks4.test.py | 83 +++++++
tests/gold_tests/tls_hooks/tls_hooks6.test.py | 81 +++++++
tests/gold_tests/tls_hooks/tls_hooks7.test.py | 80 +++++++
tests/gold_tests/tls_hooks/tls_hooks8.test.py | 80 +++++++
tests/gold_tests/tls_hooks/tls_hooks9.test.py | 76 +++++++
tests/tools/plugins/ssl_hook_test.cc | 248 +++++++++++++++++++++
tools/tsxs.in | 1 +
32 files changed, 1388 insertions(+), 105 deletions(-)
diff --git a/iocore/net/P_SSLNetVConnection.h b/iocore/net/P_SSLNetVConnection.h
index 1878400..3b26b84 100644
--- a/iocore/net/P_SSLNetVConnection.h
+++ b/iocore/net/P_SSLNetVConnection.h
@@ -215,7 +215,44 @@ public:
// Returns true if we have already called at
// least some of the hooks
- bool calledHooks(TSEvent /* eventId */) const { return (this->sslHandshakeHookState != HANDSHAKE_HOOKS_PRE); }
+ bool
+ calledHooks(TSEvent eventId) const
+ {
+ bool retval = false;
+ switch (this->sslHandshakeHookState) {
+ case HANDSHAKE_HOOKS_PRE:
+ case HANDSHAKE_HOOKS_PRE_INVOKE:
+ if (eventId == TS_EVENT_VCONN_PRE_ACCEPT) {
+ if (curHook) {
+ retval = true;
+ }
+ }
+ break;
+ case HANDSHAKE_HOOKS_SNI:
+ if (eventId == TS_EVENT_VCONN_PRE_ACCEPT) {
+ retval = true;
+ } else if (eventId == TS_EVENT_SSL_SERVERNAME) {
+ if (curHook) {
+ retval = true;
+ }
+ }
+ break;
+ case HANDSHAKE_HOOKS_CERT:
+ case HANDSHAKE_HOOKS_CERT_INVOKE:
+ if (eventId == TS_EVENT_VCONN_PRE_ACCEPT || eventId == TS_EVENT_SSL_SERVERNAME) {
+ retval = true;
+ } else if (eventId == TS_EVENT_SSL_CERT) {
+ if (curHook) {
+ retval = true;
+ }
+ }
+ break;
+ case HANDSHAKE_HOOKS_DONE:
+ retval = true;
+ break;
+ }
+ return retval;
+ }
bool
getSSLTrace() const
{
@@ -281,19 +318,12 @@ private:
/// @note For @C SSL_HOOKS_INVOKE, this is the hook to invoke.
class APIHook *curHook;
- enum {
- SSL_HOOKS_INIT, ///< Initial state, no hooks called yet.
- SSL_HOOKS_INVOKE, ///< Waiting to invoke hook.
- SSL_HOOKS_ACTIVE, ///< Hook invoked, waiting for it to complete.
- SSL_HOOKS_CONTINUE, ///< All hooks have been called and completed
- SSL_HOOKS_DONE ///< All hooks have been called and completed
- } sslPreAcceptHookState;
-
enum SSLHandshakeHookState {
HANDSHAKE_HOOKS_PRE,
+ HANDSHAKE_HOOKS_PRE_INVOKE,
+ HANDSHAKE_HOOKS_SNI,
HANDSHAKE_HOOKS_CERT,
- HANDSHAKE_HOOKS_POST,
- HANDSHAKE_HOOKS_INVOKE,
+ HANDSHAKE_HOOKS_CERT_INVOKE,
HANDSHAKE_HOOKS_DONE
} sslHandshakeHookState;
diff --git a/iocore/net/SSLNetVConnection.cc b/iocore/net/SSLNetVConnection.cc
index e510d84..53d7012 100644
--- a/iocore/net/SSLNetVConnection.cc
+++ b/iocore/net/SSLNetVConnection.cc
@@ -381,6 +381,8 @@ SSLNetVConnection::read_raw_data()
BIO *rbio = BIO_new_mem_buf(start, this->handShakeBioStored);
BIO_set_mem_eof_return(rbio, -1);
SSL_set0_rbio(this->ssl, rbio);
+ } else {
+ this->handShakeBioStored = 0;
}
Debug("ssl", "%p read r=%" PRId64 " total=%" PRId64 " bio=%d\n", this, r, total_read, this->handShakeBioStored);
@@ -419,7 +421,8 @@ SSLNetVConnection::update_rbio(bool move_to_socket)
BIO_set_mem_eof_return(rbio, -1);
SSL_set0_rbio(this->ssl, rbio);
retval = true;
- } else if (move_to_socket) { // Handshake buffer is empty, move to the socket rbio
+ // Handshake buffer is empty but we have read something, move to the socket rbio
+ } else if (move_to_socket && this->handShakeHolder->is_read_avail_more_than(0)) {
BIO *rbio = BIO_new_fd(this->get_socket(), BIO_NOCLOSE);
BIO_set_mem_eof_return(rbio, -1);
SSL_set0_rbio(this->ssl, rbio);
@@ -486,10 +489,7 @@ SSLNetVConnection::net_read_io(NetHandler *nh, EThread *lthread)
}
// If we have flipped to blind tunnel, don't read ahead
if (this->handShakeReader) {
- if (this->attributes != HttpProxyPort::TRANSPORT_BLIND_TUNNEL) {
- // Check and consume data that has been read
- update_rbio(false);
- } else {
+ if (this->attributes == HttpProxyPort::TRANSPORT_BLIND_TUNNEL) {
// Now in blind tunnel. Set things up to read what is in the buffer
// Must send the READ_COMPLETE here before considering
// forwarding on the handshake buffer, so the
@@ -544,7 +544,6 @@ SSLNetVConnection::net_read_io(NetHandler *nh, EThread *lthread)
if (this->handShakeBuffer) {
read.triggered = update_rbio(true);
} else {
- Debug("ssl", "Want read from socket");
read.triggered = 0;
}
if (!read.triggered) {
@@ -817,7 +816,6 @@ SSLNetVConnection::SSLNetVConnection()
handShakeHolder(nullptr),
handShakeReader(nullptr),
handShakeBioStored(0),
- sslPreAcceptHookState(SSL_HOOKS_INIT),
sslHandshakeHookState(HANDSHAKE_HOOKS_PRE),
npnSet(nullptr),
npnEndpoint(nullptr),
@@ -910,16 +908,11 @@ SSLNetVConnection::free(EThread *t)
sslClientRenegotiationAbort = false;
sslSessionCacheHit = false;
- if (SSL_HOOKS_ACTIVE == sslPreAcceptHookState) {
- Error("SSLNetVconnection freed with outstanding hook");
- }
-
- sslPreAcceptHookState = SSL_HOOKS_INIT;
- curHook = nullptr;
- hookOpRequested = SSL_HOOK_OP_DEFAULT;
- npnSet = nullptr;
- npnEndpoint = nullptr;
- sslHandShakeComplete = false;
+ curHook = nullptr;
+ hookOpRequested = SSL_HOOK_OP_DEFAULT;
+ npnSet = nullptr;
+ npnEndpoint = nullptr;
+ sslHandShakeComplete = false;
free_handshake_buffers();
sslTrace = false;
@@ -1050,41 +1043,29 @@ SSLNetVConnection::sslStartHandShake(int event, int &err)
int
SSLNetVConnection::sslServerHandShakeEvent(int &err)
{
- if (SSL_HOOKS_DONE != sslPreAcceptHookState) {
- // Get the first hook if we haven't started invoking yet.
- if (SSL_HOOKS_INIT == sslPreAcceptHookState) {
- curHook = ssl_hooks->get(TS_VCONN_PRE_ACCEPT_INTERNAL_HOOK);
- sslPreAcceptHookState = SSL_HOOKS_INVOKE;
- } else if (SSL_HOOKS_INVOKE == sslPreAcceptHookState) {
- // if the state is anything else, we haven't finished
- // the previous hook yet.
+ // Continue on if we are in the invoked state. The hook has not yet reenabled
+ if (sslHandshakeHookState == HANDSHAKE_HOOKS_CERT_INVOKE || sslHandshakeHookState == HANDSHAKE_HOOKS_PRE_INVOKE) {
+ return SSL_WAIT_FOR_HOOK;
+ }
+
+ // Go do the preaccept hooks
+ if (sslHandshakeHookState == HANDSHAKE_HOOKS_PRE) {
+ if (!curHook) {
+ Debug("ssl", "Initialize preaccept curHook from NULL");
+ curHook = ssl_hooks->get(TS_VCONN_PRE_ACCEPT_INTERNAL_HOOK);
+ } else {
curHook = curHook->next();
}
-
- if (SSL_HOOKS_INVOKE == sslPreAcceptHookState) {
- if (nullptr == curHook) { // no hooks left, we're done
- sslPreAcceptHookState = SSL_HOOKS_DONE;
- } else {
- sslPreAcceptHookState = SSL_HOOKS_ACTIVE;
- ContWrapper::wrap(mutex.get(), curHook->m_cont, TS_EVENT_VCONN_PRE_ACCEPT, this);
- return SSL_WAIT_FOR_HOOK;
- }
- } else { // waiting for hook to complete
- /* A note on waiting for the hook. I believe that because this logic
- cannot proceed as long as a hook is outstanding, the underlying VC
- can't go stale. If that can happen for some reason, we'll need to be
- more clever and provide some sort of cancel mechanism. I have a trap
- in SSLNetVConnection::free to check for this.
- */
+ // If no more hooks, move onto SNI
+ if (nullptr == curHook) {
+ sslHandshakeHookState = HANDSHAKE_HOOKS_SNI;
+ } else {
+ sslHandshakeHookState = HANDSHAKE_HOOKS_PRE_INVOKE;
+ ContWrapper::wrap(nh->mutex.get(), curHook->m_cont, TS_EVENT_VCONN_PRE_ACCEPT, this);
return SSL_WAIT_FOR_HOOK;
}
}
- // handle SNI Hooks after PreAccept Hooks
- if (HANDSHAKE_HOOKS_DONE != sslHandshakeHookState && HANDSHAKE_HOOKS_PRE != sslHandshakeHookState) {
- return SSL_WAIT_FOR_HOOK;
- }
-
// If a blind tunnel was requested in the pre-accept calls, convert.
// Again no data has been exchanged, so we can go directly
// without data replay.
@@ -1102,11 +1083,14 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)
sslHandShakeComplete = true;
return EVENT_DONE;
}
+
+ Debug("ssl", "Go on with the handshake state=%d", sslHandshakeHookState);
+
// All the pre-accept hooks have completed, proceed with the actual accept.
if (this->handShakeReader) {
if (BIO_eof(SSL_get_rbio(this->ssl))) { // No more data in the buffer
// Is this the first read?
- if (!this->handShakeReader->is_read_avail_more_than(0)) {
+ if (!this->handShakeReader->is_read_avail_more_than(0) && !this->handShakeHolder->is_read_avail_more_than(0)) {
Debug("ssl", "%p first read\n", this);
// Read from socket to fill in the BIO buffer with the
// raw handshake data before calling the ssl accept calls.
@@ -1114,7 +1098,7 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)
if (retval < 0) {
if (retval == -EAGAIN) {
// No data at the moment, hang tight
- // SSLDebugVC(this, "SSL handshake: EAGAIN");
+ SSLDebugVC(this, "SSL handshake: EAGAIN");
return SSL_HANDSHAKE_WANT_READ;
} else {
// An error, make us go away
@@ -1424,27 +1408,61 @@ SSLNetVConnection::select_next_protocol(SSL *ssl, const unsigned char **out, uns
void
SSLNetVConnection::reenable(NetHandler *nh)
{
- if (sslPreAcceptHookState != SSL_HOOKS_DONE) {
- sslPreAcceptHookState = SSL_HOOKS_INVOKE;
- } else if (sslHandshakeHookState == HANDSHAKE_HOOKS_INVOKE) {
- // Reenabling from the handshake callback
- //
- // Originally, we would wait for the callback to go again to execute additinonal
- // hooks, but since the callbacks are associated with the context and the context
- // can be replaced by the plugin, it didn't seem reasonable to assume that the
- // callback would be executed again. So we walk through the rest of the hooks
- // here in the reenable.
- if (curHook != nullptr) {
- curHook = curHook->next();
- }
- if (curHook != nullptr) {
- // Invoke the hook and return, wait for next reenable
+ Debug("ssl", "Handshake reenable from state=%d", sslHandshakeHookState);
+
+ switch (sslHandshakeHookState) {
+ case HANDSHAKE_HOOKS_PRE_INVOKE:
+ sslHandshakeHookState = HANDSHAKE_HOOKS_PRE;
+ break;
+ case HANDSHAKE_HOOKS_CERT_INVOKE:
+ sslHandshakeHookState = HANDSHAKE_HOOKS_CERT;
+ break;
+ default:
+ break;
+ }
+
+ // Reenabling from the handshake callback
+ //
+ // Originally, we would wait for the callback to go again to execute additinonal
+ // hooks, but since the callbacks are associated with the context and the context
+ // can be replaced by the plugin, it didn't seem reasonable to assume that the
+ // callback would be executed again. So we walk through the rest of the hooks
+ // here in the reenable.
+ if (curHook != nullptr) {
+ curHook = curHook->next();
+ Debug("ssl", "iterate from reenable curHook=%p", curHook);
+ }
+ if (curHook != nullptr) {
+ // Invoke the hook and return, wait for next reenable
+ if (sslHandshakeHookState == HANDSHAKE_HOOKS_CERT) {
+ sslHandshakeHookState = HANDSHAKE_HOOKS_CERT_INVOKE;
curHook->invoke(TS_EVENT_SSL_CERT, this);
- return;
- } else { // curHook == nullptr
- // empty, set state to HOOKS_DONE
- this->sslHandshakeHookState = HANDSHAKE_HOOKS_DONE;
+ } else if (sslHandshakeHookState == HANDSHAKE_HOOKS_SNI) {
+ curHook->invoke(TS_EVENT_SSL_SERVERNAME, this);
+ } else if (sslHandshakeHookState == HANDSHAKE_HOOKS_PRE) {
+ Debug("ssl", "Reenable preaccept");
+ sslHandshakeHookState = HANDSHAKE_HOOKS_PRE_INVOKE;
+ ContWrapper::wrap(nh->mutex.get(), curHook->m_cont, TS_EVENT_VCONN_PRE_ACCEPT, this);
+ }
+ return;
+ } else {
+ // Move onto the "next" state
+ switch (this->sslHandshakeHookState) {
+ case HANDSHAKE_HOOKS_PRE:
+ case HANDSHAKE_HOOKS_PRE_INVOKE:
+ sslHandshakeHookState = HANDSHAKE_HOOKS_SNI;
+ break;
+ case HANDSHAKE_HOOKS_SNI:
+ sslHandshakeHookState = HANDSHAKE_HOOKS_CERT;
+ break;
+ case HANDSHAKE_HOOKS_CERT:
+ case HANDSHAKE_HOOKS_CERT_INVOKE:
+ sslHandshakeHookState = HANDSHAKE_HOOKS_DONE;
+ break;
+ default:
+ break;
}
+ Debug("ssl", "iterate from reenable curHook=%p %d", curHook, sslHandshakeHookState);
}
this->readReschedule(nh);
}
@@ -1471,42 +1489,65 @@ SSLNetVConnection::callHooks(TSEvent eventId)
ink_assert(eventId == TS_EVENT_SSL_CERT || eventId == TS_EVENT_SSL_SERVERNAME);
Debug("ssl", "callHooks sslHandshakeHookState=%d", this->sslHandshakeHookState);
- // First time through, set the type of the hook that is currently being invoked
- if ((this->sslHandshakeHookState == HANDSHAKE_HOOKS_PRE || this->sslHandshakeHookState == HANDSHAKE_HOOKS_DONE) &&
- eventId == TS_EVENT_SSL_CERT) {
- // the previous hook should be DONE and set curHook to nullptr before trigger the sni hook.
- ink_assert(curHook == nullptr);
- // set to HOOKS_CERT means CERT/SNI hooks has called by SSL_accept()
- this->sslHandshakeHookState = HANDSHAKE_HOOKS_CERT;
- // get Hooks
- curHook = ssl_hooks->get(TS_SSL_CERT_INTERNAL_HOOK);
- } else if (eventId == TS_EVENT_SSL_SERVERNAME) {
+ // Move state if it is appropriate
+ switch (this->sslHandshakeHookState) {
+ case HANDSHAKE_HOOKS_PRE:
+ if (eventId == TS_EVENT_SSL_SERVERNAME) {
+ this->sslHandshakeHookState = HANDSHAKE_HOOKS_SNI;
+ } else if (eventId == TS_EVENT_SSL_CERT) {
+ this->sslHandshakeHookState = HANDSHAKE_HOOKS_CERT;
+ }
+ break;
+ case HANDSHAKE_HOOKS_SNI:
+ if (eventId == TS_EVENT_SSL_CERT) {
+ this->sslHandshakeHookState = HANDSHAKE_HOOKS_CERT;
+ }
+ break;
+ default:
+ break;
+ }
+
+ // Look for hooks associated with the event
+ switch (this->sslHandshakeHookState) {
+ case HANDSHAKE_HOOKS_SNI:
if (!curHook) {
curHook = ssl_hooks->get(TS_SSL_SERVERNAME_INTERNAL_HOOK);
+ } else {
+ curHook = curHook->next();
}
- } else {
- // Not in the right state
- // reenable and continue
+ if (!curHook) {
+ this->sslHandshakeHookState = HANDSHAKE_HOOKS_CERT;
+ }
+ break;
+ case HANDSHAKE_HOOKS_CERT:
+ case HANDSHAKE_HOOKS_CERT_INVOKE:
+ if (!curHook) {
+ curHook = ssl_hooks->get(TS_SSL_CERT_INTERNAL_HOOK);
+ } else {
+ curHook = curHook->next();
+ }
+ if (curHook == nullptr) {
+ this->sslHandshakeHookState = HANDSHAKE_HOOKS_DONE;
+ } else {
+ this->sslHandshakeHookState = HANDSHAKE_HOOKS_CERT_INVOKE;
+ }
+ break;
+ default:
+ curHook = nullptr;
+ this->sslHandshakeHookState = HANDSHAKE_HOOKS_DONE;
return true;
}
+ Debug("ssl", "callHooks iterated to curHook=%p", curHook);
+
bool reenabled = true;
if (curHook != nullptr) {
- this->sslHandshakeHookState = HANDSHAKE_HOOKS_INVOKE;
curHook->invoke(eventId, this);
- reenabled = eventId != TS_EVENT_SSL_CERT || (this->sslHandshakeHookState != HANDSHAKE_HOOKS_INVOKE);
+ reenabled =
+ (this->sslHandshakeHookState != HANDSHAKE_HOOKS_CERT_INVOKE && this->sslHandshakeHookState != HANDSHAKE_HOOKS_PRE_INVOKE);
+ Debug("ssl", "Called hook on state=%d reenabled=%d", sslHandshakeHookState, reenabled);
}
- // All done with the current hook chain
- if (curHook == nullptr) {
- if (eventId == TS_EVENT_SSL_CERT) {
- // Set the HookState to done because we are all done with the CERT/SERVERNAME hook chains
- sslHandshakeHookState = HANDSHAKE_HOOKS_DONE;
- } else if (eventId == TS_EVENT_SSL_SERVERNAME) {
- // Reset the HookState to PRE, so the cert hook chain can start
- sslHandshakeHookState = HANDSHAKE_HOOKS_PRE;
- }
- }
return reenabled;
}
diff --git a/tests/gold_tests/autest-site/trafficserver_plugins.test.ext b/tests/gold_tests/autest-site/trafficserver_plugins.test.ext
index fb53bd7..9ba2718 100644
--- a/tests/gold_tests/autest-site/trafficserver_plugins.test.ext
+++ b/tests/gold_tests/autest-site/trafficserver_plugins.test.ext
@@ -19,7 +19,7 @@ Builds, installs, and enables an ATS plugin in the sandbox environment
import os
-def prepare_plugin(self, path, tsproc):
+def prepare_plugin(self, path, tsproc, plugin_args = ""):
"""Builds, installs, and enables an ATS plugin in the sandbox environment
The source file at the given path is copied to the sandbox directory of the
@@ -39,6 +39,6 @@ def prepare_plugin(self, path, tsproc):
tsproc.Setup.RunCommand("tsxs -c {0} -o {1}".format(in_path, out_path))
# Add an entry to plugin.config.
- tsproc.Disk.plugin_config.AddLine(out_basename)
+ tsproc.Disk.plugin_config.AddLine("{0} {1}".format(out_basename,plugin_args))
ExtendTest(prepare_plugin, name="prepare_plugin")
diff --git a/tests/gold_tests/tls_hooks/gold/cert-1.gold b/tests/gold_tests/tls_hooks/gold/cert-1.gold
new file mode 100644
index 0000000..e69de29
diff --git a/tests/gold_tests/tls_hooks/gold/preaccept-1.gold b/tests/gold_tests/tls_hooks/gold/preaccept-1.gold
new file mode 100644
index 0000000..e69de29
diff --git a/tests/gold_tests/tls_hooks/gold/sni-1.gold b/tests/gold_tests/tls_hooks/gold/sni-1.gold
new file mode 100644
index 0000000..e69de29
diff --git a/tests/gold_tests/tls_hooks/gold/ts-cert-1-im-2.gold b/tests/gold_tests/tls_hooks/gold/ts-cert-1-im-2.gold
new file mode 100644
index 0000000..a2ec9b7
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/gold/ts-cert-1-im-2.gold
@@ -0,0 +1,5 @@
+`` DIAG: (ssl_hook_test) Setup callbacks pa=0 sni=0 cert=1 cert_imm=2 pa_delay=0
+`` DIAG: (ssl_hook_test) Cert callback 0 ssl_vc=``
+`` DIAG: (ssl_hook_test) Callback reenable ssl_vc=``
+`` DIAG: (ssl_hook_test) Cert callback 0 ssl_vc=``
+`` DIAG: (ssl_hook_test) Cert callback 1 ssl_vc=``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-cert-1.gold b/tests/gold_tests/tls_hooks/gold/ts-cert-1.gold
new file mode 100644
index 0000000..91f7b38
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/gold/ts-cert-1.gold
@@ -0,0 +1,3 @@
+`` DIAG: (ssl_hook_test) Setup callbacks pa=0 sni=0 cert=1 cert_imm=0 pa_delay=0
+`` DIAG: (ssl_hook_test) Cert callback 0 ssl_vc=``
+`` DIAG: (ssl_hook_test) Callback reenable ssl_vc=``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-cert-2.gold b/tests/gold_tests/tls_hooks/gold/ts-cert-2.gold
new file mode 100644
index 0000000..355c5ae
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/gold/ts-cert-2.gold
@@ -0,0 +1,5 @@
+`` DIAG: (ssl_hook_test) Setup callbacks pa=0 sni=0 cert=2 cert_imm=0 pa_delay=0
+`` DIAG: (ssl_hook_test) Cert callback 0 ssl_vc=``
+`` DIAG: (ssl_hook_test) Callback reenable ssl_vc=``
+`` DIAG: (ssl_hook_test) Cert callback 1 ssl_vc=``
+`` DIAG: (ssl_hook_test) Callback reenable ssl_vc=``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-cert-im-1.gold b/tests/gold_tests/tls_hooks/gold/ts-cert-im-1.gold
new file mode 100644
index 0000000..c571bae
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/gold/ts-cert-im-1.gold
@@ -0,0 +1,2 @@
+`` DIAG: (ssl_hook_test) Setup callbacks pa=0 sni=0 cert=0 cert_imm=1 pa_delay=0
+`` DIAG: (ssl_hook_test) Cert callback 0 ssl_vc=``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-preaccept-1.gold b/tests/gold_tests/tls_hooks/gold/ts-preaccept-1.gold
new file mode 100644
index 0000000..c8278ea
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/gold/ts-preaccept-1.gold
@@ -0,0 +1,3 @@
+`` DIAG: (ssl_hook_test) Setup callbacks pa=1 sni=0 cert=0 cert_imm=0 pa_delay=0
+`` DIAG: (ssl_hook_test) Pre accept callback 0 `` - event is good
+``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-preaccept-2.gold b/tests/gold_tests/tls_hooks/gold/ts-preaccept-2.gold
new file mode 100644
index 0000000..cfac682
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/gold/ts-preaccept-2.gold
@@ -0,0 +1,4 @@
+`` DIAG: (ssl_hook_test) Setup callbacks pa=2 sni=0 cert=0 cert_imm=0 pa_delay=0
+`` DIAG: (ssl_hook_test) Pre accept callback 0 `` - event is good
+`` DIAG: (ssl_hook_test) Pre accept callback 1 `` - event is good
+``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-preaccept-delayed-1-immdate-2.gold b/tests/gold_tests/tls_hooks/gold/ts-preaccept-delayed-1-immdate-2.gold
new file mode 100644
index 0000000..16427c9
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/gold/ts-preaccept-delayed-1-immdate-2.gold
@@ -0,0 +1,5 @@
+``DIAG: (ssl_hook_test) Setup callbacks pa=2 sni=0 cert=0 cert_imm=0 pa_delay=1
+``DIAG: (ssl_hook_test) Pre accept callback 0 `` - event is good
+``DIAG: (ssl_hook_test) Pre accept callback 1 `` - event is good
+``DIAG: (ssl_hook_test) Pre accept delay callback 0 `` - event is good
+``DIAG: (ssl_hook_test) Callback reenable ssl_vc=``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-preaccept-delayed-1.gold b/tests/gold_tests/tls_hooks/gold/ts-preaccept-delayed-1.gold
new file mode 100644
index 0000000..0b85e17
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/gold/ts-preaccept-delayed-1.gold
@@ -0,0 +1,3 @@
+`` DIAG: (ssl_hook_test) Setup callbacks pa=0 sni=0 cert=0 cert_imm=0 pa_delay=1
+`` DIAG: (ssl_hook_test) Pre accept delay callback 0 `` - event is good
+``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-preaccept1-sni1-cert1.gold b/tests/gold_tests/tls_hooks/gold/ts-preaccept1-sni1-cert1.gold
new file mode 100644
index 0000000..07ee131
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/gold/ts-preaccept1-sni1-cert1.gold
@@ -0,0 +1,5 @@
+`` DIAG: (ssl_hook_test) Setup callbacks pa=1 sni=1 cert=1 cert_imm=0 pa_delay=0
+`` DIAG: (ssl_hook_test) Pre accept callback 0 `` - event is good
+`` DIAG: (ssl_hook_test) SNI callback 0 ``
+`` DIAG: (ssl_hook_test) Cert callback 0 ssl_vc=``
+`` DIAG: (ssl_hook_test) Callback reenable ssl_vc=``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-sni-1.gold b/tests/gold_tests/tls_hooks/gold/ts-sni-1.gold
new file mode 100644
index 0000000..4b7d335
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/gold/ts-sni-1.gold
@@ -0,0 +1,3 @@
+`` DIAG: (ssl_hook_test) Setup callbacks pa=0 sni=1 cert=0 cert_imm=0 pa_delay=0
+`` DIAG: (ssl_hook_test) SNI callback 0 ``
+``
diff --git a/tests/gold_tests/tls_hooks/gold/ts-sni-2.gold b/tests/gold_tests/tls_hooks/gold/ts-sni-2.gold
new file mode 100644
index 0000000..ecb2cb6
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/gold/ts-sni-2.gold
@@ -0,0 +1,4 @@
+`` DIAG: (ssl_hook_test) Setup callbacks pa=0 sni=2 cert=0 cert_imm=0 pa_delay=0
+`` DIAG: (ssl_hook_test) SNI callback 0 ``
+`` DIAG: (ssl_hook_test) SNI callback 1 ``
+``
diff --git a/tests/gold_tests/tls_hooks/ssl/server.key b/tests/gold_tests/tls_hooks/ssl/server.key
new file mode 100644
index 0000000..4c7a661
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/ssl/server.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDWMHOiUF+ORmZjAxI8MWE9dblb7gQSJ36WCXlPFiFx6ynF+S1E
+kXAYpIip5X0pzDUaIbLukxJUAAnOtMEO0PCgxJQUrEtRWh8wiJdbdQJF0Zs/9R+u
+SUgb61f+mdTQvhqefBGx+xrpfAcgtcWiZuSA9Q3fvpDj5WOWSPWXBUuxywIDAQAB
+AoGBAJPxRX2gjFAGWmQbU/YVmXfNH6navh8X/nx9sLeqrpE0AFeJI/ZPiqDKzMal
+B43eSfNxwVi+ZxN0L1ICUbL9KKZvHs/QBxWLA1fGVAXrz7sRplEVvakPpTfHoEnv
+sKaMWVKaK/S5WGbDhElb6zb/Lwo19DsIAPjGYqFvzFJBmobJAkEA9iSeTGkR9X26
+GywZoYrIMlRh34htOIRx1UUq88rFzdrCF21kQ4lhBIkX5OZMMy652i2gyak4OZTe
+YewIv8jw9QJBAN7EQNHG8jPwXfVp91/fqxVQEfumuP2i6uiWWYQgZCmla2+0xcLZ
+pMQ6sQEe10hhTrVnzHgAUVp50Ntn2jwBX78CQF09veGAI9d1Cxzj9cmmAvRd1r2Q
+tp8kPOLnUsALXib+6WtqewLCdcf8DtsdClyRJMIraq85tRzK8fryKNZNzkkCQEgA
+yS7FDj5JgCU15hZgFk1iPx3HCt44jZM2HaL+UUHAzRQjKxTLAl3G1rWVAWLMyQML
+lORoveLvotl4HOruSsMCQQCAx9dV9JUSFoyc1CWILp/FgUH/se4cjQCThGO0DoQQ
+vGTYmntY7j9WRJ9esQrjdD6Clw8zM/45GIBNwnXzqo7Z
+-----END RSA PRIVATE KEY-----
diff --git a/tests/gold_tests/tls_hooks/ssl/server.pem b/tests/gold_tests/tls_hooks/ssl/server.pem
new file mode 100644
index 0000000..a1de94f
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/ssl/server.pem
@@ -0,0 +1,32 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDWMHOiUF+ORmZjAxI8MWE9dblb7gQSJ36WCXlPFiFx6ynF+S1E
+kXAYpIip5X0pzDUaIbLukxJUAAnOtMEO0PCgxJQUrEtRWh8wiJdbdQJF0Zs/9R+u
+SUgb61f+mdTQvhqefBGx+xrpfAcgtcWiZuSA9Q3fvpDj5WOWSPWXBUuxywIDAQAB
+AoGBAJPxRX2gjFAGWmQbU/YVmXfNH6navh8X/nx9sLeqrpE0AFeJI/ZPiqDKzMal
+B43eSfNxwVi+ZxN0L1ICUbL9KKZvHs/QBxWLA1fGVAXrz7sRplEVvakPpTfHoEnv
+sKaMWVKaK/S5WGbDhElb6zb/Lwo19DsIAPjGYqFvzFJBmobJAkEA9iSeTGkR9X26
+GywZoYrIMlRh34htOIRx1UUq88rFzdrCF21kQ4lhBIkX5OZMMy652i2gyak4OZTe
+YewIv8jw9QJBAN7EQNHG8jPwXfVp91/fqxVQEfumuP2i6uiWWYQgZCmla2+0xcLZ
+pMQ6sQEe10hhTrVnzHgAUVp50Ntn2jwBX78CQF09veGAI9d1Cxzj9cmmAvRd1r2Q
+tp8kPOLnUsALXib+6WtqewLCdcf8DtsdClyRJMIraq85tRzK8fryKNZNzkkCQEgA
+yS7FDj5JgCU15hZgFk1iPx3HCt44jZM2HaL+UUHAzRQjKxTLAl3G1rWVAWLMyQML
+lORoveLvotl4HOruSsMCQQCAx9dV9JUSFoyc1CWILp/FgUH/se4cjQCThGO0DoQQ
+vGTYmntY7j9WRJ9esQrjdD6Clw8zM/45GIBNwnXzqo7Z
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIICszCCAhwCCQCRJsJJ+mTsdDANBgkqhkiG9w0BAQsFADCBnTELMAkGA1UEBhMC
+VVMxCzAJBgNVBAgMAklMMRIwEAYDVQQHDAlDaGFtcGFpZ24xDjAMBgNVBAoMBVlh
+aG9vMQ0wCwYDVQQLDARFZGdlMSgwJgYDVQQDDB9qdWljZXByb2R1Y2UuY29ycC5u
+ZTEueWFob28uY29tMSQwIgYJKoZIhvcNAQkBFhVwZXJzaWEuYXppekB5YWhvby5j
+b20wHhcNMTYwODI1MjI1NzIxWhcNMTcwODI1MjI1NzIxWjCBnTELMAkGA1UEBhMC
+VVMxCzAJBgNVBAgMAklMMRIwEAYDVQQHDAlDaGFtcGFpZ24xDjAMBgNVBAoMBVlh
+aG9vMQ0wCwYDVQQLDARFZGdlMSgwJgYDVQQDDB9qdWljZXByb2R1Y2UuY29ycC5u
+ZTEueWFob28uY29tMSQwIgYJKoZIhvcNAQkBFhVwZXJzaWEuYXppekB5YWhvby5j
+b20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANYwc6JQX45GZmMDEjwxYT11
+uVvuBBInfpYJeU8WIXHrKcX5LUSRcBikiKnlfSnMNRohsu6TElQACc60wQ7Q8KDE
+lBSsS1FaHzCIl1t1AkXRmz/1H65JSBvrV/6Z1NC+Gp58EbH7Gul8ByC1xaJm5ID1
+Dd++kOPlY5ZI9ZcFS7HLAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAXSVfZ5p1TkhW
+QiYq9nfQlBnX2NVaf8ymA8edQR0qH/QBv4/52bNNXC7V/V+ev9LCho2iRMeYYyXB
+yo1wBAGR83lS9cF/tOABcYrxjdP54Sfkyh5fomcg8SV7zap6C8mhbV8r3EujbKCx
+igH3fMX5F/eRwNCzaMMyQsXaxTJ3trk=
+-----END CERTIFICATE-----
diff --git a/tests/gold_tests/tls_hooks/tls_hooks.test.py b/tests/gold_tests/tls_hooks/tls_hooks.test.py
new file mode 100644
index 0000000..d56a82c
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/tls_hooks.test.py
@@ -0,0 +1,78 @@
+'''
+Test 1 preaccept callback (without delay)
+'''
+# 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")
+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 http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+
+Test.prepare_plugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 'ssl_hook_test.cc'), ts, '-preaccept=1')
+
+tr = Test.AddTestRun("Test one 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-preaccept-1.gold"
+
+# the preaccept may get triggered twice because the test framework creates a TCP connection before handing off to traffic_server
+preacceptstring = "Pre accept callback 0"
+ts.Streams.All = Testers.ContainsExpression(
+ "\A(?:(?!{0}).)*{0}.*({0})?(?!.*{0}).*\Z".format(preacceptstring), "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_hooks10.test.py
new file mode 100644
index 0000000..4ff930a
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/tls_hooks10.test.py
@@ -0,0 +1,79 @@
+'''
+Test one delayed cert callback and two immediate cert callbacks
+'''
+# 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")
+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 http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+
+Test.prepare_plugin(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")
+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"
+
+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_hooks11.test.py b/tests/gold_tests/tls_hooks/tls_hooks11.test.py
new file mode 100644
index 0000000..fd23c66
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/tls_hooks11.test.py
@@ -0,0 +1,76 @@
+'''
+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")
+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 http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+
+Test.prepare_plugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 'ssl_hook_test.cc'), ts, '-d=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
+tr.Processes.Default.Streams.stdout = "gold/preaccept-1.gold"
+
+ts.Streams.stderr = "gold/ts-preaccept-delayed-1.gold"
+
+preacceptstring = "Pre accept delay callback 0"
+ts.Streams.All = Testers.ContainsExpression(
+ "\A(?:(?!{0}).)*{0}.*({0})?(?!.*{0}).*\Z".format(preacceptstring), "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_hooks12.test.py b/tests/gold_tests/tls_hooks/tls_hooks12.test.py
new file mode 100644
index 0000000..571538b
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/tls_hooks12.test.py
@@ -0,0 +1,87 @@
+'''
+Test a combination of delayed and immediate preaccept callbacks
+'''
+# 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")
+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 http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+
+Test.prepare_plugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 'ssl_hook_test.cc'), ts, '-p=2 -d=1')
+
+tr = Test.AddTestRun("Test combination of delayed and immediate preaccept hook2")
+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-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_hooks2.test.py b/tests/gold_tests/tls_hooks/tls_hooks2.test.py
new file mode 100644
index 0000000..e6dbd50
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/tls_hooks2.test.py
@@ -0,0 +1,77 @@
+'''
+Test single SNI hook
+'''
+# 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")
+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 http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+
+Test.prepare_plugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 'ssl_hook_test.cc'), ts, '-sni=1')
+
+tr = Test.AddTestRun("Test one sni 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-sni-1.gold"
+
+snistring = "SNI callback 0"
+ts.Streams.All = Testers.ContainsExpression(
+ "\A(?:(?!{0}).)*{0}(?!.*{0}).*\Z".format(snistring), "SNI 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_hooks3.test.py b/tests/gold_tests/tls_hooks/tls_hooks3.test.py
new file mode 100644
index 0000000..c6ce668
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/tls_hooks3.test.py
@@ -0,0 +1,77 @@
+'''
+Test single delayed cert 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")
+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 http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+
+Test.prepare_plugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 'ssl_hook_test.cc'), ts, '-cert=1')
+
+tr = Test.AddTestRun("Test one cert 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.gold"
+
+certstring = "Cert callback 0"
+ts.Streams.All = Testers.ContainsExpression(
+ "\A(?:(?!{0}).)*{0}(?!.*{0}).*\Z".format(certstring), "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_hooks4.test.py b/tests/gold_tests/tls_hooks/tls_hooks4.test.py
new file mode 100644
index 0000000..8a9fb48
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/tls_hooks4.test.py
@@ -0,0 +1,83 @@
+'''
+Test 1 preaccept, 1 sni, and 1 cert callback (with delay)
+'''
+# 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")
+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 http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+
+Test.prepare_plugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 'ssl_hook_test.cc'), ts, '-cert=1 -sni=1 -preaccept=1')
+
+tr = Test.AddTestRun("Test one sni, one preaccept, and one cert 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-preaccept1-sni1-cert1.gold"
+snistring = "SNI callback 0"
+preacceptstring = "Pre accept callback 0"
+certstring = "Cert callback 0"
+ts.Streams.All = Testers.ContainsExpression(
+ "\A(?:(?!{0}).)*{0}(?!.*{0}).*\Z".format(snistring), "SNI message appears only once", reflags=re.S | re.M)
+# the preaccept may get triggered twice because the test framework creates a TCP connection before handing off to traffic_server
+ts.Streams.All += Testers.ContainsExpression("\A(?:(?!{0}).)*{0}.*({0})?(?!.*{0}).*\Z".format(
+ preacceptstring), "Pre accept message appears only once or twice", reflags=re.S | re.M)
+ts.Streams.All += Testers.ContainsExpression("\A(?:(?!{0}).)*{0}(?!.*{0}).*\Z".format(certstring),
+ "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_hooks6.test.py b/tests/gold_tests/tls_hooks/tls_hooks6.test.py
new file mode 100644
index 0000000..bfc0800
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/tls_hooks6.test.py
@@ -0,0 +1,81 @@
+'''
+Test 1 preaccept callback (without delay) two times
+'''
+# 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")
+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 http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+
+Test.prepare_plugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 'ssl_hook_test.cc'), ts, '-preaccept=2')
+
+tr = Test.AddTestRun("Test two preaccept hooks")
+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-preaccept-2.gold"
+
+# the preaccept may get triggered twice because the test framework creates a TCP connection before handing off to traffic_server
+preacceptstring0 = "Pre accept callback 0"
+preacceptstring1 = "Pre accept callback 1"
+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)
+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)
+
+tr.Processes.Default.TimeOut = 5
+tr.TimeOut = 5
diff --git a/tests/gold_tests/tls_hooks/tls_hooks7.test.py b/tests/gold_tests/tls_hooks/tls_hooks7.test.py
new file mode 100644
index 0000000..bf93223
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/tls_hooks7.test.py
@@ -0,0 +1,80 @@
+'''
+Test two SNI 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")
+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 http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+
+Test.prepare_plugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 'ssl_hook_test.cc'), ts, '-sni=2')
+
+tr = Test.AddTestRun("Test two sni hooks")
+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-sni-2.gold"
+
+snistring0 = "SNI callback 0"
+snistring1 = "SNI callback 1"
+ts.Streams.All = Testers.ContainsExpression(
+ "\A(?:(?!{0}).)*{0}(?!.*{0}).*\Z".format(snistring0), "SNI message appears only once", reflags=re.S | re.M)
+ts.Streams.All = Testers.ContainsExpression(
+ "\A(?:(?!{0}).)*{0}(?!.*{0}).*\Z".format(snistring1), "SNI 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_hooks8.test.py b/tests/gold_tests/tls_hooks/tls_hooks8.test.py
new file mode 100644
index 0000000..1e201e7
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/tls_hooks8.test.py
@@ -0,0 +1,80 @@
+'''
+Test two delayed cert callbacks
+'''
+# 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")
+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 http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+
+Test.prepare_plugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 'ssl_hook_test.cc'), ts, '-cert=2')
+
+tr = Test.AddTestRun("Test two cert hooks")
+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-2.gold"
+
+certstring0 = "Cert callback 0"
+certstring1 = "Cert callback 1"
+ts.Streams.All = Testers.ContainsExpression(
+ "\A(?:(?!{0}).)*{0}(?!.*{0}).*\Z".format(certstring0), "Cert message appears only once", 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_hooks9.test.py b/tests/gold_tests/tls_hooks/tls_hooks9.test.py
new file mode 100644
index 0000000..a975f0f
--- /dev/null
+++ b/tests/gold_tests/tls_hooks/tls_hooks9.test.py
@@ -0,0 +1,76 @@
+'''
+Test one immediate cert 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")
+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 http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+
+Test.prepare_plugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 'ssl_hook_test.cc'), ts, '-i=1')
+
+tr = Test.AddTestRun("Test one immediate cert hooks")
+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-im-1.gold"
+
+certstring0 = "Cert callback 0"
+ts.Streams.All = Testers.ContainsExpression(
+ "\A(?:(?!{0}).)*{0}(?!.*{0}).*\Z".format(certstring0), "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
new file mode 100644
index 0000000..d0075b4
--- /dev/null
+++ b/tests/tools/plugins/ssl_hook_test.cc
@@ -0,0 +1,248 @@
+/** @file
+
+ SSL Preaccept test plugin
+ Implements blind tunneling based on the client IP address
+ The client ip addresses are specified in the plugin's
+ config file as an array of IP addresses or IP address ranges under the
+ key "client-blind-tunnel"
+
+ @section license License
+
+ 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 <ts/ts.h>
+#include <ts/remap.h>
+#include <getopt.h>
+#include <openssl/ssl.h>
+#include <string.h>
+
+#define PN "ssl_hook_test"
+#define PCP "[" PN " Plugin] "
+
+int
+ReenableSSL(TSCont cont, TSEvent event, void *edata)
+{
+ TSVConn ssl_vc = reinterpret_cast<TSVConn>(TSContDataGet(cont));
+ TSVConnReenable(ssl_vc);
+ TSContDestroy(cont);
+ TSDebug(PN, "Callback reenable ssl_vc=%p", ssl_vc);
+ return TS_SUCCESS;
+}
+
+int
+CB_Pre_Accept(TSCont cont, TSEvent event, void *edata)
+{
+ TSVConn ssl_vc = reinterpret_cast<TSVConn>(edata);
+
+ int count = reinterpret_cast<intptr_t>(TSContDataGet(cont));
+
+ TSDebug(PN, "Pre accept callback %d %p - event is %s", count, ssl_vc, event == TS_EVENT_VCONN_PRE_ACCEPT ? "good" : "bad");
+
+ // All done, reactivate things
+ TSVConnReenable(ssl_vc);
+ return TS_SUCCESS;
+}
+
+int
+CB_Pre_Accept_Delay(TSCont cont, TSEvent event, void *edata)
+{
+ TSVConn ssl_vc = reinterpret_cast<TSVConn>(edata);
+
+ int count = reinterpret_cast<intptr_t>(TSContDataGet(cont));
+
+ TSDebug(PN, "Pre accept delay callback %d %p - event is %s", count, ssl_vc, event == TS_EVENT_VCONN_PRE_ACCEPT ? "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_SNI(TSCont cont, TSEvent event, void *edata)
+{
+ TSVConn ssl_vc = reinterpret_cast<TSVConn>(edata);
+
+ int count = reinterpret_cast<intptr_t>(TSContDataGet(cont));
+
+ TSDebug(PN, "SNI callback %d %p", count, ssl_vc);
+
+ // All done, reactivate things
+ TSVConnReenable(ssl_vc);
+ return TS_SUCCESS;
+}
+
+int
+CB_Cert_Immediate(TSCont cont, TSEvent event, void *edata)
+{
+ TSVConn ssl_vc = reinterpret_cast<TSVConn>(edata);
+
+ int count = reinterpret_cast<intptr_t>(TSContDataGet(cont));
+
+ TSDebug(PN, "Cert callback %d ssl_vc=%p", count, ssl_vc);
+
+ TSVConnReenable(ssl_vc);
+ return TS_SUCCESS;
+}
+
+int
+CB_Cert(TSCont cont, TSEvent event, void *edata)
+{
+ TSVConn ssl_vc = reinterpret_cast<TSVConn>(edata);
+
+ int count = reinterpret_cast<intptr_t>(TSContDataGet(cont));
+
+ TSDebug(PN, "Cert callback %d ssl_vc=%p", count, ssl_vc);
+
+ 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;
+}
+
+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 i = 0;
+ const char *ptr;
+ for (i = 0; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ switch (argv[i][1]) {
+ case 'p':
+ ptr = index(argv[i], '=');
+ if (ptr) {
+ preaccept_count = atoi(ptr + 1);
+ }
+ break;
+ case 's':
+ ptr = index(argv[i], '=');
+ if (ptr) {
+ sni_count = atoi(ptr + 1);
+ }
+ break;
+ case 'c':
+ ptr = index(argv[i], '=');
+ if (ptr) {
+ cert_count = atoi(ptr + 1);
+ }
+ break;
+ case 'd':
+ ptr = index(argv[i], '=');
+ if (ptr) {
+ preaccept_count_delay = atoi(ptr + 1);
+ }
+ break;
+ case 'i':
+ ptr = index(argv[i], '=');
+ if (ptr) {
+ cert_count_immediate = atoi(ptr + 1);
+ }
+ break;
+ }
+ }
+ }
+}
+
+void
+setup_callbacks(TSHttpTxn txn, int preaccept_count, int sni_count, int cert_count, int cert_count_immediate,
+ int preaccept_count_delay)
+{
+ TSCont cb = nullptr; // pre-accept callback continuation
+ int i;
+
+ TSDebug(PN, "Setup callbacks pa=%d sni=%d cert=%d cert_imm=%d pa_delay=%d", preaccept_count, sni_count, cert_count,
+ cert_count_immediate, preaccept_count_delay);
+ for (i = 0; i < preaccept_count; i++) {
+ cb = TSContCreate(&CB_Pre_Accept, TSMutexCreate());
+ TSContDataSet(cb, (void *)(intptr_t)i);
+ if (txn) {
+ TSHttpTxnHookAdd(txn, TS_VCONN_PRE_ACCEPT_HOOK, cb);
+ } else {
+ TSHttpHookAdd(TS_VCONN_PRE_ACCEPT_HOOK, cb);
+ }
+ }
+ for (i = 0; i < preaccept_count_delay; i++) {
+ cb = TSContCreate(&CB_Pre_Accept_Delay, TSMutexCreate());
+ TSContDataSet(cb, (void *)(intptr_t)i);
+ if (txn) {
+ TSHttpTxnHookAdd(txn, TS_VCONN_PRE_ACCEPT_HOOK, cb);
+ } else {
+ TSHttpHookAdd(TS_VCONN_PRE_ACCEPT_HOOK, cb);
+ }
+ }
+ for (i = 0; i < sni_count; i++) {
+ cb = TSContCreate(&CB_SNI, TSMutexCreate());
+ TSContDataSet(cb, (void *)(intptr_t)i);
+ if (txn) {
+ TSHttpTxnHookAdd(txn, TS_SSL_SERVERNAME_HOOK, cb);
+ } else {
+ TSHttpHookAdd(TS_SSL_SERVERNAME_HOOK, cb);
+ }
+ }
+ for (i = 0; i < cert_count; i++) {
+ cb = TSContCreate(&CB_Cert, TSMutexCreate());
+ TSContDataSet(cb, (void *)(intptr_t)i);
+ if (txn) {
+ TSHttpTxnHookAdd(txn, TS_SSL_CERT_HOOK, cb);
+ } else {
+ TSHttpHookAdd(TS_SSL_CERT_HOOK, cb);
+ }
+ }
+ for (i = 0; i < cert_count_immediate; i++) {
+ cb = TSContCreate(&CB_Cert_Immediate, TSMutexCreate());
+ TSContDataSet(cb, (void *)(intptr_t)i);
+ if (txn) {
+ TSHttpTxnHookAdd(txn, TS_SSL_CERT_HOOK, cb);
+ } else {
+ TSHttpHookAdd(TS_SSL_CERT_HOOK, cb);
+ }
+ }
+
+ return;
+}
+
+// Called by ATS as our initialization point
+void
+TSPluginInit(int argc, const char *argv[])
+{
+ TSPluginRegistrationInfo info;
+ info.plugin_name = const_cast<char *>("SSL hooks test");
+ info.vendor_name = const_cast<char *>("yahoo");
+ info.support_email = const_cast<char *>("shinrich@yahoo-inc.com");
+ if (TSPluginRegister(&info) != TS_SUCCESS) {
+ TSError("[%s] Plugin registration failed", PN);
+ }
+
+ int preaccept_count = 0;
+ int sni_count = 0;
+ 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);
+ return;
+}
diff --git a/tools/tsxs.in b/tools/tsxs.in
index aa7c0eb..704300a 100755
--- a/tools/tsxs.in
+++ b/tools/tsxs.in
@@ -29,6 +29,7 @@ CFLAGS="$CFLAGS @CFLAGS@"
CXXFLAGS="$CXXFLAGS @CXXFLAGS@"
BUILD=
DEBUGECHO=
+LDFLAGS="$LDFLAGS @LDFLAGS@"
if [ -z "$CC" ]; then
CC="@CC@"
--
To stop receiving notification emails like this one, please contact
['"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>'].