/* * * 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 #include #include #include #include "DnssdHostNameRegistrar.h" #include #include #include namespace chip { namespace Dnssd { enum class ContextType { Register, Browse, BrowseWithDelegate, Resolve, }; struct GenericContext { ContextType type; void * context; // When using a GenericContext, if a DNSServiceRef is created successfully // API consumers must ensure that it gets set as serviceRef on the context // immediately, before any other operations that might fail can happen. // // In all cases, once a context has been created, Finalize() must be called // on it to clean it up properly. DNSServiceRef serviceRef = nullptr; 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(); } // The context being added is expected to have a valid serviceRef. CHIP_ERROR Add(GenericContext * context); 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 void FindAllMatchingPredicate(F predicate, std::vector & results) { results.clear(); for (auto & ctx : mContexts) { if (predicate(ctx)) { results.push_back(ctx); } } } private: MdnsContexts() = default; friend Global; static Global sInstance; std::vector 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> services; bool dispatchedSuccessOnce = false; 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 * 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 addresses; std::string fullyQualifiedDomainName; bool isDNSLookUpRequested = false; bool HasAddresses() const { return addresses.size() != 0; }; }; 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)); } inline bool operator==(const InterfaceKey & other) const { return 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 interfaces; DNSServiceProtocol protocol; std::string instanceName; std::shared_ptr 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 && consumerCounterToUse); ResolveContext(DiscoverNodeDelegate * delegate, chip::Inet::IPAddressType cbAddressType, const char * instanceNameToResolve, std::shared_ptr && 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(); }; } // namespace Dnssd } // namespace chip