Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef5bb72d5c | ||
|
|
3343ebb225 | ||
|
|
05413a554c | ||
|
|
5f015acdaa | ||
|
|
df286ef56d | ||
|
|
47d60b9f24 | ||
|
|
659d3aea23 |
+1
-1
Submodule GS-IPv6-Forwarder updated: bff3c3cf00...d2b927c7b1
+86
-8
@@ -12,6 +12,7 @@
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "relay.h"
|
||||
#include "..\version.h"
|
||||
|
||||
#pragma comment(lib, "miniupnpc.lib")
|
||||
@@ -53,9 +54,9 @@ static struct port_entry {
|
||||
{IPPROTO_UDP, 48010}
|
||||
};
|
||||
|
||||
static const int k_WolPorts[] = { 9 };
|
||||
static const int k_WolPorts[] = { 9, 47009 };
|
||||
|
||||
bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const char* myAddr, int port, bool enable, bool indefinite)
|
||||
bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const char* myAddr, int port, bool enable, bool indefinite, bool validationPass)
|
||||
{
|
||||
char intClient[16];
|
||||
char intPort[6];
|
||||
@@ -95,17 +96,42 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
||||
if (err == 714) {
|
||||
// NoSuchEntryInArray
|
||||
printf("NOT FOUND" NL);
|
||||
|
||||
if (validationPass) {
|
||||
// On validation, we found a missing entry. Convert this entry to indefinite
|
||||
// to see if it will stick.
|
||||
indefinite = true;
|
||||
}
|
||||
}
|
||||
else if (err == 606) {
|
||||
printf("UNAUTHORIZED" NL);
|
||||
|
||||
// If we're just validating, we're done. We can't know if the entry was
|
||||
// actually applied but we'll return true to avoid false errors if it was.
|
||||
if (validationPass) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (err == UPNPCOMMAND_SUCCESS) {
|
||||
// Some routers change the description, so we can't check that here
|
||||
if (!strcmp(intClient, myAddr)) {
|
||||
if (atoi(leaseDuration) == 0) {
|
||||
printf("OK (Permanent)" NL);
|
||||
printf("OK (Static)" NL);
|
||||
|
||||
// If we have an existing permanent mapping, we can just leave it alone.
|
||||
if (enable) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("OK (%s seconds remaining)" NL, leaseDuration);
|
||||
}
|
||||
|
||||
// If we're just validating, we found an entry, so we're done.
|
||||
if (validationPass) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!enable) {
|
||||
// This is our entry. Go ahead and nuke it
|
||||
printf("Deleting UPnP mapping for %s %s -> %s...", protoStr, portStr, myAddr);
|
||||
@@ -123,6 +149,11 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
||||
else {
|
||||
printf("CONFLICT: %s %s" NL, intClient, desc);
|
||||
|
||||
// If we're just validating, we found an entry, so we're done.
|
||||
if (validationPass) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Some UPnP IGDs won't let unauthenticated clients delete other conflicting port mappings
|
||||
// for security reasons, but we will give it a try anyway. If GameStream is not enabled,
|
||||
// we will leave the conflicting entry alone to avoid disturbing another PC's port forwarding
|
||||
@@ -146,6 +177,10 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
||||
}
|
||||
else {
|
||||
printf("ERROR %d (%s)" NL, err, strupnperror(err));
|
||||
|
||||
// If we get a strange error from the router, we'll assume it's some old broken IGDv1
|
||||
// device and only use indefinite lease durations to hopefully avoid confusing it.
|
||||
indefinite = true;
|
||||
}
|
||||
|
||||
// Bail if GameStream is disabled
|
||||
@@ -160,11 +195,31 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
||||
err = UPNP_AddPortMapping(
|
||||
urls->controlURL, data->first.servicetype, portStr,
|
||||
portStr, myAddr, myDesc, protoStr, nullptr, leaseDuration);
|
||||
if (err == 725 && !indefinite) { // OnlyPermanentLeasesSupported
|
||||
if (err != UPNPCOMMAND_SUCCESS && !indefinite) {
|
||||
// This may be a broken IGD that doesn't like non-static mappings. Try a static
|
||||
// mapping before finally giving up.
|
||||
err = UPNP_AddPortMapping(
|
||||
urls->controlURL, data->first.servicetype, portStr,
|
||||
portStr, myAddr, myDesc, protoStr, nullptr, "0");
|
||||
printf("PERMANENT ");
|
||||
printf("STATIC RETRY ");
|
||||
}
|
||||
else if (indefinite) {
|
||||
printf("STATIC ");
|
||||
}
|
||||
if (err == 718 && proto == IPPROTO_UDP) { // ConflictInMappingEntry
|
||||
// Some UPnP implementations incorrectly deduplicate on the internal port instead
|
||||
// of the external port, in violation of the UPnP IGD specification. Since GFE creates
|
||||
// mappings on the same internal port as us, those routers break our mappings. To
|
||||
// work around this issue, we run relays for each of the UDP ports on an alternate
|
||||
// internal port. We'll try the alternate port if we get a conflict for a UDP entry.
|
||||
// Given that these are already horribly non-spec compliant, we won't take any chances
|
||||
// and we'll use an indefinite mapping too.
|
||||
char altPortStr[6];
|
||||
snprintf(altPortStr, sizeof(altPortStr), "%d", port + RELAY_PORT_OFFSET);
|
||||
err = UPNP_AddPortMapping(
|
||||
urls->controlURL, data->first.servicetype, portStr,
|
||||
altPortStr, myAddr, myDesc, protoStr, nullptr, "0");
|
||||
printf("ALTERNATE ");
|
||||
}
|
||||
if (err == UPNPCOMMAND_SUCCESS) {
|
||||
printf("OK" NL);
|
||||
@@ -279,8 +334,9 @@ bool UPnPHandleDeviceList(struct UPNPDev* list, bool enable, char* lanAddrOverri
|
||||
portMappingInternalAddress = localAddress;
|
||||
}
|
||||
|
||||
// Create the port mappings
|
||||
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
||||
if (!UPnPMapPort(&urls, &data, k_Ports[i].proto, portMappingInternalAddress, k_Ports[i].port, enable, false)) {
|
||||
if (!UPnPMapPort(&urls, &data, k_Ports[i].proto, portMappingInternalAddress, k_Ports[i].port, enable, false, false)) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
@@ -304,13 +360,28 @@ bool UPnPHandleDeviceList(struct UPNPDev* list, bool enable, char* lanAddrOverri
|
||||
char broadcastAddrStr[128];
|
||||
inet_ntop(AF_INET, &broadcastAddr, broadcastAddrStr, sizeof(broadcastAddrStr));
|
||||
|
||||
UPnPMapPort(&urls, &data, IPPROTO_UDP, broadcastAddrStr, k_WolPorts[i], enable, true);
|
||||
UPnPMapPort(&urls, &data, IPPROTO_UDP, broadcastAddrStr, k_WolPorts[i], enable, true, false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// When we're mapping the WOL ports upstream of our router, we map directly to
|
||||
// the port on the upstream address (likely our router's WAN interface).
|
||||
UPnPMapPort(&urls, &data, IPPROTO_UDP, lanAddrOverride, k_WolPorts[i], enable, true);
|
||||
UPnPMapPort(&urls, &data, IPPROTO_UDP, lanAddrOverride, k_WolPorts[i], enable, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the rules are present and correct if they claimed to be added successfully
|
||||
if (success && enable) {
|
||||
// Wait 10 seconds for the router state to quiesce
|
||||
printf("Waiting before UPnP port validation...");
|
||||
Sleep(10000);
|
||||
printf("done" NL);
|
||||
|
||||
// Perform the validation pass (converting any now missing entries to permanent ones)
|
||||
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
||||
if (!UPnPMapPort(&urls, &data, k_Ports[i].proto, portMappingInternalAddress, k_Ports[i].port, enable, false, true)) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -797,6 +868,13 @@ int Run()
|
||||
|
||||
ResetLogFile();
|
||||
|
||||
// Create the UDP alternate port relays
|
||||
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
||||
if (k_Ports[i].proto == IPPROTO_UDP) {
|
||||
StartUdpRelay(k_Ports[i].port);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the thread to watch for GameStream state changes
|
||||
CreateThread(nullptr, 0, GameStreamStateChangeThread, gsChangeEvent, 0, nullptr);
|
||||
|
||||
|
||||
@@ -163,6 +163,7 @@
|
||||
<ItemGroup>
|
||||
<ClCompile Include="miss.cpp" />
|
||||
<ClCompile Include="pcp.cpp" />
|
||||
<ClCompile Include="relay.cpp" />
|
||||
<ClCompile Include="tracer.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -170,6 +171,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\version.h" />
|
||||
<ClInclude Include="relay.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
<ClCompile Include="pcp.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="relay.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="miss.rc">
|
||||
@@ -34,5 +37,8 @@
|
||||
<ClInclude Include="..\version.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="relay.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <WinSock2.h>
|
||||
#include <Ws2ipdef.h>
|
||||
|
||||
#include "relay.h"
|
||||
|
||||
typedef struct _UDP_TUPLE {
|
||||
SOCKET socket;
|
||||
unsigned short port;
|
||||
} UDP_TUPLE, *PUDP_TUPLE;
|
||||
|
||||
DWORD
|
||||
WINAPI
|
||||
UdpRelayThreadProc(LPVOID Context)
|
||||
{
|
||||
PUDP_TUPLE tuple = (PUDP_TUPLE)Context;
|
||||
fd_set fds;
|
||||
int err;
|
||||
SOCKADDR_IN lastRemoteAddr;
|
||||
|
||||
RtlZeroMemory(&lastRemoteAddr, sizeof(lastRemoteAddr));
|
||||
|
||||
for (;;) {
|
||||
char buffer[4096];
|
||||
SOCKADDR_IN sourceAddr;
|
||||
int sourceAddrLen;
|
||||
|
||||
FD_ZERO(&fds);
|
||||
|
||||
FD_SET(tuple->socket, &fds);
|
||||
|
||||
err = select(0, &fds, NULL, NULL, NULL);
|
||||
if (err <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
sourceAddrLen = sizeof(sourceAddr);
|
||||
err = recvfrom(tuple->socket, buffer, sizeof(buffer), 0, (PSOCKADDR)&sourceAddr, &sourceAddrLen);
|
||||
if (err == SOCKET_ERROR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SOCKADDR_IN destinationAddr;
|
||||
if (RtlEqualMemory(&sourceAddr.sin_addr, &in4addr_loopback, sizeof(sourceAddr.sin_addr))) {
|
||||
// Traffic incoming from loopback interface - send it to the last remote address
|
||||
destinationAddr = lastRemoteAddr;
|
||||
}
|
||||
else {
|
||||
// Traffic incoming from the remote host - remember the source
|
||||
lastRemoteAddr = sourceAddr;
|
||||
|
||||
// Send it to the normal port via the loopback adapter
|
||||
destinationAddr = sourceAddr;
|
||||
destinationAddr.sin_addr = in4addr_loopback;
|
||||
destinationAddr.sin_port = htons(tuple->port);
|
||||
}
|
||||
|
||||
sendto(tuple->socket, buffer, err, 0, (PSOCKADDR)&destinationAddr, sizeof(destinationAddr));
|
||||
}
|
||||
|
||||
closesocket(tuple->socket);
|
||||
free(tuple);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int StartUdpRelay(unsigned short Port)
|
||||
{
|
||||
SOCKET sock;
|
||||
SOCKADDR_IN addr;
|
||||
HANDLE thread;
|
||||
PUDP_TUPLE tuple;
|
||||
|
||||
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (sock == INVALID_SOCKET) {
|
||||
printf("socket() failed: %d\n", WSAGetLastError());
|
||||
return WSAGetLastError();
|
||||
}
|
||||
|
||||
// Bind to the alternate port
|
||||
RtlZeroMemory(&addr, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(Port + RELAY_PORT_OFFSET);
|
||||
if (bind(sock, (PSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR) {
|
||||
printf("bind() failed: %d\n", WSAGetLastError());
|
||||
closesocket(sock);
|
||||
return WSAGetLastError();
|
||||
}
|
||||
|
||||
tuple = (PUDP_TUPLE)malloc(sizeof(*tuple));
|
||||
if (tuple == NULL) {
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
tuple->socket = sock;
|
||||
tuple->port = Port;
|
||||
|
||||
thread = CreateThread(NULL, 0, UdpRelayThreadProc, tuple, 0, NULL);
|
||||
if (thread == NULL) {
|
||||
printf("CreateThread() failed: %d\n", GetLastError());
|
||||
closesocket(sock);
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
CloseHandle(thread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define RELAY_PORT_OFFSET -10000
|
||||
|
||||
int StartUdpRelay(unsigned short Port);
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <wtsapi32.h>
|
||||
#include <powerbase.h>
|
||||
#include <VersionHelpers.h>
|
||||
#include <tlhelp32.h>
|
||||
|
||||
#pragma comment(lib, "miniupnpc.lib")
|
||||
#pragma comment(lib, "libnatpmp.lib")
|
||||
@@ -268,6 +269,28 @@ bool IsGameStreamEnabled()
|
||||
}
|
||||
}
|
||||
|
||||
bool IsCurrentlyStreaming()
|
||||
{
|
||||
bool ret = false;
|
||||
HANDLE processSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
|
||||
PROCESSENTRY32 procEntry;
|
||||
procEntry.dwSize = sizeof(procEntry);
|
||||
Process32First(processSnapshot, &procEntry);
|
||||
|
||||
do {
|
||||
// If we find nvstreamer.exe running, we're currently streaming
|
||||
if (_stricmp(procEntry.szExeFile, "nvstreamer.exe") == 0) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
} while (Process32Next(processSnapshot, &procEntry));
|
||||
|
||||
CloseHandle(processSnapshot);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool IsConsoleSessionActive()
|
||||
{
|
||||
PWTS_SESSION_INFO_1 sessionInfo;
|
||||
@@ -1203,6 +1226,12 @@ int main(int argc, char* argv[])
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (IsCurrentlyStreaming()) {
|
||||
DisplayMessage("The test cannot proceed because a GameStream session is currently running on this PC.\n\n"
|
||||
"Quit the currently running app on this host within Moonlight, or reboot your PC.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!IsConsoleSessionActive()) {
|
||||
DisplayMessage("The system display is currently locked. You must sign in to your PC again to use GameStream.\n\n"
|
||||
"This is most often due to Microsoft Remote Desktop locking the screen. Use an alternate GameStream-compatible remote desktop solution like Chrome Remote Desktop or TeamViewer to unlock the PC and prevent this error in the future.",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#define VER_VERSION 5,1,0,0
|
||||
#define VER_VERSION_STR "5.1.0.0"
|
||||
#define VER_VERSION 5,2,0,0
|
||||
#define VER_VERSION_STR "5.2.0.0"
|
||||
|
||||
#define VER_COMPANYNAME_STR "Moonlight Game Streaming Project"
|
||||
#define VER_PRODUCTNAME_STR "Moonlight Internet Hosting Tool"
|
||||
|
||||
Reference in New Issue
Block a user