forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p.zzz.test2' (head b6de226d1664089488ab2b438fe7457e9fb8e563)
to branch 'i2p.i2p' (head 0cf35c87b68a5360bd35257e36dfe7f740e53693)
This commit is contained in:
@ -28,4 +28,11 @@
|
||||
<url-pattern>/*</url-pattern>
|
||||
</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>
|
||||
|
@ -26,73 +26,14 @@
|
||||
<url-pattern>/</url-pattern>
|
||||
</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>
|
||||
|
||||
|
||||
<!-- 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>
|
||||
|
@ -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 {
|
||||
"<html><head><title>503 Service Unavailable</title></head>\n"+
|
||||
"<body><h2>503 Service Unavailable</h2>\n" +
|
||||
"<p>This I2P website is unavailable. It may be down or undergoing maintenance.</p>\n" +
|
||||
"</body></html>")
|
||||
.getBytes();
|
||||
"</body></html>";
|
||||
|
||||
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 {
|
||||
"<html><head><title>403 Denied</title></head>\n"+
|
||||
"<body><h2>403 Denied</h2>\n" +
|
||||
"<p>Denied due to excessive requests. Please try again later.</p>\n" +
|
||||
"</body></html>")
|
||||
.getBytes();
|
||||
"</body></html>";
|
||||
|
||||
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 {
|
||||
"<html><head><title>403 Denied</title></head>\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" +
|
||||
"</body></html>")
|
||||
.getBytes();
|
||||
"</body></html>";
|
||||
|
||||
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) {
|
||||
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<String, List<String>> headers = readHeaders(socket, null, command,
|
||||
Map<String, List<String>> 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<String, List<String>> 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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,10 +32,14 @@
|
||||
<url-pattern>/wizard</url-pattern>
|
||||
</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>
|
||||
<welcome-file-list>
|
||||
<welcome-file>index.html</welcome-file>
|
||||
|
@ -35,6 +35,9 @@
|
||||
<session-timeout>
|
||||
30
|
||||
</session-timeout>
|
||||
<cookie-config>
|
||||
<http-only>true</http-only>
|
||||
</cookie-config>
|
||||
</session-config>
|
||||
<welcome-file-list>
|
||||
<welcome-file>index.html</welcome-file>
|
||||
|
@ -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,6 +925,7 @@ class Connection {
|
||||
* options.getSendAckDelay() from now (1000 ms)
|
||||
*/
|
||||
public void setNextSendTime(long when) {
|
||||
synchronized(_nextSendLock) {
|
||||
if (_nextSendTime >= 0) {
|
||||
if (when < _nextSendTime)
|
||||
_nextSendTime = when;
|
||||
@ -929,13 +938,7 @@ class Connection {
|
||||
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 ?
|
||||
|
@ -23,6 +23,9 @@
|
||||
</servlet-mapping>
|
||||
<session-config>
|
||||
<session-timeout>1440</session-timeout>
|
||||
<cookie-config>
|
||||
<http-only>true</http-only>
|
||||
</cookie-config>
|
||||
</session-config>
|
||||
<!-- tomcat (untested) -->
|
||||
<context-param>
|
||||
|
@ -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) {
|
||||
|
@ -1,35 +1,255 @@
|
||||
<?xml version="1.0"?>
|
||||
<!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 -->
|
||||
<!-- this configuration file should be used in combination with -->
|
||||
<!-- other configuration files. e.g. -->
|
||||
<!-- java -jar start.jar etc/jetty-ssl.xml -->
|
||||
<!-- other configuration files. -->
|
||||
<!-- -->
|
||||
<!-- 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">
|
||||
|
||||
<!-- if NIO is not available, use org.eclipse.jetty.server.ssl.SslSocketConnector -->
|
||||
|
||||
<New id="sslContextFactory" class="org.eclipse.jetty.http.ssl.SslContextFactory">
|
||||
<Set name="KeyStore">./eepsite/etc/keystore</Set>
|
||||
<Set name="KeyStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
|
||||
<Set name="KeyManagerPassword">OBF:1u2u1wml1z7s1z7a1wnl1u2g</Set>
|
||||
<Set name="TrustStore">./eepsite/etc/keystore</Set>
|
||||
<Set name="TrustStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
|
||||
<Set name="KeyStore">./eepsite/etc/keystore.ks</Set>
|
||||
<Set name="KeyStorePassword">changeit</Set>
|
||||
<Set name="KeyManagerPassword">myKeyPassword</Set>
|
||||
<Set name="TrustStore">./eepsite/etc/keystore.ks</Set>
|
||||
<Set name="TrustStorePassword">changeit</Set>
|
||||
</New>
|
||||
|
||||
<Call name="addConnector">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.server.ssl.SslSelectChannelConnector">
|
||||
<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="useDirectBuffers">false</Set>
|
||||
<Set name="Acceptors">2</Set>
|
||||
<Set name="AcceptQueueSize">100</Set>
|
||||
<Set name="acceptors">1</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>
|
||||
</Arg>
|
||||
</Call>
|
||||
|
@ -135,7 +135,6 @@
|
||||
<Set name="maxIdleTime">600000</Set>
|
||||
<Set name="Acceptors">1</Set>
|
||||
<Set name="statsOn">false</Set>
|
||||
<Set name="confidentialPort">8443</Set>
|
||||
<Set name="lowResourcesConnections">5000</Set>
|
||||
<Set name="lowResourcesMaxIdleTime">5000</Set>
|
||||
<Set name="useDirectBuffers">false</Set>
|
||||
@ -155,7 +154,6 @@
|
||||
<Set name="maxIdleTime">600000</Set>
|
||||
<Set name="Acceptors">1</Set>
|
||||
<Set name="statsOn">false</Set>
|
||||
<Set name="confidentialPort">8443</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
@ -167,19 +165,6 @@
|
||||
<!-- To enable this change clients.config args to be: -->
|
||||
<!-- -->
|
||||
<!-- 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. -->
|
||||
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
|
||||
|
||||
<!-- =========================================================== -->
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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,200 +1492,12 @@ 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() {
|
||||
File cfgUpdated = new File("wrapper.config.updated");
|
||||
|
215
router/java/src/net/i2p/router/tasks/InstallUpdate.java
Normal file
215
router/java/src/net/i2p/router/tasks/InstallUpdate.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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%
|
||||
*</pre>
|
||||
*/
|
||||
/*****
|
||||
@ -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<Long> longs = new java.util.HashSet<Long>(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)");
|
||||
}
|
||||
|
Reference in New Issue
Block a user