Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/app.pro
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ SOURCES += \
cli/startstream.cpp \
settings/compatfetcher.cpp \
settings/mappingfetcher.cpp \
settings/wolproxysettings.cpp \
settings/streamingpreferences.cpp \
streaming/input/abstouch.cpp \
streaming/input/gamepad.cpp \
Expand Down Expand Up @@ -213,6 +214,7 @@ HEADERS += \
cli/pair.h \
settings/compatfetcher.h \
settings/mappingfetcher.h \
settings/wolproxysettings.h \
utils.h \
backend/computerseeker.h \
backend/identitymanager.h \
Expand Down
6 changes: 6 additions & 0 deletions app/backend/computermanager.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "computermanager.h"
#include "settings/wolproxysettings.h"
#include "boxartmanager.h"
#include "nvhttp.h"
#include "nvpairingmanager.h"
Expand Down Expand Up @@ -200,6 +201,11 @@ ComputerManager::ComputerManager(StreamingPreferences* prefs)
connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &ComputerManager::handleAboutToQuit);
}

QList<QHostAddress> ComputerManager::getWolProxyAddresses() const
{
return WolProxySettings::parseAddressList(m_Prefs->wolProxyAddresses);
}

ComputerManager::~ComputerManager()
{
// Stop the delayed flush thread before acquiring the lock in write mode
Expand Down
2 changes: 2 additions & 0 deletions app/backend/computermanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@ class ComputerManager : public QObject

QVector<NvComputer*> getComputers();

QList<QHostAddress> getWolProxyAddresses() const;

// computer is deleted inside this call
void deleteHost(NvComputer* computer);

Expand Down
2 changes: 1 addition & 1 deletion app/backend/computerseeker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ ComputerSeeker::ComputerSeeker(ComputerManager *manager, QString computerName, Q
// If we know this computer, send a WOL packet to wake it up in case it is asleep.
NvComputer* matchingComputer = findMatchingComputer();
if (matchingComputer) {
matchingComputer->wake();
matchingComputer->wake(m_ComputerManager->getWolProxyAddresses());
}

m_TimeoutTimer->setSingleShot(true);
Expand Down
50 changes: 36 additions & 14 deletions app/backend/nvcomputer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <QHostInfo>
#include <QNetworkInterface>
#include <QNetworkProxy>
#include <QSet>

#define SER_NAME "hostname"
#define SER_UUID "uuid"
Expand Down Expand Up @@ -213,7 +214,7 @@ NvComputer::NvComputer(NvHTTP& http, QString serverInfo)
this->isSupportedServerVersion = CompatFetcher::isGfeVersionSupported(this->gfeVersion);
}

bool NvComputer::wake() const
bool NvComputer::wake(const QList<QHostAddress>& wolProxyAddresses) const
{
QByteArray wolPayload;

Expand Down Expand Up @@ -249,6 +250,34 @@ bool NvComputer::wake() const
47998, 47999, 48000, 48002, 48010, // Ports opened by GFE
};

bool success = false;
QSet<QString> sentEndpoints;

auto sendWolPacket = [&](QUdpSocket& sock, const QHostAddress& address, quint16 port, bool isProxy) {
const qint64 bytesWritten = sock.writeDatagram(wolPayload, address, port);
if (bytesWritten == wolPayload.size()) {
qInfo().nospace().noquote()
<< "Sent WoL packet to " << name
<< (isProxy ? " via proxy " : " via ")
<< address.toString() << ":" << port;
sentEndpoints.insert(QStringLiteral("[%1]:%2").arg(address.toString()).arg(port));
success = true;
return;
}

qWarning().nospace().noquote()
<< "Failed to send WoL packet to "
<< address.toString() << ":" << port
<< ": " << sock.errorString();
};

// Send to configured proxies in the order provided. Proxy addresses are
// already validated and deduplicated by WolProxySettings.
for (const QHostAddress& proxyAddress : wolProxyAddresses) {
QUdpSocket proxySocket;
sendWolPacket(proxySocket, proxyAddress, 9, true);
}

// 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.
Expand Down Expand Up @@ -290,7 +319,6 @@ bool NvComputer::wake() const
}

// Try all unique address strings or host names
bool success = false;
for (auto i = addressMap.constBegin(); i != addressMap.constEnd(); i++) {
QHostAddress literalAddress;
QList<QHostAddress> addressList;
Expand All @@ -316,12 +344,9 @@ bool NvComputer::wake() const

// Send to all static ports
for (quint16 port : STATIC_WOL_PORTS) {
if (sock.writeDatagram(wolPayload, address, port)) {
qInfo().nospace().noquote() << "Sent WoL packet to " << name << " via " << address.toString() << ":" << port;
success = true;
}
else {
qWarning() << "Send failed:" << sock.error();
const QString endpoint = QStringLiteral("[%1]:%2").arg(address.toString()).arg(port);
if (!sentEndpoints.contains(endpoint)) {
sendWolPacket(sock, address, port, false);
}
}

Expand All @@ -340,12 +365,9 @@ bool NvComputer::wake() const
for (quint16 port : DYNAMIC_WOL_PORTS) {
port = (port - 47989) + basePort;

if (sock.writeDatagram(wolPayload, address, port)) {
qInfo().nospace().noquote() << "Sent WoL packet to " << name << " via " << address.toString() << ":" << port;
success = true;
}
else {
qWarning() << "Send failed:" << sock.error();
const QString endpoint = QStringLiteral("[%1]:%2").arg(address.toString()).arg(port);
if (!sentEndpoints.contains(endpoint)) {
sendWolPacket(sock, address, port, false);
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion app/backend/nvcomputer.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <QReadWriteLock>
#include <QSettings>
#include <QRunnable>
#include <QList>

class CopySafeReadWriteLock : public QReadWriteLock
{
Expand Down Expand Up @@ -51,7 +52,7 @@ class NvComputer
update(const NvComputer& that);

bool
wake() const;
wake(const QList<QHostAddress>& wolProxyAddresses = {}) const;

enum ReachabilityType
{
Expand Down
59 changes: 59 additions & 0 deletions app/gui/SettingsView.qml
Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,65 @@ Flickable {
}
}

GroupBox {
id: wolProxySettingsGroupBox
width: (parent.width - (parent.leftPadding + parent.rightPadding))
padding: 12
title: "<font color=\"skyblue\">" + qsTr("Wake-on-LAN Proxy Settings") + "</font>"
font.pointSize: 12

Column {
anchors.fill: parent
spacing: 5

Label {
width: parent.width
text: qsTr("Proxy IP addresses")
font.pointSize: 12
wrapMode: Text.Wrap
}

Label {
width: parent.width
text: qsTr("Enter IPv4 or IPv6 addresses separated by commas. The Magic Packet is sent to each proxy on UDP port 9 before the normal Wake-on-LAN broadcast.")
font.pointSize: 9
wrapMode: Text.Wrap
}

TextField {
id: wolProxyAddressesField
width: parent.width
placeholderText: "192.168.1.10, 10.0.0.5"
selectByMouse: true
text: StreamingPreferences.wolProxyAddresses
property bool inputValid: StreamingPreferences.isValidWolProxyList(text)

onTextChanged: {
if (inputValid) {
StreamingPreferences.wolProxyAddresses = text
}
}

onEditingFinished: {
if (inputValid) {
var normalizedAddresses = StreamingPreferences.normalizeWolProxyList(text)
StreamingPreferences.wolProxyAddresses = normalizedAddresses
text = normalizedAddresses
}
}
}

Label {
width: parent.width
visible: !wolProxyAddressesField.inputValid
text: qsTr("Enter only valid IPv4 or IPv6 addresses. Hostnames, port numbers, and empty list entries are not supported.")
color: "tomato"
font.pointSize: 9
wrapMode: Text.Wrap
}
}
}

GroupBox {
id: uiSettingsGroupBox
width: (parent.width - (parent.leftPadding + parent.rightPadding))
Expand Down
13 changes: 9 additions & 4 deletions app/gui/computermodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include <QThreadPool>

#include <utility>

ComputerModel::ComputerModel(QObject* object)
: QAbstractListModel(object) {}

Expand Down Expand Up @@ -152,23 +154,26 @@ void ComputerModel::deleteComputer(int computerIndex)
class DeferredWakeHostTask : public QRunnable
{
public:
DeferredWakeHostTask(NvComputer* computer)
: m_Computer(computer) {}
DeferredWakeHostTask(NvComputer* computer, QList<QHostAddress> wolProxyAddresses)
: m_Computer(computer),
m_WolProxyAddresses(std::move(wolProxyAddresses)) {}

void run()
{
m_Computer->wake();
m_Computer->wake(m_WolProxyAddresses);
}

private:
NvComputer* m_Computer;
QList<QHostAddress> m_WolProxyAddresses;
};

void ComputerModel::wakeComputer(int computerIndex)
{
Q_ASSERT(computerIndex < m_Computers.count());

DeferredWakeHostTask* wakeTask = new DeferredWakeHostTask(m_Computers[computerIndex]);
DeferredWakeHostTask* wakeTask = new DeferredWakeHostTask(m_Computers[computerIndex],
m_ComputerManager->getWolProxyAddresses());
QThreadPool::globalInstance()->start(wakeTask);
}

Expand Down
Loading