| // Copyright 2017 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. |
| |
| #ifndef GARNET_BIN_MDNS_SERVICE_MDNS_H_ |
| #define GARNET_BIN_MDNS_SERVICE_MDNS_H_ |
| |
| #include <fuchsia/netstack/cpp/fidl.h> |
| #include <lib/async/dispatcher.h> |
| #include <lib/fit/function.h> |
| #include <memory> |
| #include <queue> |
| #include <string> |
| #include <unordered_map> |
| #include <vector> |
| #include "garnet/bin/mdns/service/dns_message.h" |
| #include "garnet/bin/mdns/service/mdns_agent.h" |
| #include "garnet/bin/mdns/service/mdns_transceiver.h" |
| #include "garnet/lib/inet/socket_address.h" |
| #include "lib/fxl/macros.h" |
| #include "lib/fxl/time/time_point.h" |
| |
| namespace mdns { |
| |
| class InstanceProber; |
| class InstanceRequestor; |
| class InstanceResponder; |
| class ResourceRenewer; |
| |
| // Implements mDNS. |
| class Mdns : public MdnsAgent::Host { |
| public: |
| // Describes an initial instance publication or query response. |
| struct Publication { |
| static std::unique_ptr<Publication> Create( |
| inet::IpPort port, |
| const std::vector<std::string>& text = std::vector<std::string>()); |
| |
| inet::IpPort port_; |
| std::vector<std::string> text_; |
| uint32_t ptr_ttl_seconds = 4500; // default 75 minutes |
| uint32_t srv_ttl_seconds = 120; // default 2 minutes |
| uint32_t txt_ttl_seconds = 4500; // default 75 minutes |
| }; |
| |
| // Abstract base class for client-supplied subscriber. |
| class Subscriber { |
| public: |
| virtual ~Subscriber(); |
| |
| // Unsubscribes from the service. If this |Subscriber| is already |
| // unsubscribed, this method does nothing. |
| void Unsubscribe(); |
| |
| // Called when a new instance is discovered. |
| virtual void InstanceDiscovered(const std::string& service, |
| const std::string& instance, |
| const inet::SocketAddress& v4_address, |
| const inet::SocketAddress& v6_address, |
| const std::vector<std::string>& text) = 0; |
| |
| // Called when a previously discovered instance changes addresses or text. |
| virtual void InstanceChanged(const std::string& service, |
| const std::string& instance, |
| const inet::SocketAddress& v4_address, |
| const inet::SocketAddress& v6_address, |
| const std::vector<std::string>& text) = 0; |
| |
| // Called when an instance is lost. |
| virtual void InstanceLost(const std::string& service, |
| const std::string& instance) = 0; |
| |
| // Called to indicate that instance changes are complete for now. |
| virtual void UpdatesComplete() = 0; |
| |
| protected: |
| Subscriber() {} |
| |
| private: |
| void Connect(std::shared_ptr<InstanceRequestor> instance_requestor); |
| |
| std::shared_ptr<InstanceRequestor> instance_subscriber_; |
| |
| friend class Mdns; |
| }; |
| |
| // Abstract base class for client-supplied publisher. |
| class Publisher { |
| public: |
| virtual ~Publisher(); |
| |
| // Sets subtypes for the service instance. If this |Publisher| is |
| // unpublished, this method does nothing. |
| void SetSubtypes(std::vector<std::string> subtypes); |
| |
| // Initiates announcement of the service instance. If this |Publisher| is |
| // unpublished, this method does nothing. |
| void Reannounce(); |
| |
| // Unpublishes the service instance. If this |Publisher| is already |
| // unpublished, this method does nothing. |
| void Unpublish(); |
| |
| // Reports whether the publication attempt was successful. Publication can |
| // fail if the service instance is currently be published by another device |
| // on the subnet. |
| virtual void ReportSuccess(bool success) = 0; |
| |
| // Provides instance information for initial announcements and query |
| // responses relating to the service instance specified in |AddResponder|. |
| // |query| indicates whether data is requested for an initial announcement |
| // (false) or in response to a query (true). If the publication relates to |
| // a subtype of the service, |subtype| contains the subtype, otherwise it is |
| // empty. If the publication provided by the callback is null, no |
| // announcement or response is transmitted. |
| virtual void GetPublication( |
| bool query, const std::string& subtype, |
| fit::function<void(std::unique_ptr<Publication>)> callback) = 0; |
| |
| protected: |
| Publisher() {} |
| |
| private: |
| void Connect(std::shared_ptr<InstanceResponder> instance_responder); |
| |
| std::shared_ptr<InstanceResponder> instance_responder_; |
| |
| friend class Mdns; |
| }; |
| |
| using ResolveHostNameCallback = fit::function<void( |
| const std::string& host_name, const inet::IpAddress& v4_address, |
| const inet::IpAddress& v6_address)>; |
| |
| Mdns(); |
| |
| virtual ~Mdns() override; |
| |
| // Determines whether message traffic will be logged. |
| void SetVerbose(bool verbose); |
| |
| // Starts the transceiver. |
| void Start(fuchsia::netstack::NetstackPtr, const std::string& host_name); |
| |
| // Stops the transceiver. |
| void Stop(); |
| |
| // Returns the host name currently in use. May be different than the host name |
| // passed in to |Start| if address probing detected conflicts. |
| std::string host_name() { return host_name_; } |
| |
| // Resolves |host_name| to one or two |IpAddress|es. |
| void ResolveHostName(const std::string& host_name, fxl::TimePoint timeout, |
| ResolveHostNameCallback callback); |
| |
| // Subscribes to the specified service. The subscription is cancelled when |
| // the subscriber is deleted or its |Unsubscribe| method is called. |
| // Multiple subscriptions may be created for a given service name. |
| void SubscribeToService(const std::string& service_name, |
| Subscriber* subscriber); |
| |
| // Publishes a service instance. Returns false if and only if the instance was |
| // already published locally. The instance is unpublished when the publisher |
| // is deleted or its |Unpublish| method is called. |
| bool PublishServiceInstance(const std::string& service_name, |
| const std::string& instance_name, |
| Publisher* publisher); |
| |
| // Writes log messages describing lifetime traffic. |
| void LogTraffic(); |
| |
| private: |
| enum class State { |
| kNotStarted, |
| kWaitingForInterfaces, |
| kAddressProbeInProgress, |
| kActive, |
| }; |
| |
| struct TaskQueueEntry { |
| TaskQueueEntry(MdnsAgent* agent, fit::closure task, fxl::TimePoint time) |
| : agent_(agent), task_(std::move(task)), time_(time) {} |
| |
| MdnsAgent* agent_; |
| // mutable because std::priority_queue doesn't provide a non-const accessor |
| // for its contents which makes it otherwise impossible to move the closure |
| // out of the queue when it is time to dispatch the task |
| mutable fit::closure task_; |
| fxl::TimePoint time_; |
| |
| bool operator<(const TaskQueueEntry& other) const { |
| return time_ > other.time_; |
| } |
| }; |
| |
| struct ReplyAddressHash { |
| std::size_t operator()(const ReplyAddress& reply_address) const noexcept { |
| return std::hash<inet::SocketAddress>{}(reply_address.socket_address()) ^ |
| (std::hash<inet::IpAddress>{}(reply_address.interface_address()) |
| << 1); |
| } |
| }; |
| |
| // Starts a probe for a conflicting host name. If a conflict is detected, a |
| // new name is generated and this method is called again. If no conflict is |
| // detected, |host_full_name_| gets set and the service is ready to start |
| // other agents. |
| void StartAddressProbe(const std::string& host_name); |
| |
| // Determines what host name to try next after a conflict is detected and |
| // calls |StartAddressProbe| with that name. |
| void OnHostNameConflict(); |
| |
| // MdnsAgent::Host implementation. |
| void PostTaskForTime(MdnsAgent* agent, fit::closure task, |
| fxl::TimePoint target_time) override; |
| |
| void SendQuestion(std::shared_ptr<DnsQuestion> question) override; |
| |
| void SendResource(std::shared_ptr<DnsResource> resource, |
| MdnsResourceSection section, |
| const ReplyAddress& reply_address) override; |
| |
| void SendAddresses(MdnsResourceSection section, |
| const ReplyAddress& reply_address) override; |
| |
| void Renew(const DnsResource& resource) override; |
| |
| void RemoveAgent(const MdnsAgent* agent, |
| const std::string& published_instance_full_name) override; |
| |
| // Adds an agent and, if |started_|, starts it. |
| void AddAgent(std::shared_ptr<MdnsAgent> agent); |
| |
| // Adds an instance responder. |
| bool ProbeAndAddInstanceResponder(const std::string& service_name, |
| const std::string& instance_name, |
| inet::IpPort port, |
| std::shared_ptr<InstanceResponder> agent); |
| |
| // Sends any messages found in |outbound_messages_by_reply_address_| and |
| // clears |outbound_messages_by_reply_address_|. |
| void SendMessages(); |
| |
| // Distributes questions to all the agents except the resource renewer. |
| void ReceiveQuestion(const DnsQuestion& question, |
| const ReplyAddress& reply_address); |
| |
| // Distributes resources to all the agents, starting with the resource |
| // renewer. |
| void ReceiveResource(const DnsResource& resource, |
| MdnsResourceSection section); |
| |
| // Runs tasks in |task_queue_| using |dispatcher_|. |
| void PostTask(); |
| |
| async_dispatcher_t* dispatcher_; |
| MdnsTransceiver transceiver_; |
| std::string original_host_name_; |
| uint32_t next_host_name_deduplicator_ = 2; |
| std::string host_name_; |
| std::string host_full_name_; |
| State state_ = State::kNotStarted; |
| std::priority_queue<TaskQueueEntry> task_queue_; |
| fxl::TimePoint posted_task_time_ = fxl::TimePoint::Max(); |
| std::unordered_map<ReplyAddress, DnsMessage, ReplyAddressHash> |
| outbound_messages_by_reply_address_; |
| std::vector<std::shared_ptr<MdnsAgent>> agents_awaiting_start_; |
| std::unordered_map<const MdnsAgent*, std::shared_ptr<MdnsAgent>> agents_; |
| std::unordered_map<std::string, std::shared_ptr<InstanceRequestor>> |
| instance_subscribers_by_service_name_; |
| std::unordered_map<std::string, std::shared_ptr<InstanceResponder>> |
| instance_publishers_by_instance_full_name_; |
| std::shared_ptr<DnsResource> address_placeholder_; |
| #ifndef NDEBUG |
| bool verbose_ = false; |
| #endif // ifndef NDEBUG |
| std::shared_ptr<ResourceRenewer> resource_renewer_; |
| bool prohibit_agent_removal_ = false; |
| |
| #ifdef NDEBUG |
| #define DPROHIBIT_AGENT_REMOVAL() ((void)0) |
| #define DALLOW_AGENT_REMOVAL() ((void)0) |
| #else |
| #define DPROHIBIT_AGENT_REMOVAL() (prohibit_agent_removal_ = true) |
| #define DALLOW_AGENT_REMOVAL() (prohibit_agent_removal_ = false) |
| #endif // NDEBUG |
| |
| FXL_DISALLOW_COPY_AND_ASSIGN(Mdns); |
| }; |
| |
| } // namespace mdns |
| |
| #endif // GARNET_BIN_MDNS_SERVICE_MDNS_H_ |