diff --git a/mist/mist.cpp b/mist/mist.cpp index 76d3231..21e6274 100644 --- a/mist/mist.cpp +++ b/mist/mist.cpp @@ -65,33 +65,53 @@ 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); - printf("--------------- MISS LOG -------------------\n"); - char missPath[MAX_PATH + 1]; - ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-current.log", missPath, sizeof(missPath)); - FILE* f = fopen(missPath, "r"); - if (f != nullptr) { - char buffer[1024]; - while (!feof(f)) { - int bytesRead = fread(buffer, 1, ARRAYSIZE(buffer), f); - fwrite(buffer, 1, bytesRead, stdout); + if (terminal) { + printf("--------------- MISS LOG -------------------\n"); + + char missPath[MAX_PATH + 1]; + ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-current.log", missPath, sizeof(missPath)); + FILE* f = fopen(missPath, "r"); + if (f != nullptr) { + char buffer[1024]; + while (!feof(f)) { + int bytesRead = fread(buffer, 1, ARRAYSIZE(buffer), f); + fwrite(buffer, 1, bytesRead, stdout); + } + fclose(f); } - fclose(f); - } - else { - printf("Failed to find MISS log\n"); + else { + printf("Failed to find MISS log\n"); + } + + fflush(stdout); } - 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) { - if (!STUNFindWanAddress(wanAddr)) { - printf("FAILED\n"); - DisplayMessage("MIST was unable to determine your public IP address. Please check your Internet connection."); + printf("Detecting WAN IP address via STUN..."); + if (!STUNFindWanAddress(wanAddr)) { + 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; - } - else { - 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; + } } + + 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; } \ No newline at end of file