Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae2647f36e | ||
|
|
8e40ef80bd | ||
|
|
16bb727b99 | ||
|
|
9194b8e22b | ||
|
|
2805464e86 | ||
|
|
20e7374480 | ||
|
|
0fdbee831b | ||
|
|
b37b99e9c3 | ||
|
|
29bbdc5319 | ||
|
|
09cd50b12a | ||
|
|
d60eda6fa8 | ||
|
|
cda9bc6be7 | ||
|
|
0008fbbcdb | ||
|
|
620aa4b6d1 | ||
|
|
b93ef55b65 | ||
|
|
fdb033f550 | ||
|
|
efdcaed4c1 | ||
|
|
5a2edb9a36 | ||
|
|
798be5fd0a | ||
|
|
b0941fee90 | ||
|
|
ebc7703bf2 | ||
|
|
bbf16383f5 | ||
|
|
dee2daac5a | ||
|
|
51e953c092 | ||
|
|
acb3f5a059 | ||
|
|
0bd212e6f1 | ||
|
|
d076f881eb | ||
|
|
46c2236342 | ||
|
|
8158c85a23 | ||
|
|
6f3898e4b4 | ||
|
|
b1a1352bbf | ||
|
|
069b0c7656 | ||
|
|
42df15c567 | ||
|
|
96a88b55db | ||
|
|
2d31cd0420 |
+1
-1
Submodule GS-IPv6-Forwarder updated: 1a01936a23...60ec35a3c0
@@ -1,4 +1,4 @@
|
|||||||
# Moonlight Internet Streaming Helper
|
# Moonlight Internet Hosting Tool
|
||||||
* Enable Moonlight streaming from your PC over the Internet with no configuration required
|
* Enable Moonlight streaming from your PC over the Internet with no configuration required
|
||||||
* Troubleshoot streaming issues with the included testing tool
|
* Troubleshoot streaming issues with the included testing tool
|
||||||
|
|
||||||
@@ -8,5 +8,6 @@
|
|||||||
|
|
||||||
# Technical Features
|
# Technical Features
|
||||||
- Supports all major port forwarding protocols (UPnP, NAT-PMP, and PCP) for the best possible router compatibility
|
- Supports all major port forwarding protocols (UPnP, NAT-PMP, and PCP) for the best possible router compatibility
|
||||||
|
- Supports streaming on IPv4-only, IPv6-only, and dual-stack Internet connections
|
||||||
- Supports successful forwarding in double-NAT environments by configuring port mappings on all NATs found on the way to the Internet
|
- Supports successful forwarding in double-NAT environments by configuring port mappings on all NATs found on the way to the Internet
|
||||||
- Supports forwarding on Carrier-Grade NATs (CGNs) by directly using PCP when the ISP doesn't implement UPnP-PCP interoperability
|
- Supports forwarding on Carrier-Grade NATs (CGNs) by directly using PCP when the ISP doesn't implement UPnP-PCP interoperability
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
|
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
|
||||||
xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
|
xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
|
||||||
<Bundle Name="Moonlight Internet Streaming Helper" Manufacturer="Moonlight Game Streaming Project" Version="!(bind.packageVersion.mish)" UpgradeCode="a2ce5056-1114-44b9-b79b-952ef46d3d50">
|
<Bundle Name="Moonlight Internet Hosting Tool" Manufacturer="Moonlight Game Streaming Project" Version="!(bind.packageVersion.mish)" UpgradeCode="a2ce5056-1114-44b9-b79b-952ef46d3d50">
|
||||||
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLicense">
|
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLicense">
|
||||||
<bal:WixStandardBootstrapperApplication
|
<bal:WixStandardBootstrapperApplication
|
||||||
LicenseFile="license.rtf"
|
LicenseFile="license.rtf"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<ProductVersion>3.10</ProductVersion>
|
<ProductVersion>3.10</ProductVersion>
|
||||||
<ProjectGuid>a2ce5056-1114-44b9-b79b-952ef46d3d50</ProjectGuid>
|
<ProjectGuid>a2ce5056-1114-44b9-b79b-952ef46d3d50</ProjectGuid>
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
<OutputName>mish_setup</OutputName>
|
<OutputName>InternetHostingToolSetup</OutputName>
|
||||||
<OutputType>Bundle</OutputType>
|
<OutputType>Bundle</OutputType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||||
|
|||||||
+4
-6
@@ -2,7 +2,7 @@
|
|||||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
|
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
|
||||||
xmlns:fire="http://schemas.microsoft.com/wix/FirewallExtension"
|
xmlns:fire="http://schemas.microsoft.com/wix/FirewallExtension"
|
||||||
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
|
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
|
||||||
<Product Id="*" Name="Moonlight Internet Streaming Helper" Language="1033" Version="!(bind.fileVersion.MistExe)"
|
<Product Id="*" Name="Moonlight Internet Hosting Tool" Language="1033" Version="!(bind.fileVersion.MistExe)"
|
||||||
Manufacturer="Moonlight Game Streaming Project" UpgradeCode="cfc2fb53-88e2-4867-851d-9ed9fe7ab2a3">
|
Manufacturer="Moonlight Game Streaming Project" UpgradeCode="cfc2fb53-88e2-4867-851d-9ed9fe7ab2a3">
|
||||||
<Package InstallerVersion="500" Compressed="yes" InstallScope="perMachine" />
|
<Package InstallerVersion="500" Compressed="yes" InstallScope="perMachine" />
|
||||||
|
|
||||||
@@ -17,10 +17,10 @@
|
|||||||
<Fragment>
|
<Fragment>
|
||||||
<Directory Id="TARGETDIR" Name="SourceDir">
|
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||||
<Directory Id="ProgramFilesFolder">
|
<Directory Id="ProgramFilesFolder">
|
||||||
<Directory Id="INSTALLFOLDER" Name="Moonlight Internet Streaming Helper" />
|
<Directory Id="INSTALLFOLDER" Name="Moonlight Internet Hosting Tool" />
|
||||||
</Directory>
|
</Directory>
|
||||||
<Directory Id="ProgramMenuFolder">
|
<Directory Id="ProgramMenuFolder">
|
||||||
<Directory Id="ApplicationProgramsFolder" Name="Moonlight Internet Streaming Helper" />
|
<Directory Id="ApplicationProgramsFolder" Name="Moonlight Internet Hosting Tool" />
|
||||||
</Directory>
|
</Directory>
|
||||||
<Directory Id="CommonAppDataFolder">
|
<Directory Id="CommonAppDataFolder">
|
||||||
<Directory Name="MISS" Id="MISSLogFolder" />
|
<Directory Name="MISS" Id="MISSLogFolder" />
|
||||||
@@ -47,9 +47,7 @@
|
|||||||
Account="NT AUTHORITY\LocalService"
|
Account="NT AUTHORITY\LocalService"
|
||||||
ErrorControl="ignore"
|
ErrorControl="ignore"
|
||||||
Interactive="no">
|
Interactive="no">
|
||||||
<ServiceConfig DelayedAutoStart="no" OnInstall="yes" OnReinstall="yes" ServiceSid="restricted">
|
<ServiceConfig DelayedAutoStart="no" OnInstall="yes" OnReinstall="yes" ServiceSid="unrestricted"/>
|
||||||
<RequiredPrivilege>SeChangeNotifyPrivilege</RequiredPrivilege>
|
|
||||||
</ServiceConfig>
|
|
||||||
</ServiceInstall>
|
</ServiceInstall>
|
||||||
<ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="MISS" Wait="yes" />
|
<ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="MISS" Wait="yes" />
|
||||||
</Component>
|
</Component>
|
||||||
|
|||||||
+12
-21
@@ -97,7 +97,7 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
|||||||
printf("NOT FOUND" NL);
|
printf("NOT FOUND" NL);
|
||||||
}
|
}
|
||||||
else if (err == UPNPCOMMAND_SUCCESS) {
|
else if (err == UPNPCOMMAND_SUCCESS) {
|
||||||
// Some routers change the description, so we can't check that here
|
// Some routers change the description, so we can't check that here
|
||||||
if (!strcmp(intClient, myAddr)) {
|
if (!strcmp(intClient, myAddr)) {
|
||||||
if (atoi(leaseDuration) == 0) {
|
if (atoi(leaseDuration) == 0) {
|
||||||
printf("OK (Permanent)" NL);
|
printf("OK (Permanent)" NL);
|
||||||
@@ -386,15 +386,6 @@ bool NATPMPMapPort(natpmp_t* natpmp, int proto, int port, bool enable, bool inde
|
|||||||
else if (response.pnu.newportmapping.mappedpublicport != port) {
|
else if (response.pnu.newportmapping.mappedpublicport != port) {
|
||||||
printf("CONFLICT" NL);
|
printf("CONFLICT" NL);
|
||||||
|
|
||||||
// Some buggy routers (Untangle) will change the *internal* port when
|
|
||||||
// adjusting a port mapping request that collides. This is why we also
|
|
||||||
// pass privateport back from the response and not from the port we originally
|
|
||||||
// asked for. Warn in this case.
|
|
||||||
if (response.pnu.newportmapping.privateport != port) {
|
|
||||||
printf("Buggy router changed the internal port when handling NAT-PMP conflict! (%d -> %d)" NL,
|
|
||||||
port, response.pnu.newportmapping.privateport);
|
|
||||||
}
|
|
||||||
|
|
||||||
// It couldn't assign us the external port we requested and gave us an alternate external port.
|
// It couldn't assign us the external port we requested and gave us an alternate external port.
|
||||||
// We can't use this alternate mapping, so immediately release it.
|
// We can't use this alternate mapping, so immediately release it.
|
||||||
printf("Deleting unwanted NAT-PMP mapping for %s %d...", proto == IPPROTO_TCP ? "TCP" : "UDP", response.pnu.newportmapping.mappedpublicport);
|
printf("Deleting unwanted NAT-PMP mapping for %s %d...", proto == IPPROTO_TCP ? "TCP" : "UDP", response.pnu.newportmapping.mappedpublicport);
|
||||||
@@ -747,18 +738,18 @@ void ResetLogFile()
|
|||||||
{
|
{
|
||||||
char oldLogFilePath[MAX_PATH + 1];
|
char oldLogFilePath[MAX_PATH + 1];
|
||||||
char currentLogFilePath[MAX_PATH + 1];
|
char currentLogFilePath[MAX_PATH + 1];
|
||||||
char timeString[MAX_PATH + 1] = {};
|
char timeString[MAX_PATH + 1] = {};
|
||||||
SYSTEMTIME time;
|
SYSTEMTIME time;
|
||||||
|
|
||||||
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-old.log", oldLogFilePath, sizeof(oldLogFilePath));
|
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-old.log", oldLogFilePath, sizeof(oldLogFilePath));
|
||||||
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-current.log", currentLogFilePath, sizeof(currentLogFilePath));
|
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-current.log", currentLogFilePath, sizeof(currentLogFilePath));
|
||||||
|
|
||||||
// Close the existing stdout handle. This is important because otherwise
|
// Close the existing stdout handle. This is important because otherwise
|
||||||
// it may still be open as stdout when we try to MoveFileEx below.
|
// it may still be open as stdout when we try to MoveFileEx below.
|
||||||
fclose(stdout);
|
fclose(stdout);
|
||||||
|
|
||||||
// Rotate the current to the old log file
|
// Rotate the current to the old log file
|
||||||
MoveFileExA(currentLogFilePath, oldLogFilePath, MOVEFILE_REPLACE_EXISTING);
|
MoveFileExA(currentLogFilePath, oldLogFilePath, MOVEFILE_REPLACE_EXISTING);
|
||||||
|
|
||||||
// Redirect stdout to this new file
|
// Redirect stdout to this new file
|
||||||
freopen(currentLogFilePath, "w", stdout);
|
freopen(currentLogFilePath, "w", stdout);
|
||||||
@@ -766,10 +757,10 @@ void ResetLogFile()
|
|||||||
// Print a log header
|
// Print a log header
|
||||||
printf("Moonlight Internet Streaming Service v" VER_VERSION_STR NL);
|
printf("Moonlight Internet Streaming Service v" VER_VERSION_STR NL);
|
||||||
|
|
||||||
// Print the current time
|
// Print the current time
|
||||||
GetSystemTime(&time);
|
GetSystemTime(&time);
|
||||||
GetTimeFormatA(LOCALE_SYSTEM_DEFAULT, 0, &time, "hh':'mm':'ss tt", timeString, ARRAYSIZE(timeString));
|
GetTimeFormatA(LOCALE_SYSTEM_DEFAULT, 0, &time, "hh':'mm':'ss tt", timeString, ARRAYSIZE(timeString));
|
||||||
printf("The current UTC time is: %s" NL, timeString);
|
printf("The current UTC time is: %s" NL, timeString);
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD WINAPI GameStreamStateChangeThread(PVOID Context)
|
DWORD WINAPI GameStreamStateChangeThread(PVOID Context)
|
||||||
|
|||||||
+591
-261
@@ -1,11 +1,5 @@
|
|||||||
#define _CRT_RAND_S
|
#include "mist.h"
|
||||||
#define _CRT_SECURE_NO_WARNINGS
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
|
||||||
#include <Windows.h>
|
|
||||||
#include <WinSock2.h>
|
|
||||||
#include <Ws2tcpip.h>
|
#include <Ws2tcpip.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@@ -13,8 +7,6 @@
|
|||||||
#include <objbase.h>
|
#include <objbase.h>
|
||||||
#include <WinInet.h>
|
#include <WinInet.h>
|
||||||
|
|
||||||
#include "..\version.h"
|
|
||||||
|
|
||||||
#pragma comment(lib, "miniupnpc.lib")
|
#pragma comment(lib, "miniupnpc.lib")
|
||||||
#pragma comment(lib, "libnatpmp.lib")
|
#pragma comment(lib, "libnatpmp.lib")
|
||||||
#pragma comment(lib, "ws2_32.lib")
|
#pragma comment(lib, "ws2_32.lib")
|
||||||
@@ -29,8 +21,6 @@
|
|||||||
#define NATPMP_STATICLIB
|
#define NATPMP_STATICLIB
|
||||||
#include <natpmp.h>
|
#include <natpmp.h>
|
||||||
|
|
||||||
bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN wanAddr);
|
|
||||||
|
|
||||||
static struct port_entry {
|
static struct port_entry {
|
||||||
int proto;
|
int proto;
|
||||||
int port;
|
int port;
|
||||||
@@ -44,8 +34,8 @@ static struct port_entry {
|
|||||||
{IPPROTO_UDP, 48000, true},
|
{IPPROTO_UDP, 48000, true},
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// These are not currently used, so let's
|
// These are not currently used, so let's
|
||||||
// avoid testing them for now.
|
// avoid testing them for now.
|
||||||
{IPPROTO_UDP, 48002, true},
|
{IPPROTO_UDP, 48002, true},
|
||||||
{IPPROTO_UDP, 48010, true}
|
{IPPROTO_UDP, 48010, true}
|
||||||
#endif
|
#endif
|
||||||
@@ -74,13 +64,14 @@ VOID CALLBACK MsgBoxHelpCallback(LPHELPINFO lpHelpInfo)
|
|||||||
|
|
||||||
void DisplayMessage(const char* message, const char* helpUrl = nullptr, MessagePriority priority = MpError, bool terminal = true)
|
void DisplayMessage(const char* message, const char* helpUrl = nullptr, MessagePriority priority = MpError, bool terminal = true)
|
||||||
{
|
{
|
||||||
printf("%s\n", message);
|
fprintf(CONSOLE_OUT, "%s\n", message);
|
||||||
|
fprintf(LOG_OUT, "%s\n", message);
|
||||||
|
|
||||||
if (terminal) {
|
if (terminal) {
|
||||||
char logPath[MAX_PATH + 1];
|
char logPath[MAX_PATH + 1];
|
||||||
FILE* f;
|
FILE* f;
|
||||||
|
|
||||||
printf("--------------- CURRENT MISS LOG -------------------\n");
|
fprintf(LOG_OUT, "--------------- CURRENT MISS LOG -------------------\n");
|
||||||
|
|
||||||
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-current.log", logPath, sizeof(logPath));
|
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-current.log", logPath, sizeof(logPath));
|
||||||
f = fopen(logPath, "r");
|
f = fopen(logPath, "r");
|
||||||
@@ -88,63 +79,63 @@ void DisplayMessage(const char* message, const char* helpUrl = nullptr, MessageP
|
|||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
while (!feof(f)) {
|
while (!feof(f)) {
|
||||||
int bytesRead = fread(buffer, 1, ARRAYSIZE(buffer), f);
|
int bytesRead = fread(buffer, 1, ARRAYSIZE(buffer), f);
|
||||||
fwrite(buffer, 1, bytesRead, stdout);
|
fwrite(buffer, 1, bytesRead, LOG_OUT);
|
||||||
}
|
}
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("Failed to find current MISS log\n");
|
fprintf(LOG_OUT, "Failed to find current MISS log\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\n----------------- OLD MISS LOG ---------------------\n");
|
fprintf(LOG_OUT, "\n----------------- OLD MISS LOG ---------------------\n");
|
||||||
|
|
||||||
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-old.log", logPath, sizeof(logPath));
|
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-old.log", logPath, sizeof(logPath));
|
||||||
f = fopen(logPath, "r");
|
f = fopen(logPath, "r");
|
||||||
if (f != nullptr) {
|
if (f != nullptr) {
|
||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
while (!feof(f)) {
|
while (!feof(f)) {
|
||||||
int bytesRead = fread(buffer, 1, ARRAYSIZE(buffer), f);
|
int bytesRead = fread(buffer, 1, ARRAYSIZE(buffer), f);
|
||||||
fwrite(buffer, 1, bytesRead, stdout);
|
fwrite(buffer, 1, bytesRead, LOG_OUT);
|
||||||
}
|
}
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("Failed to find old MISS log\n");
|
fprintf(LOG_OUT, "Failed to find old MISS log\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("--------------- CURRENT GSV6FWD LOG -------------------\n");
|
fprintf(LOG_OUT, "--------------- CURRENT GSV6FWD LOG -------------------\n");
|
||||||
|
|
||||||
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\GSv6Fwd-current.log", logPath, sizeof(logPath));
|
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\GSv6Fwd-current.log", logPath, sizeof(logPath));
|
||||||
f = fopen(logPath, "r");
|
f = fopen(logPath, "r");
|
||||||
if (f != nullptr) {
|
if (f != nullptr) {
|
||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
while (!feof(f)) {
|
while (!feof(f)) {
|
||||||
int bytesRead = fread(buffer, 1, ARRAYSIZE(buffer), f);
|
int bytesRead = fread(buffer, 1, ARRAYSIZE(buffer), f);
|
||||||
fwrite(buffer, 1, bytesRead, stdout);
|
fwrite(buffer, 1, bytesRead, LOG_OUT);
|
||||||
}
|
}
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("Failed to find current GSv6Fwd log\n");
|
fprintf(LOG_OUT, "Failed to find current GSv6Fwd log\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\n----------------- OLD GSV6FWD LOG ---------------------\n");
|
fprintf(LOG_OUT, "\n----------------- OLD GSV6FWD LOG ---------------------\n");
|
||||||
|
|
||||||
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\GSv6Fwd-old.log", logPath, sizeof(logPath));
|
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\GSv6Fwd-old.log", logPath, sizeof(logPath));
|
||||||
f = fopen(logPath, "r");
|
f = fopen(logPath, "r");
|
||||||
if (f != nullptr) {
|
if (f != nullptr) {
|
||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
while (!feof(f)) {
|
while (!feof(f)) {
|
||||||
int bytesRead = fread(buffer, 1, ARRAYSIZE(buffer), f);
|
int bytesRead = fread(buffer, 1, ARRAYSIZE(buffer), f);
|
||||||
fwrite(buffer, 1, bytesRead, stdout);
|
fwrite(buffer, 1, bytesRead, LOG_OUT);
|
||||||
}
|
}
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("Failed to find old GSv6Fwd log\n");
|
fprintf(LOG_OUT, "Failed to find old GSv6Fwd log\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
fflush(stdout);
|
fflush(LOG_OUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
MSGBOXPARAMSA msgParams;
|
MSGBOXPARAMSA msgParams;
|
||||||
@@ -175,7 +166,7 @@ void DisplayMessage(const char* message, const char* helpUrl = nullptr, MessageP
|
|||||||
|
|
||||||
if (priority != MpInfo && terminal) {
|
if (priority != MpInfo && terminal) {
|
||||||
UINT flags = MB_YESNO | MB_TOPMOST | MB_SETFOREGROUND | MB_ICONINFORMATION;
|
UINT flags = MB_YESNO | MB_TOPMOST | MB_SETFOREGROUND | MB_ICONINFORMATION;
|
||||||
switch (MessageBoxA(nullptr, "Would you like to view the troubleshooting log?",
|
switch (MessageBoxA(nullptr, "Would you like to view the troubleshooting log?\n\nYou will need to provide this log if you ask for help on the Moonlight Discord server.",
|
||||||
"Moonlight Internet Streaming Tester", flags))
|
"Moonlight Internet Streaming Tester", flags))
|
||||||
{
|
{
|
||||||
case IDYES:
|
case IDYES:
|
||||||
@@ -187,6 +178,56 @@ void DisplayMessage(const char* message, const char* helpUrl = nullptr, MessageP
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ExecuteCommand(PCSTR command, PCHAR outputBuffer, DWORD outputBufferLength)
|
||||||
|
{
|
||||||
|
SECURITY_ATTRIBUTES attribs;
|
||||||
|
STARTUPINFO si;
|
||||||
|
PROCESS_INFORMATION pi;
|
||||||
|
HANDLE outReadHandle;
|
||||||
|
DWORD bytesRead = 0;
|
||||||
|
|
||||||
|
ZeroMemory(&si, sizeof(si));
|
||||||
|
si.cb = sizeof(si);
|
||||||
|
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
|
||||||
|
si.wShowWindow = SW_HIDE;
|
||||||
|
|
||||||
|
ZeroMemory(&attribs, sizeof(attribs));
|
||||||
|
attribs.nLength = sizeof(attribs);
|
||||||
|
attribs.bInheritHandle = TRUE;
|
||||||
|
|
||||||
|
if (!CreatePipe(&outReadHandle, &si.hStdOutput, &attribs, 0))
|
||||||
|
{
|
||||||
|
fprintf(LOG_OUT, "CreatePipe() failed: %d\n", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
si.hStdError = si.hStdOutput;
|
||||||
|
|
||||||
|
if (!CreateProcess(NULL, (LPSTR)command, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
|
||||||
|
{
|
||||||
|
fprintf(LOG_OUT, "CreateProcess() failed: %d\n", GetLastError());
|
||||||
|
CloseHandle(si.hStdOutput);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||||
|
|
||||||
|
CloseHandle(si.hStdOutput);
|
||||||
|
CloseHandle(pi.hProcess);
|
||||||
|
CloseHandle(pi.hThread);
|
||||||
|
|
||||||
|
if (!ReadFile(outReadHandle, outputBuffer, outputBufferLength, &bytesRead, NULL))
|
||||||
|
{
|
||||||
|
fprintf(LOG_OUT, "ReadFile() failed: %d\n", GetLastError());
|
||||||
|
CloseHandle(outReadHandle);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputBuffer[bytesRead] = 0;
|
||||||
|
CloseHandle(outReadHandle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool IsGameStreamEnabled()
|
bool IsGameStreamEnabled()
|
||||||
{
|
{
|
||||||
DWORD error;
|
DWORD error;
|
||||||
@@ -196,7 +237,7 @@ bool IsGameStreamEnabled()
|
|||||||
|
|
||||||
error = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\NVIDIA Corporation\\NvStream", 0, KEY_READ | KEY_WOW64_64KEY, &key);
|
error = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\NVIDIA Corporation\\NvStream", 0, KEY_READ | KEY_WOW64_64KEY, &key);
|
||||||
if (error != ERROR_SUCCESS) {
|
if (error != ERROR_SUCCESS) {
|
||||||
printf("RegOpenKeyEx() failed: %d\n", error);
|
fprintf(LOG_OUT, "RegOpenKeyEx() failed: %d\n", error);
|
||||||
DisplayMessage("GeForce Experience was not detected on this PC. Make sure you're installing this utility on your GeForce GameStream-compatible PC, not the device running Moonlight.",
|
DisplayMessage("GeForce Experience was not detected on this PC. Make sure you're installing this utility on your GeForce GameStream-compatible PC, not the device running Moonlight.",
|
||||||
"https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide");
|
"https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide");
|
||||||
return false;
|
return false;
|
||||||
@@ -208,38 +249,54 @@ bool IsGameStreamEnabled()
|
|||||||
if (error != ERROR_SUCCESS || !enabled) {
|
if (error != ERROR_SUCCESS || !enabled) {
|
||||||
// GFE may not even write EnableStreaming until the user enables GameStream for the first time
|
// GFE may not even write EnableStreaming until the user enables GameStream for the first time
|
||||||
if (error != ERROR_SUCCESS) {
|
if (error != ERROR_SUCCESS) {
|
||||||
printf("RegQueryValueExA() failed: %d\n", error);
|
fprintf(LOG_OUT, "RegQueryValueExA() failed: %d\n", error);
|
||||||
}
|
}
|
||||||
DisplayMessage("GameStream is not enabled in GeForce Experience. Please open GeForce Experience settings, navigate to the Shield tab, and turn GameStream on.",
|
DisplayMessage("GameStream is not enabled in GeForce Experience. Please open GeForce Experience settings, navigate to the Shield tab, and turn GameStream on.",
|
||||||
"https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide");
|
"https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("GeForce Experience installed and GameStream is enabled\n");
|
fprintf(LOG_OUT, "GeForce Experience installed and GameStream is enabled\n");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsZeroTierInstalled()
|
||||||
|
{
|
||||||
|
DWORD error;
|
||||||
|
HKEY key;
|
||||||
|
|
||||||
|
error = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\ZeroTier, Inc.\\ZeroTier One", 0, KEY_READ | KEY_WOW64_32KEY, &key);
|
||||||
|
if (error != ERROR_SUCCESS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RegCloseKey(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
enum PortTestStatus {
|
enum PortTestStatus {
|
||||||
PortTestOk,
|
PortTestOk,
|
||||||
PortTestError,
|
PortTestError,
|
||||||
PortTestUnknown
|
PortTestUnknown
|
||||||
};
|
};
|
||||||
PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withServer)
|
PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withServer, bool isLoopbackRelay)
|
||||||
{
|
{
|
||||||
SOCKET clientSock = INVALID_SOCKET, serverSock = INVALID_SOCKET;
|
SOCKET clientSock = INVALID_SOCKET, serverSock = INVALID_SOCKET;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
clientSock = socket(addr->ss_family, proto == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM, proto);
|
clientSock = socket(addr->ss_family, proto == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM, proto);
|
||||||
if (clientSock == INVALID_SOCKET) {
|
if (clientSock == INVALID_SOCKET) {
|
||||||
printf("socket() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "socket() failed: %d\n", WSAGetLastError());
|
||||||
return PortTestError;
|
return PortTestError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (withServer) {
|
if (withServer) {
|
||||||
serverSock = socket(addr->ss_family, proto == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM, proto);
|
// Even if we are testing IPv6, our server socket should still be on IPv4 to allow the
|
||||||
|
// IPv6 relay to do its job (since it's already bound to all GFE ports on v6)
|
||||||
|
serverSock = socket(AF_INET, proto == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM, proto);
|
||||||
if (serverSock == INVALID_SOCKET) {
|
if (serverSock == INVALID_SOCKET) {
|
||||||
printf("socket() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "socket() failed: %d\n", WSAGetLastError());
|
||||||
closesocket(clientSock);
|
closesocket(clientSock);
|
||||||
return PortTestError;
|
return PortTestError;
|
||||||
}
|
}
|
||||||
@@ -251,21 +308,22 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
|
|||||||
if (err == SOCKET_ERROR) {
|
if (err == SOCKET_ERROR) {
|
||||||
if (WSAGetLastError() == WSAEADDRINUSE) {
|
if (WSAGetLastError() == WSAEADDRINUSE) {
|
||||||
// If someone is already listening (perhaps GFE is currently streaming),
|
// If someone is already listening (perhaps GFE is currently streaming),
|
||||||
// we can proceed if it's a TCP connection.
|
// we can proceed if it's a TCP connection unless it's a loopback relay
|
||||||
if (proto == IPPROTO_TCP) {
|
// which would give us a false positive result.
|
||||||
|
if (proto == IPPROTO_TCP && !isLoopbackRelay) {
|
||||||
closesocket(serverSock);
|
closesocket(serverSock);
|
||||||
serverSock = INVALID_SOCKET;
|
serverSock = INVALID_SOCKET;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// We can't continue to test for UDP ports.
|
// We can't continue to test for UDP ports.
|
||||||
printf("Unknown (in use)\n");
|
fprintf(LOG_OUT, "Unknown (in use)\n");
|
||||||
closesocket(clientSock);
|
closesocket(clientSock);
|
||||||
closesocket(serverSock);
|
closesocket(serverSock);
|
||||||
return PortTestUnknown;
|
return PortTestUnknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("bind() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "bind() failed: %d\n", WSAGetLastError());
|
||||||
closesocket(clientSock);
|
closesocket(clientSock);
|
||||||
closesocket(serverSock);
|
closesocket(serverSock);
|
||||||
return PortTestError;
|
return PortTestError;
|
||||||
@@ -275,7 +333,7 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
|
|||||||
if (proto == IPPROTO_TCP && serverSock != INVALID_SOCKET) {
|
if (proto == IPPROTO_TCP && serverSock != INVALID_SOCKET) {
|
||||||
err = listen(serverSock, 1);
|
err = listen(serverSock, 1);
|
||||||
if (err == SOCKET_ERROR) {
|
if (err == SOCKET_ERROR) {
|
||||||
printf("listen() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "listen() failed: %d\n", WSAGetLastError());
|
||||||
closesocket(clientSock);
|
closesocket(clientSock);
|
||||||
closesocket(serverSock);
|
closesocket(serverSock);
|
||||||
return PortTestError;
|
return PortTestError;
|
||||||
@@ -286,7 +344,7 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
|
|||||||
ULONG nbIo = 1;
|
ULONG nbIo = 1;
|
||||||
err = ioctlsocket(clientSock, FIONBIO, &nbIo);
|
err = ioctlsocket(clientSock, FIONBIO, &nbIo);
|
||||||
if (err == SOCKET_ERROR) {
|
if (err == SOCKET_ERROR) {
|
||||||
printf("ioctlsocket() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "ioctlsocket() failed: %d\n", WSAGetLastError());
|
||||||
closesocket(clientSock);
|
closesocket(clientSock);
|
||||||
if (serverSock != INVALID_SOCKET) {
|
if (serverSock != INVALID_SOCKET) {
|
||||||
closesocket(serverSock);
|
closesocket(serverSock);
|
||||||
@@ -304,7 +362,7 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
|
|||||||
if (proto == IPPROTO_TCP) {
|
if (proto == IPPROTO_TCP) {
|
||||||
err = connect(clientSock, (struct sockaddr*)&sin6, addrLen);
|
err = connect(clientSock, (struct sockaddr*)&sin6, addrLen);
|
||||||
if (err == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) {
|
if (err == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) {
|
||||||
printf("connect() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "connect() failed: %d\n", WSAGetLastError());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
struct timeval timeout = {};
|
struct timeval timeout = {};
|
||||||
@@ -312,30 +370,30 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
|
|||||||
|
|
||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
|
|
||||||
if (serverSock != INVALID_SOCKET) {
|
if (serverSock != INVALID_SOCKET) {
|
||||||
FD_SET(serverSock, &fds);
|
FD_SET(serverSock, &fds);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
FD_SET(clientSock, &fds);
|
FD_SET(clientSock, &fds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have a server socket, listen for the accept() instead of the
|
// If we have a server socket, listen for the accept() instead of the
|
||||||
// connect() so we can be compatible with the loopback relay.
|
// connect() so we can be compatible with the loopback relay.
|
||||||
timeout.tv_sec = 3;
|
timeout.tv_sec = 3;
|
||||||
err = select(0,
|
err = select(0,
|
||||||
serverSock != INVALID_SOCKET ? &fds : nullptr,
|
serverSock != INVALID_SOCKET ? &fds : nullptr,
|
||||||
serverSock == INVALID_SOCKET ? &fds : nullptr,
|
serverSock == INVALID_SOCKET ? &fds : nullptr,
|
||||||
nullptr, &timeout);
|
nullptr, &timeout);
|
||||||
if (err == 1) {
|
if (err == 1) {
|
||||||
// Our FD was signalled for connect() or accept() completion
|
// Our FD was signalled for connect() or accept() completion
|
||||||
printf("Success\n");
|
fprintf(LOG_OUT, "Success\n");
|
||||||
}
|
}
|
||||||
else if (err == 0) {
|
else if (err == 0) {
|
||||||
// Timed out
|
// Timed out
|
||||||
printf("Timeout\n");
|
fprintf(LOG_OUT, "Timeout\n");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("select() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "select() failed: %d\n", WSAGetLastError());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,12 +406,18 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const char testMsg[] = "moonlight-test";
|
const char testMsg[] = "moonlight-test";
|
||||||
err = sendto(clientSock, testMsg, sizeof(testMsg), 0, (struct sockaddr*)&sin6, addrLen);
|
|
||||||
if (err == SOCKET_ERROR) {
|
// Send several test packets to ensure a random lost packet doesn't make the test fail
|
||||||
printf("sendto() failed: %d\n", WSAGetLastError());
|
for (int i = 0; i < 5; i++) {
|
||||||
closesocket(clientSock);
|
err = sendto(clientSock, testMsg, sizeof(testMsg), 0, (struct sockaddr*)&sin6, addrLen);
|
||||||
closesocket(serverSock);
|
if (err == SOCKET_ERROR) {
|
||||||
return PortTestError;
|
fprintf(LOG_OUT, "sendto() failed: %d\n", WSAGetLastError());
|
||||||
|
closesocket(clientSock);
|
||||||
|
closesocket(serverSock);
|
||||||
|
return PortTestError;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sleep(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct timeval timeout = {};
|
struct timeval timeout = {};
|
||||||
@@ -362,18 +426,18 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
|
|||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
FD_SET(serverSock, &fds);
|
FD_SET(serverSock, &fds);
|
||||||
|
|
||||||
timeout.tv_sec = 3;
|
timeout.tv_sec = 2;
|
||||||
err = select(0, &fds, nullptr, nullptr, &timeout);
|
err = select(0, &fds, nullptr, nullptr, &timeout);
|
||||||
if (err == 1) {
|
if (err == 1) {
|
||||||
// Our FD was signalled for data available
|
// Our FD was signalled for data available
|
||||||
printf("Success\n");
|
fprintf(LOG_OUT, "Success\n");
|
||||||
}
|
}
|
||||||
else if (err == 0) {
|
else if (err == 0) {
|
||||||
// Timed out
|
// Timed out
|
||||||
printf("Timeout\n");
|
fprintf(LOG_OUT, "Timeout\n");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("select() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "select() failed: %d\n", WSAGetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
closesocket(clientSock);
|
closesocket(clientSock);
|
||||||
@@ -385,142 +449,274 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
|
|||||||
|
|
||||||
PortTestStatus TestHttpPort(PSOCKADDR_STORAGE addr, int port)
|
PortTestStatus TestHttpPort(PSOCKADDR_STORAGE addr, int port)
|
||||||
{
|
{
|
||||||
HINTERNET hInternet;
|
HINTERNET hInternet;
|
||||||
HINTERNET hRequest;
|
HINTERNET hRequest;
|
||||||
char url[1024];
|
char url[1024];
|
||||||
|
|
||||||
hInternet = InternetOpenA("MIST", 0, nullptr, nullptr, 0);
|
hInternet = InternetOpenA("MIST", 0, nullptr, nullptr, 0);
|
||||||
if (hInternet == nullptr) {
|
if (hInternet == nullptr) {
|
||||||
printf("InternetOpen() failed: %d\n", GetLastError());
|
fprintf(LOG_OUT, "InternetOpen() failed: %d\n", GetLastError());
|
||||||
return PortTestError;
|
return PortTestError;
|
||||||
}
|
}
|
||||||
|
|
||||||
char addrStr[64];
|
char addrStr[INET6_ADDRSTRLEN + 2];
|
||||||
inet_ntop(AF_INET, &((struct sockaddr_in*)addr)->sin_addr, addrStr, sizeof(addrStr));
|
if (addr->ss_family == AF_INET) {
|
||||||
|
inet_ntop(AF_INET, &((struct sockaddr_in*)addr)->sin_addr, addrStr, sizeof(addrStr));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// The address string must be escaped for usage in URLs
|
||||||
|
addrStr[0] = '[';
|
||||||
|
inet_ntop(AF_INET6, &((struct sockaddr_in6*)addr)->sin6_addr, &addrStr[1], INET6_ADDRSTRLEN);
|
||||||
|
strcat_s(addrStr, "]");
|
||||||
|
}
|
||||||
|
|
||||||
sprintf(url, "%s://%s:%d/",
|
sprintf(url, "%s://%s:%d/",
|
||||||
port == 47989 ? "http" : "https",
|
port == 47989 ? "http" : "https",
|
||||||
addrStr,
|
addrStr,
|
||||||
port);
|
port);
|
||||||
|
|
||||||
hRequest = InternetOpenUrlA(hInternet, url, nullptr, 0,
|
hRequest = InternetOpenUrlA(hInternet, url, nullptr, 0,
|
||||||
INTERNET_FLAG_IGNORE_CERT_DATE_INVALID | INTERNET_FLAG_NO_UI | INTERNET_FLAG_NO_AUTH | INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_RELOAD,
|
INTERNET_FLAG_IGNORE_CERT_DATE_INVALID | INTERNET_FLAG_NO_UI | INTERNET_FLAG_NO_AUTH | INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_RELOAD,
|
||||||
NULL);
|
NULL);
|
||||||
if (hRequest == nullptr) {
|
if (hRequest == nullptr) {
|
||||||
if (GetLastError() == ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED) {
|
if (GetLastError() == ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED) {
|
||||||
// This is expected for our HTTPS connection
|
// This is expected for our HTTPS connection
|
||||||
printf("Success\n");
|
fprintf(LOG_OUT, "Success\n");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// CANNOT_CONNECT is the "expected" error
|
// CANNOT_CONNECT is the "expected" error
|
||||||
if (GetLastError() == ERROR_INTERNET_CANNOT_CONNECT) {
|
if (GetLastError() == ERROR_INTERNET_CANNOT_CONNECT) {
|
||||||
printf("Failed\n");
|
fprintf(LOG_OUT, "Failed\n");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("Failed: %d\n", GetLastError());
|
fprintf(LOG_OUT, "Failed: %d\n", GetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
InternetCloseHandle(hInternet);
|
InternetCloseHandle(hInternet);
|
||||||
return PortTestError;
|
return PortTestError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("Success\n");
|
fprintf(LOG_OUT, "Success\n");
|
||||||
InternetCloseHandle(hRequest);
|
InternetCloseHandle(hRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
InternetCloseHandle(hInternet);
|
InternetCloseHandle(hInternet);
|
||||||
|
|
||||||
return PortTestOk;
|
return PortTestOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TestAllPorts(PSOCKADDR_STORAGE addr, char* portMsg, int portMsgLen)
|
bool TestAllPorts(PSOCKADDR_STORAGE addr, char* portMsg, int portMsgLen, bool isLoopbackRelay, bool consolePrint, bool* allPortsFailed = nullptr)
|
||||||
{
|
{
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
|
|
||||||
|
if (allPortsFailed) {
|
||||||
|
*allPortsFailed = true;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
||||||
printf("Testing %s %d...",
|
fprintf(LOG_OUT, "Testing %s %d...",
|
||||||
k_Ports[i].proto == IPPROTO_TCP ? "TCP" : "UDP",
|
k_Ports[i].proto == IPPROTO_TCP ? "TCP" : "UDP",
|
||||||
k_Ports[i].port);
|
k_Ports[i].port);
|
||||||
PortTestStatus status;
|
|
||||||
|
|
||||||
status = TestPort(addr, k_Ports[i].proto, k_Ports[i].port, k_Ports[i].withServer);
|
|
||||||
|
|
||||||
if (status != PortTestError && !k_Ports[i].withServer) {
|
if (consolePrint) {
|
||||||
// Test using a real HTTP client if the port wasn't totally dead.
|
fprintf(CONSOLE_OUT, "\tTesting %s %d...\n",
|
||||||
// This is required to confirm functionality with the loopback relay.
|
k_Ports[i].proto == IPPROTO_TCP ? "TCP" : "UDP",
|
||||||
// TestHttpPort() can take significantly longer to timeout than TestPort(),
|
k_Ports[i].port);
|
||||||
// so we only do this test if we believe we're likely to get a response.
|
}
|
||||||
printf("Testing TCP %d with HTTP traffic...", k_Ports[i].port);
|
|
||||||
status = TestHttpPort(addr, k_Ports[i].port);
|
PortTestStatus status = TestPort(addr, k_Ports[i].proto, k_Ports[i].port, k_Ports[i].withServer, isLoopbackRelay);
|
||||||
}
|
|
||||||
|
if (status != PortTestError && !k_Ports[i].withServer) {
|
||||||
|
// Test using a real HTTP client if the port wasn't totally dead.
|
||||||
|
// This is required to confirm functionality with the loopback relay.
|
||||||
|
// TestHttpPort() can take significantly longer to timeout than TestPort(),
|
||||||
|
// so we only do this test if we believe we're likely to get a response.
|
||||||
|
fprintf(LOG_OUT, "Testing TCP %d with HTTP traffic...", k_Ports[i].port);
|
||||||
|
status = TestHttpPort(addr, k_Ports[i].port);
|
||||||
|
}
|
||||||
|
|
||||||
if (status != PortTestOk) {
|
if (status != PortTestOk) {
|
||||||
// If we got an unknown result, assume it matches with whatever
|
// If we got an unknown result, assume it matches with whatever
|
||||||
// we've gotten so far.
|
// we've gotten so far.
|
||||||
if (status == PortTestError || !ret) {
|
if (status == PortTestError || !ret) {
|
||||||
int msgLen = snprintf(portMsg, portMsgLen, "%s %d\n",
|
if (portMsg != NULL && portMsgLen > 0) {
|
||||||
k_Ports[i].proto == IPPROTO_TCP ? "TCP" : "UDP",
|
int msgLen = snprintf(portMsg, portMsgLen, "%s %d\n",
|
||||||
k_Ports[i].port);
|
k_Ports[i].proto == IPPROTO_TCP ? "TCP" : "UDP",
|
||||||
portMsg += msgLen;
|
k_Ports[i].port);
|
||||||
portMsgLen -= msgLen;
|
portMsg += msgLen;
|
||||||
|
portMsgLen -= msgLen;
|
||||||
|
}
|
||||||
|
|
||||||
// Keep going to check all ports and report the failing ones
|
// Keep going to check all ports and report the failing ones
|
||||||
ret = false;
|
ret = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (allPortsFailed) {
|
||||||
|
*allPortsFailed = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FindLocalInterfaceIP4Address(PSOCKADDR_IN addr)
|
bool FindLocalInterfaceIPAddress(int family, PSOCKADDR_STORAGE addr)
|
||||||
{
|
{
|
||||||
SOCKET s;
|
SOCKET s;
|
||||||
struct hostent* host;
|
struct addrinfo hint = {};
|
||||||
|
struct addrinfo* result;
|
||||||
|
int err;
|
||||||
|
|
||||||
printf("Finding local IP address...");
|
fprintf(LOG_OUT, "Finding local %s address...", family == AF_INET ? "IPv4" : "IPv6");
|
||||||
|
|
||||||
host = gethostbyname("moonlight-stream.org");
|
hint.ai_family = family;
|
||||||
if (host == nullptr) {
|
hint.ai_socktype = SOCK_STREAM;
|
||||||
printf("gethostbyname() failed: %d\n", WSAGetLastError());
|
hint.ai_protocol = IPPROTO_TCP;
|
||||||
|
hint.ai_flags = AI_ADDRCONFIG;
|
||||||
|
err = getaddrinfo("moonlight-stream.org", "https", &hint, &result);
|
||||||
|
if (err != 0 || result == NULL) {
|
||||||
|
fprintf(LOG_OUT, "getaddrinfo() failed: %d\n", err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
s = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
|
||||||
if (s == INVALID_SOCKET) {
|
if (s == INVALID_SOCKET) {
|
||||||
printf("socket() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "socket() failed: %d\n", WSAGetLastError());
|
||||||
|
freeaddrinfo(result);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SOCKADDR_IN sin = {};
|
err = connect(s, (struct sockaddr*)result->ai_addr, result->ai_addrlen);
|
||||||
sin.sin_family = AF_INET;
|
|
||||||
sin.sin_port = htons(443);
|
|
||||||
sin.sin_addr = *(struct in_addr*)host->h_addr;
|
|
||||||
int err = connect(s, (struct sockaddr*)&sin, sizeof(sin));
|
|
||||||
if (err == SOCKET_ERROR) {
|
if (err == SOCKET_ERROR) {
|
||||||
printf("connect() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "connect() failed: %d\n", WSAGetLastError());
|
||||||
closesocket(s);
|
closesocket(s);
|
||||||
|
freeaddrinfo(result);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(result);
|
||||||
|
|
||||||
// Determine which local interface we bound to
|
// Determine which local interface we bound to
|
||||||
int nameLen = sizeof(*addr);
|
int nameLen = sizeof(*addr);
|
||||||
err = getsockname(s, (struct sockaddr*)addr, &nameLen);
|
err = getsockname(s, (struct sockaddr*)addr, &nameLen);
|
||||||
if (err == SOCKET_ERROR) {
|
if (err == SOCKET_ERROR) {
|
||||||
printf("getsockname() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "getsockname() failed: %d\n", WSAGetLastError());
|
||||||
closesocket(s);
|
closesocket(s);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char addrStr[64];
|
char addrStr[INET6_ADDRSTRLEN];
|
||||||
inet_ntop(AF_INET, &addr->sin_addr, addrStr, sizeof(addrStr));
|
if (addr->ss_family == AF_INET) {
|
||||||
printf("%s\n", addrStr);
|
inet_ntop(AF_INET, &((struct sockaddr_in*)addr)->sin_addr, addrStr, sizeof(addrStr));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
inet_ntop(AF_INET6, &((struct sockaddr_in6*)addr)->sin6_addr, addrStr, sizeof(addrStr));
|
||||||
|
}
|
||||||
|
fprintf(LOG_OUT, "%s\n", addrStr);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FindZeroTierInterfaceAddress(PSOCKADDR_STORAGE addr)
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
IP_ADAPTER_ADDRESSES addresses;
|
||||||
|
char buffer[8192];
|
||||||
|
};
|
||||||
|
ULONG error;
|
||||||
|
ULONG length;
|
||||||
|
PIP_ADAPTER_ADDRESSES currentAdapter;
|
||||||
|
PIP_ADAPTER_UNICAST_ADDRESS currentAddress;
|
||||||
|
|
||||||
|
// Get all IPv4 interfaces
|
||||||
|
length = sizeof(buffer);
|
||||||
|
error = GetAdaptersAddresses(AF_INET,
|
||||||
|
GAA_FLAG_SKIP_ANYCAST |
|
||||||
|
GAA_FLAG_SKIP_MULTICAST |
|
||||||
|
GAA_FLAG_SKIP_DNS_SERVER |
|
||||||
|
GAA_FLAG_SKIP_FRIENDLY_NAME,
|
||||||
|
NULL,
|
||||||
|
&addresses,
|
||||||
|
&length);
|
||||||
|
if (error != ERROR_SUCCESS) {
|
||||||
|
fprintf(LOG_OUT, "GetAdaptersAddresses() failed: %d\n", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAdapter = &addresses;
|
||||||
|
while (currentAdapter != NULL) {
|
||||||
|
// Look for ones that correspond to a ZeroTier device
|
||||||
|
if (wcsstr(currentAdapter->Description, L"ZeroTier")) {
|
||||||
|
// Check if this interface has the IP address we want
|
||||||
|
currentAddress = currentAdapter->FirstUnicastAddress;
|
||||||
|
while (currentAddress != NULL) {
|
||||||
|
if (currentAddress->Address.lpSockaddr->sa_family == AF_INET) {
|
||||||
|
RtlCopyMemory(addr, currentAddress->Address.lpSockaddr, currentAddress->Address.iSockaddrLength);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAddress = currentAddress->Next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAdapter = currentAdapter->Next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FindDuplicateDefaultInterfaces(void)
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
IP_ADAPTER_ADDRESSES addresses;
|
||||||
|
char buffer[8192];
|
||||||
|
};
|
||||||
|
ULONG error;
|
||||||
|
ULONG length;
|
||||||
|
MIB_IPFORWARDROW defaultRoute;
|
||||||
|
PIP_ADAPTER_ADDRESSES currentAdapter;
|
||||||
|
DWORD matchingInterfaces = 0;
|
||||||
|
|
||||||
|
error = GetBestRoute(0, 0, &defaultRoute);
|
||||||
|
if (error != NO_ERROR) {
|
||||||
|
fprintf(LOG_OUT, "GetBestRoute() failed: %d\n", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all IPv4 interfaces
|
||||||
|
length = sizeof(buffer);
|
||||||
|
error = GetAdaptersAddresses(AF_INET,
|
||||||
|
GAA_FLAG_SKIP_UNICAST |
|
||||||
|
GAA_FLAG_SKIP_ANYCAST |
|
||||||
|
GAA_FLAG_SKIP_MULTICAST |
|
||||||
|
GAA_FLAG_SKIP_DNS_SERVER |
|
||||||
|
GAA_FLAG_INCLUDE_GATEWAYS |
|
||||||
|
GAA_FLAG_SKIP_FRIENDLY_NAME,
|
||||||
|
NULL,
|
||||||
|
&addresses,
|
||||||
|
&length);
|
||||||
|
if (error != ERROR_SUCCESS) {
|
||||||
|
fprintf(LOG_OUT, "GetAdaptersAddresses() failed: %d\n", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAdapter = &addresses;
|
||||||
|
while (currentAdapter != NULL) {
|
||||||
|
if (currentAdapter->OperStatus == IfOperStatusUp &&
|
||||||
|
currentAdapter->FirstGatewayAddress != NULL &&
|
||||||
|
currentAdapter->FirstGatewayAddress->Address.iSockaddrLength == sizeof(SOCKADDR_IN)) {
|
||||||
|
if (((PSOCKADDR_IN)currentAdapter->FirstGatewayAddress->Address.lpSockaddr)->sin_addr.S_un.S_addr == defaultRoute.dwForwardNextHop) {
|
||||||
|
matchingInterfaces++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAdapter = currentAdapter->Next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchingInterfaces > 1;
|
||||||
|
}
|
||||||
|
|
||||||
enum UPnPPortStatus {
|
enum UPnPPortStatus {
|
||||||
NOT_FOUND,
|
NOT_FOUND,
|
||||||
OK,
|
OK,
|
||||||
@@ -551,28 +747,28 @@ UPnPPortStatus UPnPCheckPort(struct UPNPUrls* urls, struct IGDdatas* data, int p
|
|||||||
return ERRORED;
|
return ERRORED;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Checking for UPnP port mapping for %s %s -> %s...", protoStr, portStr, myAddr);
|
fprintf(LOG_OUT, "Checking for UPnP port mapping for %s %s -> %s...", protoStr, portStr, myAddr);
|
||||||
int err = UPNP_GetSpecificPortMappingEntry(
|
int err = UPNP_GetSpecificPortMappingEntry(
|
||||||
urls->controlURL, data->first.servicetype, portStr, protoStr, nullptr,
|
urls->controlURL, data->first.servicetype, portStr, protoStr, nullptr,
|
||||||
intClient, intPort, desc, enabled, leaseDuration);
|
intClient, intPort, desc, enabled, leaseDuration);
|
||||||
if (err == 714) {
|
if (err == 714) {
|
||||||
// NoSuchEntryInArray
|
// NoSuchEntryInArray
|
||||||
printf("NOT FOUND\n");
|
fprintf(LOG_OUT, "NOT FOUND\n");
|
||||||
return NOT_FOUND;
|
return NOT_FOUND;
|
||||||
}
|
}
|
||||||
else if (err == UPNPCOMMAND_SUCCESS) {
|
else if (err == UPNPCOMMAND_SUCCESS) {
|
||||||
if (!strcmp(myAddr, intClient)) {
|
if (!strcmp(myAddr, intClient)) {
|
||||||
printf("OK\n");
|
fprintf(LOG_OUT, "OK\n");
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("CONFLICT - %s %s\n", desc, intClient);
|
fprintf(LOG_OUT, "CONFLICT - %s %s\n", desc, intClient);
|
||||||
snprintf(conflictMessage, 128, "%s (%s)", desc, intClient);
|
snprintf(conflictMessage, 128, "%s (%s)", desc, intClient);
|
||||||
return CONFLICTED;
|
return CONFLICTED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("ERROR %d\n", err);
|
fprintf(LOG_OUT, "ERROR %d\n", err);
|
||||||
return ERRORED;
|
return ERRORED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -582,23 +778,27 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, PSOCKADDR_IN reportedWanAddr, bool* fo
|
|||||||
natpmp_t natpmp;
|
natpmp_t natpmp;
|
||||||
bool foundUpnpIgd = false;
|
bool foundUpnpIgd = false;
|
||||||
|
|
||||||
|
wanAddr->sin_family = AF_INET;
|
||||||
|
reportedWanAddr->sin_family = AF_INET;
|
||||||
*foundPortForwardingRules = false;
|
*foundPortForwardingRules = false;
|
||||||
*igdDisconnected = false;
|
*igdDisconnected = false;
|
||||||
|
|
||||||
bool gotReportedWanAddress = false;
|
bool gotReportedWanAddress = false;
|
||||||
int natPmpErr = initnatpmp(&natpmp, 0, 0);
|
int natPmpErr = initnatpmp(&natpmp, 0, 0);
|
||||||
if (natPmpErr != 0) {
|
if (natPmpErr != 0) {
|
||||||
printf("initnatpmp() failed: %d\n", natPmpErr);
|
fprintf(LOG_OUT, "initnatpmp() failed: %d\n", natPmpErr);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
natPmpErr = sendpublicaddressrequest(&natpmp);
|
natPmpErr = sendpublicaddressrequest(&natpmp);
|
||||||
if (natPmpErr < 0) {
|
if (natPmpErr < 0) {
|
||||||
printf("sendpublicaddressrequest() failed: %d\n", natPmpErr);
|
fprintf(LOG_OUT, "sendpublicaddressrequest() failed: %d\n", natPmpErr);
|
||||||
closenatpmp(&natpmp);
|
closenatpmp(&natpmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
fprintf(CONSOLE_OUT, "\tTesting UPnP...\n");
|
||||||
|
|
||||||
int upnpErr;
|
int upnpErr;
|
||||||
struct UPNPDev* ipv4Devs = upnpDiscoverAll(5000, nullptr, nullptr, UPNP_LOCAL_PORT_ANY, 0, 2, &upnpErr);
|
struct UPNPDev* ipv4Devs = upnpDiscoverAll(5000, nullptr, nullptr, UPNP_LOCAL_PORT_ANY, 0, 2, &upnpErr);
|
||||||
|
|
||||||
@@ -614,19 +814,19 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, PSOCKADDR_IN reportedWanAddr, bool* fo
|
|||||||
if (ret == 2) {
|
if (ret == 2) {
|
||||||
*igdDisconnected = true;
|
*igdDisconnected = true;
|
||||||
}
|
}
|
||||||
printf("Discovered UPnP IGD at: %s\n", urls.controlURL);
|
fprintf(LOG_OUT, "Discovered UPnP IGD at: %s\n", urls.controlURL);
|
||||||
printf("Detecting WAN IP address via UPnP...");
|
fprintf(LOG_OUT, "Detecting WAN IP address via UPnP...");
|
||||||
ret = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, wanAddrStr);
|
ret = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, wanAddrStr);
|
||||||
if (ret == UPNPCOMMAND_SUCCESS && strlen(wanAddrStr) > 0) {
|
if (ret == UPNPCOMMAND_SUCCESS && strlen(wanAddrStr) > 0) {
|
||||||
reportedWanAddr->sin_addr.S_un.S_addr = inet_addr(wanAddrStr);
|
reportedWanAddr->sin_addr.S_un.S_addr = inet_addr(wanAddrStr);
|
||||||
printf("%s\n", wanAddrStr);
|
fprintf(LOG_OUT, "%s\n", wanAddrStr);
|
||||||
|
|
||||||
if (reportedWanAddr->sin_addr.S_un.S_addr != 0) {
|
if (reportedWanAddr->sin_addr.S_un.S_addr != 0) {
|
||||||
gotReportedWanAddress = true;
|
gotReportedWanAddress = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("FAILED %d\n", ret);
|
fprintf(LOG_OUT, "FAILED %d\n", ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
char conflictMessage[512];
|
char conflictMessage[512];
|
||||||
@@ -652,19 +852,21 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, PSOCKADDR_IN reportedWanAddr, bool* fo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("No UPnP IGD detected\n");
|
fprintf(LOG_OUT, "No UPnP IGD detected\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
FreeUPNPUrls(&urls);
|
FreeUPNPUrls(&urls);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("No UPnP devices detected\n");
|
fprintf(LOG_OUT, "No UPnP devices detected\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the delay of upnpDiscoverAll() to also allow the NAT-PMP endpoint time to respond
|
// Use the delay of upnpDiscoverAll() to also allow the NAT-PMP endpoint time to respond
|
||||||
if (natPmpErr >= 0) {
|
if (natPmpErr >= 0) {
|
||||||
printf("Detecting WAN IP address via NAT-PMP...");
|
fprintf(CONSOLE_OUT, "\tTesting NAT-PMP...\n");
|
||||||
|
|
||||||
|
fprintf(LOG_OUT, "Detecting WAN IP address via NAT-PMP...");
|
||||||
|
|
||||||
natpmpresp_t response;
|
natpmpresp_t response;
|
||||||
natPmpErr = readnatpmpresponseorretry(&natpmp, &response);
|
natPmpErr = readnatpmpresponseorretry(&natpmp, &response);
|
||||||
@@ -674,7 +876,7 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, PSOCKADDR_IN reportedWanAddr, bool* fo
|
|||||||
char addrStr[64];
|
char addrStr[64];
|
||||||
reportedWanAddr->sin_addr = response.pnu.publicaddress.addr;
|
reportedWanAddr->sin_addr = response.pnu.publicaddress.addr;
|
||||||
inet_ntop(AF_INET, &response.pnu.publicaddress.addr, addrStr, sizeof(addrStr));
|
inet_ntop(AF_INET, &response.pnu.publicaddress.addr, addrStr, sizeof(addrStr));
|
||||||
printf("%s\n", addrStr);
|
fprintf(LOG_OUT, "%s\n", addrStr);
|
||||||
if (reportedWanAddr->sin_addr.S_un.S_addr != 0) {
|
if (reportedWanAddr->sin_addr.S_un.S_addr != 0) {
|
||||||
gotReportedWanAddress = true;
|
gotReportedWanAddress = true;
|
||||||
|
|
||||||
@@ -686,19 +888,21 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, PSOCKADDR_IN reportedWanAddr, bool* fo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("FAILED %d\n", natPmpErr);
|
fprintf(LOG_OUT, "FAILED %d\n", natPmpErr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Detecting WAN IP address via STUN...");
|
fprintf(LOG_OUT, "Detecting WAN IP address via STUN...");
|
||||||
if (!getExternalAddressPortIP4(IPPROTO_UDP, 0, wanAddr) && !getExternalAddressPortIP4(IPPROTO_TCP, 0, wanAddr)) {
|
fprintf(CONSOLE_OUT, "\tTesting STUN...\n");
|
||||||
|
|
||||||
|
if (!getExternalAddressPortIP4(0, wanAddr)) {
|
||||||
DisplayMessage("Unable to determine your public IP address. Please check your Internet connection or try again in a few minutes.");
|
DisplayMessage("Unable to determine your public IP address. Please check your Internet connection or try again in a few minutes.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
char addrStr[64];
|
char addrStr[64];
|
||||||
inet_ntop(AF_INET, &wanAddr->sin_addr, addrStr, sizeof(addrStr));
|
inet_ntop(AF_INET, &wanAddr->sin_addr, addrStr, sizeof(addrStr));
|
||||||
printf("%s\n", addrStr);
|
fprintf(LOG_OUT, "%s\n", addrStr);
|
||||||
|
|
||||||
if (!gotReportedWanAddress) {
|
if (!gotReportedWanAddress) {
|
||||||
// If we didn't get anything from UPnP or NAT-PMP, just populate the reported
|
// If we didn't get anything from UPnP or NAT-PMP, just populate the reported
|
||||||
@@ -749,25 +953,25 @@ bool IsDoubleNAT(PSOCKADDR_IN wanAddr)
|
|||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
WSADATA wsaData;
|
WSADATA wsaData;
|
||||||
SYSTEMTIME time;
|
SYSTEMTIME time;
|
||||||
char timeString[MAX_PATH + 1] = {};
|
char timeString[MAX_PATH + 1] = {};
|
||||||
|
|
||||||
char tempPath[MAX_PATH + 1];
|
char tempPath[MAX_PATH + 1];
|
||||||
GetTempPathA(sizeof(tempPath), tempPath);
|
GetTempPathA(sizeof(tempPath), tempPath);
|
||||||
|
|
||||||
snprintf(logFilePath, sizeof(logFilePath), "%s\\%s", tempPath, "mis-test.log");
|
snprintf(logFilePath, sizeof(logFilePath), "%s\\%s", tempPath, "mis-test.log");
|
||||||
freopen(logFilePath, "w", stdout);
|
freopen(logFilePath, "w", LOG_OUT);
|
||||||
|
|
||||||
// Print a log header
|
// Print a log header
|
||||||
printf("Moonlight Internet Streaming Tester v" VER_VERSION_STR "\n");
|
fprintf(LOG_OUT, "Moonlight Internet Streaming Tester v" VER_VERSION_STR "\n");
|
||||||
|
|
||||||
// Print the current time
|
// Print the current time
|
||||||
GetSystemTime(&time);
|
GetSystemTime(&time);
|
||||||
GetTimeFormatA(LOCALE_SYSTEM_DEFAULT, 0, &time, "hh':'mm':'ss tt", timeString, ARRAYSIZE(timeString));
|
GetTimeFormatA(LOCALE_SYSTEM_DEFAULT, 0, &time, "hh':'mm':'ss tt", timeString, ARRAYSIZE(timeString));
|
||||||
printf("The current UTC time is: %s\n", timeString);
|
fprintf(LOG_OUT, "The current UTC time is: %s\n", timeString);
|
||||||
|
|
||||||
// Print a console header
|
// Print a console header
|
||||||
fprintf(stderr, "Moonlight Internet Streaming Tester v" VER_VERSION_STR "\n\n");
|
fprintf(CONSOLE_OUT, "Moonlight Internet Streaming Tester v" VER_VERSION_STR "\n\n");
|
||||||
|
|
||||||
int err = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
int err = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||||
if (err != NO_ERROR) {
|
if (err != NO_ERROR) {
|
||||||
@@ -775,13 +979,36 @@ int main(int argc, char* argv[])
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "Checking if GameStream is enabled...\n");
|
fprintf(CONSOLE_OUT, "Checking if GameStream is enabled...\n");
|
||||||
|
|
||||||
// First check if GameStream is enabled
|
// First check if GameStream is enabled
|
||||||
if (!IsGameStreamEnabled()) {
|
if (!IsGameStreamEnabled()) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fprintf(CONSOLE_OUT, "Checking for anti-virus and firewall software...\n");
|
||||||
|
|
||||||
|
char wmicBuf[8192];
|
||||||
|
if (ExecuteCommand("WMIC /Node:localhost /Namespace:\\\\root\\SecurityCenter2 Path AntiVirusProduct Get displayName", wmicBuf, sizeof(wmicBuf))) {
|
||||||
|
fprintf(LOG_OUT, "AV products:\n%s", wmicBuf);
|
||||||
|
}
|
||||||
|
if (ExecuteCommand("WMIC /Node:localhost /Namespace:\\\\root\\SecurityCenter2 Path FirewallProduct Get displayName", wmicBuf, sizeof(wmicBuf))) {
|
||||||
|
fprintf(LOG_OUT, "Firewall products:\n%s", wmicBuf);
|
||||||
|
if (!strstr(wmicBuf, "No Instance(s) Available.") && !strstr(wmicBuf, "Invalid namespace")) {
|
||||||
|
DisplayMessage("Detected anti-virus and/or firewall software installed on this system. This software may interfere with NVIDIA GameStream.\n\n"
|
||||||
|
"Please try temporarily disabling your anti-virus or firewall software if you experience connection issues with Moonlight.",
|
||||||
|
"https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting", MpInfo, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(CONSOLE_OUT, "Checking network connections...\n");
|
||||||
|
|
||||||
|
if (FindDuplicateDefaultInterfaces()) {
|
||||||
|
DisplayMessage("This computer appears to have more than one connection to the same network (like both WiFi and Ethernet, for example).\n\n"
|
||||||
|
"Please disconnect the extra connection(s) to ensure hosting works reliably over the Internet. If you are connected via WiFi and Ethernet, try disabling your WiFi connection.",
|
||||||
|
"https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#multiple-connections-error", MpWarn, false);
|
||||||
|
}
|
||||||
|
|
||||||
union {
|
union {
|
||||||
SOCKADDR_STORAGE ss;
|
SOCKADDR_STORAGE ss;
|
||||||
SOCKADDR_IN sin;
|
SOCKADDR_IN sin;
|
||||||
@@ -790,63 +1017,180 @@ int main(int argc, char* argv[])
|
|||||||
char msgBuf[2048];
|
char msgBuf[2048];
|
||||||
char portMsgBuf[512];
|
char portMsgBuf[512];
|
||||||
|
|
||||||
fprintf(stderr, "Testing GameStream connectivity on this PC...\n");
|
fprintf(CONSOLE_OUT, "Testing GameStream connectivity on this PC...\n");
|
||||||
|
|
||||||
// Try to connect via IPv4 loopback
|
// Try to connect via IPv4 loopback
|
||||||
ss = {};
|
ss = {};
|
||||||
sin.sin_family = AF_INET;
|
sin.sin_family = AF_INET;
|
||||||
sin.sin_addr = in4addr_loopback;
|
sin.sin_addr = in4addr_loopback;
|
||||||
printf("Testing GameStream ports via loopback\n");
|
fprintf(LOG_OUT, "Testing GameStream ports via loopback\n");
|
||||||
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf))) {
|
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf), false, true)) {
|
||||||
snprintf(msgBuf, sizeof(msgBuf),
|
snprintf(msgBuf, sizeof(msgBuf),
|
||||||
"Local GameStream connectivity check failed.\n\nFirst, try reinstalling GeForce Experience. If that doesn't resolve the problem, try temporarily disabling your antivirus and firewall.");
|
"Local GameStream connectivity check failed.\n\nFirst, try reinstalling GeForce Experience. If that doesn't resolve the problem, try temporarily disabling your antivirus and firewall.");
|
||||||
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting");
|
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We do a special limited test pass for ZeroTier
|
||||||
|
if (IsZeroTierInstalled()) {
|
||||||
|
fprintf(LOG_OUT, "Found ZeroTier installed\n");
|
||||||
|
|
||||||
if (!FindLocalInterfaceIP4Address(&sin)) {
|
if (!FindZeroTierInterfaceAddress(&ss)) {
|
||||||
|
DisplayMessage("ZeroTier appears to be installed on this PC, but it's not connected to a network.\n\n"
|
||||||
|
"If you are trying to host with ZeroTier, connect to your ZeroTier network and restart this test.\n\n"
|
||||||
|
"If not, click OK and this test will continue assuming you aren't using ZeroTier.",
|
||||||
|
"https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide#zerotier", MpInfo, false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
char zeroTierAddrStr[INET_ADDRSTRLEN];
|
||||||
|
|
||||||
|
inet_ntop(AF_INET, &sin.sin_addr, zeroTierAddrStr, sizeof(zeroTierAddrStr));
|
||||||
|
|
||||||
|
fprintf(LOG_OUT, "Found ZeroTier connected with address: %s\n", zeroTierAddrStr);
|
||||||
|
|
||||||
|
if (strstr(zeroTierAddrStr, "169.254.")) {
|
||||||
|
DisplayMessage("ZeroTier is active, but this PC has not been authorized to connect to your ZeroTier network.\n\n"
|
||||||
|
"Make sure you check the Auth checkbox for all network members on the ZeroTier Networks webpage.",
|
||||||
|
"https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide#zerotier");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to connect via ZeroTier address
|
||||||
|
fprintf(CONSOLE_OUT, "Testing GameStream connectivity using ZeroTier...\n");
|
||||||
|
fprintf(LOG_OUT, "Testing GameStream ports via ZeroTier\n");
|
||||||
|
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf), false, true)) {
|
||||||
|
snprintf(msgBuf, sizeof(msgBuf),
|
||||||
|
"ZeroTier connectivity check failed. This is almost always caused by a firewall on your computer blocking the connection.\n\nTry temporarily disabling your antivirus and firewall.");
|
||||||
|
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get here, our testing is complete for ZeroTier
|
||||||
|
snprintf(msgBuf, sizeof(msgBuf), "This PC is ready to host over the Internet with ZeroTier!\n\n"
|
||||||
|
"Don't forget to connect to your ZeroTier network on your client before streaming over the Internet.\n\n"
|
||||||
|
"After connecting ZeroTier, type following address into Moonlight's Add PC dialog: %s", zeroTierAddrStr);
|
||||||
|
DisplayMessage(msgBuf, nullptr, MpInfo);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FindLocalInterfaceIPAddress(AF_INET, &ss) && !FindLocalInterfaceIPAddress(AF_INET6, &ss)) {
|
||||||
DisplayMessage("Unable to perform GameStream connectivity check. Please check your Internet connection and try again.");
|
DisplayMessage("Unable to perform GameStream connectivity check. Please check your Internet connection and try again.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "Testing GameStream connectivity on your local network...\n");
|
fprintf(CONSOLE_OUT, "Testing GameStream connectivity on your local network...\n");
|
||||||
|
|
||||||
// Try to connect via LAN IPv4 address
|
// Try to connect via LAN address
|
||||||
printf("Testing GameStream ports via local network\n");
|
fprintf(LOG_OUT, "Testing GameStream ports via local network\n");
|
||||||
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf))) {
|
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf), false, true)) {
|
||||||
snprintf(msgBuf, sizeof(msgBuf),
|
snprintf(msgBuf, sizeof(msgBuf),
|
||||||
"Local network GameStream connectivity check failed. This is almost always caused by a firewall on your computer blocking the connection.\n\nTry temporarily disabling your antivirus and firewall.");
|
"Local network GameStream connectivity check failed. This is almost always caused by a firewall on your computer blocking the connection.\n\nTry temporarily disabling your antivirus and firewall.");
|
||||||
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting");
|
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "Detecting public IP address...\n");
|
bool igdDisconnected;
|
||||||
|
|
||||||
bool rulesFound, igdDisconnected;
|
|
||||||
SOCKADDR_IN locallyReportedWanAddr;
|
SOCKADDR_IN locallyReportedWanAddr;
|
||||||
if (!CheckWANAccess(&sin, &locallyReportedWanAddr, &rulesFound, &igdDisconnected)) {
|
char wanAddrStr[INET_ADDRSTRLEN];
|
||||||
return -1;
|
|
||||||
|
if (ss.ss_family == AF_INET) {
|
||||||
|
bool rulesFound;
|
||||||
|
|
||||||
|
fprintf(CONSOLE_OUT, "Detecting public IP address...\n");
|
||||||
|
|
||||||
|
if (!CheckWANAccess(&sin, &locallyReportedWanAddr, &rulesFound, &igdDisconnected)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(CONSOLE_OUT, "Testing GameStream connectivity over the Internet...\n");
|
||||||
|
|
||||||
|
// Detect a double NAT by detecting STUN and and UPnP mismatches
|
||||||
|
if (sin.sin_addr.S_un.S_addr != locallyReportedWanAddr.sin_addr.S_un.S_addr) {
|
||||||
|
fprintf(LOG_OUT, "Testing GameStream ports via UPnP/NAT-PMP reported WAN address\n");
|
||||||
|
|
||||||
|
// We don't actually care about the outcome here but it's nice to have in logs
|
||||||
|
// to determine whether solving the double NAT will actually make Moonlight work.
|
||||||
|
TestAllPorts((PSOCKADDR_STORAGE)&locallyReportedWanAddr, portMsgBuf, sizeof(portMsgBuf), false, false);
|
||||||
|
|
||||||
|
fprintf(LOG_OUT, "Detected inconsistency between UPnP/NAT-PMP and STUN reported WAN addresses!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
inet_ntop(AF_INET, &sin.sin_addr, wanAddrStr, sizeof(wanAddrStr));
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
// Detect a double NAT by detecting STUN and and UPnP mismatches
|
// Go directly to the relay check if we have only IPv6 connectivity
|
||||||
if (sin.sin_addr.S_un.S_addr != locallyReportedWanAddr.sin_addr.S_un.S_addr) {
|
igdDisconnected = false;
|
||||||
printf("Testing GameStream ports via UPnP/NAT-PMP reported WAN address\n");
|
locallyReportedWanAddr = {};
|
||||||
|
goto RelayCheck;
|
||||||
// We don't actually care about the outcome here but it's nice to have in logs
|
|
||||||
// to determine whether solving the double NAT will actually make Moonlight work.
|
|
||||||
TestAllPorts((PSOCKADDR_STORAGE)&locallyReportedWanAddr, portMsgBuf, sizeof(portMsgBuf));
|
|
||||||
|
|
||||||
printf("Detected inconsistency between UPnP/NAT-PMP and STUN reported WAN addresses!\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "Testing GameStream connectivity over the Internet...\n");
|
|
||||||
|
|
||||||
char wanAddrStr[64];
|
|
||||||
inet_ntop(AF_INET, &sin.sin_addr, wanAddrStr, sizeof(wanAddrStr));
|
|
||||||
|
|
||||||
// Try to connect via WAN IPv4 address
|
// Try to connect via WAN IPv4 address
|
||||||
printf("Testing GameStream ports via STUN-reported WAN address\n");
|
fprintf(LOG_OUT, "Testing GameStream ports via STUN-reported WAN address\n");
|
||||||
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf))) {
|
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf), false, true)) {
|
||||||
|
RelayCheck:
|
||||||
|
struct addrinfo hint = {};
|
||||||
|
struct addrinfo* result;
|
||||||
|
|
||||||
|
// We can get here if the router doesn't support NAT reflection.
|
||||||
|
// We'll need to call out to our loopback server to get a second opinion.
|
||||||
|
|
||||||
|
hint.ai_family = AF_UNSPEC;
|
||||||
|
hint.ai_flags = AI_ADDRCONFIG;
|
||||||
|
err = getaddrinfo("loopback.moonlight-stream.org", NULL, &hint, &result);
|
||||||
|
if (err != 0 || result == NULL) {
|
||||||
|
fprintf(LOG_OUT, "getaddrinfo() failed: %d\n", err);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bool allPortsFailedOnV4 = true;
|
||||||
|
|
||||||
|
// First try the relay server over IPv4. If this passes, it's considered a full success
|
||||||
|
fprintf(LOG_OUT, "Testing GameStream ports via IPv4 loopback server\n");
|
||||||
|
for (struct addrinfo* current = result; current != NULL; current = current->ai_next) {
|
||||||
|
if (current->ai_family == AF_INET) {
|
||||||
|
fprintf(CONSOLE_OUT, "Testing GameStream connectivity over the Internet using a relay server...\n");
|
||||||
|
if (TestAllPorts((PSOCKADDR_STORAGE)current->ai_addr, portMsgBuf, sizeof(portMsgBuf), true, true, &allPortsFailedOnV4)) {
|
||||||
|
freeaddrinfo(result);
|
||||||
|
goto AllTestsPassed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If that fails, try the relay server over IPv6. If this passes, it will be a partial success
|
||||||
|
fprintf(LOG_OUT, "Testing GameStream ports via IPv6 loopback server\n");
|
||||||
|
for (struct addrinfo* current = result; current != NULL; current = current->ai_next) {
|
||||||
|
if (current->ai_family == AF_INET6) {
|
||||||
|
fprintf(CONSOLE_OUT, "Testing GameStream connectivity over the Internet using an IPv6 relay server...\n");
|
||||||
|
// Pass the portMsgBuf only if we've detected an IPv6-only setup. Otherwise, we want to preserve
|
||||||
|
// the failing ports from the IPv4 to display in the error dialog.
|
||||||
|
if (TestAllPorts((PSOCKADDR_STORAGE)current->ai_addr,
|
||||||
|
ss.ss_family == AF_INET6 ? portMsgBuf : NULL,
|
||||||
|
ss.ss_family == AF_INET6 ? sizeof(portMsgBuf) : 0, true, true)) {
|
||||||
|
// We will terminate the test at the IPv6 limited connectivity warning in the following cases:
|
||||||
|
// 1) Double-NAT/CGN - indicates the connection is fundamentally limited to IPv6 for end-to-end connectivity
|
||||||
|
// 2) IPv6-only - indicates the connection is fundamentally limited to IPv6 for all connectivity
|
||||||
|
// 3) All ports failed the test with our IPv4 relay - This is one final heuristic to weed out IPv4 misconfigurations. If we have some ports open,
|
||||||
|
// it clearly indicates IPv4 support is possible, just currently misconfigured.
|
||||||
|
//
|
||||||
|
// Our last (implicit) heuristic is that we actually managed to establish an IPv6 connection. This means that there was either
|
||||||
|
// no IPv6 firewall (hopefully not) or that we were able to talk to a PCP/IGDv6 gateway to allow us through. Hopefully if we have
|
||||||
|
// a gateway that is unresponsive to UPnP/NAT-PMP, we wouldn't even be able to establish this connection so we would inherently fall
|
||||||
|
// to the checks below for IPv4 issues.
|
||||||
|
if (IsDoubleNAT(&locallyReportedWanAddr) || igdDisconnected || IsPossibleCGN(&locallyReportedWanAddr) || ss.ss_family == AF_INET6 || allPortsFailedOnV4) {
|
||||||
|
snprintf(msgBuf, sizeof(msgBuf), "This PC has limited connectivity for Internet hosting. It will work only for clients on certain networks.\n\n"
|
||||||
|
"If you want to try streaming with this configuration, you must pair Moonlight to your gaming PC from your home network before trying to stream over the Internet.\n\n"
|
||||||
|
"To get full connectivity, please contact your ISP and ask for a \"public IP address\" which they may offer for free upon request. For more information and workarounds, click the Help button.");
|
||||||
|
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#limited-connectivity-for-hosting-error", MpWarn);
|
||||||
|
freeaddrinfo(result);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(result);
|
||||||
|
}
|
||||||
|
|
||||||
// Many UPnP devices report IGD disconnected when double-NATed. If it was really offline,
|
// Many UPnP devices report IGD disconnected when double-NATed. If it was really offline,
|
||||||
// we probably would not have even gotten past STUN.
|
// we probably would not have even gotten past STUN.
|
||||||
if (IsDoubleNAT(&locallyReportedWanAddr) || igdDisconnected) {
|
if (IsDoubleNAT(&locallyReportedWanAddr) || igdDisconnected) {
|
||||||
@@ -854,38 +1198,24 @@ int main(int argc, char* argv[])
|
|||||||
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#connected-through-another-router-error");
|
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#connected-through-another-router-error");
|
||||||
}
|
}
|
||||||
else if (IsPossibleCGN(&locallyReportedWanAddr)) {
|
else if (IsPossibleCGN(&locallyReportedWanAddr)) {
|
||||||
snprintf(msgBuf, sizeof(msgBuf), "Your ISP is running a Carrier-Grade NAT that is preventing you from hosting services like Moonlight on the Internet. Click the Help button for guidance on fixing this issue.");
|
snprintf(msgBuf, sizeof(msgBuf), "Your ISP is running a Carrier-Grade NAT that is preventing you from hosting services like Moonlight on the Internet.\n\n"
|
||||||
|
"Ask your ISP for a \"public IP address\" which they may offer for free upon request. For more information and workarounds, click the Help button.");
|
||||||
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#carrier-grade-nat-error");
|
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#carrier-grade-nat-error");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
struct hostent* host;
|
snprintf(msgBuf, sizeof(msgBuf), "Internet GameStream connectivity check failed.\n\n"
|
||||||
|
"Check that UPnP is enabled in your router settings. For more information and workarounds, click the Help button.\n\n"
|
||||||
|
"The following ports were not forwarded properly:\n%s", portMsgBuf);
|
||||||
|
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#internet-gamestream-connectivity-check-error");
|
||||||
|
}
|
||||||
|
|
||||||
// We can get here if the router doesn't support NAT reflection.
|
|
||||||
// We'll need to call out to our loopback server to get a second opinion.
|
|
||||||
|
|
||||||
printf("Testing GameStream ports via loopback server\n");
|
|
||||||
|
|
||||||
host = gethostbyname("loopback.moonlight-stream.org");
|
|
||||||
if (host != nullptr) {
|
|
||||||
sin.sin_addr = *(struct in_addr*)host->h_addr;
|
|
||||||
if (TestAllPorts((PSOCKADDR_STORAGE)&sin, portMsgBuf, sizeof(portMsgBuf))) {
|
|
||||||
goto AllTestsPassed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
printf("gethostbyname() failed: %d\n", WSAGetLastError());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(msgBuf, sizeof(msgBuf), "Internet GameStream connectivity check failed. Click the Help button for guidance on fixing this issue.\n\nThe following ports were not forwarded properly:\n%s", portMsgBuf);
|
|
||||||
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#internet-gamestream-connectivity-check-error");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
AllTestsPassed:
|
AllTestsPassed:
|
||||||
snprintf(msgBuf, sizeof(msgBuf), "This PC is ready to stream over the Internet!\n\n"
|
snprintf(msgBuf, sizeof(msgBuf), "This PC is ready to host over the Internet!\n\n"
|
||||||
"For the easiest setup, you should pair Moonlight to your PC from your home network before trying to stream over the Internet.\n\n"
|
"For the easiest setup, you should pair Moonlight to your gaming PC from your home network before trying to stream over the Internet.\n\n"
|
||||||
"If you can't, you can type the following address into Moonlight's Add PC dialog: %s", wanAddrStr);
|
"If you can't, you can type the following address into Moonlight's Add PC dialog: %s", wanAddrStr);
|
||||||
DisplayMessage(msgBuf, nullptr, MpInfo);
|
DisplayMessage(msgBuf, nullptr, MpInfo);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define _CRT_RAND_S
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#include <Iphlpapi.h>
|
||||||
|
#include <WinSock2.h>
|
||||||
|
|
||||||
|
#include "..\version.h"
|
||||||
|
|
||||||
|
#define CONSOLE_OUT stdout
|
||||||
|
#define LOG_OUT stderr
|
||||||
|
|
||||||
|
bool getExternalAddressPortIP4(unsigned short localPort, PSOCKADDR_IN wanAddr);
|
||||||
@@ -167,6 +167,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\version.h" />
|
<ClInclude Include="..\version.h" />
|
||||||
|
<ClInclude Include="mist.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
|||||||
@@ -26,6 +26,9 @@
|
|||||||
<ClInclude Include="..\version.h">
|
<ClInclude Include="..\version.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="mist.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="mist.rc">
|
<ResourceCompile Include="mist.rc">
|
||||||
|
|||||||
+40
-59
@@ -1,11 +1,4 @@
|
|||||||
#define _CRT_RAND_S
|
#include "mist.h"
|
||||||
#define _CRT_SECURE_NO_WARNINGS
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
|
||||||
#include <Windows.h>
|
|
||||||
#include <WinSock2.h>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
@@ -45,7 +38,7 @@ typedef struct _STUN_MESSAGE {
|
|||||||
|
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN wanAddr)
|
bool getExternalAddressPortIP4(unsigned short localPort, PSOCKADDR_IN wanAddr)
|
||||||
{
|
{
|
||||||
SOCKET sock;
|
SOCKET sock;
|
||||||
STUN_MESSAGE reqMsg;
|
STUN_MESSAGE reqMsg;
|
||||||
@@ -53,32 +46,41 @@ bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN
|
|||||||
int bytesRead;
|
int bytesRead;
|
||||||
int tries;
|
int tries;
|
||||||
int timeout;
|
int timeout;
|
||||||
|
int err;
|
||||||
PSTUN_ATTRIBUTE_HEADER attribute;
|
PSTUN_ATTRIBUTE_HEADER attribute;
|
||||||
PSTUN_MAPPED_IPV4_ADDRESS_ATTRIBUTE ipv4Attrib;
|
PSTUN_MAPPED_IPV4_ADDRESS_ATTRIBUTE ipv4Attrib;
|
||||||
struct hostent *host;
|
struct addrinfo* result;
|
||||||
|
struct addrinfo hints;
|
||||||
union {
|
union {
|
||||||
STUN_MESSAGE hdr;
|
STUN_MESSAGE hdr;
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
} resp;
|
} resp;
|
||||||
|
|
||||||
host = gethostbyname("stun.moonlight-stream.org");
|
memset(&hints, 0, sizeof(hints));
|
||||||
if (host == nullptr) {
|
hints.ai_family = AF_INET;
|
||||||
printf("gethostbyname() failed: %d\n", WSAGetLastError());
|
hints.ai_flags = AI_ADDRCONFIG;
|
||||||
|
hints.ai_socktype = SOCK_DGRAM;
|
||||||
|
hints.ai_protocol = IPPROTO_UDP;
|
||||||
|
err = getaddrinfo("stun.moonlight-stream.org", "3478", &hints, &result);
|
||||||
|
if (err != 0 || result == NULL) {
|
||||||
|
fprintf(LOG_OUT, "getaddrinfo() failed: %d\n", err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
sock = socket(AF_INET, proto == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM, proto);
|
sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
|
||||||
if (sock == INVALID_SOCKET) {
|
if (sock == INVALID_SOCKET) {
|
||||||
printf("socket() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "socket() failed: %d\n", WSAGetLastError());
|
||||||
|
freeaddrinfo(result);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sockaddr_in bindAddr = {};
|
struct sockaddr_in bindAddr = {};
|
||||||
bindAddr.sin_family = AF_INET;
|
bindAddr.sin_family = hints.ai_family;
|
||||||
bindAddr.sin_port = htons(localPort);
|
bindAddr.sin_port = htons(localPort);
|
||||||
if (bind(sock, (struct sockaddr*)&bindAddr, sizeof(bindAddr)) == SOCKET_ERROR) {
|
if (bind(sock, (struct sockaddr*)&bindAddr, sizeof(bindAddr)) == SOCKET_ERROR) {
|
||||||
printf("bind() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "bind() failed: %d\n", WSAGetLastError());
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
|
freeaddrinfo(result);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,36 +91,13 @@ bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN
|
|||||||
reqMsg.transactionId[i] = rand();
|
reqMsg.transactionId[i] = rand();
|
||||||
}
|
}
|
||||||
|
|
||||||
SOCKADDR_IN stunAddr = {};
|
|
||||||
stunAddr.sin_family = AF_INET;
|
|
||||||
stunAddr.sin_port = htons(STUN_PORT);
|
|
||||||
stunAddr.sin_addr = *(struct in_addr*)host->h_addr;
|
|
||||||
|
|
||||||
// We'll connect() even for UDP so we can use send()/recv() and share more code
|
|
||||||
if (connect(sock, (struct sockaddr*)&stunAddr, sizeof(stunAddr)) == SOCKET_ERROR) {
|
|
||||||
printf("connect() failed: %d\n", WSAGetLastError());
|
|
||||||
closesocket(sock);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For UDP, we'll do 3 iterations of 1 second each. For TCP,
|
|
||||||
// we'll do one iteration with a 3 second wait.
|
|
||||||
if (proto == IPPROTO_TCP) {
|
|
||||||
tries = 1;
|
|
||||||
timeout = STUN_RECV_TIMEOUT_SEC;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
tries = STUN_RECV_TIMEOUT_SEC;
|
|
||||||
timeout = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytesRead = 0;
|
bytesRead = 0;
|
||||||
for (i = 0; i < tries; i++) {
|
for (i = 0; i < STUN_RECV_TIMEOUT_SEC; i++) {
|
||||||
// Retransmit the request every second until the timeout elapses
|
// Retransmit the request every second to all resolved IP addresses until the timeout elapses
|
||||||
if (send(sock, (char *)&reqMsg, sizeof(reqMsg), 0) == SOCKET_ERROR) {
|
for (struct addrinfo* current = result; current != NULL; current = current->ai_next) {
|
||||||
printf("send() failed: %d\n", WSAGetLastError());
|
if (sendto(sock, (char*)&reqMsg, sizeof(reqMsg), 0, current->ai_addr, current->ai_addrlen) == SOCKET_ERROR) {
|
||||||
closesocket(sock);
|
fprintf(LOG_OUT, "sendto() failed: %d\n", WSAGetLastError());
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fd_set fds;
|
fd_set fds;
|
||||||
@@ -126,7 +105,7 @@ bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN
|
|||||||
FD_SET(sock, &fds);
|
FD_SET(sock, &fds);
|
||||||
|
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
tv.tv_sec = timeout;
|
tv.tv_sec = 1;
|
||||||
tv.tv_usec = 0;
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
int selectRes = select(0, &fds, nullptr, nullptr, &tv);
|
int selectRes = select(0, &fds, nullptr, nullptr, &tv);
|
||||||
@@ -135,40 +114,42 @@ bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (selectRes == SOCKET_ERROR) {
|
else if (selectRes == SOCKET_ERROR) {
|
||||||
printf("select() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "select() failed: %d\n", WSAGetLastError());
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
|
freeaddrinfo(result);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error handling is below
|
// Error handling is below
|
||||||
bytesRead = recv(sock, resp.buf, sizeof(resp.buf), 0);
|
bytesRead = recvfrom(sock, resp.buf, sizeof(resp.buf), 0, NULL, NULL);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(result);
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
|
|
||||||
if (bytesRead == 0) {
|
if (bytesRead == 0) {
|
||||||
printf("No response from STUN server\n");
|
fprintf(LOG_OUT, "No response from STUN server\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (bytesRead == SOCKET_ERROR) {
|
else if (bytesRead == SOCKET_ERROR) {
|
||||||
printf("Failed to read STUN binding response: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "Failed to read STUN binding response: %d\n", WSAGetLastError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (bytesRead < sizeof(resp.hdr)) {
|
else if (bytesRead < sizeof(resp.hdr)) {
|
||||||
printf("STUN message truncated: %d\n", bytesRead);
|
fprintf(LOG_OUT, "STUN message truncated: %d\n", bytesRead);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (htonl(resp.hdr.magicCookie) != STUN_MESSAGE_COOKIE) {
|
else if (htonl(resp.hdr.magicCookie) != STUN_MESSAGE_COOKIE) {
|
||||||
printf("Bad STUN cookie value: %x\n", htonl(resp.hdr.magicCookie));
|
fprintf(LOG_OUT, "Bad STUN cookie value: %x\n", htonl(resp.hdr.magicCookie));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (memcmp(reqMsg.transactionId, resp.hdr.transactionId, sizeof(reqMsg.transactionId))) {
|
else if (memcmp(reqMsg.transactionId, resp.hdr.transactionId, sizeof(reqMsg.transactionId))) {
|
||||||
printf("STUN transaction ID mismatch\n");
|
fprintf(LOG_OUT, "STUN transaction ID mismatch\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (htons(resp.hdr.messageType) != STUN_MESSAGE_BINDING_SUCCESS) {
|
else if (htons(resp.hdr.messageType) != STUN_MESSAGE_BINDING_SUCCESS) {
|
||||||
printf("STUN message type mismatch: %x\n", htons(resp.hdr.messageType));
|
fprintf(LOG_OUT, "STUN message type mismatch: %x\n", htons(resp.hdr.messageType));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +157,7 @@ bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN
|
|||||||
bytesRead -= sizeof(resp.hdr);
|
bytesRead -= sizeof(resp.hdr);
|
||||||
while (bytesRead > sizeof(*attribute)) {
|
while (bytesRead > sizeof(*attribute)) {
|
||||||
if (bytesRead < sizeof(*attribute) + htons(attribute->length)) {
|
if (bytesRead < sizeof(*attribute) + htons(attribute->length)) {
|
||||||
printf("STUN attribute out of bounds: %d\n", htons(attribute->length));
|
fprintf(LOG_OUT, "STUN attribute out of bounds: %d\n", htons(attribute->length));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Mask off the comprehension bit
|
// Mask off the comprehension bit
|
||||||
@@ -189,11 +170,11 @@ bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN
|
|||||||
|
|
||||||
ipv4Attrib = (PSTUN_MAPPED_IPV4_ADDRESS_ATTRIBUTE)attribute;
|
ipv4Attrib = (PSTUN_MAPPED_IPV4_ADDRESS_ATTRIBUTE)attribute;
|
||||||
if (htons(ipv4Attrib->hdr.length) != 8) {
|
if (htons(ipv4Attrib->hdr.length) != 8) {
|
||||||
printf("STUN address length mismatch: %d\n", htons(ipv4Attrib->hdr.length));
|
fprintf(LOG_OUT, "STUN address length mismatch: %d\n", htons(ipv4Attrib->hdr.length));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (ipv4Attrib->addressFamily != 1) {
|
else if (ipv4Attrib->addressFamily != 1) {
|
||||||
printf("STUN address family mismatch: %x\n", ipv4Attrib->addressFamily);
|
fprintf(LOG_OUT, "STUN address family mismatch: %x\n", ipv4Attrib->addressFamily);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,6 +188,6 @@ bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("No XOR mapped address found in STUN response!\n");
|
fprintf(LOG_OUT, "No XOR mapped address found in STUN response!\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define VER_VERSION 4,0,0,0
|
#define VER_VERSION 4,3,0,0
|
||||||
#define VER_VERSION_STR "4.0.0.0"
|
#define VER_VERSION_STR "4.3.0.0"
|
||||||
|
|
||||||
#define VER_COMPANYNAME_STR "Moonlight Game Streaming Project"
|
#define VER_COMPANYNAME_STR "Moonlight Game Streaming Project"
|
||||||
#define VER_PRODUCTNAME_STR "Moonlight Internet Streaming Helper"
|
#define VER_PRODUCTNAME_STR "Moonlight Internet Hosting Tool"
|
||||||
|
|||||||
Reference in New Issue
Block a user