123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399 |
- // 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/check_op.h"
- #include "base/location.h"
- #include "base/notreached.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 "third_party/boringssl/src/include/openssl/bio.h"
- namespace {
- const 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),
- write_buffer_capacity_(write_buffer_capacity),
- delegate_(delegate) {
- 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 = socket_->ReadIfReady(
- read_buffer_.get(), read_buffer_capacity_,
- base::BindOnce(&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_);
- memcpy(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);
- memcpy(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_);
- memcpy(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::BindOnce(&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
|