Compare commits

...
9 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
6 changed files with 178 additions and 121 deletions
+75 -55
View File
@@ -5,15 +5,16 @@
#include <assert.h> #include <assert.h>
#include <shellapi.h> #include <shellapi.h>
#include <objbase.h> #include <objbase.h>
#include <WinInet.h> #include <WinHttp.h>
#include <wtsapi32.h> #include <wtsapi32.h>
#include <powerbase.h> #include <powerbase.h>
#include <VersionHelpers.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")
#pragma comment(lib, "iphlpapi.lib") #pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "wininet.lib") #pragma comment(lib, "winhttp.lib")
#pragma comment(lib, "wtsapi32.lib") #pragma comment(lib, "wtsapi32.lib")
#pragma comment(lib, "powrprof.lib") #pragma comment(lib, "powrprof.lib")
@@ -25,6 +26,8 @@
#define NATPMP_STATICLIB #define NATPMP_STATICLIB
#include <natpmp.h> #include <natpmp.h>
#define LOOPBACK_SERVER_PORT_OFFSET -10000
static struct port_entry { static struct port_entry {
int proto; int proto;
int port; int port;
@@ -456,7 +459,7 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
sizeof(SOCKADDR_IN) : sizeof(SOCKADDR_IN6); sizeof(SOCKADDR_IN) : sizeof(SOCKADDR_IN6);
RtlCopyMemory(&sin6, addr, addrLen); RtlCopyMemory(&sin6, addr, addrLen);
sin6.sin6_port = htons(port); sin6.sin6_port = htons(port + (isLoopbackRelay ? LOOPBACK_SERVER_PORT_OFFSET : 0));
if (proto == IPPROTO_TCP) { if (proto == IPPROTO_TCP) {
err = connect(clientSock, (struct sockaddr*)&sin6, addrLen); err = connect(clientSock, (struct sockaddr*)&sin6, addrLen);
@@ -546,64 +549,96 @@ 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 hSession = nullptr;
HINTERNET hRequest; HINTERNET hConnection = nullptr;
char url[1024]; HINTERNET hRequest = nullptr;
PortTestStatus result;
hInternet = InternetOpenA("MIST", 0, nullptr, nullptr, 0); hSession = WinHttpOpen(L"MIST", WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (hInternet == nullptr) { if (hSession == nullptr) {
fprintf(LOG_OUT, "InternetOpen() failed: %d\n", GetLastError()); fprintf(LOG_OUT, "WinHttpOpen() failed: %d\n", GetLastError());
return PortTestError; result = PortTestError;
goto Exit;
} }
char addrStr[INET6_ADDRSTRLEN + 2]; // 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;
if (!WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &protocols, sizeof(protocols))) {
fprintf(LOG_OUT, "WinHttpSetOption(WINHTTP_OPTION_SECURE_PROTOCOLS) failed: %d\n", GetLastError());
}
}
WCHAR urlEscapedAddr[INET6_ADDRSTRLEN + 2];
if (addr->ss_family == AF_INET) { if (addr->ss_family == AF_INET) {
inet_ntop(AF_INET, &((struct sockaddr_in*)addr)->sin_addr, addrStr, sizeof(addrStr)); InetNtopW(AF_INET, &((struct sockaddr_in*)addr)->sin_addr, urlEscapedAddr, ARRAYSIZE(urlEscapedAddr));
} }
else { else {
// The address string must be escaped for usage in URLs // The address string must be escaped for usage in URLs
addrStr[0] = '['; urlEscapedAddr[0] = L'[';
inet_ntop(AF_INET6, &((struct sockaddr_in6*)addr)->sin6_addr, &addrStr[1], INET6_ADDRSTRLEN); InetNtopW(AF_INET6, &((struct sockaddr_in6*)addr)->sin6_addr, &urlEscapedAddr[1], INET6_ADDRSTRLEN);
strcat_s(addrStr, "]"); wcscat_s(urlEscapedAddr, L"]");
} }
sprintf(url, "%s://%s:%d/", hConnection = WinHttpConnect(hSession, urlEscapedAddr, port + (isLoopbackRelay ? LOOPBACK_SERVER_PORT_OFFSET : 0), NULL);
port == 47989 ? "http" : "https", if (hConnection == nullptr) {
addrStr, fprintf(LOG_OUT, "WinHttpConnect() failed: %d\n", GetLastError());
port); result = PortTestError;
goto Exit;
}
hRequest = InternetOpenUrlA(hInternet, url, nullptr, 0, hRequest = WinHttpOpenRequest(hConnection, L"GET", L"/", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES,
INTERNET_FLAG_IGNORE_CERT_DATE_INVALID | INTERNET_FLAG_NO_UI | INTERNET_FLAG_NO_AUTH | INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_RELOAD, port == 47984 ? WINHTTP_FLAG_SECURE : 0);
NULL); if (hConnection == nullptr) {
if (hRequest == nullptr) { fprintf(LOG_OUT, "WinHttpOpenRequest() failed: %d\n", GetLastError());
if (GetLastError() == ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED) { WinHttpCloseHandle(hConnection);
result = PortTestError;
goto Exit;
}
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 // This is expected for our HTTPS connection
fprintf(LOG_OUT, "Success\n"); fprintf(LOG_OUT, "Success\n");
result = PortTestOk;
} }
else { else {
// CANNOT_CONNECT is the "expected" error // CANNOT_CONNECT is the "expected" error
if (GetLastError() == ERROR_INTERNET_CANNOT_CONNECT) { if (GetLastError() == ERROR_WINHTTP_CANNOT_CONNECT) {
fprintf(LOG_OUT, "Failed\n"); fprintf(LOG_OUT, "Failed\n");
} }
else { else {
fprintf(LOG_OUT, "Failed: %d\n", GetLastError()); fprintf(LOG_OUT, "Failed: %d\n", GetLastError());
} }
InternetCloseHandle(hInternet); result = PortTestError;
return PortTestError;
} }
goto Exit;
} }
else { else {
fprintf(LOG_OUT, "Success\n"); fprintf(LOG_OUT, "Success\n");
InternetCloseHandle(hRequest); result = PortTestOk;
} }
Exit:
if (hRequest != nullptr) {
WinHttpCloseHandle(hRequest);
}
if (hConnection != nullptr) {
WinHttpCloseHandle(hConnection);
}
if (hSession != nullptr) {
WinHttpCloseHandle(hSession);
}
InternetCloseHandle(hInternet); return result;
return PortTestOk;
} }
bool TestAllPorts(PSOCKADDR_STORAGE addr, char* portMsg, int portMsgLen, bool isLoopbackRelay, bool consolePrint, bool* allPortsFailed = nullptr) bool TestAllPorts(PSOCKADDR_STORAGE addr, char* portMsg, int portMsgLen, bool isLoopbackRelay, bool consolePrint, bool* allPortsFailed = nullptr)
@@ -633,7 +668,7 @@ bool TestAllPorts(PSOCKADDR_STORAGE addr, char* portMsg, int portMsgLen, bool is
// TestHttpPort() can take significantly longer to timeout than TestPort(), // 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. // 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); fprintf(LOG_OUT, "Testing TCP %d with HTTP traffic...", k_Ports[i].port);
status = TestHttpPort(addr, k_Ports[i].port); status = TestHttpPort(addr, k_Ports[i].port, isLoopbackRelay);
} }
if (status != PortTestOk) { if (status != PortTestOk) {
@@ -673,7 +708,7 @@ bool FindLocalInterfaceIPAddress(int family, PSOCKADDR_STORAGE addr)
hint.ai_socktype = SOCK_STREAM; hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = IPPROTO_TCP; hint.ai_protocol = IPPROTO_TCP;
hint.ai_flags = AI_ADDRCONFIG; hint.ai_flags = AI_ADDRCONFIG;
err = getaddrinfo("moonlight-stream.org", "https", &hint, &result); err = getaddrinfo("moonlight-stream.org", "443", &hint, &result);
if (err != 0 || result == NULL) { if (err != 0 || result == NULL) {
fprintf(LOG_OUT, "getaddrinfo() failed: %d\n", err); fprintf(LOG_OUT, "getaddrinfo() failed: %d\n", err);
return false; return false;
@@ -1231,8 +1266,6 @@ int main(int argc, char* argv[])
return -1; return -1;
} }
fprintf(CONSOLE_OUT, "Testing GameStream connectivity over the Internet...\n");
// Detect a double NAT by detecting STUN and and UPnP mismatches // 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) { 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"); fprintf(LOG_OUT, "Testing GameStream ports via UPnP/NAT-PMP reported WAN address\n");
@@ -1250,22 +1283,14 @@ int main(int argc, char* argv[])
// Go directly to the relay check if we have only IPv6 connectivity // Go directly to the relay check if we have only IPv6 connectivity
igdDisconnected = false; igdDisconnected = false;
locallyReportedWanAddr = {}; locallyReportedWanAddr = {};
goto RelayCheck;
} }
// Try to connect via WAN IPv4 address
fprintf(LOG_OUT, "Testing GameStream ports via STUN-reported WAN address\n");
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf), false, true)) {
RelayCheck:
struct addrinfo hint = {}; struct addrinfo hint = {};
struct addrinfo* result; 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_family = AF_UNSPEC;
hint.ai_flags = AI_ADDRCONFIG; hint.ai_flags = AI_ADDRCONFIG;
err = getaddrinfo("loopback.moonlight-stream.org", NULL, &hint, &result); err = getaddrinfo("loopback-v2.moonlight-stream.org", NULL, &hint, &result);
if (err != 0 || result == NULL) { if (err != 0 || result == NULL) {
fprintf(LOG_OUT, "getaddrinfo() failed: %d\n", err); fprintf(LOG_OUT, "getaddrinfo() failed: %d\n", err);
} }
@@ -1279,7 +1304,11 @@ int main(int argc, char* argv[])
fprintf(CONSOLE_OUT, "Testing GameStream connectivity over the Internet using a relay server...\n"); 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)) { if (TestAllPorts((PSOCKADDR_STORAGE)current->ai_addr, portMsgBuf, sizeof(portMsgBuf), true, true, &allPortsFailedOnV4)) {
freeaddrinfo(result); freeaddrinfo(result);
goto AllTestsPassed; 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;
} }
} }
} }
@@ -1338,13 +1367,4 @@ int main(int argc, char* argv[])
} }
return -1; return -1;
}
AllTestsPassed:
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;
} }
+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>
+15
View File
@@ -100,6 +100,9 @@
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>..\libs\x86\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>..\libs\x86\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link> </Link>
<Manifest>
<AdditionalManifestFiles>mist.exe.manifest</AdditionalManifestFiles>
</Manifest>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile> <ClCompile>
@@ -115,6 +118,9 @@
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
</Link> </Link>
<Manifest>
<AdditionalManifestFiles>mist.exe.manifest</AdditionalManifestFiles>
</Manifest>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile> <ClCompile>
@@ -138,6 +144,9 @@
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>..\libs\x86\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>..\libs\x86\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link> </Link>
<Manifest>
<AdditionalManifestFiles>mist.exe.manifest</AdditionalManifestFiles>
</Manifest>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile> <ClCompile>
@@ -157,6 +166,9 @@
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
</Link> </Link>
<Manifest>
<AdditionalManifestFiles>mist.exe.manifest</AdditionalManifestFiles>
</Manifest>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="mist.cpp" /> <ClCompile Include="mist.cpp" />
@@ -169,6 +181,9 @@
<ClInclude Include="..\version.h" /> <ClInclude Include="..\version.h" />
<ClInclude Include="mist.h" /> <ClInclude Include="mist.h" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Manifest Include="mist.exe.manifest" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
</ImportGroup> </ImportGroup>
+5
View File
@@ -35,4 +35,9 @@
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ResourceCompile> </ResourceCompile>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Manifest Include="mist.exe.manifest">
<Filter>Resource Files</Filter>
</Manifest>
</ItemGroup>
</Project> </Project>
+2 -2
View File
@@ -1,7 +1,7 @@
#pragma once #pragma once
#define VER_VERSION 4,4,0,0 #define VER_VERSION 5,0,0,0
#define VER_VERSION_STR "4.4.0.0" #define VER_VERSION_STR "5.0.0.0"
#define VER_COMPANYNAME_STR "Moonlight Game Streaming Project" #define VER_COMPANYNAME_STR "Moonlight Game Streaming Project"
#define VER_PRODUCTNAME_STR "Moonlight Internet Hosting Tool" #define VER_PRODUCTNAME_STR "Moonlight Internet Hosting Tool"