mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2026-02-16 10:31:07 +00:00
137 lines
5.0 KiB
Java
137 lines
5.0 KiB
Java
package com.limelight.nvstream.wol;
|
|
|
|
import java.io.IOException;
|
|
import java.net.DatagramPacket;
|
|
import java.net.DatagramSocket;
|
|
import java.net.InetAddress;
|
|
import java.util.Scanner;
|
|
|
|
import com.limelight.LimeLog;
|
|
import com.limelight.nvstream.http.ComputerDetails;
|
|
|
|
public class WakeOnLanSender {
|
|
// These ports will always be tried as-is.
|
|
private static final int[] STATIC_PORTS_TO_TRY = new int[] {
|
|
9, // Standard WOL port (privileged port)
|
|
47009, // Port opened by Moonlight Internet Hosting Tool for WoL (non-privileged port)
|
|
};
|
|
|
|
// These ports will be offset by the base port number (47989) to support alternate ports.
|
|
private static final int[] DYNAMIC_PORTS_TO_TRY = new int[] {
|
|
47998, 47999, 48000, 48002, 48010, // Ports opened by GFE
|
|
};
|
|
|
|
private static void sendPacketsForAddress(InetAddress address, int httpPort, DatagramSocket sock, byte[] payload) throws IOException {
|
|
IOException lastException = null;
|
|
boolean sentWolPacket = false;
|
|
|
|
// Try the static ports
|
|
for (int port : STATIC_PORTS_TO_TRY) {
|
|
try {
|
|
DatagramPacket dp = new DatagramPacket(payload, payload.length);
|
|
dp.setAddress(address);
|
|
dp.setPort(port);
|
|
sock.send(dp);
|
|
sentWolPacket = true;
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
lastException = e;
|
|
}
|
|
}
|
|
|
|
// Try the dynamic ports
|
|
for (int port : DYNAMIC_PORTS_TO_TRY) {
|
|
try {
|
|
DatagramPacket dp = new DatagramPacket(payload, payload.length);
|
|
dp.setAddress(address);
|
|
dp.setPort((port - 47989) + httpPort);
|
|
sock.send(dp);
|
|
sentWolPacket = true;
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
lastException = e;
|
|
}
|
|
}
|
|
|
|
if (!sentWolPacket) {
|
|
throw lastException;
|
|
}
|
|
}
|
|
|
|
public static void sendWolPacket(ComputerDetails computer) throws IOException {
|
|
byte[] payload = createWolPayload(computer);
|
|
IOException lastException = null;
|
|
boolean sentWolPacket = false;
|
|
|
|
try (final DatagramSocket sock = new DatagramSocket(0)) {
|
|
// Try all resolved remote and local addresses and broadcast addresses.
|
|
// The broadcast address is required to avoid stale ARP cache entries
|
|
// making the sleeping machine unreachable.
|
|
for (ComputerDetails.AddressTuple address : new ComputerDetails.AddressTuple[] {
|
|
computer.localAddress, computer.remoteAddress,
|
|
computer.manualAddress, computer.ipv6Address,
|
|
}) {
|
|
if (address == null) {
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
sendPacketsForAddress(InetAddress.getByName("255.255.255.255"), address.port, sock, payload);
|
|
for (InetAddress resolvedAddress : InetAddress.getAllByName(address.address)) {
|
|
sendPacketsForAddress(resolvedAddress, address.port, sock, payload);
|
|
}
|
|
} catch (IOException e) {
|
|
// We may have addresses that don't resolve on this subnet,
|
|
// but don't throw and exit the whole function if that happens.
|
|
// We'll throw it at the end if we didn't send a single packet.
|
|
e.printStackTrace();
|
|
lastException = e;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Propagate the DNS resolution exception if we didn't
|
|
// manage to get a single packet out to the host.
|
|
if (!sentWolPacket && lastException != null) {
|
|
throw lastException;
|
|
}
|
|
}
|
|
|
|
private static byte[] macStringToBytes(String macAddress) {
|
|
byte[] macBytes = new byte[6];
|
|
|
|
try (@SuppressWarnings("resource")
|
|
final Scanner scan = new Scanner(macAddress).useDelimiter(":")
|
|
) {
|
|
for (int i = 0; i < macBytes.length && scan.hasNext(); i++) {
|
|
try {
|
|
macBytes[i] = (byte) Integer.parseInt(scan.next(), 16);
|
|
} catch (NumberFormatException e) {
|
|
LimeLog.warning("Malformed MAC address: " + macAddress + " (index: " + i + ")");
|
|
break;
|
|
}
|
|
}
|
|
return macBytes;
|
|
}
|
|
}
|
|
|
|
private static byte[] createWolPayload(ComputerDetails computer) {
|
|
byte[] payload = new byte[102];
|
|
byte[] macAddress = macStringToBytes(computer.macAddress);
|
|
int i;
|
|
|
|
// 6 bytes of FF
|
|
for (i = 0; i < 6; i++) {
|
|
payload[i] = (byte)0xFF;
|
|
}
|
|
|
|
// 16 repetitions of the MAC address
|
|
for (int j = 0; j < 16; j++) {
|
|
System.arraycopy(macAddress, 0, payload, i, macAddress.length);
|
|
i += macAddress.length;
|
|
}
|
|
|
|
return payload;
|
|
}
|
|
}
|