/*******************************************************************************
* 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.multiplexer;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.ConcurrentHashMap;
import staticContent.framework.socket.socketInterfaces.StreamAnonSocketMix;
import staticContent.framework.util.Util;
import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.socks_v0_001.Config;
import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.socks_v0_001.socks.SocksHandler;
public class Demultiplexer {
private StreamAnonSocketMix tunnelExit;
private ConcurrentHashMap<Integer, StreamData> connections = new ConcurrentHashMap<Integer, StreamData>();
private OutputStream osToMultiplexer;
private InputStream isFromMultiplexer;
private Object replySynchronizer = new Object();
private volatile boolean isConnected;
private Config config;
public Demultiplexer(StreamAnonSocketMix client, Config config) {
synchronized (replySynchronizer) {
this.tunnelExit = client;
this.osToMultiplexer = tunnelExit.getOutputStream();
this.isFromMultiplexer = this.tunnelExit.getInputStream();
this.config = config;
isConnected = true;
new RequestReceiver().start();
}
}
/**
* called after a lost connection on reconnect (TODO: implement calling
* part...)
*
* @param tunnelExit
*/
public void setTunnelExit(StreamAnonSocketMix tunnelExit) {
synchronized (replySynchronizer) {
this.tunnelExit = tunnelExit;
this.osToMultiplexer = tunnelExit.getOutputStream();
this.isFromMultiplexer = new BufferedInputStream(
tunnelExit.getInputStream());
isConnected = true;
replySynchronizer.notifyAll();
}
}
private void waitForConnection() {
synchronized (replySynchronizer) {
while (isConnected == false) { // wait for data if necessary
try {
replySynchronizer.wait();
} catch (InterruptedException e) {
e.printStackTrace();
continue;
}
}
}
}
/**
* Data structure with information about a multiplexed stream.
*/
private class StreamData {
int id;
ByteArrayOutputStream buffer;
SocksHandler socksHandler;
InputStream fromUser;
OutputStream toUser;
}
private class RequestReceiver extends Thread {
@Override
public void run() {
while (true) {
int id;
int len;
byte[] message;
StreamData streamData;
try {
id = Util.forceReadInt(isFromMultiplexer); // read id
if (config.TALK_A_LOT)
System.out.println("Demultiplexer: mp-id: " +id);streamData = connections.get(id);
if (streamData == null) { // new multiplexed stream
streamData = new StreamData();
connections.put(id, streamData);
streamData.id = id;
streamData.buffer = new ByteArrayOutputStream();
streamData.fromUser = new DemuxInputStream(streamData.buffer);
streamData.toUser = new DemuxOutputStream(osToMultiplexer, id);
streamData.socksHandler = new SocksHandler(streamData.fromUser, streamData.toUser, config);
}
len = Util.forceReadShort(isFromMultiplexer); // read length
if (config.TALK_A_LOT)
System.out.println("Demultiplexer: read " +len +" bytes from Multiplexer");
if (len < 0) {// DISCONNECT
streamData.socksHandler.close(); // the disconnect will be acknowledged automatically when the DemuxOutputStream is closed
connections.remove(streamData.id);
} else { // read data and store it in buffer (for socks
// handler)
message = Util.forceRead(isFromMultiplexer, len);
synchronized (streamData.buffer) {
streamData.buffer.write(message);
streamData.buffer.notifyAll();
}
}
} catch (IOException e) { // connection do multiplexer lost
e.printStackTrace();
waitForConnection();
continue;
}
}
}
}
}