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 output * @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 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 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() ] ); } }