Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d2252c4ac | ||
|
|
365b3ccd61 | ||
|
|
74140a8511 | ||
|
|
a9bf4ffd70 | ||
|
|
164d8b5467 | ||
|
|
f1ebefa59c | ||
|
|
3e060fae54 | ||
|
|
f97c04a8cc | ||
|
|
85cd20005c | ||
|
|
e3e4bf608f | ||
|
|
0be2ca51e9 | ||
|
|
3e1ffce16f | ||
|
|
c7813a7a0b | ||
|
|
87fd3ad8e2 | ||
|
|
9d3ef0a215 | ||
|
|
ae83853a27 | ||
|
|
0b8b117cb2 | ||
|
|
0050566124 | ||
|
|
cc24472193 | ||
|
|
4a11ab0f7f | ||
|
|
c1c16edf7d | ||
|
|
ae2647f36e | ||
|
|
8e40ef80bd | ||
|
|
16bb727b99 | ||
|
|
9194b8e22b | ||
|
|
2805464e86 | ||
|
|
20e7374480 | ||
|
|
0fdbee831b | ||
|
|
b37b99e9c3 | ||
|
|
29bbdc5319 | ||
|
|
09cd50b12a | ||
|
|
d60eda6fa8 | ||
|
|
cda9bc6be7 | ||
|
|
0008fbbcdb |
+1
-1
Submodule GS-IPv6-Forwarder updated: 7b395a9f2f...67f1e926b2
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.
+1
-3
@@ -47,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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
+496
-120
@@ -5,13 +5,18 @@
|
||||
#include <assert.h>
|
||||
#include <shellapi.h>
|
||||
#include <objbase.h>
|
||||
#include <WinInet.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>
|
||||
@@ -21,6 +26,8 @@
|
||||
#define NATPMP_STATICLIB
|
||||
#include <natpmp.h>
|
||||
|
||||
#define LOOPBACK_SERVER_PORT_OFFSET -10000
|
||||
|
||||
static struct port_entry {
|
||||
int proto;
|
||||
int port;
|
||||
@@ -178,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;
|
||||
@@ -211,6 +268,115 @@ bool IsGameStreamEnabled()
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -293,7 +459,7 @@ 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);
|
||||
@@ -383,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 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) {
|
||||
fprintf(LOG_OUT, "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[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) {
|
||||
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 {
|
||||
// The address string must be escaped for usage in URLs
|
||||
addrStr[0] = '[';
|
||||
inet_ntop(AF_INET6, &((struct sockaddr_in6*)addr)->sin6_addr, &addrStr[1], INET6_ADDRSTRLEN);
|
||||
strcat_s(addrStr, "]");
|
||||
urlEscapedAddr[0] = L'[';
|
||||
InetNtopW(AF_INET6, &((struct sockaddr_in6*)addr)->sin6_addr, &urlEscapedAddr[1], INET6_ADDRSTRLEN);
|
||||
wcscat_s(urlEscapedAddr, L"]");
|
||||
}
|
||||
|
||||
sprintf(url, "%s://%s:%d/",
|
||||
port == 47989 ? "http" : "https",
|
||||
addrStr,
|
||||
port);
|
||||
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 = 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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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_INTERNET_CANNOT_CONNECT) {
|
||||
if (GetLastError() == ERROR_WINHTTP_CANNOT_CONNECT) {
|
||||
fprintf(LOG_OUT, "Failed\n");
|
||||
}
|
||||
else {
|
||||
fprintf(LOG_OUT, "Failed: %d\n", GetLastError());
|
||||
}
|
||||
|
||||
InternetCloseHandle(hInternet);
|
||||
return PortTestError;
|
||||
result = PortTestError;
|
||||
}
|
||||
|
||||
goto Exit;
|
||||
}
|
||||
else {
|
||||
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 PortTestOk;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TestAllPorts(PSOCKADDR_STORAGE addr, char* portMsg, int portMsgLen, bool isLoopbackRelay, bool consolePrint, bool* allPortsFailed = nullptr)
|
||||
@@ -470,7 +668,7 @@ bool TestAllPorts(PSOCKADDR_STORAGE addr, char* portMsg, int portMsgLen, bool is
|
||||
// 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);
|
||||
status = TestHttpPort(addr, k_Ports[i].port, isLoopbackRelay);
|
||||
}
|
||||
|
||||
if (status != PortTestOk) {
|
||||
@@ -510,7 +708,7 @@ bool FindLocalInterfaceIPAddress(int family, PSOCKADDR_STORAGE addr)
|
||||
hint.ai_socktype = SOCK_STREAM;
|
||||
hint.ai_protocol = IPPROTO_TCP;
|
||||
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) {
|
||||
fprintf(LOG_OUT, "getaddrinfo() failed: %d\n", err);
|
||||
return false;
|
||||
@@ -554,6 +752,105 @@ bool FindLocalInterfaceIPAddress(int family, PSOCKADDR_STORAGE addr)
|
||||
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,
|
||||
@@ -732,7 +1029,7 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, PSOCKADDR_IN reportedWanAddr, bool* fo
|
||||
fprintf(LOG_OUT, "Detecting WAN IP address via STUN...");
|
||||
fprintf(CONSOLE_OUT, "\tTesting STUN...\n");
|
||||
|
||||
if (!getExternalAddressPortIP4(IPPROTO_UDP, 0, wanAddr) && !getExternalAddressPortIP4(IPPROTO_TCP, 0, wanAddr)) {
|
||||
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;
|
||||
}
|
||||
@@ -823,6 +1120,58 @@ int main(int argc, char* argv[])
|
||||
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;
|
||||
@@ -844,6 +1193,49 @@ int main(int argc, char* argv[])
|
||||
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 (!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.");
|
||||
@@ -861,7 +1253,6 @@ int main(int argc, char* argv[])
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
bool igdDisconnected;
|
||||
SOCKADDR_IN locallyReportedWanAddr;
|
||||
char wanAddrStr[INET_ADDRSTRLEN];
|
||||
@@ -875,8 +1266,6 @@ int main(int argc, char* argv[])
|
||||
return -1;
|
||||
}
|
||||
|
||||
fprintf(CONSOLE_OUT, "Testing GameStream connectivity over the Internet...\n");
|
||||
|
||||
// Detect a double NAT by detecting STUN and and UPnP mismatches
|
||||
if (sin.sin_addr.S_un.S_addr != locallyReportedWanAddr.sin_addr.S_un.S_addr) {
|
||||
fprintf(LOG_OUT, "Testing GameStream ports via UPnP/NAT-PMP reported WAN address\n");
|
||||
@@ -894,101 +1283,88 @@ int main(int argc, char* argv[])
|
||||
// Go directly to the relay check if we have only IPv6 connectivity
|
||||
igdDisconnected = false;
|
||||
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* result;
|
||||
struct addrinfo hint = {};
|
||||
struct addrinfo* result;
|
||||
|
||||
// We can get here if the router doesn't support NAT reflection.
|
||||
// We'll need to call out to our loopback server to get a second opinion.
|
||||
hint.ai_family = AF_UNSPEC;
|
||||
hint.ai_flags = AI_ADDRCONFIG;
|
||||
err = getaddrinfo("loopback-v2.moonlight-stream.org", NULL, &hint, &result);
|
||||
if (err != 0 || result == NULL) {
|
||||
fprintf(LOG_OUT, "getaddrinfo() failed: %d\n", err);
|
||||
}
|
||||
else {
|
||||
bool allPortsFailedOnV4 = true;
|
||||
|
||||
hint.ai_family = AF_UNSPEC;
|
||||
hint.ai_flags = AI_ADDRCONFIG;
|
||||
err = getaddrinfo("loopback.moonlight-stream.org", NULL, &hint, &result);
|
||||
if (err != 0 || result == NULL) {
|
||||
fprintf(LOG_OUT, "getaddrinfo() failed: %d\n", err);
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
bool allPortsFailedOnV4 = true;
|
||||
|
||||
// First try the relay server over IPv4. If this passes, it's considered a full success
|
||||
fprintf(LOG_OUT, "Testing GameStream ports via IPv4 loopback server\n");
|
||||
for (struct addrinfo* current = result; current != NULL; current = current->ai_next) {
|
||||
if (current->ai_family == AF_INET) {
|
||||
fprintf(CONSOLE_OUT, "Testing GameStream connectivity over the Internet using a relay server...\n");
|
||||
if (TestAllPorts((PSOCKADDR_STORAGE)current->ai_addr, portMsgBuf, sizeof(portMsgBuf), true, true, &allPortsFailedOnV4)) {
|
||||
// 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);
|
||||
goto AllTestsPassed;
|
||||
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);
|
||||
}
|
||||
|
||||
// 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"
|
||||
"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;
|
||||
freeaddrinfo(result);
|
||||
}
|
||||
|
||||
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);
|
||||
// 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 0;
|
||||
return -1;
|
||||
}
|
||||
@@ -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>
|
||||
+3
-1
@@ -7,6 +7,8 @@
|
||||
#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"
|
||||
@@ -14,4 +16,4 @@
|
||||
#define CONSOLE_OUT stdout
|
||||
#define LOG_OUT stderr
|
||||
|
||||
bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN wanAddr);
|
||||
bool getExternalAddressPortIP4(unsigned short localPort, PSOCKADDR_IN wanAddr);
|
||||
@@ -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" />
|
||||
@@ -169,6 +181,9 @@
|
||||
<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">
|
||||
</ImportGroup>
|
||||
|
||||
@@ -35,4 +35,9 @@
|
||||
<Filter>Source Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Manifest Include="mist.exe.manifest">
|
||||
<Filter>Resource Files</Filter>
|
||||
</Manifest>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
+27
-41
@@ -4,7 +4,7 @@
|
||||
|
||||
#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
|
||||
@@ -38,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) {
|
||||
fprintf(LOG_OUT, "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) {
|
||||
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) {
|
||||
fprintf(LOG_OUT, "bind() failed: %d\n", WSAGetLastError());
|
||||
closesocket(sock);
|
||||
freeaddrinfo(result);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -82,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) {
|
||||
fprintf(LOG_OUT, "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) {
|
||||
fprintf(LOG_OUT, "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;
|
||||
@@ -119,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);
|
||||
@@ -130,14 +114,16 @@ bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN
|
||||
else if (selectRes == SOCKET_ERROR) {
|
||||
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) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#define VER_VERSION 4,1,0,0
|
||||
#define VER_VERSION_STR "4.1.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 Hosting Tool"
|
||||
|
||||
Reference in New Issue
Block a user