Update ping and monitor handling

This commit is contained in:
Simon Stürz 2024-12-09 17:08:22 +01:00
parent abb656016d
commit 341a07cd85
8 changed files with 306 additions and 181 deletions

View File

@ -89,7 +89,7 @@ NetworkDeviceDiscoveryImpl::NetworkDeviceDiscoveryImpl(QObject *parent) :
// Timer for updating the monitors
m_monitorTimer = new QTimer(this);
m_monitorTimer->setInterval(5000);
m_monitorTimer->setInterval(10000);
m_monitorTimer->setSingleShot(false);
connect(m_monitorTimer, &QTimer::timeout, this, &NetworkDeviceDiscoveryImpl::evaluateMonitors);
@ -218,58 +218,6 @@ bool NetworkDeviceDiscoveryImpl::running() const
return m_running;
}
// NetworkDeviceMonitor *NetworkDeviceDiscoveryImpl::registerMonitor(const MacAddress &macAddress)
// {
// if (macAddress.isNull()) {
// qCWarning(dcNetworkDeviceDiscovery()) << "Could not register monitor for invalid" << macAddress;
// return nullptr;
// }
// // Make sure we create only one monitor per MAC and keep track how many user
// // have access to this monitor otherwise an unregister could cause a crash in
// // an other plugin plugin which might still need it
// if (m_monitors.contains(macAddress)) {
// m_monitorsReferenceCount[macAddress] += 1;
// qCInfo(dcNetworkDeviceDiscovery()) << "Register network device monitor for" << macAddress << "which already exists. Returning existing monitor having now" << m_monitorsReferenceCount[macAddress] << "references.";
// return m_monitors.value(macAddress);
// }
// qCInfo(dcNetworkDeviceDiscovery()) << "Register new network device monitor for" << macAddress;
// // Fill in cached information
// NetworkDeviceInfo info;
// if (m_networkInfoCache.contains(macAddress)) {
// info = m_networkInfoCache.value(macAddress);
// } else {
// info.setMacAddress(macAddress.toString());
// }
// NetworkDeviceMonitorImpl *monitor = new NetworkDeviceMonitorImpl(macAddress, this);
// monitor->setNetworkDeviceInfo(info);
// monitor->setLastSeen(m_lastSeen.value(macAddress, QDateTime()));
// m_monitors.insert(macAddress, monitor);
// m_monitorsReferenceCount[macAddress] = 1;
// if (!available()) {
// qCWarning(dcNetworkDeviceDiscovery()) << "Registered monitor but the hardware resource is not available. The monitor will not work as expected" << monitor;
// return monitor;
// }
// // Restart the monitor timer since we evaluate this one now
// m_monitorTimer->start();
// if (!monitor->networkDeviceInfo().isValid()) {
// qCDebug(dcNetworkDeviceDiscovery()) << "Adding network device monitor for unresolved mac address. Starting a discovery...";
// NetworkDeviceDiscoveryReply *reply = discover();
// connect(reply, &NetworkDeviceDiscoveryReply::finished, reply, &NetworkDeviceDiscoveryReply::deleteLater);
// } else {
// evaluateMonitor(monitor);
// }
// qCDebug(dcNetworkDeviceDiscovery()) << "Registered successfully" << monitor;
// return monitor;
// }
NetworkDeviceMonitor *NetworkDeviceDiscoveryImpl::registerMonitor(Thing *thing)
{
if (!thing->thingClass().interfaces().contains("networkdevice")) {
@ -308,11 +256,14 @@ NetworkDeviceMonitor *NetworkDeviceDiscoveryImpl::registerMonitor(Thing *thing)
}
}
bool newMonitor = true;
if (internalMonitor) {
qCDebug(dcNetworkDeviceDiscovery()) << "Already have an internal monitor for this network device" << internalMonitor;
newMonitor = false;
} else {
// Create a new monitor for the internal use
internalMonitor = new NetworkDeviceMonitorImpl(macAddress, hostName, address, this);
m_monitors.insert(internalMonitor, QVector<NetworkDeviceMonitorImpl*>());
}
internalMonitor->setMonitorMode(mode);
@ -354,7 +305,13 @@ NetworkDeviceMonitor *NetworkDeviceDiscoveryImpl::registerMonitor(Thing *thing)
// Create a new plugin monitor object we are going to return...
NetworkDeviceMonitorImpl *pluginMonitor = createPluginMonitor(internalMonitor);
m_monitors[internalMonitor].append(pluginMonitor);
qCDebug(dcNetworkDeviceDiscovery()) << "Registered successfully" << pluginMonitor;
// In case this is a new monitor, let's evaluate it right the way so know asap if the device is reachable or not
if (newMonitor)
evaluateMonitor(internalMonitor);
return pluginMonitor;
}
@ -374,6 +331,14 @@ PingReply *NetworkDeviceDiscoveryImpl::ping(const QHostAddress &address, uint re
return reply;
}
PingReply *NetworkDeviceDiscoveryImpl::ping(const QString &hostName, uint retries)
{
PingReply *reply = m_ping->ping(hostName, retries);
// Note: we use any ping used trough this method also for the monitor evaluation
watchPingReply(reply);
return reply;
}
PingReply *NetworkDeviceDiscoveryImpl::ping(const QHostAddress &address, bool lookupHost, uint retries)
{
PingReply *reply = m_ping->ping(address, lookupHost, retries);
@ -521,7 +486,35 @@ void NetworkDeviceDiscoveryImpl::processMonitorPingResult(PingReply *reply, Netw
// Save the last time we tried to communicate
if (reply->error() == PingReply::ErrorNoError) {
qCDebug(dcNetworkDeviceDiscovery()) << "Ping response from" << monitor << reply->duration() << "ms";
monitor->setLastSeen(QDateTime::currentDateTime());
QDateTime currentDateTime = QDateTime::currentDateTime();
m_lastSeen[reply->targetHostAddress()] = currentDateTime;
for (int i = 0; i < m_networkInfoCache.count(); i++) {
if (monitor->monitorMode() == NetworkDeviceInfo::MonitorModeHostName) {
if (m_networkInfoCache.at(i).hostName() == monitor->hostName() && m_networkInfoCache.at(i).address() != reply->targetHostAddress()) {
QHostAddress oldAddress = m_networkInfoCache.at(i).address();
qCDebug(dcNetworkDeviceDiscovery()) << "Hostname" << monitor->hostName() << "changed the IP address from"
<< oldAddress.toString()
<< "-->"
<< reply->targetHostAddress().toString();
removeFromNetworkDeviceCache(oldAddress);
NetworkDeviceInfo info = m_networkInfoCache.at(i);
info.setAddress(reply->targetHostAddress());
monitor->setNetworkDeviceInfo(info);
m_networkInfoCache[i] = info;
m_networkInfoCache.sortNetworkDevices();
saveNetworkDeviceCache(info);
break;
}
}
}
monitor->setLastSeen(currentDateTime);
monitor->setReachable(true);
} else {
qCDebug(dcNetworkDeviceDiscovery()) << "Failed to ping device from" << monitor << "retrying" << reply->retries() << "times:" << reply->error();
@ -531,29 +524,25 @@ void NetworkDeviceDiscoveryImpl::processMonitorPingResult(PingReply *reply, Netw
void NetworkDeviceDiscoveryImpl::watchPingReply(PingReply *reply)
{
connect(reply, &PingReply::finished, this, [=](){
// Search cache for mac address and update last seen
connect(reply, &PingReply::finished, this, [this, reply](){
if (reply->error() == PingReply::ErrorNoError) {
// FIXME
// if (reply->error() == PingReply::ErrorNoError) {
// foreach (const NetworkDeviceInfo &info, m_networkInfoCache) {
// if (info.address() == reply->targetHostAddress()) {
// // Found info for this ip, update the cache
// MacAddress macAddress(info.macAddress());
// if (!macAddress.isNull() && m_networkInfoCache.contains(macAddress)) {
// m_lastSeen[macAddress] = QDateTime::currentDateTime();
// saveNetworkDeviceCache(m_networkInfoCache.value(macAddress));
// }
// }
// }
// }
int index = m_networkInfoCache.indexFromHostAddress(reply->targetHostAddress());
if (index < 0)
return;
// Update any monitor
// foreach (NetworkDeviceMonitorImpl *monitor, m_monitors.values()) {
// if (monitor->networkDeviceInfo().address() == reply->targetHostAddress()) {
// processMonitorPingResult(reply, monitor);
// }
// }
m_lastSeen[reply->targetHostAddress()] = QDateTime::currentDateTime();
saveNetworkDeviceCache(m_networkInfoCache.at(index));
}
// Update any relevant monitor
foreach (NetworkDeviceMonitorImpl *monitor, m_monitors.keys()) {
if ((monitor->monitorMode() == NetworkDeviceInfo::MonitorModeIp && reply->targetHostAddress() == monitor->address()) ||
(monitor->monitorMode() == NetworkDeviceInfo::MonitorModeHostName && reply->hostName() == monitor->hostName()) ||
(monitor->monitorMode() == NetworkDeviceInfo::MonitorModeMac && reply->targetHostAddress() == monitor->networkDeviceInfo().address())) {
processMonitorPingResult(reply, monitor);
}
}
});
}
@ -562,7 +551,7 @@ void NetworkDeviceDiscoveryImpl::loadNetworkDeviceCache()
qCInfo(dcNetworkDeviceDiscovery()) << "Loading cached network device information from" << m_cacheSettings->fileName();
m_networkInfoCache.clear();
QDateTime now = QDateTime::currentDateTime();
QDateTime currentDateTime = QDateTime::currentDateTime();
uint cacheVersion = m_cacheSettings->value("version", 0).toUInt();
@ -579,7 +568,7 @@ void NetworkDeviceDiscoveryImpl::loadNetworkDeviceCache()
QDateTime lastSeen = QDateTime::fromMSecsSinceEpoch(m_cacheSettings->value("lastSeen").toLongLong());
// Remove the info from the cache if not seen fo the last 30 days...
if (lastSeen.date().addDays(m_cacheCleanupPeriod) < now.date()) {
if (lastSeen.date().addDays(m_cacheCleanupPeriod) < currentDateTime.date()) {
qCDebug(dcNetworkDeviceDiscovery()) << "Removing network device cache entry since it did not show up within the last" << m_cacheCleanupPeriod << "days" << address.toString();
m_cacheSettings->remove("");
m_cacheSettings->endGroup(); // mac address
@ -624,21 +613,6 @@ void NetworkDeviceDiscoveryImpl::loadNetworkDeviceCache()
m_lastCacheHousekeeping = QDateTime::currentDateTime();
}
void NetworkDeviceDiscoveryImpl::removeFromNetworkDeviceCache(const MacAddress &macAddress)
{
if (macAddress.isNull())
return;
// m_networkInfoCache.remove(macAddress);
// m_lastSeen.remove(macAddress);
// m_cacheSettings->beginGroup("NetworkDeviceInfos");
// m_cacheSettings->beginGroup(macAddress.toString());
// m_cacheSettings->remove("");
// m_cacheSettings->endGroup(); // mac address
// m_cacheSettings->endGroup(); // NetworkDeviceInfos
// m_cacheSettings->sync();
}
void NetworkDeviceDiscoveryImpl::removeFromNetworkDeviceCache(const QHostAddress &address)
{
if (address.isNull())
@ -707,17 +681,23 @@ void NetworkDeviceDiscoveryImpl::updateCache(const NetworkDeviceInfo &deviceInfo
void NetworkDeviceDiscoveryImpl::evaluateMonitor(NetworkDeviceMonitorImpl *monitor)
{
// if (!monitor->networkDeviceInfo().isValid())
// return;
if (monitor->currentPingReply())
if (monitor->currentPingReply()) {
qCDebug(dcNetworkDeviceDiscovery()) << "Monitor has still a ping reply pending:" << monitor;
return;
}
qCDebug(dcNetworkDeviceDiscovery()) << "Evaluate monitor" << monitor;
// Start action if we have not seen the device for gracePeriod seconds
QDateTime currentDateTime = QDateTime::currentDateTime();
bool requiresRefresh = false;
if (!monitor->networkDeviceInfo().isValid()) {
qCDebug(dcNetworkDeviceDiscovery()) << "Network device info not valid for" << monitor;
requiresRefresh = true;
}
if (monitor->lastSeen().isNull()) {
qCDebug(dcNetworkDeviceDiscovery()) << monitor << "requires refresh. Not seen since application start.";
requiresRefresh = true;
@ -744,57 +724,85 @@ void NetworkDeviceDiscoveryImpl::evaluateMonitor(NetworkDeviceMonitorImpl *monit
if (!requiresRefresh)
return;
// Try to ping first
qCDebug(dcNetworkDeviceDiscovery()) << monitor << "try to ping" << monitor->networkDeviceInfo().address().toString();
PingReply *reply = m_ping->ping(monitor->networkDeviceInfo().address(), monitor->pingRetries());
// Try to ping first.
// IMPORTANT: use the ping methods from this object, so the result will automatically
// be evaluated for the monitors and cache
PingReply *reply = nullptr;
if (monitor->monitorMode() == NetworkDeviceInfo::MonitorModeHostName) {
qCDebug(dcNetworkDeviceDiscovery()) << monitor << "try to ping" << monitor->hostName();
reply = ping(monitor->hostName(), monitor->pingRetries());
} else {
qCDebug(dcNetworkDeviceDiscovery()) << monitor << "try to ping" << monitor->networkDeviceInfo().address().toString();
reply = ping(monitor->networkDeviceInfo().address(), monitor->pingRetries());
}
monitor->setCurrentPingReply(reply);
monitor->setLastConnectionAttempt(currentDateTime);
connect(reply, &PingReply::retry, monitor, [=](PingReply::Error error, uint retryCount){
connect(reply, &PingReply::retry, monitor, [monitor](PingReply::Error error, uint retryCount){
Q_UNUSED(error)
Q_UNUSED(retryCount)
monitor->setLastConnectionAttempt(QDateTime::currentDateTime());
});
connect(reply, &PingReply::destroyed, monitor, [=](){
monitor->setCurrentPingReply(nullptr);
connect(reply, &PingReply::destroyed, monitor, [monitor, reply](){
if (monitor->currentPingReply() == reply) {
monitor->setCurrentPingReply(nullptr);
}
});
connect(reply, &PingReply::finished, monitor, [=](){
processMonitorPingResult(reply, monitor);
connect(reply, &PingReply::finished, monitor, [monitor, reply](){
if (monitor->currentPingReply() == reply) {
monitor->setCurrentPingReply(nullptr);
}
});
}
void NetworkDeviceDiscoveryImpl::processArpTraffic(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress)
{
QDateTime now = QDateTime::currentDateTime();
// m_lastSeen[macAddress] = now;
QDateTime currentDateTime = QDateTime::currentDateTime();
m_lastSeen[address] = currentDateTime;
// FIXME
// Update monitors and cache
for (int i = 0; i < m_networkInfoCache.count(); i++) {
switch (m_networkInfoCache.at(i).monitorMode()) {
case NetworkDeviceInfo::MonitorModeMac:
if (m_networkInfoCache.at(i).macAddressInfos().hasMacAddress(macAddress) &&
m_networkInfoCache.at(i).address() != address) {
// if (m_networkInfoCache.hasMacAddress(macAddress)) {
// if (m_networkInfoCache.value(macAddress).address() != address) {
// m_networkInfoCache[macAddress].setAddress(address);
// saveNetworkDeviceCache(m_networkInfoCache.value(macAddress));
// }
// }
QHostAddress oldAddress = m_networkInfoCache.at(i).address();
qCDebug(dcNetworkDeviceDiscovery()) << "Host" << macAddress.toString() << "changed the IP address from"
<< oldAddress.toString()
<< "-->"
<< address.toString();
// Update the monitors
// NetworkDeviceMonitorImpl *monitor = m_monitors.value(macAddress);
// if (monitor) {
// monitor->setLastSeen(now);
// if (monitor->networkDeviceInfo().address() != address) {
// NetworkDeviceInfo info = monitor->networkDeviceInfo();
// info.setAddress(address);
// monitor->setNetworkDeviceInfo(info);
// qCDebug(dcNetworkDeviceDiscovery()) << "NetworkDeviceMonitor" << monitor << "ip address changed";
// emit monitor->networkDeviceInfoChanged(monitor->networkDeviceInfo());
// }
removeFromNetworkDeviceCache(oldAddress);
// if (monitor->networkDeviceInfo().isComplete()) {
// monitor->setReachable(true);
// }
// }
NetworkDeviceInfo info = m_networkInfoCache.at(i);
info.setAddress(address);
m_networkInfoCache[i] = info;
m_networkInfoCache.sortNetworkDevices();
saveNetworkDeviceCache(info);
foreach (NetworkDeviceMonitorImpl *monitor, m_monitors.keys()) {
if (monitor->macAddress() == macAddress) {
monitor->setNetworkDeviceInfo(info);
monitor->setLastSeen(currentDateTime);
monitor->setReachable(true);
break;
}
}
break;
}
break;
case NetworkDeviceInfo::MonitorModeHostName:
case NetworkDeviceInfo::MonitorModeIp:
saveNetworkDeviceCache(m_networkInfoCache.at(i));
break;
}
}
// Check if we have currently reply running
if (!m_currentDiscoveryReply)
@ -910,6 +918,7 @@ void NetworkDeviceDiscoveryImpl::cleanupPluginMonitor(NetworkDeviceMonitorImpl *
foreach (NetworkDeviceMonitorImpl *internalMonitor, m_monitors.keys()) {
if (m_monitors.value(internalMonitor).contains(pluginMonitor)) {
m_monitors[internalMonitor].removeAll(pluginMonitor);
pluginMonitor->deleteLater();
if (m_monitors.value(internalMonitor).isEmpty()) {
qCDebug(dcNetworkDeviceDiscovery()) << "No monitor registered for this network device any more. Unregister internal monitor" << internalMonitor;
@ -952,7 +961,7 @@ void NetworkDeviceDiscoveryImpl::evaluateMonitors()
evaluateMonitor(monitor);
// Check if there is any monitor which has not be seen since
if (!monitor->reachable() /*&& monitor->lastConnectionAttempt().isValid()*/ && longerAgoThan(monitor->lastSeen(), m_monitorInterval)) {
if (!monitor->reachable() && monitor->lastConnectionAttempt().isValid() && longerAgoThan(monitor->lastSeen(), m_monitorInterval)) {
monitorRequiresRediscovery = true;
}
}
@ -967,7 +976,7 @@ void NetworkDeviceDiscoveryImpl::evaluateMonitors()
// // Do some cache housekeeping if required
// if (m_lastCacheHousekeeping.addDays(1) < QDateTime::currentDateTime()) {
// qCInfo(dcNetworkDeviceDiscovery()) << "Starting cache housekeeping since it is more than one day since the last clanup...";
// QDateTime now = QDateTime::currentDateTime();
// QDateTime currentDateTime = QDateTime::currentDateTime();
// foreach (const MacAddress &mac, m_lastSeen.keys()) {
// // Remove the info from the cache if not seen fo the last 30 days...
// if (m_lastSeen.value(mac).date().addDays(m_cacheCleanupPeriod) < QDateTime::currentDateTime().date()) {
@ -975,7 +984,7 @@ void NetworkDeviceDiscoveryImpl::evaluateMonitors()
// removeFromNetworkDeviceCache(mac);
// }
// }
// m_lastCacheHousekeeping = now;
// m_lastCacheHousekeeping = currentDateTime;
// }
}

View File

@ -71,6 +71,7 @@ public:
void unregisterMonitor(NetworkDeviceMonitor *networkDeviceMonitor) override;
PingReply *ping(const QHostAddress &address, uint retries = 3) override;
PingReply *ping(const QString &hostName, uint retries = 3) override;
PingReply *ping(const QHostAddress &address, bool lookupHost, uint retries = 3);
MacAddressDatabaseReply *lookupMacAddress(const QString &macAddress) override;
@ -138,6 +139,8 @@ private:
void processArpTraffic(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress);
void testPingMonitor(NetworkDeviceMonitorImpl *monitor);
// Time helpers
bool longerAgoThan(const QDateTime &dateTime, uint minutes);
QDateTime convertMinuteBased(const QDateTime &dateTime = QDateTime());

View File

@ -61,6 +61,7 @@ public:
virtual void unregisterMonitor(NetworkDeviceMonitor *networkDeviceMonitor) = 0;
virtual PingReply *ping(const QHostAddress &address, uint retries = 3) = 0;
virtual PingReply *ping(const QString &hostName, uint retries = 3) = 0;
virtual MacAddressDatabaseReply *lookupMacAddress(const QString &macAddress) = 0;
virtual MacAddressDatabaseReply *lookupMacAddress(const MacAddress &macAddress) = 0;

View File

@ -56,6 +56,17 @@ int NetworkDeviceInfos::indexFromHostAddress(const QHostAddress &address)
return -1;
}
int NetworkDeviceInfos::indexFromHostName(const QString &hostName)
{
for (int i = 0; i < this->size(); i++) {
if (at(i).hostName() == hostName) {
return i;
}
}
return -1;
}
QList<int> NetworkDeviceInfos::indexFromMacAddress(const QString &macAddress)
{
return indexFromMacAddress(MacAddress(macAddress));

View File

@ -44,6 +44,7 @@ public:
NetworkDeviceInfos(const QVector<NetworkDeviceInfo> &other);
int indexFromHostAddress(const QHostAddress &address);
int indexFromHostName(const QString &hostName);
QList<int> indexFromMacAddress(const QString &macAddress);
QList<int> indexFromMacAddress(const MacAddress &macAddress);

View File

@ -141,6 +141,18 @@ PingReply *Ping::ping(const QHostAddress &hostAddress, bool lookupHost, uint ret
return reply;
}
PingReply *Ping::ping(const QString &hostName, uint retries)
{
// Make first the host lookup, then the ping the first address and save it in the reply
PingReply *reply = createReply(hostName);
reply->m_retries = retries;
// Note: due to a Qt bug < 5.9 we need to use old SLOT style and cannot make use of lambda here
int lookupId = QHostInfo::lookupHost(hostName, this, SLOT(onHostLookupFinished(QHostInfo)));
m_pendingHostAddressLookups.insert(lookupId, reply);
return reply;
}
void Ping::sendNextReply()
{
if (m_queueTimer->isActive())
@ -150,7 +162,7 @@ void Ping::sendNextReply()
return;
m_currentReply = m_replyQueue.dequeue();
qCDebug(dcPing()) << "Send next reply " << m_currentReply->targetHostAddress().toString() << QString("0x%1").arg(m_currentReply->requestId(), 4, 16, QChar('0')) << ", " << m_replyQueue.count() << "left in queue";
qCDebug(dcPing()).nospace().noquote() << "Send next reply " << m_currentReply->targetHostAddress().toString() << " ID: " << QString("0x%1").arg(m_currentReply->requestId(), 4, 16, QChar('0')) << ", " << m_replyQueue.count() << "left in queue";
m_queueTimer->start();
QTimer::singleShot(0, this, [=]() {
if (!m_currentReply)
@ -209,8 +221,8 @@ void Ping::performPing(PingReply *reply)
}
qCDebug(dcPingTraffic()) << "Send ICMP echo request" << reply->targetHostAddress().toString() << ICMP_PACKET_SIZE << "[Bytes]"
<< "ID:" << QString("0x%1").arg(reply->requestId(), 4, 16, QChar('0'))
<< "Sequence:" << reply->sequenceNumber();
<< "ID:" << QString("0x%1").arg(reply->requestId(), 4, 16, QChar('0'))
<< "Sequence:" << reply->sequenceNumber();
// Send packet to the target ip
int bytesSent = sendto(m_socketDescriptor, &requestPacket, sizeof(requestPacket), 0, (struct sockaddr *)&pingAddress, sizeof(pingAddress));
@ -329,14 +341,43 @@ PingReply *Ping::createReply(const QHostAddress &hostAddress)
return reply;
}
PingReply *Ping::createReply(const QString &hostName)
{
PingReply *reply = new PingReply(this);
reply->m_hostName = hostName;
connect(reply, &PingReply::timeout, this, [=](){
// Note: this is not the ICMP timeout, here we actually got nothing from anybody...
finishReply(reply, PingReply::ErrorTimeout);
});
connect(reply, &PingReply::aborted, this, [=](){
finishReply(reply, PingReply::ErrorAborted);
});
connect(reply, &PingReply::finished, this, [=](){
cleanUpReply(reply);
reply->deleteLater();
});
// Just in case the reply get's deleted before beeing able to finish ...
connect(reply, &PingReply::destroyed, this, [this, reply](){
cleanUpReply(reply);
});
return reply;
}
void Ping::finishReply(PingReply *reply, PingReply::Error error)
{
// Check if we should retry
if (reply->m_retryCount >= reply->m_retries ||
error == PingReply::ErrorNoError ||
error == PingReply::ErrorAborted ||
error == PingReply::ErrorInvalidHostAddress ||
error == PingReply::ErrorPermissionDenied) {
error == PingReply::ErrorNoError ||
error == PingReply::ErrorAborted ||
error == PingReply::ErrorInvalidHostAddress ||
error == PingReply::ErrorPermissionDenied||
error == PingReply::ErrorHostNameLookupFailed ||
error == PingReply::ErrorHostNameNotFound) {
// No retry, we are done
reply->m_error = error;
reply->m_timer->stop();
@ -374,11 +415,11 @@ void Ping::cleanUpReply(PingReply *reply)
m_pendingReplies.remove(reply->requestId());
m_replyQueue.removeAll(reply);
if (m_pendingHostLookups.values().contains(reply)) {
if (m_pendingHostNameLookups.values().contains(reply)) {
// Abort any pending host lookups, the reply has been finished
int lookupId = m_pendingHostLookups.key(reply);
int lookupId = m_pendingHostNameLookups.key(reply);
QHostInfo::abortHostLookup(lookupId);
m_pendingHostLookups.remove(lookupId);
m_pendingHostNameLookups.remove(lookupId);
}
}
@ -404,19 +445,19 @@ void Ping::onSocketReadyRead(int socketDescriptor)
QHostAddress destinationAddress(qFromBigEndian(ipHeader->daddr));
qCDebug(dcPingTraffic()) << "IP header: Lenght" << ipHeaderLength
<< "Sender:" << senderAddress.toString()
<< "Destination:" << destinationAddress.toString()
<< "Size:" << htons(ipHeader->tot_len) << "B"
<< "TTL" << ipHeader->ttl;
<< "Sender:" << senderAddress.toString()
<< "Destination:" << destinationAddress.toString()
<< "Size:" << htons(ipHeader->tot_len) << "B"
<< "TTL" << ipHeader->ttl;
struct icmp *responsePacket = reinterpret_cast<struct icmp *>(receiveBuffer + ipHeaderLength);
quint16 icmpId = htons(responsePacket->icmp_id);
quint16 icmpSequnceNumber = htons(responsePacket->icmp_seq);
qCDebug(dcPingTraffic()) << "ICMP packt (Size:" << icmpPacketSize << "Bytes):"
<< "Type" << responsePacket->icmp_type
<< "Code:" << responsePacket->icmp_code
<< "ID:" << QString("0x%1").arg(icmpId, 4, 16, QChar('0'))
<< "Sequence:" << icmpSequnceNumber;
<< "Type" << responsePacket->icmp_type
<< "Code:" << responsePacket->icmp_code
<< "ID:" << QString("0x%1").arg(icmpId, 4, 16, QChar('0'))
<< "Sequence:" << icmpSequnceNumber;
if (responsePacket->icmp_type == ICMP_ECHOREPLY) {
@ -447,14 +488,14 @@ void Ping::onSocketReadyRead(int socketDescriptor)
reply->m_duration = qRound((receiveTimeValue.tv_sec * 1000 + (double)receiveTimeValue.tv_usec / 1000) * 100) / 100.0;
qCDebug(dcPing()) << "Received ICMP response" << reply->targetHostAddress().toString() << ICMP_PACKET_SIZE << "[Bytes]"
<< "ID:" << QString("0x%1").arg(icmpId, 4, 16, QChar('0'))
<< "Sequence:" << icmpSequnceNumber
<< "Time:" << reply->duration() << "[ms]";
<< "ID:" << QString("0x%1").arg(icmpId, 4, 16, QChar('0'))
<< "Sequence:" << icmpSequnceNumber
<< "Time:" << reply->duration() << "[ms]";
if (reply->doHostLookup()) {
// Note: due to a Qt bug < 5.9 we need to use old SLOT style and cannot make use of lambda here
int lookupId = QHostInfo::lookupHost(senderAddress.toString(), this, SLOT(onHostLookupFinished(QHostInfo)));
m_pendingHostLookups.insert(lookupId, reply);
m_pendingHostNameLookups.insert(lookupId, reply);
// Finish the reply after the host lookup has finished
} else {
finishReply(reply, PingReply::ErrorNoError);
@ -472,20 +513,20 @@ void Ping::onSocketReadyRead(int socketDescriptor)
QHostAddress nestedDestinationAddress(qFromBigEndian(nestedIpHeader->daddr));
qCDebug(dcPingTraffic()) << "++ IP header: Lenght" << nestedIpHeaderLength
<< "Sender:" << nestedSenderAddress.toString()
<< "Destination:" << nestedDestinationAddress.toString()
<< "Size:" << htons(nestedIpHeader->tot_len) << "B"
<< "TTL" << ipHeader->ttl;
<< "Sender:" << nestedSenderAddress.toString()
<< "Destination:" << nestedDestinationAddress.toString()
<< "Size:" << htons(nestedIpHeader->tot_len) << "B"
<< "TTL" << ipHeader->ttl;
struct icmp *nestedResponsePacket = reinterpret_cast<struct icmp *>(receiveBuffer + messageOffset + nestedIpHeaderLength);
icmpId = htons(nestedResponsePacket->icmp_id);
icmpSequnceNumber = htons(nestedResponsePacket->icmp_seq);
qCDebug(dcPingTraffic()) << "++ ICMP packt (Size:" << nestedIcmpPacketSize << "Bytes):"
<< "Type" << nestedResponsePacket->icmp_type
<< "Code:" << nestedResponsePacket->icmp_code
<< "ID:" << QString("0x%1").arg(icmpId, 4, 16, QChar('0'))
<< "Sequence:" << icmpSequnceNumber;
<< "Type" << nestedResponsePacket->icmp_type
<< "Code:" << nestedResponsePacket->icmp_code
<< "ID:" << QString("0x%1").arg(icmpId, 4, 16, QChar('0'))
<< "Sequence:" << icmpSequnceNumber;
qCDebug(dcPing()) << "ICMP destination unreachable" << nestedDestinationAddress.toString()
<< "Code:" << nestedResponsePacket->icmp_code
@ -495,9 +536,9 @@ void Ping::onSocketReadyRead(int socketDescriptor)
PingReply *reply = m_pendingReplies.value(icmpId);
if (!reply) {
qCDebug(dcPingTraffic()) << "No pending reply for ping echo response unreachable with ID"
<< QString("0x%1").arg(icmpId, 4, 16, QChar('0'))
<< "Sequence:" << icmpSequnceNumber
<< "from" << nestedSenderAddress.toString() << "to" << nestedDestinationAddress.toString();
<< QString("0x%1").arg(icmpId, 4, 16, QChar('0'))
<< "Sequence:" << icmpSequnceNumber
<< "from" << nestedSenderAddress.toString() << "to" << nestedDestinationAddress.toString();
return;
}
@ -508,20 +549,74 @@ void Ping::onSocketReadyRead(int socketDescriptor)
void Ping::onHostLookupFinished(const QHostInfo &info)
{
PingReply *reply = m_pendingHostLookups.value(info.lookupId());
if (!reply) {
qCWarning(dcPing()) << "Could not find reply after host lookup.";
return;
}
if (m_pendingHostNameLookups.contains(info.lookupId())) {
if (info.error() != QHostInfo::NoError) {
qCWarning(dcPing()) << "Failed to look up hostname after successfull ping" << reply->targetHostAddress().toString() << info.error();
} else {
qCDebug(dcPing()) << "Looked up hostname after successfull ping" << reply->targetHostAddress().toString() << info.hostName();
if (info.hostName() != reply->targetHostAddress().toString()) {
reply->m_hostName = info.hostName();
PingReply *reply = m_pendingHostNameLookups.take(info.lookupId());
if (!reply) {
qCWarning(dcPing()) << "Could not find ping reply after finished host lookup" << info.hostName() << info.addresses() << info.errorString();
return;
}
}
finishReply(reply, PingReply::ErrorNoError);
PingReply::Error pingError = PingReply::ErrorNoError;
switch(info.error()) {
case QHostInfo::NoError:
qCDebug(dcPing()) << "Looked up hostname after successfull ping" << reply->targetHostAddress().toString() << info.hostName();
if (info.hostName() != reply->targetHostAddress().toString())
reply->m_hostName = info.hostName();
break;
case QHostInfo::HostNotFound:
qCWarning(dcPing()) << "Unable to find hostname:" << reply->hostName() << info.errorString();
pingError = PingReply::ErrorHostNameNotFound;
break;
default:
qCWarning(dcPing()) << "Failed to lookup hostname" << (reply->targetHostAddress().isNull() ? reply->hostName() : reply->targetHostAddress().toString()) << info.errorString();
pingError = PingReply::ErrorHostNameLookupFailed;
break;
}
finishReply(reply, pingError);
} else if (m_pendingHostAddressLookups.contains(info.lookupId())) {
PingReply *reply = m_pendingHostAddressLookups.take(info.lookupId());
if (!reply) {
qCWarning(dcPing()) << "Could not find ping reply after finished host lookup" << info.hostName() << info.addresses() << info.errorString();
return;
}
PingReply::Error pingError = PingReply::ErrorNoError;
switch(info.error()) {
case QHostInfo::NoError:
qCDebug(dcPing()) << "Looked up address for hostname finished successfully" << info.hostName() << info.addresses();
if (info.addresses().isEmpty()) {
qCWarning(dcPing()) << "Looked up address finished succesfully but there are no addresses available for" << info.hostName();
pingError = PingReply::ErrorHostNameNotFound;
} else {
reply->m_targetHostAddress = info.addresses().first();
reply->m_networkInterface = NetworkUtils::getInterfaceForHostaddress(reply->targetHostAddress());
pingError = PingReply::ErrorNoError;
}
break;
case QHostInfo::HostNotFound:
qCDebug(dcPing()) << "Unable to find hostname:" << reply->hostName() << info.errorString();
pingError = PingReply::ErrorHostNameNotFound;
break;
default:
qCWarning(dcPing()) << "Failed to lookup hostname" << (reply->targetHostAddress().isNull() ? reply->hostName() : reply->targetHostAddress().toString()) << info.errorString();
pingError = PingReply::ErrorHostNameLookupFailed;
break;
}
if (pingError != PingReply::ErrorNoError) {
// Host lookup failed, we cannot continue, nothing to ping
finishReply(reply, pingError);
} else {
// Ping the resolved host address
m_replyQueue.enqueue(reply);
sendNextReply();
}
} else {
qCWarning(dcPing()) << "Host name lookup finished but we have no ping reply for it. Ignoring looked up information" << info.hostName() << info.addresses() << info.error();
}
}

View File

@ -67,6 +67,8 @@ public:
PingReply *ping(const QHostAddress &hostAddress, uint retries = 3);
PingReply *ping(const QHostAddress &hostAddress, bool lookupHost, uint retries = 3);
PingReply *ping(const QString &hostName, uint retries = 3);
signals:
void availableChanged(bool available);
@ -91,7 +93,8 @@ private:
QTimer *m_queueTimer = nullptr;
PingReply *m_currentReply = nullptr;
void sendNextReply();
QHash<int, PingReply *> m_pendingHostLookups;
QHash<int, PingReply *> m_pendingHostNameLookups;
QHash<int, PingReply *> m_pendingHostAddressLookups;
//Error performPing(const QString &address);
void performPing(PingReply *reply);
@ -104,13 +107,13 @@ private:
quint16 calculateRequestId();
PingReply *createReply(const QHostAddress &hostAddress);
PingReply *createReply(const QString &hostName);
void finishReply(PingReply *reply, PingReply::Error error);
void cleanUpReply(PingReply *reply);
private slots:
void onSocketReadyRead(int socketDescriptor);
void onHostLookupFinished(const QHostInfo &info);
};
#endif // PING_H

View File

@ -59,7 +59,9 @@ public:
ErrorSocketError,
ErrorTimeout,
ErrorHostUnreachable,
ErrorInvalidHostAddress
ErrorInvalidHostAddress,
ErrorHostNameLookupFailed,
ErrorHostNameNotFound
};
Q_ENUM(Error)