MDNSService.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. #include <arpa/inet.h>
  2. #include <ifaddrs.h>
  3. #include <net/if.h>
  4. #include <netdb.h>
  5. #include <unistd.h>
  6. #include <cstring>
  7. #include <vector>
  8. #include <mutex>
  9. #include <atomic>
  10. #if __has_include("avahi-client/client.h")
  11. #include <avahi-client/client.h>
  12. #include <avahi-client/publish.h>
  13. #include <avahi-common/alternative.h>
  14. #include <avahi-common/simple-watch.h>
  15. #elif !defined(BELL_DISABLE_AVAHI)
  16. #define BELL_DISABLE_AVAHI
  17. #endif
  18. #include "BellLogger.h"
  19. #include "MDNSService.h"
  20. #include "mdnssvc.h"
  21. using namespace bell;
  22. #ifndef BELL_DISABLE_AVAHI
  23. static void groupHandler(AvahiEntryGroup* g, AvahiEntryGroupState state,
  24. AVAHI_GCC_UNUSED void* userdata) {}
  25. #endif
  26. class implMDNSService : public MDNSService {
  27. private:
  28. #ifndef BELL_DISABLE_AVAHI
  29. AvahiEntryGroup* avahiGroup;
  30. #endif
  31. struct mdns_service* service;
  32. public:
  33. #ifndef BELL_DISABLE_AVAHI
  34. static AvahiClient* avahiClient;
  35. static AvahiSimplePoll* avahiPoll;
  36. #endif
  37. static struct mdnsd* mdnsServer;
  38. static in_addr_t host;
  39. static std::atomic<size_t> instances;
  40. implMDNSService(struct mdns_service* service) : service(service){ instances++; };
  41. #ifndef BELL_DISABLE_AVAHI
  42. implMDNSService(AvahiEntryGroup* avahiGroup) : avahiGroup(avahiGroup){};
  43. #endif
  44. void unregisterService();
  45. };
  46. struct mdnsd* implMDNSService::mdnsServer = NULL;
  47. in_addr_t implMDNSService::host = INADDR_ANY;
  48. std::atomic<size_t> implMDNSService::instances = 0;
  49. static std::mutex registerMutex;
  50. #ifndef BELL_DISABLE_AVAHI
  51. AvahiClient* implMDNSService::avahiClient = NULL;
  52. AvahiSimplePoll* implMDNSService::avahiPoll = NULL;
  53. #endif
  54. /**
  55. * Linux implementation of MDNSService using avahi.
  56. * @see https://www.avahi.org/doxygen/html/
  57. **/
  58. void implMDNSService::unregisterService() {
  59. #ifndef BELL_DISABLE_AVAHI
  60. if (avahiGroup) {
  61. avahi_entry_group_free(avahiGroup);
  62. if (!--instances && implMDNSService::avahiClient) {
  63. avahi_client_free(implMDNSService::avahiClient);
  64. avahi_simple_poll_free(implMDNSService::avahiPoll);
  65. implMDNSService::avahiClient = nullptr;
  66. implMDNSService::avahiPoll = nullptr;
  67. }
  68. } else
  69. #endif
  70. {
  71. mdns_service_remove(implMDNSService::mdnsServer, service);
  72. if (!--instances && implMDNSService::mdnsServer) {
  73. mdnsd_stop(implMDNSService::mdnsServer);
  74. implMDNSService::mdnsServer = nullptr;
  75. }
  76. }
  77. }
  78. std::unique_ptr<MDNSService> MDNSService::registerService(
  79. const std::string& serviceName, const std::string& serviceType,
  80. const std::string& serviceProto, const std::string& serviceHost,
  81. int servicePort, const std::map<std::string, std::string> txtData) {
  82. std::lock_guard lock(registerMutex);
  83. #ifndef BELL_DISABLE_AVAHI
  84. // try avahi first if available
  85. if (!implMDNSService::avahiPoll) {
  86. implMDNSService::avahiPoll = avahi_simple_poll_new();
  87. }
  88. if (implMDNSService::avahiPoll && !implMDNSService::avahiClient) {
  89. implMDNSService::avahiClient =
  90. avahi_client_new(avahi_simple_poll_get(implMDNSService::avahiPoll),
  91. AvahiClientFlags(0), NULL, NULL, NULL);
  92. }
  93. AvahiEntryGroup* avahiGroup = NULL;
  94. if (implMDNSService::avahiClient &&
  95. (avahiGroup = avahi_entry_group_new(implMDNSService::avahiClient,
  96. groupHandler, NULL)) == NULL) {
  97. BELL_LOG(error, "MDNS", "cannot create service %s", serviceName.c_str());
  98. }
  99. if (avahiGroup != NULL) {
  100. AvahiStringList* avahiTxt = NULL;
  101. for (auto& [key, value] : txtData) {
  102. avahiTxt =
  103. avahi_string_list_add_pair(avahiTxt, key.c_str(), value.c_str());
  104. }
  105. std::string type(serviceType + "." + serviceProto);
  106. int ret = avahi_entry_group_add_service_strlst(
  107. avahiGroup, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, (AvahiPublishFlags)0,
  108. serviceName.c_str(), type.c_str(), NULL, NULL, servicePort, avahiTxt);
  109. avahi_string_list_free(avahiTxt);
  110. if (ret >= 0) {
  111. ret = avahi_entry_group_commit(avahiGroup);
  112. }
  113. if (ret < 0) {
  114. BELL_LOG(error, "MDNS", "cannot run service %s", serviceName.c_str());
  115. avahi_entry_group_free(avahiGroup);
  116. } else {
  117. BELL_LOG(info, "MDNS", "using avahi for %s", serviceName.c_str());
  118. return std::make_unique<implMDNSService>(avahiGroup);
  119. }
  120. }
  121. #endif
  122. // avahi failed, use build-in server
  123. struct ifaddrs* ifaddr;
  124. // get the host address first
  125. if (serviceHost.size()) {
  126. struct hostent* h = gethostbyname(serviceHost.c_str());
  127. if (h) {
  128. memcpy(&implMDNSService::host, h->h_addr_list[0], 4);
  129. }
  130. }
  131. // try go guess ifaddr if we have nothing as listening to INADDR_ANY usually does not work
  132. if (implMDNSService::host == INADDR_ANY && getifaddrs(&ifaddr) != -1) {
  133. for (struct ifaddrs* ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
  134. if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET ||
  135. !(ifa->ifa_flags & IFF_UP) || !(ifa->ifa_flags & IFF_MULTICAST) ||
  136. (ifa->ifa_flags & IFF_LOOPBACK))
  137. continue;
  138. implMDNSService::host =
  139. ((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr;
  140. break;
  141. }
  142. freeifaddrs(ifaddr);
  143. }
  144. if (!implMDNSService::mdnsServer) {
  145. char hostname[256];
  146. struct in_addr addr;
  147. // it's the same, but who knows..
  148. addr.s_addr = implMDNSService::host;
  149. gethostname(hostname, sizeof(hostname));
  150. implMDNSService::mdnsServer = mdnsd_start(addr, false);
  151. if (implMDNSService::mdnsServer) {
  152. mdnsd_set_hostname(implMDNSService::mdnsServer, hostname, addr);
  153. }
  154. }
  155. if (implMDNSService::mdnsServer) {
  156. std::vector<const char*> txt;
  157. std::vector<std::unique_ptr<std::string>> txtStr;
  158. for (auto& [key, value] : txtData) {
  159. auto str = make_unique<std::string>(key + "=" + value);
  160. txtStr.push_back(std::move(str));
  161. txt.push_back(txtStr.back()->c_str());
  162. }
  163. txt.push_back(NULL);
  164. std::string type(serviceType + "." + serviceProto + ".local");
  165. BELL_LOG(info, "MDNS", "using built-in mDNS for %s", serviceName.c_str());
  166. auto service =
  167. mdnsd_register_svc(implMDNSService::mdnsServer, serviceName.c_str(),
  168. type.c_str(), servicePort, NULL, txt.data());
  169. if (service) return std::make_unique<implMDNSService>(service);
  170. }
  171. BELL_LOG(error, "MDNS", "cannot start any mDNS listener for %s",
  172. serviceName.c_str());
  173. return nullptr;
  174. }