mirror of
https://github.com/moonlight-stream/Internet-Hosting-Tool.git
synced 2025-07-03 16:25:24 +00:00
Improve reliability with NAT reflection disabled, UPnP IGD reporting disconnected, double-NATs, and CGNs
This commit is contained in:
parent
7bc806edb4
commit
45b624c203
178
mist/mist.cpp
178
mist/mist.cpp
@ -65,9 +65,17 @@ static struct port_entry {
|
|||||||
|
|
||||||
char logFilePath[MAX_PATH + 1];
|
char logFilePath[MAX_PATH + 1];
|
||||||
|
|
||||||
void DisplayMessage(const char* message, bool error = true)
|
enum MessagePriority {
|
||||||
|
MpInfo,
|
||||||
|
MpWarn,
|
||||||
|
MpError
|
||||||
|
};
|
||||||
|
|
||||||
|
void DisplayMessage(const char* message, MessagePriority priority = MpError, bool terminal = true)
|
||||||
{
|
{
|
||||||
printf("%s\n", message);
|
printf("%s\n", message);
|
||||||
|
|
||||||
|
if (terminal) {
|
||||||
printf("--------------- MISS LOG -------------------\n");
|
printf("--------------- MISS LOG -------------------\n");
|
||||||
|
|
||||||
char missPath[MAX_PATH + 1];
|
char missPath[MAX_PATH + 1];
|
||||||
@ -86,12 +94,24 @@ void DisplayMessage(const char* message, bool error = true)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
DWORD flags = MB_OK | MB_TOPMOST | MB_SETFOREGROUND;
|
DWORD flags = MB_OK | MB_TOPMOST | MB_SETFOREGROUND;
|
||||||
flags |= error ? MB_ICONERROR : MB_ICONINFORMATION;
|
switch (priority) {
|
||||||
|
case MpInfo:
|
||||||
|
flags |= MB_ICONINFORMATION;
|
||||||
|
break;
|
||||||
|
case MpWarn:
|
||||||
|
flags |= MB_ICONWARNING;
|
||||||
|
break;
|
||||||
|
case MpError:
|
||||||
|
flags |= MB_ICONERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
MessageBoxA(nullptr, message, "Moonlight Internet Streaming Tester", flags);
|
MessageBoxA(nullptr, message, "Moonlight Internet Streaming Tester", flags);
|
||||||
|
|
||||||
if (error) {
|
if (priority == MpError && terminal) {
|
||||||
flags = MB_YESNO | MB_TOPMOST | MB_SETFOREGROUND | MB_ICONINFORMATION;
|
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?",
|
||||||
"Moonlight Internet Streaming Tester", flags))
|
"Moonlight Internet Streaming Tester", flags))
|
||||||
@ -115,7 +135,7 @@ bool IsGameStreamEnabled()
|
|||||||
error = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\NVIDIA Corporation\\NvStream", 0, KEY_READ | KEY_WOW64_64KEY, &key);
|
error = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\NVIDIA Corporation\\NvStream", 0, KEY_READ | KEY_WOW64_64KEY, &key);
|
||||||
if (error != ERROR_SUCCESS) {
|
if (error != ERROR_SUCCESS) {
|
||||||
printf("RegOpenKeyEx() failed: %d\n", error);
|
printf("RegOpenKeyEx() failed: %d\n", error);
|
||||||
DisplayMessage("GeForce Experience is not installed. Please install GeForce Experience to use Moonlight.");
|
DisplayMessage("GeForce Experience was not detected on this PC. Make sure you're installing this utility on your GeForce GameStream-compatible PC, not the device running Moonlight.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,7 +273,7 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
|
|||||||
return err == 1 ? PortTestOk : PortTestError;
|
return err == 1 ? PortTestOk : PortTestError;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const char testMsg[] = "mist-test";
|
const char testMsg[] = "moonlight-test";
|
||||||
err = sendto(clientSock, testMsg, sizeof(testMsg), 0, (struct sockaddr*)&sin6, addrLen);
|
err = sendto(clientSock, testMsg, sizeof(testMsg), 0, (struct sockaddr*)&sin6, addrLen);
|
||||||
if (err == SOCKET_ERROR) {
|
if (err == SOCKET_ERROR) {
|
||||||
printf("sendto() failed: %d\n", WSAGetLastError());
|
printf("sendto() failed: %d\n", WSAGetLastError());
|
||||||
@ -289,13 +309,10 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TestAllPorts(PSOCKADDR_STORAGE addr, const char* baseMessage, char* message, int messageLength)
|
bool TestAllPorts(PSOCKADDR_STORAGE addr, char* portMsg, int portMsgLen)
|
||||||
{
|
{
|
||||||
strcpy_s(message, messageLength, baseMessage);
|
|
||||||
message += strlen(baseMessage);
|
|
||||||
messageLength -= strlen(baseMessage);
|
|
||||||
|
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
|
|
||||||
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
||||||
printf("Testing %s %d...",
|
printf("Testing %s %d...",
|
||||||
k_Ports[i].proto == IPPROTO_TCP ? "TCP" : "UDP",
|
k_Ports[i].proto == IPPROTO_TCP ? "TCP" : "UDP",
|
||||||
@ -305,11 +322,11 @@ bool TestAllPorts(PSOCKADDR_STORAGE addr, const char* baseMessage, char* message
|
|||||||
// If we got an unknown result, assume it matches with whatever
|
// If we got an unknown result, assume it matches with whatever
|
||||||
// we've gotten so far.
|
// we've gotten so far.
|
||||||
if (status == PortTestError || !ret) {
|
if (status == PortTestError || !ret) {
|
||||||
int msgLen = snprintf(message, messageLength, "%s %d\n",
|
int msgLen = snprintf(portMsg, portMsgLen, "%s %d\n",
|
||||||
k_Ports[i].proto == IPPROTO_TCP ? "TCP" : "UDP",
|
k_Ports[i].proto == IPPROTO_TCP ? "TCP" : "UDP",
|
||||||
k_Ports[i].port);
|
k_Ports[i].port);
|
||||||
message += msgLen;
|
portMsg += msgLen;
|
||||||
messageLength -= msgLen;
|
portMsgLen -= msgLen;
|
||||||
|
|
||||||
// Keep going to check all ports and report the failing ones
|
// Keep going to check all ports and report the failing ones
|
||||||
ret = false;
|
ret = false;
|
||||||
@ -524,14 +541,14 @@ bool STUNFindWanAddress(PSOCKADDR_IN wanAddr)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckWANAccess(PSOCKADDR_IN wanAddr, bool* foundPortForwardingRules)
|
bool CheckWANAccess(PSOCKADDR_IN wanAddr, PSOCKADDR_IN reportedWanAddr, bool* foundPortForwardingRules, bool* igdDisconnected)
|
||||||
{
|
{
|
||||||
natpmp_t natpmp;
|
natpmp_t natpmp;
|
||||||
|
|
||||||
*foundPortForwardingRules = false;
|
*foundPortForwardingRules = false;
|
||||||
|
*igdDisconnected = false;
|
||||||
|
|
||||||
printf("Finding WAN IP address...");
|
bool gotReportedWanAddress = false;
|
||||||
bool gotWanAddress = false;
|
|
||||||
int natPmpErr = initnatpmp(&natpmp, 0, 0);
|
int natPmpErr = initnatpmp(&natpmp, 0, 0);
|
||||||
if (natPmpErr != 0) {
|
if (natPmpErr != 0) {
|
||||||
printf("initnatpmp() failed: %d\n", natPmpErr);
|
printf("initnatpmp() failed: %d\n", natPmpErr);
|
||||||
@ -556,15 +573,23 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, bool* foundPortForwardingRules)
|
|||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
// Connected or disconnected IGD
|
// Connected or disconnected IGD
|
||||||
if (ret == 1 || ret == 2) {
|
if (ret == 1 || ret == 2) {
|
||||||
|
if (ret == 2) {
|
||||||
|
*igdDisconnected = true;
|
||||||
|
}
|
||||||
|
printf("Discovered UPnP IGD at: %s\n", urls.controlURL);
|
||||||
|
printf("Detecting WAN IP address via UPnP...");
|
||||||
ret = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, wanAddrStr);
|
ret = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, wanAddrStr);
|
||||||
if (ret == UPNPCOMMAND_SUCCESS && strlen(wanAddrStr) > 0) {
|
if (ret == UPNPCOMMAND_SUCCESS && strlen(wanAddrStr) > 0) {
|
||||||
wanAddr->sin_addr.S_un.S_addr = inet_addr(wanAddrStr);
|
reportedWanAddr->sin_addr.S_un.S_addr = wanAddr->sin_addr.S_un.S_addr = inet_addr(wanAddrStr);
|
||||||
printf("%s (UPnP)\n", wanAddrStr);
|
printf("%s\n", wanAddrStr);
|
||||||
|
|
||||||
if (wanAddr->sin_addr.S_un.S_addr != 0) {
|
if (wanAddr->sin_addr.S_un.S_addr != 0) {
|
||||||
gotWanAddress = true;
|
gotReportedWanAddress = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
printf("FAILED %d\n", ret);
|
||||||
|
}
|
||||||
|
|
||||||
char conflictMessage[512];
|
char conflictMessage[512];
|
||||||
*foundPortForwardingRules = true;
|
*foundPortForwardingRules = true;
|
||||||
@ -588,6 +613,9 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, bool* foundPortForwardingRules)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
printf("No UPnP IGD detected\n");
|
||||||
|
}
|
||||||
|
|
||||||
FreeUPNPUrls(&urls);
|
FreeUPNPUrls(&urls);
|
||||||
}
|
}
|
||||||
@ -595,36 +623,46 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, bool* foundPortForwardingRules)
|
|||||||
|
|
||||||
// Use the delay of upnpDiscoverAll() to also allow the NAT-PMP endpoint time to respond
|
// Use the delay of upnpDiscoverAll() to also allow the NAT-PMP endpoint time to respond
|
||||||
if (natPmpErr >= 0) {
|
if (natPmpErr >= 0) {
|
||||||
|
printf("Detecting WAN IP address via NAT-PMP...");
|
||||||
|
|
||||||
natpmpresp_t response;
|
natpmpresp_t response;
|
||||||
natPmpErr = readnatpmpresponseorretry(&natpmp, &response);
|
natPmpErr = readnatpmpresponseorretry(&natpmp, &response);
|
||||||
closenatpmp(&natpmp);
|
closenatpmp(&natpmp);
|
||||||
|
|
||||||
if (natPmpErr == 0 && !gotWanAddress) {
|
if (natPmpErr == 0) {
|
||||||
char addrStr[64];
|
char addrStr[64];
|
||||||
wanAddr->sin_addr = response.pnu.publicaddress.addr;
|
reportedWanAddr->sin_addr = wanAddr->sin_addr = response.pnu.publicaddress.addr;
|
||||||
inet_ntop(AF_INET, &response.pnu.publicaddress.addr, addrStr, sizeof(addrStr));
|
inet_ntop(AF_INET, &response.pnu.publicaddress.addr, addrStr, sizeof(addrStr));
|
||||||
printf("%s (NAT-PMP)\n", addrStr);
|
printf("%s\n", addrStr);
|
||||||
if (wanAddr->sin_addr.S_un.S_addr != 0) {
|
if (wanAddr->sin_addr.S_un.S_addr != 0) {
|
||||||
gotWanAddress = true;
|
gotReportedWanAddress = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
printf("FAILED %d\n", natPmpErr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gotWanAddress) {
|
printf("Detecting WAN IP address via STUN...");
|
||||||
if (!STUNFindWanAddress(wanAddr)) {
|
if (!STUNFindWanAddress(wanAddr)) {
|
||||||
printf("FAILED\n");
|
if (!gotReportedWanAddress) {
|
||||||
DisplayMessage("MIST was unable to determine your public IP address. Please check your Internet connection.");
|
DisplayMessage("Unable to determine your public IP address. Please check your Internet connection.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
char addrStr[64];
|
char addrStr[64];
|
||||||
inet_ntop(AF_INET, &wanAddr->sin_addr, addrStr, sizeof(addrStr));
|
inet_ntop(AF_INET, &wanAddr->sin_addr, addrStr, sizeof(addrStr));
|
||||||
printf("%s (STUN)\n", addrStr);
|
printf("%s (STUN)\n", addrStr);
|
||||||
return true;
|
|
||||||
|
if (!gotReportedWanAddress) {
|
||||||
|
// If we didn't get anything from UPnP or NAT-PMP, just populate the reported
|
||||||
|
// address with what we got from STUN
|
||||||
|
*reportedWanAddr = *wanAddr;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsPossibleCGN(PSOCKADDR_IN wanAddr)
|
bool IsPossibleCGN(PSOCKADDR_IN wanAddr)
|
||||||
@ -692,6 +730,7 @@ int main(int argc, char* argv[])
|
|||||||
SOCKADDR_IN6 sin6;
|
SOCKADDR_IN6 sin6;
|
||||||
};
|
};
|
||||||
char msgBuf[2048];
|
char msgBuf[2048];
|
||||||
|
char portMsgBuf[512];
|
||||||
|
|
||||||
fprintf(stderr, "Testing local GameStream connectivity...\n");
|
fprintf(stderr, "Testing local GameStream connectivity...\n");
|
||||||
|
|
||||||
@ -700,9 +739,10 @@ int main(int argc, char* argv[])
|
|||||||
sin.sin_family = AF_INET;
|
sin.sin_family = AF_INET;
|
||||||
sin.sin_addr = in4addr_loopback;
|
sin.sin_addr = in4addr_loopback;
|
||||||
printf("Testing GameStream ports via loopback\n");
|
printf("Testing GameStream ports via loopback\n");
|
||||||
if (!TestAllPorts(&ss,
|
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf))) {
|
||||||
"Local GameStream connectivity check failed. Please try reinstalling GeForce Experience.\n\nThe following ports were not working:\n",
|
snprintf(msgBuf, sizeof(msgBuf),
|
||||||
msgBuf, sizeof(msgBuf))) {
|
"Local GameStream connectivity check failed. Please try reinstalling GeForce Experience.\n\nThe following ports were not working:\n%s",
|
||||||
|
portMsgBuf);
|
||||||
DisplayMessage(msgBuf);
|
DisplayMessage(msgBuf);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -716,47 +756,69 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
// Try to connect via LAN IPv4 address
|
// Try to connect via LAN IPv4 address
|
||||||
printf("Testing GameStream ports via local network\n");
|
printf("Testing GameStream ports via local network\n");
|
||||||
if (!TestAllPorts(&ss,
|
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf))) {
|
||||||
"Local network GameStream connectivity check failed. Try temporarily disabling your firewall software or adding firewall exceptions for the following ports:\n",
|
snprintf(msgBuf, sizeof(msgBuf),
|
||||||
msgBuf, sizeof(msgBuf))) {
|
"Local network GameStream connectivity check failed. Try temporarily disabling your firewall software or adding firewall exceptions for the following ports:\n%s",
|
||||||
|
portMsgBuf);
|
||||||
DisplayMessage(msgBuf);
|
DisplayMessage(msgBuf);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "Detecting public IP address...\n");
|
fprintf(stderr, "Detecting public IP address...\n");
|
||||||
|
|
||||||
bool upnpRulesFound;
|
bool upnpRulesFound, igdDisconnected;
|
||||||
if (!CheckWANAccess(&sin, &upnpRulesFound)) {
|
SOCKADDR_IN locallyReportedWanAddr;
|
||||||
|
if (!CheckWANAccess(&sin, &locallyReportedWanAddr, &upnpRulesFound, &igdDisconnected)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (igdDisconnected) {
|
||||||
|
DisplayMessage("Your router reports to be disconnected from the Internet. Make sure UPnP is enabled in your router settings. "
|
||||||
|
"If this message persists, make sure your router isn't connected to the Internet through another router. If it is, switch one of the routers to bridge mode.\n\n"
|
||||||
|
"Just in case this warning is due to a buggy router, the test will continue anyway.", MpWarn, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
|
||||||
|
// 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(&ss, portMsgBuf, sizeof(portMsgBuf));
|
||||||
|
|
||||||
|
printf("Detected inconsistency between UPnP/NAT-PMP and STUN reported WAN addresses!\n");
|
||||||
|
}
|
||||||
|
|
||||||
fprintf(stderr, "Testing Internet GameStream connectivity...\n");
|
fprintf(stderr, "Testing Internet GameStream connectivity...\n");
|
||||||
|
|
||||||
|
char wanAddrStr[64];
|
||||||
|
inet_ntop(AF_INET, &sin.sin_addr, wanAddrStr, sizeof(wanAddrStr));
|
||||||
|
|
||||||
// Try to connect via WAN IPv4 address
|
// Try to connect via WAN IPv4 address
|
||||||
printf("Testing GameStream ports via WAN address\n");
|
printf("Testing GameStream ports via STUN-reported WAN address\n");
|
||||||
if (!TestAllPorts(&ss,
|
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf))) {
|
||||||
upnpRulesFound ? "Found UPnP rules, but they did not work correctly. Check for conflicting port forwarding entries in your router settings.\n\nThe following ports were not forwarded properly:\n" :
|
if (IsDoubleNAT(&locallyReportedWanAddr)) {
|
||||||
"Internet GameStream connectivity check failed. Make sure UPnP is enabled in your router settings.\n\nThe following ports were not forwarded properly:\n",
|
DisplayMessage("Your router appears be connected to the Internet through another router. This configuration breaks port forwarding. To resolve this, switch one of the routers into bridge mode.");
|
||||||
msgBuf, sizeof(msgBuf))) {
|
}
|
||||||
|
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. Contact your ISP and ask for a dedicated public IP address.");
|
||||||
|
}
|
||||||
|
else if (igdDisconnected) {
|
||||||
|
snprintf(msgBuf, sizeof(msgBuf), "Internet GameStream connectivity check failed. Make sure UPnP is enabled in your router settings and that you don't have two devices acting as routers connected together.");
|
||||||
|
}
|
||||||
|
else if (upnpRulesFound) {
|
||||||
|
snprintf(msgBuf, sizeof(msgBuf), "Found UPnP rules, but we couldn't confirm that they were working. You can confirm by streaming from a different network and typing the following address into Moonlight's Add PC dialog: %s\n\n"
|
||||||
|
"If that doesn't work, check your router settings for any existing Moonlight port forwarding entries and delete them.", wanAddrStr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
snprintf(msgBuf, sizeof(msgBuf), "Internet GameStream connectivity check failed. Make sure UPnP is enabled in your router settings.\n\nThe following ports were not forwarded properly:\n%s", portMsgBuf);
|
||||||
|
}
|
||||||
DisplayMessage(msgBuf);
|
DisplayMessage(msgBuf);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for double-NAT
|
snprintf(msgBuf, sizeof(msgBuf), "All tests passed! You should be able to stream by typing the following address into Moonlight's Add PC dialog: %s", wanAddrStr);
|
||||||
if (IsDoubleNAT(&sin)) {
|
DisplayMessage(msgBuf, MpInfo);
|
||||||
DisplayMessage("Your router appears be connected to another router. This configuration breaks port forwarding. To resolve this, switch one of the devices into bridge mode.");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
// Check for CGN
|
|
||||||
else if (IsPossibleCGN(&sin)) {
|
|
||||||
DisplayMessage("Your ISP is running a Carrier-Grade NAT. This prevents you from hosting services like GameStream. Contact your ISP to get a real public IP address.");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char addrStr[64];
|
|
||||||
inet_ntop(AF_INET, &sin.sin_addr, addrStr, sizeof(addrStr));
|
|
||||||
snprintf(msgBuf, sizeof(msgBuf), "All tests passed! You should be able to stream by typing the following address into Moonlight's Add PC dialog: %s", addrStr);
|
|
||||||
DisplayMessage(msgBuf, false);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user