You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
230 lines
7.8 KiB
230 lines
7.8 KiB
// Copyright 2018 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 "osp/impl/internal_services.h"
|
|
|
|
#include <algorithm>
|
|
#include <utility>
|
|
|
|
#include "osp/impl/discovery/mdns/mdns_responder_adapter_impl.h"
|
|
#include "osp/impl/mdns_responder_service.h"
|
|
#include "platform/api/udp_socket.h"
|
|
#include "platform/base/error.h"
|
|
#include "util/osp_logging.h"
|
|
|
|
namespace openscreen {
|
|
namespace osp {
|
|
namespace {
|
|
|
|
constexpr char kServiceName[] = "_openscreen";
|
|
constexpr char kServiceProtocol[] = "_udp";
|
|
const IPAddress kMulticastAddress{224, 0, 0, 251};
|
|
const IPAddress kMulticastIPv6Address{
|
|
// ff02::fb
|
|
0xff02, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00fb,
|
|
};
|
|
const uint16_t kMulticastListeningPort = 5353;
|
|
|
|
class MdnsResponderAdapterImplFactory final
|
|
: public MdnsResponderAdapterFactory {
|
|
public:
|
|
MdnsResponderAdapterImplFactory() = default;
|
|
~MdnsResponderAdapterImplFactory() override = default;
|
|
|
|
std::unique_ptr<MdnsResponderAdapter> Create() override {
|
|
return std::make_unique<MdnsResponderAdapterImpl>();
|
|
}
|
|
};
|
|
|
|
Error SetUpMulticastSocket(UdpSocket* socket, NetworkInterfaceIndex ifindex) {
|
|
const IPAddress broadcast_address =
|
|
socket->IsIPv6() ? kMulticastIPv6Address : kMulticastAddress;
|
|
|
|
socket->JoinMulticastGroup(broadcast_address, ifindex);
|
|
socket->SetMulticastOutboundInterface(ifindex);
|
|
socket->Bind();
|
|
|
|
return Error::None();
|
|
}
|
|
|
|
// Ref-counted singleton instance of InternalServices. This lives only as long
|
|
// as there is at least one ServiceListener and/or ServicePublisher alive.
|
|
InternalServices* g_instance = nullptr;
|
|
int g_instance_ref_count = 0;
|
|
|
|
} // namespace
|
|
|
|
// static
|
|
std::unique_ptr<ServiceListener> InternalServices::CreateListener(
|
|
const MdnsServiceListenerConfig& config,
|
|
ServiceListener::Observer* observer,
|
|
TaskRunner* task_runner) {
|
|
auto* services = ReferenceSingleton(task_runner);
|
|
auto listener =
|
|
std::make_unique<ServiceListenerImpl>(&services->mdns_service_);
|
|
listener->AddObserver(observer);
|
|
listener->SetDestructionCallback(&InternalServices::DereferenceSingleton,
|
|
services);
|
|
return listener;
|
|
}
|
|
|
|
// static
|
|
std::unique_ptr<ServicePublisher> InternalServices::CreatePublisher(
|
|
const ServicePublisher::Config& config,
|
|
ServicePublisher::Observer* observer,
|
|
TaskRunner* task_runner) {
|
|
auto* services = ReferenceSingleton(task_runner);
|
|
services->mdns_service_.SetServiceConfig(
|
|
config.hostname, config.service_instance_name,
|
|
config.connection_server_port, config.network_interface_indices,
|
|
{{"fn", config.friendly_name}});
|
|
auto publisher = std::make_unique<ServicePublisherImpl>(
|
|
observer, &services->mdns_service_);
|
|
publisher->SetDestructionCallback(&InternalServices::DereferenceSingleton,
|
|
services);
|
|
return publisher;
|
|
}
|
|
|
|
InternalServices::InternalPlatformLinkage::InternalPlatformLinkage(
|
|
InternalServices* parent)
|
|
: parent_(parent) {}
|
|
|
|
InternalServices::InternalPlatformLinkage::~InternalPlatformLinkage() {
|
|
// If there are open sockets, then there will be dangling references to
|
|
// destroyed objects after destruction.
|
|
OSP_CHECK(open_sockets_.empty());
|
|
}
|
|
|
|
std::vector<MdnsPlatformService::BoundInterface>
|
|
InternalServices::InternalPlatformLinkage::RegisterInterfaces(
|
|
const std::vector<NetworkInterfaceIndex>& allowlist) {
|
|
const std::vector<InterfaceInfo> interfaces = GetNetworkInterfaces();
|
|
const bool do_filter_using_allowlist = !allowlist.empty();
|
|
std::vector<NetworkInterfaceIndex> index_list;
|
|
for (const auto& interface : interfaces) {
|
|
OSP_VLOG << "Found interface: " << interface;
|
|
if (do_filter_using_allowlist &&
|
|
std::find(allowlist.begin(), allowlist.end(), interface.index) ==
|
|
allowlist.end()) {
|
|
OSP_VLOG << "Ignoring interface not in allowed list: " << interface;
|
|
continue;
|
|
}
|
|
if (!interface.addresses.empty())
|
|
index_list.push_back(interface.index);
|
|
}
|
|
OSP_LOG_IF(WARN, index_list.empty())
|
|
<< "No network interfaces had usable addresses for mDNS.";
|
|
|
|
// Set up sockets to send and listen to mDNS multicast traffic on all
|
|
// interfaces.
|
|
std::vector<BoundInterface> result;
|
|
for (NetworkInterfaceIndex index : index_list) {
|
|
const auto& interface = *std::find_if(
|
|
interfaces.begin(), interfaces.end(),
|
|
[index](const InterfaceInfo& info) { return info.index == index; });
|
|
if (interface.addresses.empty()) {
|
|
continue;
|
|
}
|
|
|
|
// Pick any address for the given interface.
|
|
const IPSubnet& primary_subnet = interface.addresses.front();
|
|
|
|
auto create_result =
|
|
UdpSocket::Create(parent_->task_runner_, parent_,
|
|
IPEndpoint{{}, kMulticastListeningPort});
|
|
if (!create_result) {
|
|
OSP_LOG_ERROR << "failed to create socket for interface " << index << ": "
|
|
<< create_result.error().message();
|
|
continue;
|
|
}
|
|
std::unique_ptr<UdpSocket> socket = std::move(create_result.value());
|
|
if (!SetUpMulticastSocket(socket.get(), index).ok()) {
|
|
continue;
|
|
}
|
|
result.emplace_back(interface, primary_subnet, socket.get());
|
|
parent_->RegisterMdnsSocket(socket.get());
|
|
|
|
open_sockets_.emplace_back(std::move(socket));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void InternalServices::InternalPlatformLinkage::DeregisterInterfaces(
|
|
const std::vector<BoundInterface>& registered_interfaces) {
|
|
for (const auto& interface : registered_interfaces) {
|
|
UdpSocket* const socket = interface.socket;
|
|
parent_->DeregisterMdnsSocket(socket);
|
|
|
|
const auto it = std::find_if(open_sockets_.begin(), open_sockets_.end(),
|
|
[socket](const std::unique_ptr<UdpSocket>& s) {
|
|
return s.get() == socket;
|
|
});
|
|
OSP_DCHECK(it != open_sockets_.end());
|
|
open_sockets_.erase(it);
|
|
}
|
|
}
|
|
|
|
InternalServices::InternalServices(ClockNowFunctionPtr now_function,
|
|
TaskRunner* task_runner)
|
|
: mdns_service_(now_function,
|
|
task_runner,
|
|
kServiceName,
|
|
kServiceProtocol,
|
|
std::make_unique<MdnsResponderAdapterImplFactory>(),
|
|
std::make_unique<InternalPlatformLinkage>(this)),
|
|
task_runner_(task_runner) {}
|
|
|
|
InternalServices::~InternalServices() = default;
|
|
|
|
void InternalServices::RegisterMdnsSocket(UdpSocket* socket) {
|
|
OSP_CHECK(g_instance) << "No listener or publisher is alive.";
|
|
// TODO(rwkeane): Hook this up to the new mDNS library once we swap out the
|
|
// mDNSResponder.
|
|
}
|
|
|
|
void InternalServices::DeregisterMdnsSocket(UdpSocket* socket) {
|
|
// TODO(rwkeane): Hook this up to the new mDNS library once we swap out the
|
|
// mDNSResponder.
|
|
}
|
|
|
|
// static
|
|
InternalServices* InternalServices::ReferenceSingleton(
|
|
TaskRunner* task_runner) {
|
|
if (!g_instance) {
|
|
OSP_CHECK_EQ(g_instance_ref_count, 0);
|
|
g_instance = new InternalServices(&Clock::now, task_runner);
|
|
}
|
|
++g_instance_ref_count;
|
|
return g_instance;
|
|
}
|
|
|
|
// static
|
|
void InternalServices::DereferenceSingleton(void* instance) {
|
|
OSP_CHECK_EQ(static_cast<InternalServices*>(instance), g_instance);
|
|
OSP_CHECK_GT(g_instance_ref_count, 0);
|
|
--g_instance_ref_count;
|
|
if (g_instance_ref_count == 0) {
|
|
delete g_instance;
|
|
g_instance = nullptr;
|
|
}
|
|
}
|
|
|
|
void InternalServices::OnError(UdpSocket* socket, Error error) {
|
|
OSP_LOG_ERROR << "failed to configure socket " << error.message();
|
|
this->DeregisterMdnsSocket(socket);
|
|
}
|
|
|
|
void InternalServices::OnSendError(UdpSocket* socket, Error error) {
|
|
// TODO(crbug.com/openscreen/67): Implement this method.
|
|
OSP_UNIMPLEMENTED();
|
|
}
|
|
|
|
void InternalServices::OnRead(UdpSocket* socket, ErrorOr<UdpPacket> packet) {
|
|
g_instance->mdns_service_.OnRead(socket, std::move(packet));
|
|
}
|
|
|
|
} // namespace osp
|
|
} // namespace openscreen
|