blob: b6cac037c3d38c8ca5dd164463be4a19f43a294a [file] [log] [blame]
// Copyright 2020 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 "fuchsia/cast_streaming/receiver_udp_socket.h"
#include <algorithm>
#include <utility>
#include "base/bind.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<ReceiverUdpSocket>(client, local_endpoint));
}
} // namespace openscreen
namespace {
// UDP packaet size is limited to 64k.
constexpr size_t kBufferSize = 65536;
} // namespace
ReceiverUdpSocket::ReceiverUdpSocket(
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::IOBuffer>(kBufferSize)) {
DCHECK(client_);
}
ReceiverUdpSocket::~ReceiverUdpSocket() = default;
bool ReceiverUdpSocket::IsIPv4() const {
return local_endpoint_.address.IsV4();
}
bool ReceiverUdpSocket::IsIPv6() const {
return local_endpoint_.address.IsV6();
}
openscreen::IPEndpoint ReceiverUdpSocket::GetLocalEndpoint() const {
return local_endpoint_;
}
void ReceiverUdpSocket::Bind() {
net::IPEndPoint endpoint =
openscreen_platform::ToNetEndPoint(local_endpoint_);
int result = udp_socket_.Open(endpoint.GetFamily());
if (result != net::OK) {
SendErrorToClient(openscreen::Error::Code::kSocketBindFailure, result);
return;
}
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);
DoRead();
}
void ReceiverUdpSocket::SetMulticastOutboundInterface(
openscreen::NetworkInterfaceIndex ifindex) {
NOTIMPLEMENTED_LOG_ONCE();
}
void ReceiverUdpSocket::JoinMulticastGroup(
const openscreen::IPAddress& address,
openscreen::NetworkInterfaceIndex ifindex) {
int result = udp_socket_.SetMulticastInterface(ifindex);
if (result == net::OK)
udp_socket_.JoinGroup(openscreen_platform::ToNetAddress(address));
if (result != net::OK) {
SendErrorToClient(openscreen::Error::Code::kSocketOptionSettingFailure,
result);
}
}
void ReceiverUdpSocket::SendMessage(const void* data,
size_t length,
const openscreen::IPEndpoint& dest) {
// Do not attempt to send another UDP packet while a SendTo() operation is
// still pending.
if (send_pending_)
return;
auto buffer = base::MakeRefCounted<net::IOBuffer>(length);
memcpy(buffer->data(), data, length);
int result = udp_socket_.SendTo(
buffer.get(), length, openscreen_platform::ToNetEndPoint(dest),
base::BindOnce(&ReceiverUdpSocket::OnSendToCompleted,
base::Unretained(this)));
send_pending_ = true;
if (result != net::ERR_IO_PENDING)
OnSendToCompleted(result);
}
void ReceiverUdpSocket::SetDscp(openscreen::UdpSocket::DscpMode state) {
NOTIMPLEMENTED_LOG_ONCE();
}
void ReceiverUdpSocket::SendErrorToClient(
openscreen::Error::Code openscreen_error,
int net_error) {
client_->OnError(
this, openscreen::Error(openscreen_error, net::ErrorToString(net_error)));
}
void ReceiverUdpSocket::DoRead() {
while (true) {
int result = udp_socket_.RecvFrom(
read_buffer_.get(), kBufferSize, &from_address_,
base::BindOnce(&ReceiverUdpSocket::OnRecvFromCompleted,
base::Unretained(this)));
if (result == net::ERR_IO_PENDING || !HandleReadResult(result))
return;
}
}
bool ReceiverUdpSocket::HandleReadResult(int result) {
if (result < 0) {
client_->OnRead(
this, openscreen::Error(openscreen::Error::Code::kSocketReadFailure,
net::ErrorToString(result)));
return false;
}
DCHECK_GT(result, 0);
openscreen::UdpPacket packet(read_buffer_->data(),
read_buffer_->data() + result);
packet.set_socket(this);
packet.set_source(openscreen_platform::ToOpenScreenEndPoint(from_address_));
client_->OnRead(this, std::move(packet));
return true;
}
void ReceiverUdpSocket::OnRecvFromCompleted(int result) {
if (HandleReadResult(result))
DoRead();
}
void ReceiverUdpSocket::OnSendToCompleted(int result) {
send_pending_ = false;
if (result < 0) {
client_->OnSendError(
this, openscreen::Error(openscreen::Error::Code::kSocketSendFailure,
net::ErrorToString(result)));
return;
}
}