propagate from branch 'i2p.i2p.zzz.test2' (head b6de226d1664089488ab2b438fe7457e9fb8e563)

to branch 'i2p.i2p' (head 0cf35c87b68a5360bd35257e36dfe7f740e53693)
This commit is contained in:
zzz
2015-04-17 13:18:22 +00:00
18 changed files with 775 additions and 371 deletions

View File

@ -28,4 +28,11 @@
<url-pattern>/*</url-pattern> <url-pattern>/*</url-pattern>
</servlet-mapping> </servlet-mapping>
<!-- this webapp doesn't actually use sessions or cookies -->
<session-config>
<session-timeout>30</session-timeout>
<cookie-config>
<http-only>true</http-only>
</cookie-config>
</session-config>
</web-app> </web-app>

View File

@ -26,73 +26,14 @@
<url-pattern>/</url-pattern> <url-pattern>/</url-pattern>
</servlet-mapping> </servlet-mapping>
<!-- this webapp doesn't actually use sessions or cookies -->
<session-config> <session-config>
<session-timeout> <session-timeout>
30 30
</session-timeout> </session-timeout>
<cookie-config>
<http-only>true</http-only>
</cookie-config>
</session-config> </session-config>
<!-- mime types not in mime.properties in the jetty 5.1.15 source -->
<mime-mapping>
<extension>mkv</extension>
<mime-type>video/x-matroska</mime-type>
</mime-mapping>
<mime-mapping>
<extension>wmv</extension>
<mime-type>video/x-ms-wmv</mime-type>
</mime-mapping>
<mime-mapping>
<extension>flv</extension>
<mime-type>video/x-flv</mime-type>
</mime-mapping>
<mime-mapping>
<extension>mp4</extension>
<mime-type>video/mp4</mime-type>
</mime-mapping>
<mime-mapping>
<extension>rar</extension>
<mime-type>application/rar</mime-type>
</mime-mapping>
<mime-mapping>
<extension>7z</extension>
<mime-type>application/x-7z-compressed</mime-type>
</mime-mapping>
<mime-mapping>
<extension>iso</extension>
<mime-type>application/x-iso9660-image</mime-type>
</mime-mapping>
<mime-mapping>
<extension>ico</extension>
<mime-type>image/x-icon</mime-type>
</mime-mapping>
<mime-mapping>
<extension>exe</extension>
<mime-type>application/x-msdos-program</mime-type>
</mime-mapping>
<mime-mapping>
<extension>flac</extension>
<mime-type>audio/flac</mime-type>
</mime-mapping>
<mime-mapping>
<extension>m4a</extension>
<mime-type>audio/mpeg</mime-type>
</mime-mapping>
<mime-mapping>
<extension>wma</extension>
<mime-type>audio/x-ms-wma</mime-type>
</mime-mapping>
</web-app> </web-app>

View File

@ -71,11 +71,16 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
private static final long TOTAL_HEADER_TIMEOUT = 2 * HEADER_TIMEOUT; private static final long TOTAL_HEADER_TIMEOUT = 2 * HEADER_TIMEOUT;
private static final long START_INTERVAL = (60 * 1000) * 3; private static final long START_INTERVAL = (60 * 1000) * 3;
private static final int MAX_LINE_LENGTH = 8*1024; 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 long _startedOn = 0L;
private ConnThrottler _postThrottler; private ConnThrottler _postThrottler;
private final static byte[] ERR_UNAVAILABLE = private final static String ERR_UNAVAILABLE =
("HTTP/1.1 503 Service Unavailable\r\n"+ "HTTP/1.1 503 Service Unavailable\r\n"+
"Content-Type: text/html; charset=iso-8859-1\r\n"+ "Content-Type: text/html; charset=iso-8859-1\r\n"+
"Cache-control: no-cache\r\n"+ "Cache-control: no-cache\r\n"+
"Connection: close\r\n"+ "Connection: close\r\n"+
@ -84,11 +89,10 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
"<html><head><title>503 Service Unavailable</title></head>\n"+ "<html><head><title>503 Service Unavailable</title></head>\n"+
"<body><h2>503 Service Unavailable</h2>\n" + "<body><h2>503 Service Unavailable</h2>\n" +
"<p>This I2P website is unavailable. It may be down or undergoing maintenance.</p>\n" + "<p>This I2P website is unavailable. It may be down or undergoing maintenance.</p>\n" +
"</body></html>") "</body></html>";
.getBytes();
private final static byte[] ERR_DENIED = private final static String ERR_DENIED =
("HTTP/1.1 403 Denied\r\n"+ "HTTP/1.1 403 Denied\r\n"+
"Content-Type: text/html; charset=iso-8859-1\r\n"+ "Content-Type: text/html; charset=iso-8859-1\r\n"+
"Cache-control: no-cache\r\n"+ "Cache-control: no-cache\r\n"+
"Connection: close\r\n"+ "Connection: close\r\n"+
@ -97,11 +101,10 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
"<html><head><title>403 Denied</title></head>\n"+ "<html><head><title>403 Denied</title></head>\n"+
"<body><h2>403 Denied</h2>\n" + "<body><h2>403 Denied</h2>\n" +
"<p>Denied due to excessive requests. Please try again later.</p>\n" + "<p>Denied due to excessive requests. Please try again later.</p>\n" +
"</body></html>") "</body></html>";
.getBytes();
private final static byte[] ERR_INPROXY = private final static String ERR_INPROXY =
("HTTP/1.1 403 Denied\r\n"+ "HTTP/1.1 403 Denied\r\n"+
"Content-Type: text/html; charset=iso-8859-1\r\n"+ "Content-Type: text/html; charset=iso-8859-1\r\n"+
"Cache-control: no-cache\r\n"+ "Cache-control: no-cache\r\n"+
"Connection: close\r\n"+ "Connection: close\r\n"+
@ -110,8 +113,64 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
"<html><head><title>403 Denied</title></head>\n"+ "<html><head><title>403 Denied</title></head>\n"+
"<body><h2>403 Denied</h2>\n" + "<body><h2>403 Denied</h2>\n" +
"<p>Inproxy access denied. You must run <a href=\"https://geti2p.net/\">I2P</a> to access this site.</p>\n" + "<p>Inproxy access denied. You must run <a href=\"https://geti2p.net/\">I2P</a> to access this site.</p>\n" +
"</body></html>") "</body></html>";
.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"+
"<html><head><title>503 Service Unavailable</title></head>\n"+
"<body><h2>503 Service Unavailable</h2>\n" +
"<p>This I2P website is not configured for SSL.</p>\n" +
"</body></html>";
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"+
"<html><head><title>414 Request URI Too Long</title></head>\n"+
"<body><h2>414 Request URI too long</h2>\n" +
"</body></html>";
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"+
"<html><head><title>431 Request Header Fields Too Large</title></head>\n"+
"<body><h2>431 Request header fields too large</h2>\n" +
"</body></html>";
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"+
"<html><head><title>408 Request Timeout</title></head>\n"+
"<body><h2>408 Request timeout</h2>\n" +
"</body></html>";
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"+
"<html><head><title>400 Bad Request</title></head>\n"+
"<body><h2>400 Bad request</h2>\n" +
"</body></html>";
public I2PTunnelHTTPServer(InetAddress host, int port, String privData, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) { public I2PTunnelHTTPServer(InetAddress host, int port, String privData, String spoofHost, Logging l, EventDispatcher notifyThis, I2PTunnel tunnel) {
super(host, port, privData, l, notifyThis, tunnel); super(host, port, privData, l, notifyThis, tunnel);
@ -131,7 +190,6 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
private void setupI2PTunnelHTTPServer(String spoofHost) { private void setupI2PTunnelHTTPServer(String spoofHost) {
_spoofHost = (spoofHost != null && spoofHost.trim().length() > 0) ? spoofHost.trim() : null; _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.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 @Override
@ -208,13 +266,88 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
//local is fast, so synchronously. Does not need that many //local is fast, so synchronously. Does not need that many
//threads. //threads.
try { 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(); long afterAccept = getTunnel().getContext().clock().now();
// The headers _should_ be in the first packet, but // The headers _should_ be in the first packet, but
// may not be, depending on the client-side options // may not be, depending on the client-side options
StringBuilder command = new StringBuilder(128); StringBuilder command = new StringBuilder(128);
Map<String, List<String>> headers = readHeaders(socket, null, command, Map<String, List<String>> headers;
CLIENT_SKIPHEADERS, getTunnel().getContext()); 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(); long afterHeaders = getTunnel().getContext().clock().now();
Properties opts = getTunnel().getClientOptions(); Properties opts = getTunnel().getClientOptions();
@ -239,7 +372,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
try { try {
// Send a 403, so the user doesn't get an HTTP Proxy error message // Send a 403, so the user doesn't get an HTTP Proxy error message
// and blame his router or the network. // and blame his router or the network.
socket.getOutputStream().write(ERR_INPROXY); socket.getOutputStream().write(ERR_INPROXY.getBytes("UTF-8"));
} catch (IOException ioe) {} } catch (IOException ioe) {}
try { try {
socket.close(); socket.close();
@ -256,7 +389,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
try { try {
// Send a 403, so the user doesn't get an HTTP Proxy error message // Send a 403, so the user doesn't get an HTTP Proxy error message
// and blame his router or the network. // and blame his router or the network.
socket.getOutputStream().write(ERR_DENIED); socket.getOutputStream().write(ERR_DENIED.getBytes("UTF-8"));
} catch (IOException ioe) {} } catch (IOException ioe) {}
try { try {
socket.close(); socket.close();
@ -341,7 +474,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
try { try {
// Send a 503, so the user doesn't get an HTTP Proxy error message // Send a 503, so the user doesn't get an HTTP Proxy error message
// and blame his router or the network. // and blame his router or the network.
socket.getOutputStream().write(ERR_UNAVAILABLE); socket.getOutputStream().write(ERR_UNAVAILABLE.getBytes("UTF-8"));
} catch (IOException ioe) {} } catch (IOException ioe) {}
try { try {
socket.close(); socket.close();
@ -362,7 +495,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
try { try {
// Send a 503, so the user doesn't get an HTTP Proxy error message // Send a 503, so the user doesn't get an HTTP Proxy error message
// and blame his router or the network. // and blame his router or the network.
socket.getOutputStream().write(ERR_UNAVAILABLE); socket.getOutputStream().write(ERR_UNAVAILABLE.getBytes("UTF-8"));
} catch (IOException ioe) {} } catch (IOException ioe) {}
try { try {
socket.close(); socket.close();
@ -453,7 +586,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
try { try {
if (browserout == null) if (browserout == null)
browserout = _browser.getOutputStream(); browserout = _browser.getOutputStream();
browserout.write(ERR_UNAVAILABLE); browserout.write(ERR_UNAVAILABLE.getBytes("UTF-8"));
} catch (IOException ioe) {} } catch (IOException ioe) {}
} catch (IOException ioe) { } catch (IOException ioe) {
if (_log.shouldLog(Log.WARN)) if (_log.shouldLog(Log.WARN))
@ -633,9 +766,6 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
return buf.toString(); 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. * Add an entry to the multimap.
*/ */
@ -680,10 +810,11 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
* @param socket if null, use in as InputStream * @param socket if null, use in as InputStream
* @param in if null, use socket.getInputStream() as InputStream * @param in if null, use socket.getInputStream() as InputStream
* @param command out parameter, first line * @param command out parameter, first line
* @param command out parameter, first line
* @throws SocketTimeoutException if timeout is reached before newline * @throws SocketTimeoutException if timeout is reached before newline
* @throws EOFException if EOF 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 * @throws IOException on other errors in the underlying stream
*/ */
private static Map<String, List<String>> readHeaders(I2PSocket socket, InputStream in, StringBuilder command, private static Map<String, List<String>> readHeaders(I2PSocket socket, InputStream in, StringBuilder command,
@ -694,51 +825,49 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
// slowloris / darkloris // slowloris / darkloris
long expire = ctx.clock().now() + TOTAL_HEADER_TIMEOUT; long expire = ctx.clock().now() + TOTAL_HEADER_TIMEOUT;
if (socket != null) { 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 { } else {
boolean ok = DataHelper.readLine(in, command); boolean ok = DataHelper.readLine(in, command);
if (!ok) 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)) //if (_log.shouldLog(Log.DEBUG))
// _log.debug("Read the http command [" + command.toString() + "]"); // _log.debug("Read the http command [" + command.toString() + "]");
// FIXME we probably don't need or want this in the outgoing direction int totalSize = command.length();
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 i = 0; int i = 0;
while (true) { while (true) {
if (++i > MAX_HEADERS) if (++i > MAX_HEADERS) {
throw new IOException("Too many header lines - max " + MAX_HEADERS); throw new LineTooLongException("Too many header lines - max " + MAX_HEADERS);
}
buf.setLength(0); buf.setLength(0);
if (socket != null) { if (socket != null) {
readLine(socket, buf, expire - ctx.clock().now()); readLine(socket, buf, expire - ctx.clock().now());
} else { } else {
boolean ok = DataHelper.readLine(in, buf); boolean ok = DataHelper.readLine(in, buf);
if (!ok) 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) || if ( (buf.length() == 0) ||
((buf.charAt(0) == '\n') || (buf.charAt(0) == '\r')) ) { ((buf.charAt(0) == '\n') || (buf.charAt(0) == '\r')) ) {
// end of headers reached // end of headers reached
return headers; return headers;
} else { } else {
if (ctx.clock().now() >= expire) if (ctx.clock().now() > expire) {
throw new IOException("Headers took too long [" + buf.toString() + "]"); throw new SocketTimeoutException("Headers took too long");
}
int split = buf.indexOf(":"); 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 name = buf.substring(0, split).trim();
String value = null; String value = null;
if (buf.length() > split + 1) if (buf.length() > split + 1)
@ -831,5 +960,23 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
super(s); 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);
}
}
} }

View File

@ -32,10 +32,14 @@
<url-pattern>/wizard</url-pattern> <url-pattern>/wizard</url-pattern>
</servlet-mapping> </servlet-mapping>
<!-- this webapp doesn't actually use sessions or cookies -->
<session-config> <session-config>
<session-timeout> <session-timeout>
30 30
</session-timeout> </session-timeout>
<cookie-config>
<http-only>true</http-only>
</cookie-config>
</session-config> </session-config>
<welcome-file-list> <welcome-file-list>
<welcome-file>index.html</welcome-file> <welcome-file>index.html</welcome-file>

View File

@ -35,6 +35,9 @@
<session-timeout> <session-timeout>
30 30
</session-timeout> </session-timeout>
<cookie-config>
<http-only>true</http-only>
</cookie-config>
</session-config> </session-config>
<welcome-file-list> <welcome-file-list>
<welcome-file>index.html</welcome-file> <welcome-file>index.html</welcome-file>

View File

@ -41,7 +41,8 @@ class Connection {
private final MessageInputStream _inputStream; private final MessageInputStream _inputStream;
private final MessageOutputStream _outputStream; private final MessageOutputStream _outputStream;
private final SchedulerChooser _chooser; private final SchedulerChooser _chooser;
private volatile long _nextSendTime; /** Locking: _nextSendLock */
private long _nextSendTime;
private long _ackedPackets; private long _ackedPackets;
private final long _createdOn; private final long _createdOn;
private final AtomicLong _closeSentOn = new AtomicLong(); private final AtomicLong _closeSentOn = new AtomicLong();
@ -70,6 +71,8 @@ class Connection {
private final AtomicBoolean _ackSinceCongestion; private final AtomicBoolean _ackSinceCongestion;
/** Notify this on connection (or connection failure) */ /** Notify this on connection (or connection failure) */
private final Object _connectLock; private final Object _connectLock;
/** Locking for _nextSendTime */
private final Object _nextSendLock;
/** how many messages have been resent and not yet ACKed? */ /** how many messages have been resent and not yet ACKed? */
private final AtomicInteger _activeResends = new AtomicInteger(); private final AtomicInteger _activeResends = new AtomicInteger();
private final ConEvent _connectionEvent; private final ConEvent _connectionEvent;
@ -145,6 +148,7 @@ class Connection {
_activityTimer = new ActivityTimer(); _activityTimer = new ActivityTimer();
_ackSinceCongestion = new AtomicBoolean(true); _ackSinceCongestion = new AtomicBoolean(true);
_connectLock = new Object(); _connectLock = new Object();
_nextSendLock = new Object();
_connectionEvent = new ConEvent(); _connectionEvent = new ConEvent();
_randomWait = _context.random().nextInt(10*1000); // just do this once to reduce usage _randomWait = _context.random().nextInt(10*1000); // just do this once to reduce usage
// all createRateStats in ConnectionManager // all createRateStats in ConnectionManager
@ -907,7 +911,11 @@ class Connection {
* instance, or want to delay an ACK. * instance, or want to delay an ACK.
* @return the next time the scheduler will want to send a packet, or -1 if never. * @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"), * If the next send time is currently >= 0 (i.e. not "never"),
@ -917,25 +925,20 @@ class Connection {
* options.getSendAckDelay() from now (1000 ms) * options.getSendAckDelay() from now (1000 ms)
*/ */
public void setNextSendTime(long when) { public void setNextSendTime(long when) {
if (_nextSendTime >= 0) { synchronized(_nextSendLock) {
if (when < _nextSendTime) if (_nextSendTime >= 0) {
_nextSendTime = when; if (when < _nextSendTime)
} else { _nextSendTime = when;
_nextSendTime = when; } else {
} _nextSendTime = when;
}
if (_nextSendTime >= 0) { if (_nextSendTime >= 0) {
long max = _context.clock().now() + _options.getSendAckDelay(); long max = _context.clock().now() + _options.getSendAckDelay();
if (max < _nextSendTime) if (max < _nextSendTime)
_nextSendTime = max; _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? /** how many packets have we sent and the other side has ACKed?
@ -1260,17 +1263,17 @@ class Connection {
*/ */
class ResendPacketEvent extends SimpleTimer2.TimedEvent { class ResendPacketEvent extends SimpleTimer2.TimedEvent {
private final PacketLocal _packet; private final PacketLocal _packet;
private long _nextSendTime; private long _nextSend;
public ResendPacketEvent(PacketLocal packet, long delay) { public ResendPacketEvent(PacketLocal packet, long delay) {
super(_timer); super(_timer);
_packet = packet; _packet = packet;
_nextSendTime = delay + _context.clock().now(); _nextSend = delay + _context.clock().now();
packet.setResendPacketEvent(ResendPacketEvent.this); packet.setResendPacketEvent(ResendPacketEvent.this);
schedule(delay); schedule(delay);
} }
public long getNextSendTime() { return _nextSendTime; } public long getNextSendTime() { return _nextSend; }
public void timeReached() { retransmit(); } public void timeReached() { retransmit(); }
/** /**
* Retransmit the packet if we need to. * Retransmit the packet if we need to.
@ -1320,7 +1323,7 @@ class Connection {
+ _activeResends + " active resend, " + _activeResends + " active resend, "
+ _outboundPackets.size() + " unacked, window size = " + _options.getWindowSize()); + _outboundPackets.size() + " unacked, window size = " + _options.getWindowSize());
forceReschedule(1333); forceReschedule(1333);
_nextSendTime = 1333 + _context.clock().now(); _nextSend = 1333 + _context.clock().now();
return false; return false;
} }
@ -1407,7 +1410,7 @@ class Connection {
if ( (timeout > MAX_RESEND_DELAY) || (timeout <= 0) ) if ( (timeout > MAX_RESEND_DELAY) || (timeout <= 0) )
timeout = MAX_RESEND_DELAY; timeout = MAX_RESEND_DELAY;
// set this before enqueue() as it passes it on to the router // 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)) { if (_outboundQueue.enqueue(_packet)) {
// first resend for this packet ? // first resend for this packet ?

View File

@ -23,6 +23,9 @@
</servlet-mapping> </servlet-mapping>
<session-config> <session-config>
<session-timeout>1440</session-timeout> <session-timeout>1440</session-timeout>
<cookie-config>
<http-only>true</http-only>
</cookie-config>
</session-config> </session-config>
<!-- tomcat (untested) --> <!-- tomcat (untested) -->
<context-param> <context-param>

View File

@ -904,10 +904,14 @@ public class EepGet {
_keepFetching = false; _keepFetching = false;
_notModified = true; _notModified = true;
return; return;
case 400: // bad req
case 401: // server auth case 401: // server auth
case 403: // bad req case 403: // bad req
case 404: // not found case 404: // not found
case 408: // req timeout
case 409: // bad addr helper case 409: // bad addr helper
case 414: // URI too long
case 431: // headers too long
case 503: // no outproxy case 503: // no outproxy
_transferFailed = true; _transferFailed = true;
if (_alreadyTransferred > 0 || !_shouldWriteErrorToOutput) { if (_alreadyTransferred > 0 || !_shouldWriteErrorToOutput) {

View File

@ -1,35 +1,255 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd"> <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<!-- ========================================================================= -->
<!-- If you have a 'split' directory installation, with configuration -->
<!-- files in ~/.i2p (Linux) or %APPDATA%\I2P (Windows), be sure to -->
<!-- edit the file in the configuration directory, NOT the install directory. -->
<!-- When running as a Linux daemon, the configuration directory is -->
<!-- /var/lib/i2p and the install directory is /usr/share/i2p . -->
<!-- -->
<!-- ========================================================================= -->
<!-- =============================================================== --> <!-- =============================================================== -->
<!-- Configure SSL for the Jetty Server --> <!-- Configure SSL for the Jetty Server -->
<!-- this configuration file should be used in combination with --> <!-- this configuration file should be used in combination with -->
<!-- other configuration files. e.g. --> <!-- other configuration files. -->
<!-- java -jar start.jar etc/jetty-ssl.xml -->
<!-- --> <!-- -->
<!-- alternately, add to the start.ini for easier usage -->
<!-- =============================================================== --> <!-- =============================================================== -->
<!-- Add a HTTPS SSL listener on port 7668 -->
<!-- -->
<!-- NOTE: -->
<!-- -->
<!-- While I2P already encrypts end-to-end, HTTPS support -->
<!-- is valuable for authentication. -->
<!-- -->
<!-- These instructions are to add SSL support to an existing -->
<!-- HTTP Jetty website. -->
<!-- -->
<!-- For HTTPS ONLY, create a standard server tunnel -->
<!-- (NOT HTTP server), and skip step 8. -->
<!-- -->
<!-- For non-Jetty servers (e.g. Apache), follow your server -->
<!-- instructions to generate and configure the certificates, -->
<!-- and skip steps 1-7. -->
<!-- -->
<!-- =============================================================== -->
<!-- -->
<!-- To add SSL support for your existing website: -->
<!-- -->
<!-- Step 1: -->
<!-- Get the b32 for your wehsite, it's the link at the -->
<!-- "preview" button in the Hidden Services Manager in -->
<!-- the console. If you aren't running i2p, you can -->
<!-- get it from your private key file -->
<!-- (probably ~/.i2p/eepsite/eepPriv.dat) -->
<!-- with the command: -->
<!-- java -cp ~/i2p/lib/i2p.jar net.i2p.data.PrivateKeyFile ~/.i2p/eepsite/eepPriv.dat -->
<!-- Save the b32 to put in the certificate's CN in Step 2. -->
<!-- -->
<!-- -->
<!-- Step 2: -->
<!-- Generate selfsigned certificates. -->
<!-- We recommend two: one for the hostname, and one for the b32. -->
<!-- Note that server-side SNI to serve the correct certificate -->
<!-- requires Java 8. Otherwise it will pick one. -->
<!-- (at random? first one?) -->
<!-- Change the CN and key password in the example, of course. -->
<!-- It's OK to keep the keystore password as "changeit" if you like. -->
<!-- Use the same passwords for both certificates. -->
<!-- See https://wiki.eclipse.org/Jetty/Howto/Configure_SSL -->
<!-- for alternate methods. -->
<!--
keytool -genkey -keystore ~/.i2p/eepsite/etc/keystore.ks -storepass changeit -alias b32 -dname CN=biglongkey.b32.i2p,OU=Eepsite,O=XX,L=XX,ST=XX,C=XX -validity 3652 -keyalg RSA -keysize 2048 -keypass myKeyPassword
keytool -genkey -keystore ~/.i2p/eepsite/etc/keystore.ks -storepass changeit -alias hostname -dname CN=example.i2p,OU=Eepsite,O=XX,L=XX,ST=XX,C=XX -validity 3652 -keyalg RSA -keysize 2048 -keypass myKeyPassword
chmod 600 ~/.i2p/eepsite/etc/keystore.ks
-->
<!-- -->
<!-- But does SNI work? see: -->
<!-- http://blog.ivanristic.com/2014/03/ssl-tls-improvements-in-java-8.html -->
<!-- http://stackoverflow.com/questions/20887504/tls-extension-server-name-indication-sni-value-not-available-on-server-side -->
<!-- -->
<!-- And no, you can't get a real certificate for an i2p -->
<!-- address from a Certificate Authority, but someday -->
<!-- it may be possible. Here's how Tor did it: -->
<!-- https://cabforum.org/2015/02/18/ballot-144-validation-rules-dot-onion-names/ -->
<!-- -->
<!-- -->
<!-- Step 3: -->
<!-- Update this configuration file. -->
<!-- Edit the KeyStorePassword, TrustStorePassword, and -->
<!-- KeyManagerPassword below to match the passwords from Step 2. -->
<!-- -->
<!-- -->
<!-- Step 4: -->
<!-- If running I2P, stop the website Jetty on /configclients -->
<!-- in the console. -->
<!-- -->
<!-- -->
<!-- Step 5: -->
<!-- Configure Jetty to read in this file at startup. -->
<!-- If running I2P, edit the website Jetty on /configclients -->
<!-- to add the argument "/path/to/.i2p/eepsite/jetty-ssl.xml". -->
<!-- -->
<!-- If I2P is not running, edit the file ~/.i2p/clients.config -->
<!-- to add the argument "/path/to/.i2p/eepsite/jetty-ssl.xml" -->
<!-- at the end of the line: -->
<-- clientApp.3.args="eepsite/jetty.xml" -->
<!-- so it now looks like: -->
<-- clientApp.3.args="/path to/.i2p/eepsite/jetty.xml" "/path/to/.i2p/eepsite/jetty-ssl.xml" -->
<!-- -->
<!-- -->
<!-- Step 6: -->
<!-- Start Jetty. -->
<!-- If running I2P, start the website Jetty on /configclients -->
<!-- in the console. -->
<!-- If I2P is not running, start it. -->
<!-- -->
<!-- Now go to the /logs page in the console and check for errors -->
<!-- in both the router and wrapper logs. -->
<!-- -->
<!-- -->
<!-- Step 7: -->
<!-- Test Jetty. -->
<!-- If there were no errors, test your Jetty SSL by -->
<!-- going to https://127.0.0.1:7668/ in your browser. -->
<!-- You will have to confirm the security exception for -->
<!-- the selfsigned certificate. -->
<!-- -->
<!-- -->
<!-- Step 8: -->
<!-- Configure i2ptunnel. -->
<!-- Tell i2ptunnel to route SSL to port 7668 by adding the -->
<!-- following custom option on the i2ptunnel edit page -->
<!-- for your website: -->
<!-- targetForPort.443=127.0.0.1:7668 -->
<!-- Also, verify that "Use SSL" near the top is NOT set. -->
<!-- That would be SSL-over-SSL, which won't work. -->
<!-- -->
<!-- -->
<!-- Step 9: -->
<!-- Start the tunnel if it isn't started. -->
<!-- -->
<!-- -->
<!-- Step 10: -->
<!-- In the i2ptunnel HTTP Client configuration, -->
<!-- enable "Allow SSL to I2P addresses" if it isn't already. -->
<!-- -->
<!-- -->
<!-- Step 11: -->
<!-- Test SSL via i2ptunnel. -->
<!-- Test SSL to your website through I2P by entering -->
<!-- https://yoursite.i2p/ in your browser. -->
<!-- If it doesn't work, check the /logs page in the console. -->
<!-- You may need to adjust your browser proxy settings to -->
<!-- ensure that https i2p URLs are fetched through the I2P proxy. -->
<!-- For example, in privoxy, add -->
<!-- https://*.i2p/* and https://*.i2p:*/* -->
<!-- -->
<!-- -->
<!-- Step 12: -->
<!-- Tell your users. -->
<!-- Put a link to the https version on your -->
<!-- home page. Remind them that in -->
<!-- the i2ptunnel HTTP Client configuration, -->
<!-- enable "Allow SSL to I2P addresses" if it isn't already. -->
<!-- Remind them to confirm the security exception for -->
<!-- the selfsigned certificate (but not one for a hostname -->
<!-- mismatch) (but see SNI issues above). -->
<!-- Users may need to adjust their browser proxy settings to -->
<!-- ensure that https i2p URLs are fetched through the I2P proxy. -->
<!-- For example, in privoxy, add -->
<!-- https://*.i2p/* and https://*.i2p:*/* -->
<!-- -->
<!-- Decide what link to use. The hostname is not secure, -->
<!-- as users may have a different hostname in their browser. -->
<!-- Also, new address helpers won't work with SSL. -->
<!-- The b32 is the recommended hostname. -->
<!-- -->
<!-- -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<Configure id="Server" class="org.eclipse.jetty.server.Server"> <Configure id="Server" class="org.eclipse.jetty.server.Server">
<!-- if NIO is not available, use org.eclipse.jetty.server.ssl.SslSocketConnector --> <!-- if NIO is not available, use org.eclipse.jetty.server.ssl.SslSocketConnector -->
<New id="sslContextFactory" class="org.eclipse.jetty.http.ssl.SslContextFactory"> <New id="sslContextFactory" class="org.eclipse.jetty.http.ssl.SslContextFactory">
<Set name="KeyStore">./eepsite/etc/keystore</Set> <Set name="KeyStore">./eepsite/etc/keystore.ks</Set>
<Set name="KeyStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set> <Set name="KeyStorePassword">changeit</Set>
<Set name="KeyManagerPassword">OBF:1u2u1wml1z7s1z7a1wnl1u2g</Set> <Set name="KeyManagerPassword">myKeyPassword</Set>
<Set name="TrustStore">./eepsite/etc/keystore</Set> <Set name="TrustStore">./eepsite/etc/keystore.ks</Set>
<Set name="TrustStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set> <Set name="TrustStorePassword">changeit</Set>
</New> </New>
<Call name="addConnector"> <Call name="addConnector">
<Arg> <Arg>
<New class="org.eclipse.jetty.server.ssl.SslSelectChannelConnector"> <New class="org.eclipse.jetty.server.ssl.SslSelectChannelConnector">
<Arg><Ref id="sslContextFactory" /></Arg> <Arg><Ref id="sslContextFactory" /></Arg>
<Set name="Port">8443</Set> <Set name="host">127.0.0.1</Set>
<Set name="port">7668</Set>
<Set name="maxIdleTime">600000</Set> <Set name="maxIdleTime">600000</Set>
<Set name="useDirectBuffers">false</Set> <Set name="useDirectBuffers">false</Set>
<Set name="Acceptors">2</Set> <Set name="acceptors">1</Set>
<Set name="AcceptQueueSize">100</Set> <Set name="statsOn">false</Set>
<Set name="lowResourcesConnections">5000</Set>
<Set name="lowResourcesMaxIdleTime">5000</Set>
<Set name="ExcludeCipherSuites">
<Array type="java.lang.String">
<Item>SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA</Item>
<Item>SSL_DH_anon_EXPORT_WITH_RC4_40_MD5</Item>
<Item>SSL_DH_anon_WITH_3DES_EDE_CBC_SHA</Item>
<Item>SSL_DH_anon_WITH_DES_CBC_SHA</Item>
<Item>SSL_DH_anon_WITH_RC4_128_MD5</Item>
<Item>SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA</Item>
<Item>SSL_DHE_DSS_WITH_DES_CBC_SHA</Item>
<Item>SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA</Item>
<Item>SSL_DHE_RSA_WITH_DES_CBC_SHA</Item>
<Item>SSL_RSA_EXPORT_WITH_DES40_CBC_SHA</Item>
<Item>SSL_RSA_EXPORT_WITH_RC4_40_MD5</Item>
<Item>SSL_RSA_WITH_DES_CBC_SHA</Item>
<Item>SSL_RSA_WITH_NULL_MD5</Item>
<Item>SSL_RSA_WITH_NULL_SHA</Item>
<Item>TLS_DH_anon_WITH_AES_128_CBC_SHA</Item>
<Item>TLS_DH_anon_WITH_AES_128_CBC_SHA256</Item>
<Item>TLS_DH_anon_WITH_AES_128_GCM_SHA256</Item>
<Item>TLS_DH_anon_WITH_AES_256_CBC_SHA</Item>
<Item>TLS_DH_anon_WITH_AES_256_CBC_SHA256</Item>
<Item>TLS_DH_anon_WITH_AES_256_GCM_SHA384</Item>
<Item>TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA</Item>
<Item>TLS_ECDH_anon_WITH_AES_128_CBC_SHA</Item>
<Item>TLS_ECDH_anon_WITH_AES_256_CBC_SHA</Item>
<Item>TLS_ECDH_anon_WITH_NULL_SHA</Item>
<Item>TLS_ECDH_anon_WITH_RC4_128_SHA</Item>
<Item>TLS_ECDH_ECDSA_WITH_NULL_SHA</Item>
<Item>TLS_ECDHE_ECDSA_WITH_NULL_SHA</Item>
<Item>TLS_ECDHE_RSA_WITH_NULL_SHA</Item>
<Item>TLS_ECDH_RSA_WITH_NULL_SHA</Item>
<Item>TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5</Item>
<Item>TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA</Item>
<Item>TLS_KRB5_EXPORT_WITH_RC4_40_MD5</Item>
<Item>TLS_KRB5_EXPORT_WITH_RC4_40_SHA</Item>
<Item>TLS_KRB5_WITH_3DES_EDE_CBC_MD5</Item>
<Item>TLS_KRB5_WITH_3DES_EDE_CBC_SHA</Item>
<Item>TLS_KRB5_WITH_DES_CBC_MD5</Item>
<Item>TLS_KRB5_WITH_DES_CBC_SHA</Item>
<Item>TLS_KRB5_WITH_RC4_128_MD5</Item>
<Item>TLS_KRB5_WITH_RC4_128_SHA</Item>
<Item>TLS_RSA_WITH_NULL_SHA256</Item>
<Item>SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA</Item>
<Item>SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA</Item>
<Item>SSL_RSA_WITH_3DES_EDE_CBC_SHA</Item>
<Item>SSL_RSA_WITH_RC4_128_MD5</Item>
<Item>SSL_RSA_WITH_RC4_128_SHA</Item>
<Item>TLS_ECDH_ECDSA_WITH_RC4_128_SHA</Item>
<Item>TLS_ECDH_RSA_WITH_RC4_128_SHA</Item>
<Item>TLS_ECDHE_ECDSA_WITH_RC4_128_SHA</Item>
<Item>TLS_ECDHE_RSA_WITH_RC4_128_SHA</Item>
<Item>TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA</Item>
<Item>TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA</Item>
<Item>TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA</Item>
<Item>TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA</Item>
</Array>
</Set>
</New> </New>
</Arg> </Arg>
</Call> </Call>

View File

@ -135,7 +135,6 @@
<Set name="maxIdleTime">600000</Set> <Set name="maxIdleTime">600000</Set>
<Set name="Acceptors">1</Set> <Set name="Acceptors">1</Set>
<Set name="statsOn">false</Set> <Set name="statsOn">false</Set>
<Set name="confidentialPort">8443</Set>
<Set name="lowResourcesConnections">5000</Set> <Set name="lowResourcesConnections">5000</Set>
<Set name="lowResourcesMaxIdleTime">5000</Set> <Set name="lowResourcesMaxIdleTime">5000</Set>
<Set name="useDirectBuffers">false</Set> <Set name="useDirectBuffers">false</Set>
@ -155,7 +154,6 @@
<Set name="maxIdleTime">600000</Set> <Set name="maxIdleTime">600000</Set>
<Set name="Acceptors">1</Set> <Set name="Acceptors">1</Set>
<Set name="statsOn">false</Set> <Set name="statsOn">false</Set>
<Set name="confidentialPort">8443</Set>
</New> </New>
</Arg> </Arg>
</Call> </Call>
@ -168,19 +166,6 @@
<!-- --> <!-- -->
<!-- clientApp3.args=etc/jetty.xml etc/jetty-ssl.xml --> <!-- clientApp3.args=etc/jetty.xml etc/jetty-ssl.xml -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- Add a HTTPS SSL listener on port 8443 -->
<!-- -->
<!-- In the unlikely event you would want SSL support for your eepsite. -->
<!-- You would need to generate a selfsigned certificate in a keystore -->
<!-- in ~/.i2p/eepsite/keystore.ks, for example with the command line: -->
<!--
keytool -genkey -storetype JKS -keystore ~/.i2p/eepsite/etc/keystore.ks -storepass changeit -alias console -dname CN=xyz123.eepsite.i2p.net,OU=Eepsite,O=I2P Anonymous Network,L=XX,ST=XX,C=XX -validity 3650 -keyalg DSA -keysize 1024 -keypass myKeyPassword
-->
<!-- Change the CN and key password in the example, of course. -->
<!-- You wouldn't want to open this up to the regular internet, -->
<!-- would you?? Untested and not recommended. -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- =========================================================== --> <!-- =========================================================== -->
<!-- Set up global session ID manager --> <!-- Set up global session ID manager -->

View File

@ -98,6 +98,8 @@ tunnel.3.option.inbound.length=3
tunnel.3.option.inbound.lengthVariance=0 tunnel.3.option.inbound.lengthVariance=0
tunnel.3.option.outbound.length=3 tunnel.3.option.outbound.length=3
tunnel.3.option.outbound.lengthVariance=0 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 tunnel.3.startOnLoad=false
# postman's SMTP server - see hq.postman.i2p # postman's SMTP server - see hq.postman.i2p

View File

@ -75,7 +75,7 @@ public class JobQueue {
/** default max # job queue runners operating */ /** default max # job queue runners operating */
private final static int DEFAULT_MAX_RUNNERS = 1; 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"; private final static String PROP_MAX_RUNNERS = "router.maxJobRunners";
/** how frequently should we check and update the max runners */ /** how frequently should we check and update the max runners */
@ -330,7 +330,7 @@ public class JobQueue {
public void allowParallelOperation() { public void allowParallelOperation() {
_allowParallelOperation = true; _allowParallelOperation = true;
runQueue(RUNNERS); runQueue(_context.getProperty(PROP_MAX_RUNNERS, RUNNERS));
} }
/** /**

View File

@ -8,15 +8,10 @@ package net.i2p.router;
* *
*/ */
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
@ -49,7 +44,6 @@ import net.i2p.router.util.EventLog;
import net.i2p.stat.RateStat; import net.i2p.stat.RateStat;
import net.i2p.stat.StatManager; import net.i2p.stat.StatManager;
import net.i2p.util.ByteCache; import net.i2p.util.ByteCache;
import net.i2p.util.FileUtil;
import net.i2p.util.FortunaRandomSource; import net.i2p.util.FortunaRandomSource;
import net.i2p.util.I2PAppThread; import net.i2p.util.I2PAppThread;
import net.i2p.util.I2PThread; 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 final static String DNS_CACHE_TIME = "" + (5*60);
private static final String EVENTLOG = "eventlog.txt"; private static final String EVENTLOG = "eventlog.txt";
private static final String PROP_JBIGI = "jbigi.loadedResource"; private static final String PROP_JBIGI = "jbigi.loadedResource";
public static final String UPDATE_FILE = "i2pupdate.zip";
private static final String originalTimeZoneID; private static final String originalTimeZoneID;
static { static {
@ -1497,199 +1492,11 @@ public class Router implements RouterClock.ClockShiftListener {
// If it does an update, it never returns. // 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 // 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. // overwrite an existing running router's jar files. Other than ours.
r.installUpdates(); InstallUpdate.installUpdates(r);
// ********* Start no threads before here ********* // // ********* Start no threads before here ********* //
r.runRouter(); 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() { private static void verifyWrapperConfig() {

View File

@ -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<String, String> config = new HashMap<String, String>(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");
}
}
}

View File

@ -4,6 +4,7 @@
Miscellaneous classes, mostly things that are executed periodically as Miscellaneous classes, mostly things that are executed periodically as
Jobs, Threads, and SimpleTimer.TimedEvents. Jobs, Threads, and SimpleTimer.TimedEvents.
These are used only by Router.java. These are used only by Router.java.
Nothing here is to be used externally, not a part of the public API.
</p> </p>
</body> </body>
</html> </html>

View File

@ -70,8 +70,12 @@ public class FIFOBandwidthRefiller implements Runnable {
public static final int MIN_INBOUND_BANDWIDTH_PEAK = 3; 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 */ /** 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; 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. * how often we replenish the queues.

View File

@ -1,9 +1,12 @@
package net.i2p.router.tunnel; package net.i2p.router.tunnel;
import java.io.File;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.router.RouterContext; import net.i2p.router.RouterContext;
import net.i2p.router.util.DecayingBloomFilter; import net.i2p.router.util.DecayingBloomFilter;
import net.i2p.router.util.DecayingHashSet; import net.i2p.router.util.DecayingHashSet;
import net.i2p.util.Log;
import net.i2p.util.SimpleByteCache; import net.i2p.util.SimpleByteCache;
import net.i2p.util.SystemVersion; 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_TO_USE_BLOOM = 64;
private static final int MIN_SHARE_KBPS_FOR_BIG_BLOOM = 512; 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_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_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_BIG_BLOOM = 128*1024*1024l;
private static final long MIN_MEM_FOR_HUGE_BLOOM = 256*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 */ /** for testing */
private static final String PROP_FORCE = "router.forceDecayingBloomFilter"; 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) { public BloomFilterIVValidator(RouterContext ctx, int KBps) {
_context = ctx; _context = ctx;
// Select the filter based on share bandwidth and memory. // Select the filter based on share bandwidth and memory.
@ -39,21 +49,36 @@ class BloomFilterIVValidator implements IVValidator {
// to keep acceptable false positive rates. // to keep acceptable false positive rates.
// See DBF, BloomSHA1, and KeySelector for details. // See DBF, BloomSHA1, and KeySelector for details.
long maxMemory = SystemVersion.getMaxMemory(); long maxMemory = SystemVersion.getMaxMemory();
if (_context.getBooleanProperty(PROP_FORCE)) if (_context.getBooleanProperty(PROP_FORCE)) {
_filter = new DecayingBloomFilter(ctx, HALFLIFE_MS, 16, "TunnelIVV"); // 2MB fixed _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 _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 _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 _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 _filter = new DecayingBloomFilter(ctx, HALFLIFE_MS, 16, "TunnelIVV"); // 2MB fixed
}
ctx.statManager().createRateStat("tunnel.duplicateIV", "Note that a duplicate IV was received", "Tunnels", ctx.statManager().createRateStat("tunnel.duplicateIV", "Note that a duplicate IV was received", "Tunnels",
new long[] { 60*60*1000l }); new long[] { 60*60*1000l });
} }
public boolean receiveIV(byte ivData[], int ivOffset, byte payload[], int payloadOffset) { 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); byte[] buf = SimpleByteCache.acquire(HopProcessor.IV_LENGTH);
DataHelper.xor(ivData, ivOffset, payload, payloadOffset, buf, 0, HopProcessor.IV_LENGTH); DataHelper.xor(ivData, ivOffset, payload, payloadOffset, buf, 0, HopProcessor.IV_LENGTH);
boolean dup = _filter.add(buf); 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 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);
}
} }

View File

@ -372,6 +372,9 @@ public class DecayingBloomFilter {
* *
* Following stats for m=25, k=10: * 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% * 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%
*</pre> *</pre>
*/ */
/***** /*****
@ -400,16 +403,23 @@ public class DecayingBloomFilter {
} }
private static void testByLong(int kbps, int m, int numRuns) { private static void testByLong(int kbps, int m, int numRuns) {
System.out.println("Starting 8 byte test");
int messages = 60 * 10 * kbps; 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); DecayingBloomFilter filter = new DecayingBloomFilter(I2PAppContext.getGlobalContext(), 600*1000, 8, "test", m);
int falsePositives = 0; int falsePositives = 0;
long totalTime = 0; long totalTime = 0;
double fpr = 0d; double fpr = 0d;
for (int j = 0; j < numRuns; j++) { for (int j = 0; j < numRuns; j++) {
// screen out birthday paradoxes (waste of time and space?)
java.util.Set<Long> longs = new java.util.HashSet<Long>(messages);
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
for (int i = 0; i < messages; i++) { 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++; falsePositives++;
//System.out.println("False positive " + falsePositives + " (testByLong j=" + j + " i=" + i + ")"); //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("False postive rate should be " + fpr);
System.out.println("After " + numRuns + " runs pushing " + messages + " entries in " System.out.println("After " + numRuns + " runs pushing " + messages + " entries in "
+ DataHelper.formatDuration(totalTime/numRuns) + " per run, there were " + 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) { 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]; 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++) for (int i = 0; i < iv.length; i++)
r.nextBytes(iv[i]); r.nextBytes(iv[i]);
@ -452,7 +463,8 @@ public class DecayingBloomFilter {
System.out.println("False postive rate should be " + fpr); System.out.println("False postive rate should be " + fpr);
System.out.println("After " + numRuns + " runs pushing " + iv.length + " entries in " System.out.println("After " + numRuns + " runs pushing " + iv.length + " entries in "
+ DataHelper.formatDuration(totalTime/numRuns) + " per run, there were " + 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() //System.out.println("inserted: " + bloom.size() + " with " + bloom.capacity()
// + " (" + bloom.falsePositives()*100.0d + "% false positive)"); // + " (" + bloom.falsePositives()*100.0d + "% false positive)");
} }