From 4783e5a4f7ef68569db2a11a05f1687acaca0de1 Mon Sep 17 00:00:00 2001 From: uncor3 Date: Sat, 4 Oct 2025 04:10:27 -0700 Subject: [PATCH] WIP: implement dnssd --- .../services/{ => avahi}/avahi_service.cpp | 0 src/core/services/{ => avahi}/avahi_service.h | 14 +- src/core/services/dnssd/dnssd_service.cpp | 275 ++++++++++++++++++ src/core/services/dnssd/dnssd_service.h | 72 +++++ src/iDescriptor.h | 13 + src/jailbrokenwidget.cpp | 28 +- src/jailbrokenwidget.h | 17 +- 7 files changed, 395 insertions(+), 24 deletions(-) rename src/core/services/{ => avahi}/avahi_service.cpp (100%) rename src/core/services/{ => avahi}/avahi_service.h (81%) create mode 100644 src/core/services/dnssd/dnssd_service.cpp create mode 100644 src/core/services/dnssd/dnssd_service.h diff --git a/src/core/services/avahi_service.cpp b/src/core/services/avahi/avahi_service.cpp similarity index 100% rename from src/core/services/avahi_service.cpp rename to src/core/services/avahi/avahi_service.cpp diff --git a/src/core/services/avahi_service.h b/src/core/services/avahi/avahi_service.h similarity index 81% rename from src/core/services/avahi_service.h rename to src/core/services/avahi/avahi_service.h index c9090a7..fe07d1c 100644 --- a/src/core/services/avahi_service.h +++ b/src/core/services/avahi/avahi_service.h @@ -1,6 +1,7 @@ #ifndef AVAHI_SERVICE_H #define AVAHI_SERVICE_H +#include "../../../iDescriptor.h" #include #include #include @@ -14,19 +15,6 @@ #include #include -struct NetworkDevice { - QString name; // service name - QString hostname; // e.g., iPhone-2.local - QString address; // IPv4 or IPv6 address - uint16_t port = 22; // SSH port - std::map txt; // TXT records - - bool operator==(const NetworkDevice &other) const - { - return name == other.name && address == other.address; - } -}; - class AvahiService : public QObject { Q_OBJECT diff --git a/src/core/services/dnssd/dnssd_service.cpp b/src/core/services/dnssd/dnssd_service.cpp new file mode 100644 index 0000000..13e1436 --- /dev/null +++ b/src/core/services/dnssd/dnssd_service.cpp @@ -0,0 +1,275 @@ +#include "dnssd_service.h" +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#pragma comment(lib, "ws2_32.lib") +#else +#include +#include +#include +#endif + +DnssdService::DnssdService(QObject *parent) + : QObject(parent), m_browseRef(nullptr), m_socketNotifier(nullptr), + m_running(false) +{ +} + +DnssdService::~DnssdService() { stopBrowsing(); } + +void DnssdService::startBrowsing() +{ + if (m_running) + return; + + qDebug() << "Starting DNS-SD browsing for Apple devices"; + + DNSServiceErrorType err = + DNSServiceBrowse(&m_browseRef, 0, 0, "_apple-mobdev2._tcp", "local.", + browseCallback, this); + + if (err != kDNSServiceErr_NoError) { + qWarning() << "DNSServiceBrowse failed:" << err; + return; + } + + int fd = DNSServiceRefSockFD(m_browseRef); + m_socketNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); + connect(m_socketNotifier, &QSocketNotifier::activated, this, + &DnssdService::processDnssdEvents); + + m_running = true; +} + +void DnssdService::stopBrowsing() +{ + if (!m_running) + return; + + qDebug() << "Stopping DNS-SD browsing"; + m_running = false; + cleanupDnssd(); + + QMutexLocker locker(&m_devicesMutex); + m_networkDevices.clear(); + m_pendingDevices.clear(); +} + +QList DnssdService::getNetworkDevices() const +{ + QMutexLocker locker(&m_devicesMutex); + return m_networkDevices; +} + +void DnssdService::processDnssdEvents() +{ + if (m_browseRef && m_running) { + DNSServiceProcessResult(m_browseRef); + } +} + +void DnssdService::cleanupDnssd() +{ + if (m_socketNotifier) { + m_socketNotifier->deleteLater(); + m_socketNotifier = nullptr; + } + + if (m_browseRef) { + DNSServiceRefDeallocate(m_browseRef); + m_browseRef = nullptr; + } +} + +void DNSSD_API DnssdService::browseCallback( + DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, + const char *replyDomain, void *context) +{ + Q_UNUSED(sdRef) + Q_UNUSED(regtype) + Q_UNUSED(replyDomain) + + if (errorCode != kDNSServiceErr_NoError) + return; + + DnssdService *service = static_cast(context); + + if (flags & kDNSServiceFlagsAdd) { + qDebug() << "Found Apple device:" << serviceName; + + // Start resolving the service + DNSServiceRef resolveRef; + DNSServiceErrorType err = + DNSServiceResolve(&resolveRef, 0, interfaceIndex, serviceName, + regtype, replyDomain, resolveCallback, context); + + if (err == kDNSServiceErr_NoError) { + // Process the resolve synchronously for simplicity + int fd = DNSServiceRefSockFD(resolveRef); + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + struct timeval timeout = {5, 0}; // 5 second timeout + if (select(fd + 1, &readfds, nullptr, nullptr, &timeout) > 0) { + if (FD_ISSET(fd, &readfds)) { + DNSServiceProcessResult(resolveRef); + } + } + DNSServiceRefDeallocate(resolveRef); + } + } else { + qDebug() << "Apple device removed:" << serviceName; + emit service->deviceRemoved(QString::fromUtf8(serviceName)); + + // Remove from our list + QMutexLocker locker(&service->m_devicesMutex); + service->m_networkDevices.removeIf( + [serviceName](const NetworkDevice &dev) { + return dev.name == QString::fromUtf8(serviceName); + }); + service->m_pendingDevices.remove(QString::fromUtf8(serviceName)); + } +} + +void DNSSD_API DnssdService::resolveCallback( + DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, + uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, + void *context) +{ + Q_UNUSED(sdRef) + Q_UNUSED(flags) + + if (errorCode != kDNSServiceErr_NoError) + return; + + DnssdService *service = static_cast(context); + QString serviceName = QString::fromUtf8(fullname); + + // Store pending device info + PendingDevice pending; + pending.name = serviceName; + pending.hostname = QString::fromUtf8(hosttarget); + pending.port = ntohs(port); + pending.interfaceIndex = interfaceIndex; + + // Parse TXT records + if (txtLen > 0 && txtRecord) { + const unsigned char *ptr = txtRecord; + const unsigned char *end = txtRecord + txtLen; + + while (ptr < end) { + uint8_t len = *ptr++; + if (ptr + len > end) + break; + + QString record = + QString::fromUtf8(reinterpret_cast(ptr), len); + int equalPos = record.indexOf('='); + if (equalPos != -1) { + QString key = record.left(equalPos); + QString value = record.mid(equalPos + 1); + pending.txt[key] = value; + } + ptr += len; + } + } + + service->m_pendingDevices[serviceName] = pending; + + qDebug() << "Resolved Apple device:" << serviceName + << "host:" << pending.hostname << "port:" << pending.port; + + // Now resolve the IP address + DNSServiceRef addrRef; + DNSServiceErrorType err = DNSServiceGetAddrInfo( + &addrRef, 0, interfaceIndex, kDNSServiceProtocol_IPv4, hosttarget, + addrInfoCallback, context); + + if (err == kDNSServiceErr_NoError) { + // Process the address resolution synchronously + int fd = DNSServiceRefSockFD(addrRef); + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + struct timeval timeout = {5, 0}; // 5 second timeout + if (select(fd + 1, &readfds, nullptr, nullptr, &timeout) > 0) { + if (FD_ISSET(fd, &readfds)) { + DNSServiceProcessResult(addrRef); + } + } + DNSServiceRefDeallocate(addrRef); + } +} + +void DNSSD_API DnssdService::addrInfoCallback( + DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *hostname, + const struct sockaddr *address, uint32_t ttl, void *context) +{ + Q_UNUSED(sdRef) + Q_UNUSED(flags) + Q_UNUSED(interfaceIndex) + Q_UNUSED(ttl) + + if (errorCode != kDNSServiceErr_NoError) + return; + + DnssdService *service = static_cast(context); + QString hostnameStr = QString::fromUtf8(hostname); + + // Find the pending device with matching hostname + QString deviceName; + for (auto it = service->m_pendingDevices.begin(); + it != service->m_pendingDevices.end(); ++it) { + if (it.value().hostname == hostnameStr) { + deviceName = it.key(); + break; + } + } + + if (deviceName.isEmpty()) { + qWarning() << "Could not find pending device for hostname:" + << hostnameStr; + return; + } + + PendingDevice pending = service->m_pendingDevices[deviceName]; + + // Convert IP address + char ip[INET_ADDRSTRLEN]; + auto *addr_in = reinterpret_cast(address); + inet_ntop(AF_INET, &addr_in->sin_addr, ip, sizeof(ip)); + + NetworkDevice device; + device.name = pending.name; + device.hostname = pending.hostname; + device.address = QString::fromUtf8(ip); + device.port = pending.port > 0 ? pending.port : 22; // Default to SSH port + // device.txt = pending.txt; + + qDebug() << "Resolved IP for Apple device:" << device.name << "at" + << device.address << ":" << device.port; + + // Add to our list if not already present + { + QMutexLocker locker(&service->m_devicesMutex); + bool exists = std::any_of(service->m_networkDevices.begin(), + service->m_networkDevices.end(), + [&device](const NetworkDevice &existing) { + return existing == device; + }); + if (!exists) { + service->m_networkDevices.append(device); + emit service->deviceAdded(device); + } + } + + // Remove from pending + service->m_pendingDevices.remove(deviceName); +} \ No newline at end of file diff --git a/src/core/services/dnssd/dnssd_service.h b/src/core/services/dnssd/dnssd_service.h new file mode 100644 index 0000000..6f2b1a3 --- /dev/null +++ b/src/core/services/dnssd/dnssd_service.h @@ -0,0 +1,72 @@ +#ifndef DNSSD_SERVICE_H +#define DNSSD_SERVICE_H + +#include "../../../iDescriptor.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class DnssdService : public QObject +{ + Q_OBJECT + +public: + explicit DnssdService(QObject *parent = nullptr); + ~DnssdService(); + + void startBrowsing(); + void stopBrowsing(); + QList getNetworkDevices() const; + +signals: + void deviceAdded(const NetworkDevice &device); + void deviceRemoved(const QString &deviceName); + +private slots: + void processDnssdEvents(); + +private: + void cleanupDnssd(); + + static void DNSSD_API browseCallback( + DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *serviceName, + const char *regtype, const char *replyDomain, void *context); + + static void DNSSD_API resolveCallback( + DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *fullname, + const char *hosttarget, uint16_t port, uint16_t txtLen, + const unsigned char *txtRecord, void *context); + + static void DNSSD_API addrInfoCallback( + DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *hostname, + const struct sockaddr *address, uint32_t ttl, void *context); + + DNSServiceRef m_browseRef; + QSocketNotifier *m_socketNotifier; + + mutable QMutex m_devicesMutex; + QList m_networkDevices; + bool m_running; + + // Temporary storage for devices being resolved + struct PendingDevice { + QString name; + QString hostname; + uint16_t port; + uint32_t interfaceIndex; + QMap txt; + }; + QMap m_pendingDevices; +}; + +#endif // DNSSD_SERVICE_H \ No newline at end of file diff --git a/src/iDescriptor.h b/src/iDescriptor.h index b7e37c2..99ec71b 100644 --- a/src/iDescriptor.h +++ b/src/iDescriptor.h @@ -451,3 +451,16 @@ void fetchAppIconFromApple(const QString &bundleId, afc_error_t afc2_client_new(idevice_t device, afc_client_t *afc); void get_cable_info(idevice_t device, plist_t &response); + +struct NetworkDevice { + QString name; // service name + QString hostname; // e.g., iPhone-2.local + QString address; // IPv4 or IPv6 address + uint16_t port = 22; // SSH port + std::map txt; // TXT records + + bool operator==(const NetworkDevice &other) const + { + return name == other.name && address == other.address; + } +}; diff --git a/src/jailbrokenwidget.cpp b/src/jailbrokenwidget.cpp index 1f0c42e..ce2d571 100644 --- a/src/jailbrokenwidget.cpp +++ b/src/jailbrokenwidget.cpp @@ -1,6 +1,12 @@ #include "jailbrokenwidget.h" #include "appcontext.h" -#include "core/services/avahi_service.h" + +#ifdef __linux__ +#include "core/services/avahi/avahi_service.h" +#else +#include "core/services/dnssd/dnssd_service.h" +#endif + #include "iDescriptor-ui.h" #include "iDescriptor.h" #include @@ -48,12 +54,19 @@ JailbrokenWidget::JailbrokenWidget(QWidget *parent) : QWidget{parent} connect(AppContext::sharedInstance(), &AppContext::deviceRemoved, this, &JailbrokenWidget::onWiredDeviceRemoved); - // Initialize Avahi service - m_avahiService = new AvahiService(this); - connect(m_avahiService, &AvahiService::deviceAdded, this, +#ifdef __linux__ + m_wirelessProvider = new AvahiService(this); + connect(m_wirelessProvider, &AvahiService::deviceAdded, this, &JailbrokenWidget::onWirelessDeviceAdded); - connect(m_avahiService, &AvahiService::deviceRemoved, this, + connect(m_wirelessProvider, &AvahiService::deviceRemoved, this, &JailbrokenWidget::onWirelessDeviceRemoved); +#else + m_wirelessProvider = new DnssdService(this); + connect(m_wirelessProvider, &DnssdService::deviceAdded, this, + &JailbrokenWidget::onWirelessDeviceAdded); + connect(m_wirelessProvider, &DnssdService::deviceRemoved, this, + &JailbrokenWidget::onWirelessDeviceRemoved); +#endif // Right side: Device selection and Terminal QWidget *rightContainer = new QWidget(); @@ -81,7 +94,7 @@ JailbrokenWidget::JailbrokenWidget(QWidget *parent) : QWidget{parent} &JailbrokenWidget::checkSshData); // Start scanning for wireless devices - m_avahiService->startBrowsing(); + m_wirelessProvider->startBrowsing(); // Populate initial devices updateDeviceList(); @@ -164,7 +177,8 @@ void JailbrokenWidget::updateDeviceList() } // Add wireless devices - QList wirelessDevices = m_avahiService->getNetworkDevices(); + QList wirelessDevices = + m_wirelessProvider->getNetworkDevices(); for (const NetworkDevice &device : wirelessDevices) { addWirelessDevice(device); } diff --git a/src/jailbrokenwidget.h b/src/jailbrokenwidget.h index 90c9ad5..81cd12c 100644 --- a/src/jailbrokenwidget.h +++ b/src/jailbrokenwidget.h @@ -1,7 +1,12 @@ #ifndef JAILBROKENWIDGET_H #define JAILBROKENWIDGET_H -#include "core/services/avahi_service.h" +#ifdef __linux__ +#include "core/services/avahi/avahi_service.h" +#else +#include "core/services/dnssd/dnssd_service.h" +#endif + #include "iDescriptor.h" #include #include @@ -63,10 +68,14 @@ private: QVBoxLayout *m_wirelessDevicesLayout; QButtonGroup *m_deviceButtonGroup; - // Avahi service for network discovery - AvahiService *m_avahiService; +#ifdef Q_OS_LINUX + AvahiService *m_wirelessProvider = nullptr; +#endif + +#ifdef __APPLE__ + DnssdService *m_wirelessProvider = nullptr; +#endif - // Selected device tracking DeviceType m_selectedDeviceType = DeviceType::None; iDescriptorDevice *m_selectedWiredDevice = nullptr; NetworkDevice m_selectedNetworkDevice;