| Index: net/socket/ssl_client_socket_win.cc
|
| ===================================================================
|
| --- net/socket/ssl_client_socket_win.cc (revision 28437)
|
| +++ net/socket/ssl_client_socket_win.cc (working copy)
|
| @@ -6,6 +6,7 @@
|
|
|
| #include <schnlsp.h>
|
|
|
| +#include "base/compiler_specific.h"
|
| #include "base/lock.h"
|
| #include "base/singleton.h"
|
| #include "base/stl_util-inl.h"
|
| @@ -288,13 +289,21 @@
|
| SSLClientSocketWin::SSLClientSocketWin(ClientSocket* transport_socket,
|
| const std::string& hostname,
|
| const SSLConfig& ssl_config)
|
| -#pragma warning(suppress: 4355)
|
| - : io_callback_(this, &SSLClientSocketWin::OnIOComplete),
|
| + : ALLOW_THIS_IN_INITIALIZER_LIST(
|
| + handshake_io_callback_(this,
|
| + &SSLClientSocketWin::OnHandshakeIOComplete)),
|
| + ALLOW_THIS_IN_INITIALIZER_LIST(
|
| + read_callback_(this, &SSLClientSocketWin::OnReadComplete)),
|
| + ALLOW_THIS_IN_INITIALIZER_LIST(
|
| + write_callback_(this, &SSLClientSocketWin::OnWriteComplete)),
|
| transport_(transport_socket),
|
| hostname_(hostname),
|
| ssl_config_(ssl_config),
|
| - user_callback_(NULL),
|
| - user_buf_len_(0),
|
| + user_connect_callback_(NULL),
|
| + user_read_callback_(NULL),
|
| + user_read_buf_len_(0),
|
| + user_write_callback_(NULL),
|
| + user_write_buf_len_(0),
|
| next_state_(STATE_NONE),
|
| creds_(NULL),
|
| isc_status_(SEC_E_OK),
|
| @@ -305,9 +314,9 @@
|
| received_ptr_(NULL),
|
| bytes_received_(0),
|
| writing_first_token_(false),
|
| - completed_handshake_(false),
|
| ignore_ok_result_(false),
|
| - renegotiating_(false) {
|
| + renegotiating_(false),
|
| + need_more_data_(false) {
|
| memset(&stream_sizes_, 0, sizeof(stream_sizes_));
|
| memset(in_buffers_, 0, sizeof(in_buffers_));
|
| memset(&send_buffer_, 0, sizeof(send_buffer_));
|
| @@ -409,7 +418,7 @@
|
| int SSLClientSocketWin::Connect(CompletionCallback* callback) {
|
| DCHECK(transport_.get());
|
| DCHECK(next_state_ == STATE_NONE);
|
| - DCHECK(!user_callback_);
|
| + DCHECK(!user_connect_callback_);
|
|
|
| int ssl_version_mask = 0;
|
| if (ssl_config_.ssl2_enabled)
|
| @@ -471,14 +480,15 @@
|
| next_state_ = STATE_HANDSHAKE_WRITE;
|
| int rv = DoLoop(OK);
|
| if (rv == ERR_IO_PENDING)
|
| - user_callback_ = callback;
|
| + user_connect_callback_ = callback;
|
| return rv;
|
| }
|
|
|
| void SSLClientSocketWin::Disconnect() {
|
| // TODO(wtc): Send SSL close_notify alert.
|
| - completed_handshake_ = false;
|
| - // Shut down anything that may call us back through io_callback_.
|
| + next_state_ = STATE_NONE;
|
| +
|
| + // Shut down anything that may call us back.
|
| verifier_.reset();
|
| transport_->Disconnect();
|
|
|
| @@ -496,6 +506,7 @@
|
| bytes_received_ = 0;
|
| writing_first_token_ = false;
|
| renegotiating_ = false;
|
| + need_more_data_ = false;
|
| }
|
|
|
| bool SSLClientSocketWin::IsConnected() const {
|
| @@ -505,7 +516,7 @@
|
| // layer (HttpNetworkTransaction) needs to handle a persistent connection
|
| // closed by the server when we send a request anyway, a false positive in
|
| // exchange for simpler code is a good trade-off.
|
| - return completed_handshake_ && transport_->IsConnected();
|
| + return completed_handshake() && transport_->IsConnected();
|
| }
|
|
|
| bool SSLClientSocketWin::IsConnectedAndIdle() const {
|
| @@ -516,14 +527,13 @@
|
| // the close_notify alert message means EOF in the SSL layer, it is just
|
| // bytes to the transport layer below, so transport_->IsConnectedAndIdle()
|
| // returns the desired false when we receive close_notify.
|
| - return completed_handshake_ && transport_->IsConnectedAndIdle();
|
| + return completed_handshake() && transport_->IsConnectedAndIdle();
|
| }
|
|
|
| int SSLClientSocketWin::Read(IOBuffer* buf, int buf_len,
|
| CompletionCallback* callback) {
|
| - DCHECK(completed_handshake_);
|
| - DCHECK(next_state_ == STATE_NONE);
|
| - DCHECK(!user_callback_);
|
| + DCHECK(completed_handshake());
|
| + DCHECK(!user_read_callback_);
|
|
|
| // If we have surplus decrypted plaintext, satisfy the Read with it without
|
| // reading more ciphertext from the transport socket.
|
| @@ -542,40 +552,43 @@
|
| return len;
|
| }
|
|
|
| - DCHECK(!user_buf_);
|
| + DCHECK(!user_read_buf_);
|
| // http://crbug.com/16371: We're seeing |buf->data()| return NULL. See if the
|
| // user is passing in an IOBuffer with a NULL |data_|.
|
| CHECK(buf);
|
| CHECK(buf->data());
|
| - user_buf_ = buf;
|
| - user_buf_len_ = buf_len;
|
| + user_read_buf_ = buf;
|
| + user_read_buf_len_ = buf_len;
|
|
|
| - SetNextStateForRead();
|
| - int rv = DoLoop(OK);
|
| + int rv = DoPayloadRead();
|
| if (rv == ERR_IO_PENDING) {
|
| - user_callback_ = callback;
|
| + user_read_callback_ = callback;
|
| } else {
|
| - user_buf_ = NULL;
|
| + user_read_buf_ = NULL;
|
| + user_read_buf_len_ = 0;
|
| }
|
| return rv;
|
| }
|
|
|
| int SSLClientSocketWin::Write(IOBuffer* buf, int buf_len,
|
| CompletionCallback* callback) {
|
| - DCHECK(completed_handshake_);
|
| - DCHECK(next_state_ == STATE_NONE);
|
| - DCHECK(!user_callback_);
|
| + DCHECK(completed_handshake());
|
| + DCHECK(!user_write_callback_);
|
|
|
| - DCHECK(!user_buf_);
|
| - user_buf_ = buf;
|
| - user_buf_len_ = buf_len;
|
| + DCHECK(!user_write_buf_);
|
| + user_write_buf_ = buf;
|
| + user_write_buf_len_ = buf_len;
|
|
|
| - next_state_ = STATE_PAYLOAD_ENCRYPT;
|
| - int rv = DoLoop(OK);
|
| + int rv = DoPayloadEncrypt();
|
| + if (rv != OK)
|
| + return rv;
|
| +
|
| + rv = DoPayloadWrite();
|
| if (rv == ERR_IO_PENDING) {
|
| - user_callback_ = callback;
|
| + user_write_callback_ = callback;
|
| } else {
|
| - user_buf_ = NULL;
|
| + user_write_buf_ = NULL;
|
| + user_write_buf_len_ = 0;
|
| }
|
| return rv;
|
| }
|
| @@ -588,23 +601,62 @@
|
| return transport_->SetSendBufferSize(size);
|
| }
|
|
|
| -void SSLClientSocketWin::DoCallback(int rv) {
|
| - DCHECK(rv != ERR_IO_PENDING);
|
| - DCHECK(user_callback_);
|
| +void SSLClientSocketWin::OnHandshakeIOComplete(int result) {
|
| + int rv = DoLoop(result);
|
|
|
| - // since Run may result in Read being called, clear user_callback_ up front.
|
| - CompletionCallback* c = user_callback_;
|
| - user_callback_ = NULL;
|
| - user_buf_ = NULL;
|
| - c->Run(rv);
|
| + // The SSL handshake has some round trips. Any error, other than waiting
|
| + // for IO, means that we've failed and need to notify the caller.
|
| + if (rv != ERR_IO_PENDING) {
|
| + // If there is no connect callback available to call, it had better be
|
| + // because we are renegotiating (which occurs because we are in the middle
|
| + // of a Read when the renegotiation process starts). We need to inform the
|
| + // caller of the SSL error, so we complete the Read here.
|
| + if (!user_connect_callback_) {
|
| + DCHECK(renegotiating_);
|
| + CompletionCallback* c = user_read_callback_;
|
| + user_read_callback_ = NULL;
|
| + user_read_buf_ = NULL;
|
| + user_read_buf_len_ = 0;
|
| + c->Run(rv);
|
| + return;
|
| + }
|
| + CompletionCallback* c = user_connect_callback_;
|
| + user_connect_callback_ = NULL;
|
| + c->Run(rv);
|
| + }
|
| }
|
|
|
| -void SSLClientSocketWin::OnIOComplete(int result) {
|
| - int rv = DoLoop(result);
|
| - if (rv != ERR_IO_PENDING)
|
| - DoCallback(rv);
|
| +void SSLClientSocketWin::OnReadComplete(int result) {
|
| + DCHECK(completed_handshake());
|
| +
|
| + result = DoPayloadReadComplete(result);
|
| + if (result > 0)
|
| + result = DoPayloadDecrypt();
|
| + if (result != ERR_IO_PENDING) {
|
| + DCHECK(user_read_callback_);
|
| + CompletionCallback* c = user_read_callback_;
|
| + user_read_callback_ = NULL;
|
| + user_read_buf_ = NULL;
|
| + user_read_buf_len_ = 0;
|
| + c->Run(result);
|
| + }
|
| }
|
|
|
| +void SSLClientSocketWin::OnWriteComplete(int result) {
|
| + DCHECK(completed_handshake());
|
| +
|
| + int rv = DoPayloadWriteComplete(result);
|
| + if (rv != ERR_IO_PENDING) {
|
| + DCHECK(user_write_callback_);
|
| + CompletionCallback* c = user_write_callback_;
|
| + user_write_callback_ = NULL;
|
| + user_write_buf_ = NULL;
|
| + user_write_buf_len_ = 0;
|
| + c->Run(rv);
|
| + }
|
| +}
|
| +
|
| +
|
| int SSLClientSocketWin::DoLoop(int last_io_result) {
|
| DCHECK(next_state_ != STATE_NONE);
|
| int rv = last_io_result;
|
| @@ -630,21 +682,13 @@
|
| case STATE_VERIFY_CERT_COMPLETE:
|
| rv = DoVerifyCertComplete(rv);
|
| break;
|
| - case STATE_PAYLOAD_READ:
|
| - rv = DoPayloadRead();
|
| + case STATE_COMPLETED_RENEGOTIATION:
|
| + rv = DoCompletedRenegotiation(rv);
|
| break;
|
| - case STATE_PAYLOAD_READ_COMPLETE:
|
| - rv = DoPayloadReadComplete(rv);
|
| - break;
|
| - case STATE_PAYLOAD_ENCRYPT:
|
| - rv = DoPayloadEncrypt();
|
| - break;
|
| - case STATE_PAYLOAD_WRITE:
|
| - rv = DoPayloadWrite();
|
| - break;
|
| - case STATE_PAYLOAD_WRITE_COMPLETE:
|
| - rv = DoPayloadWriteComplete(rv);
|
| - break;
|
| + case STATE_COMPLETED_HANDSHAKE:
|
| + next_state_ = STATE_COMPLETED_HANDSHAKE;
|
| + // This is the end of our state machine, so return.
|
| + return rv;
|
| default:
|
| rv = ERR_UNEXPECTED;
|
| NOTREACHED() << "unexpected state";
|
| @@ -667,25 +711,26 @@
|
| return ERR_UNEXPECTED;
|
| }
|
|
|
| - DCHECK(!transport_buf_);
|
| - transport_buf_ = new IOBuffer(buf_len);
|
| + DCHECK(!transport_read_buf_);
|
| + transport_read_buf_ = new IOBuffer(buf_len);
|
|
|
| - return transport_->Read(transport_buf_, buf_len, &io_callback_);
|
| + return transport_->Read(transport_read_buf_, buf_len,
|
| + &handshake_io_callback_);
|
| }
|
|
|
| int SSLClientSocketWin::DoHandshakeReadComplete(int result) {
|
| if (result < 0) {
|
| - transport_buf_ = NULL;
|
| + transport_read_buf_ = NULL;
|
| return result;
|
| }
|
|
|
| - if (transport_buf_) {
|
| + if (transport_read_buf_) {
|
| // A transition to STATE_HANDSHAKE_READ_COMPLETE is set in multiple places,
|
| - // not only in DoHandshakeRead(), so we may not have a transport_buf_.
|
| + // not only in DoHandshakeRead(), so we may not have a transport_read_buf_.
|
| DCHECK_LE(result, kRecvBufferSize - bytes_received_);
|
| char* buf = recv_buffer_.get() + bytes_received_;
|
| - memcpy(buf, transport_buf_->data(), result);
|
| - transport_buf_ = NULL;
|
| + memcpy(buf, transport_read_buf_->data(), result);
|
| + transport_read_buf_ = NULL;
|
| }
|
|
|
| if (result == 0 && !ignore_ok_result_)
|
| @@ -816,19 +861,20 @@
|
| // We should have something to send.
|
| DCHECK(send_buffer_.pvBuffer);
|
| DCHECK(send_buffer_.cbBuffer > 0);
|
| - DCHECK(!transport_buf_);
|
| + DCHECK(!transport_write_buf_);
|
|
|
| const char* buf = static_cast<char*>(send_buffer_.pvBuffer) + bytes_sent_;
|
| int buf_len = send_buffer_.cbBuffer - bytes_sent_;
|
| - transport_buf_ = new IOBuffer(buf_len);
|
| - memcpy(transport_buf_->data(), buf, buf_len);
|
| + transport_write_buf_ = new IOBuffer(buf_len);
|
| + memcpy(transport_write_buf_->data(), buf, buf_len);
|
|
|
| - return transport_->Write(transport_buf_, buf_len, &io_callback_);
|
| + return transport_->Write(transport_write_buf_, buf_len,
|
| + &handshake_io_callback_);
|
| }
|
|
|
| int SSLClientSocketWin::DoHandshakeWriteComplete(int result) {
|
| - DCHECK(transport_buf_);
|
| - transport_buf_ = NULL;
|
| + DCHECK(transport_write_buf_);
|
| + transport_write_buf_ = NULL;
|
| if (result < 0)
|
| return result;
|
|
|
| @@ -870,7 +916,8 @@
|
| flags |= X509Certificate::VERIFY_EV_CERT;
|
| verifier_.reset(new CertVerifier);
|
| return verifier_->Verify(server_cert_, hostname_, flags,
|
| - &server_cert_verify_result_, &io_callback_);
|
| + &server_cert_verify_result_,
|
| + &handshake_io_callback_);
|
| }
|
|
|
| int SSLClientSocketWin::DoVerifyCertComplete(int result) {
|
| @@ -889,19 +936,16 @@
|
|
|
| LogConnectionTypeMetrics();
|
| if (renegotiating_) {
|
| - DidCompleteRenegotiation(result);
|
| - } else {
|
| - // The initial handshake, kicked off by a Connect, has completed.
|
| - completed_handshake_ = true;
|
| - // Exit DoLoop and return the result to the caller of Connect.
|
| - DCHECK(next_state_ == STATE_NONE);
|
| + DidCompleteRenegotiation();
|
| + return result;
|
| }
|
| +
|
| + // The initial handshake has completed.
|
| + next_state_ = STATE_COMPLETED_HANDSHAKE;
|
| return result;
|
| }
|
|
|
| int SSLClientSocketWin::DoPayloadRead() {
|
| - next_state_ = STATE_PAYLOAD_READ_COMPLETE;
|
| -
|
| DCHECK(recv_buffer_.get());
|
|
|
| int buf_len = kRecvBufferSize - bytes_received_;
|
| @@ -911,157 +955,195 @@
|
| return ERR_FAILED;
|
| }
|
|
|
| - DCHECK(!transport_buf_);
|
| - transport_buf_ = new IOBuffer(buf_len);
|
| + int rv;
|
| + // If bytes_received_, we have some data from a previous read still ready
|
| + // for decoding. Otherwise, we need to issue a real read.
|
| + if (!bytes_received_ || need_more_data_) {
|
| + DCHECK(!transport_read_buf_);
|
| + transport_read_buf_ = new IOBuffer(buf_len);
|
|
|
| - return transport_->Read(transport_buf_, buf_len, &io_callback_);
|
| + rv = transport_->Read(transport_read_buf_, buf_len, &read_callback_);
|
| + if (rv != ERR_IO_PENDING)
|
| + rv = DoPayloadReadComplete(rv);
|
| + if (rv <= 0)
|
| + return rv;
|
| + }
|
| +
|
| + // Decode what we've read. If there is not enough data to decode yet,
|
| + // this may return ERR_IO_PENDING still.
|
| + return DoPayloadDecrypt();
|
| }
|
|
|
| +// result is the number of bytes that have been read; it should not be
|
| +// less than zero; a value of zero means that no additional bytes have
|
| +// been read.
|
| int SSLClientSocketWin::DoPayloadReadComplete(int result) {
|
| - if (result < 0) {
|
| - transport_buf_ = NULL;
|
| + DCHECK(completed_handshake());
|
| +
|
| + // If IO Pending, there is nothing to do here.
|
| + if (result == ERR_IO_PENDING)
|
| return result;
|
| +
|
| + // We completed a Read, so reset the need_more_data_ flag.
|
| + need_more_data_ = false;
|
| +
|
| + // Check for error
|
| + if (result <= 0) {
|
| + transport_read_buf_ = NULL;
|
| + if (result == 0 && bytes_received_ != 0) {
|
| + // TODO(wtc): Unless we have received the close_notify alert, we need
|
| + // to return an error code indicating that the SSL connection ended
|
| + // uncleanly, a potential truncation attack. See
|
| + // http://crbug.com/18586.
|
| + return ERR_SSL_PROTOCOL_ERROR;
|
| + }
|
| + return result;
|
| }
|
| - if (transport_buf_) {
|
| - // This method is called after a state transition following DoPayloadRead(),
|
| - // or if SetNextStateForRead() was called. We have a transport_buf_ only
|
| - // in the first case, and we have to transfer the data from transport_buf_
|
| - // to recv_buffer_.
|
| +
|
| + // Transfer the data from transport_read_buf_ to recv_buffer_.
|
| + if (transport_read_buf_) {
|
| DCHECK_LE(result, kRecvBufferSize - bytes_received_);
|
| char* buf = recv_buffer_.get() + bytes_received_;
|
| - memcpy(buf, transport_buf_->data(), result);
|
| - transport_buf_ = NULL;
|
| + memcpy(buf, transport_read_buf_->data(), result);
|
| + transport_read_buf_ = NULL;
|
| }
|
|
|
| - if (result == 0 && !ignore_ok_result_) {
|
| - // TODO(wtc): Unless we have received the close_notify alert, we need to
|
| - // return an error code indicating that the SSL connection ended
|
| - // uncleanly, a potential truncation attack. See http://crbug.com/18586.
|
| - if (bytes_received_ != 0)
|
| - return ERR_SSL_PROTOCOL_ERROR;
|
| - return OK;
|
| - }
|
| + bytes_received_ += result;
|
|
|
| - ignore_ok_result_ = false;
|
| + return result;
|
| +}
|
|
|
| - bytes_received_ += result;
|
| -
|
| +int SSLClientSocketWin::DoPayloadDecrypt() {
|
| // Process the contents of recv_buffer_.
|
| - SecBuffer buffers[4];
|
| - buffers[0].pvBuffer = recv_buffer_.get();
|
| - buffers[0].cbBuffer = bytes_received_;
|
| - buffers[0].BufferType = SECBUFFER_DATA;
|
| + int len = 0; // the number of bytes we've copied to the user buffer.
|
| + while (bytes_received_) {
|
| + SecBuffer buffers[4];
|
| + buffers[0].pvBuffer = recv_buffer_.get();
|
| + buffers[0].cbBuffer = bytes_received_;
|
| + buffers[0].BufferType = SECBUFFER_DATA;
|
|
|
| - buffers[1].BufferType = SECBUFFER_EMPTY;
|
| - buffers[2].BufferType = SECBUFFER_EMPTY;
|
| - buffers[3].BufferType = SECBUFFER_EMPTY;
|
| + buffers[1].BufferType = SECBUFFER_EMPTY;
|
| + buffers[2].BufferType = SECBUFFER_EMPTY;
|
| + buffers[3].BufferType = SECBUFFER_EMPTY;
|
|
|
| - SecBufferDesc buffer_desc;
|
| - buffer_desc.cBuffers = 4;
|
| - buffer_desc.pBuffers = buffers;
|
| - buffer_desc.ulVersion = SECBUFFER_VERSION;
|
| + SecBufferDesc buffer_desc;
|
| + buffer_desc.cBuffers = 4;
|
| + buffer_desc.pBuffers = buffers;
|
| + buffer_desc.ulVersion = SECBUFFER_VERSION;
|
|
|
| - SECURITY_STATUS status;
|
| - status = DecryptMessage(&ctxt_, &buffer_desc, 0, NULL);
|
| + SECURITY_STATUS status;
|
| + status = DecryptMessage(&ctxt_, &buffer_desc, 0, NULL);
|
|
|
| - if (status == SEC_E_INCOMPLETE_MESSAGE) {
|
| - next_state_ = STATE_PAYLOAD_READ;
|
| - return OK;
|
| - }
|
| + if (status == SEC_E_INCOMPLETE_MESSAGE) {
|
| + need_more_data_ = true;
|
| + return DoPayloadRead();
|
| + }
|
|
|
| - if (status == SEC_I_CONTEXT_EXPIRED) {
|
| - // Received the close_notify alert.
|
| - bytes_received_ = 0;
|
| - return OK;
|
| - }
|
| + if (status == SEC_I_CONTEXT_EXPIRED) {
|
| + // Received the close_notify alert.
|
| + bytes_received_ = 0;
|
| + return OK;
|
| + }
|
|
|
| - if (status != SEC_E_OK && status != SEC_I_RENEGOTIATE) {
|
| - DCHECK(status != SEC_E_MESSAGE_ALTERED);
|
| - return MapSecurityError(status);
|
| - }
|
| -
|
| - // The received ciphertext was decrypted in place in recv_buffer_. Remember
|
| - // the location and length of the decrypted plaintext and any unused
|
| - // ciphertext.
|
| - decrypted_ptr_ = NULL;
|
| - bytes_decrypted_ = 0;
|
| - received_ptr_ = NULL;
|
| - bytes_received_ = 0;
|
| - for (int i = 1; i < 4; i++) {
|
| - if (!decrypted_ptr_ && buffers[i].BufferType == SECBUFFER_DATA) {
|
| - decrypted_ptr_ = static_cast<char*>(buffers[i].pvBuffer);
|
| - bytes_decrypted_ = buffers[i].cbBuffer;
|
| + if (status != SEC_E_OK && status != SEC_I_RENEGOTIATE) {
|
| + DCHECK(status != SEC_E_MESSAGE_ALTERED);
|
| + return MapSecurityError(status);
|
| }
|
| - if (!received_ptr_ && buffers[i].BufferType == SECBUFFER_EXTRA) {
|
| - received_ptr_ = static_cast<char*>(buffers[i].pvBuffer);
|
| - bytes_received_ = buffers[i].cbBuffer;
|
| - }
|
| - }
|
|
|
| - int len = 0;
|
| - if (bytes_decrypted_ != 0) {
|
| - len = std::min(user_buf_len_, bytes_decrypted_);
|
| - memcpy(user_buf_->data(), decrypted_ptr_, len);
|
| - decrypted_ptr_ += len;
|
| - bytes_decrypted_ -= len;
|
| - }
|
| - if (bytes_decrypted_ == 0) {
|
| + // The received ciphertext was decrypted in place in recv_buffer_. Remember
|
| + // the location and length of the decrypted plaintext and any unused
|
| + // ciphertext.
|
| decrypted_ptr_ = NULL;
|
| - if (bytes_received_ != 0) {
|
| - memmove(recv_buffer_.get(), received_ptr_, bytes_received_);
|
| - received_ptr_ = recv_buffer_.get();
|
| + bytes_decrypted_ = 0;
|
| + received_ptr_ = NULL;
|
| + bytes_received_ = 0;
|
| + for (int i = 1; i < 4; i++) {
|
| + if (!decrypted_ptr_ && buffers[i].BufferType == SECBUFFER_DATA) {
|
| + decrypted_ptr_ = static_cast<char*>(buffers[i].pvBuffer);
|
| + bytes_decrypted_ = buffers[i].cbBuffer;
|
| + }
|
| + if (!received_ptr_ && buffers[i].BufferType == SECBUFFER_EXTRA) {
|
| + received_ptr_ = static_cast<char*>(buffers[i].pvBuffer);
|
| + bytes_received_ = buffers[i].cbBuffer;
|
| + }
|
| }
|
| - }
|
|
|
| - if (status == SEC_I_RENEGOTIATE) {
|
| - if (bytes_received_ != 0) {
|
| - // The server requested renegotiation, but there are some data yet to
|
| - // be decrypted. The Platform SDK WebClient.c sample doesn't handle
|
| - // this, so we don't know how to handle this. Assume this cannot
|
| - // happen.
|
| - LOG(ERROR) << "DecryptMessage returned SEC_I_RENEGOTIATE with a buffer "
|
| - << "of type SECBUFFER_EXTRA.";
|
| - return ERR_SSL_RENEGOTIATION_REQUESTED;
|
| + DCHECK(len == 0);
|
| + if (bytes_decrypted_ != 0) {
|
| + len = std::min(user_read_buf_len_, bytes_decrypted_);
|
| + memcpy(user_read_buf_->data(), decrypted_ptr_, len);
|
| + decrypted_ptr_ += len;
|
| + bytes_decrypted_ -= len;
|
| }
|
| - if (len != 0) {
|
| - // The server requested renegotiation, but there are some decrypted
|
| - // data. We can't start renegotiation until we have returned all
|
| - // decrypted data to the caller.
|
| - //
|
| - // This hasn't happened during testing. Assume this cannot happen even
|
| - // though we know how to handle this.
|
| - LOG(ERROR) << "DecryptMessage returned SEC_I_RENEGOTIATE with a buffer "
|
| - << "of type SECBUFFER_DATA.";
|
| - return ERR_SSL_RENEGOTIATION_REQUESTED;
|
| + if (bytes_decrypted_ == 0) {
|
| + decrypted_ptr_ = NULL;
|
| + if (bytes_received_ != 0) {
|
| + memmove(recv_buffer_.get(), received_ptr_, bytes_received_);
|
| + received_ptr_ = recv_buffer_.get();
|
| + }
|
| }
|
| - // Jump to the handshake sequence. Will come back when the rehandshake is
|
| - // done.
|
| - renegotiating_ = true;
|
| - next_state_ = STATE_HANDSHAKE_READ_COMPLETE;
|
| - ignore_ok_result_ = true; // OK doesn't mean EOF.
|
| - return len;
|
| +
|
| + if (status == SEC_I_RENEGOTIATE) {
|
| + if (bytes_received_ != 0) {
|
| + // The server requested renegotiation, but there are some data yet to
|
| + // be decrypted. The Platform SDK WebClient.c sample doesn't handle
|
| + // this, so we don't know how to handle this. Assume this cannot
|
| + // happen.
|
| + LOG(ERROR) << "DecryptMessage returned SEC_I_RENEGOTIATE with a buffer "
|
| + << "of type SECBUFFER_EXTRA.";
|
| + return ERR_SSL_RENEGOTIATION_REQUESTED;
|
| + }
|
| + if (len != 0) {
|
| + // The server requested renegotiation, but there are some decrypted
|
| + // data. We can't start renegotiation until we have returned all
|
| + // decrypted data to the caller.
|
| + //
|
| + // This hasn't happened during testing. Assume this cannot happen even
|
| + // though we know how to handle this.
|
| + LOG(ERROR) << "DecryptMessage returned SEC_I_RENEGOTIATE with a buffer "
|
| + << "of type SECBUFFER_DATA.";
|
| + return ERR_SSL_RENEGOTIATION_REQUESTED;
|
| + }
|
| + // Jump to the handshake sequence. Will come back when the rehandshake is
|
| + // done.
|
| + renegotiating_ = true;
|
| + ignore_ok_result_ = true; // OK doesn't mean EOF.
|
| + // If renegotiation handshake occurred, we need to go back into the
|
| + // handshake state machine.
|
| + next_state_ = STATE_HANDSHAKE_READ_COMPLETE;
|
| + return DoLoop(OK);
|
| + }
|
| +
|
| + // We've already copied data into the user buffer, so quit now.
|
| + // TODO(mbelshe): We really should keep decoding as long as we can. This
|
| + // break out is causing us to return pretty small chunks of data up to the
|
| + // application, even though more is already buffered and ready to be
|
| + // decoded.
|
| + if (len)
|
| + break;
|
| }
|
|
|
| // If we decrypted 0 bytes, don't report 0 bytes read, which would be
|
| // mistaken for EOF. Continue decrypting or read more.
|
| if (len == 0)
|
| - SetNextStateForRead();
|
| + return DoPayloadRead();
|
| return len;
|
| }
|
|
|
| int SSLClientSocketWin::DoPayloadEncrypt() {
|
| - DCHECK(user_buf_);
|
| - DCHECK(user_buf_len_ > 0);
|
| + DCHECK(completed_handshake());
|
| + DCHECK(user_write_buf_);
|
| + DCHECK(user_write_buf_len_ > 0);
|
|
|
| ULONG message_len = std::min(
|
| - stream_sizes_.cbMaximumMessage, static_cast<ULONG>(user_buf_len_));
|
| + stream_sizes_.cbMaximumMessage, static_cast<ULONG>(user_write_buf_len_));
|
| ULONG alloc_len =
|
| message_len + stream_sizes_.cbHeader + stream_sizes_.cbTrailer;
|
| - user_buf_len_ = message_len;
|
| + user_write_buf_len_ = message_len;
|
|
|
| payload_send_buffer_.reset(new char[alloc_len]);
|
| memcpy(&payload_send_buffer_[stream_sizes_.cbHeader],
|
| - user_buf_->data(), message_len);
|
| + user_write_buf_->data(), message_len);
|
|
|
| SecBuffer buffers[4];
|
| buffers[0].pvBuffer = payload_send_buffer_.get();
|
| @@ -1093,30 +1175,31 @@
|
| buffers[1].cbBuffer +
|
| buffers[2].cbBuffer;
|
| DCHECK(bytes_sent_ == 0);
|
| -
|
| - next_state_ = STATE_PAYLOAD_WRITE;
|
| return OK;
|
| }
|
|
|
| int SSLClientSocketWin::DoPayloadWrite() {
|
| - next_state_ = STATE_PAYLOAD_WRITE_COMPLETE;
|
| + DCHECK(completed_handshake());
|
|
|
| // We should have something to send.
|
| DCHECK(payload_send_buffer_.get());
|
| DCHECK(payload_send_buffer_len_ > 0);
|
| - DCHECK(!transport_buf_);
|
| + DCHECK(!transport_write_buf_);
|
|
|
| const char* buf = payload_send_buffer_.get() + bytes_sent_;
|
| int buf_len = payload_send_buffer_len_ - bytes_sent_;
|
| - transport_buf_ = new IOBuffer(buf_len);
|
| - memcpy(transport_buf_->data(), buf, buf_len);
|
| + transport_write_buf_ = new IOBuffer(buf_len);
|
| + memcpy(transport_write_buf_->data(), buf, buf_len);
|
|
|
| - return transport_->Write(transport_buf_, buf_len, &io_callback_);
|
| + int rv = transport_->Write(transport_write_buf_, buf_len, &write_callback_);
|
| + if (rv != ERR_IO_PENDING)
|
| + rv = DoPayloadWriteComplete(rv);
|
| + return rv;
|
| }
|
|
|
| int SSLClientSocketWin::DoPayloadWriteComplete(int result) {
|
| - DCHECK(transport_buf_);
|
| - transport_buf_ = NULL;
|
| + DCHECK(transport_write_buf_);
|
| + transport_write_buf_ = NULL;
|
| if (result < 0)
|
| return result;
|
|
|
| @@ -1133,14 +1216,21 @@
|
| if (overflow) // Bug!
|
| return ERR_UNEXPECTED;
|
| // Done
|
| - return user_buf_len_;
|
| + return user_write_buf_len_;
|
| }
|
|
|
| // Send the remaining bytes.
|
| - next_state_ = STATE_PAYLOAD_WRITE;
|
| - return OK;
|
| + return DoPayloadWrite();
|
| }
|
|
|
| +int SSLClientSocketWin::DoCompletedRenegotiation(int result) {
|
| + // The user had a read in progress, which was usurped by the renegotiation.
|
| + // Restart the read sequence.
|
| + next_state_ = STATE_COMPLETED_HANDSHAKE;
|
| + DCHECK(result == OK);
|
| + return DoPayloadRead();
|
| +}
|
| +
|
| int SSLClientSocketWin::DidCompleteHandshake() {
|
| SECURITY_STATUS status = QueryContextAttributes(
|
| &ctxt_, SECPKG_ATTR_STREAM_SIZES, &stream_sizes_);
|
| @@ -1161,7 +1251,7 @@
|
| // We already verified the server certificate. Either it is good or the
|
| // user has accepted the certificate error.
|
| CertFreeCertificateContext(server_cert_handle);
|
| - DidCompleteRenegotiation(OK);
|
| + DidCompleteRenegotiation();
|
| } else {
|
| server_cert_ = X509Certificate::CreateFromHandle(
|
| server_cert_handle, X509Certificate::SOURCE_FROM_NETWORK);
|
| @@ -1173,12 +1263,9 @@
|
|
|
| // Called when a renegotiation is completed. |result| is the verification
|
| // result of the server certificate received during renegotiation.
|
| -void SSLClientSocketWin::DidCompleteRenegotiation(int result) {
|
| - // A rehandshake, started in the middle of a Read, has completed.
|
| +void SSLClientSocketWin::DidCompleteRenegotiation() {
|
| renegotiating_ = false;
|
| - // Pick up where we left off. Go back to reading data.
|
| - if (result == OK)
|
| - SetNextStateForRead();
|
| + next_state_ = STATE_COMPLETED_RENEGOTIATION;
|
| }
|
|
|
| void SSLClientSocketWin::LogConnectionTypeMetrics() const {
|
| @@ -1195,15 +1282,6 @@
|
| UpdateConnectionTypeHistograms(CONNECTION_SSL_MD2_CA);
|
| }
|
|
|
| -void SSLClientSocketWin::SetNextStateForRead() {
|
| - if (bytes_received_ == 0) {
|
| - next_state_ = STATE_PAYLOAD_READ;
|
| - } else {
|
| - next_state_ = STATE_PAYLOAD_READ_COMPLETE;
|
| - ignore_ok_result_ = true; // OK doesn't mean EOF.
|
| - }
|
| -}
|
| -
|
| void SSLClientSocketWin::FreeSendBuffer() {
|
| SECURITY_STATUS status = FreeContextBuffer(send_buffer_.pvBuffer);
|
| DCHECK(status == SEC_E_OK);
|
|
|