Implement Wake-on-LAN

This commit is contained in:
Cameron Gutman 2018-07-03 22:11:21 -07:00
parent a02931f5fc
commit 04c9a3a2eb
2 changed files with 99 additions and 28 deletions

View File

@ -2,6 +2,8 @@
#include "nvhttp.h"
#include <QThread>
#include <QUdpSocket>
#include <QHostInfo>
#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<QString> 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<QString> NvComputer::uniqueAddresses()
{
QVector<QString> 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;

View File

@ -21,6 +21,12 @@ public:
bool
update(NvComputer& that);
bool
wake();
QVector<QString>
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<QString> 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;
}