mirror of
https://github.com/moonlight-stream/Internet-Hosting-Tool.git
synced 2025-07-03 08:15:40 +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];
|
||||
|
||||
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);
|
||||
|
||||
if (terminal) {
|
||||
printf("--------------- MISS LOG -------------------\n");
|
||||
|
||||
char missPath[MAX_PATH + 1];
|
||||
@ -86,12 +94,24 @@ void DisplayMessage(const char* message, bool error = true)
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
if (error) {
|
||||
if (priority == MpError && terminal) {
|
||||
flags = MB_YESNO | MB_TOPMOST | MB_SETFOREGROUND | MB_ICONINFORMATION;
|
||||
switch (MessageBoxA(nullptr, "Would you like to view the troubleshooting log?",
|
||||
"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);
|
||||
if (error != ERROR_SUCCESS) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -253,7 +273,7 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
|
||||
return err == 1 ? PortTestOk : PortTestError;
|
||||
}
|
||||
else {
|
||||
const char testMsg[] = "mist-test";
|
||||
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());
|
||||
@ -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;
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
||||
printf("Testing %s %d...",
|
||||
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
|
||||
// we've gotten so far.
|
||||
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].port);
|
||||
message += msgLen;
|
||||
messageLength -= msgLen;
|
||||
portMsg += msgLen;
|
||||
portMsgLen -= msgLen;
|
||||
|
||||
// Keep going to check all ports and report the failing ones
|
||||
ret = false;
|
||||
@ -524,14 +541,14 @@ bool STUNFindWanAddress(PSOCKADDR_IN wanAddr)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckWANAccess(PSOCKADDR_IN wanAddr, bool* foundPortForwardingRules)
|
||||
bool CheckWANAccess(PSOCKADDR_IN wanAddr, PSOCKADDR_IN reportedWanAddr, bool* foundPortForwardingRules, bool* igdDisconnected)
|
||||
{
|
||||
natpmp_t natpmp;
|
||||
|
||||
*foundPortForwardingRules = false;
|
||||
*igdDisconnected = false;
|
||||
|
||||
printf("Finding WAN IP address...");
|
||||
bool gotWanAddress = false;
|
||||
bool gotReportedWanAddress = false;
|
||||
int natPmpErr = initnatpmp(&natpmp, 0, 0);
|
||||
if (natPmpErr != 0) {
|
||||
printf("initnatpmp() failed: %d\n", natPmpErr);
|
||||
@ -556,15 +573,23 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, bool* foundPortForwardingRules)
|
||||
if (ret != 0) {
|
||||
// Connected or disconnected IGD
|
||||
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);
|
||||
if (ret == UPNPCOMMAND_SUCCESS && strlen(wanAddrStr) > 0) {
|
||||
wanAddr->sin_addr.S_un.S_addr = inet_addr(wanAddrStr);
|
||||
printf("%s (UPnP)\n", wanAddrStr);
|
||||
reportedWanAddr->sin_addr.S_un.S_addr = wanAddr->sin_addr.S_un.S_addr = inet_addr(wanAddrStr);
|
||||
printf("%s\n", wanAddrStr);
|
||||
|
||||
if (wanAddr->sin_addr.S_un.S_addr != 0) {
|
||||
gotWanAddress = true;
|
||||
gotReportedWanAddress = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("FAILED %d\n", ret);
|
||||
}
|
||||
|
||||
char conflictMessage[512];
|
||||
*foundPortForwardingRules = true;
|
||||
@ -588,6 +613,9 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, bool* foundPortForwardingRules)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("No UPnP IGD detected\n");
|
||||
}
|
||||
|
||||
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
|
||||
if (natPmpErr >= 0) {
|
||||
printf("Detecting WAN IP address via NAT-PMP...");
|
||||
|
||||
natpmpresp_t response;
|
||||
natPmpErr = readnatpmpresponseorretry(&natpmp, &response);
|
||||
closenatpmp(&natpmp);
|
||||
|
||||
if (natPmpErr == 0 && !gotWanAddress) {
|
||||
if (natPmpErr == 0) {
|
||||
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));
|
||||
printf("%s (NAT-PMP)\n", addrStr);
|
||||
printf("%s\n", addrStr);
|
||||
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)) {
|
||||
printf("FAILED\n");
|
||||
DisplayMessage("MIST was unable to determine your public IP address. Please check your Internet connection.");
|
||||
if (!gotReportedWanAddress) {
|
||||
DisplayMessage("Unable to determine your public IP address. Please check your Internet connection.");
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
char addrStr[64];
|
||||
inet_ntop(AF_INET, &wanAddr->sin_addr, addrStr, sizeof(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)
|
||||
@ -692,6 +730,7 @@ int main(int argc, char* argv[])
|
||||
SOCKADDR_IN6 sin6;
|
||||
};
|
||||
char msgBuf[2048];
|
||||
char portMsgBuf[512];
|
||||
|
||||
fprintf(stderr, "Testing local GameStream connectivity...\n");
|
||||
|
||||
@ -700,9 +739,10 @@ int main(int argc, char* argv[])
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr = in4addr_loopback;
|
||||
printf("Testing GameStream ports via loopback\n");
|
||||
if (!TestAllPorts(&ss,
|
||||
"Local GameStream connectivity check failed. Please try reinstalling GeForce Experience.\n\nThe following ports were not working:\n",
|
||||
msgBuf, sizeof(msgBuf))) {
|
||||
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf))) {
|
||||
snprintf(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);
|
||||
return -1;
|
||||
}
|
||||
@ -716,47 +756,69 @@ int main(int argc, char* argv[])
|
||||
|
||||
// Try to connect via LAN IPv4 address
|
||||
printf("Testing GameStream ports via local network\n");
|
||||
if (!TestAllPorts(&ss,
|
||||
"Local network GameStream connectivity check failed. Try temporarily disabling your firewall software or adding firewall exceptions for the following ports:\n",
|
||||
msgBuf, sizeof(msgBuf))) {
|
||||
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf))) {
|
||||
snprintf(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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Detecting public IP address...\n");
|
||||
|
||||
bool upnpRulesFound;
|
||||
if (!CheckWANAccess(&sin, &upnpRulesFound)) {
|
||||
bool upnpRulesFound, igdDisconnected;
|
||||
SOCKADDR_IN locallyReportedWanAddr;
|
||||
if (!CheckWANAccess(&sin, &locallyReportedWanAddr, &upnpRulesFound, &igdDisconnected)) {
|
||||
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");
|
||||
|
||||
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 WAN address\n");
|
||||
if (!TestAllPorts(&ss,
|
||||
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" :
|
||||
"Internet GameStream connectivity check failed. Make sure UPnP is enabled in your router settings.\n\nThe following ports were not forwarded properly:\n",
|
||||
msgBuf, sizeof(msgBuf))) {
|
||||
printf("Testing GameStream ports via STUN-reported WAN address\n");
|
||||
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf))) {
|
||||
if (IsDoubleNAT(&locallyReportedWanAddr)) {
|
||||
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.");
|
||||
}
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check for double-NAT
|
||||
if (IsDoubleNAT(&sin)) {
|
||||
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);
|
||||
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);
|
||||
DisplayMessage(msgBuf, MpInfo);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user