You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by jp...@apache.org on 2012/02/19 17:47:50 UTC
[1/5] git commit: TS-841: support TLS NextProtocol negotiation
Updated Branches:
refs/heads/master 19f367381 -> c85a874f4
TS-841: support TLS NextProtocol negotiation
- Add a Next Protocol accept handler to maintain the supported
protocols. Route the OpenSSL NPN callbacks up through the
SSLNetVConnection so we can advertise the correct protocol set.
Add a helper class to perform the OpenSSL protocol list serialization.
- Keep a back pointer from the SSL connection to the SSLNetVConnection
so that we can route the SSL accept through the NextProtocol acceptor.
- Cache the selected endpoint on the SSLVConnection so we can
tell which endpoint to route the accept to. Add a default endpoint
for backwards compatibility. Create a SSLNextProtocolSet API
to keep a proper endpoint mapping.
- If we are building the plugin API, track the SSL protocol
acceptors so that we can register endpoints on them later using
the TSAPI.
- Add TSNetAcceptNamedProtocol plugin API and make NPN protocol
names API constants.
Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/c85a874f
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/c85a874f
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/c85a874f
Branch: refs/heads/master
Commit: c85a874f4d92278e5bc183b587dde8a5d4491d9e
Parents: b282375
Author: James Peach <jp...@apache.org>
Authored: Sat Feb 11 20:30:11 2012 -0800
Committer: James Peach <jp...@apache.org>
Committed: Sun Feb 19 08:46:10 2012 -0800
----------------------------------------------------------------------
iocore/net/Makefile.am | 2 +
iocore/net/P_SSLNetVConnection.h | 25 +++-
iocore/net/P_SSLNextProtocolAccept.h | 61 ++++++++++
iocore/net/P_SSLNextProtocolSet.h | 68 +++++++++++
iocore/net/SSLNetProcessor.cc | 32 +-----
iocore/net/SSLNetVConnection.cc | 70 +++++++++--
iocore/net/SSLNextProtocolAccept.cc | 118 +++++++++++++++++++
iocore/net/SSLNextProtocolSet.cc | 182 +++++++++++++++++++++++++++++
lib/ts/List.h | 10 ++-
lib/ts/ink_mutex.h | 14 +++
proxy/InkAPI.cc | 27 +++++
proxy/api/ts/ts.h.in | 31 +++++
proxy/http/HttpAccept.h | 2 +-
proxy/http/HttpProxyServerMain.cc | 50 ++++++++-
14 files changed, 641 insertions(+), 51 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c85a874f/iocore/net/Makefile.am
----------------------------------------------------------------------
diff --git a/iocore/net/Makefile.am b/iocore/net/Makefile.am
index e78aba2..fed40de 100644
--- a/iocore/net/Makefile.am
+++ b/iocore/net/Makefile.am
@@ -79,6 +79,8 @@ libinknet_a_SOURCES = \
SSLNetProcessor.cc \
SSLNetVConnection.cc \
SSLNetAccept.cc \
+ SSLNextProtocolAccept.cc \
+ SSLNextProtocolSet.cc \
UDPIOEvent.cc \
UnixConnection.cc \
UnixNetAccept.cc \
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c85a874f/iocore/net/P_SSLNetVConnection.h
----------------------------------------------------------------------
diff --git a/iocore/net/P_SSLNetVConnection.h b/iocore/net/P_SSLNetVConnection.h
index dbf9372..90c0fb6 100644
--- a/iocore/net/P_SSLNetVConnection.h
+++ b/iocore/net/P_SSLNetVConnection.h
@@ -40,6 +40,7 @@
#include <openssl/ssl.h>
#include <openssl/err.h>
+class SSLNextProtocolSet;
//////////////////////////////////////////////////////////////////
//
@@ -107,22 +108,36 @@ public:
int sslClientHandShakeEvent(int &err);
virtual void net_read_io(NetHandler * nh, EThread * lthread);
virtual int64_t load_buffer_and_write(int64_t towrite, int64_t &wattempted, int64_t &total_wrote, MIOBufferAccessor & buf);
- virtual ~ SSLNetVConnection() { }
+
+ void registerNextProtocolSet(const SSLNextProtocolSet *);
+
////////////////////////////////////////////////////////////
- // instances of NetVConnection should be allocated //
+ // Instances of NetVConnection should be allocated //
// only from the free list using NetVConnection::alloc(). //
- // The constructo is public just to avoid compile errors. //
+ // The constructor is public just to avoid compile errors.//
////////////////////////////////////////////////////////////
SSLNetVConnection();
+ virtual ~SSLNetVConnection() { }
+
SSL *ssl;
X509 *client_cert;
X509 *server_cert;
+ static int advertise_next_protocol(SSL *ssl,
+ const unsigned char **out, unsigned int *outlen, void *arg);
+
+ Continuation * endpoint() const {
+ return npnEndpoint;
+ }
+
private:
- bool sslHandShakeComplete;
- bool sslClientConnection;
SSLNetVConnection(const SSLNetVConnection &);
SSLNetVConnection & operator =(const SSLNetVConnection &);
+
+ bool sslHandShakeComplete;
+ bool sslClientConnection;
+ const SSLNextProtocolSet * npnSet;
+ Continuation * npnEndpoint;
};
typedef int (SSLNetVConnection::*SSLNetVConnHandler) (int, void *);
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c85a874f/iocore/net/P_SSLNextProtocolAccept.h
----------------------------------------------------------------------
diff --git a/iocore/net/P_SSLNextProtocolAccept.h b/iocore/net/P_SSLNextProtocolAccept.h
new file mode 100644
index 0000000..7c44bbc
--- /dev/null
+++ b/iocore/net/P_SSLNextProtocolAccept.h
@@ -0,0 +1,61 @@
+/** @file
+
+ SSLNextProtocolAccept
+
+ @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.
+ */
+
+#ifndef P_SSLNextProtocolAccept_H_
+#define P_SSLNextProtocolAccept_H_
+
+#include "P_Net.h"
+#include "P_EventSystem.h"
+#include "P_UnixNet.h"
+#include "P_SSLNetVConnection.h"
+#include "P_SSLNextProtocolSet.h"
+#include "I_IOBuffer.h"
+
+class SSLNextProtocolAccept: public Continuation
+{
+public:
+ SSLNextProtocolAccept(Continuation *);
+ ~SSLNextProtocolAccept();
+
+ // Register handler as an endpoint for the specified protocol. Neither
+ // handler nor protocol are copied, so the caller must guarantee their
+ // lifetime is at least as long as that of the acceptor.
+ bool registerEndpoint(const char * protocol, Continuation * handler);
+
+ // Unregister the handler. Returns false if this protocol is not registered
+ // or if it is not registered for the specified handler.
+ bool unregisterEndpoint(const char * protocol, Continuation * handler);
+
+ SLINK(SSLNextProtocolAccept, link);
+
+private:
+ int mainEvent(int event, void * netvc);
+ SSLNextProtocolAccept(const SSLNextProtocolAccept &); // disabled
+ SSLNextProtocolAccept& operator =(const SSLNextProtocolAccept&); // disabled
+
+ MIOBuffer * buffer; // XXX do we really need this?
+ Continuation * endpoint;
+ SSLNextProtocolSet protoset;
+};
+
+#endif /* P_SSLNextProtocolAccept_H_ */
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c85a874f/iocore/net/P_SSLNextProtocolSet.h
----------------------------------------------------------------------
diff --git a/iocore/net/P_SSLNextProtocolSet.h b/iocore/net/P_SSLNextProtocolSet.h
new file mode 100644
index 0000000..98ba11b
--- /dev/null
+++ b/iocore/net/P_SSLNextProtocolSet.h
@@ -0,0 +1,68 @@
+/** @file
+
+ SSLNextProtocolSet
+
+ @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.
+ */
+
+#ifndef P_SSLNextProtocolSet_H_
+#define P_SSLNextProtocolSet_H_
+
+#include "List.h"
+
+class Continuation;
+
+class SSLNextProtocolSet
+{
+public:
+ SSLNextProtocolSet();
+ ~SSLNextProtocolSet();
+
+ bool registerEndpoint(const char *, Continuation *);
+ bool unregisterEndpoint(const char *, Continuation *);
+ bool advertiseProtocols(const unsigned char ** out, unsigned * len) const;
+
+ Continuation * findEndpoint(const char *) const;
+ Continuation * findEndpoint(const unsigned char *, unsigned) const;
+
+ struct NextProtocolEndpoint
+ {
+ // NOTE: the protocol and endpoint are NOT copied. The caller is
+ // responsible for ensuring their lifetime.
+ NextProtocolEndpoint(const char * protocol, Continuation * endpoint);
+ ~NextProtocolEndpoint();
+
+ const char * protocol;
+ Continuation * endpoint;
+ LINK(NextProtocolEndpoint, link);
+
+ typedef DLL<NextProtocolEndpoint> list_type;
+ };
+
+private:
+ SSLNextProtocolSet(const SSLNextProtocolSet&); // disabled
+ SSLNextProtocolSet& operator=(const SSLNextProtocolSet&); // disabled
+
+ mutable unsigned char * npn;
+ mutable size_t npnsz;
+
+ NextProtocolEndpoint::list_type endpoints;
+};
+
+#endif /* P_SSLNextProtocolSet_H_ */
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c85a874f/iocore/net/SSLNetProcessor.cc
----------------------------------------------------------------------
diff --git a/iocore/net/SSLNetProcessor.cc b/iocore/net/SSLNetProcessor.cc
index 826cf25..c9f848c 100644
--- a/iocore/net/SSLNetProcessor.cc
+++ b/iocore/net/SSLNetProcessor.cc
@@ -39,35 +39,6 @@ unsigned long SSL_pthreads_thread_id();
bool SSLNetProcessor::open_ssl_initialized = false;
-#if TS_USE_TLS_NPN
-static int
-npn_advertise_protocols(SSL *ssl,
- const unsigned char **out, unsigned int *outlen, void *arg)
-{
- static const unsigned char protocols[] =
- "\x08http/1.0"
- "\x08http/1.1";
-
- SSLNetProcessor * sslNetProc = (SSLNetProcessor *)arg;
-
- // XXX: At some point we need to figure out how to know which protocols to
- // advertise.
- (void)sslNetProc;
-
- // For currently defined protocol strings,
- // see http://technotes.googlecode.com/git/nextprotoneg.html. The OpenSSL
- // documentation tells us to return a string in "wire format". The draft NPN
- // RFC helpfuly refuses to document the wire format. The above link says we
- // need to send length-prefixed strings, but does not say how many bytes the
- // length is. Nice.
- *out = protocols;
- *outlen = sizeof(protocols) - 1;
-
- // Successful return tells OpenSSL to advertise.
- return SSL_TLSEXT_ERR_OK;
-}
-#endif /* TS_USE_TLS_NPN */
-
static int
SSL_CTX_add_extra_chain_cert_file(SSL_CTX * ctx, const char *file)
{
@@ -397,7 +368,8 @@ SSLNetProcessor::initSSLServerCTX(SSL_CTX * lCtx, const SslConfigParams * param,
}
#if TS_USE_TLS_NPN
- SSL_CTX_set_next_protos_advertised_cb(lCtx, npn_advertise_protocols, this);
+ SSL_CTX_set_next_protos_advertised_cb(lCtx,
+ SSLNetVConnection::advertise_next_protocol, this);
#endif /* TS_USE_TLS_NPN */
return 0;
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c85a874f/iocore/net/SSLNetVConnection.cc
----------------------------------------------------------------------
diff --git a/iocore/net/SSLNetVConnection.cc b/iocore/net/SSLNetVConnection.cc
index ff7df1d..7d7597a 100644
--- a/iocore/net/SSLNetVConnection.cc
+++ b/iocore/net/SSLNetVConnection.cc
@@ -22,6 +22,7 @@
*/
#include "ink_config.h"
#include "P_Net.h"
+#include "P_SSLNextProtocolSet.h"
#define SSL_READ_ERROR_NONE 0
#define SSL_READ_ERROR 1
@@ -34,13 +35,26 @@
#define SSL_HANDSHAKE_WANT_ACCEPT 8
#define SSL_HANDSHAKE_WANT_CONNECT 9
#define SSL_WRITE_WOULD_BLOCK 10
-ClassAllocator<SSLNetVConnection> sslNetVCAllocator("sslNetVCAllocator");
+ClassAllocator<SSLNetVConnection> sslNetVCAllocator("sslNetVCAllocator");
//
// Private
//
+static SSL *
+make_ssl_connection(SSL_CTX * ctx, SSLNetVConnection * netvc)
+{
+ SSL * ssl;
+
+ if (likely(ssl = SSL_new(ctx))) {
+ SSL_set_fd(ssl, netvc->get_socket());
+ SSL_set_app_data(ssl, netvc);
+ }
+
+ return ssl;
+}
+
static inline int
do_SSL_write(SSL * ssl, void *buf, int size)
{
@@ -408,7 +422,13 @@ SSLNetVConnection::SSLNetVConnection():
write_want_write(0),
write_want_read(0),
write_want_ssl(0),
- write_want_syscal(0), write_want_x509(0), write_error_zero(0), sslHandShakeComplete(false), sslClientConnection(false)
+ write_want_syscal(0),
+ write_want_x509(0),
+ write_error_zero(0),
+ sslHandShakeComplete(false),
+ sslClientConnection(false),
+ npnSet(0),
+ npnEndpoint(0)
{
ssl = NULL;
}
@@ -472,26 +492,25 @@ SSLNetVConnection::free(EThread * t) {
int
SSLNetVConnection::sslStartHandShake(int event, int &err)
{
- SSL_CTX *ctx = NULL;
ts_ip_endpoint ip;
int namelen = sizeof(ip);
if (event == SSL_EVENT_SERVER) {
+ SSL_CTX *ctx = ssl_NetProcessor.ctx;
+
if (ssl == NULL) {
if (sslCertLookup.hasMultipleCerts()) {
char buff[INET6_ADDRSTRLEN];
safe_getsockname(get_socket(), &ip.sa, &namelen);
ink_inet_ntop(&ip.sa, buff, sizeof(buff));
ctx = sslCertLookup.findInfoInHash(buff);
- if (ctx == NULL)
+ if (ctx == NULL) {
ctx = ssl_NetProcessor.ctx;
- } else {
- ctx = ssl_NetProcessor.ctx;
+ }
}
- ssl = SSL_new(ctx);
- if (ssl != NULL) {
- SSL_set_fd(ssl, get_socket());
- } else {
+
+ ssl = make_ssl_connection(ctx, this);
+ if (ssl == NULL) {
Debug("ssl", "SSLNetVConnection::sslServerHandShakeEvent, ssl create failed");
SSLNetProcessor::logSSLError("SSL_StartHandShake");
return EVENT_ERROR;
@@ -501,8 +520,7 @@ SSLNetVConnection::sslStartHandShake(int event, int &err)
return (sslServerHandShakeEvent(err));
} else {
if (ssl == NULL) {
- ssl = SSL_new(ssl_NetProcessor.client_ctx);
- SSL_set_fd(ssl, get_socket());
+ ssl = make_ssl_connection(ssl_NetProcessor.client_ctx, this);
}
ink_assert(event == SSL_EVENT_CLIENT);
return (sslClientHandShakeEvent(err));
@@ -549,6 +567,11 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)
} else {
Debug("ssl", "client did not select a next protocol");
}
+
+ if (len && this->npnSet) {
+ this->npnEndpoint = this->npnSet->findEndpoint(proto, len);
+ this->npnSet = NULL;
+ }
}
#endif /* TS_USE_TLS_NPN */
@@ -667,3 +690,26 @@ SSLNetVConnection::sslClientHandShakeEvent(int &err)
return EVENT_CONT;
}
+
+void
+SSLNetVConnection::registerNextProtocolSet(const SSLNextProtocolSet * s)
+{
+ ink_release_assert(this->npnSet == NULL);
+ this->npnSet = s;
+}
+
+int
+SSLNetVConnection::advertise_next_protocol(
+ SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg)
+{
+ SSLNetVConnection * netvc = (SSLNetVConnection *)SSL_get_app_data(ssl);
+
+ ink_release_assert(netvc != NULL);
+
+ if (netvc->npnSet && netvc->npnSet->advertiseProtocols(out, outlen)) {
+ // Successful return tells OpenSSL to advertise.
+ return SSL_TLSEXT_ERR_OK;
+ }
+
+ return SSL_TLSEXT_ERR_NOACK;
+}
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c85a874f/iocore/net/SSLNextProtocolAccept.cc
----------------------------------------------------------------------
diff --git a/iocore/net/SSLNextProtocolAccept.cc b/iocore/net/SSLNextProtocolAccept.cc
new file mode 100644
index 0000000..963fd7e
--- /dev/null
+++ b/iocore/net/SSLNextProtocolAccept.cc
@@ -0,0 +1,118 @@
+/** @file
+
+ SSLNextProtocolAccept
+
+ @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 "P_SSLNextProtocolAccept.h"
+
+static void
+send_plugin_event(Continuation * plugin, int event, void * edata)
+{
+ if (likely(plugin)) {
+ EThread * thread(this_ethread());
+ MUTEX_TAKE_LOCK(plugin->mutex, thread);
+ plugin->handleEvent(event, edata);
+ MUTEX_UNTAKE_LOCK(plugin->mutex, thread);
+ }
+}
+
+static SSLNetVConnection *
+ssl_netvc_cast(int event, void * edata)
+{
+ union {
+ VIO * vio;
+ NetVConnection * vc;
+ } ptr;
+
+ switch (event) {
+ case NET_EVENT_ACCEPT:
+ ptr.vc = static_cast<NetVConnection *>(edata);
+ return dynamic_cast<SSLNetVConnection *>(ptr.vc);
+ case VC_EVENT_READ_COMPLETE:
+ ptr.vio = static_cast<VIO *>(edata);
+ return dynamic_cast<SSLNetVConnection *>(ptr.vio->vc_server);
+ default:
+ return NULL;
+ }
+}
+
+int
+SSLNextProtocolAccept::mainEvent(int event, void * edata)
+{
+ SSLNetVConnection * netvc = ssl_netvc_cast(event, edata);
+ Continuation * plugin;
+
+ Debug("ssl",
+ "[SSLNextProtocolAccept:mainEvent] event %d netvc %p", event, netvc);
+
+
+ switch (event) {
+ case NET_EVENT_ACCEPT:
+ ink_release_assert(netvc != NULL);
+ // Register our protocol set with the VC and kick off a zero-length read to
+ // force the SSLNetVConnection to complete the SSL handshake. Don't tell
+ // the endpoint that there is an accept to handle until the read completes
+ // and we know which protocol was negotiated.
+ netvc->registerNextProtocolSet(&this->protoset);
+ netvc->do_io(VIO::READ, this, 0, this->buffer, 0);
+ return EVENT_CONT;
+ case VC_EVENT_READ_COMPLETE:
+ ink_release_assert(netvc != NULL);
+ plugin = netvc->endpoint();
+ if (plugin) {
+ send_plugin_event(plugin, NET_EVENT_ACCEPT, netvc);
+ } else if (this->endpoint) {
+ // Route to the default endpoint
+ send_plugin_event(this->endpoint, NET_EVENT_ACCEPT, netvc);
+ } else {
+ // No handler, what should we do? Best to just kill the VC while we can.
+ netvc->do_io(VIO::CLOSE);
+ }
+ return EVENT_CONT;
+ default:
+ return EVENT_DONE;
+ }
+}
+
+bool
+SSLNextProtocolAccept::registerEndpoint(
+ const char * protocol, Continuation * handler)
+{
+ return this->protoset.registerEndpoint(protocol, handler);
+}
+
+bool
+SSLNextProtocolAccept::unregisterEndpoint(
+ const char * protocol, Continuation * handler)
+{
+ return this->protoset.unregisterEndpoint(protocol, handler);
+}
+
+SSLNextProtocolAccept::SSLNextProtocolAccept(Continuation * ep)
+ : Continuation(new_ProxyMutex()), buffer(new_empty_MIOBuffer()), endpoint(ep)
+{
+ SET_HANDLER(&SSLNextProtocolAccept::mainEvent);
+}
+
+SSLNextProtocolAccept::~SSLNextProtocolAccept()
+{
+ free_MIOBuffer(this->buffer);
+}
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c85a874f/iocore/net/SSLNextProtocolSet.cc
----------------------------------------------------------------------
diff --git a/iocore/net/SSLNextProtocolSet.cc b/iocore/net/SSLNextProtocolSet.cc
new file mode 100644
index 0000000..15aaf8d
--- /dev/null
+++ b/iocore/net/SSLNextProtocolSet.cc
@@ -0,0 +1,182 @@
+/** @file
+
+ SSLNextProtocolSet
+
+ @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 "ink_config.h"
+#include "libts.h"
+#include "P_SSLNextProtocolSet.h"
+
+// For currently defined protocol strings, see
+// http://technotes.googlecode.com/git/nextprotoneg.html. The OpenSSL
+// documentation tells us to return a string in "wire format". The
+// draft NPN RFC helpfuly refuses to document the wire format. The
+// above link says we need to send length-prefixed strings, but does
+// not say how many bytes the length is. For the record, it's 1.
+
+unsigned char *
+append_protocol(const char * proto, unsigned char * buf)
+{
+ size_t sz = strlen(proto);
+ *buf++ = (unsigned char)sz;
+ memcpy(buf, proto, sz);
+ return buf + sz;
+}
+
+static bool
+create_npn_advertisement(
+ const SSLNextProtocolSet::NextProtocolEndpoint::list_type& endpoints,
+ unsigned char ** npn, size_t * len)
+{
+ const SSLNextProtocolSet::NextProtocolEndpoint * ep;
+ unsigned char * advertised;
+
+ *npn = NULL;
+ *len = 0;
+
+ for (ep = endpoints.head; ep != NULL; ep = endpoints.next(ep)) {
+ *len += (strlen(ep->protocol) + 1);
+ }
+
+ *npn = advertised = (unsigned char *)ats_malloc(*len);
+ if (!(*npn)) {
+ goto fail;
+ }
+
+ for (ep = endpoints.head; ep != NULL; ep = endpoints.next(ep)) {
+ advertised = append_protocol(ep->protocol, advertised);
+ }
+
+ return true;
+
+fail:
+ ats_free(*npn);
+ *npn = NULL;
+ *len = 0;
+ return false;
+}
+
+bool
+SSLNextProtocolSet::advertiseProtocols(const unsigned char ** out, unsigned * len) const
+{
+ if (!npn && !this->endpoints.empty()) {
+ create_npn_advertisement(this->endpoints, &npn, &npnsz);
+ }
+
+ if (npn && npnsz) {
+ *out = npn;
+ *len = npnsz;
+ Debug("ssl", "advertised NPN set %.*s", (int)npnsz, npn);
+ return true;
+ }
+
+ return false;
+}
+
+bool
+SSLNextProtocolSet::registerEndpoint(const char * proto, Continuation * ep)
+{
+ // Once we start advertising, the set is closed. We need to hand an immutable
+ // string down into OpenSSL, and there is no mechanism to tell us when it's
+ // done with it so we have to keep it forever.
+ if (this->npn) {
+ return false;
+ }
+
+ if (strlen(proto) > 255) {
+ return false;
+ }
+
+ if (findEndpoint(proto) == NULL) {
+ this->endpoints.push(NEW(new NextProtocolEndpoint(proto, ep)));
+ return true;
+ }
+
+ return false;
+}
+
+bool
+SSLNextProtocolSet::unregisterEndpoint(const char * proto, Continuation * ep)
+{
+
+ for (NextProtocolEndpoint * e = this->endpoints.head;
+ e; e = this->endpoints.next(e)) {
+ if (strcmp(proto, e->protocol) == 0 && e->endpoint == ep) {
+ // Protocol must be registered only once; no need to remove
+ // any more entries.
+ this->endpoints.remove(e);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+Continuation *
+SSLNextProtocolSet::findEndpoint(const unsigned char * proto, unsigned len) const
+{
+ for (const NextProtocolEndpoint * ep = this->endpoints.head;
+ ep != NULL; ep = this->endpoints.next(ep)) {
+ size_t sz = strlen(ep->protocol);
+ if (sz == len && memcmp(ep->protocol, proto, len) == 0) {
+ return ep->endpoint;
+ }
+ }
+
+ return NULL;
+}
+
+Continuation *
+SSLNextProtocolSet::findEndpoint(const char * proto) const
+{
+ for (const NextProtocolEndpoint * ep = this->endpoints.head;
+ ep != NULL; ep = this->endpoints.next(ep)) {
+ if (strcmp(proto, ep->protocol) == 0) {
+ return ep->endpoint;
+ }
+ }
+
+ return NULL;
+}
+
+SSLNextProtocolSet::SSLNextProtocolSet()
+ : npn(0), npnsz(0)
+{
+}
+
+SSLNextProtocolSet::~SSLNextProtocolSet()
+{
+ ats_free(this->npn);
+
+ for (NextProtocolEndpoint * ep; (ep = this->endpoints.pop());) {
+ delete ep;
+ }
+}
+
+SSLNextProtocolSet::NextProtocolEndpoint::NextProtocolEndpoint(
+ const char * proto, Continuation * ep)
+ : protocol(proto), endpoint(ep)
+{
+}
+
+SSLNextProtocolSet::NextProtocolEndpoint::~NextProtocolEndpoint()
+{
+}
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c85a874f/lib/ts/List.h
----------------------------------------------------------------------
diff --git a/lib/ts/List.h b/lib/ts/List.h
index 5ece3e0..e72358c 100644
--- a/lib/ts/List.h
+++ b/lib/ts/List.h
@@ -73,6 +73,7 @@ template <class C> class SLink {
};
#define SLINK(_c,_f) class Link##_##_f : public SLink<_c> { public: \
static _c *& next_link(_c *c) { return c->_f.next; } \
+ static const _c * next_link(const _c *c) { return c->_f.next; } \
}; SLink<_c> _f
#define SLINKM(_c,_m,_f) class Link##_##_m##_##_f : public SLink<_c> { public: \
static _c *& next_link(_c *c) { return c->_m._f.next; } \
@@ -88,6 +89,8 @@ template <class C> struct Link : public SLink<C> {
#define LINK(_c,_f) class Link##_##_f : public Link<_c> { public: \
static _c *& next_link(_c *c) { return c->_f.next; } \
static _c *& prev_link(_c *c) { return c->_f.prev; } \
+ static const _c * next_link(const _c *c) { return c->_f.next; } \
+ static const _c * prev_link(const _c *c) { return c->_f.prev; } \
}; Link<_c> _f
#define LINKM(_c,_m,_f) class Link##_##_m##_##_f : public Link<_c> { public: \
static _c *& next_link(_c *c) { return c->_m._f.next; } \
@@ -107,11 +110,12 @@ template <class C> struct Link : public SLink<C> {
template <class C, class L = typename C::Link_link> class SLL {
public:
C *head;
- bool empty() { return head == NULL; }
+ bool empty() const { return head == NULL; }
void push(C *e);
C *pop();
void clear() { head = NULL; }
C *& next(C *e) { return L::next_link(e); }
+ const C * next(const C *e) const { return L::next_link(e); }
SLL() : head(NULL) {}
SLL(C *c) : head(c) {}
@@ -141,7 +145,7 @@ SLL<C,L>::pop() {
//
template <class C, class L = typename C::Link_link> struct DLL {
C *head;
- bool empty() { return head == NULL; }
+ bool empty() const { return head == NULL; }
void push(C *e);
C *pop();
void remove(C *e);
@@ -150,6 +154,8 @@ template <class C, class L = typename C::Link_link> struct DLL {
void clear() { head = NULL; }
C *&next(C *e) { return *(C**)&L::next_link(e); }
C *&prev(C *e) { return *(C**)&L::prev_link(e); }
+ const C *next(const C *e) const { return L::next_link(e); }
+ const C *prev(const C *e) const { return L::prev_link(e); }
DLL() : head(NULL) {}
};
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c85a874f/lib/ts/ink_mutex.h
----------------------------------------------------------------------
diff --git a/lib/ts/ink_mutex.h b/lib/ts/ink_mutex.h
index 5919704..f54b91a 100644
--- a/lib/ts/ink_mutex.h
+++ b/lib/ts/ink_mutex.h
@@ -126,4 +126,18 @@ int ink_ProcessMutex_try_acquire(ProcessMutex *);
void ink_ProcessMutex_print(FILE * out, ProcessMutex *);
+struct ink_scoped_mutex
+{
+ explicit ink_scoped_mutex(ink_mutex& m) : mtx(m) {
+ ink_mutex_acquire(&mtx);
+ }
+
+ ~ink_scoped_mutex() {
+ ink_mutex_release(&mtx);
+ }
+
+private:
+ ink_mutex& mtx;
+};
+
#endif /* _ink_mutex_h_ */
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c85a874f/proxy/InkAPI.cc
----------------------------------------------------------------------
diff --git a/proxy/InkAPI.cc b/proxy/InkAPI.cc
index 89ce991..679fbe1 100644
--- a/proxy/InkAPI.cc
+++ b/proxy/InkAPI.cc
@@ -357,6 +357,14 @@ tsapi int TS_HTTP_LEN_PURGE;
tsapi int TS_HTTP_LEN_PUT;
tsapi int TS_HTTP_LEN_TRACE;
+/* TLS Next Protocol well-known protocol names. */
+
+tsapi const char * TS_NPN_PROTOCOL_HTTP_1_0 = "http/1.0";
+tsapi const char * TS_NPN_PROTOCOL_HTTP_1_1 = "http/1.1";
+tsapi const char * TS_NPN_PROTOCOL_SPDY_1 = "spdy/1"; // obsolete
+tsapi const char * TS_NPN_PROTOCOL_SPDY_2 = "spdy/2"; // shipping
+tsapi const char * TS_NPN_PROTOCOL_SPDY_3 = "spdy/3"; // upcoming
+
/* MLoc Constants */
tsapi const TSMLoc TS_NULL_MLOC = (TSMLoc)NULL;
@@ -6409,6 +6417,25 @@ TSNetAccept(TSCont contp, int port, int domain, int accept_threads)
return (TSAction)netProcessor.accept(i, opt);
}
+/* From proxy/http/HttpProxyServerMain.c: */
+extern bool ssl_register_protocol(const char *, Continuation *);
+extern bool ssl_unregister_protocol(const char *, Continuation *);
+
+TSReturnCode
+TSNetAcceptNamedProtocol(TSCont contp, const char * protocol)
+{
+ sdk_assert(protocol != NULL);
+ sdk_assert(contp != NULL);
+ sdk_assert(sdk_sanity_check_continuation(contp) == TS_SUCCESS);
+
+ if (!ssl_register_protocol(protocol, (INKContInternal *)contp)) {
+ ssl_unregister_protocol(protocol, (INKContInternal *)contp);
+ return TS_ERROR;
+ }
+
+ return TS_SUCCESS;
+}
+
/* DNS Lookups */
TSAction
TSHostLookup(TSCont contp, const char *hostname, size_t namelen)
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c85a874f/proxy/api/ts/ts.h.in
----------------------------------------------------------------------
diff --git a/proxy/api/ts/ts.h.in b/proxy/api/ts/ts.h.in
index 341290d..6c676ed 100644
--- a/proxy/api/ts/ts.h.in
+++ b/proxy/api/ts/ts.h.in
@@ -970,6 +970,14 @@ extern "C"
extern tsapi int TS_HTTP_LEN_TRACE;
/* --------------------------------------------------------------------------
+ TLS Next Protocol well-known protocol names. */
+ extern tsapi const char * TS_NPN_PROTOCOL_HTTP_1_0;
+ extern tsapi const char * TS_NPN_PROTOCOL_HTTP_1_1;
+ extern tsapi const char * TS_NPN_PROTOCOL_SPDY_1;
+ extern tsapi const char * TS_NPN_PROTOCOL_SPDY_2;
+ extern tsapi const char * TS_NPN_PROTOCOL_SPDY_3;
+
+ /* --------------------------------------------------------------------------
MLoc Constants */
/**
Use TS_NULL_MLOC as the parent in calls that require a parent
@@ -2533,6 +2541,29 @@ extern "C"
tsapi TSAction TSNetAccept(TSCont contp, int port, int domain, int accept_threads);
+ /**
+ Listen on all SSL ports for connections for the specified protocol name.
+
+ TSNetAcceptNamedProtocol registers the specified protocol for all
+ statically configured TLS ports. When a client using the TLS Next Protocol
+ Negotiation extension negotiates the requested protocol, TrafficServer will
+ route the request to the given handler. Note that the protocol is not
+ registered on ports opened by other plugins.
+
+ The event and data provided to the handler are the same as for
+ TSNetAccept(). If a connection is successfully accepted, the event code
+ will be TS_EVENT_NET_ACCEPT and the event data will be a valid TSVConn
+ bound to the accepted connection.
+
+ Neither contp nor protocol are copied. They must remain valid for the
+ lifetime of the plugin.
+
+ TSNetAcceptNamedProtocol fails if the requested protocol cannot be
+ registered on all of the configured TLS ports. If it fails, the protocol
+ will not be registered on any ports (ie.. no partial failure).
+ */
+ tsapi TSReturnCode TSNetAcceptNamedProtocol(TSCont contp, const char * protocol);
+
/* --------------------------------------------------------------------------
DNS Lookups */
tsapi TSAction TSHostLookup(TSCont contp, const char* hostname, size_t namelen);
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c85a874f/proxy/http/HttpAccept.h
----------------------------------------------------------------------
diff --git a/proxy/http/HttpAccept.h b/proxy/http/HttpAccept.h
index af3584b..d5f659a 100644
--- a/proxy/http/HttpAccept.h
+++ b/proxy/http/HttpAccept.h
@@ -147,7 +147,7 @@ public:
typedef detail::HttpAcceptOptions Options;
HttpAccept(Options const& opt = DEFAULT_OPTIONS)
- : Continuation(0)
+ : Continuation(new_ProxyMutex())
, detail::HttpAcceptOptions(opt) // copy these.
{
SET_HANDLER(&HttpAccept::mainEvent);
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/c85a874f/proxy/http/HttpProxyServerMain.cc
----------------------------------------------------------------------
diff --git a/proxy/http/HttpProxyServerMain.cc b/proxy/http/HttpProxyServerMain.cc
index 407918b..5ebbb22 100644
--- a/proxy/http/HttpProxyServerMain.cc
+++ b/proxy/http/HttpProxyServerMain.cc
@@ -34,6 +34,7 @@
#include "HttpPages.h"
#include "HttpTunnel.h"
#include "Tokenizer.h"
+#include "P_SSLNextProtocolAccept.h"
#ifdef DEBUG
extern "C"
@@ -76,6 +77,42 @@ struct DumpStats: public Continuation
HttpAccept *plugin_http_accept = NULL;
HttpAccept *plugin_http_transparent_accept = 0;
+#if !defined(TS_NO_API)
+static SLL<SSLNextProtocolAccept> ssl_plugin_acceptors;
+static ProcessMutex ssl_plugin_mutex;
+
+bool
+ssl_register_protocol(const char * protocol, Continuation * contp)
+{
+ ink_scoped_mutex lock(ssl_plugin_mutex);
+
+ for (SSLNextProtocolAccept * ssl = ssl_plugin_acceptors.head;
+ ssl; ssl = ssl_plugin_acceptors.next(ssl)) {
+ if (!ssl->registerEndpoint(protocol, contp)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+ssl_unregister_protocol(const char * protocol, Continuation * contp)
+{
+ ink_scoped_mutex lock(ssl_plugin_mutex);
+
+ for (SSLNextProtocolAccept * ssl = ssl_plugin_acceptors.head;
+ ssl; ssl = ssl_plugin_acceptors.next(ssl)) {
+ // Ignore possible failure because we want to try to unregister
+ // from all SSL ports.
+ ssl->unregisterEndpoint(protocol, contp);
+ }
+
+ return true;
+}
+
+#endif /* !defined(TS_NO_API) */
+
/////////////////////////////////////////////////////////////////
//
// main()
@@ -113,6 +150,7 @@ init_HttpProxyServer(void)
plugin_http_transparent_accept = NEW(new HttpAccept(ha_opt));
plugin_http_transparent_accept->mutex = new_ProxyMutex();
}
+ ink_mutex_init(&ssl_plugin_mutex, "SSL Acceptor List");
#endif
}
@@ -170,7 +208,17 @@ start_HttpProxyServer(int accept_threads)
if (HttpProxyPort::TRANSPORT_SSL == p.m_type) {
if (sslParam->getTerminationMode() & sslParam->SSL_TERM_MODE_CLIENT) {
- sslNetProcessor.main_accept(NEW(new HttpAccept(ha_opt)), p.m_fd, opt);
+ HttpAccept * http = NEW(new HttpAccept(ha_opt));
+ SSLNextProtocolAccept * ssl = NEW(new SSLNextProtocolAccept(http));
+ ssl->registerEndpoint(TS_NPN_PROTOCOL_HTTP_1_0, http);
+ ssl->registerEndpoint(TS_NPN_PROTOCOL_HTTP_1_1, http);
+
+#ifndef TS_NO_API
+ ink_scoped_mutex lock(ssl_plugin_mutex);
+ ssl_plugin_acceptors.push(ssl);
+#endif
+
+ sslNetProcessor.main_accept(ssl, p.m_fd, opt);
}
} else {
netProcessor.main_accept(NEW(new HttpAccept(ha_opt)), p.m_fd, opt);