summaryrefslogtreecommitdiffstats
path: root/src/main/java/org/openslx/satserver/util/WakeOnLanExecutor.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/openslx/satserver/util/WakeOnLanExecutor.java')
-rw-r--r--src/main/java/org/openslx/satserver/util/WakeOnLanExecutor.java136
1 files changed, 136 insertions, 0 deletions
diff --git a/src/main/java/org/openslx/satserver/util/WakeOnLanExecutor.java b/src/main/java/org/openslx/satserver/util/WakeOnLanExecutor.java
new file mode 100644
index 0000000..1ee2f81
--- /dev/null
+++ b/src/main/java/org/openslx/satserver/util/WakeOnLanExecutor.java
@@ -0,0 +1,136 @@
+package org.openslx.satserver.util;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class WakeOnLanExecutor
+{
+
+ private static final Pattern RE_IPv4 = Pattern.compile( "^(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)$" );
+ private static final Pattern RE_MAC = Pattern.compile( "^([0-9a-f]{2})[:-]([0-9a-f]{2}+)[:-]([0-9a-f]{2}+)[:-]([0-9a-f]{2}+)[:-]([0-9a-f]{2}+)[:-]([0-9a-f]{2}+)$",
+ Pattern.CASE_INSENSITIVE );
+
+ private byte[] buffer;
+
+ public WakeOnLanExecutor( String password ) throws RuntimeException
+ {
+ Matcher m = null;
+ int pwlen = 0;
+ if ( !Util.isEmpty( password ) ) {
+ m = RE_IPv4.matcher( password );
+ if ( m.matches() ) {
+ pwlen = 4;
+ } else {
+ m = RE_MAC.matcher( password );
+ if ( m.matches() ) {
+ pwlen = 6;
+ }
+ }
+ if ( pwlen == 0 ) {
+ throw new RuntimeException( "Invalid password format: " + password );
+ }
+ }
+ buffer = new byte[ 17 * 6 + pwlen ];
+ // 6 Sync bytes
+ for ( int i = 0; i < 6; ++i ) {
+ buffer[i] = -1;
+ }
+ if ( pwlen != 0 ) {
+ // Append pw
+ try {
+ for ( int i = 0; i < pwlen; ++i ) {
+ int x = Integer.parseInt( m.group( i + 1 ), pwlen == 4 ? 10 : 16 );
+ buffer[17 * 6 + i] = (byte)x;
+ }
+ } catch ( Throwable t ) {
+ throw new RuntimeException( "Invalid octet in password" );
+ }
+ }
+ }
+
+ /**
+ * Send out a bunch of WOL packets to the given list of MAC addresses. Every packet will be
+ * directed to given ip, i.e. all destination MACs have to be in the same subnet.
+ *
+ * @param log For adding log outout
+ * @param macs list of MAC addresses
+ * @param ip destination IP (broadcast or directed broadcast address)
+ * @param port destination port, usually 9
+ * @return list of MACs the packet has successfully been sent to
+ */
+ public String[] execute( MessageSink log, Collection<String> macs, String ip, int port )
+ {
+ if ( port == 0 ) {
+ port = 9;
+ } else if ( port > 65535 || port < 0 ) {
+ log.addMsg( "Invalid port: " + port + " for " + ip );
+ return new String[ 0 ];
+ }
+
+ DatagramSocket sock = null;
+ InetAddress destAddr = null;
+ Set<String> success = new HashSet<>();
+ try {
+ try {
+ destAddr = InetAddress.getByName( ip );
+ sock = new DatagramSocket();
+ sock.setBroadcast( true );
+ } catch ( SocketException e ) {
+ log.addMsg( "Cannot create UDP socket" );
+ return new String[ 0 ];
+ } catch ( UnknownHostException e ) {
+ log.addMsg( "Cannot resolve " + ip );
+ return new String[ 0 ];
+ }
+
+ // Repeat three times
+ for ( int reps = 0; reps < 3; ++reps ) {
+ if ( reps != 0 ) {
+ Thread.sleep( 400 );
+ }
+ // For each MAC, send WOL packet
+ for ( String mac : macs ) {
+ Matcher m = RE_MAC.matcher( mac );
+ if ( !m.matches() ) {
+ log.addMsg( "Cannot parse MAC address " + mac );
+ continue;
+ }
+ try {
+ // Patch in the 16 repetitions of the target mac address
+ for ( int i = 0; i < 6; ++i ) {
+ byte x = (byte)Integer.parseInt( m.group( i + 1 ), 16 );
+ for ( int offset = 0; offset < 16; ++offset ) {
+ buffer[6 + offset * 6 + i] = x;
+ }
+ }
+ DatagramPacket dp = new DatagramPacket( buffer, buffer.length, destAddr, port );
+ sock.send( dp );
+ log.addMsg( "Sent packet to " + mac );
+ success.add( mac );
+ Thread.sleep( 10 );
+ } catch ( NumberFormatException e ) {
+ log.addMsg( "Invalid octet in MAC address, skipping: " + mac );
+ } catch ( IOException e ) {
+ log.addMsg( "Error sending UDP packet to " + ip + "/" + mac + ": " + e.toString() );
+ }
+ }
+ }
+ } catch ( InterruptedException t ) {
+ Thread.currentThread().interrupt();
+ } finally {
+ Util.multiClose( sock );
+ }
+
+ return success.toArray( new String[ success.size() ] );
+ }
+
+}