package net.i2p.i2ptunnel.socks;
import java.net.InetAddress;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Iterator;
import java.util.Map;
import net.i2p.data.Destination;
import net.i2p.i2ptunnel.I2PTunnel;
import net.i2p.i2ptunnel.udpTunnel.I2PTunnelUDPClientBase;
/**
* A Datagram Tunnel that can have multiple bidirectional ports on the UDP side.
*
* TX:
* (ReplyTracker in multiple SOCKSUDPPorts -> ) I2PSink
*
* RX:
* (SOCKSUDPWrapper in multiple SOCKSUDPPorts <- ) MultiSink <- I2PSource
*
* The reply from a dest goes to the last SOCKSUDPPort that sent to that dest.
* If multiple ports are talking to a dest at the same time, this isn't
* going to work very well.
*
* @author zzz modded from streamr/StreamrConsumer
*/
public class SOCKSUDPTunnel extends I2PTunnelUDPClientBase {
/**
* Set up a tunnel with no UDP side yet.
* Use add() for each port.
*/
public SOCKSUDPTunnel(I2PTunnel tunnel) {
super(null, tunnel, tunnel, tunnel);
this.ports = new ConcurrentHashMap<Integer, SOCKSUDPPort>(1);
this.cache = new ConcurrentHashMap<Destination, SOCKSUDPPort>(1);
this.demuxer = new MultiSink<SOCKSUDPPort>(this.cache);
setSink(this.demuxer);
}
/** @return the UDP port number */
public int add(InetAddress host, int port) {
SOCKSUDPPort sup = new SOCKSUDPPort(host, port, this.cache);
this.ports.put(Integer.valueOf(sup.getPort()), sup);
sup.setSink(this);
sup.start();
return sup.getPort();
}
public void remove(Integer port) {
SOCKSUDPPort sup = this.ports.remove(port);
if (sup != null)
sup.stop();
for (Iterator<Map.Entry<Destination, SOCKSUDPPort>> iter = cache.entrySet().iterator(); iter.hasNext();) {
Map.Entry<Destination, SOCKSUDPPort> e = iter.next();
if (e.getValue() == sup)
iter.remove();
}
}
@Override
public final void startRunning() {
super.startRunning();
// demuxer start() doesn't do anything
startall();
}
@Override
public boolean close(boolean forced) {
stopall();
return super.close(forced);
}
/** you should really add() after startRunning() */
private void startall() {
}
private void stopall() {
for (SOCKSUDPPort sup : this.ports.values()) {
sup.stop();
}
this.ports.clear();
this.cache.clear();
}
private Map<Integer, SOCKSUDPPort> ports;
private Map<Destination, SOCKSUDPPort> cache;
private MultiSink<SOCKSUDPPort> demuxer;
}