diff options
Diffstat (limited to 'src/main/java/org/openslx/satserver/util/WakeOnLanExecutor.java')
-rw-r--r-- | src/main/java/org/openslx/satserver/util/WakeOnLanExecutor.java | 136 |
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() ] ); + } + +} |