diff --git a/.gitignore b/.gitignore index e28a941f..2c3f3f8c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ builddir doc/html *api.json.new *.qm +*.install diff --git a/debian/control b/debian/control index 0b1105b9..bd261142 100644 --- a/debian/control +++ b/debian/control @@ -262,6 +262,7 @@ Package: guh-plugin-networkdetector Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, + nmap Description: guh.io plugin for networkdetector The guh daemon is a plugin based IoT (Internet of Things) server. The server works like a translator for devices, things and services and diff --git a/networkdetector/devicemonitor.cpp b/networkdetector/devicemonitor.cpp new file mode 100644 index 00000000..a7e82275 --- /dev/null +++ b/networkdetector/devicemonitor.cpp @@ -0,0 +1,103 @@ +#include "devicemonitor.h" + +#include "extern-plugininfo.h" + +DeviceMonitor::DeviceMonitor(const QString &macAddress, const QString &ipAddress, QObject *parent): + QObject(parent) +{ + m_host = new Host(); + m_host->setMacAddress(macAddress); + m_host->setAddress(ipAddress); + m_host->setReachable(false); + + m_arpLookupProcess = new QProcess(this); + connect(m_arpLookupProcess, SIGNAL(finished(int)), this, SLOT(arpLookupFinished(int))); + + m_pingProcess = new QProcess(this); + connect(m_pingProcess, SIGNAL(finished(int)), this, SLOT(pingFinished(int))); +} + +DeviceMonitor::~DeviceMonitor() +{ + delete m_host; +} + +void DeviceMonitor::update() +{ + lookupArpCache(); +} + +void DeviceMonitor::lookupArpCache() +{ + m_arpLookupProcess->start("ip", {"-s", "neighbor", "list"}); +} + +void DeviceMonitor::ping() +{ + m_pingProcess->start("ping", {"-c", "1", m_host->address()}); +} + +void DeviceMonitor::arpLookupFinished(int exitCode) +{ + if (exitCode != 0) { + qWarning(dcNetworkDetector()) << "Error looking up ARP cache."; + return; + } + + QString data = QString::fromLatin1(m_arpLookupProcess->readAll()); + bool found = false; + foreach (QString line, data.split('\n')) { + line.replace(QRegExp("[ ]{1,}"), " "); + QStringList parts = line.split(" "); + int lladdrIndex = parts.indexOf("lladdr"); + if (lladdrIndex >= 0 && parts.count() > lladdrIndex && parts.at(lladdrIndex+1).toLower() == m_host->macAddress().toLower()) { + found = true; + // Verify if IP address is still the same + if (parts.first() != m_host->address()) { + m_host->setAddress(parts.first()); + emit addressChanged(parts.first()); + } + if (parts.last() == "REACHABLE") { + qDebug(dcNetworkDetector()) << "Device" << m_host->macAddress() << "found in ARP cache and claims to be REACHABLE"; + m_host->seen(); + if (!m_host->reachable()) { + m_host->setReachable(true); + emit reachableChanged(true); + } + } else { + // ARP claims the device to be stale... try to ping it. + qCDebug(dcNetworkDetector()) << "Device" << m_host->macAddress() << "found in ARP cache but is marked as" << parts.last() << ". Trying to ping it..."; + ping(); + } + break; + } + } + if (!found) { + qCDebug(dcNetworkDetector()) << "Device" << m_host->macAddress() << "not found in ARP cache. Trying to ping it..."; + ping(); + } + + if (m_host->reachable() && m_host->lastSeenTime().addSecs(150) < QDateTime::currentDateTime()) { + qCDebug(dcNetworkDetector()) << "Could not reach device for > 150 secs. Marking it as gone." << m_host->address() << m_host->macAddress(); + m_host->setReachable(false); + emit reachableChanged(false); + } +} + +void DeviceMonitor::pingFinished(int exitCode) +{ + if (exitCode == 0) { + // we were able to ping the device + m_host->seen(); + if (!m_host->reachable()) { + m_host->setReachable(true); + emit reachableChanged(true); + } + } else { + qDebug(dcNetworkDetector()) << "Could not ping device" << m_host->macAddress() << m_host->address(); + } + // read data to discard it from socket + QString data = QString::fromLatin1(m_pingProcess->readAll()); + Q_UNUSED(data) +// qCDebug(dcNetworkDetector()) << "have ping data" << data; +} diff --git a/networkdetector/devicemonitor.h b/networkdetector/devicemonitor.h new file mode 100644 index 00000000..f1f1d9c4 --- /dev/null +++ b/networkdetector/devicemonitor.h @@ -0,0 +1,38 @@ +#ifndef DEVICEMONITOR_H +#define DEVICEMONITOR_H + +#include +#include + +#include "host.h" + +class DeviceMonitor : public QObject +{ + Q_OBJECT +public: + explicit DeviceMonitor(const QString &macAddress, const QString &ipAddress, QObject *parent = nullptr); + + ~DeviceMonitor(); + + void update(); + +signals: + void addressChanged(const QString &address); + void reachableChanged(bool reachable); + +private: + void lookupArpCache(); + void ping(); + +private slots: + void arpLookupFinished(int exitCode); + void pingFinished(int exitCode); + +private: + Host *m_host; + QProcess *m_arpLookupProcess; + QProcess *m_pingProcess; + +}; + +#endif // DEVICEMONITOR_H diff --git a/networkdetector/devicepluginnetworkdetector.cpp b/networkdetector/devicepluginnetworkdetector.cpp index 3ac16ecc..284d7c10 100644 --- a/networkdetector/devicepluginnetworkdetector.cpp +++ b/networkdetector/devicepluginnetworkdetector.cpp @@ -52,37 +52,28 @@ #include #include -#include -DevicePluginNetworkDetector::DevicePluginNetworkDetector(): - m_discoveryProcess(0), - m_scanProcess(0), - m_aboutToQuit(false) +DevicePluginNetworkDetector::DevicePluginNetworkDetector() { - + m_discovery = new Discovery(this); + connect(m_discovery, &Discovery::finished, this, &DevicePluginNetworkDetector::discoveryFinished); } DevicePluginNetworkDetector::~DevicePluginNetworkDetector() { - // Stop running processes - m_aboutToQuit = true; - - if (m_scanProcess && m_scanProcess->state() == QProcess::Running) { - qCDebug(dcNetworkDetector()) << "Kill running scan process"; - m_scanProcess->kill(); - m_scanProcess->waitForFinished(5000); - } - - if (m_discoveryProcess && m_discoveryProcess->state() == QProcess::Running) { - qCDebug(dcNetworkDetector()) << "Kill running discovery process"; - m_discoveryProcess->terminate(); - m_discoveryProcess->waitForFinished(5000); + if (m_discovery->isRunning()) { + m_discovery->abort(); } } DeviceManager::DeviceSetupStatus DevicePluginNetworkDetector::setupDevice(Device *device) { qCDebug(dcNetworkDetector()) << "Setup" << device->name() << device->params(); + DeviceMonitor *monitor = new DeviceMonitor(device->paramValue(macAddressParamTypeId).toString(), device->paramValue(addressParamTypeId).toString(), this); + connect(monitor, &DeviceMonitor::reachableChanged, this, &DevicePluginNetworkDetector::deviceReachableChanged); + connect(monitor, &DeviceMonitor::addressChanged, this, &DevicePluginNetworkDetector::deviceAddressChanged); + m_monitors.insert(monitor, device); + return DeviceManager::DeviceSetupStatusSuccess; } @@ -94,12 +85,13 @@ DeviceManager::DeviceError DevicePluginNetworkDetector::discoverDevices(const De if (deviceClassId != networkDeviceClassId) return DeviceManager::DeviceErrorDeviceClassNotFound; - if (m_discoveryProcess) { + if (m_discovery->isRunning()) { qCWarning(dcNetworkDetector()) << "Network discovery already running"; return DeviceManager::DeviceErrorDeviceInUse; } - m_discoveryProcess = startScanProcesses(); + m_discovery->discoverHosts(14); + return DeviceManager::DeviceErrorAsync; } @@ -108,161 +100,55 @@ DeviceManager::HardwareResources DevicePluginNetworkDetector::requiredHardware() return DeviceManager::HardwareResourceTimer; } +void DevicePluginNetworkDetector::deviceRemoved(Device *device) +{ + DeviceMonitor *monitor = m_monitors.key(device); + m_monitors.remove(monitor); + delete monitor; +} + void DevicePluginNetworkDetector::guhTimer() { - if (!myDevices().isEmpty() && !m_scanProcess) - m_scanProcess = startScanProcesses(); - -} - -QProcess * DevicePluginNetworkDetector::startScanProcesses() -{ - QStringList targets = getDefaultTargets(); - qCDebug(dcNetworkDetector()) << "Start network discovery" << targets; - QProcess *process = new QProcess(this); - connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus))); - - QStringList arguments; - arguments << "-R" << "-oX" << "-" << "-v" << "--stats-every" << "1" << "-sn"; - arguments << targets; - - process->start(QStringLiteral("nmap"), arguments); - return process; -} - - -QStringList DevicePluginNetworkDetector::getDefaultTargets() -{ - QStringList targets; - foreach (const QHostAddress &interface, QNetworkInterface::allAddresses()) { - if (!interface.isLoopback() && interface.scopeId().isEmpty()) { - QPair pair = QHostAddress::parseSubnet(interface.toString() + "/24"); - targets << QString("%1/%2").arg(pair.first.toString()).arg(pair.second); - } - } - return targets; -} - -QList DevicePluginNetworkDetector::parseProcessOutput(const QByteArray &processData) -{ - m_reader.clear(); - m_reader.addData(processData); - - QList hosts; - - while (!m_reader.atEnd() && !m_reader.hasError()) { - - QXmlStreamReader::TokenType token = m_reader.readNext(); - if(token == QXmlStreamReader::StartDocument) - continue; - - if(token == QXmlStreamReader::StartElement && m_reader.name() == "host") { - Host host = parseHost(); - if (host.isValid()) { - hosts.append(host); - } - } - } - return hosts; -} - -Host DevicePluginNetworkDetector::parseHost() -{ - if (!m_reader.isStartElement() || m_reader.name() != "host") - return Host(); - - QString address; QString hostName; QString status; - while(!(m_reader.tokenType() == QXmlStreamReader::EndElement && m_reader.name() == "host")){ - - m_reader.readNext(); - - if (m_reader.isStartElement() && m_reader.name() == "hostname") { - QString name = m_reader.attributes().value("name").toString(); - if (!name.isEmpty()) - hostName = name; - - m_reader.readNext(); - } - - if (m_reader.name() == "address") { - QString addr = m_reader.attributes().value("addr").toString(); - if (!addr.isEmpty()) - address = addr; - } - - if (m_reader.name() == "status") { - QString state = m_reader.attributes().value("state").toString(); - if (!state.isEmpty()) - status = state; - } - } - - return Host(hostName, address, (status == "up" ? true : false)); -} - -void DevicePluginNetworkDetector::processFinished(int exitCode, QProcess::ExitStatus exitStatus) -{ - QProcess *process = static_cast(sender()); - - // If the process was killed because guhd is shutting down...we dont't care any more about the result - if (m_aboutToQuit) - return; - - // Discovery - if (process == m_discoveryProcess) { - - qCDebug(dcNetworkDetector()) << "Discovery process finished"; - - process->deleteLater(); - m_discoveryProcess = 0; - - QList deviceDescriptors; - if (exitCode != 0 || exitStatus != QProcess::NormalExit) { - qCWarning(dcNetworkDetector) << "Network scan error:" << process->readAllStandardError(); - emit devicesDiscovered(networkDeviceClassId, deviceDescriptors); - return; - } - - QByteArray outputData = process->readAllStandardOutput(); - foreach (const Host &host, parseProcessOutput(outputData)) { - DeviceDescriptor descriptor(networkDeviceClassId, host.hostName(), host.adderss()); - descriptor.setParams( ParamList() << Param(hostnameParamTypeId, host.hostName())); - deviceDescriptors.append(descriptor); - } - - emit devicesDiscovered(networkDeviceClassId, deviceDescriptors); - - } else if (process == m_scanProcess) { - // Scan - qCDebug(dcNetworkDetector()) << "Network scan process finished"; - - process->deleteLater(); - m_scanProcess = 0; - - if (exitCode != 0 || exitStatus != QProcess::NormalExit) { - qCWarning(dcNetworkDetector) << "Network scan error:" << process->readAllStandardError(); - return; - } - - if (myDevices().isEmpty()) { - process->deleteLater(); - return; - } - - QStringList upHosts; - QByteArray outputData = process->readAllStandardOutput(); - foreach (const Host &host, parseProcessOutput(outputData)) { - if (host.isValid() && host.reachable()) - upHosts.append(host.hostName()); - - } - - foreach (Device *device, myDevices()) { - if (upHosts.contains(device->paramValue(hostnameParamTypeId).toString())) { - device->setStateValue(inRangeStateTypeId, true); - } else { - device->setStateValue(inRangeStateTypeId, false); - } - } + foreach (DeviceMonitor *monitor, m_monitors.keys()) { + monitor->update(); + } +} + +void DevicePluginNetworkDetector::discoveryFinished(const QList &hosts) +{ + qCDebug(dcNetworkDetector()) << "Discovery finished. Found" << hosts.count() << "devices"; + QList discoveredDevices; + foreach (const Host &host, hosts) { + DeviceDescriptor descriptor(networkDeviceClassId, (host.hostName().isEmpty() ? host.address() : host.hostName() + "(" + host.address() + ")"), host.macAddress()); + + ParamList paramList; + Param macAddress(macAddressParamTypeId, host.macAddress()); + Param address(addressParamTypeId, host.address()); + paramList.append(macAddress); + paramList.append(address); + descriptor.setParams(paramList); + + discoveredDevices.append(descriptor); + } + + emit devicesDiscovered(networkDeviceClassId, discoveredDevices); +} + +void DevicePluginNetworkDetector::deviceReachableChanged(bool reachable) +{ + DeviceMonitor *monitor = static_cast(sender()); + Device *device = m_monitors.value(monitor); + if (device->stateValue(inRangeStateTypeId).toBool() != reachable) { + qCDebug(dcNetworkDetector()) << "Device" << device->paramValue(macAddressParamTypeId).toString() << "reachable changed" << reachable; + device->setStateValue(inRangeStateTypeId, reachable); + } +} + +void DevicePluginNetworkDetector::deviceAddressChanged(const QString &address) +{ + DeviceMonitor *monitor = static_cast(sender()); + Device *device = m_monitors.value(monitor); + if (device->paramValue(addressParamTypeId).toString() != address) { + device->setParamValue(addressParamTypeId.toString(), address); } } diff --git a/networkdetector/devicepluginnetworkdetector.h b/networkdetector/devicepluginnetworkdetector.h index d8da0317..c9946827 100644 --- a/networkdetector/devicepluginnetworkdetector.h +++ b/networkdetector/devicepluginnetworkdetector.h @@ -26,9 +26,12 @@ #include "plugin/deviceplugin.h" #include "host.h" +#include "discovery.h" +#include "devicemonitor.h" #include #include +#include class DevicePluginNetworkDetector : public DevicePlugin { @@ -44,27 +47,18 @@ public: DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override; DeviceManager::HardwareResources requiredHardware() const override; + void deviceRemoved(Device *device) override; void guhTimer() override; -private: - QProcess * m_discoveryProcess; - QProcess * m_scanProcess; - - QXmlStreamReader m_reader; - - bool m_aboutToQuit; - - QStringList getDefaultTargets(); - QProcess *startScanProcesses(); - - // Process parsing - QList parseProcessOutput(const QByteArray &processData); - Host parseHost(); - private slots: - void processFinished(int exitCode, QProcess::ExitStatus exitStatus); + void discoveryFinished(const QList &hosts); + void deviceReachableChanged(bool reachable); + void deviceAddressChanged(const QString &address); +private: + Discovery *m_discovery = nullptr; + QHash m_monitors; }; #endif // DEVICEPLUGINNETWORKDETECTOR_H diff --git a/networkdetector/devicepluginnetworkdetector.json b/networkdetector/devicepluginnetworkdetector.json index 715fd02f..886b66c6 100644 --- a/networkdetector/devicepluginnetworkdetector.json +++ b/networkdetector/devicepluginnetworkdetector.json @@ -21,12 +21,20 @@ "createMethods": ["user", "discovery"], "paramTypes": [ { - "id": "44337c69-6101-4366-9913-ca9332da6b5c", - "idName": "hostname", - "name": "host name", + "id": "c6707093-3b51-469d-9fc0-f167bff2a987", + "idName": "address", + "name": "address", "type": "QString", "index": 0, "inputType": "TextLine" + }, + { + "id": "18fd3b05-478a-49cf-b8ae-3c6a98675ccc", + "idName": "macAddress", + "name": "hardware address", + "type": "QString", + "index": 1, + "inputType": "TextLine" } ], "stateTypes": [ diff --git a/networkdetector/discovery.cpp b/networkdetector/discovery.cpp new file mode 100644 index 00000000..f70aec78 --- /dev/null +++ b/networkdetector/discovery.cpp @@ -0,0 +1,229 @@ +#include "discovery.h" +#include "extern-plugininfo.h" + +#include +#include +#include +#include +#include + +Discovery::Discovery(QObject *parent) : QObject(parent) +{ + connect(&m_timeoutTimer, &QTimer::timeout, this, &Discovery::onTimeout); +} + +void Discovery::discoverHosts(int timeout) +{ + if (isRunning()) { + qWarning(dcNetworkDetector()) << "Discovery already running. Cannot start twice."; + return; + } + m_timeoutTimer.start(timeout * 1000); + + m_discoveryProcess = new QProcess(this); + connect(m_discoveryProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(discoveryFinished(int,QProcess::ExitStatus))); + + QStringList arguments; + arguments << "-oX" << "-" << "-n" << "-sn"; + arguments << getDefaultTargets(); + + qCDebug(dcNetworkDetector) << "Scanning network:" << "nmap" << arguments.join(" "); + m_discoveryProcess->start(QStringLiteral("nmap"), arguments); +} + +void Discovery::abort() +{ + if (m_discoveryProcess && m_discoveryProcess->state() == QProcess::Running) { + qCDebug(dcNetworkDetector()) << "Kill running discovery process"; + m_discoveryProcess->terminate(); + m_discoveryProcess->waitForFinished(5000); + } + foreach (QProcess *p, m_pendingArpLookups.keys()) { + p->terminate(); + delete p; + } + m_pendingArpLookups.clear(); + m_pendingNameLookups.clear(); + qDeleteAll(m_scanResults); + m_scanResults.clear(); +} + +bool Discovery::isRunning() const +{ + return m_discoveryProcess != nullptr && m_pendingArpLookups.isEmpty() && m_pendingNameLookups.isEmpty(); +} + +void Discovery::discoveryFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + + if (exitCode != 0 || exitStatus != QProcess::NormalExit) { + qCWarning(dcNetworkDetector()) << "Nmap error failed. Is nmap installed correctly?"; + emit finished({}); + m_discoveryProcess->deleteLater(); + m_discoveryProcess = nullptr; + return; + } + + QByteArray data = m_discoveryProcess->readAll(); + m_discoveryProcess->deleteLater(); + m_discoveryProcess = nullptr; + + QXmlStreamReader reader(data); + + int foundHosts = 0; + + while (!reader.atEnd() && !reader.hasError()) { + QXmlStreamReader::TokenType token = reader.readNext(); + if(token == QXmlStreamReader::StartDocument) + continue; + + if(token == QXmlStreamReader::StartElement && reader.name() == "host") { + bool isUp = false; + QString address; + QString macAddress; + QString vendor; + while (!reader.atEnd() && !reader.hasError() && !(token == QXmlStreamReader::EndElement && reader.name() == "host")) { + token = reader.readNext(); + + if (reader.name() == "address") { + QString addr = reader.attributes().value("addr").toString(); + QString type = reader.attributes().value("addrtype").toString(); + if (type == "ipv4" && !addr.isEmpty()) { + address = addr; + } else if (type == "mac") { + macAddress = addr; + vendor = reader.attributes().value("vendor").toString(); + } + } + + if (reader.name() == "status") { + QString state = reader.attributes().value("state").toString(); + if (!state.isEmpty()) + isUp = state == "up"; + } + } + + if (isUp) { + foundHosts++; + qCDebug(dcNetworkDetector()) << "Have host:" << address; + + Host *host = new Host(); + host->setAddress(address); + + if (!macAddress.isEmpty()) { + host->setMacAddress(macAddress); + } else { + QProcess *arpLookup = new QProcess(this); + m_pendingArpLookups.insert(arpLookup, host); + connect(arpLookup, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(arpLookupDone(int,QProcess::ExitStatus))); + arpLookup->start("arp", {"-vn"}); + } + + host->setHostName(vendor); + QHostInfo::lookupHost(address, this, SLOT(hostLookupDone(QHostInfo))); + m_pendingNameLookups.insert(address, host); + + m_scanResults.append(host); + } + } + } + + if (foundHosts == 0) { + qCDebug(dcNetworkDetector()) << "Network scan successful but no hosts found in this network"; + emit finished({}); + } +} + +void Discovery::hostLookupDone(const QHostInfo &info) +{ + Host *host = m_pendingNameLookups.take(info.addresses().first().toString()); + if (!host) { + // Probably aborted... + return; + } + if (info.error() != QHostInfo::NoError) { + qWarning(dcNetworkDetector()) << "Host lookup failed:" << info.errorString(); + } + if (host->hostName().isEmpty() || info.hostName() != host->address()) { + host->setHostName(info.hostName()); + } + + finishDiscovery(); +} + +void Discovery::arpLookupDone(int exitCode, QProcess::ExitStatus exitStatus) +{ + QProcess *p = static_cast(sender()); + p->deleteLater(); + + Host *host = m_pendingArpLookups.take(p); + + if (exitCode != 0 || exitStatus != QProcess::NormalExit) { + qCWarning(dcNetworkDetector()) << "ARP lookup process failed for host" << host->address(); + finishDiscovery(); + return; + } + + QString data = QString::fromLatin1(p->readAll()); + foreach (QString line, data.split('\n')) { + line.replace(QRegExp("[ ]{1,}"), " "); + QStringList parts = line.split(" "); + if (parts.count() >= 3 && parts.first() == host->address() && parts.at(1) == "ether") { + host->setMacAddress(parts.at(2)); + break; + } + } + finishDiscovery(); +} + +void Discovery::onTimeout() +{ + qWarning(dcNetworkDetector()) << "Timeout hit. Stopping discovery"; + if (m_discoveryProcess && m_discoveryProcess->state() == QProcess::Running) { + disconnect(this, SLOT(discoveryFinished(int,QProcess::ExitStatus))); + m_discoveryProcess->terminate(); + delete m_discoveryProcess; + m_discoveryProcess = nullptr; + } + foreach (QProcess *p, m_pendingArpLookups.keys()) { + p->terminate(); + m_scanResults.removeAll(m_pendingArpLookups.value(p)); + delete p; + } + m_pendingArpLookups.clear(); + m_pendingNameLookups.clear(); + finishDiscovery(); +} + +QStringList Discovery::getDefaultTargets() +{ + QStringList targets; + foreach (const QHostAddress &interface, QNetworkInterface::allAddresses()) { + if (!interface.isLoopback() && interface.scopeId().isEmpty() && interface.protocol() == QAbstractSocket::IPv4Protocol) { + QPair pair = QHostAddress::parseSubnet(interface.toString() + "/24"); + targets << QString("%1/%2").arg(pair.first.toString()).arg(pair.second); + } + } + return targets; +} + +void Discovery::finishDiscovery() +{ + if (m_pendingNameLookups.count() > 0 || m_pendingArpLookups.count() > 0) { + // Still busy... + return; + } + + QList hosts; + foreach (Host *host, m_scanResults) { + if (!host->macAddress().isEmpty()) { + hosts.append(*host); + } + } + qDeleteAll(m_scanResults); + m_scanResults.clear(); + + qCDebug(dcNetworkDetector()) << "Emitting device discovered for" << hosts.count() << "devices"; + m_timeoutTimer.stop(); + emit finished(hosts); +} diff --git a/networkdetector/discovery.h b/networkdetector/discovery.h new file mode 100644 index 00000000..f40822e7 --- /dev/null +++ b/networkdetector/discovery.h @@ -0,0 +1,47 @@ +#ifndef DISCOVERY_H +#define DISCOVERY_H + +#include +#include +#include +#include + +#include "host.h" + +class Discovery : public QObject +{ + Q_OBJECT +public: + explicit Discovery(QObject *parent = nullptr); + + void discoverHosts(int timeout); + void abort(); + + bool isRunning() const; + + +signals: + void finished(QList hosts); + +private: + QStringList getDefaultTargets(); + + void finishDiscovery(); + +private slots: + void discoveryFinished(int exitCode, QProcess::ExitStatus exitStatus); + void hostLookupDone(const QHostInfo &info); + void arpLookupDone(int exitCode, QProcess::ExitStatus exitStatus); + void onTimeout(); + +private: + QProcess * m_discoveryProcess = nullptr; + QTimer m_timeoutTimer; + + QHash m_pendingArpLookups; + QHash m_pendingNameLookups; + QList m_scanResults; + +}; + +#endif // DISCOVERY_H diff --git a/networkdetector/host.cpp b/networkdetector/host.cpp index 2160212d..7a1047c2 100644 --- a/networkdetector/host.cpp +++ b/networkdetector/host.cpp @@ -24,15 +24,18 @@ Host::Host() { - + qRegisterMetaType(); + qRegisterMetaType >(); } -Host::Host(const QString &hostName, const QString &address, const bool &reachable): - m_hostName(hostName), - m_address(address), - m_reachable(reachable) +QString Host::macAddress() const { + return m_macAddress; +} +void Host::setMacAddress(const QString &macAddress) +{ + m_macAddress = macAddress; } QString Host::hostName() const @@ -40,23 +43,43 @@ QString Host::hostName() const return m_hostName; } -QString Host::adderss() const +void Host::setHostName(const QString &hostName) +{ + m_hostName = hostName; +} + +QString Host::address() const { return m_address; } +void Host::setAddress(const QString &address) +{ + m_address = address; +} + +void Host::seen() +{ + m_lastSeenTime = QDateTime::currentDateTime(); +} + +QDateTime Host::lastSeenTime() const +{ + return m_lastSeenTime; +} + bool Host::reachable() const { return m_reachable; } -bool Host::isValid() const +void Host::setReachable(bool reachable) { - return !m_hostName.isEmpty() && !m_address.isEmpty(); + m_reachable = reachable; } QDebug operator<<(QDebug dbg, const Host &host) { - dbg.nospace() << "Host(" << host.hostName() << ", " << host.adderss() << ", " << (host.reachable() ? "up" : "down") << ")"; + dbg.nospace() << "Host(" << host.macAddress() << "," << host.hostName() << ", " << host.address() << ", " << (host.reachable() ? "up" : "down") << ")"; return dbg.space(); } diff --git a/networkdetector/host.h b/networkdetector/host.h index 3e6dc70d..85ffaceb 100644 --- a/networkdetector/host.h +++ b/networkdetector/host.h @@ -25,24 +25,36 @@ #include #include +#include class Host { public: Host(); - Host(const QString &hostName, const QString &address, const bool &reachable); + + QString macAddress() const; + void setMacAddress(const QString &macAddress); QString hostName() const; - QString adderss() const; - bool reachable() const; + void setHostName(const QString &hostName); - bool isValid() const; + QString address() const; + void setAddress(const QString &address); + + void seen(); + QDateTime lastSeenTime() const; + + bool reachable() const; + void setReachable(bool reachable); private: + QString m_macAddress; QString m_hostName; QString m_address; + QDateTime m_lastSeenTime; bool m_reachable; }; +Q_DECLARE_METATYPE(Host) QDebug operator<<(QDebug dbg, const Host &host); diff --git a/networkdetector/networkdetector.pro b/networkdetector/networkdetector.pro index 50efb438..c8ac62a8 100644 --- a/networkdetector/networkdetector.pro +++ b/networkdetector/networkdetector.pro @@ -8,10 +8,14 @@ TARGET = $$qtLibraryTarget(guh_devicepluginnetworkdetector) SOURCES += \ devicepluginnetworkdetector.cpp \ - host.cpp + host.cpp \ + discovery.cpp \ + devicemonitor.cpp HEADERS += \ devicepluginnetworkdetector.h \ - host.h + host.h \ + discovery.h \ + devicemonitor.h