mirror of
https://github.com/iDescriptor/iDescriptor.git
synced 2026-06-21 19:35:49 +08:00
WIP: implement dnssd
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#ifndef AVAHI_SERVICE_H
|
||||
#define AVAHI_SERVICE_H
|
||||
|
||||
#include "../../../iDescriptor.h"
|
||||
#include <QList>
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
@@ -14,19 +15,6 @@
|
||||
#include <avahi-client/lookup.h>
|
||||
#include <avahi-common/simple-watch.h>
|
||||
|
||||
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<std::string, std::string> txt; // TXT records
|
||||
|
||||
bool operator==(const NetworkDevice &other) const
|
||||
{
|
||||
return name == other.name && address == other.address;
|
||||
}
|
||||
};
|
||||
|
||||
class AvahiService : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -0,0 +1,275 @@
|
||||
#include "dnssd_service.h"
|
||||
#include <QDebug>
|
||||
#include <QMutexLocker>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/select.h>
|
||||
#include <unistd.h>
|
||||
#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<NetworkDevice> 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<DnssdService *>(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<DnssdService *>(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<const char *>(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<DnssdService *>(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<const struct sockaddr_in *>(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);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
#ifndef DNSSD_SERVICE_H
|
||||
#define DNSSD_SERVICE_H
|
||||
|
||||
#include "../../../iDescriptor.h"
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QSocketNotifier>
|
||||
#include <QString>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <dns_sd.h>
|
||||
|
||||
class DnssdService : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DnssdService(QObject *parent = nullptr);
|
||||
~DnssdService();
|
||||
|
||||
void startBrowsing();
|
||||
void stopBrowsing();
|
||||
QList<NetworkDevice> 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<NetworkDevice> m_networkDevices;
|
||||
bool m_running;
|
||||
|
||||
// Temporary storage for devices being resolved
|
||||
struct PendingDevice {
|
||||
QString name;
|
||||
QString hostname;
|
||||
uint16_t port;
|
||||
uint32_t interfaceIndex;
|
||||
QMap<QString, QString> txt;
|
||||
};
|
||||
QMap<QString, PendingDevice> m_pendingDevices;
|
||||
};
|
||||
|
||||
#endif // DNSSD_SERVICE_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<std::string, std::string> txt; // TXT records
|
||||
|
||||
bool operator==(const NetworkDevice &other) const
|
||||
{
|
||||
return name == other.name && address == other.address;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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 <QButtonGroup>
|
||||
@@ -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<NetworkDevice> wirelessDevices = m_avahiService->getNetworkDevices();
|
||||
QList<NetworkDevice> wirelessDevices =
|
||||
m_wirelessProvider->getNetworkDevices();
|
||||
for (const NetworkDevice &device : wirelessDevices) {
|
||||
addWirelessDevice(device);
|
||||
}
|
||||
|
||||
+13
-4
@@ -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 <QAbstractButton>
|
||||
#include <QButtonGroup>
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user