diff --git a/.gitignore b/.gitignore index d2c08e73..e404ce66 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,9 @@ snap/prime/ snap/stage/ doc/interfacelist.qdoc +# Never add downloaded json data +data/mac-database/macaddress.io-db.json + .crossbuilder/ debs*.tar diff --git a/data/mac-database/README.md b/data/mac-database/README.md new file mode 100644 index 00000000..914e9b46 --- /dev/null +++ b/data/mac-database/README.md @@ -0,0 +1,16 @@ +# MAC address dabase + +The MAC address database has been created using the dowloaded JSON data from [https://macaddress.io](https://macaddress.io). + +## Build database + +Before you can start the `build-database.py` script, please make sure you downloaded the the online database in JSON format. + +Once the `macaddress.io-db.json` file has been downloaded and placed into this folder, the python tool can be started in order to generate a read performance optimized and minimal database for searching mac address OUIs (Organizationally Unique Identifiers). + + $ python3 build-database.py + +The final database will be named `mac-addresses.db`. + +In nymea the `MacAddressDatabase` will search for this database file in `/usr/share/nymea/mac-addresses.db` and provides an asynch mechanism to provide the manufacturer name for a given mac address. + diff --git a/data/mac-database/build-database.py b/data/mac-database/build-database.py new file mode 100644 index 00000000..6dd68ec1 --- /dev/null +++ b/data/mac-database/build-database.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# Copyright 2013 - 2021, nymea GmbH +# Contact: contact@nymea.io +# +# This file is part of nymea. +# This project including source code and documentation is protected by +# copyright law, and remains the property of nymea GmbH. All rights, including +# reproduction, publication, editing and translation, are reserved. The use of +# this project is subject to the terms of a license agreement to be concluded +# with nymea GmbH in accordance with the terms of use of nymea GmbH, available +# under https://nymea.io/license +# +# GNU Lesser General Public License Usage +# Alternatively, this project may be redistributed and/or modified under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; version 3. This project is distributed in the hope that +# it will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this project. If not, see . +# +# For any further details and any questions please contact us under +# contact@nymea.io or see our FAQ/Licensing Information on +# https://nymea.io/license/faq +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +import os +import sys +import json +import sqlite3 + +jsonDataFileName = 'macaddress.io-db.json' +print('Loading JSON data from', jsonDataFileName) +jsonDataFile = open(jsonDataFileName, 'r') +lines = jsonDataFile.readlines() + +vendorInfoHash = {} +for line in lines: + vendorMap = json.loads(line) + vendorInfoHash[vendorMap['oui']] = vendorMap['companyName'] + +jsonDataFile.close() + +# Sort by oui for good binary search in the db +sortedOuiHash = sorted(vendorInfoHash.items(), key=lambda x: x[0], reverse=False) +databaseFileName = 'mac-addresses.db' +if os.path.exists(databaseFileName): + print('Delete old db file and create a new one', databaseFileName) + os.remove(databaseFileName) + +connection = sqlite3.connect(databaseFileName) +cursor = connection.cursor() +cursor.execute('CREATE TABLE companyNames (companyName TEXT PRIMARY KEY, UNIQUE(companyName));') +cursor.execute('CREATE TABLE oui (oui TEXT PRIMARY KEY, companyNameIndex INTEGER, UNIQUE(oui)) WITHOUT ROWID;') +#cursor.execute('CREATE UNIQUE INDEX ouiIndex ON `oui` (`oui`);') + +# Insert all vendor names alphabetically +sortedVendorHash = sorted(vendorInfoHash.items(), key=lambda x: x[1], reverse=False) +vendorCount = 0 +for vendorInfo in sortedVendorHash: + insertQuery = 'INSERT OR IGNORE INTO companyNames (companyName) VALUES(?);' + cursor.execute(insertQuery, [vendorInfo[1]]) + cursor.execute('SELECT COUNT(companyName) FROM companyNames'); + countResult = cursor.fetchall() + vendorCount = countResult[0][0] + +connection.commit() + +# Insert all oui with reference to company name +ouiCount = 0 +for vendorInfo in sortedOuiHash: + insertQuery = 'INSERT OR IGNORE INTO oui (oui, companyNameIndex) VALUES(?, (SELECT rowid FROM companyNames WHERE companyName = ?));' + cursor.execute(insertQuery, [vendorInfo[0].replace(':', ''), vendorInfo[1]]) + cursor.execute('SELECT COUNT(oui) FROM oui'); + countResult = cursor.fetchall() + ouiCount = countResult[0][0] + +connection.commit() +connection.close() +print('Loaded', ouiCount, 'OUI values from', vendorCount, 'manufacturers into', databaseFileName) + + diff --git a/data/mac-database/mac-addresses.db b/data/mac-database/mac-addresses.db new file mode 100644 index 00000000..d58e3cbd Binary files /dev/null and b/data/mac-database/mac-addresses.db differ diff --git a/libnymea/libnymea.pro b/libnymea/libnymea.pro index bae23e8e..4ec5c0e1 100644 --- a/libnymea/libnymea.pro +++ b/libnymea/libnymea.pro @@ -3,8 +3,9 @@ include(../nymea.pri) TARGET = nymea TEMPLATE = lib -QT += network bluetooth dbus serialport +QT += network bluetooth dbus serialport sql QT -= gui + DEFINES += LIBNYMEA_LIBRARY CONFIG += link_pkgconfig @@ -43,6 +44,7 @@ HEADERS += \ network/apikeys/apikeysprovider.h \ network/apikeys/apikeystorage.h \ network/arpsocket.h \ + network/macaddressdatabase.h \ network/networkdevice.h \ network/networkdevicediscovery.h \ network/networkdevicediscoveryreply.h \ @@ -149,6 +151,7 @@ SOURCES += \ network/apikeys/apikeysprovider.cpp \ network/apikeys/apikeystorage.cpp \ network/arpsocket.cpp \ + network/macaddressdatabase.cpp \ network/networkdevice.cpp \ network/networkdevicediscovery.cpp \ network/networkdevicediscoveryreply.cpp \ diff --git a/libnymea/network/macaddressdatabase.cpp b/libnymea/network/macaddressdatabase.cpp new file mode 100644 index 00000000..4b652786 --- /dev/null +++ b/libnymea/network/macaddressdatabase.cpp @@ -0,0 +1,200 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "macaddressdatabase.h" +#include "loggingcategories.h" + +#include +#include +#include +#include +#include +#include + +NYMEA_LOGGING_CATEGORY(dcMacAddressDatabase, "MacAddressDatabase") + +MacAddressDatabase::MacAddressDatabase(QObject *parent) : QObject(parent) +{ + m_available = initDatabase(); + if (m_available) { + m_futureWatcher = new QFutureWatcher(this); + connect(m_futureWatcher, &QFutureWatcher::finished, this, &MacAddressDatabase::onLookupFinished); + } +} + +MacAddressDatabase::MacAddressDatabase(const QString &databaseName, QObject *parent) : + QObject(parent), + m_databaseName(databaseName) +{ + m_available = initDatabase(); + if (m_available) { + m_futureWatcher = new QFutureWatcher(this); + connect(m_futureWatcher, &QFutureWatcher::finished, this, &MacAddressDatabase::onLookupFinished); + } +} + +MacAddressDatabase::~MacAddressDatabase() +{ + m_db.close(); + m_db = QSqlDatabase(); + QSqlDatabase::removeDatabase(m_connectionName); +} + +bool MacAddressDatabase::available() const +{ + return m_available; +} + +MacAddressDatabaseReply *MacAddressDatabase::lookupMacAddress(const QString &macAddress) +{ + MacAddressDatabaseReply *reply = new MacAddressDatabaseReply(this); + connect(reply, &MacAddressDatabaseReply::finished, reply, &MacAddressDatabaseReply::deleteLater); + reply->m_macAddress = macAddress; + + if (!m_available) { + QTimer::singleShot(0, this, [=](){ emit reply->finished(); }); + return reply; + } + + m_pendingReplies.enqueue(reply); + runNextLookup(); + return reply; +} + +bool MacAddressDatabase::initDatabase() +{ + m_connectionName = QFileInfo(m_databaseName).baseName(); + m_db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), m_connectionName); + m_db.setDatabaseName(m_databaseName); + + if (!m_db.isValid()) { + qCWarning(dcMacAddressDatabase()) << "The network database is not valid" << m_db.databaseName(); + return false; + } + + m_db.close(); + if (!m_db.open()) { + qCWarning(dcMacAddressDatabase()) << "Could not open database" << m_db.databaseName() << "Initialization failed."; + return false; + } + + // Verify the tables we need exist + qCDebug(dcMacAddressDatabase()) << "Tables" << m_db.tables(); + if (!m_db.tables().contains("oui")) { + qCWarning(dcMacAddressDatabase()) << "Invalid database. Could not find \"oui\" table in" << m_db.databaseName(); + return false; + } + + if (!m_db.tables().contains("companyNames")) { + qCWarning(dcMacAddressDatabase()) << "Invalid database. Could not find \"companyNames\" table in" << m_db.databaseName(); + return false; + } + + return true; +} + +void MacAddressDatabase::runNextLookup() +{ + if (m_pendingReplies.isEmpty()) + return; + + if (m_futureWatcher->isRunning() || m_currentReply) + return; + + m_currentReply = m_pendingReplies.dequeue(); + m_currentReply->m_startTimestamp = QDateTime::currentMSecsSinceEpoch(); + QFuture future = QtConcurrent::run(this, &MacAddressDatabase::lookupMacAddressVendorInternal, m_currentReply->macAddress()); + m_futureWatcher->setFuture(future); +} + +void MacAddressDatabase::onLookupFinished() +{ + if (m_currentReply) { + QString manufacturer = m_futureWatcher->future().result(); + qCDebug(dcMacAddressDatabase()) << "Manufacturer lookup for" << m_currentReply->macAddress() << "finished:" << manufacturer << QDateTime::currentMSecsSinceEpoch() - m_currentReply->m_startTimestamp << "ms"; + m_currentReply->m_manufacturer = manufacturer; + emit m_currentReply->finished(); + m_currentReply = nullptr; + } + + runNextLookup(); +} + +QString MacAddressDatabase::lookupMacAddressVendorInternal(const QString &macAddress) +{ + qCDebug(dcMacAddressDatabase()) << "Start looking up vendor for" << macAddress; + // Convert the mac address string to upper like in the database and remove : since they have been removed for size reasons + QString fullMacAddressString = QString(macAddress).toUpper().remove(":"); + + QString manufacturer; + int length = 6; + while (true) { + QString searchString = fullMacAddressString.left(length); + QString queryString = QString("SELECT COUNT(oui) FROM oui WHERE oui LIKE \'%1%\';").arg(searchString); + qCDebug(dcMacAddressDatabase()) << "Query:" << queryString; + QSqlQuery countQuery = m_db.exec(queryString); + if (countQuery.lastError().isValid()) { + qCWarning(dcMacAddressDatabase()) << "Query finished with error" << countQuery.lastError().text(); + break; + } + + if (!countQuery.next()) + break; + + int rowCount = countQuery.value(0).toInt(); + qCDebug(dcMacAddressDatabase()) << "Found" << rowCount << "with" << searchString; + // If we have found the one... + if (rowCount == 1) { + // Query the name + queryString = QString("SELECT companyName from companyNames WHERE rowid IS (SELECT companyNameIndex FROM oui WHERE oui=\'%1\');").arg(searchString); + qCDebug(dcMacAddressDatabase()) << "Query:" << queryString; + countQuery = m_db.exec(queryString); + if (!countQuery.next()) + break; + + manufacturer = countQuery.value(0).toString(); + break; + } + + // If nothing found + if (rowCount == 0) + break; + + // Found to many results, lets add a value until we find the matching vendor + length += 1; + if (length > fullMacAddressString) + break; + + // Search with one addition digit + } + + return manufacturer; +} + diff --git a/libnymea/network/macaddressdatabase.h b/libnymea/network/macaddressdatabase.h new file mode 100644 index 00000000..d661ad1d --- /dev/null +++ b/libnymea/network/macaddressdatabase.h @@ -0,0 +1,93 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef MACADDRESSDATABASE_H +#define MACADDRESSDATABASE_H + +#include +#include +#include +#include + +#include "libnymea.h" + +class LIBNYMEA_EXPORT MacAddressDatabaseReply : public QObject +{ + Q_OBJECT + friend class MacAddressDatabase; + +public: + QString macAddress() const { return m_macAddress; }; + QString manufacturer() const { return m_manufacturer; }; + +private: + explicit MacAddressDatabaseReply(QObject *parent = nullptr) : QObject(parent) { }; + QString m_macAddress; + QString m_manufacturer; + qint64 m_startTimestamp; + +signals: + void finished(); + +}; + + +class LIBNYMEA_EXPORT MacAddressDatabase : public QObject +{ + Q_OBJECT +public: + explicit MacAddressDatabase(QObject *parent = nullptr); + MacAddressDatabase(const QString &databaseName, QObject *parent = nullptr); + ~MacAddressDatabase(); + + bool available() const; + + MacAddressDatabaseReply *lookupMacAddress(const QString &macAddress); + +private: + QSqlDatabase m_db; + bool m_available = false; + QString m_connectionName; + QString m_databaseName = "/usr/share/nymea/mac-addresses.db"; + + MacAddressDatabaseReply *m_currentReply = nullptr; + QFutureWatcher *m_futureWatcher = nullptr; + QQueue m_pendingReplies; + + bool initDatabase(); + void runNextLookup(); + +private slots: + void onLookupFinished(); + QString lookupMacAddressVendorInternal(const QString &macAddress); + +}; + +#endif // MACADDRESSDATABASE_H diff --git a/libnymea/network/networkdevicediscovery.cpp b/libnymea/network/networkdevicediscovery.cpp index 52b14798..85bf354f 100644 --- a/libnymea/network/networkdevicediscovery.cpp +++ b/libnymea/network/networkdevicediscovery.cpp @@ -31,6 +31,9 @@ #include "networkdevicediscovery.h" #include "loggingcategories.h" #include "networkutils.h" +#include "macaddressdatabase.h" + +#include NYMEA_LOGGING_CATEGORY(dcNetworkDeviceDiscovery, "NetworkDeviceDiscovery") @@ -49,6 +52,10 @@ NetworkDeviceDiscovery::NetworkDeviceDiscovery(QObject *parent) : if (!m_ping->available()) qCWarning(dcNetworkDeviceDiscovery()) << "Failed to create ping tool" << m_ping->error(); + m_macAddressDatabase = new MacAddressDatabase(this); + if (!m_macAddressDatabase->available()) + qCWarning(dcNetworkDeviceDiscovery()) << "The mac address database is not available. Network discovery will not lookup mac address manufacturer"; + m_discoveryTimer = new QTimer(this); m_discoveryTimer->setInterval(5000); m_discoveryTimer->setSingleShot(true); @@ -72,6 +79,7 @@ NetworkDeviceDiscoveryReply *NetworkDeviceDiscovery::discover() NetworkDeviceDiscoveryReply *reply = new NetworkDeviceDiscoveryReply(this); m_currentReply = reply; + m_currentReply->m_startTimestamp = QDateTime::currentMSecsSinceEpoch(); if (m_ping->available()) { pingAllNetworkDevices(); @@ -179,13 +187,53 @@ void NetworkDeviceDiscovery::finishDiscovery() m_running = false; emit runningChanged(m_running); - qCDebug(dcNetworkDeviceDiscovery()) << "Discovery finished. Found" << m_currentReply->networkDevices().count() << "network devices."; + qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_currentReply->m_startTimestamp; + qCDebug(dcNetworkDeviceDiscovery()) << "Discovery finished. Found" << m_currentReply->networkDevices().count() << "network devices in" << QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz"); m_arpSocket->closeSocket(); emit m_currentReply->finished(); m_currentReply->deleteLater(); m_currentReply = nullptr; } +void NetworkDeviceDiscovery::updateOrAddNetworkDeviceArp(const QNetworkInterface &interface, const QHostAddress &address, const QString &macAddress, const QString &manufacturer) +{ + int index = m_currentReply->networkDevices().indexFromHostAddress(address); + if (index >= 0) { + // Update the network device + m_currentReply->networkDevices()[index].setMacAddress(macAddress); + if (!manufacturer.isEmpty()) + m_currentReply->networkDevices()[index].setMacAddressManufacturer(manufacturer); + + if (interface.isValid()) { + m_currentReply->networkDevices()[index].setNetworkInterface(interface); + } + } else { + index = m_currentReply->networkDevices().indexFromMacAddress(macAddress); + if (index >= 0) { + // Update the network device + m_currentReply->networkDevices()[index].setAddress(address); + if (!manufacturer.isEmpty()) + m_currentReply->networkDevices()[index].setMacAddressManufacturer(manufacturer); + + if (interface.isValid()) { + m_currentReply->networkDevices()[index].setNetworkInterface(interface); + } + } else { + // Add the network device + NetworkDevice networkDevice; + networkDevice.setAddress(address); + networkDevice.setMacAddress(macAddress); + if (!manufacturer.isEmpty()) + networkDevice.setMacAddressManufacturer(manufacturer); + + if (interface.isValid()) + networkDevice.setNetworkInterface(interface); + + m_currentReply->networkDevices().append(networkDevice); + } + } +} + void NetworkDeviceDiscovery::onArpResponseRceived(const QNetworkInterface &interface, const QHostAddress &address, const QString &macAddress) { if (!m_currentReply) { @@ -194,31 +242,15 @@ void NetworkDeviceDiscovery::onArpResponseRceived(const QNetworkInterface &inter } qCDebug(dcNetworkDeviceDiscovery()) << "ARP reply received" << address.toString() << macAddress << interface.name(); - // Update or add network device - int index = m_currentReply->networkDevices().indexFromHostAddress(address); - if (index >= 0) { - // Update the network device - m_currentReply->networkDevices()[index].setMacAddress(macAddress); - if (interface.isValid()) { - m_currentReply->networkDevices()[index].setNetworkInterface(interface); - } - } else { - index = m_currentReply->networkDevices().indexFromMacAddress(macAddress); - if (index >= 0) { - // Update the network device - m_currentReply->networkDevices()[index].setAddress(address); - if (interface.isValid()) { - m_currentReply->networkDevices()[index].setNetworkInterface(interface); - } - } else { - // Add the network device - NetworkDevice networkDevice; - networkDevice.setAddress(address); - networkDevice.setMacAddress(macAddress); - if (interface.isValid()) - networkDevice.setNetworkInterface(interface); - m_currentReply->networkDevices().append(networkDevice); - } + // Lookup the mac address vendor if possible + if (m_macAddressDatabase->available()) { + MacAddressDatabaseReply *reply = m_macAddressDatabase->lookupMacAddress(macAddress); + connect(reply, &MacAddressDatabaseReply::finished, this, [=](){ + qCDebug(dcNetworkDeviceDiscovery()) << "MAC manufacturer lookup finished for" << macAddress << ":" << reply->manufacturer(); + updateOrAddNetworkDeviceArp(interface, address, macAddress, reply->manufacturer()); + }); + } else { + updateOrAddNetworkDeviceArp(interface, address, macAddress); } } diff --git a/libnymea/network/networkdevicediscovery.h b/libnymea/network/networkdevicediscovery.h index 0d426383..acd9d8f1 100644 --- a/libnymea/network/networkdevicediscovery.h +++ b/libnymea/network/networkdevicediscovery.h @@ -40,6 +40,8 @@ #include "arpsocket.h" #include "networkdevicediscoveryreply.h" +class MacAddressDatabase; + Q_DECLARE_LOGGING_CATEGORY(dcNetworkDeviceDiscovery) class LIBNYMEA_EXPORT NetworkDeviceDiscovery : public QObject @@ -59,6 +61,7 @@ signals: void runningChanged(bool running); private: + MacAddressDatabase *m_macAddressDatabase = nullptr; ArpSocket *m_arpSocket = nullptr; Ping *m_ping = nullptr; bool m_running = false; @@ -70,6 +73,8 @@ private: void pingAllNetworkDevices(); void finishDiscovery(); + void updateOrAddNetworkDeviceArp(const QNetworkInterface &interface, const QHostAddress &address, const QString &macAddress, const QString &manufacturer = QString()); + private slots: void onArpResponseRceived(const QNetworkInterface &interface, const QHostAddress &address, const QString &macAddress); diff --git a/libnymea/network/networkdevicediscoveryreply.h b/libnymea/network/networkdevicediscoveryreply.h index 1787c02e..670294d1 100644 --- a/libnymea/network/networkdevicediscoveryreply.h +++ b/libnymea/network/networkdevicediscoveryreply.h @@ -43,15 +43,15 @@ class LIBNYMEA_EXPORT NetworkDeviceDiscoveryReply : public QObject friend class NetworkDeviceDiscovery; public: - explicit NetworkDeviceDiscoveryReply(QObject *parent = nullptr); - NetworkDevices &networkDevices(); signals: void finished(); private: + explicit NetworkDeviceDiscoveryReply(QObject *parent = nullptr); NetworkDevices m_networkDevices; + qint64 m_startTimestamp; }; diff --git a/libnymea/network/ping.cpp b/libnymea/network/ping.cpp index c26b2914..54b66fdd 100644 --- a/libnymea/network/ping.cpp +++ b/libnymea/network/ping.cpp @@ -200,6 +200,10 @@ void Ping::performPing(PingReply *reply) // Start reply timer and handle timeout m_pendingReplies.insert(reply->requestId(), reply); + reply->m_timer->start(8000); + connect(reply, &PingReply::timeout, this, [=](){ + finishReply(reply, PingReply::ErrorTimeout); + }); } void Ping::verifyErrno(int error) @@ -336,7 +340,7 @@ void Ping::onSocketReadyRead(int socketDescriptor) // Verify sequence number if (responsePacket->icmp_seq != reply->sequenceNumber()) { qCWarning(dcPing()) << "Received echo reply with different sequence number" << htons(responsePacket->icmp_seq); - finishReply(reply, PingReply::ErrorInvalidSequenceNumberResponse); + finishReply(reply, PingReply::ErrorInvalidResponse); return; } diff --git a/libnymea/network/pingreply.cpp b/libnymea/network/pingreply.cpp index e97034a1..c3fa499e 100644 --- a/libnymea/network/pingreply.cpp +++ b/libnymea/network/pingreply.cpp @@ -32,7 +32,10 @@ PingReply::PingReply(QObject *parent) : QObject(parent) { - + m_timer = new QTimer(this); + m_timer->setSingleShot(true); + connect(m_timer, &QTimer::timeout, this, &PingReply::timeout); + connect(this, &PingReply::finished, m_timer, &QTimer::stop); } QHostAddress PingReply::targetHostAddress() const diff --git a/libnymea/network/pingreply.h b/libnymea/network/pingreply.h index e1c8d9c2..60837181 100644 --- a/libnymea/network/pingreply.h +++ b/libnymea/network/pingreply.h @@ -51,11 +51,12 @@ class LIBNYMEA_EXPORT PingReply : public QObject public: enum Error { ErrorNoError, - ErrorInvalidSequenceNumberResponse, + ErrorInvalidResponse, ErrorNetworkDown, ErrorNetworkUnreachable, ErrorPermissionDenied, ErrorSocketError, + ErrorTimeout, ErrorHostUnreachable }; Q_ENUM(Error) @@ -74,8 +75,10 @@ public: signals: void finished(); + void timeout(); private: + QTimer *m_timer = nullptr; QHostAddress m_targetHostAddress; quint16 m_sequenceNumber = 0; quint16 m_requestId = 0;