Webthing-CPP: a modern CPP implementation of the WebThings API


LCOV - code coverage report
Current view: top level - webthing - mdns.hpp (source / functions) Coverage Total Hit
Test: filtered_coverage.info Lines: 56.9 % 392 223
Test Date: 2025-03-15 12:45:00 Functions: 91.7 % 12 11

            Line data    Source code
       1              : // Webthing-CPP
       2              : // SPDX-FileCopyrightText: 2023-present Benno Waldhauer
       3              : // SPDX-License-Identifier: MIT
       4              : 
       5              : // MdnsService follows mdns.c example of Mattia Janssons mdns lib.
       6              : // also cf. https://github.com/mjansson/mdns 
       7              : 
       8              : #pragma once
       9              : 
      10              : // Link with Iphlpapi.lib
      11              : #pragma comment(lib, "IPHLPAPI.lib")
      12              : 
      13              : #include <mdns.h>
      14              : #include <bw/webthing/utils.hpp>
      15              : 
      16              : #ifdef _WIN32
      17              : #include <iphlpapi.h>
      18              : #else
      19              : #include <ifaddrs.h>
      20              : #include <net/if.h>
      21              : #include <netdb.h>
      22              : #endif
      23              : 
      24              : namespace bw::webthing
      25              : {
      26              : 
      27          105 : static void mdns_log(std::string msg)
      28              : {
      29          105 :     logger::trace("MdnsService - " + msg);
      30          105 : }
      31              : 
      32              : const static int MAX_HOST_SIZE = 1025;
      33              : 
      34              : static char addrbuffer[64];
      35              : static char entrybuffer[256];
      36              : static char namebuffer[256];
      37              : static char sendbuffer[1024];
      38              : static mdns_record_txt_t txtbuffer[128];
      39              : 
      40              : static struct sockaddr_in service_address_ipv4;
      41              : static struct sockaddr_in6 service_address_ipv6;
      42              : 
      43              : static int has_ipv4;
      44              : static int has_ipv6;
      45              : 
      46              : static int is_tls_server;
      47              : 
      48              : 
      49           45 : static mdns_string_t ipv4_address_to_string(char* buffer, size_t capacity, const struct sockaddr_in* addr,
      50              :                     size_t addrlen) {
      51           45 :     char host[MAX_HOST_SIZE] = {0};
      52           45 :     char service[NI_MAXSERV] = {0};
      53           45 :     int ret = getnameinfo((const struct sockaddr*)addr, (socklen_t)addrlen, host, MAX_HOST_SIZE,
      54              :                         service, NI_MAXSERV, NI_NUMERICSERV | NI_NUMERICHOST);
      55           45 :     int len = 0;
      56           45 :     if (ret == 0) {
      57           45 :         if (addr->sin_port != 0)
      58            0 :             len = snprintf(buffer, capacity, "%s:%s", host, service);
      59              :         else
      60           45 :             len = snprintf(buffer, capacity, "%s", host);
      61              :     }
      62           45 :     if (len >= (int)capacity)
      63            0 :         len = (int)capacity - 1;
      64              :     mdns_string_t str;
      65           45 :     str.str = buffer;
      66           45 :     str.length = len;
      67           45 :     return str;
      68              : }
      69              : 
      70            0 : static mdns_string_t ipv6_address_to_string(char* buffer, size_t capacity, const struct sockaddr_in6* addr,
      71              :                     size_t addrlen) {
      72            0 :     char host[NI_MAXHOST] = {0};
      73            0 :     char service[NI_MAXSERV] = {0};
      74            0 :     int ret = getnameinfo((const struct sockaddr*)addr, (socklen_t)addrlen, host, NI_MAXHOST,
      75              :                         service, NI_MAXSERV, NI_NUMERICSERV | NI_NUMERICHOST);
      76            0 :     int len = 0;
      77            0 :     if (ret == 0) {
      78            0 :         if (addr->sin6_port != 0)
      79            0 :             len = snprintf(buffer, capacity, "[%s]:%s", host, service);
      80              :         else
      81            0 :             len = snprintf(buffer, capacity, "%s", host);
      82              :     }
      83            0 :     if (len >= (int)capacity)
      84            0 :         len = (int)capacity - 1;
      85              :     mdns_string_t str;
      86            0 :     str.str = buffer;
      87            0 :     str.length = len;
      88            0 :     return str;
      89              : }
      90              : 
      91           30 : static mdns_string_t ip_address_to_string(char* buffer, size_t capacity, const struct sockaddr* addr, size_t addrlen) {
      92           30 :     if (addr->sa_family == AF_INET6)
      93            0 :         return ipv6_address_to_string(buffer, capacity, (const struct sockaddr_in6*)addr, addrlen);
      94           30 :     return ipv4_address_to_string(buffer, capacity, (const struct sockaddr_in*)addr, addrlen);
      95              : }
      96              : 
      97           15 : static std::vector<std::string> get_addresses()
      98              : {
      99           15 :     std::vector<std::string> ips;
     100              : 
     101              :     #ifdef _WIN32
     102              : 
     103              :         PIP_ADAPTER_ADDRESSES ifaddresses = NULL;
     104              :         ULONG size = 0;
     105              :         DWORD ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_ANYCAST, NULL, ifaddresses, &size);
     106              :         if (ret == ERROR_BUFFER_OVERFLOW)
     107              :         {
     108              :             ifaddresses = (IP_ADAPTER_ADDRESSES *)malloc(size);
     109              :             ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_ANYCAST, NULL, ifaddresses, &size);
     110              :         }
     111              : 
     112              :         for (PIP_ADAPTER_ADDRESSES adapter = ifaddresses; adapter != NULL; adapter = adapter->Next)
     113              :         {
     114              :             for (PIP_ADAPTER_UNICAST_ADDRESS unicast = adapter->FirstUnicastAddress; unicast != NULL; unicast = unicast->Next)
     115              :             {
     116              :                 sockaddr *sa = unicast->Address.lpSockaddr;
     117              :                 char ip[INET6_ADDRSTRLEN];
     118              : 
     119              :                 if (sa->sa_family == AF_INET)
     120              :                 {
     121              :                     struct sockaddr_in *saddr = (struct sockaddr_in *)sa;
     122              :                     if (saddr->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
     123              :                         continue;
     124              : 
     125              :                     inet_ntop(AF_INET, &(saddr)->sin_addr, ip, INET_ADDRSTRLEN);
     126              :                     ips.push_back(ip);
     127              :                 }
     128              :                 else if (sa->sa_family == AF_INET6)
     129              :                 {
     130              :                     struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)sa;
     131              :                     // Ignore link-local addresses
     132              :                     if (saddr->sin6_scope_id)
     133              :                         continue;
     134              : 
     135              :                     static const unsigned char localhost[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
     136              :                     static const unsigned char localhost_mapped[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x7f, 0, 0, 1};
     137              : 
     138              :                     if (!memcmp(saddr->sin6_addr.s6_addr, localhost, 16) ||
     139              :                         !memcmp(saddr->sin6_addr.s6_addr, localhost_mapped, 16))
     140              :                     {
     141              :                         continue;
     142              :                     }
     143              : 
     144              :                     inet_ntop(AF_INET6, &((sockaddr_in6 *)sa)->sin6_addr, ip, INET6_ADDRSTRLEN);
     145              :                     ips.push_back(ip);
     146              :                 }
     147              :             }
     148              :         }
     149              :         free(ifaddresses);
     150              :         
     151              :     #else
     152           15 :         struct ifaddrs* ifaddr = 0;
     153           15 :         struct ifaddrs* ifa = 0;
     154              : 
     155           15 :         if (getifaddrs(&ifaddr) >= 0)
     156              :         {
     157              :             char addr_buffer[64];
     158          135 :             for (ifa = ifaddr; ifa; ifa = ifa->ifa_next)
     159              :             {
     160          120 :                 if(!ifa->ifa_addr)
     161            0 :                     continue;
     162              : 
     163          120 :                 if(ifa->ifa_addr->sa_family == AF_INET)
     164              :                 {
     165           45 :                     struct sockaddr_in* saddr = (struct sockaddr_in*)ifa->ifa_addr;
     166           45 :                     if (saddr->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
     167           15 :                         continue;
     168           30 :                     mdns_string_t ip = ip_address_to_string(addr_buffer, sizeof(addr_buffer), ifa->ifa_addr, sizeof(sockaddr_in));
     169           60 :                     ips.push_back(ip.str);
     170              : 
     171              :                 }
     172           75 :                 else if(ifa->ifa_addr->sa_family == AF_INET6)
     173              :                 {
     174           30 :                     struct sockaddr_in6* saddr = (struct sockaddr_in6*)ifa->ifa_addr;
     175              :                     // Ignore link-local addresses
     176           30 :                     if (saddr->sin6_scope_id)
     177           30 :                         continue;
     178              : 
     179              :                     static const unsigned char localhost[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
     180              :                     static const unsigned char localhost_mapped[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x7f, 0, 0, 1};
     181              : 
     182           15 :                     if(!memcmp(saddr->sin6_addr.s6_addr, localhost, 16) ||
     183            0 :                        !memcmp(saddr->sin6_addr.s6_addr, localhost_mapped, 16))
     184              :                     {
     185           15 :                         continue;
     186              :                     }
     187              : 
     188            0 :                     mdns_string_t ip = ip_address_to_string(addr_buffer, sizeof(addr_buffer), ifa->ifa_addr, sizeof(sockaddr_in6));
     189            0 :                     ips.push_back(ip.str);
     190              :                 }
     191              :             }
     192              :         }
     193           15 :         freeifaddrs(ifaddr);
     194              :     #endif
     195              : 
     196           30 :     return ips;
     197            0 : }
     198              : 
     199              : 
     200              : 
     201              : struct MdnsService
     202              : {
     203              :     volatile bool run_requested = false;
     204              :     volatile bool running = false;
     205              :     
     206              :     // Data for our service including the mDNS records
     207              :     typedef struct {
     208              :         mdns_string_t service;
     209              :         mdns_string_t hostname;
     210              :         mdns_string_t service_instance;
     211              :         mdns_string_t hostname_qualified;
     212              :         struct sockaddr_in address_ipv4;
     213              :         struct sockaddr_in6 address_ipv6;
     214              :         int port;
     215              :         mdns_record_t record_ptr;
     216              :         mdns_record_t record_srv;
     217              :         mdns_record_t record_a;
     218              :         mdns_record_t record_aaaa;
     219              :         mdns_record_t txt_record[2];
     220              :     } service_t;
     221              :     
     222              :    
     223              :     // Callback handling questions incoming on service sockets
     224           96 :     static int service_callback(int sock, const struct sockaddr* from, size_t addrlen, mdns_entry_type_t entry,
     225              :                     uint16_t query_id, uint16_t ui16_rtype, uint16_t rclass, uint32_t ttl, const void* data,
     226              :                     size_t size, size_t name_offset, size_t name_length, size_t record_offset,
     227              :                     size_t record_length, void* user_data) {
     228              :         (void)sizeof(ttl);
     229           96 :         if (entry != MDNS_ENTRYTYPE_QUESTION)
     230           96 :             return 0;
     231              : 
     232            0 :         mdns_record_type rtype = static_cast<mdns_record_type>(ui16_rtype);
     233              : 
     234            0 :         const char dns_sd[] = "_services._dns-sd._udp.local.";
     235            0 :         const service_t* service = (const service_t*)user_data;
     236              : 
     237            0 :         mdns_string_t fromaddrstr = ip_address_to_string(addrbuffer, sizeof(addrbuffer), from, addrlen);
     238              : 
     239            0 :         size_t offset = name_offset;
     240            0 :         mdns_string_t name = mdns_string_extract(data, size, &offset, namebuffer, sizeof(namebuffer));
     241              : 
     242            0 :         const char* record_name = 0;
     243            0 :         if (rtype == MDNS_RECORDTYPE_PTR)
     244            0 :             record_name = "PTR";
     245            0 :         else if (rtype == MDNS_RECORDTYPE_SRV)
     246            0 :             record_name = "SRV";
     247            0 :         else if (rtype == MDNS_RECORDTYPE_A)
     248            0 :             record_name = "A";
     249            0 :         else if (rtype == MDNS_RECORDTYPE_AAAA)
     250            0 :             record_name = "AAAA";
     251            0 :         else if (rtype == MDNS_RECORDTYPE_TXT)
     252            0 :             record_name = "TXT";
     253            0 :         else if (rtype == MDNS_RECORDTYPE_ANY)
     254            0 :             record_name = "ANY";
     255              :         else
     256            0 :             return 0;
     257            0 :         mdns_log("Query " + std::string(record_name) + " " +  std::string(name.str));
     258              : 
     259            0 :         if ((name.length == (sizeof(dns_sd) - 1)) &&
     260            0 :             (strncmp(name.str, dns_sd, sizeof(dns_sd) - 1) == 0)) {
     261            0 :             if ((rtype == MDNS_RECORDTYPE_PTR) || (rtype == MDNS_RECORDTYPE_ANY)) {
     262              :                 // The PTR query was for the DNS-SD domain, send answer with a PTR record for the
     263              :                 // service name we advertise, typically on the "<_service-name>._tcp.local." format
     264              : 
     265              :                 // Answer PTR record reverse mapping "<_service-name>._tcp.local." to
     266              :                 // "<hostname>.<_service-name>._tcp.local."
     267            0 :                 mdns_record_t answer = {
     268            0 :                     name, MDNS_RECORDTYPE_PTR, {service->service}};
     269              : 
     270              :                 // Send the answer, unicast or multicast depending on flag in query
     271            0 :                 uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE);
     272            0 :                 mdns_log("  --> answer " + std::string(answer.data.ptr.name.str) + " " + 
     273            0 :                     std::string(unicast ? "unicast" : "multicast"));
     274              : 
     275            0 :                 if (unicast) {
     276            0 :                     mdns_query_answer_unicast(sock, from, addrlen, sendbuffer, sizeof(sendbuffer),
     277              :                                             query_id, rtype, name.str, name.length, answer, 0, 0, 0,
     278              :                                             0);
     279              :                 } else {
     280            0 :                     mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, 0, 0, 0,
     281              :                                                 0);
     282              :                 }
     283              :             }
     284            0 :         } else if ((name.length == service->service.length) &&
     285            0 :                 (strncmp(name.str, service->service.str, name.length) == 0)) {
     286            0 :             if ((rtype == MDNS_RECORDTYPE_PTR) || (rtype == MDNS_RECORDTYPE_ANY)) {
     287              :                 // The PTR query was for our service (usually "<_service-name._tcp.local"), answer a PTR
     288              :                 // record reverse mapping the queried service name to our service instance name
     289              :                 // (typically on the "<hostname>.<_service-name>._tcp.local." format), and add
     290              :                 // additional records containing the SRV record mapping the service instance name to our
     291              :                 // qualified hostname (typically "<hostname>.local.") and port, as well as any IPv4/IPv6
     292              :                 // address for the hostname as A/AAAA records, and two test TXT records
     293              : 
     294              :                 // Answer PTR record reverse mapping "<_service-name>._tcp.local." to
     295              :                 // "<hostname>.<_service-name>._tcp.local."
     296            0 :                 mdns_record_t answer = service->record_ptr;
     297              : 
     298            0 :                 mdns_record_t additional[5] = {0};
     299            0 :                 size_t additional_count = 0;
     300              : 
     301              :                 // SRV record mapping "<hostname>.<_service-name>._tcp.local." to
     302              :                 // "<hostname>.local." with port. Set weight & priority to 0.
     303            0 :                 additional[additional_count++] = service->record_srv;
     304              : 
     305              :                 // A/AAAA records mapping "<hostname>.local." to IPv4/IPv6 addresses
     306            0 :                 if (service->address_ipv4.sin_family == AF_INET)
     307            0 :                     additional[additional_count++] = service->record_a;
     308            0 :                 if (service->address_ipv6.sin6_family == AF_INET6)
     309            0 :                     additional[additional_count++] = service->record_aaaa;
     310              : 
     311              :                 // Add two test TXT records for our service instance name, will be coalesced into
     312              :                 // one record with both key-value pair strings by the library
     313            0 :                 additional[additional_count++] = service->txt_record[0];
     314            0 :                 if(is_tls_server)
     315            0 :                     additional[additional_count++] = service->txt_record[1];
     316              : 
     317              :                 // Send the answer, unicast or multicast depending on flag in query
     318            0 :                 uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE);
     319            0 :                 mdns_log("  --> answer " + std::string(service->record_ptr.data.ptr.name.str) + 
     320            0 :                     " (" + std::string(unicast ? "unicast" : "multicast") + ")");
     321              : 
     322            0 :                 if (unicast) {
     323            0 :                     mdns_query_answer_unicast(sock, from, addrlen, sendbuffer, sizeof(sendbuffer),
     324              :                                             query_id, rtype, name.str, name.length, answer, 0, 0,
     325              :                                             additional, additional_count);
     326              :                 } else {
     327            0 :                     mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, 0, 0,
     328              :                                                 additional, additional_count);
     329              :                 }
     330              :             }
     331            0 :         } else if ((name.length == service->service_instance.length) &&
     332            0 :                 (strncmp(name.str, service->service_instance.str, name.length) == 0)) {
     333            0 :             if ((rtype == MDNS_RECORDTYPE_SRV) || (rtype == MDNS_RECORDTYPE_ANY)) {
     334              :                 // The SRV query was for our service instance (usually
     335              :                 // "<hostname>.<_service-name._tcp.local"), answer a SRV record mapping the service
     336              :                 // instance name to our qualified hostname (typically "<hostname>.local.") and port, as
     337              :                 // well as any IPv4/IPv6 address for the hostname as A/AAAA records, and two test TXT
     338              :                 // records
     339              : 
     340              :                 // Answer PTR record reverse mapping "<_service-name>._tcp.local." to
     341              :                 // "<hostname>.<_service-name>._tcp.local."
     342            0 :                 mdns_record_t answer = service->record_srv;
     343              : 
     344            0 :                 mdns_record_t additional[5] = {0};
     345            0 :                 size_t additional_count = 0;
     346              : 
     347              :                 // A/AAAA records mapping "<hostname>.local." to IPv4/IPv6 addresses
     348            0 :                 if (service->address_ipv4.sin_family == AF_INET)
     349            0 :                     additional[additional_count++] = service->record_a;
     350            0 :                 if (service->address_ipv6.sin6_family == AF_INET6)
     351            0 :                     additional[additional_count++] = service->record_aaaa;
     352              : 
     353              :                 // Add two test TXT records for our service instance name, will be coalesced into
     354              :                 // one record with both key-value pair strings by the library
     355            0 :                 additional[additional_count++] = service->txt_record[0];
     356            0 :                 if(is_tls_server)
     357            0 :                     additional[additional_count++] = service->txt_record[1];
     358              : 
     359              :                 // Send the answer, unicast or multicast depending on flag in query
     360            0 :                 uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE);
     361            0 :                 mdns_log("  --> answer " + std::string(service->record_srv.data.srv.name.str) + 
     362            0 :                     " port " + std::to_string(service->port) + " (" + (unicast ? "unicast" : "multicast") + ")");
     363              : 
     364            0 :                 if (unicast) {
     365            0 :                     mdns_query_answer_unicast(sock, from, addrlen, sendbuffer, sizeof(sendbuffer),
     366              :                                             query_id, rtype, name.str, name.length, answer, 0, 0,
     367              :                                             additional, additional_count);
     368              :                 } else {
     369            0 :                     mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, 0, 0,
     370              :                                                 additional, additional_count);
     371              :                 }
     372              :             }
     373            0 :         } else if ((name.length == service->hostname_qualified.length) &&
     374            0 :                 (strncmp(name.str, service->hostname_qualified.str, name.length) == 0)) {
     375            0 :             if (((rtype == MDNS_RECORDTYPE_A) || (rtype == MDNS_RECORDTYPE_ANY)) &&
     376            0 :                 (service->address_ipv4.sin_family == AF_INET)) {
     377              :                 // The A query was for our qualified hostname (typically "<hostname>.local.") and we
     378              :                 // have an IPv4 address, answer with an A record mappiing the hostname to an IPv4
     379              :                 // address, as well as any IPv6 address for the hostname, and two test TXT records
     380              : 
     381              :                 // Answer A records mapping "<hostname>.local." to IPv4 address
     382            0 :                 mdns_record_t answer = service->record_a;
     383              : 
     384            0 :                 mdns_record_t additional[5] = {0};
     385            0 :                 size_t additional_count = 0;
     386              : 
     387              :                 // AAAA record mapping "<hostname>.local." to IPv6 addresses
     388            0 :                 if (service->address_ipv6.sin6_family == AF_INET6)
     389            0 :                     additional[additional_count++] = service->record_aaaa;
     390              : 
     391              :                 // Add two test TXT records for our service instance name, will be coalesced into
     392              :                 // one record with both key-value pair strings by the library
     393            0 :                 additional[additional_count++] = service->txt_record[0];
     394            0 :                 if(is_tls_server)
     395            0 :                     additional[additional_count++] = service->txt_record[1];
     396              : 
     397              :                 // Send the answer, unicast or multicast depending on flag in query
     398            0 :                 uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE);
     399            0 :                 mdns_string_t addrstr = ip_address_to_string(
     400            0 :                     addrbuffer, sizeof(addrbuffer), (struct sockaddr*)&service->record_a.data.a.addr,
     401              :                     sizeof(service->record_a.data.a.addr));
     402            0 :                 mdns_log("  --> answer " + std::string(service->record_a.name.str) + 
     403            0 :                     " IPv4 " + std::string(addrstr.str) + " (" + std::string(unicast ? "unicast" : "multicast") + ")");
     404              : 
     405            0 :                 if (unicast) {
     406            0 :                     mdns_query_answer_unicast(sock, from, addrlen, sendbuffer, sizeof(sendbuffer),
     407              :                                             query_id, rtype, name.str, name.length, answer, 0, 0,
     408              :                                             additional, additional_count);
     409              :                 } else {
     410            0 :                     mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, 0, 0,
     411              :                                                 additional, additional_count);
     412              :                 }
     413            0 :             } else if (((rtype == MDNS_RECORDTYPE_AAAA) || (rtype == MDNS_RECORDTYPE_ANY)) &&
     414            0 :                     (service->address_ipv6.sin6_family == AF_INET6)) {
     415              :                 // The AAAA query was for our qualified hostname (typically "<hostname>.local.") and we
     416              :                 // have an IPv6 address, answer with an AAAA record mappiing the hostname to an IPv6
     417              :                 // address, as well as any IPv4 address for the hostname, and two test TXT records
     418              : 
     419              :                 // Answer AAAA records mapping "<hostname>.local." to IPv6 address
     420            0 :                 mdns_record_t answer = service->record_aaaa;
     421              : 
     422            0 :                 mdns_record_t additional[5] = {0};
     423            0 :                 size_t additional_count = 0;
     424              : 
     425              :                 // A record mapping "<hostname>.local." to IPv4 addresses
     426            0 :                 if (service->address_ipv4.sin_family == AF_INET)
     427            0 :                     additional[additional_count++] = service->record_a;
     428              : 
     429              :                 // Add two test TXT records for our service instance name, will be coalesced into
     430              :                 // one record with both key-value pair strings by the library
     431            0 :                 additional[additional_count++] = service->txt_record[0];
     432            0 :                 if(is_tls_server)
     433            0 :                     additional[additional_count++] = service->txt_record[1];
     434              : 
     435              :                 // Send the answer, unicast or multicast depending on flag in query
     436            0 :                 uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE);
     437              :                 mdns_string_t addrstr =
     438            0 :                     ip_address_to_string(addrbuffer, sizeof(addrbuffer),
     439            0 :                                         (struct sockaddr*)&service->record_aaaa.data.aaaa.addr,
     440              :                                         sizeof(service->record_aaaa.data.aaaa.addr));
     441            0 :                 mdns_log("  --> answer " + std::string(service->record_aaaa.name.str) + 
     442            0 :                     " IPv6 " + std::string(addrstr.str) + " (" + std::string(unicast ? "unicast" : "multicast") + ")");
     443              : 
     444            0 :                 if (unicast) {
     445            0 :                     mdns_query_answer_unicast(sock, from, addrlen, sendbuffer, sizeof(sendbuffer),
     446              :                                             query_id, rtype, name.str, name.length, answer, 0, 0,
     447              :                                             additional, additional_count);
     448              :                 } else {
     449            0 :                     mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, 0, 0,
     450              :                                                 additional, additional_count);
     451              :                 }
     452              :             }
     453              :         }
     454            0 :         return 0;
     455              :     }
     456              : 
     457              :     // Open sockets for sending one-shot multicast queries from an ephemeral port
     458           15 :     static int open_client_sockets(int* sockets, int max_sockets, int port) {
     459              :         // When sending, each socket can only send to one network interface
     460              :         // Thus we need to open one socket for each interface and address family
     461           15 :         int num_sockets = 0;
     462              : 
     463              :     #ifdef _WIN32
     464              : 
     465              :         IP_ADAPTER_ADDRESSES* adapter_address = 0;
     466              :         ULONG address_size = 8000;
     467              :         unsigned int ret;
     468              :         unsigned int num_retries = 4;
     469              :         do {
     470              :             adapter_address = (IP_ADAPTER_ADDRESSES*)malloc(address_size);
     471              :             ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_ANYCAST, 0,
     472              :                                     adapter_address, &address_size);
     473              :             if (ret == ERROR_BUFFER_OVERFLOW) {
     474              :                 free(adapter_address);
     475              :                 adapter_address = 0;
     476              :                 address_size *= 2;
     477              :             } else {
     478              :                 break;
     479              :             }
     480              :         } while (num_retries-- > 0);
     481              : 
     482              :         if (!adapter_address || (ret != NO_ERROR)) {
     483              :             free(adapter_address);
     484              :             mdns_log("Failed to get network adapter addresses");
     485              :             return num_sockets;
     486              :         }
     487              : 
     488              :         int first_ipv4 = 1;
     489              :         int first_ipv6 = 1;
     490              :         for (PIP_ADAPTER_ADDRESSES adapter = adapter_address; adapter; adapter = adapter->Next) {
     491              :             if (adapter->TunnelType == TUNNEL_TYPE_TEREDO)
     492              :                 continue;
     493              :             if (adapter->OperStatus != IfOperStatusUp)
     494              :                 continue;
     495              : 
     496              :             for (IP_ADAPTER_UNICAST_ADDRESS* unicast = adapter->FirstUnicastAddress; unicast;
     497              :                 unicast = unicast->Next) {
     498              :                 if (unicast->Address.lpSockaddr->sa_family == AF_INET) {
     499              :                     struct sockaddr_in* saddr = (struct sockaddr_in*)unicast->Address.lpSockaddr;
     500              :                     if ((saddr->sin_addr.S_un.S_un_b.s_b1 != 127) ||
     501              :                         (saddr->sin_addr.S_un.S_un_b.s_b2 != 0) ||
     502              :                         (saddr->sin_addr.S_un.S_un_b.s_b3 != 0) ||
     503              :                         (saddr->sin_addr.S_un.S_un_b.s_b4 != 1)) {
     504              :                         int log_addr = 0;
     505              :                         if (first_ipv4) {
     506              :                             service_address_ipv4 = *saddr;
     507              :                             first_ipv4 = 0;
     508              :                             log_addr = 1;
     509              :                         }
     510              :                         has_ipv4 = 1;
     511              :                         if (num_sockets < max_sockets) {
     512              :                             saddr->sin_port = htons((unsigned short)port);
     513              :                             int sock = mdns_socket_open_ipv4(saddr);
     514              :                             if (sock >= 0) {
     515              :                                 sockets[num_sockets++] = sock;
     516              :                                 log_addr = 1;
     517              :                             } else {
     518              :                                 log_addr = 0;
     519              :                             }
     520              :                         }
     521              :                         if (log_addr) {
     522              :                             char buffer[128];
     523              :                             mdns_string_t addr = ipv4_address_to_string(buffer, sizeof(buffer), saddr,
     524              :                                                                         sizeof(struct sockaddr_in));
     525              :                             mdns_log("Local IPv4 address: " + std::string(addr.str));
     526              :                         }
     527              :                     }
     528              :                 } else if (unicast->Address.lpSockaddr->sa_family == AF_INET6) {
     529              :                     struct sockaddr_in6* saddr = (struct sockaddr_in6*)unicast->Address.lpSockaddr;
     530              :                     // Ignore link-local addresses
     531              :                     if (saddr->sin6_scope_id)
     532              :                         continue;
     533              :                     static const unsigned char localhost[] = {0, 0, 0, 0, 0, 0, 0, 0,
     534              :                                                             0, 0, 0, 0, 0, 0, 0, 1};
     535              :                     static const unsigned char localhost_mapped[] = {0, 0, 0,    0,    0,    0, 0, 0,
     536              :                                                                     0, 0, 0xff, 0xff, 0x7f, 0, 0, 1};
     537              :                     if ((unicast->DadState == NldsPreferred) &&
     538              :                         memcmp(saddr->sin6_addr.s6_addr, localhost, 16) &&
     539              :                         memcmp(saddr->sin6_addr.s6_addr, localhost_mapped, 16)) {
     540              :                         int log_addr = 0;
     541              :                         if (first_ipv6) {
     542              :                             service_address_ipv6 = *saddr;
     543              :                             first_ipv6 = 0;
     544              :                             log_addr = 1;
     545              :                         }
     546              :                         has_ipv6 = 1;
     547              :                         if (num_sockets < max_sockets) {
     548              :                             saddr->sin6_port = htons((unsigned short)port);
     549              :                             int sock = mdns_socket_open_ipv6(saddr);
     550              :                             if (sock >= 0) {
     551              :                                 sockets[num_sockets++] = sock;
     552              :                                 log_addr = 1;
     553              :                             } else {
     554              :                                 log_addr = 0;
     555              :                             }
     556              :                         }
     557              :                         if (log_addr) {
     558              :                             char buffer[128];
     559              :                             mdns_string_t addr = ipv6_address_to_string(buffer, sizeof(buffer), saddr,
     560              :                                                                         sizeof(struct sockaddr_in6));
     561              :                             mdns_log("Local IPv6 address: " + std::string(addr.str));
     562              :                         }
     563              :                     }
     564              :                 }
     565              :             }
     566              :         }
     567              : 
     568              :         free(adapter_address);
     569              : 
     570              :     #else
     571              : 
     572           15 :         struct ifaddrs* ifaddr = 0;
     573           15 :         struct ifaddrs* ifa = 0;
     574              : 
     575           15 :         if (getifaddrs(&ifaddr) < 0)
     576            0 :             mdns_log("Unable to get interface addresses");
     577              : 
     578           15 :         int first_ipv4 = 1;
     579           15 :         int first_ipv6 = 1;
     580          135 :         for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
     581          120 :             if (!ifa->ifa_addr)
     582            0 :                 continue;
     583          120 :             if (!(ifa->ifa_flags & IFF_UP) || !(ifa->ifa_flags & IFF_MULTICAST))
     584           45 :                 continue;
     585           75 :             if ((ifa->ifa_flags & IFF_LOOPBACK) || (ifa->ifa_flags & IFF_POINTOPOINT))
     586            0 :                 continue;
     587              : 
     588           75 :             if (ifa->ifa_addr->sa_family == AF_INET) {
     589           30 :                 struct sockaddr_in* saddr = (struct sockaddr_in*)ifa->ifa_addr;
     590           30 :                 if (saddr->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
     591           30 :                     int log_addr = 0;
     592           30 :                     if (first_ipv4) {
     593           15 :                         service_address_ipv4 = *saddr;
     594           15 :                         first_ipv4 = 0;
     595           15 :                         log_addr = 1;
     596              :                     }
     597           30 :                     has_ipv4 = 1;
     598           30 :                     if (num_sockets < max_sockets) {
     599            0 :                         saddr->sin_port = htons(port);
     600            0 :                         int sock = mdns_socket_open_ipv4(saddr);
     601            0 :                         if (sock >= 0) {
     602            0 :                             sockets[num_sockets++] = sock;
     603            0 :                             log_addr = 1;
     604              :                         } else {
     605            0 :                             log_addr = 0;
     606              :                         }
     607              :                     }
     608           30 :                     if (log_addr) {
     609              :                         char buffer[128];
     610           15 :                         mdns_string_t addr = ipv4_address_to_string(buffer, sizeof(buffer), saddr,
     611              :                                                                     sizeof(struct sockaddr_in));
     612           30 :                         mdns_log("Local IPv4 address: " + std::string(addr.str));
     613              :                     }
     614              :                 }
     615           45 :             } else if (ifa->ifa_addr->sa_family == AF_INET6) {
     616           15 :                 struct sockaddr_in6* saddr = (struct sockaddr_in6*)ifa->ifa_addr;
     617              :                 // Ignore link-local addresses
     618           15 :                 if (saddr->sin6_scope_id)
     619           15 :                     continue;
     620              :                 static const unsigned char localhost[] = {0, 0, 0, 0, 0, 0, 0, 0,
     621              :                                                         0, 0, 0, 0, 0, 0, 0, 1};
     622              :                 static const unsigned char localhost_mapped[] = {0, 0, 0,    0,    0,    0, 0, 0,
     623              :                                                                 0, 0, 0xff, 0xff, 0x7f, 0, 0, 1};
     624            0 :                 if (memcmp(saddr->sin6_addr.s6_addr, localhost, 16) &&
     625            0 :                     memcmp(saddr->sin6_addr.s6_addr, localhost_mapped, 16)) {
     626            0 :                     int log_addr = 0;
     627            0 :                     if (first_ipv6) {
     628            0 :                         service_address_ipv6 = *saddr;
     629            0 :                         first_ipv6 = 0;
     630            0 :                         log_addr = 1;
     631              :                     }
     632            0 :                     has_ipv6 = 1;
     633            0 :                     if (num_sockets < max_sockets) {
     634            0 :                         saddr->sin6_port = htons(port);
     635            0 :                         int sock = mdns_socket_open_ipv6(saddr);
     636            0 :                         if (sock >= 0) {
     637            0 :                             sockets[num_sockets++] = sock;
     638            0 :                             log_addr = 1;
     639              :                         } else {
     640            0 :                             log_addr = 0;
     641              :                         }
     642              :                     }
     643            0 :                     if (log_addr) {
     644              :                         char buffer[128];
     645            0 :                         mdns_string_t addr = ipv6_address_to_string(buffer, sizeof(buffer), saddr,
     646              :                                                                     sizeof(struct sockaddr_in6));
     647            0 :                         mdns_log("Local IPv6 address: " + std::string(addr.str));
     648              :                     }
     649              :                 }
     650              :             }
     651              :         }
     652              : 
     653           15 :         freeifaddrs(ifaddr);
     654              : 
     655              :     #endif
     656              : 
     657           15 :         return num_sockets;
     658              :     }
     659              : 
     660              :     // Open sockets to listen to incoming mDNS queries on port 5353
     661           15 :     static int open_service_sockets(int* sockets, int max_sockets) {
     662              :         // When recieving, each socket can recieve data from all network interfaces
     663              :         // Thus we only need to open one socket for each address family
     664           15 :         int num_sockets = 0;
     665              : 
     666              :         // Call the client socket function to enumerate and get local addresses,
     667              :         // but not open the actual sockets
     668           15 :         open_client_sockets(0, 0, 0);
     669              : 
     670           15 :         if (num_sockets < max_sockets) {
     671              :             struct sockaddr_in sock_addr;
     672           15 :             memset(&sock_addr, 0, sizeof(struct sockaddr_in));
     673           15 :             sock_addr.sin_family = AF_INET;
     674              :     #ifdef _WIN32
     675              :             sock_addr.sin_addr = in4addr_any;
     676              :     #else
     677           15 :             sock_addr.sin_addr.s_addr = INADDR_ANY;
     678              :     #endif
     679           15 :             sock_addr.sin_port = htons(MDNS_PORT);
     680              :     #ifdef __APPLE__
     681              :             sock_addr.sin_len = sizeof(struct sockaddr_in);
     682              :     #endif
     683           15 :             int sock = mdns_socket_open_ipv4(&sock_addr);
     684           15 :             if (sock >= 0)
     685           15 :                 sockets[num_sockets++] = sock;
     686              :         }
     687              : 
     688           15 :         if (num_sockets < max_sockets) {
     689              :             struct sockaddr_in6 sock_addr;
     690           15 :             memset(&sock_addr, 0, sizeof(struct sockaddr_in6));
     691           15 :             sock_addr.sin6_family = AF_INET6;
     692           15 :             sock_addr.sin6_addr = in6addr_any;
     693           15 :             sock_addr.sin6_port = htons(MDNS_PORT);
     694              :     #ifdef __APPLE__
     695              :             sock_addr.sin6_len = sizeof(struct sockaddr_in6);
     696              :     #endif
     697           15 :             int sock = mdns_socket_open_ipv6(&sock_addr);
     698           15 :             if (sock >= 0)
     699           15 :                 sockets[num_sockets++] = sock;
     700              :         }
     701              : 
     702           15 :         return num_sockets;
     703              :     }
     704              : 
     705              : 
     706              : 
     707              :     // Provide a mDNS service, answering incoming DNS-SD and mDNS queries
     708           15 :     int service_mdns(const char* hostname, const char* service_name, int service_port, const char* path, bool tls)
     709              :     {
     710           15 :         running = true;
     711              : 
     712              :         int sockets[32];
     713           15 :         int num_sockets = open_service_sockets(sockets, sizeof(sockets) / sizeof(sockets[0]));
     714           15 :         if (num_sockets <= 0) {
     715            0 :             mdns_log("Failed to open any client sockets");
     716            0 :             return -1;
     717              :         }
     718           15 :         mdns_log("Opened " + std::to_string(num_sockets) + " socket" + std::string(num_sockets ? "s" : "") + " for mDNS service");
     719              : 
     720           15 :         size_t service_name_length = strlen(service_name);
     721           15 :         if (!service_name_length) {
     722            0 :             mdns_log("Invalid service name");
     723            0 :             return -1;
     724              :         }
     725              : 
     726           15 :         char* service_name_buffer = (char*)malloc(service_name_length + 2);
     727           15 :         memcpy(service_name_buffer, service_name, service_name_length);
     728           15 :         if (service_name_buffer[service_name_length - 1] != '.')
     729            0 :             service_name_buffer[service_name_length++] = '.';
     730           15 :         service_name_buffer[service_name_length] = 0;
     731           15 :         service_name = service_name_buffer;
     732              : 
     733           45 :         mdns_log("Service mDNS: " + std::string(service_name) + ":" + std::to_string(service_port));
     734           15 :         mdns_log("Hostname: " + std::string(hostname));
     735              : 
     736           15 :         size_t capacity = 2048;
     737           15 :         void* buffer = malloc(capacity);
     738              : 
     739           15 :         mdns_string_t service_string = mdns_string_t{service_name, strlen(service_name)};
     740           15 :         mdns_string_t hostname_string = mdns_string_t{hostname, strlen(hostname)};
     741              : 
     742              :         // Build the service instance "<hostname>.<_service-name>._tcp.local." string
     743           15 :         char service_instance_buffer[256] = {0};
     744           15 :         snprintf(service_instance_buffer, sizeof(service_instance_buffer) - 1, "%.*s.%.*s",
     745           15 :                 MDNS_STRING_FORMAT(hostname_string), MDNS_STRING_FORMAT(service_string));
     746              :         mdns_string_t service_instance_string =
     747           15 :             mdns_string_t{service_instance_buffer, strlen(service_instance_buffer)};
     748              : 
     749              :         // Build the "<hostname>.local." string
     750           15 :         char qualified_hostname_buffer[256] = {0};
     751           15 :         snprintf(qualified_hostname_buffer, sizeof(qualified_hostname_buffer) - 1, "%.*s.local.",
     752           15 :                 MDNS_STRING_FORMAT(hostname_string));
     753              :         mdns_string_t hostname_qualified_string =
     754           15 :             mdns_string_t{qualified_hostname_buffer, strlen(qualified_hostname_buffer)};
     755              : 
     756           15 :         service_t service = {0};
     757           15 :         service.service = service_string;
     758           15 :         service.hostname = hostname_string;
     759           15 :         service.service_instance = service_instance_string;
     760           15 :         service.hostname_qualified = hostname_qualified_string;
     761           15 :         service.address_ipv4 = service_address_ipv4;
     762           15 :         service.address_ipv6 = service_address_ipv6;
     763           15 :         service.port = service_port;
     764              : 
     765              :         // Setup our mDNS records
     766              : 
     767              :         // PTR record reverse mapping "<_service-name>._tcp.local." to
     768              :         // "<hostname>.<_service-name>._tcp.local."
     769              :         mdns_record_t::mdns_record_data rd_ptr;
     770           15 :         rd_ptr.ptr = { service.service_instance };
     771              : 
     772              :         mdns_record_t ptr_rec;
     773           15 :         ptr_rec.name = service.service;
     774           15 :         ptr_rec.type = MDNS_RECORDTYPE_PTR;
     775           15 :         ptr_rec.data = rd_ptr;
     776           15 :         ptr_rec.rclass = 0;
     777           15 :         ptr_rec.ttl = 0;
     778              : 
     779           15 :         service.record_ptr = ptr_rec;
     780              : 
     781              :         // SRV record mapping "<hostname>.<_service-name>._tcp.local." to
     782              :         // "<hostname>.local." with port. Set weight & priority to 0.
     783           15 :         uint16_t p = service.port;
     784              :         mdns_record_t::mdns_record_data rd_srv;
     785           15 :         rd_srv.srv = { 0, 0, p, service.hostname_qualified};
     786              : 
     787              :         mdns_record_t srv_rec;
     788           15 :         srv_rec.name = service.service_instance;
     789           15 :         srv_rec.type = MDNS_RECORDTYPE_SRV;
     790           15 :         srv_rec.data = rd_srv;
     791           15 :         srv_rec.rclass = 0;
     792           15 :         srv_rec.ttl = 0;
     793              : 
     794           15 :         service.record_srv = srv_rec;
     795              : 
     796              :         // A/AAAA records mapping "<hostname>.local." to IPv4/IPv6 addresses
     797              :         mdns_record_t::mdns_record_data rd_a;
     798           15 :         rd_a.a = {service.address_ipv4};
     799              : 
     800              :         mdns_record_t a_rec;
     801           15 :         a_rec.name = service.hostname_qualified;
     802           15 :         a_rec.type = MDNS_RECORDTYPE_A;
     803           15 :         a_rec.data = rd_a;
     804           15 :         a_rec.rclass = 0;
     805           15 :         a_rec.ttl = 0;
     806              : 
     807           15 :         service.record_a = a_rec;
     808              : 
     809              : 
     810              :         mdns_record_t::mdns_record_data rd_aaaa;
     811           15 :         rd_aaaa.aaaa = {service.address_ipv6};
     812              : 
     813              :         mdns_record_t aaaa_rec;
     814           15 :         aaaa_rec.name = service.hostname_qualified;
     815           15 :         aaaa_rec.type = MDNS_RECORDTYPE_AAAA;
     816           15 :         aaaa_rec.data = rd_aaaa;
     817           15 :         aaaa_rec.rclass = 0;
     818           15 :         aaaa_rec.ttl = 0;
     819              : 
     820           15 :         service.record_aaaa = aaaa_rec;
     821              : 
     822              :         // Add two test TXT records for our service instance name, will be coalesced into
     823              :         // one record with both key-value pair strings by the library
     824              :         mdns_record_t::mdns_record_data rd_path;
     825           15 :         rd_path.txt = {{MDNS_STRING_CONST("path")},{path, std::strlen(path)}};
     826              : 
     827              :         mdns_record_t path_rec;
     828           15 :         path_rec.name = service.service_instance;
     829           15 :         path_rec.type = MDNS_RECORDTYPE_TXT;
     830           15 :         path_rec.data = rd_path;
     831           15 :         path_rec.rclass = 0;
     832           15 :         path_rec.ttl = 0;
     833              : 
     834           15 :         service.txt_record[0] = path_rec;
     835              :         
     836              :         mdns_record_t::mdns_record_data rd_tls;
     837           15 :         rd_tls.txt = {{MDNS_STRING_CONST("tls")},{MDNS_STRING_CONST(tls ? "1" : "0")}};
     838              :         
     839              :         mdns_record_t tls_rec;
     840           15 :         tls_rec.name = service.service_instance;
     841           15 :         tls_rec.type = MDNS_RECORDTYPE_TXT;
     842           15 :         tls_rec.data = rd_tls;
     843           15 :         tls_rec.rclass = 0;
     844           15 :         tls_rec.ttl = 0;
     845              : 
     846           15 :         service.txt_record[1] = tls_rec;
     847              : 
     848              :         // Send an announcement on startup of service
     849              :         {
     850           15 :             mdns_log("Sending announce");
     851           15 :             mdns_record_t additional[5] = {0};
     852           15 :             size_t additional_count = 0;
     853           15 :             additional[additional_count++] = service.record_srv;
     854           15 :             if (service.address_ipv4.sin_family == AF_INET)
     855           15 :                 additional[additional_count++] = service.record_a;
     856           15 :             if (service.address_ipv6.sin6_family == AF_INET6)
     857            0 :                 additional[additional_count++] = service.record_aaaa;
     858           15 :             additional[additional_count++] = service.txt_record[0];
     859           15 :             if(is_tls_server)
     860            0 :                 additional[additional_count++] = service.txt_record[1];
     861              : 
     862           45 :             for (int isock = 0; isock < num_sockets; ++isock)
     863           30 :                 mdns_announce_multicast(sockets[isock], buffer, capacity, service.record_ptr, 0, 0,
     864              :                                         additional, additional_count);
     865              :         }
     866              : 
     867              :         // This is a crude implementation that checks for incoming queries
     868           45 :         while (run_requested) {
     869           30 :             int nfds = 0;
     870              :             fd_set readfs;
     871          510 :             FD_ZERO(&readfs);
     872           90 :             for (int isock = 0; isock < num_sockets; ++isock) {
     873           60 :                 if (sockets[isock] >= nfds)
     874           60 :                     nfds = sockets[isock] + 1;
     875           60 :                 FD_SET(sockets[isock], &readfs);
     876              :             }
     877              : 
     878              :             struct timeval timeout;
     879           30 :             timeout.tv_sec = 0;
     880           30 :             timeout.tv_usec = 100000;
     881              : 
     882           30 :             if (select(nfds, &readfs, 0, 0, &timeout) >= 0) {
     883           90 :                 for (int isock = 0; isock < num_sockets; ++isock) {
     884           60 :                     if (FD_ISSET(sockets[isock], &readfs)) {
     885           24 :                         mdns_socket_listen(sockets[isock], buffer, capacity, service_callback,
     886              :                                         &service);
     887              :                     }
     888           60 :                     FD_SET(sockets[isock], &readfs);
     889              :                 }
     890              :             } else {
     891            0 :                 break;
     892              :             }
     893              :         }
     894              : 
     895              :         // Send a goodbye on end of service
     896              :         {
     897           15 :             mdns_log("Sending goodbye");
     898           15 :             mdns_record_t additional[5] = {0};
     899           15 :             size_t additional_count = 0;
     900           15 :             additional[additional_count++] = service.record_srv;
     901           15 :             if (service.address_ipv4.sin_family == AF_INET)
     902           15 :                 additional[additional_count++] = service.record_a;
     903           15 :             if (service.address_ipv6.sin6_family == AF_INET6)
     904            0 :                 additional[additional_count++] = service.record_aaaa;
     905           15 :             additional[additional_count++] = service.txt_record[0];
     906           15 :             if(is_tls_server)
     907            0 :                 additional[additional_count++] = service.txt_record[1];
     908              : 
     909           45 :             for (int isock = 0; isock < num_sockets; ++isock)
     910           30 :                 mdns_goodbye_multicast(sockets[isock], buffer, capacity, service.record_ptr, 0, 0,
     911              :                                         additional, additional_count);
     912              :         }
     913              : 
     914           15 :         free(buffer);
     915           15 :         free(service_name_buffer);
     916              : 
     917           45 :         for (int isock = 0; isock < num_sockets; ++isock)
     918           30 :             mdns_socket_close(sockets[isock]);
     919           15 :         mdns_log("Closed socket" + std::string(num_sockets ? "s" : ""));
     920              : 
     921           15 :         return 0;
     922              :     }
     923              : 
     924              : 
     925           15 :     void start_service(std::string hostname, std::string service, int port, std::string path, bool tls)
     926              :     {
     927           15 :         is_tls_server = tls;
     928           15 :         run_requested = true;
     929           15 :         service_mdns(hostname.c_str(), service.c_str(), port, path.c_str(), tls);
     930           15 :         running = false;
     931           15 :     }
     932              : 
     933           15 :     void stop_service()
     934              :     {
     935           15 :         run_requested = false;
     936           15 :     }
     937              : 
     938          884 :     bool is_running()
     939              :     {
     940          884 :         return running;
     941              :     }
     942              : };
     943              : 
     944              : } // bw::webthing
        

Generated by: LCOV version 2.0-1