package edu.washington.cs.oneswarm.ui.gwt;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import org.bouncycastle.util.encoders.Hex;
import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.config.impl.ConfigurationManager;
import org.gudy.azureus2.core3.ipfilter.IpRange;
import org.gudy.azureus2.core3.ipfilter.impl.IpRangeImpl;
import org.gudy.azureus2.core3.util.Average;
import org.gudy.azureus2.core3.util.Constants;
import org.gudy.azureus2.core3.util.Debug;
import com.aelitis.azureus.core.networkmanager.EventWaiter;
import com.aelitis.azureus.core.networkmanager.NetworkConnection;
import com.aelitis.azureus.core.networkmanager.NetworkManager;
import com.aelitis.azureus.core.networkmanager.RawMessage;
import com.aelitis.azureus.core.networkmanager.Transport;
import com.aelitis.azureus.core.networkmanager.impl.TransportHelper;
import com.aelitis.azureus.core.networkmanager.impl.osssl.OneSwarmSslTransportHelperFilterStream;
import com.aelitis.azureus.core.peermanager.messaging.Message;
import com.aelitis.azureus.core.peermanager.messaging.MessageStreamDecoder;
import com.aelitis.azureus.core.peermanager.messaging.MessageStreamEncoder;
import com.aelitis.azureus.core.peermanager.messaging.MessageStreamFactory;
import edu.washington.cs.oneswarm.ui.gwt.rpc.OneSwarmConstants;
import edu.washington.cs.oneswarm.ui.gwt.rpc.OneSwarmException;
public class RemoteAccessForward {
private static Logger logger = Logger.getLogger(RemoteAccessForward.class.getName());
private final ConcurrentHashMap<ConnectionForwarder, Long> connectionForwarders;
private final NetworkManager.ByteMatcher matcher;
private volatile boolean running = false;
// private final GlobalManagerStats stats;
public RemoteAccessForward() {
connectionForwarders = new ConcurrentHashMap<ConnectionForwarder, Long>();
matcher = new HttpSSLMatcher();
logger.fine("started remote access forward");
Timer t = new Timer("Remote Access idle checker", true);
t.schedule(new ForwardIdleTimeoutChecker(), 0, 10 * 1000);
}
public synchronized void start() {
if (!running) {
running = true;
String type = COConfigurationManager.getStringParameter(
OneSwarmConstants.REMOTE_ACCESS_LIMIT_TYPE_KEY,
OneSwarmConstants.REMOTE_ACCESS_LIMIT_TYPE_NOLIMIT);
String filter = COConfigurationManager.getStringParameter(
OneSwarmConstants.REMOTE_ACCESS_LIMIT_IPS_KEY, "");
try {
NetworkManager.getSingleton()
.requestIncomingConnectionRouting(matcher,
new OsNetworkRouterListener(type, filter),
new DummyMessageDecoderFactory());
} catch (OneSwarmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public synchronized void stop() {
if (running) {
for (Iterator<ConnectionForwarder> iterator = connectionForwarders.keySet().iterator(); iterator
.hasNext();) {
ConnectionForwarder cf = iterator.next();
cf.stop(false);
iterator.remove();
}
NetworkManager.getSingleton().cancelIncomingConnectionRouting(matcher);
running = false;
}
}
private static class DummyMessageDecoderFactory implements MessageStreamFactory {
public MessageStreamDecoder createDecoder() {
return new MessageStreamDecoder() {
public ByteBuffer destroy() {
return null;
}
public int getDataBytesDecoded() {
return 0;
}
public int getPercentDoneOfCurrentMessage() {
return 0;
}
public int getProtocolBytesDecoded() {
return 0;
}
public void pauseDecoding() {
}
public int performStreamDecode(Transport transport, int max_bytes)
throws IOException {
return 0;
}
public Message[] removeDecodedMessages() {
return null;
}
public void resumeDecoding() {
}
};
}
public MessageStreamEncoder createEncoder() {
return new MessageStreamEncoder() {
public RawMessage[] encodeMessage(Message message) {
return null;
}
};
}
}
public List<Map<String, String>> getRemoteAccessStats() {
List<Map<String, String>> stats = new LinkedList<Map<String, String>>();
for (ConnectionForwarder f : connectionForwarders.keySet()) {
Map<String, String> s = new HashMap<String, String>();
s.put("remote_ip", f.getRemoteIp());
String remoteDns = getRemoteDomain(f.getRemoteHostName());
if (remoteDns != null) {
s.put("remote_dns", remoteDns);
}
s.put("age", "" + f.getAge());
s.put("total_uploaded", "" + f.getUploadTotal());
s.put("total_downloaded", "" + f.getDownloadTotal());
s.put("upload_rate", "" + f.getUploadRate());
s.put("download_rate", f.getDownloadRate() + "");
s.put("lan_local", "" + f.isLanLocal());
s.put("idle_out", f.getOutIdleTime() + "");
s.put("idle_in", f.getInIdleTime() + "");
stats.add(s);
}
return stats;
}
private static String getRemoteDomain(String hostname) {
if (hostname != null) {
String[] s = hostname.split("\\.");
if (s.length > 2) {
String last3 = s[s.length - 3] + "." + s[s.length - 2] + "." + s[s.length - 1];
if (s.length > 3) {
last3 = "..." + last3;
}
return last3;
} else if (s.length > 1) {
return s[s.length - 2] + "." + s[s.length - 1];
}
}
return null;
}
private class ConnectionForwarder {
private long started;
private static final int BUFFER_SIZE = 5000;
private static final int READ_POLL_RATE = 100;
private static final int IDLE_TIME_OUT = 60 * 1000;
private boolean quit = false;
private Socket socket;
private OutputStream sslToWebStream;
private final Transport transport;
private final NetworkConnection connection;
private InputStream webToSslStream;
private final Average upload = Average.getInstance(1000, 10);
private final Average download = Average.getInstance(1000, 10);
private final String remoteIp;
private String hostname;
public ConnectionForwarder(NetworkConnection connection) {
this.transport = connection.getTransport();
this.connection = connection;
lanLocal = connection.isLANLocal();
logger.fine("created remote access forwarder");
this.remoteIp = connection.getEndpoint().getNotionalAddress().getAddress()
.getHostAddress();
this.hostname = remoteIp;
Thread t = new Thread(new Runnable() {
public void run() {
try {
hostname = InetAddress.getByName(remoteIp).getCanonicalHostName();
logger.finer("resolved remote host: " + remoteIp + "(" + hostname + ")");
if (hostname != null) {
String[] s = hostname.split("\\.");
boolean onlyNumbers = true;
for (String e : s) {
try {
int i = Integer.parseInt(e);
logger.finer("resolved " + e + " to " + i);
} catch (NumberFormatException ex) {
onlyNumbers = false;
}
}
if (onlyNumbers) {
logger.finer("hostname only numbers, setting to null");
hostname = null;
}
}
} catch (Throwable t) {
// this is ok
t.printStackTrace();
}
}
});
t.setName("ip resolver thread");
t.setDaemon(true);
t.start();
}
public boolean isLanLocal() {
return lanLocal;
}
public String getRemoteHostName() {
return hostname;
}
private final GwtWebToSSL gwtWebToSSL = new GwtWebToSSL();
private final SSLToGwtWeb sslToGwtWeb = new SSLToGwtWeb();
private final boolean lanLocal;
public void start() throws UnknownHostException, IOException {
started = System.currentTimeMillis();
socket = new Socket();
socket.connect(new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
Constants.LOCAL_WEB_SERVER_PORT_AUTH));
logger.fine("connected to:" + socket.getInetAddress().getHostAddress() + ":"
+ socket.getPort());
webToSslStream = socket.getInputStream();
sslToWebStream = socket.getOutputStream();
Thread t = new Thread(gwtWebToSSL);
t.setName("GwtToSSLMover");
t.setDaemon(true);
t.start();
Thread t2 = new Thread(sslToGwtWeb);
t2.setName("SSLToGwtWebMover");
t2.setDaemon(true);
t2.start();
}
public long getDownloadRate() {
return download.getAverage();
}
public long getUploadRate() {
return upload.getAverage();
}
public long getUploadTotal() {
return gwtWebToSSL.getTotalWritten();
}
public long getDownloadTotal() {
return sslToGwtWeb.getTotalWritten();
}
public long getAge() {
return System.currentTimeMillis() - started;
}
public long getOutIdleTime() {
return gwtWebToSSL.getIdleTime();
}
public long getInIdleTime() {
return sslToGwtWeb.getIdleTime();
}
public boolean isTimedOut() {
if (getOutIdleTime() > IDLE_TIME_OUT && getInIdleTime() > IDLE_TIME_OUT) {
return true;
}
return false;
}
public String getRemoteIp() {
return remoteIp;
}
public void stop(boolean deregister) {
if (!quit) {
logger.fine("forwarder stopped, ->ssl: " + sslToGwtWeb.getTotalWritten()
+ " ->gwt: " + gwtWebToSSL.getTotalWritten());
}
this.quit = true;
connection.close();
if (deregister) {
connectionForwarders.remove(this);
}
}
private class GwtWebToSSL implements Runnable {
long lastWriteTime = System.currentTimeMillis();
long totalRead = 0;
long totalWritten = 0;
public long getTotalWritten() {
return totalWritten;
}
public long getIdleTime() {
return System.currentTimeMillis() - lastWriteTime;
}
public void run() {
byte[] readBuffer = new byte[BUFFER_SIZE];
ByteBuffer writeBuffer = ByteBuffer.allocate(BUFFER_SIZE);
writeBuffer.clear();
writeBuffer.flip();
ByteBuffer[] writeBuffers = new ByteBuffer[] { writeBuffer };
try {
while (!quit) {
// System.out.println("->SSL: remaining: " +
// writeBuffer.remaining());
if (writeBuffer.remaining() == 0) {
// System.out.println("->SSL: reading");
// int available = webToSslStream.available();
// int len = 0;
// if (available > 0) {
// /*
// * prefer to read whatever is available even if
// * it won't fill the entire buffer
// */
//
// len = webToSslStream.read(readBuffer, 0,
// Math.min(available, readBuffer.length));
// } else {
// /*
// * else, block until you can read one byte
// */
// len = webToSslStream.read(readBuffer, 0, 1);
// }
int len = webToSslStream.read(readBuffer);
if (len == -1) {
stop(true);
return;
}
totalRead += len;
logger.finest("->SSL: read: " + len + " total: " + totalRead);
writeBuffer.clear();
writeBuffer.put(readBuffer, 0, len);
writeBuffer.flip();
// System.out.println("->SSL: can write " +
// writeBuffer.remaining());
// System.out.println("'" + new String(readBuffer,
// 0, len, "UTF-8") + "'");
}
EventWaiter waiter = new EventWaiter();
if (!transport.isReadyForWrite(waiter)) {
// System.out.println("->SSL: waiting for transport")
// ;
waiter.waitForEvent(READ_POLL_RATE);
}
if (transport.isReadyForWrite(null)) {
long len = transport.write(writeBuffers, 0, 1);
// rateHandler.bytesProcessed((int) len);
totalWritten += len;
// stats.protocolBytesSent((int) len, lanLocal);
upload.addValue(len);
logger.finest("->SSL: wrote: " + len + " total: " + totalWritten);
lastWriteTime = System.currentTimeMillis();
}
}
} catch (Throwable e) {
logger.fine("closing connection forwarder, got exception:" + e.getMessage());
}
ConnectionForwarder.this.stop(true);
}
}
private class SSLToGwtWeb implements Runnable {
private final ByteBuffer readBuffer = ByteBuffer.allocate(BUFFER_SIZE);
private final ByteBuffer[] readBuffers = new ByteBuffer[] { readBuffer };
private final byte[] writeBuffer = new byte[BUFFER_SIZE];
long totalRead = 0;
long totalWritten = 0;
long lastReadTime = System.currentTimeMillis();
public long getTotalWritten() {
return totalWritten;
}
public long getIdleTime() {
return System.currentTimeMillis() - lastReadTime;
}
public void run() {
try {
while (!quit) {
EventWaiter waiter = new EventWaiter();
if (!transport.isReadyForRead(waiter)) {
// System.out.println(
// "->GWT: transport not ready, waiting");
waiter.waitForEvent(READ_POLL_RATE);
}
if (transport.isReadyForRead(null)) {
// cast is safe, len can't be more than BUFFER_SIZE
readBuffer.clear();
// System.out.println("->GWT: trying to read: " +
// readBuffer.remaining());
// int len = (int) transport.read(readBuffers, 0,
// 1);
transport.read(readBuffers, 0, 1);
int len = readBuffer.position();
lastReadTime = System.currentTimeMillis();
// stats.protocolBytesReceived(len, lanLocal);
download.addValue(len);
totalRead += len;
logger.finest("->GWT: read: " + len + " total: " + totalRead);
if (len > 1) {
readBuffer.flip();
// System.out.println("len=" + len +
// " remaining=" + readBuffer.remaining());
readBuffer.get(writeBuffer, 0, readBuffer.remaining());
// System.out.println("->GWT: copied " + len +
// " bytes");
sslToWebStream.write(writeBuffer, 0, len);
totalWritten += len;
logger.finest("->GWT: wrote " + len + " total: " + totalWritten);
// System.out.println("'" + new
// String(writeBuffer, 0, len, "UTF-8") + "'");
} else {
// System.out.println(
// "->GWT: read 0 bytes, waiting for selects");
}
} else {
// nop
}
}
} catch (Throwable e) {
logger.fine("->GWT: transport/socket closed: " + e.getMessage());
// e.printStackTrace();
}
ConnectionForwarder.this.stop(true);
}
}
}
private static class HttpSSLMatcher implements NetworkManager.ByteMatcher {
private final static String GET_HANDSHAKE = new String("GET /");
private final static String POST_HANDSHAKE = new String("POST /");
public HttpSSLMatcher() {
}
public byte[][] getSharedSecrets() {
return null;
}
public int getSpecificPort() {
return (ConfigurationManager.getInstance().getIntParameter("TCP.Listen.Port"));
}
public Object matches(TransportHelper transport, ByteBuffer to_compare, int port) {
int old_limit = to_compare.limit();
int old_pos = to_compare.position();
byte[] compareBytes = new byte[to_compare.remaining()];
to_compare.get(compareBytes);
String compareString = null;
try {
compareString = new String(compareBytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
logger.finer("checking " + compareString.length() + " chars for http remote access:");
// if (compareString.contains("\n")) {
// String[] split = compareString.split("\\n");
// System.err.println("checking http match for: " + split[0]);
// }
logger.finer("'" + compareString + "'");
logger.finer("'" + new String(Hex.encode(compareBytes)) + "'");
boolean matches = compareString.startsWith(GET_HANDSHAKE)
|| compareString.startsWith(POST_HANDSHAKE);
if (matches) {
logger.finer("matches http");
return "";
} else {
logger.finer("does not match http");
to_compare.limit(old_limit); // restore buffer structure
to_compare.position(old_pos);
return null;
}
}
public int matchThisSizeOrBigger() {
return Math.max(GET_HANDSHAKE.length(), POST_HANDSHAKE.length());
}
public int maxSize() {
return matchThisSizeOrBigger();
}
public Object minMatches(TransportHelper transport, ByteBuffer to_compare, int port) {
return (matches(transport, to_compare, port));
}
public int minSize() {
return matchThisSizeOrBigger();
}
}
static final String redirect = "<html><HEAD>\r\n" + "<script language=\"javascript\">\r\n"
+ "if (document.location.protocol != \"https:\")\r\n" + "{\r\n"
+ "document.location.href = \"https://\" + document.location.host;\r\n" + "};\r\n"
+ "</script>\r\n" + "<title>OneSwarm Remote Acess</title>\r\n" + "</HEAD>\r\n"
+ "<body>\r\n" + "<p>Remote access requires the use of SSL.</p></body></html>\r\n";
static final String accessDenied = "<html><HEAD>\r\n"
+ "<title>OneSwarm Remote Acess</title>\r\n" + "</HEAD>\r\n" + "<body>\r\n"
+ "<h1>Access Denied</h1><p>Connections from ==IP== not allowed</p></body></html>\r\n";
private class OsNetworkRouterListener implements NetworkManager.RoutingListener {
private final String ipFilterType;
private final List<IpRange> allowedIpRanges;
public OsNetworkRouterListener(String ipFilterType, String ipFilterString)
throws OneSwarmException {
this.ipFilterType = ipFilterType;
if (OneSwarmConstants.REMOTE_ACCESS_LIMIT_TYPE_RANGE.equals(ipFilterType)) {
this.allowedIpRanges = createIpFilter(ipFilterString);
} else {
this.allowedIpRanges = null;
}
}
public boolean autoCryptoFallback() {
return (false);
}
public void connectionRouted(NetworkConnection connection, Object routing_data) {
boolean allowed = false;
// first check if filters
String remoteIp = connection.getEndpoint().getNotionalAddress().getAddress()
.getHostAddress();
if (OneSwarmConstants.REMOTE_ACCESS_LIMIT_TYPE_NOLIMIT.equals(this.ipFilterType)) {
allowed = true;
} else if (OneSwarmConstants.REMOTE_ACCESS_LIMIT_TYPE_LAN.equals(this.ipFilterType)) {
allowed = connection.isLANLocal();
logger.fine("remote access connection ip-local: " + allowed);
} else if (OneSwarmConstants.REMOTE_ACCESS_LIMIT_TYPE_RANGE.equals(this.ipFilterType)) {
allowed = isInRange(allowedIpRanges, remoteIp);
} else {
Debug.out("unknown ip filter type");
}
if (!allowed) {
try {
byte[] message = accessDenied.replaceAll("==IP==", remoteIp).getBytes("UTF-8");
ByteBuffer b = ByteBuffer.allocate(message.length);
b.put(message);
b.flip();
connection.getTransport().write(new ByteBuffer[] { b }, 0, 1);
connection.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
logger.fine("connection closed due to IP range limits: " + connection);
return;
}
if (!connection.getTransport().getEncryption()
.startsWith(OneSwarmSslTransportHelperFilterStream.SSL_NAME)) {
logger.fine("remote OSGWT ui connection without SSL: " + connection + " ("
+ connection.getTransport().getEncryption() + "), redirecting");
try {
byte[] errorMessage = redirect.getBytes("UTF-8");
ByteBuffer b = ByteBuffer.allocate(errorMessage.length);
b.put(errorMessage);
b.flip();
connection.getTransport().write(new ByteBuffer[] { b }, 0, 1);
connection.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return;
}
logger.fine("Incoming connection from [" + connection
+ "] successfully routed to OneSwarm GWT Remote: encr: "
+ connection.getTransport().getEncryption());
try {
ConnectionForwarder connectionForwarder = new ConnectionForwarder(connection);
connectionForwarders.put(connectionForwarder, System.currentTimeMillis());
connectionForwarder.start();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static List<IpRange> createIpFilter(String filterString) throws OneSwarmException {
List<IpRange> allowedRanges = new LinkedList<IpRange>();
if (filterString == null) {
return allowedRanges;
}
filterString = filterString.trim();
if (filterString.length() == 0) {
return allowedRanges;
}
/*
*
*/
String[] split = filterString.split(",");
for (String s : split) {
s = s.trim();
String baseProblem = "Problem occured when processing '" + s + "': ";
if (s.contains("-")) {
String[] ips = s.split("-");
if (ips.length != 2) {
throw new OneSwarmException(baseProblem + "- detected more than once");
}
String ip = null;
InetAddress from = null;
InetAddress to = null;
try {
ip = ips[0].trim();
from = InetAddress.getByName(ip);
ip = ips[1].trim();
to = InetAddress.getByName(ip);
} catch (UnknownHostException e) {
throw new OneSwarmException(baseProblem + " unable to parse ip: '" + ip + "':"
+ e.getMessage());
}
IpRange r = new IpRangeImpl("", from.getHostAddress(), to.getHostAddress(), true);
if (!r.isValid()) {
throw new OneSwarmException(baseProblem + " invalid ip range");
}
allowedRanges.add(r);
} else if (s.contains("/")) {
String[] parts = s.trim().split("/");
if (parts.length != 2) {
throw new OneSwarmException(baseProblem + "/ detected more than once");
}
int mask = -1;
try {
mask = Integer.parseInt(parts[1].trim());
} catch (NumberFormatException e) {
e.printStackTrace();
}
if (mask < 0 || mask > 32) {
throw new OneSwarmException(baseProblem + "invalid mask, '" + parts[1] + "'");
}
InetAddress baseIp = null;
try {
baseIp = InetAddress.getByName(parts[0]);
} catch (UnknownHostException e) {
throw new OneSwarmException(baseProblem + " unable to parse ip: '" + parts[0]
+ "':" + e.getMessage());
}
byte[] baseIPBytes = baseIp.getAddress();
InetAddress from = null;
InetAddress to = null;
try {
from = InetAddress.getByAddress(setBits(baseIPBytes, mask, false));
to = InetAddress.getByAddress(setBits(baseIPBytes, mask, true));
} catch (UnknownHostException e) {
throw new OneSwarmException(baseProblem + e.getMessage());
}
IpRange r = new IpRangeImpl("", from.getHostAddress(), to.getHostAddress(), true);
if (!r.isValid()) {
throw new OneSwarmException(baseProblem + " invalid ip range");
}
allowedRanges.add(r);
} else {
InetAddress baseIp = null;
try {
baseIp = InetAddress.getByName(s.trim());
} catch (UnknownHostException e) {
throw new OneSwarmException(baseProblem + " unable to parse ip: '" + s + "':"
+ e.getMessage());
}
IpRange r = new IpRangeImpl("", baseIp.getHostAddress(), baseIp.getHostAddress(),
false);
allowedRanges.add(r);
}
}
return allowedRanges;
}
public static byte[] setBits(byte[] array, int startingFromBit, boolean bitValue) {
byte[] clone = new byte[array.length];
System.arraycopy(array, 0, clone, 0, clone.length);
for (int i = 0; i < clone.length; i++) {
for (int j = 0; j < 8; j++) {
if (startingFromBit <= i * 8 + j) {
if (bitValue) {
// set it to 1
clone[i] |= 1 << (7 - j);
} else {
// set the bit to 0
clone[i] &= ~(1 << (7 - j));
}
}
}
}
return clone;
}
private static boolean isInRange(List<IpRange> ranges, String ip) {
for (IpRange ipRange : ranges) {
if (ipRange.isInRange(ip)) {
logger.finer(ip + " is in range: " + ipRange.getStartIp() + "-"
+ ipRange.getEndIp());
return true;
}
}
logger.finer(ip + " is NOT in any range");
return false;
}
public static void main(String[] args) {
try {
List<IpRange> filter = createIpFilter("192.168.0.1-192.168.1.1, 192.168.2.128/22, 10.0.1.1");
for (IpRange ipRange : filter) {
System.out.println(ipRange.getStartIp() + "-" + ipRange.getEndIp());
}
String[] ipToTest = { "192.168.1.1", "192.168.3.1", "10.0.1.1", "10.1.0.1" };
for (String ip : ipToTest) {
isInRange(filter, ip);
}
} catch (OneSwarmException e) {
System.err.println(e.getMessage());
}
/*
* test errors
*/
try {
createIpFilter("afgwedsf");
} catch (OneSwarmException e) {
System.err.println(e.getMessage());
}
try {
createIpFilter("192.168.0.1-192.168.1.1-192.167.13.2, 192.168.2.0/24,10.0.1.1");
} catch (OneSwarmException e) {
System.err.println(e.getMessage());
}
try {
createIpFilter("192.168.1.1-192.168.0.1, 192.168.2.0/24,10.0.1.1");
} catch (OneSwarmException e) {
System.err.println(e.getMessage());
}
try {
createIpFilter("192.168.1.1/42");
} catch (OneSwarmException e) {
System.err.println(e.getMessage());
}
}
private class ForwardIdleTimeoutChecker extends TimerTask {
@Override
public void run() {
for (Iterator<ConnectionForwarder> iterator = connectionForwarders.keySet().iterator(); iterator
.hasNext();) {
ConnectionForwarder cf = iterator.next();
if (cf.isTimedOut()) {
logger.fine("closing remote access connection (idle time out)");
cf.stop(false);
iterator.remove();
}
}
}
}
}