/******************************************************************************* * 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; import java.io.IOException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; import staticContent.framework.controller.Implementation; import staticContent.framework.interfaces.Layer5ApplicationClient; import staticContent.framework.routing.RoutingMode; import staticContent.framework.socket.socketInterfaces.StreamAnonSocket; import staticContent.framework.socket.socketInterfaces.AnonSocketOptions.CommunicationDirection; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.dataObjects.Connection; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.entryClient.ApplicationConnectionPool; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.entryClient.EntryDataFromMix; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.entryClient.EntryDataToMix; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.entryClient.MixConnection; /** * @author bash * * This class provides the client on entry side for network improvement * measures. It contains all needed subclasses. * * It handles the complete communication between the webbrowser and the mix. * The communication with the webbrowser is managed by the ApplicationConnectionPool (see ApplicationConnectionPool). * The communication with the mix is handled by the MixEntryConnection (see MixEntryConnection). * It also contains the improvementthreads. * * There is a hashtable to identify a connection by the connectionId. (Key: ConnectionId; Value: Connection) * * * For automatic test purpose there is a control channel. * It allows to control the client by remote. So it is possible to start and stop the client via script. * The control server is able to send a stop or restart signal. Therefore it has to connect via tcp on port 4060 * If the control server send a 1 the client resets the client. If the server send a 2 the client stops. */// TODO: make sure DNS-requests are sent through the anonymous tunnel as well public class ClientPlugIn extends Implementation implements Layer5ApplicationClient { private MixConnection mixConnection; private ConcurrentHashMap<Integer, Connection> connectionMap; private int improvmentThreadCounter; /** * this socket will accept SOCKS connections from user applications (e.g. * web browsers) */ //private ServerSocket userServerSocket; // /** * the anonymous tunnel the socks connections will be tunneled through (all * connections open via userServerSocket will be multiplexed through this * tunnel) */ //private Multiplexer multiplexedAnonTunnel; /** * The ApplicationConnectionPool manage all connection to a browser */ private ApplicationConnectionPool connectionPool; /** * This queue contains the connection which contains data from the outside */ public LinkedBlockingQueue<Connection> readableConnections; /** * This queue contains the connection which contains data from the mix */ public LinkedBlockingQueue<Connection> writeableConnections; private EntryDataToMix[] improvementThreads; private EntryDataFromMix[] unImprovementThreads; //private boolean DEBUG; //private boolean TALK_A_LOT; //private boolean SKIP_ROUNDTRIP; @Override public void constructor() { if (!super.anonNode.IS_DUPLEX) throw new RuntimeException("Socks requires a DUPLEX anonymous channel"); if (!super.anonNode.IS_CONNECTION_BASED) throw new RuntimeException("Socks requires a CONNECTION_BASED anonymous channel"); if (!super.anonNode.IS_ORDER_PRESERVING) throw new RuntimeException("Socks requires an ORDER_PRESERVING anonymous channel"); if (!super.anonNode.IS_RELIABLE) throw new RuntimeException("Socks requires a RELIABLE anonymous channel"); //this.config = new Config(settings); this.readableConnections = new LinkedBlockingQueue<Connection>(); this.writeableConnections = new LinkedBlockingQueue<Connection>(); this.connectionMap = new ConcurrentHashMap<Integer, Connection>(); improvmentThreadCounter = settings.getPropertyAsInt("HP_THREAD_COUNTER"); //this.DEBUG = settings.getPropertyAsBoolean("HP_DEBUG"); //this.TALK_A_LOT = settings.getPropertyAsBoolean("HP_TALK_A_LOT"); //this.SKIP_ROUNDTRIP = settings.getPropertyAsBoolean("HP_SKIP_ROUNDTRIP"); } @Override public void initialize() { } @Override public void begin() { StreamAnonSocket anonSocket = null; try { anonSocket = super.anonNode.createStreamSocket( CommunicationDirection.DUPLEX, super.anonNode.ROUTING_MODE != RoutingMode.GLOBAL_ROUTING); anonSocket.connect(1080); } catch (IOException e) { System.err.println("Client SOCKS-Proxy: could not connect to mixes"); e.printStackTrace(); return; } //this.multiplexedAnonTunnel = new Multiplexer(anonSocket, settings); //openSocksServerSocket(); System.err.println("Warning: This is a test plug-in - do NOT send any sensitive data via this plug-in!"); mixConnection = new MixConnection(anonSocket, connectionMap, writeableConnections); mixConnection.start(); startConnectionPool(); startImprovement(); //new AcceptorThread().start(); } /*public void openSocksServerSocket() { //synchronized (multiplexedAnonTunnel) { if (userServerSocket != null && userServerSocket.isBound()) // already // open return; InetAddress address = null; try { address = InetAddress.getByName(settings.getProperty("HP_LISTENING_IP_ADDRESS")); } catch (UnknownHostException e) { e.printStackTrace(); } try { userServerSocket = new ServerSocket(settings.getPropertyAsInt("HP_LISTENING_PORT"), 1000, address); System.out.println("client: waiting for socks connections on " + address +":" + settings.getPropertyAsInt("HP_LISTENING_PORT")); } catch (IOException e) { if (DEBUG) e.printStackTrace(); throw new RuntimeException("client: couldn't bind client socks socket " + address +":" + settings.getPropertyAsInt("HP_LISTENING_PORT")); } //} }*/ /*private class AcceptorThread extends Thread { /** * Accepts new socks connections (from user applications) and hands them * over to the multiplexer. If SKIP_ROUNDTRIP is set, answers the * Version Identifier/Method Selection Method from user and don't hands * this message over to the multiplexer. *//* @Override public void run() { Socket clientSocket = null; while (true) { if (TALK_A_LOT == true) System.out.println("client: waiting for connection from user application (e.g. web browser)"); try { clientSocket = userServerSocket.accept(); if (TALK_A_LOT == true) System.out.println("client: received a connection from a user application " + clientSocket.getInetAddress() + ":" + clientSocket.getPort()); if (SKIP_ROUNDTRIP == true) { InputStream fromClient = clientSocket.getInputStream(); OutputStream toClient = clientSocket.getOutputStream(); // read SOCKS // "version identifier/method selection message" (RFC // 1928, p. 3) /** * Client -1-> Proxy +----+----------+----------+ |VER | * NMETHODS | METHODS | +----+----------+----------+ | 1 * | 1 | 1 to 255 | +----+----------+----------+ *//* byte version = 0x00; try { version = (byte) fromClient.read(); } catch (IOException e) { e.printStackTrace(); } if (TALK_A_LOT == true) { System.out.println("Client: reading Identifier Message from Client."); } if (version == 0x05) { try { int nMethods = Util .unsignedByteToShort((byte) fromClient .read()); byte[] methods = Util.forceRead(fromClient, nMethods); if (TALK_A_LOT == true) { System.out.println("Client: " + nMethods + " method(s) (in hex): " + Util.toHex(methods)); } // TODO: parse methods (necessary for // authentication only) // send "NO AUTHENTICATION REQUIRED" message byte method = 0x00; SocksHandler.sendSocks5MethodReply(toClient, (byte) method); } catch (IOException e) { e.printStackTrace(); } } else if (version == 0x04) { } else { System.out.println("Client: connection does not seem to be socks"); return; } } multiplexedAnonTunnel.addConnection(clientSocket); } catch (IOException e) { System.err.println("client: connection attempt from user application failed"); if (DEBUG) e.printStackTrace(); openSocksServerSocket(); continue; } } } }*/ /** * Method to start the thread to improve the messages */ public void startImprovement() { improvementThreads = new EntryDataToMix[improvmentThreadCounter]; unImprovementThreads = new EntryDataFromMix[improvmentThreadCounter]; for (int i = 0; i < improvmentThreadCounter; i++) { // System.out.println("TEST"); try { improvementThreads[i] = new EntryDataToMix(readableConnections, mixConnection, settings); improvementThreads[i].start(); unImprovementThreads[i] = new EntryDataFromMix(writeableConnections, settings); unImprovementThreads[i].start(); } catch (ClassNotFoundException e) { System.err.println("Plugin not found, please check path!"); e.printStackTrace(); } catch (InstantiationException e) { System.err.println("Could not instatiate the plugin!"); e.printStackTrace(); } catch (IllegalAccessException e) { System.err.println("Plugin not accessable, please check rights!"); e.printStackTrace(); } } } /** * Starts the entry connection pool */ public void startConnectionPool() { connectionPool = new ApplicationConnectionPool(settings.getPropertyAsInt("HP_LISTENING_PORT"), settings.getPropertyAsInt("HP_BUFFER_SIZE"), readableConnections, connectionMap, settings); connectionPool.start(); } /** * Method to reset the client * It clears the connection table and restarts the improvementthreads */ public void resetConnectionPool() { ConcurrentHashMap<Integer, Connection> pool = connectionPool.getConnectionMap(); for(Thread t: improvementThreads) { t.interrupt(); } for(Thread t: unImprovementThreads) { t.interrupt(); } startImprovement(); for (Connection con : pool.values()) { try { con.getServerSocket().close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } connectionPool.setConnectionMap(new ConcurrentHashMap<Integer, Connection>()); System.out.println("Control: Client reseted"); } /** * This class establishes the control channel by startup */ /*public void establishControlChannel() { InetAddress address = null; System.out.println("control: Awaits controlconnection via 4060"); try { address = InetAddress.getByName("10.1.1.31"); ServerSocket anonServerSocket = new ServerSocket(4060, 30, address); System.out.println("control: waiting for controlconnections on " + address + ": 4060" ); Socket client = anonServerSocket.accept(); System.out.println("control: controlconnection accepted (from " + client.getInetAddress() + ":" + client.getPort() + ")"); fromControlStream = client.getInputStream(); toControlStream = client.getOutputStream(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); throw new RuntimeException("controlchannel could not bind" + address + ": 4060"); } } */ /** * This method checks the controlstream for controlsignals * @throws IOException */ /* public void checkControlStream() throws IOException { int readBytes; boolean flow = true; while(flow) { int command = fromControlStream.read(); switch (command) { case -1: flow = false; break; case 1: resetConnectionPool(); break; case 2: stopClient(); break; default: break; } } }*/ /** * Method to stop the client * */ public void stopClient() { for(Thread t: improvementThreads) { t.interrupt(); } for(Thread t: unImprovementThreads) { t.interrupt(); } mixConnection.stopConnection(); connectionPool.stop(); System.exit(0); } }