Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
363b62f69f | ||
|
|
3175b1c4fa | ||
|
|
404af38d10 | ||
|
|
dd72f65ffb | ||
|
|
6589dc4853 | ||
|
|
9772139229 | ||
|
|
03c89e4022 | ||
|
|
d1fe5f6a9b | ||
|
|
a88b8d0d04 | ||
|
|
fa5ae73abb | ||
|
|
4d197cae02 | ||
|
|
624af65b55 | ||
|
|
6b1a0856cc | ||
|
|
01a1012e74 | ||
|
|
66b36684ec | ||
|
|
e7db5242c9 | ||
|
|
5e09217af4 | ||
|
|
c21a64c874 | ||
|
|
ebe91d6799 | ||
|
|
077c9814e0 | ||
|
|
393dfb70a7 | ||
|
|
88fb77083f | ||
|
|
38514d08c1 | ||
|
|
5d75b11a35 | ||
|
|
f511b830ea | ||
|
|
8a45ea2066 | ||
|
|
3844ea59de | ||
|
|
a19b95f259 | ||
|
|
ae81d4305d | ||
|
|
8066cf4be1 | ||
|
|
021474cdd2 | ||
|
|
308cd0dd00 | ||
|
|
83360d775b | ||
|
|
cdbaa0aff3 | ||
|
|
36ea86faaa | ||
|
|
8590a0813b | ||
|
|
51ae1c8770 | ||
|
|
f1ca6a71f0 | ||
|
|
ac92212464 | ||
|
|
dbf43ac7a1 | ||
|
|
893aa76c9c | ||
|
|
21d8c71a2c | ||
|
|
41ef072c9b | ||
|
|
53246bd4c5 | ||
|
|
ac850e79d8 | ||
|
|
944c8993e8 | ||
|
|
b6508d9024 | ||
|
|
ef5bb72d5c | ||
|
|
3343ebb225 | ||
|
|
05413a554c | ||
|
|
5f015acdaa | ||
|
|
df286ef56d | ||
|
|
47d60b9f24 | ||
|
|
659d3aea23 |
+1
-1
Submodule GS-IPv6-Forwarder updated: bff3c3cf00...ca9a1345cd
@@ -1,4 +1,4 @@
|
||||
/* $Id: miniupnpc.h,v 1.53 2018/05/07 11:05:16 nanard Exp $ */
|
||||
/* $Id: miniupnpc.h,v 1.55 2020/11/09 19:49:32 nanard Exp $ */
|
||||
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
||||
* Project: miniupnp
|
||||
* http://miniupnp.free.fr/
|
||||
@@ -20,7 +20,7 @@
|
||||
#define UPNPDISCOVER_MEMORY_ERROR (-102)
|
||||
|
||||
/* versions : */
|
||||
#define MINIUPNPC_VERSION "2.1"
|
||||
#define MINIUPNPC_VERSION "2.2.0"
|
||||
#define MINIUPNPC_API_VERSION 17
|
||||
|
||||
/* Source port:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/* $Id: upnpdev.h,v 1.1 2015/08/28 12:14:19 nanard Exp $ */
|
||||
/* $Id: upnpdev.h,v 1.3 2020/05/29 15:57:42 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* Web : http://miniupnp.free.fr/
|
||||
* Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
|
||||
* Author : Thomas BERNARD
|
||||
* copyright (c) 2005-2018 Thomas Bernard
|
||||
* copyright (c) 2005-2020 Thomas Bernard
|
||||
* This software is subjet to the conditions detailed in the
|
||||
* provided LICENSE file. */
|
||||
#ifndef UPNPDEV_H_INCLUDED
|
||||
@@ -20,7 +20,15 @@ struct UPNPDev {
|
||||
char * st;
|
||||
char * usn;
|
||||
unsigned int scope_id;
|
||||
char buffer[3];
|
||||
#if defined(__STDC_VERSION) && __STDC_VERSION__ >= 199901L
|
||||
/* C99 flexible array member */
|
||||
char buffer[];
|
||||
#elif defined(__GNUC__)
|
||||
char buffer[0];
|
||||
#else
|
||||
/* Fallback to a hack */
|
||||
char buffer[1];
|
||||
#endif
|
||||
};
|
||||
|
||||
/* freeUPNPDevlist()
|
||||
|
||||
@@ -51,6 +51,13 @@ typedef unsigned short uint16_t;
|
||||
#endif /* _WIN32 */
|
||||
#include "natpmp_declspec.h"
|
||||
|
||||
/* Set to 9 by https://tools.ietf.org/html/rfc6886#section-3.1 which leads to a
|
||||
* maximum timeout of 127.75 seconds, due to the initial 250 ms timeout doubling
|
||||
* each time, so we allow a compile-time modification here.*/
|
||||
#ifndef NATPMP_MAX_RETRIES
|
||||
#define NATPMP_MAX_RETRIES (9)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int s; /* socket */
|
||||
in_addr_t gateway; /* default gateway (IPv4) */
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+33
-1
@@ -1,6 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?define VCREDIST_VER = "14.28.29910" ?>
|
||||
<?define VCREDIST_X86_SIZE = "14308616" ?>
|
||||
<?define VCREDIST_X86_SHA1 = "CC6D8F9286645F2A11BCAE9D0E000CF012B08112" ?>
|
||||
<?define VCREDIST_X86_URL = "https://download.visualstudio.microsoft.com/download/pr/d64b93c3-f270-4750-9e75-bc12b2e899fb/4521ED84B9B1679A706E719423D54EF5E413DC50DDE1CF362232D7359D7E89C4/VC_redist.x86.exe" ?>
|
||||
<?define VCREDIST_X86_UPGRADE_CODE = "65E5BD06-6392-3027-8C26-853107D3CF1A" ?>
|
||||
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
|
||||
xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
|
||||
xmlns:bal="http://schemas.microsoft.com/wix/BalExtension"
|
||||
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
|
||||
<Bundle Name="Moonlight Internet Hosting Tool" Manufacturer="Moonlight Game Streaming Project" Version="!(bind.packageVersion.mish)" UpgradeCode="a2ce5056-1114-44b9-b79b-952ef46d3d50">
|
||||
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLicense">
|
||||
<bal:WixStandardBootstrapperApplication
|
||||
@@ -12,7 +20,31 @@
|
||||
|
||||
<WixVariable Id="WixUILicenseRtf" Value="" />
|
||||
|
||||
<util:ProductSearch Id="VCREDIST_142_x86"
|
||||
UpgradeCode="$(var.VCREDIST_X86_UPGRADE_CODE)"
|
||||
Result="version"
|
||||
Variable="VCREDIST_142_x86" />
|
||||
|
||||
<Chain>
|
||||
<ExePackage Name="Microsoft Visual C++ 2015-2019 Redistributable - x86"
|
||||
Cache="no"
|
||||
Compressed="no"
|
||||
PerMachine="yes"
|
||||
Permanent="yes"
|
||||
Vital="yes"
|
||||
InstallCommand="/install /quiet /norestart"
|
||||
DownloadUrl="$(var.VCREDIST_X86_URL)"
|
||||
DetectCondition="VCREDIST_142_x86 >= v$(var.VCREDIST_VER)">
|
||||
|
||||
<RemotePayload Description="Microsoft Visual C++ 2015-2019 Redistributable - x86"
|
||||
ProductName="Microsoft Visual C++ 2015-2019 Redistributable - x86"
|
||||
Size="$(var.VCREDIST_X86_SIZE)"
|
||||
Version="$(var.VCREDIST_VER).0"
|
||||
Hash="$(var.VCREDIST_X86_SHA1)"/>
|
||||
|
||||
<!-- Newer version installed is fine -->
|
||||
<ExitCode Value="1638" Behavior="success" />
|
||||
</ExePackage>
|
||||
<MsiPackage Id="mish" SourceFile="$(var.mish.TargetPath)" />
|
||||
<MsiPackage SourceFile="$(var.GSv6FwdSetup.TargetPath)" />
|
||||
</Chain>
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
<HintPath>$(WixExtDir)\WixBalExtension.dll</HintPath>
|
||||
<Name>WixBalExtension</Name>
|
||||
</WixExtension>
|
||||
<WixExtension Include="WixUtilExtension">
|
||||
<HintPath>$(WixExtDir)\WixUtilExtension.dll</HintPath>
|
||||
<Name>WixUtilExtension</Name>
|
||||
</WixExtension>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GS-IPv6-Forwarder\GSv6FwdSetup\GSv6FwdSetup.wixproj">
|
||||
|
||||
@@ -17,52 +17,32 @@ Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "GSv6FwdSetup", "GS-IPv6-For
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{B71C4E7F-0D92-4E19-BBD2-D33F0EF879B1}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{B71C4E7F-0D92-4E19-BBD2-D33F0EF879B1}.Debug|x64.Build.0 = Debug|x64
|
||||
{B71C4E7F-0D92-4E19-BBD2-D33F0EF879B1}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{B71C4E7F-0D92-4E19-BBD2-D33F0EF879B1}.Debug|x86.Build.0 = Debug|Win32
|
||||
{B71C4E7F-0D92-4E19-BBD2-D33F0EF879B1}.Release|x64.ActiveCfg = Release|x64
|
||||
{B71C4E7F-0D92-4E19-BBD2-D33F0EF879B1}.Release|x64.Build.0 = Release|x64
|
||||
{B71C4E7F-0D92-4E19-BBD2-D33F0EF879B1}.Release|x86.ActiveCfg = Release|Win32
|
||||
{B71C4E7F-0D92-4E19-BBD2-D33F0EF879B1}.Release|x86.Build.0 = Release|Win32
|
||||
{8F78D8D2-A837-489C-BAC5-81494C9CEF7A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{8F78D8D2-A837-489C-BAC5-81494C9CEF7A}.Debug|x64.Build.0 = Debug|x64
|
||||
{8F78D8D2-A837-489C-BAC5-81494C9CEF7A}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{8F78D8D2-A837-489C-BAC5-81494C9CEF7A}.Debug|x86.Build.0 = Debug|Win32
|
||||
{8F78D8D2-A837-489C-BAC5-81494C9CEF7A}.Release|x64.ActiveCfg = Release|x64
|
||||
{8F78D8D2-A837-489C-BAC5-81494C9CEF7A}.Release|x64.Build.0 = Release|x64
|
||||
{8F78D8D2-A837-489C-BAC5-81494C9CEF7A}.Release|x86.ActiveCfg = Release|Win32
|
||||
{8F78D8D2-A837-489C-BAC5-81494C9CEF7A}.Release|x86.Build.0 = Release|Win32
|
||||
{F0DEE5F3-4B62-47A3-B00B-61C614C924FD}.Debug|x64.ActiveCfg = Debug|x86
|
||||
{F0DEE5F3-4B62-47A3-B00B-61C614C924FD}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{F0DEE5F3-4B62-47A3-B00B-61C614C924FD}.Debug|x86.Build.0 = Debug|x86
|
||||
{F0DEE5F3-4B62-47A3-B00B-61C614C924FD}.Release|x64.ActiveCfg = Release|x86
|
||||
{F0DEE5F3-4B62-47A3-B00B-61C614C924FD}.Release|x86.ActiveCfg = Release|x86
|
||||
{F0DEE5F3-4B62-47A3-B00B-61C614C924FD}.Release|x86.Build.0 = Release|x86
|
||||
{A2CE5056-1114-44B9-B79B-952EF46D3D50}.Debug|x64.ActiveCfg = Debug|x86
|
||||
{A2CE5056-1114-44B9-B79B-952EF46D3D50}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{A2CE5056-1114-44B9-B79B-952EF46D3D50}.Debug|x86.Build.0 = Debug|x86
|
||||
{A2CE5056-1114-44B9-B79B-952EF46D3D50}.Release|x64.ActiveCfg = Release|x86
|
||||
{A2CE5056-1114-44B9-B79B-952EF46D3D50}.Release|x86.ActiveCfg = Release|x86
|
||||
{A2CE5056-1114-44B9-B79B-952EF46D3D50}.Release|x86.Build.0 = Release|x86
|
||||
{87DEAE49-7638-4CDB-88EB-054B1F3CB0D2}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{87DEAE49-7638-4CDB-88EB-054B1F3CB0D2}.Debug|x64.Build.0 = Debug|x64
|
||||
{87DEAE49-7638-4CDB-88EB-054B1F3CB0D2}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{87DEAE49-7638-4CDB-88EB-054B1F3CB0D2}.Debug|x86.Build.0 = Debug|Win32
|
||||
{87DEAE49-7638-4CDB-88EB-054B1F3CB0D2}.Release|x64.ActiveCfg = Release|x64
|
||||
{87DEAE49-7638-4CDB-88EB-054B1F3CB0D2}.Release|x64.Build.0 = Release|x64
|
||||
{87DEAE49-7638-4CDB-88EB-054B1F3CB0D2}.Release|x86.ActiveCfg = Release|Win32
|
||||
{87DEAE49-7638-4CDB-88EB-054B1F3CB0D2}.Release|x86.Build.0 = Release|Win32
|
||||
{F8171B99-F5F9-4ABF-9FE5-6753539611AF}.Debug|x64.ActiveCfg = Debug|x86
|
||||
{F8171B99-F5F9-4ABF-9FE5-6753539611AF}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{F8171B99-F5F9-4ABF-9FE5-6753539611AF}.Debug|x86.Build.0 = Debug|x86
|
||||
{F8171B99-F5F9-4ABF-9FE5-6753539611AF}.Release|x64.ActiveCfg = Release|x86
|
||||
{F8171B99-F5F9-4ABF-9FE5-6753539611AF}.Release|x86.ActiveCfg = Release|x86
|
||||
{F8171B99-F5F9-4ABF-9FE5-6753539611AF}.Release|x86.Build.0 = Release|x86
|
||||
EndGlobalSection
|
||||
|
||||
@@ -52,6 +52,12 @@
|
||||
|
||||
<Fragment>
|
||||
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
|
||||
<Component Id="MiniupnpcDll">
|
||||
<File Source="$(var.miss.TargetDir)miniupnpc.dll" KeyPath="yes"/>
|
||||
</Component>
|
||||
<Component Id="LibnatpmpDll">
|
||||
<File Source="$(var.miss.TargetDir)libnatpmp.dll" KeyPath="yes"/>
|
||||
</Component>
|
||||
<Component Id="MISS">
|
||||
<File Source="$(var.miss.TargetPath)" KeyPath="yes">
|
||||
<fire:FirewallException Id="MISSFwException"
|
||||
@@ -70,6 +76,7 @@
|
||||
ErrorControl="ignore"
|
||||
Interactive="no">
|
||||
<ServiceConfig DelayedAutoStart="no" OnInstall="yes" OnReinstall="yes" ServiceSid="unrestricted"/>
|
||||
<util:ServiceConfig FirstFailureActionType="restart" SecondFailureActionType="restart" ThirdFailureActionType="restart" ResetPeriodInDays="1" RestartServiceDelayInSeconds="10"/>
|
||||
</ServiceInstall>
|
||||
<ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="MISS" Wait="yes" />
|
||||
</Component>
|
||||
@@ -80,6 +87,48 @@
|
||||
Name="Moonlight Internet Streaming Tester"/>
|
||||
</File>
|
||||
</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="*">
|
||||
<Shortcut Id="StartMenuShortcut"
|
||||
Name="Moonlight Internet Streaming Tester"
|
||||
|
||||
+236
-74
@@ -12,6 +12,7 @@
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "relay.h"
|
||||
#include "..\version.h"
|
||||
|
||||
#pragma comment(lib, "miniupnpc.lib")
|
||||
@@ -38,6 +39,7 @@ bool PCPMapPort(PSOCKADDR_STORAGE localAddr, int localAddrLen, PSOCKADDR_STORAGE
|
||||
#define POLLING_DELAY_SEC 120
|
||||
#define PORT_MAPPING_DURATION_SEC 3600
|
||||
#define UPNP_DISCOVERY_DELAY_MS 5000
|
||||
#define GAA_INITIAL_SIZE 8192
|
||||
|
||||
static struct port_entry {
|
||||
int proto;
|
||||
@@ -53,9 +55,12 @@ static struct port_entry {
|
||||
{IPPROTO_UDP, 48010}
|
||||
};
|
||||
|
||||
static const int k_WolPorts[] = { 9 };
|
||||
static const int k_WolPorts[] = { 9, 47009 };
|
||||
|
||||
bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const char* myAddr, int port, bool enable, bool indefinite)
|
||||
static HANDLE s_StopEvent;
|
||||
static CRITICAL_SECTION s_PortMappingUpdateLock;
|
||||
|
||||
bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const char* myAddr, int port, bool enable, bool indefinite, bool validationPass)
|
||||
{
|
||||
char intClient[16];
|
||||
char intPort[6];
|
||||
@@ -95,15 +100,40 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
||||
if (err == 714) {
|
||||
// NoSuchEntryInArray
|
||||
printf("NOT FOUND" NL);
|
||||
|
||||
if (validationPass) {
|
||||
// On validation, we found a missing entry. Convert this entry to indefinite
|
||||
// to see if it will stick.
|
||||
indefinite = true;
|
||||
}
|
||||
}
|
||||
else if (err == 606) {
|
||||
printf("UNAUTHORIZED" NL);
|
||||
|
||||
// If we're just validating, we're done. We can't know if the entry was
|
||||
// actually applied but we'll return true to avoid false errors if it was.
|
||||
if (validationPass) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (err == UPNPCOMMAND_SUCCESS) {
|
||||
// Some routers change the description, so we can't check that here
|
||||
if (!strcmp(intClient, myAddr)) {
|
||||
if (atoi(leaseDuration) == 0) {
|
||||
printf("OK (Permanent)" NL);
|
||||
printf("OK (Static, Internal port: %s)" NL, intPort);
|
||||
|
||||
// If we have an existing permanent mapping, we can just leave it alone.
|
||||
if (enable) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
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 (validationPass) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!enable) {
|
||||
@@ -123,6 +153,11 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
||||
else {
|
||||
printf("CONFLICT: %s %s" NL, intClient, desc);
|
||||
|
||||
// If we're just validating, we found an entry, so we're done.
|
||||
if (validationPass) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Some UPnP IGDs won't let unauthenticated clients delete other conflicting port mappings
|
||||
// for security reasons, but we will give it a try anyway. If GameStream is not enabled,
|
||||
// we will leave the conflicting entry alone to avoid disturbing another PC's port forwarding
|
||||
@@ -146,6 +181,10 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
||||
}
|
||||
else {
|
||||
printf("ERROR %d (%s)" NL, err, strupnperror(err));
|
||||
|
||||
// If we get a strange error from the router, we'll assume it's some old broken IGDv1
|
||||
// device and only use indefinite lease durations to hopefully avoid confusing it.
|
||||
indefinite = true;
|
||||
}
|
||||
|
||||
// Bail if GameStream is disabled
|
||||
@@ -160,11 +199,31 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
||||
err = UPNP_AddPortMapping(
|
||||
urls->controlURL, data->first.servicetype, portStr,
|
||||
portStr, myAddr, myDesc, protoStr, nullptr, leaseDuration);
|
||||
if (err == 725 && !indefinite) { // OnlyPermanentLeasesSupported
|
||||
if (err != UPNPCOMMAND_SUCCESS && !indefinite) {
|
||||
// This may be a broken IGD that doesn't like non-static mappings. Try a static
|
||||
// mapping before finally giving up.
|
||||
err = UPNP_AddPortMapping(
|
||||
urls->controlURL, data->first.servicetype, portStr,
|
||||
portStr, myAddr, myDesc, protoStr, nullptr, "0");
|
||||
printf("PERMANENT ");
|
||||
printf("STATIC RETRY ");
|
||||
}
|
||||
else if (indefinite) {
|
||||
printf("STATIC ");
|
||||
}
|
||||
if (err == 718 && proto == IPPROTO_UDP && port >= 47000) { // ConflictInMappingEntry
|
||||
// Some UPnP implementations incorrectly deduplicate on the internal port instead
|
||||
// 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
|
||||
// work around this issue, we run relays for each of the UDP ports on an alternate
|
||||
// internal port. We'll try the alternate port if we get a conflict for a UDP entry.
|
||||
// Given that these are already horribly non-spec compliant, we won't take any chances
|
||||
// and we'll use an indefinite mapping too.
|
||||
char altPortStr[6];
|
||||
snprintf(altPortStr, sizeof(altPortStr), "%d", port + RELAY_PORT_OFFSET);
|
||||
err = UPNP_AddPortMapping(
|
||||
urls->controlURL, data->first.servicetype, portStr,
|
||||
altPortStr, myAddr, myDesc, protoStr, nullptr, "0");
|
||||
printf("ALTERNATE ");
|
||||
}
|
||||
if (err == UPNPCOMMAND_SUCCESS) {
|
||||
printf("OK" NL);
|
||||
@@ -178,10 +237,7 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
||||
|
||||
bool GetIP4OnLinkPrefixLength(char* lanAddressString, int* prefixLength)
|
||||
{
|
||||
union {
|
||||
IP_ADAPTER_ADDRESSES addresses;
|
||||
char buffer[8192];
|
||||
};
|
||||
PIP_ADAPTER_ADDRESSES addresses;
|
||||
ULONG error;
|
||||
ULONG length;
|
||||
PIP_ADAPTER_ADDRESSES currentAdapter;
|
||||
@@ -190,22 +246,34 @@ bool GetIP4OnLinkPrefixLength(char* lanAddressString, int* prefixLength)
|
||||
|
||||
inet_pton(AF_INET, lanAddressString, &targetAddress);
|
||||
|
||||
// Get a list of all interfaces with IPv4 addresses on the system
|
||||
length = sizeof(buffer);
|
||||
error = GetAdaptersAddresses(AF_INET,
|
||||
GAA_FLAG_SKIP_ANYCAST |
|
||||
GAA_FLAG_SKIP_MULTICAST |
|
||||
GAA_FLAG_SKIP_DNS_SERVER |
|
||||
GAA_FLAG_SKIP_FRIENDLY_NAME,
|
||||
NULL,
|
||||
&addresses,
|
||||
&length);
|
||||
addresses = NULL;
|
||||
length = GAA_INITIAL_SIZE;
|
||||
do {
|
||||
free(addresses);
|
||||
addresses = (PIP_ADAPTER_ADDRESSES)malloc(length);
|
||||
if (addresses == NULL) {
|
||||
printf("malloc(%u) failed" NL, length);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
printf("GetAdaptersAddresses() failed: %d" NL, error);
|
||||
free(addresses);
|
||||
return false;
|
||||
}
|
||||
|
||||
currentAdapter = &addresses;
|
||||
currentAdapter = addresses;
|
||||
currentAddress = nullptr;
|
||||
while (currentAdapter != nullptr) {
|
||||
currentAddress = currentAdapter->FirstUnicastAddress;
|
||||
@@ -216,6 +284,7 @@ bool GetIP4OnLinkPrefixLength(char* lanAddressString, int* prefixLength)
|
||||
|
||||
if (RtlEqualMemory(¤tAddrV4->sin_addr, &targetAddress, sizeof(targetAddress))) {
|
||||
*prefixLength = currentAddress->OnLinkPrefixLength;
|
||||
free(addresses);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -226,6 +295,7 @@ bool GetIP4OnLinkPrefixLength(char* lanAddressString, int* prefixLength)
|
||||
}
|
||||
|
||||
printf("No adapter found with IPv4 address: %s" NL, lanAddressString);
|
||||
free(addresses);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -249,10 +319,10 @@ bool UPnPHandleDeviceList(struct UPNPDev* list, bool enable, char* lanAddrOverri
|
||||
return false;
|
||||
}
|
||||
else if (ret == 1) {
|
||||
printf("Found a connected UPnP IGD" NL);
|
||||
printf("Found a connected UPnP IGD (%s)" NL, urls.rootdescURL);
|
||||
}
|
||||
else if (ret == 2) {
|
||||
printf("Found a disconnected UPnP IGD (!)" NL);
|
||||
printf("Found a disconnected (!) UPnP IGD (%s)" NL, urls.rootdescURL);
|
||||
|
||||
// Even if we are able to add forwarding entries, go ahead and try NAT-PMP
|
||||
success = false;
|
||||
@@ -279,8 +349,9 @@ bool UPnPHandleDeviceList(struct UPNPDev* list, bool enable, char* lanAddrOverri
|
||||
portMappingInternalAddress = localAddress;
|
||||
}
|
||||
|
||||
// Create the port mappings
|
||||
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
||||
if (!UPnPMapPort(&urls, &data, k_Ports[i].proto, portMappingInternalAddress, k_Ports[i].port, enable, false)) {
|
||||
if (!UPnPMapPort(&urls, &data, k_Ports[i].proto, portMappingInternalAddress, k_Ports[i].port, enable, false, false)) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
@@ -304,13 +375,32 @@ bool UPnPHandleDeviceList(struct UPNPDev* list, bool enable, char* lanAddrOverri
|
||||
char broadcastAddrStr[128];
|
||||
inet_ntop(AF_INET, &broadcastAddr, broadcastAddrStr, sizeof(broadcastAddrStr));
|
||||
|
||||
UPnPMapPort(&urls, &data, IPPROTO_UDP, broadcastAddrStr, k_WolPorts[i], enable, true);
|
||||
UPnPMapPort(&urls, &data, IPPROTO_UDP, broadcastAddrStr, k_WolPorts[i], enable, true, false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// When we're mapping the WOL ports upstream of our router, we map directly to
|
||||
// the port on the upstream address (likely our router's WAN interface).
|
||||
UPnPMapPort(&urls, &data, IPPROTO_UDP, lanAddrOverride, k_WolPorts[i], enable, true);
|
||||
UPnPMapPort(&urls, &data, IPPROTO_UDP, lanAddrOverride, k_WolPorts[i], enable, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the rules are present and correct if they claimed to be added successfully
|
||||
if (success && enable) {
|
||||
// Wait 10 seconds for the router state to quiesce or the stop event to be set
|
||||
printf("Waiting before UPnP port validation...");
|
||||
if (WaitForSingleObject(s_StopEvent, 10000) == WAIT_TIMEOUT) {
|
||||
printf("done" NL);
|
||||
|
||||
// Perform the validation pass (converting any now missing entries to permanent ones)
|
||||
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
||||
if (!UPnPMapPort(&urls, &data, k_Ports[i].proto, portMappingInternalAddress, k_Ports[i].port, enable, false, true)) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("aborted" NL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +434,7 @@ bool NATPMPMapPort(natpmp_t* natpmp, int proto, int port, bool enable, bool inde
|
||||
lifetime = 604800; // 1 week
|
||||
}
|
||||
else {
|
||||
lifetime = 3600;
|
||||
lifetime = PORT_MAPPING_DURATION_SEC;
|
||||
}
|
||||
|
||||
printf("Updating NAT-PMP port mapping for %s %d...", proto == IPPROTO_TCP ? "TCP" : "UDP", port);
|
||||
@@ -450,14 +540,8 @@ bool IsGameStreamEnabled()
|
||||
printf("RegQueryValueExA() failed: %d" NL, error);
|
||||
return false;
|
||||
}
|
||||
else if (!enabled) {
|
||||
printf("GameStream is OFF!" NL);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
printf("GameStream is ON!" NL);
|
||||
return true;
|
||||
}
|
||||
|
||||
return enabled != 0;
|
||||
}
|
||||
|
||||
void UpdatePortMappingsForTarget(bool enable, char* targetAddressIP4, char* internalAddressIP4, char* upstreamAddressIP4)
|
||||
@@ -502,6 +586,12 @@ void UpdatePortMappingsForTarget(bool enable, char* targetAddressIP4, char* inte
|
||||
ipv4Devs = getUPnPDevicesByAddress(addr);
|
||||
}
|
||||
|
||||
// Abort if this is an add/update request and we're stopping
|
||||
if (enable && WaitForSingleObject(s_StopEvent, 0) == WAIT_OBJECT_0) {
|
||||
printf("Aborting port mapping update due to stop request" NL);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
// Use the delay of discovery to also allow the NAT-PMP endpoint time to respond
|
||||
if (natPmpErr >= 0) {
|
||||
natpmpresp_t response;
|
||||
@@ -734,25 +824,28 @@ void NETIOAPI_API_ IpInterfaceChangeNotificationCallback(PVOID context, PMIB_IPI
|
||||
SetEvent((HANDLE)context);
|
||||
}
|
||||
|
||||
void ResetLogFile()
|
||||
void ResetLogFile(bool standaloneExe)
|
||||
{
|
||||
char oldLogFilePath[MAX_PATH + 1];
|
||||
char currentLogFilePath[MAX_PATH + 1];
|
||||
char timeString[MAX_PATH + 1] = {};
|
||||
SYSTEMTIME time;
|
||||
|
||||
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-old.log", oldLogFilePath, sizeof(oldLogFilePath));
|
||||
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-current.log", currentLogFilePath, sizeof(currentLogFilePath));
|
||||
if (!standaloneExe) {
|
||||
char oldLogFilePath[MAX_PATH + 1];
|
||||
char currentLogFilePath[MAX_PATH + 1];
|
||||
|
||||
// Close the existing stdout handle. This is important because otherwise
|
||||
// it may still be open as stdout when we try to MoveFileEx below.
|
||||
fclose(stdout);
|
||||
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-old.log", oldLogFilePath, sizeof(oldLogFilePath));
|
||||
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-current.log", currentLogFilePath, sizeof(currentLogFilePath));
|
||||
|
||||
// Rotate the current to the old log file
|
||||
MoveFileExA(currentLogFilePath, oldLogFilePath, MOVEFILE_REPLACE_EXISTING);
|
||||
// Close the existing stdout handle. This is important because otherwise
|
||||
// it may still be open as stdout when we try to MoveFileEx below.
|
||||
fclose(stdout);
|
||||
|
||||
// Redirect stdout to this new file
|
||||
freopen(currentLogFilePath, "w", stdout);
|
||||
// Rotate the current to the old log file
|
||||
MoveFileExA(currentLogFilePath, oldLogFilePath, MOVEFILE_REPLACE_EXISTING);
|
||||
|
||||
// Redirect stdout to this new file
|
||||
freopen(currentLogFilePath, "w", stdout);
|
||||
}
|
||||
|
||||
// Print a log header
|
||||
printf("Moonlight Internet Streaming Service v" VER_VERSION_STR NL);
|
||||
@@ -766,36 +859,66 @@ void ResetLogFile()
|
||||
DWORD WINAPI GameStreamStateChangeThread(PVOID Context)
|
||||
{
|
||||
HKEY key;
|
||||
DWORD err;
|
||||
|
||||
// We're watching this key that way we can still detect GameStream turning on
|
||||
// if GFE wasn't even installed when our service started
|
||||
DWORD err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\NVIDIA Corporation", 0, KEY_READ | KEY_WOW64_64KEY, &key);
|
||||
if (err != ERROR_SUCCESS) {
|
||||
printf("RegOpenKeyExA() failed: %d" NL, err);
|
||||
return err;
|
||||
}
|
||||
do {
|
||||
// We're watching this key that way we can still detect GameStream turning on
|
||||
// if GFE wasn't even installed when our service started
|
||||
do {
|
||||
err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\NVIDIA Corporation", 0, KEY_READ | KEY_WOW64_64KEY, &key);
|
||||
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
|
||||
bool lastGameStreamState = IsGameStreamEnabled();
|
||||
while ((err = RegNotifyChangeKeyValue(key, true, REG_NOTIFY_CHANGE_LAST_SET, nullptr, false)) == ERROR_SUCCESS) {
|
||||
bool currentGameStreamState = IsGameStreamEnabled();
|
||||
if (lastGameStreamState != currentGameStreamState) {
|
||||
SetEvent((HANDLE)Context);
|
||||
// Notify the main thread when the GameStream state changes
|
||||
bool lastGameStreamState = IsGameStreamEnabled();
|
||||
while ((err = RegNotifyChangeKeyValue(key, true, REG_NOTIFY_CHANGE_LAST_SET, nullptr, false)) == ERROR_SUCCESS) {
|
||||
bool currentGameStreamState = IsGameStreamEnabled();
|
||||
if (lastGameStreamState != currentGameStreamState) {
|
||||
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;
|
||||
}
|
||||
|
||||
int Run()
|
||||
int Initialize()
|
||||
{
|
||||
// Create the stop event
|
||||
s_StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (s_StopEvent == NULL) {
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
InitializeCriticalSection(&s_PortMappingUpdateLock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Run(bool standaloneExe)
|
||||
{
|
||||
HANDLE ifaceChangeEvent = CreateEvent(nullptr, true, false, nullptr);
|
||||
HANDLE gsChangeEvent = CreateEvent(nullptr, true, false, nullptr);
|
||||
HANDLE events[2] = { ifaceChangeEvent, gsChangeEvent };
|
||||
HANDLE events[3] = { ifaceChangeEvent, gsChangeEvent, s_StopEvent };
|
||||
|
||||
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
|
||||
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
||||
if (k_Ports[i].proto == IPPROTO_UDP) {
|
||||
StartUdpRelay(k_Ports[i].port);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the thread to watch for GameStream state changes
|
||||
CreateThread(nullptr, 0, GameStreamStateChangeThread, gsChangeEvent, 0, nullptr);
|
||||
@@ -807,7 +930,27 @@ int Run()
|
||||
for (;;) {
|
||||
ResetEvent(gsChangeEvent);
|
||||
ResetEvent(ifaceChangeEvent);
|
||||
UpdatePortMappings(IsGameStreamEnabled());
|
||||
|
||||
bool gameStreamEnabled = IsGameStreamEnabled();
|
||||
|
||||
if (gameStreamEnabled) {
|
||||
printf("GameStream is ON!" NL);
|
||||
}
|
||||
else {
|
||||
printf("GameStream is OFF!" NL);
|
||||
}
|
||||
|
||||
// Acquire the mapping lock and update port mappings
|
||||
if (TryEnterCriticalSection(&s_PortMappingUpdateLock)) {
|
||||
// If the stop event is set, bail out now
|
||||
if (WaitForSingleObject(s_StopEvent, 0) == WAIT_OBJECT_0) {
|
||||
LeaveCriticalSection(&s_PortMappingUpdateLock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
UpdatePortMappings(gameStreamEnabled);
|
||||
LeaveCriticalSection(&s_PortMappingUpdateLock);
|
||||
}
|
||||
|
||||
// Refresh when half the duration is expired or if an IP interface
|
||||
// change event occurs.
|
||||
@@ -817,7 +960,7 @@ int Run()
|
||||
ULONGLONG beforeSleepTime = GetTickCount64();
|
||||
DWORD ret = WaitForMultipleObjects(ARRAYSIZE(events), events, false, POLLING_DELAY_SEC * 1000);
|
||||
if (ret == WAIT_OBJECT_0) {
|
||||
ResetLogFile();
|
||||
ResetLogFile(standaloneExe);
|
||||
|
||||
printf("Woke up for interface change notification after %lld seconds" NL,
|
||||
(GetTickCount64() - beforeSleepTime) / 1000);
|
||||
@@ -826,13 +969,17 @@ int Run()
|
||||
Sleep(10000);
|
||||
}
|
||||
else if (ret == WAIT_OBJECT_0 + 1) {
|
||||
ResetLogFile();
|
||||
ResetLogFile(standaloneExe);
|
||||
|
||||
printf("Woke up for GameStream state change notification after %lld seconds" NL,
|
||||
(GetTickCount64() - beforeSleepTime) / 1000);
|
||||
}
|
||||
else if (ret == WAIT_OBJECT_0 + 2) {
|
||||
printf("Woke up for stop notification" NL);
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
ResetLogFile();
|
||||
ResetLogFile(standaloneExe);
|
||||
|
||||
printf("Woke up for periodic refresh" NL);
|
||||
}
|
||||
@@ -852,14 +999,21 @@ HandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpConte
|
||||
return NO_ERROR;
|
||||
|
||||
case SERVICE_CONTROL_STOP:
|
||||
// Stop future port mapping updates
|
||||
SetEvent(s_StopEvent);
|
||||
|
||||
ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
||||
ServiceStatus.dwControlsAccepted = 0;
|
||||
ServiceStatus.dwWaitHint = 120 * 1000; // 2 minutes
|
||||
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
|
||||
|
||||
// Remove existing port mappings
|
||||
EnterCriticalSection(&s_PortMappingUpdateLock);
|
||||
printf("Removing UPnP/NAT-PMP/PCP rules after service stop request\n");
|
||||
UpdatePortMappings(false);
|
||||
LeaveCriticalSection(&s_PortMappingUpdateLock);
|
||||
|
||||
printf("The service is stopping\n");
|
||||
printf("The service is stopping now\n");
|
||||
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
||||
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
|
||||
return NO_ERROR;
|
||||
@@ -881,6 +1035,14 @@ ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv)
|
||||
return;
|
||||
}
|
||||
|
||||
err = Initialize();
|
||||
if (err != 0) {
|
||||
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
||||
ServiceStatus.dwWin32ExitCode = err;
|
||||
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
|
||||
return;
|
||||
}
|
||||
|
||||
ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
||||
ServiceStatus.dwServiceSpecificExitCode = 0;
|
||||
ServiceStatus.dwWin32ExitCode = NO_ERROR;
|
||||
@@ -893,7 +1055,7 @@ ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv)
|
||||
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
|
||||
|
||||
// Start the service
|
||||
err = Run();
|
||||
err = Run(false);
|
||||
if (err != 0) {
|
||||
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
||||
ServiceStatus.dwWin32ExitCode = err;
|
||||
@@ -916,8 +1078,8 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
if (argc == 2 && !strcmp(argv[1], "exe")) {
|
||||
Run();
|
||||
return 0;
|
||||
Initialize();
|
||||
return Run(true);
|
||||
}
|
||||
|
||||
return StartServiceCtrlDispatcher(ServiceTable);
|
||||
|
||||
+26
-2
@@ -93,13 +93,19 @@
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>..\libs\include;..\libs\include\miniupnpc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>..\libs\x86\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<CETCompat>true</CETCompat>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy /Y "$(SolutionDir)libs\x86\Debug\*" "$(TargetDir)"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
@@ -111,11 +117,17 @@
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<CETCompat>true</CETCompat>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy /Y "$(SolutionDir)libs\x86\Debug\*" "$(TargetDir)"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
@@ -130,7 +142,8 @@
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>..\libs\include;..\libs\include\miniupnpc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@@ -138,7 +151,11 @@
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>..\libs\x86\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<CETCompat>true</CETCompat>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy /Y "$(SolutionDir)libs\x86\Release\*" "$(TargetDir)"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
@@ -152,17 +169,23 @@
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<CETCompat>true</CETCompat>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy /Y "$(SolutionDir)libs\x86\Release\*" "$(TargetDir)"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="miss.cpp" />
|
||||
<ClCompile Include="pcp.cpp" />
|
||||
<ClCompile Include="relay.cpp" />
|
||||
<ClCompile Include="tracer.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -170,6 +193,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\version.h" />
|
||||
<ClInclude Include="relay.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
<ClCompile Include="pcp.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="relay.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="miss.rc">
|
||||
@@ -34,5 +37,8 @@
|
||||
<ClInclude Include="..\version.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="relay.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
+1
-1
@@ -118,7 +118,7 @@ bool PCPMapPort(PSOCKADDR_STORAGE localAddr, int localAddrLen, PSOCKADDR_STORAGE
|
||||
int bytesRead;
|
||||
union {
|
||||
PCP_MAP_RESPONSE hdr;
|
||||
char buf[1024];
|
||||
char buf[1100];
|
||||
} resp;
|
||||
int lifetime;
|
||||
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <WinSock2.h>
|
||||
#include <Ws2ipdef.h>
|
||||
|
||||
#include "relay.h"
|
||||
|
||||
typedef struct _UDP_TUPLE {
|
||||
SOCKET socket;
|
||||
unsigned short port;
|
||||
} UDP_TUPLE, *PUDP_TUPLE;
|
||||
|
||||
DWORD
|
||||
WINAPI
|
||||
UdpRelayThreadProc(LPVOID Context)
|
||||
{
|
||||
PUDP_TUPLE tuple = (PUDP_TUPLE)Context;
|
||||
USHORT nboPort = htons(tuple->port);
|
||||
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));
|
||||
|
||||
for (;;) {
|
||||
char buffer[4096];
|
||||
SOCKADDR_IN sourceAddr;
|
||||
int sourceAddrLen;
|
||||
int recvLen;
|
||||
|
||||
sourceAddrLen = sizeof(sourceAddr);
|
||||
recvLen = recvfrom(tuple->socket, buffer, sizeof(buffer), 0, (PSOCKADDR)&sourceAddr, &sourceAddrLen);
|
||||
if (recvLen == SOCKET_ERROR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SOCKADDR_IN destinationAddr;
|
||||
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
|
||||
destinationAddr = lastRemoteAddr;
|
||||
}
|
||||
else {
|
||||
// Traffic incoming from the remote host - remember the source
|
||||
lastRemoteAddr = sourceAddr;
|
||||
|
||||
// Send it to the normal port via the loopback adapter
|
||||
destinationAddr = sourceAddr;
|
||||
destinationAddr.sin_addr = in4addr_loopback;
|
||||
destinationAddr.sin_port = nboPort;
|
||||
}
|
||||
|
||||
sendto(tuple->socket, buffer, recvLen, 0, (PSOCKADDR)&destinationAddr, sizeof(destinationAddr));
|
||||
}
|
||||
|
||||
closesocket(tuple->socket);
|
||||
free(tuple);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int StartUdpRelay(unsigned short Port)
|
||||
{
|
||||
SOCKET sock;
|
||||
SOCKADDR_IN addr;
|
||||
HANDLE thread;
|
||||
PUDP_TUPLE tuple;
|
||||
int error;
|
||||
|
||||
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (sock == INVALID_SOCKET) {
|
||||
error = WSAGetLastError();
|
||||
printf("socket() failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
// Bind to the alternate port
|
||||
RtlZeroMemory(&addr, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(Port + RELAY_PORT_OFFSET);
|
||||
if (bind(sock, (PSOCKADDR)&addr, sizeof(addr)) == SOCKET_ERROR) {
|
||||
error = WSAGetLastError();
|
||||
printf("bind() failed: %d\n", error);
|
||||
closesocket(sock);
|
||||
return error;
|
||||
}
|
||||
|
||||
tuple = (PUDP_TUPLE)malloc(sizeof(*tuple));
|
||||
if (tuple == NULL) {
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
tuple->socket = sock;
|
||||
tuple->port = Port;
|
||||
|
||||
thread = CreateThread(NULL, 0, UdpRelayThreadProc, tuple, 0, NULL);
|
||||
if (thread == NULL) {
|
||||
error = GetLastError();
|
||||
printf("CreateThread() failed: %d\n", error);
|
||||
closesocket(sock);
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(thread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define RELAY_PORT_OFFSET -10000
|
||||
|
||||
int StartUdpRelay(unsigned short Port);
|
||||
+182
-50
@@ -9,6 +9,7 @@
|
||||
#include <wtsapi32.h>
|
||||
#include <powerbase.h>
|
||||
#include <VersionHelpers.h>
|
||||
#include <tlhelp32.h>
|
||||
|
||||
#pragma comment(lib, "miniupnpc.lib")
|
||||
#pragma comment(lib, "libnatpmp.lib")
|
||||
@@ -28,6 +29,8 @@
|
||||
|
||||
#define LOOPBACK_SERVER_PORT_OFFSET -10000
|
||||
|
||||
#define GAA_INITIAL_SIZE 8192
|
||||
|
||||
static struct port_entry {
|
||||
int proto;
|
||||
int port;
|
||||
@@ -268,6 +271,28 @@ bool IsGameStreamEnabled()
|
||||
}
|
||||
}
|
||||
|
||||
bool IsCurrentlyStreaming()
|
||||
{
|
||||
bool ret = false;
|
||||
HANDLE processSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
|
||||
PROCESSENTRY32 procEntry;
|
||||
procEntry.dwSize = sizeof(procEntry);
|
||||
Process32First(processSnapshot, &procEntry);
|
||||
|
||||
do {
|
||||
// If we find nvstreamer.exe running, we're currently streaming
|
||||
if (_stricmp(procEntry.szExeFile, "nvstreamer.exe") == 0) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
} while (Process32Next(processSnapshot, &procEntry));
|
||||
|
||||
CloseHandle(processSnapshot);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool IsConsoleSessionActive()
|
||||
{
|
||||
PWTS_SESSION_INFO_1 sessionInfo;
|
||||
@@ -377,12 +402,42 @@ bool IsZeroTierInstalled()
|
||||
return true;
|
||||
}
|
||||
|
||||
PIP_ADAPTER_ADDRESSES
|
||||
AllocAndGetAdaptersAddresses(ULONG Family, ULONG Flags)
|
||||
{
|
||||
PIP_ADAPTER_ADDRESSES addresses;
|
||||
ULONG length;
|
||||
ULONG error;
|
||||
|
||||
addresses = NULL;
|
||||
length = GAA_INITIAL_SIZE;
|
||||
do {
|
||||
free(addresses);
|
||||
addresses = (PIP_ADAPTER_ADDRESSES)malloc(length);
|
||||
if (addresses == NULL) {
|
||||
fprintf(LOG_OUT, "malloc(%u) failed\n", length);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
error = GetAdaptersAddresses(Family, Flags, NULL, addresses, &length);
|
||||
} while (error == ERROR_BUFFER_OVERFLOW);
|
||||
|
||||
if (error != ERROR_SUCCESS) {
|
||||
fprintf(LOG_OUT, "GetAdaptersAddresses() failed: %d\n", error);
|
||||
free(addresses);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return addresses;
|
||||
}
|
||||
|
||||
|
||||
enum PortTestStatus {
|
||||
PortTestOk,
|
||||
PortTestError,
|
||||
PortTestUnknown
|
||||
};
|
||||
PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withServer, bool isLoopbackRelay)
|
||||
PortTestStatus TestPort(PSOCKADDR_STORAGE addr, PSOCKADDR_STORAGE localBindAddr, int proto, int port, bool withServer, bool isLoopbackRelay, bool mtuTest)
|
||||
{
|
||||
SOCKET clientSock = INVALID_SOCKET, serverSock = INVALID_SOCKET;
|
||||
int err;
|
||||
@@ -443,6 +498,21 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the explicit bind if a local address was provided
|
||||
if (localBindAddr != nullptr) {
|
||||
SOCKADDR_IN6 bind6;
|
||||
int addrLen = localBindAddr->ss_family == AF_INET ?
|
||||
sizeof(SOCKADDR_IN) : sizeof(SOCKADDR_IN6);
|
||||
|
||||
RtlCopyMemory(&bind6, localBindAddr, addrLen);
|
||||
bind6.sin6_port = 0;
|
||||
|
||||
err = bind(clientSock, (struct sockaddr*)&bind6, addrLen);
|
||||
if (err == SOCKET_ERROR) {
|
||||
fprintf(LOG_OUT, "bind() failed: %d\n", WSAGetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
ULONG nbIo = 1;
|
||||
err = ioctlsocket(clientSock, FIONBIO, &nbIo);
|
||||
if (err == SOCKET_ERROR) {
|
||||
@@ -507,11 +577,14 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
|
||||
return err == 1 ? PortTestOk : PortTestError;
|
||||
}
|
||||
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
|
||||
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) {
|
||||
fprintf(LOG_OUT, "sendto() failed: %d\n", WSAGetLastError());
|
||||
closesocket(clientSock);
|
||||
@@ -645,7 +718,7 @@ Exit:
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TestAllPorts(PSOCKADDR_STORAGE addr, char* portMsg, int portMsgLen, bool isLoopbackRelay, bool consolePrint, bool* allPortsFailed = nullptr)
|
||||
bool TestAllPorts(PSOCKADDR_STORAGE addr, PSOCKADDR_STORAGE localBindAddr, char* portMsg, int portMsgLen, bool isLoopbackRelay, bool consolePrint, bool* allPortsFailed = nullptr)
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
@@ -662,9 +735,12 @@ bool TestAllPorts(PSOCKADDR_STORAGE addr, char* portMsg, int portMsgLen, bool is
|
||||
k_Ports[i].port);
|
||||
}
|
||||
|
||||
if (!k_Ports[i].withServer) {
|
||||
if (!k_Ports[i].withServer && (localBindAddr == nullptr || localBindAddr->ss_family == AF_INET)) {
|
||||
// Test using a real HTTP client if the port wasn't totally dead.
|
||||
// This is required to confirm functionality with the loopback relay.
|
||||
// NB: We can't do this with IPv6 because we're not guaranteed that our outbound traffic
|
||||
// will go out on the correct local IPv6 address, preventing a response from making
|
||||
// it back.
|
||||
assert(k_Ports[i].proto == IPPROTO_TCP);
|
||||
fprintf(LOG_OUT, "Testing TCP %d with HTTP traffic...", k_Ports[i].port);
|
||||
status = TestHttpPort(addr, k_Ports[i].port, isLoopbackRelay);
|
||||
@@ -673,7 +749,8 @@ bool TestAllPorts(PSOCKADDR_STORAGE addr, char* portMsg, int portMsgLen, bool is
|
||||
fprintf(LOG_OUT, "Testing %s %d...",
|
||||
k_Ports[i].proto == IPPROTO_TCP ? "TCP" : "UDP",
|
||||
k_Ports[i].port);
|
||||
status = TestPort(addr, k_Ports[i].proto, k_Ports[i].port, k_Ports[i].withServer, isLoopbackRelay);
|
||||
status = TestPort(addr, localBindAddr, k_Ports[i].proto, k_Ports[i].port,
|
||||
k_Ports[i].withServer, isLoopbackRelay, k_Ports[i].port == 47998);
|
||||
}
|
||||
|
||||
if (status != PortTestOk) {
|
||||
@@ -832,6 +909,71 @@ bool FindLocalInterfaceIPAddress(int family, PSOCKADDR_STORAGE addr)
|
||||
inet_ntop(AF_INET, &((struct sockaddr_in*)addr)->sin_addr, addrStr, sizeof(addrStr));
|
||||
}
|
||||
else {
|
||||
// There may be multiple IPv6 addresses on this interface, and chances are that the
|
||||
// outbound address is a temporary IPv6 address which is not meant to receive traffic.
|
||||
// Let's look up a suitable address by finding the correct interface and looking for
|
||||
// an address with a non-random suffix.
|
||||
|
||||
PIP_ADAPTER_ADDRESSES addresses;
|
||||
PIP_ADAPTER_ADDRESSES currentAdapter;
|
||||
PIP_ADAPTER_UNICAST_ADDRESS currentAddress;
|
||||
|
||||
addresses = AllocAndGetAdaptersAddresses(AF_UNSPEC,
|
||||
GAA_FLAG_SKIP_ANYCAST |
|
||||
GAA_FLAG_SKIP_MULTICAST |
|
||||
GAA_FLAG_SKIP_DNS_SERVER |
|
||||
GAA_FLAG_SKIP_FRIENDLY_NAME);
|
||||
if (addresses == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// First, find the interface that owns the incoming address
|
||||
currentAdapter = addresses;
|
||||
while (currentAdapter != NULL) {
|
||||
// Check if this interface has the IPv6 address we want
|
||||
currentAddress = currentAdapter->FirstUnicastAddress;
|
||||
while (currentAddress != NULL) {
|
||||
if (currentAddress->Address.lpSockaddr->sa_family == AF_INET6) {
|
||||
PSOCKADDR_IN6 ifaceAddrV6 = (PSOCKADDR_IN6)currentAddress->Address.lpSockaddr;
|
||||
if (RtlEqualMemory(&((struct sockaddr_in6*)addr)->sin6_addr, &ifaceAddrV6->sin6_addr, sizeof(IN6_ADDR))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
currentAddress = currentAddress->Next;
|
||||
}
|
||||
|
||||
if (currentAddress != NULL) {
|
||||
// It does, bail out
|
||||
break;
|
||||
}
|
||||
|
||||
currentAdapter = currentAdapter->Next;
|
||||
}
|
||||
|
||||
// Check if we found the incoming interface. If not, we'll
|
||||
// just return the IP address as given from getsockname().
|
||||
if (currentAdapter != NULL) {
|
||||
// Now find a non-privacy/non-LL IPv6 address on this interface
|
||||
currentAddress = currentAdapter->FirstUnicastAddress;
|
||||
while (currentAddress != NULL) {
|
||||
if (currentAddress->Address.lpSockaddr->sa_family == AF_INET6) {
|
||||
// Exclude link-local and privacy addresses and deprecated addresses
|
||||
PSOCKADDR_IN6 currentAddrV6 = (PSOCKADDR_IN6)currentAddress->Address.lpSockaddr;
|
||||
if (currentAddrV6->sin6_scope_id == 0 &&
|
||||
currentAddress->SuffixOrigin != IpSuffixOriginRandom &&
|
||||
currentAddress->DadState != IpDadStateDeprecated) {
|
||||
RtlCopyMemory(addr, currentAddress->Address.lpSockaddr, currentAddress->Address.iSockaddrLength);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
currentAddress = currentAddress->Next;
|
||||
}
|
||||
}
|
||||
|
||||
free(addresses);
|
||||
|
||||
inet_ntop(AF_INET6, &((struct sockaddr_in6*)addr)->sin6_addr, addrStr, sizeof(addrStr));
|
||||
}
|
||||
fprintf(LOG_OUT, "%s\n", addrStr);
|
||||
@@ -841,31 +983,21 @@ bool FindLocalInterfaceIPAddress(int family, PSOCKADDR_STORAGE addr)
|
||||
|
||||
bool FindZeroTierInterfaceAddress(PSOCKADDR_STORAGE addr)
|
||||
{
|
||||
union {
|
||||
IP_ADAPTER_ADDRESSES addresses;
|
||||
char buffer[8192];
|
||||
};
|
||||
ULONG error;
|
||||
ULONG length;
|
||||
PIP_ADAPTER_ADDRESSES addresses;
|
||||
PIP_ADAPTER_ADDRESSES currentAdapter;
|
||||
PIP_ADAPTER_UNICAST_ADDRESS currentAddress;
|
||||
|
||||
// Get all IPv4 interfaces
|
||||
length = sizeof(buffer);
|
||||
error = GetAdaptersAddresses(AF_INET,
|
||||
addresses = AllocAndGetAdaptersAddresses(AF_INET,
|
||||
GAA_FLAG_SKIP_ANYCAST |
|
||||
GAA_FLAG_SKIP_MULTICAST |
|
||||
GAA_FLAG_SKIP_DNS_SERVER |
|
||||
GAA_FLAG_SKIP_FRIENDLY_NAME,
|
||||
NULL,
|
||||
&addresses,
|
||||
&length);
|
||||
if (error != ERROR_SUCCESS) {
|
||||
fprintf(LOG_OUT, "GetAdaptersAddresses() failed: %d\n", error);
|
||||
GAA_FLAG_SKIP_FRIENDLY_NAME);
|
||||
if (addresses == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
currentAdapter = &addresses;
|
||||
currentAdapter = addresses;
|
||||
while (currentAdapter != NULL) {
|
||||
// Look for ones that correspond to a ZeroTier device
|
||||
if (wcsstr(currentAdapter->Description, L"ZeroTier")) {
|
||||
@@ -874,6 +1006,7 @@ bool FindZeroTierInterfaceAddress(PSOCKADDR_STORAGE addr)
|
||||
while (currentAddress != NULL) {
|
||||
if (currentAddress->Address.lpSockaddr->sa_family == AF_INET) {
|
||||
RtlCopyMemory(addr, currentAddress->Address.lpSockaddr, currentAddress->Address.iSockaddrLength);
|
||||
free(addresses);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -884,17 +1017,14 @@ bool FindZeroTierInterfaceAddress(PSOCKADDR_STORAGE addr)
|
||||
currentAdapter = currentAdapter->Next;
|
||||
}
|
||||
|
||||
free(addresses);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FindDuplicateDefaultInterfaces(void)
|
||||
{
|
||||
union {
|
||||
IP_ADAPTER_ADDRESSES addresses;
|
||||
char buffer[8192];
|
||||
};
|
||||
PIP_ADAPTER_ADDRESSES addresses;
|
||||
ULONG error;
|
||||
ULONG length;
|
||||
MIB_IPFORWARDROW defaultRoute;
|
||||
PIP_ADAPTER_ADDRESSES currentAdapter;
|
||||
DWORD matchingInterfaces = 0;
|
||||
@@ -906,23 +1036,18 @@ bool FindDuplicateDefaultInterfaces(void)
|
||||
}
|
||||
|
||||
// Get all IPv4 interfaces
|
||||
length = sizeof(buffer);
|
||||
error = GetAdaptersAddresses(AF_INET,
|
||||
addresses = AllocAndGetAdaptersAddresses(AF_INET,
|
||||
GAA_FLAG_SKIP_UNICAST |
|
||||
GAA_FLAG_SKIP_ANYCAST |
|
||||
GAA_FLAG_SKIP_MULTICAST |
|
||||
GAA_FLAG_SKIP_DNS_SERVER |
|
||||
GAA_FLAG_INCLUDE_GATEWAYS |
|
||||
GAA_FLAG_SKIP_FRIENDLY_NAME,
|
||||
NULL,
|
||||
&addresses,
|
||||
&length);
|
||||
if (error != ERROR_SUCCESS) {
|
||||
fprintf(LOG_OUT, "GetAdaptersAddresses() failed: %d\n", error);
|
||||
GAA_FLAG_SKIP_FRIENDLY_NAME);
|
||||
if (addresses == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
currentAdapter = &addresses;
|
||||
currentAdapter = addresses;
|
||||
while (currentAdapter != NULL) {
|
||||
if (currentAdapter->OperStatus == IfOperStatusUp &&
|
||||
currentAdapter->FirstGatewayAddress != NULL &&
|
||||
@@ -935,6 +1060,7 @@ bool FindDuplicateDefaultInterfaces(void)
|
||||
currentAdapter = currentAdapter->Next;
|
||||
}
|
||||
|
||||
free(addresses);
|
||||
return matchingInterfaces > 1;
|
||||
}
|
||||
|
||||
@@ -979,7 +1105,7 @@ UPnPPortStatus UPnPCheckPort(struct UPNPUrls* urls, struct IGDdatas* data, int p
|
||||
}
|
||||
else if (err == UPNPCOMMAND_SUCCESS) {
|
||||
if (!strcmp(myAddr, intClient)) {
|
||||
fprintf(LOG_OUT, "OK\n");
|
||||
fprintf(LOG_OUT, "OK (Internal port: %s)\n", intPort);
|
||||
return OK;
|
||||
}
|
||||
else {
|
||||
@@ -1203,6 +1329,12 @@ int main(int argc, char* argv[])
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (IsCurrentlyStreaming()) {
|
||||
DisplayMessage("The test cannot proceed because a GameStream session is currently running on this PC.\n\n"
|
||||
"Quit the currently running app on this host within Moonlight, or reboot your PC.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!IsConsoleSessionActive()) {
|
||||
DisplayMessage("The system display is currently locked. You must sign in to your PC again to use GameStream.\n\n"
|
||||
"This is most often due to Microsoft Remote Desktop locking the screen. Use an alternate GameStream-compatible remote desktop solution like Chrome Remote Desktop or TeamViewer to unlock the PC and prevent this error in the future.",
|
||||
@@ -1270,7 +1402,7 @@ int main(int argc, char* argv[])
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr = in4addr_loopback;
|
||||
fprintf(LOG_OUT, "Testing GameStream ports via loopback\n");
|
||||
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf), false, true)) {
|
||||
if (!TestAllPorts(&ss, nullptr, portMsgBuf, sizeof(portMsgBuf), false, true)) {
|
||||
snprintf(msgBuf, sizeof(msgBuf),
|
||||
"Local GameStream connectivity check failed.\n\nFirst, try reinstalling GeForce Experience. If that doesn't resolve the problem, try temporarily disabling your antivirus and firewall.");
|
||||
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting");
|
||||
@@ -1304,7 +1436,7 @@ int main(int argc, char* argv[])
|
||||
// Try to connect via ZeroTier address
|
||||
fprintf(CONSOLE_OUT, "Testing GameStream connectivity using ZeroTier...\n");
|
||||
fprintf(LOG_OUT, "Testing GameStream ports via ZeroTier\n");
|
||||
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf), false, true)) {
|
||||
if (!TestAllPorts(&ss, nullptr, portMsgBuf, sizeof(portMsgBuf), false, true)) {
|
||||
snprintf(msgBuf, sizeof(msgBuf),
|
||||
"ZeroTier connectivity check failed. This is almost always caused by a firewall on your computer blocking the connection.\n\nTry temporarily disabling your antivirus and firewall.");
|
||||
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting");
|
||||
@@ -1321,9 +1453,8 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
bool hasV4Connectivity, hasV6Connectivity;
|
||||
SOCKADDR_STORAGE v4, v6;
|
||||
{
|
||||
SOCKADDR_STORAGE v4, v6;
|
||||
|
||||
hasV4Connectivity = FindLocalInterfaceIPAddress(AF_INET, &v4);
|
||||
hasV6Connectivity = FindLocalInterfaceIPAddress(AF_INET6, &v6);
|
||||
|
||||
@@ -1344,7 +1475,7 @@ int main(int argc, char* argv[])
|
||||
|
||||
// Try to connect via LAN address
|
||||
fprintf(LOG_OUT, "Testing GameStream ports via local network\n");
|
||||
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf), false, true)) {
|
||||
if (!TestAllPorts(&ss, nullptr, portMsgBuf, sizeof(portMsgBuf), false, true)) {
|
||||
snprintf(msgBuf, sizeof(msgBuf),
|
||||
"Local network GameStream connectivity check failed. This is almost always caused by a firewall on your computer blocking the connection.\n\nTry temporarily disabling your antivirus and firewall.");
|
||||
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting");
|
||||
@@ -1370,7 +1501,7 @@ int main(int argc, char* argv[])
|
||||
|
||||
// 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((PSOCKADDR_STORAGE)&locallyReportedWanAddr, portMsgBuf, sizeof(portMsgBuf), false, false);
|
||||
TestAllPorts((PSOCKADDR_STORAGE)&locallyReportedWanAddr, &ss, portMsgBuf, sizeof(portMsgBuf), false, false);
|
||||
|
||||
fprintf(LOG_OUT, "Detected inconsistency between UPnP/NAT-PMP and STUN reported WAN addresses!\n");
|
||||
}
|
||||
@@ -1409,11 +1540,13 @@ int main(int argc, char* argv[])
|
||||
|
||||
testServerWasReachable = true;
|
||||
|
||||
if (TestAllPorts((PSOCKADDR_STORAGE)current->ai_addr, portMsgBuf, sizeof(portMsgBuf), true, true, &allPortsFailedOnV4)) {
|
||||
if (TestAllPorts((PSOCKADDR_STORAGE)current->ai_addr, &v4, portMsgBuf, sizeof(portMsgBuf), true, true, &allPortsFailedOnV4)) {
|
||||
freeaddrinfo(result);
|
||||
snprintf(msgBuf, sizeof(msgBuf), "This PC is ready to host over the Internet!\n\n"
|
||||
"For the easiest setup, you should pair Moonlight to your gaming PC from your home network before trying to stream over the Internet.\n\n"
|
||||
"If you can't, you can type the following address into Moonlight's Add PC dialog: %s", wanAddrStr);
|
||||
"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"
|
||||
"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);
|
||||
return 0;
|
||||
}
|
||||
@@ -1439,7 +1572,7 @@ int main(int argc, char* argv[])
|
||||
|
||||
// Pass the portMsgBuf only if we've detected an IPv6-only setup. Otherwise, we want to preserve
|
||||
// the failing ports from the IPv4 to display in the error dialog.
|
||||
if (TestAllPorts((PSOCKADDR_STORAGE)current->ai_addr,
|
||||
if (TestAllPorts((PSOCKADDR_STORAGE)current->ai_addr, &v6,
|
||||
ss.ss_family == AF_INET6 ? portMsgBuf : NULL,
|
||||
ss.ss_family == AF_INET6 ? sizeof(portMsgBuf) : 0, true, true)) {
|
||||
// We will terminate the test at the IPv6 limited connectivity warning in the following cases:
|
||||
@@ -1461,10 +1594,9 @@ int main(int argc, char* argv[])
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Tested against a working server and it failed
|
||||
break;
|
||||
}
|
||||
|
||||
// Tested against a IPv6 working server
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+24
-2
@@ -93,16 +93,22 @@
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>..\libs\include;..\libs\include\miniupnpc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>..\libs\x86\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<CETCompat>true</CETCompat>
|
||||
</Link>
|
||||
<Manifest>
|
||||
<AdditionalManifestFiles>mist.exe.manifest</AdditionalManifestFiles>
|
||||
</Manifest>
|
||||
<PostBuildEvent>
|
||||
<Command>copy /Y "$(SolutionDir)libs\x86\Debug\*" "$(TargetDir)"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
@@ -113,14 +119,20 @@
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<CETCompat>true</CETCompat>
|
||||
</Link>
|
||||
<Manifest>
|
||||
<AdditionalManifestFiles>mist.exe.manifest</AdditionalManifestFiles>
|
||||
</Manifest>
|
||||
<PostBuildEvent>
|
||||
<Command>copy /Y "$(SolutionDir)libs\x86\Debug\*" "$(TargetDir)"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
@@ -135,7 +147,8 @@
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>..\libs\include;..\libs\include\miniupnpc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@@ -143,10 +156,14 @@
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>..\libs\x86\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<CETCompat>true</CETCompat>
|
||||
</Link>
|
||||
<Manifest>
|
||||
<AdditionalManifestFiles>mist.exe.manifest</AdditionalManifestFiles>
|
||||
</Manifest>
|
||||
<PostBuildEvent>
|
||||
<Command>copy /Y "$(SolutionDir)libs\x86\Release\*" "$(TargetDir)"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
@@ -159,16 +176,21 @@
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<CETCompat>true</CETCompat>
|
||||
</Link>
|
||||
<Manifest>
|
||||
<AdditionalManifestFiles>mist.exe.manifest</AdditionalManifestFiles>
|
||||
</Manifest>
|
||||
<PostBuildEvent>
|
||||
<Command>copy /Y "$(SolutionDir)libs\x86\Release\*" "$(TargetDir)"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="mist.cpp" />
|
||||
|
||||
+56
-40
@@ -45,15 +45,20 @@ bool getExternalAddressPortIP4(unsigned short localPort, PSOCKADDR_IN wanAddr)
|
||||
int i;
|
||||
int bytesRead;
|
||||
int err;
|
||||
bool ret;
|
||||
PSTUN_ATTRIBUTE_HEADER attribute;
|
||||
PSTUN_MAPPED_IPV4_ADDRESS_ATTRIBUTE ipv4Attrib;
|
||||
struct addrinfo* result;
|
||||
struct addrinfo hints;
|
||||
struct sockaddr_in bindAddr;
|
||||
union {
|
||||
STUN_MESSAGE hdr;
|
||||
char buf[1024];
|
||||
} resp;
|
||||
|
||||
sock = INVALID_SOCKET;
|
||||
ret = false;
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
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);
|
||||
if (sock == INVALID_SOCKET) {
|
||||
fprintf(LOG_OUT, "socket() failed: %d\n", WSAGetLastError());
|
||||
freeaddrinfo(result);
|
||||
return false;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
struct sockaddr_in bindAddr = {};
|
||||
bindAddr = {};
|
||||
bindAddr.sin_family = hints.ai_family;
|
||||
bindAddr.sin_port = htons(localPort);
|
||||
if (bind(sock, (struct sockaddr*)&bindAddr, sizeof(bindAddr)) == SOCKET_ERROR) {
|
||||
fprintf(LOG_OUT, "bind() failed: %d\n", WSAGetLastError());
|
||||
closesocket(sock);
|
||||
freeaddrinfo(result);
|
||||
return false;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
reqMsg.messageType = htons(STUN_MESSAGE_BINDING_REQUEST);
|
||||
@@ -98,57 +100,60 @@ bool getExternalAddressPortIP4(unsigned short localPort, PSOCKADDR_IN wanAddr)
|
||||
}
|
||||
}
|
||||
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(sock, &fds);
|
||||
for (;;) {
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(sock, &fds);
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 1;
|
||||
tv.tv_usec = 0;
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 1;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
int selectRes = select(0, &fds, nullptr, nullptr, &tv);
|
||||
if (selectRes == 0) {
|
||||
// Timeout - continue looping
|
||||
continue;
|
||||
int selectRes = select(0, &fds, nullptr, nullptr, &tv);
|
||||
if (selectRes == 0) {
|
||||
// Timeout - break to the enclosing loop
|
||||
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);
|
||||
closesocket(sock);
|
||||
|
||||
RecvDone:
|
||||
if (bytesRead == 0) {
|
||||
fprintf(LOG_OUT, "No response from STUN server\n");
|
||||
return false;
|
||||
goto Exit;
|
||||
}
|
||||
else if (bytesRead == SOCKET_ERROR) {
|
||||
fprintf(LOG_OUT, "Failed to read STUN binding response: %d\n", WSAGetLastError());
|
||||
return false;
|
||||
goto Exit;
|
||||
}
|
||||
else if (bytesRead < sizeof(resp.hdr)) {
|
||||
fprintf(LOG_OUT, "STUN message truncated: %d\n", bytesRead);
|
||||
return false;
|
||||
goto Exit;
|
||||
}
|
||||
else if (htonl(resp.hdr.magicCookie) != STUN_MESSAGE_COOKIE) {
|
||||
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))) {
|
||||
fprintf(LOG_OUT, "STUN transaction ID mismatch\n");
|
||||
return false;
|
||||
goto Exit;
|
||||
}
|
||||
else if (htons(resp.hdr.messageType) != STUN_MESSAGE_BINDING_SUCCESS) {
|
||||
fprintf(LOG_OUT, "STUN message type mismatch: %x\n", htons(resp.hdr.messageType));
|
||||
return false;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
attribute = (PSTUN_ATTRIBUTE_HEADER)(&resp.hdr + 1);
|
||||
@@ -156,7 +161,7 @@ bool getExternalAddressPortIP4(unsigned short localPort, PSOCKADDR_IN wanAddr)
|
||||
while (bytesRead > sizeof(*attribute)) {
|
||||
if (bytesRead < sizeof(*attribute) + 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
|
||||
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;
|
||||
if (htons(ipv4Attrib->hdr.length) != 8) {
|
||||
fprintf(LOG_OUT, "STUN address length mismatch: %d\n", htons(ipv4Attrib->hdr.length));
|
||||
return false;
|
||||
goto Exit;
|
||||
}
|
||||
else if (ipv4Attrib->addressFamily != 1) {
|
||||
fprintf(LOG_OUT, "STUN address family mismatch: %x\n", ipv4Attrib->addressFamily);
|
||||
return false;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
*wanAddr = {};
|
||||
@@ -183,9 +188,20 @@ bool getExternalAddressPortIP4(unsigned short localPort, PSOCKADDR_IN wanAddr)
|
||||
wanAddr->sin_port = ipv4Attrib->port ^ (short)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");
|
||||
return false;
|
||||
|
||||
Exit:
|
||||
if (sock != INVALID_SOCKET) {
|
||||
closesocket(sock);
|
||||
}
|
||||
|
||||
if (result != NULL) {
|
||||
freeaddrinfo(result);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#define VER_VERSION 5,1,0,0
|
||||
#define VER_VERSION_STR "5.1.0.0"
|
||||
#define VER_VERSION 5,5,3,0
|
||||
#define VER_VERSION_STR "5.5.3.0"
|
||||
|
||||
#define VER_COMPANYNAME_STR "Moonlight Game Streaming Project"
|
||||
#define VER_PRODUCTNAME_STR "Moonlight Internet Hosting Tool"
|
||||
|
||||
Reference in New Issue
Block a user