From 04c9a3a2eb1d3722649aca89c648feb7da30f1d0 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Tue, 3 Jul 2018 22:11:21 -0700 Subject: [PATCH] Implement Wake-on-LAN --- app/backend/computermanager.cpp | 92 +++++++++++++++++++++++++++++++++ app/backend/computermanager.h | 35 +++---------- 2 files changed, 99 insertions(+), 28 deletions(-) diff --git a/app/backend/computermanager.cpp b/app/backend/computermanager.cpp index b8549b9f..83fbbaf0 100644 --- a/app/backend/computermanager.cpp +++ b/app/backend/computermanager.cpp @@ -2,6 +2,8 @@ #include "nvhttp.h" #include +#include +#include #define SER_HOSTS "hosts" #define SER_NAME "hostname" @@ -112,6 +114,96 @@ NvComputer::NvComputer(QString address, QString serverInfo) this->state = NvComputer::CS_ONLINE; } +bool NvComputer::wake() +{ + if (state == NvComputer::CS_ONLINE) { + qWarning() << name << " is already online"; + return true; + } + + if (macAddress.isNull()) { + qWarning() << name << " has no MAC address stored"; + return false; + } + + const quint16 WOL_PORTS[] = { + 7, 9, // Standard WOL ports + 47998, 47999, 48000, // Ports opened by GFE + }; + + // Create the WoL payload + QByteArray wolPayload; + wolPayload.append(QByteArray::fromHex("FFFFFFFFFFFF")); + for (int i = 0; i < 16; i++) { + wolPayload.append(macAddress); + } + Q_ASSERT(wolPayload.count() == 102); + + // Add the addresses that we know this host to be + // and broadcast addresses for this link just in + // case the host has timed out in ARP entries. + QVector addressList = uniqueAddresses(); + addressList.append("255.255.255.255"); + + // Try all unique address strings or host names + bool success = false; + for (QString& addressString : addressList) { + QHostInfo hostInfo = QHostInfo::fromName(addressString); + + // Try all IP addresses that this string resolves to + for (QHostAddress& address : hostInfo.addresses()) { + QUdpSocket sock; + + // Bind to any address on the correct protocol + if (sock.bind(address.protocol() == QUdpSocket::IPv4Protocol ? + QHostAddress::AnyIPv4 : QHostAddress::AnyIPv6)) { + + // Send to all ports + for (quint16 port : WOL_PORTS) { + if (sock.writeDatagram(wolPayload, address, port)) { + qDebug() << "Send WoL packet to " << name << " via " << address.toString() << ":" << port; + success = true; + } + } + } + } + } + + return success; +} + +QVector NvComputer::uniqueAddresses() +{ + QVector uniqueAddressList; + + // Start with addresses correctly ordered + uniqueAddressList.append(activeAddress); + uniqueAddressList.append(localAddress); + uniqueAddressList.append(remoteAddress); + uniqueAddressList.append(manualAddress); + + // Prune duplicates (always giving precedence to the first) + for (int i = 0; i < uniqueAddressList.count(); i++) { + if (uniqueAddressList[i].isEmpty() || uniqueAddressList[i].isNull()) { + uniqueAddressList.remove(i); + i--; + continue; + } + for (int j = i + 1; j < uniqueAddressList.count(); j++) { + if (uniqueAddressList[i] == uniqueAddressList[j]) { + // Always remove the later occurrence + uniqueAddressList.remove(j); + j--; + } + } + } + + // We must have at least 1 address + Q_ASSERT(uniqueAddressList.count() != 0); + + return uniqueAddressList; +} + bool NvComputer::update(NvComputer& that) { bool changed = false; diff --git a/app/backend/computermanager.h b/app/backend/computermanager.h index e03700b3..4343658f 100644 --- a/app/backend/computermanager.h +++ b/app/backend/computermanager.h @@ -21,6 +21,12 @@ public: bool update(NvComputer& that); + bool + wake(); + + QVector + uniqueAddresses(); + void serialize(QSettings& settings); @@ -131,36 +137,9 @@ private: // Always fetch the applist the first time int pollsSinceLastAppListFetch = POLLS_PER_APPLIST_FETCH; while (!isInterruptionRequested()) { - QVector uniqueAddressList; - - // Start with addresses correctly ordered - uniqueAddressList.append(m_Computer->activeAddress); - uniqueAddressList.append(m_Computer->localAddress); - uniqueAddressList.append(m_Computer->remoteAddress); - uniqueAddressList.append(m_Computer->manualAddress); - - // Prune duplicates (always giving precedence to the first) - for (int i = 0; i < uniqueAddressList.count(); i++) { - if (uniqueAddressList[i].isEmpty() || uniqueAddressList[i].isNull()) { - uniqueAddressList.remove(i); - i--; - continue; - } - for (int j = i + 1; j < uniqueAddressList.count(); j++) { - if (uniqueAddressList[i] == uniqueAddressList[j]) { - // Always remove the later occurrence - uniqueAddressList.remove(j); - j--; - } - } - } - - // We must have at least 1 address for this host - Q_ASSERT(uniqueAddressList.count() != 0); - bool stateChanged = false; for (int i = 0; i < TRIES_BEFORE_OFFLINING; i++) { - for (auto& address : uniqueAddressList) { + for (auto& address : m_Computer->uniqueAddresses()) { if (isInterruptionRequested()) { return; }