|  | // Copyright 2016 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "net/socket/socket_bio_adapter.h" | 
|  |  | 
|  | #include <string.h> | 
|  |  | 
|  | #include <algorithm> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/feature_list.h" | 
|  | #include "base/location.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/threading/thread_task_runner_handle.h" | 
|  | #include "net/base/io_buffer.h" | 
|  | #include "net/base/net_errors.h" | 
|  | #include "net/socket/socket.h" | 
|  | #include "net/socket/stream_socket.h" | 
|  | #include "net/ssl/openssl_ssl_util.h" | 
|  | #include "net/traffic_annotation/network_traffic_annotation.h" | 
|  | #include "starboard/memory.h" | 
|  | #include "starboard/types.h" | 
|  | #include "third_party/boringssl/src/include/openssl/bio.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | net::NetworkTrafficAnnotationTag kTrafficAnnotation = | 
|  | net::DefineNetworkTrafficAnnotation("socket_bio_adapter", R"( | 
|  | semantics { | 
|  | sender: "Socket BIO Adapter" | 
|  | description: | 
|  | "SocketBIOAdapter is used only internal to //net code as an internal " | 
|  | "detail to implement a TLS connection for a Socket class, and is not " | 
|  | "being called directly outside of this abstraction." | 
|  | trigger: | 
|  | "Establishing a TLS connection to a remote endpoint. There are many " | 
|  | "different ways in which a TLS connection may be triggered, such as " | 
|  | "loading an HTTPS URL." | 
|  | data: | 
|  | "All data sent or received over a TLS connection. This traffic may " | 
|  | "either be the handshake or application data. During the handshake, " | 
|  | "the target host name, user's IP, data related to previous " | 
|  | "handshake, client certificates, and channel ID, may be sent. When " | 
|  | "the connection is used to load an HTTPS URL, the application data " | 
|  | "includes cookies, request headers, and the response body." | 
|  | destination: OTHER | 
|  | destination_other: | 
|  | "Any destination the implementing socket is connected to." | 
|  | } | 
|  | policy { | 
|  | cookies_allowed: NO | 
|  | setting: "This feature cannot be disabled." | 
|  | policy_exception_justification: "Essential for navigation." | 
|  | })"); | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | namespace net { | 
|  |  | 
|  | SocketBIOAdapter::SocketBIOAdapter(StreamSocket* socket, | 
|  | int read_buffer_capacity, | 
|  | int write_buffer_capacity, | 
|  | Delegate* delegate) | 
|  | : socket_(socket), | 
|  | read_buffer_capacity_(read_buffer_capacity), | 
|  | read_offset_(0), | 
|  | read_result_(0), | 
|  | write_buffer_capacity_(write_buffer_capacity), | 
|  | write_buffer_used_(0), | 
|  | write_error_(OK), | 
|  | delegate_(delegate), | 
|  | weak_factory_(this) { | 
|  | bio_.reset(BIO_new(&kBIOMethod)); | 
|  | bio_->ptr = this; | 
|  | bio_->init = 1; | 
|  |  | 
|  | read_callback_ = base::BindRepeating(&SocketBIOAdapter::OnSocketReadComplete, | 
|  | weak_factory_.GetWeakPtr()); | 
|  | write_callback_ = base::BindRepeating( | 
|  | &SocketBIOAdapter::OnSocketWriteComplete, weak_factory_.GetWeakPtr()); | 
|  | } | 
|  |  | 
|  | SocketBIOAdapter::~SocketBIOAdapter() { | 
|  | // BIOs are reference-counted and may outlive the adapter. Clear the pointer | 
|  | // so future operations fail. | 
|  | bio_->ptr = nullptr; | 
|  | } | 
|  |  | 
|  | bool SocketBIOAdapter::HasPendingReadData() { | 
|  | return read_result_ > 0; | 
|  | } | 
|  |  | 
|  | size_t SocketBIOAdapter::GetAllocationSize() const { | 
|  | size_t buffer_size = 0; | 
|  | if (read_buffer_) | 
|  | buffer_size += read_buffer_capacity_; | 
|  |  | 
|  | if (write_buffer_) | 
|  | buffer_size += write_buffer_capacity_; | 
|  | return buffer_size; | 
|  | } | 
|  |  | 
|  | int SocketBIOAdapter::BIORead(char* out, int len) { | 
|  | if (len <= 0) | 
|  | return len; | 
|  |  | 
|  | // If there is no result available synchronously, report any Write() errors | 
|  | // that were observed. Otherwise the application may have encountered a socket | 
|  | // error while writing that would otherwise not be reported until the | 
|  | // application attempted to write again - which it may never do. See | 
|  | // https://crbug.com/249848. | 
|  | if (write_error_ != OK && write_error_ != ERR_IO_PENDING && | 
|  | (read_result_ == 0 || read_result_ == ERR_IO_PENDING)) { | 
|  | OpenSSLPutNetError(FROM_HERE, write_error_); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (read_result_ == 0) { | 
|  | // Instantiate the read buffer and read from the socket. Although only |len| | 
|  | // bytes were requested, intentionally read to the full buffer size. The SSL | 
|  | // layer reads the record header and body in separate reads to avoid | 
|  | // overreading, but issuing one is more efficient. SSL sockets are not | 
|  | // reused after shutdown for non-SSL traffic, so overreading is fine. | 
|  | DCHECK(!read_buffer_); | 
|  | DCHECK_EQ(0, read_offset_); | 
|  | read_buffer_ = base::MakeRefCounted<IOBuffer>(read_buffer_capacity_); | 
|  | int result = ERR_READ_IF_READY_NOT_IMPLEMENTED; | 
|  | if (base::FeatureList::IsEnabled(Socket::kReadIfReadyExperiment)) { | 
|  | result = socket_->ReadIfReady( | 
|  | read_buffer_.get(), read_buffer_capacity_, | 
|  | base::Bind(&SocketBIOAdapter::OnSocketReadIfReadyComplete, | 
|  | weak_factory_.GetWeakPtr())); | 
|  | if (result == ERR_IO_PENDING) | 
|  | read_buffer_ = nullptr; | 
|  | } | 
|  | if (result == ERR_READ_IF_READY_NOT_IMPLEMENTED) { | 
|  | result = socket_->Read(read_buffer_.get(), read_buffer_capacity_, | 
|  | read_callback_); | 
|  | } | 
|  | if (result == ERR_IO_PENDING) { | 
|  | read_result_ = ERR_IO_PENDING; | 
|  | } else { | 
|  | HandleSocketReadResult(result); | 
|  | } | 
|  | } | 
|  |  | 
|  | // There is a pending Read(). Inform the caller to retry when it completes. | 
|  | if (read_result_ == ERR_IO_PENDING) { | 
|  | BIO_set_retry_read(bio()); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // If the last Read() failed, report the error. | 
|  | if (read_result_ < 0) { | 
|  | OpenSSLPutNetError(FROM_HERE, read_result_); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Report the result of the last Read() if non-empty. | 
|  | CHECK_LT(read_offset_, read_result_); | 
|  | len = std::min(len, read_result_ - read_offset_); | 
|  | SbMemoryCopy(out, read_buffer_->data() + read_offset_, len); | 
|  | read_offset_ += len; | 
|  |  | 
|  | // Release the buffer when empty. | 
|  | if (read_offset_ == read_result_) { | 
|  | read_buffer_ = nullptr; | 
|  | read_offset_ = 0; | 
|  | read_result_ = 0; | 
|  | } | 
|  |  | 
|  | return len; | 
|  | } | 
|  |  | 
|  | void SocketBIOAdapter::HandleSocketReadResult(int result) { | 
|  | DCHECK_NE(ERR_IO_PENDING, result); | 
|  |  | 
|  | // If an EOF, canonicalize to ERR_CONNECTION_CLOSED here, so that higher | 
|  | // levels don't report success. | 
|  | if (result == 0) | 
|  | result = ERR_CONNECTION_CLOSED; | 
|  |  | 
|  | read_result_ = result; | 
|  |  | 
|  | // The read buffer is no longer needed. | 
|  | if (read_result_ <= 0) | 
|  | read_buffer_ = nullptr; | 
|  | } | 
|  |  | 
|  | void SocketBIOAdapter::OnSocketReadComplete(int result) { | 
|  | DCHECK_EQ(ERR_IO_PENDING, read_result_); | 
|  |  | 
|  | HandleSocketReadResult(result); | 
|  | delegate_->OnReadReady(); | 
|  | } | 
|  |  | 
|  | void SocketBIOAdapter::OnSocketReadIfReadyComplete(int result) { | 
|  | DCHECK_EQ(ERR_IO_PENDING, read_result_); | 
|  | DCHECK_GE(OK, result); | 
|  |  | 
|  | // Do not use HandleSocketReadResult() because result == OK doesn't mean EOF. | 
|  | read_result_ = result; | 
|  |  | 
|  | delegate_->OnReadReady(); | 
|  | } | 
|  |  | 
|  | int SocketBIOAdapter::BIOWrite(const char* in, int len) { | 
|  | if (len <= 0) | 
|  | return len; | 
|  |  | 
|  | // If the write buffer is not empty, there must be a pending Write() to flush | 
|  | // it. | 
|  | DCHECK(write_buffer_used_ == 0 || write_error_ == ERR_IO_PENDING); | 
|  |  | 
|  | // If a previous Write() failed, report the error. | 
|  | if (write_error_ != OK && write_error_ != ERR_IO_PENDING) { | 
|  | OpenSSLPutNetError(FROM_HERE, write_error_); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Instantiate the write buffer if needed. | 
|  | if (!write_buffer_) { | 
|  | DCHECK_EQ(0, write_buffer_used_); | 
|  | write_buffer_ = base::MakeRefCounted<GrowableIOBuffer>(); | 
|  | write_buffer_->SetCapacity(write_buffer_capacity_); | 
|  | } | 
|  |  | 
|  | // If the ring buffer is full, inform the caller to try again later. | 
|  | if (write_buffer_used_ == write_buffer_->capacity()) { | 
|  | BIO_set_retry_write(bio()); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int bytes_copied = 0; | 
|  |  | 
|  | // If there is space after the offset, fill it. | 
|  | if (write_buffer_used_ < write_buffer_->RemainingCapacity()) { | 
|  | int chunk = | 
|  | std::min(write_buffer_->RemainingCapacity() - write_buffer_used_, len); | 
|  | SbMemoryCopy(write_buffer_->data() + write_buffer_used_, in, chunk); | 
|  | in += chunk; | 
|  | len -= chunk; | 
|  | bytes_copied += chunk; | 
|  | write_buffer_used_ += chunk; | 
|  | } | 
|  |  | 
|  | // If there is still space for remaining data, try to wrap around. | 
|  | if (len > 0 && write_buffer_used_ < write_buffer_->capacity()) { | 
|  | // If there were any room after the offset, the previous branch would have | 
|  | // filled it. | 
|  | CHECK_LE(write_buffer_->RemainingCapacity(), write_buffer_used_); | 
|  | int write_offset = write_buffer_used_ - write_buffer_->RemainingCapacity(); | 
|  | int chunk = std::min(len, write_buffer_->capacity() - write_buffer_used_); | 
|  | SbMemoryCopy(write_buffer_->StartOfBuffer() + write_offset, in, chunk); | 
|  | in += chunk; | 
|  | len -= chunk; | 
|  | bytes_copied += chunk; | 
|  | write_buffer_used_ += chunk; | 
|  | } | 
|  |  | 
|  | // Either the buffer is now full or there is no more input. | 
|  | DCHECK(len == 0 || write_buffer_used_ == write_buffer_->capacity()); | 
|  |  | 
|  | // Schedule a socket Write() if necessary. (The ring buffer may previously | 
|  | // have been empty.) | 
|  | SocketWrite(); | 
|  |  | 
|  | // If a read-interrupting write error was synchronously discovered, | 
|  | // asynchronously notify OnReadReady. See https://crbug.com/249848. Avoid | 
|  | // reentrancy by deferring it to a later event loop iteration. | 
|  | if (write_error_ != OK && write_error_ != ERR_IO_PENDING && | 
|  | read_result_ == ERR_IO_PENDING) { | 
|  | base::ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, base::Bind(&SocketBIOAdapter::CallOnReadReady, | 
|  | weak_factory_.GetWeakPtr())); | 
|  | } | 
|  |  | 
|  | return bytes_copied; | 
|  | } | 
|  |  | 
|  | void SocketBIOAdapter::SocketWrite() { | 
|  | while (write_error_ == OK && write_buffer_used_ > 0) { | 
|  | int write_size = | 
|  | std::min(write_buffer_used_, write_buffer_->RemainingCapacity()); | 
|  | int result = socket_->Write(write_buffer_.get(), write_size, | 
|  | write_callback_, kTrafficAnnotation); | 
|  | if (result == ERR_IO_PENDING) { | 
|  | write_error_ = ERR_IO_PENDING; | 
|  | return; | 
|  | } | 
|  |  | 
|  | HandleSocketWriteResult(result); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SocketBIOAdapter::HandleSocketWriteResult(int result) { | 
|  | DCHECK_NE(ERR_IO_PENDING, result); | 
|  |  | 
|  | if (result < 0) { | 
|  | write_error_ = result; | 
|  |  | 
|  | // The write buffer is no longer needed. | 
|  | write_buffer_ = nullptr; | 
|  | write_buffer_used_ = 0; | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Advance the ring buffer. | 
|  | write_buffer_->set_offset(write_buffer_->offset() + result); | 
|  | write_buffer_used_ -= result; | 
|  | if (write_buffer_->RemainingCapacity() == 0) | 
|  | write_buffer_->set_offset(0); | 
|  | write_error_ = OK; | 
|  |  | 
|  | // Release the write buffer if empty. | 
|  | if (write_buffer_used_ == 0) | 
|  | write_buffer_ = nullptr; | 
|  | } | 
|  |  | 
|  | void SocketBIOAdapter::OnSocketWriteComplete(int result) { | 
|  | DCHECK_EQ(ERR_IO_PENDING, write_error_); | 
|  |  | 
|  | bool was_full = write_buffer_used_ == write_buffer_->capacity(); | 
|  |  | 
|  | HandleSocketWriteResult(result); | 
|  | SocketWrite(); | 
|  |  | 
|  | // If transitioning from being unable to accept data to being able to, signal | 
|  | // OnWriteReady. | 
|  | if (was_full) { | 
|  | base::WeakPtr<SocketBIOAdapter> guard(weak_factory_.GetWeakPtr()); | 
|  | delegate_->OnWriteReady(); | 
|  | // OnWriteReady may delete the adapter. | 
|  | if (!guard) | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Write errors are fed back into BIO_read once the read buffer is empty. If | 
|  | // BIO_read is currently blocked, signal early that a read result is ready. | 
|  | if (result < 0 && read_result_ == ERR_IO_PENDING) | 
|  | delegate_->OnReadReady(); | 
|  | } | 
|  |  | 
|  | void SocketBIOAdapter::CallOnReadReady() { | 
|  | if (read_result_ == ERR_IO_PENDING) | 
|  | delegate_->OnReadReady(); | 
|  | } | 
|  |  | 
|  | SocketBIOAdapter* SocketBIOAdapter::GetAdapter(BIO* bio) { | 
|  | DCHECK_EQ(&kBIOMethod, bio->method); | 
|  | SocketBIOAdapter* adapter = reinterpret_cast<SocketBIOAdapter*>(bio->ptr); | 
|  | if (adapter) | 
|  | DCHECK_EQ(bio, adapter->bio()); | 
|  | return adapter; | 
|  | } | 
|  |  | 
|  | int SocketBIOAdapter::BIOWriteWrapper(BIO* bio, const char* in, int len) { | 
|  | BIO_clear_retry_flags(bio); | 
|  |  | 
|  | SocketBIOAdapter* adapter = GetAdapter(bio); | 
|  | if (!adapter) { | 
|  | OpenSSLPutNetError(FROM_HERE, ERR_UNEXPECTED); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return adapter->BIOWrite(in, len); | 
|  | } | 
|  |  | 
|  | int SocketBIOAdapter::BIOReadWrapper(BIO* bio, char* out, int len) { | 
|  | BIO_clear_retry_flags(bio); | 
|  |  | 
|  | SocketBIOAdapter* adapter = GetAdapter(bio); | 
|  | if (!adapter) { | 
|  | OpenSSLPutNetError(FROM_HERE, ERR_UNEXPECTED); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return adapter->BIORead(out, len); | 
|  | } | 
|  |  | 
|  | long SocketBIOAdapter::BIOCtrlWrapper(BIO* bio, | 
|  | int cmd, | 
|  | long larg, | 
|  | void* parg) { | 
|  | switch (cmd) { | 
|  | case BIO_CTRL_FLUSH: | 
|  | // The SSL stack requires BIOs handle BIO_flush. | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | NOTIMPLEMENTED(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | const BIO_METHOD SocketBIOAdapter::kBIOMethod = { | 
|  | 0,        // type (unused) | 
|  | nullptr,  // name (unused) | 
|  | SocketBIOAdapter::BIOWriteWrapper, | 
|  | SocketBIOAdapter::BIOReadWrapper, | 
|  | nullptr,  // puts | 
|  | nullptr,  // gets | 
|  | SocketBIOAdapter::BIOCtrlWrapper, | 
|  | nullptr,  // create | 
|  | nullptr,  // destroy | 
|  | nullptr,  // callback_ctrl | 
|  | }; | 
|  |  | 
|  | }  // namespace net |