davidgraeff
2/3/2017 - 3:05 AM

Proxy to milight v6 bridge

Proxy to milight v6 bridge

package testing_delete;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.Enumeration;

public class ProxyCommunication {
    boolean willbeclosed = false;
    InetAddress milight;
    byte milightMAC[] = { 0, 0, 0, 0, 0, 0 };

    String networkInterfaceName;
    InetAddress localAddr;
    InetAddress appAddr;
    int appPort;
    byte fakeMAC[] = { (byte) 0xAC, (byte) 0xCF, (byte) 0x23, (byte) 0xF5, (byte) 0x7A, (byte) 0xD4 };
    DatagramSocket socketFromAppToProxy, socketFromBridgeToProxy;
    FileOutputStream file;

    public void tests() {
        try {
            if (isPacketFromBridge("10.1.1.27,ACCF23F57AD4,HF-LPB100")) {
                String remote_id = String.format("%02X%02X%02X%02X%02X%02X", milightMAC[0], milightMAC[1],
                        milightMAC[2], milightMAC[3], milightMAC[4], milightMAC[5]);
                System.out.println("works! " + milight.toString() + " " + remote_id);
            }
        } catch (UnknownHostException e1) {
            e1.printStackTrace();
        }
    }

    public void start(NetworkInterface networkInterface)
            throws FileNotFoundException, SocketException, UnknownHostException {
        Enumeration<InetAddress> e = networkInterface.getInetAddresses();
        while (e.hasMoreElements()) {
            InetAddress addr = e.nextElement();
            if (addr instanceof Inet6Address) {
                continue;
            }
            this.localAddr = addr;
            break;
        }
        if (this.localAddr == null) {
            System.err.println("## No ipv4 found on network interface " + networkInterfaceName);
            return;
        }

        System.out.println("## Your IP is " + localAddr.getHostAddress() + " on " + networkInterfaceName);

        milight = InetAddress.getByName("255.255.255.255");

        if (!runRealBridgeDiscovery()) {
            System.err.println("No iBox found!");
            return;
        }

        start(milight, networkInterface.getName());
    }

    public void start(InetAddress destIP, String networkInterfaceName)
            throws FileNotFoundException, SocketException, UnknownHostException {
        this.networkInterfaceName = networkInterfaceName;
        this.localAddr = InetAddress.getByName("0.0.0.0");
        milight = destIP;

        String remote_id = String.format("%02X%02X%02X%02X%02X%02X", milightMAC[0], milightMAC[1], milightMAC[2],
                milightMAC[3], milightMAC[4], milightMAC[5]);
        System.out.println("## Your iBox is at " + milight.toString() + " BridgeID: " + remote_id);

        file = new FileOutputStream("milight_" + networkInterfaceName + ".txt");

        socketFromAppToProxy = new DatagramSocket(null);
        socketFromAppToProxy.setReuseAddress(true);
        socketFromAppToProxy.bind(new InetSocketAddress(5987));
        socketFromBridgeToProxy = new DatagramSocket();

        new Thread(new Runnable() {
            @Override
            public void run() {
                runReadFromApp();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                runReadFromBridge();
            }
        }).start();
    }

    public boolean runRealBridgeDiscovery() {
        byte[] discoverbuffer_v6 = "HF-A11ASSISTHREAD".getBytes();

        try {
            System.out.println("## Discovery receive thread ready on " + networkInterfaceName);
            final DatagramPacket discoverPacket_v6 = new DatagramPacket(discoverbuffer_v6, discoverbuffer_v6.length,
                    milight, 48899);
            DatagramSocket datagramSocket = new DatagramSocket();
            datagramSocket.setSoTimeout(1000);
            byte[] buffer = new byte[1024];
            DatagramPacket r_packet = new DatagramPacket(buffer, buffer.length);

            // Now loop forever, waiting to receive packets and printing them.
            while (!willbeclosed) {
                System.out.println("Find iBox bridge on " + networkInterfaceName);
                datagramSocket.send(discoverPacket_v6);
                try {
                    r_packet.setLength(buffer.length);
                    datagramSocket.receive(r_packet);
                } catch (SocketTimeoutException e) {
                    continue;
                }
                if (isPacketFromBridge(new String(r_packet.getData()))) {
                    return true;
                }
            }
        } catch (IOException e) {
            if (willbeclosed) {
                return false;
            }
            System.out.println(e.getLocalizedMessage());
        }
        return false;
    }

    private boolean isPacketFromBridge(String data) throws UnknownHostException {
        // example: 10.1.1.27,ACCF23F57AD4,HF-LPB100
        String[] msg = data.split(",");
        if (msg.length >= 2 && msg[1].length() == 12) {
            int version = msg.length >= 3 && msg[2].length() > 0 ? 6 : 3;
            if (version != 6) {
                System.out.println("Found old milight bridge. Not of use for us though.");
            } else {
                byte mac[] = new byte[6];
                for (int i = 0; i < 6; ++i) {
                    mac[i] = Integer.valueOf(msg[1].substring(i * 2, i * 2 + 2), 16).byteValue();
                }
                milight = InetAddress.getByName(msg[0]);
                milightMAC = mac;
                return true;
            }
        } else {
            System.out.println("Unexpected data received " + data + " on " + networkInterfaceName);
        }
        return false;
    }

    public void runReadFromApp() {
        try {
            byte[] a = new byte[0];
            DatagramPacket s_packet = new DatagramPacket(a, a.length);

            System.out.println("Read from app thread ready on " + networkInterfaceName);
            byte[] buffer = new byte[1024];
            DatagramPacket r_packet = new DatagramPacket(buffer, buffer.length);

            // Now loop forever, waiting to receive packets from app and sending them to the bridge
            while (!willbeclosed) {
                r_packet.setLength(buffer.length);
                socketFromAppToProxy.receive(r_packet);
                appAddr = r_packet.getAddress();
                appPort = r_packet.getPort();

                int len = r_packet.getLength();

                // Translate the fake mac to the actual bridge mac
                int macIndex = ArrayUtils.findArray(buffer, len, fakeMAC);
                if (macIndex != -1) {
                    System.out.println("FAKE-MAC at " + String.valueOf(macIndex) + " replaced");
                    buffer[macIndex + 0] = milightMAC[0];
                    buffer[macIndex + 1] = milightMAC[1];
                    buffer[macIndex + 2] = milightMAC[2];
                    buffer[macIndex + 3] = milightMAC[3];
                    buffer[macIndex + 4] = milightMAC[4];
                    buffer[macIndex + 5] = milightMAC[5];
                }

                logPacket(buffer, len, "From App (" + appAddr.getHostAddress() + ")");

                s_packet.setAddress(milight);
                s_packet.setPort(5987);
                s_packet.setData(buffer, 0, len);
                socketFromBridgeToProxy.send(s_packet);

            }
        } catch (IOException e) {
            if (willbeclosed) {
                return;
            }
            System.out.println(e.getLocalizedMessage());
        }
    }

    public void runReadFromBridge() {
        try {
            byte[] a = new byte[0];
            DatagramPacket s_packet = new DatagramPacket(a, a.length);

            System.out.println("Read from bridge thread ready on " + networkInterfaceName);
            byte[] buffer = new byte[1024];
            DatagramPacket r_packet = new DatagramPacket(buffer, buffer.length);

            // Now loop forever, waiting to receive packets from app and sending them to the bridge
            while (!willbeclosed) {
                r_packet.setLength(buffer.length);
                socketFromBridgeToProxy.receive(r_packet);
                if (appAddr == null) {
                    System.out.println("Received packet from bridge, but app has to send first packet!");
                    return;
                }

                int len = r_packet.getLength();

                // Translate the bridge mac to our fake mac
                int macIndex = ArrayUtils.findArray(buffer, len, milightMAC);
                if (macIndex != -1) {
                    System.out.println("BRIDGE-MAC at " + String.valueOf(macIndex) + " replaced");
                    buffer[macIndex + 0] = fakeMAC[0];
                    buffer[macIndex + 1] = fakeMAC[1];
                    buffer[macIndex + 2] = fakeMAC[2];
                    buffer[macIndex + 3] = fakeMAC[3];
                    buffer[macIndex + 4] = fakeMAC[4];
                    buffer[macIndex + 5] = fakeMAC[5];
                }

                logPacket(buffer, len, "From Bridge");

                s_packet.setAddress(appAddr);
                s_packet.setPort(appPort);
                s_packet.setData(buffer, 0, len);
                socketFromAppToProxy.send(s_packet);

            }
        } catch (IOException e) {
            if (willbeclosed) {
                return;
            }
            System.out.println(e.getLocalizedMessage());
        }
    }

    protected void logPacket(byte[] data, int len, String reason) {
        StringBuffer s = new StringBuffer();
        for (int i = 0; i < len; ++i) {
            s.append(String.format("%02X ", data[i]));
        }

        String d = reason + " " + new Date().toString() + "\n" + s.toString() + "\n\n";

        System.out.print(d);
        try {
            file.write(d.getBytes());
            file.flush();
        } catch (IOException e) {
        }
    }

}
package testing_delete;

import java.io.FileNotFoundException;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

public class Main {

    public static void main(String[] args) {
        if (args.length >= 2) {
            System.err.println(
                    "This program will find the iBox bridge automatically. Please do not provide any arguments");
            return;
        }

        System.out.println(
                "Please add the iBox to your official Milight app before using this program and check if all commands work as expected.\n"
                        + "Open this program and search now for devices and add the newly appeared bridge. Control your bulbs via this new added bridge.\n");
        System.out.println(
                "You will find a logfile milight.txt with all intercepted messages in the current working directory. Please only submit this file to the forum if you see a line saying BRIDGE-MAC.\n");
        System.out.println(
                "Please use the following commands in order:\nSwitch app to color mode\nOFF iBox, OFF zone 0, ON iBox, ON zone 0,\nany brightness change iBox, any brightness change zone 0,\nany color change iBox, any color change zone 0");
        System.out.println("If there is a white bulb mode in the app, switch to that one and repeat all steps");
        System.out.println("Thank you for your help\n");

        ProxyCommunication main = new ProxyCommunication();
        try {
            main.milight = InetAddress.getByName("192.168.1.119");
            main.runRealBridgeDiscovery();
            main.start(InetAddress.getByName("192.168.1.119"), "tun0");
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SocketException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // try {
        // Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
        // while (e.hasMoreElements()) {
        // NetworkInterface networkInterface = e.nextElement();
        // if (networkInterface.isLoopback()) {
        // continue;
        // }
        // new Thread(new Runnable() {
        // @Override
        // public void run() {
        // ProxyCommunication main = new ProxyCommunication();
        // try {
        // main.start(networkInterface);
        // } catch (FileNotFoundException e) {
        // // TODO Auto-generated catch block
        // e.printStackTrace();
        // } catch (SocketException e) {
        // // TODO Auto-generated catch block
        // e.printStackTrace();
        // }
        // }
        // }).start();
        //
        // }
        // Thread.sleep(1000 * 60 * 5);
        // } catch (SocketException e) {
        // // TODO Auto-generated catch block
        // e.printStackTrace();
        // } catch (InterruptedException e1) {
        // // TODO Auto-generated catch block
        // e1.printStackTrace();
        // }

    }

}
package testing_delete;

public class ArrayUtils {

    /**
     * Finds a sub array in a large array
     *
     * @param largeArray
     * @param subArray
     * @return index of sub array
     */
    public static int findArray(byte[] largeArray, byte[] subArray) {

        /* If any of the arrays is empty then not found */
        if (largeArray.length == 0 || subArray.length == 0) {
            return -1;
        }

        /* If subarray is larger than large array then not found */
        if (subArray.length > largeArray.length) {
            return -1;
        }

        for (int i = 0; i < largeArray.length; i++) {
            /* Check if the next element of large array is the same as the first element of subarray */
            if (largeArray[i] == subArray[0]) {

                boolean subArrayFound = true;
                for (int j = 0; j < subArray.length; j++) {
                    /* If outside of large array or elements not equal then leave the loop */
                    if (largeArray.length <= i + j || subArray[j] != largeArray[i + j]) {
                        subArrayFound = false;
                        break;
                    }
                }

                /* Sub array found - return its index */
                if (subArrayFound) {
                    return i;
                }

            }
        }

        /* Return default value */
        return -1;
    }

}