diff --git a/core/backends/lan/lanlinkprovider.cpp b/core/backends/lan/lanlinkprovider.cpp index 3d72c6b49..9c044b679 100644 --- a/core/backends/lan/lanlinkprovider.cpp +++ b/core/backends/lan/lanlinkprovider.cpp @@ -75,6 +75,7 @@ void LanLinkProvider::onNetworkConfigurationChanged(const QNetworkConfiguration if (m_lastConfig != config && config.state() == QNetworkConfiguration::Active) { m_lastConfig = config; onNetworkChange(); + m_mdnsDiscovery.onNetworkChange(); } } diff --git a/core/backends/lan/mdns_wrapper.cpp b/core/backends/lan/mdns_wrapper.cpp index 06859a65b..7acc0179c 100644 --- a/core/backends/lan/mdns_wrapper.cpp +++ b/core/backends/lan/mdns_wrapper.cpp @@ -165,6 +165,7 @@ int Discoverer::listenForQueryResponses() static char buffer[2048]; size_t num_records = mdns_query_recv(socket, buffer, sizeof(buffer), query_callback, (void *)&discoveredService, 0); + Q_UNUSED(num_records); // qCDebug(KDECONNECT_CORE) << "Discovered service" << discoveredService.name << "at" << discoveredService.address << "in" << num_records << // "records via socket" << socket; @@ -207,8 +208,41 @@ static mdns_string_t createMdnsString(const QByteArray &str) return mdns_string_t{str.constData(), (size_t)str.length()}; } +static QHostAddress findBestAddressMatch(QVector hostAddresses, const struct sockaddr *fromAddress) +{ + if (hostAddresses.size() == 1 || fromAddress == nullptr) { + return hostAddresses[0]; + } + // FIXME + return hostAddresses[0]; +} + +static sockaddr_in qHostAddresstoSockaddr(QHostAddress hostAddress) +{ + Q_ASSERT(hostAddress.protocol() == QAbstractSocket::IPv4Protocol); + sockaddr_in socketAddress; + memset(&socketAddress, 0, sizeof(socketAddress)); + socketAddress.sin_family = AF_INET; + socketAddress.sin_addr.s_addr = htonl(hostAddress.toIPv4Address()); + return socketAddress; +} + +static sockaddr_in6 qHostAddresstoSockaddr6(QHostAddress hostAddress) +{ + Q_ASSERT(hostAddress.protocol() == QAbstractSocket::IPv6Protocol); + sockaddr_in6 socketAddress; + memset(&socketAddress, 0, sizeof(socketAddress)); + socketAddress.sin6_family = AF_INET6; + Q_IPV6ADDR ipv6Address = hostAddress.toIPv6Address(); + for (int i = 0; i < 16; ++i) { + socketAddress.sin6_addr.s6_addr[i] = ipv6Address[i]; + } + return socketAddress; +} + static mdns_record_t createMdnsRecord(const Announcer::AnnouncedInfo &self, mdns_record_type_t record_type, + const struct sockaddr *fromAddress = nullptr, // used to determine IP to set in A and AAAA records QHash::const_iterator txtIterator = {}) { mdns_record_t answer; @@ -228,11 +262,11 @@ static mdns_record_t createMdnsRecord(const Announcer::AnnouncedInfo &self, break; case MDNS_RECORDTYPE_A: // maps ".local." to IPv4 answer.name = createMdnsString(self.hostname); - answer.data.a.addr = self.address_ipv4; + answer.data.a.addr = qHostAddresstoSockaddr(findBestAddressMatch(self.addressesV4, fromAddress)); break; case MDNS_RECORDTYPE_AAAA: // maps ".local." to IPv6 answer.name = createMdnsString(self.hostname); - answer.data.aaaa.addr = self.address_ipv6; + answer.data.aaaa.addr = qHostAddresstoSockaddr6(findBestAddressMatch(self.addressesV6, fromAddress)); break; case MDNS_RECORDTYPE_TXT: answer.name = createMdnsString(self.serviceInstance); @@ -241,7 +275,7 @@ static mdns_record_t createMdnsRecord(const Announcer::AnnouncedInfo &self, answer.data.txt.value = createMdnsString(txtIterator.value()); break; default: - assert(false); + Q_ASSERT(false); } return answer; } @@ -298,15 +332,15 @@ static int service_callback(int sock, const struct sockaddr* from, size_t addrle QVector additional; additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_SRV)); - if (self.address_ipv4.sin_family == AF_INET) { - additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_A)); + if (!self.addressesV4.empty()) { + additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_A, from)); } - if (self.address_ipv4.sin_family == AF_INET6) { - additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_AAAA)); + if (!self.addressesV6.empty()) { + additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_AAAA, from)); } for (auto txtIterator = self.txtRecords.cbegin(); txtIterator != self.txtRecords.cend(); txtIterator++) { - additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_TXT, txtIterator)); + additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_TXT, nullptr, txtIterator)); } // Send the answer, unicast or multicast depending on flag in query @@ -332,15 +366,15 @@ static int service_callback(int sock, const struct sockaddr* from, size_t addrle mdns_record_t answer = createMdnsRecord(self, MDNS_RECORDTYPE_SRV); QVector additional; - if (self.address_ipv4.sin_family == AF_INET) { - additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_A)); + if (!self.addressesV4.empty()) { + additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_A, from)); } - if (self.address_ipv4.sin_family == AF_INET6) { - additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_AAAA)); + if (!self.addressesV6.empty()) { + additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_AAAA, from)); } for (auto txtIterator = self.txtRecords.cbegin(); txtIterator != self.txtRecords.cend(); txtIterator++) { - additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_TXT, txtIterator)); + additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_TXT, nullptr, txtIterator)); } // Send the answer, unicast or multicast depending on flag in query @@ -358,19 +392,19 @@ static int service_callback(int sock, const struct sockaddr* from, size_t addrle } } else if (name == self.hostname) { qWarning() << "Someone queried my host for" << recordTypeToStr(record_type); - if (((record_type == MDNS_RECORDTYPE_A) || (record_type == MDNS_RECORDTYPE_ANY)) && self.address_ipv4.sin_family == AF_INET) { + if (((record_type == MDNS_RECORDTYPE_A) || (record_type == MDNS_RECORDTYPE_ANY)) && !self.addressesV4.empty()) { // The A query was for our qualified hostname and we have an IPv4 address, answer with an A // record mapping the hostname to an IPv4 address, as well as an AAAA record and TXT records - mdns_record_t answer = createMdnsRecord(self, MDNS_RECORDTYPE_A); + mdns_record_t answer = createMdnsRecord(self, MDNS_RECORDTYPE_A, from); QVector additional; - if (self.address_ipv4.sin_family == AF_INET6) { - additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_AAAA)); + if (!self.addressesV6.empty()) { + additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_AAAA, from)); } for (auto txtIterator = self.txtRecords.cbegin(); txtIterator != self.txtRecords.cend(); txtIterator++) { - additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_TXT, txtIterator)); + additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_TXT, nullptr, txtIterator)); } // Send the answer, unicast or multicast depending on flag in query @@ -385,19 +419,19 @@ static int service_callback(int sock, const struct sockaddr* from, size_t addrle mdns_query_answer_multicast(sock, sendbuffer, sizeof(sendbuffer), answer, 0, 0, additional.constData(), additional.length()); } - } else if (((record_type == MDNS_RECORDTYPE_AAAA) || (record_type == MDNS_RECORDTYPE_ANY)) && self.address_ipv6.sin6_family == AF_INET6) { + } else if (((record_type == MDNS_RECORDTYPE_AAAA) || (record_type == MDNS_RECORDTYPE_ANY)) && !self.addressesV6.empty()) { // The AAAA query was for our qualified hostname and we have an IPv6 address, answer with an AAAA // record mapping the hostname to an IPv4 address, as well as an A record and TXT records - mdns_record_t answer = createMdnsRecord(self, MDNS_RECORDTYPE_AAAA); + mdns_record_t answer = createMdnsRecord(self, MDNS_RECORDTYPE_AAAA, from); QVector additional; - if (self.address_ipv4.sin_family == AF_INET) { - additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_A)); + if (!self.addressesV4.empty()) { + additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_A, from)); } for (auto txtIterator = self.txtRecords.cbegin(); txtIterator != self.txtRecords.cend(); txtIterator++) { - additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_TXT, txtIterator)); + additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_TXT, nullptr, txtIterator)); } // Send the answer, unicast or multicast depending on flag in query @@ -479,11 +513,33 @@ Announcer::Announcer(const QString &serviceName, const QString &serviceType, uin // mdns.h needs all the qualified names to end with dot for some reason self.serviceType.append('.'); } - self.port = port; - self.hostname = QHostInfo::localHostName().toLatin1() + QByteArray(".local."); self.serviceInstance = serviceName.toLatin1() + '.' + self.serviceType; - memset(&self.address_ipv4, 0, sizeof(struct sockaddr_in)); - memset(&self.address_ipv6, 0, sizeof(struct sockaddr_in6)); + self.hostname = QHostInfo::localHostName().toLatin1() + ".local."; + self.port = port; + detectHostAddresses(); +} + +void Announcer::detectHostAddresses() +{ + qWarning() << "detectHostAddresses"; + self.addressesV4.clear(); + self.addressesV6.clear(); + for (const QNetworkInterface &iface : QNetworkInterface::allInterfaces()) { + int flags = iface.flags(); + if (!(flags & QNetworkInterface::IsUp) || !(flags & QNetworkInterface::CanMulticast) || (flags & QNetworkInterface::IsLoopBack)) { + continue; + } + for (const QNetworkAddressEntry &ifaceAddress : iface.addressEntries()) { + QHostAddress sourceAddress = ifaceAddress.ip(); + if (sourceAddress.protocol() == QAbstractSocket::IPv4Protocol && sourceAddress != QHostAddress::LocalHost) { + qWarning() << "Found ipv4" << sourceAddress; + self.addressesV4.append(sourceAddress); + } else if (sourceAddress.protocol() == QAbstractSocket::IPv6Protocol && sourceAddress != QHostAddress::LocalHostIPv6) { + qWarning() << "Found ipv6" << sourceAddress; + self.addressesV6.append(sourceAddress); + } + } + } } void Announcer::startAnnouncing() @@ -520,15 +576,15 @@ void Announcer::sendMulticastAnnounce(bool isGoodbye) QVector additional; additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_SRV)); - if (self.address_ipv4.sin_family == AF_INET) { + if (!self.addressesV4.empty()) { additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_A)); } - if (self.address_ipv4.sin_family == AF_INET6) { + if (!self.addressesV6.empty()) { additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_AAAA)); } for (auto txtIterator = self.txtRecords.cbegin(); txtIterator != self.txtRecords.cend(); txtIterator++) { - additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_TXT, txtIterator)); + additional.append(createMdnsRecord(self, MDNS_RECORDTYPE_TXT, nullptr, txtIterator)); } static char buffer[2048]; diff --git a/core/backends/lan/mdns_wrapper.h b/core/backends/lan/mdns_wrapper.h index ca35f0588..06b3478d5 100644 --- a/core/backends/lan/mdns_wrapper.h +++ b/core/backends/lan/mdns_wrapper.h @@ -13,13 +13,6 @@ #include #include -#ifdef _WIN32 -#include -#include -#else -#include -#endif - /* * A Qt wrapper for the mdns.h header-only library * from https://github.com/mjansson/mdns @@ -39,7 +32,7 @@ public: QMap txtRecords; }; - // serviceType should be of the form _kdeconnect._udp.local + // serviceType must be of the form "_._.local" void startDiscovering(const QString &serviceType); void stopDiscovering(); @@ -64,8 +57,8 @@ public: QByteArray serviceType; // ie: "<_service-type>._tcp.local." QByteArray serviceInstance; // ie: ".<_service-type>._tcp.local." QByteArray hostname; // ie: ".local." - struct sockaddr_in address_ipv4; - struct sockaddr_in6 address_ipv6; + QVector addressesV4; + QVector addressesV6; uint16_t port; QHash txtRecords; }; @@ -83,10 +76,19 @@ public: void sendMulticastAnnounce(bool isGoodbye); +public Q_SLOTS: + // notify that network interfaces changed since the creation of this Announcer + void onNetworkChange() + { + detectHostAddresses(); + } + private: int listenForQueries(); void stopListeningForQueries(); + void detectHostAddresses(); + AnnouncedInfo self; QSocketNotifier *socketNotifier = nullptr; diff --git a/core/backends/lan/mdnsdiscovery.h b/core/backends/lan/mdnsdiscovery.h index c6ca9b134..3d6f8ab0f 100644 --- a/core/backends/lan/mdnsdiscovery.h +++ b/core/backends/lan/mdnsdiscovery.h @@ -29,6 +29,12 @@ public: void stopAnnouncing(); void startAnnouncing(); +public Q_SLOTS: + void onNetworkChange() + { + mdnsAnnouncer.onNetworkChange(); + } + private: LanLinkProvider *lanLinkProvider = nullptr; MdnsWrapper::Discoverer mdnsDiscoverer;