// Copyright 2019 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 "platform/impl/tls_connection_posix.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "absl/types/optional.h" #include "absl/types/span.h" #include "platform/api/task_runner.h" #include "platform/base/error.h" #include "platform/impl/stream_socket.h" #include "util/crypto/openssl_util.h" #include "util/osp_logging.h" namespace openscreen { // TODO(jophba, rwkeane): implement write blocking/unblocking TlsConnectionPosix::TlsConnectionPosix(IPEndpoint local_address, TaskRunner* task_runner) : task_runner_(task_runner), socket_(std::make_unique(local_address)) { OSP_DCHECK(task_runner_); } TlsConnectionPosix::TlsConnectionPosix(IPAddress::Version version, TaskRunner* task_runner) : task_runner_(task_runner), socket_(std::make_unique(version)) { OSP_DCHECK(task_runner_); } TlsConnectionPosix::TlsConnectionPosix(std::unique_ptr socket, TaskRunner* task_runner) : task_runner_(task_runner), socket_(std::move(socket)) { OSP_DCHECK(task_runner_); } TlsConnectionPosix::~TlsConnectionPosix() { if (platform_client_) { platform_client_->tls_data_router()->DeregisterConnection(this); } // TODO(issuetracker.google.com/169966671): This is only tested by CastSocket // E2E tests at the moment. if (ssl_) { SSL_shutdown(ssl_.get()); } } void TlsConnectionPosix::TryReceiveMessage() { OSP_DCHECK(ssl_); constexpr int kMaxApplicationDataBytes = 4096; std::vector block(kMaxApplicationDataBytes); ClearOpenSSLERRStack(CURRENT_LOCATION); const int bytes_read = SSL_read(ssl_.get(), block.data(), kMaxApplicationDataBytes); // Read operator was not successful, either due to a closed connection, // no application data available, an error occurred, or we have to take an // action. if (bytes_read <= 0) { const Error error = GetSSLError(ssl_.get(), bytes_read); if (!error.ok() && (error != Error::Code::kAgain)) { DispatchError(error); } return; } block.resize(bytes_read); task_runner_->PostTask([weak_this = weak_factory_.GetWeakPtr(), moved_block = std::move(block)]() mutable { if (auto* self = weak_this.get()) { if (auto* client = self->client_) { client->OnRead(self, std::move(moved_block)); } } }); } void TlsConnectionPosix::SetClient(Client* client) { OSP_DCHECK(task_runner_->IsRunningOnTaskRunner()); client_ = client; } bool TlsConnectionPosix::Send(const void* data, size_t len) { OSP_DCHECK(task_runner_->IsRunningOnTaskRunner()); return buffer_.Push(data, len); } IPEndpoint TlsConnectionPosix::GetRemoteEndpoint() const { OSP_DCHECK(task_runner_->IsRunningOnTaskRunner()); absl::optional endpoint = socket_->remote_address(); OSP_DCHECK(endpoint.has_value()); return endpoint.value(); } void TlsConnectionPosix::RegisterConnectionWithDataRouter( PlatformClientPosix* platform_client) { OSP_DCHECK(!platform_client_); platform_client_ = platform_client; platform_client_->tls_data_router()->RegisterConnection(this); } void TlsConnectionPosix::SendAvailableBytes() { absl::Span sendable_bytes = buffer_.GetReadableRegion(); if (sendable_bytes.empty()) { return; } ClearOpenSSLERRStack(CURRENT_LOCATION); const int result = SSL_write(ssl_.get(), sendable_bytes.data(), sendable_bytes.size()); if (result <= 0) { const Error result_error = GetSSLError(ssl_.get(), result); if (!result_error.ok() && (result_error.code() != Error::Code::kAgain)) { DispatchError(result_error); } } else { buffer_.Consume(static_cast(result)); } } void TlsConnectionPosix::DispatchError(Error error) { task_runner_->PostTask([weak_this = weak_factory_.GetWeakPtr(), moved_error = std::move(error)]() mutable { if (auto* self = weak_this.get()) { if (auto* client = self->client_) { client->OnError(self, std::move(moved_error)); } } }); } } // namespace openscreen