/*******************************************************************************
* 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.socks_v0_001;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
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 staticContent.framework.util.Util;
import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.socks_v0_001.Config;
import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.socks_v0_001.multiplexer.Multiplexer;
// TODO: make sure DNS-requests are sent through the anonymous tunnel as well
public class ClientPlugIn extends Implementation implements
Layer5ApplicationClient {
/**
* 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;
private Config config;
@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);
}
@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, config);
openSocksServerSocket();
System.err.println("Warning: This is a test plug-in - do NOT send any sensitive data via this plug-in!");
new AcceptorThread().start();
}
public void openSocksServerSocket() {
synchronized (multiplexedAnonTunnel) {
if (userServerSocket != null && userServerSocket.isBound()) // already
// open
return;
InetAddress address = null;
try {
address = InetAddress.getByName(config.CLIENT_SOCKS_IP_ADDRESS);
} catch (UnknownHostException e) {
e.printStackTrace();
}
try {
userServerSocket = new ServerSocket(config.CLIENT_SOCKS_PORT,
1000, address);
System.out.println("\n\nWAITING FOR SOCKS CONNECTIONS ON " + address +":" + config.CLIENT_SOCKS_PORT +"\n");
} catch (IOException e) {
if (config.DEBUG)
e.printStackTrace();
throw new RuntimeException("client: couldn't bind client socks socket " + address +":" + config.CLIENT_SOCKS_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 (config.TALK_A_LOT == true)
System.out.println("client: waiting for connection from user application (e.g. web browser)");
try {
clientSocket = userServerSocket.accept();
if (config.TALK_A_LOT == true)
System.out.println("client: received a connection from a user application "
+ clientSocket.getInetAddress()
+ ":"
+ clientSocket.getPort());
if (config.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 (config.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 (config.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;
this.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 (config.DEBUG)
e.printStackTrace();
openSocksServerSocket();
continue;
}
}
}
/**
* Sends a SOCKS5 Method Reply Message to user.
*
* +----+--------+ |VER | METHOD | +----+--------+ | 1 | 1 |
* +----+--------+
*
* @param toClient
* OutputStream of Client
* @param method
* Chosen authentifiaction method
*/
private void sendSocks5MethodReply(OutputStream toClient, byte method) {
try {
toClient.write(0x05); // version
toClient.write(method); // method
toClient.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}