diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java index 083123b5fc698908d24a6504734094816295e628..d1dec5248e008e5b5f10ef03756896d1ca454667 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java @@ -45,7 +45,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { private static final String[] CLIENT_SKIPHEADERS = {HASH_HEADER, DEST64_HEADER, DEST32_HEADER}; private static final String SERVER_HEADER = "Server"; private static final String[] SERVER_SKIPHEADERS = {SERVER_HEADER}; - private static final long HEADER_TIMEOUT = 60*1000; + private static final long HEADER_TIMEOUT = 15*1000; + private static final long TOTAL_HEADER_TIMEOUT = 2 * HEADER_TIMEOUT; private static final long START_INTERVAL = (60 * 1000) * 3; private long _startedOn = 0L; @@ -492,7 +493,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { } } - protected static Map<String, List<String>> readHeaders(InputStream in, StringBuilder command, String[] skipHeaders, I2PAppContext ctx) throws IOException { + protected static Map<String, List<String>> readHeaders(InputStream in, StringBuilder command, + String[] skipHeaders, I2PAppContext ctx) throws IOException { HashMap<String, List<String>> headers = new HashMap<String, List<String>>(); StringBuilder buf = new StringBuilder(128); @@ -516,6 +518,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { if (trimmed > 0) ctx.statManager().addRateData("i2ptunnel.httpNullWorkaround", trimmed, 0); + // slowloris / darkloris + long expire = ctx.clock().now() + TOTAL_HEADER_TIMEOUT; int i = 0; while (true) { if (++i > MAX_HEADERS) @@ -528,6 +532,8 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { // end of headers reached return headers; } else { + if (ctx.clock().now() > expire) + throw new IOException("Headers took too long [" + buf.toString() + "]"); int split = buf.indexOf(":"); if (split <= 0) throw new IOException("Invalid HTTP header, missing colon [" + buf.toString() + "]"); String name = buf.substring(0, split).trim(); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java index b592ebcf82dcc267f90ddbbb621329b6207403f1..31cc4c26c2ffe43f780ad1ba0497ea3efc968489 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java @@ -62,7 +62,8 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable { public static final String PROP_WEBIRC_SPOOF_IP_DEFAULT="127.0.0.1"; public static final String PROP_HOSTNAME="ircserver.fakeHostname"; public static final String PROP_HOSTNAME_DEFAULT="%f.b32.i2p"; - private static final long HEADER_TIMEOUT = 60*1000; + private static final long HEADER_TIMEOUT = 15*1000; + private static final long TOTAL_HEADER_TIMEOUT = 2 * HEADER_TIMEOUT; private final static byte[] ERR_UNAVAILABLE = (":ircserver.i2p 499 you :" + @@ -188,12 +189,16 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable { StringBuilder buf = new StringBuilder(128); int lineCount = 0; + // slowloris / darkloris + long expire = System.currentTimeMillis() + TOTAL_HEADER_TIMEOUT; while (true) { String s = DataHelper.readLine(in); if (s == null) throw new IOException("EOF reached before the end of the headers [" + buf.toString() + "]"); if (++lineCount > 10) throw new IOException("Too many lines before USER or SERVER, giving up"); + if (System.currentTimeMillis() > expire) + throw new IOException("Headers took too long [" + buf.toString() + "]"); s = s.trim(); //if (_log.shouldLog(Log.DEBUG)) // _log.debug("Got line: " + s); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java index 2e3b55fa1bc5d31919607d6fb8fa6a2491cd235e..24e2c58eab5b04ca6abcd058218ebf34cc600d23 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java @@ -49,8 +49,8 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { protected Logging l; - private static final long DEFAULT_READ_TIMEOUT = -1; // 3*60*1000; - /** default timeout to 3 minutes - override if desired */ + private static final long DEFAULT_READ_TIMEOUT = 5*60*1000; + /** default timeout to 5 minutes - override if desired */ protected long readTimeout = DEFAULT_READ_TIMEOUT; /** do we use threads? default true (ignored for standard servers, always false) */ diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketOptions.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketOptions.java index c20a89a591e6983cd75456f1c17d0979c839c334..1095a9334ef0cafd1266fecc78e327777d507f7d 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketOptions.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketOptions.java @@ -17,6 +17,9 @@ public interface I2PSocketOptions { /** * How long we will wait for the ACK from a SYN, in milliseconds. * + * Default 60 seconds. Max of 2 minutes enforced in Connection.java, + * and it also interprets <= 0 as default. + * * @return milliseconds to wait, or -1 if we will wait indefinitely */ public long getConnectTimeout(); @@ -24,6 +27,9 @@ public interface I2PSocketOptions { /** * Define how long we will wait for the ACK from a SYN, in milliseconds. * + * Default 60 seconds. Max of 2 minutes enforced in Connection.java, + * and it also interprets <= 0 as default. + * * @param ms timeout in ms */ public void setConnectTimeout(long ms); @@ -32,6 +38,9 @@ public interface I2PSocketOptions { * What is the longest we'll block on the input stream while waiting * for more data. If this value is exceeded, the read() throws * InterruptedIOException + * + * WARNING: Default -1 (unlimited), which is probably not what you want. + * * @return timeout in ms */ public long getReadTimeout(); @@ -40,6 +49,9 @@ public interface I2PSocketOptions { * What is the longest we'll block on the input stream while waiting * for more data. If this value is exceeded, the read() throws * InterruptedIOException + * + * WARNING: Default -1 (unlimited), which is probably not what you want. + * * @param ms timeout in ms */ public void setReadTimeout(long ms); @@ -50,6 +62,8 @@ public interface I2PSocketOptions { * either some data is removed or the connection is closed. If this is * less than or equal to zero, there is no limit (warning: can eat ram) * + * Default 64 KB + * * @return buffer size limit, in bytes */ public int getMaxBufferSize(); @@ -60,6 +74,8 @@ public interface I2PSocketOptions { * either some data is removed or the connection is closed. If this is * less than or equal to zero, there is no limit (warning: can eat ram) * + * Default 64 KB + * * @param numBytes How much data will we accept that hasn't been written out yet. */ public void setMaxBufferSize(int numBytes); @@ -69,6 +85,9 @@ public interface I2PSocketOptions { * for the data to flush. If this value is exceeded, the write() throws * InterruptedIOException. If this is less than or equal to zero, there * is no timeout. + * + * Default -1 (unlimited) + * * @return wait time to block on the output stream while waiting for the data to flush. */ public long getWriteTimeout(); @@ -78,6 +97,9 @@ public interface I2PSocketOptions { * for the data to flush. If this value is exceeded, the write() throws * InterruptedIOException. If this is less than or equal to zero, there * is no timeout. + * + * Default -1 (unlimited) + * * @param ms wait time to block on the output stream while waiting for the data to flush. */ public void setWriteTimeout(long ms); diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketOptionsImpl.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketOptionsImpl.java index 5d0636676be05648a04f407e6d0c58c0a8a5865a..3a1722dfb31942c1ffa174b71be64894139d843c 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketOptionsImpl.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketOptionsImpl.java @@ -96,6 +96,9 @@ class I2PSocketOptionsImpl implements I2PSocketOptions { /** * How long we will wait for the ACK from a SYN, in milliseconds. * + * Default 60 seconds. Max of 2 minutes enforced in Connection.java, + * and it also interprets <= 0 as default. + * * @return milliseconds to wait, or -1 if we will wait indefinitely */ public long getConnectTimeout() { @@ -105,6 +108,9 @@ class I2PSocketOptionsImpl implements I2PSocketOptions { /** * Define how long we will wait for the ACK from a SYN, in milliseconds. * + * Default 60 seconds. Max of 2 minutes enforced in Connection.java, + * and it also interprets <= 0 as default. + * */ public void setConnectTimeout(long ms) { _connectTimeout = ms; @@ -114,6 +120,8 @@ class I2PSocketOptionsImpl implements I2PSocketOptions { * What is the longest we'll block on the input stream while waiting * for more data. If this value is exceeded, the read() throws * InterruptedIOException + * + * WARNING: Default -1 (unlimited), which is probably not what you want. */ public long getReadTimeout() { return _readTimeout; @@ -123,6 +131,8 @@ class I2PSocketOptionsImpl implements I2PSocketOptions { * What is the longest we'll block on the input stream while waiting * for more data. If this value is exceeded, the read() throws * InterruptedIOException + * + * WARNING: Default -1 (unlimited), which is probably not what you want. */ public void setReadTimeout(long ms) { _readTimeout = ms; @@ -134,6 +144,8 @@ class I2PSocketOptionsImpl implements I2PSocketOptions { * either some data is removed or the connection is closed. If this is * less than or equal to zero, there is no limit (warning: can eat ram) * + * Default 64 KB + * * @return buffer size limit, in bytes */ public int getMaxBufferSize() { @@ -146,6 +158,8 @@ class I2PSocketOptionsImpl implements I2PSocketOptions { * either some data is removed or the connection is closed. If this is * less than or equal to zero, there is no limit (warning: can eat ram) * + * Default 64 KB + * */ public void setMaxBufferSize(int numBytes) { _maxBufferSize = numBytes; @@ -156,6 +170,8 @@ class I2PSocketOptionsImpl implements I2PSocketOptions { * for the data to flush. If this value is exceeded, the write() throws * InterruptedIOException. If this is less than or equal to zero, there * is no timeout. + * + * Default -1 (unlimited) */ public long getWriteTimeout() { return _writeTimeout; @@ -166,6 +182,8 @@ class I2PSocketOptionsImpl implements I2PSocketOptions { * for the data to flush. If this value is exceeded, the write() throws * InterruptedIOException. If this is less than or equal to zero, there * is no timeout. + * + * Default -1 (unlimited) */ public void setWriteTimeout(long ms) { _writeTimeout = ms; diff --git a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java index 29a05512f71737526e42be160bc264582b281042..99909b6b30c49c2f1ebbd5454518215402d1c144 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java @@ -87,7 +87,7 @@ class Connection { /** wait up to 5 minutes after disconnection so we can ack/close packets */ public static final int DISCONNECT_TIMEOUT = 5*60*1000; - private static final long DEFAULT_CONNECT_TIMEOUT = 60*1000; + public static final int DEFAULT_CONNECT_TIMEOUT = 60*1000; private static final long MAX_CONNECT_TIMEOUT = 2*60*1000; public static final int MAX_WINDOW_SIZE = 128; @@ -549,7 +549,15 @@ class Connection { public void setInbound() { _isInbound = true; } public boolean isInbound() { return _isInbound; } + + /** + * Always true at the start, even if we haven't gotten a reply on an + * outbound connection. Only set to false on disconnect. + * For outbound, use getHighestAckedThrough() >= 0 also, + * to determine if the connection is up. + */ public boolean getIsConnected() { return _connected; } + public boolean getHardDisconnected() { return _hardDisconnected; } public boolean getResetSent() { return _resetSent; } public long getResetSentOn() { return _resetSentOn; } @@ -1017,7 +1025,7 @@ class Connection { @Override public String toString() { - StringBuilder buf = new StringBuilder(128); + StringBuilder buf = new StringBuilder(256); buf.append("[Connection "); if (_receiveStreamId > 0) buf.append(Packet.toId(_receiveStreamId)); @@ -1075,6 +1083,7 @@ class Connection { buf.append(" close received ").append(DataHelper.formatDuration(_context.clock().now() - getCloseReceivedOn())).append(" ago"); buf.append(" sent: ").append(1 + _lastSendId.get()); buf.append(" rcvd: ").append(1 + _inputStream.getHighestBlockId() - missing); + buf.append(" ackThru ").append(_highestAckedThrough); buf.append(" maxWin ").append(getOptions().getMaxWindowSize()); buf.append(" MTU ").append(getOptions().getMaxMessageSize()); diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java index cd535cc310b70abc60c41b851fadbafe335cad35..aac78d99369a55e1828dc8c6b388abcd711ac707 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java @@ -343,8 +343,8 @@ class ConnectionOptions extends I2PSocketOptionsImpl { DEFAULT_CONGESTION_AVOIDANCE_GROWTH_RATE_FACTOR)); setSlowStartGrowthRateFactor(getInt(opts, PROP_SLOW_START_GROWTH_RATE_FACTOR, DEFAULT_SLOW_START_GROWTH_RATE_FACTOR)); - // overrides default in super() - setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT)); + // overrides default in super()... why? + //setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT)); setAnswerPings(getBool(opts, PROP_ANSWER_PINGS, DEFAULT_ANSWER_PINGS)); setEnforceProtocol(getBool(opts, PROP_ENFORCE_PROTO, DEFAULT_ENFORCE_PROTO)); initLists(opts); @@ -399,9 +399,8 @@ class ConnectionOptions extends I2PSocketOptionsImpl { setSlowStartGrowthRateFactor(getInt(opts, PROP_SLOW_START_GROWTH_RATE_FACTOR, DEFAULT_SLOW_START_GROWTH_RATE_FACTOR)); if (opts.containsKey(PROP_CONNECT_TIMEOUT)) - // wow 5 minutes!!! FIXME!! // overrides default in super() - setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DISCONNECT_TIMEOUT)); + setConnectTimeout(getInt(opts, PROP_CONNECT_TIMEOUT, Connection.DEFAULT_CONNECT_TIMEOUT)); if (opts.containsKey(PROP_ANSWER_PINGS)) setAnswerPings(getBool(opts, PROP_ANSWER_PINGS, DEFAULT_ANSWER_PINGS)); if (opts.containsKey(PROP_ENFORCE_PROTO)) diff --git a/apps/streaming/java/src/net/i2p/client/streaming/SchedulerChooser.java b/apps/streaming/java/src/net/i2p/client/streaming/SchedulerChooser.java index 17918a6e129219842da6e374e4b5c2f8dd09c5e2..1dd473907aa165f1e8e63a32c41a76e6722e0458 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/SchedulerChooser.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/SchedulerChooser.java @@ -29,7 +29,7 @@ class SchedulerChooser { TaskScheduler scheduler = _schedulers.get(i); if (scheduler.accept(con)) { //if (_log.shouldLog(Log.DEBUG)) - // _log.debug("Scheduling for " + con + " with " + scheduler.getClass().getName()); + // _log.debug("Scheduling for " + con + " with " + scheduler.getClass().getSimpleName()); return scheduler; } } diff --git a/core/java/src/net/i2p/util/SocketTimeout.java b/core/java/src/net/i2p/util/SocketTimeout.java index 3a8f57400b3713c028a45c1a3a18ba52857c4ce0..e5c02d5c9713e2cf96f83d895814c5650aa329fd 100644 --- a/core/java/src/net/i2p/util/SocketTimeout.java +++ b/core/java/src/net/i2p/util/SocketTimeout.java @@ -7,7 +7,7 @@ import java.util.Date; /** * This should be deprecated. - * It is only used by EepGet, and it uses the inefficient SimpleTimer. + * It is only used by EepGet. * The only advantage seems to be a total timeout period, which is the second * argument to EepGet.fetch(headerTimeout, totalTimeout, inactivityTimeout), * which is most likely always set to -1. @@ -15,14 +15,16 @@ import java.util.Date; * Use socket.setsotimeout instead? */ public class SocketTimeout extends SimpleTimer2.TimedEvent { - private Socket _targetSocket; - private long _startTime; - private long _inactivityDelay; - private long _lastActivity; - private long _totalTimeoutTime; - private boolean _cancelled; - private Runnable _command; + private volatile Socket _targetSocket; + private final long _startTime; + private volatile long _inactivityDelay; + private volatile long _lastActivity; + private volatile long _totalTimeoutTime; + private volatile boolean _cancelled; + private volatile Runnable _command; + public SocketTimeout(long delay) { this(null, delay); } + public SocketTimeout(Socket socket, long delay) { super(SimpleTimer2.getInstance()); _inactivityDelay = delay; @@ -32,6 +34,7 @@ public class SocketTimeout extends SimpleTimer2.TimedEvent { _totalTimeoutTime = -1; schedule(delay); } + public void timeReached() { if (_cancelled) return; @@ -53,25 +56,29 @@ public class SocketTimeout extends SimpleTimer2.TimedEvent { _cancelled = true; return super.cancel(); } + public void setSocket(Socket s) { _targetSocket = s; } public void resetTimer() { _lastActivity = System.currentTimeMillis(); } public void setInactivityTimeout(long timeout) { _inactivityDelay = timeout; } + public void setTotalTimeoutPeriod(long timeoutPeriod) { if (timeoutPeriod > 0) _totalTimeoutTime = _startTime + timeoutPeriod; else _totalTimeoutTime = -1; } + public void setTimeoutCommand(Runnable job) { _command = job; } private static final SimpleDateFormat _fmt = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss.SSS"); private static String ts(long when) { synchronized (_fmt) { return _fmt.format(new Date(when)); } } + @Override public String toString() { StringBuilder buf = new StringBuilder(); - buf.append("started on "); + buf.append("SocketTimeout started on "); buf.append(ts(_startTime)); - buf.append("idle for "); + buf.append(" idle for "); buf.append(System.currentTimeMillis() - _lastActivity); buf.append("ms "); if (_totalTimeoutTime > 0) diff --git a/history.txt b/history.txt index 070d8aeb5e1f59dd23616ca6ae919636640bdd6c..bf03074db7dec032ef70754f695c5b01e3355306 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,15 @@ +2012-10-07 zzz + * I2PAppContext: Improved synching in constructor + * i2ptunnel: + - Set default read timeout in standard server + - Reduce header timeout, enforce total header timeout + in IRC and HTTP servers (ticket #723) + * Logs: + - Flush buffers in logs.jsp + - Add dup message to buffers, was in file only + * Streaming: Don't ignore option or force connect timeout to 5 minutes + * UPnP: Workaround NPE (ticket #728) + 2012-10-06 zzz * configlogging.jsp: Fix IAE * error500.jsp: Fix whitespace diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 7ca1a3ec2c4e6b0a58a1f89cf73e6cb3036dcf84..5a58b19bc22ea9b489b5610aade18177efc4505e 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 10; + public final static long BUILD = 11; /** for example "-test" */ public final static String EXTRA = "";