Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ddd86d91eb | ||
|
|
546e30f363 | ||
|
|
432c2e226c | ||
|
|
cc98c2cd17 | ||
|
|
c8a5fe2efa | ||
|
|
6d5d83d420 | ||
|
|
1e1d347052 | ||
|
|
307623914b | ||
|
|
8f011440b4 | ||
|
|
771e34f199 | ||
|
|
c8e8af7d4b | ||
|
|
18b914d9f6 | ||
|
|
e9ddf91157 | ||
|
|
28a5935ee7 | ||
|
|
ce3b9c3d31 | ||
|
|
0f3506a92c | ||
|
|
1e6337b9b0 | ||
|
|
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 | ||
|
|
e4a4d42ece | ||
|
|
189e861362 | ||
|
|
fd022b67d3 | ||
|
|
14d27b4cf5 | ||
|
|
17cb084968 | ||
|
|
ad8ef228d5 | ||
|
|
8d2252c4ac | ||
|
|
365b3ccd61 | ||
|
|
74140a8511 | ||
|
|
a9bf4ffd70 | ||
|
|
164d8b5467 | ||
|
|
f1ebefa59c | ||
|
|
3e060fae54 | ||
|
|
f97c04a8cc | ||
|
|
85cd20005c | ||
|
|
e3e4bf608f | ||
|
|
0be2ca51e9 | ||
|
|
3e1ffce16f | ||
|
|
c7813a7a0b | ||
|
|
87fd3ad8e2 | ||
|
|
9d3ef0a215 | ||
|
|
ae83853a27 | ||
|
|
0b8b117cb2 | ||
|
|
0050566124 | ||
|
|
cc24472193 | ||
|
|
4a11ab0f7f | ||
|
|
c1c16edf7d | ||
|
|
ae2647f36e | ||
|
|
8e40ef80bd | ||
|
|
16bb727b99 | ||
|
|
9194b8e22b | ||
|
|
2805464e86 | ||
|
|
20e7374480 | ||
|
|
0fdbee831b | ||
|
|
b37b99e9c3 | ||
|
|
29bbdc5319 | ||
|
|
09cd50b12a | ||
|
|
d60eda6fa8 | ||
|
|
cda9bc6be7 | ||
|
|
0008fbbcdb | ||
|
|
620aa4b6d1 | ||
|
|
b93ef55b65 | ||
|
|
fdb033f550 | ||
|
|
efdcaed4c1 | ||
|
|
5a2edb9a36 | ||
|
|
798be5fd0a | ||
|
|
b0941fee90 | ||
|
|
ebc7703bf2 | ||
|
|
bbf16383f5 | ||
|
|
dee2daac5a | ||
|
|
51e953c092 | ||
|
|
acb3f5a059 | ||
|
|
0bd212e6f1 | ||
|
|
d076f881eb | ||
|
|
46c2236342 | ||
|
|
8158c85a23 | ||
|
|
6f3898e4b4 | ||
|
|
b1a1352bbf | ||
|
|
069b0c7656 | ||
|
|
42df15c567 | ||
|
|
96a88b55db | ||
|
|
2d31cd0420 | ||
|
|
ebb8c9fb68 | ||
|
|
b31d257566 | ||
|
|
51dc3142ca | ||
|
|
c8ab32812f | ||
|
|
21802eac19 | ||
|
|
dab033ee3b | ||
|
|
204dde8a73 | ||
|
|
3d9e14fb56 | ||
|
|
771e09973f | ||
|
|
1bc6cc5634 | ||
|
|
0941cc5bc1 | ||
|
|
a7e41d15f5 | ||
|
|
6916cfd0de | ||
|
|
bae92c6c95 | ||
|
|
c8de19a3b6 | ||
|
|
1da8f371aa | ||
|
|
cb55a02599 | ||
|
|
dc820bdbf6 | ||
|
|
106960cbb5 | ||
|
|
3c92a79fe2 | ||
|
|
2c2f275c3d | ||
|
|
156940cc5b | ||
|
|
a835cf43a5 | ||
|
|
18b704b85a | ||
|
|
cd4adae22b | ||
|
|
ef59e2858b | ||
|
|
218c0d7385 | ||
|
|
e7f03cfa93 |
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "GS-IPv6-Forwarder"]
|
||||||
|
path = GS-IPv6-Forwarder
|
||||||
|
url = https://github.com/moonlight-stream/GS-IPv6-Forwarder.git
|
||||||
Submodule
+1
Submodule GS-IPv6-Forwarder added at e0db902f7d
@@ -1,4 +1,4 @@
|
|||||||
# Moonlight Internet Streaming Helper
|
# Moonlight Internet Hosting Tool
|
||||||
* Enable Moonlight streaming from your PC over the Internet with no configuration required
|
* Enable Moonlight streaming from your PC over the Internet with no configuration required
|
||||||
* Troubleshoot streaming issues with the included testing tool
|
* Troubleshoot streaming issues with the included testing tool
|
||||||
|
|
||||||
@@ -8,5 +8,6 @@
|
|||||||
|
|
||||||
# Technical Features
|
# Technical Features
|
||||||
- Supports all major port forwarding protocols (UPnP, NAT-PMP, and PCP) for the best possible router compatibility
|
- Supports all major port forwarding protocols (UPnP, NAT-PMP, and PCP) for the best possible router compatibility
|
||||||
|
- Supports streaming on IPv4-only, IPv6-only, and dual-stack Internet connections
|
||||||
- Supports successful forwarding in double-NAT environments by configuring port mappings on all NATs found on the way to the Internet
|
- Supports successful forwarding in double-NAT environments by configuring port mappings on all NATs found on the way to the Internet
|
||||||
- Supports forwarding on Carrier-Grade NATs (CGNs) by directly using PCP when the ISP doesn't implement UPnP-PCP interoperability
|
- Supports forwarding on Carrier-Grade NATs (CGNs) by directly using PCP when the ISP doesn't implement UPnP-PCP interoperability
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
/* $Id: miniupnpc.h,v 1.53 2018/05/07 11:05:16 nanard Exp $ */
|
/* $Id: miniupnpc.h,v 1.59 2021/09/28 21:39:17 nanard Exp $ */
|
||||||
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
||||||
* Project: miniupnp
|
* Project: miniupnp
|
||||||
* http://miniupnp.free.fr/
|
* http://miniupnp.free.fr/
|
||||||
* Author: Thomas Bernard
|
* Author: Thomas Bernard
|
||||||
* Copyright (c) 2005-2018 Thomas Bernard
|
* Copyright (c) 2005-2021 Thomas Bernard
|
||||||
* This software is subjects to the conditions detailed
|
* This software is subjects to the conditions detailed
|
||||||
* in the LICENCE file provided within this distribution */
|
* in the LICENCE file provided within this distribution */
|
||||||
#ifndef MINIUPNPC_H_INCLUDED
|
#ifndef MINIUPNPC_H_INCLUDED
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
#define UPNPDISCOVER_MEMORY_ERROR (-102)
|
#define UPNPDISCOVER_MEMORY_ERROR (-102)
|
||||||
|
|
||||||
/* versions : */
|
/* versions : */
|
||||||
#define MINIUPNPC_VERSION "2.1"
|
#define MINIUPNPC_VERSION "2.2.3"
|
||||||
#define MINIUPNPC_API_VERSION 17
|
#define MINIUPNPC_API_VERSION 17
|
||||||
|
|
||||||
/* Source port:
|
/* Source port:
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#ifndef MINIUPNPC_SOCKETDEF_H_INCLUDED
|
#ifndef MINIUPNPC_SOCKETDEF_H_INCLUDED
|
||||||
#define MINIUPNPC_SOCKETDEF_H_INCLUDED
|
#define MINIUPNPC_SOCKETDEF_H_INCLUDED
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _WIN32
|
||||||
|
|
||||||
#define ISINVALID(s) (INVALID_SOCKET==(s))
|
#define ISINVALID(s) (INVALID_SOCKET==(s))
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
/* $Id: miniupnpctypes.h,v 1.1 2011/02/15 11:10:40 nanard Exp $ */
|
/* $Id: miniupnpctypes.h,v 1.1 2011/02/15 11:10:40 nanard Exp $ */
|
||||||
/* Miniupnp project : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org
|
/* Miniupnp project : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org
|
||||||
* Author : Thomas Bernard
|
* Author : Thomas Bernard
|
||||||
* Copyright (c) 2011 Thomas Bernard
|
* Copyright (c) 2021 Thomas Bernard
|
||||||
* This software is subject to the conditions detailed in the
|
* This software is subject to the conditions detailed in the
|
||||||
* LICENCE file provided within this distribution */
|
* LICENCE file provided within this distribution */
|
||||||
#ifndef MINIUPNPCTYPES_H_INCLUDED
|
#ifndef MINIUPNPCTYPES_H_INCLUDED
|
||||||
#define MINIUPNPCTYPES_H_INCLUDED
|
#define MINIUPNPCTYPES_H_INCLUDED
|
||||||
|
|
||||||
#if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
|
/* Use unsigned long long when available :
|
||||||
|
* strtoull is C99 */
|
||||||
|
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||||
#define UNSIGNED_INTEGER unsigned long long
|
#define UNSIGNED_INTEGER unsigned long long
|
||||||
#define STRTOUI strtoull
|
#define STRTOUI strtoull
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/* $Id: portlistingparse.h,v 1.10 2014/11/01 10:37:32 nanard Exp $ */
|
||||||
|
/* MiniUPnP project
|
||||||
|
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||||
|
* (c) 2011-2015 Thomas Bernard
|
||||||
|
* This software is subject to the conditions detailed
|
||||||
|
* in the LICENCE file provided within the distribution */
|
||||||
|
#ifndef PORTLISTINGPARSE_H_INCLUDED
|
||||||
|
#define PORTLISTINGPARSE_H_INCLUDED
|
||||||
|
|
||||||
|
#include "miniupnpc_declspec.h"
|
||||||
|
/* for the definition of UNSIGNED_INTEGER */
|
||||||
|
#include "miniupnpctypes.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* sample of PortMappingEntry :
|
||||||
|
<p:PortMappingEntry>
|
||||||
|
<p:NewRemoteHost>202.233.2.1</p:NewRemoteHost>
|
||||||
|
<p:NewExternalPort>2345</p:NewExternalPort>
|
||||||
|
<p:NewProtocol>TCP</p:NewProtocol>
|
||||||
|
<p:NewInternalPort>2345</p:NewInternalPort>
|
||||||
|
<p:NewInternalClient>192.168.1.137</p:NewInternalClient>
|
||||||
|
<p:NewEnabled>1</p:NewEnabled>
|
||||||
|
<p:NewDescription>dooom</p:NewDescription>
|
||||||
|
<p:NewLeaseTime>345</p:NewLeaseTime>
|
||||||
|
</p:PortMappingEntry>
|
||||||
|
*/
|
||||||
|
typedef enum { PortMappingEltNone,
|
||||||
|
PortMappingEntry, NewRemoteHost,
|
||||||
|
NewExternalPort, NewProtocol,
|
||||||
|
NewInternalPort, NewInternalClient,
|
||||||
|
NewEnabled, NewDescription,
|
||||||
|
NewLeaseTime } portMappingElt;
|
||||||
|
|
||||||
|
struct PortMapping {
|
||||||
|
struct PortMapping * l_next; /* list next element */
|
||||||
|
UNSIGNED_INTEGER leaseTime;
|
||||||
|
unsigned short externalPort;
|
||||||
|
unsigned short internalPort;
|
||||||
|
char remoteHost[64];
|
||||||
|
char internalClient[64];
|
||||||
|
char description[64];
|
||||||
|
char protocol[4];
|
||||||
|
unsigned char enabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PortMappingParserData {
|
||||||
|
struct PortMapping * l_head; /* list head */
|
||||||
|
portMappingElt curelt;
|
||||||
|
};
|
||||||
|
|
||||||
|
MINIUPNP_LIBSPEC void
|
||||||
|
ParsePortListing(const char * buffer, int bufsize,
|
||||||
|
struct PortMappingParserData * pdata);
|
||||||
|
|
||||||
|
MINIUPNP_LIBSPEC void
|
||||||
|
FreePortListing(struct PortMappingParserData * pdata);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
/* $Id: upnpdev.h,v 1.1 2015/08/28 12:14:19 nanard Exp $ */
|
/* $Id: upnpdev.h,v 1.4 2021/08/21 09:45:01 nanard Exp $ */
|
||||||
/* Project : miniupnp
|
/* Project : miniupnp
|
||||||
* Web : http://miniupnp.free.fr/
|
* Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
|
||||||
* Author : Thomas BERNARD
|
* Author : Thomas BERNARD
|
||||||
* copyright (c) 2005-2018 Thomas Bernard
|
* copyright (c) 2005-2021 Thomas Bernard
|
||||||
* This software is subjet to the conditions detailed in the
|
* This software is subjet to the conditions detailed in the
|
||||||
* provided LICENSE file. */
|
* provided LICENSE file. */
|
||||||
#ifndef UPNPDEV_H_INCLUDED
|
#ifndef UPNPDEV_H_INCLUDED
|
||||||
@@ -20,7 +20,15 @@ struct UPNPDev {
|
|||||||
char * st;
|
char * st;
|
||||||
char * usn;
|
char * usn;
|
||||||
unsigned int scope_id;
|
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()
|
/* freeUPNPDevlist()
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/* $Id: upnpreplyparse.h,v 1.19 2014/10/27 16:33:19 nanard Exp $ */
|
||||||
|
/* MiniUPnP project
|
||||||
|
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||||
|
* (c) 2006-2013 Thomas Bernard
|
||||||
|
* This software is subject to the conditions detailed
|
||||||
|
* in the LICENCE file provided within the distribution */
|
||||||
|
|
||||||
|
#ifndef UPNPREPLYPARSE_H_INCLUDED
|
||||||
|
#define UPNPREPLYPARSE_H_INCLUDED
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct NameValue {
|
||||||
|
struct NameValue * l_next;
|
||||||
|
char name[64];
|
||||||
|
char value[128];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameValueParserData {
|
||||||
|
struct NameValue * l_head;
|
||||||
|
char curelt[64];
|
||||||
|
char * portListing;
|
||||||
|
int portListingLength;
|
||||||
|
int topelt;
|
||||||
|
const char * cdata;
|
||||||
|
int cdatalen;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ParseNameValue() */
|
||||||
|
void
|
||||||
|
ParseNameValue(const char * buffer, int bufsize,
|
||||||
|
struct NameValueParserData * data);
|
||||||
|
|
||||||
|
/* ClearNameValueList() */
|
||||||
|
void
|
||||||
|
ClearNameValueList(struct NameValueParserData * pdata);
|
||||||
|
|
||||||
|
/* GetValueFromNameValueList() */
|
||||||
|
char *
|
||||||
|
GetValueFromNameValueList(struct NameValueParserData * pdata,
|
||||||
|
const char * Name);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* GetValueFromNameValueListIgnoreNS() */
|
||||||
|
char *
|
||||||
|
GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
|
||||||
|
const char * Name);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* DisplayNameValueList() */
|
||||||
|
#ifdef DEBUG
|
||||||
|
void
|
||||||
|
DisplayNameValueList(char * buffer, int bufsize);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -51,6 +51,13 @@ typedef unsigned short uint16_t;
|
|||||||
#endif /* _WIN32 */
|
#endif /* _WIN32 */
|
||||||
#include "natpmp_declspec.h"
|
#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 {
|
typedef struct {
|
||||||
int s; /* socket */
|
int s; /* socket */
|
||||||
in_addr_t gateway; /* default gateway (IPv4) */
|
in_addr_t gateway; /* default gateway (IPv4) */
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#ifndef DECLSPEC_H_INCLUDED
|
#ifndef NATPMP_DECLSPEC_H_INCLUDED
|
||||||
#define DECLSPEC_H_INCLUDED
|
#define NATPMP_DECLSPEC_H_INCLUDED
|
||||||
|
|
||||||
#if defined(_WIN32) && !defined(NATPMP_STATICLIB)
|
#if defined(_WIN32) && !defined(NATPMP_STATICLIB)
|
||||||
/* for windows dll */
|
/* for windows dll */
|
||||||
|
|||||||
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.
@@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?define VCREDIST_VER = "14.36.32532.0" ?>
|
||||||
|
<?define VCREDIST_X86_SIZE = "13837672" ?>
|
||||||
|
<?define VCREDIST_X86_SHA1 = "C9B5B7969E499A4FD9E580EF4187322778E1936A" ?>
|
||||||
|
<?define VCREDIST_X86_URL = "https://download.visualstudio.microsoft.com/download/pr/eaab1f82-787d-4fd7-8c73-f782341a0c63/5365A927487945ECB040E143EA770ADBB296074ECE4021B1D14213BDE538C490/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: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
|
||||||
|
LicenseFile="license.rtf"
|
||||||
|
ShowVersion="yes"
|
||||||
|
SuppressOptionsUI="yes"
|
||||||
|
/>
|
||||||
|
</BootstrapperApplicationRef>
|
||||||
|
|
||||||
|
<WixVariable Id="WixUILicenseRtf" Value="" />
|
||||||
|
|
||||||
|
<util:ProductSearch Id="VCREDIST_14_x86"
|
||||||
|
UpgradeCode="$(var.VCREDIST_X86_UPGRADE_CODE)"
|
||||||
|
Result="version"
|
||||||
|
Variable="VCREDIST_14_x86" />
|
||||||
|
|
||||||
|
<Chain>
|
||||||
|
<ExePackage Name="Microsoft Visual C++ 2015-2022 Redistributable - x86"
|
||||||
|
Cache="no"
|
||||||
|
Compressed="no"
|
||||||
|
PerMachine="yes"
|
||||||
|
Permanent="yes"
|
||||||
|
Vital="yes"
|
||||||
|
InstallCommand="/install /quiet /norestart"
|
||||||
|
DownloadUrl="$(var.VCREDIST_X86_URL)"
|
||||||
|
DetectCondition="VCREDIST_14_x86 >= v$(var.VCREDIST_VER)">
|
||||||
|
|
||||||
|
<RemotePayload Description="Microsoft Visual C++ 2015-2022 Redistributable - x86"
|
||||||
|
ProductName="Microsoft Visual C++ 2015-2022 Redistributable - x86"
|
||||||
|
Size="$(var.VCREDIST_X86_SIZE)"
|
||||||
|
Version="$(var.VCREDIST_VER)"
|
||||||
|
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>
|
||||||
|
</Bundle>
|
||||||
|
</Wix>
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" DefaultTargets="Build" InitialTargets="EnsureWixToolsetInstalled" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||||
|
<ProductVersion>3.10</ProductVersion>
|
||||||
|
<ProjectGuid>a2ce5056-1114-44b9-b79b-952ef46d3d50</ProjectGuid>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<OutputName>InternetHostingToolSetup</OutputName>
|
||||||
|
<OutputType>Bundle</OutputType>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||||
|
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||||
|
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
|
||||||
|
<DefineConstants>Debug</DefineConstants>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||||
|
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||||
|
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Bundle.wxs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<WixExtension Include="WixBalExtension">
|
||||||
|
<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">
|
||||||
|
<Name>GSv6FwdSetup</Name>
|
||||||
|
<Project>{f8171b99-f5f9-4abf-9fe5-6753539611af}</Project>
|
||||||
|
<Private>True</Private>
|
||||||
|
<DoNotHarvest>True</DoNotHarvest>
|
||||||
|
<RefProjectOutputGroups>Binaries;Content;Satellites</RefProjectOutputGroups>
|
||||||
|
<RefTargetDir>INSTALLFOLDER</RefTargetDir>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\mish\mish.wixproj">
|
||||||
|
<Name>mish</Name>
|
||||||
|
<Project>{f0dee5f3-4b62-47a3-b00b-61c614c924fd}</Project>
|
||||||
|
<Private>True</Private>
|
||||||
|
<DoNotHarvest>True</DoNotHarvest>
|
||||||
|
<RefProjectOutputGroups>Binaries;Content;Satellites</RefProjectOutputGroups>
|
||||||
|
<RefTargetDir>INSTALLFOLDER</RefTargetDir>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(WixTargetsPath)" Condition=" '$(WixTargetsPath)' != '' " />
|
||||||
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets" Condition=" '$(WixTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets') " />
|
||||||
|
<Target Name="EnsureWixToolsetInstalled" Condition=" '$(WixTargetsImported)' != 'true' ">
|
||||||
|
<Error Text="The WiX Toolset v3.11 (or newer) build tools must be installed to build this project. To download the WiX Toolset, see http://wixtoolset.org/releases/" />
|
||||||
|
</Target>
|
||||||
|
<!--
|
||||||
|
To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Wix.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 15
|
# Visual Studio Version 16
|
||||||
VisualStudioVersion = 15.0.28010.2046
|
VisualStudioVersion = 16.0.29102.190
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miss", "miss\miss.vcxproj", "{B71C4E7F-0D92-4E19-BBD2-D33F0EF879B1}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miss", "miss\miss.vcxproj", "{B71C4E7F-0D92-4E19-BBD2-D33F0EF879B1}"
|
||||||
EndProject
|
EndProject
|
||||||
@@ -9,36 +9,42 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mist", "mist\mist.vcxproj",
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "mish", "mish\mish.wixproj", "{F0DEE5F3-4B62-47A3-B00B-61C614C924FD}"
|
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "mish", "mish\mish.wixproj", "{F0DEE5F3-4B62-47A3-B00B-61C614C924FD}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "mish-setup", "mish-setup\mish-setup.wixproj", "{A2CE5056-1114-44B9-B79B-952EF46D3D50}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GSv6Fwd", "GS-IPv6-Forwarder\GSv6Fwd\GSv6Fwd.vcxproj", "{87DEAE49-7638-4CDB-88EB-054B1F3CB0D2}"
|
||||||
|
EndProject
|
||||||
|
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "GSv6FwdSetup", "GS-IPv6-Forwarder\GSv6FwdSetup\GSv6FwdSetup.wixproj", "{F8171B99-F5F9-4ABF-9FE5-6753539611AF}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|x64 = Debug|x64
|
|
||||||
Debug|x86 = Debug|x86
|
Debug|x86 = Debug|x86
|
||||||
Release|x64 = Release|x64
|
|
||||||
Release|x86 = Release|x86
|
Release|x86 = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
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.ActiveCfg = Debug|Win32
|
||||||
{B71C4E7F-0D92-4E19-BBD2-D33F0EF879B1}.Debug|x86.Build.0 = 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.ActiveCfg = Release|Win32
|
||||||
{B71C4E7F-0D92-4E19-BBD2-D33F0EF879B1}.Release|x86.Build.0 = 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.ActiveCfg = Debug|Win32
|
||||||
{8F78D8D2-A837-489C-BAC5-81494C9CEF7A}.Debug|x86.Build.0 = 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.ActiveCfg = Release|Win32
|
||||||
{8F78D8D2-A837-489C-BAC5-81494C9CEF7A}.Release|x86.Build.0 = 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.ActiveCfg = Debug|x86
|
||||||
{F0DEE5F3-4B62-47A3-B00B-61C614C924FD}.Debug|x86.Build.0 = 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.ActiveCfg = Release|x86
|
||||||
{F0DEE5F3-4B62-47A3-B00B-61C614C924FD}.Release|x86.Build.0 = Release|x86
|
{F0DEE5F3-4B62-47A3-B00B-61C614C924FD}.Release|x86.Build.0 = Release|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|x86.ActiveCfg = Release|x86
|
||||||
|
{A2CE5056-1114-44B9-B79B-952EF46D3D50}.Release|x86.Build.0 = Release|x86
|
||||||
|
{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|x86.ActiveCfg = Release|Win32
|
||||||
|
{87DEAE49-7638-4CDB-88EB-054B1F3CB0D2}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{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|x86.ActiveCfg = Release|x86
|
||||||
|
{F8171B99-F5F9-4ABF-9FE5-6753539611AF}.Release|x86.Build.0 = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
+90
-10
@@ -1,28 +1,48 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
|
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
|
||||||
xmlns:fire="http://schemas.microsoft.com/wix/FirewallExtension">
|
xmlns:fire="http://schemas.microsoft.com/wix/FirewallExtension"
|
||||||
<Product Id="*" Name="Moonlight Internet Streaming Helper" Language="1033" Version="!(bind.fileVersion.MistExe)"
|
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
|
||||||
|
<Product Id="*" Name="Moonlight Internet Hosting Tool" Language="1033" Version="!(bind.fileVersion.MistExe)"
|
||||||
Manufacturer="Moonlight Game Streaming Project" UpgradeCode="cfc2fb53-88e2-4867-851d-9ed9fe7ab2a3">
|
Manufacturer="Moonlight Game Streaming Project" UpgradeCode="cfc2fb53-88e2-4867-851d-9ed9fe7ab2a3">
|
||||||
<Package InstallerVersion="500" Compressed="yes" InstallScope="perMachine" />
|
<Package InstallerVersion="500" Compressed="yes" InstallScope="perMachine" />
|
||||||
|
|
||||||
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
|
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
|
||||||
<MediaTemplate EmbedCab="yes" />
|
<MediaTemplate EmbedCab="yes" />
|
||||||
|
|
||||||
<WixVariable Id="WixUILicenseRtf" Value="license.rtf" />
|
|
||||||
<UIRef Id="WixUI_Minimal" />
|
|
||||||
|
|
||||||
<Feature Id="ProductFeature" Title="MISHSetup" Level="1">
|
<Feature Id="ProductFeature" Title="MISHSetup" Level="1">
|
||||||
<ComponentGroupRef Id="ProductComponents" />
|
<ComponentGroupRef Id="ProductComponents" />
|
||||||
</Feature>
|
</Feature>
|
||||||
</Product>
|
</Product>
|
||||||
|
|
||||||
|
<!-- Enable the Windows Firewall service if it is disabled. Disabling the Windows Firewall service
|
||||||
|
will paradoxically block *all* incoming network traffic and prevent GameStream, MIST, MISS,
|
||||||
|
and other local servers from working at all. The proper way to disable Windows Firewall is via
|
||||||
|
the Windows Firewall control panel applet. -->
|
||||||
|
<Fragment>
|
||||||
|
<Property Id="MPSSVC_START">
|
||||||
|
<RegistrySearch Id="MpsSvcStart"
|
||||||
|
Root="HKLM"
|
||||||
|
Key="System\CurrentControlSet\Services\MpsSvc"
|
||||||
|
Name="Start"
|
||||||
|
Type="raw" />
|
||||||
|
</Property>
|
||||||
|
|
||||||
|
<SetProperty Id="EnableMpsSvc" Value='"[SystemFolder]sc.exe" config MpsSvc start= auto' Sequence="execute" Before="InstallInitialize"/>
|
||||||
|
<CustomAction Id="EnableMpsSvc" BinaryKey="WixCA" DllEntry="WixQuietExec"
|
||||||
|
Execute="deferred" Return="ignore" Impersonate="no"/>
|
||||||
|
|
||||||
|
<SetProperty Id="StartMpsSvc" Value='"[SystemFolder]net.exe" start MpsSvc' Sequence="execute" Before="InstallInitialize"/>
|
||||||
|
<CustomAction Id="StartMpsSvc" BinaryKey="WixCA" DllEntry="WixQuietExec"
|
||||||
|
Execute="deferred" Return="ignore" Impersonate="no"/>
|
||||||
|
</Fragment>
|
||||||
|
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<Directory Id="TARGETDIR" Name="SourceDir">
|
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||||
<Directory Id="ProgramFilesFolder">
|
<Directory Id="ProgramFilesFolder">
|
||||||
<Directory Id="INSTALLFOLDER" Name="Moonlight Internet Streaming Helper" />
|
<Directory Id="INSTALLFOLDER" Name="Moonlight Internet Hosting Tool" />
|
||||||
</Directory>
|
</Directory>
|
||||||
<Directory Id="ProgramMenuFolder">
|
<Directory Id="ProgramMenuFolder">
|
||||||
<Directory Id="ApplicationProgramsFolder" Name="Moonlight Internet Streaming Helper" />
|
<Directory Id="ApplicationProgramsFolder" Name="Moonlight Internet Hosting Tool" />
|
||||||
</Directory>
|
</Directory>
|
||||||
<Directory Id="CommonAppDataFolder">
|
<Directory Id="CommonAppDataFolder">
|
||||||
<Directory Name="MISS" Id="MISSLogFolder" />
|
<Directory Name="MISS" Id="MISSLogFolder" />
|
||||||
@@ -32,22 +52,31 @@
|
|||||||
|
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
|
<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">
|
<Component Id="MISS">
|
||||||
<File Source="$(var.miss.TargetPath)" KeyPath="yes">
|
<File Source="$(var.miss.TargetPath)" KeyPath="yes">
|
||||||
<fire:FirewallException Id="MISSFwException"
|
<fire:FirewallException Id="MISSFwException"
|
||||||
Scope="any"
|
Scope="any"
|
||||||
Name="Moonlight Internet Streaming Service"/>
|
Name="Moonlight Internet Streaming Service"/>
|
||||||
</File>
|
</File>
|
||||||
<CreateFolder Directory="MISSLogFolder" />
|
<CreateFolder Directory="MISSLogFolder">
|
||||||
|
<util:PermissionEx User="NT SERVICE\MISS" GenericAll="yes" />
|
||||||
|
</CreateFolder>
|
||||||
<ServiceInstall Type="ownProcess"
|
<ServiceInstall Type="ownProcess"
|
||||||
Name="MISS"
|
Name="MISS"
|
||||||
DisplayName="Moonlight Internet Streaming Service"
|
DisplayName="Moonlight Internet Streaming Service"
|
||||||
Description="Automatically manages router port forwarding rules required to use Moonlight over the Internet"
|
Description="Automatically manages router port forwarding rules required to use Moonlight over the Internet"
|
||||||
Start="auto"
|
Start="auto"
|
||||||
Account="LocalSystem"
|
Account="NT AUTHORITY\LocalService"
|
||||||
ErrorControl="ignore"
|
ErrorControl="ignore"
|
||||||
Interactive="no">
|
Interactive="no">
|
||||||
<ServiceConfig DelayedAutoStart="no" OnInstall="yes" OnReinstall="yes" />
|
<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>
|
||||||
@@ -58,6 +87,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"
|
||||||
@@ -69,5 +140,14 @@
|
|||||||
<RegistryValue Root="HKCU" Key="Software\Moonlight Internet Streaming Tester" Name="Installed" Type="integer" Value="1" KeyPath="yes" />
|
<RegistryValue Root="HKCU" Key="Software\Moonlight Internet Streaming Tester" Name="Installed" Type="integer" Value="1" KeyPath="yes" />
|
||||||
</Component>
|
</Component>
|
||||||
</ComponentGroup>
|
</ComponentGroup>
|
||||||
|
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<Custom Action="EnableMpsSvc" Before="StartMpsSvc">
|
||||||
|
<![CDATA[MPSSVC_START <> "#2"]]>
|
||||||
|
</Custom>
|
||||||
|
<Custom Action="StartMpsSvc" Before="StopServices">
|
||||||
|
<![CDATA[MPSSVC_START <> "#2"]]>
|
||||||
|
</Custom>
|
||||||
|
</InstallExecuteSequence>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
</Wix>
|
</Wix>
|
||||||
|
|||||||
@@ -48,6 +48,10 @@
|
|||||||
<HintPath>$(WixExtDir)\WixUIExtension.dll</HintPath>
|
<HintPath>$(WixExtDir)\WixUIExtension.dll</HintPath>
|
||||||
<Name>WixUIExtension</Name>
|
<Name>WixUIExtension</Name>
|
||||||
</WixExtension>
|
</WixExtension>
|
||||||
|
<WixExtension Include="WixUtilExtension">
|
||||||
|
<HintPath>$(WixExtDir)\WixUtilExtension.dll</HintPath>
|
||||||
|
<Name>WixUtilExtension</Name>
|
||||||
|
</WixExtension>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(WixTargetsPath)" Condition=" '$(WixTargetsPath)' != '' " />
|
<Import Project="$(WixTargetsPath)" Condition=" '$(WixTargetsPath)' != '' " />
|
||||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets" Condition=" '$(WixTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets') " />
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets" Condition=" '$(WixTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets') " />
|
||||||
|
|||||||
+342
-134
@@ -12,6 +12,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "relay.h"
|
||||||
#include "..\version.h"
|
#include "..\version.h"
|
||||||
|
|
||||||
#pragma comment(lib, "miniupnpc.lib")
|
#pragma comment(lib, "miniupnpc.lib")
|
||||||
@@ -31,13 +32,12 @@ bool getHopsIP4(IN_ADDR* hopAddress, int* hopAddressCount);
|
|||||||
struct UPNPDev* getUPnPDevicesByAddress(IN_ADDR address);
|
struct UPNPDev* getUPnPDevicesByAddress(IN_ADDR address);
|
||||||
bool PCPMapPort(PSOCKADDR_STORAGE localAddr, int localAddrLen, PSOCKADDR_STORAGE pcpAddr, int pcpAddrLen, int proto, int port, bool enable, bool indefinite);
|
bool PCPMapPort(PSOCKADDR_STORAGE localAddr, int localAddrLen, PSOCKADDR_STORAGE pcpAddr, int pcpAddrLen, int proto, int port, bool enable, bool indefinite);
|
||||||
|
|
||||||
#define NL "\n"
|
|
||||||
|
|
||||||
#define SERVICE_NAME "MISS"
|
#define SERVICE_NAME "MISS"
|
||||||
#define UPNP_SERVICE_NAME "Moonlight"
|
#define UPNP_SERVICE_NAME "Moonlight"
|
||||||
#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;
|
||||||
@@ -53,9 +53,12 @@ static struct port_entry {
|
|||||||
{IPPROTO_UDP, 48010}
|
{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 intClient[16];
|
||||||
char intPort[6];
|
char intPort[6];
|
||||||
@@ -94,15 +97,41 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
|||||||
intClient, intPort, desc, enabled, leaseDuration);
|
intClient, intPort, desc, enabled, leaseDuration);
|
||||||
if (err == 714) {
|
if (err == 714) {
|
||||||
// NoSuchEntryInArray
|
// NoSuchEntryInArray
|
||||||
printf("NOT FOUND" NL);
|
printf("NOT FOUND\n");
|
||||||
|
|
||||||
|
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\n");
|
||||||
|
|
||||||
|
// 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) {
|
else if (err == UPNPCOMMAND_SUCCESS) {
|
||||||
if (!strcmp(intClient, myAddr) && !strcmp(desc, myDesc)) {
|
// Some routers change the description, so we can't check that here
|
||||||
|
if (!strcmp(intClient, myAddr)) {
|
||||||
if (atoi(leaseDuration) == 0) {
|
if (atoi(leaseDuration) == 0) {
|
||||||
printf("OK (Permanent)" NL);
|
printf("OK (Static, Internal port: %s)\n", intPort);
|
||||||
|
|
||||||
|
// If we have an existing permanent mapping, we can just leave it alone.
|
||||||
|
if (enable) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("OK (%s seconds remaining)" NL, leaseDuration);
|
printf("OK (%s seconds remaining, Internal port: %s)\n", leaseDuration, intPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're just validating, we found an entry, so we're done.
|
||||||
|
if (validationPass) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!enable) {
|
if (!enable) {
|
||||||
@@ -110,17 +139,22 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
|||||||
printf("Deleting UPnP mapping for %s %s -> %s...", protoStr, portStr, myAddr);
|
printf("Deleting UPnP mapping for %s %s -> %s...", protoStr, portStr, myAddr);
|
||||||
err = UPNP_DeletePortMapping(urls->controlURL, data->first.servicetype, portStr, protoStr, nullptr);
|
err = UPNP_DeletePortMapping(urls->controlURL, data->first.servicetype, portStr, protoStr, nullptr);
|
||||||
if (err == UPNPCOMMAND_SUCCESS) {
|
if (err == UPNPCOMMAND_SUCCESS) {
|
||||||
printf("OK" NL);
|
printf("OK\n");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("ERROR %d" NL, err);
|
printf("ERROR %d\n", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("CONFLICT: %s %s" NL, intClient, desc);
|
printf("CONFLICT: %s %s\n", 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
|
// 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,
|
// for security reasons, but we will give it a try anyway. If GameStream is not enabled,
|
||||||
@@ -130,21 +164,25 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
|||||||
printf("Trying to delete conflicting UPnP mapping for %s %s -> %s...", protoStr, portStr, intClient);
|
printf("Trying to delete conflicting UPnP mapping for %s %s -> %s...", protoStr, portStr, intClient);
|
||||||
err = UPNP_DeletePortMapping(urls->controlURL, data->first.servicetype, portStr, protoStr, nullptr);
|
err = UPNP_DeletePortMapping(urls->controlURL, data->first.servicetype, portStr, protoStr, nullptr);
|
||||||
if (err == UPNPCOMMAND_SUCCESS) {
|
if (err == UPNPCOMMAND_SUCCESS) {
|
||||||
printf("OK" NL);
|
printf("OK\n");
|
||||||
}
|
}
|
||||||
else if (err == 606) {
|
else if (err == 606) {
|
||||||
printf("UNAUTHORIZED" NL);
|
printf("UNAUTHORIZED\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("ERROR %d" NL, err);
|
printf("ERROR %d\n", err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("ERROR %d (%s)" NL, err, strupnperror(err));
|
printf("ERROR %d (%s)\n", 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
|
// Bail if GameStream is disabled
|
||||||
@@ -159,28 +197,45 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
|||||||
err = UPNP_AddPortMapping(
|
err = UPNP_AddPortMapping(
|
||||||
urls->controlURL, data->first.servicetype, portStr,
|
urls->controlURL, data->first.servicetype, portStr,
|
||||||
portStr, myAddr, myDesc, protoStr, nullptr, leaseDuration);
|
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(
|
err = UPNP_AddPortMapping(
|
||||||
urls->controlURL, data->first.servicetype, portStr,
|
urls->controlURL, data->first.servicetype, portStr,
|
||||||
portStr, myAddr, myDesc, protoStr, nullptr, "0");
|
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) {
|
if (err == UPNPCOMMAND_SUCCESS) {
|
||||||
printf("OK" NL);
|
printf("OK\n");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("ERROR %d (%s)" NL, err, strupnperror(err));
|
printf("ERROR %d (%s)\n", err, strupnperror(err));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
@@ -189,22 +244,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\n", 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\n", 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;
|
||||||
@@ -215,6 +282,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +292,8 @@ bool GetIP4OnLinkPrefixLength(char* lanAddressString, int* prefixLength)
|
|||||||
currentAdapter = currentAdapter->Next;
|
currentAdapter = currentAdapter->Next;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("No adapter found with IPv4 address: %s" NL, lanAddressString);
|
printf("No adapter found with IPv4 address: %s\n", lanAddressString);
|
||||||
|
free(addresses);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,31 +308,31 @@ bool UPnPHandleDeviceList(struct UPNPDev* list, bool enable, char* lanAddrOverri
|
|||||||
|
|
||||||
int ret = UPNP_GetValidIGD(list, &urls, &data, localAddress, sizeof(localAddress));
|
int ret = UPNP_GetValidIGD(list, &urls, &data, localAddress, sizeof(localAddress));
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
printf("No UPnP device found!" NL);
|
printf("No UPnP device found!\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (ret == 3) {
|
else if (ret == 3) {
|
||||||
printf("No UPnP IGD found!" NL);
|
printf("No UPnP IGD found!\n");
|
||||||
FreeUPNPUrls(&urls);
|
FreeUPNPUrls(&urls);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (ret == 1) {
|
else if (ret == 1) {
|
||||||
printf("Found a connected UPnP IGD" NL);
|
printf("Found a connected UPnP IGD (%s)\n", urls.rootdescURL);
|
||||||
}
|
}
|
||||||
else if (ret == 2) {
|
else if (ret == 2) {
|
||||||
printf("Found a disconnected UPnP IGD (!)" NL);
|
printf("Found a disconnected (!) UPnP IGD (%s)\n", urls.rootdescURL);
|
||||||
|
|
||||||
// Even if we are able to add forwarding entries, go ahead and try NAT-PMP
|
// Even if we are able to add forwarding entries, go ahead and try NAT-PMP
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("UPNP_GetValidIGD() failed: %d" NL, ret);
|
printf("UPNP_GetValidIGD() failed: %d\n", ret);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, wanAddr);
|
ret = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, wanAddr);
|
||||||
if (ret == UPNPCOMMAND_SUCCESS) {
|
if (ret == UPNPCOMMAND_SUCCESS) {
|
||||||
printf("UPnP IGD WAN address is: %s" NL, wanAddr);
|
printf("UPnP IGD WAN address is: %s\n", wanAddr);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Empty string
|
// Empty string
|
||||||
@@ -278,8 +347,9 @@ bool UPnPHandleDeviceList(struct UPNPDev* list, bool enable, char* lanAddrOverri
|
|||||||
portMappingInternalAddress = localAddress;
|
portMappingInternalAddress = localAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create the port mappings
|
||||||
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
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;
|
success = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,13 +373,32 @@ bool UPnPHandleDeviceList(struct UPNPDev* list, bool enable, char* lanAddrOverri
|
|||||||
char broadcastAddrStr[128];
|
char broadcastAddrStr[128];
|
||||||
inet_ntop(AF_INET, &broadcastAddr, broadcastAddrStr, sizeof(broadcastAddrStr));
|
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 {
|
else {
|
||||||
// When we're mapping the WOL ports upstream of our router, we map directly to
|
// 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).
|
// 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\n");
|
||||||
|
|
||||||
|
// 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\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,13 +432,13 @@ 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);
|
||||||
int err = sendnewportmappingrequest(natpmp, natPmpProto, port, enable ? port : 0, lifetime);
|
int err = sendnewportmappingrequest(natpmp, natPmpProto, port, enable ? port : 0, lifetime);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
printf("ERROR %d" NL, err);
|
printf("ERROR %d\n", err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,7 +454,7 @@ bool NATPMPMapPort(natpmp_t* natpmp, int proto, int port, bool enable, bool inde
|
|||||||
err = getnatpmprequesttimeout(natpmp, &timeout);
|
err = getnatpmprequesttimeout(natpmp, &timeout);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
assert(err == 0);
|
assert(err == 0);
|
||||||
printf("WAIT FAILED: %d" NL, err);
|
printf("WAIT FAILED: %d\n", err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,31 +464,22 @@ bool NATPMPMapPort(natpmp_t* natpmp, int proto, int port, bool enable, bool inde
|
|||||||
} while (err == NATPMP_TRYAGAIN);
|
} while (err == NATPMP_TRYAGAIN);
|
||||||
|
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
printf("FAILED %d" NL, err);
|
printf("FAILED %d\n", err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (response.pnu.newportmapping.lifetime == 0 && !enable) {
|
else if (response.pnu.newportmapping.lifetime == 0 && !enable) {
|
||||||
printf("DELETED" NL);
|
printf("DELETED\n");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (response.pnu.newportmapping.mappedpublicport != port) {
|
else if (response.pnu.newportmapping.mappedpublicport != port) {
|
||||||
printf("CONFLICT" NL);
|
printf("CONFLICT\n");
|
||||||
|
|
||||||
// Some buggy routers (Untangle) will change the *internal* port when
|
|
||||||
// adjusting a port mapping request that collides. This is why we also
|
|
||||||
// pass privateport back from the response and not from the port we originally
|
|
||||||
// asked for. Warn in this case.
|
|
||||||
if (response.pnu.newportmapping.privateport != port) {
|
|
||||||
printf("Buggy router changed the internal port when handling NAT-PMP conflict! (%d -> %d)" NL,
|
|
||||||
port, response.pnu.newportmapping.privateport);
|
|
||||||
}
|
|
||||||
|
|
||||||
// It couldn't assign us the external port we requested and gave us an alternate external port.
|
// It couldn't assign us the external port we requested and gave us an alternate external port.
|
||||||
// We can't use this alternate mapping, so immediately release it.
|
// We can't use this alternate mapping, so immediately release it.
|
||||||
printf("Deleting unwanted NAT-PMP mapping for %s %d...", proto == IPPROTO_TCP ? "TCP" : "UDP", response.pnu.newportmapping.mappedpublicport);
|
printf("Deleting unwanted NAT-PMP mapping for %s %d...", proto == IPPROTO_TCP ? "TCP" : "UDP", response.pnu.newportmapping.mappedpublicport);
|
||||||
err = sendnewportmappingrequest(natpmp, natPmpProto, response.pnu.newportmapping.privateport, 0, 0);
|
err = sendnewportmappingrequest(natpmp, natPmpProto, response.pnu.newportmapping.privateport, 0, 0);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
printf("ERROR %d" NL, err);
|
printf("ERROR %d\n", err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -413,7 +493,7 @@ bool NATPMPMapPort(natpmp_t* natpmp, int proto, int port, bool enable, bool inde
|
|||||||
err = getnatpmprequesttimeout(natpmp, &timeout);
|
err = getnatpmprequesttimeout(natpmp, &timeout);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
assert(err == 0);
|
assert(err == 0);
|
||||||
printf("WAIT FAILED: %d" NL, err);
|
printf("WAIT FAILED: %d\n", err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,17 +503,17 @@ bool NATPMPMapPort(natpmp_t* natpmp, int proto, int port, bool enable, bool inde
|
|||||||
} while (err == NATPMP_TRYAGAIN);
|
} while (err == NATPMP_TRYAGAIN);
|
||||||
|
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
printf("OK" NL);
|
printf("OK\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("FAILED %d" NL, err);
|
printf("FAILED %d\n", err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("OK (%d seconds remaining)" NL, response.pnu.newportmapping.lifetime);
|
printf("OK (%d seconds remaining)\n", response.pnu.newportmapping.lifetime);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -455,17 +535,47 @@ bool IsGameStreamEnabled()
|
|||||||
error = RegQueryValueExA(key, "EnableStreaming", nullptr, nullptr, (LPBYTE)&enabled, &len);
|
error = RegQueryValueExA(key, "EnableStreaming", nullptr, nullptr, (LPBYTE)&enabled, &len);
|
||||||
RegCloseKey(key);
|
RegCloseKey(key);
|
||||||
if (error != ERROR_SUCCESS) {
|
if (error != ERROR_SUCCESS) {
|
||||||
printf("RegQueryValueExA() failed: %d" NL, error);
|
printf("RegQueryValueExA() failed: %d\n", error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (!enabled) {
|
|
||||||
printf("GameStream is OFF!" NL);
|
return enabled != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsAlternateHostSoftwareRunning()
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
PMIB_TCPTABLE tcp_table = nullptr;
|
||||||
|
ULONG table_size = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
// Query all open TCPv4 sockets
|
||||||
|
err = GetTcpTable(tcp_table, &table_size, false);
|
||||||
|
if (err == ERROR_INSUFFICIENT_BUFFER) {
|
||||||
|
free(tcp_table);
|
||||||
|
tcp_table = (PMIB_TCPTABLE)malloc(table_size);
|
||||||
|
}
|
||||||
|
} while (err == ERROR_INSUFFICIENT_BUFFER);
|
||||||
|
|
||||||
|
if (!tcp_table || err != NO_ERROR) {
|
||||||
|
printf("GetTcpTable() failed: %d\n", err);
|
||||||
|
free(tcp_table);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
printf("GameStream is ON!" NL);
|
bool result = false;
|
||||||
return true;
|
for (DWORD i = 0; i < tcp_table->dwNumEntries; i++) {
|
||||||
|
auto& entry = tcp_table->table[i];
|
||||||
|
|
||||||
|
// Look for TCP 47989 port in the listening state
|
||||||
|
if (entry.dwLocalPort == _byteswap_ushort(47989) && entry.dwState == MIB_TCP_STATE_LISTEN) {
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(tcp_table);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdatePortMappingsForTarget(bool enable, char* targetAddressIP4, char* internalAddressIP4, char* upstreamAddressIP4)
|
void UpdatePortMappingsForTarget(bool enable, char* targetAddressIP4, char* internalAddressIP4, char* upstreamAddressIP4)
|
||||||
@@ -476,18 +586,18 @@ void UpdatePortMappingsForTarget(bool enable, char* targetAddressIP4, char* inte
|
|||||||
char upstreamAddrNatPmp[128] = {};
|
char upstreamAddrNatPmp[128] = {};
|
||||||
char upstreamAddrUPnP[128] = {};
|
char upstreamAddrUPnP[128] = {};
|
||||||
|
|
||||||
printf("Starting port mapping update on %s to %s..." NL,
|
printf("Starting port mapping update on %s to %s...\n",
|
||||||
targetAddressIP4 ? targetAddressIP4 : "default gateway",
|
targetAddressIP4 ? targetAddressIP4 : "default gateway",
|
||||||
internalAddressIP4 ? internalAddressIP4 : "local machine");
|
internalAddressIP4 ? internalAddressIP4 : "local machine");
|
||||||
|
|
||||||
int natPmpErr = initnatpmp(&natpmp, targetAddressIP4 ? 1 : 0, targetAddressIP4 ? inet_addr(targetAddressIP4) : 0);
|
int natPmpErr = initnatpmp(&natpmp, targetAddressIP4 ? 1 : 0, targetAddressIP4 ? inet_addr(targetAddressIP4) : 0);
|
||||||
if (natPmpErr != 0) {
|
if (natPmpErr != 0) {
|
||||||
printf("initnatpmp() failed: %d" NL, natPmpErr);
|
printf("initnatpmp() failed: %d\n", natPmpErr);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
natPmpErr = sendpublicaddressrequest(&natpmp);
|
natPmpErr = sendpublicaddressrequest(&natpmp);
|
||||||
if (natPmpErr < 0) {
|
if (natPmpErr < 0) {
|
||||||
printf("sendpublicaddressrequest() failed: %d" NL, natPmpErr);
|
printf("sendpublicaddressrequest() failed: %d\n", natPmpErr);
|
||||||
closenatpmp(&natpmp);
|
closenatpmp(&natpmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -501,7 +611,7 @@ void UpdatePortMappingsForTarget(bool enable, char* targetAddressIP4, char* inte
|
|||||||
if (targetAddressIP4 == nullptr) {
|
if (targetAddressIP4 == nullptr) {
|
||||||
// If we have no target, use discovery to find the first hop
|
// If we have no target, use discovery to find the first hop
|
||||||
ipv4Devs = upnpDiscoverAll(UPNP_DISCOVERY_DELAY_MS, nullptr, nullptr, UPNP_LOCAL_PORT_ANY, 0, 2, &upnpErr);
|
ipv4Devs = upnpDiscoverAll(UPNP_DISCOVERY_DELAY_MS, nullptr, nullptr, UPNP_LOCAL_PORT_ANY, 0, 2, &upnpErr);
|
||||||
printf("UPnP IPv4 IGD discovery completed with error code: %d" NL, upnpErr);
|
printf("UPnP IPv4 IGD discovery completed with error code: %d\n", upnpErr);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// We have a specified target, so do discovery against that directly (may be outside our subnet in case of double-NAT)
|
// We have a specified target, so do discovery against that directly (may be outside our subnet in case of double-NAT)
|
||||||
@@ -510,23 +620,29 @@ void UpdatePortMappingsForTarget(bool enable, char* targetAddressIP4, char* inte
|
|||||||
ipv4Devs = getUPnPDevicesByAddress(addr);
|
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\n");
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
// Use the delay of discovery to also allow the NAT-PMP endpoint time to respond
|
// Use the delay of discovery to also allow the NAT-PMP endpoint time to respond
|
||||||
if (natPmpErr >= 0) {
|
if (natPmpErr >= 0) {
|
||||||
natpmpresp_t response;
|
natpmpresp_t response;
|
||||||
natPmpErr = readnatpmpresponseorretry(&natpmp, &response);
|
natPmpErr = readnatpmpresponseorretry(&natpmp, &response);
|
||||||
if (natPmpErr == 0) {
|
if (natPmpErr == 0) {
|
||||||
inet_ntop(AF_INET, &response.pnu.publicaddress.addr, upstreamAddrNatPmp, sizeof(upstreamAddrNatPmp));
|
inet_ntop(AF_INET, &response.pnu.publicaddress.addr, upstreamAddrNatPmp, sizeof(upstreamAddrNatPmp));
|
||||||
printf("NAT-PMP upstream address is: %s" NL, upstreamAddrNatPmp);
|
printf("NAT-PMP upstream address is: %s\n", upstreamAddrNatPmp);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("NAT-PMP public address request failed: %d" NL, natPmpErr);
|
printf("NAT-PMP public address request failed: %d\n", natPmpErr);
|
||||||
closenatpmp(&natpmp);
|
closenatpmp(&natpmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't try NAT-PMP if UPnP succeeds
|
// Don't try NAT-PMP if UPnP succeeds
|
||||||
if (UPnPHandleDeviceList(ipv4Devs, enable, internalAddressIP4, upstreamAddrUPnP)) {
|
if (UPnPHandleDeviceList(ipv4Devs, enable, internalAddressIP4, upstreamAddrUPnP)) {
|
||||||
printf("UPnP IPv4 port mapping successful" NL);
|
printf("UPnP IPv4 port mapping successful\n");
|
||||||
if (enable) {
|
if (enable) {
|
||||||
// We still want to try NAT-PMP if we're removing
|
// We still want to try NAT-PMP if we're removing
|
||||||
// rules to ensure any NAT-PMP rules get cleaned up
|
// rules to ensure any NAT-PMP rules get cleaned up
|
||||||
@@ -545,7 +661,7 @@ void UpdatePortMappingsForTarget(bool enable, char* targetAddressIP4, char* inte
|
|||||||
// if we created the rules we'd be deleting. Since we don't have that, we can't
|
// if we created the rules we'd be deleting. Since we don't have that, we can't
|
||||||
// safely remove mappings that could be shared by another machine behind a double NAT.
|
// safely remove mappings that could be shared by another machine behind a double NAT.
|
||||||
if (!enable && targetAddressIP4 != nullptr) {
|
if (!enable && targetAddressIP4 != nullptr) {
|
||||||
printf("Not removing upstream NAT-PMP mappings on non-default gateway device" NL);
|
printf("Not removing upstream NAT-PMP mappings on non-default gateway device\n");
|
||||||
tryNatPmp = false;
|
tryNatPmp = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -581,7 +697,7 @@ void UpdatePortMappingsForTarget(bool enable, char* targetAddressIP4, char* inte
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
printf("NAT-PMP IPv4 port mapping successful" NL);
|
printf("NAT-PMP IPv4 port mapping successful\n");
|
||||||
|
|
||||||
// Always try all possibilities when disabling to ensure
|
// Always try all possibilities when disabling to ensure
|
||||||
// we completely clean up
|
// we completely clean up
|
||||||
@@ -613,7 +729,7 @@ void UpdatePortMappingsForTarget(bool enable, char* targetAddressIP4, char* inte
|
|||||||
targetAddr.sin_addr.S_un.S_addr = route.dwForwardNextHop;
|
targetAddr.sin_addr.S_un.S_addr = route.dwForwardNextHop;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("GetBestRoute() failed: %d" NL, error);
|
printf("GetBestRoute() failed: %d\n", error);
|
||||||
goto Exit;
|
goto Exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -641,22 +757,22 @@ void UpdatePortMappingsForTarget(bool enable, char* targetAddressIP4, char* inte
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
printf("PCP IPv4 port mapping successful" NL);
|
printf("PCP IPv4 port mapping successful\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Exit:
|
Exit:
|
||||||
// Write this at the end to avoid clobbering an input parameter
|
// Write this at the end to avoid clobbering an input parameter
|
||||||
if (upstreamAddrNatPmp[0] != 0 && inet_addr(upstreamAddrNatPmp) != 0) {
|
if (upstreamAddrNatPmp[0] != 0 && inet_addr(upstreamAddrNatPmp) != 0) {
|
||||||
printf("Using NAT-PMP upstream IPv4 address: %s" NL, upstreamAddrNatPmp);
|
printf("Using NAT-PMP upstream IPv4 address: %s\n", upstreamAddrNatPmp);
|
||||||
strcpy(upstreamAddressIP4, upstreamAddrNatPmp);
|
strcpy(upstreamAddressIP4, upstreamAddrNatPmp);
|
||||||
}
|
}
|
||||||
else if (upstreamAddrUPnP[0] != 0 && inet_addr(upstreamAddrUPnP) != 0) {
|
else if (upstreamAddrUPnP[0] != 0 && inet_addr(upstreamAddrUPnP) != 0) {
|
||||||
printf("Using UPnP upstream IPv4 address: %s" NL, upstreamAddrUPnP);
|
printf("Using UPnP upstream IPv4 address: %s\n", upstreamAddrUPnP);
|
||||||
strcpy(upstreamAddressIP4, upstreamAddrUPnP);
|
strcpy(upstreamAddressIP4, upstreamAddrUPnP);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("No valid upstream IPv4 address found!" NL);
|
printf("No valid upstream IPv4 address found!\n");
|
||||||
upstreamAddressIP4[0] = 0;
|
upstreamAddressIP4[0] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -692,12 +808,12 @@ void UpdatePortMappings(bool enable)
|
|||||||
char upstreamAddrStr[128];
|
char upstreamAddrStr[128];
|
||||||
unsigned long upstreamAddr;
|
unsigned long upstreamAddr;
|
||||||
|
|
||||||
printf("Finding upstream IPv4 hops via traceroute..." NL);
|
printf("Finding upstream IPv4 hops via traceroute...\n");
|
||||||
if (!getHopsIP4(hops, &hopCount)) {
|
if (!getHopsIP4(hops, &hopCount)) {
|
||||||
hopCount = 0;
|
hopCount = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("Found %d hops" NL, hopCount);
|
printf("Found %d hops\n", hopCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// getHopsIP4() already skips the default gateway, so 0
|
// getHopsIP4() already skips the default gateway, so 0
|
||||||
@@ -709,10 +825,10 @@ void UpdatePortMappings(bool enable)
|
|||||||
while (upstreamAddrStr[0] != 0 && (upstreamAddr = inet_addr(upstreamAddrStr)) != 0) {
|
while (upstreamAddrStr[0] != 0 && (upstreamAddr = inet_addr(upstreamAddrStr)) != 0) {
|
||||||
// We got an upstream address. Let's check if this is a NAT
|
// We got an upstream address. Let's check if this is a NAT
|
||||||
if (IsLikelyNAT(upstreamAddr)) {
|
if (IsLikelyNAT(upstreamAddr)) {
|
||||||
printf("Upstream address %s is likely a NAT" NL, upstreamAddrStr);
|
printf("Upstream address %s is likely a NAT\n", upstreamAddrStr);
|
||||||
|
|
||||||
if (nextHopIndex >= hopCount) {
|
if (nextHopIndex >= hopCount) {
|
||||||
printf("Traceroute didn't reach this hop! Aborting!" NL);
|
printf("Traceroute didn't reach this hop! Aborting!\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -726,7 +842,7 @@ void UpdatePortMappings(bool enable)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// If we reach a proper public IP address, we're done
|
// If we reach a proper public IP address, we're done
|
||||||
printf("Reached the Internet at hop %d" NL, nextHopIndex);
|
printf("Reached the Internet at hop %d\n", nextHopIndex);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -742,60 +858,105 @@ 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 timeString[MAX_PATH + 1] = {};
|
||||||
char currentLogFilePath[MAX_PATH + 1];
|
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];
|
||||||
|
|
||||||
// Delete the old log file
|
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-old.log", oldLogFilePath, sizeof(oldLogFilePath));
|
||||||
DeleteFileA(oldLogFilePath);
|
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-current.log", currentLogFilePath, sizeof(currentLogFilePath));
|
||||||
|
|
||||||
// Rotate the current to the old log file
|
// Close the existing stdout handle. This is important because otherwise
|
||||||
MoveFileA(currentLogFilePath, oldLogFilePath);
|
// 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
|
||||||
|
if (freopen(currentLogFilePath, "w", stdout) == NULL) {
|
||||||
|
// If we couldn't create a log file, just redirect stdout to NUL.
|
||||||
|
// We have to open _something_ or printf() will crash.
|
||||||
|
freopen("NUL", "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 "\n");
|
||||||
|
|
||||||
|
// Print the current time
|
||||||
|
GetSystemTime(&time);
|
||||||
|
GetTimeFormatA(LOCALE_SYSTEM_DEFAULT, 0, &time, "hh':'mm':'ss tt", timeString, ARRAYSIZE(timeString));
|
||||||
|
printf("The current UTC time is: %s\n", timeString);
|
||||||
}
|
}
|
||||||
|
|
||||||
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_NAME | 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 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 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[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
|
// Create the thread to watch for GameStream state changes
|
||||||
CreateThread(nullptr, 0, GameStreamStateChangeThread, gsChangeEvent, 0, nullptr);
|
CreateThread(nullptr, 0, GameStreamStateChangeThread, gsChangeEvent, 0, nullptr);
|
||||||
@@ -807,34 +968,66 @@ int Run()
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
ResetEvent(gsChangeEvent);
|
ResetEvent(gsChangeEvent);
|
||||||
ResetEvent(ifaceChangeEvent);
|
ResetEvent(ifaceChangeEvent);
|
||||||
UpdatePortMappings(IsGameStreamEnabled());
|
|
||||||
|
bool gameStreamEnabled = IsGameStreamEnabled();
|
||||||
|
|
||||||
|
if (gameStreamEnabled) {
|
||||||
|
printf("GFE GameStream is ON!\n");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("GFE GameStream is OFF!\n");
|
||||||
|
|
||||||
|
if (IsAlternateHostSoftwareRunning()) {
|
||||||
|
printf("Sunshine is RUNNING!\n");
|
||||||
|
gameStreamEnabled = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("Sunshine is NOT RUNNING!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
// Refresh when half the duration is expired or if an IP interface
|
||||||
// change event occurs.
|
// change event occurs.
|
||||||
printf("Going to sleep..." NL);
|
printf("Going to sleep...\n");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
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\n",
|
||||||
(GetTickCount64() - beforeSleepTime) / 1000);
|
(GetTickCount64() - beforeSleepTime) / 1000);
|
||||||
|
|
||||||
// Wait a little bit for the interface to settle down (DHCP, RA, etc)
|
// Wait a little bit for the interface to settle down (DHCP, RA, etc)
|
||||||
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\n",
|
||||||
(GetTickCount64() - beforeSleepTime) / 1000);
|
(GetTickCount64() - beforeSleepTime) / 1000);
|
||||||
}
|
}
|
||||||
|
else if (ret == WAIT_OBJECT_0 + 2) {
|
||||||
|
printf("Woke up for stop notification\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
ResetLogFile();
|
ResetLogFile(standaloneExe);
|
||||||
|
|
||||||
printf("Woke up for periodic refresh" NL);
|
printf("Woke up for periodic refresh\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -852,14 +1045,21 @@ HandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpConte
|
|||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
|
|
||||||
case SERVICE_CONTROL_STOP:
|
case SERVICE_CONTROL_STOP:
|
||||||
|
// Stop future port mapping updates
|
||||||
|
SetEvent(s_StopEvent);
|
||||||
|
|
||||||
ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
||||||
ServiceStatus.dwControlsAccepted = 0;
|
ServiceStatus.dwControlsAccepted = 0;
|
||||||
|
ServiceStatus.dwWaitHint = 120 * 1000; // 2 minutes
|
||||||
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
|
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
|
||||||
|
|
||||||
|
// Remove existing port mappings
|
||||||
|
EnterCriticalSection(&s_PortMappingUpdateLock);
|
||||||
printf("Removing UPnP/NAT-PMP/PCP rules after service stop request\n");
|
printf("Removing UPnP/NAT-PMP/PCP rules after service stop request\n");
|
||||||
UpdatePortMappings(false);
|
UpdatePortMappings(false);
|
||||||
|
LeaveCriticalSection(&s_PortMappingUpdateLock);
|
||||||
|
|
||||||
printf("The service is stopping\n");
|
printf("The service is stopping now\n");
|
||||||
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
||||||
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
|
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
@@ -877,7 +1077,15 @@ ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv)
|
|||||||
|
|
||||||
ServiceStatusHandle = RegisterServiceCtrlHandlerEx(SERVICE_NAME, HandlerEx, NULL);
|
ServiceStatusHandle = RegisterServiceCtrlHandlerEx(SERVICE_NAME, HandlerEx, NULL);
|
||||||
if (ServiceStatusHandle == NULL) {
|
if (ServiceStatusHandle == NULL) {
|
||||||
fprintf(stderr, "RegisterServiceCtrlHandlerEx() failed: %d" NL, GetLastError());
|
fprintf(stderr, "RegisterServiceCtrlHandlerEx() failed: %d\n", GetLastError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = Initialize();
|
||||||
|
if (err != 0) {
|
||||||
|
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
||||||
|
ServiceStatus.dwWin32ExitCode = err;
|
||||||
|
SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -893,7 +1101,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;
|
||||||
@@ -916,8 +1124,8 @@ int main(int argc, char* argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (argc == 2 && !strcmp(argv[1], "exe")) {
|
if (argc == 2 && !strcmp(argv[1], "exe")) {
|
||||||
Run();
|
Initialize();
|
||||||
return 0;
|
return Run(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return StartServiceCtrlDispatcher(ServiceTable);
|
return StartServiceCtrlDispatcher(ServiceTable);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION VER_VERSION
|
FILEVERSION VER_VERSION
|
||||||
|
PRODUCTVERSION VER_VERSION
|
||||||
BEGIN
|
BEGIN
|
||||||
BLOCK "StringFileInfo"
|
BLOCK "StringFileInfo"
|
||||||
BEGIN
|
BEGIN
|
||||||
@@ -11,6 +12,8 @@ BEGIN
|
|||||||
VALUE "CompanyName", VER_COMPANYNAME_STR
|
VALUE "CompanyName", VER_COMPANYNAME_STR
|
||||||
VALUE "FileDescription", "Moonlight Internet Streaming Service"
|
VALUE "FileDescription", "Moonlight Internet Streaming Service"
|
||||||
VALUE "ProductName", VER_PRODUCTNAME_STR
|
VALUE "ProductName", VER_PRODUCTNAME_STR
|
||||||
|
VALUE "FileVersion", VER_VERSION_STR
|
||||||
|
VALUE "ProductVersion", VER_VERSION_STR
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
|
|
||||||
|
|||||||
+31
-7
@@ -23,32 +23,32 @@
|
|||||||
<ProjectGuid>{B71C4E7F-0D92-4E19-BBD2-D33F0EF879B1}</ProjectGuid>
|
<ProjectGuid>{B71C4E7F-0D92-4E19-BBD2-D33F0EF879B1}</ProjectGuid>
|
||||||
<Keyword>Win32Proj</Keyword>
|
<Keyword>Win32Proj</Keyword>
|
||||||
<RootNamespace>miss</RootNamespace>
|
<RootNamespace>miss</RootNamespace>
|
||||||
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
<CharacterSet>NotSet</CharacterSet>
|
<CharacterSet>NotSet</CharacterSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
<CharacterSet>NotSet</CharacterSet>
|
<CharacterSet>NotSet</CharacterSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
<CharacterSet>NotSet</CharacterSet>
|
<CharacterSet>NotSet</CharacterSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
<CharacterSet>NotSet</CharacterSet>
|
<CharacterSet>NotSet</CharacterSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -93,13 +93,19 @@
|
|||||||
<PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>
|
||||||
</PrecompiledHeaderFile>
|
</PrecompiledHeaderFile>
|
||||||
<AdditionalIncludeDirectories>..\libs\include;..\libs\include\miniupnpc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>..\libs\include;..\libs\include\miniupnpc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||||
|
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||||
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<AdditionalLibraryDirectories>..\libs\x86\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
<AdditionalLibraryDirectories>..\libs\x86\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<CETCompat>true</CETCompat>
|
||||||
</Link>
|
</Link>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>copy /Y "$(SolutionDir)libs\x86\Debug\*" "$(TargetDir)"</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
@@ -111,11 +117,17 @@
|
|||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>
|
||||||
</PrecompiledHeaderFile>
|
</PrecompiledHeaderFile>
|
||||||
|
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||||
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<CETCompat>true</CETCompat>
|
||||||
</Link>
|
</Link>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>copy /Y "$(SolutionDir)libs\x86\Debug\*" "$(TargetDir)"</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
@@ -130,7 +142,8 @@
|
|||||||
<PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>
|
||||||
</PrecompiledHeaderFile>
|
</PrecompiledHeaderFile>
|
||||||
<AdditionalIncludeDirectories>..\libs\include;..\libs\include\miniupnpc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>..\libs\include;..\libs\include\miniupnpc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||||
|
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
@@ -138,7 +151,11 @@
|
|||||||
<OptimizeReferences>true</OptimizeReferences>
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<AdditionalLibraryDirectories>..\libs\x86\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
<AdditionalLibraryDirectories>..\libs\x86\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<CETCompat>true</CETCompat>
|
||||||
</Link>
|
</Link>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>copy /Y "$(SolutionDir)libs\x86\Release\*" "$(TargetDir)"</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
@@ -152,17 +169,23 @@
|
|||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>
|
||||||
</PrecompiledHeaderFile>
|
</PrecompiledHeaderFile>
|
||||||
|
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<CETCompat>true</CETCompat>
|
||||||
</Link>
|
</Link>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>copy /Y "$(SolutionDir)libs\x86\Release\*" "$(TargetDir)"</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="miss.cpp" />
|
<ClCompile Include="miss.cpp" />
|
||||||
<ClCompile Include="pcp.cpp" />
|
<ClCompile Include="pcp.cpp" />
|
||||||
|
<ClCompile Include="relay.cpp" />
|
||||||
<ClCompile Include="tracer.cpp" />
|
<ClCompile Include="tracer.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -170,6 +193,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\version.h" />
|
<ClInclude Include="..\version.h" />
|
||||||
|
<ClInclude Include="relay.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
|||||||
@@ -24,6 +24,9 @@
|
|||||||
<ClCompile Include="pcp.cpp">
|
<ClCompile Include="pcp.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="relay.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="miss.rc">
|
<ResourceCompile Include="miss.rc">
|
||||||
@@ -34,5 +37,8 @@
|
|||||||
<ClInclude Include="..\version.h">
|
<ClInclude Include="..\version.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="relay.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
+5
-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;
|
||||||
|
|
||||||
@@ -244,6 +244,10 @@ bool PCPMapPort(PSOCKADDR_STORAGE localAddr, int localAddrLen, PSOCKADDR_STORAGE
|
|||||||
printf("PCP message truncated: %d\n", bytesRead);
|
printf("PCP message truncated: %d\n", bytesRead);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
else if (resp.hdr.hdr.version != PCP_VERSION) {
|
||||||
|
printf("PCP version mismatch: %x\n", resp.hdr.hdr.version);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
else if (resp.hdr.hdr.opcode != OPCODE_MAP_RESPONSE) {
|
else if (resp.hdr.hdr.opcode != OPCODE_MAP_RESPONSE) {
|
||||||
printf("PCP message type mismatch: %x\n", resp.hdr.hdr.opcode);
|
printf("PCP message type mismatch: %x\n", resp.hdr.hdr.opcode);
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|||||||
+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);
|
||||||
+83
-28
@@ -22,6 +22,55 @@ static const char* k_SsdpSearchFormatString =
|
|||||||
"MX: 5\r\n"
|
"MX: 5\r\n"
|
||||||
"\r\n";
|
"\r\n";
|
||||||
|
|
||||||
|
// Based on logic in https://github.com/miniupnp/miniupnp/blob/master/miniupnpc/minissdpc.c
|
||||||
|
static void
|
||||||
|
parseReply(const char* reply, int size,
|
||||||
|
const char** location, int *locationsize,
|
||||||
|
const char** st, int* stsize,
|
||||||
|
const char** usn, int* usnsize)
|
||||||
|
{
|
||||||
|
int lineStartIdx = 0;
|
||||||
|
int headerEndIdx = 0;
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
switch (reply[i])
|
||||||
|
{
|
||||||
|
case ':':
|
||||||
|
// Stop parsing the header at the first colon, but ignore subsequent colons
|
||||||
|
if (headerEndIdx == 0) {
|
||||||
|
headerEndIdx = i;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
case '\n':
|
||||||
|
if (headerEndIdx != 0) {
|
||||||
|
// Skip the colon and spaces
|
||||||
|
do { headerEndIdx++; } while (reply[headerEndIdx] == ' ');
|
||||||
|
|
||||||
|
// Check if it's one of the values we care about
|
||||||
|
if (!_strnicmp(reply + lineStartIdx, "location:", 9)) {
|
||||||
|
*location = reply + headerEndIdx;
|
||||||
|
*locationsize = i - headerEndIdx;
|
||||||
|
}
|
||||||
|
else if (!_strnicmp(reply + lineStartIdx, "st:", 3)) {
|
||||||
|
*st = reply + headerEndIdx;
|
||||||
|
*stsize = i - headerEndIdx;
|
||||||
|
}
|
||||||
|
else if (!_strnicmp(reply + lineStartIdx, "usn:", 4)) {
|
||||||
|
*usn = reply + headerEndIdx;
|
||||||
|
*usnsize = i - headerEndIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move on to the next header value
|
||||||
|
headerEndIdx = 0;
|
||||||
|
}
|
||||||
|
lineStartIdx = i + 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct UPNPDev* getUPnPDevicesByAddress(IN_ADDR address)
|
struct UPNPDev* getUPnPDevicesByAddress(IN_ADDR address)
|
||||||
{
|
{
|
||||||
SOCKET s;
|
SOCKET s;
|
||||||
@@ -47,6 +96,13 @@ struct UPNPDev* getUPnPDevicesByAddress(IN_ADDR address)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We will be reading all responses at the end, so ensure we have ample buffer space
|
||||||
|
// to allow responses to accumulate without loss.
|
||||||
|
int recvBufferSize = 65535;
|
||||||
|
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)&recvBufferSize, sizeof(recvBufferSize)) == SOCKET_ERROR) {
|
||||||
|
printf("setsockopt() failed: %d\n", WSAGetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
// Send the first search message with HOST set properly
|
// Send the first search message with HOST set properly
|
||||||
chars = snprintf(searchBuffer, ARRAYSIZE(searchBuffer), k_SsdpSearchFormatString, inet_ntoa(address));
|
chars = snprintf(searchBuffer, ARRAYSIZE(searchBuffer), k_SsdpSearchFormatString, inet_ntoa(address));
|
||||||
if (send(s, searchBuffer, chars, 0) == SOCKET_ERROR) {
|
if (send(s, searchBuffer, chars, 0) == SOCKET_ERROR) {
|
||||||
@@ -115,38 +171,37 @@ struct UPNPDev* getUPnPDevicesByAddress(IN_ADDR address)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse the header options
|
// Parse the header options
|
||||||
// SERVER: FreeBSD/11.2-RELEASE-p2 UPnP/1.1 MiniUPnPd/2.0\r\n
|
char* remainder = strtok(nullptr, "");
|
||||||
char* location = nullptr;
|
const char* loc = nullptr;
|
||||||
char* st = nullptr;
|
const char* st = nullptr;
|
||||||
while (char* headerName = strtok(nullptr, "\r\n:")) {
|
const char* usn = ""; // Initialize to empty since it's optional
|
||||||
char* headerValue = strtok(nullptr, "\r");
|
int locSize = 0;
|
||||||
if (headerValue == nullptr) {
|
int stSize = 0;
|
||||||
printf("Unexpected end of SSDP header\n");
|
int usnSize = 0;
|
||||||
break;
|
parseReply(remainder, strlen(remainder),
|
||||||
}
|
&loc, &locSize, &st, &stSize, &usn, &usnSize);
|
||||||
|
|
||||||
// Skip leading spaces
|
if (!loc || locSize == 0 || !st || stSize == 0) {
|
||||||
while (*headerValue == ' ') headerValue++;
|
printf("Required value missing: %d %d\n", locSize, stSize);
|
||||||
|
|
||||||
if (!_stricmp(headerName, "LOCATION")) {
|
|
||||||
location = headerValue;
|
|
||||||
}
|
|
||||||
else if (!_stricmp(headerName, "ST")) {
|
|
||||||
st = headerValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!location || location[0] == 0 || !st || st[0] == 0) {
|
|
||||||
printf("Required value missing: \"%s\" \"%s\"\n", location, st);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UPNPDev* newDev = (struct UPNPDev*)malloc(sizeof(*newDev) + strlen(location) + strlen(st) + 2);
|
struct UPNPDev* newDev = (struct UPNPDev*)malloc(sizeof(*newDev) + usnSize + locSize + stSize + 3);
|
||||||
|
|
||||||
newDev->pNext = deviceList;
|
newDev->pNext = deviceList;
|
||||||
newDev->usn = &newDev->buffer[0]; newDev->buffer[0] = 0;
|
|
||||||
newDev->descURL = strcpy(newDev->usn + strlen(newDev->usn) + 1, location);
|
newDev->usn = &newDev->buffer[0];
|
||||||
newDev->st = strcpy(newDev->descURL + strlen(newDev->descURL) + 1, st);
|
memcpy(newDev->usn, usn, usnSize);
|
||||||
|
newDev->usn[usnSize] = 0;
|
||||||
|
|
||||||
|
newDev->descURL = newDev->usn + usnSize + 1;
|
||||||
|
memcpy(newDev->descURL, loc, locSize);
|
||||||
|
newDev->descURL[locSize] = 0;
|
||||||
|
|
||||||
|
newDev->st = newDev->descURL + locSize + 1;
|
||||||
|
memcpy(newDev->st, st, stSize);
|
||||||
|
newDev->st[stSize] = 0;
|
||||||
|
|
||||||
newDev->scope_id = 0; // IPv6 only
|
newDev->scope_id = 0; // IPv6 only
|
||||||
|
|
||||||
deviceList = newDev;
|
deviceList = newDev;
|
||||||
@@ -170,7 +225,7 @@ bool getHopsIP4(IN_ADDR* hopAddress, int* hopAddressCount)
|
|||||||
char replyBuffer[128];
|
char replyBuffer[128];
|
||||||
};
|
};
|
||||||
|
|
||||||
host = gethostbyname("google.com");
|
host = gethostbyname("moonlight-stream.org");
|
||||||
if (host == nullptr) {
|
if (host == nullptr) {
|
||||||
printf("gethostbyname() failed: %d\n", WSAGetLastError());
|
printf("gethostbyname() failed: %d\n", WSAGetLastError());
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
+1167
-209
@@ -1,23 +1,23 @@
|
|||||||
#define _CRT_RAND_S
|
#include "mist.h"
|
||||||
#define _CRT_SECURE_NO_WARNINGS
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
|
||||||
#include <Windows.h>
|
|
||||||
#include <WinSock2.h>
|
|
||||||
#include <Ws2tcpip.h>
|
#include <Ws2tcpip.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
#include <objbase.h>
|
#include <objbase.h>
|
||||||
|
#include <WinHttp.h>
|
||||||
#include "..\version.h"
|
#include <wtsapi32.h>
|
||||||
|
#include <powerbase.h>
|
||||||
|
#include <VersionHelpers.h>
|
||||||
|
#include <tlhelp32.h>
|
||||||
|
|
||||||
#pragma comment(lib, "miniupnpc.lib")
|
#pragma comment(lib, "miniupnpc.lib")
|
||||||
#pragma comment(lib, "libnatpmp.lib")
|
#pragma comment(lib, "libnatpmp.lib")
|
||||||
#pragma comment(lib, "ws2_32.lib")
|
#pragma comment(lib, "ws2_32.lib")
|
||||||
#pragma comment(lib, "iphlpapi.lib")
|
#pragma comment(lib, "iphlpapi.lib")
|
||||||
|
#pragma comment(lib, "winhttp.lib")
|
||||||
|
#pragma comment(lib, "wtsapi32.lib")
|
||||||
|
#pragma comment(lib, "powrprof.lib")
|
||||||
|
|
||||||
#define MINIUPNP_STATICLIB
|
#define MINIUPNP_STATICLIB
|
||||||
#include <miniupnpc/miniupnpc.h>
|
#include <miniupnpc/miniupnpc.h>
|
||||||
@@ -27,7 +27,9 @@
|
|||||||
#define NATPMP_STATICLIB
|
#define NATPMP_STATICLIB
|
||||||
#include <natpmp.h>
|
#include <natpmp.h>
|
||||||
|
|
||||||
bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN wanAddr);
|
#define LOOPBACK_SERVER_PORT_OFFSET -10000
|
||||||
|
|
||||||
|
#define GAA_INITIAL_SIZE 8192
|
||||||
|
|
||||||
static struct port_entry {
|
static struct port_entry {
|
||||||
int proto;
|
int proto;
|
||||||
@@ -40,8 +42,13 @@ static struct port_entry {
|
|||||||
{IPPROTO_UDP, 47998, true},
|
{IPPROTO_UDP, 47998, true},
|
||||||
{IPPROTO_UDP, 47999, true},
|
{IPPROTO_UDP, 47999, true},
|
||||||
{IPPROTO_UDP, 48000, true},
|
{IPPROTO_UDP, 48000, true},
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// These are not currently used, so let's
|
||||||
|
// avoid testing them for now.
|
||||||
{IPPROTO_UDP, 48002, true},
|
{IPPROTO_UDP, 48002, true},
|
||||||
{IPPROTO_UDP, 48010, true}
|
{IPPROTO_UDP, 48010, true}
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
char logFilePath[MAX_PATH + 1];
|
char logFilePath[MAX_PATH + 1];
|
||||||
@@ -52,49 +59,124 @@ enum MessagePriority {
|
|||||||
MpError
|
MpError
|
||||||
};
|
};
|
||||||
|
|
||||||
void DisplayMessage(const char* message, MessagePriority priority = MpError, bool terminal = true)
|
VOID CALLBACK MsgBoxHelpCallback(LPHELPINFO lpHelpInfo)
|
||||||
{
|
{
|
||||||
printf("%s\n", message);
|
const char* helpUrl = (const char*)lpHelpInfo->dwContextId;
|
||||||
|
|
||||||
|
if (!helpUrl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's recommended to initialize COM before calling ShellExecute()
|
||||||
|
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
||||||
|
ShellExecuteA(nullptr, "open", helpUrl, nullptr, nullptr, SW_SHOWNORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayMessage(const char* message, const char* helpUrl = nullptr, MessagePriority priority = MpError, bool terminal = true)
|
||||||
|
{
|
||||||
|
fprintf(CONSOLE_OUT, "%s\n", message);
|
||||||
|
fprintf(LOG_OUT, "%s\n", message);
|
||||||
|
|
||||||
if (terminal) {
|
if (terminal) {
|
||||||
printf("--------------- MISS LOG -------------------\n");
|
char logPath[MAX_PATH + 1];
|
||||||
|
FILE* f;
|
||||||
|
|
||||||
char missPath[MAX_PATH + 1];
|
fprintf(LOG_OUT, "--------------- CURRENT MISS LOG -------------------\n");
|
||||||
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-current.log", missPath, sizeof(missPath));
|
|
||||||
FILE* f = fopen(missPath, "r");
|
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-current.log", logPath, sizeof(logPath));
|
||||||
|
f = fopen(logPath, "r");
|
||||||
if (f != nullptr) {
|
if (f != nullptr) {
|
||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
while (!feof(f)) {
|
while (!feof(f)) {
|
||||||
int bytesRead = fread(buffer, 1, ARRAYSIZE(buffer), f);
|
int bytesRead = fread(buffer, 1, ARRAYSIZE(buffer), f);
|
||||||
fwrite(buffer, 1, bytesRead, stdout);
|
fwrite(buffer, 1, bytesRead, LOG_OUT);
|
||||||
}
|
}
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("Failed to find MISS log\n");
|
fprintf(LOG_OUT, "Failed to find current MISS log\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
fflush(stdout);
|
fprintf(LOG_OUT, "\n----------------- OLD MISS LOG ---------------------\n");
|
||||||
|
|
||||||
|
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\miss-old.log", logPath, sizeof(logPath));
|
||||||
|
f = fopen(logPath, "r");
|
||||||
|
if (f != nullptr) {
|
||||||
|
char buffer[1024];
|
||||||
|
while (!feof(f)) {
|
||||||
|
int bytesRead = fread(buffer, 1, ARRAYSIZE(buffer), f);
|
||||||
|
fwrite(buffer, 1, bytesRead, LOG_OUT);
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(LOG_OUT, "Failed to find old MISS log\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(LOG_OUT, "--------------- CURRENT GSV6FWD LOG -------------------\n");
|
||||||
|
|
||||||
|
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\GSv6Fwd-current.log", logPath, sizeof(logPath));
|
||||||
|
f = fopen(logPath, "r");
|
||||||
|
if (f != nullptr) {
|
||||||
|
char buffer[1024];
|
||||||
|
while (!feof(f)) {
|
||||||
|
int bytesRead = fread(buffer, 1, ARRAYSIZE(buffer), f);
|
||||||
|
fwrite(buffer, 1, bytesRead, LOG_OUT);
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(LOG_OUT, "Failed to find current GSv6Fwd log\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(LOG_OUT, "\n----------------- OLD GSV6FWD LOG ---------------------\n");
|
||||||
|
|
||||||
|
ExpandEnvironmentStringsA("%ProgramData%\\MISS\\GSv6Fwd-old.log", logPath, sizeof(logPath));
|
||||||
|
f = fopen(logPath, "r");
|
||||||
|
if (f != nullptr) {
|
||||||
|
char buffer[1024];
|
||||||
|
while (!feof(f)) {
|
||||||
|
int bytesRead = fread(buffer, 1, ARRAYSIZE(buffer), f);
|
||||||
|
fwrite(buffer, 1, bytesRead, LOG_OUT);
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(LOG_OUT, "Failed to find old GSv6Fwd log\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fflush(LOG_OUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MSGBOXPARAMSA msgParams;
|
||||||
DWORD flags = MB_OK | MB_TOPMOST | MB_SETFOREGROUND;
|
msgParams.cbSize = sizeof(msgParams);
|
||||||
|
msgParams.hwndOwner = nullptr;
|
||||||
|
msgParams.hInstance = nullptr;
|
||||||
|
msgParams.lpszText = message;
|
||||||
|
msgParams.lpszCaption = "Moonlight Internet Streaming Tester";
|
||||||
|
msgParams.dwStyle = MB_OK | MB_TOPMOST | MB_SETFOREGROUND;
|
||||||
|
if (helpUrl) {
|
||||||
|
msgParams.dwStyle |= MB_HELP;
|
||||||
|
}
|
||||||
switch (priority) {
|
switch (priority) {
|
||||||
case MpInfo:
|
case MpInfo:
|
||||||
flags |= MB_ICONINFORMATION;
|
msgParams.dwStyle |= MB_ICONINFORMATION;
|
||||||
break;
|
break;
|
||||||
case MpWarn:
|
case MpWarn:
|
||||||
flags |= MB_ICONWARNING;
|
msgParams.dwStyle |= MB_ICONWARNING;
|
||||||
break;
|
break;
|
||||||
case MpError:
|
case MpError:
|
||||||
flags |= MB_ICONERROR;
|
msgParams.dwStyle |= MB_ICONERROR;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
MessageBoxA(nullptr, message, "Moonlight Internet Streaming Tester", flags);
|
msgParams.lpfnMsgBoxCallback = MsgBoxHelpCallback;
|
||||||
|
msgParams.dwContextHelpId = (DWORD_PTR)helpUrl;
|
||||||
|
msgParams.dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
|
||||||
|
MessageBoxIndirectA(&msgParams);
|
||||||
|
|
||||||
if (priority != MpInfo && terminal) {
|
if (priority != MpInfo && terminal) {
|
||||||
flags = MB_YESNO | MB_TOPMOST | MB_SETFOREGROUND | MB_ICONINFORMATION;
|
UINT flags = MB_YESNO | MB_TOPMOST | MB_SETFOREGROUND | MB_ICONINFORMATION;
|
||||||
switch (MessageBoxA(nullptr, "Would you like to view the troubleshooting log?",
|
switch (MessageBoxA(nullptr, "Would you like to view the troubleshooting log?\n\nYou will need to provide this log if you ask for help on the Moonlight Discord server.",
|
||||||
"Moonlight Internet Streaming Tester", flags))
|
"Moonlight Internet Streaming Tester", flags))
|
||||||
{
|
{
|
||||||
case IDYES:
|
case IDYES:
|
||||||
@@ -106,7 +188,78 @@ void DisplayMessage(const char* message, MessagePriority priority = MpError, boo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsGameStreamEnabled()
|
bool ExecuteCommand(PCSTR command, PCHAR outputBuffer, DWORD outputBufferLength)
|
||||||
|
{
|
||||||
|
SECURITY_ATTRIBUTES attribs;
|
||||||
|
STARTUPINFO si;
|
||||||
|
PROCESS_INFORMATION pi;
|
||||||
|
HANDLE outReadHandle;
|
||||||
|
DWORD bytesRead = 0;
|
||||||
|
|
||||||
|
ZeroMemory(&si, sizeof(si));
|
||||||
|
si.cb = sizeof(si);
|
||||||
|
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
|
||||||
|
si.wShowWindow = SW_HIDE;
|
||||||
|
|
||||||
|
ZeroMemory(&attribs, sizeof(attribs));
|
||||||
|
attribs.nLength = sizeof(attribs);
|
||||||
|
attribs.bInheritHandle = TRUE;
|
||||||
|
|
||||||
|
if (!CreatePipe(&outReadHandle, &si.hStdOutput, &attribs, 0))
|
||||||
|
{
|
||||||
|
fprintf(LOG_OUT, "CreatePipe() failed: %d\n", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
si.hStdError = si.hStdOutput;
|
||||||
|
|
||||||
|
if (!CreateProcess(NULL, (LPSTR)command, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
|
||||||
|
{
|
||||||
|
fprintf(LOG_OUT, "CreateProcess() failed: %d\n", GetLastError());
|
||||||
|
CloseHandle(si.hStdOutput);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||||
|
|
||||||
|
CloseHandle(si.hStdOutput);
|
||||||
|
CloseHandle(pi.hProcess);
|
||||||
|
CloseHandle(pi.hThread);
|
||||||
|
|
||||||
|
if (!ReadFile(outReadHandle, outputBuffer, outputBufferLength, &bytesRead, NULL))
|
||||||
|
{
|
||||||
|
fprintf(LOG_OUT, "ReadFile() failed: %d\n", GetLastError());
|
||||||
|
CloseHandle(outReadHandle);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputBuffer[bytesRead] = 0;
|
||||||
|
CloseHandle(outReadHandle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsSunshineRunning()
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
HANDLE processSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||||
|
|
||||||
|
PROCESSENTRY32 procEntry;
|
||||||
|
procEntry.dwSize = sizeof(procEntry);
|
||||||
|
Process32First(processSnapshot, &procEntry);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (_stricmp(procEntry.szExeFile, "sunshine.exe") == 0) {
|
||||||
|
ret = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (Process32Next(processSnapshot, &procEntry));
|
||||||
|
|
||||||
|
CloseHandle(processSnapshot);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsGameStreamEnabled(bool sunshineRunning)
|
||||||
{
|
{
|
||||||
DWORD error;
|
DWORD error;
|
||||||
DWORD enabled;
|
DWORD enabled;
|
||||||
@@ -115,8 +268,11 @@ bool IsGameStreamEnabled()
|
|||||||
|
|
||||||
error = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\NVIDIA Corporation\\NvStream", 0, KEY_READ | KEY_WOW64_64KEY, &key);
|
error = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\NVIDIA Corporation\\NvStream", 0, KEY_READ | KEY_WOW64_64KEY, &key);
|
||||||
if (error != ERROR_SUCCESS) {
|
if (error != ERROR_SUCCESS) {
|
||||||
printf("RegOpenKeyEx() failed: %d\n", error);
|
fprintf(LOG_OUT, "RegOpenKeyEx() failed: %d\n", error);
|
||||||
DisplayMessage("GeForce Experience was not detected on this PC. Make sure you're installing this utility on your GeForce GameStream-compatible PC, not the device running Moonlight.");
|
if (!sunshineRunning) {
|
||||||
|
DisplayMessage("Neither GeForce Experience nor Sunshine are running on this PC. Make sure you're installing this utility on your host PC, not the device running Moonlight.",
|
||||||
|
"https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide");
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,37 +282,203 @@ bool IsGameStreamEnabled()
|
|||||||
if (error != ERROR_SUCCESS || !enabled) {
|
if (error != ERROR_SUCCESS || !enabled) {
|
||||||
// GFE may not even write EnableStreaming until the user enables GameStream for the first time
|
// GFE may not even write EnableStreaming until the user enables GameStream for the first time
|
||||||
if (error != ERROR_SUCCESS) {
|
if (error != ERROR_SUCCESS) {
|
||||||
printf("RegQueryValueExA() failed: %d\n", error);
|
fprintf(LOG_OUT, "RegQueryValueExA() failed: %d\n", error);
|
||||||
|
}
|
||||||
|
if (!sunshineRunning) {
|
||||||
|
DisplayMessage("GameStream is not enabled in GeForce Experience. Please open GeForce Experience settings, navigate to the Shield tab, and turn GameStream on.",
|
||||||
|
"https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide");
|
||||||
}
|
}
|
||||||
DisplayMessage("GameStream is not enabled in GeForce Experience. Please open GeForce Experience settings, navigate to the Shield tab, and turn GameStream on.");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("GeForce Experience installed and GameStream is enabled\n");
|
fprintf(LOG_OUT, "GeForce Experience installed and GameStream is enabled\n");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
DWORD sessionCount;
|
||||||
|
DWORD level;
|
||||||
|
bool ret;
|
||||||
|
DWORD activeSessionId = WTSGetActiveConsoleSessionId();
|
||||||
|
|
||||||
|
if (activeSessionId == 0xFFFFFFFF) {
|
||||||
|
fprintf(LOG_OUT, "No active console session detected\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
level = 1;
|
||||||
|
if (!WTSEnumerateSessionsEx(WTS_CURRENT_SERVER_HANDLE, &level, 0, &sessionInfo, &sessionCount)) {
|
||||||
|
fprintf(LOG_OUT, "WTSEnumerateSessionsEx() failed: %d\n", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = false;
|
||||||
|
for (int i = 0; i < sessionCount; i++)
|
||||||
|
{
|
||||||
|
if (sessionInfo[i].SessionId == activeSessionId) {
|
||||||
|
if (sessionInfo[i].pUserName != nullptr) {
|
||||||
|
fprintf(LOG_OUT, "Session %d has active user\n", sessionInfo[i].SessionId);
|
||||||
|
ret = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WTSFreeMemoryExW(WTSTypeSessionInfoLevel1, sessionInfo, sessionCount);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsSleepEnabled()
|
||||||
|
{
|
||||||
|
SYSTEM_POWER_POLICY powerPolicy;
|
||||||
|
|
||||||
|
if (CallNtPowerInformation(SystemPowerPolicyAc, NULL, 0, &powerPolicy, sizeof(powerPolicy)) < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return powerPolicy.IdleTimeout != 0 && powerPolicy.Idle.Action == PowerActionSleep;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsHibernationEnabled()
|
||||||
|
{
|
||||||
|
SYSTEM_POWER_POLICY powerPolicy;
|
||||||
|
|
||||||
|
if (CallNtPowerInformation(SystemPowerPolicyAc, NULL, 0, &powerPolicy, sizeof(powerPolicy)) < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return powerPolicy.DozeS4Timeout != 0 || (powerPolicy.IdleTimeout != 0 && powerPolicy.Idle.Action == PowerActionHibernate);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsLocalNetworkAccessBlocked()
|
||||||
|
{
|
||||||
|
MIB_IPFORWARDROW route;
|
||||||
|
DWORD error;
|
||||||
|
SOCKADDR_IN sin;
|
||||||
|
SOCKET s;
|
||||||
|
|
||||||
|
error = GetBestRoute(0, 0, &route);
|
||||||
|
if (error != NO_ERROR) {
|
||||||
|
fprintf(LOG_OUT, "GetBestRoute() failed: %d\n", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
if (s == INVALID_SOCKET) {
|
||||||
|
fprintf(LOG_OUT, "socket() failed: %d\n", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RtlZeroMemory(&sin, sizeof(sin));
|
||||||
|
sin.sin_family = AF_INET;
|
||||||
|
sin.sin_addr.S_un.S_addr = route.dwForwardNextHop;
|
||||||
|
sin.sin_port = htons(80);
|
||||||
|
|
||||||
|
if (connect(s, (PSOCKADDR)&sin, sizeof(sin)) == 0) {
|
||||||
|
error = NO_ERROR;
|
||||||
|
fprintf(LOG_OUT, "connect(%s, %d) successful\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
error = WSAGetLastError();
|
||||||
|
fprintf(LOG_OUT, "connect(%s, %d) failed: %d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
closesocket(s);
|
||||||
|
|
||||||
|
return error == WSAEACCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsZeroTierInstalled()
|
||||||
|
{
|
||||||
|
DWORD error;
|
||||||
|
HKEY key;
|
||||||
|
|
||||||
|
error = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\ZeroTier, Inc.\\ZeroTier One", 0, KEY_READ | KEY_WOW64_32KEY, &key);
|
||||||
|
if (error != ERROR_SUCCESS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RegCloseKey(key);
|
||||||
|
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 {
|
enum PortTestStatus {
|
||||||
PortTestOk,
|
PortTestOk,
|
||||||
PortTestError,
|
PortTestError,
|
||||||
PortTestUnknown
|
PortTestUnknown
|
||||||
};
|
};
|
||||||
PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withServer)
|
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;
|
SOCKET clientSock = INVALID_SOCKET, serverSock = INVALID_SOCKET;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
clientSock = socket(addr->ss_family, proto == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM, proto);
|
clientSock = socket(addr->ss_family, proto == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM, proto);
|
||||||
if (clientSock == INVALID_SOCKET) {
|
if (clientSock == INVALID_SOCKET) {
|
||||||
printf("socket() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "socket() failed: %d\n", WSAGetLastError());
|
||||||
return PortTestError;
|
return PortTestError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (withServer) {
|
if (withServer) {
|
||||||
serverSock = socket(addr->ss_family, proto == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM, proto);
|
// Even if we are testing IPv6, our server socket should still be on IPv4 to allow the
|
||||||
|
// IPv6 relay to do its job (since it's already bound to all GFE ports on v6)
|
||||||
|
serverSock = socket(AF_INET, proto == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM, proto);
|
||||||
if (serverSock == INVALID_SOCKET) {
|
if (serverSock == INVALID_SOCKET) {
|
||||||
printf("socket() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "socket() failed: %d\n", WSAGetLastError());
|
||||||
closesocket(clientSock);
|
closesocket(clientSock);
|
||||||
return PortTestError;
|
return PortTestError;
|
||||||
}
|
}
|
||||||
@@ -168,21 +490,22 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
|
|||||||
if (err == SOCKET_ERROR) {
|
if (err == SOCKET_ERROR) {
|
||||||
if (WSAGetLastError() == WSAEADDRINUSE) {
|
if (WSAGetLastError() == WSAEADDRINUSE) {
|
||||||
// If someone is already listening (perhaps GFE is currently streaming),
|
// If someone is already listening (perhaps GFE is currently streaming),
|
||||||
// we can proceed if it's a TCP connection.
|
// we can proceed if it's a TCP connection unless it's a loopback relay
|
||||||
if (proto == IPPROTO_TCP) {
|
// which would give us a false positive result.
|
||||||
|
if (proto == IPPROTO_TCP && !isLoopbackRelay) {
|
||||||
closesocket(serverSock);
|
closesocket(serverSock);
|
||||||
serverSock = INVALID_SOCKET;
|
serverSock = INVALID_SOCKET;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// We can't continue to test for UDP ports.
|
// We can't continue to test for UDP ports.
|
||||||
printf("Unknown (in use)\n");
|
fprintf(LOG_OUT, "Unknown (in use)\n");
|
||||||
closesocket(clientSock);
|
closesocket(clientSock);
|
||||||
closesocket(serverSock);
|
closesocket(serverSock);
|
||||||
return PortTestUnknown;
|
return PortTestUnknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("bind() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "bind() failed: %d\n", WSAGetLastError());
|
||||||
closesocket(clientSock);
|
closesocket(clientSock);
|
||||||
closesocket(serverSock);
|
closesocket(serverSock);
|
||||||
return PortTestError;
|
return PortTestError;
|
||||||
@@ -192,7 +515,7 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
|
|||||||
if (proto == IPPROTO_TCP && serverSock != INVALID_SOCKET) {
|
if (proto == IPPROTO_TCP && serverSock != INVALID_SOCKET) {
|
||||||
err = listen(serverSock, 1);
|
err = listen(serverSock, 1);
|
||||||
if (err == SOCKET_ERROR) {
|
if (err == SOCKET_ERROR) {
|
||||||
printf("listen() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "listen() failed: %d\n", WSAGetLastError());
|
||||||
closesocket(clientSock);
|
closesocket(clientSock);
|
||||||
closesocket(serverSock);
|
closesocket(serverSock);
|
||||||
return PortTestError;
|
return PortTestError;
|
||||||
@@ -200,10 +523,25 @@ 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;
|
ULONG nbIo = 1;
|
||||||
err = ioctlsocket(clientSock, FIONBIO, &nbIo);
|
err = ioctlsocket(clientSock, FIONBIO, &nbIo);
|
||||||
if (err == SOCKET_ERROR) {
|
if (err == SOCKET_ERROR) {
|
||||||
printf("ioctlsocket() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "ioctlsocket() failed: %d\n", WSAGetLastError());
|
||||||
closesocket(clientSock);
|
closesocket(clientSock);
|
||||||
if (serverSock != INVALID_SOCKET) {
|
if (serverSock != INVALID_SOCKET) {
|
||||||
closesocket(serverSock);
|
closesocket(serverSock);
|
||||||
@@ -216,32 +554,43 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
|
|||||||
sizeof(SOCKADDR_IN) : sizeof(SOCKADDR_IN6);
|
sizeof(SOCKADDR_IN) : sizeof(SOCKADDR_IN6);
|
||||||
|
|
||||||
RtlCopyMemory(&sin6, addr, addrLen);
|
RtlCopyMemory(&sin6, addr, addrLen);
|
||||||
sin6.sin6_port = htons(port);
|
sin6.sin6_port = htons(port + (isLoopbackRelay ? LOOPBACK_SERVER_PORT_OFFSET : 0));
|
||||||
|
|
||||||
if (proto == IPPROTO_TCP) {
|
if (proto == IPPROTO_TCP) {
|
||||||
err = connect(clientSock, (struct sockaddr*)&sin6, addrLen);
|
err = connect(clientSock, (struct sockaddr*)&sin6, addrLen);
|
||||||
if (err == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) {
|
if (err == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) {
|
||||||
printf("connect() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "connect() failed: %d\n", WSAGetLastError());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
struct timeval timeout = {};
|
struct timeval timeout = {};
|
||||||
fd_set fds;
|
fd_set fds;
|
||||||
|
|
||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
FD_SET(clientSock, &fds);
|
|
||||||
|
|
||||||
|
if (serverSock != INVALID_SOCKET) {
|
||||||
|
FD_SET(serverSock, &fds);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
FD_SET(clientSock, &fds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a server socket, listen for the accept() instead of the
|
||||||
|
// connect() so we can be compatible with the loopback relay.
|
||||||
timeout.tv_sec = 3;
|
timeout.tv_sec = 3;
|
||||||
err = select(0, nullptr, &fds, nullptr, &timeout);
|
err = select(0,
|
||||||
|
serverSock != INVALID_SOCKET ? &fds : nullptr,
|
||||||
|
serverSock == INVALID_SOCKET ? &fds : nullptr,
|
||||||
|
nullptr, &timeout);
|
||||||
if (err == 1) {
|
if (err == 1) {
|
||||||
// Our FD was signalled for connect() completion
|
// Our FD was signalled for connect() or accept() completion
|
||||||
printf("Success\n");
|
fprintf(LOG_OUT, "Success\n");
|
||||||
}
|
}
|
||||||
else if (err == 0) {
|
else if (err == 0) {
|
||||||
// Timed out
|
// Timed out
|
||||||
printf("Timeout\n");
|
fprintf(LOG_OUT, "Timeout\n");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("select() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "select() failed: %d\n", WSAGetLastError());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,13 +602,22 @@ 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.
|
||||||
err = sendto(clientSock, testMsg, sizeof(testMsg), 0, (struct sockaddr*)&sin6, addrLen);
|
const char testMsg[1040] = "moonlight-test";
|
||||||
if (err == SOCKET_ERROR) {
|
|
||||||
printf("sendto() failed: %d\n", WSAGetLastError());
|
// Send several test packets to ensure a random lost packet doesn't make the test fail
|
||||||
closesocket(clientSock);
|
for (int i = 0; i < 5; i++) {
|
||||||
closesocket(serverSock);
|
// Send the full payload for MTU tests and the truncated payload for other tests.
|
||||||
return PortTestError;
|
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);
|
||||||
|
closesocket(serverSock);
|
||||||
|
return PortTestError;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sleep(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct timeval timeout = {};
|
struct timeval timeout = {};
|
||||||
@@ -268,18 +626,18 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
|
|||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
FD_SET(serverSock, &fds);
|
FD_SET(serverSock, &fds);
|
||||||
|
|
||||||
timeout.tv_sec = 3;
|
timeout.tv_sec = 2;
|
||||||
err = select(0, &fds, nullptr, nullptr, &timeout);
|
err = select(0, &fds, nullptr, nullptr, &timeout);
|
||||||
if (err == 1) {
|
if (err == 1) {
|
||||||
// Our FD was signalled for data available
|
// Our FD was signalled for data available
|
||||||
printf("Success\n");
|
fprintf(LOG_OUT, "Success\n");
|
||||||
}
|
}
|
||||||
else if (err == 0) {
|
else if (err == 0) {
|
||||||
// Timed out
|
// Timed out
|
||||||
printf("Timeout\n");
|
fprintf(LOG_OUT, "Timeout\n");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("select() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "select() failed: %d\n", WSAGetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
closesocket(clientSock);
|
closesocket(clientSock);
|
||||||
@@ -289,80 +647,448 @@ PortTestStatus TestPort(PSOCKADDR_STORAGE addr, int proto, int port, bool withSe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TestAllPorts(PSOCKADDR_STORAGE addr, char* portMsg, int portMsgLen)
|
PortTestStatus TestHttpPort(PSOCKADDR_STORAGE addr, int port, bool isLoopbackRelay)
|
||||||
|
{
|
||||||
|
HINTERNET hSession = nullptr;
|
||||||
|
HINTERNET hConnection = nullptr;
|
||||||
|
HINTERNET hRequest = nullptr;
|
||||||
|
PortTestStatus result;
|
||||||
|
|
||||||
|
hSession = WinHttpOpen(L"MIST", WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
|
||||||
|
if (hSession == nullptr) {
|
||||||
|
fprintf(LOG_OUT, "WinHttpOpen() failed: %d\n", GetLastError());
|
||||||
|
result = PortTestError;
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// WinHTTP's default timeouts are very long. Set them to something more reasonable.
|
||||||
|
if (!WinHttpSetTimeouts(hSession, 0, 3000, 5000, 5000)) {
|
||||||
|
fprintf(LOG_OUT, "WinHttpSetTimeouts() failed: %d\n", GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Windows 8.1 enabled TLSv1.2 for WinHTTP by default (8.0 enables it for Schannel but not WinHTTP)
|
||||||
|
// https://docs.microsoft.com/en-us/security/engineering/solving-tls1-problem
|
||||||
|
if (!IsWindows8Point1OrGreater()) {
|
||||||
|
DWORD protocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 |
|
||||||
|
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
|
||||||
|
WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2;
|
||||||
|
|
||||||
|
if (!WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &protocols, sizeof(protocols))) {
|
||||||
|
fprintf(LOG_OUT, "WinHttpSetOption(WINHTTP_OPTION_SECURE_PROTOCOLS) failed: %d\n", GetLastError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WCHAR urlEscapedAddr[INET6_ADDRSTRLEN + 2];
|
||||||
|
if (addr->ss_family == AF_INET) {
|
||||||
|
InetNtopW(AF_INET, &((struct sockaddr_in*)addr)->sin_addr, urlEscapedAddr, ARRAYSIZE(urlEscapedAddr));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// The address string must be escaped for usage in URLs
|
||||||
|
urlEscapedAddr[0] = L'[';
|
||||||
|
InetNtopW(AF_INET6, &((struct sockaddr_in6*)addr)->sin6_addr, &urlEscapedAddr[1], INET6_ADDRSTRLEN);
|
||||||
|
wcscat_s(urlEscapedAddr, L"]");
|
||||||
|
}
|
||||||
|
|
||||||
|
hConnection = WinHttpConnect(hSession, urlEscapedAddr, port + (isLoopbackRelay ? LOOPBACK_SERVER_PORT_OFFSET : 0), NULL);
|
||||||
|
if (hConnection == nullptr) {
|
||||||
|
fprintf(LOG_OUT, "WinHttpConnect() failed: %d\n", GetLastError());
|
||||||
|
result = PortTestError;
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
hRequest = WinHttpOpenRequest(hConnection, L"GET", L"/", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES,
|
||||||
|
port == 47984 ? WINHTTP_FLAG_SECURE : 0);
|
||||||
|
if (hConnection == nullptr) {
|
||||||
|
fprintf(LOG_OUT, "WinHttpOpenRequest() failed: %d\n", GetLastError());
|
||||||
|
result = PortTestError;
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0) || !WinHttpReceiveResponse(hRequest, NULL)) {
|
||||||
|
if (GetLastError() == ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED) {
|
||||||
|
// This is expected for our HTTPS connection
|
||||||
|
fprintf(LOG_OUT, "Success\n");
|
||||||
|
result = PortTestOk;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// CANNOT_CONNECT is the "expected" error
|
||||||
|
if (GetLastError() == ERROR_WINHTTP_CANNOT_CONNECT) {
|
||||||
|
fprintf(LOG_OUT, "Failed\n");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(LOG_OUT, "Failed: %d\n", GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
result = PortTestError;
|
||||||
|
}
|
||||||
|
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(LOG_OUT, "Success\n");
|
||||||
|
result = PortTestOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
Exit:
|
||||||
|
if (hRequest != nullptr) {
|
||||||
|
WinHttpCloseHandle(hRequest);
|
||||||
|
}
|
||||||
|
if (hConnection != nullptr) {
|
||||||
|
WinHttpCloseHandle(hConnection);
|
||||||
|
}
|
||||||
|
if (hSession != nullptr) {
|
||||||
|
WinHttpCloseHandle(hSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TestAllPorts(PSOCKADDR_STORAGE addr, PSOCKADDR_STORAGE localBindAddr, char* portMsg, int portMsgLen, bool isLoopbackRelay, bool consolePrint, bool* allPortsFailed = nullptr)
|
||||||
{
|
{
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
|
|
||||||
|
if (allPortsFailed) {
|
||||||
|
*allPortsFailed = true;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
||||||
printf("Testing %s %d...",
|
PortTestStatus status;
|
||||||
k_Ports[i].proto == IPPROTO_TCP ? "TCP" : "UDP",
|
|
||||||
k_Ports[i].port);
|
if (consolePrint) {
|
||||||
PortTestStatus status = TestPort(addr, k_Ports[i].proto, k_Ports[i].port, k_Ports[i].withServer);
|
fprintf(CONSOLE_OUT, "\tTesting %s %d...\n",
|
||||||
|
k_Ports[i].proto == IPPROTO_TCP ? "TCP" : "UDP",
|
||||||
|
k_Ports[i].port);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(LOG_OUT, "Testing %s %d...",
|
||||||
|
k_Ports[i].proto == IPPROTO_TCP ? "TCP" : "UDP",
|
||||||
|
k_Ports[i].port);
|
||||||
|
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) {
|
if (status != PortTestOk) {
|
||||||
// If we got an unknown result, assume it matches with whatever
|
// If we got an unknown result, assume it matches with whatever
|
||||||
// we've gotten so far.
|
// we've gotten so far.
|
||||||
if (status == PortTestError || !ret) {
|
if (status == PortTestError || !ret) {
|
||||||
int msgLen = snprintf(portMsg, portMsgLen, "%s %d\n",
|
if (portMsg != NULL && portMsgLen > 0) {
|
||||||
k_Ports[i].proto == IPPROTO_TCP ? "TCP" : "UDP",
|
int msgLen = snprintf(portMsg, portMsgLen, "%s %d\n",
|
||||||
k_Ports[i].port);
|
k_Ports[i].proto == IPPROTO_TCP ? "TCP" : "UDP",
|
||||||
portMsg += msgLen;
|
k_Ports[i].port);
|
||||||
portMsgLen -= msgLen;
|
portMsg += msgLen;
|
||||||
|
portMsgLen -= msgLen;
|
||||||
|
}
|
||||||
|
|
||||||
// Keep going to check all ports and report the failing ones
|
// Keep going to check all ports and report the failing ones
|
||||||
ret = false;
|
ret = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (allPortsFailed) {
|
||||||
|
*allPortsFailed = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FindLocalInterfaceIP4Address(PSOCKADDR_IN addr)
|
bool IsTestServerReachable(struct addrinfo* addrinfo, unsigned short port)
|
||||||
{
|
{
|
||||||
SOCKET s;
|
SOCKET s;
|
||||||
struct hostent* host;
|
FD_SET writeFds, exceptFds;
|
||||||
|
int err;
|
||||||
|
struct timeval tv = {};
|
||||||
|
SOCKADDR_STORAGE addr;
|
||||||
|
char testServerStr[INET6_ADDRSTRLEN];
|
||||||
|
|
||||||
printf("Finding local IP address...");
|
memcpy(&addr, addrinfo->ai_addr, addrinfo->ai_addrlen);
|
||||||
|
((PSOCKADDR_IN6)&addr)->sin6_port = htons(port);
|
||||||
|
|
||||||
host = gethostbyname("google.com");
|
if (addr.ss_family == AF_INET) {
|
||||||
if (host == nullptr) {
|
inet_ntop(AF_INET, &((struct sockaddr_in*)&addr)->sin_addr, testServerStr, sizeof(testServerStr));
|
||||||
printf("gethostbyname() failed: %d\n", WSAGetLastError());
|
}
|
||||||
return false;
|
else {
|
||||||
|
inet_ntop(AF_INET6, &((struct sockaddr_in6*)&addr)->sin6_addr, testServerStr, sizeof(testServerStr));
|
||||||
}
|
}
|
||||||
|
|
||||||
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
fprintf(LOG_OUT, "Testing reachability of relay server %s...", testServerStr);
|
||||||
|
|
||||||
|
s = socket(addrinfo->ai_family, SOCK_STREAM, IPPROTO_TCP);
|
||||||
if (s == INVALID_SOCKET) {
|
if (s == INVALID_SOCKET) {
|
||||||
printf("socket() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "socket() failed: %d\n", WSAGetLastError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SOCKADDR_IN sin = {};
|
ULONG nbIo = 1;
|
||||||
sin.sin_family = AF_INET;
|
err = ioctlsocket(s, FIONBIO, &nbIo);
|
||||||
sin.sin_port = htons(443);
|
|
||||||
sin.sin_addr = *(struct in_addr*)host->h_addr;
|
|
||||||
int err = connect(s, (struct sockaddr*)&sin, sizeof(sin));
|
|
||||||
if (err == SOCKET_ERROR) {
|
if (err == SOCKET_ERROR) {
|
||||||
printf("connect() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "ioctlsocket() failed: %d\n", WSAGetLastError());
|
||||||
closesocket(s);
|
closesocket(s);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = connect(s, (PSOCKADDR)&addr, addrinfo->ai_addrlen);
|
||||||
|
if (err == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) {
|
||||||
|
fprintf(LOG_OUT, "Unreachable (%d)\n", WSAGetLastError());
|
||||||
|
closesocket(s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FD_ZERO(&writeFds);
|
||||||
|
FD_ZERO(&exceptFds);
|
||||||
|
|
||||||
|
FD_SET(s, &writeFds);
|
||||||
|
FD_SET(s, &exceptFds);
|
||||||
|
|
||||||
|
tv.tv_sec = 3;
|
||||||
|
err = select(0, nullptr, &writeFds, &exceptFds, &tv);
|
||||||
|
if (err == SOCKET_ERROR) {
|
||||||
|
fprintf(LOG_OUT, "select() failed: %d\n", WSAGetLastError());
|
||||||
|
closesocket(s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (err == 0) {
|
||||||
|
fprintf(LOG_OUT, "Unreachable (timeout)\n");
|
||||||
|
closesocket(s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int optlen = sizeof(err);
|
||||||
|
getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&err, &optlen);
|
||||||
|
closesocket(s);
|
||||||
|
|
||||||
|
if (err != 0 || FD_ISSET(s, &exceptFds)) {
|
||||||
|
fprintf(LOG_OUT, "Unreachable (%d)\n", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(LOG_OUT, "Reachable\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FindLocalInterfaceIPAddress(int family, PSOCKADDR_STORAGE addr)
|
||||||
|
{
|
||||||
|
SOCKET s;
|
||||||
|
struct addrinfo hint = {};
|
||||||
|
struct addrinfo* result;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
fprintf(LOG_OUT, "Finding local %s address...", family == AF_INET ? "IPv4" : "IPv6");
|
||||||
|
|
||||||
|
hint.ai_family = family;
|
||||||
|
hint.ai_socktype = SOCK_STREAM;
|
||||||
|
hint.ai_protocol = IPPROTO_TCP;
|
||||||
|
hint.ai_flags = AI_ADDRCONFIG;
|
||||||
|
err = getaddrinfo("moonlight-stream.org", "443", &hint, &result);
|
||||||
|
if (err != 0 || result == NULL) {
|
||||||
|
// AI_ADDRCONFIG will mask unusable addresses, so we may get nothing
|
||||||
|
// We get WSANO_DATA or WSAHOST_NOT_FOUND depending on whether it's V4 or V6 :(
|
||||||
|
if (err == WSANO_DATA || err == WSAHOST_NOT_FOUND) {
|
||||||
|
fprintf(LOG_OUT, "NONE\n");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(LOG_OUT, "getaddrinfo() failed: %d\n", err);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
|
||||||
|
if (s == INVALID_SOCKET) {
|
||||||
|
fprintf(LOG_OUT, "socket() failed: %d\n", WSAGetLastError());
|
||||||
|
freeaddrinfo(result);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = connect(s, (struct sockaddr*)result->ai_addr, result->ai_addrlen);
|
||||||
|
if (err == SOCKET_ERROR) {
|
||||||
|
fprintf(LOG_OUT, "connect() failed: %d\n", WSAGetLastError());
|
||||||
|
closesocket(s);
|
||||||
|
freeaddrinfo(result);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(result);
|
||||||
|
|
||||||
// Determine which local interface we bound to
|
// Determine which local interface we bound to
|
||||||
int nameLen = sizeof(*addr);
|
int nameLen = sizeof(*addr);
|
||||||
err = getsockname(s, (struct sockaddr*)addr, &nameLen);
|
err = getsockname(s, (struct sockaddr*)addr, &nameLen);
|
||||||
if (err == SOCKET_ERROR) {
|
if (err == SOCKET_ERROR) {
|
||||||
printf("getsockname() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "getsockname() failed: %d\n", WSAGetLastError());
|
||||||
closesocket(s);
|
closesocket(s);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char addrStr[64];
|
char addrStr[INET6_ADDRSTRLEN];
|
||||||
inet_ntop(AF_INET, &addr->sin_addr, addrStr, sizeof(addrStr));
|
if (addr->ss_family == AF_INET) {
|
||||||
printf("%s\n", addrStr);
|
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);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FindZeroTierInterfaceAddress(PSOCKADDR_STORAGE addr)
|
||||||
|
{
|
||||||
|
PIP_ADAPTER_ADDRESSES addresses;
|
||||||
|
PIP_ADAPTER_ADDRESSES currentAdapter;
|
||||||
|
PIP_ADAPTER_UNICAST_ADDRESS currentAddress;
|
||||||
|
|
||||||
|
// Get all IPv4 interfaces
|
||||||
|
addresses = AllocAndGetAdaptersAddresses(AF_INET,
|
||||||
|
GAA_FLAG_SKIP_ANYCAST |
|
||||||
|
GAA_FLAG_SKIP_MULTICAST |
|
||||||
|
GAA_FLAG_SKIP_DNS_SERVER |
|
||||||
|
GAA_FLAG_SKIP_FRIENDLY_NAME);
|
||||||
|
if (addresses == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAdapter = addresses;
|
||||||
|
while (currentAdapter != NULL) {
|
||||||
|
// Look for ones that correspond to a ZeroTier device
|
||||||
|
if (wcsstr(currentAdapter->Description, L"ZeroTier")) {
|
||||||
|
// Check if this interface has the IP address we want
|
||||||
|
currentAddress = currentAdapter->FirstUnicastAddress;
|
||||||
|
while (currentAddress != NULL) {
|
||||||
|
if (currentAddress->Address.lpSockaddr->sa_family == AF_INET) {
|
||||||
|
RtlCopyMemory(addr, currentAddress->Address.lpSockaddr, currentAddress->Address.iSockaddrLength);
|
||||||
|
free(addresses);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAddress = currentAddress->Next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAdapter = currentAdapter->Next;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(addresses);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FindDuplicateDefaultInterfaces(void)
|
||||||
|
{
|
||||||
|
PIP_ADAPTER_ADDRESSES addresses;
|
||||||
|
ULONG error;
|
||||||
|
MIB_IPFORWARDROW defaultRoute;
|
||||||
|
PIP_ADAPTER_ADDRESSES currentAdapter;
|
||||||
|
DWORD matchingInterfaces = 0;
|
||||||
|
|
||||||
|
error = GetBestRoute(0, 0, &defaultRoute);
|
||||||
|
if (error != NO_ERROR) {
|
||||||
|
fprintf(LOG_OUT, "GetBestRoute() failed: %d\n", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all IPv4 interfaces
|
||||||
|
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);
|
||||||
|
if (addresses == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAdapter = addresses;
|
||||||
|
while (currentAdapter != NULL) {
|
||||||
|
if (currentAdapter->OperStatus == IfOperStatusUp &&
|
||||||
|
currentAdapter->FirstGatewayAddress != NULL &&
|
||||||
|
currentAdapter->FirstGatewayAddress->Address.iSockaddrLength == sizeof(SOCKADDR_IN)) {
|
||||||
|
if (((PSOCKADDR_IN)currentAdapter->FirstGatewayAddress->Address.lpSockaddr)->sin_addr.S_un.S_addr == defaultRoute.dwForwardNextHop) {
|
||||||
|
matchingInterfaces++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAdapter = currentAdapter->Next;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(addresses);
|
||||||
|
return matchingInterfaces > 1;
|
||||||
|
}
|
||||||
|
|
||||||
enum UPnPPortStatus {
|
enum UPnPPortStatus {
|
||||||
NOT_FOUND,
|
NOT_FOUND,
|
||||||
OK,
|
OK,
|
||||||
@@ -393,28 +1119,28 @@ UPnPPortStatus UPnPCheckPort(struct UPNPUrls* urls, struct IGDdatas* data, int p
|
|||||||
return ERRORED;
|
return ERRORED;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Checking for UPnP port mapping for %s %s -> %s...", protoStr, portStr, myAddr);
|
fprintf(LOG_OUT, "Checking for UPnP port mapping for %s %s -> %s...", protoStr, portStr, myAddr);
|
||||||
int err = UPNP_GetSpecificPortMappingEntry(
|
int err = UPNP_GetSpecificPortMappingEntry(
|
||||||
urls->controlURL, data->first.servicetype, portStr, protoStr, nullptr,
|
urls->controlURL, data->first.servicetype, portStr, protoStr, nullptr,
|
||||||
intClient, intPort, desc, enabled, leaseDuration);
|
intClient, intPort, desc, enabled, leaseDuration);
|
||||||
if (err == 714) {
|
if (err == 714) {
|
||||||
// NoSuchEntryInArray
|
// NoSuchEntryInArray
|
||||||
printf("NOT FOUND\n");
|
fprintf(LOG_OUT, "NOT FOUND\n");
|
||||||
return NOT_FOUND;
|
return NOT_FOUND;
|
||||||
}
|
}
|
||||||
else if (err == UPNPCOMMAND_SUCCESS) {
|
else if (err == UPNPCOMMAND_SUCCESS) {
|
||||||
if (!strcmp(myAddr, intClient)) {
|
if (!strcmp(myAddr, intClient)) {
|
||||||
printf("OK\n");
|
fprintf(LOG_OUT, "OK (Internal port: %s)\n", intPort);
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("CONFLICT - %s %s\n", desc, intClient);
|
fprintf(LOG_OUT, "CONFLICT - %s %s\n", desc, intClient);
|
||||||
snprintf(conflictMessage, 128, "%s (%s)", desc, intClient);
|
snprintf(conflictMessage, 128, "%s (%s)", desc, intClient);
|
||||||
return CONFLICTED;
|
return CONFLICTED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("ERROR %d\n", err);
|
fprintf(LOG_OUT, "ERROR %d\n", err);
|
||||||
return ERRORED;
|
return ERRORED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -424,23 +1150,27 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, PSOCKADDR_IN reportedWanAddr, bool* fo
|
|||||||
natpmp_t natpmp;
|
natpmp_t natpmp;
|
||||||
bool foundUpnpIgd = false;
|
bool foundUpnpIgd = false;
|
||||||
|
|
||||||
|
wanAddr->sin_family = AF_INET;
|
||||||
|
reportedWanAddr->sin_family = AF_INET;
|
||||||
*foundPortForwardingRules = false;
|
*foundPortForwardingRules = false;
|
||||||
*igdDisconnected = false;
|
*igdDisconnected = false;
|
||||||
|
|
||||||
bool gotReportedWanAddress = false;
|
bool gotReportedWanAddress = false;
|
||||||
int natPmpErr = initnatpmp(&natpmp, 0, 0);
|
int natPmpErr = initnatpmp(&natpmp, 0, 0);
|
||||||
if (natPmpErr != 0) {
|
if (natPmpErr != 0) {
|
||||||
printf("initnatpmp() failed: %d\n", natPmpErr);
|
fprintf(LOG_OUT, "initnatpmp() failed: %d\n", natPmpErr);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
natPmpErr = sendpublicaddressrequest(&natpmp);
|
natPmpErr = sendpublicaddressrequest(&natpmp);
|
||||||
if (natPmpErr < 0) {
|
if (natPmpErr < 0) {
|
||||||
printf("sendpublicaddressrequest() failed: %d\n", natPmpErr);
|
fprintf(LOG_OUT, "sendpublicaddressrequest() failed: %d\n", natPmpErr);
|
||||||
closenatpmp(&natpmp);
|
closenatpmp(&natpmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
fprintf(CONSOLE_OUT, "\tTesting UPnP...\n");
|
||||||
|
|
||||||
int upnpErr;
|
int upnpErr;
|
||||||
struct UPNPDev* ipv4Devs = upnpDiscoverAll(5000, nullptr, nullptr, UPNP_LOCAL_PORT_ANY, 0, 2, &upnpErr);
|
struct UPNPDev* ipv4Devs = upnpDiscoverAll(5000, nullptr, nullptr, UPNP_LOCAL_PORT_ANY, 0, 2, &upnpErr);
|
||||||
|
|
||||||
@@ -456,19 +1186,19 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, PSOCKADDR_IN reportedWanAddr, bool* fo
|
|||||||
if (ret == 2) {
|
if (ret == 2) {
|
||||||
*igdDisconnected = true;
|
*igdDisconnected = true;
|
||||||
}
|
}
|
||||||
printf("Discovered UPnP IGD at: %s\n", urls.controlURL);
|
fprintf(LOG_OUT, "Discovered UPnP IGD at: %s\n", urls.controlURL);
|
||||||
printf("Detecting WAN IP address via UPnP...");
|
fprintf(LOG_OUT, "Detecting WAN IP address via UPnP...");
|
||||||
ret = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, wanAddrStr);
|
ret = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, wanAddrStr);
|
||||||
if (ret == UPNPCOMMAND_SUCCESS && strlen(wanAddrStr) > 0) {
|
if (ret == UPNPCOMMAND_SUCCESS && strlen(wanAddrStr) > 0) {
|
||||||
reportedWanAddr->sin_addr.S_un.S_addr = wanAddr->sin_addr.S_un.S_addr = inet_addr(wanAddrStr);
|
reportedWanAddr->sin_addr.S_un.S_addr = inet_addr(wanAddrStr);
|
||||||
printf("%s\n", wanAddrStr);
|
fprintf(LOG_OUT, "%s\n", wanAddrStr);
|
||||||
|
|
||||||
if (wanAddr->sin_addr.S_un.S_addr != 0) {
|
if (reportedWanAddr->sin_addr.S_un.S_addr != 0) {
|
||||||
gotReportedWanAddress = true;
|
gotReportedWanAddress = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("FAILED %d\n", ret);
|
fprintf(LOG_OUT, "FAILED %d\n", ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
char conflictMessage[512];
|
char conflictMessage[512];
|
||||||
@@ -484,7 +1214,7 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, PSOCKADDR_IN reportedWanAddr, bool* fo
|
|||||||
case CONFLICTED:
|
case CONFLICTED:
|
||||||
snprintf(conflictMessage, sizeof(conflictMessage),
|
snprintf(conflictMessage, sizeof(conflictMessage),
|
||||||
"Detected a port forwarding conflict with another PC on your network: %s\n\n"
|
"Detected a port forwarding conflict with another PC on your network: %s\n\n"
|
||||||
"Remove that PC from your network or uninstall the Moonlight Internet Streaming Service from it, then restart your router.",
|
"Remove that PC from your network or uninstall the Moonlight Internet Streaming Helper from it, then restart your router.",
|
||||||
conflictEntry);
|
conflictEntry);
|
||||||
DisplayMessage(conflictMessage);
|
DisplayMessage(conflictMessage);
|
||||||
return false;
|
return false;
|
||||||
@@ -494,30 +1224,32 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, PSOCKADDR_IN reportedWanAddr, bool* fo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("No UPnP IGD detected\n");
|
fprintf(LOG_OUT, "No UPnP IGD detected\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
FreeUPNPUrls(&urls);
|
FreeUPNPUrls(&urls);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("No UPnP devices detected\n");
|
fprintf(LOG_OUT, "No UPnP devices detected\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the delay of upnpDiscoverAll() to also allow the NAT-PMP endpoint time to respond
|
// Use the delay of upnpDiscoverAll() to also allow the NAT-PMP endpoint time to respond
|
||||||
if (natPmpErr >= 0) {
|
if (natPmpErr >= 0) {
|
||||||
printf("Detecting WAN IP address via NAT-PMP...");
|
fprintf(CONSOLE_OUT, "\tTesting NAT-PMP...\n");
|
||||||
|
|
||||||
|
fprintf(LOG_OUT, "Detecting WAN IP address via NAT-PMP...");
|
||||||
|
|
||||||
natpmpresp_t response;
|
natpmpresp_t response;
|
||||||
natPmpErr = readnatpmpresponseorretry(&natpmp, &response);
|
natPmpErr = readnatpmpresponseorretry(&natpmp, &response);
|
||||||
closenatpmp(&natpmp);
|
closenatpmp(&natpmp);
|
||||||
|
|
||||||
if (natPmpErr == 0) {
|
if (natPmpErr == 0) {
|
||||||
char addrStr[64];
|
char addrStr[INET_ADDRSTRLEN];
|
||||||
reportedWanAddr->sin_addr = wanAddr->sin_addr = response.pnu.publicaddress.addr;
|
reportedWanAddr->sin_addr = response.pnu.publicaddress.addr;
|
||||||
inet_ntop(AF_INET, &response.pnu.publicaddress.addr, addrStr, sizeof(addrStr));
|
inet_ntop(AF_INET, &response.pnu.publicaddress.addr, addrStr, sizeof(addrStr));
|
||||||
printf("%s\n", addrStr);
|
fprintf(LOG_OUT, "%s\n", addrStr);
|
||||||
if (wanAddr->sin_addr.S_un.S_addr != 0) {
|
if (reportedWanAddr->sin_addr.S_un.S_addr != 0) {
|
||||||
gotReportedWanAddress = true;
|
gotReportedWanAddress = true;
|
||||||
|
|
||||||
if (!foundUpnpIgd) {
|
if (!foundUpnpIgd) {
|
||||||
@@ -528,21 +1260,21 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, PSOCKADDR_IN reportedWanAddr, bool* fo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("FAILED %d\n", natPmpErr);
|
fprintf(LOG_OUT, "FAILED %d\n", natPmpErr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Detecting WAN IP address via STUN...");
|
fprintf(LOG_OUT, "Detecting WAN IP address via STUN...");
|
||||||
if (!getExternalAddressPortIP4(IPPROTO_UDP, 0, wanAddr)) {
|
fprintf(CONSOLE_OUT, "\tTesting STUN...\n");
|
||||||
if (!gotReportedWanAddress) {
|
|
||||||
DisplayMessage("Unable to determine your public IP address. Please check your Internet connection.");
|
if (!getExternalAddressPortIP4(0, wanAddr)) {
|
||||||
return false;
|
DisplayMessage("Unable to determine your public IP address. Please check your Internet connection or try again in a few minutes.");
|
||||||
}
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
char addrStr[64];
|
char addrStr[INET_ADDRSTRLEN];
|
||||||
inet_ntop(AF_INET, &wanAddr->sin_addr, addrStr, sizeof(addrStr));
|
inet_ntop(AF_INET, &wanAddr->sin_addr, addrStr, sizeof(addrStr));
|
||||||
printf("%s\n", addrStr);
|
fprintf(LOG_OUT, "%s\n", addrStr);
|
||||||
|
|
||||||
if (!gotReportedWanAddress) {
|
if (!gotReportedWanAddress) {
|
||||||
// If we didn't get anything from UPnP or NAT-PMP, just populate the reported
|
// If we didn't get anything from UPnP or NAT-PMP, just populate the reported
|
||||||
@@ -554,16 +1286,12 @@ bool CheckWANAccess(PSOCKADDR_IN wanAddr, PSOCKADDR_IN reportedWanAddr, bool* fo
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsPossibleCGN(PSOCKADDR_IN wanAddr)
|
bool IsCGN(PSOCKADDR_IN wanAddr)
|
||||||
{
|
{
|
||||||
DWORD addr = htonl(wanAddr->sin_addr.S_un.S_addr);
|
DWORD addr = htonl(wanAddr->sin_addr.S_un.S_addr);
|
||||||
|
|
||||||
// 10.0.0.0/8 - ISPs used to use this
|
|
||||||
if ((addr & 0xFF000000) == 0x0A000000) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// 100.64.0.0/10 - RFC6598 official CGN address
|
// 100.64.0.0/10 - RFC6598 official CGN address
|
||||||
else if ((addr & 0xFFC00000) == 0x64400000) {
|
if ((addr & 0xFFC00000) == 0x64400000) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -593,18 +1321,25 @@ bool IsDoubleNAT(PSOCKADDR_IN wanAddr)
|
|||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
WSADATA wsaData;
|
WSADATA wsaData;
|
||||||
|
SYSTEMTIME time;
|
||||||
|
char timeString[MAX_PATH + 1] = {};
|
||||||
|
|
||||||
char tempPath[MAX_PATH + 1];
|
char tempPath[MAX_PATH + 1];
|
||||||
GetTempPathA(sizeof(tempPath), tempPath);
|
GetTempPathA(sizeof(tempPath), tempPath);
|
||||||
|
|
||||||
snprintf(logFilePath, sizeof(logFilePath), "%s\\%s", tempPath, "mis-test.log");
|
snprintf(logFilePath, sizeof(logFilePath), "%s\\%s", tempPath, "mis-test.log");
|
||||||
freopen(logFilePath, "w", stdout);
|
freopen(logFilePath, "w", LOG_OUT);
|
||||||
|
|
||||||
// Print a log header
|
// Print a log header
|
||||||
printf("Moonlight Internet Streaming Tester v" VER_VERSION_STR "\n");
|
fprintf(LOG_OUT, "Moonlight Internet Streaming Tester v" VER_VERSION_STR "\n");
|
||||||
|
|
||||||
|
// Print the current time
|
||||||
|
GetSystemTime(&time);
|
||||||
|
GetTimeFormatA(LOCALE_SYSTEM_DEFAULT, 0, &time, "hh':'mm':'ss tt", timeString, ARRAYSIZE(timeString));
|
||||||
|
fprintf(LOG_OUT, "The current UTC time is: %s\n", timeString);
|
||||||
|
|
||||||
// Print a console header
|
// Print a console header
|
||||||
fprintf(stderr, "Moonlight Internet Streaming Tester v" VER_VERSION_STR "\n\n");
|
fprintf(CONSOLE_OUT, "Moonlight Internet Streaming Tester v" VER_VERSION_STR "\n\n");
|
||||||
|
|
||||||
int err = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
int err = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||||
if (err != NO_ERROR) {
|
if (err != NO_ERROR) {
|
||||||
@@ -612,13 +1347,81 @@ int main(int argc, char* argv[])
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "Checking if GameStream is enabled...\n");
|
fprintf(CONSOLE_OUT, "Checking if GameStream is enabled...\n");
|
||||||
|
|
||||||
// First check if GameStream is enabled
|
bool sunshineRunning = IsSunshineRunning();
|
||||||
if (!IsGameStreamEnabled()) {
|
bool gfeGameStreamRunning = true;
|
||||||
|
if (!IsGameStreamEnabled(sunshineRunning)) {
|
||||||
|
if (sunshineRunning) {
|
||||||
|
DisplayMessage("The Moonlight Internet Hosting Tool is not designed for use with Sunshine.\n\n"
|
||||||
|
"To stream over the Internet with Sunshine, simply enable the UPnP option in the Sunshine Web UI.\n\n"
|
||||||
|
"Test results WILL be inaccurate if the Port option in Sunshine has been adjusted from the default value of 47989!",
|
||||||
|
nullptr, MpWarn, false);
|
||||||
|
gfeGameStreamRunning = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gfeGameStreamRunning && !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.",
|
||||||
|
"https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#display-locked-error");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(CONSOLE_OUT, "Checking power settings...\n");
|
||||||
|
|
||||||
|
if (IsSleepEnabled()) {
|
||||||
|
DisplayMessage("This computer has sleep mode enabled. Sleep mode may prevent this PC from being available for streaming.\n\n"
|
||||||
|
"Please ensure sleep is disabled in Power Options so this PC is always ready for streaming. Click the Help button for more information.",
|
||||||
|
"https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#sleep-mode-enabled-warning", MpWarn, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsHibernationEnabled()) {
|
||||||
|
DisplayMessage("This computer has hibernation enabled. Hibernation may prevent this PC from being available for streaming.\n\n"
|
||||||
|
"Please ensure hibernation is disabled in Power Options so this PC is always ready for streaming. Click the Help button for more information.",
|
||||||
|
"https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#hibernation-enabled-warning", MpWarn, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(CONSOLE_OUT, "Checking network connections...\n");
|
||||||
|
|
||||||
|
if (IsLocalNetworkAccessBlocked()) {
|
||||||
|
DisplayMessage("Local network access appears to be blocked by another application installed on this PC.\n\n"
|
||||||
|
"If you have firewall or VPN software installed, make sure it is configured to allow applications to access the local network. Click the Help button for guidance on fixing this issue.",
|
||||||
|
"https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#local-network-access-blocked-error");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FindDuplicateDefaultInterfaces()) {
|
||||||
|
DisplayMessage("This computer appears to have more than one connection to the same network (like both WiFi and Ethernet, for example).\n\n"
|
||||||
|
"Please disconnect the extra connection(s) to ensure hosting works reliably over the Internet. If you are connected via WiFi and Ethernet, try disabling your WiFi connection.",
|
||||||
|
"https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#multiple-connections-error", MpWarn, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(CONSOLE_OUT, "Checking for anti-virus and firewall software...\n");
|
||||||
|
|
||||||
|
char wmicBuf[8192];
|
||||||
|
if (ExecuteCommand("WMIC /Node:localhost /Namespace:\\\\root\\SecurityCenter2 Path AntiVirusProduct Get displayName", wmicBuf, sizeof(wmicBuf))) {
|
||||||
|
fprintf(LOG_OUT, "AV products:\n%s", wmicBuf);
|
||||||
|
}
|
||||||
|
if (ExecuteCommand("WMIC /Node:localhost /Namespace:\\\\root\\SecurityCenter2 Path FirewallProduct Get displayName", wmicBuf, sizeof(wmicBuf))) {
|
||||||
|
fprintf(LOG_OUT, "Firewall products:\n%s", wmicBuf);
|
||||||
|
if (strstr(wmicBuf, "displayName")) {
|
||||||
|
DisplayMessage("Detected anti-virus and/or firewall software installed on this system. This software may interfere with NVIDIA GameStream.\n\n"
|
||||||
|
"Please try temporarily disabling your anti-virus or firewall software if you experience connection issues with Moonlight.",
|
||||||
|
"https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting#known-application-compatibility-issues", MpInfo, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
union {
|
union {
|
||||||
SOCKADDR_STORAGE ss;
|
SOCKADDR_STORAGE ss;
|
||||||
SOCKADDR_IN sin;
|
SOCKADDR_IN sin;
|
||||||
@@ -627,92 +1430,247 @@ int main(int argc, char* argv[])
|
|||||||
char msgBuf[2048];
|
char msgBuf[2048];
|
||||||
char portMsgBuf[512];
|
char portMsgBuf[512];
|
||||||
|
|
||||||
fprintf(stderr, "Testing local GameStream connectivity...\n");
|
fprintf(CONSOLE_OUT, "Testing GameStream connectivity on this PC...\n");
|
||||||
|
|
||||||
// Try to connect via IPv4 loopback
|
// Try to connect via IPv4 loopback
|
||||||
ss = {};
|
ss = {};
|
||||||
sin.sin_family = AF_INET;
|
sin.sin_family = AF_INET;
|
||||||
sin.sin_addr = in4addr_loopback;
|
sin.sin_addr = in4addr_loopback;
|
||||||
printf("Testing GameStream ports via loopback\n");
|
fprintf(LOG_OUT, "Testing GameStream ports via loopback\n");
|
||||||
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf))) {
|
if (!TestAllPorts(&ss, nullptr, portMsgBuf, sizeof(portMsgBuf), false, true)) {
|
||||||
snprintf(msgBuf, sizeof(msgBuf),
|
if (gfeGameStreamRunning) {
|
||||||
"Local GameStream connectivity check failed. Please try reinstalling GeForce Experience.\n\nThe following ports were not working:\n%s",
|
snprintf(msgBuf, sizeof(msgBuf),
|
||||||
portMsgBuf);
|
"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);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!FindLocalInterfaceIP4Address(&sin)) {
|
|
||||||
DisplayMessage("Unable to perform GameStream connectivity check. Please check your Internet connection and try again.");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "Testing network GameStream connectivity...\n");
|
|
||||||
|
|
||||||
// Try to connect via LAN IPv4 address
|
|
||||||
printf("Testing GameStream ports via local network\n");
|
|
||||||
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf))) {
|
|
||||||
snprintf(msgBuf, sizeof(msgBuf),
|
|
||||||
"Local network GameStream connectivity check failed. Try temporarily disabling your firewall software or adding firewall exceptions for the following ports:\n%s",
|
|
||||||
portMsgBuf);
|
|
||||||
DisplayMessage(msgBuf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "Detecting public IP address...\n");
|
|
||||||
|
|
||||||
bool rulesFound, igdDisconnected;
|
|
||||||
SOCKADDR_IN locallyReportedWanAddr;
|
|
||||||
if (!CheckWANAccess(&sin, &locallyReportedWanAddr, &rulesFound, &igdDisconnected)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect a double NAT by detecting STUN and and UPnP mismatches
|
|
||||||
if (sin.sin_addr.S_un.S_addr != locallyReportedWanAddr.sin_addr.S_un.S_addr) {
|
|
||||||
printf("Testing GameStream ports via UPnP/NAT-PMP reported WAN address\n");
|
|
||||||
|
|
||||||
// We don't actually care about the outcome here but it's nice to have in logs
|
|
||||||
// to determine whether solving the double NAT will actually make Moonlight work.
|
|
||||||
TestAllPorts((PSOCKADDR_STORAGE)&locallyReportedWanAddr, portMsgBuf, sizeof(portMsgBuf));
|
|
||||||
|
|
||||||
printf("Detected inconsistency between UPnP/NAT-PMP and STUN reported WAN addresses!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "Testing Internet GameStream connectivity...\n");
|
|
||||||
|
|
||||||
char wanAddrStr[64];
|
|
||||||
inet_ntop(AF_INET, &sin.sin_addr, wanAddrStr, sizeof(wanAddrStr));
|
|
||||||
|
|
||||||
// Try to connect via WAN IPv4 address
|
|
||||||
printf("Testing GameStream ports via STUN-reported WAN address\n");
|
|
||||||
if (!TestAllPorts(&ss, portMsgBuf, sizeof(portMsgBuf))) {
|
|
||||||
if (IsDoubleNAT(&locallyReportedWanAddr)) {
|
|
||||||
snprintf(msgBuf, sizeof(msgBuf), "Your router appears be connected to the Internet through another router. Make sure both routers have UPnP or NAT-PMP enabled, or better yet, switch one of the routers into bridge/AP mode.");
|
|
||||||
DisplayMessage(msgBuf);
|
|
||||||
}
|
|
||||||
else if (IsPossibleCGN(&locallyReportedWanAddr)) {
|
|
||||||
snprintf(msgBuf, sizeof(msgBuf), "Your ISP is running a Carrier-Grade NAT that is preventing you from hosting services like Moonlight on the Internet. Contact your ISP and ask for a dedicated public IP address.");
|
|
||||||
DisplayMessage(msgBuf);
|
|
||||||
}
|
|
||||||
else if (igdDisconnected) {
|
|
||||||
snprintf(msgBuf, sizeof(msgBuf), "Internet GameStream connectivity check failed. Make sure you don't have two devices acting as routers connected together.");
|
|
||||||
DisplayMessage(msgBuf);
|
|
||||||
}
|
|
||||||
else if (rulesFound) {
|
|
||||||
snprintf(msgBuf, sizeof(msgBuf), "Manual Internet streaming test required!\n\n"
|
|
||||||
"Connect your client device to a different network or cellular data (it MUST NOT on be the same network as this PC for testing!). If Moonlight doesn't automatically connect, you can type the following address into Moonlight's Add PC dialog: %s\n\n"
|
|
||||||
"If that doesn't work, check your router settings for any existing Moonlight port forwarding entries and delete them or try restarting your router.", wanAddrStr);
|
|
||||||
DisplayMessage(msgBuf, MpWarn);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
snprintf(msgBuf, sizeof(msgBuf), "Internet GameStream connectivity check failed. Make sure UPnP or NAT-PMP is enabled in your router settings.\n\nThe following ports were not forwarded properly:\n%s", portMsgBuf);
|
snprintf(msgBuf, sizeof(msgBuf),
|
||||||
DisplayMessage(msgBuf);
|
"Local GameStream connectivity check failed.\n\nFirst, try restarting Sunshine. If that doesn't resolve the problem, try temporarily disabling your antivirus and firewall.\n\nNOTE: Sunshine must be configured to use the default 47989 port to test with this tool.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Troubleshooting");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We do a special limited test pass for ZeroTier
|
||||||
|
if (IsZeroTierInstalled()) {
|
||||||
|
fprintf(LOG_OUT, "Found ZeroTier installed\n");
|
||||||
|
|
||||||
|
if (!FindZeroTierInterfaceAddress(&ss)) {
|
||||||
|
DisplayMessage("ZeroTier appears to be installed on this PC, but it's not connected to a network.\n\n"
|
||||||
|
"If you are trying to host with ZeroTier, connect to your ZeroTier network and restart this test.\n\n"
|
||||||
|
"If not, click OK and this test will continue assuming you aren't using ZeroTier.",
|
||||||
|
"https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide#zerotier", MpInfo, false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
char zeroTierAddrStr[INET_ADDRSTRLEN];
|
||||||
|
|
||||||
|
inet_ntop(AF_INET, &sin.sin_addr, zeroTierAddrStr, sizeof(zeroTierAddrStr));
|
||||||
|
|
||||||
|
fprintf(LOG_OUT, "Found ZeroTier connected with address: %s\n", zeroTierAddrStr);
|
||||||
|
|
||||||
|
if (strstr(zeroTierAddrStr, "169.254.")) {
|
||||||
|
DisplayMessage("ZeroTier is active, but this PC has not been authorized to connect to your ZeroTier network.\n\n"
|
||||||
|
"Make sure you check the Auth checkbox for all network members on the ZeroTier Networks webpage.",
|
||||||
|
"https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide#zerotier");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, 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");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get here, our testing is complete for ZeroTier
|
||||||
|
snprintf(msgBuf, sizeof(msgBuf), "This PC is ready to host over the Internet with ZeroTier!\n\n"
|
||||||
|
"Don't forget to connect to your ZeroTier network on your client before streaming over the Internet.\n\n"
|
||||||
|
"After connecting ZeroTier, type following address into Moonlight's Add PC dialog: %s", zeroTierAddrStr);
|
||||||
|
DisplayMessage(msgBuf, nullptr, MpInfo);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasV4Connectivity, hasV6Connectivity;
|
||||||
|
SOCKADDR_STORAGE v4, v6;
|
||||||
|
{
|
||||||
|
hasV4Connectivity = FindLocalInterfaceIPAddress(AF_INET, &v4);
|
||||||
|
hasV6Connectivity = FindLocalInterfaceIPAddress(AF_INET6, &v6);
|
||||||
|
|
||||||
|
// Prefer v4 connectivity because that's what GFE uses natively
|
||||||
|
if (hasV4Connectivity) {
|
||||||
|
ss = v4;
|
||||||
|
}
|
||||||
|
else if (hasV6Connectivity) {
|
||||||
|
ss = v6;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DisplayMessage("Unable to perform GameStream connectivity check. Please check your Internet connection and try again.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(CONSOLE_OUT, "Testing GameStream connectivity on your local network...\n");
|
||||||
|
|
||||||
|
// Try to connect via LAN address
|
||||||
|
fprintf(LOG_OUT, "Testing GameStream ports via local network\n");
|
||||||
|
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");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(msgBuf, sizeof(msgBuf), "All tests passed! If Moonlight doesn't automatically connect outside your network, you can type the following address into Moonlight's Add PC dialog: %s", wanAddrStr);
|
bool igdDisconnected;
|
||||||
DisplayMessage(msgBuf, MpInfo);
|
SOCKADDR_IN locallyReportedWanAddr;
|
||||||
|
char wanAddrStr[INET6_ADDRSTRLEN];
|
||||||
|
|
||||||
return 0;
|
if (ss.ss_family == AF_INET) {
|
||||||
|
bool rulesFound;
|
||||||
|
|
||||||
|
fprintf(CONSOLE_OUT, "Detecting public IP address...\n");
|
||||||
|
|
||||||
|
if (!CheckWANAccess(&sin, &locallyReportedWanAddr, &rulesFound, &igdDisconnected)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect a double NAT by detecting STUN and and UPnP mismatches
|
||||||
|
if (sin.sin_addr.S_un.S_addr != locallyReportedWanAddr.sin_addr.S_un.S_addr) {
|
||||||
|
fprintf(LOG_OUT, "Testing GameStream ports via UPnP/NAT-PMP reported WAN address\n");
|
||||||
|
|
||||||
|
// We don't actually care about the outcome here but it's nice to have in logs
|
||||||
|
// to determine whether solving the double NAT will actually make Moonlight work.
|
||||||
|
TestAllPorts((PSOCKADDR_STORAGE)&locallyReportedWanAddr, &ss, portMsgBuf, sizeof(portMsgBuf), false, false);
|
||||||
|
|
||||||
|
fprintf(LOG_OUT, "Detected inconsistency between UPnP/NAT-PMP and STUN reported WAN addresses!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
inet_ntop(AF_INET, &sin.sin_addr, wanAddrStr, sizeof(wanAddrStr));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Go directly to the relay check if we have only IPv6 connectivity
|
||||||
|
igdDisconnected = false;
|
||||||
|
locallyReportedWanAddr = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct addrinfo hint = {};
|
||||||
|
struct addrinfo* result;
|
||||||
|
|
||||||
|
hint.ai_family = AF_UNSPEC;
|
||||||
|
hint.ai_flags = AI_ADDRCONFIG;
|
||||||
|
err = getaddrinfo("loopback-v2.moonlight-stream.org", NULL, &hint, &result);
|
||||||
|
if (err != 0 || result == NULL) {
|
||||||
|
fprintf(LOG_OUT, "getaddrinfo() failed: %d\n", err);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bool testServerWasReachable = false;
|
||||||
|
bool allPortsFailedOnV4 = true;
|
||||||
|
|
||||||
|
// First try the relay server over IPv4. If this passes, it's considered a full success
|
||||||
|
fprintf(LOG_OUT, "Testing GameStream ports via IPv4 loopback server\n");
|
||||||
|
for (struct addrinfo* current = result; current != NULL; current = current->ai_next) {
|
||||||
|
if (current->ai_family == AF_INET) {
|
||||||
|
fprintf(CONSOLE_OUT, "Testing GameStream connectivity over the Internet using a relay server...\n");
|
||||||
|
|
||||||
|
if (!IsTestServerReachable(current, 443)) {
|
||||||
|
fprintf(CONSOLE_OUT, "Skipping unreachable relay server...\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
testServerWasReachable = true;
|
||||||
|
|
||||||
|
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"
|
||||||
|
"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;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Tested against a working server and it failed
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If that fails, try the relay server over IPv6. If this passes, it will be a partial success
|
||||||
|
fprintf(LOG_OUT, "Testing GameStream ports via IPv6 loopback server\n");
|
||||||
|
for (struct addrinfo* current = result; current != NULL; current = current->ai_next) {
|
||||||
|
if (current->ai_family == AF_INET6) {
|
||||||
|
fprintf(CONSOLE_OUT, "Testing GameStream connectivity over the Internet using an IPv6 relay server...\n");
|
||||||
|
|
||||||
|
if (!IsTestServerReachable(current, 443)) {
|
||||||
|
fprintf(CONSOLE_OUT, "Skipping unreachable IPv6 relay server...\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
testServerWasReachable = true;
|
||||||
|
|
||||||
|
// 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, &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:
|
||||||
|
// 1) Double-NAT/CGN - indicates the connection is fundamentally limited to IPv6 for end-to-end connectivity
|
||||||
|
// 2) IPv6-only - indicates the connection is fundamentally limited to IPv6 for all connectivity
|
||||||
|
// 3) All ports failed the test with our IPv4 relay - This is one final heuristic to weed out IPv4 misconfigurations. If we have some ports open,
|
||||||
|
// it clearly indicates IPv4 support is possible, just currently misconfigured.
|
||||||
|
//
|
||||||
|
// Our last (implicit) heuristic is that we actually managed to establish an IPv6 connection. This means that there was either
|
||||||
|
// no IPv6 firewall (hopefully not) or that we were able to talk to a PCP/IGDv6 gateway to allow us through. Hopefully if we have
|
||||||
|
// a gateway that is unresponsive to UPnP/NAT-PMP, we wouldn't even be able to establish this connection so we would inherently fall
|
||||||
|
// to the checks below for IPv4 issues.
|
||||||
|
if (IsDoubleNAT(&locallyReportedWanAddr) || igdDisconnected || IsCGN(&locallyReportedWanAddr) || ss.ss_family == AF_INET6 || allPortsFailedOnV4) {
|
||||||
|
snprintf(msgBuf, sizeof(msgBuf), "This PC has limited connectivity for Internet hosting. It will work only for clients on certain networks.\n\n"
|
||||||
|
"If you want to try streaming with this configuration, you must pair Moonlight to your gaming PC from your home network before trying to stream over the Internet.\n\n"
|
||||||
|
"To get full connectivity, please contact your ISP and ask for a \"public IPv4 address\" which they may offer for free upon request. For more information and workarounds, click the Help button.");
|
||||||
|
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#limited-connectivity-for-hosting-error", MpWarn);
|
||||||
|
freeaddrinfo(result);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tested against a IPv6 working server
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(result);
|
||||||
|
|
||||||
|
if (!testServerWasReachable) {
|
||||||
|
snprintf(msgBuf, sizeof(msgBuf), "None of Moonlight's connection testing servers were reachable from your PC. Check your Internet connection and try again later.");
|
||||||
|
DisplayMessage(msgBuf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Many UPnP devices report IGD disconnected when double-NATed. If it was really offline,
|
||||||
|
// we probably would not have even gotten past STUN.
|
||||||
|
//
|
||||||
|
// We try to tell double-NAT from CGN by checking if IPv6 connectivity is available. If it
|
||||||
|
// is, we assume we're in a DS-Lite or similar configuration. If not, we'll assume it's a
|
||||||
|
// real double-NAT setup.
|
||||||
|
if (IsCGN(&locallyReportedWanAddr) || ((IsDoubleNAT(&locallyReportedWanAddr) || igdDisconnected) && hasV6Connectivity)) {
|
||||||
|
snprintf(msgBuf, sizeof(msgBuf), "Your ISP is running a Carrier-Grade NAT that is preventing you from hosting services like Moonlight on the Internet.\n\n"
|
||||||
|
"Ask your ISP for a \"public IPv4 address\" which they may offer for free upon request. For more information and workarounds, click the Help button.");
|
||||||
|
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#carrier-grade-nat-error");
|
||||||
|
}
|
||||||
|
else if ((IsDoubleNAT(&locallyReportedWanAddr) || igdDisconnected) /* && !hasV6Connectivity */) {
|
||||||
|
snprintf(msgBuf, sizeof(msgBuf), "Your router appears be connected to the Internet through another router. Click the Help button for guidance on fixing this issue.");
|
||||||
|
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#connected-through-another-router-error");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
snprintf(msgBuf, sizeof(msgBuf), "Internet GameStream connectivity check failed.\n\n"
|
||||||
|
"First, try restarting your router. If that fails, check that UPnP is enabled in your router settings. For more information and workarounds, click the Help button.\n\n"
|
||||||
|
"The following ports were not forwarded properly:\n%s", portMsgBuf);
|
||||||
|
DisplayMessage(msgBuf, "https://github.com/moonlight-stream/moonlight-docs/wiki/Internet-Streaming-Errors#internet-gamestream-connectivity-check-error");
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0">
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<!-- Windows 10 -->
|
||||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||||
|
<!-- Windows 8.1 -->
|
||||||
|
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||||
|
<!-- Windows 8 -->
|
||||||
|
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||||
|
<!-- Windows 7 -->
|
||||||
|
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||||
|
<!-- Windows Vista -->
|
||||||
|
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
</assembly>
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define _CRT_RAND_S
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#include <Iphlpapi.h>
|
||||||
|
#include <WinSock2.h>
|
||||||
|
|
||||||
|
#include "..\version.h"
|
||||||
|
|
||||||
|
#define CONSOLE_OUT stdout
|
||||||
|
#define LOG_OUT stderr
|
||||||
|
|
||||||
|
bool getExternalAddressPortIP4(unsigned short localPort, PSOCKADDR_IN wanAddr);
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION VER_VERSION
|
FILEVERSION VER_VERSION
|
||||||
|
PRODUCTVERSION VER_VERSION
|
||||||
BEGIN
|
BEGIN
|
||||||
BLOCK "StringFileInfo"
|
BLOCK "StringFileInfo"
|
||||||
BEGIN
|
BEGIN
|
||||||
@@ -11,6 +12,8 @@ BEGIN
|
|||||||
VALUE "CompanyName", VER_COMPANYNAME_STR
|
VALUE "CompanyName", VER_COMPANYNAME_STR
|
||||||
VALUE "FileDescription", "Moonlight Internet Streaming Tester"
|
VALUE "FileDescription", "Moonlight Internet Streaming Tester"
|
||||||
VALUE "ProductName", VER_PRODUCTNAME_STR
|
VALUE "ProductName", VER_PRODUCTNAME_STR
|
||||||
|
VALUE "FileVersion", VER_VERSION_STR
|
||||||
|
VALUE "ProductVersion", VER_VERSION_STR
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
|
|
||||||
|
|||||||
+45
-7
@@ -23,32 +23,32 @@
|
|||||||
<ProjectGuid>{8F78D8D2-A837-489C-BAC5-81494C9CEF7A}</ProjectGuid>
|
<ProjectGuid>{8F78D8D2-A837-489C-BAC5-81494C9CEF7A}</ProjectGuid>
|
||||||
<Keyword>Win32Proj</Keyword>
|
<Keyword>Win32Proj</Keyword>
|
||||||
<RootNamespace>mist</RootNamespace>
|
<RootNamespace>mist</RootNamespace>
|
||||||
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
<CharacterSet>NotSet</CharacterSet>
|
<CharacterSet>NotSet</CharacterSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
<CharacterSet>NotSet</CharacterSet>
|
<CharacterSet>NotSet</CharacterSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -93,13 +93,22 @@
|
|||||||
<PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>
|
||||||
</PrecompiledHeaderFile>
|
</PrecompiledHeaderFile>
|
||||||
<AdditionalIncludeDirectories>..\libs\include;..\libs\include\miniupnpc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>..\libs\include;..\libs\include\miniupnpc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||||
|
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||||
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<AdditionalLibraryDirectories>..\libs\x86\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
<AdditionalLibraryDirectories>..\libs\x86\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<CETCompat>true</CETCompat>
|
||||||
</Link>
|
</Link>
|
||||||
|
<Manifest>
|
||||||
|
<AdditionalManifestFiles>mist.exe.manifest</AdditionalManifestFiles>
|
||||||
|
</Manifest>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>copy /Y "$(SolutionDir)libs\x86\Debug\*" "$(TargetDir)"</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
@@ -110,11 +119,20 @@
|
|||||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||||
|
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||||
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<CETCompat>true</CETCompat>
|
||||||
</Link>
|
</Link>
|
||||||
|
<Manifest>
|
||||||
|
<AdditionalManifestFiles>mist.exe.manifest</AdditionalManifestFiles>
|
||||||
|
</Manifest>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>copy /Y "$(SolutionDir)libs\x86\Debug\*" "$(TargetDir)"</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
@@ -129,7 +147,8 @@
|
|||||||
<PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>
|
||||||
</PrecompiledHeaderFile>
|
</PrecompiledHeaderFile>
|
||||||
<AdditionalIncludeDirectories>..\libs\include;..\libs\include\miniupnpc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>..\libs\include;..\libs\include\miniupnpc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||||
|
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
@@ -137,7 +156,14 @@
|
|||||||
<OptimizeReferences>true</OptimizeReferences>
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<AdditionalLibraryDirectories>..\libs\x86\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
<AdditionalLibraryDirectories>..\libs\x86\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<CETCompat>true</CETCompat>
|
||||||
</Link>
|
</Link>
|
||||||
|
<Manifest>
|
||||||
|
<AdditionalManifestFiles>mist.exe.manifest</AdditionalManifestFiles>
|
||||||
|
</Manifest>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>copy /Y "$(SolutionDir)libs\x86\Release\*" "$(TargetDir)"</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
@@ -150,13 +176,21 @@
|
|||||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||||
|
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<CETCompat>true</CETCompat>
|
||||||
</Link>
|
</Link>
|
||||||
|
<Manifest>
|
||||||
|
<AdditionalManifestFiles>mist.exe.manifest</AdditionalManifestFiles>
|
||||||
|
</Manifest>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>copy /Y "$(SolutionDir)libs\x86\Release\*" "$(TargetDir)"</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="mist.cpp" />
|
<ClCompile Include="mist.cpp" />
|
||||||
@@ -167,6 +201,10 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\version.h" />
|
<ClInclude Include="..\version.h" />
|
||||||
|
<ClInclude Include="mist.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Manifest Include="mist.exe.manifest" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
|||||||
@@ -26,10 +26,18 @@
|
|||||||
<ClInclude Include="..\version.h">
|
<ClInclude Include="..\version.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="mist.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="mist.rc">
|
<ResourceCompile Include="mist.rc">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ResourceCompile>
|
</ResourceCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Manifest Include="mist.exe.manifest">
|
||||||
|
<Filter>Resource Files</Filter>
|
||||||
|
</Manifest>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
+93
-97
@@ -1,17 +1,10 @@
|
|||||||
#define _CRT_RAND_S
|
#include "mist.h"
|
||||||
#define _CRT_SECURE_NO_WARNINGS
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
|
||||||
#include <Windows.h>
|
|
||||||
#include <WinSock2.h>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#define STUN_PORT 3478
|
#define STUN_PORT 3478
|
||||||
|
|
||||||
#define STUN_RECV_TIMEOUT_SEC 3
|
#define STUN_RECV_TIMEOUT_SEC 5
|
||||||
|
|
||||||
#define STUN_MESSAGE_BINDING_REQUEST 0x0001
|
#define STUN_MESSAGE_BINDING_REQUEST 0x0001
|
||||||
#define STUN_MESSAGE_BINDING_SUCCESS 0x0101
|
#define STUN_MESSAGE_BINDING_SUCCESS 0x0101
|
||||||
@@ -45,41 +38,50 @@ typedef struct _STUN_MESSAGE {
|
|||||||
|
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN wanAddr)
|
bool getExternalAddressPortIP4(unsigned short localPort, PSOCKADDR_IN wanAddr)
|
||||||
{
|
{
|
||||||
SOCKET sock;
|
SOCKET sock;
|
||||||
STUN_MESSAGE reqMsg;
|
STUN_MESSAGE reqMsg;
|
||||||
int i;
|
int i;
|
||||||
int bytesRead;
|
int bytesRead;
|
||||||
int tries;
|
int err;
|
||||||
int timeout;
|
bool ret;
|
||||||
PSTUN_ATTRIBUTE_HEADER attribute;
|
PSTUN_ATTRIBUTE_HEADER attribute;
|
||||||
PSTUN_MAPPED_IPV4_ADDRESS_ATTRIBUTE ipv4Attrib;
|
PSTUN_MAPPED_IPV4_ADDRESS_ATTRIBUTE ipv4Attrib;
|
||||||
struct hostent *host;
|
struct addrinfo* result;
|
||||||
|
struct addrinfo hints;
|
||||||
|
struct sockaddr_in bindAddr;
|
||||||
union {
|
union {
|
||||||
STUN_MESSAGE hdr;
|
STUN_MESSAGE hdr;
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
} resp;
|
} resp;
|
||||||
|
|
||||||
host = gethostbyname("stun.stunprotocol.org");
|
sock = INVALID_SOCKET;
|
||||||
if (host == nullptr) {
|
ret = false;
|
||||||
printf("gethostbyname() failed: %d\n", WSAGetLastError());
|
|
||||||
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
hints.ai_family = AF_INET;
|
||||||
|
hints.ai_flags = AI_ADDRCONFIG;
|
||||||
|
hints.ai_socktype = SOCK_DGRAM;
|
||||||
|
hints.ai_protocol = IPPROTO_UDP;
|
||||||
|
err = getaddrinfo("stun.moonlight-stream.org", "3478", &hints, &result);
|
||||||
|
if (err != 0 || result == NULL) {
|
||||||
|
fprintf(LOG_OUT, "getaddrinfo() failed: %d\n", err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
sock = socket(AF_INET, proto == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM, proto);
|
sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
|
||||||
if (sock == INVALID_SOCKET) {
|
if (sock == INVALID_SOCKET) {
|
||||||
printf("socket() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "socket() failed: %d\n", WSAGetLastError());
|
||||||
return false;
|
goto Exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sockaddr_in bindAddr = {};
|
bindAddr = {};
|
||||||
bindAddr.sin_family = AF_INET;
|
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) {
|
||||||
printf("bind() failed: %d\n", WSAGetLastError());
|
fprintf(LOG_OUT, "bind() failed: %d\n", WSAGetLastError());
|
||||||
closesocket(sock);
|
goto Exit;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reqMsg.messageType = htons(STUN_MESSAGE_BINDING_REQUEST);
|
reqMsg.messageType = htons(STUN_MESSAGE_BINDING_REQUEST);
|
||||||
@@ -89,97 +91,80 @@ bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN
|
|||||||
reqMsg.transactionId[i] = rand();
|
reqMsg.transactionId[i] = rand();
|
||||||
}
|
}
|
||||||
|
|
||||||
SOCKADDR_IN stunAddr = {};
|
bytesRead = 0;
|
||||||
stunAddr.sin_family = AF_INET;
|
for (i = 0; i < STUN_RECV_TIMEOUT_SEC; i++) {
|
||||||
stunAddr.sin_port = htons(STUN_PORT);
|
// Retransmit the request every second to all resolved IP addresses until the timeout elapses
|
||||||
stunAddr.sin_addr = *(struct in_addr*)host->h_addr;
|
for (struct addrinfo* current = result; current != NULL; current = current->ai_next) {
|
||||||
|
if (sendto(sock, (char*)&reqMsg, sizeof(reqMsg), 0, current->ai_addr, current->ai_addrlen) == SOCKET_ERROR) {
|
||||||
// We'll connect() even for UDP so we can use send()/recv() and share more code
|
fprintf(LOG_OUT, "sendto() failed: %d\n", WSAGetLastError());
|
||||||
if (connect(sock, (struct sockaddr*)&stunAddr, sizeof(stunAddr)) == SOCKET_ERROR) {
|
}
|
||||||
printf("connect() failed: %d\n", WSAGetLastError());
|
|
||||||
closesocket(sock);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For UDP, we'll do 3 iterations of 1 second each. For TCP,
|
|
||||||
// we'll do one iteration with a 3 second wait.
|
|
||||||
if (proto == IPPROTO_TCP) {
|
|
||||||
tries = 1;
|
|
||||||
timeout = STUN_RECV_TIMEOUT_SEC;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
tries = STUN_RECV_TIMEOUT_SEC;
|
|
||||||
timeout = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytesRead = SOCKET_ERROR;
|
|
||||||
for (i = 0; i < tries; i++) {
|
|
||||||
// Retransmit the request every second until the timeout elapses
|
|
||||||
if (send(sock, (char *)&reqMsg, sizeof(reqMsg), 0) == SOCKET_ERROR) {
|
|
||||||
printf("send() failed: %d\n", WSAGetLastError());
|
|
||||||
closesocket(sock);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = timeout;
|
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) {
|
|
||||||
printf("select() failed: %d\n", WSAGetLastError());
|
|
||||||
closesocket(sock);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error handling is below
|
|
||||||
bytesRead = recv(sock, resp.buf, sizeof(resp.buf), 0);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
closesocket(sock);
|
RecvDone:
|
||||||
|
|
||||||
if (bytesRead == 0) {
|
if (bytesRead == 0) {
|
||||||
printf("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) {
|
||||||
printf("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)) {
|
||||||
printf("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) {
|
||||||
printf("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))) {
|
||||||
printf("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) {
|
||||||
printf("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);
|
||||||
bytesRead -= sizeof(resp.hdr);
|
bytesRead -= sizeof(resp.hdr);
|
||||||
while (bytesRead > sizeof(*attribute)) {
|
while (bytesRead > sizeof(*attribute)) {
|
||||||
if (bytesRead < sizeof(*attribute) + htons(attribute->length)) {
|
if (bytesRead < sizeof(*attribute) + htons(attribute->length)) {
|
||||||
printf("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;
|
||||||
}
|
}
|
||||||
else if (htons(attribute->type) != STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS) {
|
// Mask off the comprehension bit
|
||||||
|
else if ((htons(attribute->type) & 0x7FFF) != STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS) {
|
||||||
// Continue searching if this wasn't our address
|
// Continue searching if this wasn't our address
|
||||||
bytesRead -= sizeof(*attribute) + htons(attribute->length);
|
bytesRead -= sizeof(*attribute) + htons(attribute->length);
|
||||||
attribute = (PSTUN_ATTRIBUTE_HEADER)(((char*)attribute) + sizeof(*attribute) + htons(attribute->length));
|
attribute = (PSTUN_ATTRIBUTE_HEADER)(((char*)attribute) + sizeof(*attribute) + htons(attribute->length));
|
||||||
@@ -188,12 +173,12 @@ bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN
|
|||||||
|
|
||||||
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) {
|
||||||
printf("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) {
|
||||||
printf("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 = {};
|
||||||
@@ -203,9 +188,20 @@ bool getExternalAddressPortIP4(int proto, unsigned short localPort, PSOCKADDR_IN
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("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 2,1,0,0
|
#define VER_VERSION 5,6,1,0
|
||||||
#define VER_VERSION_STR "2.1.0.0"
|
#define VER_VERSION_STR "5.6.1.0"
|
||||||
|
|
||||||
#define VER_COMPANYNAME_STR "Moonlight Game Streaming Project"
|
#define VER_COMPANYNAME_STR "Moonlight Game Streaming Project"
|
||||||
#define VER_PRODUCTNAME_STR "Moonlight Internet Streaming Helper"
|
#define VER_PRODUCTNAME_STR "Moonlight Internet Hosting Tool"
|
||||||
|
|||||||
Reference in New Issue
Block a user