blob: c66a8c23f700b6f4b94f77862439a9b2013c9121 [file] [log] [blame]
/*
*
* 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