You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by am...@apache.org on 2014/09/22 21:13:12 UTC

[2/2] git commit: TS-3006: Add SSL extensions and examples.

TS-3006: Add SSL extensions and examples.


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/044da699
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/044da699
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/044da699

Branch: refs/heads/master
Commit: 044da6999442449434b282d8b537d8858505bbfc
Parents: 2f6d6e0
Author: Susan Hinrichs <sh...@network-geographics.com>
Authored: Mon Sep 22 14:12:15 2014 -0500
Committer: Alan M. Carroll <am...@apache.org>
Committed: Mon Sep 22 14:12:15 2014 -0500

----------------------------------------------------------------------
 CHANGES                                         |   2 +
 build/plugins.mk                                |   3 +-
 configure.ac                                    |   1 +
 example/Makefile.am                             |   6 +
 example/ssl-preaccept/ats-util.h                |  40 ++
 example/ssl-preaccept/ssl-preaccept.cc          | 197 +++++++
 example/ssl-preaccept/ssl_preaccept.config      |   7 +
 example/ssl-sni-whitelist/ssl-sni-whitelist.cc  | 141 +++++
 .../ssl-sni-whitelist/ssl_sni_whitelist.config  |   3 +
 example/ssl-sni/ssl-sni.cc                      | 162 ++++++
 example/ssl-sni/ssl_sni.config                  |   7 +
 iocore/net/Makefile.am                          |   4 +-
 iocore/net/OCSPStapling.cc                      |  31 +-
 iocore/net/P_SSLCertLookup.h                    |  53 +-
 iocore/net/P_SSLNetVConnection.h                |  56 ++
 iocore/net/SSLCertLookup.cc                     | 171 +++---
 iocore/net/SSLNetVConnection.cc                 | 508 +++++++++++++++--
 iocore/net/SSLUtils.cc                          | 110 +++-
 iocore/net/UnixNetVConnection.cc                |   6 +-
 lib/records/I_RecHttp.h                         |   9 +-
 lib/ts/Vec.h                                    |   7 +
 lib/ts/apidefs.h.in                             |  20 +
 lib/ts/ink_sock.cc                              |  10 +
 lib/ts/ink_sock.h                               |   1 +
 plugins/experimental/Makefile.am                |   1 +
 .../experimental/ssl_cert_loader/Makefile.am    |  26 +
 plugins/experimental/ssl_cert_loader/ats-util.h |  40 ++
 .../experimental/ssl_cert_loader/domain-tree.cc | 168 ++++++
 .../experimental/ssl_cert_loader/domain-tree.h  |  66 +++
 .../ssl_cert_loader/ssl-cert-loader.cc          | 541 +++++++++++++++++++
 .../ssl_cert_loader/ssl_cert_loader.cfg         | 135 +++++
 .../experimental/ssl_cert_loader/ssl_start.cfg  |  55 ++
 proxy/InkAPI.cc                                 | 129 ++++-
 proxy/InkAPIInternal.h                          |  12 +
 proxy/api/ts/ts.h                               |  15 +
 proxy/config/ssl_multicert.config.default       |  14 +-
 proxy/http/HttpDebugNames.cc                    |   4 +
 proxy/http/HttpSM.cc                            |   2 +-
 proxy/http/HttpSessionAccept.cc                 |   6 +-
 proxy/http/HttpSessionAccept.h                  |   1 +
 proxy/http/HttpTransact.cc                      |   8 +-
 proxy/http/HttpTunnel.cc                        |   3 +-
 42 files changed, 2620 insertions(+), 161 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index d79dadb..446f140 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,7 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache Traffic Server 5.2.0
+  *) [TS-3006] Add SSL extensions and examples.
+     Author: Susan Hinrichs <sh...@network-geographics.com>
 
   *) [TS-3054] Forward partial chunked data to client to be more transparent.
      Author: Susan Hinrichs <sh...@network-geographics.com>

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/build/plugins.mk
----------------------------------------------------------------------
diff --git a/build/plugins.mk b/build/plugins.mk
index 560d8b9..b92eb1f 100644
--- a/build/plugins.mk
+++ b/build/plugins.mk
@@ -26,7 +26,8 @@ TS_PLUGIN_CPPFLAGS = \
   -I$(top_builddir)/proxy/api \
   -I$(top_srcdir)/proxy/api \
   -I$(top_builddir)/lib/ts \
-  -I$(top_srcdir)/lib/ts
+  -I$(top_srcdir)/lib/ts \
+  -I$(top_srcdir)/lib
 
 # Provide a default AM_CPPFLAGS. Automake handles this correctly, but libtool
 # throws an error if we try to do the same with AM_LDFLAGS. Hence, we provide

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 0785790..c3c7e0e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1942,6 +1942,7 @@ AS_IF([test "x$enable_experimental_plugins" = xyes], [
     plugins/experimental/remap_stats/Makefile
     plugins/experimental/s3_auth/Makefile
     plugins/experimental/sslheaders/Makefile
+    plugins/experimental/ssl_cert_loader/Makefile
     plugins/experimental/stale_while_revalidate/Makefile
     plugins/experimental/ts_lua/Makefile
     plugins/experimental/url_sig/Makefile

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/example/Makefile.am
----------------------------------------------------------------------
diff --git a/example/Makefile.am b/example/Makefile.am
index c57fbea..47c1d90 100644
--- a/example/Makefile.am
+++ b/example/Makefile.am
@@ -40,6 +40,9 @@ plugins = \
   response-header-1.la \
   secure-link.la \
   server-transform.la \
+  ssl-preaccept.la \
+  ssl-sni.la \
+  ssl-sni-whitelist.la \
   thread-1.la \
   version.la
 
@@ -72,6 +75,9 @@ thread_1_la_SOURCES = thread-1/thread-1.c
 psi_la_SOURCES = thread-pool/psi.c thread-pool/thread.c
 version_la_SOURCES = version/version.c
 secure_link_la_SOURCES = secure-link/secure-link.c
+ssl_preaccept_la_SOURCES = ssl-preaccept/ssl-preaccept.cc 
+ssl_sni_la_SOURCES = ssl-sni/ssl-sni.cc 
+ssl_sni_whitelist_la_SOURCES = ssl-sni-whitelist/ssl-sni-whitelist.cc 
 
 # The following examples do not build:
 #

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/example/ssl-preaccept/ats-util.h
----------------------------------------------------------------------
diff --git a/example/ssl-preaccept/ats-util.h b/example/ssl-preaccept/ats-util.h
new file mode 100644
index 0000000..1164a20
--- /dev/null
+++ b/example/ssl-preaccept/ats-util.h
@@ -0,0 +1,40 @@
+# if !defined(_ats_util_h)
+# define _ats_util_h
+
+# if defined(__cplusplus)
+/** Set data to zero.
+
+    Calls @c memset on @a t with a value of zero and a length of @c
+    sizeof(t). This can be used on ordinary and array variables. While
+    this can be used on variables of intrinsic type it's inefficient.
+
+    @note Because this uses templates it cannot be used on unnamed or
+    locally scoped structures / classes. This is an inherent
+    limitation of templates.
+
+    Examples:
+    @code
+    foo bar; // value.
+    ink_zero(bar); // zero bar.
+
+    foo *bar; // pointer.
+    ink_zero(bar); // WRONG - makes the pointer @a bar zero.
+    ink_zero(*bar); // zero what bar points at.
+
+    foo bar[ZOMG]; // Array of structs.
+    ink_zero(bar); // Zero all structs in array.
+
+    foo *bar[ZOMG]; // array of pointers.
+    ink_zero(bar); // zero all pointers in the array.
+    @endcode
+    
+ */
+template < typename T > inline void
+ink_zero(
+	 T& t ///< Object to zero.
+	 ) {
+  memset(&t, 0, sizeof(t));
+}
+# endif  /* __cplusplus */
+
+# endif // ats-util.h

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/example/ssl-preaccept/ssl-preaccept.cc
----------------------------------------------------------------------
diff --git a/example/ssl-preaccept/ssl-preaccept.cc b/example/ssl-preaccept/ssl-preaccept.cc
new file mode 100644
index 0000000..9a9a7be
--- /dev/null
+++ b/example/ssl-preaccept/ssl-preaccept.cc
@@ -0,0 +1,197 @@
+/** @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"
+*/
+
+# include <stdio.h>
+# include <memory.h>
+# include <inttypes.h>
+# include <ts/ts.h>
+# include <tsconfig/TsValue.h>
+# include <alloca.h>
+# include <ts/ink_inet.h>
+
+using ts::config::Configuration;
+using ts::config::Value;
+
+# define PN "ssl-preaccept"
+# define PCP "[" PN " Plugin] "
+
+namespace {
+
+std::string ConfigPath;
+typedef std::pair<IpAddr, IpAddr> IpRange;
+typedef std::deque<IpRange> IpRangeQueue;
+IpRangeQueue ClientBlindTunnelIp;
+
+Configuration Config;	// global configuration
+
+void
+Parse_Addr_String(ts::ConstBuffer const &text, IpRange &range) {
+  IpAddr newAddr;
+  std::string textstr(text._ptr, text._size);
+  // Is there a hyphen?
+  size_t hyphen_pos = textstr.find("-");
+  if (hyphen_pos != std::string::npos) {
+    std::string addr1 = textstr.substr(0, hyphen_pos);
+    std::string addr2 = textstr.substr(hyphen_pos+1);
+    range.first.load(ts::ConstBuffer(addr1.c_str(), addr1.length()));
+    range.second.load(ts::ConstBuffer(addr2.c_str(), addr2.length()));
+  }
+  else { // Assume it is a single address
+    newAddr.load(text);
+    range.first = newAddr;
+    range.second = newAddr; 
+  }
+}
+
+/// Get a string value from a config node.
+void Load_Config_Value(Value const& parent, char const* name, IpRangeQueue &addrs) {
+  Value v = parent[name];
+  std::string zret;
+  IpRange ipRange;
+  if (v.isLiteral()) {
+    Parse_Addr_String(v.getText(), ipRange);
+    addrs.push_back(ipRange);
+  } else if (v.isContainer()) {
+    size_t i;
+    for (i = 0; i < v.childCount(); i++) {
+      std::string val_str(v[i].getText()._ptr, v[i].getText()._size);
+      Parse_Addr_String(v[i].getText(), ipRange);
+      addrs.push_back(ipRange);
+    }
+  }
+}
+
+
+int
+Load_Config_File() {
+  ts::Rv<Configuration> cv = Configuration::loadFromPath(ConfigPath.c_str());
+  if (!cv.isOK()) {
+    TSError(PCP "Failed to parse %s as TSConfig format", ConfigPath.c_str());
+    return -1;
+  }
+  Config = cv;
+  return 1;
+}
+
+int
+Load_Configuration(int argc, const char *argv[]) {
+ts::ConstBuffer text;
+  std::string s; // temp holder.
+  TSMgmtString config_path = NULL;
+
+  // get the path to the config file if one was specified
+  static char const * const CONFIG_ARG = "--config=";
+  int arg_idx;
+  for (arg_idx = 0; arg_idx < argc; arg_idx++) {
+    if (0 == memcmp(argv[arg_idx], CONFIG_ARG, strlen(CONFIG_ARG))) {
+       config_path = TSstrdup(argv[arg_idx] + strlen(CONFIG_ARG));
+       TSDebug(PN, "Found config path %s", config_path);
+    }
+  }
+  if (NULL == config_path) {
+    static char const * const DEFAULT_CONFIG_PATH = "ssl_preaccept.config";
+    config_path = TSstrdup(DEFAULT_CONFIG_PATH);
+    TSDebug(PN, "No config path set in arguments, using default: %s", DEFAULT_CONFIG_PATH);
+  }
+
+  // translate relative paths to absolute
+  if (config_path[0] != '/') {
+    ConfigPath = std::string(TSConfigDirGet()) + '/' + std::string(config_path);
+  } else {
+    ConfigPath = config_path;
+  }
+
+  // free up the path
+  TSfree(config_path);
+
+  int ret = Load_Config_File();
+  if (ret != 0) {
+    TSError(PCP "Failed to load the config file, check debug output for errata");
+  }
+
+  // Still need to use the file
+  Value root = Config.getRoot();
+  Load_Config_Value(root, "client-blind-tunnel", ClientBlindTunnelIp);
+
+  return 0;
+}
+
+int
+CB_Pre_Accept(TSCont, TSEvent event, void *edata) {
+  TSVConn ssl_vc = reinterpret_cast<TSVConn>(edata);
+  IpAddr ip(TSNetVConnLocalAddrGet(ssl_vc));
+  char buff[INET6_ADDRSTRLEN];
+  IpAddr ip_client(TSNetVConnRemoteAddrGet(ssl_vc));
+  char buff2[INET6_ADDRSTRLEN];
+
+  TSDebug("skh", "Pre accept callback %p - event is %s, target address %s, client address %s"
+          , ssl_vc
+          , event == TS_EVENT_VCONN_PRE_ACCEPT ? "good" : "bad"
+          , ip.toString(buff, sizeof(buff))
+          , ip_client.toString(buff2, sizeof(buff2))
+    );
+
+  // Not the worlds most efficient address comparison.  For short lists
+  // shouldn't be too bad.  If the client IP is in any of the ranges, 
+  // flip the tunnel to be blind tunneled instead of decrypted and proxied
+  bool proxy_tunnel = true;
+  IpRangeQueue::iterator iter;
+  for (iter = ClientBlindTunnelIp.begin(); iter != ClientBlindTunnelIp.end() && proxy_tunnel; iter++) {
+    if (ip_client >= iter->first && ip_client <= iter->second) {
+      proxy_tunnel = false;
+    }
+  }
+  if (!proxy_tunnel) {
+    TSDebug("skh", "Blind tunnel");
+    // Push everything to blind tunnel
+    TSVConnTunnel(ssl_vc);
+  }
+  else {
+    TSDebug("skh", "Proxy tunnel");
+  }
+
+  // All done, reactivate things
+  TSVConnReenable(ssl_vc);
+  return TS_SUCCESS;
+}
+
+} // Anon namespace
+
+// Called by ATS as our initialization point
+void
+TSPluginInit(int argc, const char *argv[]) {
+  bool success = false;
+  TSPluginRegistrationInfo info;
+  TSCont cb_pa = 0; // pre-accept callback continuation
+
+  info.plugin_name = const_cast<char*>("SSL Preaccept test");
+  info.vendor_name = const_cast<char*>("Network Geographics");
+  info.support_email = const_cast<char*>("shinrich@network-geographics.com");
+
+  if (TS_SUCCESS != TSPluginRegister(TS_SDK_VERSION_2_0, &info)) {
+    TSError(PCP "registration failed.");
+  } else if (TSTrafficServerVersionGetMajor() < 2) {
+    TSError(PCP "requires Traffic Server 2.0 or later.");
+  } else if (0 > Load_Configuration(argc, argv)) {
+    TSError(PCP "Failed to load config file.");
+  } else if (0 == (cb_pa = TSContCreate(&CB_Pre_Accept, TSMutexCreate()))) {
+    TSError(PCP "Failed to pre-accept callback.");
+  } else {
+    TSHttpHookAdd(TS_VCONN_PRE_ACCEPT_HOOK, cb_pa);
+    success = true;
+  }
+ 
+  if (!success) {
+    if (cb_pa) TSContDestroy(cb_pa);
+    TSError(PCP "not initialized");
+  }
+  TSDebug(PN, "Plugin %s", success ? "online" : "offline");
+
+  return;
+}
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/example/ssl-preaccept/ssl_preaccept.config
----------------------------------------------------------------------
diff --git a/example/ssl-preaccept/ssl_preaccept.config b/example/ssl-preaccept/ssl_preaccept.config
new file mode 100644
index 0000000..2ec52ec
--- /dev/null
+++ b/example/ssl-preaccept/ssl_preaccept.config
@@ -0,0 +1,7 @@
+
+// SSL traffic initiating from these addresses should
+// be placed into a blind tunnel.  ATS should not inspect
+// the tunnel
+client-blind-tunnel = "192.168.56.145"
+
+//client-blind-tunnel = "192.168.56.0/24"

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/example/ssl-sni-whitelist/ssl-sni-whitelist.cc
----------------------------------------------------------------------
diff --git a/example/ssl-sni-whitelist/ssl-sni-whitelist.cc b/example/ssl-sni-whitelist/ssl-sni-whitelist.cc
new file mode 100644
index 0000000..4cd1aaa
--- /dev/null
+++ b/example/ssl-sni-whitelist/ssl-sni-whitelist.cc
@@ -0,0 +1,141 @@
+/** @file 
+    SSL SNI white list plugin
+    If the server name and IP address are not in the ssl_multicert.config
+    go head and blind tunnel it.
+*/
+
+# include <stdio.h>
+# include <memory.h>
+# include <inttypes.h>
+# include <ts/ts.h>
+# include <tsconfig/TsValue.h>
+# include <alloca.h>
+# include <openssl/ssl.h>
+
+using ts::config::Configuration;
+using ts::config::Value;
+
+# define PN "ssl-sni-whitelist"
+# define PCP "[" PN " Plugin] "
+
+namespace {
+
+std::string ConfigPath;
+
+Configuration Config;	// global configuration
+
+int
+Load_Config_File() {
+  ts::Rv<Configuration> cv = Configuration::loadFromPath(ConfigPath.c_str());
+  if (!cv.isOK()) {
+    TSError(PCP "Failed to parse %s as TSConfig format", ConfigPath.c_str());
+    return -1;
+  }
+  Config = cv;
+  return 1;
+}
+
+int
+Load_Configuration(int argc, const char *argv[]) {
+ts::ConstBuffer text;
+  std::string s; // temp holder.
+  TSMgmtString config_path = NULL;
+
+  // get the path to the config file if one was specified
+  static char const * const CONFIG_ARG = "--config=";
+  int arg_idx;
+  for (arg_idx = 0; arg_idx < argc; arg_idx++) {
+    if (0 == memcmp(argv[arg_idx], CONFIG_ARG, strlen(CONFIG_ARG))) {
+       config_path = TSstrdup(argv[arg_idx] + strlen(CONFIG_ARG));
+       TSDebug(PN, "Found config path %s", config_path);
+    }
+  }
+  if (NULL == config_path) {
+    static char const * const DEFAULT_CONFIG_PATH = "ssl_sni_whitelist.config";
+    config_path = TSstrdup(DEFAULT_CONFIG_PATH);
+    TSDebug(PN, "No config path set in arguments, using default: %s", DEFAULT_CONFIG_PATH);
+  }
+
+  // translate relative paths to absolute
+  if (config_path[0] != '/') {
+    ConfigPath = std::string(TSConfigDirGet()) + '/' + std::string(config_path);
+  } else {
+    ConfigPath = config_path;
+  }
+
+  // free up the path
+  TSfree(config_path);
+
+  int ret = Load_Config_File();
+  if (ret != 0) {
+    TSError(PCP "Failed to load the config file, check debug output for errata");
+  }
+
+  return 0;
+}
+
+int
+CB_servername_whitelist(TSCont contp, TSEvent event, void *edata) {
+  TSVConn ssl_vc = reinterpret_cast<TSVConn>(edata);
+  TSSslConnection sslobj = TSVConnSSLConnectionGet(ssl_vc);
+  SSL *ssl = reinterpret_cast<SSL *>(sslobj);
+  const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+
+  bool do_blind_tunnel = true;
+  if (servername != NULL) {
+    TSSslContext ctxobj = TSSslContextFindByName(servername);
+    if (ctxobj != NULL) {
+      do_blind_tunnel = false;
+    }
+    else {
+      // Look up by destination address
+      ctxobj = TSSslContextFindByAddr(TSNetVConnRemoteAddrGet(ssl_vc));
+      if (ctxobj != NULL) {
+        do_blind_tunnel = false;
+      }
+    }
+  }
+  if (do_blind_tunnel) {
+    TSDebug("skh", "SNI callback: do blind tunnel for %s", servername);
+    TSVConnTunnel(ssl_vc);
+    return TS_SUCCESS; // Don't re-enable so we interrupt processing
+  }  
+  TSVConnReenable(ssl_vc);
+  return TS_SUCCESS;
+}        
+
+} // Anon namespace
+
+// Called by ATS as our initialization point
+void
+TSPluginInit(int argc, const char *argv[]) {
+  bool success = false;
+  TSPluginRegistrationInfo info;
+  TSCont cb_sni = 0; // sni callback continuation
+
+  info.plugin_name = const_cast<char*>("SSL SNI whitelist");
+  info.vendor_name = const_cast<char*>("Network Geographics");
+  info.support_email = const_cast<char*>("shinrich@network-geographics.com");
+
+  if (TS_SUCCESS != TSPluginRegister(TS_SDK_VERSION_2_0, &info)) {
+    TSError(PCP "registration failed.");
+  } else if (TSTrafficServerVersionGetMajor() < 2) {
+    TSError(PCP "requires Traffic Server 2.0 or later.");
+  } else if (0 > Load_Configuration(argc, argv)) {
+    TSError(PCP "Failed to load config file.");
+  } else if (0 == (cb_sni = TSContCreate(&CB_servername_whitelist, TSMutexCreate()))) {
+    TSError(PCP "Failed to create SNI callback.");
+  } else {
+    TSHttpHookAdd(TS_SSL_SNI_HOOK, cb_sni);
+    success = true;
+  }
+ 
+  if (!success) {
+    if (cb_sni) TSContDestroy(cb_sni);
+    TSError(PCP "not initialized");
+  }
+  TSDebug(PN, "Plugin %s", success ? "online" : "offline");
+
+  return;
+}
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/example/ssl-sni-whitelist/ssl_sni_whitelist.config
----------------------------------------------------------------------
diff --git a/example/ssl-sni-whitelist/ssl_sni_whitelist.config b/example/ssl-sni-whitelist/ssl_sni_whitelist.config
new file mode 100644
index 0000000..706e6a8
--- /dev/null
+++ b/example/ssl-sni-whitelist/ssl_sni_whitelist.config
@@ -0,0 +1,3 @@
+//
+// Place holder
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/example/ssl-sni/ssl-sni.cc
----------------------------------------------------------------------
diff --git a/example/ssl-sni/ssl-sni.cc b/example/ssl-sni/ssl-sni.cc
new file mode 100644
index 0000000..d1aa856
--- /dev/null
+++ b/example/ssl-sni/ssl-sni.cc
@@ -0,0 +1,162 @@
+/** @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"
+*/
+
+# include <stdio.h>
+# include <memory.h>
+# include <inttypes.h>
+# include <ts/ts.h>
+# include <tsconfig/TsValue.h>
+# include <alloca.h>
+# include <openssl/ssl.h>
+
+using ts::config::Configuration;
+using ts::config::Value;
+
+# define PN "ssl-sni-test"
+# define PCP "[" PN " Plugin] "
+
+namespace {
+
+std::string ConfigPath;
+
+Configuration Config;	// global configuration
+
+int
+Load_Config_File() {
+  ts::Rv<Configuration> cv = Configuration::loadFromPath(ConfigPath.c_str());
+  if (!cv.isOK()) {
+    TSError(PCP "Failed to parse %s as TSConfig format", ConfigPath.c_str());
+    return -1;
+  }
+  Config = cv;
+  return 1;
+}
+
+int
+Load_Configuration(int argc, const char *argv[]) {
+ts::ConstBuffer text;
+  std::string s; // temp holder.
+  TSMgmtString config_path = NULL;
+
+  // get the path to the config file if one was specified
+  static char const * const CONFIG_ARG = "--config=";
+  int arg_idx;
+  for (arg_idx = 0; arg_idx < argc; arg_idx++) {
+    if (0 == memcmp(argv[arg_idx], CONFIG_ARG, strlen(CONFIG_ARG))) {
+       config_path = TSstrdup(argv[arg_idx] + strlen(CONFIG_ARG));
+       TSDebug(PN, "Found config path %s", config_path);
+    }
+  }
+  if (NULL == config_path) {
+    static char const * const DEFAULT_CONFIG_PATH = "ssl_sni.config";
+    config_path = TSstrdup(DEFAULT_CONFIG_PATH);
+    TSDebug(PN, "No config path set in arguments, using default: %s", DEFAULT_CONFIG_PATH);
+  }
+
+  // translate relative paths to absolute
+  if (config_path[0] != '/') {
+    ConfigPath = std::string(TSConfigDirGet()) + '/' + std::string(config_path);
+  } else {
+    ConfigPath = config_path;
+  }
+
+  // free up the path
+  TSfree(config_path);
+
+  int ret = Load_Config_File();
+  if (ret != 0) {
+    TSError(PCP "Failed to load the config file, check debug output for errata");
+  }
+
+  return 0;
+}
+
+/**
+   Somewhat nonscensically exercise some scenarios of proxying
+   and blind tunneling from the SNI callback plugin
+
+   Case 1: If the servername ends in facebook.com, blind tunnel
+   Case 2: If the servername is www.yahoo.com and there is a context
+   entry for "safelyfiled.com", use the "safelyfiled.com" context for
+   this connection.
+ */
+int
+CB_servername(TSCont /* contp */, TSEvent /* event */, void *edata) {
+  TSVConn ssl_vc = reinterpret_cast<TSVConn>(edata);
+  TSSslConnection sslobj = TSVConnSSLConnectionGet(ssl_vc);
+  SSL *ssl = reinterpret_cast<SSL *>(sslobj);
+  const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+  if (servername != NULL) {
+    int servername_len = strlen(servername);
+    int facebook_name_len = strlen("facebook.com");
+    if (servername_len >= facebook_name_len) {
+      const char *server_ptr = servername + (servername_len - facebook_name_len);
+      if (strcmp(server_ptr, "facebook.com") == 0) {
+        TSDebug("skh", "Blind tunnel from SNI callback");
+        TSVConnTunnel(ssl_vc);
+        // Don't reenable to ensure that we break out of the
+        // SSL handshake processing
+        return TS_SUCCESS; // Don't re-enable so we interrupt processing
+      }
+    }
+    // If the name is yahoo, look for a context for safelyfiled and use that here
+    if (strcmp("www.yahoo.com", servername) == 0) {
+      TSDebug("skh", "SNI name is yahoo ssl obj is %p", sslobj);
+      if (sslobj) {
+        TSSslContext ctxobj = TSSslContextFindByName("safelyfiled.com");
+        if (ctxobj != NULL) {
+          TSDebug("skh", "Found cert for safelyfiled");
+          SSL_CTX *ctx = reinterpret_cast<SSL_CTX *>(ctxobj);
+          SSL_set_SSL_CTX(ssl, ctx); 
+          TSDebug("skh", "SNI plugin cb: replace SSL CTX");
+        }
+      }
+    }
+  } 
+
+
+  // All done, reactivate things
+  TSVConnReenable(ssl_vc);
+  return TS_SUCCESS;
+}
+
+} // Anon namespace
+
+// Called by ATS as our initialization point
+void
+TSPluginInit(int argc, const char *argv[]) {
+  bool success = false;
+  TSPluginRegistrationInfo info;
+  TSCont cb_sni = 0; // sni callback continuation
+
+  info.plugin_name = const_cast<char*>("SSL SNI callback test");
+  info.vendor_name = const_cast<char*>("Network Geographics");
+  info.support_email = const_cast<char*>("shinrich@network-geographics.com");
+
+  if (TS_SUCCESS != TSPluginRegister(TS_SDK_VERSION_2_0, &info)) {
+    TSError(PCP "registration failed.");
+  } else if (TSTrafficServerVersionGetMajor() < 2) {
+    TSError(PCP "requires Traffic Server 2.0 or later.");
+  } else if (0 > Load_Configuration(argc, argv)) {
+    TSError(PCP "Failed to load config file.");
+  } else if (0 == (cb_sni = TSContCreate(&CB_servername, TSMutexCreate()))) {
+    TSError(PCP "Failed to create SNI callback.");
+  } else {
+    TSHttpHookAdd(TS_SSL_SNI_HOOK, cb_sni);
+    success = true;
+  }
+ 
+  if (!success) {
+    if (cb_sni) TSContDestroy(cb_sni);
+    TSError(PCP "not initialized");
+  }
+  TSDebug(PN, "Plugin %s", success ? "online" : "offline");
+
+  return;
+}
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/example/ssl-sni/ssl_sni.config
----------------------------------------------------------------------
diff --git a/example/ssl-sni/ssl_sni.config b/example/ssl-sni/ssl_sni.config
new file mode 100644
index 0000000..2ec52ec
--- /dev/null
+++ b/example/ssl-sni/ssl_sni.config
@@ -0,0 +1,7 @@
+
+// SSL traffic initiating from these addresses should
+// be placed into a blind tunnel.  ATS should not inspect
+// the tunnel
+client-blind-tunnel = "192.168.56.145"
+
+//client-blind-tunnel = "192.168.56.0/24"

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/iocore/net/Makefile.am
----------------------------------------------------------------------
diff --git a/iocore/net/Makefile.am b/iocore/net/Makefile.am
index 907be2e..0120528 100644
--- a/iocore/net/Makefile.am
+++ b/iocore/net/Makefile.am
@@ -26,7 +26,9 @@ AM_CPPFLAGS = \
   -I$(top_srcdir)/proxy/hdrs \
   -I$(top_srcdir)/proxy/shared \
   -I$(top_srcdir)/mgmt \
-  -I$(top_srcdir)/mgmt/utils
+  -I$(top_srcdir)/mgmt/utils \
+  -I$(top_srcdir)/proxy/api/ts \
+  -I$(top_srcdir)/proxy/http
 
 TESTS = $(check_PROGRAMS)
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/iocore/net/OCSPStapling.cc
----------------------------------------------------------------------
diff --git a/iocore/net/OCSPStapling.cc b/iocore/net/OCSPStapling.cc
index 136c623..51212a7 100644
--- a/iocore/net/OCSPStapling.cc
+++ b/iocore/net/OCSPStapling.cc
@@ -363,20 +363,23 @@ ocsp_update()
   const unsigned ctxCount = certLookup->count();
 
   for (unsigned i = 0; i < ctxCount; i++) {
-    ctx = certLookup->get(i);
-    cinf = stapling_get_cert_info(ctx);
-    if (cinf) {
-      ink_mutex_acquire(&cinf->stapling_mutex);
-      current_time = time(NULL);
-      if (cinf->resp_derlen == 0 || cinf->is_expire || cinf->expire_time < current_time) {
-        ink_mutex_release(&cinf->stapling_mutex);
-        if (stapling_refresh_response(cinf, &resp)) {
-          Note("Success to refresh OCSP response for 1 certificate.");
-        } else {
-          Note("Fail to refresh OCSP response for 1 certificate.");
-        }
-      } else {
-        ink_mutex_release(&cinf->stapling_mutex);
+    SSLCertContext *cc = certLookup->get(i);
+    if (cc && cc->ctx) {
+      ctx = cc->ctx; 
+      cinf = stapling_get_cert_info(ctx);
+       if (cinf) {
+         ink_mutex_acquire(&cinf->stapling_mutex);
+         current_time = time(NULL);
+         if (cinf->resp_derlen == 0 || cinf->is_expire || cinf->expire_time < current_time) {
+           ink_mutex_release(&cinf->stapling_mutex);
+           if (stapling_refresh_response(cinf, &resp)) {
+             Note("Success to refresh OCSP response for 1 certificate.");
+           } else {
+             Note("Fail to refresh OCSP response for 1 certificate.");
+           }
+         } else {
+           ink_mutex_release(&cinf->stapling_mutex);
+         }
       }
     }
   }

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/iocore/net/P_SSLCertLookup.h
----------------------------------------------------------------------
diff --git a/iocore/net/P_SSLCertLookup.h b/iocore/net/P_SSLCertLookup.h
index f2328ef..05d908c 100644
--- a/iocore/net/P_SSLCertLookup.h
+++ b/iocore/net/P_SSLCertLookup.h
@@ -30,21 +30,64 @@
 struct SSLConfigParams;
 struct SSLContextStorage;
 
+/** A certificate context.
+
+    This holds data about a certificate and how it is used by the SSL logic. Current this is mainly
+    the openSSL certificate and an optional action, which in turn is limited to just tunneling.
+
+    Instances are passed around and returned when matching connections to certificates.
+
+    Instances of this class are stored on a list and then referenced via index in that list so that
+    there is exactly one place we can find all the @c SSL_CTX instances exactly once.
+
+*/
+struct SSLCertContext
+{
+  /** Special things to do instead of use a context.
+      In general an option will be associated with a @c NULL context because
+      the context is not used.
+  */
+  enum Option {
+    OPT_NONE, ///< Nothing special. Implies valid context.
+    OPT_TUNNEL ///< Just tunnel, don't terminate.
+  };
+
+  SSLCertContext() : ctx(0), opt(OPT_NONE) {}
+  explicit SSLCertContext(SSL_CTX* c) : ctx(c), opt(OPT_NONE) {}
+  SSLCertContext(SSL_CTX* c, Option o) : ctx(c), opt(o) {}
+  
+  SSL_CTX* ctx; ///< openSSL context.
+  Option opt; ///< Special handling option.
+};
+
 struct SSLCertLookup : public ConfigInfo
 {
   SSLContextStorage * ssl_storage;
   SSL_CTX *           ssl_default;
 
-  bool insert(SSL_CTX * ctx, const char * name);
-  bool insert(SSL_CTX * ctx, const IpEndpoint& address);
-  SSL_CTX * findInfoInHash(const char * address) const;
-  SSL_CTX * findInfoInHash(const IpEndpoint& address) const;
+  int insert(const char *name, SSLCertContext const &cc);
+  int insert(const IpEndpoint& address, SSLCertContext const &cc);
+
+  /** Find certificate context by IP address.
+      The IP addresses are taken from the socket @a s.
+      Exact matches have priority, then wildcards. The destination address is preferred to the source address.
+      @return @c A pointer to the matched context, @c NULL if no match is found.
+  */
+  SSLCertContext* find(const IpEndpoint& address) const;
+
+  /** Find certificate context by name (FQDN).
+      Exact matches have priority, then wildcards. Only destination based matches are checked.
+      @return @c A pointer to the matched context, @c NULL if no match is found.
+  */
+  SSLCertContext* find(char const* name) const;
+
+
 
   // Return the last-resort default TLS context if there is no name or address match.
   SSL_CTX * defaultContext() const { return ssl_default; }
 
   unsigned count() const;
-  SSL_CTX * get(unsigned i) const;
+  SSLCertContext * get(unsigned i) const;
 
   SSLCertLookup();
   virtual ~SSLCertLookup();

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/iocore/net/P_SSLNetVConnection.h
----------------------------------------------------------------------
diff --git a/iocore/net/P_SSLNetVConnection.h b/iocore/net/P_SSLNetVConnection.h
index c464e60..863b5da 100644
--- a/iocore/net/P_SSLNetVConnection.h
+++ b/iocore/net/P_SSLNetVConnection.h
@@ -36,6 +36,7 @@
 #include "P_EventSystem.h"
 #include "P_UnixNetVConnection.h"
 #include "P_UnixNet.h"
+#include "apidefs.h"
 
 #include <openssl/ssl.h>
 #include <openssl/err.h>
@@ -52,6 +53,7 @@
 #endif
 
 class SSLNextProtocolSet;
+struct SSLCertLookup;
 
 //////////////////////////////////////////////////////////////////
 //
@@ -62,6 +64,7 @@ class SSLNextProtocolSet;
 //////////////////////////////////////////////////////////////////
 class SSLNetVConnection:public UnixNetVConnection
 {
+  typedef UnixNetVConnection super; ///< Parent type.
 public:
   virtual int sslStartHandShake(int event, int &err);
   virtual void free(EThread * t);
@@ -120,6 +123,36 @@ public:
     sslClientRenegotiationAbort = state;
   };
 
+  /// Reenable the VC after a pre-accept or SNI hook is called.
+  virtual void reenable(NetHandler* nh);
+  /// Set the SSL context.
+  /// @note This must be called after the SSL endpoint has been created.
+  virtual bool sslContextSet(void* ctx);
+
+  /// Set by asynchronous hooks to request a specific operation.
+  TSSslVConnOp hookOpRequested;
+
+  // Store the servername returned by SNI
+  char sniServername[TS_MAX_HOST_NAME_LEN];
+
+  int64_t read_raw_data();
+  void initialize_handshake_buffers() {
+    this->handShakeBuffer = new_MIOBuffer();
+    this->handShakeReader = this->handShakeBuffer->alloc_reader();
+    this->handShakeHolder = this->handShakeReader->clone();
+  }
+  void free_handshake_buffers() {
+    
+    this->handShakeReader->dealloc();
+    this->handShakeHolder->dealloc();
+    free_MIOBuffer(this->handShakeBuffer);
+    this->handShakeReader = NULL;
+    this->handShakeHolder = NULL;
+    this->handShakeBuffer = NULL;
+  }
+  // Returns true if all the hooks reenabled
+  bool callHooks(TSHttpHookID eventId);
+
 private:
   SSLNetVConnection(const SSLNetVConnection &);
   SSLNetVConnection & operator =(const SSLNetVConnection &);
@@ -127,6 +160,29 @@ private:
   bool sslHandShakeComplete;
   bool sslClientConnection;
   bool sslClientRenegotiationAbort;
+  MIOBuffer *handShakeBuffer;
+  IOBufferReader *handShakeHolder;
+  IOBufferReader *handShakeReader;
+
+  /// The current hook.
+  /// @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 {
+    SNI_HOOKS_INIT,
+    SNI_HOOKS_ACTIVE,
+    SNI_HOOKS_DONE,
+    SNI_HOOKS_CONTINUE
+  } sslSNIHookState;
+
   const SSLNextProtocolSet * npnSet;
   Continuation * npnEndpoint;
 };

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/iocore/net/SSLCertLookup.cc
----------------------------------------------------------------------
diff --git a/iocore/net/SSLCertLookup.cc b/iocore/net/SSLCertLookup.cc
index 253f9a2..19c22ca 100644
--- a/iocore/net/SSLCertLookup.cc
+++ b/iocore/net/SSLCertLookup.cc
@@ -70,28 +70,51 @@ private:
 
 struct SSLContextStorage
 {
+public:
   SSLContextStorage();
   ~SSLContextStorage();
 
-  bool insert(SSL_CTX * ctx, const char * name);
-  SSL_CTX * lookup(const char * name) const;
-  unsigned count() const { return this->references.count(); }
-  SSL_CTX * get(unsigned i) const { return this->references[i]; }
+  /// Add a cert context to storage
+  /// @return The @a host_store index or -1 on error.
+  int insert(const char * name, SSLCertContext const& cc);
+  /// Add a cert context to storage.
+  /// @a idx must be a value returned by a previous call to insert.
+  /// This creates an alias, a different @a name referring to the same
+  /// cert context.
+  /// @return @a idx
+  int insert(const char * name, int idx);
+  SSLCertContext* lookup(const char * name) const;
+  unsigned count() const { return this->ctx_store.length(); }
+  SSLCertContext* get(unsigned i) const { return &this->ctx_store[i]; }
 
 private:
-  struct SSLEntry
+  /** A struct that can stored a @c Trie.
+      It contains the index of the real certificate and the
+      linkage required by @c Trie.
+  */
+  struct ContextRef 
   {
-    explicit SSLEntry(SSL_CTX * c) : ctx(c) {}
-
-    void Print() const { Debug("ssl", "SSLEntry=%p SSL_CTX=%p", this, ctx); }
-
-    SSL_CTX * ctx;
-    LINK(SSLEntry, link);
+    ContextRef(): idx(-1) {}
+    explicit ContextRef(int n) : idx(n) {}
+    void Print() const { Debug("ssl", "Item=%p SSL_CTX=#%d", this, idx); }
+    int idx; ///< Index in the context store.
+    LINK(ContextRef, link); ///< Require by @c Trie
   };
 
-  Trie<SSLEntry>  wildcards;
+  /// Items tored by wildcard name
+  Trie<ContextRef>  wildcards;
+  /// Contexts store by IP address or FQDN
   InkHashTable *  hostnames;
-  Vec<SSL_CTX *>  references;
+  /// List for cleanup.
+  /// Exactly one pointer to each SSL context is stored here.
+  Vec<SSLCertContext>  ctx_store;
+
+  /// Add a context to the clean up list.
+  /// @return The index of the added context.
+  int store(SSLCertContext const& cc);
+  /// Remove last added context
+  void unstore();
+
 };
 
 SSLCertLookup::SSLCertLookup()
@@ -104,21 +127,21 @@ SSLCertLookup::~SSLCertLookup()
   delete this->ssl_storage;
 }
 
-SSL_CTX *
-SSLCertLookup::findInfoInHash(const char * address) const
+SSLCertContext *
+SSLCertLookup::find(const char * address) const
 {
   return this->ssl_storage->lookup(address);
 }
 
-SSL_CTX *
-SSLCertLookup::findInfoInHash(const IpEndpoint& address) const
+SSLCertContext *
+SSLCertLookup::find(const IpEndpoint& address) const
 {
-  SSL_CTX * ctx;
+  SSLCertContext * cc;
   SSLAddressLookupKey key(address);
 
   // First try the full address.
-  if ((ctx = this->ssl_storage->lookup(key.get()))) {
-    return ctx;
+  if ((cc = this->ssl_storage->lookup(key.get()))) {
+    return cc;
   }
 
   // If that failed, try the address without the port.
@@ -130,17 +153,17 @@ SSLCertLookup::findInfoInHash(const IpEndpoint& address) const
   return NULL;
 }
 
-bool
-SSLCertLookup::insert(SSL_CTX * ctx, const char * name)
+int
+SSLCertLookup::insert(const char *name, SSLCertContext const &cc)
 {
-  return this->ssl_storage->insert(ctx, name);
+  return this->ssl_storage->insert(name, cc);
 }
 
-bool
-SSLCertLookup::insert(SSL_CTX * ctx, const IpEndpoint& address)
+int
+SSLCertLookup::insert(const IpEndpoint& address, SSLCertContext const &cc)
 {
   SSLAddressLookupKey key(address);
-  return this->ssl_storage->insert(ctx, key.get());
+  return this->ssl_storage->insert(key.get(), cc);
 }
 
 unsigned
@@ -149,7 +172,7 @@ SSLCertLookup::count() const
   return ssl_storage->count();
 }
 
-SSL_CTX *
+SSLCertContext *
 SSLCertLookup::get(unsigned i) const
 {
   return ssl_storage->get(i);
@@ -212,15 +235,38 @@ SSLContextStorage::SSLContextStorage()
 
 SSLContextStorage::~SSLContextStorage()
 {
-  for (unsigned i = 0; i < this->references.count(); ++i) {
-    SSLReleaseContext(this->references[i]);
+  for (unsigned i = 0; i < this->ctx_store.length(); ++i) {
+    SSLReleaseContext(this->ctx_store[i].ctx);
   }
 
   ink_hash_table_destroy(this->hostnames);
 }
 
-bool
-SSLContextStorage::insert(SSL_CTX * ctx, const char * name)
+int
+SSLContextStorage::store(SSLCertContext const& cc)
+{
+  int idx = this->ctx_store.length();
+  this->ctx_store.add(cc);
+  return idx;
+}
+
+void
+SSLContextStorage::unstore()
+{
+  this->ctx_store.drop();
+}
+
+int
+SSLContextStorage::insert(const char* name, SSLCertContext const& cc)
+{
+  int idx = this->store(cc);
+  idx = this->insert(name, idx);
+  if (idx < 0) this->unstore();
+  return idx;
+}
+
+int 
+SSLContextStorage::insert(const char* name, int idx) 
 {
   ats_wildcard_matcher wildcard;
   bool inserted = false;
@@ -230,70 +276,59 @@ SSLContextStorage::insert(SSL_CTX * ctx, const char * name)
     // so that we can do a longest match lookup.
     char namebuf[TS_MAX_HOST_NAME_LEN + 1];
     char * reversed;
-    ats_scoped_obj<SSLEntry> entry;
+    ats_scoped_obj<ContextRef> ref;
 
     reversed = reverse_dns_name(name + 1, namebuf);
     if (!reversed) {
       Error("wildcard name '%s' is too long", name);
-      return false;
+      return -1;
     }
 
-    entry = new SSLEntry(ctx);
-    inserted = this->wildcards.Insert(reversed, entry, 0 /* rank */, -1 /* keylen */);
+    ref = new ContextRef(idx);
+    inserted = this->wildcards.Insert(reversed, ref, 0 /* rank */, -1 /* keylen */);
     if (!inserted) {
-      SSLEntry * found;
+      ContextRef * found;
 
       // We fail to insert, so the longest wildcard match search should return the full match value.
       found = this->wildcards.Search(reversed);
-      if (found != NULL && found->ctx != ctx) {
-        Warning("previously indexed wildcard certificate for '%s' as '%s', cannot index it with SSL_CTX %p now",
-            name, reversed, ctx);
+      if (found != NULL && found->idx != idx) {
+        Warning("previously indexed wildcard certificate for '%s' as '%s', cannot index it with SSL_CTX #%d now",
+            name, reversed, idx);
       }
-
-      goto done;
+      idx = -1;
     }
 
-    Debug("ssl", "indexed wildcard certificate for '%s' as '%s' with SSL_CTX %p", name, reversed, ctx);
-    entry.release();
+    Debug("ssl", "%s wildcard certificate for '%s' as '%s' with SSL_CTX %p [%d]", 
+      idx >= 0 ? "index" : "failed to index", name, reversed, this->ctx_store[(*ref).idx].ctx, (*ref).idx);
+    ref.release();
   } else {
     InkHashTableValue value;
 
-    if (ink_hash_table_lookup(this->hostnames, name, &value) && (void *)ctx != value) {
-      Warning("previously indexed '%s' with SSL_CTX %p, cannot index it with SSL_CTX %p now", name, value, ctx);
-      goto done;
+    if (ink_hash_table_lookup(this->hostnames, name, &value) && (void *)idx != value) {
+      Warning("previously indexed '%s' with SSL_CTX %p, cannot index it with SSL_CTX #%d now", name, value, idx);
+    } else {
+      inserted = true;
+      ink_hash_table_insert(this->hostnames, name, reinterpret_cast<void*>(static_cast<intptr_t>(idx)));
+      Debug("ssl", "indexed '%s' with SSL_CTX %p [%d]", 
+        name, this->ctx_store[idx].ctx, idx);
     }
-
-    inserted = true;
-    ink_hash_table_insert(this->hostnames, name, (void *)ctx);
-    Debug("ssl", "indexed '%s' with SSL_CTX %p", name, ctx);
   }
-
-done:
-  // Keep a unique reference to the SSL_CTX, so that we can free it later. Since we index by name, multiple
-  // certificates can be indexed for the same name. If this happens, we will overwrite the previous pointer
-  // and leak a context. So if we insert a certificate, keep an ownership reference to it.
-  if (inserted) {
-    if (this->references.in(ctx) == NULL) {
-      this->references.push_back(ctx);
-    }
-  }
-
-  return inserted;
+  return idx;
 }
 
-SSL_CTX *
+SSLCertContext *
 SSLContextStorage::lookup(const char * name) const
 {
   InkHashTableValue value;
 
   if (ink_hash_table_lookup(const_cast<InkHashTable *>(this->hostnames), name, &value)) {
-    return (SSL_CTX *)value;
+    return &(this->ctx_store[reinterpret_cast<intptr_t>(value)]);
   }
 
   if (!this->wildcards.Empty()) {
     char namebuf[TS_MAX_HOST_NAME_LEN + 1];
     char * reversed;
-    SSLEntry * entry;
+    ContextRef * ref;
 
     reversed = reverse_dns_name(name, namebuf);
     if (!reversed) {
@@ -302,9 +337,9 @@ SSLContextStorage::lookup(const char * name) const
     }
 
     Debug("ssl", "attempting wildcard match for %s", reversed);
-    entry = this->wildcards.Search(reversed);
-    if (entry) {
-      return entry->ctx;
+    ref = this->wildcards.Search(reversed);
+    if (ref) {
+      return &(this->ctx_store[ref->idx]);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/iocore/net/SSLNetVConnection.cc
----------------------------------------------------------------------
diff --git a/iocore/net/SSLNetVConnection.cc b/iocore/net/SSLNetVConnection.cc
index 7978013..b4269e7 100644
--- a/iocore/net/SSLNetVConnection.cc
+++ b/iocore/net/SSLNetVConnection.cc
@@ -21,9 +21,11 @@
   limitations under the License.
  */
 #include "ink_config.h"
+#include "records/I_RecHttp.h"
 #include "P_Net.h"
 #include "P_SSLNextProtocolSet.h"
 #include "P_SSLUtils.h"
+#include "InkAPIInternal.h"	// Added to include the ssl_hook definitions
 
 #define SSL_READ_ERROR_NONE	  0
 #define SSL_READ_ERROR		  1
@@ -36,9 +38,87 @@
 #define SSL_HANDSHAKE_WANT_ACCEPT 8
 #define SSL_HANDSHAKE_WANT_CONNECT 9
 #define SSL_WRITE_WOULD_BLOCK     10
+#define SSL_WAIT_FOR_HOOK         11
+
+#ifndef UIO_MAXIOV
+#define NET_MAX_IOV 16          // UIO_MAXIOV shall be at least 16 1003.1g (5.4.1.1)
+#else
+#define NET_MAX_IOV UIO_MAXIOV
+#endif
 
 ClassAllocator<SSLNetVConnection> sslNetVCAllocator("sslNetVCAllocator");
 
+namespace {
+  /// Callback to get two locks.
+  /// The lock for this continuation, and for the target continuation.
+  class ContWrapper : public Continuation
+  {
+  public:
+    /** Constructor.
+        This takes the secondary @a mutex and the @a target continuation
+        to invoke, along with the arguments for that invocation.
+    */
+    ContWrapper(
+      ProxyMutex* mutex ///< Mutex for this continuation (primary lock).
+      , Continuation* target ///< "Real" continuation we want to call.
+      , int eventId = EVENT_IMMEDIATE ///< Event ID for invocation of @a target.
+      , void* edata = 0 ///< Data for invocation of @a target.
+      )
+      : Continuation(mutex)
+      , _target(target)
+      , _eventId(eventId)
+      , _edata(edata)
+    {
+      SET_HANDLER(&ContWrapper::event_handler);
+    }
+
+    /// Required event handler method.
+    int event_handler(int, void*)
+    {
+      EThread* eth = this_ethread();
+
+      MUTEX_TRY_LOCK(lock, _target->mutex, eth);
+      if (lock) { // got the target lock, we can proceed.
+        _target->handleEvent(_eventId, _edata);
+        delete this;
+      } else { // can't get both locks, try again.
+        eventProcessor.schedule_imm(this, ET_NET);
+      }
+      return 0;
+    }
+
+    /** Convenience static method.
+
+        This lets a client make one call and not have to (accurately)
+        copy the invocation logic embedded here. We duplicate it near
+        by textually so it is easier to keep in sync.
+
+        This takes the same arguments as the constructor but, if the
+        lock can be obtained immediately, does not construct an
+        instance but simply calls the @a target.
+    */
+    static void wrap(
+      ProxyMutex* mutex ///< Mutex for this continuation (primary lock).
+      , Continuation* target ///< "Real" continuation we want to call.
+      , int eventId = EVENT_IMMEDIATE ///< Event ID for invocation of @a target.
+      , void* edata = 0 ///< Data for invocation of @a target.
+      ) {
+      EThread* eth = this_ethread();
+      MUTEX_TRY_LOCK(lock, target->mutex, eth);
+      if (lock) {
+        target->handleEvent(eventId, edata);
+      } else {
+        eventProcessor.schedule_imm(new ContWrapper(mutex, target, eventId, edata), ET_NET);
+      }
+    }
+
+  private:
+    Continuation* _target; ///< Continuation to invoke.
+    int _eventId; ///< with this event
+    void* _edata; ///< and this data
+  };
+}
+
 //
 // Private
 //
@@ -49,7 +129,19 @@ make_ssl_connection(SSL_CTX * ctx, SSLNetVConnection * netvc)
   SSL * ssl;
 
   if (likely(ssl = SSL_new(ctx))) {
-    SSL_set_fd(ssl, netvc->get_socket());
+    netvc->ssl = ssl;
+
+    // Only set up the bio stuff for the server side
+    if (netvc->getSSLClientConnection()) {
+      SSL_set_fd(ssl, netvc->get_socket());
+    } else {
+      netvc->initialize_handshake_buffers();
+      BIO *rbio = BIO_new(BIO_s_mem());
+      BIO *wbio = BIO_new_fd(netvc->get_socket(), BIO_NOCLOSE);
+      BIO_set_mem_eof_return(wbio, -1); 
+      SSL_set_bio(ssl, rbio, wbio);
+    }
+
     SSL_set_app_data(ssl, netvc);
   }
 
@@ -208,6 +300,99 @@ ssl_read_from_net(SSLNetVConnection * sslvc, EThread * lthread, int64_t &ret)
 
 }
 
+/**
+ * Read from socket directly for handshake data.  Store the data in 
+ * a MIOBuffer.  Place the data in the read BIO so the openssl library
+ * has access to it.  
+ * If for some ready we much abort out of the handshake, we can replay
+ * the stored data (e.g. back out to blind tunneling)
+ */
+int64_t
+SSLNetVConnection::read_raw_data()
+{
+  int64_t r = 0;
+  int64_t toread = INT_MAX;
+
+  // read data
+  int64_t rattempted = 0, total_read = 0;
+  int niov = 0;
+  IOVec tiovec[NET_MAX_IOV];
+  if (toread) {
+    IOBufferBlock *b = this->handShakeBuffer->first_write_block();
+    do {
+      niov = 0;
+      rattempted = 0;
+      while (b && niov < NET_MAX_IOV) {
+        int64_t a = b->write_avail();
+        if (a > 0) {
+          tiovec[niov].iov_base = b->_end;
+          int64_t togo = toread - total_read - rattempted;
+          if (a > togo)
+            a = togo;
+          tiovec[niov].iov_len = a;
+          rattempted += a;
+          niov++;
+          if (a >= togo)
+            break;
+        }
+        b = b->next;
+      }
+
+      if (niov == 1) {
+        r = socketManager.read(this->con.fd, tiovec[0].iov_base, tiovec[0].iov_len);
+      } else {
+        r = socketManager.readv(this->con.fd, &tiovec[0], niov);
+      }
+      NET_DEBUG_COUNT_DYN_STAT(net_calls_to_read_stat, 1);
+      total_read += rattempted;
+    } while (rattempted && r == rattempted && total_read < toread);
+
+    // if we have already moved some bytes successfully, summarize in r
+    if (total_read != rattempted) {
+      if (r <= 0)
+        r = total_read - rattempted;
+      else
+        r = total_read - rattempted + r;
+    }
+    // check for errors
+    if (r <= 0) {
+
+      if (r == -EAGAIN || r == -ENOTCONN) {
+        NET_DEBUG_COUNT_DYN_STAT(net_calls_to_read_nodata_stat, 1);
+        return r;
+      }
+
+      if (!r || r == -ECONNRESET) {
+        return r;
+      }
+      return r;
+    }
+    NET_SUM_DYN_STAT(net_read_bytes_stat, r);
+
+    this->handShakeBuffer->fill(r);
+
+  } else
+    r = 0;
+
+  char *start = this->handShakeReader->start();
+  char *end = this->handShakeReader->end();
+
+  // Sets up the buffer as a read only bio target
+  // Must be reset on each read
+  BIO *rbio = BIO_new_mem_buf(start, end - start);
+  BIO_set_mem_eof_return(rbio, -1); 
+  // Assigning directly into the SSL structure
+  // is dirty, but there is no openssl function that only
+  // assigns the read bio.  Originally I was getting and
+  // resetting the same write bio, but that caused the 
+  // inserted buffer bios to be freed and then reinserted.
+  //BIO *wbio = SSL_get_wbio(this->ssl);
+  //SSL_set_bio(this->ssl, rbio, wbio);
+  this->ssl->rbio = rbio;
+ 
+  return r;
+}
+
 
 //changed by YTS Team, yamsat
 void
@@ -220,6 +405,11 @@ SSLNetVConnection::net_read_io(NetHandler *nh, EThread *lthread)
   MIOBufferAccessor &buf = s->vio.buffer;
   int64_t ntodo = s->vio.ntodo();
 
+  if (HttpProxyPort::TRANSPORT_BLIND_TUNNEL == this->attributes) {
+    this->super::net_read_io(nh, lthread);
+    return;
+  }
+
   if (sslClientRenegotiationAbort == true) {
     this->read.triggered = 0;
     readSignalError(nh, (int)r);
@@ -246,37 +436,82 @@ SSLNetVConnection::net_read_io(NetHandler *nh, EThread *lthread)
   // vc is an SSLNetVConnection.
   if (!getSSLHandShakeComplete()) {
     int err;
+    int data_to_read = 0;
+    char *data_ptr = NULL;
 
-    if (getSSLClientConnection()) {
-      ret = sslStartHandShake(SSL_EVENT_CLIENT, err);
-    } else {
-      ret = sslStartHandShake(SSL_EVENT_SERVER, err);
-    }
+    // Not done handshaking, go into the SSL handshake logic again
+    if (!getSSLHandShakeComplete()) {
 
-    if (ret == EVENT_ERROR) {
-      this->read.triggered = 0;
-      readSignalError(nh, err);
-    } else if (ret == SSL_HANDSHAKE_WANT_READ || ret == SSL_HANDSHAKE_WANT_ACCEPT) {
-      read.triggered = 0;
-      nh->read_ready_list.remove(this);
-      readReschedule(nh);
-    } else if (ret == SSL_HANDSHAKE_WANT_CONNECT || ret == SSL_HANDSHAKE_WANT_WRITE) {
-      write.triggered = 0;
-      nh->write_ready_list.remove(this);
-      writeReschedule(nh);
-    } else if (ret == EVENT_DONE) {
-      // If this was driven by a zero length read, signal complete when
-      // the handshake is complete. Otherwise set up for continuing read
-      // operations.
-      if (ntodo <= 0) {
-        readSignalDone(VC_EVENT_READ_COMPLETE, nh);
+      if (getSSLClientConnection()) {
+        ret = sslStartHandShake(SSL_EVENT_CLIENT, err);
       } else {
-        read.triggered = 1;
-        if (read.enabled)
-          nh->read_ready_list.in_or_enqueue(this);
+        ret = sslStartHandShake(SSL_EVENT_SERVER, err);
       }
-    } else
-      readReschedule(nh);
+      // 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
+          int data_still_to_read = BIO_get_mem_data(SSL_get_rbio(this->ssl), &data_ptr);
+          data_to_read = this->handShakeReader->read_avail();
+          this->handShakeReader->consume(data_to_read - data_still_to_read);
+        }
+        else {  // Now in blind tunnel. Set things up to read what is in the buffer
+          this->readSignalDone(VC_EVENT_READ_COMPLETE, nh);
+  
+          // If the handshake isn't set yet, this means the tunnel 
+          // decision was make in the SNI callback.  We must move
+          // the client hello message back into the standard read.vio
+          // so it will get forwarded onto the origin server
+          if (!this->sslHandShakeComplete) {
+            // Kick things to get the http forwarding buffers set up
+            this->sslHandShakeComplete = 1;
+            // Copy over all data already read in during the SSL_accept
+            // (the client hello message)
+            NetState *s = &this->read;
+            MIOBufferAccessor &buf = s->vio.buffer;
+            int64_t r = buf.writer()->write(this->handShakeHolder);
+            s->vio.nbytes += r;
+            s->vio.ndone += r;
+ 
+            // Clean up the handshake buffers
+            this->free_handshake_buffers();
+
+            // Kick things again, so the data that was copied into the
+            // vio.read buffer gets processed
+            this->readSignalDone(VC_EVENT_READ_COMPLETE, nh);
+          }
+          return;
+        }
+      }
+
+      if (ret == EVENT_ERROR) {
+        this->read.triggered = 0;
+        readSignalError(nh, err);
+      } else if (ret == SSL_HANDSHAKE_WANT_READ || ret == SSL_HANDSHAKE_WANT_ACCEPT) {
+        read.triggered = 0;
+        nh->read_ready_list.remove(this);
+        readReschedule(nh);
+      } else if (ret == SSL_HANDSHAKE_WANT_CONNECT || ret == SSL_HANDSHAKE_WANT_WRITE) {
+        write.triggered = 0;
+        nh->write_ready_list.remove(this);
+        writeReschedule(nh);
+      } else if (ret == EVENT_DONE) {
+        // If this was driven by a zero length read, signal complete when
+        // the handshake is complete. Otherwise set up for continuing read
+        // operations.
+        if (ntodo <= 0) {
+          readSignalDone(VC_EVENT_READ_COMPLETE, nh);
+        } else {
+          read.triggered = 1;
+          if (read.enabled)
+            nh->read_ready_list.in_or_enqueue(this);
+        }
+      } else if (ret == SSL_WAIT_FOR_HOOK) {
+        // avoid readReschedule - done when the plugin calls us back to reenable
+      } else {
+        readReschedule(nh);
+      }
+    }
     return;
   }
 
@@ -286,7 +521,43 @@ SSLNetVConnection::net_read_io(NetHandler *nh, EThread *lthread)
     return;
   }
 
-  // not sure if this do-while loop is really needed here, please replace this comment if you know
+  // At this point we are at the post-handshake SSL processing
+  // If the read BIO is not already a socket, consider changing it
+  if (this->handShakeReader) {
+    if (this->handShakeReader->read_avail() <= 0) { 
+      // Switch the read bio over to a socket bio
+      SSL_set_rfd(this->ssl, this->get_socket());
+      this->free_handshake_buffers();
+    } 
+    else { // There is still data in the buffer to drain
+      char *data_ptr = NULL;
+      int data_still_to_read = BIO_get_mem_data(SSL_get_rbio(this->ssl), &data_ptr);
+      if (data_still_to_read >  0) {
+        // Still data remaining in the current BIO block
+      }
+      else { 
+        // reset the block
+        char *start = this->handShakeReader->start();
+        char *end = this->handShakeReader->end();
+        // Sets up the buffer as a read only bio target
+        // Must be reset on each read
+        BIO *rbio = BIO_new_mem_buf(start, end - start);
+        BIO_set_mem_eof_return(rbio, -1); 
+        // So assigning directly into the SSL structure
+        // is dirty, but there is no openssl function that only
+        // assigns the read bio.  Originally I was getting and
+        // resetting the same write bio, but that caused the 
+        // inserted buffer bios to be freed and then reinserted.
+        this->ssl->rbio = rbio;
+        //BIO *wbio = SSL_get_wbio(this->ssl);
+        //SSL_set_bio(this->ssl, rbio, wbio);
+      }
+    }
+  }
+  // Otherwise, we already replaced the buffer bio with a socket bio
+
+  // not sure if this do-while loop is really needed here, please replace 
+  // this comment if you know
   do {
     ret = ssl_read_from_net(this, lthread, r);
     if (ret == SSL_READ_READY || ret == SSL_READ_ERROR_NONE) {
@@ -370,6 +641,10 @@ SSLNetVConnection::load_buffer_and_write(int64_t towrite, int64_t &wattempted, i
   int64_t offset = buf.reader()->start_offset;
   IOBufferBlock *b = buf.reader()->block;
 
+  if (HttpProxyPort::TRANSPORT_BLIND_TUNNEL == this->attributes) {
+    return this->super::load_buffer_and_write(towrite, wattempted, total_wrote, buf, needs);
+  }
+
   do {
     // check if we have done this block
     l = b->read_avail();
@@ -476,14 +751,21 @@ SSLNetVConnection::load_buffer_and_write(int64_t towrite, int64_t &wattempted, i
 }
 
 SSLNetVConnection::SSLNetVConnection():
+  ssl(NULL),
+  sslHandshakeBeginTime(0),
+  hookOpRequested(TS_SSL_HOOK_OP_DEFAULT),
   sslHandShakeComplete(false),
   sslClientConnection(false),
   sslClientRenegotiationAbort(false),
+  handShakeBuffer(NULL),
+  handShakeHolder(NULL),
+  handShakeReader(NULL),
+  sslPreAcceptHookState(SSL_HOOKS_INIT),
+  sslSNIHookState(SNI_HOOKS_INIT),
   npnSet(NULL),
   npnEndpoint(NULL)
 {
-  ssl = NULL;
-  sslHandshakeBeginTime = 0;
+  sniServername[0] = '\0';
 }
 
 void
@@ -511,6 +793,12 @@ SSLNetVConnection::free(EThread * t) {
   sslHandShakeComplete = false;
   sslClientConnection = false;
   sslClientRenegotiationAbort = false;
+  if (SSL_HOOKS_ACTIVE == sslPreAcceptHookState) {
+    Error("SSLNetVconnection freed with outstanding hook");
+  }
+  sslPreAcceptHookState = SSL_HOOKS_INIT;
+  curHook = 0;
+  hookOpRequested = TS_SSL_HOOK_OP_DEFAULT;
   npnSet = NULL;
   npnEndpoint= NULL;
 
@@ -529,6 +817,33 @@ SSLNetVConnection::sslStartHandShake(int event, int &err)
   case SSL_EVENT_SERVER:
     if (this->ssl == NULL) {
       SSLCertificateConfig::scoped_config lookup;
+      IpEndpoint ip;
+      int namelen = sizeof(ip);
+      safe_getsockname(this->get_socket(), &ip.sa, &namelen);
+      SSLCertContext *cc = lookup->find(ip);
+      if (is_debug_tag_set("ssl")) {
+        IpEndpoint src, dst;
+        ip_port_text_buffer ipb1, ipb2;
+        int ip_len;
+        
+        safe_getsockname(this->get_socket(), &dst.sa, &(ip_len = sizeof ip));
+        safe_getpeername(this->get_socket(), &src.sa, &(ip_len = sizeof ip));
+        ats_ip_nptop(&dst, ipb1, sizeof(ipb1));
+        ats_ip_nptop(&src, ipb2, sizeof(ipb2));
+        Debug("ssl", "IP context is %p for [%s] -> [%s], default context %p", cc, ipb2, ipb1, lookup->defaultContext());
+      }
+
+      // Escape if this is marked to be a tunnel.
+      // No data has been read at this point, so we can go
+      // directly into blind tunnel mode
+      if (cc && SSLCertContext::OPT_TUNNEL == cc->opt && this->is_transparent) {
+        this->attributes = HttpProxyPort::TRANSPORT_BLIND_TUNNEL;
+        sslHandShakeComplete = 1;
+        SSL_free(this->ssl);
+        this->ssl = NULL;
+        return EVENT_DONE;
+      }
+ 
 
       // Attach the default SSL_CTX to this SSL session. The default context is never going to be able
       // to negotiate a SSL session, but it's enough to trampoline us into the SNI callback where we
@@ -565,7 +880,66 @@ SSLNetVConnection::sslStartHandShake(int event, int &err)
 int
 SSLNetVConnection::sslServerHandShakeEvent(int &err)
 {
-  int ret = SSL_accept(ssl);
+  int ret;
+
+  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.
+      curHook = curHook->next();
+    }
+    if (SSL_HOOKS_INVOKE == sslPreAcceptHookState) {
+      if (0 == curHook) { // no hooks left, we're done
+        sslPreAcceptHookState = SSL_HOOKS_DONE;
+      } else {
+        sslPreAcceptHookState = SSL_HOOKS_ACTIVE;
+        ContWrapper::wrap(mutex, 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.
+      */
+      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.
+  // Note we can't arrive here if a hook is active.
+  if (TS_SSL_HOOK_OP_TUNNEL == hookOpRequested) {
+    this->attributes = HttpProxyPort::TRANSPORT_BLIND_TUNNEL;
+    SSL_free(this->ssl);
+    this->ssl = NULL;
+    sslHandShakeComplete = 1;
+    return EVENT_DONE;
+  } else if (TS_SSL_HOOK_OP_TERMINATE == hookOpRequested) {
+    sslHandShakeComplete = 1;
+    return EVENT_DONE;
+  }
+
+  // All the pre-accept hooks have completed, proceed with the actual accept.
+
+  char *data_ptr = NULL;
+  int data_to_read = BIO_get_mem_data(SSL_get_rbio(this->ssl), &data_ptr);
+  if (data_to_read <= 0) { // If there is not already data in the buffer
+    // Read from socket to fill in the BIO buffer with the 
+    // raw handshake data before calling the ssl accept calls.
+    int64_t data_read;
+    if ((data_read = this->read_raw_data()) > 0) {
+      data_to_read = BIO_get_mem_data(SSL_get_rbio(this->ssl), &data_ptr);
+    }
+  }
+
+  ret = SSL_accept(ssl);
   int ssl_error = SSL_get_error(ssl, ret);
 
   if (ssl_error != SSL_ERROR_NONE) {
@@ -644,6 +1018,23 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)
   case SSL_ERROR_WANT_READ:
     return SSL_HANDSHAKE_WANT_READ;
 
+// This value is only defined in openssl has been patched to
+// enable the sni callback to break out of the SSL_accept processing
+#ifdef SSL_ERROR_WANT_SNI_RESOLVE
+  case SSL_ERROR_WANT_SNI_RESOLVE:
+    if (this->attributes == HttpProxyPort::TRANSPORT_BLIND_TUNNEL ||
+        TS_SSL_HOOK_OP_TUNNEL == hookOpRequested) {
+      this->attributes = HttpProxyPort::TRANSPORT_BLIND_TUNNEL;
+      sslHandShakeComplete = 0;
+      return EVENT_CONT;
+    }
+    else {
+      //  Stopping for some other reason, perhaps loading certificate
+      //;return EVENT_ERROR;
+      return EVENT_CONT;
+    }
+#endif
+
   case SSL_ERROR_WANT_ACCEPT:
   case SSL_ERROR_WANT_X509_LOOKUP:
     return EVENT_CONT;
@@ -673,7 +1064,7 @@ SSLNetVConnection::sslClientHandShakeEvent(int &err)
     }
   }
 #endif
-
+  
   ret = SSL_connect(ssl);
   switch (SSL_get_error(ssl, ret)) {
   case SSL_ERROR_NONE:
@@ -794,3 +1185,54 @@ SSLNetVConnection::select_next_protocol(SSL * ssl, const unsigned char ** out, u
   *outlen = 0;
   return SSL_TLSEXT_ERR_NOACK;
 }
+
+void
+SSLNetVConnection::reenable(NetHandler* nh) {
+  if (this->sslPreAcceptHookState != SSL_HOOKS_DONE) {
+    this->sslPreAcceptHookState = SSL_HOOKS_INVOKE;
+    this->readReschedule(nh);
+  } else {
+    // Reenabling from the SNI callback
+    this->sslSNIHookState = SNI_HOOKS_CONTINUE;
+  }
+}
+
+
+bool
+SSLNetVConnection::sslContextSet(void* ctx) {
+  bool zret = true;
+  if (ssl)
+    SSL_set_SSL_CTX(ssl, static_cast<SSL_CTX*>(ctx));
+  else
+    zret = false;
+  return zret;
+}
+
+bool 
+SSLNetVConnection::callHooks(TSHttpHookID eventId) 
+{
+  // Only dealing with the SNI hook so far
+  ink_assert(eventId == TS_SSL_SNI_HOOK);
+
+  APIHook *hook = ssl_hooks->get(TS_SSL_SNI_INTERNAL_HOOK);
+  bool reenabled = true;
+  while (hook && reenabled) {
+    // Must reset to a completed state for each invocation
+    this->sslSNIHookState = SNI_HOOKS_DONE;
+
+    // Invoke the hook
+    hook->invoke(TS_SSL_SNI_HOOK, this);
+
+    // If it did not re-enable, return the code to 
+    // stop the accept processing
+    if (this->sslSNIHookState == SNI_HOOKS_DONE) {
+      reenabled = false;
+    }
+
+    // Otherwise, look for the next plugin code
+    hook = hook->next();
+  }
+  return reenabled;
+}
+
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/iocore/net/SSLUtils.cc
----------------------------------------------------------------------
diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index 9ebdf2e..8a144cd 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -20,6 +20,7 @@
  */
 
 #include "ink_config.h"
+#include "records/I_RecHttp.h"
 #include "libts.h"
 #include "I_Layout.h"
 #include "P_Net.h"
@@ -57,6 +58,8 @@
 #define SSL_CERT_TAG          "ssl_cert_name"
 #define SSL_PRIVATE_KEY_TAG   "ssl_key_name"
 #define SSL_CA_TAG            "ssl_ca_name"
+#define SSL_ACTION_TAG        "action"
+#define SSL_ACTION_TUNNEL_TAG "tunnel"
 #define SSL_SESSION_TICKET_ENABLED "ssl_ticket_enabled"
 #define SSL_SESSION_TICKET_KEY_FILE_TAG "ticket_key_name"
 #define SSL_KEY_DIALOG        "ssl_key_dialog"
@@ -84,7 +87,7 @@ typedef SSL_METHOD * ink_ssl_method_t;
 // gather user provided settings from ssl_multicert.config in to a single struct
 struct ssl_user_config
 {
-  ssl_user_config () : session_ticket_enabled(1) {
+  ssl_user_config () : session_ticket_enabled(1), opt(SSLCertContext::OPT_NONE) {
   }
 
   int session_ticket_enabled;  // ssl_ticket_enabled - session ticket enabled
@@ -95,6 +98,7 @@ struct ssl_user_config
   ats_scoped_str key;    // ssl_key_name - Private key
   ats_scoped_str ticket_key_filename; // ticket_key_name - session key file. [key_name (16Byte) + HMAC_secret (16Byte) + AES_key (16Byte)]
   ats_scoped_str dialog; // ssl_key_dialog - Private key dialog
+  SSLCertContext::Option opt;
 };
 
 // Check if the ticket_key callback #define is available, and if so, enable session tickets.
@@ -173,27 +177,51 @@ SSL_CTX_add_extra_chain_cert_file(SSL_CTX * ctx, const char * chainfile)
 #if TS_USE_TLS_SNI
 
 static int
-ssl_servername_callback(SSL * ssl, int * ad, void * arg)
+ssl_servername_callback(SSL * ssl, int * ad, void * /*arg*/)
 {
   SSL_CTX *           ctx = NULL;
-  SSLCertLookup *     lookup = (SSLCertLookup *) arg;
+  SSLCertContext *    cc = NULL;
+  // Fetching the lookup via the callback arg allows for race
+  // condition with reconfigure
+  //SSLCertLookup *     lookup = (SSLCertLookup *) arg;
+  SSLCertLookup *lookup = SSLCertificateConfig::acquire();
   const char *        servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
   SSLNetVConnection * netvc = (SSLNetVConnection *)SSL_get_app_data(ssl);
+  bool found = true;
+  bool reenabled;
+  int retval = SSL_TLSEXT_ERR_OK;
 
-  Debug("ssl", "ssl_servername_callback ssl=%p ad=%d lookup=%p server=%s handshake_complete=%d", ssl, *ad, lookup, servername,
+  Debug("ssl", "ssl_servername_callback ssl=%p ad=%d server=%s handshake_complete=%d", ssl, *ad, servername,
     netvc->getSSLHandShakeComplete());
+  
+  if (servername != NULL) {
+    strncpy(netvc->sniServername, servername, TS_MAX_HOST_NAME_LEN);
+  }
 
   // catch the client renegotiation early on
   if (SSLConfigParams::ssl_allow_client_renegotiation == false && netvc->getSSLHandShakeComplete()) {
     Debug("ssl", "ssl_servername_callback trying to renegotiate from the client");
-    return SSL_TLSEXT_ERR_ALERT_FATAL;
+    retval = SSL_TLSEXT_ERR_ALERT_FATAL;
+    goto done;
   }
 
   // The incoming SSL_CTX is either the one mapped from the inbound IP address or the default one. If we
   // don't find a name-based match at this point, we *do not* want to mess with the context because we've
   // already made a best effort to find the best match.
   if (likely(servername)) {
-    ctx = lookup->findInfoInHash((char *)servername);
+    cc = lookup->find((char *)servername);
+    if (cc && cc->ctx) ctx = cc->ctx;
+    if (cc && SSLCertContext::OPT_TUNNEL == cc->opt && netvc->get_is_transparent()) {
+#ifdef SSL_TLSEXT_ERR_READ_AGAIN
+      netvc->attributes = HttpProxyPort::TRANSPORT_BLIND_TUNNEL;
+      netvc->setSSLHandShakeComplete(true);
+      retval = SSL_TLSEXT_ERR_READ_AGAIN;
+#else 
+      Error("Must have openssl patch to support OPT_TUNNEL from SNI callback");
+      retval = SSL_TLSEXT_ERR_ALERT_FATAL;
+#endif
+      goto done;
+    }
   }
 
   // If there's no match on the server name, try to match on the peer address.
@@ -202,36 +230,60 @@ ssl_servername_callback(SSL * ssl, int * ad, void * arg)
     int namelen = sizeof(ip);
 
     safe_getsockname(netvc->get_socket(), &ip.sa, &namelen);
-    ctx = lookup->findInfoInHash(ip);
+    cc = lookup->find(ip);
+    if (cc && cc->ctx) ctx = cc->ctx;
   }
 
   if (ctx != NULL) {
     SSL_set_SSL_CTX(ssl, ctx);
   }
+  else {
+    found = false;
+  }
 
   ctx = SSL_get_SSL_CTX(ssl);
-  Debug("ssl", "ssl_servername_callback found SSL context %p for requested name '%s'", ctx, servername);
+  Debug("ssl", "ssl_servername_callback %s SSL context %p for requested name '%s'", found ? "found" : "using", ctx, servername);
 
   if (ctx == NULL) {
-    return SSL_TLSEXT_ERR_NOACK;
+    retval = SSL_TLSEXT_ERR_NOACK;
+    goto done;
+  }
+
+  // Call the plugin SNI code
+  reenabled = netvc->callHooks(TS_SSL_SNI_HOOK);
+  // If it did not re-enable, return the code to 
+  // stop the accept processing
+  if (!reenabled){
+#ifdef SSL_TLSEXT_ERR_READ_AGAIN
+    retval = SSL_TLSEXT_ERR_READ_AGAIN;
+#else 
+    Error("Must have openssl patch to support OPT_TUNNEL from SNI callback");
+    retval = SSL_TLSEXT_ERR_ALERT_FATAL;
+#endif
+    goto done;
   }
 
+done:
+  SSLCertificateConfig::release(lookup);
   // We need to return one of the SSL_TLSEXT_ERR constants. If we return an
   // error, we can fill in *ad with an alert code to propgate to the
   // client, see SSL_AD_*.
-  return SSL_TLSEXT_ERR_OK;
+  return retval;
 }
 
 #endif /* TS_USE_TLS_SNI */
 
 static SSL_CTX *
-ssl_context_enable_sni(SSL_CTX * ctx, SSLCertLookup * lookup)
+ssl_context_enable_sni(SSL_CTX * ctx, SSLCertLookup * /*lookup*/)
 {
 #if TS_USE_TLS_SNI
   if (ctx) {
     Debug("ssl", "setting SNI callbacks with for ctx %p", ctx);
     SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_callback);
-    SSL_CTX_set_tlsext_servername_arg(ctx, lookup);
+    // Possible race conditions against reconfigure here
+    // Better to use the SSLCertificate.acquire function to access the
+    // lookup data structure safely
+    //SSL_CTX_set_tlsext_servername_arg(ctx, lookup);
   }
 #else
   (void)lookup;
@@ -487,12 +539,13 @@ SSLRecRawStatSyncCount(const char *name, RecDataT data_type, RecData *data, RecR
   if (certLookup) {
     const unsigned ctxCount = certLookup->count();
     for (size_t i = 0; i < ctxCount; i++) {
-      SSL_CTX * ctx = certLookup->get(i);
-
-      sessions += SSL_CTX_sess_accept_good(ctx);
-      hits += SSL_CTX_sess_hits(ctx);
-      misses += SSL_CTX_sess_misses(ctx);
-      timeouts += SSL_CTX_sess_timeouts(ctx);
+      SSLCertContext *cc = certLookup->get(i);
+      if (cc && cc->ctx) {
+        sessions += SSL_CTX_sess_accept_good(cc->ctx);
+        hits += SSL_CTX_sess_hits(cc->ctx);
+        misses += SSL_CTX_sess_misses(cc->ctx);
+        timeouts += SSL_CTX_sess_timeouts(cc->ctx);
+      }
     }
   }
 
@@ -1224,7 +1277,7 @@ asn1_strdup(ASN1_STRING * s)
 // table aliases for subject CN and subjectAltNames DNS without wildcard,
 // insert trie aliases for those with wildcard.
 static void
-ssl_index_certificate(SSLCertLookup * lookup, SSL_CTX * ctx, const char * certfile)
+ssl_index_certificate(SSLCertLookup * lookup, SSLCertContext const& cc, const char * certfile)
 {
   X509_NAME *   subject = NULL;
   X509 *        cert;
@@ -1250,7 +1303,7 @@ ssl_index_certificate(SSLCertLookup * lookup, SSL_CTX * ctx, const char * certfi
       ats_scoped_str name(asn1_strdup(cn));
 
       Debug("ssl", "mapping '%s' to certificate %s", (const char *) name, certfile);
-      lookup->insert(ctx, name);
+      lookup->insert(name, cc);
     }
   }
 
@@ -1266,7 +1319,7 @@ ssl_index_certificate(SSLCertLookup * lookup, SSL_CTX * ctx, const char * certfi
       if (name->type == GEN_DNS) {
         ats_scoped_str dns(asn1_strdup(name->d.dNSName));
         Debug("ssl", "mapping '%s' to certificate %s", (const char *) dns, certfile);
-        lookup->insert(ctx, dns);
+        lookup->insert(dns, cc);
       }
     }
 
@@ -1339,13 +1392,13 @@ ssl_store_ssl_context(
   if (sslMultCertSettings.addr) {
     if (strcmp(sslMultCertSettings.addr, "*") == 0) {
       lookup->ssl_default = ctx;
-      lookup->insert(ctx, sslMultCertSettings.addr);
+      lookup->insert(sslMultCertSettings.addr, SSLCertContext(ctx, sslMultCertSettings.opt));
     } else {
       IpEndpoint ep;
 
       if (ats_ip_pton(sslMultCertSettings.addr, &ep) == 0) {
         Debug("ssl", "mapping '%s' to certificate %s", (const char *)sslMultCertSettings.addr, (const char *)certpath);
-        lookup->insert(ctx, ep);
+        lookup->insert(ep, SSLCertContext(ctx, sslMultCertSettings.opt));
       } else {
         Error("'%s' is not a valid IPv4 or IPv6 address", (const char *)sslMultCertSettings.addr);
       }
@@ -1386,7 +1439,7 @@ ssl_store_ssl_context(
   // this code is updated to reconfigure the SSL certificates, it will need some sort of
   // refcounting or alternate way of avoiding double frees.
   Debug("ssl", "importing SNI names from %s", (const char *)certpath);
-  ssl_index_certificate(lookup, ctx, certpath);
+  ssl_index_certificate(lookup, SSLCertContext(ctx, sslMultCertSettings.opt), certpath);
 
   if (SSLConfigParams::init_ssl_ctx_cb) {
     SSLConfigParams::init_ssl_ctx_cb(ctx, true);
@@ -1439,6 +1492,15 @@ ssl_extract_certificate(
     if (strcasecmp(label, SSL_KEY_DIALOG) == 0) {
       sslMultCertSettings.dialog = ats_strdup(value);
     }
+    if (strcasecmp(label, SSL_ACTION_TAG) == 0) {
+      if (strcasecmp(SSL_ACTION_TUNNEL_TAG, value) == 0) {
+        sslMultCertSettings.opt = SSLCertContext::OPT_TUNNEL;
+      }
+      else {
+        Error("Unrecognized action for " SSL_ACTION_TAG);
+        return false;
+      }
+    }
   }
   if (!sslMultCertSettings.cert) {
     Error("missing %s tag", SSL_CERT_TAG);

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/iocore/net/UnixNetVConnection.cc
----------------------------------------------------------------------
diff --git a/iocore/net/UnixNetVConnection.cc b/iocore/net/UnixNetVConnection.cc
index ec8605c..35db956 100644
--- a/iocore/net/UnixNetVConnection.cc
+++ b/iocore/net/UnixNetVConnection.cc
@@ -384,7 +384,9 @@ write_to_net_io(NetHandler *nh, UnixNetVConnection *vc, EThread *thread)
       nh->read_ready_list.remove(vc);
       vc->write.triggered = 0;
       nh->write_ready_list.remove(vc);
-      if (!(ret == SSL_HANDSHAKE_WANT_READ || ret == SSL_HANDSHAKE_WANT_ACCEPT))
+      if (ret == SSL_HANDSHAKE_WANT_READ || ret == SSL_HANDSHAKE_WANT_ACCEPT)
+        read_reschedule(nh, vc);
+      else
         write_reschedule(nh, vc);
     } else if (ret == EVENT_DONE) {
       vc->write.triggered = 1;
@@ -497,7 +499,6 @@ write_to_net_io(NetHandler *nh, UnixNetVConnection *vc, EThread *thread)
     } else if (signalled && (wbe_event != vc->write_buffer_empty_event)) {
       // @a signalled means we won't send an event, and the event values differing means we
       // had a write buffer trap and cleared it, so we need to send it now.
-      Debug("amc", "empty write buffer trap [%d]", wbe_event);
       if (write_signal_and_update(wbe_event, vc) != EVENT_CONT)
         return;
     } else if (!signalled) {
@@ -1187,6 +1188,7 @@ UnixNetVConnection::free(EThread *t)
   action_.mutex.clear();
   got_remote_addr = 0;
   got_local_addr = 0;
+  attributes = 0;
   read.vio.mutex.clear();
   write.vio.mutex.clear();
   flags = 0;

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/lib/records/I_RecHttp.h
----------------------------------------------------------------------
diff --git a/lib/records/I_RecHttp.h b/lib/records/I_RecHttp.h
index c121dfe..32a9f2d 100644
--- a/lib/records/I_RecHttp.h
+++ b/lib/records/I_RecHttp.h
@@ -181,11 +181,12 @@ public:
 
   /// Type of transport on the connection.
   enum TransportType {
-    TRANSPORT_DEFAULT = 0, ///< Default (normal HTTP).
-    TRANSPORT_COMPRESSED, ///< Compressed HTTP.
+    TRANSPORT_NONE = 0,     ///< Unspecified / uninitialized
+    TRANSPORT_DEFAULT,      ///< Default (normal HTTP).
+    TRANSPORT_COMPRESSED,   ///< Compressed HTTP.
     TRANSPORT_BLIND_TUNNEL, ///< Blind tunnel (no processing).
-    TRANSPORT_SSL, ///< SSL connection.
-    TRANSPORT_PLUGIN /// < Protocol plugin connection
+    TRANSPORT_SSL,          ///< SSL connection.
+    TRANSPORT_PLUGIN        /// < Protocol plugin connection
   };
 
   int m_fd; ///< Pre-opened file descriptor if present.

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/lib/ts/Vec.h
----------------------------------------------------------------------
diff --git a/lib/ts/Vec.h b/lib/ts/Vec.h
index 589da0e..9dcf933 100644
--- a/lib/ts/Vec.h
+++ b/lib/ts/Vec.h
@@ -61,6 +61,7 @@ class Vec {
   void push_back(C a) { add(a); } // std::vector name
   bool add_exclusive(C a);
   C& add();
+  void drop();
   C pop();
   void reset();
   void clear();
@@ -211,6 +212,12 @@ Vec<C,A,S>::add() {
   return *ret;
 }
 
+template <class C, class A, int S> inline void
+Vec<C,A,S>::drop() {
+  if (n && 0 == --n)
+    clear();
+}
+
 template <class C, class A, int S> inline C
 Vec<C,A,S>::pop() {
   if (!n)

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/lib/ts/apidefs.h.in
----------------------------------------------------------------------
diff --git a/lib/ts/apidefs.h.in b/lib/ts/apidefs.h.in
index 96896f6..2b62427 100644
--- a/lib/ts/apidefs.h.in
+++ b/lib/ts/apidefs.h.in
@@ -255,6 +255,10 @@ extern "C"
 
       The two transform hooks can ONLY be added as transaction hooks.
 
+      TS_VCONN_PRE_ACCEPT_HOOK - Called before the SSL hand
+      shake starts.  No handshake data has been read or sent (from the 
+      proxy) at this point
+
       TS_HTTP_LAST_HOOK _must_ be the last element. Only right place
       to insert a new element is just before TS_HTTP_LAST_HOOK.
 
@@ -278,6 +282,12 @@ extern "C"
     TS_HTTP_PRE_REMAP_HOOK,
     TS_HTTP_POST_REMAP_HOOK,
     TS_HTTP_RESPONSE_CLIENT_HOOK,
+    // Putting the SSL hooks in the same enum space
+    // So both sets of hooks can be set by the same Hook function
+    TS_SSL_FIRST_HOOK,
+    TS_VCONN_PRE_ACCEPT_HOOK = TS_SSL_FIRST_HOOK,
+    TS_SSL_SNI_HOOK,
+    TS_SSL_LAST_HOOK = TS_SSL_SNI_HOOK,
     TS_HTTP_LAST_HOOK
   } TSHttpHookID;
   #define TS_HTTP_READ_REQUEST_PRE_REMAP_HOOK TS_HTTP_PRE_REMAP_HOOK  /* backwards compat */
@@ -437,6 +447,7 @@ extern "C"
     TS_EVENT_LIFECYCLE_CACHE_READY = 60020,
     TS_EVENT_LIFECYCLE_SERVER_SSL_CTX_INITIALIZED = 60021,
     TS_EVENT_LIFECYCLE_CLIENT_SSL_CTX_INITIALIZED = 60022,
+    TS_EVENT_VCONN_PRE_ACCEPT = 60023,
     TS_EVENT_MGMT_UPDATE = 60100,
 
     /* EVENTS 60200 - 60202 for internal use */
@@ -781,6 +792,13 @@ extern "C"
       TS_MILESTONE_LAST_ENTRY
     } TSMilestonesType;
 
+  /// Operation requested by asynchronous hooks during SSL processing
+ typedef enum {
+    TS_SSL_HOOK_OP_DEFAULT, ///< Null / initialization value. Do normal processing.
+    TS_SSL_HOOK_OP_TUNNEL, ///< Switch to blind tunnel
+    TS_SSL_HOOK_OP_TERMINATE, ///< Termination connection / transaction.
+    TS_SSL_HOOK_OP_LAST = TS_SSL_HOOK_OP_TERMINATE ///< End marker value.
+  } TSSslVConnOp;
 
   /* These typedefs are used with the corresponding TSMgmt*Get functions
      for storing the values retrieved by those functions. For example,
@@ -796,6 +814,7 @@ extern "C"
   typedef struct tsapi_mbuffer* TSMBuffer;
   typedef struct tsapi_httpssn* TSHttpSsn;
   typedef struct tsapi_httptxn* TSHttpTxn;
+  typedef struct tsapi_ssl_obj* TSSslConnection;
   typedef struct tsapi_httpaltinfo* TSHttpAltInfo;
   typedef struct tsapi_mimeparser* TSMimeParser;
   typedef struct tsapi_httpparser* TSHttpParser;
@@ -810,6 +829,7 @@ extern "C"
   typedef struct tsapi_config* TSConfig;
   typedef struct tsapi_cont* TSCont;
   typedef struct tsapi_cont* TSVConn; /* a VConn is really a specialized TSCont */
+  typedef struct tsapi_ssl_context* TSSslContext;
   typedef struct tsapi_action* TSAction;
   typedef struct tsapi_iobuffer* TSIOBuffer;
   typedef struct tsapi_iobufferdata* TSIOBufferData;

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/lib/ts/ink_sock.cc
----------------------------------------------------------------------
diff --git a/lib/ts/ink_sock.cc b/lib/ts/ink_sock.cc
index 7429e53..055cd38 100644
--- a/lib/ts/ink_sock.cc
+++ b/lib/ts/ink_sock.cc
@@ -192,6 +192,16 @@ safe_getsockname(int s, struct sockaddr *name, int *namelen)
   return r;
 }
 
+int 
+safe_getpeername(int s, struct sockaddr *name, int *namelen) 
+{
+  int r;
+  do {
+    r = getpeername(s, name, (socklen_t*)namelen);
+  } while (r < 0 && (errno == EAGAIN || errno == EINTR));
+  return r;
+}
+
 char
 fd_read_char(int fd)
 {

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/lib/ts/ink_sock.h
----------------------------------------------------------------------
diff --git a/lib/ts/ink_sock.h b/lib/ts/ink_sock.h
index 39488d9..0f6660d 100644
--- a/lib/ts/ink_sock.h
+++ b/lib/ts/ink_sock.h
@@ -40,6 +40,7 @@ int safe_getsockopt(int s, int level, int optname, char *optval, int *optlevel);
 int safe_bind(int s, struct sockaddr const* name, int namelen);
 int safe_listen(int s, int backlog);
 int safe_getsockname(int s, struct sockaddr *name, int *namelen);
+int safe_getpeername(int s, struct sockaddr *name, int *namelen);
 
 int safe_fcntl(int fd, int cmd, int arg);
 int safe_ioctl(int fd, int request, char *arg);

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/plugins/experimental/Makefile.am
----------------------------------------------------------------------
diff --git a/plugins/experimental/Makefile.am b/plugins/experimental/Makefile.am
index 7e5ab51..7bb1452 100644
--- a/plugins/experimental/Makefile.am
+++ b/plugins/experimental/Makefile.am
@@ -32,6 +32,7 @@ SUBDIRS = \
  regex_revalidate \
  remap_stats \
  s3_auth \
+ ssl_cert_loader \
  sslheaders \
  stale_while_revalidate \
  url_sig \

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/plugins/experimental/ssl_cert_loader/Makefile.am
----------------------------------------------------------------------
diff --git a/plugins/experimental/ssl_cert_loader/Makefile.am b/plugins/experimental/ssl_cert_loader/Makefile.am
new file mode 100644
index 0000000..def7a7f
--- /dev/null
+++ b/plugins/experimental/ssl_cert_loader/Makefile.am
@@ -0,0 +1,26 @@
+#  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 $(top_srcdir)/build/plugins.mk
+
+AM_CPPFLAGS += -I$(top_srcdir)/lib -I$(top_srcdir)/lib/ts
+
+pkglib_LTLIBRARIES = ssl_cert_loader.la
+
+ssl_cert_loader_la_SOURCES = ssl-cert-loader.cc ip.cc IpMap.cc domain-tree.cc
+ssl_cert_loader_la_LDFLAGS = $(TS_PLUGIN_LDFLAGS) -L$(top_srcdir)/lib/tsconfig/.libs -ltsconfig
+
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/plugins/experimental/ssl_cert_loader/ats-util.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/ssl_cert_loader/ats-util.h b/plugins/experimental/ssl_cert_loader/ats-util.h
new file mode 100644
index 0000000..1164a20
--- /dev/null
+++ b/plugins/experimental/ssl_cert_loader/ats-util.h
@@ -0,0 +1,40 @@
+# if !defined(_ats_util_h)
+# define _ats_util_h
+
+# if defined(__cplusplus)
+/** Set data to zero.
+
+    Calls @c memset on @a t with a value of zero and a length of @c
+    sizeof(t). This can be used on ordinary and array variables. While
+    this can be used on variables of intrinsic type it's inefficient.
+
+    @note Because this uses templates it cannot be used on unnamed or
+    locally scoped structures / classes. This is an inherent
+    limitation of templates.
+
+    Examples:
+    @code
+    foo bar; // value.
+    ink_zero(bar); // zero bar.
+
+    foo *bar; // pointer.
+    ink_zero(bar); // WRONG - makes the pointer @a bar zero.
+    ink_zero(*bar); // zero what bar points at.
+
+    foo bar[ZOMG]; // Array of structs.
+    ink_zero(bar); // Zero all structs in array.
+
+    foo *bar[ZOMG]; // array of pointers.
+    ink_zero(bar); // zero all pointers in the array.
+    @endcode
+    
+ */
+template < typename T > inline void
+ink_zero(
+	 T& t ///< Object to zero.
+	 ) {
+  memset(&t, 0, sizeof(t));
+}
+# endif  /* __cplusplus */
+
+# endif // ats-util.h