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