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; }