Compare commits

...
59 Commits
Author SHA1 Message Date
Cameron Gutman 8d2252c4ac Version 5.0 2020-07-18 17:59:54 -07:00
Cameron Gutman 365b3ccd61 Remove redundant print 2020-07-18 17:55:45 -07:00
Cameron Gutman 74140a8511 Switch to the new loopback server protocol 2020-07-18 17:12:35 -07:00
Cameron Gutman a9bf4ffd70 Always run the loopback server test
It appears that in rare cases NAT reflection can work where real WAN traffic fails
2020-07-18 16:13:56 -07:00
Cameron Gutman 164d8b5467 Fix testing on Windows 7 with GFE 3.20.4 2020-07-18 15:53:47 -07:00
Cameron Gutman f1ebefa59c Add missing success print 2020-07-18 15:53:08 -07:00
Cameron Gutman 3e060fae54 Add supportedOS entries to the embedded manifest 2020-07-18 15:47:39 -07:00
Cameron Gutman f97c04a8cc Switch to WinHTTP instead of WinInet for HTTP testing 2020-07-18 13:27:52 -07:00
Cameron Gutman 85cd20005c Pass the port number directly rather than "https" to getaddrinfo()
Apparently, the services file in drivers\etc can be deleted and GFE still works.
2020-04-10 17:40:30 -07:00
Cameron Gutman e3e4bf608f Version 4.4 2020-02-21 18:03:38 -08:00
Cameron Gutman 0be2ca51e9 Send firewall/AV help to Known app compat issues section 2020-02-21 17:43:35 -08:00
Cameron Gutman 3e1ffce16f Add a check for blocked LAN access 2020-02-21 17:33:06 -08:00
Cameron Gutman c7813a7a0b Add specific help text for power option warnings and RDP error 2020-02-21 16:49:28 -08:00
Cameron Gutman 87fd3ad8e2 Check sleep and hibernation settings 2020-02-21 16:48:22 -08:00
Cameron Gutman 9d3ef0a215 Update miniupnpc to 44366328661826603982d1e0d7ebb4062c5f2bfc 2020-02-21 16:05:26 -08:00
Cameron Gutman ae83853a27 Rebuild libnatpmp with VC v16.4.5 2020-02-21 16:04:49 -08:00
Cameron Gutman 0b8b117cb2 Remove unused variables 2020-02-21 15:53:36 -08:00
Cameron Gutman 0050566124 Add a note to attempt restarting your router if UPnP is not detected 2020-02-21 15:52:21 -08:00
Cameron Gutman cc24472193 Add a check for a screen lock due to RDP 2020-02-21 15:51:51 -08:00
Cameron Gutman 4a11ab0f7f Fix firewall detection warning on non-English systems 2019-09-28 20:28:10 -07:00
Cameron Gutman c1c16edf7d Increase STUN retry limit 2019-09-28 20:26:01 -07:00
Cameron Gutman ae2647f36e Fix false firewall warning on Server SKUs 2019-09-28 16:36:40 -07:00
Cameron Gutman 8e40ef80bd Version 4.3 2019-09-28 16:27:25 -07:00
Cameron Gutman 16bb727b99 Update GS-IPv6-Forwarder to 2.3.1 2019-09-28 16:27:14 -07:00
Cameron Gutman 9194b8e22b Detect AV and firewall software that could interfere with GameStream 2019-09-28 16:06:03 -07:00
Cameron Gutman 2805464e86 Detect multiple connections to the same network 2019-09-28 15:24:53 -07:00
Cameron Gutman 20e7374480 Remove privilege restrictions due to app compat issues 2019-09-28 14:17:36 -07:00
Cameron Gutman 0fdbee831b Revert to an unrestricted service SID to address some app-compat issues with 3rd party Winsock providers 2019-09-28 14:11:32 -07:00
Cameron Gutman b37b99e9c3 Remove the "buggy router" warning that was actually caused by a libnatpmp bug 2019-09-28 14:03:32 -07:00
Cameron Gutman 29bbdc5319 Update libnatpmp to cgutman/libnatpmp eca208e3df44072d71bf3cd61753b4e77e1ddab0 2019-09-28 14:02:41 -07:00
Cameron Gutman 09cd50b12a Version 4.2 2019-08-08 01:34:09 -07:00
Cameron Gutman d60eda6fa8 Update GS IPv6 Forwarder for release 2019-08-08 01:33:45 -07:00
Cameron Gutman cda9bc6be7 Add support for testing ZeroTier configurations 2019-08-08 01:31:09 -07:00
Cameron Gutman 0008fbbcdb Try all resolved STUN servers 2019-08-08 00:02:53 -07:00
Cameron Gutman 620aa4b6d1 Version 4.1 2019-07-30 22:20:01 -07:00
Cameron Gutman b93ef55b65 Fix displaying broken ports when running IPv6-only 2019-07-30 22:12:45 -07:00
Cameron Gutman fdb033f550 Reduce wait time for UDP to offset retransmission delays 2019-07-30 21:48:48 -07:00
Cameron Gutman efdcaed4c1 Don't proceed after WSAEADDRINUSE for the loopback relay to avoid false positives 2019-07-30 21:47:29 -07:00
Cameron Gutman 5a2edb9a36 Add note about log for Discord 2019-07-30 21:45:26 -07:00
Cameron Gutman 798be5fd0a Implement heuristics for falling through to v4 detection even with working v6 2019-07-30 21:31:28 -07:00
Cameron Gutman b0941fee90 Update README.md 2019-07-29 22:54:53 -07:00
Cameron Gutman ebc7703bf2 Update error message text 2019-07-29 22:02:27 -07:00
Cameron Gutman bbf16383f5 Fix uninitialized sin_family field returned from CheckWANAccess() 2019-07-29 21:59:18 -07:00
Cameron Gutman dee2daac5a Fix invalid free on getaddrinfo() failure 2019-07-29 21:58:16 -07:00
Cameron Gutman 51e953c092 Test using the relay even if we believe it may be a double-NAT configuration 2019-07-29 21:25:40 -07:00
Cameron Gutman acb3f5a059 Add support for IPv6-only environments 2019-07-29 21:20:30 -07:00
Cameron Gutman 0bd212e6f1 Fix addrinfo leak 2019-07-29 21:06:18 -07:00
Cameron Gutman d076f881eb Print dialog text to the console 2019-07-29 21:04:17 -07:00
Cameron Gutman 46c2236342 Add support for getting local IPv6 address 2019-07-29 21:01:55 -07:00
Cameron Gutman 8158c85a23 Add support for IPv6 testing 2019-07-28 18:33:33 -07:00
Cameron Gutman 6f3898e4b4 Untabify source 2019-07-28 13:25:51 -07:00
Cameron Gutman b1a1352bbf Retransmit UDP test packets a few times to be safe 2019-07-28 13:16:22 -07:00
Cameron Gutman 069b0c7656 Add more periodic test output to avoid looking like the test is hung 2019-07-28 13:11:04 -07:00
Cameron Gutman 42df15c567 Switch stderr and stdout to prevent miniupnp warnings from appearing on the console 2019-07-28 13:09:48 -07:00
Cameron Gutman 96a88b55db Prepare to swap stdout and stderr for console output 2019-07-28 12:46:45 -07:00
Cameron Gutman 2d31cd0420 Rename Internet Streaming Helper to Internet Hosting Tool 2019-07-28 12:33:05 -07:00
Cameron Gutman ebb8c9fb68 Version 4.0 2019-07-13 15:21:23 -07:00
Cameron Gutman b31d257566 Integrate GSv6Fwd into MISH 2019-07-13 15:04:20 -07:00
Cameron Gutman 51dc3142ca Retarget to VS2019 2019-07-13 11:29:55 -07:00
25 changed files with 1005 additions and 358 deletions
+3
View File
@@ -0,0 +1,3 @@
[submodule "GS-IPv6-Forwarder"]
path = GS-IPv6-Forwarder
url = https://github.com/moonlight-stream/GS-IPv6-Forwarder.git
+1
Submodule GS-IPv6-Forwarder added at 67f1e926b2
+2 -1
View File
@@ -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
* Troubleshoot streaming issues with the included testing tool
@@ -8,5 +8,6 @@
# Technical Features
- 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 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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
<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">
<bal:WixStandardBootstrapperApplication
LicenseFile="license.rtf"
ShowVersion="yes"
SuppressOptionsUI="yes"
/>
</BootstrapperApplicationRef>
<WixVariable Id="WixUILicenseRtf" Value="" />
<Chain>
<MsiPackage Id="mish" SourceFile="$(var.mish.TargetPath)" />
<MsiPackage SourceFile="$(var.GSv6FwdSetup.TargetPath)" />
</Chain>
</Bundle>
</Wix>
+61
View File
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" InitialTargets="EnsureWixToolsetInstalled" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>3.10</ProductVersion>
<ProjectGuid>a2ce5056-1114-44b9-b79b-952ef46d3d50</ProjectGuid>
<SchemaVersion>2.0</SchemaVersion>
<OutputName>InternetHostingToolSetup</OutputName>
<OutputType>Bundle</OutputType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<DefineConstants>Debug</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="Bundle.wxs" />
</ItemGroup>
<ItemGroup>
<WixExtension Include="WixBalExtension">
<HintPath>$(WixExtDir)\WixBalExtension.dll</HintPath>
<Name>WixBalExtension</Name>
</WixExtension>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GS-IPv6-Forwarder\GSv6FwdSetup\GSv6FwdSetup.wixproj">
<Name>GSv6FwdSetup</Name>
<Project>{f8171b99-f5f9-4abf-9fe5-6753539611af}</Project>
<Private>True</Private>
<DoNotHarvest>True</DoNotHarvest>
<RefProjectOutputGroups>Binaries;Content;Satellites</RefProjectOutputGroups>
<RefTargetDir>INSTALLFOLDER</RefTargetDir>
</ProjectReference>
<ProjectReference Include="..\mish\mish.wixproj">
<Name>mish</Name>
<Project>{f0dee5f3-4b62-47a3-b00b-61c614c924fd}</Project>
<Private>True</Private>
<DoNotHarvest>True</DoNotHarvest>
<RefProjectOutputGroups>Binaries;Content;Satellites</RefProjectOutputGroups>
<RefTargetDir>INSTALLFOLDER</RefTargetDir>
</ProjectReference>
</ItemGroup>
<Import Project="$(WixTargetsPath)" Condition=" '$(WixTargetsPath)' != '' " />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets" Condition=" '$(WixTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets') " />
<Target Name="EnsureWixToolsetInstalled" Condition=" '$(WixTargetsImported)' != 'true' ">
<Error Text="The WiX Toolset v3.11 (or newer) build tools must be installed to build this project. To download the WiX Toolset, see http://wixtoolset.org/releases/" />
</Target>
<!--
To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Wix.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
+28 -2
View File
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28010.2046
# Visual Studio Version 16
VisualStudioVersion = 16.0.29102.190
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miss", "miss\miss.vcxproj", "{B71C4E7F-0D92-4E19-BBD2-D33F0EF879B1}"
EndProject
@@ -9,6 +9,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mist", "mist\mist.vcxproj",
EndProject
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "mish", "mish\mish.wixproj", "{F0DEE5F3-4B62-47A3-B00B-61C614C924FD}"
EndProject
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "mish-setup", "mish-setup\mish-setup.wixproj", "{A2CE5056-1114-44B9-B79B-952EF46D3D50}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GSv6Fwd", "GS-IPv6-Forwarder\GSv6Fwd\GSv6Fwd.vcxproj", "{87DEAE49-7638-4CDB-88EB-054B1F3CB0D2}"
EndProject
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "GSv6FwdSetup", "GS-IPv6-Forwarder\GSv6FwdSetup\GSv6FwdSetup.wixproj", "{F8171B99-F5F9-4ABF-9FE5-6753539611AF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -39,6 +45,26 @@ Global
{F0DEE5F3-4B62-47A3-B00B-61C614C924FD}.Release|x64.ActiveCfg = Release|x86
{F0DEE5F3-4B62-47A3-B00B-61C614C924FD}.Release|x86.ActiveCfg = Release|x86
{F0DEE5F3-4B62-47A3-B00B-61C614C924FD}.Release|x86.Build.0 = Release|x86
{A2CE5056-1114-44B9-B79B-952EF46D3D50}.Debug|x64.ActiveCfg = Debug|x86
{A2CE5056-1114-44B9-B79B-952EF46D3D50}.Debug|x86.ActiveCfg = Debug|x86
{A2CE5056-1114-44B9-B79B-952EF46D3D50}.Debug|x86.Build.0 = Debug|x86
{A2CE5056-1114-44B9-B79B-952EF46D3D50}.Release|x64.ActiveCfg = Release|x86
{A2CE5056-1114-44B9-B79B-952EF46D3D50}.Release|x86.ActiveCfg = Release|x86
{A2CE5056-1114-44B9-B79B-952EF46D3D50}.Release|x86.Build.0 = Release|x86
{87DEAE49-7638-4CDB-88EB-054B1F3CB0D2}.Debug|x64.ActiveCfg = Debug|x64
{87DEAE49-7638-4CDB-88EB-054B1F3CB0D2}.Debug|x64.Build.0 = Debug|x64
{87DEAE49-7638-4CDB-88EB-054B1F3CB0D2}.Debug|x86.ActiveCfg = Debug|Win32
{87DEAE49-7638-4CDB-88EB-054B1F3CB0D2}.Debug|x86.Build.0 = Debug|Win32
{87DEAE49-7638-4CDB-88EB-054B1F3CB0D2}.Release|x64.ActiveCfg = Release|x64
{87DEAE49-7638-4CDB-88EB-054B1F3CB0D2}.Release|x64.Build.0 = Release|x64
{87DEAE49-7638-4CDB-88EB-054B1F3CB0D2}.Release|x86.ActiveCfg = Release|Win32
{87DEAE49-7638-4CDB-88EB-054B1F3CB0D2}.Release|x86.Build.0 = Release|Win32
{F8171B99-F5F9-4ABF-9FE5-6753539611AF}.Debug|x64.ActiveCfg = Debug|x86
{F8171B99-F5F9-4ABF-9FE5-6753539611AF}.Debug|x86.ActiveCfg = Debug|x86
{F8171B99-F5F9-4ABF-9FE5-6753539611AF}.Debug|x86.Build.0 = Debug|x86
{F8171B99-F5F9-4ABF-9FE5-6753539611AF}.Release|x64.ActiveCfg = Release|x86
{F8171B99-F5F9-4ABF-9FE5-6753539611AF}.Release|x86.ActiveCfg = Release|x86
{F8171B99-F5F9-4ABF-9FE5-6753539611AF}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+4 -9
View File
@@ -2,16 +2,13 @@
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:fire="http://schemas.microsoft.com/wix/FirewallExtension"
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">
<Package InstallerVersion="500" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes" />
<WixVariable Id="WixUILicenseRtf" Value="license.rtf" />
<UIRef Id="WixUI_Minimal" />
<Feature Id="ProductFeature" Title="MISHSetup" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
@@ -20,10 +17,10 @@
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="Moonlight Internet Streaming Helper" />
<Directory Id="INSTALLFOLDER" Name="Moonlight Internet Hosting Tool" />
</Directory>
<Directory Id="ProgramMenuFolder">
<Directory Id="ApplicationProgramsFolder" Name="Moonlight Internet Streaming Helper" />
<Directory Id="ApplicationProgramsFolder" Name="Moonlight Internet Hosting Tool" />
</Directory>
<Directory Id="CommonAppDataFolder">
<Directory Name="MISS" Id="MISSLogFolder" />
@@ -50,9 +47,7 @@
Account="NT AUTHORITY\LocalService"
ErrorControl="ignore"
Interactive="no">
<ServiceConfig DelayedAutoStart="no" OnInstall="yes" OnReinstall="yes" ServiceSid="restricted">
<RequiredPrivilege>SeChangeNotifyPrivilege</RequiredPrivilege>
</ServiceConfig>
<ServiceConfig DelayedAutoStart="no" OnInstall="yes" OnReinstall="yes" ServiceSid="unrestricted"/>
</ServiceInstall>
<ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="MISS" Wait="yes" />
</Component>
+12 -21
View File
@@ -97,7 +97,7 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
printf("NOT FOUND" NL);
}
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 (atoi(leaseDuration) == 0) {
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) {
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.
// 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);
@@ -747,18 +738,18 @@ void ResetLogFile()
{
char oldLogFilePath[MAX_PATH + 1];
char currentLogFilePath[MAX_PATH + 1];
char timeString[MAX_PATH + 1] = {};
SYSTEMTIME time;
char timeString[MAX_PATH + 1] = {};
SYSTEMTIME time;
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-old.log", oldLogFilePath, sizeof(oldLogFilePath));
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-current.log", currentLogFilePath, sizeof(currentLogFilePath));
// Close the existing stdout handle. This is important because otherwise
// it may still be open as stdout when we try to MoveFileEx below.
fclose(stdout);
// Close the existing stdout handle. This is important because otherwise
// it may still be open as stdout when we try to MoveFileEx below.
fclose(stdout);
// Rotate the current to the old log file
MoveFileExA(currentLogFilePath, oldLogFilePath, MOVEFILE_REPLACE_EXISTING);
// Rotate the current to the old log file
MoveFileExA(currentLogFilePath, oldLogFilePath, MOVEFILE_REPLACE_EXISTING);
// Redirect stdout to this new file
freopen(currentLogFilePath, "w", stdout);
@@ -766,10 +757,10 @@ void ResetLogFile()
// Print a log header
printf("Moonlight Internet Streaming Service v" VER_VERSION_STR NL);
// Print the current time
GetSystemTime(&time);
GetTimeFormatA(LOCALE_SYSTEM_DEFAULT, 0, &time, "hh':'mm':'ss tt", timeString, ARRAYSIZE(timeString));
printf("The current UTC time is: %s" NL, timeString);
// Print the current time
GetSystemTime(&time);
GetTimeFormatA(LOCALE_SYSTEM_DEFAULT, 0, &time, "hh':'mm':'ss tt", timeString, ARRAYSIZE(timeString));
printf("The current UTC time is: %s" NL, timeString);
}
DWORD WINAPI GameStreamStateChangeThread(PVOID Context)
+5 -5
View File
@@ -23,32 +23,32 @@
<ProjectGuid>{B71C4E7F-0D92-4E19-BBD2-D33F0EF879B1}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>miss</RootNamespace>
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
+760 -250
View File
@@ -1,25 +1,22 @@
#define _CRT_RAND_S
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include "mist.h"
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <Windows.h>
#include <WinSock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>
#include <assert.h>
#include <shellapi.h>
#include <objbase.h>
#include <WinInet.h>
#include "..\version.h"
#include <WinHttp.h>
#include <wtsapi32.h>
#include <powerbase.h>
#include <VersionHelpers.h>
#pragma comment(lib, "miniupnpc.lib")
#pragma comment(lib, "libnatpmp.lib")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "wininet.lib")
#pragma comment(lib, "winhttp.lib")
#pragma comment(lib, "wtsapi32.lib")
#pragma comment(lib, "powrprof.lib")
#define MINIUPNP_STATICLIB
#include <miniupnpc/miniupnpc.h>
@@ -29,7 +26,7 @@
#define NATPMP_STATICLIB
#include <natpmp.h>
bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN wanAddr);
#define LOOPBACK_SERVER_PORT_OFFSET -10000
static struct port_entry {
int proto;
@@ -44,8 +41,8 @@ static struct port_entry {
{IPPROTO_UDP, 48000, true},
#if 0
// These are not currently used, so let's
// avoid testing them for now.
// These are not currently used, so let's
// avoid testing them for now.
{IPPROTO_UDP, 48002, true},
{IPPROTO_UDP, 48010, true}
#endif
@@ -74,45 +71,78 @@ VOID CALLBACK MsgBoxHelpCallback(LPHELPINFO lpHelpInfo)
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) {
char missPath[MAX_PATH + 1];
FILE* f;
char logPath[MAX_PATH + 1];
FILE* f;
printf("--------------- CURRENT MISS LOG -------------------\n");
fprintf(LOG_OUT, "--------------- CURRENT MISS LOG -------------------\n");
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-current.log", missPath, sizeof(missPath));
f = fopen(missPath, "r");
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-current.log", logPath, sizeof(logPath));
f = fopen(logPath, "r");
if (f != nullptr) {
char buffer[1024];
while (!feof(f)) {
int bytesRead = fread(buffer, 1, ARRAYSIZE(buffer), f);
fwrite(buffer, 1, bytesRead, stdout);
fwrite(buffer, 1, bytesRead, LOG_OUT);
}
fclose(f);
}
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", missPath, sizeof(missPath));
f = fopen(missPath, "r");
if (f != nullptr) {
char buffer[1024];
while (!feof(f)) {
int bytesRead = fread(buffer, 1, ARRAYSIZE(buffer), f);
fwrite(buffer, 1, bytesRead, stdout);
}
fclose(f);
}
else {
printf("Failed to find old MISS log\n");
}
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-old.log", logPath, sizeof(logPath));
f = fopen(logPath, "r");
if (f != nullptr) {
char buffer[1024];
while (!feof(f)) {
int bytesRead = fread(buffer, 1, ARRAYSIZE(buffer), f);
fwrite(buffer, 1, bytesRead, LOG_OUT);
}
fclose(f);
}
else {
fprintf(LOG_OUT, "Failed to find old MISS log\n");
}
fflush(stdout);
fprintf(LOG_OUT, "--------------- CURRENT GSV6FWD LOG -------------------\n");
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\GSv6Fwd-current.log", logPath, sizeof(logPath));
f = fopen(logPath, "r");
if (f != nullptr) {
char buffer[1024];
while (!feof(f)) {
int bytesRead = fread(buffer, 1, ARRAYSIZE(buffer), f);
fwrite(buffer, 1, bytesRead, LOG_OUT);
}
fclose(f);
}
else {
fprintf(LOG_OUT, "Failed to find current GSv6Fwd log\n");
}
fprintf(LOG_OUT, "\n----------------- OLD GSV6FWD LOG ---------------------\n");
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\GSv6Fwd-old.log", logPath, sizeof(logPath));
f = fopen(logPath, "r");
if (f != nullptr) {
char buffer[1024];
while (!feof(f)) {
int bytesRead = fread(buffer, 1, ARRAYSIZE(buffer), f);
fwrite(buffer, 1, bytesRead, LOG_OUT);
}
fclose(f);
}
else {
fprintf(LOG_OUT, "Failed to find old GSv6Fwd log\n");
}
fflush(LOG_OUT);
}
MSGBOXPARAMSA msgParams;
@@ -143,7 +173,7 @@ void DisplayMessage(const char* message, const char* helpUrl = nullptr, MessageP
if (priority != MpInfo && terminal) {
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))
{
case IDYES:
@@ -155,6 +185,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()
{
DWORD error;
@@ -164,7 +244,7 @@ bool IsGameStreamEnabled()
error = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\NVIDIA Corporation\\NvStream", 0, KEY_READ | KEY_WOW64_64KEY, &key);
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.",
"https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide");
return false;
@@ -176,38 +256,149 @@ bool IsGameStreamEnabled()
if (error != ERROR_SUCCESS || !enabled) {
// GFE may not even write EnableStreaming until the user enables GameStream for the first time
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.",
"https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide");
return false;
}
else {
printf("GeForce Experience installed and GameStream is enabled\n");
fprintf(LOG_OUT, "GeForce Experience installed and GameStream is enabled\n");
return true;
}
}
bool IsConsoleSessionActive()
{
PWTS_SESSION_INFO_1 sessionInfo;
DWORD sessionCount;
DWORD level;
bool ret;
DWORD activeSessionId = WTSGetActiveConsoleSessionId();
if (activeSessionId == 0xFFFFFFFF) {
fprintf(LOG_OUT, "No active console session detected\n");
return false;
}
level = 1;
if (!WTSEnumerateSessionsEx(WTS_CURRENT_SERVER_HANDLE, &level, 0, &sessionInfo, &sessionCount)) {
fprintf(LOG_OUT, "WTSEnumerateSessionsEx() failed: %d\n", GetLastError());
return false;
}
ret = false;
for (int i = 0; i < sessionCount; i++)
{
if (sessionInfo[i].SessionId == activeSessionId) {
if (sessionInfo[i].pUserName != nullptr) {
fprintf(LOG_OUT, "Session %d has active user\n", sessionInfo[i].SessionId);
ret = true;
break;
}
}
}
WTSFreeMemoryExW(WTSTypeSessionInfoLevel1, sessionInfo, sessionCount);
return ret;
}
bool IsSleepEnabled()
{
SYSTEM_POWER_POLICY powerPolicy;
if (CallNtPowerInformation(SystemPowerPolicyAc, NULL, 0, &powerPolicy, sizeof(powerPolicy)) < 0) {
return false;
}
return powerPolicy.IdleTimeout != 0 && powerPolicy.Idle.Action == PowerActionSleep;
}
bool IsHibernationEnabled()
{
SYSTEM_POWER_POLICY powerPolicy;
if (CallNtPowerInformation(SystemPowerPolicyAc, NULL, 0, &powerPolicy, sizeof(powerPolicy)) < 0) {
return false;
}
return powerPolicy.DozeS4Timeout != 0 || (powerPolicy.IdleTimeout != 0 && powerPolicy.Idle.Action == PowerActionHibernate);
}
bool IsLocalNetworkAccessBlocked()
{
MIB_IPFORWARDROW route;
DWORD error;
SOCKADDR_IN sin;
SOCKET s;
error = GetBestRoute(0, 0, &route);
if (error != NO_ERROR) {
fprintf(LOG_OUT, "GetBestRoute() failed: %d\n", error);
return false;
}
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET) {
fprintf(LOG_OUT, "socket() failed: %d\n", error);
return false;
}
RtlZeroMemory(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.S_un.S_addr = route.dwForwardNextHop;
sin.sin_port = htons(80);
if (connect(s, (PSOCKADDR)&sin, sizeof(sin)) == 0) {
error = NO_ERROR;
fprintf(LOG_OUT, "connect(%s, %d) successful\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
}
else {
error = WSAGetLastError();
fprintf(LOG_OUT, "connect(%s, %d) failed: %d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), error);
}
closesocket(s);
return error == WSAEACCES;
}
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 {
PortTestOk,
PortTestError,
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;
int err;
clientSock = socket(addr->ss_family, proto == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM, proto);
if (clientSock == INVALID_SOCKET) {
printf("socket() failed: %d\n", WSAGetLastError());
fprintf(LOG_OUT, "socket() failed: %d\n", WSAGetLastError());
return PortTestError;
}
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) {
printf("socket() failed: %d\n", WSAGetLastError());
fprintf(LOG_OUT, "socket() failed: %d\n", WSAGetLastError());
closesocket(clientSock);
return PortTestError;
}
@@ -219,21 +410,22 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
if (err == SOCKET_ERROR) {
if (WSAGetLastError() == WSAEADDRINUSE) {
// If someone is already listening (perhaps GFE is currently streaming),
// we can proceed if it's a TCP connection.
if (proto == IPPROTO_TCP) {
// we can proceed if it's a TCP connection unless it's a loopback relay
// which would give us a false positive result.
if (proto == IPPROTO_TCP && !isLoopbackRelay) {
closesocket(serverSock);
serverSock = INVALID_SOCKET;
}
else {
// We can't continue to test for UDP ports.
printf("Unknown (in use)\n");
fprintf(LOG_OUT, "Unknown (in use)\n");
closesocket(clientSock);
closesocket(serverSock);
return PortTestUnknown;
}
}
else {
printf("bind() failed: %d\n", WSAGetLastError());
fprintf(LOG_OUT, "bind() failed: %d\n", WSAGetLastError());
closesocket(clientSock);
closesocket(serverSock);
return PortTestError;
@@ -243,7 +435,7 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
if (proto == IPPROTO_TCP && serverSock != INVALID_SOCKET) {
err = listen(serverSock, 1);
if (err == SOCKET_ERROR) {
printf("listen() failed: %d\n", WSAGetLastError());
fprintf(LOG_OUT, "listen() failed: %d\n", WSAGetLastError());
closesocket(clientSock);
closesocket(serverSock);
return PortTestError;
@@ -254,7 +446,7 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
ULONG nbIo = 1;
err = ioctlsocket(clientSock, FIONBIO, &nbIo);
if (err == SOCKET_ERROR) {
printf("ioctlsocket() failed: %d\n", WSAGetLastError());
fprintf(LOG_OUT, "ioctlsocket() failed: %d\n", WSAGetLastError());
closesocket(clientSock);
if (serverSock != INVALID_SOCKET) {
closesocket(serverSock);
@@ -267,12 +459,12 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
sizeof(SOCKADDR_IN) : sizeof(SOCKADDR_IN6);
RtlCopyMemory(&sin6, addr, addrLen);
sin6.sin6_port = htons(port);
sin6.sin6_port = htons(port + (isLoopbackRelay ? LOOPBACK_SERVER_PORT_OFFSET : 0));
if (proto == IPPROTO_TCP) {
err = connect(clientSock, (struct sockaddr*)&sin6, addrLen);
if (err == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) {
printf("connect() failed: %d\n", WSAGetLastError());
fprintf(LOG_OUT, "connect() failed: %d\n", WSAGetLastError());
}
else {
struct timeval timeout = {};
@@ -280,30 +472,30 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
FD_ZERO(&fds);
if (serverSock != INVALID_SOCKET) {
FD_SET(serverSock, &fds);
}
else {
FD_SET(clientSock, &fds);
}
if (serverSock != INVALID_SOCKET) {
FD_SET(serverSock, &fds);
}
else {
FD_SET(clientSock, &fds);
}
// If we have a server socket, listen for the accept() instead of the
// connect() so we can be compatible with the loopback relay.
// If we have a server socket, listen for the accept() instead of the
// connect() so we can be compatible with the loopback relay.
timeout.tv_sec = 3;
err = select(0,
serverSock != INVALID_SOCKET ? &fds : nullptr,
serverSock == INVALID_SOCKET ? &fds : nullptr,
nullptr, &timeout);
serverSock != INVALID_SOCKET ? &fds : nullptr,
serverSock == INVALID_SOCKET ? &fds : nullptr,
nullptr, &timeout);
if (err == 1) {
// Our FD was signalled for connect() or accept() completion
printf("Success\n");
fprintf(LOG_OUT, "Success\n");
}
else if (err == 0) {
// Timed out
printf("Timeout\n");
fprintf(LOG_OUT, "Timeout\n");
}
else {
printf("select() failed: %d\n", WSAGetLastError());
fprintf(LOG_OUT, "select() failed: %d\n", WSAGetLastError());
}
}
@@ -316,12 +508,18 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
}
else {
const char testMsg[] = "moonlight-test";
err = sendto(clientSock, testMsg, sizeof(testMsg), 0, (struct sockaddr*)&sin6, addrLen);
if (err == SOCKET_ERROR) {
printf("sendto() failed: %d\n", WSAGetLastError());
closesocket(clientSock);
closesocket(serverSock);
return PortTestError;
// Send several test packets to ensure a random lost packet doesn't make the test fail
for (int i = 0; i < 5; i++) {
err = sendto(clientSock, testMsg, sizeof(testMsg), 0, (struct sockaddr*)&sin6, addrLen);
if (err == SOCKET_ERROR) {
fprintf(LOG_OUT, "sendto() failed: %d\n", WSAGetLastError());
closesocket(clientSock);
closesocket(serverSock);
return PortTestError;
}
Sleep(200);
}
struct timeval timeout = {};
@@ -330,18 +528,18 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
FD_ZERO(&fds);
FD_SET(serverSock, &fds);
timeout.tv_sec = 3;
timeout.tv_sec = 2;
err = select(0, &fds, nullptr, nullptr, &timeout);
if (err == 1) {
// Our FD was signalled for data available
printf("Success\n");
fprintf(LOG_OUT, "Success\n");
}
else if (err == 0) {
// Timed out
printf("Timeout\n");
fprintf(LOG_OUT, "Timeout\n");
}
else {
printf("select() failed: %d\n", WSAGetLastError());
fprintf(LOG_OUT, "select() failed: %d\n", WSAGetLastError());
}
closesocket(clientSock);
@@ -351,144 +549,308 @@ 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, bool isLoopbackRelay)
{
HINTERNET hInternet;
HINTERNET hRequest;
char url[1024];
HINTERNET hSession = nullptr;
HINTERNET hConnection = nullptr;
HINTERNET hRequest = nullptr;
PortTestStatus result;
hInternet = InternetOpenA("MIST", 0, nullptr, nullptr, 0);
if (hInternet == nullptr) {
printf("InternetOpen() failed: %d\n", GetLastError());
return PortTestError;
}
hSession = WinHttpOpen(L"MIST", WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (hSession == nullptr) {
fprintf(LOG_OUT, "WinHttpOpen() failed: %d\n", GetLastError());
result = PortTestError;
goto Exit;
}
char addrStr[64];
inet_ntop(AF_INET, &((struct sockaddr_in*)addr)->sin_addr, addrStr, sizeof(addrStr));
// Windows 8.1 enabled TLSv1.2 for WinHTTP by default (8.0 enables it for Schannel but not WinHTTP)
// https://docs.microsoft.com/en-us/security/engineering/solving-tls1-problem
if (!IsWindows8Point1OrGreater()) {
DWORD protocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 |
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2;
sprintf(url, "%s://%s:%d/",
port == 47989 ? "http" : "https",
addrStr,
port);
if (!WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &protocols, sizeof(protocols))) {
fprintf(LOG_OUT, "WinHttpSetOption(WINHTTP_OPTION_SECURE_PROTOCOLS) failed: %d\n", GetLastError());
}
}
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,
NULL);
if (hRequest == nullptr) {
if (GetLastError() == ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED) {
// This is expected for our HTTPS connection
printf("Success\n");
}
else {
// CANNOT_CONNECT is the "expected" error
if (GetLastError() == ERROR_INTERNET_CANNOT_CONNECT) {
printf("Failed\n");
}
else {
printf("Failed: %d\n", GetLastError());
}
WCHAR urlEscapedAddr[INET6_ADDRSTRLEN + 2];
if (addr->ss_family == AF_INET) {
InetNtopW(AF_INET, &((struct sockaddr_in*)addr)->sin_addr, urlEscapedAddr, ARRAYSIZE(urlEscapedAddr));
}
else {
// The address string must be escaped for usage in URLs
urlEscapedAddr[0] = L'[';
InetNtopW(AF_INET6, &((struct sockaddr_in6*)addr)->sin6_addr, &urlEscapedAddr[1], INET6_ADDRSTRLEN);
wcscat_s(urlEscapedAddr, L"]");
}
InternetCloseHandle(hInternet);
return PortTestError;
}
}
else {
printf("Success\n");
InternetCloseHandle(hRequest);
}
hConnection = WinHttpConnect(hSession, urlEscapedAddr, port + (isLoopbackRelay ? LOOPBACK_SERVER_PORT_OFFSET : 0), NULL);
if (hConnection == nullptr) {
fprintf(LOG_OUT, "WinHttpConnect() failed: %d\n", GetLastError());
result = PortTestError;
goto Exit;
}
hRequest = WinHttpOpenRequest(hConnection, L"GET", L"/", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES,
port == 47984 ? WINHTTP_FLAG_SECURE : 0);
if (hConnection == nullptr) {
fprintf(LOG_OUT, "WinHttpOpenRequest() failed: %d\n", GetLastError());
WinHttpCloseHandle(hConnection);
result = PortTestError;
goto Exit;
}
InternetCloseHandle(hInternet);
if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0) || !WinHttpReceiveResponse(hRequest, NULL)) {
if (GetLastError() == ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED) {
// This is expected for our HTTPS connection
fprintf(LOG_OUT, "Success\n");
result = PortTestOk;
}
else {
// CANNOT_CONNECT is the "expected" error
if (GetLastError() == ERROR_WINHTTP_CANNOT_CONNECT) {
fprintf(LOG_OUT, "Failed\n");
}
else {
fprintf(LOG_OUT, "Failed: %d\n", GetLastError());
}
return PortTestOk;
result = PortTestError;
}
goto Exit;
}
else {
fprintf(LOG_OUT, "Success\n");
result = PortTestOk;
}
Exit:
if (hRequest != nullptr) {
WinHttpCloseHandle(hRequest);
}
if (hConnection != nullptr) {
WinHttpCloseHandle(hConnection);
}
if (hSession != nullptr) {
WinHttpCloseHandle(hSession);
}
return result;
}
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;
if (allPortsFailed) {
*allPortsFailed = true;
}
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].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) {
// 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.
printf("Testing TCP %d with HTTP traffic...", k_Ports[i].port);
status = TestHttpPort(addr, k_Ports[i].port);
}
if (consolePrint) {
fprintf(CONSOLE_OUT, "\tTesting %s %d...\n",
k_Ports[i].proto == IPPROTO_TCP ? "TCP" : "UDP",
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, isLoopbackRelay);
}
if (status != PortTestOk) {
// If we got an unknown result, assume it matches with whatever
// we've gotten so far.
if (status == PortTestError || !ret) {
int msgLen = snprintf(portMsg, portMsgLen, "%s %d\n",
k_Ports[i].proto == IPPROTO_TCP ? "TCP" : "UDP",
k_Ports[i].port);
portMsg += msgLen;
portMsgLen -= msgLen;
if (portMsg != NULL && portMsgLen > 0) {
int msgLen = snprintf(portMsg, portMsgLen, "%s %d\n",
k_Ports[i].proto == IPPROTO_TCP ? "TCP" : "UDP",
k_Ports[i].port);
portMsg += msgLen;
portMsgLen -= msgLen;
}
// Keep going to check all ports and report the failing ones
ret = false;
}
}
else if (allPortsFailed) {
*allPortsFailed = false;
}
}
return ret;
}
bool FindLocalInterfaceIP4Address(PSOCKADDR_IN addr)
bool FindLocalInterfaceIPAddress(int family, PSOCKADDR_STORAGE addr)
{
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");
if (host == nullptr) {
printf("gethostbyname() failed: %d\n", WSAGetLastError());
hint.ai_family = family;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = IPPROTO_TCP;
hint.ai_flags = AI_ADDRCONFIG;
err = getaddrinfo("moonlight-stream.org", "443", &hint, &result);
if (err != 0 || result == NULL) {
fprintf(LOG_OUT, "getaddrinfo() failed: %d\n", err);
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) {
printf("socket() failed: %d\n", WSAGetLastError());
fprintf(LOG_OUT, "socket() failed: %d\n", WSAGetLastError());
freeaddrinfo(result);
return false;
}
SOCKADDR_IN sin = {};
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));
err = connect(s, (struct sockaddr*)result->ai_addr, result->ai_addrlen);
if (err == SOCKET_ERROR) {
printf("connect() failed: %d\n", WSAGetLastError());
fprintf(LOG_OUT, "connect() failed: %d\n", WSAGetLastError());
closesocket(s);
freeaddrinfo(result);
return false;
}
freeaddrinfo(result);
// Determine which local interface we bound to
int nameLen = sizeof(*addr);
err = getsockname(s, (struct sockaddr*)addr, &nameLen);
if (err == SOCKET_ERROR) {
printf("getsockname() failed: %d\n", WSAGetLastError());
fprintf(LOG_OUT, "getsockname() failed: %d\n", WSAGetLastError());
closesocket(s);
return false;
}
char addrStr[64];
inet_ntop(AF_INET, &addr->sin_addr, addrStr, sizeof(addrStr));
printf("%s\n", addrStr);
char addrStr[INET6_ADDRSTRLEN];
if (addr->ss_family == AF_INET) {
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;
}
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 {
NOT_FOUND,
OK,
@@ -519,28 +881,28 @@ UPnPPortStatus UPnPCheckPort(struct UPNPUrls* urls, struct IGDdatas* data, int p
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(
urls->controlURL, data->first.servicetype, portStr, protoStr, nullptr,
intClient, intPort, desc, enabled, leaseDuration);
if (err == 714) {
// NoSuchEntryInArray
printf("NOT FOUND\n");
fprintf(LOG_OUT, "NOT FOUND\n");
return NOT_FOUND;
}
else if (err == UPNPCOMMAND_SUCCESS) {
if (!strcmp(myAddr, intClient)) {
printf("OK\n");
fprintf(LOG_OUT, "OK\n");
return OK;
}
else {
printf("CONFLICT - %s %s\n", desc, intClient);
fprintf(LOG_OUT, "CONFLICT - %s %s\n", desc, intClient);
snprintf(conflictMessage, 128, "%s (%s)", desc, intClient);
return CONFLICTED;
}
}
else {
printf("ERROR %d\n", err);
fprintf(LOG_OUT, "ERROR %d\n", err);
return ERRORED;
}
}
@@ -550,23 +912,27 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, PSOCKADDR_IN reportedWanAddr, bool* fo
natpmp_t natpmp;
bool foundUpnpIgd = false;
wanAddr->sin_family = AF_INET;
reportedWanAddr->sin_family = AF_INET;
*foundPortForwardingRules = false;
*igdDisconnected = false;
bool gotReportedWanAddress = false;
int natPmpErr = initnatpmp(&natpmp, 0, 0);
if (natPmpErr != 0) {
printf("initnatpmp() failed: %d\n", natPmpErr);
fprintf(LOG_OUT, "initnatpmp() failed: %d\n", natPmpErr);
}
else {
natPmpErr = sendpublicaddressrequest(&natpmp);
if (natPmpErr < 0) {
printf("sendpublicaddressrequest() failed: %d\n", natPmpErr);
fprintf(LOG_OUT, "sendpublicaddressrequest() failed: %d\n", natPmpErr);
closenatpmp(&natpmp);
}
}
{
fprintf(CONSOLE_OUT, "\tTesting UPnP...\n");
int upnpErr;
struct UPNPDev* ipv4Devs = upnpDiscoverAll(5000, nullptr, nullptr, UPNP_LOCAL_PORT_ANY, 0, 2, &upnpErr);
@@ -582,19 +948,19 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, PSOCKADDR_IN reportedWanAddr, bool* fo
if (ret == 2) {
*igdDisconnected = true;
}
printf("Discovered UPnP IGD at: %s\n", urls.controlURL);
printf("Detecting WAN IP address via UPnP...");
fprintf(LOG_OUT, "Discovered UPnP IGD at: %s\n", urls.controlURL);
fprintf(LOG_OUT, "Detecting WAN IP address via UPnP...");
ret = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, wanAddrStr);
if (ret == UPNPCOMMAND_SUCCESS && strlen(wanAddrStr) > 0) {
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) {
gotReportedWanAddress = true;
}
}
else {
printf("FAILED %d\n", ret);
fprintf(LOG_OUT, "FAILED %d\n", ret);
}
char conflictMessage[512];
@@ -620,19 +986,21 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, PSOCKADDR_IN reportedWanAddr, bool* fo
}
}
else {
printf("No UPnP IGD detected\n");
fprintf(LOG_OUT, "No UPnP IGD detected\n");
}
FreeUPNPUrls(&urls);
}
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
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;
natPmpErr = readnatpmpresponseorretry(&natpmp, &response);
@@ -642,7 +1010,7 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, PSOCKADDR_IN reportedWanAddr, bool* fo
char addrStr[64];
reportedWanAddr->sin_addr = response.pnu.publicaddress.addr;
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) {
gotReportedWanAddress = true;
@@ -654,19 +1022,21 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, PSOCKADDR_IN reportedWanAddr, bool* fo
}
}
else {
printf("FAILED %d\n", natPmpErr);
fprintf(LOG_OUT, "FAILED %d\n", natPmpErr);
}
}
printf("Detecting WAN IP address via STUN...");
if (!getExternalAddressPortIP4(IPPROTO_UDP, 0, wanAddr) && !getExternalAddressPortIP4(IPPROTO_TCP, 0, wanAddr)) {
fprintf(LOG_OUT, "Detecting WAN IP address via STUN...");
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.");
return false;
}
else {
char addrStr[64];
inet_ntop(AF_INET, &wanAddr->sin_addr, addrStr, sizeof(addrStr));
printf("%s\n", addrStr);
fprintf(LOG_OUT, "%s\n", addrStr);
if (!gotReportedWanAddress) {
// If we didn't get anything from UPnP or NAT-PMP, just populate the reported
@@ -717,25 +1087,25 @@ bool IsDoubleNAT(PSOCKADDR_IN wanAddr)
int main(int argc, char* argv[])
{
WSADATA wsaData;
SYSTEMTIME time;
char timeString[MAX_PATH + 1] = {};
SYSTEMTIME time;
char timeString[MAX_PATH + 1] = {};
char tempPath[MAX_PATH + 1];
GetTempPathA(sizeof(tempPath), tempPath);
snprintf(logFilePath, sizeof(logFilePath), "%s\\%s", tempPath, "mis-test.log");
freopen(logFilePath, "w", stdout);
freopen(logFilePath, "w", LOG_OUT);
// 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
GetSystemTime(&time);
GetTimeFormatA(LOCALE_SYSTEM_DEFAULT, 0, &time, "hh':'mm':'ss tt", timeString, ARRAYSIZE(timeString));
printf("The current UTC time is: %s\n", timeString);
// Print the current time
GetSystemTime(&time);
GetTimeFormatA(LOCALE_SYSTEM_DEFAULT, 0, &time, "hh':'mm':'ss tt", timeString, ARRAYSIZE(timeString));
fprintf(LOG_OUT, "The current UTC time is: %s\n", timeString);
// 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);
if (err != NO_ERROR) {
@@ -743,13 +1113,65 @@ int main(int argc, char* argv[])
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
if (!IsGameStreamEnabled()) {
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.",
"https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#display-locked-error");
return -1;
}
fprintf(CONSOLE_OUT, "Checking power settings...\n");
if (IsSleepEnabled()) {
DisplayMessage("This computer has sleep mode enabled. Sleep mode may prevent this PC from being available for streaming.\n\n"
"Please ensure sleep is disabled in Power Options so this PC is always ready for streaming. Click the Help button for more information.",
"https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#sleep-mode-enabled-warning", MpWarn, false);
}
if (IsHibernationEnabled()) {
DisplayMessage("This computer has hibernation enabled. Hibernation may prevent this PC from being available for streaming.\n\n"
"Please ensure hibernation is disabled in Power Options so this PC is always ready for streaming. Click the Help button for more information.",
"https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#hibernation-enabled-warning", MpWarn, false);
}
fprintf(CONSOLE_OUT, "Checking network connections...\n");
if (IsLocalNetworkAccessBlocked()) {
DisplayMessage("Local network access appears to be blocked by another application installed on this PC.\n\n"
"If you have firewall or VPN software installed, make sure it is configured to allow applications to access the local network. Click the Help button for guidance on fixing this issue.",
"https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#local-network-access-blocked-error");
return -1;
}
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);
}
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, "displayName")) {
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#known-application-compatibility-issues", MpInfo, false);
}
}
union {
SOCKADDR_STORAGE ss;
SOCKADDR_IN sin;
@@ -758,103 +1180,191 @@ int main(int argc, char* argv[])
char msgBuf[2048];
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
ss = {};
sin.sin_family = AF_INET;
sin.sin_addr = in4addr_loopback;
printf("Testing GameStream ports via loopback\n");
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf))) {
fprintf(LOG_OUT, "Testing GameStream ports via loopback\n");
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf), false, true)) {
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.");
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting");
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.");
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
printf("Testing GameStream ports via local network\n");
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf))) {
// Try to connect via LAN address
fprintf(LOG_OUT, "Testing GameStream ports via local network\n");
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf), false, true)) {
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.");
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting");
return -1;
}
fprintf(stderr, "Detecting public IP address...\n");
bool rulesFound, igdDisconnected;
bool igdDisconnected;
SOCKADDR_IN locallyReportedWanAddr;
if (!CheckWANAccess(&sin, &locallyReportedWanAddr, &rulesFound, &igdDisconnected)) {
return -1;
char wanAddrStr[INET_ADDRSTRLEN];
if (ss.ss_family == AF_INET) {
bool rulesFound;
fprintf(CONSOLE_OUT, "Detecting public IP address...\n");
if (!CheckWANAccess(&sin, &locallyReportedWanAddr, &rulesFound, &igdDisconnected)) {
return -1;
}
// 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 {
// Go directly to the relay check if we have only IPv6 connectivity
igdDisconnected = false;
locallyReportedWanAddr = {};
}
// 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) {
printf("Testing GameStream ports via UPnP/NAT-PMP reported WAN address\n");
struct addrinfo hint = {};
struct addrinfo* result;
// 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));
hint.ai_family = AF_UNSPEC;
hint.ai_flags = AI_ADDRCONFIG;
err = getaddrinfo("loopback-v2.moonlight-stream.org", NULL, &hint, &result);
if (err != 0 || result == NULL) {
fprintf(LOG_OUT, "getaddrinfo() failed: %d\n", err);
}
else {
bool allPortsFailedOnV4 = true;
printf("Detected inconsistency between UPnP/NAT-PMP and STUN reported WAN addresses!\n");
// 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);
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 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);
DisplayMessage(msgBuf, nullptr, MpInfo);
return 0;
}
}
}
// 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);
}
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
printf("Testing GameStream ports via STUN-reported WAN address\n");
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf))) {
// Many UPnP devices report IGD disconnected when double-NATed. If it was really offline,
// we probably would not have even gotten past STUN.
if (IsDoubleNAT(&locallyReportedWanAddr) || igdDisconnected) {
snprintf(msgBuf, sizeof(msgBuf), "Your router appears be connected to the Internet through another router. Click the Help button for guidance on fixing this issue.");
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#connected-through-another-router-error");
}
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.");
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#carrier-grade-nat-error");
}
else {
struct hostent* host;
// 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);
// Many UPnP devices report IGD disconnected when double-NATed. If it was really offline,
// we probably would not have even gotten past STUN.
if (IsDoubleNAT(&locallyReportedWanAddr) || igdDisconnected) {
snprintf(msgBuf, sizeof(msgBuf), "Your router appears be connected to the Internet through another router. Click the Help button for guidance on fixing this issue.");
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#connected-through-another-router-error");
}
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.\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");
}
else {
snprintf(msgBuf, sizeof(msgBuf), "Internet GameStream connectivity check failed.\n\n"
"First, try restarting your router. If that fails, 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");
return -1;
}
AllTestsPassed:
snprintf(msgBuf, sizeof(msgBuf), "This PC is ready to stream 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"
"If you can't, you can type the following address into Moonlight's Add PC dialog: %s", wanAddrStr);
DisplayMessage(msgBuf, nullptr, MpInfo);
return 0;
return -1;
}
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0">
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
</assembly>
+19
View File
@@ -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);
+21 -5
View File
@@ -23,32 +23,32 @@
<ProjectGuid>{8F78D8D2-A837-489C-BAC5-81494C9CEF7A}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>mist</RootNamespace>
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
@@ -100,6 +100,9 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>..\libs\x86\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Manifest>
<AdditionalManifestFiles>mist.exe.manifest</AdditionalManifestFiles>
</Manifest>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
@@ -115,6 +118,9 @@
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<Manifest>
<AdditionalManifestFiles>mist.exe.manifest</AdditionalManifestFiles>
</Manifest>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
@@ -138,6 +144,9 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>..\libs\x86\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Manifest>
<AdditionalManifestFiles>mist.exe.manifest</AdditionalManifestFiles>
</Manifest>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
@@ -157,6 +166,9 @@
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<Manifest>
<AdditionalManifestFiles>mist.exe.manifest</AdditionalManifestFiles>
</Manifest>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="mist.cpp" />
@@ -167,6 +179,10 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\version.h" />
<ClInclude Include="mist.h" />
</ItemGroup>
<ItemGroup>
<Manifest Include="mist.exe.manifest" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
+8
View File
@@ -26,10 +26,18 @@
<ClInclude Include="..\version.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="mist.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="mist.rc">
<Filter>Source Files</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<Manifest Include="mist.exe.manifest">
<Filter>Resource Files</Filter>
</Manifest>
</ItemGroup>
</Project>
+41 -62
View File
@@ -1,17 +1,10 @@
#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 <WinSock2.h>
#include "mist.h"
#include <stdio.h>
#define STUN_PORT 3478
#define STUN_RECV_TIMEOUT_SEC 3
#define STUN_RECV_TIMEOUT_SEC 5
#define STUN_MESSAGE_BINDING_REQUEST 0x0001
#define STUN_MESSAGE_BINDING_SUCCESS 0x0101
@@ -45,40 +38,47 @@ typedef struct _STUN_MESSAGE {
#pragma pack(pop)
bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN wanAddr)
bool getExternalAddressPortIP4(unsigned short localPort, PSOCKADDR_IN wanAddr)
{
SOCKET sock;
STUN_MESSAGE reqMsg;
int i;
int bytesRead;
int tries;
int timeout;
int err;
PSTUN_ATTRIBUTE_HEADER attribute;
PSTUN_MAPPED_IPV4_ADDRESS_ATTRIBUTE ipv4Attrib;
struct hostent *host;
struct addrinfo* result;
struct addrinfo hints;
union {
STUN_MESSAGE hdr;
char buf[1024];
} resp;
host = gethostbyname("stun.moonlight-stream.org");
if (host == nullptr) {
printf("gethostbyname() failed: %d\n", WSAGetLastError());
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
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;
}
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) {
printf("socket() failed: %d\n", WSAGetLastError());
fprintf(LOG_OUT, "socket() failed: %d\n", WSAGetLastError());
freeaddrinfo(result);
return false;
}
struct sockaddr_in bindAddr = {};
bindAddr.sin_family = AF_INET;
bindAddr.sin_family = hints.ai_family;
bindAddr.sin_port = htons(localPort);
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);
freeaddrinfo(result);
return false;
}
@@ -89,36 +89,13 @@ bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN
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;
for (i = 0; i < tries; i++) {
// Retransmit the request every second until the timeout elapses
if (send(sock, (char *)&reqMsg, sizeof(reqMsg), 0) == SOCKET_ERROR) {
printf("send() failed: %d\n", WSAGetLastError());
closesocket(sock);
return false;
for (i = 0; i < STUN_RECV_TIMEOUT_SEC; i++) {
// Retransmit the request every second to all resolved IP addresses until the timeout elapses
for (struct addrinfo* current = result; current != NULL; current = current->ai_next) {
if (sendto(sock, (char*)&reqMsg, sizeof(reqMsg), 0, current->ai_addr, current->ai_addrlen) == SOCKET_ERROR) {
fprintf(LOG_OUT, "sendto() failed: %d\n", WSAGetLastError());
}
}
fd_set fds;
@@ -126,7 +103,7 @@ bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN
FD_SET(sock, &fds);
struct timeval tv;
tv.tv_sec = timeout;
tv.tv_sec = 1;
tv.tv_usec = 0;
int selectRes = select(0, &fds, nullptr, nullptr, &tv);
@@ -135,40 +112,42 @@ bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN
continue;
}
else if (selectRes == SOCKET_ERROR) {
printf("select() failed: %d\n", WSAGetLastError());
fprintf(LOG_OUT, "select() failed: %d\n", WSAGetLastError());
closesocket(sock);
freeaddrinfo(result);
return false;
}
// 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;
}
freeaddrinfo(result);
closesocket(sock);
if (bytesRead == 0) {
printf("No response from STUN server\n");
fprintf(LOG_OUT, "No response from STUN server\n");
return false;
}
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;
}
else if (bytesRead < sizeof(resp.hdr)) {
printf("STUN message truncated: %d\n", bytesRead);
fprintf(LOG_OUT, "STUN message truncated: %d\n", bytesRead);
return false;
}
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;
}
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;
}
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;
}
@@ -176,7 +155,7 @@ bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN
bytesRead -= sizeof(resp.hdr);
while (bytesRead > sizeof(*attribute)) {
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;
}
// Mask off the comprehension bit
@@ -189,11 +168,11 @@ bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN
ipv4Attrib = (PSTUN_MAPPED_IPV4_ADDRESS_ATTRIBUTE)attribute;
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;
}
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;
}
@@ -207,6 +186,6 @@ bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN
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;
}
+3 -3
View File
@@ -1,7 +1,7 @@
#pragma once
#define VER_VERSION 3,0,0,0
#define VER_VERSION_STR "3.0.0.0"
#define VER_VERSION 5,0,0,0
#define VER_VERSION_STR "5.0.0.0"
#define VER_COMPANYNAME_STR "Moonlight Game Streaming Project"
#define VER_PRODUCTNAME_STR "Moonlight Internet Streaming Helper"
#define VER_PRODUCTNAME_STR "Moonlight Internet Hosting Tool"