1 /* 2 * Copyright (c) 2017, The OpenThread Authors. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. Neither the name of the copyright holder nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /** 30 * @file 31 * This file includes definition for mDNS publisher based on avahi. 32 */ 33 34 #ifndef OTBR_AGENT_MDNS_AVAHI_HPP_ 35 #define OTBR_AGENT_MDNS_AVAHI_HPP_ 36 37 #include "openthread-br/config.h" 38 39 #include <memory> 40 #include <set> 41 #include <vector> 42 43 #include <avahi-client/client.h> 44 #include <avahi-client/lookup.h> 45 #include <avahi-client/publish.h> 46 #include <avahi-common/domain.h> 47 #include <avahi-common/watch.h> 48 49 #include "mdns.hpp" 50 #include "common/code_utils.hpp" 51 #include "common/mainloop.hpp" 52 #include "common/time.hpp" 53 54 /** 55 * @addtogroup border-router-mdns 56 * 57 * @brief 58 * This module includes definition for avahi-based mDNS publisher. 59 * 60 * @{ 61 */ 62 63 namespace otbr { 64 65 namespace Mdns { 66 67 class AvahiPoller; 68 69 /** 70 * This class implements mDNS publisher with avahi. 71 */ 72 class PublisherAvahi : public Publisher 73 { 74 public: 75 PublisherAvahi(StateCallback aStateCallback); 76 ~PublisherAvahi(void) override; 77 78 void UnpublishService(const std::string &aName, const std::string &aType, ResultCallback &&aCallback) override; 79 void UnpublishHost(const std::string &aName, ResultCallback &&aCallback) override; 80 void UnpublishKey(const std::string &aName, ResultCallback &&aCallback) override; 81 void SubscribeService(const std::string &aType, const std::string &aInstanceName) override; 82 void UnsubscribeService(const std::string &aType, const std::string &aInstanceName) override; 83 void SubscribeHost(const std::string &aHostName) override; 84 void UnsubscribeHost(const std::string &aHostName) override; 85 otbrError Start(void) override; 86 bool IsStarted(void) const override; 87 void Stop(void) override; 88 89 protected: 90 otbrError PublishServiceImpl(const std::string &aHostName, 91 const std::string &aName, 92 const std::string &aType, 93 const SubTypeList &aSubTypeList, 94 uint16_t aPort, 95 const TxtData &aTxtData, 96 ResultCallback &&aCallback) override; 97 otbrError PublishHostImpl(const std::string &aName, 98 const AddressList &aAddresses, 99 ResultCallback &&aCallback) override; 100 otbrError PublishKeyImpl(const std::string &aName, const KeyData &aKeyData, ResultCallback &&aCallback) override; 101 void OnServiceResolveFailedImpl(const std::string &aType, 102 const std::string &aInstanceName, 103 int32_t aErrorCode) override; 104 void OnHostResolveFailedImpl(const std::string &aHostName, int32_t aErrorCode) override; 105 otbrError DnsErrorToOtbrError(int32_t aErrorCode) override; 106 107 private: 108 static constexpr size_t kMaxSizeOfTxtRecord = 1024; 109 static constexpr uint32_t kDefaultTtl = 10; // In seconds. 110 static constexpr uint16_t kDnsKeyRecordType = 25; 111 112 class AvahiServiceRegistration : public ServiceRegistration 113 { 114 public: AvahiServiceRegistration(const std::string & aHostName,const std::string & aName,const std::string & aType,const SubTypeList & aSubTypeList,uint16_t aPort,const TxtData & aTxtData,ResultCallback && aCallback,AvahiEntryGroup * aEntryGroup,PublisherAvahi * aPublisher)115 AvahiServiceRegistration(const std::string &aHostName, 116 const std::string &aName, 117 const std::string &aType, 118 const SubTypeList &aSubTypeList, 119 uint16_t aPort, 120 const TxtData &aTxtData, 121 ResultCallback &&aCallback, 122 AvahiEntryGroup *aEntryGroup, 123 PublisherAvahi *aPublisher) 124 : ServiceRegistration(aHostName, 125 aName, 126 aType, 127 aSubTypeList, 128 aPort, 129 aTxtData, 130 std::move(aCallback), 131 aPublisher) 132 , mEntryGroup(aEntryGroup) 133 { 134 } 135 136 ~AvahiServiceRegistration(void) override; GetEntryGroup(void) const137 const AvahiEntryGroup *GetEntryGroup(void) const { return mEntryGroup; } 138 139 private: 140 AvahiEntryGroup *mEntryGroup; 141 }; 142 143 class AvahiHostRegistration : public HostRegistration 144 { 145 public: AvahiHostRegistration(const std::string & aName,const AddressList & aAddresses,ResultCallback && aCallback,AvahiEntryGroup * aEntryGroup,PublisherAvahi * aPublisher)146 AvahiHostRegistration(const std::string &aName, 147 const AddressList &aAddresses, 148 ResultCallback &&aCallback, 149 AvahiEntryGroup *aEntryGroup, 150 PublisherAvahi *aPublisher) 151 : HostRegistration(aName, aAddresses, std::move(aCallback), aPublisher) 152 , mEntryGroup(aEntryGroup) 153 { 154 } 155 156 ~AvahiHostRegistration(void) override; GetEntryGroup(void) const157 const AvahiEntryGroup *GetEntryGroup(void) const { return mEntryGroup; } 158 159 private: 160 AvahiEntryGroup *mEntryGroup; 161 }; 162 163 class AvahiKeyRegistration : public KeyRegistration 164 { 165 public: AvahiKeyRegistration(const std::string & aName,const KeyData & aKeyData,ResultCallback && aCallback,AvahiEntryGroup * aEntryGroup,PublisherAvahi * aPublisher)166 AvahiKeyRegistration(const std::string &aName, 167 const KeyData &aKeyData, 168 ResultCallback &&aCallback, 169 AvahiEntryGroup *aEntryGroup, 170 PublisherAvahi *aPublisher) 171 : KeyRegistration(aName, aKeyData, std::move(aCallback), aPublisher) 172 , mEntryGroup(aEntryGroup) 173 { 174 } 175 176 ~AvahiKeyRegistration(void) override; GetEntryGroup(void) const177 const AvahiEntryGroup *GetEntryGroup(void) const { return mEntryGroup; } 178 179 private: 180 AvahiEntryGroup *mEntryGroup; 181 }; 182 183 struct Subscription : private ::NonCopyable 184 { 185 PublisherAvahi *mPublisherAvahi; 186 Subscriptionotbr::Mdns::PublisherAvahi::Subscription187 explicit Subscription(PublisherAvahi &aPublisherAvahi) 188 : mPublisherAvahi(&aPublisherAvahi) 189 { 190 } 191 }; 192 193 struct HostSubscription : public Subscription 194 { HostSubscriptionotbr::Mdns::PublisherAvahi::HostSubscription195 explicit HostSubscription(PublisherAvahi &aAvahiPublisher, std::string aHostName) 196 : Subscription(aAvahiPublisher) 197 , mHostName(std::move(aHostName)) 198 , mRecordBrowser(nullptr) 199 { 200 } 201 ~HostSubscriptionotbr::Mdns::PublisherAvahi::HostSubscription202 ~HostSubscription() { Release(); } 203 204 void Release(void); 205 void Resolve(void); 206 static void HandleResolveResult(AvahiRecordBrowser *aRecordBrowser, 207 AvahiIfIndex aInterfaceIndex, 208 AvahiProtocol aProtocol, 209 AvahiBrowserEvent aEvent, 210 const char *aName, 211 uint16_t aClazz, 212 uint16_t aType, 213 const void *aRdata, 214 size_t aSize, 215 AvahiLookupResultFlags aFlags, 216 void *aContext); 217 218 void HandleResolveResult(AvahiRecordBrowser *aRecordBrowser, 219 AvahiIfIndex aInterfaceIndex, 220 AvahiProtocol aProtocol, 221 AvahiBrowserEvent aEvent, 222 const char *aName, 223 uint16_t aClazz, 224 uint16_t aType, 225 const void *aRdata, 226 size_t aSize, 227 AvahiLookupResultFlags aFlags); 228 229 std::string mHostName; 230 DiscoveredHostInfo mHostInfo; 231 AvahiRecordBrowser *mRecordBrowser; 232 }; 233 234 struct ServiceResolver 235 { ~ServiceResolverotbr::Mdns::PublisherAvahi::ServiceResolver236 ~ServiceResolver() 237 { 238 if (mServiceResolver) 239 { 240 avahi_service_resolver_free(mServiceResolver); 241 } 242 if (mRecordBrowser) 243 { 244 avahi_record_browser_free(mRecordBrowser); 245 } 246 } 247 248 static void HandleResolveServiceResult(AvahiServiceResolver *aServiceResolver, 249 AvahiIfIndex aInterfaceIndex, 250 AvahiProtocol Protocol, 251 AvahiResolverEvent aEvent, 252 const char *aName, 253 const char *aType, 254 const char *aDomain, 255 const char *aHostName, 256 const AvahiAddress *aAddress, 257 uint16_t aPort, 258 AvahiStringList *aTxt, 259 AvahiLookupResultFlags aFlags, 260 void *aContext); 261 262 void HandleResolveServiceResult(AvahiServiceResolver *aServiceResolver, 263 AvahiIfIndex aInterfaceIndex, 264 AvahiProtocol Protocol, 265 AvahiResolverEvent aEvent, 266 const char *aName, 267 const char *aType, 268 const char *aDomain, 269 const char *aHostName, 270 const AvahiAddress *aAddress, 271 uint16_t aPort, 272 AvahiStringList *aTxt, 273 AvahiLookupResultFlags aFlags); 274 275 static void HandleResolveHostResult(AvahiRecordBrowser *aRecordBrowser, 276 AvahiIfIndex aInterfaceIndex, 277 AvahiProtocol aProtocol, 278 AvahiBrowserEvent aEvent, 279 const char *aName, 280 uint16_t aClazz, 281 uint16_t aType, 282 const void *aRdata, 283 size_t aSize, 284 AvahiLookupResultFlags aFlags, 285 void *aContext); 286 287 void HandleResolveHostResult(AvahiRecordBrowser *aRecordBrowser, 288 AvahiIfIndex aInterfaceIndex, 289 AvahiProtocol aProtocol, 290 AvahiBrowserEvent aEvent, 291 const char *aName, 292 uint16_t aClazz, 293 uint16_t aType, 294 const void *aRdata, 295 size_t aSize, 296 AvahiLookupResultFlags aFlags); 297 298 std::string mType; 299 PublisherAvahi *mPublisherAvahi; 300 AvahiServiceResolver *mServiceResolver = nullptr; 301 AvahiRecordBrowser *mRecordBrowser = nullptr; 302 DiscoveredInstanceInfo mInstanceInfo; 303 }; 304 struct ServiceSubscription : public Subscription 305 { ServiceSubscriptionotbr::Mdns::PublisherAvahi::ServiceSubscription306 explicit ServiceSubscription(PublisherAvahi &aPublisherAvahi, std::string aType, std::string aInstanceName) 307 : Subscription(aPublisherAvahi) 308 , mType(std::move(aType)) 309 , mInstanceName(std::move(aInstanceName)) 310 , mServiceBrowser(nullptr) 311 { 312 } 313 ~ServiceSubscriptionotbr::Mdns::PublisherAvahi::ServiceSubscription314 ~ServiceSubscription() { Release(); } 315 316 void Release(void); 317 void Browse(void); 318 void Resolve(uint32_t aInterfaceIndex, 319 AvahiProtocol aProtocol, 320 const std::string &aInstanceName, 321 const std::string &aType); 322 void AddServiceResolver(const std::string &aInstanceName, ServiceResolver *aServiceResolver); 323 void RemoveServiceResolver(const std::string &aInstanceName); 324 325 static void HandleBrowseResult(AvahiServiceBrowser *aServiceBrowser, 326 AvahiIfIndex aInterfaceIndex, 327 AvahiProtocol aProtocol, 328 AvahiBrowserEvent aEvent, 329 const char *aName, 330 const char *aType, 331 const char *aDomain, 332 AvahiLookupResultFlags aFlags, 333 void *aContext); 334 335 void HandleBrowseResult(AvahiServiceBrowser *aServiceBrowser, 336 AvahiIfIndex aInterfaceIndex, 337 AvahiProtocol aProtocol, 338 AvahiBrowserEvent aEvent, 339 const char *aName, 340 const char *aType, 341 const char *aDomain, 342 AvahiLookupResultFlags aFlags); 343 344 std::string mType; 345 std::string mInstanceName; 346 AvahiServiceBrowser *mServiceBrowser; 347 348 using ServiceResolversMap = std::map<std::string, std::set<ServiceResolver *>>; 349 ServiceResolversMap mServiceResolvers; 350 }; 351 352 typedef std::vector<std::unique_ptr<ServiceSubscription>> ServiceSubscriptionList; 353 typedef std::vector<std::unique_ptr<HostSubscription>> HostSubscriptionList; 354 355 static void HandleClientState(AvahiClient *aClient, AvahiClientState aState, void *aContext); 356 void HandleClientState(AvahiClient *aClient, AvahiClientState aState); 357 358 AvahiEntryGroup *CreateGroup(AvahiClient *aClient); 359 static void ReleaseGroup(AvahiEntryGroup *aGroup); 360 361 static void HandleGroupState(AvahiEntryGroup *aGroup, AvahiEntryGroupState aState, void *aContext); 362 void HandleGroupState(AvahiEntryGroup *aGroup, AvahiEntryGroupState aState); 363 void CallHostOrServiceCallback(AvahiEntryGroup *aGroup, otbrError aError); 364 365 static otbrError TxtDataToAvahiStringList(const TxtData &aTxtData, 366 AvahiStringList *aBuffer, 367 size_t aBufferSize, 368 AvahiStringList *&aHead); 369 370 ServiceRegistration *FindServiceRegistration(const AvahiEntryGroup *aEntryGroup); 371 HostRegistration *FindHostRegistration(const AvahiEntryGroup *aEntryGroup); 372 KeyRegistration *FindKeyRegistration(const AvahiEntryGroup *aEntryGroup); 373 374 AvahiClient *mClient; 375 std::unique_ptr<AvahiPoller> mPoller; 376 State mState; 377 StateCallback mStateCallback; 378 379 ServiceSubscriptionList mSubscribedServices; 380 HostSubscriptionList mSubscribedHosts; 381 }; 382 383 } // namespace Mdns 384 385 } // namespace otbr 386 387 /** 388 * @} 389 */ 390 #endif // OTBR_AGENT_MDNS_AVAHI_HPP_ 391