Index: net/socket/ssl_client_socket_openssl.cc |
=================================================================== |
--- net/socket/ssl_client_socket_openssl.cc (revision 68726) |
+++ net/socket/ssl_client_socket_openssl.cc (working copy) |
@@ -311,6 +311,13 @@ |
SSL_CTX_set_timeout(ssl_ctx_.get(), kSessionCacheTimeoutSeconds); |
SSL_CTX_sess_set_cache_size(ssl_ctx_.get(), kSessionCacheMaxEntires); |
SSL_CTX_set_client_cert_cb(ssl_ctx_.get(), ClientCertCallback); |
+#if defined(OPENSSL_NPN_NEGOTIATED) |
+ // TODO(kristianm): Only select this if ssl_config_.next_proto is not empty. |
+ // It would be better if the callback were not a global setting, |
+ // but that is an OpenSSL issue. |
+ SSL_CTX_set_next_proto_select_cb(ssl_ctx_.get(), SelectNextProtoCallback, |
+ NULL); |
+#endif |
} |
static int NewSessionCallbackStatic(SSL* ssl, SSL_SESSION* session) { |
@@ -338,6 +345,14 @@ |
return socket->ClientCertRequestCallback(ssl, x509, pkey); |
} |
+ static int SelectNextProtoCallback(SSL* ssl, |
+ unsigned char** out, unsigned char* outlen, |
+ const unsigned char* in, |
+ unsigned int inlen, void* arg) { |
+ SSLClientSocketOpenSSL* socket = Get()->GetClientSocketFromSSL(ssl); |
+ return socket->SelectNextProtoCallback(out, outlen, in, inlen); |
+ } |
+ |
// This is the index used with SSL_get_ex_data to retrieve the owner |
// SSLClientSocketOpenSSL object from an SSL instance. |
int ssl_socket_data_index_; |
@@ -384,6 +399,7 @@ |
host_and_port_(host_and_port), |
ssl_config_(ssl_config), |
trying_cached_session_(false), |
+ npn_status_(kNextProtoUnsupported), |
net_log_(transport_socket->socket()->NetLog()) { |
} |
@@ -567,8 +583,8 @@ |
SSLClientSocket::NextProtoStatus SSLClientSocketOpenSSL::GetNextProto( |
std::string* proto) { |
- proto->clear(); |
- return kNextProtoUnsupported; |
+ *proto = npn_proto_; |
+ return npn_status_; |
} |
void SSLClientSocketOpenSSL::DoReadCallback(int rv) { |
@@ -742,6 +758,42 @@ |
return net_error; |
} |
+int SSLClientSocketOpenSSL::SelectNextProtoCallback(unsigned char** out, |
+ unsigned char* outlen, |
+ const unsigned char* in, |
+ unsigned int inlen) { |
+#if defined(OPENSSL_NPN_NEGOTIATED) |
+ if (ssl_config_.next_protos.empty()) { |
+ *out = "http/1.1"; |
+ *outlen = 8; |
+ npn_status_ = SSLClientSocket::kNextProtoUnsupported; |
+ return SSL_TLSEXT_ERR_OK; |
+ } |
+ |
+ int status = SSL_select_next_proto( |
+ out, outlen, in, inlen, |
+ reinterpret_cast<const unsigned char*>(ssl_config_.next_protos.data()), |
+ ssl_config_.next_protos.size()); |
+ |
+ npn_proto_.assign(reinterpret_cast<const char*>(*out), *outlen); |
+ switch (status) { |
+ case OPENSSL_NPN_UNSUPPORTED: |
+ npn_status_ = SSLClientSocket::kNextProtoUnsupported; |
+ break; |
+ case OPENSSL_NPN_NEGOTIATED: |
+ npn_status_ = SSLClientSocket::kNextProtoNegotiated; |
+ break; |
+ case OPENSSL_NPN_NO_OVERLAP: |
+ npn_status_ = SSLClientSocket::kNextProtoNoOverlap; |
+ break; |
+ default: |
+ NOTREACHED() << status; |
+ break; |
+ } |
+#endif |
+ return SSL_TLSEXT_ERR_OK; |
+} |
+ |
int SSLClientSocketOpenSSL::DoVerifyCert(int result) { |
DCHECK(server_cert_); |
GotoState(STATE_VERIFY_CERT_COMPLETE); |