Local communication support
[iotcloud.git] / version2 / src / java / iotcloud / CloudComm.java
index ac499e51b881bfaa23d6e5af1e2049989fea008e..5741019513afcad9027b1d866963bce07d6e77fe 100644 (file)
@@ -16,16 +16,20 @@ import java.security.SecureRandom;
 
 
 class CloudComm {
-       String baseurl;
-       Cipher encryptCipher;
-       Cipher decryptCipher;
-       Mac mac;
-       String password;
-       SecureRandom random;
-       static final int SALT_SIZE = 8;
-       static final int TIMEOUT_MILLIS = 100;
-       byte salt[];
-       Table table;
+       private static final int SALT_SIZE = 8;
+       private static final int TIMEOUT_MILLIS = 100;
+
+       private String baseurl;
+       private Cipher encryptCipher;
+       private Cipher decryptCipher;
+       private Mac mac;
+       private String password;
+       private SecureRandom random;
+       private byte salt[];
+       private Table table;
+       private int listeningPort = -1;
+       private Thread localServerThread = null;
+       private boolean doEnd = false;
 
        /**
         * Empty Constructor needed for child class.
@@ -36,11 +40,21 @@ class CloudComm {
        /**
         * Constructor for actual use. Takes in the url and password.
         */
-       CloudComm(Table _table,  String _baseurl, String _password) {
+       CloudComm(Table _table,  String _baseurl, String _password, int _listeningPort) {
                this.table = _table;
                this.baseurl = _baseurl;
                this.password = _password;
                this.random = new SecureRandom();
+               this.listeningPort = _listeningPort;
+
+               if (this.listeningPort > 0) {
+                       localServerThread = new Thread(new Runnable() {
+                               public void run() {
+                                       localServerWorkerFunction();
+                               }
+                       });
+                       localServerThread.start();
+               }
        }
 
        /**
@@ -48,7 +62,10 @@ class CloudComm {
         */
        private SecretKeySpec initKey() {
                try {
-                       PBEKeySpec keyspec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128);
+                       PBEKeySpec keyspec = new PBEKeySpec(password.toCharArray(),
+                                                           salt,
+                                                           65536,
+                                                           128);
                        SecretKey tmpkey = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256").generateSecret(keyspec);
                        return new SecretKeySpec(tmpkey.getEncoded(), "AES");
                } catch (Exception e) {
@@ -61,6 +78,11 @@ class CloudComm {
         * Inits the HMAC generator.
         */
        private void initCrypt() {
+
+               if (password == null) {
+                       return;
+               }
+
                try {
                        SecretKeySpec key = initKey();
                        password = null; // drop password
@@ -88,12 +110,22 @@ class CloudComm {
        }
 
        public void setSalt() throws ServerException {
+
+               if (salt != null) {
+                       // Salt already sent to server so dont set it again
+                       return;
+               }
+               byte[] saltTmp = new byte[SALT_SIZE];
+               random.nextBytes(saltTmp);
+
+               URL url = null;
+               URLConnection con = null;
+               HttpURLConnection http = null;
+
                try {
-                       byte[] saltTmp = new byte[SALT_SIZE];
-                       random.nextBytes(saltTmp);
-                       URL url = new URL(baseurl + "?req=setsalt");
-                       URLConnection con = url.openConnection();
-                       HttpURLConnection http = (HttpURLConnection) con;
+                       url = new URL(baseurl + "?req=setsalt");
+                       con = url.openConnection();
+                       http = (HttpURLConnection) con;
                        http.setRequestMethod("POST");
                        http.setFixedLengthStreamingMode(saltTmp.length);
                        http.setDoOutput(true);
@@ -104,15 +136,34 @@ class CloudComm {
                        int responsecode = http.getResponseCode();
                        if (responsecode != HttpURLConnection.HTTP_OK) {
                                // TODO: Remove this print
-                               // System.out.println(responsecode);
+                               System.out.println(responsecode);
                                throw new Error("Invalid response");
                        }
 
-                       salt = saltTmp;
                } catch (Exception e) {
                        throw new ServerException("Failed setting salt", ServerException.TypeConnectTimeout);
                }
-               initCrypt();
+
+
+               try {
+                       InputStream is = http.getInputStream();
+                       DataInputStream dis = new DataInputStream(is);
+                       // byte [] tmp = new byte[1];
+                       byte tmp = dis.readByte();
+
+                       if (tmp == 0) {
+                               salt = saltTmp;
+                               initCrypt();
+                       } else {
+                               getSalt(); // there was already a salt so we need to get it
+                       }
+
+               } catch (SocketTimeoutException e) {
+                       throw new ServerException("setSalt failed", ServerException.TypeInputTimeout);
+               } catch (Exception e) {
+                       e.printStackTrace();
+                       throw new Error("setSlot failed");
+               }
        }
 
        private void getSalt() throws ServerException {
@@ -154,7 +205,6 @@ class CloudComm {
                        e.printStackTrace();
                        throw new Error("getSlot failed");
                }
-
        }
 
        /*
@@ -191,6 +241,8 @@ class CloudComm {
                        OutputStream os = http.getOutputStream();
                        os.write(bytes);
                        os.flush();
+
+                       // System.out.println("Bytes Sent: " + bytes.length);
                } catch (SocketTimeoutException e) {
                        throw new ServerException("putSlot failed", ServerException.TypeConnectTimeout);
                } catch (Exception e) {
@@ -221,7 +273,6 @@ class CloudComm {
                }
        }
 
-
        /**
         * Request the server to send all slots with the given
         * sequencenumber or newer.
@@ -243,11 +294,11 @@ class CloudComm {
                        http.setRequestMethod("POST");
                        http.setConnectTimeout(TIMEOUT_MILLIS);
                        http.setReadTimeout(TIMEOUT_MILLIS);
-
-
                        http.connect();
-               } catch (ServerException e) {
+               } catch (SocketTimeoutException e) {
                        throw new ServerException("getSlots failed", ServerException.TypeConnectTimeout);
+               } catch (ServerException e) {
+                       throw e;
                } catch (Exception e) {
                        e.printStackTrace();
                        throw new Error("getSlots failed");
@@ -262,7 +313,7 @@ class CloudComm {
                                throw new Error("Bad Response: " + new String(resptype));
                        else
                                return processSlots(dis);
-               } catch (ServerException e) {
+               } catch (SocketTimeoutException e) {
                        throw new ServerException("getSlots failed", ServerException.TypeInputTimeout);
                } catch (Exception e) {
                        e.printStackTrace();
@@ -277,11 +328,20 @@ class CloudComm {
        private Slot[] processSlots(DataInputStream dis) throws Exception {
                int numberofslots = dis.readInt();
                int[] sizesofslots = new int[numberofslots];
+
+
+               // System.out.println("number of slots: " + numberofslots);
+
+
+
                Slot[] slots = new Slot[numberofslots];
                for (int i = 0; i < numberofslots; i++)
                        sizesofslots[i] = dis.readInt();
 
                for (int i = 0; i < numberofslots; i++) {
+
+                       // System.out.println("Size of slot: " + sizesofslots[i]);
+
                        byte[] data = new byte[sizesofslots[i]];
                        dis.readFully(data);
 
@@ -292,4 +352,137 @@ class CloudComm {
                dis.close();
                return slots;
        }
+
+       public byte[] sendLocalData(byte[] sendData, String host, int port) {
+
+               if (salt == null) {
+                       return null;
+               }
+               try {
+                       // Encrypt the data for sending
+                       byte[] encryptedData = encryptCipher.doFinal(sendData);
+
+                       // Open a TCP socket connection to a local device
+                       Socket socket = new Socket(host, port);
+                       socket.setReuseAddress(true);
+                       DataOutputStream output = new DataOutputStream(socket.getOutputStream());
+                       DataInputStream input = new DataInputStream(socket.getInputStream());
+
+                       // Send data to output (length of data, the data)
+                       output.writeInt(encryptedData.length);
+                       output.write(encryptedData, 0, encryptedData.length);
+                       output.flush();
+
+                       int lengthOfReturnData = input.readInt();
+                       byte[] returnData = new byte[lengthOfReturnData];
+                       input.readFully(returnData);
+                       returnData = decryptCipher.doFinal(returnData);
+
+                       // We are dont with this socket
+                       socket.close();
+
+                       return returnData;
+               } catch (SocketTimeoutException e) {
+
+               } catch (BadPaddingException e) {
+
+               } catch (IllegalBlockSizeException e) {
+
+               } catch (UnknownHostException e) {
+
+               } catch (IOException e) {
+
+               }
+
+               return null;
+       }
+
+       private void localServerWorkerFunction() {
+
+               ServerSocket inputSocket = null;
+
+               try {
+                       // Local server socket
+                       inputSocket = new ServerSocket(listeningPort);
+                       inputSocket.setReuseAddress(true);
+                       inputSocket.setSoTimeout(TIMEOUT_MILLIS);
+               } catch (Exception e) {
+                       e.printStackTrace();
+                       throw new Error("Local server setup failure...");
+               }
+
+               while (!doEnd) {
+
+                       try {
+                               // Accept incoming socket
+                               Socket socket = inputSocket.accept();
+
+                               DataInputStream input = new DataInputStream(socket.getInputStream());
+                               DataOutputStream output = new DataOutputStream(socket.getOutputStream());
+
+                               // Get the encrypted data from the server
+                               int dataSize = input.readInt();
+                               byte[] readData = new byte[dataSize];
+                               input.readFully(readData);
+
+                               // Decrypt the data
+                               readData = decryptCipher.doFinal(readData);
+
+                               // Process the data
+                               byte[] sendData = table.acceptDataFromLocal(readData);
+
+                               // Encrypt the data for sending
+                               sendData = encryptCipher.doFinal(sendData);
+
+                               // Send data to output (length of data, the data)
+                               output.writeInt(sendData.length);
+                               output.write(sendData, 0, sendData.length);
+                               output.flush();
+
+                               // close the socket
+                               socket.close();
+                       } catch (SocketTimeoutException e) {
+
+                       } catch (BadPaddingException e) {
+
+                       } catch (IllegalBlockSizeException e) {
+
+                       } catch (UnknownHostException e) {
+
+                       } catch (IOException e) {
+
+                       }
+               }
+
+               if (inputSocket != null) {
+                       try {
+                               inputSocket.close();
+                       } catch (Exception e) {
+                               e.printStackTrace();
+                               throw new Error("Local server close failure...");
+                       }
+               }
+       }
+
+       public void close() {
+               doEnd = true;
+
+               try {
+                       localServerThread.join();
+               } catch (Exception e) {
+                       e.printStackTrace();
+                       throw new Error("Local Server thread join issue...");
+               }
+
+               System.out.println("Done Closing");
+       }
+
+       protected void finalize() throws Throwable {
+               try {
+                       close();        // close open files
+               } finally {
+                       super.finalize();
+               }
+       }
+
 }