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>
</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>

View File

@ -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>

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 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,
CLIENT_SKIPHEADERS, getTunnel().getContext());
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) {
readLine(socket, command, HEADER_TIMEOUT);
try {
readLine(socket, command, HEADER_TIMEOUT);
} catch (LineTooLongException ltle) {
// convert for first line
throw new RequestTooLongException("Request too long - max " + MAX_LINE_LENGTH);
}
} else {
boolean ok = DataHelper.readLine(in, command);
if (!ok)
throw new IOException("EOF reached before the end of the headers [" + buf.toString() + "]");
throw new EOFException("EOF reached before the end of the headers");
}
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Read the http command [" + command.toString() + "]");
// FIXME we probably don't need or want this in the outgoing direction
int trimmed = 0;
if (command.length() > 0) {
for (int i = 0; i < command.length(); i++) {
if (command.charAt(i) == 0) {
command = command.deleteCharAt(i);
i--;
trimmed++;
}
}
}
if (trimmed > 0)
ctx.statManager().addRateData("i2ptunnel.httpNullWorkaround", trimmed);
int totalSize = command.length();
int i = 0;
while (true) {
if (++i > MAX_HEADERS)
throw new IOException("Too many header lines - max " + MAX_HEADERS);
if (++i > MAX_HEADERS) {
throw new LineTooLongException("Too many header lines - max " + MAX_HEADERS);
}
buf.setLength(0);
if (socket != null) {
readLine(socket, buf, expire - ctx.clock().now());
} else {
boolean ok = DataHelper.readLine(in, buf);
if (!ok)
throw new IOException("EOF reached before the end of the headers [" + buf.toString() + "]");
throw new BadRequestException("EOF reached before the end of the headers");
}
if ( (buf.length() == 0) ||
((buf.charAt(0) == '\n') || (buf.charAt(0) == '\r')) ) {
// end of headers reached
return headers;
} else {
if (ctx.clock().now() >= expire)
throw new IOException("Headers took too long [" + buf.toString() + "]");
if (ctx.clock().now() > expire) {
throw new SocketTimeoutException("Headers took too long");
}
int split = buf.indexOf(":");
if (split <= 0) throw new IOException("Invalid HTTP header, missing colon [" + buf.toString() + "]");
if (split <= 0)
throw new BadRequestException("Invalid HTTP header, missing colon");
totalSize += buf.length();
if (totalSize > MAX_TOTAL_HEADER_SIZE)
throw new LineTooLongException("Req+headers too big");
String name = buf.substring(0, split).trim();
String value = null;
if (buf.length() > split + 1)
@ -831,5 +960,23 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
super(s);
}
}
/**
* @since 0.9.20
*/
private static class RequestTooLongException extends IOException {
public RequestTooLongException(String s) {
super(s);
}
}
/**
* @since 0.9.20
*/
private static class BadRequestException extends IOException {
public BadRequestException(String s) {
super(s);
}
}
}

View File

@ -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>

View 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>

View 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,25 +925,20 @@ class Connection {
* options.getSendAckDelay() from now (1000 ms)
*/
public void setNextSendTime(long when) {
if (_nextSendTime >= 0) {
if (when < _nextSendTime)
_nextSendTime = when;
} else {
_nextSendTime = when;
}
synchronized(_nextSendLock) {
if (_nextSendTime >= 0) {
if (when < _nextSendTime)
_nextSendTime = when;
} else {
_nextSendTime = when;
}
if (_nextSendTime >= 0) {
long max = _context.clock().now() + _options.getSendAckDelay();
if (max < _nextSendTime)
_nextSendTime = max;
if (_nextSendTime >= 0) {
long max = _context.clock().now() + _options.getSendAckDelay();
if (max < _nextSendTime)
_nextSendTime = max;
}
}
//if (_log.shouldLog(Log.DEBUG) && false) {
// if (_nextSendTime <= 0)
// _log.debug("set next send time to an unknown time", new Exception(toString()));
// else
// _log.debug("set next send time to " + (_nextSendTime-_context.clock().now()) + "ms from now", new Exception(toString()));
//}
}
/** how many packets have we sent and the other side has ACKed?
@ -1260,17 +1263,17 @@ class Connection {
*/
class ResendPacketEvent extends SimpleTimer2.TimedEvent {
private final PacketLocal _packet;
private long _nextSendTime;
private long _nextSend;
public ResendPacketEvent(PacketLocal packet, long delay) {
super(_timer);
_packet = packet;
_nextSendTime = delay + _context.clock().now();
_nextSend = delay + _context.clock().now();
packet.setResendPacketEvent(ResendPacketEvent.this);
schedule(delay);
}
public long getNextSendTime() { return _nextSendTime; }
public long getNextSendTime() { return _nextSend; }
public void timeReached() { retransmit(); }
/**
* Retransmit the packet if we need to.
@ -1320,7 +1323,7 @@ class Connection {
+ _activeResends + " active resend, "
+ _outboundPackets.size() + " unacked, window size = " + _options.getWindowSize());
forceReschedule(1333);
_nextSendTime = 1333 + _context.clock().now();
_nextSend = 1333 + _context.clock().now();
return false;
}
@ -1407,7 +1410,7 @@ class Connection {
if ( (timeout > MAX_RESEND_DELAY) || (timeout <= 0) )
timeout = MAX_RESEND_DELAY;
// set this before enqueue() as it passes it on to the router
_nextSendTime = timeout + _context.clock().now();
_nextSend = timeout + _context.clock().now();
if (_outboundQueue.enqueue(_packet)) {
// first resend for this packet ?

View File

@ -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>

View File

@ -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) {

View File

@ -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>

View File

@ -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>
@ -168,19 +166,6 @@
<!-- -->
<!-- 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 -->

View File

@ -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

View File

@ -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));
}
/**

View File

@ -8,15 +8,10 @@ package net.i2p.router;
*
*/
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@ -49,7 +44,6 @@ import net.i2p.router.util.EventLog;
import net.i2p.stat.RateStat;
import net.i2p.stat.StatManager;
import net.i2p.util.ByteCache;
import net.i2p.util.FileUtil;
import net.i2p.util.FortunaRandomSource;
import net.i2p.util.I2PAppThread;
import net.i2p.util.I2PThread;
@ -113,6 +107,7 @@ public class Router implements RouterClock.ClockShiftListener {
private final static String DNS_CACHE_TIME = "" + (5*60);
private static final String EVENTLOG = "eventlog.txt";
private static final String PROP_JBIGI = "jbigi.loadedResource";
public static final String UPDATE_FILE = "i2pupdate.zip";
private static final String originalTimeZoneID;
static {
@ -1497,199 +1492,11 @@ public class Router implements RouterClock.ClockShiftListener {
// If it does an update, it never returns.
// I guess it's better to have the other-router check above this, we don't want to
// overwrite an existing running router's jar files. Other than ours.
r.installUpdates();
InstallUpdate.installUpdates(r);
// ********* Start no threads before here ********* //
r.runRouter();
}
}
public static final String UPDATE_FILE = "i2pupdate.zip";
private static final String DELETE_FILE = "deletelist.txt";
/**
* Context must be available.
* Unzip update file found in the router dir OR base dir, to the base dir
*
* If successful, will call exit() and never return.
*
* If we can't write to the base dir, complain.
* Note: _log not available here.
*/
private void installUpdates() {
File updateFile = new File(_context.getRouterDir(), UPDATE_FILE);
boolean exists = updateFile.exists();
if (!exists) {
updateFile = new File(_context.getBaseDir(), UPDATE_FILE);
exists = updateFile.exists();
}
if (exists) {
// do a simple permissions test, if it fails leave the file in place and don't restart
File test = new File(_context.getBaseDir(), "history.txt");
if ((test.exists() && !test.canWrite()) || (!_context.getBaseDir().canWrite())) {
System.out.println("ERROR: No write permissions on " + _context.getBaseDir() +
" to extract software update file");
// carry on
return;
}
System.out.println("INFO: Update file exists [" + UPDATE_FILE + "] - installing");
// verify the whole thing first
// we could remember this fails, and not bother restarting, but who cares...
boolean ok = FileUtil.verifyZip(updateFile);
if (ok) {
// This may be useful someday. First added in 0.8.2
// Moved above the extract so we don't NCDFE
_config.put("router.updateLastInstalled", "" + System.currentTimeMillis());
// Set the last version to the current version, since 0.8.13
_config.put("router.previousVersion", RouterVersion.VERSION);
_config.put("router.previousFullVersion", RouterVersion.FULL_VERSION);
saveConfig();
ok = FileUtil.extractZip(updateFile, _context.getBaseDir());
}
// Very important - we have now trashed our jars.
// After this point, do not use any new I2P classes, or they will fail to load
// and we will die with NCDFE.
// Ideally, do not use I2P classes at all, new or not.
try {
if (ok) {
// We do this here so we may delete old jars before we restart
deleteListedFiles();
System.out.println("INFO: Update installed");
} else {
System.out.println("ERROR: Update failed!");
}
if (!ok) {
// we can't leave the file in place or we'll continually restart, so rename it
File bad = new File(_context.getRouterDir(), "BAD-" + UPDATE_FILE);
boolean renamed = updateFile.renameTo(bad);
if (renamed) {
System.out.println("Moved update file to " + bad.getAbsolutePath());
} else {
System.out.println("Deleting file " + updateFile.getAbsolutePath());
ok = true; // so it will be deleted
}
}
if (ok) {
boolean deleted = updateFile.delete();
if (!deleted) {
System.out.println("ERROR: Unable to delete the update file!");
updateFile.deleteOnExit();
}
}
// exit whether ok or not
if (_context.hasWrapper())
System.out.println("INFO: Restarting after update");
else
System.out.println("WARNING: Exiting after update, restart I2P");
} catch (Throwable t) {
// hide the NCDFE
// hopefully the update file got deleted or we will loop
}
System.exit(EXIT_HARD_RESTART);
} else {
deleteJbigiFiles();
// It was here starting in 0.8.12 so it could be used the very first time
// Now moved up so it is usually run only after an update
// But the first time before jetty 6 it will run here...
// Here we can't remove jars
deleteListedFiles();
}
}
/**
* Remove extracted libjbigi.so and libjcpuid.so files if we have a newer jbigi.jar,
* so the new ones will be extracted.
* We do this after the restart, not after the extract, because it's safer, and
* because people may upgrade their jbigi.jar file manually.
*
* Copied from NativeBigInteger, which we can't access here or the
* libs will get loaded.
*/
private void deleteJbigiFiles() {
boolean isX86 = SystemVersion.isX86();
String osName = System.getProperty("os.name").toLowerCase(Locale.US);
boolean isWin = SystemVersion.isWindows();
boolean isMac = SystemVersion.isMac();
// only do this on these OSes
boolean goodOS = isWin || isMac ||
osName.contains("linux") || osName.contains("freebsd");
// only do this on these x86
File jbigiJar = new File(_context.getBaseDir(), "lib/jbigi.jar");
if (isX86 && goodOS && jbigiJar.exists()) {
String libPrefix = (isWin ? "" : "lib");
String libSuffix = (isWin ? ".dll" : isMac ? ".jnilib" : ".so");
File jcpuidLib = new File(_context.getBaseDir(), libPrefix + "jcpuid" + libSuffix);
if (jcpuidLib.canWrite() && jbigiJar.lastModified() > jcpuidLib.lastModified()) {
String path = jcpuidLib.getAbsolutePath();
boolean success = FileUtil.copy(path, path + ".bak", true, true);
if (success) {
boolean success2 = jcpuidLib.delete();
if (success2) {
System.out.println("New jbigi.jar detected, moved jcpuid library to " +
path + ".bak");
System.out.println("Check logs for successful installation of new library");
}
}
}
File jbigiLib = new File(_context.getBaseDir(), libPrefix + "jbigi" + libSuffix);
if (jbigiLib.canWrite() && jbigiJar.lastModified() > jbigiLib.lastModified()) {
String path = jbigiLib.getAbsolutePath();
boolean success = FileUtil.copy(path, path + ".bak", true, true);
if (success) {
boolean success2 = jbigiLib.delete();
if (success2) {
System.out.println("New jbigi.jar detected, moved jbigi library to " +
path + ".bak");
System.out.println("Check logs for successful installation of new library");
}
}
}
}
}
/**
* Delete all files listed in the delete file.
* Format: One file name per line, comment lines start with '#'.
* All file names must be relative to $I2P, absolute file names not allowed.
* We probably can't remove old jars this way.
* Fails silently.
* Use no new I2P classes here so it may be called after zip extraction.
* @since 0.8.12
*/
private void deleteListedFiles() {
File deleteFile = new File(_context.getBaseDir(), DELETE_FILE);
if (!deleteFile.exists())
return;
// this is similar to FileUtil.readTextFile() but we can't use any I2P classes here
FileInputStream fis = null;
BufferedReader in = null;
try {
fis = new FileInputStream(deleteFile);
in = new BufferedReader(new InputStreamReader(fis, "UTF-8"));
String line;
while ( (line = in.readLine()) != null) {
String fl = line.trim();
if (fl.contains("..") || fl.startsWith("#") || fl.length() == 0)
continue;
File df = new File(fl);
if (df.isAbsolute())
continue;
df = new File(_context.getBaseDir(), fl);
if (df.exists() && !df.isDirectory()) {
if (df.delete())
System.out.println("INFO: File [" + fl + "] deleted");
}
}
} catch (IOException ioe) {}
finally {
if (in != null) try { in.close(); } catch(IOException ioe) {}
if (deleteFile.delete())
System.out.println("INFO: File [" + DELETE_FILE + "] deleted");
}
}
/*******
private static void verifyWrapperConfig() {

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
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>

View File

@ -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.

View File

@ -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);
}
}

View File

@ -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)");
}