Chromium Code Reviews| Index: net/socket/ssl_server_socket_nss.cc |
| diff --git a/net/socket/ssl_server_socket_nss.cc b/net/socket/ssl_server_socket_nss.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..208424eaf6e8121306966ac396abb7754281dcf2 |
| --- /dev/null |
| +++ b/net/socket/ssl_server_socket_nss.cc |
| @@ -0,0 +1,626 @@ |
| +// Copyright (c) 2010 The Chromium Authors. All rights reserved. |
|
wtc
2010/12/17 00:16:26
If ssl_server_socket_nss.cc is heavily based on
ss
Alpha Left Google
2010/12/17 08:30:43
In fact I can't do this.. since git doesn't have a
|
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "net/socket/ssl_server_socket_nss.h" |
| + |
| +#if defined(USE_SYSTEM_SSL) |
| +#include <dlfcn.h> |
| +#endif |
| +#if defined(OS_MACOSX) |
| +#include <Security/Security.h> |
| +#endif |
| +#include <certdb.h> |
| +#include <cryptohi.h> |
| +#include <hasht.h> |
| +#include <keyhi.h> |
| +#include <nspr.h> |
| +#include <nss.h> |
| +#include <pk11pub.h> |
| +#include <secerr.h> |
| +#include <sechash.h> |
| +#include <ssl.h> |
| +#include <sslerr.h> |
| +#include <sslproto.h> |
| + |
| +#include <limits> |
| + |
| +#include "base/crypto/rsa_private_key.h" |
| +#include "base/ref_counted.h" |
| +#include "net/base/io_buffer.h" |
| +#include "net/base/net_errors.h" |
| +#include "net/base/net_log.h" |
| +#include "net/ocsp/nss_ocsp.h" |
| +#include "net/socket/nss_common.h" |
| +#include "net/socket/ssl_error_params.h" |
| + |
| +static const int kRecvBufferSize = 4096; |
| + |
| +#define GotoState(s) next_handshake_state_ = s |
| + |
| +namespace net { |
| + |
| +SSLServerSocket* CreateSSLServerSocket( |
| + Socket* socket, scoped_refptr<X509Certificate> cert, |
| + base::RSAPrivateKey* key) { |
| + return new SSLServerSocketNSS(socket, cert, key); |
| +} |
| + |
| +SSLServerSocketNSS::SSLServerSocketNSS( |
| + Socket* socket, scoped_refptr<X509Certificate> cert, |
| + base::RSAPrivateKey* key) |
| + : ALLOW_THIS_IN_INITIALIZER_LIST(buffer_send_callback_( |
| + this, &SSLServerSocketNSS::BufferSendComplete)), |
| + ALLOW_THIS_IN_INITIALIZER_LIST(buffer_recv_callback_( |
| + this, &SSLServerSocketNSS::BufferRecvComplete)), |
| + transport_send_busy_(false), |
| + transport_recv_busy_(false), |
| + user_accept_callback_(NULL), |
| + user_read_callback_(NULL), |
| + user_write_callback_(NULL), |
| + nss_fd_(NULL), |
| + nss_bufs_(NULL), |
| + socket_(socket), |
| + cert_(cert), |
| + next_handshake_state_(STATE_NONE), |
| + completed_handshake_(false) { |
| + ssl_config_.false_start_enabled = false; |
| + ssl_config_.ssl3_enabled = true; |
| + ssl_config_.tls1_enabled = true; |
| + |
| + // TODO(hclam): Need a better way to clone a key. |
| + std::vector<uint8> key_bytes; |
| + CHECK(key->ExportPrivateKey(&key_bytes)); |
| + key_.reset(base::RSAPrivateKey::CreateFromPrivateKeyInfo(key_bytes)); |
| + CHECK(key_.get()); |
| +} |
| + |
| +int SSLServerSocketNSS::Init() { |
| + // Initialize the NSS SSL library in a threadsafe way. This also |
| + // initializes the NSS base library. |
| + EnsureNSSSSLInit(); |
| + if (!NSS_IsInitialized()) |
| + return ERR_UNEXPECTED; |
| +#if !defined(OS_MACOSX) && !defined(OS_WIN) |
| + // We must call EnsureOCSPInit() here, on the IO thread, to get the IO loop |
| + // by MessageLoopForIO::current(). |
| + // X509Certificate::Verify() runs on a worker thread of CertVerifier. |
| + EnsureOCSPInit(); |
| +#endif |
| + |
| + return OK; |
| +} |
| + |
| +int SSLServerSocketNSS::Accept(CompletionCallback* callback) { |
| + net_log_.BeginEvent(NetLog::TYPE_SSL_ACCEPT, NULL); |
| + |
| + int rv = Init(); |
| + if (rv != OK) { |
| + LOG(ERROR) << "Failed to initialize NSS"; |
| + net_log_.EndEvent(NetLog::TYPE_SSL_ACCEPT, NULL); |
| + return rv; |
| + } |
| + |
| + rv = InitializeSSLOptions(); |
| + if (rv != OK) { |
| + LOG(ERROR) << "Failed to initialize SSL options"; |
| + net_log_.EndEvent(NetLog::TYPE_SSL_ACCEPT, NULL); |
| + return rv; |
| + } |
| + |
| + // Set peer address. TODO(hclam): This should be in a separate method. |
| + PRNetAddr peername; |
| + memset(&peername, 0, sizeof(peername)); |
| + peername.raw.family = AF_INET; |
| + memio_SetPeerName(nss_fd_, &peername); |
| + |
| + GotoState(STATE_HANDSHAKE); |
| + rv = DoHandshakeLoop(net::OK); |
| + if (rv == ERR_IO_PENDING) { |
| + user_accept_callback_ = callback; |
| + } else { |
| + net_log_.EndEvent(NetLog::TYPE_SSL_ACCEPT, NULL); |
| + } |
| + |
| + return rv > OK ? OK : rv; |
| +} |
| + |
| +int SSLServerSocketNSS::Read(IOBuffer* buf, int buf_len, |
| + CompletionCallback* callback) { |
| + DCHECK(!user_read_callback_); |
| + DCHECK(!user_accept_callback_); |
| + DCHECK(!user_read_buf_); |
| + DCHECK(nss_bufs_); |
| + |
| + user_read_buf_ = buf; |
| + user_read_buf_len_ = buf_len; |
| + |
| + DCHECK(completed_handshake_); |
| + |
| + 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 SSLServerSocketNSS::Write(IOBuffer* buf, int buf_len, |
| + CompletionCallback* callback) { |
| + DCHECK(!user_write_callback_); |
| + DCHECK(!user_write_buf_); |
| + DCHECK(nss_bufs_); |
| + |
| + 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; |
| +} |
| + |
| +// static |
| +// NSS calls this if an incoming certificate needs to be verified. |
| +// Do nothing but return SECSuccess. |
| +// This is called only in full handshake mode. |
| +// Peer certificate is retrieved in HandshakeCallback() later, which is called |
| +// in full handshake mode or in resumption handshake mode. |
| +SECStatus SSLServerSocketNSS::OwnAuthCertHandler(void* arg, |
| + PRFileDesc* socket, |
| + PRBool checksig, |
| + PRBool is_server) { |
| + // TODO(hclam): Implement. |
| + // Tell NSS to not verify the certificate. |
| + return SECSuccess; |
| +} |
| + |
| +// static |
| +// NSS calls this when handshake is completed. |
| +// After the SSL handshake is finished we need to verify the certificate. |
| +void SSLServerSocketNSS::HandshakeCallback(PRFileDesc* socket, |
| + void* arg) { |
| + // TODO(hclam): Implement. |
| +} |
| + |
| +int SSLServerSocketNSS::InitializeSSLOptions() { |
| + // Transport connected, now hook it up to nss |
| + // TODO(port): specify rx and tx buffer sizes separately |
| + nss_fd_ = memio_CreateIOLayer(kRecvBufferSize); |
| + if (nss_fd_ == NULL) { |
| + return ERR_OUT_OF_MEMORY; // TODO(port): map NSPR error code. |
| + } |
| + |
| + // Grab pointer to buffers |
| + nss_bufs_ = memio_GetSecret(nss_fd_); |
| + |
| + /* Create SSL state machine */ |
| + /* Push SSL onto our fake I/O socket */ |
| + nss_fd_ = SSL_ImportFD(NULL, nss_fd_); |
| + if (nss_fd_ == NULL) { |
| + LogFailedNSSFunction(net_log_, "SSL_ImportFD", ""); |
| + return ERR_OUT_OF_MEMORY; // TODO(port): map NSPR/NSS error code. |
| + } |
| + // TODO(port): set more ssl options! Check errors! |
| + |
| + int rv; |
| + |
| + rv = SSL_OptionSet(nss_fd_, SSL_SECURITY, PR_TRUE); |
| + if (rv != SECSuccess) { |
| + LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_SECURITY"); |
| + return ERR_UNEXPECTED; |
| + } |
| + |
| + rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL2, PR_FALSE); |
| + if (rv != SECSuccess) { |
| + LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_ENABLE_SSL2"); |
| + return ERR_UNEXPECTED; |
| + } |
| + |
| + rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL3, PR_TRUE); |
| + if (rv != SECSuccess) { |
| + LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_ENABLE_SSL3"); |
| + return ERR_UNEXPECTED; |
| + } |
| + |
| + rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_TLS, ssl_config_.tls1_enabled); |
| + if (rv != SECSuccess) { |
| + LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_ENABLE_TLS"); |
| + return ERR_UNEXPECTED; |
| + } |
| + |
| + for (std::vector<uint16>::const_iterator it = |
| + ssl_config_.disabled_cipher_suites.begin(); |
| + it != ssl_config_.disabled_cipher_suites.end(); ++it) { |
| + // This will fail if the specified cipher is not implemented by NSS, but |
| + // the failure is harmless. |
| + SSL_CipherPrefSet(nss_fd_, *it, PR_FALSE); |
| + } |
| + |
| + // Server socket doesn't need session tickets. |
| + rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SESSION_TICKETS, PR_FALSE); |
| + if (rv != SECSuccess) { |
| + LogFailedNSSFunction( |
| + net_log_, "SSL_OptionSet", "SSL_ENABLE_SESSION_TICKETS"); |
| + } |
| + |
| + // Doing this will force PR_Accept perform handshake as server. |
| + rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_CLIENT, PR_FALSE); |
| + if (rv != SECSuccess) { |
| + LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_HANDSHAKE_AS_CLIENT"); |
| + return ERR_UNEXPECTED; |
| + } |
| + |
| + rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_SERVER, PR_TRUE); |
| + if (rv != SECSuccess) { |
| + LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_HANDSHAKE_AS_SERVER"); |
| + return ERR_UNEXPECTED; |
| + } |
| + |
| + rv = SSL_OptionSet(nss_fd_, SSL_REQUEST_CERTIFICATE, PR_FALSE); |
| + if (rv != SECSuccess) { |
| + LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_REQUEST_CERTIFICATE"); |
| + return ERR_UNEXPECTED; |
| + } |
| + |
| + rv = SSL_OptionSet(nss_fd_, SSL_REQUIRE_CERTIFICATE, PR_FALSE); |
| + if (rv != SECSuccess) { |
| + LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_REQUIRE_CERTIFICATE"); |
| + return ERR_UNEXPECTED; |
| + } |
| + |
| + rv = SSL_OptionSet(nss_fd_, SSL_NO_CACHE, PR_TRUE); |
| + if (rv != SECSuccess) { |
| + LogFailedNSSFunction(net_log_, "SSL_OptionSet", "SSL_NO_CACHE"); |
| + return ERR_UNEXPECTED; |
| + } |
| + |
| + rv = SSL_AuthCertificateHook(nss_fd_, OwnAuthCertHandler, this); |
| + if (rv != SECSuccess) { |
| + LogFailedNSSFunction(net_log_, "SSL_AuthCertificateHook", ""); |
| + return ERR_UNEXPECTED; |
| + } |
| + |
| + rv = SSL_HandshakeCallback(nss_fd_, HandshakeCallback, this); |
| + if (rv != SECSuccess) { |
| + LogFailedNSSFunction(net_log_, "SSL_HandshakeCallback", ""); |
| + return ERR_UNEXPECTED; |
| + } |
| + |
| + // Assign server certificate and private key. |
| + SSLKEAType cert_kea = NSS_FindCertKEAType(cert_->os_cert_handle()); |
| + rv = SSL_ConfigSecureServer(nss_fd_, cert_->os_cert_handle(), |
| + key_->key(), cert_kea); |
| + if (rv != SECSuccess) { |
| + PRErrorCode prerr = PR_GetError(); |
| + LOG(ERROR) << "Failed to config SSL server: " << prerr; |
| + LogFailedNSSFunction(net_log_, "SSL_ConfigureSecureServer", ""); |
| + return ERR_UNEXPECTED; |
| + } |
| + |
| + // Tell SSL we're a server; needed if not letting NSPR do socket I/O |
| + rv = SSL_ResetHandshake(nss_fd_, PR_TRUE); |
| + if (rv != SECSuccess) { |
| + LogFailedNSSFunction(net_log_, "SSL_ResetHandshake", ""); |
| + return ERR_UNEXPECTED; |
| + } |
| + |
| + return OK; |
| +} |
| + |
| +// Return 0 for EOF, |
| +// > 0 for bytes transferred immediately, |
| +// < 0 for error (or the non-error ERR_IO_PENDING). |
| +int SSLServerSocketNSS::BufferSend(void) { |
| + if (transport_send_busy_) |
| + return ERR_IO_PENDING; |
| + |
| + const char* buf1; |
| + const char* buf2; |
| + unsigned int len1, len2; |
| + memio_GetWriteParams(nss_bufs_, &buf1, &len1, &buf2, &len2); |
| + const unsigned int len = len1 + len2; |
| + |
| + int rv = 0; |
| + if (len) { |
| + scoped_refptr<IOBuffer> send_buffer(new IOBuffer(len)); |
| + memcpy(send_buffer->data(), buf1, len1); |
| + memcpy(send_buffer->data() + len1, buf2, len2); |
| + rv = socket_->Write(send_buffer, len, |
| + &buffer_send_callback_); |
| + if (rv == ERR_IO_PENDING) { |
| + transport_send_busy_ = true; |
| + } else { |
| + memio_PutWriteResult(nss_bufs_, MapErrorToNSS(rv)); |
| + } |
| + } |
| + |
| + return rv; |
| +} |
| + |
| +void SSLServerSocketNSS::BufferSendComplete(int result) { |
| + memio_PutWriteResult(nss_bufs_, MapErrorToNSS(result)); |
| + transport_send_busy_ = false; |
| + OnSendComplete(result); |
| +} |
| + |
| +int SSLServerSocketNSS::BufferRecv(void) { |
| + if (transport_recv_busy_) return ERR_IO_PENDING; |
| + |
| + char *buf; |
| + int nb = memio_GetReadParams(nss_bufs_, &buf); |
| + int rv; |
| + if (!nb) { |
| + // buffer too full to read into, so no I/O possible at moment |
| + rv = ERR_IO_PENDING; |
| + } else { |
| + recv_buffer_ = new IOBuffer(nb); |
| + rv = socket_->Read(recv_buffer_, nb, &buffer_recv_callback_); |
| + if (rv == ERR_IO_PENDING) { |
| + transport_recv_busy_ = true; |
| + } else { |
| + if (rv > 0) |
| + memcpy(buf, recv_buffer_->data(), rv); |
| + memio_PutReadResult(nss_bufs_, MapErrorToNSS(rv)); |
| + recv_buffer_ = NULL; |
| + } |
| + } |
| + return rv; |
| +} |
| + |
| +void SSLServerSocketNSS::BufferRecvComplete(int result) { |
| + if (result > 0) { |
| + char *buf; |
| + memio_GetReadParams(nss_bufs_, &buf); |
| + memcpy(buf, recv_buffer_->data(), result); |
| + } |
| + recv_buffer_ = NULL; |
| + memio_PutReadResult(nss_bufs_, MapErrorToNSS(result)); |
| + transport_recv_busy_ = false; |
| + OnRecvComplete(result); |
| +} |
| + |
| +void SSLServerSocketNSS::OnSendComplete(int result) { |
| + if (next_handshake_state_ == STATE_HANDSHAKE) { |
| + // In handshake phase. |
| + OnHandshakeIOComplete(result); |
| + return; |
| + } |
| + |
| + // OnSendComplete may need to call DoPayloadRead while the renegotiation |
|
wtc
2010/12/17 00:16:26
You should omit the renegotiation-related code, wh
Alpha Left Google
2010/12/17 08:30:43
Done.
|
| + // handshake is in progress. |
| + int rv_read = ERR_IO_PENDING; |
| + int rv_write = ERR_IO_PENDING; |
| + bool network_moved; |
| + do { |
| + if (user_read_buf_) |
| + rv_read = DoPayloadRead(); |
| + if (user_write_buf_) |
| + rv_write = DoPayloadWrite(); |
| + network_moved = DoTransportIO(); |
| + } while (rv_read == ERR_IO_PENDING && |
| + rv_write == ERR_IO_PENDING && |
| + network_moved); |
| + |
| + if (user_read_buf_ && rv_read != ERR_IO_PENDING) |
| + DoReadCallback(rv_read); |
| + if (user_write_buf_ && rv_write != ERR_IO_PENDING) |
| + DoWriteCallback(rv_write); |
| +} |
| + |
| +void SSLServerSocketNSS::OnRecvComplete(int result) { |
| + if (next_handshake_state_ == STATE_HANDSHAKE) { |
| + // In handshake phase. |
| + OnHandshakeIOComplete(result); |
| + return; |
| + } |
| + |
| + // Network layer received some data, check if client requested to read |
| + // decrypted data. |
| + if (!user_read_buf_ || !completed_handshake_) { |
| + return; |
| + } |
| + |
| + int rv = DoReadLoop(result); |
| + if (rv != ERR_IO_PENDING) |
| + DoReadCallback(rv); |
| +} |
| + |
| +void SSLServerSocketNSS::OnHandshakeIOComplete(int result) { |
| + int rv = DoHandshakeLoop(result); |
| + if (rv != ERR_IO_PENDING) { |
| + net_log_.EndEvent(net::NetLog::TYPE_SSL_ACCEPT, NULL); |
| + if (user_accept_callback_) |
| + DoAcceptCallback(rv); |
| + } |
| +} |
| + |
| +void SSLServerSocketNSS::DoAcceptCallback(int rv) { |
| + DCHECK_NE(rv, ERR_IO_PENDING); |
| + |
| + CompletionCallback* c = user_accept_callback_; |
| + user_accept_callback_ = NULL; |
| + c->Run(rv > OK ? OK : rv); |
| +} |
| + |
| +void SSLServerSocketNSS::DoReadCallback(int rv) { |
| + DCHECK(rv != ERR_IO_PENDING); |
| + DCHECK(user_read_callback_); |
| + |
| + // Since Run may result in Read being called, clear |user_read_callback_| |
| + // up front. |
| + CompletionCallback* c = user_read_callback_; |
| + user_read_callback_ = NULL; |
| + user_read_buf_ = NULL; |
| + user_read_buf_len_ = 0; |
| + c->Run(rv); |
| +} |
| + |
| +void SSLServerSocketNSS::DoWriteCallback(int rv) { |
| + DCHECK(rv != ERR_IO_PENDING); |
| + DCHECK(user_write_callback_); |
| + |
| + // Since Run may result in Write being called, clear |user_write_callback_| |
| + // up front. |
| + CompletionCallback* c = user_write_callback_; |
| + user_write_callback_ = NULL; |
| + user_write_buf_ = NULL; |
| + user_write_buf_len_ = 0; |
| + c->Run(rv); |
| +} |
| + |
| +// Do network I/O between the given buffer and the given socket. |
| +// Return true if some I/O performed, false otherwise (error or ERR_IO_PENDING) |
| +bool SSLServerSocketNSS::DoTransportIO() { |
| + bool network_moved = false; |
| + if (nss_bufs_ != NULL) { |
| + int nsent = BufferSend(); |
| + int nreceived = BufferRecv(); |
| + network_moved = (nsent > 0 || nreceived >= 0); |
| + } |
| + return network_moved; |
| +} |
| + |
| +int SSLServerSocketNSS::DoPayloadRead() { |
| + DCHECK(user_read_buf_); |
| + DCHECK_GT(user_read_buf_len_, 0); |
| + int rv = PR_Read(nss_fd_, user_read_buf_->data(), user_read_buf_len_); |
| + if (rv >= 0) |
| + return rv; |
| + PRErrorCode prerr = PR_GetError(); |
| + if (prerr == PR_WOULD_BLOCK_ERROR) { |
| + return ERR_IO_PENDING; |
| + } |
| + rv = MapNSPRError(prerr); |
| + net_log_.AddEvent(NetLog::TYPE_SSL_READ_ERROR, |
| + make_scoped_refptr(new SSLErrorParams(rv, prerr))); |
| + return rv; |
| +} |
| + |
| +int SSLServerSocketNSS::DoPayloadWrite() { |
| + DCHECK(user_write_buf_); |
| + int rv = PR_Write(nss_fd_, user_write_buf_->data(), user_write_buf_len_); |
| + if (rv >= 0) |
| + return rv; |
| + PRErrorCode prerr = PR_GetError(); |
| + if (prerr == PR_WOULD_BLOCK_ERROR) { |
| + return ERR_IO_PENDING; |
| + } |
| + rv = MapNSPRError(prerr); |
| + net_log_.AddEvent(NetLog::TYPE_SSL_WRITE_ERROR, |
| + make_scoped_refptr(new SSLErrorParams(rv, prerr))); |
| + return rv; |
| +} |
| + |
| +int SSLServerSocketNSS::DoHandshakeLoop(int last_io_result) { |
| + bool network_moved; |
| + 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_NONE: |
| + // we're just pumping data between the buffer and the network |
| + break; |
| + case STATE_HANDSHAKE: |
| + rv = DoHandshake(); |
| + break; |
| + default: |
| + rv = ERR_UNEXPECTED; |
| + LOG(DFATAL) << "unexpected state " << state; |
| + break; |
| + } |
| + |
| + // Do the actual network I/O |
| + network_moved = DoTransportIO(); |
| + } while ((rv != ERR_IO_PENDING || network_moved) && |
| + next_handshake_state_ != STATE_NONE); |
| + return rv; |
| +} |
| + |
| +int SSLServerSocketNSS::DoReadLoop(int result) { |
| + DCHECK(completed_handshake_); |
| + DCHECK(next_handshake_state_ == STATE_NONE); |
| + |
| + if (result < 0) |
| + return result; |
| + |
| + if (!nss_bufs_) { |
| + LOG(DFATAL) << "!nss_bufs_"; |
| + int rv = ERR_UNEXPECTED; |
| + net_log_.AddEvent(NetLog::TYPE_SSL_READ_ERROR, |
| + make_scoped_refptr(new SSLErrorParams(rv, 0))); |
| + return rv; |
| + } |
| + |
| + bool network_moved; |
| + int rv; |
| + do { |
| + rv = DoPayloadRead(); |
| + network_moved = DoTransportIO(); |
| + } while (rv == ERR_IO_PENDING && network_moved); |
| + return rv; |
| +} |
| + |
| +int SSLServerSocketNSS::DoWriteLoop(int result) { |
| + DCHECK(completed_handshake_); |
| + DCHECK(next_handshake_state_ == STATE_NONE); |
| + |
| + if (result < 0) |
| + return result; |
| + |
| + if (!nss_bufs_) { |
| + LOG(DFATAL) << "!nss_bufs_"; |
| + int rv = ERR_UNEXPECTED; |
| + net_log_.AddEvent(NetLog::TYPE_SSL_WRITE_ERROR, |
| + make_scoped_refptr(new SSLErrorParams(rv, 0))); |
| + return rv; |
| + } |
| + |
| + bool network_moved; |
| + int rv; |
| + do { |
| + rv = DoPayloadWrite(); |
| + network_moved = DoTransportIO(); |
| + } while (rv == ERR_IO_PENDING && network_moved); |
| + return rv; |
| +} |
| + |
| +int SSLServerSocketNSS::DoHandshake() { |
| + int net_error = net::OK; |
| + SECStatus rv = SSL_ForceHandshake(nss_fd_); |
| + |
| + if (rv == SECSuccess) { |
| + completed_handshake_ = true; |
| + } else { |
| + PRErrorCode prerr = PR_GetError(); |
| + net_error = MapHandshakeError(prerr); |
| + |
| + // If not done, stay in this state |
| + if (net_error == ERR_IO_PENDING) { |
| + GotoState(STATE_HANDSHAKE); |
| + } else { |
| + LOG(ERROR) << "handshake failed; NSS error code " << prerr |
| + << ", net_error " << net_error; |
| + net_log_.AddEvent( |
| + NetLog::TYPE_SSL_HANDSHAKE_ERROR, |
| + make_scoped_refptr(new SSLErrorParams(net_error, prerr))); |
| + } |
| + } |
| + return net_error; |
| +} |
| + |
| +} // namespace net |