| // 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. |
| |
| #ifndef SERVICES_NETWORK_MDNS_RESPONDER_H_ |
| #define SERVICES_NETWORK_MDNS_RESPONDER_H_ |
| |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include "base/containers/flat_set.h" |
| #include "base/containers/unique_ptr_adapters.h" |
| #include "mojo/public/cpp/bindings/binding_set.h" |
| #include "net/dns/dns_query.h" |
| #include "net/dns/dns_response.h" |
| #include "services/network/public/mojom/mdns_responder.mojom.h" |
| |
| namespace base { |
| class TickClock; |
| } // namespace base |
| |
| namespace net { |
| class IOBufferWithSize; |
| class IPAddress; |
| class MDnsSocketFactory; |
| } // namespace net |
| |
| namespace network { |
| |
| class MdnsResponder; |
| |
| namespace mdns_helper { |
| |
| // Creates an mDNS response, of which the Answer section contains the address |
| // records for |name_addr_map|, and the Additional section contains the |
| // corresponding NSEC records that assert the existence of only address |
| // records in the Answer section. |
| COMPONENT_EXPORT(NETWORK_SERVICE) |
| scoped_refptr<net::IOBufferWithSize> CreateResolutionResponse( |
| const base::TimeDelta& ttl, |
| const std::map<std::string, net::IPAddress>& name_addr_map); |
| // Creates an mDNS response, of which the Answer section contains NSEC records |
| // that assert the existence of only address records for |name_addr_map|, and |
| // the Additional section contains the corresponding address records. |
| COMPONENT_EXPORT(NETWORK_SERVICE) |
| scoped_refptr<net::IOBufferWithSize> CreateNegativeResponse( |
| const std::map<std::string, net::IPAddress>& name_addr_map); |
| |
| } // namespace mdns_helper |
| |
| // Options to configure the transmission of mDNS responses. |
| struct COMPONENT_EXPORT(NETWORK_SERVICE) MdnsResponseSendOption |
| : public base::RefCounted<MdnsResponseSendOption> { |
| public: |
| enum class ResponseClass { |
| UNSPECIFIED, |
| ANNOUNCEMENT, |
| PROBE_RESOLUTION, |
| REGULAR_RESOLUTION, |
| NEGATIVE, |
| GOODBYE, |
| }; |
| |
| MdnsResponseSendOption(); |
| // As a shorthand, an empty set denotes all interfaces. |
| std::set<uint16_t> send_socket_handler_ids; |
| // Used for rate limiting. |
| base::flat_set<std::string> names_for_rate_limit; |
| // Used for retry after send failure. |
| ResponseClass klass = ResponseClass::UNSPECIFIED; |
| // The number of retries done for the same response due to send failure. |
| uint8_t num_send_retries_done = 0; |
| |
| private: |
| friend class RefCounted<MdnsResponseSendOption>; |
| |
| ~MdnsResponseSendOption(); |
| }; |
| |
| // The responder manager creates and manages responder instances spawned for |
| // each Mojo binding. It also manages the underlying network IO by delegating |
| // the IO task to socket handlers of each interface. When there is a network |
| // stack error or a Mojo binding error, the manager also offers the |
| // corresponding error handling. |
| class COMPONENT_EXPORT(NETWORK_SERVICE) MdnsResponderManager { |
| public: |
| // Wraps a name generation method that can be configured in tests via |
| // SetNameGeneratorForTesting below. |
| class NameGenerator { |
| public: |
| virtual ~NameGenerator() = default; |
| virtual std::string CreateName() = 0; |
| }; |
| |
| MdnsResponderManager(); |
| explicit MdnsResponderManager(net::MDnsSocketFactory* socket_factory); |
| ~MdnsResponderManager(); |
| |
| // Creates an instance of MdnsResponder for the binding request from an |
| // InterfacePtr. |
| void CreateMdnsResponder(mojom::MdnsResponderRequest request); |
| #ifdef DEBUG |
| // The methods below are only used for extra uniqueness validation of names |
| // owned by responders. By default, we use the RandomUuidNameGenerator (see |
| // mdns_responder.cc), which probabilistically guarantees the uniqueness of |
| // generated names. |
| // |
| // Adds a name to the set of all existing names generated by all responders |
| // (i.e., names owned by an instance of responder). Return true if the name is |
| // not in the set before the addition; false otherwise. |
| bool AddName(const std::string& name) { |
| auto result = names_.insert(name); |
| return result.second; |
| } |
| // Removes a name from the set of all existing names generated by all |
| // responders. Return true if the name exists in the set before the removal; |
| // false otherwise. |
| bool RemoveName(const std::string& name) { return names_.erase(name) == 1; } |
| #endif |
| // Sends an mDNS response in the wire format given by |buf|. See |
| // MdnsResponseSendOption for configurable options in |option|. |
| // |
| // Sending responses is rate-limited, and this method returns true if the |
| // response is successfully scheduled to send on all successfully bound |
| // interfaces specified in |option.send_socket_handler_ids|, and false |
| // otherwise. |
| bool Send(scoped_refptr<net::IOBufferWithSize> buf, |
| scoped_refptr<MdnsResponseSendOption> option); |
| // The error handler that is invoked when the Mojo binding of |responder| is |
| // closed (e.g. the InterfacePtr on the client side is destroyed) or |
| // encounters an error. It removes this responder instance, which further |
| // clears the existing name-address associations owned by this responder in |
| // the local network. |
| void OnMojoConnectionError(MdnsResponder* responder); |
| |
| // Called when an external mDNS response is received and it contains |
| // records of names generated by an owned MdnsResponder instance. |
| void HandleNameConflictIfAny( |
| const std::map<std::string, std::set<net::IPAddress>>& external_maps); |
| |
| NameGenerator* name_generator() const { return name_generator_.get(); } |
| // Sets the name generator that is shared by all MdnsResponder instances. |
| // Changing the name generator affects all existing responder instances and |
| // also the ones spawned in the future. |
| // |
| // Used for tests only. |
| void SetNameGeneratorForTesting( |
| std::unique_ptr<NameGenerator> name_generator); |
| |
| // Sets the tick clock that is used for rate limiting of mDNS responses, and |
| // also resets the internal schedule for rate limiting. |
| // |
| // Used for tests only. |
| void SetTickClockForTesting(const base::TickClock* tick_clock); |
| |
| private: |
| enum class SocketHandlerStartResult { |
| UNSPECIFIED, |
| // Handlers started for all interfaces. |
| ALL_SUCCESS, |
| // Handlers started for a subset of interfaces. |
| PARTIAL_SUCCESS, |
| // No handler started. |
| ALL_FAILURE, |
| }; |
| // Handles the underlying sending and receiving of mDNS messages on each |
| // interface available. The implementation mostly resembles |
| // net::MdnsConnection::SocketHandler. |
| class SocketHandler; |
| |
| // Initializes socket handlers and sets |start_result_|; |
| void Start(); |
| // Dispatches a parsed query from a socket handler to each responder instance. |
| void OnMdnsQueryReceived(const net::DnsQuery& query, |
| uint16_t recv_socket_handler_id); |
| void OnSocketHandlerReadError(uint16_t socket_handler_id, int result); |
| |
| std::unique_ptr<net::MDnsSocketFactory> owned_socket_factory_; |
| net::MDnsSocketFactory* socket_factory_; |
| // Only the socket handlers that have successfully bound and started are kept. |
| std::map<uint16_t, std::unique_ptr<SocketHandler>> socket_handler_by_id_; |
| SocketHandlerStartResult start_result_ = |
| SocketHandlerStartResult::UNSPECIFIED; |
| #ifdef DEBUG |
| // Used in debug only for the extra uniqueness validation of names generated |
| // by responders. |
| std::set<std::string> names_; |
| #endif |
| std::unique_ptr<NameGenerator> name_generator_; |
| std::set<std::unique_ptr<MdnsResponder>, base::UniquePtrComparator> |
| responders_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MdnsResponderManager); |
| }; |
| |
| // Implementation of the mDNS service that can provide utilities of an mDNS |
| // responder. |
| class COMPONENT_EXPORT(NETWORK_SERVICE) MdnsResponder |
| : public mojom::MdnsResponder { |
| public: |
| MdnsResponder(mojom::MdnsResponderRequest request, |
| MdnsResponderManager* manager); |
| // When destroyed, clears all existing name-address associations owned by this |
| // responder in the local network by sending out goodbye packets. See |
| // SendGoodbyePacketForNameAddressMap below. |
| ~MdnsResponder() override; |
| |
| // mojom::MdnsResponder overrides. |
| void CreateNameForAddress( |
| const net::IPAddress& address, |
| mojom::MdnsResponder::CreateNameForAddressCallback callback) override; |
| void RemoveNameForAddress( |
| const net::IPAddress& address, |
| mojom::MdnsResponder::RemoveNameForAddressCallback callback) override; |
| |
| // Processes the given query and generates a response if the query contains an |
| // mDNS name that this responder has a mapped IP address. |
| void OnMdnsQueryReceived(const net::DnsQuery& query, |
| uint16_t recv_socket_handler_id); |
| |
| bool HasConflictWithExternalResolution( |
| const std::string& name, |
| const std::set<net::IPAddress>& external_mapped_addreses); |
| |
| void SetNameGeneratorForTesting( |
| MdnsResponderManager::NameGenerator* name_generator) { |
| name_generator_ = name_generator; |
| } |
| |
| private: |
| // Returns true if the response is successfully scheduled to send on all |
| // successfully bound interfaces after rate limiting, and false otherwise. See |
| // also MdnsResponderManager::Send. |
| bool SendMdnsResponse(scoped_refptr<net::IOBufferWithSize> response, |
| scoped_refptr<MdnsResponseSendOption> option); |
| // RFC 6761, Section 10.1 "Goodbye Packets". |
| // |
| // The responder should send out an unsolicited mDNS response with an resource |
| // record of zero TTL to clear the name-to-address mapping in neighboring |
| // hosts, when the mapping is no longer valid. |
| // |
| // Returns true if the goodbye message is successfully scheduled to send on |
| // all interfaces after rate limiting, and false otherwise. See also |
| // MdnsResponderManager::Send. |
| bool SendGoodbyePacketForNameAddressMap( |
| const std::map<std::string, net::IPAddress>& name_addr_map); |
| |
| std::map<std::string, net::IPAddress>::iterator FindNameCreatedForAddress( |
| const net::IPAddress& address); |
| |
| mojo::Binding<network::mojom::MdnsResponder> binding_; |
| // A back pointer to the responder manager that owns this responder. The |
| // responder should be destroyed before |manager_| becomes invalid or a weak |
| // reference should be used to access the manager when there is no such |
| // guarantee in an operation. |
| MdnsResponderManager* const manager_; |
| std::map<std::string, net::IPAddress> name_addr_map_; |
| std::map<std::string, uint16_t> name_refcount_map_; |
| MdnsResponderManager::NameGenerator* name_generator_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MdnsResponder); |
| }; |
| |
| } // namespace network |
| |
| #endif // SERVICES_NETWORK_MDNS_RESPONDER_H_ |