diff --git a/apps/addressbook/web.xml b/apps/addressbook/web.xml index 1aebb0d096..d86d32289b 100644 --- a/apps/addressbook/web.xml +++ b/apps/addressbook/web.xml @@ -28,4 +28,11 @@ /* + + + 30 + + true + + diff --git a/apps/i2psnark/web.xml b/apps/i2psnark/web.xml index 68e6abd641..d98f637338 100644 --- a/apps/i2psnark/web.xml +++ b/apps/i2psnark/web.xml @@ -26,73 +26,14 @@ / + 30 + + true + - - - - - mkv - video/x-matroska - - - - wmv - video/x-ms-wmv - - - - flv - video/x-flv - - - - mp4 - video/mp4 - - - - rar - application/rar - - - - 7z - application/x-7z-compressed - - - - iso - application/x-iso9660-image - - - - ico - image/x-icon - - - - exe - application/x-msdos-program - - - - flac - audio/flac - - - - m4a - audio/mpeg - - - - wma - audio/x-ms-wma - - diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java index 8f963e6492..4b06ae0789 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java @@ -71,11 +71,16 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { private static final long TOTAL_HEADER_TIMEOUT = 2 * HEADER_TIMEOUT; private static final long START_INTERVAL = (60 * 1000) * 3; private static final int MAX_LINE_LENGTH = 8*1024; + /** ridiculously long, just to prevent OOM DOS @since 0.7.13 */ + private static final int MAX_HEADERS = 60; + /** Includes request, just to prevent OOM DOS @since 0.9.20 */ + private static final int MAX_TOTAL_HEADER_SIZE = 32*1024; + private long _startedOn = 0L; private ConnThrottler _postThrottler; - private final static byte[] ERR_UNAVAILABLE = - ("HTTP/1.1 503 Service Unavailable\r\n"+ + private final static String ERR_UNAVAILABLE = + "HTTP/1.1 503 Service Unavailable\r\n"+ "Content-Type: text/html; charset=iso-8859-1\r\n"+ "Cache-control: no-cache\r\n"+ "Connection: close\r\n"+ @@ -84,11 +89,10 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { "503 Service Unavailable\n"+ "

503 Service Unavailable

\n" + "

This I2P website is unavailable. It may be down or undergoing maintenance.

\n" + - "") - .getBytes(); + ""; - private final static byte[] ERR_DENIED = - ("HTTP/1.1 403 Denied\r\n"+ + private final static String ERR_DENIED = + "HTTP/1.1 403 Denied\r\n"+ "Content-Type: text/html; charset=iso-8859-1\r\n"+ "Cache-control: no-cache\r\n"+ "Connection: close\r\n"+ @@ -97,11 +101,10 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { "403 Denied\n"+ "

403 Denied

\n" + "

Denied due to excessive requests. Please try again later.

\n" + - "") - .getBytes(); + ""; - private final static byte[] ERR_INPROXY = - ("HTTP/1.1 403 Denied\r\n"+ + private final static String ERR_INPROXY = + "HTTP/1.1 403 Denied\r\n"+ "Content-Type: text/html; charset=iso-8859-1\r\n"+ "Cache-control: no-cache\r\n"+ "Connection: close\r\n"+ @@ -110,8 +113,64 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { "403 Denied\n"+ "

403 Denied

\n" + "

Inproxy access denied. You must run I2P to access this site.

\n" + - "") - .getBytes(); + ""; + + private final static String ERR_SSL = + "HTTP/1.1 503 Service Unavailable\r\n"+ + "Content-Type: text/html; charset=iso-8859-1\r\n"+ + "Cache-control: no-cache\r\n"+ + "Connection: close\r\n"+ + "Proxy-Connection: close\r\n"+ + "\r\n"+ + "503 Service Unavailable\n"+ + "

503 Service Unavailable

\n" + + "

This I2P website is not configured for SSL.

\n" + + ""; + + private final static String ERR_REQUEST_URI_TOO_LONG = + "HTTP/1.1 414 Request URI too long\r\n"+ + "Content-Type: text/html; charset=iso-8859-1\r\n"+ + "Cache-control: no-cache\r\n"+ + "Connection: close\r\n"+ + "Proxy-Connection: close\r\n"+ + "\r\n"+ + "414 Request URI Too Long\n"+ + "

414 Request URI too long

\n" + + ""; + + private final static String ERR_HEADERS_TOO_LARGE = + "HTTP/1.1 431 Request header fields too large\r\n"+ + "Content-Type: text/html; charset=iso-8859-1\r\n"+ + "Cache-control: no-cache\r\n"+ + "Connection: close\r\n"+ + "Proxy-Connection: close\r\n"+ + "\r\n"+ + "431 Request Header Fields Too Large\n"+ + "

431 Request header fields too large

\n" + + ""; + + private final static String ERR_REQUEST_TIMEOUT = + "HTTP/1.1 408 Request timeout\r\n"+ + "Content-Type: text/html; charset=iso-8859-1\r\n"+ + "Cache-control: no-cache\r\n"+ + "Connection: close\r\n"+ + "Proxy-Connection: close\r\n"+ + "\r\n"+ + "408 Request Timeout\n"+ + "

408 Request timeout

\n" + + ""; + + private final static String ERR_BAD_REQUEST = + "HTTP/1.1 400 Bad Request\r\n"+ + "Content-Type: text/html; charset=iso-8859-1\r\n"+ + "Cache-control: no-cache\r\n"+ + "Connection: close\r\n"+ + "Proxy-Connection: close\r\n"+ + "\r\n"+ + "400 Bad Request\n"+ + "

400 Bad request

\n" + + ""; + public I2PTunnelHTTPServer(InetAddress host, int port, String privData, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) { super(host, port, privData, l, notifyThis, tunnel); @@ -131,7 +190,6 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { private void setupI2PTunnelHTTPServer(String spoofHost) { _spoofHost = (spoofHost != null && spoofHost.trim().length() > 0) ? spoofHost.trim() : null; getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 }); - getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpNullWorkaround", "How often an http server works around a streaming lib or i2ptunnel bug", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000 }); } @Override @@ -208,13 +266,88 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { //local is fast, so synchronously. Does not need that many //threads. try { + if (socket.getLocalPort() == 443) { + if (getTunnel().getClientOptions().getProperty("targetForPort.443") == null) { + try { + socket.getOutputStream().write(ERR_SSL.getBytes("UTF-8")); + } catch (IOException ioe) { + } finally { + try { + socket.close(); + } catch (IOException ioe) {} + } + return; + } + Socket s = getSocket(socket.getPeerDestination().calculateHash(), 443); + Runnable t = new I2PTunnelRunner(s, socket, slock, null, null, + null, (I2PTunnelRunner.FailCallback) null); + _clientExecutor.execute(t); + return; + } + long afterAccept = getTunnel().getContext().clock().now(); + // The headers _should_ be in the first packet, but // may not be, depending on the client-side options StringBuilder command = new StringBuilder(128); - Map> headers = readHeaders(socket, null, command, - CLIENT_SKIPHEADERS, getTunnel().getContext()); + Map> headers; + try { + // catch specific exceptions thrown, to return a good + // error to the client + headers = readHeaders(socket, null, command, + CLIENT_SKIPHEADERS, getTunnel().getContext()); + } catch (SocketTimeoutException ste) { + try { + socket.getOutputStream().write(ERR_REQUEST_TIMEOUT.getBytes("UTF-8")); + } catch (IOException ioe) { + } finally { + try { socket.close(); } catch (IOException ioe) {} + } + if (_log.shouldLog(Log.WARN)) + _log.warn("Error while receiving the new HTTP request", ste); + return; + } catch (EOFException eofe) { + try { + socket.getOutputStream().write(ERR_BAD_REQUEST.getBytes("UTF-8")); + } catch (IOException ioe) { + } finally { + try { socket.close(); } catch (IOException ioe) {} + } + if (_log.shouldLog(Log.WARN)) + _log.warn("Error while receiving the new HTTP request", eofe); + return; + } catch (LineTooLongException ltle) { + try { + socket.getOutputStream().write(ERR_HEADERS_TOO_LARGE.getBytes("UTF-8")); + } catch (IOException ioe) { + } finally { + try { socket.close(); } catch (IOException ioe) {} + } + if (_log.shouldLog(Log.WARN)) + _log.warn("Error while receiving the new HTTP request", ltle); + return; + } catch (RequestTooLongException rtle) { + try { + socket.getOutputStream().write(ERR_REQUEST_URI_TOO_LONG.getBytes("UTF-8")); + } catch (IOException ioe) { + } finally { + try { socket.close(); } catch (IOException ioe) {} + } + if (_log.shouldLog(Log.WARN)) + _log.warn("Error while receiving the new HTTP request", rtle); + return; + } catch (BadRequestException bre) { + try { + socket.getOutputStream().write(ERR_BAD_REQUEST.getBytes("UTF-8")); + } catch (IOException ioe) { + } finally { + try { socket.close(); } catch (IOException ioe) {} + } + if (_log.shouldLog(Log.WARN)) + _log.warn("Error while receiving the new HTTP request", bre); + return; + } long afterHeaders = getTunnel().getContext().clock().now(); Properties opts = getTunnel().getClientOptions(); @@ -239,7 +372,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { try { // Send a 403, so the user doesn't get an HTTP Proxy error message // and blame his router or the network. - socket.getOutputStream().write(ERR_INPROXY); + socket.getOutputStream().write(ERR_INPROXY.getBytes("UTF-8")); } catch (IOException ioe) {} try { socket.close(); @@ -256,7 +389,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { try { // Send a 403, so the user doesn't get an HTTP Proxy error message // and blame his router or the network. - socket.getOutputStream().write(ERR_DENIED); + socket.getOutputStream().write(ERR_DENIED.getBytes("UTF-8")); } catch (IOException ioe) {} try { socket.close(); @@ -341,7 +474,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { try { // Send a 503, so the user doesn't get an HTTP Proxy error message // and blame his router or the network. - socket.getOutputStream().write(ERR_UNAVAILABLE); + socket.getOutputStream().write(ERR_UNAVAILABLE.getBytes("UTF-8")); } catch (IOException ioe) {} try { socket.close(); @@ -362,7 +495,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { try { // Send a 503, so the user doesn't get an HTTP Proxy error message // and blame his router or the network. - socket.getOutputStream().write(ERR_UNAVAILABLE); + socket.getOutputStream().write(ERR_UNAVAILABLE.getBytes("UTF-8")); } catch (IOException ioe) {} try { socket.close(); @@ -453,7 +586,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { try { if (browserout == null) browserout = _browser.getOutputStream(); - browserout.write(ERR_UNAVAILABLE); + browserout.write(ERR_UNAVAILABLE.getBytes("UTF-8")); } catch (IOException ioe) {} } catch (IOException ioe) { if (_log.shouldLog(Log.WARN)) @@ -633,9 +766,6 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { return buf.toString(); } - /** ridiculously long, just to prevent OOM DOS @since 0.7.13 */ - private static final int MAX_HEADERS = 60; - /** * Add an entry to the multimap. */ @@ -680,10 +810,11 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { * @param socket if null, use in as InputStream * @param in if null, use socket.getInputStream() as InputStream * @param command out parameter, first line - * @param command out parameter, first line * @throws SocketTimeoutException if timeout is reached before newline * @throws EOFException if EOF is reached before newline - * @throws LineTooLongException if too long + * @throws LineTooLongException if one header too long, or too many headers, or total size too big + * @throws RequestTooLongException if too long + * @throws BadRequestException on bad headers * @throws IOException on other errors in the underlying stream */ private static Map> readHeaders(I2PSocket socket, InputStream in, StringBuilder command, @@ -694,51 +825,49 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { // slowloris / darkloris long expire = ctx.clock().now() + TOTAL_HEADER_TIMEOUT; if (socket != null) { - readLine(socket, command, HEADER_TIMEOUT); + try { + readLine(socket, command, HEADER_TIMEOUT); + } catch (LineTooLongException ltle) { + // convert for first line + throw new RequestTooLongException("Request too long - max " + MAX_LINE_LENGTH); + } } else { boolean ok = DataHelper.readLine(in, command); if (!ok) - throw new IOException("EOF reached before the end of the headers [" + buf.toString() + "]"); + throw new EOFException("EOF reached before the end of the headers"); } //if (_log.shouldLog(Log.DEBUG)) // _log.debug("Read the http command [" + command.toString() + "]"); - // FIXME we probably don't need or want this in the outgoing direction - int trimmed = 0; - if (command.length() > 0) { - for (int i = 0; i < command.length(); i++) { - if (command.charAt(i) == 0) { - command = command.deleteCharAt(i); - i--; - trimmed++; - } - } - } - if (trimmed > 0) - ctx.statManager().addRateData("i2ptunnel.httpNullWorkaround", trimmed); - + int totalSize = command.length(); int i = 0; while (true) { - if (++i > MAX_HEADERS) - throw new IOException("Too many header lines - max " + MAX_HEADERS); + if (++i > MAX_HEADERS) { + throw new LineTooLongException("Too many header lines - max " + MAX_HEADERS); + } buf.setLength(0); if (socket != null) { readLine(socket, buf, expire - ctx.clock().now()); } else { boolean ok = DataHelper.readLine(in, buf); if (!ok) - throw new IOException("EOF reached before the end of the headers [" + buf.toString() + "]"); + throw new BadRequestException("EOF reached before the end of the headers"); } if ( (buf.length() == 0) || ((buf.charAt(0) == '\n') || (buf.charAt(0) == '\r')) ) { // end of headers reached return headers; } else { - if (ctx.clock().now() >= expire) - throw new IOException("Headers took too long [" + buf.toString() + "]"); + if (ctx.clock().now() > expire) { + throw new SocketTimeoutException("Headers took too long"); + } int split = buf.indexOf(":"); - if (split <= 0) throw new IOException("Invalid HTTP header, missing colon [" + buf.toString() + "]"); + if (split <= 0) + throw new BadRequestException("Invalid HTTP header, missing colon"); + totalSize += buf.length(); + if (totalSize > MAX_TOTAL_HEADER_SIZE) + throw new LineTooLongException("Req+headers too big"); String name = buf.substring(0, split).trim(); String value = null; if (buf.length() > split + 1) @@ -831,5 +960,23 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { super(s); } } + + /** + * @since 0.9.20 + */ + private static class RequestTooLongException extends IOException { + public RequestTooLongException(String s) { + super(s); + } + } + + /** + * @since 0.9.20 + */ + private static class BadRequestException extends IOException { + public BadRequestException(String s) { + super(s); + } + } } diff --git a/apps/i2ptunnel/jsp/web.xml b/apps/i2ptunnel/jsp/web.xml index 1fd11c13d8..0a74b68357 100644 --- a/apps/i2ptunnel/jsp/web.xml +++ b/apps/i2ptunnel/jsp/web.xml @@ -32,10 +32,14 @@ /wizard + 30 + + true + index.html diff --git a/apps/routerconsole/jsp/web.xml b/apps/routerconsole/jsp/web.xml index 67f0b4ca6e..ea183c8356 100644 --- a/apps/routerconsole/jsp/web.xml +++ b/apps/routerconsole/jsp/web.xml @@ -35,6 +35,9 @@ 30 + + true + index.html diff --git a/apps/streaming/java/src/net/i2p/client/streaming/impl/Connection.java b/apps/streaming/java/src/net/i2p/client/streaming/impl/Connection.java index 8c6212503f..451bb5f37c 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/impl/Connection.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/impl/Connection.java @@ -41,7 +41,8 @@ class Connection { private final MessageInputStream _inputStream; private final MessageOutputStream _outputStream; private final SchedulerChooser _chooser; - private volatile long _nextSendTime; + /** Locking: _nextSendLock */ + private long _nextSendTime; private long _ackedPackets; private final long _createdOn; private final AtomicLong _closeSentOn = new AtomicLong(); @@ -70,6 +71,8 @@ class Connection { private final AtomicBoolean _ackSinceCongestion; /** Notify this on connection (or connection failure) */ private final Object _connectLock; + /** Locking for _nextSendTime */ + private final Object _nextSendLock; /** how many messages have been resent and not yet ACKed? */ private final AtomicInteger _activeResends = new AtomicInteger(); private final ConEvent _connectionEvent; @@ -145,6 +148,7 @@ class Connection { _activityTimer = new ActivityTimer(); _ackSinceCongestion = new AtomicBoolean(true); _connectLock = new Object(); + _nextSendLock = new Object(); _connectionEvent = new ConEvent(); _randomWait = _context.random().nextInt(10*1000); // just do this once to reduce usage // all createRateStats in ConnectionManager @@ -907,7 +911,11 @@ class Connection { * instance, or want to delay an ACK. * @return the next time the scheduler will want to send a packet, or -1 if never. */ - public long getNextSendTime() { return _nextSendTime; } + public long getNextSendTime() { + synchronized(_nextSendLock) { + return _nextSendTime; + } + } /** * If the next send time is currently >= 0 (i.e. not "never"), @@ -917,25 +925,20 @@ class Connection { * options.getSendAckDelay() from now (1000 ms) */ public void setNextSendTime(long when) { - if (_nextSendTime >= 0) { - if (when < _nextSendTime) - _nextSendTime = when; - } else { - _nextSendTime = when; - } + synchronized(_nextSendLock) { + if (_nextSendTime >= 0) { + if (when < _nextSendTime) + _nextSendTime = when; + } else { + _nextSendTime = when; + } - if (_nextSendTime >= 0) { - long max = _context.clock().now() + _options.getSendAckDelay(); - if (max < _nextSendTime) - _nextSendTime = max; + if (_nextSendTime >= 0) { + long max = _context.clock().now() + _options.getSendAckDelay(); + if (max < _nextSendTime) + _nextSendTime = max; + } } - - //if (_log.shouldLog(Log.DEBUG) && false) { - // if (_nextSendTime <= 0) - // _log.debug("set next send time to an unknown time", new Exception(toString())); - // else - // _log.debug("set next send time to " + (_nextSendTime-_context.clock().now()) + "ms from now", new Exception(toString())); - //} } /** how many packets have we sent and the other side has ACKed? @@ -1260,17 +1263,17 @@ class Connection { */ class ResendPacketEvent extends SimpleTimer2.TimedEvent { private final PacketLocal _packet; - private long _nextSendTime; + private long _nextSend; public ResendPacketEvent(PacketLocal packet, long delay) { super(_timer); _packet = packet; - _nextSendTime = delay + _context.clock().now(); + _nextSend = delay + _context.clock().now(); packet.setResendPacketEvent(ResendPacketEvent.this); schedule(delay); } - public long getNextSendTime() { return _nextSendTime; } + public long getNextSendTime() { return _nextSend; } public void timeReached() { retransmit(); } /** * Retransmit the packet if we need to. @@ -1320,7 +1323,7 @@ class Connection { + _activeResends + " active resend, " + _outboundPackets.size() + " unacked, window size = " + _options.getWindowSize()); forceReschedule(1333); - _nextSendTime = 1333 + _context.clock().now(); + _nextSend = 1333 + _context.clock().now(); return false; } @@ -1407,7 +1410,7 @@ class Connection { if ( (timeout > MAX_RESEND_DELAY) || (timeout <= 0) ) timeout = MAX_RESEND_DELAY; // set this before enqueue() as it passes it on to the router - _nextSendTime = timeout + _context.clock().now(); + _nextSend = timeout + _context.clock().now(); if (_outboundQueue.enqueue(_packet)) { // first resend for this packet ? diff --git a/apps/susimail/src/WEB-INF/web.xml b/apps/susimail/src/WEB-INF/web.xml index 23adde2d5b..22f6e9b6f0 100644 --- a/apps/susimail/src/WEB-INF/web.xml +++ b/apps/susimail/src/WEB-INF/web.xml @@ -23,6 +23,9 @@ 1440 + + true + diff --git a/core/java/src/net/i2p/util/EepGet.java b/core/java/src/net/i2p/util/EepGet.java index 157a86a713..6a1c4230aa 100644 --- a/core/java/src/net/i2p/util/EepGet.java +++ b/core/java/src/net/i2p/util/EepGet.java @@ -904,10 +904,14 @@ public class EepGet { _keepFetching = false; _notModified = true; return; + case 400: // bad req case 401: // server auth case 403: // bad req case 404: // not found + case 408: // req timeout case 409: // bad addr helper + case 414: // URI too long + case 431: // headers too long case 503: // no outproxy _transferFailed = true; if (_alreadyTransferred > 0 || !_shouldWriteErrorToOutput) { diff --git a/installer/resources/eepsite/jetty-ssl.xml b/installer/resources/eepsite/jetty-ssl.xml index c6d91cc83d..d27effe37b 100644 --- a/installer/resources/eepsite/jetty-ssl.xml +++ b/installer/resources/eepsite/jetty-ssl.xml @@ -1,35 +1,255 @@ + + + + + + + + + - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<-- clientApp.3.args="eepsite/jetty.xml" --> + +<-- clientApp.3.args="/path to/.i2p/eepsite/jetty.xml" "/path/to/.i2p/eepsite/jetty-ssl.xml" --> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - ./eepsite/etc/keystore - OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4 - OBF:1u2u1wml1z7s1z7a1wnl1u2g - ./eepsite/etc/keystore - OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4 + ./eepsite/etc/keystore.ks + changeit + myKeyPassword + ./eepsite/etc/keystore.ks + changeit - 8443 + 127.0.0.1 + 7668 600000 false - 2 - 100 + 1 + false + 5000 + 5000 + + + SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA + SSL_DH_anon_EXPORT_WITH_RC4_40_MD5 + SSL_DH_anon_WITH_3DES_EDE_CBC_SHA + SSL_DH_anon_WITH_DES_CBC_SHA + SSL_DH_anon_WITH_RC4_128_MD5 + SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA + SSL_DHE_DSS_WITH_DES_CBC_SHA + SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA + SSL_DHE_RSA_WITH_DES_CBC_SHA + SSL_RSA_EXPORT_WITH_DES40_CBC_SHA + SSL_RSA_EXPORT_WITH_RC4_40_MD5 + SSL_RSA_WITH_DES_CBC_SHA + SSL_RSA_WITH_NULL_MD5 + SSL_RSA_WITH_NULL_SHA + TLS_DH_anon_WITH_AES_128_CBC_SHA + TLS_DH_anon_WITH_AES_128_CBC_SHA256 + TLS_DH_anon_WITH_AES_128_GCM_SHA256 + TLS_DH_anon_WITH_AES_256_CBC_SHA + TLS_DH_anon_WITH_AES_256_CBC_SHA256 + TLS_DH_anon_WITH_AES_256_GCM_SHA384 + TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA + TLS_ECDH_anon_WITH_AES_128_CBC_SHA + TLS_ECDH_anon_WITH_AES_256_CBC_SHA + TLS_ECDH_anon_WITH_NULL_SHA + TLS_ECDH_anon_WITH_RC4_128_SHA + TLS_ECDH_ECDSA_WITH_NULL_SHA + TLS_ECDHE_ECDSA_WITH_NULL_SHA + TLS_ECDHE_RSA_WITH_NULL_SHA + TLS_ECDH_RSA_WITH_NULL_SHA + TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 + TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA + TLS_KRB5_EXPORT_WITH_RC4_40_MD5 + TLS_KRB5_EXPORT_WITH_RC4_40_SHA + TLS_KRB5_WITH_3DES_EDE_CBC_MD5 + TLS_KRB5_WITH_3DES_EDE_CBC_SHA + TLS_KRB5_WITH_DES_CBC_MD5 + TLS_KRB5_WITH_DES_CBC_SHA + TLS_KRB5_WITH_RC4_128_MD5 + TLS_KRB5_WITH_RC4_128_SHA + TLS_RSA_WITH_NULL_SHA256 + SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA + SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA + SSL_RSA_WITH_3DES_EDE_CBC_SHA + SSL_RSA_WITH_RC4_128_MD5 + SSL_RSA_WITH_RC4_128_SHA + TLS_ECDH_ECDSA_WITH_RC4_128_SHA + TLS_ECDH_RSA_WITH_RC4_128_SHA + TLS_ECDHE_ECDSA_WITH_RC4_128_SHA + TLS_ECDHE_RSA_WITH_RC4_128_SHA + TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA + TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA + TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA + + diff --git a/installer/resources/eepsite/jetty.xml b/installer/resources/eepsite/jetty.xml index b524459531..432bb347e3 100644 --- a/installer/resources/eepsite/jetty.xml +++ b/installer/resources/eepsite/jetty.xml @@ -135,7 +135,6 @@ 600000 1 false - 8443 5000 5000 false @@ -155,7 +154,6 @@ 600000 1 false - 8443 @@ -168,19 +166,6 @@ - - - - - - - - - - - diff --git a/installer/resources/i2ptunnel.config b/installer/resources/i2ptunnel.config index 0f5092cc0a..944def78d9 100644 --- a/installer/resources/i2ptunnel.config +++ b/installer/resources/i2ptunnel.config @@ -98,6 +98,8 @@ tunnel.3.option.inbound.length=3 tunnel.3.option.inbound.lengthVariance=0 tunnel.3.option.outbound.length=3 tunnel.3.option.outbound.lengthVariance=0 +# uncomment for HTTPS to port 7668 +#tunnel.3.option.targetForPort.443=127.0.0.1:7668 tunnel.3.startOnLoad=false # postman's SMTP server - see hq.postman.i2p diff --git a/router/java/src/net/i2p/router/JobQueue.java b/router/java/src/net/i2p/router/JobQueue.java index 42e88ca2cd..0b0f84fd27 100644 --- a/router/java/src/net/i2p/router/JobQueue.java +++ b/router/java/src/net/i2p/router/JobQueue.java @@ -75,7 +75,7 @@ public class JobQueue { /** default max # job queue runners operating */ private final static int DEFAULT_MAX_RUNNERS = 1; - /** router.config parameter to override the max runners @deprecated unimplemented */ + /** router.config parameter to override the max runners */ private final static String PROP_MAX_RUNNERS = "router.maxJobRunners"; /** how frequently should we check and update the max runners */ @@ -330,7 +330,7 @@ public class JobQueue { public void allowParallelOperation() { _allowParallelOperation = true; - runQueue(RUNNERS); + runQueue(_context.getProperty(PROP_MAX_RUNNERS, RUNNERS)); } /** diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java index 524836cf3f..3a244d715a 100644 --- a/router/java/src/net/i2p/router/Router.java +++ b/router/java/src/net/i2p/router/Router.java @@ -8,15 +8,10 @@ package net.i2p.router; * */ -import java.io.BufferedReader; import java.io.File; -import java.io.FileInputStream; -import java.io.InputStreamReader; -import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.Set; @@ -49,7 +44,6 @@ import net.i2p.router.util.EventLog; import net.i2p.stat.RateStat; import net.i2p.stat.StatManager; import net.i2p.util.ByteCache; -import net.i2p.util.FileUtil; import net.i2p.util.FortunaRandomSource; import net.i2p.util.I2PAppThread; import net.i2p.util.I2PThread; @@ -113,6 +107,7 @@ public class Router implements RouterClock.ClockShiftListener { private final static String DNS_CACHE_TIME = "" + (5*60); private static final String EVENTLOG = "eventlog.txt"; private static final String PROP_JBIGI = "jbigi.loadedResource"; + public static final String UPDATE_FILE = "i2pupdate.zip"; private static final String originalTimeZoneID; static { @@ -1497,199 +1492,11 @@ public class Router implements RouterClock.ClockShiftListener { // If it does an update, it never returns. // I guess it's better to have the other-router check above this, we don't want to // overwrite an existing running router's jar files. Other than ours. - r.installUpdates(); + InstallUpdate.installUpdates(r); // ********* Start no threads before here ********* // r.runRouter(); } } - - public static final String UPDATE_FILE = "i2pupdate.zip"; - private static final String DELETE_FILE = "deletelist.txt"; - - /** - * Context must be available. - * Unzip update file found in the router dir OR base dir, to the base dir - * - * If successful, will call exit() and never return. - * - * If we can't write to the base dir, complain. - * Note: _log not available here. - */ - private void installUpdates() { - File updateFile = new File(_context.getRouterDir(), UPDATE_FILE); - boolean exists = updateFile.exists(); - if (!exists) { - updateFile = new File(_context.getBaseDir(), UPDATE_FILE); - exists = updateFile.exists(); - } - if (exists) { - // do a simple permissions test, if it fails leave the file in place and don't restart - File test = new File(_context.getBaseDir(), "history.txt"); - if ((test.exists() && !test.canWrite()) || (!_context.getBaseDir().canWrite())) { - System.out.println("ERROR: No write permissions on " + _context.getBaseDir() + - " to extract software update file"); - // carry on - return; - } - System.out.println("INFO: Update file exists [" + UPDATE_FILE + "] - installing"); - // verify the whole thing first - // we could remember this fails, and not bother restarting, but who cares... - boolean ok = FileUtil.verifyZip(updateFile); - if (ok) { - // This may be useful someday. First added in 0.8.2 - // Moved above the extract so we don't NCDFE - _config.put("router.updateLastInstalled", "" + System.currentTimeMillis()); - // Set the last version to the current version, since 0.8.13 - _config.put("router.previousVersion", RouterVersion.VERSION); - _config.put("router.previousFullVersion", RouterVersion.FULL_VERSION); - saveConfig(); - ok = FileUtil.extractZip(updateFile, _context.getBaseDir()); - } - - // Very important - we have now trashed our jars. - // After this point, do not use any new I2P classes, or they will fail to load - // and we will die with NCDFE. - // Ideally, do not use I2P classes at all, new or not. - try { - if (ok) { - // We do this here so we may delete old jars before we restart - deleteListedFiles(); - System.out.println("INFO: Update installed"); - } else { - System.out.println("ERROR: Update failed!"); - } - if (!ok) { - // we can't leave the file in place or we'll continually restart, so rename it - File bad = new File(_context.getRouterDir(), "BAD-" + UPDATE_FILE); - boolean renamed = updateFile.renameTo(bad); - if (renamed) { - System.out.println("Moved update file to " + bad.getAbsolutePath()); - } else { - System.out.println("Deleting file " + updateFile.getAbsolutePath()); - ok = true; // so it will be deleted - } - } - if (ok) { - boolean deleted = updateFile.delete(); - if (!deleted) { - System.out.println("ERROR: Unable to delete the update file!"); - updateFile.deleteOnExit(); - } - } - // exit whether ok or not - if (_context.hasWrapper()) - System.out.println("INFO: Restarting after update"); - else - System.out.println("WARNING: Exiting after update, restart I2P"); - } catch (Throwable t) { - // hide the NCDFE - // hopefully the update file got deleted or we will loop - } - System.exit(EXIT_HARD_RESTART); - } else { - deleteJbigiFiles(); - // It was here starting in 0.8.12 so it could be used the very first time - // Now moved up so it is usually run only after an update - // But the first time before jetty 6 it will run here... - // Here we can't remove jars - deleteListedFiles(); - } - } - - /** - * Remove extracted libjbigi.so and libjcpuid.so files if we have a newer jbigi.jar, - * so the new ones will be extracted. - * We do this after the restart, not after the extract, because it's safer, and - * because people may upgrade their jbigi.jar file manually. - * - * Copied from NativeBigInteger, which we can't access here or the - * libs will get loaded. - */ - private void deleteJbigiFiles() { - boolean isX86 = SystemVersion.isX86(); - String osName = System.getProperty("os.name").toLowerCase(Locale.US); - boolean isWin = SystemVersion.isWindows(); - boolean isMac = SystemVersion.isMac(); - // only do this on these OSes - boolean goodOS = isWin || isMac || - osName.contains("linux") || osName.contains("freebsd"); - - // only do this on these x86 - File jbigiJar = new File(_context.getBaseDir(), "lib/jbigi.jar"); - if (isX86 && goodOS && jbigiJar.exists()) { - String libPrefix = (isWin ? "" : "lib"); - String libSuffix = (isWin ? ".dll" : isMac ? ".jnilib" : ".so"); - - File jcpuidLib = new File(_context.getBaseDir(), libPrefix + "jcpuid" + libSuffix); - if (jcpuidLib.canWrite() && jbigiJar.lastModified() > jcpuidLib.lastModified()) { - String path = jcpuidLib.getAbsolutePath(); - boolean success = FileUtil.copy(path, path + ".bak", true, true); - if (success) { - boolean success2 = jcpuidLib.delete(); - if (success2) { - System.out.println("New jbigi.jar detected, moved jcpuid library to " + - path + ".bak"); - System.out.println("Check logs for successful installation of new library"); - } - } - } - - File jbigiLib = new File(_context.getBaseDir(), libPrefix + "jbigi" + libSuffix); - if (jbigiLib.canWrite() && jbigiJar.lastModified() > jbigiLib.lastModified()) { - String path = jbigiLib.getAbsolutePath(); - boolean success = FileUtil.copy(path, path + ".bak", true, true); - if (success) { - boolean success2 = jbigiLib.delete(); - if (success2) { - System.out.println("New jbigi.jar detected, moved jbigi library to " + - path + ".bak"); - System.out.println("Check logs for successful installation of new library"); - } - } - } - } - } - - /** - * Delete all files listed in the delete file. - * Format: One file name per line, comment lines start with '#'. - * All file names must be relative to $I2P, absolute file names not allowed. - * We probably can't remove old jars this way. - * Fails silently. - * Use no new I2P classes here so it may be called after zip extraction. - * @since 0.8.12 - */ - private void deleteListedFiles() { - File deleteFile = new File(_context.getBaseDir(), DELETE_FILE); - if (!deleteFile.exists()) - return; - // this is similar to FileUtil.readTextFile() but we can't use any I2P classes here - FileInputStream fis = null; - BufferedReader in = null; - try { - fis = new FileInputStream(deleteFile); - in = new BufferedReader(new InputStreamReader(fis, "UTF-8")); - String line; - while ( (line = in.readLine()) != null) { - String fl = line.trim(); - if (fl.contains("..") || fl.startsWith("#") || fl.length() == 0) - continue; - File df = new File(fl); - if (df.isAbsolute()) - continue; - df = new File(_context.getBaseDir(), fl); - if (df.exists() && !df.isDirectory()) { - if (df.delete()) - System.out.println("INFO: File [" + fl + "] deleted"); - } - } - } catch (IOException ioe) {} - finally { - if (in != null) try { in.close(); } catch(IOException ioe) {} - if (deleteFile.delete()) - System.out.println("INFO: File [" + DELETE_FILE + "] deleted"); - } - } /******* private static void verifyWrapperConfig() { diff --git a/router/java/src/net/i2p/router/tasks/InstallUpdate.java b/router/java/src/net/i2p/router/tasks/InstallUpdate.java new file mode 100644 index 0000000000..b4bea67706 --- /dev/null +++ b/router/java/src/net/i2p/router/tasks/InstallUpdate.java @@ -0,0 +1,215 @@ +package net.i2p.router.tasks; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import net.i2p.router.Router; +import net.i2p.router.RouterContext; +import net.i2p.router.RouterVersion; +import net.i2p.util.FileUtil; +import net.i2p.util.SystemVersion; + +/** + * If the i2pupdate.zip file is present, + * unzip it and JVM exit. + * + * @since 0.9.20 moved from Router.java + */ +public class InstallUpdate { + + private static final String DELETE_FILE = "deletelist.txt"; + + /** + * Context must be available. + * Unzip update file found in the router dir OR base dir, to the base dir + * + * If successful, will call exit() and never return. + * + * If we can't write to the base dir, write message to System.out and return. + * Note: _log not available here. + */ + public static void installUpdates(Router r) { + RouterContext context = r.getContext(); + File updateFile = new File(context.getRouterDir(), Router.UPDATE_FILE); + boolean exists = updateFile.exists(); + if (!exists) { + updateFile = new File(context.getBaseDir(), Router.UPDATE_FILE); + exists = updateFile.exists(); + } + if (exists) { + // do a simple permissions test, if it fails leave the file in place and don't restart + File test = new File(context.getBaseDir(), "history.txt"); + if ((test.exists() && !test.canWrite()) || (!context.getBaseDir().canWrite())) { + System.out.println("ERROR: No write permissions on " + context.getBaseDir() + + " to extract software update file"); + // carry on + return; + } + System.out.println("INFO: Update file exists [" + Router.UPDATE_FILE + "] - installing"); + // verify the whole thing first + // we could remember this fails, and not bother restarting, but who cares... + boolean ok = FileUtil.verifyZip(updateFile); + if (ok) { + // This may be useful someday. First added in 0.8.2 + // Moved above the extract so we don't NCDFE + Map config = new HashMap(4); + config.put("router.updateLastInstalled", "" + System.currentTimeMillis()); + // Set the last version to the current version, since 0.8.13 + config.put("router.previousVersion", RouterVersion.VERSION); + config.put("router.previousFullVersion", RouterVersion.FULL_VERSION); + r.saveConfig(config, null); + ok = FileUtil.extractZip(updateFile, context.getBaseDir()); + } + + // Very important - we have now trashed our jars. + // After this point, do not use any new I2P classes, or they will fail to load + // and we will die with NCDFE. + // Ideally, do not use I2P classes at all, new or not. + try { + if (ok) { + // We do this here so we may delete old jars before we restart + deleteListedFiles(context); + System.out.println("INFO: Update installed"); + } else { + System.out.println("ERROR: Update failed!"); + } + if (!ok) { + // we can't leave the file in place or we'll continually restart, so rename it + File bad = new File(context.getRouterDir(), "BAD-" + Router.UPDATE_FILE); + boolean renamed = updateFile.renameTo(bad); + if (renamed) { + System.out.println("Moved update file to " + bad.getAbsolutePath()); + } else { + System.out.println("Deleting file " + updateFile.getAbsolutePath()); + ok = true; // so it will be deleted + } + } + if (ok) { + boolean deleted = updateFile.delete(); + if (!deleted) { + System.out.println("ERROR: Unable to delete the update file!"); + updateFile.deleteOnExit(); + } + } + // exit whether ok or not + if (context.hasWrapper()) + System.out.println("INFO: Restarting after update"); + else + System.out.println("WARNING: Exiting after update, restart I2P"); + } catch (Throwable t) { + // hide the NCDFE + // hopefully the update file got deleted or we will loop + } + System.exit(Router.EXIT_HARD_RESTART); + } else { + deleteJbigiFiles(context); + // It was here starting in 0.8.12 so it could be used the very first time + // Now moved up so it is usually run only after an update + // But the first time before jetty 6 it will run here... + // Here we can't remove jars + deleteListedFiles(context); + } + } + + /** + * Remove extracted libjbigi.so and libjcpuid.so files if we have a newer jbigi.jar, + * so the new ones will be extracted. + * We do this after the restart, not after the extract, because it's safer, and + * because people may upgrade their jbigi.jar file manually. + * + * Copied from NativeBigInteger, which we can't access here or the + * libs will get loaded. + */ + private static void deleteJbigiFiles(RouterContext context) { + boolean isX86 = SystemVersion.isX86(); + String osName = System.getProperty("os.name").toLowerCase(Locale.US); + boolean isWin = SystemVersion.isWindows(); + boolean isMac = SystemVersion.isMac(); + // only do this on these OSes + boolean goodOS = isWin || isMac || + osName.contains("linux") || osName.contains("freebsd"); + + // only do this on these x86 + File jbigiJar = new File(context.getBaseDir(), "lib/jbigi.jar"); + if (isX86 && goodOS && jbigiJar.exists()) { + String libPrefix = (isWin ? "" : "lib"); + String libSuffix = (isWin ? ".dll" : isMac ? ".jnilib" : ".so"); + + File jcpuidLib = new File(context.getBaseDir(), libPrefix + "jcpuid" + libSuffix); + if (jcpuidLib.canWrite() && jbigiJar.lastModified() > jcpuidLib.lastModified()) { + String path = jcpuidLib.getAbsolutePath(); + boolean success = FileUtil.copy(path, path + ".bak", true, true); + if (success) { + boolean success2 = jcpuidLib.delete(); + if (success2) { + System.out.println("New jbigi.jar detected, moved jcpuid library to " + + path + ".bak"); + System.out.println("Check logs for successful installation of new library"); + } + } + } + + File jbigiLib = new File(context.getBaseDir(), libPrefix + "jbigi" + libSuffix); + if (jbigiLib.canWrite() && jbigiJar.lastModified() > jbigiLib.lastModified()) { + String path = jbigiLib.getAbsolutePath(); + boolean success = FileUtil.copy(path, path + ".bak", true, true); + if (success) { + boolean success2 = jbigiLib.delete(); + if (success2) { + System.out.println("New jbigi.jar detected, moved jbigi library to " + + path + ".bak"); + System.out.println("Check logs for successful installation of new library"); + } + } + } + } + } + + /** + * Delete all files listed in the delete file. + * Format: One file name per line, comment lines start with '#'. + * All file names must be relative to $I2P, absolute file names not allowed. + * We probably can't remove old jars this way. + * Fails silently. + * Use no new I2P classes here so it may be called after zip extraction. + * @since 0.8.12 + */ + private static void deleteListedFiles(RouterContext context) { + File deleteFile = new File(context.getBaseDir(), DELETE_FILE); + if (!deleteFile.exists()) + return; + // this is similar to FileUtil.readTextFile() but we can't use any I2P classes here + FileInputStream fis = null; + BufferedReader in = null; + try { + fis = new FileInputStream(deleteFile); + in = new BufferedReader(new InputStreamReader(fis, "UTF-8")); + String line; + while ( (line = in.readLine()) != null) { + String fl = line.trim(); + if (fl.contains("..") || fl.startsWith("#") || fl.length() == 0) + continue; + File df = new File(fl); + if (df.isAbsolute()) + continue; + df = new File(context.getBaseDir(), fl); + if (df.exists() && !df.isDirectory()) { + if (df.delete()) + System.out.println("INFO: File [" + fl + "] deleted"); + } + } + } catch (IOException ioe) {} + finally { + if (in != null) try { in.close(); } catch(IOException ioe) {} + if (deleteFile.delete()) + System.out.println("INFO: File [" + DELETE_FILE + "] deleted"); + } + } +} + diff --git a/router/java/src/net/i2p/router/tasks/package.html b/router/java/src/net/i2p/router/tasks/package.html index e032edd6cb..bc1a927308 100644 --- a/router/java/src/net/i2p/router/tasks/package.html +++ b/router/java/src/net/i2p/router/tasks/package.html @@ -4,6 +4,7 @@ Miscellaneous classes, mostly things that are executed periodically as Jobs, Threads, and SimpleTimer.TimedEvents. These are used only by Router.java. +Nothing here is to be used externally, not a part of the public API.

diff --git a/router/java/src/net/i2p/router/transport/FIFOBandwidthRefiller.java b/router/java/src/net/i2p/router/transport/FIFOBandwidthRefiller.java index 3dc610b781..190c3b6416 100644 --- a/router/java/src/net/i2p/router/transport/FIFOBandwidthRefiller.java +++ b/router/java/src/net/i2p/router/transport/FIFOBandwidthRefiller.java @@ -70,8 +70,12 @@ public class FIFOBandwidthRefiller implements Runnable { public static final int MIN_INBOUND_BANDWIDTH_PEAK = 3; /** For now, until there is some tuning and safe throttling, we set the floor at a 3KBps during burst */ public static final int MIN_OUTBOUND_BANDWIDTH_PEAK = 3; - /** Max for reasonable bloom filter false positive rate. See util/DecayingBloomFilter and tunnel/BloomFilterIVValidator */ - public static final int MAX_OUTBOUND_BANDWIDTH = 4096; + /** + * Max for reasonable Bloom filter false positive rate. + * Do not increase without adding a new Bloom filter size! + * See util/DecayingBloomFilter and tunnel/BloomFilterIVValidator. + */ + public static final int MAX_OUTBOUND_BANDWIDTH = 8192; /** * how often we replenish the queues. diff --git a/router/java/src/net/i2p/router/tunnel/BloomFilterIVValidator.java b/router/java/src/net/i2p/router/tunnel/BloomFilterIVValidator.java index 578390aa33..bf0faad97b 100644 --- a/router/java/src/net/i2p/router/tunnel/BloomFilterIVValidator.java +++ b/router/java/src/net/i2p/router/tunnel/BloomFilterIVValidator.java @@ -1,9 +1,12 @@ package net.i2p.router.tunnel; +import java.io.File; + import net.i2p.data.DataHelper; import net.i2p.router.RouterContext; import net.i2p.router.util.DecayingBloomFilter; import net.i2p.router.util.DecayingHashSet; +import net.i2p.util.Log; import net.i2p.util.SimpleByteCache; import net.i2p.util.SystemVersion; @@ -26,12 +29,19 @@ class BloomFilterIVValidator implements IVValidator { private static final int MIN_SHARE_KBPS_TO_USE_BLOOM = 64; private static final int MIN_SHARE_KBPS_FOR_BIG_BLOOM = 512; private static final int MIN_SHARE_KBPS_FOR_HUGE_BLOOM = 1536; + private static final int MIN_SHARE_KBPS_FOR_HUGE2_BLOOM = 4096; private static final long MIN_MEM_TO_USE_BLOOM = 64*1024*1024l; private static final long MIN_MEM_FOR_BIG_BLOOM = 128*1024*1024l; private static final long MIN_MEM_FOR_HUGE_BLOOM = 256*1024*1024l; + private static final long MIN_MEM_FOR_HUGE2_BLOOM = 384*1024*1024l; /** for testing */ private static final String PROP_FORCE = "router.forceDecayingBloomFilter"; + /** for testing */ + private static final String PROP_DISABLE = "router.disableDecayingBloomFilter"; + /** + * @param Kbps share bandwidth + */ public BloomFilterIVValidator(RouterContext ctx, int KBps) { _context = ctx; // Select the filter based on share bandwidth and memory. @@ -39,21 +49,36 @@ class BloomFilterIVValidator implements IVValidator { // to keep acceptable false positive rates. // See DBF, BloomSHA1, and KeySelector for details. long maxMemory = SystemVersion.getMaxMemory(); - if (_context.getBooleanProperty(PROP_FORCE)) + if (_context.getBooleanProperty(PROP_FORCE)) { _filter = new DecayingBloomFilter(ctx, HALFLIFE_MS, 16, "TunnelIVV"); // 2MB fixed - else if (KBps < MIN_SHARE_KBPS_TO_USE_BLOOM || maxMemory < MIN_MEM_TO_USE_BLOOM) + } else if (_context.getBooleanProperty(PROP_DISABLE)) { + _filter = null; + } else if (KBps < MIN_SHARE_KBPS_TO_USE_BLOOM || maxMemory < MIN_MEM_TO_USE_BLOOM) { + if (KBps >= MIN_SHARE_KBPS_TO_USE_BLOOM) + warn(maxMemory, KBps, MIN_MEM_TO_USE_BLOOM, MIN_SHARE_KBPS_TO_USE_BLOOM); _filter = new DecayingHashSet(ctx, HALFLIFE_MS, 16, "TunnelIVV"); // appx. 4MB max - else if (KBps >= MIN_SHARE_KBPS_FOR_HUGE_BLOOM && maxMemory >= MIN_MEM_FOR_HUGE_BLOOM) + } else if (KBps >= MIN_SHARE_KBPS_FOR_HUGE2_BLOOM && maxMemory >= MIN_MEM_FOR_HUGE2_BLOOM) { + _filter = new DecayingBloomFilter(ctx, HALFLIFE_MS, 16, "TunnelIVV", 26); // 16MB fixed + } else if (KBps >= MIN_SHARE_KBPS_FOR_HUGE_BLOOM && maxMemory >= MIN_MEM_FOR_HUGE_BLOOM) { + if (KBps >= MIN_SHARE_KBPS_FOR_HUGE2_BLOOM) + warn(maxMemory, KBps, MIN_MEM_FOR_HUGE2_BLOOM, MIN_SHARE_KBPS_FOR_HUGE2_BLOOM); _filter = new DecayingBloomFilter(ctx, HALFLIFE_MS, 16, "TunnelIVV", 25); // 8MB fixed - else if (KBps >= MIN_SHARE_KBPS_FOR_BIG_BLOOM && maxMemory >= MIN_MEM_FOR_BIG_BLOOM) + } else if (KBps >= MIN_SHARE_KBPS_FOR_BIG_BLOOM && maxMemory >= MIN_MEM_FOR_BIG_BLOOM) { + if (KBps >= MIN_SHARE_KBPS_FOR_HUGE_BLOOM) + warn(maxMemory, KBps, MIN_MEM_FOR_HUGE_BLOOM, MIN_SHARE_KBPS_FOR_HUGE_BLOOM); _filter = new DecayingBloomFilter(ctx, HALFLIFE_MS, 16, "TunnelIVV", 24); // 4MB fixed - else + } else { + if (KBps >= MIN_SHARE_KBPS_FOR_BIG_BLOOM) + warn(maxMemory, KBps, MIN_MEM_FOR_BIG_BLOOM, MIN_SHARE_KBPS_FOR_BIG_BLOOM); _filter = new DecayingBloomFilter(ctx, HALFLIFE_MS, 16, "TunnelIVV"); // 2MB fixed + } ctx.statManager().createRateStat("tunnel.duplicateIV", "Note that a duplicate IV was received", "Tunnels", new long[] { 60*60*1000l }); } public boolean receiveIV(byte ivData[], int ivOffset, byte payload[], int payloadOffset) { + if (_filter == null) // testing only + return true; byte[] buf = SimpleByteCache.acquire(HopProcessor.IV_LENGTH); DataHelper.xor(ivData, ivOffset, payload, payloadOffset, buf, 0, HopProcessor.IV_LENGTH); boolean dup = _filter.add(buf); @@ -62,5 +87,26 @@ class BloomFilterIVValidator implements IVValidator { return !dup; // return true if it is OK, false if it isn't } - public void destroy() { _filter.stopDecaying(); } + public void destroy() { + if (_filter != null) + _filter.stopDecaying(); + } + + /** @since 0.9.20 */ + private void warn(long maxMemory, int KBps, long recMaxMem, int threshKBps) { + if (SystemVersion.isAndroid()) + return; + String msg = + "Configured for " + DataHelper.formatSize(KBps *1024) + + "Bps share bandwidth but only " + + DataHelper.formatSize(maxMemory) + "B available memory." + + " Recommend increasing wrapper.java.maxmemory in " + + _context.getBaseDir() + File.separatorChar + "wrapper.config" + + // getMaxMemory() returns significantly lower than wrapper config, so add 10% + " to at least " + (recMaxMem * 11 / 10 / (1024*1024)) + " (MB)" + + " if the actual share bandwidth exceeds " + + DataHelper.formatSize(threshKBps * 1024) + "Bps."; + System.out.println("WARN: " + msg); + _context.logManager().getLog(BloomFilterIVValidator.class).logAlways(Log.WARN, msg); + } } diff --git a/router/java/src/net/i2p/router/util/DecayingBloomFilter.java b/router/java/src/net/i2p/router/util/DecayingBloomFilter.java index 9aa9c6715f..a5910e36ef 100644 --- a/router/java/src/net/i2p/router/util/DecayingBloomFilter.java +++ b/router/java/src/net/i2p/router/util/DecayingBloomFilter.java @@ -372,6 +372,9 @@ public class DecayingBloomFilter { * * Following stats for m=25, k=10: * 1792 2.4E-6; 4096 0.14%; 5120 0.6%; 6144 1.7%; 8192 6.8%; 10240 15% + * + * Following stats for m=26, k=10: + * 4096 7.3E-6; 5120 4.5E-5; 6144 1.8E-4; 8192 0.14%; 10240 0.6%, 12288 1.7% * */ /***** @@ -400,16 +403,23 @@ public class DecayingBloomFilter { } private static void testByLong(int kbps, int m, int numRuns) { + System.out.println("Starting 8 byte test"); int messages = 60 * 10 * kbps; - Random r = new Random(); + java.util.Random r = new java.util.Random(); DecayingBloomFilter filter = new DecayingBloomFilter(I2PAppContext.getGlobalContext(), 600*1000, 8, "test", m); int falsePositives = 0; long totalTime = 0; double fpr = 0d; for (int j = 0; j < numRuns; j++) { + // screen out birthday paradoxes (waste of time and space?) + java.util.Set longs = new java.util.HashSet(messages); long start = System.currentTimeMillis(); for (int i = 0; i < messages; i++) { - if (filter.add(r.nextLong())) { + long rand; + do { + rand = r.nextLong(); + } while (!longs.add(Long.valueOf(rand))); + if (filter.add(rand)) { falsePositives++; //System.out.println("False positive " + falsePositives + " (testByLong j=" + j + " i=" + i + ")"); } @@ -422,13 +432,14 @@ public class DecayingBloomFilter { System.out.println("False postive rate should be " + fpr); System.out.println("After " + numRuns + " runs pushing " + messages + " entries in " + DataHelper.formatDuration(totalTime/numRuns) + " per run, there were " - + falsePositives + " false positives"); - + + falsePositives + " false positives (" + + (((double) falsePositives) / messages) + ')'); } private static void testByBytes(int kbps, int m, int numRuns) { + System.out.println("Starting 16 byte test"); byte iv[][] = new byte[60*10*kbps][16]; - Random r = new Random(); + java.util.Random r = new java.util.Random(); for (int i = 0; i < iv.length; i++) r.nextBytes(iv[i]); @@ -452,7 +463,8 @@ public class DecayingBloomFilter { System.out.println("False postive rate should be " + fpr); System.out.println("After " + numRuns + " runs pushing " + iv.length + " entries in " + DataHelper.formatDuration(totalTime/numRuns) + " per run, there were " - + falsePositives + " false positives"); + + falsePositives + " false positives (" + + (((double) falsePositives) / iv.length) + ')'); //System.out.println("inserted: " + bloom.size() + " with " + bloom.capacity() // + " (" + bloom.falsePositives()*100.0d + "% false positive)"); }