blob: f43b4526058a76a4753b5db0d8376386bee21ded [file] [log] [blame]
// Copyright 2022 The Fuchsia 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 "src/connectivity/network/mdns/service/services/service_subscriber_service_impl.h"
#include "src/connectivity/network/mdns/service/common/mdns_fidl_util.h"
#include "src/connectivity/network/mdns/service/common/mdns_names.h"
#include "src/connectivity/network/mdns/service/common/reply_address.h"
#include "src/connectivity/network/mdns/service/common/type_converters.h"
namespace mdns {
ServiceSubscriberServiceImpl::ServiceSubscriberServiceImpl(
Mdns& mdns, fidl::InterfaceRequest<fuchsia::net::mdns::ServiceSubscriber2> request,
fit::closure deleter)
: ServiceImplBase<fuchsia::net::mdns::ServiceSubscriber2>(mdns, std::move(request),
std::move(deleter)) {}
void ServiceSubscriberServiceImpl::SubscribeToService(
std::string service, fuchsia::net::mdns::ServiceSubscriptionOptions options,
fidl::InterfaceHandle<fuchsia::net::mdns::ServiceSubscriptionListener> listener_handle) {
if (!MdnsNames::IsValidServiceName(service)) {
FX_LOGS(ERROR) << "SubscribeToService called with invalid service name " << service
<< ", closing connection";
Quit();
return;
}
Media media = options.has_media() ? fidl::To<Media>(options.media()) : Media::kBoth;
IpVersions ip_versions =
options.has_ip_versions() ? fidl::To<IpVersions>(options.ip_versions()) : IpVersions::kBoth;
size_t id = next_service_instance_subscriber_id_++;
auto subscriber = std::make_unique<Subscriber>(
std::move(listener_handle), [this, id]() { service_instance_subscribers_by_id_.erase(id); });
bool include_local = !options.has_exclude_local() || !options.exclude_local();
bool include_local_proxies =
!options.has_exclude_local_proxies() || !options.exclude_local_proxies();
mdns().SubscribeToService(service, media, ip_versions, include_local, include_local_proxies,
subscriber.get());
service_instance_subscribers_by_id_.emplace(id, std::move(subscriber));
}
void ServiceSubscriberServiceImpl::SubscribeToAllServices(
fuchsia::net::mdns::ServiceSubscriptionOptions options,
fidl::InterfaceHandle<fuchsia::net::mdns::ServiceSubscriptionListener> listener_handle) {
Media media = options.has_media() ? fidl::To<Media>(options.media()) : Media::kBoth;
IpVersions ip_versions =
options.has_ip_versions() ? fidl::To<IpVersions>(options.ip_versions()) : IpVersions::kBoth;
size_t id = next_service_instance_subscriber_id_++;
auto subscriber = std::make_unique<Subscriber>(
std::move(listener_handle), [this, id]() { service_instance_subscribers_by_id_.erase(id); });
bool include_local = !options.has_exclude_local() || !options.exclude_local();
bool include_local_proxies =
!options.has_exclude_local_proxies() || !options.exclude_local_proxies();
mdns().SubscribeToAllServices(media, ip_versions, include_local, include_local_proxies,
subscriber.get());
service_instance_subscribers_by_id_.emplace(id, std::move(subscriber));
}
////////////////////////////////////////////////////////////////////////////////
// ServiceSubscriberServiceImpl::Subscriber implementation
ServiceSubscriberServiceImpl::Subscriber::Subscriber(
fidl::InterfaceHandle<fuchsia::net::mdns::ServiceSubscriptionListener> handle,
fit::closure deleter)
: deleter_(std::move(deleter)) {
client_.Bind(std::move(handle));
client_.set_error_handler([this](zx_status_t status) mutable { MaybeDelete(); });
}
ServiceSubscriberServiceImpl::Subscriber::~Subscriber() {
client_.set_error_handler(nullptr);
if (client_.is_bound()) {
client_.Unbind();
}
}
void ServiceSubscriberServiceImpl::Subscriber::InstanceDiscovered(
const std::string& service, const std::string& instance,
const std::vector<inet::SocketAddress>& addresses,
const std::vector<std::vector<uint8_t>>& text, uint16_t srv_priority, uint16_t srv_weight,
const std::string& target) {
Entry entry{.type = EntryType::kInstanceDiscovered};
MdnsFidlUtil::FillServiceInstance(&entry.service_instance, service, instance, addresses, text,
srv_priority, srv_weight, target);
entries_.push(std::move(entry));
MaybeSendNextEntry();
}
void ServiceSubscriberServiceImpl::Subscriber::InstanceChanged(
const std::string& service, const std::string& instance,
const std::vector<inet::SocketAddress>& addresses,
const std::vector<std::vector<uint8_t>>& text, uint16_t srv_priority, uint16_t srv_weight,
const std::string& target) {
Entry entry{.type = EntryType::kInstanceChanged};
MdnsFidlUtil::FillServiceInstance(&entry.service_instance, service, instance, addresses, text,
srv_priority, srv_weight, target);
entries_.push(std::move(entry));
MaybeSendNextEntry();
}
void ServiceSubscriberServiceImpl::Subscriber::InstanceLost(const std::string& service,
const std::string& instance) {
Entry entry{.type = EntryType::kInstanceLost};
entry.service_instance.set_service(service);
entry.service_instance.set_instance(instance);
entries_.push(std::move(entry));
MaybeSendNextEntry();
}
void ServiceSubscriberServiceImpl::Subscriber::Query(DnsType type_queried) {
entries_.push({.type = EntryType::kQuery, .type_queried = type_queried});
MaybeSendNextEntry();
}
void ServiceSubscriberServiceImpl::Subscriber::MaybeSendNextEntry() {
FX_DCHECK(pipeline_depth_ <= kMaxPipelineDepth);
if (pipeline_depth_ == kMaxPipelineDepth || entries_.empty()) {
return;
}
Entry& entry = entries_.front();
auto on_reply = fit::bind_member<&ServiceSubscriberServiceImpl::Subscriber::ReplyReceived>(this);
// The error handler for |client_| may be called synchronously in any of the proxy calls below.
// To ensure |this| doesn't get deleted while this method is running, we defer deletion until
// the method terminates.
DeferDeletion();
FX_DCHECK(client_);
switch (entry.type) {
case EntryType::kInstanceDiscovered:
client_->OnInstanceDiscovered(std::move(entry.service_instance), std::move(on_reply));
break;
case EntryType::kInstanceChanged:
client_->OnInstanceChanged(std::move(entry.service_instance), std::move(on_reply));
break;
case EntryType::kInstanceLost:
client_->OnInstanceLost(entry.service_instance.service(), entry.service_instance.instance(),
std::move(on_reply));
break;
case EntryType::kQuery:
client_->OnQuery(fidl::To<fuchsia::net::mdns::ResourceType>(entry.type_queried),
std::move(on_reply));
break;
}
++pipeline_depth_;
entries_.pop();
MaybeDelete();
}
void ServiceSubscriberServiceImpl::Subscriber::ReplyReceived() {
FX_DCHECK(pipeline_depth_ != 0);
--pipeline_depth_;
MaybeSendNextEntry();
}
void ServiceSubscriberServiceImpl::Subscriber::DeferDeletion() { ++one_based_delete_counter_; }
void ServiceSubscriberServiceImpl::Subscriber::MaybeDelete() {
if (--one_based_delete_counter_ != 0) {
return;
}
client_.set_error_handler(nullptr);
client_.Unbind();
deleter_();
}
} // namespace mdns