MDNSService.cpp 5.0 KB

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