Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d75b11a35 | ||
|
|
f511b830ea | ||
|
|
8a45ea2066 | ||
|
|
3844ea59de | ||
|
|
a19b95f259 | ||
|
|
ae81d4305d | ||
|
|
8066cf4be1 | ||
|
|
021474cdd2 | ||
|
|
308cd0dd00 | ||
|
|
83360d775b | ||
|
|
cdbaa0aff3 | ||
|
|
36ea86faaa | ||
|
|
8590a0813b | ||
|
|
51ae1c8770 | ||
|
|
f1ca6a71f0 | ||
|
|
ac92212464 | ||
|
|
dbf43ac7a1 | ||
|
|
893aa76c9c | ||
|
|
21d8c71a2c | ||
|
|
41ef072c9b | ||
|
|
53246bd4c5 | ||
|
|
ac850e79d8 | ||
|
|
944c8993e8 | ||
|
|
b6508d9024 |
+1
-1
Submodule GS-IPv6-Forwarder updated: d2b927c7b1...90642afdd1
@@ -70,6 +70,7 @@
|
|||||||
ErrorControl="ignore"
|
ErrorControl="ignore"
|
||||||
Interactive="no">
|
Interactive="no">
|
||||||
<ServiceConfig DelayedAutoStart="no" OnInstall="yes" OnReinstall="yes" ServiceSid="unrestricted"/>
|
<ServiceConfig DelayedAutoStart="no" OnInstall="yes" OnReinstall="yes" ServiceSid="unrestricted"/>
|
||||||
|
<util:ServiceConfig FirstFailureActionType="restart" SecondFailureActionType="restart" ThirdFailureActionType="restart" ResetPeriodInDays="1" RestartServiceDelayInSeconds="10"/>
|
||||||
</ServiceInstall>
|
</ServiceInstall>
|
||||||
<ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="MISS" Wait="yes" />
|
<ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="MISS" Wait="yes" />
|
||||||
</Component>
|
</Component>
|
||||||
@@ -80,6 +81,48 @@
|
|||||||
Name="Moonlight Internet Streaming Tester"/>
|
Name="Moonlight Internet Streaming Tester"/>
|
||||||
</File>
|
</File>
|
||||||
</Component>
|
</Component>
|
||||||
|
<Component Id="GameStreamFirewallRules" Guid="{16CA2511-B533-46F8-8882-F8AEEFDFD7DF}" KeyPath="yes">
|
||||||
|
<fire:FirewallException Id="HttpsFwException"
|
||||||
|
Scope="any"
|
||||||
|
Port="47984"
|
||||||
|
Protocol="tcp"
|
||||||
|
Name="Moonlight - HTTPS"/>
|
||||||
|
<fire:FirewallException Id="HttpFwException"
|
||||||
|
Scope="any"
|
||||||
|
Port="47989"
|
||||||
|
Protocol="tcp"
|
||||||
|
Name="Moonlight - HTTP"/>
|
||||||
|
<fire:FirewallException Id="RtspFwException"
|
||||||
|
Scope="any"
|
||||||
|
Port="48010"
|
||||||
|
Protocol="tcp"
|
||||||
|
Name="Moonlight - RTSP"/>
|
||||||
|
<fire:FirewallException Id="VideoFwException"
|
||||||
|
Scope="any"
|
||||||
|
Port="47998"
|
||||||
|
Protocol="udp"
|
||||||
|
Name="Moonlight - Video"/>
|
||||||
|
<fire:FirewallException Id="ControlFwException"
|
||||||
|
Scope="any"
|
||||||
|
Port="47999"
|
||||||
|
Protocol="udp"
|
||||||
|
Name="Moonlight - Control"/>
|
||||||
|
<fire:FirewallException Id="AudioFwException"
|
||||||
|
Scope="any"
|
||||||
|
Port="48000"
|
||||||
|
Protocol="udp"
|
||||||
|
Name="Moonlight - Audio"/>
|
||||||
|
<fire:FirewallException Id="RtspuFwException"
|
||||||
|
Scope="any"
|
||||||
|
Port="48010"
|
||||||
|
Protocol="udp"
|
||||||
|
Name="Moonlight - RTSPU"/>
|
||||||
|
<fire:FirewallException Id="MdnsFwException"
|
||||||
|
Scope="any"
|
||||||
|
Port="5353"
|
||||||
|
Protocol="udp"
|
||||||
|
Name="Moonlight - mDNS"/>
|
||||||
|
</Component>
|
||||||
<Component Id="Shortcuts" Guid="*">
|
<Component Id="Shortcuts" Guid="*">
|
||||||
<Shortcut Id="StartMenuShortcut"
|
<Shortcut Id="StartMenuShortcut"
|
||||||
Name="Moonlight Internet Streaming Tester"
|
Name="Moonlight Internet Streaming Tester"
|
||||||
|
|||||||
+93
-63
@@ -39,6 +39,7 @@ bool PCPMapPort(PSOCKADDR_STORAGE localAddr, int localAddrLen, PSOCKADDR_STORAGE
|
|||||||
#define POLLING_DELAY_SEC 120
|
#define POLLING_DELAY_SEC 120
|
||||||
#define PORT_MAPPING_DURATION_SEC 3600
|
#define PORT_MAPPING_DURATION_SEC 3600
|
||||||
#define UPNP_DISCOVERY_DELAY_MS 5000
|
#define UPNP_DISCOVERY_DELAY_MS 5000
|
||||||
|
#define GAA_INITIAL_SIZE 8192
|
||||||
|
|
||||||
static struct port_entry {
|
static struct port_entry {
|
||||||
int proto;
|
int proto;
|
||||||
@@ -116,7 +117,7 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
|||||||
// Some routers change the description, so we can't check that here
|
// Some routers change the description, so we can't check that here
|
||||||
if (!strcmp(intClient, myAddr)) {
|
if (!strcmp(intClient, myAddr)) {
|
||||||
if (atoi(leaseDuration) == 0) {
|
if (atoi(leaseDuration) == 0) {
|
||||||
printf("OK (Static)" NL);
|
printf("OK (Static, Internal port: %s)" NL, intPort);
|
||||||
|
|
||||||
// If we have an existing permanent mapping, we can just leave it alone.
|
// If we have an existing permanent mapping, we can just leave it alone.
|
||||||
if (enable) {
|
if (enable) {
|
||||||
@@ -124,7 +125,7 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("OK (%s seconds remaining)" NL, leaseDuration);
|
printf("OK (%s seconds remaining, Internal port: %s)" NL, leaseDuration, intPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're just validating, we found an entry, so we're done.
|
// If we're just validating, we found an entry, so we're done.
|
||||||
@@ -206,7 +207,7 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
|||||||
else if (indefinite) {
|
else if (indefinite) {
|
||||||
printf("STATIC ");
|
printf("STATIC ");
|
||||||
}
|
}
|
||||||
if (err == 718 && proto == IPPROTO_UDP) { // ConflictInMappingEntry
|
if (err == 718 && proto == IPPROTO_UDP && port >= 47000) { // ConflictInMappingEntry
|
||||||
// Some UPnP implementations incorrectly deduplicate on the internal port instead
|
// Some UPnP implementations incorrectly deduplicate on the internal port instead
|
||||||
// of the external port, in violation of the UPnP IGD specification. Since GFE creates
|
// of the external port, in violation of the UPnP IGD specification. Since GFE creates
|
||||||
// mappings on the same internal port as us, those routers break our mappings. To
|
// mappings on the same internal port as us, those routers break our mappings. To
|
||||||
@@ -233,10 +234,7 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
|||||||
|
|
||||||
bool GetIP4OnLinkPrefixLength(char* lanAddressString, int* prefixLength)
|
bool GetIP4OnLinkPrefixLength(char* lanAddressString, int* prefixLength)
|
||||||
{
|
{
|
||||||
union {
|
PIP_ADAPTER_ADDRESSES addresses;
|
||||||
IP_ADAPTER_ADDRESSES addresses;
|
|
||||||
char buffer[8192];
|
|
||||||
};
|
|
||||||
ULONG error;
|
ULONG error;
|
||||||
ULONG length;
|
ULONG length;
|
||||||
PIP_ADAPTER_ADDRESSES currentAdapter;
|
PIP_ADAPTER_ADDRESSES currentAdapter;
|
||||||
@@ -245,22 +243,34 @@ bool GetIP4OnLinkPrefixLength(char* lanAddressString, int* prefixLength)
|
|||||||
|
|
||||||
inet_pton(AF_INET, lanAddressString, &targetAddress);
|
inet_pton(AF_INET, lanAddressString, &targetAddress);
|
||||||
|
|
||||||
// Get a list of all interfaces with IPv4 addresses on the system
|
addresses = NULL;
|
||||||
length = sizeof(buffer);
|
length = GAA_INITIAL_SIZE;
|
||||||
error = GetAdaptersAddresses(AF_INET,
|
do {
|
||||||
GAA_FLAG_SKIP_ANYCAST |
|
free(addresses);
|
||||||
GAA_FLAG_SKIP_MULTICAST |
|
addresses = (PIP_ADAPTER_ADDRESSES)malloc(length);
|
||||||
GAA_FLAG_SKIP_DNS_SERVER |
|
if (addresses == NULL) {
|
||||||
GAA_FLAG_SKIP_FRIENDLY_NAME,
|
printf("malloc(%u) failed" NL, length);
|
||||||
NULL,
|
return false;
|
||||||
&addresses,
|
}
|
||||||
&length);
|
|
||||||
|
// Get a list of all interfaces with IPv4 addresses on the system
|
||||||
|
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);
|
||||||
|
} while (error == ERROR_BUFFER_OVERFLOW);
|
||||||
|
|
||||||
if (error != ERROR_SUCCESS) {
|
if (error != ERROR_SUCCESS) {
|
||||||
printf("GetAdaptersAddresses() failed: %d" NL, error);
|
printf("GetAdaptersAddresses() failed: %d" NL, error);
|
||||||
|
free(addresses);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentAdapter = &addresses;
|
currentAdapter = addresses;
|
||||||
currentAddress = nullptr;
|
currentAddress = nullptr;
|
||||||
while (currentAdapter != nullptr) {
|
while (currentAdapter != nullptr) {
|
||||||
currentAddress = currentAdapter->FirstUnicastAddress;
|
currentAddress = currentAdapter->FirstUnicastAddress;
|
||||||
@@ -271,6 +281,7 @@ bool GetIP4OnLinkPrefixLength(char* lanAddressString, int* prefixLength)
|
|||||||
|
|
||||||
if (RtlEqualMemory(¤tAddrV4->sin_addr, &targetAddress, sizeof(targetAddress))) {
|
if (RtlEqualMemory(¤tAddrV4->sin_addr, &targetAddress, sizeof(targetAddress))) {
|
||||||
*prefixLength = currentAddress->OnLinkPrefixLength;
|
*prefixLength = currentAddress->OnLinkPrefixLength;
|
||||||
|
free(addresses);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,6 +292,7 @@ bool GetIP4OnLinkPrefixLength(char* lanAddressString, int* prefixLength)
|
|||||||
}
|
}
|
||||||
|
|
||||||
printf("No adapter found with IPv4 address: %s" NL, lanAddressString);
|
printf("No adapter found with IPv4 address: %s" NL, lanAddressString);
|
||||||
|
free(addresses);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,7 +427,7 @@ bool NATPMPMapPort(natpmp_t* natpmp, int proto, int port, bool enable, bool inde
|
|||||||
lifetime = 604800; // 1 week
|
lifetime = 604800; // 1 week
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
lifetime = 3600;
|
lifetime = PORT_MAPPING_DURATION_SEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Updating NAT-PMP port mapping for %s %d...", proto == IPPROTO_TCP ? "TCP" : "UDP", port);
|
printf("Updating NAT-PMP port mapping for %s %d...", proto == IPPROTO_TCP ? "TCP" : "UDP", port);
|
||||||
@@ -521,14 +533,8 @@ bool IsGameStreamEnabled()
|
|||||||
printf("RegQueryValueExA() failed: %d" NL, error);
|
printf("RegQueryValueExA() failed: %d" NL, error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (!enabled) {
|
|
||||||
printf("GameStream is OFF!" NL);
|
return enabled != 0;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
printf("GameStream is ON!" NL);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdatePortMappingsForTarget(bool enable, char* targetAddressIP4, char* internalAddressIP4, char* upstreamAddressIP4)
|
void UpdatePortMappingsForTarget(bool enable, char* targetAddressIP4, char* internalAddressIP4, char* upstreamAddressIP4)
|
||||||
@@ -805,25 +811,28 @@ void NETIOAPI_API_ IpInterfaceChangeNotificationCallback(PVOID context, PMIB_IPI
|
|||||||
SetEvent((HANDLE)context);
|
SetEvent((HANDLE)context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResetLogFile()
|
void ResetLogFile(bool standaloneExe)
|
||||||
{
|
{
|
||||||
char oldLogFilePath[MAX_PATH + 1];
|
|
||||||
char currentLogFilePath[MAX_PATH + 1];
|
|
||||||
char timeString[MAX_PATH + 1] = {};
|
char timeString[MAX_PATH + 1] = {};
|
||||||
SYSTEMTIME time;
|
SYSTEMTIME time;
|
||||||
|
|
||||||
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-old.log", oldLogFilePath, sizeof(oldLogFilePath));
|
if (!standaloneExe) {
|
||||||
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-current.log", currentLogFilePath, sizeof(currentLogFilePath));
|
char oldLogFilePath[MAX_PATH + 1];
|
||||||
|
char currentLogFilePath[MAX_PATH + 1];
|
||||||
|
|
||||||
// Close the existing stdout handle. This is important because otherwise
|
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-old.log", oldLogFilePath, sizeof(oldLogFilePath));
|
||||||
// it may still be open as stdout when we try to MoveFileEx below.
|
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-current.log", currentLogFilePath, sizeof(currentLogFilePath));
|
||||||
fclose(stdout);
|
|
||||||
|
|
||||||
// Rotate the current to the old log file
|
// Close the existing stdout handle. This is important because otherwise
|
||||||
MoveFileExA(currentLogFilePath, oldLogFilePath, MOVEFILE_REPLACE_EXISTING);
|
// it may still be open as stdout when we try to MoveFileEx below.
|
||||||
|
fclose(stdout);
|
||||||
|
|
||||||
// Redirect stdout to this new file
|
// Rotate the current to the old log file
|
||||||
freopen(currentLogFilePath, "w", stdout);
|
MoveFileExA(currentLogFilePath, oldLogFilePath, MOVEFILE_REPLACE_EXISTING);
|
||||||
|
|
||||||
|
// Redirect stdout to this new file
|
||||||
|
freopen(currentLogFilePath, "w", stdout);
|
||||||
|
}
|
||||||
|
|
||||||
// Print a log header
|
// Print a log header
|
||||||
printf("Moonlight Internet Streaming Service v" VER_VERSION_STR NL);
|
printf("Moonlight Internet Streaming Service v" VER_VERSION_STR NL);
|
||||||
@@ -837,36 +846,47 @@ void ResetLogFile()
|
|||||||
DWORD WINAPI GameStreamStateChangeThread(PVOID Context)
|
DWORD WINAPI GameStreamStateChangeThread(PVOID Context)
|
||||||
{
|
{
|
||||||
HKEY key;
|
HKEY key;
|
||||||
|
DWORD err;
|
||||||
|
|
||||||
// We're watching this key that way we can still detect GameStream turning on
|
do {
|
||||||
// if GFE wasn't even installed when our service started
|
// We're watching this key that way we can still detect GameStream turning on
|
||||||
DWORD err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\NVIDIA Corporation", 0, KEY_READ | KEY_WOW64_64KEY, &key);
|
// if GFE wasn't even installed when our service started
|
||||||
if (err != ERROR_SUCCESS) {
|
do {
|
||||||
printf("RegOpenKeyExA() failed: %d" NL, err);
|
err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\NVIDIA Corporation", 0, KEY_READ | KEY_WOW64_64KEY, &key);
|
||||||
return err;
|
if (err != ERROR_SUCCESS) {
|
||||||
}
|
// Wait 10 seconds and try again
|
||||||
|
Sleep(10000);
|
||||||
|
}
|
||||||
|
} while (err != ERROR_SUCCESS);
|
||||||
|
|
||||||
// Notify the main thread when the GameStream state changes
|
// Notify the main thread when the GameStream state changes
|
||||||
bool lastGameStreamState = IsGameStreamEnabled();
|
bool lastGameStreamState = IsGameStreamEnabled();
|
||||||
while ((err = RegNotifyChangeKeyValue(key, true, REG_NOTIFY_CHANGE_LAST_SET, nullptr, false)) == ERROR_SUCCESS) {
|
while ((err = RegNotifyChangeKeyValue(key, true, REG_NOTIFY_CHANGE_LAST_SET, nullptr, false)) == ERROR_SUCCESS) {
|
||||||
bool currentGameStreamState = IsGameStreamEnabled();
|
bool currentGameStreamState = IsGameStreamEnabled();
|
||||||
if (lastGameStreamState != currentGameStreamState) {
|
if (lastGameStreamState != currentGameStreamState) {
|
||||||
SetEvent((HANDLE)Context);
|
SetEvent((HANDLE)Context);
|
||||||
|
}
|
||||||
|
lastGameStreamState = currentGameStreamState;
|
||||||
}
|
}
|
||||||
lastGameStreamState = currentGameStreamState;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("RegNotifyChangeKeyValue() failed: %d" NL, err);
|
// If the key is deleted (by DDU or similar), we will hit this code path and poll until it comes back.
|
||||||
|
RegCloseKey(key);
|
||||||
|
} while (err == ERROR_KEY_DELETED);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Run()
|
int Run(bool standaloneExe)
|
||||||
{
|
{
|
||||||
HANDLE ifaceChangeEvent = CreateEvent(nullptr, true, false, nullptr);
|
HANDLE ifaceChangeEvent = CreateEvent(nullptr, true, false, nullptr);
|
||||||
HANDLE gsChangeEvent = CreateEvent(nullptr, true, false, nullptr);
|
HANDLE gsChangeEvent = CreateEvent(nullptr, true, false, nullptr);
|
||||||
HANDLE events[2] = { ifaceChangeEvent, gsChangeEvent };
|
HANDLE events[2] = { ifaceChangeEvent, gsChangeEvent };
|
||||||
|
|
||||||
ResetLogFile();
|
ResetLogFile(standaloneExe);
|
||||||
|
|
||||||
|
// Bump the process priority class to above normal. The UDP relay threads will
|
||||||
|
// further raise their own thread priorities to avoid preemption by other activity.
|
||||||
|
SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
|
||||||
|
|
||||||
// Create the UDP alternate port relays
|
// Create the UDP alternate port relays
|
||||||
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
||||||
@@ -885,7 +905,17 @@ int Run()
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
ResetEvent(gsChangeEvent);
|
ResetEvent(gsChangeEvent);
|
||||||
ResetEvent(ifaceChangeEvent);
|
ResetEvent(ifaceChangeEvent);
|
||||||
UpdatePortMappings(IsGameStreamEnabled());
|
|
||||||
|
bool gameStreamEnabled = IsGameStreamEnabled();
|
||||||
|
|
||||||
|
if (gameStreamEnabled) {
|
||||||
|
printf("GameStream is ON!" NL);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("GameStream is OFF!" NL);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdatePortMappings(gameStreamEnabled);
|
||||||
|
|
||||||
// Refresh when half the duration is expired or if an IP interface
|
// Refresh when half the duration is expired or if an IP interface
|
||||||
// change event occurs.
|
// change event occurs.
|
||||||
@@ -895,7 +925,7 @@ int Run()
|
|||||||
ULONGLONG beforeSleepTime = GetTickCount64();
|
ULONGLONG beforeSleepTime = GetTickCount64();
|
||||||
DWORD ret = WaitForMultipleObjects(ARRAYSIZE(events), events, false, POLLING_DELAY_SEC * 1000);
|
DWORD ret = WaitForMultipleObjects(ARRAYSIZE(events), events, false, POLLING_DELAY_SEC * 1000);
|
||||||
if (ret == WAIT_OBJECT_0) {
|
if (ret == WAIT_OBJECT_0) {
|
||||||
ResetLogFile();
|
ResetLogFile(standaloneExe);
|
||||||
|
|
||||||
printf("Woke up for interface change notification after %lld seconds" NL,
|
printf("Woke up for interface change notification after %lld seconds" NL,
|
||||||
(GetTickCount64() - beforeSleepTime) / 1000);
|
(GetTickCount64() - beforeSleepTime) / 1000);
|
||||||
@@ -904,13 +934,13 @@ int Run()
|
|||||||
Sleep(10000);
|
Sleep(10000);
|
||||||
}
|
}
|
||||||
else if (ret == WAIT_OBJECT_0 + 1) {
|
else if (ret == WAIT_OBJECT_0 + 1) {
|
||||||
ResetLogFile();
|
ResetLogFile(standaloneExe);
|
||||||
|
|
||||||
printf("Woke up for GameStream state change notification after %lld seconds" NL,
|
printf("Woke up for GameStream state change notification after %lld seconds" NL,
|
||||||
(GetTickCount64() - beforeSleepTime) / 1000);
|
(GetTickCount64() - beforeSleepTime) / 1000);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ResetLogFile();
|
ResetLogFile(standaloneExe);
|
||||||
|
|
||||||
printf("Woke up for periodic refresh" NL);
|
printf("Woke up for periodic refresh" NL);
|
||||||
}
|
}
|
||||||
@@ -971,7 +1001,7 @@ ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv)
|
|||||||
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
|
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
|
||||||
|
|
||||||
// Start the service
|
// Start the service
|
||||||
err = Run();
|
err = Run(false);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
||||||
ServiceStatus.dwWin32ExitCode = err;
|
ServiceStatus.dwWin32ExitCode = err;
|
||||||
@@ -994,7 +1024,7 @@ int main(int argc, char* argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (argc == 2 && !strcmp(argv[1], "exe")) {
|
if (argc == 2 && !strcmp(argv[1], "exe")) {
|
||||||
Run();
|
Run(true);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -118,7 +118,7 @@ bool PCPMapPort(PSOCKADDR_STORAGE localAddr, int localAddrLen, PSOCKADDR_STORAGE
|
|||||||
int bytesRead;
|
int bytesRead;
|
||||||
union {
|
union {
|
||||||
PCP_MAP_RESPONSE hdr;
|
PCP_MAP_RESPONSE hdr;
|
||||||
char buf[1024];
|
char buf[1100];
|
||||||
} resp;
|
} resp;
|
||||||
int lifetime;
|
int lifetime;
|
||||||
|
|
||||||
|
|||||||
+20
-22
@@ -19,34 +19,28 @@ WINAPI
|
|||||||
UdpRelayThreadProc(LPVOID Context)
|
UdpRelayThreadProc(LPVOID Context)
|
||||||
{
|
{
|
||||||
PUDP_TUPLE tuple = (PUDP_TUPLE)Context;
|
PUDP_TUPLE tuple = (PUDP_TUPLE)Context;
|
||||||
fd_set fds;
|
USHORT nboPort = htons(tuple->port);
|
||||||
int err;
|
|
||||||
SOCKADDR_IN lastRemoteAddr;
|
SOCKADDR_IN lastRemoteAddr;
|
||||||
|
|
||||||
|
// Ensure the relay threads aren't preempted by games or other CPU intensive activity
|
||||||
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
|
||||||
|
|
||||||
RtlZeroMemory(&lastRemoteAddr, sizeof(lastRemoteAddr));
|
RtlZeroMemory(&lastRemoteAddr, sizeof(lastRemoteAddr));
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
char buffer[4096];
|
char buffer[4096];
|
||||||
SOCKADDR_IN sourceAddr;
|
SOCKADDR_IN sourceAddr;
|
||||||
int sourceAddrLen;
|
int sourceAddrLen;
|
||||||
|
int recvLen;
|
||||||
FD_ZERO(&fds);
|
|
||||||
|
|
||||||
FD_SET(tuple->socket, &fds);
|
|
||||||
|
|
||||||
err = select(0, &fds, NULL, NULL, NULL);
|
|
||||||
if (err <= 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceAddrLen = sizeof(sourceAddr);
|
sourceAddrLen = sizeof(sourceAddr);
|
||||||
err = recvfrom(tuple->socket, buffer, sizeof(buffer), 0, (PSOCKADDR)&sourceAddr, &sourceAddrLen);
|
recvLen = recvfrom(tuple->socket, buffer, sizeof(buffer), 0, (PSOCKADDR)&sourceAddr, &sourceAddrLen);
|
||||||
if (err == SOCKET_ERROR) {
|
if (recvLen == SOCKET_ERROR) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SOCKADDR_IN destinationAddr;
|
SOCKADDR_IN destinationAddr;
|
||||||
if (RtlEqualMemory(&sourceAddr.sin_addr, &in4addr_loopback, sizeof(sourceAddr.sin_addr))) {
|
if (RtlEqualMemory(&sourceAddr.sin_addr, &in4addr_loopback, sizeof(sourceAddr.sin_addr)) && sourceAddr.sin_port == nboPort) {
|
||||||
// Traffic incoming from loopback interface - send it to the last remote address
|
// Traffic incoming from loopback interface - send it to the last remote address
|
||||||
destinationAddr = lastRemoteAddr;
|
destinationAddr = lastRemoteAddr;
|
||||||
}
|
}
|
||||||
@@ -57,10 +51,10 @@ UdpRelayThreadProc(LPVOID Context)
|
|||||||
// Send it to the normal port via the loopback adapter
|
// Send it to the normal port via the loopback adapter
|
||||||
destinationAddr = sourceAddr;
|
destinationAddr = sourceAddr;
|
||||||
destinationAddr.sin_addr = in4addr_loopback;
|
destinationAddr.sin_addr = in4addr_loopback;
|
||||||
destinationAddr.sin_port = htons(tuple->port);
|
destinationAddr.sin_port = nboPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendto(tuple->socket, buffer, err, 0, (PSOCKADDR)&destinationAddr, sizeof(destinationAddr));
|
sendto(tuple->socket, buffer, recvLen, 0, (PSOCKADDR)&destinationAddr, sizeof(destinationAddr));
|
||||||
}
|
}
|
||||||
|
|
||||||
closesocket(tuple->socket);
|
closesocket(tuple->socket);
|
||||||
@@ -74,11 +68,13 @@ int StartUdpRelay(unsigned short Port)
|
|||||||
SOCKADDR_IN addr;
|
SOCKADDR_IN addr;
|
||||||
HANDLE thread;
|
HANDLE thread;
|
||||||
PUDP_TUPLE tuple;
|
PUDP_TUPLE tuple;
|
||||||
|
int error;
|
||||||
|
|
||||||
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
if (sock == INVALID_SOCKET) {
|
if (sock == INVALID_SOCKET) {
|
||||||
printf("socket() failed: %d\n", WSAGetLastError());
|
error = WSAGetLastError();
|
||||||
return WSAGetLastError();
|
printf("socket() failed: %d\n", error);
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind to the alternate port
|
// Bind to the alternate port
|
||||||
@@ -86,9 +82,10 @@ int StartUdpRelay(unsigned short Port)
|
|||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
addr.sin_port = htons(Port + RELAY_PORT_OFFSET);
|
addr.sin_port = htons(Port + RELAY_PORT_OFFSET);
|
||||||
if (bind(sock, (PSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR) {
|
if (bind(sock, (PSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR) {
|
||||||
printf("bind() failed: %d\n", WSAGetLastError());
|
error = WSAGetLastError();
|
||||||
|
printf("bind() failed: %d\n", error);
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
return WSAGetLastError();
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
tuple = (PUDP_TUPLE)malloc(sizeof(*tuple));
|
tuple = (PUDP_TUPLE)malloc(sizeof(*tuple));
|
||||||
@@ -101,9 +98,10 @@ int StartUdpRelay(unsigned short Port)
|
|||||||
|
|
||||||
thread = CreateThread(NULL, 0, UdpRelayThreadProc, tuple, 0, NULL);
|
thread = CreateThread(NULL, 0, UdpRelayThreadProc, tuple, 0, NULL);
|
||||||
if (thread == NULL) {
|
if (thread == NULL) {
|
||||||
printf("CreateThread() failed: %d\n", GetLastError());
|
error = GetLastError();
|
||||||
|
printf("CreateThread() failed: %d\n", error);
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
return GetLastError();
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
CloseHandle(thread);
|
CloseHandle(thread);
|
||||||
|
|||||||
+71
-43
@@ -29,6 +29,8 @@
|
|||||||
|
|
||||||
#define LOOPBACK_SERVER_PORT_OFFSET -10000
|
#define LOOPBACK_SERVER_PORT_OFFSET -10000
|
||||||
|
|
||||||
|
#define GAA_INITIAL_SIZE 8192
|
||||||
|
|
||||||
static struct port_entry {
|
static struct port_entry {
|
||||||
int proto;
|
int proto;
|
||||||
int port;
|
int port;
|
||||||
@@ -405,7 +407,7 @@ enum PortTestStatus {
|
|||||||
PortTestError,
|
PortTestError,
|
||||||
PortTestUnknown
|
PortTestUnknown
|
||||||
};
|
};
|
||||||
PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withServer, bool isLoopbackRelay)
|
PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withServer, bool isLoopbackRelay, bool mtuTest)
|
||||||
{
|
{
|
||||||
SOCKET clientSock = INVALID_SOCKET, serverSock = INVALID_SOCKET;
|
SOCKET clientSock = INVALID_SOCKET, serverSock = INVALID_SOCKET;
|
||||||
int err;
|
int err;
|
||||||
@@ -530,11 +532,14 @@ 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[] = "moonlight-test";
|
// Video packets are up to 1040 bytes.
|
||||||
|
const char testMsg[1040] = "moonlight-test";
|
||||||
|
|
||||||
// Send several test packets to ensure a random lost packet doesn't make the test fail
|
// Send several test packets to ensure a random lost packet doesn't make the test fail
|
||||||
for (int i = 0; i < 5; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
err = sendto(clientSock, testMsg, sizeof(testMsg), 0, (struct sockaddr*)&sin6, addrLen);
|
// Send the full payload for MTU tests and the truncated payload for other tests.
|
||||||
|
err = sendto(clientSock, testMsg,
|
||||||
|
mtuTest ? sizeof(testMsg) : strlen(testMsg), 0, (struct sockaddr*)&sin6, addrLen);
|
||||||
if (err == SOCKET_ERROR) {
|
if (err == SOCKET_ERROR) {
|
||||||
fprintf(LOG_OUT, "sendto() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "sendto() failed: %d\n", WSAGetLastError());
|
||||||
closesocket(clientSock);
|
closesocket(clientSock);
|
||||||
@@ -696,7 +701,8 @@ bool TestAllPorts(PSOCKADDR_STORAGE addr, char* portMsg, int portMsgLen, bool is
|
|||||||
fprintf(LOG_OUT, "Testing %s %d...",
|
fprintf(LOG_OUT, "Testing %s %d...",
|
||||||
k_Ports[i].proto == IPPROTO_TCP ? "TCP" : "UDP",
|
k_Ports[i].proto == IPPROTO_TCP ? "TCP" : "UDP",
|
||||||
k_Ports[i].port);
|
k_Ports[i].port);
|
||||||
status = TestPort(addr, k_Ports[i].proto, k_Ports[i].port, k_Ports[i].withServer, isLoopbackRelay);
|
status = TestPort(addr, k_Ports[i].proto, k_Ports[i].port,
|
||||||
|
k_Ports[i].withServer, isLoopbackRelay, k_Ports[i].port == 47998);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status != PortTestOk) {
|
if (status != PortTestOk) {
|
||||||
@@ -864,31 +870,40 @@ bool FindLocalInterfaceIPAddress(int family, PSOCKADDR_STORAGE addr)
|
|||||||
|
|
||||||
bool FindZeroTierInterfaceAddress(PSOCKADDR_STORAGE addr)
|
bool FindZeroTierInterfaceAddress(PSOCKADDR_STORAGE addr)
|
||||||
{
|
{
|
||||||
union {
|
PIP_ADAPTER_ADDRESSES addresses;
|
||||||
IP_ADAPTER_ADDRESSES addresses;
|
|
||||||
char buffer[8192];
|
|
||||||
};
|
|
||||||
ULONG error;
|
ULONG error;
|
||||||
ULONG length;
|
ULONG length;
|
||||||
PIP_ADAPTER_ADDRESSES currentAdapter;
|
PIP_ADAPTER_ADDRESSES currentAdapter;
|
||||||
PIP_ADAPTER_UNICAST_ADDRESS currentAddress;
|
PIP_ADAPTER_UNICAST_ADDRESS currentAddress;
|
||||||
|
|
||||||
// Get all IPv4 interfaces
|
addresses = NULL;
|
||||||
length = sizeof(buffer);
|
length = GAA_INITIAL_SIZE;
|
||||||
error = GetAdaptersAddresses(AF_INET,
|
do {
|
||||||
GAA_FLAG_SKIP_ANYCAST |
|
free(addresses);
|
||||||
GAA_FLAG_SKIP_MULTICAST |
|
addresses = (PIP_ADAPTER_ADDRESSES)malloc(length);
|
||||||
GAA_FLAG_SKIP_DNS_SERVER |
|
if (addresses == NULL) {
|
||||||
GAA_FLAG_SKIP_FRIENDLY_NAME,
|
fprintf(LOG_OUT, "malloc(%u) failed\n", length);
|
||||||
NULL,
|
return false;
|
||||||
&addresses,
|
}
|
||||||
&length);
|
|
||||||
|
// Get all IPv4 interfaces
|
||||||
|
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);
|
||||||
|
} while (error == ERROR_BUFFER_OVERFLOW);
|
||||||
|
|
||||||
if (error != ERROR_SUCCESS) {
|
if (error != ERROR_SUCCESS) {
|
||||||
fprintf(LOG_OUT, "GetAdaptersAddresses() failed: %d\n", error);
|
fprintf(LOG_OUT, "GetAdaptersAddresses() failed: %d\n", error);
|
||||||
|
free(addresses);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentAdapter = &addresses;
|
currentAdapter = addresses;
|
||||||
while (currentAdapter != NULL) {
|
while (currentAdapter != NULL) {
|
||||||
// Look for ones that correspond to a ZeroTier device
|
// Look for ones that correspond to a ZeroTier device
|
||||||
if (wcsstr(currentAdapter->Description, L"ZeroTier")) {
|
if (wcsstr(currentAdapter->Description, L"ZeroTier")) {
|
||||||
@@ -897,6 +912,7 @@ bool FindZeroTierInterfaceAddress(PSOCKADDR_STORAGE addr)
|
|||||||
while (currentAddress != NULL) {
|
while (currentAddress != NULL) {
|
||||||
if (currentAddress->Address.lpSockaddr->sa_family == AF_INET) {
|
if (currentAddress->Address.lpSockaddr->sa_family == AF_INET) {
|
||||||
RtlCopyMemory(addr, currentAddress->Address.lpSockaddr, currentAddress->Address.iSockaddrLength);
|
RtlCopyMemory(addr, currentAddress->Address.lpSockaddr, currentAddress->Address.iSockaddrLength);
|
||||||
|
free(addresses);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -907,15 +923,13 @@ bool FindZeroTierInterfaceAddress(PSOCKADDR_STORAGE addr)
|
|||||||
currentAdapter = currentAdapter->Next;
|
currentAdapter = currentAdapter->Next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(addresses);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FindDuplicateDefaultInterfaces(void)
|
bool FindDuplicateDefaultInterfaces(void)
|
||||||
{
|
{
|
||||||
union {
|
PIP_ADAPTER_ADDRESSES addresses;
|
||||||
IP_ADAPTER_ADDRESSES addresses;
|
|
||||||
char buffer[8192];
|
|
||||||
};
|
|
||||||
ULONG error;
|
ULONG error;
|
||||||
ULONG length;
|
ULONG length;
|
||||||
MIB_IPFORWARDROW defaultRoute;
|
MIB_IPFORWARDROW defaultRoute;
|
||||||
@@ -928,24 +942,36 @@ bool FindDuplicateDefaultInterfaces(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all IPv4 interfaces
|
addresses = NULL;
|
||||||
length = sizeof(buffer);
|
length = GAA_INITIAL_SIZE;
|
||||||
error = GetAdaptersAddresses(AF_INET,
|
do {
|
||||||
GAA_FLAG_SKIP_UNICAST |
|
free(addresses);
|
||||||
GAA_FLAG_SKIP_ANYCAST |
|
addresses = (PIP_ADAPTER_ADDRESSES)malloc(length);
|
||||||
GAA_FLAG_SKIP_MULTICAST |
|
if (addresses == NULL) {
|
||||||
GAA_FLAG_SKIP_DNS_SERVER |
|
fprintf(LOG_OUT, "malloc(%u) failed\n", length);
|
||||||
GAA_FLAG_INCLUDE_GATEWAYS |
|
return false;
|
||||||
GAA_FLAG_SKIP_FRIENDLY_NAME,
|
}
|
||||||
NULL,
|
|
||||||
&addresses,
|
// Get all IPv4 interfaces
|
||||||
&length);
|
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);
|
||||||
|
} while (error == ERROR_BUFFER_OVERFLOW);
|
||||||
|
|
||||||
if (error != ERROR_SUCCESS) {
|
if (error != ERROR_SUCCESS) {
|
||||||
fprintf(LOG_OUT, "GetAdaptersAddresses() failed: %d\n", error);
|
fprintf(LOG_OUT, "GetAdaptersAddresses() failed: %d\n", error);
|
||||||
|
free(addresses);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentAdapter = &addresses;
|
currentAdapter = addresses;
|
||||||
while (currentAdapter != NULL) {
|
while (currentAdapter != NULL) {
|
||||||
if (currentAdapter->OperStatus == IfOperStatusUp &&
|
if (currentAdapter->OperStatus == IfOperStatusUp &&
|
||||||
currentAdapter->FirstGatewayAddress != NULL &&
|
currentAdapter->FirstGatewayAddress != NULL &&
|
||||||
@@ -958,6 +984,7 @@ bool FindDuplicateDefaultInterfaces(void)
|
|||||||
currentAdapter = currentAdapter->Next;
|
currentAdapter = currentAdapter->Next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(addresses);
|
||||||
return matchingInterfaces > 1;
|
return matchingInterfaces > 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1002,7 +1029,7 @@ UPnPPortStatus UPnPCheckPort(struct UPNPUrls* urls, struct IGDdatas* data, int p
|
|||||||
}
|
}
|
||||||
else if (err == UPNPCOMMAND_SUCCESS) {
|
else if (err == UPNPCOMMAND_SUCCESS) {
|
||||||
if (!strcmp(myAddr, intClient)) {
|
if (!strcmp(myAddr, intClient)) {
|
||||||
fprintf(LOG_OUT, "OK\n");
|
fprintf(LOG_OUT, "OK (Internal port: %s)\n", intPort);
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -1441,8 +1468,10 @@ int main(int argc, char* argv[])
|
|||||||
if (TestAllPorts((PSOCKADDR_STORAGE)current->ai_addr, portMsgBuf, sizeof(portMsgBuf), true, true, &allPortsFailedOnV4)) {
|
if (TestAllPorts((PSOCKADDR_STORAGE)current->ai_addr, portMsgBuf, sizeof(portMsgBuf), true, true, &allPortsFailedOnV4)) {
|
||||||
freeaddrinfo(result);
|
freeaddrinfo(result);
|
||||||
snprintf(msgBuf, sizeof(msgBuf), "This PC is ready to host over the Internet!\n\n"
|
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"
|
"Do not uninstall the Moonlight Internet Hosting Tool, unless you no longer want to stream over the Internet. It needs to remain installed on your PC to maintain the port forwarding entries on your router.\n\n"
|
||||||
"If you can't, you can type the following address into Moonlight's Add PC dialog: %s", wanAddrStr);
|
"For the easiest setup, you can simply pair Moonlight to your gaming PC while they are both on the same network and Internet streaming will work automatically.\n\n"
|
||||||
|
"If your client is not connected to the same network as your gaming PC, you can type the following address into Moonlight's Add PC dialog: %s",
|
||||||
|
wanAddrStr);
|
||||||
DisplayMessage(msgBuf, nullptr, MpInfo);
|
DisplayMessage(msgBuf, nullptr, MpInfo);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1490,10 +1519,9 @@ int main(int argc, char* argv[])
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// Tested against a working server and it failed
|
// Tested against a IPv6 working server
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+56
-40
@@ -45,15 +45,20 @@ bool getExternalAddressPortIP4(unsigned short localPort, PSOCKADDR_IN wanAddr)
|
|||||||
int i;
|
int i;
|
||||||
int bytesRead;
|
int bytesRead;
|
||||||
int err;
|
int err;
|
||||||
|
bool ret;
|
||||||
PSTUN_ATTRIBUTE_HEADER attribute;
|
PSTUN_ATTRIBUTE_HEADER attribute;
|
||||||
PSTUN_MAPPED_IPV4_ADDRESS_ATTRIBUTE ipv4Attrib;
|
PSTUN_MAPPED_IPV4_ADDRESS_ATTRIBUTE ipv4Attrib;
|
||||||
struct addrinfo* result;
|
struct addrinfo* result;
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
|
struct sockaddr_in bindAddr;
|
||||||
union {
|
union {
|
||||||
STUN_MESSAGE hdr;
|
STUN_MESSAGE hdr;
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
} resp;
|
} resp;
|
||||||
|
|
||||||
|
sock = INVALID_SOCKET;
|
||||||
|
ret = false;
|
||||||
|
|
||||||
memset(&hints, 0, sizeof(hints));
|
memset(&hints, 0, sizeof(hints));
|
||||||
hints.ai_family = AF_INET;
|
hints.ai_family = AF_INET;
|
||||||
hints.ai_flags = AI_ADDRCONFIG;
|
hints.ai_flags = AI_ADDRCONFIG;
|
||||||
@@ -68,18 +73,15 @@ bool getExternalAddressPortIP4(unsigned short localPort, PSOCKADDR_IN wanAddr)
|
|||||||
sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
|
sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
|
||||||
if (sock == INVALID_SOCKET) {
|
if (sock == INVALID_SOCKET) {
|
||||||
fprintf(LOG_OUT, "socket() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "socket() failed: %d\n", WSAGetLastError());
|
||||||
freeaddrinfo(result);
|
goto Exit;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sockaddr_in bindAddr = {};
|
bindAddr = {};
|
||||||
bindAddr.sin_family = hints.ai_family;
|
bindAddr.sin_family = hints.ai_family;
|
||||||
bindAddr.sin_port = htons(localPort);
|
bindAddr.sin_port = htons(localPort);
|
||||||
if (bind(sock, (struct sockaddr*)&bindAddr, sizeof(bindAddr)) == SOCKET_ERROR) {
|
if (bind(sock, (struct sockaddr*)&bindAddr, sizeof(bindAddr)) == SOCKET_ERROR) {
|
||||||
fprintf(LOG_OUT, "bind() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "bind() failed: %d\n", WSAGetLastError());
|
||||||
closesocket(sock);
|
goto Exit;
|
||||||
freeaddrinfo(result);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reqMsg.messageType = htons(STUN_MESSAGE_BINDING_REQUEST);
|
reqMsg.messageType = htons(STUN_MESSAGE_BINDING_REQUEST);
|
||||||
@@ -98,57 +100,60 @@ bool getExternalAddressPortIP4(unsigned short localPort, PSOCKADDR_IN wanAddr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fd_set fds;
|
for (;;) {
|
||||||
FD_ZERO(&fds);
|
fd_set fds;
|
||||||
FD_SET(sock, &fds);
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(sock, &fds);
|
||||||
|
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
tv.tv_sec = 1;
|
tv.tv_sec = 1;
|
||||||
tv.tv_usec = 0;
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
int selectRes = select(0, &fds, nullptr, nullptr, &tv);
|
int selectRes = select(0, &fds, nullptr, nullptr, &tv);
|
||||||
if (selectRes == 0) {
|
if (selectRes == 0) {
|
||||||
// Timeout - continue looping
|
// Timeout - break to the enclosing loop
|
||||||
continue;
|
break;
|
||||||
|
}
|
||||||
|
else if (selectRes == SOCKET_ERROR) {
|
||||||
|
fprintf(LOG_OUT, "select() failed: %d\n", WSAGetLastError());
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// recvfrom() will return WSAECONNRESET if the socket has received an ICMP Port Unreachable
|
||||||
|
// message from one of the IP addresses we've attempted communication with. The socket is still
|
||||||
|
// operable (and may even contain queued messages to receive). We should ignore that error and
|
||||||
|
// continue reading, otherwise exit the loop.
|
||||||
|
bytesRead = recvfrom(sock, resp.buf, sizeof(resp.buf), 0, NULL, NULL);
|
||||||
|
if (bytesRead != SOCKET_ERROR || WSAGetLastError() != WSAECONNRESET) {
|
||||||
|
goto RecvDone;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (selectRes == SOCKET_ERROR) {
|
|
||||||
fprintf(LOG_OUT, "select() failed: %d\n", WSAGetLastError());
|
|
||||||
closesocket(sock);
|
|
||||||
freeaddrinfo(result);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error handling is below
|
|
||||||
bytesRead = recvfrom(sock, resp.buf, sizeof(resp.buf), 0, NULL, NULL);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
freeaddrinfo(result);
|
RecvDone:
|
||||||
closesocket(sock);
|
|
||||||
|
|
||||||
if (bytesRead == 0) {
|
if (bytesRead == 0) {
|
||||||
fprintf(LOG_OUT, "No response from STUN server\n");
|
fprintf(LOG_OUT, "No response from STUN server\n");
|
||||||
return false;
|
goto Exit;
|
||||||
}
|
}
|
||||||
else if (bytesRead == SOCKET_ERROR) {
|
else if (bytesRead == SOCKET_ERROR) {
|
||||||
fprintf(LOG_OUT, "Failed to read STUN binding response: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "Failed to read STUN binding response: %d\n", WSAGetLastError());
|
||||||
return false;
|
goto Exit;
|
||||||
}
|
}
|
||||||
else if (bytesRead < sizeof(resp.hdr)) {
|
else if (bytesRead < sizeof(resp.hdr)) {
|
||||||
fprintf(LOG_OUT, "STUN message truncated: %d\n", bytesRead);
|
fprintf(LOG_OUT, "STUN message truncated: %d\n", bytesRead);
|
||||||
return false;
|
goto Exit;
|
||||||
}
|
}
|
||||||
else if (htonl(resp.hdr.magicCookie) != STUN_MESSAGE_COOKIE) {
|
else if (htonl(resp.hdr.magicCookie) != STUN_MESSAGE_COOKIE) {
|
||||||
fprintf(LOG_OUT, "Bad STUN cookie value: %x\n", htonl(resp.hdr.magicCookie));
|
fprintf(LOG_OUT, "Bad STUN cookie value: %x\n", htonl(resp.hdr.magicCookie));
|
||||||
return false;
|
goto Exit;
|
||||||
}
|
}
|
||||||
else if (memcmp(reqMsg.transactionId, resp.hdr.transactionId, sizeof(reqMsg.transactionId))) {
|
else if (memcmp(reqMsg.transactionId, resp.hdr.transactionId, sizeof(reqMsg.transactionId))) {
|
||||||
fprintf(LOG_OUT, "STUN transaction ID mismatch\n");
|
fprintf(LOG_OUT, "STUN transaction ID mismatch\n");
|
||||||
return false;
|
goto Exit;
|
||||||
}
|
}
|
||||||
else if (htons(resp.hdr.messageType) != STUN_MESSAGE_BINDING_SUCCESS) {
|
else if (htons(resp.hdr.messageType) != STUN_MESSAGE_BINDING_SUCCESS) {
|
||||||
fprintf(LOG_OUT, "STUN message type mismatch: %x\n", htons(resp.hdr.messageType));
|
fprintf(LOG_OUT, "STUN message type mismatch: %x\n", htons(resp.hdr.messageType));
|
||||||
return false;
|
goto Exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
attribute = (PSTUN_ATTRIBUTE_HEADER)(&resp.hdr + 1);
|
attribute = (PSTUN_ATTRIBUTE_HEADER)(&resp.hdr + 1);
|
||||||
@@ -156,7 +161,7 @@ bool getExternalAddressPortIP4(unsigned short localPort, PSOCKADDR_IN wanAddr)
|
|||||||
while (bytesRead > sizeof(*attribute)) {
|
while (bytesRead > sizeof(*attribute)) {
|
||||||
if (bytesRead < sizeof(*attribute) + htons(attribute->length)) {
|
if (bytesRead < sizeof(*attribute) + htons(attribute->length)) {
|
||||||
fprintf(LOG_OUT, "STUN attribute out of bounds: %d\n", htons(attribute->length));
|
fprintf(LOG_OUT, "STUN attribute out of bounds: %d\n", htons(attribute->length));
|
||||||
return false;
|
goto Exit;
|
||||||
}
|
}
|
||||||
// Mask off the comprehension bit
|
// Mask off the comprehension bit
|
||||||
else if ((htons(attribute->type) & 0x7FFF) != STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS) {
|
else if ((htons(attribute->type) & 0x7FFF) != STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS) {
|
||||||
@@ -169,11 +174,11 @@ bool getExternalAddressPortIP4(unsigned short localPort, PSOCKADDR_IN wanAddr)
|
|||||||
ipv4Attrib = (PSTUN_MAPPED_IPV4_ADDRESS_ATTRIBUTE)attribute;
|
ipv4Attrib = (PSTUN_MAPPED_IPV4_ADDRESS_ATTRIBUTE)attribute;
|
||||||
if (htons(ipv4Attrib->hdr.length) != 8) {
|
if (htons(ipv4Attrib->hdr.length) != 8) {
|
||||||
fprintf(LOG_OUT, "STUN address length mismatch: %d\n", htons(ipv4Attrib->hdr.length));
|
fprintf(LOG_OUT, "STUN address length mismatch: %d\n", htons(ipv4Attrib->hdr.length));
|
||||||
return false;
|
goto Exit;
|
||||||
}
|
}
|
||||||
else if (ipv4Attrib->addressFamily != 1) {
|
else if (ipv4Attrib->addressFamily != 1) {
|
||||||
fprintf(LOG_OUT, "STUN address family mismatch: %x\n", ipv4Attrib->addressFamily);
|
fprintf(LOG_OUT, "STUN address family mismatch: %x\n", ipv4Attrib->addressFamily);
|
||||||
return false;
|
goto Exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
*wanAddr = {};
|
*wanAddr = {};
|
||||||
@@ -183,9 +188,20 @@ bool getExternalAddressPortIP4(unsigned short localPort, PSOCKADDR_IN wanAddr)
|
|||||||
wanAddr->sin_port = ipv4Attrib->port ^ (short)resp.hdr.magicCookie;
|
wanAddr->sin_port = ipv4Attrib->port ^ (short)resp.hdr.magicCookie;
|
||||||
wanAddr->sin_addr.S_un.S_addr = ipv4Attrib->address ^ resp.hdr.magicCookie;
|
wanAddr->sin_addr.S_un.S_addr = ipv4Attrib->address ^ resp.hdr.magicCookie;
|
||||||
|
|
||||||
return true;
|
ret = true;
|
||||||
|
goto Exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(LOG_OUT, "No XOR mapped address found in STUN response!\n");
|
fprintf(LOG_OUT, "No XOR mapped address found in STUN response!\n");
|
||||||
return false;
|
|
||||||
|
Exit:
|
||||||
|
if (sock != INVALID_SOCKET) {
|
||||||
|
closesocket(sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != NULL) {
|
||||||
|
freeaddrinfo(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define VER_VERSION 5,2,0,0
|
#define VER_VERSION 5,4,0,0
|
||||||
#define VER_VERSION_STR "5.2.0.0"
|
#define VER_VERSION_STR "5.4.0.0"
|
||||||
|
|
||||||
#define VER_COMPANYNAME_STR "Moonlight Game Streaming Project"
|
#define VER_COMPANYNAME_STR "Moonlight Game Streaming Project"
|
||||||
#define VER_PRODUCTNAME_STR "Moonlight Internet Hosting Tool"
|
#define VER_PRODUCTNAME_STR "Moonlight Internet Hosting Tool"
|
||||||
|
|||||||
Reference in New Issue
Block a user