| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "extensions/browser/api/socket/tls_socket.h" |
| |
| #include <utility> |
| |
| #include "base/check_op.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "extensions/browser/api/api_resource.h" |
| #include "extensions/browser/api/socket/mojo_data_pump.h" |
| #include "net/base/address_list.h" |
| #include "net/base/ip_endpoint.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/url_util.h" |
| #include "url/url_canon.h" |
| |
| namespace extensions { |
| |
| const char kTLSSocketTypeInvalidError[] = |
| "Cannot listen on a socket that is already connected."; |
| |
| TLSSocket::TLSSocket( |
| mojo::PendingRemote<network::mojom::TLSClientSocket> tls_socket, |
| const net::IPEndPoint& local_addr, |
| const net::IPEndPoint& peer_addr, |
| mojo::ScopedDataPipeConsumerHandle receive_stream, |
| mojo::ScopedDataPipeProducerHandle send_stream, |
| const std::string& owner_extension_id) |
| : ResumableTCPSocket(nullptr, owner_extension_id), |
| tls_socket_(std::move(tls_socket)), |
| local_addr_(local_addr), |
| peer_addr_(peer_addr), |
| mojo_data_pump_(std::make_unique<MojoDataPump>(std::move(receive_stream), |
| std::move(send_stream))) { |
| DCHECK(tls_socket_); |
| is_connected_ = true; |
| } |
| |
| TLSSocket::~TLSSocket() { |
| Disconnect(true /* socket_destroying */); |
| } |
| |
| void TLSSocket::Connect(const net::AddressList& address, |
| net::CompletionOnceCallback callback) { |
| std::move(callback).Run(net::ERR_CONNECTION_FAILED); |
| } |
| |
| void TLSSocket::Disconnect(bool socket_destroying) { |
| is_connected_ = false; |
| tls_socket_.reset(); |
| local_addr_ = std::nullopt; |
| peer_addr_ = std::nullopt; |
| mojo_data_pump_ = nullptr; |
| // TODO(devlin): Should we do this for all callbacks? |
| if (read_callback_) { |
| std::move(read_callback_) |
| .Run(net::ERR_CONNECTION_CLOSED, nullptr, socket_destroying); |
| } |
| } |
| |
| void TLSSocket::Read(int count, ReadCompletionCallback callback) { |
| DCHECK(callback); |
| DCHECK(!read_callback_); |
| |
| const bool socket_destroying = false; |
| if (!mojo_data_pump_) { |
| std::move(callback).Run(net::ERR_SOCKET_NOT_CONNECTED, nullptr, |
| socket_destroying); |
| return; |
| } |
| if (mojo_data_pump_->HasPendingRead()) { |
| std::move(callback).Run(net::ERR_IO_PENDING, nullptr, socket_destroying); |
| return; |
| } |
| read_callback_ = std::move(callback); |
| mojo_data_pump_->Read(count, base::BindOnce(&TLSSocket::OnReadComplete, |
| base::Unretained(this))); |
| } |
| |
| void TLSSocket::Listen(const std::string& address, |
| uint16_t port, |
| int backlog, |
| ListenCallback callback) { |
| std::move(callback).Run(net::ERR_NOT_IMPLEMENTED, kTLSSocketTypeInvalidError); |
| } |
| |
| bool TLSSocket::IsConnected() { |
| return is_connected_; |
| } |
| |
| bool TLSSocket::GetPeerAddress(net::IPEndPoint* address) { |
| if (!IsConnected()) |
| return false; |
| if (!peer_addr_) |
| return false; |
| *address = peer_addr_.value(); |
| return true; |
| } |
| |
| bool TLSSocket::GetLocalAddress(net::IPEndPoint* address) { |
| if (!IsConnected()) |
| return false; |
| if (!local_addr_) |
| return false; |
| *address = local_addr_.value(); |
| return true; |
| } |
| |
| Socket::SocketType TLSSocket::GetSocketType() const { |
| return Socket::TYPE_TLS; |
| } |
| |
| int TLSSocket::WriteImpl(net::IOBuffer* io_buffer, |
| int io_buffer_size, |
| net::CompletionOnceCallback callback) { |
| if (!mojo_data_pump_) |
| return net::ERR_SOCKET_NOT_CONNECTED; |
| mojo_data_pump_->Write( |
| io_buffer, io_buffer_size, |
| base::BindOnce(&TLSSocket::OnWriteComplete, base::Unretained(this), |
| std::move(callback))); |
| return net::ERR_IO_PENDING; |
| } |
| |
| void TLSSocket::OnWriteComplete(net::CompletionOnceCallback callback, |
| int result) { |
| if (result < 0) { |
| // Write side has terminated. This can be an error or a graceful close. |
| // TCPSocketEventDispatcher doesn't distinguish between the two. |
| Disconnect(false /* socket_destroying */); |
| } |
| std::move(callback).Run(result); |
| } |
| |
| void TLSSocket::OnReadComplete(int result, |
| scoped_refptr<net::IOBuffer> io_buffer) { |
| DCHECK(read_callback_); |
| DCHECK_GE(result, 0); |
| |
| // Use a local variable for |read_callback_|, because otherwise Disconnect() |
| // will try to invoke |read_callback_| with a hardcoded result code. |
| ReadCompletionCallback callback = std::move(read_callback_); |
| if (result == 0) { |
| // Read side has terminated. This can be an error or a graceful close. |
| // TCPSocketEventDispatcher doesn't distinguish between the two. Treat them |
| // as connection close. |
| Disconnect(false /* socket_destroying */); |
| } |
| std::move(callback).Run(result, io_buffer, false /* socket_destroying */); |
| } |
| |
| } // namespace extensions |