mirror of
https://github.com/moonlight-stream/Internet-Hosting-Tool.git
synced 2026-06-18 14:41:04 +00:00
Implement best-effort forwarding for Wake-on-LAN
This commit is contained in:
+134
-9
@@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
bool getHopsIP4(IN_ADDR* hopAddress, int* hopAddressCount);
|
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 PCPMapPort(PSOCKADDR_STORAGE localAddr, int localAddrLen, PSOCKADDR_STORAGE pcpAddr, int pcpAddrLen, int proto, int port, bool enable, bool indefinite);
|
||||||
|
|
||||||
#define NL "\n"
|
#define NL "\n"
|
||||||
|
|
||||||
@@ -53,6 +53,8 @@ static struct port_entry {
|
|||||||
{IPPROTO_UDP, 48010}
|
{IPPROTO_UDP, 48010}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const int k_WolPorts[] = { 7, 9 };
|
||||||
|
|
||||||
void UPnPCreatePinholeForPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const char* myAddr, int port)
|
void UPnPCreatePinholeForPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const char* myAddr, int port)
|
||||||
{
|
{
|
||||||
char uniqueId[8];
|
char uniqueId[8];
|
||||||
@@ -74,7 +76,7 @@ void UPnPCreatePinholeForPort(struct UPNPUrls* urls, struct IGDdatas* data, int
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const char* myAddr, int port, bool enable)
|
bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const char* myAddr, int port, bool enable, bool indefinite)
|
||||||
{
|
{
|
||||||
char intClient[16];
|
char intClient[16];
|
||||||
char intPort[6];
|
char intPort[6];
|
||||||
@@ -172,12 +174,13 @@ bool UPnPMapPort(struct UPNPUrls* urls, struct IGDdatas* data, int proto, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create or update the expiration time of an existing mapping
|
// Create or update the expiration time of an existing mapping
|
||||||
snprintf(leaseDuration, sizeof(leaseDuration), "%d", PORT_MAPPING_DURATION_SEC);
|
snprintf(leaseDuration, sizeof(leaseDuration), "%d",
|
||||||
|
indefinite ? 0 : PORT_MAPPING_DURATION_SEC);
|
||||||
printf("Updating UPnP port mapping for %s %s -> %s...", protoStr, portStr, myAddr);
|
printf("Updating UPnP port mapping for %s %s -> %s...", protoStr, portStr, myAddr);
|
||||||
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) { // OnlyPermanentLeasesSupported
|
if (err == 725 && !indefinite) { // OnlyPermanentLeasesSupported
|
||||||
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");
|
||||||
@@ -280,6 +283,59 @@ bool ResolveStableIP6Address(char* tmpAddr)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GetIP4OnLinkPrefixLength(char* lanAddressString, int* prefixLength)
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
IP_ADAPTER_ADDRESSES addresses;
|
||||||
|
char buffer[8192];
|
||||||
|
};
|
||||||
|
ULONG error;
|
||||||
|
ULONG length;
|
||||||
|
PIP_ADAPTER_ADDRESSES currentAdapter;
|
||||||
|
PIP_ADAPTER_UNICAST_ADDRESS currentAddress;
|
||||||
|
in_addr targetAddress;
|
||||||
|
|
||||||
|
inet_pton(AF_INET, lanAddressString, &targetAddress);
|
||||||
|
|
||||||
|
// Get a list of all interfaces with IPv4 addresses on the system
|
||||||
|
length = sizeof(buffer);
|
||||||
|
error = GetAdaptersAddresses(AF_INET,
|
||||||
|
GAA_FLAG_SKIP_ANYCAST |
|
||||||
|
GAA_FLAG_SKIP_MULTICAST |
|
||||||
|
GAA_FLAG_SKIP_DNS_SERVER |
|
||||||
|
GAA_FLAG_SKIP_FRIENDLY_NAME,
|
||||||
|
NULL,
|
||||||
|
&addresses,
|
||||||
|
&length);
|
||||||
|
if (error != ERROR_SUCCESS) {
|
||||||
|
printf("GetAdaptersAddresses() failed: %d" NL, error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAdapter = &addresses;
|
||||||
|
currentAddress = nullptr;
|
||||||
|
while (currentAdapter != nullptr) {
|
||||||
|
currentAddress = currentAdapter->FirstUnicastAddress;
|
||||||
|
while (currentAddress != nullptr) {
|
||||||
|
assert(currentAddress->Address.lpSockaddr->sa_family == AF_INET);
|
||||||
|
|
||||||
|
PSOCKADDR_IN currentAddrV4 = (PSOCKADDR_IN)currentAddress->Address.lpSockaddr;
|
||||||
|
|
||||||
|
if (RtlEqualMemory(¤tAddrV4->sin_addr, &targetAddress, sizeof(targetAddress))) {
|
||||||
|
*prefixLength = currentAddress->OnLinkPrefixLength;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAddress = currentAddress->Next;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAdapter = currentAdapter->Next;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("No adapter found with IPv4 address: %s" NL, lanAddressString);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool UPnPHandleDeviceList(struct UPNPDev* list, bool ipv6, bool enable, char* lanAddrOverride, char* wanAddr)
|
bool UPnPHandleDeviceList(struct UPNPDev* list, bool ipv6, bool enable, char* lanAddrOverride, char* wanAddr)
|
||||||
{
|
{
|
||||||
struct UPNPUrls urls;
|
struct UPNPUrls urls;
|
||||||
@@ -359,7 +415,7 @@ bool UPnPHandleDeviceList(struct UPNPDev* list, bool ipv6, bool enable, char* la
|
|||||||
|
|
||||||
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
||||||
if (!ipv6) {
|
if (!ipv6) {
|
||||||
if (!UPnPMapPort(&urls, &data, k_Ports[i].proto, portMappingInternalAddress, k_Ports[i].port, enable)) {
|
if (!UPnPMapPort(&urls, &data, k_Ports[i].proto, portMappingInternalAddress, k_Ports[i].port, enable, false)) {
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -368,11 +424,42 @@ bool UPnPHandleDeviceList(struct UPNPDev* list, bool ipv6, bool enable, char* la
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do a best-effort for IPv4 Wake-on-LAN broadcast mappings
|
||||||
|
if (!ipv6) {
|
||||||
|
for (int i = 0; i < ARRAYSIZE(k_WolPorts); i++) {
|
||||||
|
if (lanAddrOverride == nullptr) {
|
||||||
|
// Map the port to the broadcast address (may not work on all routers). This
|
||||||
|
// ensures delivery even after the ARP entry for this PC times out on the router.
|
||||||
|
int onLinkPrefixLen;
|
||||||
|
if (GetIP4OnLinkPrefixLength(localAddress, &onLinkPrefixLen)) {
|
||||||
|
int netmask = 0;
|
||||||
|
for (int j = 0; j < onLinkPrefixLen; j++) {
|
||||||
|
netmask |= (1 << j);
|
||||||
|
}
|
||||||
|
|
||||||
|
in_addr broadcastAddr;
|
||||||
|
broadcastAddr.S_un.S_addr = inet_addr(localAddress);
|
||||||
|
broadcastAddr.S_un.S_addr |= ~netmask;
|
||||||
|
|
||||||
|
char broadcastAddrStr[128];
|
||||||
|
inet_ntop(AF_INET, &broadcastAddr, broadcastAddrStr, sizeof(broadcastAddrStr));
|
||||||
|
|
||||||
|
UPnPMapPort(&urls, &data, IPPROTO_UDP, broadcastAddrStr, k_WolPorts[i], enable, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// When we're mapping the WOL ports upstream of our router, we map directly to
|
||||||
|
// the port on the upstream address (likely our router's WAN interface).
|
||||||
|
UPnPMapPort(&urls, &data, IPPROTO_UDP, lanAddrOverride, k_WolPorts[i], enable, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FreeUPNPUrls(&urls);
|
FreeUPNPUrls(&urls);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NATPMPMapPort(natpmp_t* natpmp, int proto, int port, bool enable)
|
bool NATPMPMapPort(natpmp_t* natpmp, int proto, int port, bool enable, bool indefinite)
|
||||||
{
|
{
|
||||||
int natPmpProto;
|
int natPmpProto;
|
||||||
|
|
||||||
@@ -389,8 +476,20 @@ bool NATPMPMapPort(natpmp_t* natpmp, int proto, int port, bool enable)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int lifetime;
|
||||||
|
|
||||||
|
if (!enable) {
|
||||||
|
lifetime = 0;
|
||||||
|
}
|
||||||
|
else if (indefinite) {
|
||||||
|
lifetime = 604800; // 1 week
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lifetime = 3600;
|
||||||
|
}
|
||||||
|
|
||||||
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, enable ? PORT_MAPPING_DURATION_SEC : 0);
|
int err = sendnewportmappingrequest(natpmp, natPmpProto, port, enable ? port : 0, lifetime);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
printf("ERROR %d" NL, err);
|
printf("ERROR %d" NL, err);
|
||||||
return false;
|
return false;
|
||||||
@@ -620,10 +719,22 @@ void UpdatePortMappingsForTarget(bool enable, char* targetAddressIP4, char* inte
|
|||||||
if (tryNatPmp) {
|
if (tryNatPmp) {
|
||||||
bool success = true;
|
bool success = true;
|
||||||
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
||||||
if (!NATPMPMapPort(&natpmp, k_Ports[i].proto, k_Ports[i].port, enable)) {
|
if (!NATPMPMapPort(&natpmp, k_Ports[i].proto, k_Ports[i].port, enable, false)) {
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We can only map ports for the non-default gateway case because
|
||||||
|
// it will use our LAN address as the internal client address, which
|
||||||
|
// doesn't work (needs to be broadcast) for the last hop.
|
||||||
|
if (targetAddressIP4 != nullptr) {
|
||||||
|
// Best effort, don't care if we fail for WOL
|
||||||
|
for (int i = 0; i < ARRAYSIZE(k_WolPorts); i++) {
|
||||||
|
// Indefinite mapping since we may not be awake to refresh it
|
||||||
|
NATPMPMapPort(&natpmp, IPPROTO_UDP, k_WolPorts[i], enable, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
printf("NAT-PMP IPv4 port mapping successful" NL);
|
printf("NAT-PMP IPv4 port mapping successful" NL);
|
||||||
tryPcp = false;
|
tryPcp = false;
|
||||||
@@ -647,10 +758,24 @@ void UpdatePortMappingsForTarget(bool enable, char* targetAddressIP4, char* inte
|
|||||||
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
||||||
if (!PCPMapPort((PSOCKADDR_STORAGE)&internalAddr, sizeof(internalAddr),
|
if (!PCPMapPort((PSOCKADDR_STORAGE)&internalAddr, sizeof(internalAddr),
|
||||||
(PSOCKADDR_STORAGE)&targetAddr, sizeof(targetAddr),
|
(PSOCKADDR_STORAGE)&targetAddr, sizeof(targetAddr),
|
||||||
k_Ports[i].proto, k_Ports[i].port, enable)) {
|
k_Ports[i].proto, k_Ports[i].port, enable, false)) {
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We can only map ports for the non-default gateway case because
|
||||||
|
// it will use our internal address as the internal client address, which
|
||||||
|
// doesn't work (needs to be broadcast) for the last hop.
|
||||||
|
if (internalAddressIP4 != nullptr) {
|
||||||
|
// Best effort, don't care if we fail for WOL
|
||||||
|
for (int i = 0; i < ARRAYSIZE(k_WolPorts); i++) {
|
||||||
|
// Indefinite mapping since we may not be awake to refresh it
|
||||||
|
PCPMapPort((PSOCKADDR_STORAGE)&internalAddr, sizeof(internalAddr),
|
||||||
|
(PSOCKADDR_STORAGE)&targetAddr, sizeof(targetAddr),
|
||||||
|
IPPROTO_UDP, k_WolPorts[i], enable, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
printf("PCP IPv4 port mapping successful" NL);
|
printf("PCP IPv4 port mapping successful" NL);
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-2
@@ -109,7 +109,7 @@ static void populateAddressFromSockAddr(PSOCKADDR_STORAGE sockAddr, unsigned cha
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PCPMapPort(PSOCKADDR_STORAGE localAddr, int localAddrLen, PSOCKADDR_STORAGE pcpAddr, int pcpAddrLen, int proto, int port, bool enable)
|
bool PCPMapPort(PSOCKADDR_STORAGE localAddr, int localAddrLen, PSOCKADDR_STORAGE pcpAddr, int pcpAddrLen, int proto, int port, bool enable, bool indefinite)
|
||||||
{
|
{
|
||||||
SOCKET sock;
|
SOCKET sock;
|
||||||
PCP_MAP_REQUEST reqMsg;
|
PCP_MAP_REQUEST reqMsg;
|
||||||
@@ -120,6 +120,17 @@ bool PCPMapPort(PSOCKADDR_STORAGE localAddr, int localAddrLen, PSOCKADDR_STORAGE
|
|||||||
PCP_MAP_RESPONSE hdr;
|
PCP_MAP_RESPONSE hdr;
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
} resp;
|
} resp;
|
||||||
|
int lifetime;
|
||||||
|
|
||||||
|
if (!enable) {
|
||||||
|
lifetime = 0;
|
||||||
|
}
|
||||||
|
else if (indefinite) {
|
||||||
|
lifetime = 604800; // 1 week
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lifetime = 3600;
|
||||||
|
}
|
||||||
|
|
||||||
assert(localAddr->ss_family == pcpAddr->ss_family);
|
assert(localAddr->ss_family == pcpAddr->ss_family);
|
||||||
|
|
||||||
@@ -152,7 +163,7 @@ bool PCPMapPort(PSOCKADDR_STORAGE localAddr, int localAddrLen, PSOCKADDR_STORAGE
|
|||||||
reqMsg = {};
|
reqMsg = {};
|
||||||
reqMsg.hdr.version = PCP_VERSION;
|
reqMsg.hdr.version = PCP_VERSION;
|
||||||
reqMsg.hdr.opcode = OPCODE_MAP_REQUEST;
|
reqMsg.hdr.opcode = OPCODE_MAP_REQUEST;
|
||||||
reqMsg.hdr.lifetime = htonl(enable ? 3600 : 0);
|
reqMsg.hdr.lifetime = htonl(lifetime);
|
||||||
populateAddressFromSockAddr(localAddr, reqMsg.hdr.localAddress);
|
populateAddressFromSockAddr(localAddr, reqMsg.hdr.localAddress);
|
||||||
|
|
||||||
reqMsg.protocol = proto;
|
reqMsg.protocol = proto;
|
||||||
|
|||||||
Reference in New Issue
Block a user