2006-02-15 jrandom

* Merged in the i2p_0_6_1_10_PRE branch to the trunk, so CVS HEAD is no
      longer backwards compatible (and should not be used until 0.6.1.1 is
      out)
This commit is contained in:
jrandom
2006-02-15 05:33:17 +00:00
committed by zzz
parent 1374ea0ea1
commit 113fbc1df3
127 changed files with 2687 additions and 1309 deletions
@@ -130,7 +130,7 @@ public class I2PSnarkUtil {
_log.debug("Fetching [" + url + "] proxy=" + _proxyHost + ":" + _proxyPort + ": " + _shouldProxy);
File out = null;
try {
out = File.createTempFile("i2psnark", "url");
out = File.createTempFile("i2psnark", "url", new File("."));
} catch (IOException ioe) {
ioe.printStackTrace();
out.delete();
@@ -138,31 +138,35 @@ class HTTPResponseOutputStream extends FilterOutputStream {
if (lastEnd == -1) {
responseLine = new String(_headerBuffer.getData(), 0, i+1); // includes NL
responseLine = filterResponseLine(responseLine);
responseLine = (responseLine.trim() + "\n");
responseLine = (responseLine.trim() + "\r\n");
out.write(responseLine.getBytes());
} else {
for (int j = lastEnd+1; j < i; j++) {
if (_headerBuffer.getData()[j] == ':') {
int keyLen = j-(lastEnd+1);
int valLen = i-(j+2);
if ( (keyLen <= 0) || (valLen <= 0) )
int valLen = i-(j+1);
if ( (keyLen <= 0) || (valLen < 0) )
throw new IOException("Invalid header @ " + j);
String key = new String(_headerBuffer.getData(), lastEnd+1, keyLen);
String val = new String(_headerBuffer.getData(), j+2, valLen).trim();
String val = null;
if (valLen == 0)
val = "";
else
val = new String(_headerBuffer.getData(), j+2, valLen).trim();
if (_log.shouldLog(Log.INFO))
_log.info("Response header [" + key + "] = [" + val + "]");
if ("Connection".equalsIgnoreCase(key)) {
out.write("Connection: close\n".getBytes());
out.write("Connection: close\r\n".getBytes());
connectionSent = true;
} else if ("Proxy-Connection".equalsIgnoreCase(key)) {
out.write("Proxy-Connection: close\n".getBytes());
out.write("Proxy-Connection: close\r\n".getBytes());
proxyConnectionSent = true;
} else if ( ("Content-encoding".equalsIgnoreCase(key)) && ("x-i2p-gzip".equalsIgnoreCase(val)) ) {
_gzip = true;
} else {
out.write((key.trim() + ": " + val.trim() + "\n").getBytes());
out.write((key.trim() + ": " + val.trim() + "\r\n").getBytes());
}
break;
}
@@ -173,9 +177,9 @@ class HTTPResponseOutputStream extends FilterOutputStream {
}
if (!connectionSent)
out.write("Connection: close\n".getBytes());
out.write("Connection: close\r\n".getBytes());
if (!proxyConnectionSent)
out.write("Proxy-Connection: close\n".getBytes());
out.write("Proxy-Connection: close\r\n".getBytes());
finishHeaders();
@@ -196,7 +200,7 @@ class HTTPResponseOutputStream extends FilterOutputStream {
protected boolean shouldCompress() { return _gzip; }
protected void finishHeaders() throws IOException {
out.write("\n".getBytes()); // end of the headers
out.write("\r\n".getBytes()); // end of the headers
}
public void close() throws IOException {
@@ -349,6 +353,10 @@ class HTTPResponseOutputStream extends FilterOutputStream {
"Content-length: 32\n" +
"\n" +
"hi ho, this is the body";
String blankval = "HTTP/1.0 200 OK\n" +
"A:\n" +
"\n";
/* */
test("Simple", simple, true);
test("Filtered", filtered, true);
@@ -356,6 +364,7 @@ class HTTPResponseOutputStream extends FilterOutputStream {
test("Minimal", minimal, true);
test("Windows", winmin, true);
test("Large", large, true);
test("Blank whitespace", blankval, true);
test("Invalid (short headers)", invalid1, true);
test("Invalid (no headers)", invalid2, true);
test("Invalid (windows with short headers)", invalid3, true);
@@ -62,7 +62,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
private Object conLock = new Object();
/** List of Socket for those accept()ed but not yet started up */
private List _waitingSockets;
private List _waitingSockets = new ArrayList();
/** How many connections will we allow to be in the process of being built at once? */
private int _numConnectionBuilders;
/** How long will we allow sockets to sit in the _waitingSockets map before killing them? */
@@ -224,6 +224,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
if (_log.shouldLog(Log.DEBUG))
_log.debug(getPrefix(requestId) + "Method is null for [" + line + "]");
line = line.trim();
int pos = line.indexOf(" ");
if (pos == -1) break;
method = line.substring(0, pos);
@@ -467,7 +468,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
newRequest.append("Connection: close\r\n\r\n");
break;
} else {
newRequest.append(line).append("\r\n"); // HTTP spec
newRequest.append(line.trim()).append("\r\n"); // HTTP spec
}
}
if (_log.shouldLog(Log.DEBUG))
@@ -589,7 +590,11 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
out.write(errMessage);
if (targetRequest != null) {
int protopos = targetRequest.indexOf(" ");
String uri = targetRequest.substring(0, protopos);
String uri = null;
if (protopos >= 0)
uri = targetRequest.substring(0, protopos);
else
uri = targetRequest;
out.write("<a href=\"http://".getBytes());
out.write(uri.getBytes());
out.write("\">http://".getBytes());
@@ -224,7 +224,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
protected void finishHeaders() throws IOException {
if (_log.shouldLog(Log.INFO))
_log.info("Including x-i2p-gzip as the content encoding in the response");
out.write("Content-encoding: x-i2p-gzip\n".getBytes());
out.write("Content-encoding: x-i2p-gzip\r\n".getBytes());
super.finishHeaders();
}
@@ -274,13 +274,13 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
private String formatHeaders(Properties headers, StringBuffer command) {
StringBuffer buf = new StringBuffer(command.length() + headers.size() * 64);
buf.append(command.toString()).append('\n');
buf.append(command.toString().trim()).append("\r\n");
for (Iterator iter = headers.keySet().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
String val = headers.getProperty(name);
buf.append(name).append(": ").append(val).append('\n');
buf.append(name.trim()).append(": ").append(val.trim()).append("\r\n");
}
buf.append('\n');
buf.append("\r\n");
return buf.toString();
}
@@ -316,10 +316,14 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
// end of headers reached
return headers;
} else {
int split = buf.indexOf(": ");
int split = buf.indexOf(":");
if (split <= 0) throw new IOException("Invalid HTTP header, missing colon [" + buf.toString() + "]");
String name = buf.substring(0, split);
String value = buf.substring(split+2); // ": "
String name = buf.substring(0, split).trim();
String value = null;
if (buf.length() > split + 1)
value = buf.substring(split+1).trim(); // ":"
else
value = "";
if ("Accept-encoding".equalsIgnoreCase(name))
name = "Accept-encoding";
else if ("X-Accept-encoding".equalsIgnoreCase(name))
@@ -47,13 +47,14 @@ public class ConfigNetHandler extends FormHandler {
private String _outboundBurst;
private String _reseedFrom;
private String _sharePct;
private boolean _ratesOnly;
protected void processForm() {
if (_guessRequested) {
guessHostname();
} else if (_reseedRequested) {
reseed();
} else if (_saveRequested) {
} else if (_saveRequested || ( (_action != null) && ("Save changes".equals(_action)) )) {
saveChanges();
} else if (_recheckReachabilityRequested) {
recheckReachability();
@@ -70,6 +71,7 @@ public class ConfigNetHandler extends FormHandler {
public void setRequireIntroductions(String moo) { _requireIntroductions = true; }
public void setHiddenMode(String moo) { _hiddenMode = true; }
public void setDynamicKeys(String moo) { _dynamicKeys = true; }
public void setUpdateratesonly(String moo) { _ratesOnly = true; }
public void setHostname(String hostname) {
_hostname = (hostname != null ? hostname.trim() : null);
@@ -231,88 +233,95 @@ public class ConfigNetHandler extends FormHandler {
private void saveChanges() {
boolean restartRequired = false;
if ( (_hostname != null) && (_hostname.length() > 0) ) {
String oldHost = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_HOSTNAME);
if ( (oldHost == null) || (!oldHost.equalsIgnoreCase(_hostname)) ) {
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_HOSTNAME, _hostname);
addFormNotice("Updating hostname from " + oldHost + " to " + _hostname);
restartRequired = true;
if (!_ratesOnly) {
if ( (_hostname != null) && (_hostname.length() > 0) ) {
String oldHost = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_HOSTNAME);
if ( (oldHost == null) || (!oldHost.equalsIgnoreCase(_hostname)) ) {
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_HOSTNAME, _hostname);
addFormNotice("Updating hostname from " + oldHost + " to " + _hostname);
restartRequired = true;
}
}
}
if ( (_tcpPort != null) && (_tcpPort.length() > 0) ) {
String oldPort = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT);
if ( (oldPort == null) && (_tcpPort.equals("8887")) ) {
// still on default.. noop
} else if ( (oldPort == null) || (!oldPort.equalsIgnoreCase(_tcpPort)) ) {
// its not the default OR it has changed
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT, _tcpPort);
addFormNotice("Updating TCP port from " + oldPort + " to " + _tcpPort);
restartRequired = true;
if ( (_tcpPort != null) && (_tcpPort.length() > 0) ) {
String oldPort = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT);
if ( (oldPort == null) && (_tcpPort.equals("8887")) ) {
// still on default.. noop
} else if ( (oldPort == null) || (!oldPort.equalsIgnoreCase(_tcpPort)) ) {
// its not the default OR it has changed
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT, _tcpPort);
addFormNotice("Updating TCP port from " + oldPort + " to " + _tcpPort);
restartRequired = true;
}
}
}
if ( (_udpPort != null) && (_udpPort.length() > 0) ) {
String oldPort = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_UDP_PORT);
if ( (oldPort == null) && (_udpPort.equals("8887")) ) {
// still on default.. noop
} else if ( (oldPort == null) || (!oldPort.equalsIgnoreCase(_udpPort)) ) {
// its not the default OR it has changed
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT, _udpPort);
addFormNotice("Updating UDP port from " + oldPort + " to " + _udpPort);
restartRequired = true;
if ( (_udpPort != null) && (_udpPort.length() > 0) ) {
String oldPort = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_UDP_PORT);
if ( (oldPort == null) && (_udpPort.equals("8887")) ) {
// still on default.. noop
} else if ( (oldPort == null) || (!oldPort.equalsIgnoreCase(_udpPort)) ) {
// its not the default OR it has changed
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT, _udpPort);
addFormNotice("Updating UDP port from " + oldPort + " to " + _udpPort);
restartRequired = true;
}
}
}
updateRates();
if (_sharePct != null) {
String old = _context.router().getConfigSetting(ConfigNetHelper.PROP_SHARE_PERCENTAGE);
if ( (old == null) || (!old.equalsIgnoreCase(_sharePct)) ) {
_context.router().setConfigSetting(ConfigNetHelper.PROP_SHARE_PERCENTAGE, _sharePct);
addFormNotice("Updating bandwidth share percentage");
if (!_ratesOnly) {
if (_sharePct != null) {
String old = _context.router().getConfigSetting(ConfigNetHelper.PROP_SHARE_PERCENTAGE);
if ( (old == null) || (!old.equalsIgnoreCase(_sharePct)) ) {
_context.router().setConfigSetting(ConfigNetHelper.PROP_SHARE_PERCENTAGE, _sharePct);
addFormNotice("Updating bandwidth share percentage");
}
}
}
// If hidden mode value changes, restart is required
if (_hiddenMode && "false".equalsIgnoreCase(_context.getProperty(Router.PROP_HIDDEN, "false"))) {
_context.router().setConfigSetting(Router.PROP_HIDDEN, "true");
_context.router().getRouterInfo().addCapability(RouterInfo.CAPABILITY_HIDDEN);
addFormNotice("Gracefully restarting into Hidden Router Mode. Make sure you have no 0-1 length "
+ "<a href=\"configtunnels.jsp\">tunnels!</a>");
hiddenSwitch();
}
// If hidden mode value changes, restart is required
if (_hiddenMode && "false".equalsIgnoreCase(_context.getProperty(Router.PROP_HIDDEN, "false"))) {
_context.router().setConfigSetting(Router.PROP_HIDDEN, "true");
_context.router().getRouterInfo().addCapability(RouterInfo.CAPABILITY_HIDDEN);
addFormNotice("Gracefully restarting into Hidden Router Mode. Make sure you have no 0-1 length "
+ "<a href=\"configtunnels.jsp\">tunnels!</a>");
hiddenSwitch();
}
if (!_hiddenMode && "true".equalsIgnoreCase(_context.getProperty(Router.PROP_HIDDEN, "false"))) {
_context.router().removeConfigSetting(Router.PROP_HIDDEN);
_context.router().getRouterInfo().delCapability(RouterInfo.CAPABILITY_HIDDEN);
addFormNotice("Gracefully restarting to exit Hidden Router Mode");
hiddenSwitch();
}
if (!_hiddenMode && "true".equalsIgnoreCase(_context.getProperty(Router.PROP_HIDDEN, "false"))) {
_context.router().removeConfigSetting(Router.PROP_HIDDEN);
_context.router().getRouterInfo().delCapability(RouterInfo.CAPABILITY_HIDDEN);
addFormNotice("Gracefully restarting to exit Hidden Router Mode");
hiddenSwitch();
}
if (_dynamicKeys) {
_context.router().setConfigSetting(Router.PROP_DYNAMIC_KEYS, "true");
} else {
_context.router().removeConfigSetting(Router.PROP_DYNAMIC_KEYS);
}
if (_requireIntroductions) {
_context.router().setConfigSetting(UDPTransport.PROP_FORCE_INTRODUCERS, "true");
addFormNotice("Requiring SSU introduers");
} else {
_context.router().removeConfigSetting(UDPTransport.PROP_FORCE_INTRODUCERS);
}
if (true || _timeSyncEnabled) {
// Time sync enable, means NOT disabled
_context.router().setConfigSetting(Timestamper.PROP_DISABLED, "false");
} else {
_context.router().setConfigSetting(Timestamper.PROP_DISABLED, "true");
if (_dynamicKeys) {
_context.router().setConfigSetting(Router.PROP_DYNAMIC_KEYS, "true");
} else {
_context.router().removeConfigSetting(Router.PROP_DYNAMIC_KEYS);
}
if (_requireIntroductions) {
_context.router().setConfigSetting(UDPTransport.PROP_FORCE_INTRODUCERS, "true");
addFormNotice("Requiring SSU introduers");
} else {
_context.router().removeConfigSetting(UDPTransport.PROP_FORCE_INTRODUCERS);
}
if (true || _timeSyncEnabled) {
// Time sync enable, means NOT disabled
_context.router().setConfigSetting(Timestamper.PROP_DISABLED, "false");
} else {
_context.router().setConfigSetting(Timestamper.PROP_DISABLED, "true");
}
}
boolean saved = _context.router().saveConfig();
if (saved)
addFormNotice("Configuration saved successfully");
else
addFormNotice("Error saving the configuration (applied but not saved) - please see the error logs");
if ( (_action != null) && ("Save changes".equals(_action)) ) {
if (saved)
addFormNotice("Configuration saved successfully");
else
addFormNotice("Error saving the configuration (applied but not saved) - please see the error logs");
}
if (restartRequired) {
addFormNotice("Performing a soft restart");
@@ -382,7 +391,7 @@ public class ConfigNetHandler extends FormHandler {
}
}
if (updated)
if (updated && !_ratesOnly)
addFormNotice("Updated bandwidth limits");
}
}
@@ -137,7 +137,7 @@ public class ConfigNetHelper {
return "32";
}
public String getInboundBurstFactorBox() {
String rate = _context.getProperty(PROP_INBOUND_KBPS);
String rate = _context.getProperty(PROP_INBOUND_BURST_KBPS);
String burst = _context.getProperty(PROP_INBOUND_BURST);
int numSeconds = 1;
if ( (burst != null) && (rate != null) ) {
@@ -157,7 +157,7 @@ public class ConfigNetHelper {
}
public String getOutboundBurstFactorBox() {
String rate = _context.getProperty(PROP_OUTBOUND_KBPS);
String rate = _context.getProperty(PROP_OUTBOUND_BURST_KBPS);
String burst = _context.getProperty(PROP_OUTBOUND_BURST);
int numSeconds = 1;
if ( (burst != null) && (rate != null) ) {
@@ -121,7 +121,7 @@ public class ConfigServiceHandler extends FormHandler {
browseOnStartup(false);
addFormNotice("Console is not to be shown on startup");
} else {
addFormNotice("Blah blah blah. whatever. I'm not going to " + _action);
//addFormNotice("Blah blah blah. whatever. I'm not going to " + _action);
}
}
@@ -118,6 +118,12 @@ public class FormHandler {
_valid = false;
return;
}
String sharedNonce = System.getProperty("router.consoleNonce");
if ( (sharedNonce != null) && (sharedNonce.equals(_nonce) ) ) {
return;
}
String nonce = System.getProperty(getClass().getName() + ".nonce");
String noncePrev = System.getProperty(getClass().getName() + ".noncePrev");
if ( ( (nonce == null) || (!_nonce.equals(nonce)) ) &&
@@ -58,7 +58,7 @@ public class ReseedHandler {
}
}
private static final String DEFAULT_SEED_URL = "http://dev.i2p.net/i2pdb/";
private static final String DEFAULT_SEED_URL = "http://dev.i2p.net/i2pdb2/";
/**
* Reseed has been requested, so lets go ahead and do it. Fetch all of
* the routerInfo-*.dat files from the specified URL (or the default) and
@@ -82,7 +82,7 @@ public class SummaryHelper {
}
if (!_context.clock().getUpdatedSuccessfully())
return now + " (nknown skew)";
return now + " (Unknown skew)";
long ms = _context.clock().getOffset();
+1 -1
View File
@@ -43,7 +43,7 @@ Update through the eepProxy?
<!-- prompt for the eepproxy -->
Trusted keys:
<textarea name="trustedKeys" disabled="true" cols="60" rows="2"><jsp:getProperty name="updatehelper" property="trustedKeys" /></textarea>
<input type="submit" value="Save" />
<input type="submit" name="action" value="Save" />
</form>
</div>
+44
View File
@@ -6,6 +6,11 @@
<title>I2P Router Console - home</title>
<link rel="stylesheet" href="default.css" type="text/css" />
</head><body>
<%
if (System.getProperty("router.consoleNonce") == null) {
System.setProperty("router.consoleNonce", new java.util.Random().nextLong() + "");
}
%>
<%@include file="nav.jsp" %>
<%@include file="summary.jsp" %>
@@ -18,6 +23,45 @@
</div>
<div class="main" id="main">
<jsp:useBean class="net.i2p.router.web.ConfigServiceHandler" id="servicehandler" scope="request" />
<jsp:setProperty name="servicehandler" property="*" />
<jsp:setProperty name="servicehandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<font color="red"><jsp:getProperty name="servicehandler" property="errors" /></font>
<i><jsp:getProperty name="servicehandler" property="notices" /></i>
<jsp:useBean class="net.i2p.router.web.ConfigNetHandler" id="nethandler" scope="request" />
<jsp:setProperty name="nethandler" property="*" />
<jsp:setProperty name="nethandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<font color="red"><jsp:getProperty name="nethandler" property="errors" /></font>
<i><jsp:getProperty name="nethandler" property="notices" /></i>
<jsp:useBean class="net.i2p.router.web.ConfigNetHelper" id="nethelper" scope="request" />
<jsp:setProperty name="nethelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<form action="index.jsp" method="POST">
<input type="hidden" name="nonce" value="<%=System.getProperty("router.consoleNonce")%>" />
<input type="hidden" name="updateratesonly" value="true" />
<input type="hidden" name="save" value="Save changes" />
Inbound bandwidth:
<input name="inboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="inboundRate" />" /> KBps
bursting up to
<input name="inboundburstrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="inboundBurstRate" />" /> KBps for
<jsp:getProperty name="nethelper" property="inboundBurstFactorBox" /><br />
Outbound bandwidth:
<input name="outboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="outboundRate" />" /> KBps
bursting up to
<input name="outboundburstrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="outboundBurstRate" />" /> KBps for
<jsp:getProperty name="nethelper" property="outboundBurstFactorBox" /><br />
<i>KBps = kilobytes per second = 1024 bytes per second.</i>
<input type="submit" value="Save changes" name="action" />
<hr />
<input type="submit" name="action" value="Graceful restart" />
<input type="submit" name="action" value="Shutdown gracefully" />
<a href="configservice.jsp">Other shutdown/restart options</a>
<hr />
</form>
<jsp:useBean class="net.i2p.router.web.ContentHelper" id="contenthelper" scope="request" />
<jsp:setProperty name="contenthelper" property="page" value="docs/readme.html" />
<jsp:setProperty name="contenthelper" property="maxLines" value="300" />
@@ -123,6 +123,7 @@ public class Connection {
_context.statManager().createRateStat("stream.con.windowSizeAtCongestion", "How large was our send window when we send a dup?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("stream.chokeSizeBegin", "How many messages were outstanding when we started to choke?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("stream.chokeSizeEnd", "How many messages were outstanding when we stopped being choked?", "Stream", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("stream.fastRetransmit", "How long a packet has been around for if it has been resent per the fast retransmit timer?", "Stream", new long[] { 60*1000, 10*60*1000 });
if (_log.shouldLog(Log.INFO))
_log.info("New connection created with options: " + _options);
}
@@ -377,6 +378,8 @@ public class Connection {
for (int i = 0; i < nacks.length; i++) {
if (nacks[i] == id.longValue()) {
nacked = true;
PacketLocal nackedPacket = (PacketLocal)_outboundPackets.get(id);
nackedPacket.incrementNACKs();
break; // NACKed
}
}
@@ -929,6 +932,13 @@ public class Connection {
public String toString() { return "event on " + Connection.this.toString(); }
}
/**
* If we have been explicitly NACKed three times, retransmit the packet even if
* there are other packets in flight.
*
*/
static final int FAST_RETRANSMIT_THRESHOLD = 3;
/**
* Coordinate the resends of a given packet
*/
@@ -969,8 +979,9 @@ public class Connection {
if (_outboundPackets.containsKey(new Long(_packet.getSequenceNum())))
resend = true;
}
if ( (resend) && (_packet.getAckTime() < 0) ) {
if (!isLowest) {
if ( (resend) && (_packet.getAckTime() <= 0) ) {
boolean fastRetransmit = ( (_packet.getNACKs() >= FAST_RETRANSMIT_THRESHOLD) && (_packet.getNumSends() == 1));
if ( (!isLowest) && (!fastRetransmit) ) {
// we want to resend this packet, but there are already active
// resends in the air and we dont want to make a bad situation
// worse. wait another second
@@ -981,6 +992,10 @@ public class Connection {
_nextSendTime = 1000 + _context.clock().now();
return false;
}
if (fastRetransmit)
_context.statManager().addRateData("stream.fastRetransmit", _packet.getLifetime(), _packet.getLifetime());
// revamp various fields, in case we need to ack more, etc
_inputStream.updateAcks(_packet);
int choke = getOptions().getChoke();
@@ -54,10 +54,10 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
public static final String PROP_SLOW_START_GROWTH_RATE_FACTOR = "i2p.streaming.slowStartGrowthRateFactor";
private static final int TREND_COUNT = 3;
static final int INITIAL_WINDOW_SIZE = 6;
static final int INITIAL_WINDOW_SIZE = 12;
static final int DEFAULT_MAX_SENDS = 8;
static final int MIN_WINDOW_SIZE = 6;
static final int MIN_WINDOW_SIZE = INITIAL_WINDOW_SIZE;
public ConnectionOptions() {
super();
@@ -105,7 +105,7 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
setRTT(getInt(opts, PROP_INITIAL_RTT, 10*1000));
setReceiveWindow(getInt(opts, PROP_INITIAL_RECEIVE_WINDOW, 1));
setResendDelay(getInt(opts, PROP_INITIAL_RESEND_DELAY, 1000));
setSendAckDelay(getInt(opts, PROP_INITIAL_ACK_DELAY, 500));
setSendAckDelay(getInt(opts, PROP_INITIAL_ACK_DELAY, 2000));
setWindowSize(getInt(opts, PROP_INITIAL_WINDOW_SIZE, INITIAL_WINDOW_SIZE));
setMaxResends(getInt(opts, PROP_MAX_RESENDS, DEFAULT_MAX_SENDS));
setWriteTimeout(getInt(opts, PROP_WRITE_TIMEOUT, -1));
@@ -136,7 +136,7 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
if (opts.containsKey(PROP_INITIAL_RESEND_DELAY))
setResendDelay(getInt(opts, PROP_INITIAL_RESEND_DELAY, 1000));
if (opts.containsKey(PROP_INITIAL_ACK_DELAY))
setSendAckDelay(getInt(opts, PROP_INITIAL_ACK_DELAY, 500));
setSendAckDelay(getInt(opts, PROP_INITIAL_ACK_DELAY, 2000));
if (opts.containsKey(PROP_INITIAL_WINDOW_SIZE))
setWindowSize(getInt(opts, PROP_INITIAL_WINDOW_SIZE, INITIAL_WINDOW_SIZE));
if (opts.containsKey(PROP_MAX_RESENDS))
@@ -33,7 +33,8 @@ public class ConnectionPacketHandler {
void receivePacket(Packet packet, Connection con) throws I2PException {
boolean ok = verifyPacket(packet, con);
if (!ok) {
if ( (!packet.isFlagSet(Packet.FLAG_RESET)) && (_log.shouldLog(Log.ERROR)) )
boolean isTooFast = con.getSendStreamId() <= 0;
if ( (!packet.isFlagSet(Packet.FLAG_RESET)) && (!isTooFast) && (_log.shouldLog(Log.ERROR)) )
_log.error("Packet does NOT verify: " + packet + " on " + con);
packet.releasePayload();
return;
@@ -45,6 +46,7 @@ public class ConnectionPacketHandler {
if (_log.shouldLog(Log.WARN))
_log.warn("Received a data packet after hard disconnect: " + packet + " on " + con);
con.sendReset();
con.disconnect(false);
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("Received a packet after hard disconnect, ignoring: " + packet + " on " + con);
@@ -59,6 +61,7 @@ public class ConnectionPacketHandler {
_log.warn("Received new data when we've sent them data and all of our data is acked: "
+ packet + " on " + con + "");
con.sendReset();
con.disconnect(false);
packet.releasePayload();
return;
}
@@ -365,8 +368,8 @@ public class ConnectionPacketHandler {
if (packet.getSequenceNum() < MAX_INITIAL_PACKETS) {
return true;
} else {
if (_log.shouldLog(Log.ERROR))
_log.error("Packet without RST or SYN where we dont know stream ID: "
if (_log.shouldLog(Log.WARN))
_log.warn("Packet without RST or SYN where we dont know stream ID: "
+ packet);
return false;
}
@@ -111,7 +111,7 @@ public class PacketHandler {
private static final SimpleDateFormat _fmt = new SimpleDateFormat("HH:mm:ss.SSS");
void displayPacket(Packet packet, String prefix, String suffix) {
if (!_log.shouldLog(Log.DEBUG)) return;
if (!_log.shouldLog(Log.INFO)) return;
StringBuffer buf = new StringBuffer(256);
synchronized (_fmt) {
buf.append(_fmt.format(new Date()));
@@ -120,7 +120,10 @@ public class PacketHandler {
buf.append(packet.toString());
if (suffix != null)
buf.append(" ").append(suffix);
System.out.println(buf.toString());
String str = buf.toString();
System.out.println(str);
if (_log.shouldLog(Log.DEBUG))
_log.debug(str);
}
private void receiveKnownCon(Connection con, Packet packet) {
@@ -162,7 +165,7 @@ public class PacketHandler {
} else {
if ( (con.getSendStreamId() <= 0) ||
(DataHelper.eq(con.getSendStreamId(), packet.getReceiveStreamId())) ||
(packet.getSequenceNum() <= 5) ) { // its in flight from the first batch
(packet.getSequenceNum() <= ConnectionOptions.MIN_WINDOW_SIZE) ) { // its in flight from the first batch
long oldId = con.getSendStreamId();
if (packet.isFlagSet(Packet.FLAG_SYNCHRONIZE)) {
if (oldId <= 0) {
@@ -259,15 +262,17 @@ public class PacketHandler {
_manager.getConnectionHandler().receiveNewSyn(packet);
} else {
if (_log.shouldLog(Log.WARN)) {
_log.warn("Packet belongs to no other cons: " + packet);
}
if (_log.shouldLog(Log.DEBUG)) {
StringBuffer buf = new StringBuffer(128);
Set cons = _manager.listConnections();
for (Iterator iter = cons.iterator(); iter.hasNext(); ) {
Connection con = (Connection)iter.next();
buf.append(con.toString()).append(" ");
}
_log.warn("Packet belongs to no other cons: " + packet + " connections: "
+ buf.toString() + " sendId: "
+ (sendId > 0 ? Packet.toId(sendId) : " unknown"));
_log.debug("connections: " + buf.toString() + " sendId: "
+ (sendId > 0 ? Packet.toId(sendId) : " unknown"));
}
packet.releasePayload();
}
@@ -25,6 +25,8 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
private long _acceptedOn;
private long _ackOn;
private long _cancelledOn;
private volatile int _nackCount;
private volatile boolean _retransmitted;
private SimpleTimer.TimedEvent _resendEvent;
public PacketLocal(I2PAppContext ctx, Destination to) {
@@ -38,6 +40,8 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
_connection = con;
_lastSend = -1;
_cancelledOn = -1;
_nackCount = 0;
_retransmitted = false;
}
public Destination getTo() { return _to; }
@@ -113,6 +117,16 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
public int getNumSends() { return _numSends; }
public long getLastSend() { return _lastSend; }
public Connection getConnection() { return _connection; }
public void incrementNACKs() {
int cnt = ++_nackCount;
SimpleTimer.TimedEvent evt = _resendEvent;
if ( (cnt >= Connection.FAST_RETRANSMIT_THRESHOLD) && (evt != null) && (!_retransmitted)) {
_retransmitted = true;
RetransmissionTimer.getInstance().addEvent(evt, 0);
}
}
public int getNACKs() { return _nackCount; }
public void setResendPacketEvent(SimpleTimer.TimedEvent evt) { _resendEvent = evt; }
@@ -51,7 +51,7 @@ public class Updater {
User user = new User();
RemoteArchiveBean rab = new RemoteArchiveBean();
rab.fetchIndex(user, "web", archive, bm.getDefaultProxyHost(), bm.getDefaultProxyPort());
rab.fetchIndex(user, "web", archive, bm.getDefaultProxyHost(), bm.getDefaultProxyPort(), true);
if (rab.getRemoteIndex() != null) {
HashMap parameters = new HashMap();
parameters.put("action", new String[] {"Fetch all new entries"});
@@ -117,8 +117,9 @@ public class User {
PetName pn = _petnames.getByName(name);
if (pn == null) continue;
String proto = pn.getProtocol();
if ( (proto != null) && (AddressesServlet.PROTO_TAG.equals(proto)) )
rv.add(pn.getLocation());
String loc = pn.getLocation();
if ( (proto != null) && (AddressesServlet.PROTO_TAG.equals(proto)) && (loc != null) )
rv.add(loc);
}
if (rv.size() <= 0) {
for (int i = 0; i < DEFAULT_FAVORITE_TAGS.length; i++) {
@@ -162,7 +162,7 @@ public class BlogPostInfoRenderer extends EventReceiverImpl {
if (pn != null)
out.append(HTMLRenderer.sanitizeString(pn.getName()));
else
out.append(HTMLRenderer.sanitizeString(blog.name));
out.append(HTMLRenderer.sanitizeTagParam(blog.name));
out.append(" on ").append(getEntryDate(blog.entryId));
out.append("</a>");
} else if (blog.hash != null) {
@@ -183,7 +183,7 @@ public class BlogPostInfoRenderer extends EventReceiverImpl {
// generate a new nym
while ( (pn = db.getByName(blog.name)) != null)
blog.name = blog.name + ".";
out.append(HTMLRenderer.sanitizeString(blog.name)).append("</a>");
out.append(HTMLRenderer.sanitizeTagParam(blog.name)).append("</a>");
/* <a href=\"profile.jsp?");
_bodyBuffer.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append("=");
_bodyBuffer.append(HTMLRenderer.sanitizeTagParam(blog.hash)).append("\" title=\"View their profile\">");
@@ -320,23 +320,24 @@ public class HTMLRenderer extends EventReceiverImpl {
if ( (description != null) && (description.trim().length() > 0) ) {
_bodyBuffer.append(sanitizeString(description));
} else if ( (name != null) && (name.trim().length() > 0) ) {
_bodyBuffer.append(sanitizeString(name));
_bodyBuffer.append(sanitizeTagParam(name));
} else {
_bodyBuffer.append("[view entry]");
}
_bodyBuffer.append("</a>");
} else if ( (description != null) && (description.trim().length() > 0) ) {
_bodyBuffer.append(sanitizeString(description));
}
//String url = getPageURL(blog, null, -1, -1, -1, (_user != null ? _user.getShowExpanded() : false), (_user != null ? _user.getShowImages() : false));
String url = getMetadataURL(blog);
_bodyBuffer.append(getSpan("blogEntrySummary")).append(" [<a ").append(getClass("blogLink")).append(" href=\"").append(url);
_bodyBuffer.append("\">");
_bodyBuffer.append(getSpan("blogEntrySummary"));
_bodyBuffer.append(" [<a ").append(getClass("blogLink")).append(" href=\"").append(url).append("\">");
if ( (name != null) && (name.trim().length() > 0) )
_bodyBuffer.append(sanitizeString(name));
_bodyBuffer.append(sanitizeTagParam(name));
else
_bodyBuffer.append("view");
_bodyBuffer.append("</a> ");
_bodyBuffer.append("</a>");
//_bodyBuffer.append("</a> (<a ").append(getClass("blogMeta")).append(" href=\"").append(getMetadataURL(blog)).append("\">meta</a>)");
if ( (tag != null) && (tag.trim().length() > 0) ) {
url = getPageURL(blog, tag, -1, -1, -1, false, false);
@@ -348,7 +349,7 @@ public class HTMLRenderer extends EventReceiverImpl {
for (int i = 0; i < locations.size(); i++) {
SafeURL surl = (SafeURL)locations.get(i);
if (_user.getAuthenticated() && BlogManager.instance().authorizeRemote(_user) )
_bodyBuffer.append("<a ").append(getClass("blogArchiveView")).append(" href=\"").append(getArchiveURL(blog, surl)).append("\">").append(sanitizeString(surl.toString())).append("</a> ");
_bodyBuffer.append(" <a ").append(getClass("blogArchiveView")).append(" href=\"").append(getArchiveURL(blog, surl)).append("\">").append(sanitizeString(surl.toString())).append("</a> ");
else
_bodyBuffer.append(getSpan("blogArchiveURL")).append(sanitizeString(surl.toString())).append("</span> ");
}
@@ -900,9 +901,13 @@ public class HTMLRenderer extends EventReceiverImpl {
if (str == null) return "";
//str = str.replace('&', '_'); // this should be &amp;
str = str.replaceAll("&", "&amp;");
if (str.indexOf('\"') < 0)
if (str.indexOf("\"") < 0 && str.indexOf("'") < 0)
return sanitizeString(str);
str = str.replace('\"', '\'');
str = str.replaceAll("\"", "&quot;");
str = str.replaceAll("'", "&#39;"); // as &apos;, but supported by IE
return sanitizeString(str);
}
@@ -160,7 +160,7 @@ public class RSSRenderer extends HTMLRenderer {
if ( (description != null) && (description.trim().length() > 0) ) {
_bodyBuffer.append(sanitizeString(description));
} else if ( (name != null) && (name.trim().length() > 0) ) {
_bodyBuffer.append(sanitizeString(name));
_bodyBuffer.append(sanitizeTagParam(name));
} else {
_bodyBuffer.append("[view entry]");
}
@@ -322,7 +322,7 @@ public class SMLParser {
try {
return Integer.parseInt(val.trim());
} catch (NumberFormatException nfe) {
nfe.printStackTrace();
//nfe.printStackTrace();
return -1;
}
} else {
@@ -336,7 +336,7 @@ public class SMLParser {
try {
return Long.parseLong(val.trim());
} catch (NumberFormatException nfe) {
nfe.printStackTrace();
//nfe.printStackTrace();
return -1;
}
} else {
@@ -370,7 +370,7 @@ public class SMLParser {
} else if (c == EQ) {
if (nameEnd < 0)
nameEnd = off;
} else if ( (c == QUOTE) || (c == DQUOTE) ) {
} else if ( c == DQUOTE ) {
if (valStart < 0) {
valStart = off;
} else {
@@ -450,11 +450,11 @@ public class SMLParser {
test("A: B\nC: D\n\n<a href=\"http://odci.gov\">hi</a>");
test("A: B\n\n[a b='c']d[/a]");
test("A: B\n\n[a b='c' d='e' f='g']h[/a]");
test("A: B\n\n[a b='c' d='e' f='g']h[/a][a b='c' d='e' f='g']h[/a][a b='c' d='e' f='g']h[/a]");
test("A: B\n\n[a b=\"c\"]d[/a]");
test("A: B\n\n[a b=\"c\" d=\"e\" f=\"g\"]h[/a]");
test("A: B\n\n[a b=\"c\" d=\"e\" f=\"g\"]h[/a][a b=\"c\" d=\"e\" f=\"g\"]h[/a][a b=\"c\" d=\"e\" f=\"g\"]h[/a]");
test("A: B\n\n[a b='c' ]d[/a]");
test("A: B\n\n[a b=\"plural c's\" ]d[/a]");
test("A: B\n\n[a b=\"c\" ]d[/a]");
test("A: B\n\n[b]This[/b] is [i]special[/i][cut]why?[/cut][u]because I say so[/u].\neven if you dont care");
@@ -34,14 +34,20 @@ public class AdminServlet extends BaseServlet {
writeAuthActionFields(out);
out.write("<tr><td colspan=\"3\">");
// stop people from shooting themselves in the foot - only geeks can enable multiuser mode
// (by adding the single user flag to their syndie.config)
if (BlogManager.instance().isSingleUser())
out.write("<input type=\"hidden\" name=\"singleuser\" value=\"checked\" />\n");
/*
out.write("<em class=\"b_adminField\">Single user?</em> <input type=\"checkbox\" class=\"b_adminField\" name=\"singleuser\" ");
if (BlogManager.instance().isSingleUser())
out.write(" checked=\"true\" ");
out.write(" /><br />\n");
out.write("<span class=\"b_adminDescr\">If this is checked, the registration, admin, and remote passwords are unnecessary - anyone");
out.write("can register and administer Syndie, as well as use any remote functionality. This should not be checked if untrusted");
out.write("parties can access this web interface.</span><br />\n");
*/
out.write("<span class=\"b_adminField\">Default user:</span> <input class=\"b_adminField\" type=\"text\" name=\"defaultUser\" size=\"10\" value=\"");
out.write(BlogManager.instance().getDefaultLogin());
out.write("\" />\n");
@@ -716,11 +716,13 @@ public abstract class BaseServlet extends HttpServlet {
for (Iterator iter = names.iterator(); iter.hasNext(); ) {
String name = (String) iter.next();
PetName pn = db.getByName(name);
if ("syndieblog".equals(pn.getProtocol()) && pn.isMember(FilteredThreadIndex.GROUP_FAVORITE)) {
String proto = pn.getProtocol();
String loc = pn.getLocation();
if (proto != null && loc != null && "syndieblog".equals(proto) && pn.isMember(FilteredThreadIndex.GROUP_FAVORITE)) {
if ( (author != null) && (author.equals(pn.getLocation())) )
out.write("<option value=\"" + pn.getLocation() + "\" selected=\"true\">Threads " + name + " posted in</option>\n");
out.write("<option value=\"" + loc + "\" selected=\"true\">Threads " + name + " posted in</option>\n");
else
out.write("<option value=\"" + pn.getLocation() + "\">Threads " + name + " posted in</option>\n");
out.write("<option value=\"" + loc + "\">Threads " + name + " posted in</option>\n");
}
}
out.write("</select>\n");
@@ -217,7 +217,7 @@ public class PostServlet extends BaseServlet {
out.write("<span class=\"b_postField\">Post subject:</span> ");
out.write("<input type=\"text\" class=\"b_postSubject\" size=\"80\" name=\"" + PARAM_SUBJECT
+ "\" value=\"" + HTMLRenderer.sanitizeTagParam(subject) + "\" title=\"One line summary\" /><br />\n");
out.write("<span class=\"b_postField\">Post content (in raw <a href=\"smlref.jsp\" target=\"_blank\">SML</a>, no headers):</span><br />\n");
out.write("<span class=\"b_postField\">Post content (in raw <a href=\"smlref.jsp\" target=\"_blank\" title=\"SML cheatsheet\">SML</a>, no headers):</span><br />\n");
out.write("<textarea class=\"b_postText\" rows=\"6\" cols=\"80\" name=\"" + PARAM_TEXT + "\">" + getParam(req, PARAM_TEXT) + "</textarea><br />\n");
out.write("<span class=\"b_postField\">SML post headers:</span><br />\n");
out.write("<textarea class=\"b_postHeaders\" rows=\"2\" cols=\"80\" name=\"" + PARAM_HEADERS + "\" title=\"Most people can leave this empty\" >" + getParam(req, PARAM_HEADERS) + "</textarea><br />\n");
@@ -273,7 +273,7 @@ public class PostServlet extends BaseServlet {
out.write("<span class=\"b_postField\">Post subject:</span> ");
out.write("<input type=\"text\" class=\"b_postSubject\" size=\"80\" name=\"" + PARAM_SUBJECT
+ "\" value=\"" + HTMLRenderer.sanitizeTagParam(subject) + "\" /><br />\n");
out.write("<span class=\"b_postField\">Post content (in raw <a href=\"smlref.jsp\" target=\"_blank\">SML</a>, no headers):</span><br />\n");
out.write("<span class=\"b_postField\">Post content (in raw <a href=\"smlref.jsp\" target=\"_blank\" title=\"SML cheatsheet\">SML</a>, no headers):</span><br />\n");
out.write("<textarea class=\"b_postText\" rows=\"6\" cols=\"80\" name=\"" + PARAM_TEXT + "\">" + getParam(req, PARAM_TEXT) + "</textarea><br />\n");
out.write("<span class=\"b_postField\">SML post headers:</span><br />\n");
out.write("<textarea class=\"b_postHeaders\" rows=\"3\" cols=\"80\" name=\"" + PARAM_HEADERS + "\">" + getParam(req, PARAM_HEADERS) + "</textarea><br />\n");
@@ -278,7 +278,7 @@ public class RemoteArchiveBean {
scheduler.fetch(shouldBlock);
}
public void fetchIndex(User user, String schema, String location, String proxyHost, String proxyPort) {
public void fetchIndex(User user, String schema, String location, String proxyHost, String proxyPort, boolean allowCaching) {
_fetchIndexInProgress = true;
_remoteIndex = null;
_remoteLocation = location;
@@ -330,9 +330,12 @@ public class RemoteArchiveBean {
} catch (IOException ioe) {
//ignore
}
String tag = null;
if (allowCaching)
tag = etags.getProperty(location);
EepGet eep = new EepGet(I2PAppContext.getGlobalContext(), ((_proxyHost != null) && (_proxyPort > 0)),
_proxyHost, _proxyPort, 0, archiveFile.getAbsolutePath(), location, true, etags.getProperty(location));
_proxyHost, _proxyPort, 0, archiveFile.getAbsolutePath(), location, allowCaching, tag);
eep.addStatusListener(new IndexFetcherStatusListener(archiveFile));
eep.fetch();
@@ -49,9 +49,11 @@ public class SyndicateServlet extends BaseServlet {
if (pnval != null) location = pnval.getLocation();
}
// dont allow caching if they explicit ask for a fetch
boolean allowCaching = false;
remote.fetchIndex(user, req.getParameter(PARAM_SCHEMA), location,
req.getParameter("proxyhost"),
req.getParameter("proxyport"));
req.getParameter("proxyport"), allowCaching);
} else if ("Fetch metadata".equals(action)) {
remote.fetchMetadata(user, req.getParameterMap());
} else if ("Fetch selected entries".equals(action)) {
@@ -135,6 +135,8 @@ public class CPUID {
{
if(!_nativeOk)
throw new UnknownCPUException("Failed to read CPU information from the system. Please verify the existence of the jcpuid dll/so.");
if(getCPUVendorID().equals("CentaurHauls"))
return new VIAC3Impl();
if(!isX86)
throw new UnknownCPUException("Failed to read CPU information from the system. The CPUID instruction exists on x86 CPU's only");
if(getCPUVendorID().equals("AuthenticAMD"))
@@ -159,6 +161,11 @@ public class CPUID {
public boolean hasSSE2(){
return (getCPUFlags() & 0x4000000) >0; //Bit 26
}
public boolean IsC3Compatible() { return false; }
}
protected static class VIAC3Impl extends CPUIDCPUInfo implements CPUInfo {
public boolean isC3Compatible() { return true; }
public String getCPUModelString() { return "VIA C3"; }
}
protected static class AMDInfoImpl extends CPUIDCPUInfo implements AMDCPUInfo
{
@@ -41,5 +41,6 @@ public interface CPUInfo
* @return true iff the CPU support the SSE2 instruction set.
*/
public boolean hasSSE2();
public boolean IsC3Compatible();
}
@@ -34,10 +34,11 @@ class SessionStatusMessageHandler extends HandlerImpl {
break;
case SessionStatusMessage.STATUS_DESTROYED:
_log.info("Session destroyed");
session.destroySession();
//session.destroySession();
session.reconnect(); // la la la
break;
case SessionStatusMessage.STATUS_INVALID:
session.destroySession();
session.destroySession(); // ok, honor this destroy message, because we're b0rked
break;
case SessionStatusMessage.STATUS_UPDATED:
_log.info("Session status updated");
@@ -228,7 +228,7 @@ public class DHSessionKeyBuilder {
*/
public BigInteger generateMyValue() {
long start = System.currentTimeMillis();
_myPrivateValue = new NativeBigInteger(2048, RandomSource.getInstance());
_myPrivateValue = new NativeBigInteger(KeyGenerator.PUBKEY_EXPONENT_SIZE, RandomSource.getInstance());
BigInteger myValue = CryptoConstants.elgg.modPow(_myPrivateValue, CryptoConstants.elgp);
long end = System.currentTimeMillis();
long diff = end - start;
@@ -40,7 +40,7 @@ public class HMACSHA256Generator {
_useMD5 = true;
else
_useMD5 = false;
if ("true".equals(context.getProperty("i2p.HMACBrokenSize", "true")))
if ("true".equals(context.getProperty("i2p.HMACBrokenSize", "false")))
_macSize = 32;
else
_macSize = (_useMD5 ? 16 : 32);
+16 -2
View File
@@ -52,13 +52,27 @@ public class KeyGenerator {
key.setData(data);
return key;
}
/** standard exponent size */
private static final int PUBKEY_EXPONENT_SIZE_FULL = 2048;
/**
* short exponent size, which should be safe for use with the Oakley primes,
* per "On Diffie-Hellman Key Agreement with Short Exponents" - van Oorschot, Weiner
* at EuroCrypt 96, and crypto++'s benchmarks at http://www.eskimo.com/~weidai/benchmarks.html
* Also, "Koshiba & Kurosawa: Short Exponent Diffie-Hellman Problems" (PKC 2004, LNCS 2947, pp. 173-186)
* aparently supports this, according to
* http://groups.google.com/group/sci.crypt/browse_thread/thread/1855a5efa7416677/339fa2f945cc9ba0#339fa2f945cc9ba0
* (damn commercial access to http://www.springerlink.com/(xrkdvv45w0cmnur4aimsxx55)/app/home/contribution.asp?referrer=parent&backto=issue,13,31;journal,893,3280;linkingpublicationresults,1:105633,1 )
*/
private static final int PUBKEY_EXPONENT_SIZE_SHORT = 226;
public static final int PUBKEY_EXPONENT_SIZE = PUBKEY_EXPONENT_SIZE_SHORT;
/** Generate a pair of keys, where index 0 is a PublicKey, and
* index 1 is a PrivateKey
* @return pair of keys
*/
public Object[] generatePKIKeypair() {
BigInteger a = new NativeBigInteger(2048, _context.random());
BigInteger a = new NativeBigInteger(PUBKEY_EXPONENT_SIZE, _context.random());
BigInteger aalpha = CryptoConstants.elgg.modPow(a, CryptoConstants.elgp);
Object[] keys = new Object[2];
@@ -130,7 +144,7 @@ public class KeyGenerator {
* Pad the buffer w/ leading 0s or trim off leading bits so the result is the
* given length.
*/
private final static byte[] padBuffer(byte src[], int length) {
final static byte[] padBuffer(byte src[], int length) {
byte buf[] = new byte[length];
if (src.length > buf.length) // extra bits, chop leading bits
@@ -129,7 +129,7 @@ class YKGenerator {
long t1 = 0;
while (k == null) {
t0 = Clock.getInstance().now();
k = new NativeBigInteger(2048, RandomSource.getInstance());
k = new NativeBigInteger(KeyGenerator.PUBKEY_EXPONENT_SIZE, RandomSource.getInstance());
t1 = Clock.getInstance().now();
if (BigInteger.ZERO.compareTo(k) == 0) {
k = null;
+3 -1
View File
@@ -34,6 +34,8 @@ public class Certificate extends DataStructureImpl {
public final static int CERTIFICATE_TYPE_NULL = 0;
/** specifies a Hashcash style certificate */
public final static int CERTIFICATE_TYPE_HASHCASH = 1;
/** we should not be used for anything (don't use us in the netDb, in tunnels, or tell others about us) */
public final static int CERTIFICATE_TYPE_HIDDEN = 2;
public Certificate() {
_type = 0;
@@ -76,7 +78,7 @@ public class Certificate extends DataStructureImpl {
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_type < 0) throw new DataFormatException("Invalid certificate type: " + _type);
if ((_type != 0) && (_payload == null)) throw new DataFormatException("Payload is required for non null type");
//if ((_type != 0) && (_payload == null)) throw new DataFormatException("Payload is required for non null type");
DataHelper.writeLong(out, 1, _type);
if (_payload != null) {
@@ -63,6 +63,16 @@ public class RouterIdentity extends DataStructureImpl {
_signingKey = key;
__calculatedHash = null;
}
/**
* This router specified that they should not be used as a part of a tunnel,
* nor queried for the netDb, and that disclosure of their contact information
* should be limited.
*
*/
public boolean isHidden() {
return (_certificate != null) && (_certificate.getCertificateType() == Certificate.CERTIFICATE_TYPE_HIDDEN);
}
public void readBytes(InputStream in) throws DataFormatException, IOException {
_publicKey = new PublicKey();
@@ -80,14 +80,17 @@ public class DecayingBloomFilter {
*
*/
public boolean add(byte entry[]) {
return add(entry, 0, entry.length);
}
public boolean add(byte entry[], int off, int len) {
if (ALWAYS_MISS) return false;
if (entry == null)
throw new IllegalArgumentException("Null entry");
if (entry.length != _entryBytes)
throw new IllegalArgumentException("Bad entry [" + entry.length + ", expected "
if (len != _entryBytes)
throw new IllegalArgumentException("Bad entry [" + len + ", expected "
+ _entryBytes + "]");
synchronized (this) {
return locked_add(entry);
return locked_add(entry, off, len);
}
}
@@ -101,14 +104,15 @@ public class DecayingBloomFilter {
if (ALWAYS_MISS) return false;
synchronized (this) {
if (_entryBytes <= 7)
entry &= _longToEntryMask;
entry = ((entry ^ _longToEntryMask) & ((1 << 31)-1)) | (entry ^ _longToEntryMask);
//entry &= _longToEntryMask;
if (entry < 0) {
DataHelper.toLong(_longToEntry, 0, _entryBytes, 0-entry);
_longToEntry[0] |= (1 << 7);
} else {
DataHelper.toLong(_longToEntry, 0, _entryBytes, entry);
}
return locked_add(_longToEntry);
return locked_add(_longToEntry, 0, _longToEntry.length);
}
}
@@ -121,26 +125,26 @@ public class DecayingBloomFilter {
if (ALWAYS_MISS) return false;
synchronized (this) {
if (_entryBytes <= 7)
entry &= _longToEntryMask;
entry = ((entry ^ _longToEntryMask) & ((1 << 31)-1)) | (entry ^ _longToEntryMask);
if (entry < 0) {
DataHelper.toLong(_longToEntry, 0, _entryBytes, 0-entry);
_longToEntry[0] |= (1 << 7);
} else {
DataHelper.toLong(_longToEntry, 0, _entryBytes, entry);
}
return locked_add(_longToEntry, false);
return locked_add(_longToEntry, 0, _longToEntry.length, false);
}
}
private boolean locked_add(byte entry[]) {
return locked_add(entry, true);
private boolean locked_add(byte entry[], int offset, int len) {
return locked_add(entry, offset, len, true);
}
private boolean locked_add(byte entry[], boolean addIfNew) {
private boolean locked_add(byte entry[], int offset, int len, boolean addIfNew) {
if (_extended != null) {
// extend the entry to 32 bytes
System.arraycopy(entry, 0, _extended, 0, entry.length);
System.arraycopy(entry, offset, _extended, 0, len);
for (int i = 0; i < _extenders.length; i++)
DataHelper.xor(entry, 0, _extenders[i], 0, _extended, _entryBytes * (i+1), _entryBytes);
DataHelper.xor(entry, offset, _extenders[i], 0, _extended, _entryBytes * (i+1), _entryBytes);
boolean seen = _current.member(_extended);
seen = seen || _previous.member(_extended);
@@ -155,15 +159,15 @@ public class DecayingBloomFilter {
return false;
}
} else {
boolean seen = _current.locked_member(entry);
seen = seen || _previous.locked_member(entry);
boolean seen = _current.locked_member(entry, offset, len);
seen = seen || _previous.locked_member(entry, offset, len);
if (seen) {
_currentDuplicates++;
return true;
} else {
if (addIfNew) {
_current.locked_insert(entry);
_previous.locked_insert(entry);
_current.locked_insert(entry, offset, len);
_previous.locked_insert(entry, offset, len);
}
return false;
}
@@ -103,6 +103,7 @@ public class NativeBigInteger extends BigInteger {
private final static String JBIGI_OPTIMIZATION_PENTIUM2 = "pentium2";
private final static String JBIGI_OPTIMIZATION_PENTIUM3 = "pentium3";
private final static String JBIGI_OPTIMIZATION_PENTIUM4 = "pentium4";
private final static String JBIGI_OPTIMIZATION_VIAC3 = "viac3";
private static final boolean _isWin = System.getProperty("os.name").startsWith("Win");
private static final boolean _isOS2 = System.getProperty("os.name").startsWith("OS/2");
@@ -134,6 +135,8 @@ public class NativeBigInteger extends BigInteger {
try {
CPUInfo c = CPUID.getInfo();
if (c.IsC3Compatible())
return JBIGI_OPTIMIZATION_VIAC3;
if (c instanceof AMDCPUInfo) {
AMDCPUInfo amdcpu = (AMDCPUInfo) c;
if (amdcpu.IsAthlon64Compatible())
@@ -146,20 +149,18 @@ public class NativeBigInteger extends BigInteger {
return JBIGI_OPTIMIZATION_K6_2;
if (amdcpu.IsK6Compatible())
return JBIGI_OPTIMIZATION_K6;
} else {
if (c instanceof IntelCPUInfo) {
IntelCPUInfo intelcpu = (IntelCPUInfo) c;
if (intelcpu.IsPentium4Compatible())
return JBIGI_OPTIMIZATION_PENTIUM4;
if (intelcpu.IsPentium3Compatible())
return JBIGI_OPTIMIZATION_PENTIUM3;
if (intelcpu.IsPentium2Compatible())
return JBIGI_OPTIMIZATION_PENTIUM2;
if (intelcpu.IsPentiumMMXCompatible())
return JBIGI_OPTIMIZATION_PENTIUMMMX;
if (intelcpu.IsPentiumCompatible())
return JBIGI_OPTIMIZATION_PENTIUM;
}
} else if (c instanceof IntelCPUInfo) {
IntelCPUInfo intelcpu = (IntelCPUInfo) c;
if (intelcpu.IsPentium4Compatible())
return JBIGI_OPTIMIZATION_PENTIUM4;
if (intelcpu.IsPentium3Compatible())
return JBIGI_OPTIMIZATION_PENTIUM3;
if (intelcpu.IsPentium2Compatible())
return JBIGI_OPTIMIZATION_PENTIUM2;
if (intelcpu.IsPentiumMMXCompatible())
return JBIGI_OPTIMIZATION_PENTIUMMMX;
if (intelcpu.IsPentiumCompatible())
return JBIGI_OPTIMIZATION_PENTIUM;
}
return null;
} catch (UnknownCPUException e) {
@@ -287,7 +288,7 @@ public class NativeBigInteger extends BigInteger {
int runsProcessed = 0;
for (runsProcessed = 0; runsProcessed < numRuns; runsProcessed++) {
BigInteger bi = new BigInteger(2048, rand);
BigInteger bi = new BigInteger(226, rand); // 2048, rand); //
NativeBigInteger g = new NativeBigInteger(_sampleGenerator);
NativeBigInteger p = new NativeBigInteger(_samplePrime);
NativeBigInteger k = new NativeBigInteger(1, bi.toByteArray());
@@ -148,14 +148,16 @@ public class BloomSHA1 {
*
* @param b byte array representing a key (SHA1 digest)
*/
public void insert (byte[]b) {
public void insert (byte[]b) { insert(b, 0, b.length); }
public void insert (byte[]b, int offset, int len) {
synchronized(this) {
locked_insert(b);
}
}
public final void locked_insert(byte[]b) {
ks.getOffsets(b);
public final void locked_insert(byte[]b) { locked_insert(b, 0, b.length); }
public final void locked_insert(byte[]b, int offset, int len) {
ks.getOffsets(b, offset, len);
for (int i = 0; i < k; i++) {
filter[wordOffset[i]] |= 1 << bitOffset[i];
}
@@ -168,8 +170,9 @@ public class BloomSHA1 {
* @param b byte array representing a key (SHA1 digest)
* @return true if b is in the filter
*/
protected final boolean isMember(byte[] b) {
ks.getOffsets(b);
protected final boolean isMember(byte[] b) { return isMember(b, 0, b.length); }
protected final boolean isMember(byte[] b, int offset, int len) {
ks.getOffsets(b, offset, len);
for (int i = 0; i < k; i++) {
if (! ((filter[wordOffset[i]] & (1 << bitOffset[i])) != 0) ) {
return false;
@@ -179,6 +182,7 @@ public class BloomSHA1 {
}
public final boolean locked_member(byte[]b) { return isMember(b); }
public final boolean locked_member(byte[]b, int offset, int len) { return isMember(b, offset, len); }
/**
* Is a key in the filter. External interface, internally synchronized.
@@ -186,9 +190,10 @@ public class BloomSHA1 {
* @param b byte array representing a key (SHA1 digest)
* @return true if b is in the filter
*/
public final boolean member(byte[]b) {
public final boolean member(byte[]b) { return member(b, 0, b.length); }
public final boolean member(byte[]b, int offset, int len) {
synchronized (this) {
return isMember(b);
return isMember(b, offset, len);
}
}
@@ -18,6 +18,8 @@ public class KeySelector {
private int m;
private int k;
private byte[] b;
private int offset; // index into b to select
private int length; // length into b to select
private int[] bitOffset;
private int[] wordOffset;
private BitSelector bitSel;
@@ -70,7 +72,7 @@ public class KeySelector {
public class GenericBitSelector implements BitSelector {
/** Do the extraction */
public void getBitSelectors() {
int curBit = 0;
int curBit = 8 * offset;
int curByte;
for (int j = 0; j < k; j++) {
curByte = curBit / 8;
@@ -126,7 +128,7 @@ public class KeySelector {
public void getWordSelectors() {
int stride = m - 5;
//assert true: stride<16;
int curBit = k * 5;
int curBit = (k * 5) + (offset * 8);
int curByte;
for (int j = 0; j < k; j++) {
curByte = curBit / 8;
@@ -216,15 +218,18 @@ public class KeySelector {
*
* @param key cryptographic key used in populating the arrays
*/
public void getOffsets (byte[] key) {
public void getOffsets (byte[] key) { getOffsets(key, 0, key.length); }
public void getOffsets (byte[] key, int off, int len) {
if (key == null) {
throw new IllegalArgumentException("null key");
}
if (key.length < 20) {
if (len < 20) {
throw new IllegalArgumentException(
"key must be at least 20 bytes long");
}
b = key;
offset = off;
length = len;
// // DEBUG
// System.out.println("KeySelector.getOffsets for "
// + BloomSHA1.keyToString(b));
+123 -1
View File
@@ -1,4 +1,126 @@
$Id: history.txt,v 1.395 2006/01/25 10:34:28 dust Exp $
$Id: history.txt,v 1.396.2.19 2006/02/15 00:16:31 jrandom Exp $
2006-02-15 jrandom
* Merged in the i2p_0_6_1_10_PRE branch to the trunk, so CVS HEAD is no
longer backwards compatible (and should not be used until 0.6.1.1 is
out)
2006-02-14 jrandom
* Syndie ui bugfixes (thanks all!)
2006-02-13 jrandom
* Use the current directory for some temporary I2PSnark files, rather than
the OS default temp dir (thanks anon!)
* Increase the base streaming lib window size (still shrinks to 1 on
retransmission though, of course)
* Fixed the I2PTunnel newlines to work with lighthttpd (thanks all!)
* Implement fast retransmit in the streaming lib (fires at most once per
packet), and increased the default ack delay to 2 seconds (from .5s)
* Don't ask for garlic level message acks for end to end messages unless
they're useful (e.g. to ack session tags)
2006-02-12 cervantes
* Use a different santisation method for some SML attributes
* Make router console update config save button actually save.
* Fix console bandwidth limiter burst rate dropdowns, so the display
relates to what is saved in the config.
2006-02-12 cervantes
* SML is now stricter in it's formatting (attributes should only use
double quotes instead of being allowed to mix with singles).
* Using apostrophes in SML attributes will no longer invalidate the tag.
* Some instances of [blog] tag description were not being displayed
correctly.
2006-02-12 jrandom
* Further SSU peer test throttling
* Put the most common router console features on the main index page too
2006-02-11 jrandom
* Be more careful about SSU peer test floods
2006-02-09 jrandom
* Adjusted one of the SSU timeouts so we don't drop peers as easily (duh)
2006-02-08 jrandom
* Added transparent support for VIA C3 CPUs to jbigi (thanks Nekow42), and
bundled a precompiled libjbigi.so in the jbigi.jar
* Cleaned up the synchronization for some SSU packet handling code
* Allow explicit rejection of more lagged tunnel build requests, rather
than dropping them outright
* Use lighter load testing
2006-02-07 jrandom
* Handle HTTP headers without any values (thanks Sugadude!)
* Don't show the option to make Syndie multiuser, since very few people
need it, and multiuser mode is a lot more complex to use. Geeks can
enable it by adding "syndie.singleUser=false" to syndie/syndie.config
(or in the router's advanced config, for the embedded Syndie)
* When a peer rejects participation in a tunnel, they mean it (duh)
* Decrease tunnel test timeout period to 20s (a 40s lag is insane)
* Remove a throttle on the size of the SSU active outbound pool, since
it was essentially arbitrary
* Use a more appropriate SSU bloom filter size
* Don't "proactively" drop SSU connections if we have partially received
inbound messages (duh)
* Migrate most of the message state across SSU connection reestablishment
2006-02-06 jrandom
* Reduce the SSU retransmit timeout range, and increase the number of ACKs
piggybacked
2006-02-05 jrandom
* Experiment with short exponents for DH/ElGamal, using a 226bit x instead
of a 2048bit x, as reports suggest that size is sufficient for 2048bit
DH/ElGamal when using safe primes (see KeyGenerator.java for references)
* Enable the messageHistory.txt by default, for debugging
2006-02-05 jrandom
* Substantial bugfix for the duplicate message detection in the transport
layer
* Handle tunnel build responses ASAP, rather than queueing them up to wait
in line (processing them is really fast - just a few AES loops)
* Don't bother handling build requests that we have queued up for a while
locally, as the requestor will have timed it out anyway (perhaps we
should reply regardless, but with a backoff instead?)
2006-02-04 jrandom
* Further tunnel test cleanup and disabling of the old tunnel creation
code
2006-02-04 jrandom
* Clean up and reenable the tunnel testing for the new tunnel system.
2006-02-04 jrandom
* Don't cache the archive.txt in syndie when fetching it through the web
interface.
* Logging updates
2006-02-03 jrandom
* Added further replay prevention on the tunnel build requests
* More aggressive streaming lib closing on reset
2006-02-03 jrandom
* More aggressive refusal of peers from the wrong network (oops)
2006-02-01 jrandom
* Instruct the router to reseed against a new URL, for migration purposes:
http://dev.i2p.net/i2pdb2/
* Aggressive error handling during UDP packet creation (thanks cervantes)
2006-02-01 jrandom
* Fix the new tunnel creation crypto, including the addition of a 4 byte
"next message ID" to the encrypted request structure in the spec.
* Backwards incompatible change, using the new tunnel creation crypto, the
fixed MD5 HMAC size, and a new network ID (to prevent cross pollination
with the old incompatible network).
* Reworked the leaseSet request process to handle a race condition
* Disable the TCP transport
* Run four separate threads on the job queue to cut down on job lag
2006-01-28 jrandom
* Removed a race that could show up in leaseSet requesting with the new
tunnel building process
2006-01-25 jrandom
* Run the peer profile coalescing/reorganization outside the job queue
+3
View File
@@ -16,3 +16,6 @@ jbigi.jar after being mistakenly removed in the Sep 18 update (d'oh!)
On Dec 30, 2005, the libjcpuid-x86-linux.so was updated to use the
(year old) C version of jcpuid, rather than the C++ version. This removes
the libg++.so.5 dependency that has been a problem for a few linux distros.
On Feb 8, 2006, the libjbigi-linux-viac3.so was added to jbigi.jar after
being compiled by jrandom on linux/p4 (cross compiled to --host=viac3)
Binary file not shown.
Binary file not shown.
+32 -29
View File
@@ -17,7 +17,7 @@ pre { font-size: 10; font-family: sans-serif }
<center>
<b class="title">Introducing I2P</b><br />
<span class="subtitle">a scalable framework for anonymous communication</span><br />
<i style="font-size: 8">$Id: techintro.html,v 1.7 2005/10/04 20:45:21 jrandom Exp $</i>
<i style="font-size: 8">$Id: techintro.html,v 1.8.2.1 2006/02/13 07:13:35 jrandom Exp $</i>
<br />
<br />
@@ -56,15 +56,16 @@ pre { font-size: 10; font-family: sans-serif }
<h1 id="intro">Introduction</h1>
<p>
I2P is a scalable, self organizing, resilient message based anonymous network layer,
I2P is a scalable, self organizing, resilient packet switched anonymous network layer,
upon which any number of different anonymity or security conscious applications
can operate. Each of these applications may make their own anonymity, latency, and
throughput tradeoffs without worrying about the proper implementation of a free
route mixnet, allowing them to blend their activity with the larger anonymity set of
users already running on top of I2P. Applications available already provide the full
range of typical Internet activities - anonymous web browsing, anonymous web hosting,
anonymous blogging (with <a href="#app.syndie">Syndie</a>), anonymous chat (via IRC or
Jabber), anonymous swarming file transfers (with <a href="#app.i2pbt">i2p-bt</a> and
anonymous blogging and content syndication (with <a href="#app.syndie">Syndie</a>),
anonymous chat (via IRC or Jabber), anonymous swarming file transfers (with <a
href="#app.i2pbt">i2p-bt</a>, <a href="#app.i2psnark">I2PSnark</a>, and
<a href="#app.azneti2p">Azureus</a>), anonymous file sharing (with
<a href="#app.i2phex">I2Phex</a>), anonymous email (with <a href="#app.i2pmail">I2Pmail</a>
and <a href="#app.i2pmail">susimail</a>), anonymous newsgroups, as well as several
@@ -85,8 +86,8 @@ to allow I2P's anonymous best-effort messages to transfer as reliable, in-order
transparently offering a TCP based congestion control algorithm tuned for the high
bandwidth delay product of the network. While there have been several simple SOCKS
proxies available to tie existing applications into the network, their value has been
limited as nearly every application routinely exposes what in an anonymity context is
sensitive information. The only safe way to go is to fully audit an application to
limited as nearly every application routinely exposes what, in an anonymous context,
is sensitive information. The only safe way to go is to fully audit an application to
ensure proper operation, and to assist in that we provide a series of APIs in various
languages which can be used to make the most out of the network.
</p>
@@ -113,14 +114,14 @@ level of anonymity to those who need it. It has been in active development sinc
early 2003 with one full time developer and a dedicated group of part time contributors
from all over the world. All of the work done on I2P is open source and
freely available on the <a href="http://www.i2p.net/">website</a>, with the majority
of the code released outright into the public domain but making use of a few
of the code released outright into the public domain, though making use of a few
cryptographic routines under BSD-style licenses. The people working on I2P do not
control what people release client applications under, and there are several GPL'ed
applications available (<a href="#app.i2ptunnel">I2PTunnel</a>,
<a href="#app.i2pmail">susimail</a>, <a href="#app.azneti2p">Azureus</a>,
<a href="#app.i2pmail">susimail</a>, <a href="#app.i2psnark">I2PSnark</a>, <a href="#app.azneti2p">Azureus</a>,
<a href="#app.i2phex">I2Phex</a>). <a href="http://www.i2p.net/halloffame">Funding</a>
for I2P comes entirely from donations, and does not receive any tax breaks in any
jurisdiction, as many of the developers are themselves anonymous.
jurisdiction at this time, as many of the developers are themselves anonymous.
</p>
<h1 id="op">Operation</h1>
@@ -165,16 +166,18 @@ inbound tunnels as well as when that tunnel will expire. The leaseSet also
contains a pair of public keys which can be used for layered garlic encryption.
</p>
<!--
<p>
I2P's operation can be understood by putting those three concepts together:
</p>
<p><img src="net.png"></p>
!-->
<p>
When Alice wants to send a message to Bob, she first does a lookup in the
netDb to find Bob's leaseSet, giving her his current inbound tunnel gateways
(3 and 4). She then picks one of her outbound tunnels and sends the message
netDb to find Bob's leaseSet, giving her his current inbound tunnel gateways.
She then picks one of her outbound tunnels and sends the message
down it with instructions for the outbound tunnel's endpoint to forward the
message on to one of Bob's inbound tunnel gateways. When the outbound
tunnel endpoint receives those instructions, it forwards the message as
@@ -263,7 +266,7 @@ by measuring their indirect behavior - for instance, when a peer responds to
a netDb lookup in 1.3 seconds, that round trip latency is recorded in the
profiles for all of the routers involved in the two tunnels (inbound and
outbound) through which the request and response passed, as well as the queried
peer's profile. Direction measurement, such as transport layer latency or
peer's profile. Direct measurement, such as transport layer latency or
congestion, is not used as part of the profile, as it can be manipulated and
associated with the measuring router, exposing them to trivial attacks. While
gathering these profiles, a series of calculations are run on each to summarize
@@ -438,10 +441,10 @@ addressing network obstacles, like most NATs or firewalls.
A bare minimum set of cryptographic primitives are combined together to provide I2P's
layered defenses against a variety of adversaries. At the lowest level, interrouter
communication is protected by the transport layer security - SSU
encrypts each packet with AES256/CBC with both an explicit IV and MAC (HMAC-SHA256-128)
encrypts each packet with AES256/CBC with both an explicit IV and MAC (HMAC-MD5-128)
after agreeing upon an ephemeral session key through a 2048bit Diffie-Hellman exchange,
station-to-station authentication with the other router's DSA key, plus each network
message has their own SHA256 hash for local integrity checking.
message has their own hash for local integrity checking.
<a href="#op.tunnels">Tunnel</a> messages passed over the transports have their own
layered AES256/CBC encryption with an explicit IV and verified at the tunnel endpoint
with an additional SHA256 hash. Various other messages are passed along inside
@@ -686,14 +689,10 @@ outbound tunnel along the same routers.</p>
Another anonymity issue comes up in Tor's use of telescopic tunnel creation, as
simple packet counting and timing measurements as the cells in a circuit pass
through an adversary's node exposes statistical information regarding where the
adversary is within the circuit. I2P's use of exploratory tunnels for delivering
and receiving the tunnel creation requests and responses effectively spreads the
messages randomly across the network, so that each of the peers who forwards the
individual tunnel creation messages only see the peer they transmit to or receive
from, and thanks to the garlic encryption, they are not aware of whether the message
is part of a tunnel creation process or not. The participant positional information
is useful to an adversary for mounting predecessor, intersection, and traffic
confirmation attacks.
adversary is within the circuit. I2P's unidirectional tunnel creation with a
single message so that this data is not exposed. Protecting the position in a
tunnel is important, as an adversary would otherwise be able to mounting a
series of powerful predecessor, intersection, and traffic confirmation attacks.
</p>
<p>
@@ -754,13 +753,6 @@ has been said the anonymity and scalability claims seem highly dubious. In
particular, the appropriateness for use in hostile regimes against state level
adversaries has been tremendously overstated, and any analysis on the implications
of resource scarcity upon the scalability of the network has seemingly been avoided.
Specifically, while publishing the "anonymous" topology in the darknet does not
necessarily immediately expose all identities, it is equivalent to publishing an
organizational chart for a covert group, which can in turn be used by an adversary
alongside existing knowledge of their target to narrow down or identify different
participants. In addition, by using only peers that are locally connected, the
network's mixnet layer is vulnerable to a class of
<a href="http://www.im.pwr.wroc.pl/~klonowsk/LocalViewAttack.ps">local view attacks</a>.
Further questions regarding susceptibility to traffic analysis, trust, and other topics
do exist, but a more in-depth review of this "globally scalable darknet" will have
to wait until the Freenet team makes more information available.
@@ -941,6 +933,17 @@ application and to take into consideration the fact that IPs cannot be used for
identifying peers.
</p>
<h2 id="app.i2psnark">I2PSnark</h2>
<p><i>I2PSnark developed: jrandom, et al, ported from <a
href="http://www.klomp.org/mark/">mjw</a>'s <a
href="http://www.klomp.org/snark/">Snark</a> client</i></p>
<p>
Bundled with the I2P install, I2PSnark offers a simple anonymous bittorrent
client with multitorrent capabilities, exposing all of the functionality through
a plain HTML web interface.
</p>
<h2 id="app.azneti2p">Azureus/azneti2p</h2>
<p><i>Developed by: parg, et al</i></p>
+5 -3
View File
@@ -1,4 +1,4 @@
<code>$Id: tunnel-alt.html,v 1.9 2005/07/27 14:04:07 jrandom Exp $</code>
<code>$Id: tunnel-alt-creation.html,v 1.1.2.1 2006/02/01 20:28:34 jrandom Exp $</code>
<pre>
1) <a href="#tunnelCreate.overview">Tunnel creation</a>
1.1) <a href="#tunnelCreate.requestRecord">Tunnel creation request record</a>
@@ -35,12 +35,14 @@ the asymmetrically encrypted record only at the appropriate time.</p>
bytes 168-183: reply IV
byte 184: flags
bytes 185-188: request time (in hours since the epoch)
bytes 189-222: uninterpreted / random padding</pre>
bytes 189-192: next message ID
bytes 193-222: uninterpreted / random padding</pre>
<p>The next tunnel ID and next router identity hash fields are used to
specify the next hop in the tunnel, though for an outbound tunnel
endpoint, they specify where the rewritten tunnel creation reply
message should be sent.</p>
message should be sent. In addition, the next message ID specifies the
message ID that the message (or reply) should use.</p>
<p>The flags field currently has two bits defined:</p><pre>
bit 0: if set, allow messages from anyone
+3 -3
View File
@@ -1,4 +1,4 @@
<code>$Id: udp.html,v 1.17 2005/09/09 23:30:37 jrandom Exp $</code>
<code>$Id: udp.html,v 1.18.2.1 2006/02/15 00:16:29 jrandom Exp $</code>
<h1>Secure Semireliable UDP (SSU)</h1>
<b>DRAFT</b>
@@ -44,10 +44,10 @@ capabilities, see <a href="#capabilities">below</a>.</p>
<p>All UDP datagrams begin with a MAC and an IV, followed by a variable
size payload encrypted with the appropriate key. The MAC used is
HMAC-SHA256, truncated to 16 bytes, while the key is a full AES256
HMAC-MD5, truncated to 16 bytes, while the key is a full AES256
key. The specific construct of the MAC is the first 16 bytes from:</p>
<pre>
HMAC-SHA256(payload || IV || payloadLength, macKey)
HMAC-MD5(payload || IV || payloadLength, macKey)
</pre>
<p>The payload itself is AES256/CBC encrypted with the IV and the
@@ -17,7 +17,8 @@ import net.i2p.data.*;
* bytes 168-183: reply IV
* byte 184: flags
* bytes 185-188: request time (in hours since the epoch)
* bytes 189-222: uninterpreted / random padding
* bytes 189-192: next message ID
* bytes 193-222: uninterpreted / random padding
* </pre>
*
*/
@@ -57,6 +58,7 @@ public class BuildRequestRecord {
private static final int OFF_REPLY_IV = OFF_REPLY_KEY + SessionKey.KEYSIZE_BYTES;
private static final int OFF_FLAG = OFF_REPLY_IV + IV_SIZE;
private static final int OFF_REQ_TIME = OFF_FLAG + 1;
private static final int OFF_SEND_MSG_ID = OFF_REQ_TIME + 4;
/** what tunnel ID should this receive messages on */
public long readReceiveTunnelId() {
@@ -135,7 +137,14 @@ public class BuildRequestRecord {
public long readRequestTime() {
return DataHelper.fromLong(_data.getData(), _data.getOffset() + OFF_REQ_TIME, 4) * 60l * 60l * 1000l;
}
/**
* What message ID should we send the request to the next hop with. If this is the outbound tunnel endpoint,
* this specifies the message ID with which the reply should be sent.
*/
public long readReplyMessageId() {
return DataHelper.fromLong(_data.getData(), _data.getOffset() + OFF_SEND_MSG_ID, 4);
}
/**
* Encrypt the record to the specified peer. The result is formatted as: <pre>
* bytes 0-15: SHA-256-128 of the current hop's identity (the toPeer parameter)
@@ -144,7 +153,7 @@ public class BuildRequestRecord {
*/
public void encryptRecord(I2PAppContext ctx, PublicKey toKey, Hash toPeer, byte out[], int outOffset) {
System.arraycopy(toPeer.getData(), 0, out, outOffset, PEER_SIZE);
byte preEncr[] = new byte[OFF_REQ_TIME + 4 + PADDING_SIZE];
byte preEncr[] = new byte[OFF_SEND_MSG_ID + 4 + PADDING_SIZE];
System.arraycopy(_data.getData(), _data.getOffset(), preEncr, 0, preEncr.length);
byte encrypted[] = ctx.elGamalEngine().encrypt(preEncr, toKey);
// the elg engine formats it kind of weird, giving 257 bytes for each part rather than 256, so
@@ -175,7 +184,7 @@ public class BuildRequestRecord {
}
}
private static final int PADDING_SIZE = 33;
private static final int PADDING_SIZE = 29;
/**
* Populate this instance with data. A new buffer is created to contain the data, with the
@@ -185,6 +194,7 @@ public class BuildRequestRecord {
* @param peer current hop's identity
* @param nextTunnelId id for the next hop, or where we send the reply (if we are the outbound endpoint)
* @param nextHop next hop's identity, or where we send the reply (if we are the outbound endpoint)
* @param nextMsgId message ID to use when sending on to the next hop (or for the reply)
* @param layerKey tunnel layer key to be used by the peer
* @param ivKey tunnel IV key to be used by the peer
* @param replyKey key to be used when encrypting the reply to this build request
@@ -192,12 +202,12 @@ public class BuildRequestRecord {
* @param isInGateway are we the gateway of an inbound tunnel?
* @param isOutEndpoint are we the endpoint of an outbound tunnel?
*/
public void createRecord(I2PAppContext ctx, long receiveTunnelId, Hash peer, long nextTunnelId, Hash nextHop,
public void createRecord(I2PAppContext ctx, long receiveTunnelId, Hash peer, long nextTunnelId, Hash nextHop, long nextMsgId,
SessionKey layerKey, SessionKey ivKey, SessionKey replyKey, byte iv[], boolean isInGateway,
boolean isOutEndpoint) {
if ( (_data == null) || (_data.getData() != null) )
_data = new ByteArray();
byte buf[] = new byte[OFF_REQ_TIME+4+PADDING_SIZE];
byte buf[] = new byte[OFF_SEND_MSG_ID+4+PADDING_SIZE];
_data.setData(buf);
/* bytes 0-3: tunnel ID to receive messages as
@@ -210,7 +220,8 @@ public class BuildRequestRecord {
* bytes 168-183: reply IV
* byte 184: flags
* bytes 185-188: request time (in hours since the epoch)
* bytes 189-222: uninterpreted / random padding
* bytes 189-192: next message ID
* bytes 193-222: uninterpreted / random padding
*/
DataHelper.toLong(buf, OFF_RECV_TUNNEL, 4, receiveTunnelId);
System.arraycopy(peer.getData(), 0, buf, OFF_OUR_IDENT, Hash.HASH_LENGTH);
@@ -227,9 +238,10 @@ public class BuildRequestRecord {
long truncatedHour = ctx.clock().now();
truncatedHour /= (60l*60l*1000l);
DataHelper.toLong(buf, OFF_REQ_TIME, 4, truncatedHour);
DataHelper.toLong(buf, OFF_SEND_MSG_ID, 4, nextMsgId);
byte rnd[] = new byte[PADDING_SIZE];
ctx.random().nextBytes(rnd);
System.arraycopy(rnd, 0, buf, OFF_REQ_TIME+4, rnd.length);
System.arraycopy(rnd, 0, buf, OFF_SEND_MSG_ID+4, rnd.length);
byte wroteIV[] = readReplyIV();
if (!DataHelper.eq(iv, wroteIV))
@@ -2,6 +2,7 @@ package net.i2p.data.i2np;
import net.i2p.I2PAppContext;
import net.i2p.data.*;
import net.i2p.util.Log;
/**
* Read and write the reply to a tunnel build message record.
@@ -11,13 +12,18 @@ public class BuildResponseRecord {
/**
* Create a new encrypted response
*/
public byte[] create(I2PAppContext ctx, int status, SessionKey replyKey, byte replyIV[]) {
public byte[] create(I2PAppContext ctx, int status, SessionKey replyKey, byte replyIV[], long responseMessageId) {
Log log = ctx.logManager().getLog(BuildResponseRecord.class);
byte rv[] = new byte[TunnelBuildReplyMessage.RECORD_SIZE];
ctx.random().nextBytes(rv);
DataHelper.toLong(rv, TunnelBuildMessage.RECORD_SIZE-1, 1, status);
// rv = AES(SHA256(padding+status) + padding + status, replyKey, replyIV)
ctx.sha().calculateHash(rv, Hash.HASH_LENGTH, rv.length - Hash.HASH_LENGTH, rv, 0);
if (log.shouldLog(Log.DEBUG))
log.debug(responseMessageId + ": before encrypt: " + Base64.encode(rv, 0, 128) + " with " + replyKey.toBase64() + "/" + Base64.encode(replyIV));
ctx.aes().encrypt(rv, 0, rv, 0, replyKey, replyIV, rv.length);
if (log.shouldLog(Log.DEBUG))
log.debug(responseMessageId + ": after encrypt: " + Base64.encode(rv, 0, 128));
return rv;
}
}
@@ -33,14 +33,15 @@ public class TunnelBuildMessage extends I2NPMessageImpl {
for (int i = 0; i < RECORD_COUNT; i++) {
int off = offset + (i * RECORD_SIZE);
int len = RECORD_SIZE;
setRecord(i, new ByteArray(data, off, len));
byte rec[] = new byte[RECORD_SIZE];
System.arraycopy(data, off, rec, 0, RECORD_SIZE);
setRecord(i, new ByteArray(rec)); //new ByteArray(data, off, len));
}
}
protected int writeMessageBody(byte[] out, int curIndex) throws I2NPMessageException {
int remaining = out.length - (curIndex + calculateWrittenLength());
if (remaining <= 0)
if (remaining < 0)
throw new I2NPMessageException("Not large enough (too short by " + remaining + ")");
for (int i = 0; i < RECORD_COUNT; i++) {
System.arraycopy(_records[i].getData(), _records[i].getOffset(), out, curIndex, RECORD_SIZE);
@@ -35,13 +35,16 @@ public class TunnelBuildReplyMessage extends I2NPMessageImpl {
for (int i = 0; i < RECORD_COUNT; i++) {
int off = offset + (i * RECORD_SIZE);
int len = RECORD_SIZE;
setRecord(i, new ByteArray(data, off, len));
byte rec[] = new byte[RECORD_SIZE];
System.arraycopy(data, off, rec, 0, RECORD_SIZE);
setRecord(i, new ByteArray(rec));
//setRecord(i, new ByteArray(data, off, len));
}
}
protected int writeMessageBody(byte[] out, int curIndex) throws I2NPMessageException {
int remaining = out.length - (curIndex + calculateWrittenLength());
if (remaining <= 0)
if (remaining < 0)
throw new I2NPMessageException("Not large enough (too short by " + remaining + ")");
for (int i = 0; i < RECORD_COUNT; i++) {
System.arraycopy(_records[i].getData(), _records[i].getOffset(), out, curIndex, RECORD_SIZE);
@@ -63,7 +63,7 @@ public class InNetMessagePool implements Service {
public InNetMessagePool(RouterContext context) {
_context = context;
_handlerJobBuilders = new HandlerJobBuilder[20];
_handlerJobBuilders = new HandlerJobBuilder[32];
_pendingDataMessages = new ArrayList(16);
_pendingDataMessagesFrom = new ArrayList(16);
_pendingGatewayMessages = new ArrayList(16);
@@ -133,7 +133,7 @@ public class InNetMessagePool implements Service {
+ ": " + messageBody);
_context.statManager().addRateData("inNetPool.dropped", 1, 0);
_context.statManager().addRateData("inNetPool.duplicate", 1, 0);
_context.messageHistory().droppedOtherMessage(messageBody);
_context.messageHistory().droppedOtherMessage(messageBody, (fromRouter != null ? fromRouter.calculateHash() : fromRouterHash));
_context.messageHistory().messageProcessingError(messageBody.getUniqueId(),
messageBody.getClass().getName(),
"Duplicate/expired");
@@ -184,7 +184,7 @@ public class InNetMessagePool implements Service {
// not handled as a reply
if (!jobFound) {
// was not handled via HandlerJobBuilder
_context.messageHistory().droppedOtherMessage(messageBody);
_context.messageHistory().droppedOtherMessage(messageBody, (fromRouter != null ? fromRouter.calculateHash() : fromRouterHash));
if (type == DeliveryStatusMessage.MESSAGE_TYPE) {
long timeSinceSent = _context.clock().now() -
((DeliveryStatusMessage)messageBody).getArrival();
+3 -1
View File
@@ -228,6 +228,7 @@ public class JobQueue {
public void allowParallelOperation() {
_allowParallelOperation = true;
runQueue(4);
}
public void restart() {
@@ -579,7 +580,8 @@ public class JobQueue {
activeJobs.add(job);
} else {
job = runner.getLastJob();
justFinishedJobs.add(job);
if (job != null)
justFinishedJobs.add(job);
}
}
numRunners = _queueRunners.size();
@@ -70,38 +70,10 @@ public class LoadTestManager {
public static final boolean TEST_LIVE_TUNNELS = true;
public Job getTestJob() { return new TestJob(_context); }
private class TestJob extends JobImpl {
public TestJob(RouterContext ctx) {
super(ctx);
// wait 5m to start up
getTiming().setStartAfter(3*60*1000 + getContext().clock().now());
}
public String getName() { return "run load tests"; }
public void runJob() {
if (!TEST_LIVE_TUNNELS) {
runTest();
getTiming().setStartAfter(10*60*1000 + getContext().clock().now());
getContext().jobQueue().addJob(TestJob.this);
}
}
}
/** 1 peer at a time */
private static final int CONCURRENT_PEERS = 1;
/** 4 messages per peer at a time */
private static final int CONCURRENT_MESSAGES = 4;
public void runTest() {
if ( (_untestedPeers == null) || (_untestedPeers.size() <= 0) ) {
UDPTransport t = UDPTransport._instance();
if (t != null)
_untestedPeers = t._getActivePeers();
}
int peers = getConcurrency();
for (int i = 0; i < peers && _untestedPeers.size() > 0; i++)
buildTestTunnel((Hash)_untestedPeers.remove(0));
}
private static final int CONCURRENT_MESSAGES = 1;//4;
private int getConcurrency() {
int rv = CONCURRENT_PEERS;
@@ -118,11 +90,14 @@ public class LoadTestManager {
}
private int getPeerMessages() {
String msgsPerPeer = _context.getProperty("router.loadTestMessagesPerPeer");
int rv = CONCURRENT_MESSAGES;
try {
rv = Integer.parseInt(_context.getProperty("router.loadTestMessagesPerPeer", CONCURRENT_MESSAGES+""));
} catch (NumberFormatException nfe) {
rv = CONCURRENT_MESSAGES;
if (msgsPerPeer != null) {
try {
rv = Integer.parseInt(msgsPerPeer);
} catch (NumberFormatException nfe) {
rv = CONCURRENT_MESSAGES;
}
}
if (rv < 1)
rv = 1;
@@ -449,121 +424,6 @@ public class LoadTestManager {
}
}
private boolean getBuildOneHop() {
return Boolean.valueOf(_context.getProperty("router.loadTestOneHop", "false")).booleanValue();
}
private void buildTestTunnel(Hash peer) {
if (getBuildOneHop()) {
buildOneHop(peer);
} else {
buildLonger(peer);
}
}
private void buildOneHop(Hash peer) {
long expiration = _context.clock().now() + 10*60*1000;
PooledTunnelCreatorConfig cfg = new PooledTunnelCreatorConfig(_context, 2, true);
// cfg.getPeer() is ordered gateway first
cfg.setPeer(0, peer);
HopConfig hop = cfg.getConfig(0);
hop.setExpiration(expiration);
hop.setIVKey(_context.keyGenerator().generateSessionKey());
hop.setLayerKey(_context.keyGenerator().generateSessionKey());
// now for ourselves
cfg.setPeer(1, _context.routerHash());
hop = cfg.getConfig(1);
hop.setExpiration(expiration);
hop.setIVKey(_context.keyGenerator().generateSessionKey());
hop.setLayerKey(_context.keyGenerator().generateSessionKey());
cfg.setExpiration(expiration);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Config for " + peer.toBase64() + ": " + cfg);
LoadTestTunnelConfig ltCfg = new LoadTestTunnelConfig(cfg);
CreatedJob onCreated = new CreatedJob(_context, ltCfg);
FailedJob fail = new FailedJob(_context, ltCfg);
RequestTunnelJob req = new RequestTunnelJob(_context, cfg, onCreated, fail, cfg.getLength()-1, false, true);
_context.jobQueue().addJob(req);
}
private Hash pickFastPeer(Hash skipPeer) {
String peers = _context.getProperty("router.loadTestFastPeers");
if (peers != null) {
StringTokenizer tok = new StringTokenizer(peers.trim(), ", \t");
List peerList = new ArrayList();
while (tok.hasMoreTokens()) {
String str = tok.nextToken();
try {
Hash h = new Hash();
h.fromBase64(str);
peerList.add(h);
} catch (DataFormatException dfe) {
// ignore
}
}
Collections.shuffle(peerList);
while (peerList.size() > 0) {
Hash cur = (Hash)peerList.remove(0);
if (!cur.equals(skipPeer))
return cur;
}
}
return null;
}
private void buildLonger(Hash peer) {
long expiration = _context.clock().now() + 10*60*1000;
PooledTunnelCreatorConfig cfg = new PooledTunnelCreatorConfig(_context, 3, true);
// cfg.getPeer() is ordered gateway first
cfg.setPeer(0, peer);
HopConfig hop = cfg.getConfig(0);
hop.setExpiration(expiration);
hop.setIVKey(_context.keyGenerator().generateSessionKey());
hop.setLayerKey(_context.keyGenerator().generateSessionKey());
// now lets put in a fast peer
Hash fastPeer = pickFastPeer(peer);
if (fastPeer == null) {
if (_log.shouldLog(Log.INFO))
_log.info("Unable to pick a fast peer for the load test of " + peer.toBase64());
buildOneHop(peer);
return;
} else if (fastPeer.equals(peer)) {
if (_log.shouldLog(Log.WARN))
_log.warn("Can't test the peer with themselves, going one hop for " + peer.toBase64());
buildOneHop(peer);
return;
}
cfg.setPeer(1, fastPeer);
hop = cfg.getConfig(1);
hop.setExpiration(expiration);
hop.setIVKey(_context.keyGenerator().generateSessionKey());
hop.setLayerKey(_context.keyGenerator().generateSessionKey());
// now for ourselves
cfg.setPeer(2, _context.routerHash());
hop = cfg.getConfig(2);
hop.setExpiration(expiration);
hop.setIVKey(_context.keyGenerator().generateSessionKey());
hop.setLayerKey(_context.keyGenerator().generateSessionKey());
cfg.setExpiration(expiration);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Config for " + peer.toBase64() + " with fastPeer: " + fastPeer.toBase64() + ": " + cfg);
LoadTestTunnelConfig ltCfg = new LoadTestTunnelConfig(cfg);
CreatedJob onCreated = new CreatedJob(_context, ltCfg);
FailedJob fail = new FailedJob(_context, ltCfg);
RequestTunnelJob req = new RequestTunnelJob(_context, cfg, onCreated, fail, cfg.getLength()-1, false, true);
_context.jobQueue().addJob(req);
}
/**
* If we are testing live tunnels, see if we want to test the one that was just created
* fully.
@@ -647,8 +507,8 @@ public class LoadTestManager {
runTest(_cfg);
}
}
private long TEST_PERIOD_MAX = 10*60*1000;
private long TEST_PERIOD_MIN = 90*1000;
private long TEST_PERIOD_MAX = 5*60*1000;
private long TEST_PERIOD_MIN = 1*60*1000;
private class Expire extends JobImpl {
private LoadTestTunnelConfig _cfg;
@@ -39,7 +39,7 @@ public class MessageHistory {
/** config property determining whether we want to debug with the message history */
public final static String PROP_KEEP_MESSAGE_HISTORY = "router.keepHistory";
public final static boolean DEFAULT_KEEP_MESSAGE_HISTORY = false;
public final static boolean DEFAULT_KEEP_MESSAGE_HISTORY = true;
/** config property determining where we want to log the message history, if we're keeping one */
public final static String PROP_MESSAGE_HISTORY_FILENAME = "router.historyFilename";
public final static String DEFAULT_MESSAGE_HISTORY_FILENAME = "messageHistory.txt";
@@ -48,6 +48,7 @@ public class MessageHistory {
public MessageHistory(RouterContext context) {
_context = context;
_log = context.logManager().getLog(getClass());
_fmt = new SimpleDateFormat("yy/MM/dd.HH:mm:ss.SSS");
_fmt.setTimeZone(TimeZone.getTimeZone("GMT"));
_reinitializeJob = new ReinitializeJob();
@@ -270,6 +271,16 @@ public class MessageHistory {
addEntry(buf.toString());
}
public void tunnelParticipantRejected(Hash peer, String msg) {
if (!_doLog) return;
if (peer == null) return;
StringBuffer buf = new StringBuffer(128);
buf.append(getPrefix());
buf.append("tunnel participation rejected by [");
buf.append(getName(peer)).append("]: ").append(msg);
addEntry(buf.toString());
}
/**
* The peer did not accept the tunnel join for the given reason (this may be because
* of a timeout or an explicit refusal).
@@ -305,16 +316,37 @@ public class MessageHistory {
/**
* We received another message we weren't waiting for and don't know how to handle
*/
public void droppedOtherMessage(I2NPMessage message) {
public void droppedOtherMessage(I2NPMessage message, Hash from) {
if (!_doLog) return;
if (message == null) return;
StringBuffer buf = new StringBuffer(512);
buf.append(getPrefix());
buf.append("dropped [").append(message.getClass().getName()).append("] ").append(message.getUniqueId());
buf.append(" [").append(message.toString()).append("]");
buf.append(" [").append(message.toString()).append("] from [");
if (from != null)
buf.append(from.toBase64());
else
buf.append("unknown");
buf.append("] expiring in ").append(message.getMessageExpiration()-_context.clock().now()).append("ms");
addEntry(buf.toString());
}
public void droppedInboundMessage(long messageId, Hash from, String info) {
if (!_doLog) return;
StringBuffer buf = new StringBuffer(512);
buf.append(getPrefix());
buf.append("dropped inbound message ").append(messageId);
buf.append(" from ");
if (from != null)
buf.append(from.toBase64());
else
buf.append("unknown");
buf.append(": ").append(info);
addEntry(buf.toString());
//if (_log.shouldLog(Log.ERROR))
// _log.error(buf.toString(), new Exception("source"));
}
/**
* The message wanted a reply but no reply came in the time expected
*
@@ -348,6 +380,24 @@ public class MessageHistory {
addEntry(buf.toString());
}
/**
* We shitlisted the peer
*/
public void shitlist(Hash peer, String reason) {
if (!_doLog) return;
if (peer == null) return;
addEntry("Shitlist " + peer.toBase64() + ": " + reason);
}
/**
* We unshitlisted the peer
*/
public void unshitlist(Hash peer) {
if (!_doLog) return;
if (peer == null) return;
addEntry("Unshitlist " + peer.toBase64());
}
/**
* We just sent a message to the peer
*
@@ -358,7 +408,7 @@ public class MessageHistory {
* @param peer router that the message was sent to
* @param sentOk whether the message was sent successfully
*/
public void sendMessage(String messageType, long messageId, long expiration, Hash peer, boolean sentOk) {
public void sendMessage(String messageType, long messageId, long expiration, Hash peer, boolean sentOk, String info) {
if (!_doLog) return;
if (false) return;
StringBuffer buf = new StringBuffer(256);
@@ -370,6 +420,8 @@ public class MessageHistory {
buf.append("successfully");
else
buf.append("failed");
if (info != null)
buf.append(info);
addEntry(buf.toString());
}
@@ -469,22 +521,30 @@ public class MessageHistory {
buf.append(" ").append(status);
addEntry(buf.toString());
}
public void fragmentMessage(long messageId, int numFragments) {
public void fragmentMessage(long messageId, int numFragments, int totalLength, List messageIds, String msg) {
if (!_doLog) return;
if (messageId == -1) throw new IllegalArgumentException("why are you -1?");
//if (messageId == -1) throw new IllegalArgumentException("why are you -1?");
StringBuffer buf = new StringBuffer(48);
buf.append(getPrefix());
buf.append("Break message ").append(messageId).append(" into fragments: ").append(numFragments);
buf.append(" total size ").append(totalLength);
buf.append(" contained in ").append(messageIds);
if (msg != null)
buf.append(": ").append(msg);
addEntry(buf.toString());
}
public void fragmentMessage(long messageId, int numFragments, Object tunnel) {
public void fragmentMessage(long messageId, int numFragments, int totalLength, List messageIds, Object tunnel, String msg) {
if (!_doLog) return;
if (messageId == -1) throw new IllegalArgumentException("why are you -1?");
//if (messageId == -1) throw new IllegalArgumentException("why are you -1?");
StringBuffer buf = new StringBuffer(48);
buf.append(getPrefix());
buf.append("Break message ").append(messageId).append(" into fragments: ").append(numFragments);
buf.append(" total size ").append(totalLength);
buf.append(" contained in ").append(messageIds);
if (tunnel != null)
buf.append(" on ").append(tunnel.toString());
if (msg != null)
buf.append(": ").append(msg);
addEntry(buf.toString());
}
public void droppedTunnelDataMessageUnknown(long msgId, long tunnelId) {
+20 -6
View File
@@ -28,11 +28,7 @@ import java.util.TreeSet;
import net.i2p.CoreVersion;
import net.i2p.crypto.DHSessionKeyBuilder;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.RouterAddress;
import net.i2p.data.RouterInfo;
import net.i2p.data.SigningPrivateKey;
import net.i2p.data.*;
import net.i2p.data.i2np.GarlicMessage;
//import net.i2p.data.i2np.TunnelMessage;
import net.i2p.router.message.GarlicMessageHandler;
@@ -73,7 +69,7 @@ public class Router {
public final static long CLOCK_FUDGE_FACTOR = 1*60*1000;
/** used to differentiate routerInfo files on different networks */
public static final int NETWORK_ID = 1;
public static final int NETWORK_ID = 2;
public final static String PROP_HIDDEN = "router.hiddenMode";
public final static String PROP_DYNAMIC_KEYS = "router.dynamicKeys";
@@ -389,6 +385,24 @@ public class Router {
}
}
public boolean isHidden() {
RouterInfo ri = _routerInfo;
if ( (ri != null) && (ri.isHidden()) )
return true;
return Boolean.valueOf(_context.getProperty("router.isHidden", "false")).booleanValue();
}
public Certificate createCertificate() {
Certificate cert = new Certificate();
if (isHidden()) {
cert.setCertificateType(Certificate.CERTIFICATE_TYPE_HIDDEN);
cert.setPayload(null);
} else {
cert.setCertificateType(Certificate.CERTIFICATE_TYPE_NULL);
cert.setPayload(null);
}
return cert;
}
/**
* Ugly list of files that we need to kill if we are building a new identity
*
@@ -25,7 +25,7 @@ public interface RouterThrottle {
*
* @return 0 if it should be accepted, higher values for more severe rejection
*/
public int acceptTunnelRequest(TunnelCreateMessage msg);
public int acceptTunnelRequest();
/**
* Should we accept the netDb lookup message, replying either with the
* value or some closer peers, or should we simply drop it due to overload?
@@ -21,7 +21,7 @@ class RouterThrottleImpl implements RouterThrottle {
* to a job, we're congested.
*
*/
private static int JOB_LAG_LIMIT = 10*1000;
private static int JOB_LAG_LIMIT = 2*1000;
/**
* Arbitrary hard limit - if we throttle our network connection this many
* times in the previous 2 minute period, don't accept requests to
@@ -80,7 +80,7 @@ class RouterThrottleImpl implements RouterThrottle {
}
}
public int acceptTunnelRequest(TunnelCreateMessage msg) {
public int acceptTunnelRequest() {
if (_context.getProperty(Router.PROP_SHUTDOWN_IN_PROGRESS) != null) {
if (_log.shouldLog(Log.WARN))
_log.warn("Refusing tunnel request since we are shutting down ASAP");
@@ -253,7 +253,7 @@ class RouterThrottleImpl implements RouterThrottle {
_context.statManager().addRateData("router.throttleTunnelBandwidthExceeded", (long)bytesAllocated, 0);
return TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
}
_context.statManager().addRateData("tunnel.bytesAllocatedAtAccept", (long)bytesAllocated, msg.getDurationSeconds()*1000);
_context.statManager().addRateData("tunnel.bytesAllocatedAtAccept", (long)bytesAllocated, 60*10*1000);
if (_log.shouldLog(Log.DEBUG))
@@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
*
*/
public class RouterVersion {
public final static String ID = "$Revision: 1.339 $ $Date: 2006/01/25 10:34:31 $";
public final static String ID = "$Revision: 1.340.2.17 $ $Date: 2006/02/15 00:16:30 $";
public final static String VERSION = "0.6.1.9";
public final static long BUILD = 8;
public final static long BUILD = 25;
public static void main(String args[]) {
System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
System.out.println("Router ID: " + RouterVersion.ID);
+5 -1
View File
@@ -85,6 +85,8 @@ public class Shitlist {
_context.netDb().fail(peer);
//_context.tunnelManager().peerFailed(peer);
_context.messageRegistry().peerFailed(peer);
if (!wasAlready)
_context.messageHistory().shitlist(peer, reason);
return wasAlready;
}
@@ -93,7 +95,8 @@ public class Shitlist {
}
private void unshitlistRouter(Hash peer, boolean realUnshitlist) {
if (peer == null) return;
_log.info("Unshitlisting router " + peer.toBase64());
if (_log.shouldLog(Log.INFO))
_log.info("Unshitlisting router " + peer.toBase64());
synchronized (_shitlist) {
_shitlist.remove(peer);
_shitlistCause.remove(peer);
@@ -103,6 +106,7 @@ public class Shitlist {
if (prof != null)
prof.unshitlist();
}
_context.messageHistory().unshitlist(peer);
}
public boolean isShitlisted(Hash peer) {
@@ -110,7 +110,7 @@ public class StatisticsManager implements Service {
includeRate("tunnel.fragmentedDropped", stats, new long[] { 10*60*1000, 3*60*60*1000 });
//includeRate("tunnel.fullFragments", stats, new long[] { 10*60*1000, 3*60*60*1000 });
//includeRate("tunnel.smallFragments", stats, new long[] { 10*60*1000, 3*60*60*1000 });
includeRate("tunnel.testFailedTime", stats, new long[] { 60*60*1000 });
includeRate("tunnel.testFailedTime", stats, new long[] { 10*60*1000 });
includeRate("tunnel.buildFailure", stats, new long[] { 60*60*1000 });
includeRate("tunnel.buildSuccess", stats, new long[] { 60*60*1000 });
@@ -129,7 +129,7 @@ public class StatisticsManager implements Service {
includeRate("udp.statusDifferent", stats, new long[] { 20*60*1000 });
includeRate("udp.statusReject", stats, new long[] { 20*60*1000 });
includeRate("udp.statusUnknown", stats, new long[] { 20*60*1000 });
includeRate("udp.statusKnownharlie", stats, new long[] { 1*60*1000, 10*60*1000 });
includeRate("udp.statusKnownCharlie", stats, new long[] { 1*60*1000, 10*60*1000 });
includeRate("udp.addressUpdated", stats, new long[] { 1*60*1000 });
includeRate("udp.addressTestInsteadOfUpdate", stats, new long[] { 1*60*1000 });
@@ -137,19 +137,34 @@ public class StatisticsManager implements Service {
//includeRate("transport.sendProcessingTime", stats, new long[] { 60*60*1000 });
//includeRate("jobQueue.jobRunSlow", stats, new long[] { 10*60*1000l, 60*60*1000l });
includeRate("crypto.elGamal.encrypt", stats, new long[] { 60*60*1000 });
includeRate("crypto.elGamal.encrypt", stats, new long[] { 60*1000, 60*60*1000 });
includeRate("tunnel.participatingTunnels", stats, new long[] { 5*60*1000, 60*60*1000 });
includeRate("tunnel.testSuccessTime", stats, new long[] { 60*60*1000l, 24*60*60*1000l });
includeRate("tunnel.testSuccessTime", stats, new long[] { 10*60*1000l });
includeRate("client.sendAckTime", stats, new long[] { 60*60*1000 }, true);
includeRate("udp.sendConfirmTime", stats, new long[] { 10*60*1000 });
includeRate("udp.sendVolleyTime", stats, new long[] { 10*60*1000 });
includeRate("udp.ignoreRecentDuplicate", stats, new long[] { 10*60*1000 });
includeRate("udp.ignoreRecentDuplicate", stats, new long[] { 60*1000 });
includeRate("udp.congestionOccurred", stats, new long[] { 10*60*1000 });
//includeRate("stream.con.sendDuplicateSize", stats, new long[] { 60*60*1000 });
//includeRate("stream.con.receiveDuplicateSize", stats, new long[] { 60*60*1000 });
stats.setProperty("stat_uptime", DataHelper.formatDuration(_context.router().getUptime()));
stats.setProperty("stat__rateKey", "avg;maxAvg;pctLifetime;[sat;satLim;maxSat;maxSatLim;][num;lifetimeFreq;maxFreq]");
includeRate("tunnel.buildRequestTime", stats, new long[] { 60*1000, 10*60*1000 });
includeRate("tunnel.decryptRequestTime", stats, new long[] { 60*1000, 10*60*1000 });
includeRate("tunnel.buildClientExpire", stats, new long[] { 60*1000, 10*60*1000 });
includeRate("tunnel.buildClientReject", stats, new long[] { 60*1000, 10*60*1000 });
includeRate("tunnel.buildClientSuccess", stats, new long[] { 60*1000, 10*60*1000 });
includeRate("tunnel.buildExploratoryExpire", stats, new long[] { 60*1000, 10*60*1000 });
includeRate("tunnel.buildExploratoryReject", stats, new long[] { 60*1000, 10*60*1000 });
includeRate("tunnel.buildExploratorySuccess", stats, new long[] { 60*1000, 10*60*1000 });
includeRate("tunnel.rejectTimeout", stats, new long[] { 60*1000, 10*60*1000 });
includeRate("udp.packetDequeueTime", stats, new long[] { 60*1000 });
includeRate("udp.packetVerifyTime", stats, new long[] { 60*1000 });
includeRate("tunnel.rejectOverloaded", stats, new long[] { 60*1000, 10*60*1000 });
includeRate("tunnel.acceptLoad", stats, new long[] { 60*1000, 10*60*1000 });
if (FloodfillNetworkDatabaseFacade.isFloodfill(_context.router().getRouterInfo())) {
stats.setProperty("netdb.knownRouters", ""+_context.netDb().getKnownRouters());
stats.setProperty("netdb.knownLeaseSets", ""+_context.netDb().getKnownLeaseSets());
@@ -159,7 +174,7 @@ public class StatisticsManager implements Service {
} else {
_log.debug("Not publishing peer rankings");
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Building status: " + stats);
return stats;
@@ -36,6 +36,7 @@ import net.i2p.router.RouterContext;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
import net.i2p.util.RandomSource;
import net.i2p.util.SimpleTimer;
/**
* Bridge the router and the client - managing state for a client.
@@ -149,7 +150,13 @@ public class ClientConnectionRunner {
void setSessionId(SessionId id) { if (id != null) _sessionId = id; }
/** data for the current leaseRequest, or null if there is no active leaseSet request */
LeaseRequestState getLeaseRequest() { return _leaseRequest; }
void setLeaseRequest(LeaseRequestState req) { _leaseRequest = req; }
void setLeaseRequest(LeaseRequestState req) {
synchronized (this) {
if ( (_leaseRequest != null) && (req != _leaseRequest) )
_log.error("Changing leaseRequest from " + _leaseRequest + " to " + req);
_leaseRequest = req;
}
}
/** already closed? */
boolean isDead() { return _dead; }
/** message body */
@@ -214,16 +221,23 @@ public class ClientConnectionRunner {
* updated. This takes care of all the LeaseRequestState stuff (including firing any jobs)
*/
void leaseSetCreated(LeaseSet ls) {
if (_leaseRequest == null) {
_log.error("LeaseRequest is null and we've received a new lease?! WTF");
return;
} else {
_leaseRequest.setIsSuccessful(true);
if (_leaseRequest.getOnGranted() != null)
_context.jobQueue().addJob(_leaseRequest.getOnGranted());
_leaseRequest = null;
_currentLeaseSet = ls;
LeaseRequestState state = null;
synchronized (this) {
state = _leaseRequest;
if (state == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("LeaseRequest is null and we've received a new lease?! perhaps this is odd... " + ls);
return;
} else {
state.setIsSuccessful(true);
_currentLeaseSet = ls;
if (_log.shouldLog(Log.DEBUG))
_log.debug("LeaseSet created fully: " + state + " / " + ls);
_leaseRequest = null;
}
}
if ( (state != null) && (state.getOnGranted() != null) )
_context.jobQueue().addJob(state.getOnGranted());
}
void disconnectClient(String reason) {
@@ -236,7 +250,7 @@ public class ClientConnectionRunner {
try {
doSend(msg);
} catch (I2CPMessageException ime) {
_log.error("Error writing out the disconnect message", ime);
_log.error("Error writing out the disconnect message: " + ime);
}
stopRunning();
}
@@ -288,12 +302,14 @@ public class ClientConnectionRunner {
*
*/
void ackSendMessage(MessageId id, long nonce) {
SessionId sid = _sessionId;
if (sid == null) return;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Acking message send [accepted]" + id + " / " + nonce + " for sessionId "
+ _sessionId, new Exception("sendAccepted"));
+ sid, new Exception("sendAccepted"));
MessageStatusMessage status = new MessageStatusMessage();
status.setMessageId(id.getMessageId());
status.setSessionId(_sessionId.getSessionId());
status.setSessionId(sid.getSessionId());
status.setSize(0L);
status.setNonce(nonce);
status.setStatus(MessageStatusMessage.STATUS_SEND_ACCEPTED);
@@ -312,7 +328,7 @@ public class ClientConnectionRunner {
+ " overall, synchronized took " + (inLock - beforeLock));
}
} catch (I2CPMessageException ime) {
_log.error("Error writing out the message status message", ime);
_log.error("Error writing out the message status message: " + ime);
}
}
@@ -323,7 +339,7 @@ public class ClientConnectionRunner {
void receiveMessage(Destination toDest, Destination fromDest, Payload payload) {
if (_dead) return;
MessageReceivedJob j = new MessageReceivedJob(_context, this, toDest, fromDest, payload);
j.runJob();
_context.jobQueue().addJob(j);//j.runJob();
}
/**
@@ -348,16 +364,65 @@ public class ClientConnectionRunner {
* @param onFailedJob Job to run after the timeout passes without receiving authorization
*/
void requestLeaseSet(LeaseSet set, long expirationTime, Job onCreateJob, Job onFailedJob) {
if (_dead) return;
if ( (_currentLeaseSet != null) && (_currentLeaseSet.equals(set)) )
if (_dead) {
if (_log.shouldLog(Log.WARN))
_log.warn("Requesting leaseSet from a dead client: " + set);
if (onFailedJob != null)
_context.jobQueue().addJob(onFailedJob);
return;
}
if ( (_currentLeaseSet != null) && (_currentLeaseSet.equals(set)) ) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Requested leaseSet hasn't changed");
if (onCreateJob != null)
_context.jobQueue().addJob(onCreateJob);
return; // no change
if (_leaseRequest != null)
return; // already requesting
_context.jobQueue().addJob(new RequestLeaseSetJob(_context, this, set, _context.clock().now() + expirationTime, onCreateJob, onFailedJob));
}
LeaseRequestState state = null;
synchronized (this) {
state = _leaseRequest;
if (state != null) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Already requesting " + state);
LeaseSet requested = state.getRequested();
LeaseSet granted = state.getGranted();
long ours = set.getEarliestLeaseDate();
if ( ( (requested != null) && (requested.getEarliestLeaseDate() > ours) ) ||
( (granted != null) && (granted.getEarliestLeaseDate() > ours) ) ) {
// theirs is newer
} else {
// ours is newer, so wait a few secs and retry
SimpleTimer.getInstance().addEvent(new Rerequest(set, expirationTime, onCreateJob, onFailedJob), 3*1000);
}
// fire onCreated?
return; // already requesting
} else {
_leaseRequest = state = new LeaseRequestState(onCreateJob, onFailedJob, _context.clock().now() + expirationTime, set);
_log.debug("Not already requesting, continue to request " + set);
}
}
_context.jobQueue().addJob(new RequestLeaseSetJob(_context, this, set, _context.clock().now() + expirationTime, onCreateJob, onFailedJob, state));
}
private class Rerequest implements SimpleTimer.TimedEvent {
private LeaseSet _ls;
private long _expirationTime;
private Job _onCreate;
private Job _onFailed;
public Rerequest(LeaseSet ls, long expirationTime, Job onCreate, Job onFailed) {
_ls = ls;
_expirationTime = expirationTime;
_onCreate = onCreate;
_onFailed = onFailed;
}
public void timeReached() {
requestLeaseSet(_ls, _expirationTime, _onCreate, _onFailed);
}
}
void disconnected() {
_log.error("Disconnected", new Exception("Disconnected?"));
if (_log.shouldLog(Log.WARN))
_log.warn("Disconnected", new Exception("Disconnected?"));
stopRunning();
}
@@ -376,10 +441,10 @@ public class ClientConnectionRunner {
_log.debug("after writeMessage("+ msg.getClass().getName() + "): "
+ (_context.clock().now()-before) + "ms");;
} catch (I2CPMessageException ime) {
_log.error("Message exception sending I2CP message", ime);
_log.error("Message exception sending I2CP message: " + ime);
stopRunning();
} catch (IOException ioe) {
_log.error("IO exception sending I2CP message", ioe);
_log.error("IO exception sending I2CP message: " + ioe);
stopRunning();
} catch (Throwable t) {
_log.log(Log.CRIT, "Unhandled exception sending I2CP message", t);
@@ -29,13 +29,13 @@ class LeaseRequestState {
private boolean _successful;
public LeaseRequestState(Job onGranted, Job onFailed, long expiration, LeaseSet requested) {
_onGranted = onGranted;
_onFailed = onFailed;
_expiration = expiration;
_requestedLeaseSet = requested;
_successful = false;
_onGranted = onGranted;
_onFailed = onFailed;
_expiration = expiration;
_requestedLeaseSet = requested;
_successful = false;
}
/** created lease set from client */
public LeaseSet getGranted() { return _grantedLeaseSet; }
public void setGranted(LeaseSet ls) { _grantedLeaseSet = ls; }
@@ -59,4 +59,11 @@ class LeaseRequestState {
/** whether the request was successful in the time allotted */
public boolean getIsSuccessful() { return _successful; }
public void setIsSuccessful(boolean is) { _successful = is; }
public String toString() {
return "leaseSet request asking for " + _requestedLeaseSet
+ " having received " + _grantedLeaseSet
+ " succeeding? " + _successful
+ " expiring on " + _expiration;
}
}
@@ -33,7 +33,9 @@ class RequestLeaseSetJob extends JobImpl {
private long _expiration;
private Job _onCreate;
private Job _onFail;
public RequestLeaseSetJob(RouterContext ctx, ClientConnectionRunner runner, LeaseSet set, long expiration, Job onCreate, Job onFail) {
private LeaseRequestState _requestState;
public RequestLeaseSetJob(RouterContext ctx, ClientConnectionRunner runner, LeaseSet set, long expiration, Job onCreate, Job onFail, LeaseRequestState state) {
super(ctx);
_log = ctx.logManager().getLog(RequestLeaseSetJob.class);
_runner = runner;
@@ -41,6 +43,7 @@ class RequestLeaseSetJob extends JobImpl {
_expiration = expiration;
_onCreate = onCreate;
_onFail = onFail;
_requestState = state;
ctx.statManager().createRateStat("client.requestLeaseSetSuccess", "How frequently the router requests successfully a new leaseSet?", "ClientMessages", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
ctx.statManager().createRateStat("client.requestLeaseSetTimeout", "How frequently the router requests a new leaseSet but gets no reply?", "ClientMessages", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
ctx.statManager().createRateStat("client.requestLeaseSetDropped", "How frequently the router requests a new leaseSet but the client drops?", "ClientMessages", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
@@ -49,43 +52,31 @@ class RequestLeaseSetJob extends JobImpl {
public String getName() { return "Request Lease Set"; }
public void runJob() {
if (_runner.isDead()) return;
LeaseRequestState oldReq = _runner.getLeaseRequest();
if (oldReq != null) {
if (oldReq.getExpiration() > getContext().clock().now()) {
_log.info("request of a leaseSet is still active, wait a little bit before asking again");
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("Old *expired* leaseRequest exists! Why did the old request not get killed? (expiration = " + new Date(oldReq.getExpiration()) + ")", getAddedBy());
}
return;
}
LeaseRequestState state = new LeaseRequestState(_onCreate, _onFail, _expiration, _ls);
RequestLeaseSetMessage msg = new RequestLeaseSetMessage();
Date end = null;
// get the earliest end date
for (int i = 0; i < state.getRequested().getLeaseCount(); i++) {
if ( (end == null) || (end.getTime() > state.getRequested().getLease(i).getEndDate().getTime()) )
end = state.getRequested().getLease(i).getEndDate();
for (int i = 0; i < _requestState.getRequested().getLeaseCount(); i++) {
if ( (end == null) || (end.getTime() > _requestState.getRequested().getLease(i).getEndDate().getTime()) )
end = _requestState.getRequested().getLease(i).getEndDate();
}
msg.setEndDate(end);
msg.setSessionId(_runner.getSessionId());
for (int i = 0; i < state.getRequested().getLeaseCount(); i++) {
msg.addEndpoint(state.getRequested().getLease(i).getGateway(), state.getRequested().getLease(i).getTunnelId());
for (int i = 0; i < _requestState.getRequested().getLeaseCount(); i++) {
msg.addEndpoint(_requestState.getRequested().getLease(i).getGateway(), _requestState.getRequested().getLease(i).getTunnelId());
}
try {
_runner.setLeaseRequest(state);
//_runner.setLeaseRequest(state);
_runner.doSend(msg);
getContext().jobQueue().addJob(new CheckLeaseRequestStatus(getContext(), state));
getContext().jobQueue().addJob(new CheckLeaseRequestStatus(getContext(), _requestState));
return;
} catch (I2CPMessageException ime) {
getContext().statManager().addRateData("client.requestLeaseSetDropped", 1, 0);
_log.error("Error sending I2CP message requesting the lease set", ime);
state.setIsSuccessful(false);
_requestState.setIsSuccessful(false);
_runner.setLeaseRequest(null);
_runner.disconnectClient("I2CP error requesting leaseSet");
return;
@@ -100,24 +91,32 @@ class RequestLeaseSetJob extends JobImpl {
*/
private class CheckLeaseRequestStatus extends JobImpl {
private LeaseRequestState _req;
private long _start;
public CheckLeaseRequestStatus(RouterContext enclosingContext, LeaseRequestState state) {
super(enclosingContext);
_req = state;
_start = System.currentTimeMillis();
getTiming().setStartAfter(state.getExpiration());
}
public void runJob() {
if (_runner.isDead()) return;
if (_runner.isDead()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Already dead, dont try to expire the leaseSet lookup");
return;
}
if (_req.getIsSuccessful()) {
// we didn't fail
RequestLeaseSetJob.CheckLeaseRequestStatus.this.getContext().statManager().addRateData("client.requestLeaseSetSuccess", 1, 0);
return;
} else {
RequestLeaseSetJob.CheckLeaseRequestStatus.this.getContext().statManager().addRateData("client.requestLeaseSetTimeout", 1, 0);
if (_log.shouldLog(Log.CRIT))
_log.log(Log.CRIT, "Failed to receive a leaseSet in the time allotted (" + new Date(_req.getExpiration()) + ") for "
if (_log.shouldLog(Log.CRIT)) {
long waited = System.currentTimeMillis() - _start;
_log.log(Log.CRIT, "Failed to receive a leaseSet in the time allotted (" + waited + "): " + _req + " for "
+ _runner.getConfig().getDestination().calculateHash().toBase64());
}
_runner.disconnectClient("Took too long to request leaseSet");
if (_req.getOnFailed() != null)
RequestLeaseSetJob.this.getContext().jobQueue().addJob(_req.getOnFailed());
@@ -30,13 +30,23 @@ import net.i2p.util.Log;
*
*/
public class GarlicMessageBuilder {
public static int estimateAvailableTags(RouterContext ctx, PublicKey key) {
SessionKey curKey = ctx.sessionKeyManager().getCurrentKey(key);
if (curKey == null)
return 0;
return ctx.sessionKeyManager().getAvailableTags(key, curKey);
}
public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config) {
return buildMessage(ctx, config, new SessionKey(), new HashSet());
}
public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags) {
return buildMessage(ctx, config, wrappedKey, wrappedTags, 50);
return buildMessage(ctx, config, wrappedKey, wrappedTags, 100);
}
public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags, int numTagsToDeliver) {
return buildMessage(ctx, config, wrappedKey, wrappedTags, numTagsToDeliver, false);
}
public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags, int numTagsToDeliver, boolean forceElGamal) {
Log log = ctx.logManager().getLog(GarlicMessageBuilder.class);
PublicKey key = config.getRecipientPublicKey();
if (key == null) {
@@ -54,31 +64,33 @@ public class GarlicMessageBuilder {
log.info("Encrypted with public key " + key + " to expire on " + new Date(config.getExpiration()));
SessionKey curKey = ctx.sessionKeyManager().getCurrentKey(key);
SessionTag curTag = null;
if (curKey == null)
curKey = ctx.sessionKeyManager().createSession(key);
SessionTag curTag = ctx.sessionKeyManager().consumeNextAvailableTag(key, curKey);
if (!forceElGamal) {
curTag = ctx.sessionKeyManager().consumeNextAvailableTag(key, curKey);
int availTags = ctx.sessionKeyManager().getAvailableTags(key, curKey);
if (log.shouldLog(Log.DEBUG))
log.debug("Available tags for encryption to " + key + ": " + availTags);
int availTags = ctx.sessionKeyManager().getAvailableTags(key, curKey);
if (log.shouldLog(Log.DEBUG))
log.debug("Available tags for encryption to " + key + ": " + availTags);
if (availTags < 10) { // arbitrary threshold
for (int i = 0; i < numTagsToDeliver; i++)
wrappedTags.add(new SessionTag(true));
if (log.shouldLog(Log.INFO))
log.info("Less than 10 tags are available (" + availTags + "), so we're including more");
} else if (ctx.sessionKeyManager().getAvailableTimeLeft(key, curKey) < 60*1000) {
// if we have > 10 tags, but they expire in under 30 seconds, we want more
for (int i = 0; i < numTagsToDeliver; i++)
wrappedTags.add(new SessionTag(true));
if (log.shouldLog(Log.INFO))
log.info("Tags are almost expired, adding new ones");
} else {
// always tack on at least one more - not necessary.
//wrappedTags.add(new SessionTag(true));
if (availTags < 20) { // arbitrary threshold
for (int i = 0; i < numTagsToDeliver; i++)
wrappedTags.add(new SessionTag(true));
if (log.shouldLog(Log.INFO))
log.info("Less than 20 tags are available (" + availTags + "), so we're including more");
} else if (ctx.sessionKeyManager().getAvailableTimeLeft(key, curKey) < 60*1000) {
// if we have > 20 tags, but they expire in under 30 seconds, we want more
for (int i = 0; i < numTagsToDeliver; i++)
wrappedTags.add(new SessionTag(true));
if (log.shouldLog(Log.INFO))
log.info("Tags are almost expired, adding new ones");
} else {
// always tack on at least one more - not necessary.
//wrappedTags.add(new SessionTag(true));
}
}
wrappedKey.setData(curKey.getData());
return buildMessage(ctx, config, wrappedKey, wrappedTags, key, curKey, curTag);
@@ -107,8 +107,8 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
ctx.statManager().createFrequencyStat("client.sendMessageFailFrequency", "How often does a client fail to send a message?", "ClientMessages", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.sendMessageSize", "How large are messages sent by the client?", "ClientMessages", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.sendAckTime", "How long does it take to get an ACK back from a message?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.timeoutCongestionTunnel", "How lagged our tunnels are when a send times out?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.sendAckTime", "How long does it take to get an ACK back from a message?", "ClientMessages", new long[] { 60*1000l, 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.timeoutCongestionTunnel", "How lagged our tunnels are when a send times out?", "ClientMessages", new long[] { 60*1000l, 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.timeoutCongestionMessage", "How fast we process messages locally when a send times out?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.timeoutCongestionInbound", "How much faster we are receiving data than our average bps when a send times out?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.leaseSetFoundLocally", "How often we tried to look for a leaseSet and found it locally?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
@@ -118,6 +118,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
ctx.statManager().createRateStat("client.dispatchTime", "How long until we've dispatched the message (since we started)?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.dispatchSendTime", "How long the actual dispatching takes?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.dispatchNoTunnels", "How long after start do we run out of tunnels to send/receive with?", "ClientMessages", new long[] { 5*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("client.dispatchNoACK", "How often we send a client message without asking for an ACK?", "ClientMessages", new long[] { 60*1000l, 5*60*1000l, 60*60*1000l });
long timeoutMs = OVERALL_TIMEOUT_MS_DEFAULT;
_clientMessage = msg;
_clientMessageId = msg.getMessageId();
@@ -312,7 +313,12 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
*/
private void send() {
if (_finished) return;
long token = getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE);
boolean wantACK = true;
int existingTags = GarlicMessageBuilder.estimateAvailableTags(getContext(), _leaseSet.getEncryptionKey());
if (existingTags > 30)
wantACK = false;
long token = (wantACK ? getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE) : -1);
PublicKey key = _leaseSet.getEncryptionKey();
SessionKey sessKey = new SessionKey();
Set tags = new HashSet();
@@ -321,7 +327,8 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
replyLeaseSet = getContext().netDb().lookupLeaseSetLocally(_from.calculateHash());
}
_inTunnel = selectInboundTunnel();
if (wantACK)
_inTunnel = selectInboundTunnel();
buildClove();
if (_log.shouldLog(Log.DEBUG))
@@ -331,7 +338,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
_clove, _from.calculateHash(),
_to, _inTunnel,
sessKey, tags,
true, replyLeaseSet);
wantACK, replyLeaseSet);
if (msg == null) {
// set to null if there are no tunnels to ack the reply back through
// (should we always fail for this? or should we send it anyway, even if
@@ -346,9 +353,14 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getJobId() + ": send() - token expected " + token + " to " + _toString);
SendSuccessJob onReply = new SendSuccessJob(getContext(), sessKey, tags);
SendTimeoutJob onFail = new SendTimeoutJob(getContext());
ReplySelector selector = new ReplySelector(token);
SendSuccessJob onReply = null;
SendTimeoutJob onFail = null;
ReplySelector selector = null;
if (wantACK) {
onReply = new SendSuccessJob(getContext(), sessKey, tags);
onFail = new SendTimeoutJob(getContext());
selector = new ReplySelector(token);
}
if (_log.shouldLog(Log.DEBUG))
_log.debug(getJobId() + ": Placing GarlicMessage into the new tunnel message bound for "
@@ -378,6 +390,8 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
_clientMessage = null;
_clove = null;
getContext().statManager().addRateData("client.dispatchPrepareTime", getContext().clock().now() - _start, 0);
if (!wantACK)
getContext().statManager().addRateData("client.dispatchNoACK", 1, 0);
}
private class DispatchJob extends JobImpl {
@@ -396,7 +410,8 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
}
public String getName() { return "Dispatch outbound client message"; }
public void runJob() {
getContext().messageRegistry().registerPending(_selector, _replyFound, _replyTimeout, _timeoutMs);
if (_selector != null)
getContext().messageRegistry().registerPending(_selector, _replyFound, _replyTimeout, _timeoutMs);
if (_log.shouldLog(Log.INFO))
_log.info("Dispatching message to " + _toString + ": " + _msg);
long before = getContext().clock().now();
@@ -120,7 +120,7 @@ public class HandleDatabaseLookupMessageJob extends JobImpl {
} else {
RouterInfo info = getContext().netDb().lookupRouterInfoLocally(_message.getSearchKey());
if ( (info != null) && (info.isCurrent(EXPIRE_DELAY)) ) {
if (isUnreachable(info) && !publishUnreachable()) {
if ( (info.getIdentity().isHidden()) || (isUnreachable(info) && !publishUnreachable()) ) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Not answering a query for a netDb peer who isn't reachable");
Set us = new HashSet(1);
@@ -56,7 +56,8 @@ class FloodfillStoreJob extends StoreJob {
*/
protected void succeed() {
super.succeed();
getContext().jobQueue().addJob(new FloodfillVerifyStoreJob(getContext(), _state.getTarget(), _facade));
if (_state != null)
getContext().jobQueue().addJob(new FloodfillVerifyStoreJob(getContext(), _state.getTarget(), _facade));
}
public String getName() { return "Floodfill netDb store"; }
@@ -110,8 +110,8 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
public final static String PROP_DB_DIR = "router.networkDatabase.dbDir";
public final static String DEFAULT_DB_DIR = "netDb";
/** if we have less than 20 routers left, don't drop any more, even if they're failing or doing bad shit */
private final static int MIN_REMAINING_ROUTERS = 20;
/** if we have less than 5 routers left, don't drop any more, even if they're failing or doing bad shit */
private final static int MIN_REMAINING_ROUTERS = 5;
/**
* dont accept any dbDtore of a router over 24 hours old (unless we dont
@@ -644,6 +644,11 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
_log.warn("Invalid routerInfo signature! forged router structure! router = " + routerInfo);
return "Invalid routerInfo signature on " + key.toBase64();
} else if (!routerInfo.isCurrent(ROUTER_INFO_EXPIRATION)) {
if (routerInfo.getNetworkId() != Router.NETWORK_ID) {
_context.shitlist().shitlistRouter(key, "Peer is not in our network");
return "Peer is not in our network (" + routerInfo.getNetworkId() + ", wants "
+ Router.NETWORK_ID + "): " + routerInfo.calculateHash().toBase64();
}
long age = _context.clock().now() - routerInfo.getPublished();
int existing = _kb.size();
if (existing >= MIN_REMAINING_ROUTERS) {
@@ -713,18 +718,22 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
isRouterInfo = true;
if (isRouterInfo) {
int remaining = _kb.size();
if (remaining < MIN_REMAINING_ROUTERS) {
if (_log.shouldLog(Log.ERROR))
_log.error("Not removing " + dbEntry + " because we have so few routers left ("
+ remaining + ") - perhaps a reseed is necessary?");
return;
}
if (System.currentTimeMillis() < _started + DONT_FAIL_PERIOD) {
if (_log.shouldLog(Log.WARN))
_log.warn("Not failing the key " + dbEntry.toBase64()
+ " since we've just started up and don't want to drop /everyone/");
return;
if (((RouterInfo)o).getNetworkId() != Router.NETWORK_ID) {
// definitely drop them
} else {
int remaining = _kb.size();
if (remaining < MIN_REMAINING_ROUTERS) {
if (_log.shouldLog(Log.WARN))
_log.warn("Not removing " + dbEntry + " because we have so few routers left ("
+ remaining + ") - perhaps a reseed is necessary?");
return;
}
if (System.currentTimeMillis() < _started + DONT_FAIL_PERIOD) {
if (_log.shouldLog(Log.WARN))
_log.warn("Not failing the key " + dbEntry.toBase64()
+ " since we've just started up and don't want to drop /everyone/");
return;
}
}
_context.peerManager().removeCapabilities(dbEntry);
@@ -18,8 +18,7 @@ import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.*;
import net.i2p.router.RouterContext;
import net.i2p.router.peermanager.PeerProfile;
import net.i2p.stat.Rate;
@@ -120,7 +119,10 @@ class PeerSelector {
return;
if (_toIgnore.contains(entry))
return;
if (_context.netDb().lookupRouterInfoLocally(entry) == null)
RouterInfo info = _context.netDb().lookupRouterInfoLocally(entry);
if (info == null)
return;
if (info.getIdentity().isHidden())
return;
BigInteger diff = getDistance(_key, entry);
@@ -22,6 +22,7 @@ import net.i2p.data.Hash;
import net.i2p.data.LeaseSet;
import net.i2p.data.RouterInfo;
import net.i2p.router.JobImpl;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
@@ -346,11 +347,18 @@ class PersistentDataStore extends TransientDataStore {
fis = new FileInputStream(_routerFile);
RouterInfo ri = new RouterInfo();
ri.readBytes(fis);
try {
_facade.store(ri.getIdentity().getHash(), ri);
} catch (IllegalArgumentException iae) {
_log.info("Refused locally loaded routerInfo - deleting");
if (ri.getNetworkId() != Router.NETWORK_ID) {
corrupt = true;
if (_log.shouldLog(Log.WARN))
_log.warn("The router is from a different network: "
+ ri.getIdentity().calculateHash().toBase64());
} else {
try {
_facade.store(ri.getIdentity().getHash(), ri);
} catch (IllegalArgumentException iae) {
_log.info("Refused locally loaded routerInfo - deleting");
corrupt = true;
}
}
} catch (DataFormatException dfe) {
_log.warn("Error reading the routerInfo from " + _routerFile.getAbsolutePath(), dfe);
@@ -158,8 +158,10 @@ class StoreJob extends JobImpl {
} else {
int peerTimeout = _facade.getPeerTimeout(peer);
PeerProfile prof = getContext().profileOrganizer().getProfile(peer);
RateStat failing = prof.getDBHistory().getFailedLookupRate();
Rate failed = failing.getRate(60*60*1000);
if (prof != null) {
RateStat failing = prof.getDBHistory().getFailedLookupRate();
Rate failed = failing.getRate(60*60*1000);
}
//long failedCount = failed.getCurrentEventCount()+failed.getLastEventCount();
//if (failedCount > 10) {
// _state.addSkipped(peer);
@@ -18,6 +18,7 @@ import java.util.Set;
import java.util.TreeSet;
import net.i2p.data.Hash;
import net.i2p.data.RouterInfo;
import net.i2p.router.RouterContext;
import net.i2p.router.NetworkDatabaseFacade;
import net.i2p.stat.Rate;
@@ -791,10 +792,17 @@ public class ProfileOrganizer {
return false; // never select a shitlisted peer
}
if (null != netDb.lookupRouterInfoLocally(peer)) {
if (_log.shouldLog(Log.INFO))
_log.info("Peer " + peer.toBase64() + " is locally known, allowing its use");
return true;
RouterInfo info = netDb.lookupRouterInfoLocally(peer);
if (null != info) {
if (info.getIdentity().isHidden()) {
if (_log.shouldLog(Log.WARN))
_log.warn("Peer " + peer.toBase64() + " is marked as hidden, disallowing its use");
return false;
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Peer " + peer.toBase64() + " is locally known, allowing its use");
return true;
}
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("Peer " + peer.toBase64() + " is NOT locally known, disallowing its use");
@@ -63,9 +63,7 @@ public class CreateRouterInfoJob extends JobImpl {
info.setPeers(new HashSet());
info.setPublished(getCurrentPublishDate(getContext()));
RouterIdentity ident = new RouterIdentity();
Certificate cert = new Certificate();
cert.setCertificateType(Certificate.CERTIFICATE_TYPE_NULL);
cert.setPayload(null);
Certificate cert = getContext().router().createCertificate();
ident.setCertificate(cert);
PublicKey pubkey = null;
PrivateKey privkey = null;
@@ -106,7 +106,8 @@ public class RebuildRouterInfoJob extends JobImpl {
SigningPublicKey signingPubKey = new SigningPublicKey();
signingPubKey.readBytes(fis);
RouterIdentity ident = new RouterIdentity();
ident.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
Certificate cert = getContext().router().createCertificate();
ident.setCertificate(cert);
ident.setPublicKey(pubkey);
ident.setSigningPublicKey(signingPubKey);
info.setIdentity(ident);
@@ -77,6 +77,7 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
public short getReachabilityStatus() {
if (_manager == null) return CommSystemFacade.STATUS_UNKNOWN;
if (_context.router().isHidden()) return CommSystemFacade.STATUS_OK;
return _manager.getReachabilityStatus();
}
public void recheckReachability() { _manager.recheckReachability(); }
@@ -109,6 +110,7 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
private final static String PROP_I2NP_TCP_DISABLED = "i2np.tcp.disable";
private RouterAddress createTCPAddress() {
if (true) return null;
RouterAddress addr = new RouterAddress();
addr.setCost(10);
addr.setExpiration(null);
@@ -117,7 +119,7 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
String port = _context.router().getConfigSetting(PROP_I2NP_TCP_PORT);
String disabledStr = _context.router().getConfigSetting(PROP_I2NP_TCP_DISABLED);
boolean disabled = false;
if ( (disabledStr != null) && ("true".equalsIgnoreCase(disabledStr)) )
if ( (disabledStr == null) || ("true".equalsIgnoreCase(disabledStr)) )
return null;
if ( (name == null) || (port == null) ) {
//_log.info("TCP Host/Port not specified in config file - skipping TCP transport");
@@ -208,10 +208,13 @@ public abstract class TransportImpl implements Transport {
if (log) {
String type = msg.getMessageType();
// the udp transport logs some further details
/*
_context.messageHistory().sendMessage(type, msg.getMessageId(),
msg.getExpiration(),
msg.getTarget().getIdentity().getHash(),
sendSuccessful);
*/
}
long now = _context.clock().now();
@@ -58,7 +58,7 @@ public class TransportManager implements TransportEventListener {
private void configTransports() {
String disableTCP = _context.router().getConfigSetting(PROP_DISABLE_TCP);
if ( (disableTCP != null) && (Boolean.TRUE.toString().equalsIgnoreCase(disableTCP)) ) {
if ( true || (disableTCP == null) || (Boolean.TRUE.toString().equalsIgnoreCase(disableTCP)) ) {
_log.info("Explicitly disabling the TCP transport!");
} else {
Transport t = new TCPTransport(_context);
@@ -84,7 +84,7 @@ public class VMCommSystem extends CommSystemFacade {
if (true) {
I2NPMessage dmsg = msg.getMessage();
String type = dmsg.getClass().getName();
_context.messageHistory().sendMessage(type, dmsg.getUniqueId(), dmsg.getMessageExpiration(), msg.getTarget().getIdentity().getHash(), sendSuccessful);
_context.messageHistory().sendMessage(type, dmsg.getUniqueId(), dmsg.getMessageExpiration(), msg.getTarget().getIdentity().getHash(), sendSuccessful, null);
}
msg.discardData();
@@ -122,7 +122,12 @@ public class EstablishmentManager {
public void establish(OutNetMessage msg) {
RouterAddress ra = msg.getTarget().getTargetAddress(_transport.getStyle());
if (ra == null) {
_transport.failed(msg);
_transport.failed(msg, "Remote peer has no address, cannot establish");
return;
}
if (msg.getTarget().getNetworkId() != Router.NETWORK_ID) {
_context.shitlist().shitlistRouter(msg.getTarget().getIdentity().calculateHash());
_transport.failed(msg, "Remote peer is on the wrong network, cannot establish");
return;
}
UDPAddress addr = new UDPAddress(ra);
@@ -133,7 +138,7 @@ public class EstablishmentManager {
to = new RemoteHostId(remAddr.getAddress(), port);
if (!_transport.isValid(to.getIP())) {
_transport.failed(msg);
_transport.failed(msg, "Remote peer's IP isn't valid");
_context.shitlist().shitlistRouter(msg.getTarget().getIdentity().calculateHash(), "Invalid SSU address");
return;
}
@@ -294,7 +299,7 @@ public class EstablishmentManager {
// _log.log(Log.CRIT, "Admitted " + admitted + " with " + remaining + " remaining queued and " + active + " active");
if (_log.shouldLog(Log.INFO))
_log.info("Outbound established completely! yay");
_log.info("Outbound established completely! yay: " + state);
PeerState peer = handleCompletelyEstablished(state);
notifyActivity();
return peer;
@@ -316,7 +321,7 @@ public class EstablishmentManager {
RouterAddress ra = msg.getTarget().getTargetAddress(_transport.getStyle());
if (ra == null) {
for (int i = 0; i < queued.size(); i++)
_transport.failed((OutNetMessage)queued.get(i));
_transport.failed((OutNetMessage)queued.get(i), "Cannot admit to the queue, as it has no address");
continue;
}
UDPAddress addr = new UDPAddress(ra);
@@ -354,8 +359,6 @@ public class EstablishmentManager {
*
*/
private void handleCompletelyEstablished(InboundEstablishState state) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Handle completely established (inbound): " + state.getRemoteHostId().toString());
long now = _context.clock().now();
RouterIdentity remote = state.getConfirmedIdentity();
PeerState peer = new PeerState(_context);
@@ -369,6 +372,11 @@ public class EstablishmentManager {
peer.setRemotePeer(remote.calculateHash());
peer.setWeRelayToThemAs(state.getSentRelayTag());
peer.setTheyRelayToUsAs(0);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Handle completely established (inbound): " + state.getRemoteHostId().toString()
+ " - " + peer.getRemotePeer().toBase64());
//if (true) // for now, only support direct
// peer.setRemoteRequiresIntroduction(false);
@@ -377,7 +385,7 @@ public class EstablishmentManager {
_transport.inboundConnectionReceived();
_context.statManager().addRateData("udp.inboundEstablishTime", state.getLifetime(), 0);
sendOurInfo(peer);
sendOurInfo(peer, true);
}
/**
@@ -386,8 +394,6 @@ public class EstablishmentManager {
*
*/
private PeerState handleCompletelyEstablished(OutboundEstablishState state) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Handle completely established (outbound): " + state.getRemoteHostId().toString());
long now = _context.clock().now();
RouterIdentity remote = state.getRemoteIdentity();
PeerState peer = new PeerState(_context);
@@ -402,10 +408,15 @@ public class EstablishmentManager {
peer.setTheyRelayToUsAs(state.getReceivedRelayTag());
peer.setWeRelayToThemAs(0);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Handle completely established (outbound): " + state.getRemoteHostId().toString()
+ " - " + peer.getRemotePeer().toBase64());
_transport.addRemotePeerState(peer);
_context.statManager().addRateData("udp.outboundEstablishTime", state.getLifetime(), 0);
sendOurInfo(peer);
sendOurInfo(peer, false);
int i = 0;
while (true) {
@@ -414,7 +425,7 @@ public class EstablishmentManager {
break;
if (now - Router.CLOCK_FUDGE_FACTOR > msg.getExpiration()) {
msg.timestamp("took too long but established...");
_transport.failed(msg);
_transport.failed(msg, "Took too long to establish, but it was established");
} else {
msg.timestamp("session fully established and sent " + i);
_transport.send(msg);
@@ -424,9 +435,10 @@ public class EstablishmentManager {
return peer;
}
private void sendOurInfo(PeerState peer) {
private void sendOurInfo(PeerState peer, boolean isInbound) {
if (_log.shouldLog(Log.INFO))
_log.info("Publishing to the peer after confirm: " + peer);
_log.info("Publishing to the peer after confirm: " +
(isInbound ? " inbound con from " + peer : "outbound con to " + peer));
DatabaseStoreMessage m = new DatabaseStoreMessage(_context);
m.setKey(_context.routerHash());
@@ -765,7 +777,7 @@ public class EstablishmentManager {
OutNetMessage msg = outboundState.getNextQueuedMessage();
if (msg == null)
break;
_transport.failed(msg);
_transport.failed(msg, "Expired during failed establish");
}
String err = null;
switch (outboundState.getState()) {
@@ -32,8 +32,8 @@ public class InboundMessageFragments /*implements UDPTransport.PartialACKSource
private MessageReceiver _messageReceiver;
private boolean _alive;
/** decay the recently completed every 2 minutes */
private static final int DECAY_PERIOD = 120*1000;
/** decay the recently completed every 20 seconds */
private static final int DECAY_PERIOD = 10*1000;
public InboundMessageFragments(RouterContext ctx, OutboundMessageFragments outbound, UDPTransport transport) {
_context = ctx;
@@ -57,7 +57,7 @@ public class InboundMessageFragments /*implements UDPTransport.PartialACKSource
// may want to extend the DecayingBloomFilter so we can use a smaller
// array size (currently its tuned for 10 minute rates for the
// messageValidator)
_recentlyCompletedMessages = new DecayingBloomFilter(_context, DECAY_PERIOD, 8);
_recentlyCompletedMessages = new DecayingBloomFilter(_context, DECAY_PERIOD, 4);
_ackSender.startup();
_messageReceiver.startup();
}
@@ -114,6 +114,7 @@ public class InboundMessageFragments /*implements UDPTransport.PartialACKSource
_log.warn("Message received is a dup: " + mid + " dups: "
+ _recentlyCompletedMessages.getCurrentDuplicateCount() + " out of "
+ _recentlyCompletedMessages.getInsertedCount());
_context.messageHistory().droppedInboundMessage(mid, from.getRemotePeer(), "dup");
continue;
}
@@ -162,6 +163,7 @@ public class InboundMessageFragments /*implements UDPTransport.PartialACKSource
state.releaseResources();
if (_log.shouldLog(Log.WARN))
_log.warn("Message expired while only being partially read: " + state);
_context.messageHistory().droppedInboundMessage(state.getMessageId(), state.getFrom(), "expired hile partially read: " + state.toString());
} else if (partialACK) {
// not expired but not yet complete... lets queue up a partial ACK
if (_log.shouldLog(Log.DEBUG))
@@ -165,10 +165,18 @@ public class InboundMessageState {
public String toString() {
StringBuffer buf = new StringBuffer(32);
buf.append("Message: ").append(_messageId);
//if (isComplete()) {
// buf.append(" completely received with ");
// buf.append(getCompleteSize()).append(" bytes");
//}
if (isComplete()) {
buf.append(" completely received with ");
buf.append(getCompleteSize()).append(" bytes");
} else {
for (int i = 0; (_fragments != null) && (i < _fragments.length); i++) {
buf.append(" fragment ").append(i);
if (_fragments[i] != null)
buf.append(": known at size ").append(_fragments[i].getValid());
else
buf.append(": unknown");
}
}
buf.append(" lifetime: ").append(getLifetime());
return buf.toString();
}
@@ -82,13 +82,23 @@ public class IntroductionManager {
}
public void receiveRelayIntro(RemoteHostId bob, UDPPacketReader reader) {
if (_context.router().isHidden())
return;
if (_log.shouldLog(Log.INFO))
_log.info("Receive relay intro from " + bob);
_context.statManager().addRateData("udp.receiveRelayIntro", 1, 0);
_transport.send(_builder.buildHolePunch(reader));
}
public void receiveRelayRequest(RemoteHostId alice, UDPPacketReader reader) {
if (_context.router().isHidden())
return;
long tag = reader.getRelayRequestReader().readTag();
PeerState charlie = _transport.getPeerState(tag);
if (_log.shouldLog(Log.INFO))
_log.info("Receive relay request from " + alice
+ " for tag " + tag
+ " and relaying with " + charlie);
if (charlie == null)
return;
byte key[] = new byte[SessionKey.KEYSIZE_BYTES];
@@ -115,9 +115,11 @@ public class MessageReceiver implements Runnable {
} catch (I2NPMessageException ime) {
if (_log.shouldLog(Log.WARN))
_log.warn("Message invalid: " + state, ime);
_context.messageHistory().droppedInboundMessage(state.getMessageId(), state.getFrom(), "error: " + ime.toString() + ": " + state.toString());
return null;
} catch (Exception e) {
_log.log(Log.CRIT, "Error dealing with a message: " + state, e);
_context.messageHistory().droppedInboundMessage(state.getMessageId(), state.getFrom(), "error: " + e.toString() + ": " + state.toString());
return null;
} finally {
state.releaseResources();
@@ -75,7 +75,7 @@ public class OutboundMessageFragments {
_context.statManager().createRateStat("udp.sendPiggyback", "How many acks were piggybacked on a data packet (time == message lifetime)", "udp", new long[] { 60*1000, 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("udp.sendPiggybackPartial", "How many partial acks were piggybacked on a data packet (time == message lifetime)", "udp", new long[] { 60*1000, 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("udp.activeDelay", "How often we wait blocking on the active queue", "udp", new long[] { 60*1000, 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("udp.packetsRetransmitted", "How many packets have been retransmitted (lifetime) when a burst of packets are retransmitted (period == packets transmitted, lifetime)", "udp", new long[] { 60*1000, 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("udp.packetsRetransmitted", "Lifetime of packets during their retransmission (period == packets transmitted, lifetime)", "udp", new long[] { 60*1000, 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("udp.peerPacketsRetransmitted", "How many packets have been retransmitted to the peer (lifetime) when a burst of packets are retransmitted (period == packets transmitted, lifetime)", "udp", new long[] { 60*1000, 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("udp.blockedRetransmissions", "How packets have been transmitted to the peer when we blocked a retransmission to them?", "udp", new long[] { 60*1000, 10*60*1000, 60*60*1000, 24*60*60*1000 });
}
@@ -97,7 +97,7 @@ public class OutboundMessageFragments {
public boolean waitForMoreAllowed() {
// test without choking.
// perhaps this should check the lifetime of the first activeMessage?
if (false) return true;
if (true) return true;
long start = _context.clock().now();
int numActive = 0;
@@ -173,7 +173,7 @@ public class OutboundMessageFragments {
if (state.isComplete()) {
_activeMessages.remove(i);
locked_removeRetransmitter(state);
_transport.succeeded(state.getMessage());
_transport.succeeded(state);
if ( (peer != null) && (peer.getSendWindowBytesRemaining() > 0) )
_throttle.unchoke(peer.getRemotePeer());
state.releaseResources();
@@ -299,8 +299,8 @@ public class OutboundMessageFragments {
if (state.getMessage() != null)
state.getMessage().timestamp("peer disconnected");
_transport.failed(state);
if (_log.shouldLog(Log.WARN))
_log.warn("Peer disconnected for " + state);
if (_log.shouldLog(Log.ERROR))
_log.error("Peer disconnected for " + state);
if ( (peer != null) && (peer.getSendWindowBytesRemaining() > 0) )
_throttle.unchoke(peer.getRemotePeer());
state.releaseResources();
@@ -403,7 +403,7 @@ public class OutboundMessageFragments {
}
int size = state.getUnackedSize();
if (peer.allocateSendingBytes(size)) {
if (peer.allocateSendingBytes(size, state.getPushCount())) {
if (_log.shouldLog(Log.INFO))
_log.info("Allocation of " + size + " allowed with "
+ peer.getSendWindowBytesRemaining()
@@ -413,6 +413,7 @@ public class OutboundMessageFragments {
if (state.getPushCount() > 0) {
_retransmitters.put(peer, state);
/*
int fragments = state.getFragmentCount();
int toSend = 0;
@@ -428,6 +429,7 @@ public class OutboundMessageFragments {
if (_log.shouldLog(Log.WARN))
_log.warn("Retransmitting " + state + " to " + peer);
_context.statManager().addRateData("udp.sendVolleyTime", state.getLifetime(), toSend);
*/
}
state.push();
@@ -482,7 +484,14 @@ public class OutboundMessageFragments {
UDPPacket rv[] = new UDPPacket[fragments]; //sparse
for (int i = 0; i < fragments; i++) {
if (state.needsSending(i)) {
rv[i] = _builder.buildPacket(state, i, peer, remaining, partialACKBitfields);
try {
rv[i] = _builder.buildPacket(state, i, peer, remaining, partialACKBitfields);
} catch (ArrayIndexOutOfBoundsException aioobe) {
_log.log(Log.CRIT, "Corrupt trying to build a packet - please tell jrandom: " +
partialACKBitfields + " / " + remaining + " / " + msgIds);
sparseCount++;
continue;
}
if (rv[i] == null) {
sparseCount++;
continue;
@@ -520,6 +529,16 @@ public class OutboundMessageFragments {
if (_log.shouldLog(Log.INFO))
_log.info("Building packet for " + state + " to " + peer + " with sparse count: " + sparseCount);
peer.packetsTransmitted(fragments - sparseCount);
if (state.getPushCount() > 1) {
int toSend = fragments-sparseCount;
peer.messageRetransmitted(toSend);
_packetsRetransmitted += toSend; // lifetime for the transport
_context.statManager().addRateData("udp.peerPacketsRetransmitted", peer.getPacketsRetransmitted(), peer.getPacketsTransmitted());
_context.statManager().addRateData("udp.packetsRetransmitted", state.getLifetime(), peer.getPacketsTransmitted());
if (_log.shouldLog(Log.WARN))
_log.warn("Retransmitting " + state + " to " + peer);
_context.statManager().addRateData("udp.sendVolleyTime", state.getLifetime(), toSend);
}
return rv;
} else {
// !alive
@@ -595,7 +614,7 @@ public class OutboundMessageFragments {
_context.statManager().addRateData("udp.sendConfirmFragments", state.getFragmentCount(), state.getLifetime());
if (numSends > 1)
_context.statManager().addRateData("udp.sendConfirmVolley", numSends, state.getFragmentCount());
_transport.succeeded(state.getMessage());
_transport.succeeded(state);
int numFragments = state.getFragmentCount();
PeerState peer = state.getPeer();
if (peer != null) {
@@ -682,7 +701,7 @@ public class OutboundMessageFragments {
_context.statManager().addRateData("udp.sendConfirmVolley", numSends, state.getFragmentCount());
if (state.getMessage() != null)
state.getMessage().timestamp("partial ack to complete after " + numSends);
_transport.succeeded(state.getMessage());
_transport.succeeded(state);
if (state.getPeer() != null) {
// this adjusts the rtt/rto/window/etc
@@ -175,7 +175,7 @@ public class OutboundMessageState {
// stupid brute force, but the cardinality should be trivial
short sends[] = _fragmentSends;
if (sends != null)
for (int i = 0; i < bitfield.fragmentCount(); i++)
for (int i = 0; i < bitfield.fragmentCount() && i < sends.length; i++)
if (bitfield.received(i))
sends[i] = (short)-1;
@@ -57,9 +57,12 @@ public class PacketBuilder {
StringBuffer msg = null;
boolean acksIncluded = false;
if (_log.shouldLog(Log.WARN)) {
if (_log.shouldLog(Log.INFO)) {
msg = new StringBuffer(128);
msg.append("building data packet with acks to ").append(peer.getRemotePeer().toBase64().substring(0,6));
msg.append("Send to ").append(peer.getRemotePeer().toBase64());
msg.append(" msg ").append(state.getMessageId()).append(":").append(fragment);
if (fragment == state.getFragmentCount() - 1)
msg.append("*");
}
byte data[] = packet.getPacket().getData();
@@ -136,7 +139,7 @@ public class PacketBuilder {
}
if ( (msg != null) && (acksIncluded) )
_log.warn(msg.toString());
_log.debug(msg.toString());
DataHelper.toLong(data, off, 1, 1); // only one fragment in this message
off++;
@@ -181,6 +184,11 @@ public class PacketBuilder {
packet.getPacket().setLength(off);
authenticate(packet, peer.getCurrentCipherKey(), peer.getCurrentMACKey());
setTo(packet, peer.getRemoteIPAddress(), peer.getRemotePort());
if (_log.shouldLog(Log.INFO)) {
_log.info(msg.toString());
}
return packet;
}
@@ -193,7 +201,7 @@ public class PacketBuilder {
UDPPacket packet = UDPPacket.acquire(_context);
StringBuffer msg = null;
if (_log.shouldLog(Log.WARN)) {
if (_log.shouldLog(Log.DEBUG)) {
msg = new StringBuffer(128);
msg.append("building ACK packet to ").append(peer.getRemotePeer().toBase64().substring(0,6));
}
@@ -270,7 +278,7 @@ public class PacketBuilder {
off++;
if (msg != null)
_log.warn(msg.toString());
_log.debug(msg.toString());
// we can pad here if we want, maybe randomized?
@@ -34,7 +34,7 @@ public class PacketHandler {
private boolean _keepReading;
private List _handlers;
private static final int NUM_HANDLERS = 3;
private static final int NUM_HANDLERS = 5;
/** let packets be up to 30s slow */
private static final long GRACE_PERIOD = Router.CLOCK_FUDGE_FACTOR + 30*1000;
@@ -60,6 +60,8 @@ public class PacketHandler {
_context.statManager().createRateStat("udp.droppedInvalidEstablish", "How old the packet we dropped due to invalidity (establishment, bad key) was", "udp", new long[] { 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("udp.droppedInvalidInboundEstablish", "How old the packet we dropped due to invalidity (inbound establishment, bad key) was", "udp", new long[] { 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("udp.droppedInvalidSkew", "How skewed the packet we dropped due to invalidity (valid except bad skew) was", "udp", new long[] { 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("udp.packetDequeueTime", "How long it takes the UDPReader to pull a packet off the inbound packet queue (when its slow)", "udp", new long[] { 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("udp.packetVerifyTime", "How long it takes the PacketHandler to verify a data packet after dequeueing (when its slow)", "udp", new long[] { 10*60*1000, 60*60*1000 });
}
public void startup() {
@@ -101,8 +103,9 @@ public class PacketHandler {
UDPPacket packet = _endpoint.receive();
_state = 3;
if (packet == null) continue; // keepReading is probably false...
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received the packet " + packet);
packet.received();
if (_log.shouldLog(Log.INFO))
_log.info("Received the packet " + packet);
_state = 4;
long queueTime = packet.getLifetime();
long handleStart = _context.clock().now();
@@ -116,16 +119,30 @@ public class PacketHandler {
_log.error("Crazy error handling a packet: " + packet, e);
}
long handleTime = _context.clock().now() - handleStart;
packet.afterHandling();
_context.statManager().addRateData("udp.handleTime", handleTime, packet.getLifetime());
_context.statManager().addRateData("udp.queueTime", queueTime, packet.getLifetime());
_state = 8;
if (_log.shouldLog(Log.INFO))
_log.info("Done receiving the packet " + packet);
if (handleTime > 1000) {
if (_log.shouldLog(Log.WARN))
_log.warn("Took " + handleTime + " to process the packet "
+ packet + ": " + _reader);
}
long timeToDequeue = packet.getTimeSinceEnqueue() - packet.getTimeSinceReceived();
long timeToVerify = 0;
long beforeRecv = packet.getTimeSinceReceiveFragments();
if (beforeRecv > 0)
timeToVerify = beforeRecv - packet.getTimeSinceReceived();
if (timeToDequeue > 50)
_context.statManager().addRateData("udp.packetDequeueTime", timeToDequeue, timeToDequeue);
if (timeToVerify > 50)
_context.statManager().addRateData("udp.packetVerifyTime", timeToVerify, timeToVerify);
// back to the cache with thee!
packet.release();
_state = 9;
@@ -396,7 +413,22 @@ public class PacketHandler {
state = _establisher.receiveData(outState);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received new DATA packet from " + state + ": " + packet);
_inbound.receiveData(state, reader.getDataReader());
UDPPacketReader.DataReader dr = reader.getDataReader();
if (_log.shouldLog(Log.INFO)) {
StringBuffer msg = new StringBuffer();
msg.append("Receive ").append(System.identityHashCode(packet));
msg.append(" from ").append(state.getRemotePeer().toBase64()).append(" ").append(state.getRemoteHostId());
for (int i = 0; i < dr.readFragmentCount(); i++) {
msg.append(" msg ").append(dr.readMessageId(i));
msg.append(":").append(dr.readMessageFragmentNum(i));
if (dr.readMessageIsLast(i))
msg.append("*");
}
msg.append(": ").append(dr.toString());
_log.info(msg.toString());
}
packet.beforeReceiveFragments();
_inbound.receiveData(state, dr);
break;
case UDPPacket.PAYLOAD_TYPE_TEST:
_state = 51;
@@ -14,6 +14,7 @@ import net.i2p.I2PAppContext;
import net.i2p.data.Hash;
import net.i2p.data.SessionKey;
import net.i2p.util.Log;
import net.i2p.router.RouterContext;
/**
* Contain all of the state about a UDP connection to a peer.
@@ -168,6 +169,9 @@ public class PeerState {
/** Message (Long) to InboundMessageState for active message */
private Map _inboundMessages;
/** have we migrated away from this peer to another newer one? */
private volatile boolean _dead;
private static final int DEFAULT_SEND_WINDOW_BYTES = 8*1024;
private static final int MINIMUM_WINDOW_BYTES = DEFAULT_SEND_WINDOW_BYTES;
private static final int MAX_SEND_WINDOW_BYTES = 1024*1024;
@@ -188,8 +192,8 @@ public class PeerState {
*/
private static final int LARGE_MTU = 1350;
private static final int MIN_RTO = 500 + ACKSender.ACK_FREQUENCY;
private static final int MAX_RTO = 2500; // 5000;
private static final int MIN_RTO = 100 + ACKSender.ACK_FREQUENCY;
private static final int MAX_RTO = 1200; // 5000;
/** override the default MTU */
private static final String PROP_DEFAULT_MTU = "i2np.udp.mtu";
@@ -241,6 +245,7 @@ public class PeerState {
_packetsReceived = 0;
_packetsReceivedDuplicate = 0;
_inboundMessages = new HashMap(8);
_dead = false;
_context.statManager().createRateStat("udp.congestionOccurred", "How large the cwin was when congestion occurred (duration == sendBps)", "udp", new long[] { 60*1000, 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("udp.congestedRTO", "retransmission timeout after congestion (duration == rtt dev)", "udp", new long[] { 60*1000, 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("udp.sendACKPartial", "Number of partial ACKs sent (duration == number of full ACKs in that ack packet)", "udp", new long[] { 60*1000, 10*60*1000, 60*60*1000, 24*60*60*1000 });
@@ -433,14 +438,21 @@ public class PeerState {
* the previous second's ACKs be sent?
*/
public void remoteDoesNotWantPreviousACKs() { _remoteWantsPreviousACKs = false; }
/** should we ignore the peer state's congestion window, and let anything through? */
private static final boolean IGNORE_CWIN = false;
/** should we ignore the congestion window on the first push of every message? */
private static final boolean ALWAYS_ALLOW_FIRST_PUSH = false;
/**
* Decrement the remaining bytes in the current period's window,
* returning true if the full size can be decremented, false if it
* cannot. If it is not decremented, the window size remaining is
* not adjusted at all.
*/
public boolean allocateSendingBytes(int size) { return allocateSendingBytes(size, false); }
public boolean allocateSendingBytes(int size, boolean isForACK) {
public boolean allocateSendingBytes(int size, int messagePushCount) { return allocateSendingBytes(size, false, messagePushCount); }
public boolean allocateSendingBytes(int size, boolean isForACK) { return allocateSendingBytes(size, isForACK, -1); }
public boolean allocateSendingBytes(int size, boolean isForACK, int messagePushCount) {
long now = _context.clock().now();
long duration = now - _lastSendRefill;
if (duration >= 1000) {
@@ -456,7 +468,7 @@ public class PeerState {
_lastSendRefill = now;
}
//if (true) return true;
if (size <= _sendWindowBytesRemaining) {
if (IGNORE_CWIN || size <= _sendWindowBytesRemaining || (ALWAYS_ALLOW_FIRST_PUSH && messagePushCount == 0)) {
_sendWindowBytesRemaining -= size;
_sendBytes += size;
_lastSendTime = now;
@@ -541,6 +553,32 @@ public class PeerState {
* Access to this map must be synchronized explicitly!
*/
public Map getInboundMessages() { return _inboundMessages; }
/**
* Expire partially received inbound messages, returning how many are still pending.
* This should probably be fired periodically, in case a peer goes silent and we don't
* try to send them any messages (and don't receive any messages from them either)
*
*/
public int expireInboundMessages() {
int rv = 0;
synchronized (_inboundMessages) {
for (Iterator iter = _inboundMessages.values().iterator(); iter.hasNext(); ) {
InboundMessageState state = (InboundMessageState)iter.next();
if (state.isExpired()) {
iter.remove();
} else {
if (state.isComplete()) {
_log.error("inbound message is complete, but wasn't handled inline? " + state + " with " + this);
iter.remove();
} else {
rv++;
}
}
}
}
return rv;
}
/**
* either they told us to back off, or we had to resend to get
@@ -593,7 +631,7 @@ public class PeerState {
_lastACKSend = _context.clock().now();
}
private static final int MAX_RESEND_ACKS = 8;
private static final int MAX_RESEND_ACKS = 16;
/**
* grab a list of ACKBitfield instances, some of which may fully
@@ -674,6 +712,8 @@ public class PeerState {
for (Iterator iter = _inboundMessages.values().iterator(); iter.hasNext(); ) {
InboundMessageState state = (InboundMessageState)iter.next();
if (state.isExpired()) {
//if (_context instanceof RouterContext)
// ((RouterContext)_context).messageHistory().droppedInboundMessage(state.getMessageId(), state.getFrom(), "expired partially received: " + state.toString());
iter.remove();
} else {
if (!state.isComplete()) {
@@ -877,6 +917,42 @@ public class PeerState {
public RemoteHostId getRemoteHostId() { return _remoteHostId; }
/**
* Transfer the basic activity/state from the old peer to the current peer
*
*/
public void loadFrom(PeerState oldPeer) {
_rto = oldPeer._rto;
_rtt = oldPeer._rtt;
_rttDeviation = oldPeer._rttDeviation;
_slowStartThreshold = oldPeer._slowStartThreshold;
_sendWindowBytes = oldPeer._sendWindowBytes;
oldPeer._dead = true;
List tmp = new ArrayList();
synchronized (oldPeer._currentACKs) {
tmp.addAll(oldPeer._currentACKs);
oldPeer._currentACKs.clear();
}
synchronized (_currentACKs) { _currentACKs.addAll(tmp); }
tmp.clear();
synchronized (oldPeer._currentACKsResend) {
tmp.addAll(oldPeer._currentACKsResend);
oldPeer._currentACKsResend.clear();
}
synchronized (_currentACKsResend) { _currentACKsResend.addAll(tmp); }
tmp.clear();
Map msgs = new HashMap();
synchronized (oldPeer._inboundMessages) {
msgs.putAll(oldPeer._inboundMessages);
oldPeer._inboundMessages.clear();
}
synchronized (_inboundMessages) { _inboundMessages.putAll(msgs); }
}
public int hashCode() {
if (_remotePeer != null)
return _remotePeer.hashCode();
@@ -901,6 +977,17 @@ public class PeerState {
buf.append(_remoteHostId.toString());
if (_remotePeer != null)
buf.append(" ").append(_remotePeer.toBase64().substring(0,6));
long now = _context.clock().now();
buf.append(" recvAge: ").append(now-_lastReceiveTime);
buf.append(" sendAge: ").append(now-_lastSendFullyTime);
buf.append(" sendAttemptAge: ").append(now-_lastSendTime);
buf.append(" sendACKAge: ").append(now-_lastACKSend);
buf.append(" lifetime: ").append(now-_keyEstablishedTime);
buf.append(" cwin: ").append(_sendWindowBytes);
buf.append(" acwin: ").append(_sendWindowBytesRemaining);
buf.append(" recv OK/Dup: ").append(_packetsReceived).append('/').append(_packetsReceivedDuplicate);
buf.append(" send OK/Dup: ").append(_packetsTransmitted).append('/').append(_packetsRetransmitted);
return buf.toString();
}
}
@@ -31,6 +31,7 @@ class PeerTestManager {
private Map _activeTests;
/** current test we are running, or null */
private PeerTestState _currentTest;
private boolean _currentTestComplete;
private List _recentTests;
/** longest we will keep track of a Charlie nonce for */
@@ -44,6 +45,7 @@ class PeerTestManager {
_recentTests = Collections.synchronizedList(new ArrayList(16));
_packetBuilder = new PacketBuilder(context, transport);
_currentTest = null;
_currentTestComplete = false;
_context.statManager().createRateStat("udp.statusKnownCharlie", "How often the bob we pick passes us to a charlie we already have a session with?", "udp", new long[] { 60*1000, 20*60*1000, 60*60*1000 });
}
@@ -62,6 +64,7 @@ class PeerTestManager {
test.setLastSendTime(test.getBeginTime());
test.setOurRole(PeerTestState.ALICE);
_currentTest = test;
_currentTestComplete = false;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Running test with bob = " + bobIP + ":" + bobPort + " " + test.getNonce());
@@ -81,7 +84,7 @@ class PeerTestManager {
// already completed
return;
} else if (expired()) {
testComplete();
testComplete(true);
} else if (_context.clock().now() - state.getLastSendTime() >= RESEND_TIMEOUT) {
if (state.getReceiveBobTime() <= 0) {
// no message from Bob yet, send it again
@@ -98,31 +101,36 @@ class PeerTestManager {
SimpleTimer.getInstance().addEvent(ContinueTest.this, RESEND_TIMEOUT);
}
}
private boolean expired() {
PeerTestState state = _currentTest;
if (state != null)
return _currentTest.getBeginTime() + MAX_TEST_TIME < _context.clock().now();
else
return true;
}
}
private boolean expired() {
PeerTestState state = _currentTest;
if (state != null)
return state.getBeginTime() + MAX_TEST_TIME < _context.clock().now();
else
return true;
}
private void sendTestToBob() {
PeerTestState test = _currentTest;
if (test != null) {
if (!expired()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending test to bob: " + test.getBobIP() + ":" + test.getBobPort());
_transport.send(_packetBuilder.buildPeerTestFromAlice(test.getBobIP(), test.getBobPort(), test.getBobCipherKey(), test.getBobMACKey(), //_bobIntroKey,
test.getNonce(), _transport.getIntroKey()));
} else {
_currentTest = null;
}
}
private void sendTestToCharlie() {
PeerTestState test = _currentTest;
if (test != null) {
if (!expired()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending test to charlie: " + test.getCharlieIP() + ":" + test.getCharliePort());
_transport.send(_packetBuilder.buildPeerTestFromAlice(test.getCharlieIP(), test.getCharliePort(), test.getCharlieIntroKey(),
test.getNonce(), _transport.getIntroKey()));
} else {
_currentTest = null;
}
}
@@ -139,7 +147,10 @@ class PeerTestManager {
*/
private void receiveTestReply(RemoteHostId from, UDPPacketReader.PeerTestReader testInfo) {
PeerTestState test = _currentTest;
if (test == null) return;
if (expired())
return;
if (_currentTestComplete)
return;
if ( (DataHelper.eq(from.getIP(), test.getBobIP().getAddress())) && (from.getPort() == test.getBobPort()) ) {
byte ip[] = new byte[testInfo.readIPSize()];
testInfo.readIP(ip, 0);
@@ -152,7 +163,7 @@ class PeerTestManager {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive test reply from bob @ " + from.getIP() + " via our " + test.getAlicePort() + "/" + test.getAlicePortFromCharlie());
if (test.getAlicePortFromCharlie() > 0)
testComplete();
testComplete(false);
} catch (UnknownHostException uhe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Unable to get our IP from bob's reply: " + from + ", " + testInfo, uhe);
@@ -166,12 +177,11 @@ class PeerTestManager {
if (_log.shouldLog(Log.WARN))
_log.warn("Bob chose a charlie we already have a session to, cancelling the test and rerunning (bob: "
+ _currentTest + ", charlie: " + from + ")");
_currentTest = null;
_context.statManager().addRateData("udp.statusKnownCharlie", 1, 0);
honorStatus(CommSystemFacade.STATUS_UNKNOWN);
return;
}
if (test.getReceiveCharlieTime() > 0) {
// this is our second charlie, yay!
test.setAlicePortFromCharlie(testInfo.readPort());
@@ -184,12 +194,19 @@ class PeerTestManager {
_log.debug("Receive test reply from charlie @ " + test.getCharlieIP() + " via our "
+ test.getAlicePort() + "/" + test.getAlicePortFromCharlie());
if (test.getReceiveBobTime() > 0)
testComplete();
testComplete(false);
} catch (UnknownHostException uhe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Charlie @ " + from + " said we were an invalid IP address: " + uhe.getMessage(), uhe);
}
} else {
if (test.getPacketsRelayed() > MAX_RELAYED_PER_TEST) {
testComplete(false);
if (_log.shouldLog(Log.WARN))
_log.warn("Received too many packets on the test: " + test);
return;
}
// ok, first charlie. send 'em a packet
test.setReceiveCharlieTime(_context.clock().now());
SessionKey charlieIntroKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
@@ -214,10 +231,14 @@ class PeerTestManager {
* we have successfully received the second PeerTest from a Charlie.
*
*/
private void testComplete() {
private void testComplete(boolean forgetTest) {
_currentTestComplete = true;
short status = -1;
PeerTestState test = _currentTest;
if (test == null) return;
if (expired()) {
_currentTest = null;
return;
}
if (test.getAlicePortFromCharlie() > 0) {
// we received a second message from charlie
if ( (test.getAlicePort() == test.getAlicePortFromCharlie()) &&
@@ -243,7 +264,8 @@ class PeerTestManager {
_log.info("Test complete: " + test);
honorStatus(status);
_currentTest = null;
if (forgetTest)
_currentTest = null;
}
/**
@@ -324,6 +346,8 @@ class PeerTestManager {
}
}
private static final int MAX_RELAYED_PER_TEST = 5;
/**
* The packet's IP/port does not match the IP/port included in the message,
* so we must be Charlie receiving a PeerTest from Bob.
@@ -370,6 +394,14 @@ class PeerTestManager {
state.setBobMACKey(bob.getCurrentMACKey());
}
state.incrementPacketsRelayed();
if (state.getPacketsRelayed() > MAX_RELAYED_PER_TEST) {
if (_log.shouldLog(Log.WARN))
_log.warn("Receive from bob (" + from + ") as charlie with alice @ " + aliceIP + ":" + alicePort
+ ", but we've already relayed too many packets to that test, so we're dropping it");
return;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive from bob (" + from + ") as charlie, sending back to bob and sending to alice @ " + aliceIP + ":" + alicePort);
@@ -446,6 +478,14 @@ class PeerTestManager {
state.setOurRole(PeerTestState.BOB);
state.setReceiveAliceTime(_context.clock().now());
state.incrementPacketsRelayed();
if (state.getPacketsRelayed() > MAX_RELAYED_PER_TEST) {
if (_log.shouldLog(Log.WARN))
_log.warn("Receive from alice (" + aliceIP + ":" + from.getPort()
+ ") as bob, but we've already relayed too many packets to that test, so we're dropping it");
return;
}
if (isNew) {
synchronized (_activeTests) {
_activeTests.put(new Long(nonce), state);
@@ -477,6 +517,14 @@ class PeerTestManager {
*/
private void receiveFromCharlieAsBob(RemoteHostId from, PeerTestState state) {
state.setReceiveCharlieTime(_context.clock().now());
state.incrementPacketsRelayed();
if (state.getPacketsRelayed() > MAX_RELAYED_PER_TEST) {
if (_log.shouldLog(Log.WARN))
_log.warn("Received from charlie (" + from + ") as bob (" + state + "), but we've already relayed too many, so drop it");
return;
}
UDPPacket packet = _packetBuilder.buildPeerTestToAlice(state.getAliceIP(), state.getAlicePort(),
state.getAliceIntroKey(), state.getCharlieIntroKey(),
state.getNonce());
@@ -26,6 +26,7 @@ class PeerTestState {
private long _receiveAliceTime;
private long _receiveBobTime;
private long _receiveCharlieTime;
private int _packetsRelayed;
public static final short ALICE = 1;
public static final short BOB = 2;
@@ -91,6 +92,9 @@ class PeerTestState {
public synchronized long getReceiveCharlieTime() { return _receiveCharlieTime; }
public synchronized void setReceiveCharlieTime(long when) { _receiveCharlieTime = when; }
public int getPacketsRelayed() { return _packetsRelayed; }
public void incrementPacketsRelayed() { ++_packetsRelayed; }
public synchronized String toString() {
StringBuffer buf = new StringBuffer(512);
buf.append("Role: ");
@@ -113,6 +117,7 @@ class PeerTestState {
buf.append(" receive from bob after ").append(_receiveBobTime - _beginTime).append("ms");
if (_receiveCharlieTime > 0)
buf.append(" receive from charlie after ").append(_receiveCharlieTime - _beginTime).append("ms");
buf.append(" packets relayed: ").append(_packetsRelayed);
return buf.toString();
}
}
@@ -224,7 +224,7 @@ public class TimedWeightedPriorityMessageQueue implements MessageQueue, Outbound
}
public interface FailedListener {
public void failed(OutNetMessage msg);
public void failed(OutNetMessage msg, String reason);
}
/**
@@ -253,7 +253,7 @@ public class TimedWeightedPriorityMessageQueue implements MessageQueue, Outbound
for (int i = 0; i < removed.size(); i++) {
OutNetMessage m = (OutNetMessage)removed.get(i);
m.timestamp("expirer killed it");
_listener.failed(m);
_listener.failed(m, "expired before getting on the active pool");
}
removed.clear();
@@ -36,6 +36,10 @@ public class UDPPacket {
private volatile boolean _released;
private volatile Exception _releasedBy;
private volatile Exception _acquiredBy;
private long _enqueueTime;
private long _receivedTime;
private long _beforeReceiveFragments;
private long _afterHandlingTime;
private static final List _packetCache;
static {
@@ -194,6 +198,24 @@ public class UDPPacket {
_context.aes().decrypt(_data, _packet.getOffset() + MAC_SIZE + IV_SIZE, _data, _packet.getOffset() + MAC_SIZE + IV_SIZE, cipherKey, iv.getData(), len - MAC_SIZE - IV_SIZE);
_ivCache.release(iv);
}
/** the UDPReceiver has tossed it onto the inbound queue */
void enqueue() { _enqueueTime = _context.clock().now(); }
/** a packet handler has pulled it off the inbound queue */
void received() { _receivedTime = _context.clock().now(); }
/** a packet handler has decrypted and verified the packet and is about to parse out the good bits */
void beforeReceiveFragments() { _beforeReceiveFragments = _context.clock().now(); }
/** a packet handler has finished parsing out the good bits */
void afterHandling() { _afterHandlingTime = _context.clock().now(); }
/** the UDPReceiver has tossed it onto the inbound queue */
long getTimeSinceEnqueue() { return (_enqueueTime > 0 ? _context.clock().now() - _enqueueTime : 0); }
/** a packet handler has pulled it off the inbound queue */
long getTimeSinceReceived() { return (_receivedTime > 0 ? _context.clock().now() - _receivedTime : 0); }
/** a packet handler has decrypted and verified the packet and is about to parse out the good bits */
long getTimeSinceReceiveFragments() { return (_beforeReceiveFragments > 0 ? _context.clock().now() - _beforeReceiveFragments : 0); }
/** a packet handler has finished parsing out the good bits */
long getTimeSinceHandling() { return (_afterHandlingTime > 0 ? _context.clock().now() - _afterHandlingTime : 0); }
public String toString() {
verifyNotReleased();
@@ -203,7 +225,12 @@ public class UDPPacket {
buf.append(_packet.getAddress().getHostAddress()).append(":");
buf.append(_packet.getPort());
buf.append(" id=").append(System.identityHashCode(this));
buf.append("\ndata=").append(Base64.encode(_packet.getData(), _packet.getOffset(), _packet.getLength()));
buf.append(" sinceEnqueued=").append((_enqueueTime > 0 ? _context.clock().now()-_enqueueTime : -1));
buf.append(" sinceReceived=").append((_receivedTime > 0 ? _context.clock().now()-_receivedTime : -1));
buf.append(" beforeReceiveFragments=").append((_beforeReceiveFragments > 0 ? _context.clock().now()-_beforeReceiveFragments : -1));
buf.append(" sinceHandled=").append((_afterHandlingTime > 0 ? _context.clock().now()-_afterHandlingTime : -1));
//buf.append("\ndata=").append(Base64.encode(_packet.getData(), _packet.getOffset(), _packet.getLength()));
return buf.toString();
}
@@ -29,10 +29,13 @@ public class UDPReceiver {
private boolean _keepRunning;
private Runner _runner;
private UDPTransport _transport;
private static int __id;
private int _id;
public UDPReceiver(RouterContext ctx, UDPTransport transport, DatagramSocket socket, String name) {
_context = ctx;
_log = ctx.logManager().getLog(UDPReceiver.class);
_id = ++_id;
_name = name;
_inboundQueue = new ArrayList(128);
_socket = socket;
@@ -48,7 +51,7 @@ public class UDPReceiver {
public void startup() {
adjustDropProbability();
_keepRunning = true;
I2PThread t = new I2PThread(_runner, _name);
I2PThread t = new I2PThread(_runner, _name + "." + _id);
t.setDaemon(true);
t.start();
}
@@ -65,11 +68,11 @@ public class UDPReceiver {
String p = _context.getProperty("i2np.udp.dropProbability");
if (p != null) {
try {
ARTIFICIAL_DROP_PROBABILITY = Float.parseFloat(p);
ARTIFICIAL_DROP_PROBABILITY = Integer.parseInt(p);
} catch (NumberFormatException nfe) {}
if (ARTIFICIAL_DROP_PROBABILITY < 0) ARTIFICIAL_DROP_PROBABILITY = 0;
} else {
ARTIFICIAL_DROP_PROBABILITY = 0;
//ARTIFICIAL_DROP_PROBABILITY = 0;
}
}
@@ -83,12 +86,12 @@ public class UDPReceiver {
}
/** if a packet been sitting in the queue for a full second (meaning the handlers are overwhelmed), drop subsequent packets */
private static final long MAX_QUEUE_PERIOD = 1*1000;
private static final long MAX_QUEUE_PERIOD = 2*1000;
private static float ARTIFICIAL_DROP_PROBABILITY = 0.0f; // 0.02f; // 0.0f;
private static int ARTIFICIAL_DROP_PROBABILITY = 0; // 4
private static final int ARTIFICIAL_DELAY = 0; // 100;
private static final int ARTIFICIAL_DELAY_BASE = 0; //100;
private static final int ARTIFICIAL_DELAY = 0; // 200;
private static final int ARTIFICIAL_DELAY_BASE = 0; //600;
private int receive(UDPPacket packet) {
//adjustDropProbability();
@@ -96,10 +99,10 @@ public class UDPReceiver {
if (ARTIFICIAL_DROP_PROBABILITY > 0) {
// the first check is to let the compiler optimize away this
// random block on the live system when the probability is == 0
int v = _context.random().nextInt(1000);
if (v < ARTIFICIAL_DROP_PROBABILITY*1000) {
int v = _context.random().nextInt(100);
if (v <= ARTIFICIAL_DROP_PROBABILITY) {
if (_log.shouldLog(Log.ERROR))
_log.error("Drop with v=" + v + " p=" + ARTIFICIAL_DROP_PROBABILITY + " packet size: " + packet.getPacket().getLength());
_log.error("Drop with v=" + v + " p=" + ARTIFICIAL_DROP_PROBABILITY + " packet size: " + packet.getPacket().getLength() + ": " + packet);
_context.statManager().addRateData("udp.droppedInboundProbabalistically", 1, 0);
return -1;
} else {
@@ -108,15 +111,20 @@ public class UDPReceiver {
}
if ( (ARTIFICIAL_DELAY > 0) || (ARTIFICIAL_DELAY_BASE > 0) ) {
SimpleTimer.getInstance().addEvent(new ArtificiallyDelayedReceive(packet), ARTIFICIAL_DELAY_BASE + _context.random().nextInt(ARTIFICIAL_DELAY));
long delay = ARTIFICIAL_DELAY_BASE + _context.random().nextInt(ARTIFICIAL_DELAY);
if (_log.shouldLog(Log.INFO))
_log.info("Delay packet " + packet + " for " + delay);
SimpleTimer.getInstance().addEvent(new ArtificiallyDelayedReceive(packet), delay);
return -1;
}
return doReceive(packet);
}
private final int doReceive(UDPPacket packet) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received: " + packet);
if (_log.shouldLog(Log.INFO))
_log.info("Received: " + packet);
packet.enqueue();
boolean rejected = false;
int queueSize = 0;
long headPeriod = 0;
@@ -164,17 +172,16 @@ public class UDPReceiver {
*/
public UDPPacket receiveNext() {
while (_keepRunning) {
try {
synchronized (_inboundQueue) {
if (_inboundQueue.size() > 0) {
UDPPacket rv = (UDPPacket)_inboundQueue.remove(0);
synchronized (_inboundQueue) {
if (_inboundQueue.size() <= 0)
try { _inboundQueue.wait(); } catch (InterruptedException ie) {}
if (_inboundQueue.size() > 0) {
UDPPacket rv = (UDPPacket)_inboundQueue.remove(0);
if (_inboundQueue.size() > 0)
_inboundQueue.notifyAll();
return rv;
} else {
_inboundQueue.wait(500);
}
return rv;
}
} catch (InterruptedException ie) {}
}
}
return null;
}
@@ -185,7 +192,7 @@ public class UDPReceiver {
_socketChanged = false;
while (_keepRunning) {
if (_socketChanged) {
Thread.currentThread().setName(_name);
Thread.currentThread().setName(_name + "." + _id);
_socketChanged = false;
}
UDPPacket packet = UDPPacket.acquire(_context);
@@ -197,14 +204,14 @@ public class UDPReceiver {
try { Thread.sleep(10); } catch (InterruptedException ie) {}
try {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Before blocking socket.receive");
if (_log.shouldLog(Log.INFO))
_log.info("Before blocking socket.receive on " + System.identityHashCode(packet));
synchronized (Runner.this) {
_socket.receive(packet.getPacket());
}
int size = packet.getPacket().getLength();
if (_log.shouldLog(Log.DEBUG))
_log.debug("After blocking socket.receive: packet is " + size + " bytes!");
if (_log.shouldLog(Log.INFO))
_log.info("After blocking socket.receive: packet is " + size + " bytes on " + System.identityHashCode(packet));
packet.resetBegin();
// and block after we know how much we read but before
@@ -185,6 +185,8 @@ public class UDPSender {
}
long sendTime = _context.clock().now() - before;
_context.statManager().addRateData("udp.socketSendTime", sendTime, packet.getLifetime());
if (_log.shouldLog(Log.INFO))
_log.info("Sent the packet " + packet);
long throttleTime = afterBW - acquireTime;
if (throttleTime > 10)
_context.statManager().addRateData("udp.sendBWThrottleTime", throttleTime, acquireTime - packet.getBegin());
@@ -469,15 +469,15 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
boolean addRemotePeerState(PeerState peer) {
if (_log.shouldLog(Log.INFO))
_log.info("Add remote peer state: " + peer);
Hash remotePeer = peer.getRemotePeer();
long oldEstablishedOn = -1;
PeerState oldPeer = null;
if (peer.getRemotePeer() != null) {
if (remotePeer != null) {
synchronized (_peersByIdent) {
oldPeer = (PeerState)_peersByIdent.put(peer.getRemotePeer(), peer);
oldPeer = (PeerState)_peersByIdent.put(remotePeer, peer);
if ( (oldPeer != null) && (oldPeer != peer) ) {
// should we transfer the oldPeer's RTT/RTO/etc? nah
// or perhaps reject the new session? nah,
// using the new one allow easier reconnect
// transfer over the old state/inbound message fragments/etc
peer.loadFrom(oldPeer);
oldEstablishedOn = oldPeer.getKeyEstablishedTime();
}
}
@@ -491,8 +491,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
synchronized (_peersByRemoteHost) {
oldPeer = (PeerState)_peersByRemoteHost.put(remoteId, peer);
if ( (oldPeer != null) && (oldPeer != peer) ) {
//_peersByRemoteHost.put(remoteString, oldPeer);
//return false;
// transfer over the old state/inbound message fragments/etc
peer.loadFrom(oldPeer);
oldEstablishedOn = oldPeer.getKeyEstablishedTime();
}
}
@@ -531,13 +531,56 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
return super.getCurrentAddress();
}
public void messageReceived(I2NPMessage inMsg, RouterIdentity remoteIdent, Hash remoteIdentHash, long msToReceive, int bytesReceived) {
if (inMsg.getType() == DatabaseStoreMessage.MESSAGE_TYPE) {
DatabaseStoreMessage dsm = (DatabaseStoreMessage)inMsg;
if ( (dsm.getRouterInfo() != null) &&
(dsm.getRouterInfo().getNetworkId() != Router.NETWORK_ID) ) {
/*
if (remoteIdentHash != null) {
_context.shitlist().shitlistRouter(remoteIdentHash, "Sent us a peer from the wrong network");
dropPeer(remoteIdentHash);
if (_log.shouldLog(Log.ERROR))
_log.error("Dropping the peer " + remoteIdentHash
+ " because they are in the wrong net");
} else if (remoteIdent != null) {
_context.shitlist().shitlistRouter(remoteIdent.calculateHash(), "Sent us a peer from the wrong network");
dropPeer(remoteIdent.calculateHash());
if (_log.shouldLog(Log.ERROR))
_log.error("Dropping the peer " + remoteIdent.calculateHash()
+ " because they are in the wrong net");
}
*/
_context.shitlist().shitlistRouter(dsm.getRouterInfo().getIdentity().calculateHash(), "Part of the wrong network");
dropPeer(dsm.getRouterInfo().getIdentity().calculateHash());
if (_log.shouldLog(Log.WARN))
_log.warn("Dropping the peer " + dsm.getRouterInfo().getIdentity().calculateHash().toBase64()
+ " because they are in the wrong net");
return;
} else {
if (dsm.getRouterInfo() != null) {
if (_log.shouldLog(Log.INFO))
_log.info("Received an RI from the same net");
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Received a leaseSet: " + dsm);
}
}
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Received another message: " + inMsg.getClass().getName());
}
super.messageReceived(inMsg, remoteIdent, remoteIdentHash, msToReceive, bytesReceived);
}
void dropPeer(Hash peer) {
PeerState state = getPeerState(peer);
if (state != null)
dropPeer(state, false);
}
private void dropPeer(PeerState peer, boolean shouldShitlist) {
if (_log.shouldLog(Log.INFO)) {
if (_log.shouldLog(Log.WARN)) {
long now = _context.clock().now();
StringBuffer buf = new StringBuffer(4096);
long timeSinceSend = now - peer.getLastSendTime();
@@ -574,7 +617,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
buf.append("\n");
}
}
_log.info(buf.toString(), new Exception("Dropped by"));
_log.warn(buf.toString(), new Exception("Dropped by"));
}
_introManager.remove(peer);
@@ -684,10 +727,12 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
long lastSend = peer.getLastSendFullyTime();
long lastRecv = peer.getLastReceiveTime();
long now = _context.clock().now();
int inboundActive = peer.expireInboundMessages();
if ( (lastSend > 0) && (lastRecv > 0) ) {
if ( (now - lastSend > MAX_IDLE_TIME) &&
(now - lastRecv > MAX_IDLE_TIME) &&
(peer.getConsecutiveFailedSends() > 0) ) {
(peer.getConsecutiveFailedSends() > 0) &&
(inboundActive <= 0)) {
// peer is waaaay idle, drop the con and queue it up as a new con
dropPeer(peer, false);
msg.timestamp("peer is really idle, dropping con and reestablishing");
@@ -747,6 +792,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
void rebuildExternalAddress() { rebuildExternalAddress(true); }
void rebuildExternalAddress(boolean allowRebuildRouterInfo) {
if (_context.router().isHidden())
return;
// if the external port is specified, we want to use that to bind to even
// if we don't know the external host.
String port = _context.getProperty(PROP_EXTERNAL_PORT);
@@ -822,6 +870,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
boolean wantsRebuild = false;
if ( (_externalAddress == null) || !(_externalAddress.equals(addr)) )
wantsRebuild = true;
RouterAddress oldAddress = _externalAddress;
_externalAddress = addr;
if (_log.shouldLog(Log.INFO))
@@ -883,7 +932,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
return "";
}
private static final int DROP_INACTIVITY_TIME = 10*1000;
private static final int DROP_INACTIVITY_TIME = 60*1000;
public void failed(OutboundMessageState msg) {
if (msg == null) return;
@@ -892,31 +941,76 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
( (msg.getMaxSends() >= OutboundMessageFragments.MAX_VOLLEYS) ||
(msg.isExpired())) ) {
OutNetMessage m = msg.getMessage();
long recvDelay = _context.clock().now() - msg.getPeer().getLastReceiveTime();
long sendDelay = _context.clock().now() - msg.getPeer().getLastSendFullyTime();
if (m != null)
m.timestamp("message failure - volleys = " + msg.getMaxSends()
+ " lastReceived: " + (_context.clock().now() - msg.getPeer().getLastReceiveTime())
+ " lastSentFully: " + (_context.clock().now() - msg.getPeer().getLastSendFullyTime())
+ " lastReceived: " + recvDelay
+ " lastSentFully: " + sendDelay
+ " expired? " + msg.isExpired());
consecutive = msg.getPeer().incrementConsecutiveFailedSends();
if (_log.shouldLog(Log.WARN))
_log.warn("Consecutive failure #" + consecutive + " sending to " + msg.getPeer());
_log.warn("Consecutive failure #" + consecutive
+ " on " + msg.toString()
+ " to " + msg.getPeer());
if ( (consecutive > MAX_CONSECUTIVE_FAILED) && (msg.getPeer().getInactivityTime() > DROP_INACTIVITY_TIME))
dropPeer(msg.getPeer(), false);
}
failed(msg.getMessage());
noteSend(msg, false);
super.afterSend(msg.getMessage(), false);
}
public void failed(OutNetMessage msg) {
private void noteSend(OutboundMessageState msg, boolean successful) {
int pushCount = msg.getPushCount();
int sends = msg.getMaxSends();
boolean expired = msg.isExpired();
OutNetMessage m = msg.getMessage();
PeerState p = msg.getPeer();
StringBuffer buf = new StringBuffer(64);
buf.append(" lifetime: ").append(msg.getLifetime());
buf.append(" sends: ").append(sends);
buf.append(" pushes: ").append(pushCount);
buf.append(" expired? ").append(expired);
buf.append(" unacked: ").append(msg.getUnackedSize());
if (!successful) {
buf.append(" consec_failed: ").append(p.getConsecutiveFailedSends());
long timeSinceSend = _context.clock().now() - p.getLastSendFullyTime();
buf.append(" lastFullSend: ").append(timeSinceSend);
long timeSinceRecv = _context.clock().now() - p.getLastReceiveTime();
buf.append(" lastRecv: ").append(timeSinceRecv);
buf.append(" xfer: ").append(p.getSendBps()).append("/").append(p.getReceiveBps());
buf.append(" mtu: ").append(p.getMTU());
buf.append(" rto: ").append(p.getRTO());
buf.append(" sent: ").append(p.getMessagesSent()).append("/").append(p.getPacketsTransmitted());
buf.append(" recv: ").append(p.getMessagesReceived()).append("/").append(p.getPacketsReceived());
buf.append(" uptime: ").append(_context.clock().now()-p.getKeyEstablishedTime());
}
if ( (m != null) && (p != null) ) {
_context.messageHistory().sendMessage(m.getMessageType(), msg.getMessageId(), m.getExpiration(),
p.getRemotePeer(), successful, buf.toString());
} else {
_context.messageHistory().sendMessage("establish", msg.getMessageId(), -1,
(p != null ? p.getRemotePeer() : null), successful, buf.toString());
}
}
public void failed(OutNetMessage msg, String reason) {
if (msg == null) return;
if (_log.shouldLog(Log.WARN))
_log.warn("Sending message failed: " + msg, new Exception("failed from"));
_context.messageHistory().sendMessage(msg.getMessageType(), msg.getMessageId(), msg.getExpiration(),
msg.getTarget().getIdentity().calculateHash(), false, reason);
super.afterSend(msg, false);
}
public void succeeded(OutNetMessage msg) {
public void succeeded(OutboundMessageState msg) {
if (msg == null) return;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending message succeeded: " + msg);
super.afterSend(msg, true);
noteSend(msg, true);
if (msg.getMessage() != null)
super.afterSend(msg.getMessage(), true);
}
public int countActivePeers() {
@@ -68,23 +68,6 @@ public class BatchedPreprocessor extends TrivialPreprocessor {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Preprocess queue with " + pending.size() + " to send");
if (false) {
if (DISABLE_BATCHING || getSendDelay() <= 0) {
if (_log.shouldLog(Log.INFO))
_log.info("No batching, send all messages immediately");
while (pending.size() > 0) {
// loops because sends may be partial
TunnelGateway.Pending msg = (TunnelGateway.Pending)pending.get(0);
send(pending, 0, 0, sender, rec);
if (msg.getOffset() >= msg.getData().length) {
notePreprocessing(msg.getMessageId(), msg.getFragmentNumber());
pending.remove(0);
}
}
return false;
}
}
int batchCount = 0;
int beforeLooping = pending.size();
@@ -104,7 +87,7 @@ public class BatchedPreprocessor extends TrivialPreprocessor {
msg = (TunnelGateway.Pending)pending.get(i);
allocated -= curWanted;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Pushback of " + curWanted + " (message " + (i+1) + ")");
_log.debug("Pushback of " + curWanted + " (message " + (i+1) + " in " + pending + ")");
}
if (_pendingSince > 0) {
long waited = _context.clock().now() - _pendingSince;
@@ -122,13 +105,13 @@ public class BatchedPreprocessor extends TrivialPreprocessor {
if (cur.getOffset() < cur.getData().length)
throw new IllegalArgumentException("i=" + i + " j=" + j + " off=" + cur.getOffset()
+ " len=" + cur.getData().length + " alloc=" + allocated);
notePreprocessing(cur.getMessageId(), cur.getFragmentNumber());
notePreprocessing(cur.getMessageId(), cur.getFragmentNumber(), cur.getData().length, cur.getMessageIds(), "flushed allocated");
_context.statManager().addRateData("tunnel.writeDelay", cur.getLifetime(), cur.getData().length);
}
if (msg.getOffset() >= msg.getData().length) {
// ok, this last message fit perfectly, remove it too
TunnelGateway.Pending cur = (TunnelGateway.Pending)pending.remove(0);
notePreprocessing(cur.getMessageId(), cur.getFragmentNumber());
notePreprocessing(cur.getMessageId(), cur.getFragmentNumber(), msg.getData().length, msg.getMessageIds(), "flushed tail, remaining: " + pending);
_context.statManager().addRateData("tunnel.writeDelay", cur.getLifetime(), cur.getData().length);
}
if (i > 0)
@@ -160,7 +143,7 @@ public class BatchedPreprocessor extends TrivialPreprocessor {
TunnelGateway.Pending cur = (TunnelGateway.Pending)pending.get(i);
if (cur.getOffset() >= cur.getData().length) {
pending.remove(i);
notePreprocessing(cur.getMessageId(), cur.getFragmentNumber());
notePreprocessing(cur.getMessageId(), cur.getFragmentNumber(), cur.getData().length, cur.getMessageIds(), "flushed remaining");
_context.statManager().addRateData("tunnel.writeDelay", cur.getLifetime(), cur.getData().length);
i--;
}
@@ -234,7 +217,7 @@ public class BatchedPreprocessor extends TrivialPreprocessor {
*/
protected void send(List pending, int startAt, int sendThrough, TunnelGateway.Sender sender, TunnelGateway.Receiver rec) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Sending " + startAt + ":" + sendThrough + " out of " + pending.size());
_log.debug("Sending " + startAt + ":" + sendThrough + " out of " + pending);
byte preprocessed[] = _dataCache.acquire().getData();
int offset = 0;
@@ -256,7 +239,11 @@ public class BatchedPreprocessor extends TrivialPreprocessor {
preprocess(preprocessed, offset);
sender.sendPreprocessed(preprocessed, rec);
long msgId = sender.sendPreprocessed(preprocessed, rec);
for (int i = 0; i < pending.size(); i++) {
TunnelGateway.Pending cur = (TunnelGateway.Pending)pending.get(i);
cur.addMessageId(msgId);
}
}
/**
@@ -1,6 +1,6 @@
package net.i2p.router.tunnel;
import java.util.Properties;
import java.util.*;
import net.i2p.router.RouterContext;
/**
@@ -75,10 +75,10 @@ public class BatchedRouterPreprocessor extends BatchedPreprocessor {
return DEFAULT_BATCH_FREQUENCY;
}
protected void notePreprocessing(long messageId, int numFragments) {
protected void notePreprocessing(long messageId, int numFragments, int totalLength, List messageIds, String msg) {
if (_config != null)
_routerContext.messageHistory().fragmentMessage(messageId, numFragments, _config);
_routerContext.messageHistory().fragmentMessage(messageId, numFragments, totalLength, messageIds, _config, msg);
else
_routerContext.messageHistory().fragmentMessage(messageId, numFragments, _hopConfig);
_routerContext.messageHistory().fragmentMessage(messageId, numFragments, totalLength, messageIds, _hopConfig, msg);
}
}

Some files were not shown because too many files have changed in this diff Show More