| /* |
| * |
| * Copyright (c) 2020 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 <sys/select.h> |
| #include <unistd.h> |
| |
| #include <atomic> |
| #include <chrono> |
| #include <list> |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include <avahi-client/client.h> |
| #include <avahi-client/lookup.h> |
| #include <avahi-client/publish.h> |
| #include <avahi-common/domain.h> |
| #include <avahi-common/error.h> |
| #include <avahi-common/watch.h> |
| |
| #include <lib/dnssd/platform/Dnssd.h> |
| #include <system/SystemLayer.h> |
| |
| struct AvahiWatch |
| { |
| int mSocket; |
| chip::System::SocketWatchToken mSocketWatch; |
| AvahiWatchCallback mCallback; ///< The function to be called when interested events happened on mFd. |
| AvahiWatchEvent mPendingIO; ///< The pending events from the currently active or most recent callback. |
| void * mContext; ///< A pointer to application-specific context. |
| void * mPoller; ///< The poller created this watch. |
| }; |
| |
| struct AvahiTimeout |
| { |
| std::chrono::steady_clock::time_point mAbsTimeout; ///< Absolute time when this timer timeout. |
| AvahiTimeoutCallback mCallback; ///< The function to be called when timeout. |
| bool mEnabled; ///< Whether the timeout is enabled. |
| void * mContext; ///< The pointer to application-specific context. |
| void * mPoller; ///< The poller created this timer. |
| }; |
| |
| namespace chip { |
| namespace Dnssd { |
| |
| class Poller |
| { |
| public: |
| Poller(void); |
| |
| void HandleTimeout(); |
| |
| const AvahiPoll * GetAvahiPoll(void) const { return &mAvahiPoller; } |
| |
| private: |
| static AvahiWatch * WatchNew(const struct AvahiPoll * poller, int fd, AvahiWatchEvent event, AvahiWatchCallback callback, |
| void * context); |
| AvahiWatch * WatchNew(int fd, AvahiWatchEvent event, AvahiWatchCallback callback, void * context); |
| |
| static void WatchUpdate(AvahiWatch * watch, AvahiWatchEvent event); |
| |
| static AvahiWatchEvent WatchGetEvents(AvahiWatch * watch); |
| |
| static void WatchFree(AvahiWatch * watch); |
| void WatchFree(AvahiWatch & watch); |
| |
| static AvahiTimeout * TimeoutNew(const AvahiPoll * poller, const struct timeval * timeout, AvahiTimeoutCallback callback, |
| void * context); |
| AvahiTimeout * TimeoutNew(const struct timeval * timeout, AvahiTimeoutCallback callback, void * context); |
| |
| static void TimeoutUpdate(AvahiTimeout * timer, const struct timeval * timeout); |
| |
| static void TimeoutFree(AvahiTimeout * timer); |
| void TimeoutFree(AvahiTimeout & timer); |
| |
| void SystemTimerUpdate(AvahiTimeout * timer); |
| static void SystemTimerCallback(System::Layer * layer, void * data); |
| |
| std::vector<std::unique_ptr<AvahiWatch>> mWatches; |
| std::vector<std::unique_ptr<AvahiTimeout>> mTimers; |
| std::chrono::steady_clock::time_point mEarliestTimeout; |
| |
| AvahiPoll mAvahiPoller; |
| }; |
| |
| class MdnsAvahi |
| { |
| public: |
| MdnsAvahi(const MdnsAvahi &) = delete; |
| MdnsAvahi & operator=(const MdnsAvahi &) = delete; |
| |
| CHIP_ERROR Init(DnssdAsyncReturnCallback initCallback, DnssdAsyncReturnCallback errorCallback, void * context); |
| void Shutdown(); |
| CHIP_ERROR SetHostname(const char * hostname); |
| CHIP_ERROR PublishService(const DnssdService & service, DnssdPublishCallback callback, void * context); |
| CHIP_ERROR StopPublish(); |
| CHIP_ERROR Browse(const char * type, DnssdServiceProtocol protocol, chip::Inet::IPAddressType addressType, |
| chip::Inet::InterfaceId interface, DnssdBrowseCallback callback, void * context, intptr_t * browseIdentifier); |
| CHIP_ERROR StopBrowse(intptr_t browseIdentifier); |
| CHIP_ERROR Resolve(const char * name, const char * type, DnssdServiceProtocol protocol, chip::Inet::IPAddressType addressType, |
| chip::Inet::IPAddressType transportType, chip::Inet::InterfaceId interface, DnssdResolveCallback callback, |
| void * context); |
| void StopResolve(const char * name); |
| |
| Poller & GetPoller() { return mPoller; } |
| |
| static MdnsAvahi & GetInstance() { return sInstance; } |
| |
| private: |
| struct BrowseContext |
| { |
| MdnsAvahi * mInstance; |
| DnssdBrowseCallback mCallback; |
| void * mContext; |
| Inet::IPAddressType mAddressType; |
| std::vector<DnssdService> mServices; |
| size_t mBrowseRetries; |
| AvahiIfIndex mInterface; |
| std::string mProtocol; |
| chip::System::Clock::Timeout mNextRetryDelay = chip::System::Clock::Seconds16(1); |
| std::atomic_bool mStopped{ false }; |
| }; |
| |
| struct ResolveContext |
| { |
| size_t mNumber; // unique number for this context |
| MdnsAvahi * mInstance; |
| DnssdResolveCallback mCallback; |
| void * mContext; |
| char mName[Common::kInstanceNameMaxLength + 1]; |
| AvahiIfIndex mInterface; |
| AvahiProtocol mTransport; |
| AvahiProtocol mAddressType; |
| std::string mFullType; |
| uint8_t mAttempts = 0; |
| AvahiServiceResolver * mResolver = nullptr; |
| |
| ~ResolveContext() |
| { |
| if (mResolver != nullptr) |
| { |
| avahi_service_resolver_free(mResolver); |
| mResolver = nullptr; |
| } |
| } |
| }; |
| |
| MdnsAvahi() : mClient(nullptr) {} |
| static MdnsAvahi sInstance; |
| |
| /// Allocates a new resolve context with a unique `mNumber` |
| ResolveContext * AllocateResolveContext(); |
| |
| ResolveContext * ResolveContextForHandle(size_t handle); |
| void FreeResolveContext(size_t handle); |
| void FreeResolveContext(const char * name); |
| |
| static void HandleClientState(AvahiClient * client, AvahiClientState state, void * context); |
| void HandleClientState(AvahiClient * client, AvahiClientState state); |
| |
| static void HandleGroupState(AvahiEntryGroup * group, AvahiEntryGroupState state, void * context); |
| void HandleGroupState(AvahiEntryGroup * group, AvahiEntryGroupState state); |
| |
| static void HandleBrowse(AvahiServiceBrowser * broswer, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, |
| const char * name, const char * type, const char * domain, AvahiLookupResultFlags flags, |
| void * userdata); |
| static void BrowseRetryCallback(chip::System::Layer * aLayer, void * appState); |
| static void HandleResolve(AvahiServiceResolver * resolver, AvahiIfIndex interface, AvahiProtocol protocol, |
| AvahiResolverEvent event, const char * name, const char * type, const char * domain, |
| const char * host_name, const AvahiAddress * address, uint16_t port, AvahiStringList * txt, |
| AvahiLookupResultFlags flags, void * userdata); |
| |
| DnssdAsyncReturnCallback mInitCallback; |
| DnssdAsyncReturnCallback mErrorCallback; |
| void * mAsyncReturnContext; |
| |
| AvahiClient * mClient; |
| std::map<std::string, AvahiEntryGroup *> mPublishedGroups; |
| Poller mPoller; |
| static constexpr size_t kMaxBrowseRetries = 4; |
| |
| // Handling of allocated resolves |
| size_t mResolveCount = 0; |
| std::list<ResolveContext *> mAllocatedResolves; |
| }; |
| |
| } // namespace Dnssd |
| } // namespace chip |