MDNSService.cpp 6.1 KB

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