| // Copyright 2020 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "components/openscreen_platform/net_udp_socket.h" | |
| #include <algorithm> | |
| #include <utility> | |
| #include "base/functional/bind.h" | |
| #include "base/notimplemented.h" | |
| #include "components/openscreen_platform/network_util.h" | |
| #include "net/base/net_errors.h" | |
| namespace openscreen { | |
| // static | |
| ErrorOr<std::unique_ptr<UdpSocket>> UdpSocket::Create( | |
| TaskRunner& task_runner, | |
| Client* client, | |
| const IPEndpoint& local_endpoint) { | |
| return ErrorOr<std::unique_ptr<UdpSocket>>( | |
| std::make_unique<openscreen_platform::NetUdpSocket>(client, | |
| local_endpoint)); | |
| } | |
| } // namespace openscreen | |
| namespace openscreen_platform { | |
| NetUdpSocket::NetUdpSocket(openscreen::UdpSocket::Client* client, | |
| const openscreen::IPEndpoint& local_endpoint) | |
| : client_(client), | |
| local_endpoint_(local_endpoint), | |
| udp_socket_(net::DatagramSocket::DEFAULT_BIND, | |
| nullptr /* net_log */, | |
| net::NetLogSource()), | |
| read_buffer_(base::MakeRefCounted<net::IOBufferWithSize>( | |
| openscreen::UdpPacket::kUdpMaxPacketSize)) { | |
| DVLOG(1) << __func__; | |
| DCHECK(client_); | |
| } | |
| NetUdpSocket::~NetUdpSocket() = default; | |
| void NetUdpSocket::SendErrorToClient(openscreen::Error::Code openscreen_error, | |
| int net_error) { | |
| DVLOG(1) << __func__; | |
| RunClientCallback([this, openscreen_error, net_error](Client& client) { | |
| client.OnError(this, openscreen::Error(openscreen_error, | |
| net::ErrorToString(net_error))); | |
| }); | |
| } | |
| void NetUdpSocket::DoRead() { | |
| DVLOG(3) << __func__; | |
| base::WeakPtr<NetUdpSocket> weak_this = weak_ptr_factory_.GetWeakPtr(); | |
| while (weak_this && | |
| HandleRecvFromResult(udp_socket_.RecvFrom( | |
| read_buffer_.get(), openscreen::UdpPacket::kUdpMaxPacketSize, | |
| &from_address_, | |
| base::BindOnce(&NetUdpSocket::OnRecvFromCompleted, | |
| weak_ptr_factory_.GetWeakPtr())))) { | |
| } | |
| } | |
| bool NetUdpSocket::HandleRecvFromResult(int result) { | |
| DVLOG(3) << __func__; | |
| if (result == net::ERR_IO_PENDING) { | |
| return false; | |
| } | |
| if (result < 0) { | |
| RunClientCallback([this, result](Client& client) { | |
| client.OnRead( | |
| this, openscreen::Error(openscreen::Error::Code::kSocketReadFailure, | |
| net::ErrorToString(result))); | |
| }); | |
| return false; | |
| } | |
| DCHECK_GT(result, 0); | |
| base::span<const uint8_t> packet_data = | |
| read_buffer_->first(static_cast<size_t>(result)); | |
| openscreen::UdpPacket packet(packet_data.begin(), packet_data.end()); | |
| packet.set_source(openscreen_platform::ToOpenScreenEndPoint(from_address_)); | |
| return RunClientCallback([this, &packet](Client& client) { | |
| client.OnRead(this, std::move(packet)); | |
| }); | |
| } | |
| void NetUdpSocket::OnRecvFromCompleted(int result) { | |
| DVLOG(3) << __func__; | |
| if (HandleRecvFromResult(result)) { | |
| DoRead(); | |
| } | |
| } | |
| void NetUdpSocket::OnSendToCompleted(int result) { | |
| DVLOG(3) << __func__; | |
| send_pending_ = false; | |
| if (result < 0) { | |
| RunClientCallback([this, result](Client& client) { | |
| client.OnSendError( | |
| this, openscreen::Error(openscreen::Error::Code::kSocketSendFailure, | |
| net::ErrorToString(result))); | |
| }); | |
| } | |
| } | |
| bool NetUdpSocket::IsIPv4() const { | |
| DVLOG(2) << __func__; | |
| return local_endpoint_.address.IsV4(); | |
| } | |
| bool NetUdpSocket::IsIPv6() const { | |
| DVLOG(2) << __func__; | |
| return local_endpoint_.address.IsV6(); | |
| } | |
| openscreen::IPEndpoint NetUdpSocket::GetLocalEndpoint() const { | |
| DVLOG(2) << __func__; | |
| return local_endpoint_; | |
| } | |
| void NetUdpSocket::Bind() { | |
| DVLOG(1) << __func__; | |
| net::IPEndPoint endpoint = | |
| openscreen_platform::ToNetEndPoint(local_endpoint_); | |
| int result = udp_socket_.Open(endpoint.GetFamily()); | |
| if (result == net::OK) { | |
| result = udp_socket_.Bind(endpoint); | |
| } | |
| net::IPEndPoint local_endpoint; | |
| if (result == net::OK) { | |
| result = udp_socket_.GetLocalAddress(&local_endpoint); | |
| } | |
| if (result != net::OK) { | |
| SendErrorToClient(openscreen::Error::Code::kSocketBindFailure, result); | |
| return; | |
| } | |
| local_endpoint_ = openscreen_platform::ToOpenScreenEndPoint(local_endpoint); | |
| if (RunClientCallback([this](Client& client) { client.OnBound(this); })) { | |
| DoRead(); | |
| } | |
| } | |
| void NetUdpSocket::SetMulticastOutboundInterface( | |
| openscreen::NetworkInterfaceIndex ifindex) { | |
| DVLOG(1) << __func__; | |
| const int result = udp_socket_.SetMulticastInterface(ifindex); | |
| if (result != net::OK) { | |
| SendErrorToClient(openscreen::Error::Code::kSocketOptionSettingFailure, | |
| result); | |
| } | |
| } | |
| void NetUdpSocket::JoinMulticastGroup( | |
| const openscreen::IPAddress& address, | |
| openscreen::NetworkInterfaceIndex ifindex) { | |
| DVLOG(1) << __func__; | |
| const int result = udp_socket_.SetMulticastInterface(ifindex); | |
| if (result == net::OK) { | |
| udp_socket_.JoinGroup(openscreen_platform::ToNetAddress(address)); | |
| } else { | |
| SendErrorToClient(openscreen::Error::Code::kSocketOptionSettingFailure, | |
| result); | |
| } | |
| } | |
| void NetUdpSocket::SendMessage(openscreen::ByteView data, | |
| const openscreen::IPEndpoint& dest) { | |
| DVLOG(3) << __func__; | |
| if (send_pending_) { | |
| RunClientCallback([this](Client& client) { | |
| client.OnSendError(this, | |
| openscreen::Error(openscreen::Error::Code::kAgain)); | |
| }); | |
| return; | |
| } | |
| auto buffer = base::MakeRefCounted<net::IOBufferWithSize>(data.size()); | |
| buffer->span().copy_from_nonoverlapping(base::span(data)); | |
| base::WeakPtr<NetUdpSocket> weak_this = weak_ptr_factory_.GetWeakPtr(); | |
| const int result = udp_socket_.SendTo( | |
| buffer.get(), data.size(), openscreen_platform::ToNetEndPoint(dest), | |
| base::BindOnce(&NetUdpSocket::OnSendToCompleted, | |
| weak_ptr_factory_.GetWeakPtr())); | |
| send_pending_ = true; | |
| if (result != net::ERR_IO_PENDING && weak_this) { | |
| OnSendToCompleted(result); | |
| } | |
| } | |
| void NetUdpSocket::SetDscp(openscreen::UdpSocket::DscpMode state) { | |
| NOTIMPLEMENTED_LOG_ONCE(); | |
| } | |
| } // namespace openscreen_platform |