package org.fnppl.opensdx.file_transfer; /* * Copyright (C) 2010-2015 * fine people e.V. <opensdx@fnppl.org> * Henning Thieß <ht@fnppl.org> * * http://fnppl.org */ /* * Software license * * As far as this file or parts of this file is/are software, rather than documentation, this software-license applies / shall be applied. * * This file is part of openSDX * openSDX is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * openSDX is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * and GNU General Public License along with openSDX. * If not, see <http://www.gnu.org/licenses/>. * */ /* * Documentation license * * As far as this file or parts of this file is/are documentation, rather than software, this documentation-license applies / shall be applied. * * This file is part of openSDX. * Permission is granted to copy, distribute and/or modify this document * under the terms of the GNU Free Documentation License, Version 1.3 * or any later version published by the Free Software Foundation; * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. * A copy of the license is included in the section entitled "GNU * Free Documentation License" resp. in the file called "FDL.txt". * */ import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import org.fnppl.opensdx.file_transfer.errors.*; import org.fnppl.opensdx.file_transfer.helper.FileTransferLog; import org.fnppl.opensdx.security.SecurityHelper; import org.fnppl.opensdx.security.SymmetricKey; public class SecureConnection { private boolean DEBUG = false; //request public static byte TYPE_TEXT = -52; // = CC public static byte TYPE_DATA = -35; // = DD //response public static byte TYPE_ACK = -86; // = AA public static byte TYPE_ACK_WITH_MD5 = -95; // = A1 for upload/download of file (COULD) public static byte TYPE_ACK_COMPLETE = -94; // = A2 upload/download complete public static byte TYPE_ABORT_COMMAND = -69; // = BB public static byte TYPE_ERROR_E0 = -32; // = E0 //command not understood public static byte TYPE_ERROR_E1 = -31; // = E1 public static byte TYPE_ERROR_E2 = -30; // = E2 public static byte TYPE_ERROR_E3 = -29; // = E3 public static byte TYPE_ERROR_E4 = -28; // = E4 public static byte TYPE_ERROR_E5 = -27; // = E5 public static byte TYPE_ERROR_E6 = -26; // = E6 public static byte TYPE_ERROR_E7 = -25; // = E7 public static byte TYPE_ERROR_E8 = -24; // = E8 public static byte TYPE_ERROR_E9 = -23; // = E9 public static byte TYPE_ERROR_EA = -22; // = EA public static byte TYPE_ERROR_EB = -21; // = EB public static byte TYPE_ERROR_EC = -20; // = EC public static byte TYPE_ERROR_ED = -19; // = ED public static byte TYPE_ERROR = -18; // = EE // error with error message //FILE ERROS public static final byte ERROR_FILE_RESTRICTED = 1; public static final byte ERROR_FILE_NOT_EXISTS = 2; public static final byte ERROR_FILE_ALREADY_EXISTS = 3; public static final byte ERROR_FILENAME_IS_MISSING = 4; public static final byte ERROR_FILE_LENGTH_PARAM = 5; public static final byte ERROR_CANNOT_DELETE_FILE = 6; public static final byte ERROR_RETRIEVING_FILE_INFO = 7; public static final byte ERROR_WRONG_FILESIZE = 8; //DIRECTORY ERRORS public static final byte ERROR_CANNOT_DELETE_DIR = 9; public static final byte ERROR_DIRECTORY_NOT_EXISTS = 10; public static final byte ERROR_DIRECTORY_DEPTH = 11; public static final byte ERROR_DIRECTORY_DOWNLOAD_NOT_IMPLEMENTED = 12; public static final byte ERROR_NOT_A_DIRECTORY = 13; //LOGIN ERRORS public static final byte ERROR_LOGIN_ACCESS_DENIED = 14; public static final byte ERROR_LOGIN_USERNAME_MISSING = 15; //UPLOAD ERRORS public static final byte ERROR_UPLOAD_IS_NULL = 16; public static final byte ERROR_UPLOAD_CANCEL = 17; public static final byte ERROR_UPLOAD_HALT = 18; //FILESYSTEM ERRORS public static final byte ERROR_PATH_IS_NOT_ABSOLUTE = 19; public static final byte ERROR_PATH_IS_MISSING = 20; public static final byte ERROR_WRONG_DESTINATION = 21; public static final byte ERROR_CANNOT_RENAME = 22; public static final byte ERROR_PATH_IS_RESTRICTED = 23; public static final byte ERROR_PATH_ALREADY_EXISTS = 24; public static final byte ERROR_MKDIR = 25; //OTHER ERRORS public static final byte ERROR_WITH_MESSAGE = 26; public static final byte ERROR_MD5_CHECK = 27; public static final byte ERROR_RIGHTS_AND_DUTIES = 28; public long id; public int num; public byte type; int len = 0; public SymmetricKey key; public byte[] content; public BufferedInputStream in; public BufferedOutputStream out; private FileTransferLog log = null; public SecureConnection(SymmetricKey key, BufferedInputStream in, BufferedOutputStream out) { this.key = key; this.out = out; this.in = in; } public void setLog(FileTransferLog log) { this.log = log; } public boolean receiveNextPackage() throws Exception { //read package header byte[] header = new byte[32]; int read = 1; int offset = 0; boolean headerNotComplete = true; while (headerNotComplete && read>0) {//don't block, if socket is closed read = in.read(header, offset, 32-offset); //this one blocks if (read>0) { offset += read; if (offset>=32) { //System.out.println("enc header = "+SecurityHelper.HexDecoder.encode(header)); //header complete -> read values header = key.decrypt(header); id = bytesToLong(header, 0); num = bytesToInt(header, 8); type = header[12]; header[12] = (byte)0; len = bytesToInt(header, 12); if (DEBUG) { header[12] = type; System.out.println("header = "+SecurityHelper.HexDecoder.encode(header)+" :: id="+id+"\ttype="+SecurityHelper.HexDecoder.encode(new byte[]{type})+"\tlen="+len); } headerNotComplete = false; } } else if (read<0){ return false; //socket closed } } //read content if (len<=0) { content = null; } else { content = new byte[len]; offset=0; while (read>0 && offset<len) {//don't block, if socket is closed read = in.read(content,offset,len-offset); //this one blocks if (read>0) { offset += read; } else if (read<0){ return false; //socket closed } } if (offset==len) { //System.out.println("enc content = "+SecurityHelper.HexDecoder.encode(content)); //content complete -> decrypt content = key.decrypt(content); } } return true; } public void setAck(long id, int num) { this.id = id; this.num = num; this.type = TYPE_ACK; this.content = null; } public void setAck(long id, int num, String message) { this.id = id; this.num = num; this.type = TYPE_ACK; setContent(message); } public void setAckComplete(long id, int num) { this.id = id; this.num = num; this.type = TYPE_ACK_COMPLETE; this.content = null; } public void setError(long id, int num, String message, OSDXErrorCode errCode) { this.id = id; this.num = num; this.type = OSDXErrorCode.OSDXErrorCodeToByte(errCode); setContent(message); } public void setErrorOLD(long id, int num, String message) { this.id = id; this.num = num; this.type = TYPE_ERROR; setContent(message); } public void setErrorCommandNotUnderstood(long id, int num) { this.id = id; this.num = num; this.type = TYPE_ERROR_E0; this.content = null; } public void setData(long id, int num, byte[] content) { //System.out.println("SETTING DATA:: len="+content.length); this.id = id; this.num = num; this.type = TYPE_DATA; this.content = content; } public void setCommand(long id, String command) { this.id = id; this.num = 0; this.type = TYPE_TEXT; setContent(command); } private void setContent(String command) { try { this.content = command.getBytes("UTF-8"); } catch (UnsupportedEncodingException ex) { ex.printStackTrace();} } public void sendPackage() throws Exception { byte[] encContent = new byte[16777216]; byte[] encHeader = new byte[500*1024]; //content len = 0; if (content != null) { len = key.encrypt(content, encContent); if (len > 16777216) { throw new RuntimeException("Max. 16Mb of content allowed."); } } //header byte[] header = buildPackageHeader(id, num, type, len); int len_header = key.encrypt(header, encHeader); //package if (DEBUG) { System.out.println("header: "+SecurityHelper.HexDecoder.encode(header, 0, header.length, ':', -1)); System.out.println("header: enc "+SecurityHelper.HexDecoder.encode(encHeader, 0, len_header, ':', -1)); System.out.println("content: "+SecurityHelper.HexDecoder.encode(content, 0, content.length, ':', -1)); System.out.println("content: enc "+SecurityHelper.HexDecoder.encode(encContent, 0, len, ':', -1)); //System.out.println("package: "+SecurityHelper.HexDecoder.encode(encHeader)+SecurityHelper.HexDecoder.encode(encContent)); } out.write(encHeader, 0, len_header); if (content != null) { out.write(encContent, 0, len); } out.flush(); } public void sendRawBytes(byte[] data) throws Exception { byte[] len = new byte[4]; intToBytes(data.length, len, 0); out.write(len); out.write(data); out.flush(); } public byte[] receiveRawBytesPackage() throws Exception { //read package header byte[] header = new byte[4]; int read = 1; int offset = 0; boolean headerNotComplete = true; int rawDataLen = 0; while (headerNotComplete && read>0) {//don't block, if socket is closed read = in.read(header,offset,4-offset); //this one blocks if (read>0) { offset += read; if (offset>=4) { rawDataLen = bytesToInt(header, 0); headerNotComplete = false; } } else if (read<0){ // System.out.println("Socket closed."); return null; //socket closed } } //read content byte[] content = null; if (rawDataLen>0) { content = new byte[rawDataLen]; offset=0; while (read>0 && offset<rawDataLen) {//don't block, if socket is closed read = in.read(content,offset,rawDataLen-offset); //this one blocks if (read>0) { offset += read; } else if (read<0){ // System.out.println("Socket closed."); return null; //socket closed } } } return content; } public void setKey(SymmetricKey key) { this.key = key; } public boolean isError() { if (type <= TYPE_ERROR && type >= TYPE_ERROR_E0 || type >= ERROR_FILE_RESTRICTED && type <= ERROR_RIGHTS_AND_DUTIES) { return true; } else { return false; } } public static boolean isError(byte code) { if (code <= TYPE_ERROR && code >= TYPE_ERROR_E0 || code >= ERROR_FILE_RESTRICTED && code <= ERROR_RIGHTS_AND_DUTIES) { return true; } else { return false; } } private String getMessageFromContent(byte[] content) { if (content==null) return null; try { return new String(content,"UTF-8"); } catch (UnsupportedEncodingException e) { } return null; } /** * Generates an OSDXError Object if the server returned an error code in the Message * * @return OSDXError Object */ public OSDXErrorCode getError(){ if(isError()){ return OSDXErrorCode.byteToOSDXErrorCode(type); } return null; } private static byte[] buildPackageHeader(long id, int num, byte type, int length) { byte[] b = new byte[16]; longToBytes(id, b, 0); intToBytes(num, b, 8); intToBytes(length, b, 12); b[12] = type; return b; } private static long bytesToLong(byte[] b, int startPos) { long l = 0; for (int i = 0; i < 8; i++) { l = (l << 8) + (b[i+startPos] & 0xff); } return l; } private static int bytesToInt(byte[] b, int startPos) { int v = 0; for (int i = 0; i < 4; i++) { v = (v << 8) + (b[i+startPos] & 0xff); } return v; } // private static byte[] longToByte(long l) { // byte[] b = new byte[8]; // for(int i= 0; i < 8; i++){ // b[7 - i] = (byte)(l >>> (i * 8)); // } // return b; // } private static void longToBytes(long l, byte[] b, int startPos) { startPos += 7; //reverse order for(int i= 0; i < 8; i++){ b[startPos-i] = (byte)(l >>> (i * 8)); } } private static void intToBytes(int v, byte[] b, int startPos) { startPos += 3; //reverse order for(int i= 0; i < 4; i++){ b[startPos-i] = (byte)(v >>> (i * 8)); } } private static Object sync_id = new Object(); private static long lastTimeStamp = System.currentTimeMillis(); public static long getID() { long now = System.currentTimeMillis(); synchronized(sync_id) { if(now <= lastTimeStamp) { now = lastTimeStamp +1 ; } lastTimeStamp = now; } return now; } public static void main(String args[]) { try { for (byte b =-126;b<127;b++) { System.out.println(b+" "+SecurityHelper.HexDecoder.encode(new byte[]{b})); } } catch (Exception ex) { ex.printStackTrace(); } } }