/******************************************************************************* * gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/ * Copyright (C) 2014 SVS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ /** * */ package userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.mix; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import java.util.Arrays; import java.util.concurrent.LinkedBlockingQueue; import staticContent.framework.config.Settings; import staticContent.framework.util.Util; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.dataObjects.Connection; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.dataObjects.HttpInfo; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.dataObjects.HttpPartType; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.dataObjects.SynchronizedBuffer; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.helper.SocksHandler; /** * @author bash * This class represents the superclass for all communication to mix from the webbrowser or webserver. * It contains methods which are used by the subclasses EntryDataToMix and ExitDataToMix * */ public abstract class DataToMix extends Thread { private LinkedBlockingQueue<Connection> readableConnections; private MixWriteInterface mixTunnel; private int bufferSize; private Settings settings; // public TrafficLog trafficlog; /** * Constructor * * @param readableConnections * @param mixTunnel */ public DataToMix(LinkedBlockingQueue<Connection> readableConnections, MixWriteInterface mixTunnel, Settings settings) {; this.settings = settings; this.readableConnections = readableConnections; this.mixTunnel = mixTunnel; this.bufferSize = settings.getPropertyAsInt("HP_BUFFER_SIZE"); } /** * This class checks if a connection contains readable data either from Webbrowser or webserver. * The data handling depends on the state of the connection */ @Override public void run() { while (true) { Connection readableConnection = null; try { // Takes connection from queue. This connection contains data ready to send to mix readableConnection = readableConnections.take(); readableConnection.setInProgressSend(true); } catch (InterruptedException e1) { break; } int id = readableConnection.getId(); SocketChannel socket = readableConnection.getServerSocket(); ByteBuffer buffer = ByteBuffer.allocate(bufferSize); buffer.clear(); int readBytes; try { while ((readBytes = socket.read(buffer)) > 0) ; //If true the connection is closed by the Host if (readBytes == -1) { System.out.println("Connection " + readableConnection.getId() + " STOP!"); socket.close(); } else { readableConnection.selector.registerSocket(socket, readableConnection, SelectionKey.OP_READ); } byte[] message = Arrays.copyOfRange(buffer.array(), 0, buffer.position()); readableConnection.setConnectionMessageIncomplete(false); readableConnection.getConnectionBuffer().addArrayToBuffer(message); improveMessage(id, readableConnection); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * Method to exercise the neccessary steps to improvement * @param id * @param readableConnection */ private void improveMessage(int id, Connection readableConnection) { while (readableConnection.isConnectionBuffer()) { HttpPartType messageType = readableConnection.getStatusHTTPFromApp().getType(); switch (messageType) { case Header: headerHandling(readableConnection); break; case Body: bodyHandling(readableConnection); break; case BodyChunk: bodyChunkHandling(readableConnection); break; case SocksRequest: sendSocksRequest(readableConnection); break; case SocksAuth: sendSocksAuth(readableConnection); break; case SocksReply: System.err .println("Methode wird doch aufgerufen SocksReply!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); default: System.out.println("Unknown Type"); break; } if(readableConnection.isConnectionMessageIncomplete()){ break; } } readableConnection.setInProgressSend(false); } /** * Method to handle a http header * * @param readableConnection */ public abstract void headerHandling(Connection readableConnection); /** * Method to handle a http body which is not chunked type * * @param readableConnection */ public abstract void bodyHandling(Connection readableConnection); /** * Method to handle a http body chunked type * * @param readableConnection */ public abstract void bodyChunkHandling(Connection readableConnection); /** * Method to write out a body after handling. * Same for both encodings * @param message * @param readableConnection * @return message as byte[] */ public byte[] bodyRelayWrite(byte[] message, Connection readableConnection) { int id = readableConnection.getId(); int bodyLength = readableConnection.getStatusHTTPFromApp().getLength(); int newLength = bodyLength - message.length; readableConnection.getStatusHTTPFromApp().setLength((newLength)); if (readableConnection.getHeaderBuffer() != null) { writeChunkToMix(id, Util.concatArrays(readableConnection.getHeaderBuffer(), message)); readableConnection.setHeaderBuffer(null); } else { writeChunkToMix(id, message); } if (newLength <= 0 && readableConnection.getStatusHTTPFromApp().isBody()) { readableConnection.setStatusHTTPFromApp(new HttpInfo(HttpPartType.Header, 0)); } return message; } /** * Method to handle SocksAuth * * @param message * @param readableConnection */ public void sendSocksAuth(Connection readableConnection) { SynchronizedBuffer buffer = readableConnection.getConnectionBuffer(); byte[] message = buffer.removeBytes(2); int len = message[1]; byte[] mixMessage = buffer.removeBytes(len); System.out.println("SocksAuth"); boolean containsMethod = false; for (int i = 0; i < mixMessage.length; i++) { if (mixMessage[i] == (byte) 0) { containsMethod = true; break; } } System.out.println("SocksAuth Method" + containsMethod); byte[] reply; if (containsMethod) { reply = SocksHandler.sendSocks5MethodReply((byte) 0); } else { reply = SocksHandler.sendSocks5MethodReply((byte) 0xff); } System.out.println("SocksAuth Reply " + reply); readableConnection.writeChunk(ByteBuffer.wrap(reply)); System.out.println("SocksAuth Reply written"); readableConnection.setStatusHTTPFromApp(new HttpInfo(HttpPartType.SocksRequest, 0)); // return message; } /** * Method to send a socks request to generate a Connection * @param readableConnection */ public void sendSocksRequest(Connection readableConnection) { SynchronizedBuffer buffer = readableConnection.getConnectionBuffer(); byte[] message = buffer.removeBytes(4); if (settings.getPropertyAsBoolean("HP_SKIP_ROUNDTRIP")) { int length = 0; if (message[3] == 0x01) { length = 10; } else if (message[3] == 0x02) { length = 6 + message[4]; } else if (message[3] == 0x03) { length = 22; } else { System.err.println("ERROR in Socks parsing!"); } byte[] mixMessage = new byte[length]; System.arraycopy(message, 0, mixMessage, 0, 4); byte[] socksMessage = buffer.removeBytes(length - 4); System.arraycopy(socksMessage, 0, mixMessage, 4, length - 4); readableConnection.setStatusHTTPFromApp(new HttpInfo(HttpPartType.Header, 0)); writeChunkToMix(readableConnection.getId(), mixMessage); byte code = 0x00; InetSocketAddress address = SocksHandler.getInetAddress(mixMessage); byte aTyp = message[3]; byte[] socksReply = SocksHandler.sendSocks5ConnectionReply(code, aTyp, address); readableConnection.writeChunk(ByteBuffer.wrap(socksReply)); } } /** * Write Message to Mix * * @param id * @param message */ protected void writeChunkToMix(int id, byte[] message) { int len = message.length; mixTunnel.writeChunk(Util.concatArrays(Util.intToByteArray(id), Util.concatArrays(Util.intToByteArray(len), message))); } }