summaryrefslogtreecommitdiffstats
path: root/net/socket
diff options
context:
space:
mode:
authorrsleevi@chromium.org <rsleevi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-09 04:32:22 +0000
committerrsleevi@chromium.org <rsleevi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-09 04:32:22 +0000
commitb9b651f0e2f8d5ef82743af2c42180112613c05c (patch)
treee12e0feca2d04055a962a69f7036de20236f49c4 /net/socket
parent82591ee1bfca43a04e986eec374af9764279c17a (diff)
downloadchromium_src-b9b651f0e2f8d5ef82743af2c42180112613c05c.zip
chromium_src-b9b651f0e2f8d5ef82743af2c42180112613c05c.tar.gz
chromium_src-b9b651f0e2f8d5ef82743af2c42180112613c05c.tar.bz2
Style: Clean-up SSLClientSocketOpenSSL to match header declaration
This aligns SSLClientSocketOpenSSL with the rest of the net/ work that was done in http://crrev.com/71017 and http://crrev.com/52528. While cosmetic in nature, it is consistent with the rest of net/ BUG=none R=agl Review URL: https://codereview.chromium.org/65393002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@234090 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/socket')
-rw-r--r--net/socket/ssl_client_socket_openssl.cc1173
1 files changed, 584 insertions, 589 deletions
diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc
index a53eb52..1415075 100644
--- a/net/socket/ssl_client_socket_openssl.cc
+++ b/net/socket/ssl_client_socket_openssl.cc
@@ -465,6 +465,279 @@ SSLClientSocketOpenSSL::~SSLClientSocketOpenSSL() {
Disconnect();
}
+void SSLClientSocketOpenSSL::GetSSLCertRequestInfo(
+ SSLCertRequestInfo* cert_request_info) {
+ cert_request_info->host_and_port = host_and_port_.ToString();
+ cert_request_info->cert_authorities = cert_authorities_;
+}
+
+SSLClientSocket::NextProtoStatus SSLClientSocketOpenSSL::GetNextProto(
+ std::string* proto, std::string* server_protos) {
+ *proto = npn_proto_;
+ *server_protos = server_protos_;
+ return npn_status_;
+}
+
+ServerBoundCertService*
+SSLClientSocketOpenSSL::GetServerBoundCertService() const {
+ return server_bound_cert_service_;
+}
+
+int SSLClientSocketOpenSSL::ExportKeyingMaterial(
+ const base::StringPiece& label,
+ bool has_context, const base::StringPiece& context,
+ unsigned char* out, unsigned int outlen) {
+ crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
+
+ int rv = SSL_export_keying_material(
+ ssl_, out, outlen, const_cast<char*>(label.data()),
+ label.size(),
+ reinterpret_cast<unsigned char*>(const_cast<char*>(context.data())),
+ context.length(),
+ context.length() > 0);
+
+ if (rv != 1) {
+ int ssl_error = SSL_get_error(ssl_, rv);
+ LOG(ERROR) << "Failed to export keying material;"
+ << " returned " << rv
+ << ", SSL error code " << ssl_error;
+ return MapOpenSSLError(ssl_error, err_tracer);
+ }
+ return OK;
+}
+
+int SSLClientSocketOpenSSL::GetTLSUniqueChannelBinding(std::string* out) {
+ return ERR_NOT_IMPLEMENTED;
+}
+
+int SSLClientSocketOpenSSL::Connect(const CompletionCallback& callback) {
+ net_log_.BeginEvent(NetLog::TYPE_SSL_CONNECT);
+
+ // Set up new ssl object.
+ if (!Init()) {
+ int result = ERR_UNEXPECTED;
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_CONNECT, result);
+ return result;
+ }
+
+ // Set SSL to client mode. Handshake happens in the loop below.
+ SSL_set_connect_state(ssl_);
+
+ GotoState(STATE_HANDSHAKE);
+ int rv = DoHandshakeLoop(net::OK);
+ if (rv == ERR_IO_PENDING) {
+ user_connect_callback_ = callback;
+ } else {
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_CONNECT, rv);
+ }
+
+ return rv > OK ? OK : rv;
+}
+
+void SSLClientSocketOpenSSL::Disconnect() {
+ if (ssl_) {
+ // Calling SSL_shutdown prevents the session from being marked as
+ // unresumable.
+ SSL_shutdown(ssl_);
+ SSL_free(ssl_);
+ ssl_ = NULL;
+ }
+ if (transport_bio_) {
+ BIO_free_all(transport_bio_);
+ transport_bio_ = NULL;
+ }
+
+ // Shut down anything that may call us back.
+ verifier_.reset();
+ transport_->socket()->Disconnect();
+
+ // Null all callbacks, delete all buffers.
+ transport_send_busy_ = false;
+ send_buffer_ = NULL;
+ transport_recv_busy_ = false;
+ transport_recv_eof_ = false;
+ recv_buffer_ = NULL;
+
+ user_connect_callback_.Reset();
+ user_read_callback_.Reset();
+ user_write_callback_.Reset();
+ user_read_buf_ = NULL;
+ user_read_buf_len_ = 0;
+ user_write_buf_ = NULL;
+ user_write_buf_len_ = 0;
+
+ server_cert_verify_result_.Reset();
+ completed_handshake_ = false;
+
+ cert_authorities_.clear();
+ client_auth_cert_needed_ = false;
+}
+
+bool SSLClientSocketOpenSSL::IsConnected() const {
+ // If the handshake has not yet completed.
+ if (!completed_handshake_)
+ return false;
+ // If an asynchronous operation is still pending.
+ if (user_read_buf_.get() || user_write_buf_.get())
+ return true;
+
+ return transport_->socket()->IsConnected();
+}
+
+bool SSLClientSocketOpenSSL::IsConnectedAndIdle() const {
+ // If the handshake has not yet completed.
+ if (!completed_handshake_)
+ return false;
+ // If an asynchronous operation is still pending.
+ if (user_read_buf_.get() || user_write_buf_.get())
+ return false;
+ // If there is data waiting to be sent, or data read from the network that
+ // has not yet been consumed.
+ if (BIO_ctrl_pending(transport_bio_) > 0 ||
+ BIO_ctrl_wpending(transport_bio_) > 0) {
+ return false;
+ }
+
+ return transport_->socket()->IsConnectedAndIdle();
+}
+
+int SSLClientSocketOpenSSL::GetPeerAddress(IPEndPoint* addressList) const {
+ return transport_->socket()->GetPeerAddress(addressList);
+}
+
+int SSLClientSocketOpenSSL::GetLocalAddress(IPEndPoint* addressList) const {
+ return transport_->socket()->GetLocalAddress(addressList);
+}
+
+const BoundNetLog& SSLClientSocketOpenSSL::NetLog() const {
+ return net_log_;
+}
+
+void SSLClientSocketOpenSSL::SetSubresourceSpeculation() {
+ if (transport_.get() && transport_->socket()) {
+ transport_->socket()->SetSubresourceSpeculation();
+ } else {
+ NOTREACHED();
+ }
+}
+
+void SSLClientSocketOpenSSL::SetOmniboxSpeculation() {
+ if (transport_.get() && transport_->socket()) {
+ transport_->socket()->SetOmniboxSpeculation();
+ } else {
+ NOTREACHED();
+ }
+}
+
+bool SSLClientSocketOpenSSL::WasEverUsed() const {
+ if (transport_.get() && transport_->socket())
+ return transport_->socket()->WasEverUsed();
+
+ NOTREACHED();
+ return false;
+}
+
+bool SSLClientSocketOpenSSL::UsingTCPFastOpen() const {
+ if (transport_.get() && transport_->socket())
+ return transport_->socket()->UsingTCPFastOpen();
+
+ NOTREACHED();
+ return false;
+}
+
+bool SSLClientSocketOpenSSL::GetSSLInfo(SSLInfo* ssl_info) {
+ ssl_info->Reset();
+ if (!server_cert_.get())
+ return false;
+
+ ssl_info->cert = server_cert_verify_result_.verified_cert;
+ ssl_info->cert_status = server_cert_verify_result_.cert_status;
+ ssl_info->is_issued_by_known_root =
+ server_cert_verify_result_.is_issued_by_known_root;
+ ssl_info->public_key_hashes =
+ server_cert_verify_result_.public_key_hashes;
+ ssl_info->client_cert_sent =
+ ssl_config_.send_client_cert && ssl_config_.client_cert.get();
+ ssl_info->channel_id_sent = WasChannelIDSent();
+
+ RecordChannelIDSupport(server_bound_cert_service_,
+ channel_id_xtn_negotiated_,
+ ssl_config_.channel_id_enabled,
+ crypto::ECPrivateKey::IsSupported());
+
+ const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl_);
+ CHECK(cipher);
+ ssl_info->security_bits = SSL_CIPHER_get_bits(cipher, NULL);
+ const COMP_METHOD* compression = SSL_get_current_compression(ssl_);
+
+ ssl_info->connection_status = EncodeSSLConnectionStatus(
+ SSL_CIPHER_get_id(cipher),
+ compression ? compression->type : 0,
+ GetNetSSLVersion(ssl_));
+
+ bool peer_supports_renego_ext = !!SSL_get_secure_renegotiation_support(ssl_);
+ if (!peer_supports_renego_ext)
+ ssl_info->connection_status |= SSL_CONNECTION_NO_RENEGOTIATION_EXTENSION;
+ UMA_HISTOGRAM_ENUMERATION("Net.RenegotiationExtensionSupported",
+ implicit_cast<int>(peer_supports_renego_ext), 2);
+
+ if (ssl_config_.version_fallback)
+ ssl_info->connection_status |= SSL_CONNECTION_VERSION_FALLBACK;
+
+ ssl_info->handshake_type = SSL_session_reused(ssl_) ?
+ SSLInfo::HANDSHAKE_RESUME : SSLInfo::HANDSHAKE_FULL;
+
+ DVLOG(3) << "Encoded connection status: cipher suite = "
+ << SSLConnectionStatusToCipherSuite(ssl_info->connection_status)
+ << " version = "
+ << SSLConnectionStatusToVersion(ssl_info->connection_status);
+ return true;
+}
+
+int SSLClientSocketOpenSSL::Read(IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) {
+ user_read_buf_ = buf;
+ user_read_buf_len_ = buf_len;
+
+ int rv = DoReadLoop(OK);
+
+ if (rv == ERR_IO_PENDING) {
+ user_read_callback_ = callback;
+ } else {
+ user_read_buf_ = NULL;
+ user_read_buf_len_ = 0;
+ }
+
+ return rv;
+}
+
+int SSLClientSocketOpenSSL::Write(IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) {
+ user_write_buf_ = buf;
+ user_write_buf_len_ = buf_len;
+
+ int rv = DoWriteLoop(OK);
+
+ if (rv == ERR_IO_PENDING) {
+ user_write_callback_ = callback;
+ } else {
+ user_write_buf_ = NULL;
+ user_write_buf_len_ = 0;
+ }
+
+ return rv;
+}
+
+bool SSLClientSocketOpenSSL::SetReceiveBufferSize(int32 size) {
+ return transport_->socket()->SetReceiveBufferSize(size);
+}
+
+bool SSLClientSocketOpenSSL::SetSendBufferSize(int32 size) {
+ return transport_->socket()->SetSendBufferSize(size);
+}
+
bool SSLClientSocketOpenSSL::Init() {
DCHECK(!ssl_);
DCHECK(!transport_bio_);
@@ -587,189 +860,6 @@ bool SSLClientSocketOpenSSL::Init() {
return true;
}
-int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl,
- X509** x509,
- EVP_PKEY** pkey) {
- DVLOG(3) << "OpenSSL ClientCertRequestCallback called";
- DCHECK(ssl == ssl_);
- DCHECK(*x509 == NULL);
- DCHECK(*pkey == NULL);
-
- if (!ssl_config_.send_client_cert) {
- // First pass: we know that a client certificate is needed, but we do not
- // have one at hand.
- client_auth_cert_needed_ = true;
- STACK_OF(X509_NAME) *authorities = SSL_get_client_CA_list(ssl);
- for (int i = 0; i < sk_X509_NAME_num(authorities); i++) {
- X509_NAME *ca_name = (X509_NAME *)sk_X509_NAME_value(authorities, i);
- unsigned char* str = NULL;
- int length = i2d_X509_NAME(ca_name, &str);
- cert_authorities_.push_back(std::string(
- reinterpret_cast<const char*>(str),
- static_cast<size_t>(length)));
- OPENSSL_free(str);
- }
-
- return -1; // Suspends handshake.
- }
-
- // Second pass: a client certificate should have been selected.
- if (ssl_config_.client_cert.get()) {
- // A note about ownership: FetchClientCertPrivateKey() increments
- // the reference count of the EVP_PKEY. Ownership of this reference
- // is passed directly to OpenSSL, which will release the reference
- // using EVP_PKEY_free() when the SSL object is destroyed.
- OpenSSLClientKeyStore::ScopedEVP_PKEY privkey;
- if (OpenSSLClientKeyStore::GetInstance()->FetchClientCertPrivateKey(
- ssl_config_.client_cert.get(), &privkey)) {
- // TODO(joth): (copied from NSS) We should wait for server certificate
- // verification before sending our credentials. See http://crbug.com/13934
- *x509 = X509Certificate::DupOSCertHandle(
- ssl_config_.client_cert->os_cert_handle());
- *pkey = privkey.release();
- return 1;
- }
- LOG(WARNING) << "Client cert found without private key";
- }
-
- // Send no client certificate.
- return 0;
-}
-
-void SSLClientSocketOpenSSL::ChannelIDRequestCallback(SSL* ssl,
- EVP_PKEY** pkey) {
- DVLOG(3) << "OpenSSL ChannelIDRequestCallback called";
- DCHECK_EQ(ssl, ssl_);
- DCHECK(!*pkey);
-
- channel_id_xtn_negotiated_ = true;
- if (!channel_id_private_key_.size()) {
- channel_id_request_return_value_ =
- server_bound_cert_service_->GetOrCreateDomainBoundCert(
- host_and_port_.host(),
- &channel_id_private_key_,
- &channel_id_cert_,
- base::Bind(&SSLClientSocketOpenSSL::OnHandshakeIOComplete,
- base::Unretained(this)),
- &channel_id_request_handle_);
- if (channel_id_request_return_value_ != OK)
- return;
- }
-
- // Decode key.
- std::vector<uint8> encrypted_private_key_info;
- std::vector<uint8> subject_public_key_info;
- encrypted_private_key_info.assign(
- channel_id_private_key_.data(),
- channel_id_private_key_.data() + channel_id_private_key_.size());
- subject_public_key_info.assign(
- channel_id_cert_.data(),
- channel_id_cert_.data() + channel_id_cert_.size());
- scoped_ptr<crypto::ECPrivateKey> ec_private_key(
- crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo(
- ServerBoundCertService::kEPKIPassword,
- encrypted_private_key_info,
- subject_public_key_info));
- set_channel_id_sent(true);
- *pkey = EVP_PKEY_dup(ec_private_key->key());
-}
-
-// SSLClientSocket methods
-
-bool SSLClientSocketOpenSSL::GetSSLInfo(SSLInfo* ssl_info) {
- ssl_info->Reset();
- if (!server_cert_.get())
- return false;
-
- ssl_info->cert = server_cert_verify_result_.verified_cert;
- ssl_info->cert_status = server_cert_verify_result_.cert_status;
- ssl_info->is_issued_by_known_root =
- server_cert_verify_result_.is_issued_by_known_root;
- ssl_info->public_key_hashes =
- server_cert_verify_result_.public_key_hashes;
- ssl_info->client_cert_sent =
- ssl_config_.send_client_cert && ssl_config_.client_cert.get();
- ssl_info->channel_id_sent = WasChannelIDSent();
-
- RecordChannelIDSupport(server_bound_cert_service_,
- channel_id_xtn_negotiated_,
- ssl_config_.channel_id_enabled,
- crypto::ECPrivateKey::IsSupported());
-
- const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl_);
- CHECK(cipher);
- ssl_info->security_bits = SSL_CIPHER_get_bits(cipher, NULL);
- const COMP_METHOD* compression = SSL_get_current_compression(ssl_);
-
- ssl_info->connection_status = EncodeSSLConnectionStatus(
- SSL_CIPHER_get_id(cipher),
- compression ? compression->type : 0,
- GetNetSSLVersion(ssl_));
-
- bool peer_supports_renego_ext = !!SSL_get_secure_renegotiation_support(ssl_);
- if (!peer_supports_renego_ext)
- ssl_info->connection_status |= SSL_CONNECTION_NO_RENEGOTIATION_EXTENSION;
- UMA_HISTOGRAM_ENUMERATION("Net.RenegotiationExtensionSupported",
- implicit_cast<int>(peer_supports_renego_ext), 2);
-
- if (ssl_config_.version_fallback)
- ssl_info->connection_status |= SSL_CONNECTION_VERSION_FALLBACK;
-
- ssl_info->handshake_type = SSL_session_reused(ssl_) ?
- SSLInfo::HANDSHAKE_RESUME : SSLInfo::HANDSHAKE_FULL;
-
- DVLOG(3) << "Encoded connection status: cipher suite = "
- << SSLConnectionStatusToCipherSuite(ssl_info->connection_status)
- << " version = "
- << SSLConnectionStatusToVersion(ssl_info->connection_status);
- return true;
-}
-
-void SSLClientSocketOpenSSL::GetSSLCertRequestInfo(
- SSLCertRequestInfo* cert_request_info) {
- cert_request_info->host_and_port = host_and_port_.ToString();
- cert_request_info->cert_authorities = cert_authorities_;
-}
-
-int SSLClientSocketOpenSSL::ExportKeyingMaterial(
- const base::StringPiece& label,
- bool has_context, const base::StringPiece& context,
- unsigned char* out, unsigned int outlen) {
- crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
-
- int rv = SSL_export_keying_material(
- ssl_, out, outlen, const_cast<char*>(label.data()),
- label.size(),
- reinterpret_cast<unsigned char*>(const_cast<char*>(context.data())),
- context.length(),
- context.length() > 0);
-
- if (rv != 1) {
- int ssl_error = SSL_get_error(ssl_, rv);
- LOG(ERROR) << "Failed to export keying material;"
- << " returned " << rv
- << ", SSL error code " << ssl_error;
- return MapOpenSSLError(ssl_error, err_tracer);
- }
- return OK;
-}
-
-int SSLClientSocketOpenSSL::GetTLSUniqueChannelBinding(std::string* out) {
- return ERR_NOT_IMPLEMENTED;
-}
-
-SSLClientSocket::NextProtoStatus SSLClientSocketOpenSSL::GetNextProto(
- std::string* proto, std::string* server_protos) {
- *proto = npn_proto_;
- *server_protos = server_protos_;
- return npn_status_;
-}
-
-ServerBoundCertService*
-SSLClientSocketOpenSSL::GetServerBoundCertService() const {
- return server_bound_cert_service_;
-}
-
void SSLClientSocketOpenSSL::DoReadCallback(int rv) {
// Since Run may result in Read being called, clear |user_read_callback_|
// up front.
@@ -786,107 +876,19 @@ void SSLClientSocketOpenSSL::DoWriteCallback(int rv) {
base::ResetAndReturn(&user_write_callback_).Run(rv);
}
-// StreamSocket implementation.
-int SSLClientSocketOpenSSL::Connect(const CompletionCallback& callback) {
- net_log_.BeginEvent(NetLog::TYPE_SSL_CONNECT);
-
- // Set up new ssl object.
- if (!Init()) {
- int result = ERR_UNEXPECTED;
- net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_CONNECT, result);
- return result;
- }
-
- // Set SSL to client mode. Handshake happens in the loop below.
- SSL_set_connect_state(ssl_);
-
- GotoState(STATE_HANDSHAKE);
- int rv = DoHandshakeLoop(net::OK);
- if (rv == ERR_IO_PENDING) {
- user_connect_callback_ = callback;
- } else {
- net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_CONNECT, rv);
- }
-
- return rv > OK ? OK : rv;
-}
-
-void SSLClientSocketOpenSSL::Disconnect() {
- if (ssl_) {
- // Calling SSL_shutdown prevents the session from being marked as
- // unresumable.
- SSL_shutdown(ssl_);
- SSL_free(ssl_);
- ssl_ = NULL;
- }
- if (transport_bio_) {
- BIO_free_all(transport_bio_);
- transport_bio_ = NULL;
- }
-
- // Shut down anything that may call us back.
- verifier_.reset();
- transport_->socket()->Disconnect();
-
- // Null all callbacks, delete all buffers.
- transport_send_busy_ = false;
- send_buffer_ = NULL;
- transport_recv_busy_ = false;
- transport_recv_eof_ = false;
- recv_buffer_ = NULL;
-
- user_connect_callback_.Reset();
- user_read_callback_.Reset();
- user_write_callback_.Reset();
- user_read_buf_ = NULL;
- user_read_buf_len_ = 0;
- user_write_buf_ = NULL;
- user_write_buf_len_ = 0;
-
- server_cert_verify_result_.Reset();
- completed_handshake_ = false;
-
- cert_authorities_.clear();
- client_auth_cert_needed_ = false;
-}
-
-int SSLClientSocketOpenSSL::DoHandshakeLoop(int last_io_result) {
- int rv = last_io_result;
+bool SSLClientSocketOpenSSL::DoTransportIO() {
+ bool network_moved = false;
+ int rv;
+ // Read and write as much data as possible. The loop is necessary because
+ // Write() may return synchronously.
do {
- // Default to STATE_NONE for next state.
- // (This is a quirk carried over from the windows
- // implementation. It makes reading the logs a bit harder.)
- // State handlers can and often do call GotoState just
- // to stay in the current state.
- State state = next_handshake_state_;
- GotoState(STATE_NONE);
- switch (state) {
- case STATE_HANDSHAKE:
- rv = DoHandshake();
- break;
- case STATE_VERIFY_CERT:
- DCHECK(rv == OK);
- rv = DoVerifyCert(rv);
- break;
- case STATE_VERIFY_CERT_COMPLETE:
- rv = DoVerifyCertComplete(rv);
- break;
- case STATE_NONE:
- default:
- rv = ERR_UNEXPECTED;
- NOTREACHED() << "unexpected state" << state;
- break;
- }
-
- bool network_moved = DoTransportIO();
- if (network_moved && next_handshake_state_ == STATE_HANDSHAKE) {
- // In general we exit the loop if rv is ERR_IO_PENDING. In this
- // special case we keep looping even if rv is ERR_IO_PENDING because
- // the transport IO may allow DoHandshake to make progress.
- rv = OK; // This causes us to stay in the loop.
- }
- } while (rv != ERR_IO_PENDING && next_handshake_state_ != STATE_NONE);
- return rv;
+ rv = BufferSend();
+ if (rv != ERR_IO_PENDING && rv != 0)
+ network_moved = true;
+ } while (rv > 0);
+ if (!transport_recv_eof_ && BufferRecv() != ERR_IO_PENDING)
+ network_moved = true;
+ return network_moved;
}
int SSLClientSocketOpenSSL::DoHandshake() {
@@ -948,58 +950,6 @@ int SSLClientSocketOpenSSL::DoHandshake() {
return net_error;
}
-// SelectNextProtoCallback is called by OpenSSL during the handshake. If the
-// server supports NPN, selects a protocol from the list that the server
-// provides. According to third_party/openssl/openssl/ssl/ssl_lib.c, the
-// callback can assume that |in| is syntactically valid.
-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 = reinterpret_cast<uint8*>(
- const_cast<char*>(kDefaultSupportedNPNProtocol));
- *outlen = arraysize(kDefaultSupportedNPNProtocol) - 1;
- npn_status_ = kNextProtoUnsupported;
- return SSL_TLSEXT_ERR_OK;
- }
-
- // Assume there's no overlap between our protocols and the server's list.
- npn_status_ = kNextProtoNoOverlap;
-
- // For each protocol in server preference order, see if we support it.
- for (unsigned int i = 0; i < inlen; i += in[i] + 1) {
- for (std::vector<std::string>::const_iterator
- j = ssl_config_.next_protos.begin();
- j != ssl_config_.next_protos.end(); ++j) {
- if (in[i] == j->size() &&
- memcmp(&in[i + 1], j->data(), in[i]) == 0) {
- // We found a match.
- *out = const_cast<unsigned char*>(in) + i + 1;
- *outlen = in[i];
- npn_status_ = kNextProtoNegotiated;
- break;
- }
- }
- if (npn_status_ == kNextProtoNegotiated)
- break;
- }
-
- // If we didn't find a protocol, we select the first one from our list.
- if (npn_status_ == kNextProtoNoOverlap) {
- *out = reinterpret_cast<uint8*>(const_cast<char*>(
- ssl_config_.next_protos[0].data()));
- *outlen = ssl_config_.next_protos[0].size();
- }
-
- npn_proto_.assign(reinterpret_cast<const char*>(*out), *outlen);
- server_protos_.assign(reinterpret_cast<const char*>(in), inlen);
- DVLOG(2) << "next protocol: '" << npn_proto_ << "' status: " << npn_status_;
-#endif
- return SSL_TLSEXT_ERR_OK;
-}
-
int SSLClientSocketOpenSSL::DoVerifyCert(int result) {
DCHECK(server_cert_.get());
GotoState(STATE_VERIFY_CERT_COMPLETE);
@@ -1051,6 +1001,14 @@ int SSLClientSocketOpenSSL::DoVerifyCertComplete(int result) {
return result;
}
+void SSLClientSocketOpenSSL::DoConnectCallback(int rv) {
+ if (!user_connect_callback_.is_null()) {
+ CompletionCallback c = user_connect_callback_;
+ user_connect_callback_.Reset();
+ c.Run(rv > OK ? OK : rv);
+ }
+}
+
X509Certificate* SSLClientSocketOpenSSL::UpdateServerCert() {
if (server_cert_.get())
return server_cert_.get();
@@ -1075,146 +1033,6 @@ X509Certificate* SSLClientSocketOpenSSL::UpdateServerCert() {
return server_cert_.get();
}
-bool SSLClientSocketOpenSSL::DoTransportIO() {
- bool network_moved = false;
- int rv;
- // Read and write as much data as possible. The loop is necessary because
- // Write() may return synchronously.
- do {
- rv = BufferSend();
- if (rv != ERR_IO_PENDING && rv != 0)
- network_moved = true;
- } while (rv > 0);
- if (!transport_recv_eof_ && BufferRecv() != ERR_IO_PENDING)
- network_moved = true;
- return network_moved;
-}
-
-int SSLClientSocketOpenSSL::BufferSend(void) {
- if (transport_send_busy_)
- return ERR_IO_PENDING;
-
- if (!send_buffer_.get()) {
- // Get a fresh send buffer out of the send BIO.
- size_t max_read = BIO_ctrl_pending(transport_bio_);
- if (!max_read)
- return 0; // Nothing pending in the OpenSSL write BIO.
- send_buffer_ = new DrainableIOBuffer(new IOBuffer(max_read), max_read);
- int read_bytes = BIO_read(transport_bio_, send_buffer_->data(), max_read);
- DCHECK_GT(read_bytes, 0);
- CHECK_EQ(static_cast<int>(max_read), read_bytes);
- }
-
- int rv = transport_->socket()->Write(
- send_buffer_.get(),
- send_buffer_->BytesRemaining(),
- base::Bind(&SSLClientSocketOpenSSL::BufferSendComplete,
- base::Unretained(this)));
- if (rv == ERR_IO_PENDING) {
- transport_send_busy_ = true;
- } else {
- TransportWriteComplete(rv);
- }
- return rv;
-}
-
-void SSLClientSocketOpenSSL::BufferSendComplete(int result) {
- transport_send_busy_ = false;
- TransportWriteComplete(result);
- OnSendComplete(result);
-}
-
-void SSLClientSocketOpenSSL::TransportWriteComplete(int result) {
- DCHECK(ERR_IO_PENDING != result);
- if (result < 0) {
- // Got a socket write error; close the BIO to indicate this upward.
- DVLOG(1) << "TransportWriteComplete error " << result;
- (void)BIO_shutdown_wr(transport_bio_);
- BIO_set_mem_eof_return(transport_bio_, 0);
- send_buffer_ = NULL;
- } else {
- DCHECK(send_buffer_.get());
- send_buffer_->DidConsume(result);
- DCHECK_GE(send_buffer_->BytesRemaining(), 0);
- if (send_buffer_->BytesRemaining() <= 0)
- send_buffer_ = NULL;
- }
-}
-
-int SSLClientSocketOpenSSL::BufferRecv(void) {
- if (transport_recv_busy_)
- return ERR_IO_PENDING;
-
- // Determine how much was requested from |transport_bio_| that was not
- // actually available.
- size_t requested = BIO_ctrl_get_read_request(transport_bio_);
- if (requested == 0) {
- // This is not a perfect match of error codes, as no operation is
- // actually pending. However, returning 0 would be interpreted as
- // a possible sign of EOF, which is also an inappropriate match.
- return ERR_IO_PENDING;
- }
-
- // Known Issue: While only reading |requested| data is the more correct
- // implementation, it has the downside of resulting in frequent reads:
- // One read for the SSL record header (~5 bytes) and one read for the SSL
- // record body. Rather than issuing these reads to the underlying socket
- // (and constantly allocating new IOBuffers), a single Read() request to
- // fill |transport_bio_| is issued. As long as an SSL client socket cannot
- // be gracefully shutdown (via SSL close alerts) and re-used for non-SSL
- // traffic, this over-subscribed Read()ing will not cause issues.
- size_t max_write = BIO_ctrl_get_write_guarantee(transport_bio_);
- if (!max_write)
- return ERR_IO_PENDING;
-
- recv_buffer_ = new IOBuffer(max_write);
- int rv = transport_->socket()->Read(
- recv_buffer_.get(),
- max_write,
- base::Bind(&SSLClientSocketOpenSSL::BufferRecvComplete,
- base::Unretained(this)));
- if (rv == ERR_IO_PENDING) {
- transport_recv_busy_ = true;
- } else {
- TransportReadComplete(rv);
- }
- return rv;
-}
-
-void SSLClientSocketOpenSSL::BufferRecvComplete(int result) {
- TransportReadComplete(result);
- OnRecvComplete(result);
-}
-
-void SSLClientSocketOpenSSL::TransportReadComplete(int result) {
- DCHECK(ERR_IO_PENDING != result);
- if (result <= 0) {
- DVLOG(1) << "TransportReadComplete result " << result;
- // Received 0 (end of file) or an error. Either way, bubble it up to the
- // SSL layer via the BIO. TODO(joth): consider stashing the error code, to
- // relay up to the SSL socket client (i.e. via DoReadCallback).
- if (result == 0)
- transport_recv_eof_ = true;
- BIO_set_mem_eof_return(transport_bio_, 0);
- (void)BIO_shutdown_wr(transport_bio_);
- } else {
- DCHECK(recv_buffer_.get());
- int ret = BIO_write(transport_bio_, recv_buffer_->data(), result);
- // A write into a memory BIO should always succeed.
- CHECK_EQ(result, ret);
- }
- recv_buffer_ = NULL;
- transport_recv_busy_ = false;
-}
-
-void SSLClientSocketOpenSSL::DoConnectCallback(int rv) {
- if (!user_connect_callback_.is_null()) {
- CompletionCallback c = user_connect_callback_;
- user_connect_callback_.Reset();
- c.Run(rv > OK ? OK : rv);
- }
-}
-
void SSLClientSocketOpenSSL::OnHandshakeIOComplete(int result) {
int rv = DoHandshakeLoop(result);
if (rv != ERR_IO_PENDING) {
@@ -1275,95 +1093,42 @@ void SSLClientSocketOpenSSL::OnRecvComplete(int result) {
DoReadCallback(rv);
}
-bool SSLClientSocketOpenSSL::IsConnected() const {
- // If the handshake has not yet completed.
- if (!completed_handshake_)
- return false;
- // If an asynchronous operation is still pending.
- if (user_read_buf_.get() || user_write_buf_.get())
- return true;
-
- return transport_->socket()->IsConnected();
-}
-
-bool SSLClientSocketOpenSSL::IsConnectedAndIdle() const {
- // If the handshake has not yet completed.
- if (!completed_handshake_)
- return false;
- // If an asynchronous operation is still pending.
- if (user_read_buf_.get() || user_write_buf_.get())
- return false;
- // If there is data waiting to be sent, or data read from the network that
- // has not yet been consumed.
- if (BIO_ctrl_pending(transport_bio_) > 0 ||
- BIO_ctrl_wpending(transport_bio_) > 0) {
- return false;
- }
-
- return transport_->socket()->IsConnectedAndIdle();
-}
-
-int SSLClientSocketOpenSSL::GetPeerAddress(IPEndPoint* addressList) const {
- return transport_->socket()->GetPeerAddress(addressList);
-}
-
-int SSLClientSocketOpenSSL::GetLocalAddress(IPEndPoint* addressList) const {
- return transport_->socket()->GetLocalAddress(addressList);
-}
-
-const BoundNetLog& SSLClientSocketOpenSSL::NetLog() const {
- return net_log_;
-}
-
-void SSLClientSocketOpenSSL::SetSubresourceSpeculation() {
- if (transport_.get() && transport_->socket()) {
- transport_->socket()->SetSubresourceSpeculation();
- } else {
- NOTREACHED();
- }
-}
-
-void SSLClientSocketOpenSSL::SetOmniboxSpeculation() {
- if (transport_.get() && transport_->socket()) {
- transport_->socket()->SetOmniboxSpeculation();
- } else {
- NOTREACHED();
- }
-}
-
-bool SSLClientSocketOpenSSL::WasEverUsed() const {
- if (transport_.get() && transport_->socket())
- return transport_->socket()->WasEverUsed();
-
- NOTREACHED();
- return false;
-}
-
-bool SSLClientSocketOpenSSL::UsingTCPFastOpen() const {
- if (transport_.get() && transport_->socket())
- return transport_->socket()->UsingTCPFastOpen();
-
- NOTREACHED();
- return false;
-}
-
-// Socket methods
-
-int SSLClientSocketOpenSSL::Read(IOBuffer* buf,
- int buf_len,
- const CompletionCallback& callback) {
- user_read_buf_ = buf;
- user_read_buf_len_ = buf_len;
-
- int rv = DoReadLoop(OK);
-
- if (rv == ERR_IO_PENDING) {
- user_read_callback_ = callback;
- } else {
- user_read_buf_ = NULL;
- user_read_buf_len_ = 0;
- }
+int SSLClientSocketOpenSSL::DoHandshakeLoop(int last_io_result) {
+ int rv = last_io_result;
+ do {
+ // Default to STATE_NONE for next state.
+ // (This is a quirk carried over from the windows
+ // implementation. It makes reading the logs a bit harder.)
+ // State handlers can and often do call GotoState just
+ // to stay in the current state.
+ State state = next_handshake_state_;
+ GotoState(STATE_NONE);
+ switch (state) {
+ case STATE_HANDSHAKE:
+ rv = DoHandshake();
+ break;
+ case STATE_VERIFY_CERT:
+ DCHECK(rv == OK);
+ rv = DoVerifyCert(rv);
+ break;
+ case STATE_VERIFY_CERT_COMPLETE:
+ rv = DoVerifyCertComplete(rv);
+ break;
+ case STATE_NONE:
+ default:
+ rv = ERR_UNEXPECTED;
+ NOTREACHED() << "unexpected state" << state;
+ break;
+ }
+ bool network_moved = DoTransportIO();
+ if (network_moved && next_handshake_state_ == STATE_HANDSHAKE) {
+ // In general we exit the loop if rv is ERR_IO_PENDING. In this
+ // special case we keep looping even if rv is ERR_IO_PENDING because
+ // the transport IO may allow DoHandshake to make progress.
+ rv = OK; // This causes us to stay in the loop.
+ }
+ } while (rv != ERR_IO_PENDING && next_handshake_state_ != STATE_NONE);
return rv;
}
@@ -1381,24 +1146,6 @@ int SSLClientSocketOpenSSL::DoReadLoop(int result) {
return rv;
}
-int SSLClientSocketOpenSSL::Write(IOBuffer* buf,
- int buf_len,
- const CompletionCallback& callback) {
- user_write_buf_ = buf;
- user_write_buf_len_ = buf_len;
-
- int rv = DoWriteLoop(OK);
-
- if (rv == ERR_IO_PENDING) {
- user_write_callback_ = callback;
- } else {
- user_write_buf_ = NULL;
- user_write_buf_len_ = 0;
- }
-
- return rv;
-}
-
int SSLClientSocketOpenSSL::DoWriteLoop(int result) {
if (result < 0)
return result;
@@ -1413,14 +1160,6 @@ int SSLClientSocketOpenSSL::DoWriteLoop(int result) {
return rv;
}
-bool SSLClientSocketOpenSSL::SetReceiveBufferSize(int32 size) {
- return transport_->socket()->SetReceiveBufferSize(size);
-}
-
-bool SSLClientSocketOpenSSL::SetSendBufferSize(int32 size) {
- return transport_->socket()->SetSendBufferSize(size);
-}
-
int SSLClientSocketOpenSSL::DoPayloadRead() {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
@@ -1502,4 +1241,260 @@ int SSLClientSocketOpenSSL::DoPayloadWrite() {
return MapOpenSSLError(err, err_tracer);
}
+int SSLClientSocketOpenSSL::BufferSend(void) {
+ if (transport_send_busy_)
+ return ERR_IO_PENDING;
+
+ if (!send_buffer_.get()) {
+ // Get a fresh send buffer out of the send BIO.
+ size_t max_read = BIO_ctrl_pending(transport_bio_);
+ if (!max_read)
+ return 0; // Nothing pending in the OpenSSL write BIO.
+ send_buffer_ = new DrainableIOBuffer(new IOBuffer(max_read), max_read);
+ int read_bytes = BIO_read(transport_bio_, send_buffer_->data(), max_read);
+ DCHECK_GT(read_bytes, 0);
+ CHECK_EQ(static_cast<int>(max_read), read_bytes);
+ }
+
+ int rv = transport_->socket()->Write(
+ send_buffer_.get(),
+ send_buffer_->BytesRemaining(),
+ base::Bind(&SSLClientSocketOpenSSL::BufferSendComplete,
+ base::Unretained(this)));
+ if (rv == ERR_IO_PENDING) {
+ transport_send_busy_ = true;
+ } else {
+ TransportWriteComplete(rv);
+ }
+ return rv;
+}
+
+int SSLClientSocketOpenSSL::BufferRecv(void) {
+ if (transport_recv_busy_)
+ return ERR_IO_PENDING;
+
+ // Determine how much was requested from |transport_bio_| that was not
+ // actually available.
+ size_t requested = BIO_ctrl_get_read_request(transport_bio_);
+ if (requested == 0) {
+ // This is not a perfect match of error codes, as no operation is
+ // actually pending. However, returning 0 would be interpreted as
+ // a possible sign of EOF, which is also an inappropriate match.
+ return ERR_IO_PENDING;
+ }
+
+ // Known Issue: While only reading |requested| data is the more correct
+ // implementation, it has the downside of resulting in frequent reads:
+ // One read for the SSL record header (~5 bytes) and one read for the SSL
+ // record body. Rather than issuing these reads to the underlying socket
+ // (and constantly allocating new IOBuffers), a single Read() request to
+ // fill |transport_bio_| is issued. As long as an SSL client socket cannot
+ // be gracefully shutdown (via SSL close alerts) and re-used for non-SSL
+ // traffic, this over-subscribed Read()ing will not cause issues.
+ size_t max_write = BIO_ctrl_get_write_guarantee(transport_bio_);
+ if (!max_write)
+ return ERR_IO_PENDING;
+
+ recv_buffer_ = new IOBuffer(max_write);
+ int rv = transport_->socket()->Read(
+ recv_buffer_.get(),
+ max_write,
+ base::Bind(&SSLClientSocketOpenSSL::BufferRecvComplete,
+ base::Unretained(this)));
+ if (rv == ERR_IO_PENDING) {
+ transport_recv_busy_ = true;
+ } else {
+ TransportReadComplete(rv);
+ }
+ return rv;
+}
+
+void SSLClientSocketOpenSSL::BufferSendComplete(int result) {
+ transport_send_busy_ = false;
+ TransportWriteComplete(result);
+ OnSendComplete(result);
+}
+
+void SSLClientSocketOpenSSL::BufferRecvComplete(int result) {
+ TransportReadComplete(result);
+ OnRecvComplete(result);
+}
+
+void SSLClientSocketOpenSSL::TransportWriteComplete(int result) {
+ DCHECK(ERR_IO_PENDING != result);
+ if (result < 0) {
+ // Got a socket write error; close the BIO to indicate this upward.
+ DVLOG(1) << "TransportWriteComplete error " << result;
+ (void)BIO_shutdown_wr(transport_bio_);
+ BIO_set_mem_eof_return(transport_bio_, 0);
+ send_buffer_ = NULL;
+ } else {
+ DCHECK(send_buffer_.get());
+ send_buffer_->DidConsume(result);
+ DCHECK_GE(send_buffer_->BytesRemaining(), 0);
+ if (send_buffer_->BytesRemaining() <= 0)
+ send_buffer_ = NULL;
+ }
+}
+
+void SSLClientSocketOpenSSL::TransportReadComplete(int result) {
+ DCHECK(ERR_IO_PENDING != result);
+ if (result <= 0) {
+ DVLOG(1) << "TransportReadComplete result " << result;
+ // Received 0 (end of file) or an error. Either way, bubble it up to the
+ // SSL layer via the BIO. TODO(joth): consider stashing the error code, to
+ // relay up to the SSL socket client (i.e. via DoReadCallback).
+ if (result == 0)
+ transport_recv_eof_ = true;
+ BIO_set_mem_eof_return(transport_bio_, 0);
+ (void)BIO_shutdown_wr(transport_bio_);
+ } else {
+ DCHECK(recv_buffer_.get());
+ int ret = BIO_write(transport_bio_, recv_buffer_->data(), result);
+ // A write into a memory BIO should always succeed.
+ CHECK_EQ(result, ret);
+ }
+ recv_buffer_ = NULL;
+ transport_recv_busy_ = false;
+}
+
+int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl,
+ X509** x509,
+ EVP_PKEY** pkey) {
+ DVLOG(3) << "OpenSSL ClientCertRequestCallback called";
+ DCHECK(ssl == ssl_);
+ DCHECK(*x509 == NULL);
+ DCHECK(*pkey == NULL);
+
+ if (!ssl_config_.send_client_cert) {
+ // First pass: we know that a client certificate is needed, but we do not
+ // have one at hand.
+ client_auth_cert_needed_ = true;
+ STACK_OF(X509_NAME) *authorities = SSL_get_client_CA_list(ssl);
+ for (int i = 0; i < sk_X509_NAME_num(authorities); i++) {
+ X509_NAME *ca_name = (X509_NAME *)sk_X509_NAME_value(authorities, i);
+ unsigned char* str = NULL;
+ int length = i2d_X509_NAME(ca_name, &str);
+ cert_authorities_.push_back(std::string(
+ reinterpret_cast<const char*>(str),
+ static_cast<size_t>(length)));
+ OPENSSL_free(str);
+ }
+
+ return -1; // Suspends handshake.
+ }
+
+ // Second pass: a client certificate should have been selected.
+ if (ssl_config_.client_cert.get()) {
+ // A note about ownership: FetchClientCertPrivateKey() increments
+ // the reference count of the EVP_PKEY. Ownership of this reference
+ // is passed directly to OpenSSL, which will release the reference
+ // using EVP_PKEY_free() when the SSL object is destroyed.
+ OpenSSLClientKeyStore::ScopedEVP_PKEY privkey;
+ if (OpenSSLClientKeyStore::GetInstance()->FetchClientCertPrivateKey(
+ ssl_config_.client_cert.get(), &privkey)) {
+ // TODO(joth): (copied from NSS) We should wait for server certificate
+ // verification before sending our credentials. See http://crbug.com/13934
+ *x509 = X509Certificate::DupOSCertHandle(
+ ssl_config_.client_cert->os_cert_handle());
+ *pkey = privkey.release();
+ return 1;
+ }
+ LOG(WARNING) << "Client cert found without private key";
+ }
+
+ // Send no client certificate.
+ return 0;
+}
+
+void SSLClientSocketOpenSSL::ChannelIDRequestCallback(SSL* ssl,
+ EVP_PKEY** pkey) {
+ DVLOG(3) << "OpenSSL ChannelIDRequestCallback called";
+ DCHECK_EQ(ssl, ssl_);
+ DCHECK(!*pkey);
+
+ channel_id_xtn_negotiated_ = true;
+ if (!channel_id_private_key_.size()) {
+ channel_id_request_return_value_ =
+ server_bound_cert_service_->GetOrCreateDomainBoundCert(
+ host_and_port_.host(),
+ &channel_id_private_key_,
+ &channel_id_cert_,
+ base::Bind(&SSLClientSocketOpenSSL::OnHandshakeIOComplete,
+ base::Unretained(this)),
+ &channel_id_request_handle_);
+ if (channel_id_request_return_value_ != OK)
+ return;
+ }
+
+ // Decode key.
+ std::vector<uint8> encrypted_private_key_info;
+ std::vector<uint8> subject_public_key_info;
+ encrypted_private_key_info.assign(
+ channel_id_private_key_.data(),
+ channel_id_private_key_.data() + channel_id_private_key_.size());
+ subject_public_key_info.assign(
+ channel_id_cert_.data(),
+ channel_id_cert_.data() + channel_id_cert_.size());
+ scoped_ptr<crypto::ECPrivateKey> ec_private_key(
+ crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo(
+ ServerBoundCertService::kEPKIPassword,
+ encrypted_private_key_info,
+ subject_public_key_info));
+ set_channel_id_sent(true);
+ *pkey = EVP_PKEY_dup(ec_private_key->key());
+}
+
+// SelectNextProtoCallback is called by OpenSSL during the handshake. If the
+// server supports NPN, selects a protocol from the list that the server
+// provides. According to third_party/openssl/openssl/ssl/ssl_lib.c, the
+// callback can assume that |in| is syntactically valid.
+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 = reinterpret_cast<uint8*>(
+ const_cast<char*>(kDefaultSupportedNPNProtocol));
+ *outlen = arraysize(kDefaultSupportedNPNProtocol) - 1;
+ npn_status_ = kNextProtoUnsupported;
+ return SSL_TLSEXT_ERR_OK;
+ }
+
+ // Assume there's no overlap between our protocols and the server's list.
+ npn_status_ = kNextProtoNoOverlap;
+
+ // For each protocol in server preference order, see if we support it.
+ for (unsigned int i = 0; i < inlen; i += in[i] + 1) {
+ for (std::vector<std::string>::const_iterator
+ j = ssl_config_.next_protos.begin();
+ j != ssl_config_.next_protos.end(); ++j) {
+ if (in[i] == j->size() &&
+ memcmp(&in[i + 1], j->data(), in[i]) == 0) {
+ // We found a match.
+ *out = const_cast<unsigned char*>(in) + i + 1;
+ *outlen = in[i];
+ npn_status_ = kNextProtoNegotiated;
+ break;
+ }
+ }
+ if (npn_status_ == kNextProtoNegotiated)
+ break;
+ }
+
+ // If we didn't find a protocol, we select the first one from our list.
+ if (npn_status_ == kNextProtoNoOverlap) {
+ *out = reinterpret_cast<uint8*>(const_cast<char*>(
+ ssl_config_.next_protos[0].data()));
+ *outlen = ssl_config_.next_protos[0].size();
+ }
+
+ npn_proto_.assign(reinterpret_cast<const char*>(*out), *outlen);
+ server_protos_.assign(reinterpret_cast<const char*>(in), inlen);
+ DVLOG(2) << "next protocol: '" << npn_proto_ << "' status: " << npn_status_;
+#endif
+ return SSL_TLSEXT_ERR_OK;
+}
+
} // namespace net