File indexing completed on 2024-05-12 04:58:52

0001 /**
0002  * SPDX-FileCopyrightText: 2023 Albert Vaca Cintora <albertvaka@gmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include "mdns_wrapper.h"
0008 
0009 #include "core_debug.h"
0010 
0011 #include "mdns.h"
0012 
0013 #include <errno.h>
0014 
0015 #include <QHostInfo>
0016 #include <QNetworkInterface>
0017 #include <QSocketNotifier>
0018 
0019 namespace MdnsWrapper
0020 {
0021 const char *recordTypeToStr(int rtype)
0022 {
0023     switch (rtype) {
0024     case MDNS_RECORDTYPE_PTR:
0025         return "PTR";
0026     case MDNS_RECORDTYPE_SRV:
0027         return "SRV";
0028     case MDNS_RECORDTYPE_TXT:
0029         return "TXT";
0030     case MDNS_RECORDTYPE_A:
0031         return "A";
0032     case MDNS_RECORDTYPE_AAAA:
0033         return "AAAA";
0034     case MDNS_RECORDTYPE_ANY:
0035         return "ANY";
0036     default:
0037         return "UNKNOWN";
0038     }
0039 }
0040 
0041 const char *entryTypeToStr(int entry)
0042 {
0043     switch (entry) {
0044     case MDNS_ENTRYTYPE_QUESTION:
0045         return "QUESTION";
0046     case MDNS_ENTRYTYPE_ANSWER:
0047         return "ANSWER";
0048     case MDNS_ENTRYTYPE_AUTHORITY:
0049         return "AUTHORITY";
0050     case MDNS_ENTRYTYPE_ADDITIONAL:
0051         return "ADDITIONAL";
0052     default:
0053         return "UNKNOWN";
0054     }
0055 }
0056 
0057 static sockaddr_in qHostAddressToSockaddr(QHostAddress hostAddress)
0058 {
0059     Q_ASSERT(hostAddress.protocol() == QAbstractSocket::IPv4Protocol);
0060     sockaddr_in socketAddress;
0061     memset(&socketAddress, 0, sizeof(struct sockaddr_in));
0062     socketAddress.sin_family = AF_INET;
0063     socketAddress.sin_addr.s_addr = htonl(hostAddress.toIPv4Address());
0064     return socketAddress;
0065 }
0066 
0067 static sockaddr_in6 qHostAddressToSockaddr6(QHostAddress hostAddress)
0068 {
0069     Q_ASSERT(hostAddress.protocol() == QAbstractSocket::IPv6Protocol);
0070     sockaddr_in6 socketAddress;
0071     memset(&socketAddress, 0, sizeof(struct sockaddr_in6));
0072     socketAddress.sin6_family = AF_INET6;
0073     Q_IPV6ADDR ipv6Address = hostAddress.toIPv6Address();
0074     for (int i = 0; i < 16; ++i) {
0075         socketAddress.sin6_addr.s6_addr[i] = ipv6Address[i];
0076     }
0077     return socketAddress;
0078 }
0079 
0080 // Callback that handles responses to a query
0081 static int query_callback(int sock,
0082                           const struct sockaddr *from,
0083                           size_t addrlen,
0084                           mdns_entry_type_t entry_type,
0085                           uint16_t query_id,
0086                           uint16_t record_type,
0087                           uint16_t rclass,
0088                           uint32_t ttl,
0089                           const void *data,
0090                           size_t size,
0091                           size_t name_offset,
0092                           size_t name_length,
0093                           size_t record_offset,
0094                           size_t record_length,
0095                           void *user_data)
0096 {
0097     Q_UNUSED(sock);
0098     Q_UNUSED(addrlen);
0099     Q_UNUSED(query_id);
0100     Q_UNUSED(entry_type);
0101     Q_UNUSED(rclass);
0102     Q_UNUSED(ttl);
0103     Q_UNUSED(name_offset);
0104     Q_UNUSED(name_length);
0105 
0106     // qCDebug(KDECONNECT_CORE) << "Received DNS record of type" << recordTypeToStr(record_type) << "from socket" << sock << "with type" <<
0107     // entryTypeToStr(entry_type);
0108 
0109     Discoverer::MdnsService *discoveredService = (Discoverer::MdnsService *)user_data;
0110 
0111     switch (record_type) {
0112     case MDNS_RECORDTYPE_PTR: {
0113         // We don't use mdns_record_parse_ptr() because we want to extract just the service name instead of the full
0114         // "<instance-name>._<service-type>._tcp.local." string
0115         mdns_string_pair_t instanceNamePos = mdns_get_next_substring(data, size, record_offset);
0116         discoveredService->name = QString::fromLatin1((char *)data + instanceNamePos.offset, instanceNamePos.length);
0117         // static char instanceNameBuffer[256];
0118         // mdns_string_t instanceName = mdns_record_parse_ptr(data, size, record_offset, record_length, instanceNameBuffer, sizeof(instanceNameBuffer));
0119         // discoveredService->name = QString::fromLatin1(instanceName.str, instanceName.length);
0120         if (discoveredService->address == QHostAddress::Null) {
0121             discoveredService->address = QHostAddress(from); // In case we don't receive a A record, use from as address
0122         }
0123     } break;
0124     case MDNS_RECORDTYPE_SRV: {
0125         static char nameBuffer[256];
0126         mdns_record_srv_t record = mdns_record_parse_srv(data, size, record_offset, record_length, nameBuffer, sizeof(nameBuffer));
0127         // We can use the IP to connect so we don't need to store the "<hostname>.local." address.
0128         // discoveredService->qualifiedHostname = QString::fromLatin1(record.name.str, record.name.length);
0129         discoveredService->port = record.port;
0130     } break;
0131     case MDNS_RECORDTYPE_A: {
0132         sockaddr_in addr;
0133         mdns_record_parse_a(data, size, record_offset, record_length, &addr);
0134         discoveredService->address = QHostAddress(ntohl(addr.sin_addr.s_addr));
0135     } break;
0136     case MDNS_RECORDTYPE_AAAA:
0137         // Ignore IPv6 for now
0138         // sockaddr_in6 addr6;
0139         // mdns_record_parse_aaaa(data, size, record_offset, record_length, &addr6);
0140         break;
0141     case MDNS_RECORDTYPE_TXT: {
0142         mdns_record_txt_t records[24];
0143         size_t parsed = mdns_record_parse_txt(data, size, record_offset, record_length, records, sizeof(records) / sizeof(mdns_record_txt_t));
0144         for (size_t itxt = 0; itxt < parsed; ++itxt) {
0145             QString key = QString::fromLatin1(records[itxt].key.str, records[itxt].key.length);
0146             QString value = QString::fromLatin1(records[itxt].value.str, records[itxt].value.length);
0147             discoveredService->txtRecords[key] = value;
0148         }
0149     } break;
0150     default:
0151         // Ignore unknown record types
0152         break;
0153     }
0154 
0155     return 0;
0156 }
0157 
0158 void Discoverer::startDiscovering(const QString &serviceType)
0159 {
0160     int num_sockets = listenForQueryResponses();
0161     if (num_sockets <= 0) {
0162         qCWarning(KDECONNECT_CORE) << "Failed to open any MDNS server sockets";
0163         return;
0164     }
0165     sendQuery(serviceType);
0166 }
0167 
0168 void Discoverer::stopDiscovering()
0169 {
0170     stopListeningForQueryResponses();
0171 }
0172 
0173 void Discoverer::stopListeningForQueryResponses()
0174 {
0175     qCDebug(KDECONNECT_CORE) << "Closing" << responseSocketNotifiers.size() << "sockets";
0176     for (QSocketNotifier *socketNotifier : qAsConst(responseSocketNotifiers)) {
0177         mdns_socket_close(socketNotifier->socket());
0178         delete socketNotifier;
0179     }
0180     responseSocketNotifiers.clear();
0181 }
0182 
0183 int Discoverer::listenForQueryResponses()
0184 {
0185     // Open a socket for each interface
0186     QVector<int> sockets;
0187     for (const QNetworkInterface &iface : QNetworkInterface::allInterfaces()) {
0188         int flags = iface.flags();
0189         if (!(flags & QNetworkInterface::IsUp) || !(flags & QNetworkInterface::CanMulticast) || (flags & QNetworkInterface::IsLoopBack)) {
0190             continue;
0191         }
0192         for (const QNetworkAddressEntry &ifaceAddress : iface.addressEntries()) {
0193             QHostAddress sourceAddress = ifaceAddress.ip();
0194             if (sourceAddress.protocol() == QAbstractSocket::IPv4Protocol && sourceAddress != QHostAddress::LocalHost) {
0195                 qCDebug(KDECONNECT_CORE) << "Opening socket for address" << sourceAddress;
0196                 struct sockaddr_in saddr = qHostAddressToSockaddr(sourceAddress);
0197                 int socket = mdns_socket_open_ipv4(&saddr);
0198                 if (socket >= 0) {
0199                     sockets.append(socket);
0200                 } else {
0201                     qCDebug(KDECONNECT_CORE) << "Couldn't open socket";
0202                 }
0203             } else if (sourceAddress.protocol() == QAbstractSocket::IPv6Protocol && sourceAddress != QHostAddress::LocalHostIPv6) {
0204                 qCDebug(KDECONNECT_CORE) << "Opening socket for address6" << sourceAddress;
0205                 struct sockaddr_in6 saddr = qHostAddressToSockaddr6(sourceAddress);
0206                 int socket = mdns_socket_open_ipv6(&saddr);
0207                 if (socket >= 0) {
0208                     sockets.append(socket);
0209                 } else {
0210                     qCDebug(KDECONNECT_CORE) << "Couldn't open socket";
0211                 }
0212             }
0213         }
0214     }
0215 
0216     // Start listening on all sockets
0217     for (int socket : std::as_const(sockets)) {
0218         QSocketNotifier *socketNotifier = new QSocketNotifier(socket, QSocketNotifier::Read);
0219         QObject::connect(socketNotifier, &QSocketNotifier::activated, [this](QSocketDescriptor socket) {
0220             MdnsService discoveredService;
0221 
0222             static char buffer[2048];
0223             size_t num_records = mdns_query_recv(socket, buffer, sizeof(buffer), query_callback, (void *)&discoveredService, 0);
0224             Q_UNUSED(num_records);
0225 
0226             // qCDebug(KDECONNECT_CORE) << "Discovered service" << discoveredService.name << "at" << discoveredService.address << "in" <<  num_records <<
0227             // "records via socket" << socket;
0228 
0229             Q_EMIT serviceFound(discoveredService);
0230         });
0231         responseSocketNotifiers.append(socketNotifier);
0232     }
0233 
0234     qCDebug(KDECONNECT_CORE) << "Opened" << sockets.size() << "sockets to listen for MDNS query responses";
0235 
0236     return sockets.size();
0237 }
0238 
0239 void Discoverer::sendQuery(const QString &serviceType)
0240 {
0241     qCDebug(KDECONNECT_CORE) << "Sending MDNS query for service" << serviceType;
0242 
0243     mdns_query_t query;
0244     QByteArray serviceTypeBytes = serviceType.toLatin1();
0245     query.name = serviceTypeBytes.constData();
0246     query.length = serviceTypeBytes.length();
0247     query.type = MDNS_RECORDTYPE_PTR;
0248 
0249     static char buffer[2048];
0250     for (QSocketNotifier *socketNotifier : qAsConst(responseSocketNotifiers)) {
0251         int socket = socketNotifier->socket();
0252         qCDebug(KDECONNECT_CORE) << "Sending mDNS query via socket" << socket;
0253         int ret = mdns_multiquery_send(socket, &query, 1, buffer, sizeof(buffer), 0);
0254         if (ret < 0) {
0255             qCWarning(KDECONNECT_CORE) << "Failed to send mDNS query:" << strerror(errno);
0256         }
0257     }
0258 }
0259 
0260 const QByteArray dnsSdName = QByteArray("_services._dns-sd._udp.local.");
0261 
0262 static mdns_string_t createMdnsString(const QByteArray &str)
0263 {
0264     return mdns_string_t{str.constData(), (size_t)str.length()};
0265 }
0266 
0267 int countCommonLeadingBits(quint32 int1, quint32 int2)
0268 {
0269     int count = 0;
0270     while (int1 != 0 && int2 != 0) {
0271         if ((int1 & 0x80000000) == (int2 & 0x80000000)) {
0272             count++;
0273             int1 <<= 1;
0274             int2 <<= 1;
0275         } else {
0276             break;
0277         }
0278     }
0279     return count;
0280 }
0281 
0282 static QHostAddress findBestAddressMatchV4(const QVector<QHostAddress> &hostAddresses, const struct sockaddr *fromAddress)
0283 {
0284     Q_ASSERT(!hostAddresses.empty());
0285     if (hostAddresses.size() == 1 || fromAddress == nullptr) {
0286         return hostAddresses[0];
0287     }
0288 
0289     QHostAddress otherIp = QHostAddress(fromAddress);
0290 
0291     if (otherIp.protocol() != QAbstractSocket::IPv4Protocol) {
0292         return hostAddresses[0];
0293     }
0294 
0295     // qDebug() << "I have more than one IP address:" << hostAddresses << "- Finding best match for source IP:" << otherIp;
0296 
0297     QHostAddress matchingIp = hostAddresses[0];
0298     int matchingBits = -1;
0299     quint32 rawOtherIp = otherIp.toIPv4Address();
0300     for (const QHostAddress &ip : hostAddresses) {
0301         Q_ASSERT(ip.protocol() == QAbstractSocket::IPv4Protocol);
0302         quint32 rawMyIp = ip.toIPv4Address();
0303         // Since we don't have the network mask, we just compare the prefixes of the IPs to find the longest match
0304         int matchingBitsCount = countCommonLeadingBits(rawMyIp, rawOtherIp);
0305         if (matchingBitsCount > matchingBits) {
0306             matchingIp = ip;
0307             matchingBits = matchingBitsCount;
0308         }
0309     }
0310 
0311     // qDebug() << "Found match:" << matchingIp;
0312 
0313     return matchingIp;
0314 }
0315 
0316 static QHostAddress findBestAddressMatchV6(const QVector<QHostAddress> &hostAddresses, const struct sockaddr *fromAddress)
0317 {
0318     Q_ASSERT(!hostAddresses.empty());
0319     // We could do the same logic for v6 that we do for V4, but we don't care that much about IPv6
0320     return hostAddresses[0];
0321     Q_UNUSED(fromAddress);
0322 }
0323 
0324 static mdns_record_t createMdnsRecord(const Announcer::AnnouncedInfo &self,
0325                                       mdns_record_type_t record_type,
0326                                       const struct sockaddr *fromAddress = nullptr, // used to determine IP to set in A and AAAA records
0327                                       QHash<QByteArray, QByteArray>::const_iterator txtIterator = {})
0328 {
0329     mdns_record_t answer;
0330     answer.type = record_type;
0331     answer.rclass = 0;
0332     answer.ttl = 0;
0333     switch (record_type) {
0334     case MDNS_RECORDTYPE_PTR: // maps "_<service-type>._tcp.local." to "<instance-name>._<service-type>._tcp.local."
0335         answer.name = createMdnsString(self.serviceType);
0336         answer.data.ptr.name = createMdnsString(self.serviceInstance);
0337         break;
0338     case MDNS_RECORDTYPE_SRV: // maps "<instance-name>._<service-type>._tcp.local." to "<hostname>.local." and port
0339         answer.name = createMdnsString(self.serviceInstance);
0340         answer.data.srv.name = createMdnsString(self.hostname);
0341         answer.data.srv.port = self.port;
0342         answer.data.srv.priority = 0;
0343         answer.data.srv.weight = 0;
0344         break;
0345     case MDNS_RECORDTYPE_A: // maps "<hostname>.local." to IPv4
0346         answer.name = createMdnsString(self.hostname);
0347         answer.data.a.addr = qHostAddressToSockaddr(findBestAddressMatchV4(self.addressesV4, fromAddress));
0348         break;
0349     case MDNS_RECORDTYPE_AAAA: // maps "<hostname>.local." to IPv6
0350         answer.name = createMdnsString(self.hostname);
0351         answer.data.aaaa.addr = qHostAddressToSockaddr6(findBestAddressMatchV6(self.addressesV6, fromAddress));
0352         break;
0353     case MDNS_RECORDTYPE_TXT:
0354         answer.name = createMdnsString(self.serviceInstance);
0355         answer.type = MDNS_RECORDTYPE_TXT;
0356         answer.data.txt.key = createMdnsString(txtIterator.key());
0357         answer.data.txt.value = createMdnsString(txtIterator.value());
0358         break;
0359     default:
0360         Q_ASSERT(false);
0361     }
0362     return answer;
0363 }
0364 
0365 // Callback handling questions incoming on service sockets
0366 static int service_callback(int sock,
0367                             const struct sockaddr *from,
0368                             size_t addrlen,
0369                             mdns_entry_type_t entry_type,
0370                             uint16_t query_id,
0371                             uint16_t record_type,
0372                             uint16_t rclass,
0373                             uint32_t ttl,
0374                             const void *data,
0375                             size_t size,
0376                             size_t name_offset,
0377                             size_t name_length,
0378                             size_t record_offset,
0379                             size_t record_length,
0380                             void *user_data)
0381 {
0382     Q_UNUSED(ttl);
0383     Q_UNUSED(name_length);
0384     Q_UNUSED(record_offset);
0385     Q_UNUSED(record_length);
0386     static char sendbuffer[2024];
0387 
0388     const Announcer::AnnouncedInfo &self = *((Announcer::AnnouncedInfo *)user_data);
0389 
0390     if (entry_type != MDNS_ENTRYTYPE_QUESTION) {
0391         return 0;
0392     }
0393 
0394     static char nameBuffer[256];
0395     mdns_string_t nameMdnsString = mdns_string_extract(data, size, &name_offset, nameBuffer, sizeof(nameBuffer));
0396     QByteArray name = QByteArray(nameMdnsString.str, nameMdnsString.length);
0397 
0398     int ret = 0;
0399 
0400     if (name == dnsSdName) {
0401         if ((record_type == MDNS_RECORDTYPE_PTR) || (record_type == MDNS_RECORDTYPE_ANY)) {
0402             // The PTR query was for the DNS-SD domain, send answer with a PTR record for the service name we advertise.
0403 
0404             mdns_record_t answer = createMdnsRecord(self, MDNS_RECORDTYPE_PTR);
0405 
0406             uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE);
0407             if (unicast) {
0408                 ret = mdns_query_answer_unicast(sock,
0409                                                 from,
0410                                                 addrlen,
0411                                                 sendbuffer,
0412                                                 sizeof(sendbuffer),
0413                                                 query_id,
0414                                                 (mdns_record_type_t)record_type,
0415                                                 nameMdnsString.str,
0416                                                 nameMdnsString.length,
0417                                                 answer,
0418                                                 nullptr,
0419                                                 0,
0420                                                 nullptr,
0421                                                 0);
0422             } else {
0423                 ret = mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, nullptr, 0, nullptr, 0);
0424             }
0425         }
0426     } else if (name == self.serviceType) {
0427         if ((record_type == MDNS_RECORDTYPE_PTR) || (record_type == MDNS_RECORDTYPE_ANY)) {
0428             // The PTR query was for our service, answer a PTR record reverse mapping the queried service name
0429             // to our service instance name and add additional records containing the SRV record mapping the
0430             // service instance name to our qualified hostname and port, as well as any IPv4/IPv6 and TXT records
0431 
0432             mdns_record_t answer = createMdnsRecord(self, MDNS_RECORDTYPE_PTR);
0433 
0434             QVector<mdns_record_t> additional;
0435             additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_SRV));
0436             if (!self.addressesV4.empty()) {
0437                 additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_A, from));
0438             }
0439             if (!self.addressesV6.empty()) {
0440                 additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_AAAA, from));
0441             }
0442 
0443             for (auto txtIterator = self.txtRecords.cbegin(); txtIterator != self.txtRecords.cend(); txtIterator++) {
0444                 additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_TXT, nullptr, txtIterator));
0445             }
0446 
0447             uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE);
0448             if (unicast) {
0449                 ret = mdns_query_answer_unicast(sock,
0450                                                 from,
0451                                                 addrlen,
0452                                                 sendbuffer,
0453                                                 sizeof(sendbuffer),
0454                                                 query_id,
0455                                                 (mdns_record_type_t)record_type,
0456                                                 nameMdnsString.str,
0457                                                 nameMdnsString.length,
0458                                                 answer,
0459                                                 nullptr,
0460                                                 0,
0461                                                 additional.constData(),
0462                                                 additional.length());
0463             } else {
0464                 ret = mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, nullptr, 0, additional.constData(), additional.length());
0465             }
0466         }
0467     } else if (name == self.serviceInstance) {
0468         if ((record_type == MDNS_RECORDTYPE_SRV) || (record_type == MDNS_RECORDTYPE_ANY)) {
0469             // The SRV query was for our service instance, answer a SRV record mapping the service
0470             // instance name to our qualified hostname (typically "<hostname>.local.") and port, as
0471             // well as any IPv4/IPv6 address for the hostname as A/AAAA records and TXT records
0472 
0473             mdns_record_t answer = createMdnsRecord(self, MDNS_RECORDTYPE_SRV);
0474 
0475             QVector<mdns_record_t> additional;
0476             if (!self.addressesV4.empty()) {
0477                 additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_A, from));
0478             }
0479             if (!self.addressesV6.empty()) {
0480                 additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_AAAA, from));
0481             }
0482 
0483             for (auto txtIterator = self.txtRecords.cbegin(); txtIterator != self.txtRecords.cend(); txtIterator++) {
0484                 additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_TXT, nullptr, txtIterator));
0485             }
0486 
0487             uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE);
0488             if (unicast) {
0489                 ret = mdns_query_answer_unicast(sock,
0490                                                 from,
0491                                                 addrlen,
0492                                                 sendbuffer,
0493                                                 sizeof(sendbuffer),
0494                                                 query_id,
0495                                                 (mdns_record_type_t)record_type,
0496                                                 nameMdnsString.str,
0497                                                 nameMdnsString.length,
0498                                                 answer,
0499                                                 nullptr,
0500                                                 0,
0501                                                 additional.constData(),
0502                                                 additional.length());
0503             } else {
0504                 ret = mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, nullptr, 0, additional.constData(), additional.length());
0505             }
0506         }
0507     } else if (name == self.hostname) {
0508         if (((record_type == MDNS_RECORDTYPE_A) || (record_type == MDNS_RECORDTYPE_ANY)) && !self.addressesV4.empty()) {
0509             // The A query was for our qualified hostname and we have an IPv4 address, answer with an A
0510             // record mapping the hostname to an IPv4 address, as well as an AAAA record and TXT records
0511 
0512             mdns_record_t answer = createMdnsRecord(self, MDNS_RECORDTYPE_A, from);
0513 
0514             QVector<mdns_record_t> additional;
0515             if (!self.addressesV6.empty()) {
0516                 additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_AAAA, from));
0517             }
0518 
0519             for (auto txtIterator = self.txtRecords.cbegin(); txtIterator != self.txtRecords.cend(); txtIterator++) {
0520                 additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_TXT, nullptr, txtIterator));
0521             }
0522 
0523             uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE);
0524             if (unicast) {
0525                 ret = mdns_query_answer_unicast(sock,
0526                                                 from,
0527                                                 addrlen,
0528                                                 sendbuffer,
0529                                                 sizeof(sendbuffer),
0530                                                 query_id,
0531                                                 (mdns_record_type_t)record_type,
0532                                                 nameMdnsString.str,
0533                                                 nameMdnsString.length,
0534                                                 answer,
0535                                                 nullptr,
0536                                                 0,
0537                                                 additional.constData(),
0538                                                 additional.length());
0539             } else {
0540                 ret = mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, nullptr, 0, additional.constData(), additional.length());
0541             }
0542         } else if (((record_type == MDNS_RECORDTYPE_AAAA) || (record_type == MDNS_RECORDTYPE_ANY)) && !self.addressesV6.empty()) {
0543             // The AAAA query was for our qualified hostname and we have an IPv6 address, answer with an AAAA
0544             // record mapping the hostname to an IPv4 address, as well as an A record and TXT records
0545 
0546             mdns_record_t answer = createMdnsRecord(self, MDNS_RECORDTYPE_AAAA, from);
0547 
0548             QVector<mdns_record_t> additional;
0549             if (!self.addressesV4.empty()) {
0550                 additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_A, from));
0551             }
0552 
0553             for (auto txtIterator = self.txtRecords.cbegin(); txtIterator != self.txtRecords.cend(); txtIterator++) {
0554                 additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_TXT, nullptr, txtIterator));
0555             }
0556 
0557             uint16_t unicast = (rclass & MDNS_UNICAST_RESPONSE);
0558             if (unicast) {
0559                 ret = mdns_query_answer_unicast(sock,
0560                                                 from,
0561                                                 addrlen,
0562                                                 sendbuffer,
0563                                                 sizeof(sendbuffer),
0564                                                 query_id,
0565                                                 (mdns_record_type_t)record_type,
0566                                                 nameMdnsString.str,
0567                                                 nameMdnsString.length,
0568                                                 answer,
0569                                                 nullptr,
0570                                                 0,
0571                                                 additional.constData(),
0572                                                 additional.length());
0573             } else {
0574                 ret = mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, nullptr, 0, additional.constData(), additional.length());
0575             }
0576         }
0577     } // else request is not for me
0578     if (ret < 0) {
0579         qCWarning(KDECONNECT_CORE) << "Error sending MDNS query response";
0580     }
0581     return ret;
0582 }
0583 
0584 // Open sockets to listen to incoming mDNS queries on port 5353
0585 // When recieving, each socket can recieve data from all network interfaces
0586 // Thus we only need to open one socket for each address family
0587 int Announcer::listenForQueries()
0588 {
0589     auto callback = [this](QSocketDescriptor socket) {
0590         static char buffer[2048];
0591         mdns_socket_listen(socket, buffer, sizeof(buffer), service_callback, &self);
0592     };
0593 
0594     int numSockets = 0;
0595 
0596     {
0597         struct sockaddr_in sock_addr;
0598         memset(&sock_addr, 0, sizeof(struct sockaddr_in));
0599         sock_addr.sin_family = AF_INET;
0600 #ifdef _WIN32
0601         sock_addr.sin_addr = in4addr_any;
0602 #else
0603         sock_addr.sin_addr.s_addr = INADDR_ANY;
0604 #endif
0605         sock_addr.sin_port = htons(MDNS_PORT);
0606 #ifdef __APPLE__
0607         sock_addr.sin_len = sizeof(struct sockaddr_in);
0608 #endif
0609         int socket = mdns_socket_open_ipv4(&sock_addr);
0610         if (socket >= 0) {
0611             socketNotifier = new QSocketNotifier(socket, QSocketNotifier::Read);
0612             QObject::connect(socketNotifier, &QSocketNotifier::activated, callback);
0613             numSockets++;
0614         }
0615     }
0616 
0617     {
0618         struct sockaddr_in6 sock_addr;
0619         memset(&sock_addr, 0, sizeof(struct sockaddr_in6));
0620         sock_addr.sin6_family = AF_INET6;
0621         sock_addr.sin6_addr = in6addr_any;
0622         sock_addr.sin6_port = htons(MDNS_PORT);
0623 #ifdef __APPLE__
0624         sock_addr.sin6_len = sizeof(struct sockaddr_in6);
0625 #endif
0626         int socket = mdns_socket_open_ipv6(&sock_addr);
0627         if (socket >= 0) {
0628             socketNotifierV6 = new QSocketNotifier(socket, QSocketNotifier::Read);
0629             QObject::connect(socketNotifierV6, &QSocketNotifier::activated, callback);
0630             numSockets++;
0631         }
0632     }
0633 
0634     return numSockets;
0635 }
0636 
0637 Announcer::Announcer(const QString &instanceName, const QString &serviceType, uint16_t port)
0638 {
0639     self.serviceType = serviceType.toLatin1();
0640     if (!self.serviceType.endsWith('.')) {
0641         // mdns.h needs all the qualified names to end with dot for some reason
0642         self.serviceType.append('.');
0643     }
0644     self.serviceInstance = instanceName.toLatin1() + '.' + self.serviceType;
0645     self.hostname = QHostInfo::localHostName().toLatin1() + ".local.";
0646     self.port = port;
0647     detectHostAddresses();
0648 }
0649 
0650 void Announcer::detectHostAddresses()
0651 {
0652     self.addressesV4.clear();
0653     self.addressesV6.clear();
0654     for (const QNetworkInterface &iface : QNetworkInterface::allInterfaces()) {
0655         int flags = iface.flags();
0656         if (!(flags & QNetworkInterface::IsUp) || !(flags & QNetworkInterface::CanMulticast) || (flags & QNetworkInterface::IsLoopBack)) {
0657             continue;
0658         }
0659         for (const QNetworkAddressEntry &ifaceAddress : iface.addressEntries()) {
0660             QHostAddress sourceAddress = ifaceAddress.ip();
0661             if (sourceAddress.protocol() == QAbstractSocket::IPv4Protocol && sourceAddress != QHostAddress::LocalHost) {
0662                 self.addressesV4.append(sourceAddress);
0663             } else if (sourceAddress.protocol() == QAbstractSocket::IPv6Protocol && sourceAddress != QHostAddress::LocalHostIPv6) {
0664                 self.addressesV6.append(sourceAddress);
0665             }
0666         }
0667     }
0668 }
0669 
0670 void Announcer::startAnnouncing()
0671 {
0672     int num_sockets = listenForQueries();
0673     if (num_sockets <= 0) {
0674         qCWarning(KDECONNECT_CORE) << "Failed to open any MDNS client sockets";
0675         return;
0676     }
0677     sendMulticastAnnounce(false);
0678 }
0679 
0680 void Announcer::stopAnnouncing()
0681 {
0682     sendMulticastAnnounce(true);
0683     stopListeningForQueries();
0684 }
0685 
0686 void Announcer::stopListeningForQueries()
0687 {
0688     delete socketNotifier;
0689     socketNotifier = nullptr;
0690     delete socketNotifierV6;
0691     socketNotifierV6 = nullptr;
0692 }
0693 
0694 void Announcer::sendMulticastAnnounce(bool isGoodbye)
0695 {
0696     mdns_record_t ptr_record = createMdnsRecord(self, MDNS_RECORDTYPE_PTR);
0697 
0698     QVector<mdns_record_t> additional;
0699     additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_SRV));
0700     if (!self.addressesV4.empty()) {
0701         additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_A));
0702     }
0703     if (!self.addressesV6.empty()) {
0704         additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_AAAA));
0705     }
0706 
0707     for (auto txtIterator = self.txtRecords.cbegin(); txtIterator != self.txtRecords.cend(); txtIterator++) {
0708         additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_TXT, nullptr, txtIterator));
0709     }
0710 
0711     static char buffer[2048];
0712     if (isGoodbye) {
0713         qCDebug(KDECONNECT_CORE) << "Sending goodbye";
0714         if (socketNotifier)
0715             mdns_goodbye_multicast(socketNotifier->socket(), buffer, sizeof(buffer), ptr_record, nullptr, 0, additional.constData(), additional.length());
0716         if (socketNotifierV6)
0717             mdns_goodbye_multicast(socketNotifierV6->socket(), buffer, sizeof(buffer), ptr_record, nullptr, 0, additional.constData(), additional.length());
0718     } else {
0719         qCDebug(KDECONNECT_CORE) << "Sending announce";
0720         if (socketNotifier)
0721             mdns_announce_multicast(socketNotifier->socket(), buffer, sizeof(buffer), ptr_record, nullptr, 0, additional.constData(), additional.length());
0722         if (socketNotifierV6)
0723             mdns_announce_multicast(socketNotifierV6->socket(), buffer, sizeof(buffer), ptr_record, nullptr, 0, additional.constData(), additional.length());
0724     }
0725 }
0726 
0727 } // namespace MdnsWrapper