mirror of
https://github.com/moonlight-stream/Internet-Hosting-Tool.git
synced 2026-04-19 14:41:27 +00:00
Implement best-effort forwarding for Wake-on-LAN
This commit is contained in:
143
miss/miss.cpp
143
miss/miss.cpp
@@ -29,7 +29,7 @@
|
||||
|
||||
bool getHopsIP4(IN_ADDR* hopAddress, int* hopAddressCount);
|
||||
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"
|
||||
|
||||
@@ -53,6 +53,8 @@ static struct port_entry {
|
||||
{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)
|
||||
{
|
||||
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 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
|
||||
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);
|
||||
err = UPNP_AddPortMapping(
|
||||
urls->controlURL, data->first.servicetype, portStr,
|
||||
portStr, myAddr, myDesc, protoStr, nullptr, leaseDuration);
|
||||
if (err == 725) { // OnlyPermanentLeasesSupported
|
||||
if (err == 725 && !indefinite) { // OnlyPermanentLeasesSupported
|
||||
err = UPNP_AddPortMapping(
|
||||
urls->controlURL, data->first.servicetype, portStr,
|
||||
portStr, myAddr, myDesc, protoStr, nullptr, "0");
|
||||
@@ -280,6 +283,59 @@ bool ResolveStableIP6Address(char* tmpAddr)
|
||||
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)
|
||||
{
|
||||
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++) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
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;
|
||||
|
||||
@@ -389,8 +476,20 @@ bool NATPMPMapPort(natpmp_t* natpmp, int proto, int port, bool enable)
|
||||
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);
|
||||
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) {
|
||||
printf("ERROR %d" NL, err);
|
||||
return false;
|
||||
@@ -620,10 +719,22 @@ void UpdatePortMappingsForTarget(bool enable, char* targetAddressIP4, char* inte
|
||||
if (tryNatPmp) {
|
||||
bool success = true;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
printf("NAT-PMP IPv4 port mapping successful" NL);
|
||||
tryPcp = false;
|
||||
@@ -647,10 +758,24 @@ void UpdatePortMappingsForTarget(bool enable, char* targetAddressIP4, char* inte
|
||||
for (int i = 0; i < ARRAYSIZE(k_Ports); i++) {
|
||||
if (!PCPMapPort((PSOCKADDR_STORAGE)&internalAddr, sizeof(internalAddr),
|
||||
(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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
printf("PCP IPv4 port mapping successful" NL);
|
||||
}
|
||||
|
||||
15
miss/pcp.cpp
15
miss/pcp.cpp
@@ -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;
|
||||
PCP_MAP_REQUEST reqMsg;
|
||||
@@ -120,6 +120,17 @@ bool PCPMapPort(PSOCKADDR_STORAGE localAddr, int localAddrLen, PSOCKADDR_STORAGE
|
||||
PCP_MAP_RESPONSE hdr;
|
||||
char buf[1024];
|
||||
} resp;
|
||||
int lifetime;
|
||||
|
||||
if (!enable) {
|
||||
lifetime = 0;
|
||||
}
|
||||
else if (indefinite) {
|
||||
lifetime = 604800; // 1 week
|
||||
}
|
||||
else {
|
||||
lifetime = 3600;
|
||||
}
|
||||
|
||||
assert(localAddr->ss_family == pcpAddr->ss_family);
|
||||
|
||||
@@ -152,7 +163,7 @@ bool PCPMapPort(PSOCKADDR_STORAGE localAddr, int localAddrLen, PSOCKADDR_STORAGE
|
||||
reqMsg = {};
|
||||
reqMsg.hdr.version = PCP_VERSION;
|
||||
reqMsg.hdr.opcode = OPCODE_MAP_REQUEST;
|
||||
reqMsg.hdr.lifetime = htonl(enable ? 3600 : 0);
|
||||
reqMsg.hdr.lifetime = htonl(lifetime);
|
||||
populateAddressFromSockAddr(localAddr, reqMsg.hdr.localAddress);
|
||||
|
||||
reqMsg.protocol = proto;
|
||||
|
||||
Reference in New Issue
Block a user