mirror of
https://github.com/i2p/i2p.i2p.git
synced 2024-12-06 19:27:00 +01:00
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:
@@ -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();
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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 &
|
||||
str = str.replaceAll("&", "&");
|
||||
if (str.indexOf('\"') < 0)
|
||||
|
||||
if (str.indexOf("\"") < 0 && str.indexOf("'") < 0)
|
||||
return sanitizeString(str);
|
||||
str = str.replace('\"', '\'');
|
||||
|
||||
str = str.replaceAll("\"", """);
|
||||
str = str.replaceAll("'", "'"); // as ', 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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"; }
|
||||
|
||||
+23
-14
@@ -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
Reference in New Issue
Block a user