| /* |
| * |
| * Copyright (c) 2021 Project CHIP Authors |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #pragma once |
| |
| #include <dns_sd.h> |
| #include <lib/core/Global.h> |
| #include <lib/dnssd/platform/Dnssd.h> |
| #include <platform/CHIPDeviceLayer.h> |
| |
| #include "DnssdHostNameRegistrar.h" |
| |
| #include <map> |
| #include <string> |
| #include <vector> |
| |
| namespace chip { |
| namespace Dnssd { |
| |
| enum class ContextType |
| { |
| Register, |
| Browse, |
| BrowseWithDelegate, |
| Resolve, |
| }; |
| |
| struct GenericContext |
| { |
| ContextType type; |
| void * context; |
| DNSServiceRef serviceRef; |
| |
| virtual ~GenericContext() {} |
| |
| CHIP_ERROR Finalize(CHIP_ERROR err); |
| CHIP_ERROR Finalize(DNSServiceErrorType err = kDNSServiceErr_NoError); |
| |
| virtual void DispatchFailure(const char * errorStr, CHIP_ERROR err) = 0; |
| virtual void DispatchSuccess() = 0; |
| |
| private: |
| CHIP_ERROR FinalizeInternal(const char * errorStr, CHIP_ERROR err); |
| }; |
| |
| struct BrowseWithDelegateContext; |
| struct RegisterContext; |
| struct ResolveContext; |
| |
| class MdnsContexts |
| { |
| public: |
| MdnsContexts(const MdnsContexts &) = delete; |
| MdnsContexts & operator=(const MdnsContexts &) = delete; |
| ~MdnsContexts(); |
| static MdnsContexts & GetInstance() { return sInstance.get(); } |
| |
| CHIP_ERROR Add(GenericContext * context, DNSServiceRef sdRef); |
| CHIP_ERROR Remove(GenericContext * context); |
| CHIP_ERROR RemoveAllOfType(ContextType type); |
| CHIP_ERROR Has(GenericContext * context); |
| |
| /** |
| * @brief |
| * Returns a pointer to a RegisterContext that has previously been registered |
| * with a given type. |
| * |
| * @param[in] type A service type. Service type are composed of |
| * of the service name, the service protocol, and the PTR records. |
| * Example: |
| * _matterc._udp,_V65521,_S15,_L3840,_CM |
| * _matter._tcp,_I4CEEAD044CC35B63 |
| * @param[in] name The instance name for the service. |
| * @param[out] context A reference to the context previously registered |
| * |
| * @return On success, the context parameter will point to the previously |
| * registered context. |
| */ |
| CHIP_ERROR GetRegisterContextOfTypeAndName(const char * type, const char * name, RegisterContext ** context); |
| |
| /** |
| * Return a pointer to an existing ResolveContext for the given |
| * instanceName, if any. Returns nullptr if there are none. |
| */ |
| ResolveContext * GetExistingResolveForInstanceName(const char * instanceName); |
| |
| /** |
| * Return a pointer to an existing BrowserWithDelegateContext for the given |
| * delegate, if any. Returns nullptr if there are none. |
| */ |
| BrowseWithDelegateContext * GetExistingBrowseForDelegate(DnssdBrowseDelegate * delegate); |
| |
| /** |
| * Remove context from the list, if it's present in the list. Return |
| * whether it was present. |
| */ |
| bool RemoveWithoutDeleting(GenericContext * context); |
| |
| void Delete(GenericContext * context); |
| |
| /** |
| * Fill the provided vector with all contexts for which the given predicate |
| * returns true. |
| */ |
| template <typename F> |
| void FindAllMatchingPredicate(F predicate, std::vector<GenericContext *> & results) |
| { |
| results.clear(); |
| for (auto & ctx : mContexts) |
| { |
| if (predicate(ctx)) |
| { |
| results.push_back(ctx); |
| } |
| } |
| } |
| |
| private: |
| MdnsContexts() = default; |
| friend Global<MdnsContexts>; |
| static Global<MdnsContexts> sInstance; |
| |
| std::vector<GenericContext *> mContexts; |
| }; |
| |
| struct RegisterContext : public GenericContext |
| { |
| DnssdPublishCallback callback; |
| std::string mType; |
| std::string mInstanceName; |
| HostNameRegistrar mHostNameRegistrar; |
| |
| RegisterContext(const char * sType, const char * instanceName, DnssdPublishCallback cb, void * cbContext); |
| virtual ~RegisterContext() { mHostNameRegistrar.Unregister(); } |
| |
| void DispatchFailure(const char * errorStr, CHIP_ERROR err) override; |
| void DispatchSuccess() override; |
| |
| bool matches(const char * type, const char * name) { return mType == type && mInstanceName == name; } |
| }; |
| |
| struct BrowseHandler : public GenericContext |
| { |
| virtual ~BrowseHandler() {} |
| |
| DnssdServiceProtocol protocol; |
| |
| virtual void OnBrowse(DNSServiceFlags flags, const char * name, const char * type, const char * domain, |
| uint32_t interfaceId) = 0; |
| virtual void OnBrowseAdd(const char * name, const char * type, const char * domain, uint32_t interfaceId) = 0; |
| virtual void OnBrowseRemove(const char * name, const char * type, const char * domain, uint32_t interfaceId) = 0; |
| }; |
| |
| struct BrowseContext : public BrowseHandler |
| { |
| DnssdBrowseCallback callback; |
| std::vector<std::pair<DnssdService, std::string>> services; |
| |
| BrowseContext(void * cbContext, DnssdBrowseCallback cb, DnssdServiceProtocol cbContextProtocol); |
| |
| void DispatchFailure(const char * errorStr, CHIP_ERROR err) override; |
| void DispatchSuccess() override; |
| |
| // Dispatch what we have found so far, but don't stop browsing. |
| void DispatchPartialSuccess(); |
| |
| void OnBrowse(DNSServiceFlags flags, const char * name, const char * type, const char * domain, uint32_t interfaceId) override; |
| void OnBrowseAdd(const char * name, const char * type, const char * domain, uint32_t interfaceId) override; |
| void OnBrowseRemove(const char * name, const char * type, const char * domain, uint32_t interfaceId) override; |
| |
| // While we are dispatching partial success, sContextDispatchingSuccess will |
| // be set to the BrowseContext doing the dispatch. This allows resolves |
| // triggered by the browse dispatch to be associated with the browse. This |
| // relies on our consumer starting the resolves synchronously from the |
| // partial success callback. |
| // |
| // The other option would be to do the resolve ourselves before signaling |
| // browse success, but that would only allow us to pass in one ip per |
| // discovered hostname, and we want to pass in all the IPs we resolve. |
| // |
| // TODO: Consider fixing the higher-level APIs to make it possible to pass |
| // in multiple IPs for a successful browse result. |
| static BrowseContext * sContextDispatchingSuccess; |
| static std::vector<DnssdService> * sDispatchedServices; |
| }; |
| |
| struct BrowseWithDelegateContext : public BrowseHandler |
| { |
| BrowseWithDelegateContext(DnssdBrowseDelegate * delegate, DnssdServiceProtocol cbContextProtocol); |
| |
| void DispatchFailure(const char * errorStr, CHIP_ERROR err) override; |
| void DispatchSuccess() override; |
| |
| void OnBrowse(DNSServiceFlags flags, const char * name, const char * type, const char * domain, uint32_t interfaceId) override; |
| void OnBrowseAdd(const char * name, const char * type, const char * domain, uint32_t interfaceId) override; |
| void OnBrowseRemove(const char * name, const char * type, const char * domain, uint32_t interfaceId) override; |
| |
| bool Matches(DnssdBrowseDelegate * otherDelegate) const { return context == otherDelegate; } |
| }; |
| |
| struct InterfaceInfo |
| { |
| InterfaceInfo(); |
| InterfaceInfo(InterfaceInfo && other); |
| // Copying is not safe, because DnssdService bits need to be |
| // copied/deallocated properly. |
| InterfaceInfo(const InterfaceInfo & other) = delete; |
| ~InterfaceInfo(); |
| |
| DnssdService service; |
| std::vector<Inet::IPAddress> addresses; |
| std::string fullyQualifiedDomainName; |
| bool isDNSLookUpRequested = false; |
| }; |
| |
| struct InterfaceKey |
| { |
| InterfaceKey() = default; |
| ~InterfaceKey() = default; |
| inline bool operator<(const InterfaceKey & other) const |
| { |
| return (this->interfaceId < other.interfaceId) || |
| ((this->interfaceId == other.interfaceId) && (this->hostname < other.hostname)) || |
| ((this->interfaceId == other.interfaceId) && (this->hostname == other.hostname) && |
| (this->isSRPResult < other.isSRPResult)); |
| } |
| |
| uint32_t interfaceId; |
| std::string hostname; |
| bool isSRPResult = false; |
| }; |
| |
| struct ResolveContextWithType |
| { |
| ResolveContextWithType() = delete; |
| ~ResolveContextWithType() = default; |
| |
| ResolveContext * const context; |
| const bool isSRPResolve; |
| }; |
| |
| struct ResolveContext : public GenericContext |
| { |
| DnssdResolveCallback callback; |
| std::map<InterfaceKey, InterfaceInfo> interfaces; |
| DNSServiceProtocol protocol; |
| std::string instanceName; |
| std::shared_ptr<uint32_t> consumerCounter; |
| BrowseContext * const browseThatCausedResolve; // Can be null |
| |
| // Indicates whether the timer should be started to give the resolve |
| // on SRP domain some extra time to complete. |
| bool shouldStartSRPTimerForResolve = false; |
| bool isSRPTimerRunning = false; |
| |
| ResolveContextWithType resolveContextWithSRPType = { this, true }; |
| ResolveContextWithType resolveContextWithNonSRPType = { this, false }; |
| |
| // browseCausingResolve can be null. |
| ResolveContext(void * cbContext, DnssdResolveCallback cb, chip::Inet::IPAddressType cbAddressType, |
| const char * instanceNameToResolve, BrowseContext * browseCausingResolve, |
| std::shared_ptr<uint32_t> && consumerCounterToUse); |
| ResolveContext(DiscoverNodeDelegate * delegate, chip::Inet::IPAddressType cbAddressType, const char * instanceNameToResolve, |
| std::shared_ptr<uint32_t> && consumerCounterToUse); |
| virtual ~ResolveContext(); |
| |
| void DispatchFailure(const char * errorStr, CHIP_ERROR err) override; |
| void DispatchSuccess() override; |
| |
| CHIP_ERROR OnNewAddress(const InterfaceKey & interfaceKey, const struct sockaddr * address); |
| bool HasAddress(); |
| |
| void OnNewInterface(uint32_t interfaceId, const char * fullname, const char * hostname, uint16_t port, uint16_t txtLen, |
| const unsigned char * txtRecord, bool isSRPResult); |
| bool HasInterface(); |
| bool Matches(const char * otherInstanceName) const { return instanceName == otherInstanceName; } |
| |
| /** |
| * @brief Callback that is called when the timeout for resolving on the kSRPDot domain has expired. |
| * |
| * @param[in] systemLayer The system layer. |
| * @param[in] callbackContext The context passed to the timer callback. |
| */ |
| static void SRPTimerExpiredCallback(chip::System::Layer * systemLayer, void * callbackContext); |
| |
| /** |
| * @brief Cancels the timer that was started to wait for the resolution on the kSRPDot domain to happen. |
| * |
| */ |
| void CancelSRPTimerIfRunning(); |
| |
| private: |
| /** |
| * Try reporting the results we got on the provided interface index. |
| * Returns true if information was reported, false if not (e.g. if there |
| * were no IP addresses, etc). |
| */ |
| bool TryReportingResultsForInterfaceIndex(uint32_t interfaceIndex, const std::string & hostname, bool isSRPResult); |
| |
| bool TryReportingResultsForInterfaceIndex(uint32_t interfaceIndex); |
| }; |
| |
| } // namespace Dnssd |
| } // namespace chip |