From 8d891b99d17889ef2fea5bd51619c934acd70e86 Mon Sep 17 00:00:00 2001
From: zzz
Date: Tue, 20 Jan 2009 17:12:24 +0000
Subject: [PATCH 01/16] * Router: Add a keyring for decrypting leases
* Routerconsole: Add configkeyring.jsp
---
.../i2p/router/web/ConfigKeyringHandler.java | 55 ++++++++++
.../i2p/router/web/ConfigKeyringHelper.java | 36 ++++++
apps/routerconsole/jsp/configkeyring.jsp | 58 ++++++++++
apps/routerconsole/jsp/confignav.jsp | 2 +
core/java/src/net/i2p/I2PAppContext.java | 22 ++++
core/java/src/net/i2p/util/KeyRing.java | 20 ++++
.../src/net/i2p/router/PersistentKeyRing.java | 103 ++++++++++++++++++
.../src/net/i2p/router/RouterContext.java | 18 +++
8 files changed, 314 insertions(+)
create mode 100644 apps/routerconsole/java/src/net/i2p/router/web/ConfigKeyringHandler.java
create mode 100644 apps/routerconsole/java/src/net/i2p/router/web/ConfigKeyringHelper.java
create mode 100644 apps/routerconsole/jsp/configkeyring.jsp
create mode 100644 core/java/src/net/i2p/util/KeyRing.java
create mode 100644 router/java/src/net/i2p/router/PersistentKeyRing.java
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigKeyringHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigKeyringHandler.java
new file mode 100644
index 000000000..09f0905bf
--- /dev/null
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigKeyringHandler.java
@@ -0,0 +1,55 @@
+package net.i2p.router.web;
+
+import net.i2p.I2PAppContext;
+import net.i2p.data.DataFormatException;
+import net.i2p.data.Destination;
+import net.i2p.data.Hash;
+import net.i2p.data.SessionKey;
+
+/**
+ * Support additions via B64 Destkey, B64 Desthash, or blahblah.i2p
+ */
+public class ConfigKeyringHandler extends FormHandler {
+ private String _peer;
+ private String _key;
+
+ protected void processForm() {
+ if ("Add key".equals(_action)) {
+ if (_peer == null || _key == null) {
+ addFormError("You must enter a destination and a key");
+ return;
+ }
+ Hash h = new Hash();
+ try {
+ h.fromBase64(_peer);
+ } catch (DataFormatException dfe) {}
+ if (h.getData() == null) {
+ try {
+ Destination d = new Destination();
+ d.fromBase64(_peer);
+ h = d.calculateHash();
+ } catch (DataFormatException dfe) {}
+ }
+ if (h.getData() == null) {
+ Destination d = _context.namingService().lookup(_peer);
+ if (d != null)
+ h = d.calculateHash();
+ }
+ SessionKey sk = new SessionKey();
+ try {
+ sk.fromBase64(_key);
+ } catch (DataFormatException dfe) {}
+ if (h.getData() != null && sk.getData() != null) {
+ _context.keyRing().put(h, sk);
+ addFormNotice("Key for " + h.toBase64() + " added to keyring");
+ } else {
+ addFormError("Invalid destination or key");
+ }
+ } else {
+ addFormError("Unsupported");
+ }
+ }
+
+ public void setPeer(String peer) { _peer = peer; }
+ public void setKey(String peer) { _key = peer; }
+}
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigKeyringHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigKeyringHelper.java
new file mode 100644
index 000000000..48bc15068
--- /dev/null
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigKeyringHelper.java
@@ -0,0 +1,36 @@
+package net.i2p.router.web;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+
+import net.i2p.router.RouterContext;
+
+public class ConfigKeyringHelper {
+ private RouterContext _context;
+ /**
+ * Configure this bean to query a particular router context
+ *
+ * @param contextId begging few characters of the routerHash, or null to pick
+ * the first one we come across.
+ */
+ public void setContextId(String contextId) {
+ try {
+ _context = ContextHelper.getContext(contextId);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+
+ public ConfigKeyringHelper() {}
+
+ public String getSummary() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(4*1024);
+ try {
+ _context.keyRing().renderStatusHTML(new OutputStreamWriter(baos));
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return new String(baos.toByteArray());
+ }
+}
diff --git a/apps/routerconsole/jsp/configkeyring.jsp b/apps/routerconsole/jsp/configkeyring.jsp
new file mode 100644
index 000000000..7dd8bf178
--- /dev/null
+++ b/apps/routerconsole/jsp/configkeyring.jsp
@@ -0,0 +1,58 @@
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+
+
+
+I2P Router Console - config keyring
+
+
+
+<%@include file="nav.jsp" %>
+<%@include file="summary.jsp" %>
+
+
+ <%@include file="confignav.jsp" %>
+
+
+
+
" />
+
+
+
+
+
+
+ " />
+
+
+
Keyring
+ The router keyring is used to decrypt encrypted leaseSets.
+ The keyring may contain keys for local or remote encrypted destinations.
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/routerconsole/jsp/confignav.jsp b/apps/routerconsole/jsp/confignav.jsp
index b6a5ce6df..851ab79b5 100644
--- a/apps/routerconsole/jsp/confignav.jsp
+++ b/apps/routerconsole/jsp/confignav.jsp
@@ -10,6 +10,8 @@
%>Clients | <% } else { %>Clients | <% }
if (request.getRequestURI().indexOf("configpeer.jsp") != -1) {
%>Peers | <% } else { %>Peers | <% }
+ if (request.getRequestURI().indexOf("configkeyring.jsp") != -1) {
+ %>Keyring | <% } else { %>Keyring | <% }
if (request.getRequestURI().indexOf("configlogging.jsp") != -1) {
%>Logging | <% } else { %>Logging | <% }
if (request.getRequestURI().indexOf("configstats.jsp") != -1) {
diff --git a/core/java/src/net/i2p/I2PAppContext.java b/core/java/src/net/i2p/I2PAppContext.java
index 3e514ea18..6b3b0fd5b 100644
--- a/core/java/src/net/i2p/I2PAppContext.java
+++ b/core/java/src/net/i2p/I2PAppContext.java
@@ -24,6 +24,7 @@ import net.i2p.data.RoutingKeyGenerator;
import net.i2p.stat.StatManager;
import net.i2p.util.Clock;
import net.i2p.util.FortunaRandomSource;
+import net.i2p.util.KeyRing;
import net.i2p.util.LogManager;
import net.i2p.util.PooledRandomSource;
import net.i2p.util.RandomSource;
@@ -75,6 +76,7 @@ public class I2PAppContext {
private RoutingKeyGenerator _routingKeyGenerator;
private RandomSource _random;
private KeyGenerator _keyGenerator;
+ protected KeyRing _keyRing; // overridden in RouterContext
private volatile boolean _statManagerInitialized;
private volatile boolean _sessionKeyManagerInitialized;
private volatile boolean _namingServiceInitialized;
@@ -91,6 +93,7 @@ public class I2PAppContext {
private volatile boolean _routingKeyGeneratorInitialized;
private volatile boolean _randomInitialized;
private volatile boolean _keyGeneratorInitialized;
+ protected volatile boolean _keyRingInitialized; // used in RouterContext
/**
@@ -141,12 +144,14 @@ public class I2PAppContext {
_elGamalEngine = null;
_elGamalAESEngine = null;
_logManager = null;
+ _keyRing = null;
_statManagerInitialized = false;
_sessionKeyManagerInitialized = false;
_namingServiceInitialized = false;
_elGamalEngineInitialized = false;
_elGamalAESEngineInitialized = false;
_logManagerInitialized = false;
+ _keyRingInitialized = false;
}
/**
@@ -512,6 +517,23 @@ public class I2PAppContext {
}
}
+ /**
+ * Basic hash map
+ */
+ public KeyRing keyRing() {
+ if (!_keyRingInitialized)
+ initializeKeyRing();
+ return _keyRing;
+ }
+
+ protected void initializeKeyRing() {
+ synchronized (this) {
+ if (_keyRing == null)
+ _keyRing = new KeyRing();
+ _keyRingInitialized = true;
+ }
+ }
+
/**
* [insert snarky comment here]
*
diff --git a/core/java/src/net/i2p/util/KeyRing.java b/core/java/src/net/i2p/util/KeyRing.java
new file mode 100644
index 000000000..6bbfb38de
--- /dev/null
+++ b/core/java/src/net/i2p/util/KeyRing.java
@@ -0,0 +1,20 @@
+package net.i2p.util;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+import net.i2p.data.Hash;
+import net.i2p.data.SessionKey;
+
+/**
+ * simple
+ */
+public class KeyRing extends ConcurrentHashMap {
+ public KeyRing() {
+ super(0);
+ }
+
+ public void renderStatusHTML(Writer out) throws IOException {}
+}
diff --git a/router/java/src/net/i2p/router/PersistentKeyRing.java b/router/java/src/net/i2p/router/PersistentKeyRing.java
new file mode 100644
index 000000000..d02275ea2
--- /dev/null
+++ b/router/java/src/net/i2p/router/PersistentKeyRing.java
@@ -0,0 +1,103 @@
+package net.i2p.router;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+import net.i2p.data.Base64;
+import net.i2p.data.DataFormatException;
+import net.i2p.data.Destination;
+import net.i2p.data.Hash;
+import net.i2p.data.LeaseSet;
+import net.i2p.data.SessionKey;
+import net.i2p.router.TunnelPoolSettings;
+import net.i2p.util.KeyRing;
+
+/**
+ * ConcurrentHashMap with backing in the router.config file.
+ * router.keyring.key.{base64 hash, with = replaced with $}={base64 session key}
+ * Caution - not all HashMap methods are overridden.
+ */
+public class PersistentKeyRing extends KeyRing {
+ private RouterContext _ctx;
+ private static final String PROP_PFX = "router.keyring.key.";
+
+ public PersistentKeyRing(RouterContext ctx) {
+ super();
+ _ctx = ctx;
+ addFromProperties();
+ }
+
+ public SessionKey put(Hash h, SessionKey sk) {
+ SessionKey old = super.put(h, sk);
+ if (!sk.equals(old)) {
+ _ctx.router().setConfigSetting(PROP_PFX + h.toBase64().replace("=", "$"),
+ sk.toBase64());
+ _ctx.router().saveConfig();
+ }
+ return old;
+ }
+
+ public SessionKey remove(Hash h) {
+ _ctx.router().removeConfigSetting(PROP_PFX + h.toBase64().replace("=", "$"));
+ _ctx.router().saveConfig();
+ return super.remove(h);
+ }
+
+ private void addFromProperties() {
+ for (Iterator iter = _ctx.getPropertyNames().iterator(); iter.hasNext(); ) {
+ String prop = (String) iter.next();
+ if (!prop.startsWith(PROP_PFX))
+ continue;
+ String key = _ctx.getProperty(prop);
+ if (key == null || key.length() != 44)
+ continue;
+ String hb = prop.substring(PROP_PFX.length());
+ hb.replace("$", "=");
+ Hash dest = new Hash();
+ SessionKey sk = new SessionKey();
+ try {
+ dest.fromBase64(hb);
+ sk.fromBase64(key);
+ super.put(dest, sk);
+ } catch (DataFormatException dfe) { continue; }
+ }
+ }
+
+ public void renderStatusHTML(Writer out) throws IOException {
+ StringBuffer buf = new StringBuffer(1024);
+ buf.append("\n| Destination Hash | Name or Dest. | Session Key |
");
+ for (Entry e : entrySet()) {
+ buf.append("\n| ");
+ Hash h = e.getKey();
+ buf.append(h.toBase64().substring(0, 6)).append("...");
+ buf.append(" | ");
+ LeaseSet ls = _ctx.netDb().lookupLeaseSetLocally(h);
+ if (ls != null) {
+ Destination dest = ls.getDestination();
+ if (_ctx.clientManager().isLocal(dest)) {
+ TunnelPoolSettings in = _ctx.tunnelManager().getInboundSettings(h);
+ if (in != null && in.getDestinationNickname() != null)
+ buf.append(in.getDestinationNickname());
+ else
+ buf.append(dest.toBase64().substring(0, 6)).append("...");
+ } else {
+ String host = _ctx.namingService().reverseLookup(dest);
+ if (host != null)
+ buf.append(host);
+ else
+ buf.append(dest.toBase64().substring(0, 6)).append("...");
+ }
+ }
+ buf.append(" | ");
+ SessionKey sk = e.getValue();
+ buf.append(sk.toBase64());
+ }
+ buf.append("\n |
\n");
+ out.write(buf.toString());
+ out.flush();
+ }
+}
diff --git a/router/java/src/net/i2p/router/RouterContext.java b/router/java/src/net/i2p/router/RouterContext.java
index 087cde918..517a5ba35 100644
--- a/router/java/src/net/i2p/router/RouterContext.java
+++ b/router/java/src/net/i2p/router/RouterContext.java
@@ -26,6 +26,7 @@ import net.i2p.router.transport.VMCommSystem;
import net.i2p.router.tunnel.TunnelDispatcher;
import net.i2p.router.tunnel.pool.TunnelPoolManager;
import net.i2p.util.Clock;
+import net.i2p.util.KeyRing;
/**
* Build off the core I2P context to provide a root for a router instance to
@@ -366,4 +367,21 @@ public class RouterContext extends I2PAppContext {
}
}
+ /** override to support storage in router.config */
+ @Override
+ public KeyRing keyRing() {
+ if (!_keyRingInitialized)
+ initializeKeyRing();
+ return _keyRing;
+ }
+
+ @Override
+ protected void initializeKeyRing() {
+ synchronized (this) {
+ if (_keyRing == null)
+ _keyRing = new PersistentKeyRing(this);
+ _keyRingInitialized = true;
+ }
+ }
+
}
From 0e2a4227ef653e4dbad4bb1daae4bb1f647abd28 Mon Sep 17 00:00:00 2001
From: zzz
Date: Tue, 20 Jan 2009 17:16:24 +0000
Subject: [PATCH 02/16] * LeaseSet: Add encrypt/decrypt methods
---
core/java/src/net/i2p/data/LeaseSet.java | 172 ++++++++++++++++++++++-
1 file changed, 170 insertions(+), 2 deletions(-)
diff --git a/core/java/src/net/i2p/data/LeaseSet.java b/core/java/src/net/i2p/data/LeaseSet.java
index 7dd74a9d7..8a05dd956 100644
--- a/core/java/src/net/i2p/data/LeaseSet.java
+++ b/core/java/src/net/i2p/data/LeaseSet.java
@@ -9,6 +9,7 @@ package net.i2p.data;
*
*/
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -17,13 +18,34 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import net.i2p.I2PAppContext;
import net.i2p.crypto.DSAEngine;
import net.i2p.util.Clock;
import net.i2p.util.Log;
+import net.i2p.util.RandomSource;
/**
* Defines the set of leases a destination currently has.
*
+ * Support encryption and decryption with a supplied key.
+ * Only the gateways and tunnel IDs in the individual
+ * leases are encrypted.
+ *
+ * Encrypted leases are not indicated as such.
+ * The only way to tell a lease is encrypted is to
+ * determine that the listed gateways do not exist.
+ * Routers wishing to decrypt a leaseset must have the
+ * desthash and key in their keyring.
+ * This is required for the local router as well, since
+ * the encryption is done on the client side of I2CP, the
+ * router must decrypt it back again for local usage
+ * (but not for transmission to the floodfills)
+ *
+ * Decrypted leases are only available through the getLease()
+ * method, so that storage and network transmission via
+ * writeBytes() will output the original encrypted
+ * leases and the original leaseset signature.
+ *
* @author jrandom
*/
public class LeaseSet extends DataStructureImpl {
@@ -40,6 +62,9 @@ public class LeaseSet extends DataStructureImpl {
// Store these since isCurrent() and getEarliestLeaseDate() are called frequently
private long _firstExpiration;
private long _lastExpiration;
+ private List _decryptedLeases;
+ private boolean _decrypted;
+ private boolean _checked;
/** This seems like plenty */
private final static int MAX_LEASES = 6;
@@ -55,6 +80,8 @@ public class LeaseSet extends DataStructureImpl {
_receivedAsPublished = false;
_firstExpiration = Long.MAX_VALUE;
_lastExpiration = 0;
+ _decrypted = false;
+ _checked = false;
}
public Destination getDestination() {
@@ -104,11 +131,17 @@ public class LeaseSet extends DataStructureImpl {
}
public int getLeaseCount() {
- return _leases.size();
+ if (isEncrypted())
+ return _leases.size() - 1;
+ else
+ return _leases.size();
}
public Lease getLease(int index) {
- return (Lease) _leases.get(index);
+ if (isEncrypted())
+ return (Lease) _decryptedLeases.get(index);
+ else
+ return (Lease) _leases.get(index);
}
public Signature getSignature() {
@@ -335,4 +368,139 @@ public class LeaseSet extends DataStructureImpl {
buf.append("]");
return buf.toString();
}
+
+ private static final int DATA_LEN = Hash.HASH_LENGTH + 4;
+ private static final int IV_LEN = 16;
+
+ /**
+ * Encrypt the gateway and tunnel ID of each lease, leaving the expire dates unchanged.
+ * This adds an extra dummy lease, because AES data must be padded to 16 bytes.
+ * The fact that it is encrypted is not stored anywhere.
+ * Must be called after all the leases are in place, but before sign().
+ */
+ public void encrypt(SessionKey key) {
+ if (_log.shouldLog(Log.WARN))
+ _log.warn("encrypting lease: " + _destination.calculateHash());
+ try {
+ encryp(key);
+ } catch (DataFormatException dfe) {
+ _log.error("Error encrypting lease: " + _destination.calculateHash());
+ } catch (IOException ioe) {
+ _log.error("Error encrypting lease: " + _destination.calculateHash());
+ }
+ }
+
+ /**
+ * - Put the {Gateway Hash, TunnelID} pairs for all the leases in a buffer
+ * - Pad with random data to a multiple of 16 bytes
+ * - Use the first part of the dest's public key as an IV
+ * - Encrypt
+ * - Pad with random data to a multiple of 36 bytes
+ * - Add an extra lease
+ * - Replace the Hash and TunnelID in each Lease
+ */
+ private void encryp(SessionKey key) throws DataFormatException, IOException {
+ int size = _leases.size();
+ if (size < 1 || size > MAX_LEASES-1)
+ throw new IllegalArgumentException("Bad number of leases for encryption");
+ int datalen = ((DATA_LEN * size / 16) + 1) * 16;
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(datalen);
+ for (int i = 0; i < size; i++) {
+ ((Lease)_leases.get(i)).getGateway().writeBytes(baos);
+ ((Lease)_leases.get(i)).getTunnelId().writeBytes(baos);
+ }
+ // pad out to multiple of 16 with random data before encryption
+ int padlen = datalen - (DATA_LEN * size);
+ byte[] pad = new byte[padlen];
+ RandomSource.getInstance().nextBytes(pad);
+ baos.write(pad);
+ byte[] iv = new byte[IV_LEN];
+ System.arraycopy(_destination.getPublicKey().getData(), 0, iv, 0, IV_LEN);
+ byte[] enc = new byte[DATA_LEN * (size + 1)];
+ I2PAppContext.getGlobalContext().aes().encrypt(baos.toByteArray(), 0, enc, 0, key, iv, datalen);
+ // pad out to multiple of 36 with random data after encryption
+ // (even for 4 leases, where 36*4 is a multiple of 16, we add another, just to be consistent)
+ padlen = enc.length - datalen;
+ pad = new byte[padlen];
+ RandomSource.getInstance().nextBytes(pad);
+ System.arraycopy(pad, 0, enc, datalen, padlen);
+ // add the padded lease...
+ Lease padLease = new Lease();
+ padLease.setEndDate(((Lease)_leases.get(0)).getEndDate());
+ _leases.add(padLease);
+ // ...and replace all the gateways and tunnel ids
+ ByteArrayInputStream bais = new ByteArrayInputStream(enc);
+ for (int i = 0; i < size+1; i++) {
+ Hash h = new Hash();
+ h.readBytes(bais);
+ ((Lease)_leases.get(i)).setGateway(h);
+ TunnelId t = new TunnelId();
+ t.readBytes(bais);
+ ((Lease)_leases.get(i)).setTunnelId(t);
+ }
+ }
+
+ /**
+ * Decrypt the leases, except for the last one which is partially padding.
+ * Store the new decrypted leases in a backing store,
+ * and keep the original leases so that verify() still works and the
+ * encrypted leaseset can be sent on to others (via writeBytes())
+ */
+ private void decrypt(SessionKey key) throws DataFormatException, IOException {
+ if (_log.shouldLog(Log.WARN))
+ _log.warn("decrypting lease: " + _destination.calculateHash());
+ int size = _leases.size();
+ if (size < 2)
+ throw new DataFormatException("Bad number of leases for decryption");
+ int datalen = DATA_LEN * size;
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(datalen);
+ for (int i = 0; i < size; i++) {
+ ((Lease)_leases.get(i)).getGateway().writeBytes(baos);
+ ((Lease)_leases.get(i)).getTunnelId().writeBytes(baos);
+ }
+ byte[] iv = new byte[IV_LEN];
+ System.arraycopy(_destination.getPublicKey().getData(), 0, iv, 0, IV_LEN);
+ int enclen = ((DATA_LEN * (size - 1) / 16) + 1) * 16;
+ byte[] enc = new byte[enclen];
+ System.arraycopy(baos.toByteArray(), 0, enc, 0, enclen);
+ byte[] dec = new byte[enclen];
+ I2PAppContext.getGlobalContext().aes().decrypt(enc, 0, dec, 0, key, iv, enclen);
+ ByteArrayInputStream bais = new ByteArrayInputStream(dec);
+ _decryptedLeases = new ArrayList(size - 1);
+ for (int i = 0; i < size-1; i++) {
+ Lease l = new Lease();
+ Hash h = new Hash();
+ h.readBytes(bais);
+ l.setGateway(h);
+ TunnelId t = new TunnelId();
+ t.readBytes(bais);
+ l.setTunnelId(t);
+ l.setEndDate(((Lease)_leases.get(i)).getEndDate());
+ _decryptedLeases.add(l);
+ }
+ }
+
+ /**
+ * @return true if it was encrypted, and we decrypted it successfully.
+ * Decrypts on first call.
+ */
+ private synchronized boolean isEncrypted() {
+ if (_decrypted)
+ return true;
+ if (_checked || _destination == null)
+ return false;
+ SessionKey key = I2PAppContext.getGlobalContext().keyRing().get(_destination.calculateHash());
+ if (key != null) {
+ try {
+ decrypt(key);
+ _decrypted = true;
+ } catch (DataFormatException dfe) {
+ _log.error("Error decrypting lease: " + _destination.calculateHash() + dfe);
+ } catch (IOException ioe) {
+ _log.error("Error decrypting lease: " + _destination.calculateHash() + ioe);
+ }
+ }
+ _checked = true;
+ return _decrypted;
+ }
}
From ab92206b773351f9d6b278d4fa2fee368a5f3e3e Mon Sep 17 00:00:00 2001
From: zzz
Date: Tue, 20 Jan 2009 17:20:37 +0000
Subject: [PATCH 03/16] * Streaming: TCB control block sharing also
tweak ResendPacketEvent to prepare for PacketQueue sending timeout to I2CP
---
.../net/i2p/client/streaming/Connection.java | 57 +++++---
.../client/streaming/ConnectionManager.java | 6 +
.../streaming/ConnectionPacketHandler.java | 4 +
.../net/i2p/client/streaming/TCBShare.java | 137 ++++++++++++++++++
4 files changed, 181 insertions(+), 23 deletions(-)
create mode 100644 apps/streaming/java/src/net/i2p/client/streaming/TCBShare.java
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java
index 73e7253ac..85872e9c5 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java
@@ -45,6 +45,7 @@ public class Connection {
private long _congestionWindowEnd;
private long _highestAckedThrough;
private boolean _isInbound;
+ private boolean _updatedShareOpts;
/** Packet ID (Long) to PacketLocal for sent but unacked packets */
private Map _outboundPackets;
private PacketQueue _outboundQueue;
@@ -120,6 +121,7 @@ public class Connection {
_activeResends = 0;
_resetSentOn = -1;
_isInbound = false;
+ _updatedShareOpts = false;
_connectionEvent = new ConEvent();
_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 });
@@ -586,6 +588,8 @@ public class Connection {
if (_remotePeerSet) throw new RuntimeException("Remote peer already set [" + _remotePeer + ", " + peer + "]");
_remotePeerSet = true;
_remotePeer = peer;
+ // now that we know who the other end is, get the rtt etc. from the cache
+ _connectionManager.updateOptsFromShare(this);
}
private boolean _sendStreamIdSet = false;
@@ -709,7 +713,13 @@ public class Connection {
}
public long getCloseReceivedOn() { return _closeReceivedOn; }
public void setCloseReceivedOn(long when) { _closeReceivedOn = when; }
-
+
+ public void updateShareOpts() {
+ if (_closeSentOn > 0 && !_updatedShareOpts) {
+ _connectionManager.updateShareOpts(this);
+ _updatedShareOpts = true;
+ }
+ }
public void incrementUnackedPacketsReceived() { _unackedPacketsReceived++; }
public int getUnackedPacketsReceived() { return _unackedPacketsReceived; }
/** how many packets have we sent but not yet received an ACK for?
@@ -998,7 +1008,7 @@ public class Connection {
/**
* Coordinate the resends of a given packet
*/
- private class ResendPacketEvent implements SimpleTimer.TimedEvent {
+ public class ResendPacketEvent implements SimpleTimer.TimedEvent {
private PacketLocal _packet;
private long _nextSendTime;
public ResendPacketEvent(PacketLocal packet, long sendTime) {
@@ -1104,26 +1114,6 @@ public class Connection {
_context.sessionKeyManager().failTags(_remotePeer.getPublicKey());
}
- if (numSends - 1 <= _options.getMaxResends()) {
- if (_log.shouldLog(Log.INFO))
- _log.info("Resend packet " + _packet + " time " + numSends +
- " activeResends: " + _activeResends +
- " (wsize "
- + newWindowSize + " lifetime "
- + (_context.clock().now() - _packet.getCreatedOn()) + "ms)");
- _outboundQueue.enqueue(_packet);
- _lastSendTime = _context.clock().now();
- }
-
- // acked during resending (... or somethin')
- if ( (_packet.getAckTime() > 0) && (_packet.getNumSends() > 1) ) {
- _activeResends--;
- synchronized (_outboundPackets) {
- _outboundPackets.notifyAll();
- }
- return true;
- }
-
if (numSends - 1 > _options.getMaxResends()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Too many resends");
@@ -1137,11 +1127,32 @@ public class Connection {
long timeout = rto << (numSends-1);
if ( (timeout > MAX_RESEND_DELAY) || (timeout <= 0) )
timeout = MAX_RESEND_DELAY;
+ // set this before enqueue() as it passes it on to the router
+ _nextSendTime = timeout + _context.clock().now();
+
+ if (_log.shouldLog(Log.INFO))
+ _log.info("Resend packet " + _packet + " time " + numSends +
+ " activeResends: " + _activeResends +
+ " (wsize "
+ + newWindowSize + " lifetime "
+ + (_context.clock().now() - _packet.getCreatedOn()) + "ms)");
+ _outboundQueue.enqueue(_packet);
+ _lastSendTime = _context.clock().now();
+
if (_log.shouldLog(Log.DEBUG))
_log.debug("Scheduling resend in " + timeout + "ms for " + _packet);
RetransmissionTimer.getInstance().addEvent(ResendPacketEvent.this, timeout);
- _nextSendTime = timeout + _context.clock().now();
}
+
+ // acked during resending (... or somethin')
+ if ( (_packet.getAckTime() > 0) && (_packet.getNumSends() > 1) ) {
+ _activeResends--;
+ synchronized (_outboundPackets) {
+ _outboundPackets.notifyAll();
+ }
+ return true;
+ }
+
return true;
} else {
//if (_log.shouldLog(Log.DEBUG))
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java
index da2b1ab12..7826ba2a8 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java
@@ -30,6 +30,7 @@ public class ConnectionManager {
private PacketQueue _outboundQueue;
private SchedulerChooser _schedulerChooser;
private ConnectionPacketHandler _conPacketHandler;
+ private TCBShare _tcbShare;
/** Inbound stream ID (Long) to Connection map */
private Map _connectionByInboundId;
/** Ping ID (Long) to PingRequest */
@@ -52,6 +53,7 @@ public class ConnectionManager {
_connectionHandler = new ConnectionHandler(context, this);
_schedulerChooser = new SchedulerChooser(context);
_conPacketHandler = new ConnectionPacketHandler(context);
+ _tcbShare = new TCBShare(context);
_session = session;
session.setSessionListener(_messageHandler);
_outboundQueue = new PacketQueue(context, session, this);
@@ -127,6 +129,7 @@ public class ConnectionManager {
*/
public Connection receiveConnection(Packet synPacket) {
Connection con = new Connection(_context, this, _schedulerChooser, _outboundQueue, _conPacketHandler, new ConnectionOptions(_defaultOptions));
+ _tcbShare.updateOptsFromShare(con);
con.setInbound();
long receiveId = _context.random().nextLong(Packet.MAX_STREAM_ID-1)+1;
boolean reject = false;
@@ -277,6 +280,8 @@ public class ConnectionManager {
public ConnectionHandler getConnectionHandler() { return _connectionHandler; }
public I2PSession getSession() { return _session; }
public PacketQueue getPacketQueue() { return _outboundQueue; }
+ public void updateOptsFromShare(Connection con) { _tcbShare.updateOptsFromShare(con); }
+ public void updateShareOpts(Connection con) { _tcbShare.updateShareOpts(con); }
/**
* Something b0rked hard, so kill all of our connections without mercy.
@@ -292,6 +297,7 @@ public class ConnectionManager {
_connectionByInboundId.clear();
_connectionLock.notifyAll();
}
+ _tcbShare.stop();
}
/**
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionPacketHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionPacketHandler.java
index 6a062d4a6..7c445f038 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionPacketHandler.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionPacketHandler.java
@@ -213,6 +213,10 @@ public class ConnectionPacketHandler {
packet.releasePayload();
}
+ // update the TCB Cache now that we've processed the acks and updated our rtt etc.
+ if (isNew && packet.isFlagSet(Packet.FLAG_CLOSE) && packet.isFlagSet(Packet.FLAG_SIGNATURE_INCLUDED))
+ con.updateShareOpts();
+
//if (choke)
// con.fastRetransmit();
}
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/TCBShare.java b/apps/streaming/java/src/net/i2p/client/streaming/TCBShare.java
new file mode 100644
index 000000000..1562f948e
--- /dev/null
+++ b/apps/streaming/java/src/net/i2p/client/streaming/TCBShare.java
@@ -0,0 +1,137 @@
+package net.i2p.client.streaming;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import net.i2p.I2PAppContext;
+import net.i2p.data.Destination;
+import net.i2p.util.Log;
+import net.i2p.util.SimpleTimer;
+
+/**
+ * Share important TCP Control Block parameters across Connections
+ * to the same remote peer.
+ * This is intended for "temporal" sharing at connection open/close time,
+ * not "ensemble" sharing during a connection. Ref. RFC 2140.
+ *
+ * There is a TCB share per ConnectionManager (i.e. per local Destination)
+ * so that there is no information leakage to other Destinations on the
+ * same router.
+ *
+ */
+public class TCBShare {
+ private I2PAppContext _context;
+ private Log _log;
+ private Map _cache;
+ private CleanEvent _cleaner;
+
+ private static final long EXPIRE_TIME = 30*60*1000;
+ private static final long CLEAN_TIME = 10*60*1000;
+ private static final double RTT_DAMPENING = 0.75;
+ private static final double WDW_DAMPENING = 0.75;
+ private static final int MAX_RTT = ((int) Connection.MAX_RESEND_DELAY) / 2;
+ private static final int MAX_WINDOW_SIZE = Connection.MAX_WINDOW_SIZE / 4;
+
+ public TCBShare(I2PAppContext ctx) {
+ _context = ctx;
+ _log = ctx.logManager().getLog(TCBShare.class);
+ _cache = new ConcurrentHashMap(4);
+ _cleaner = new CleanEvent();
+ SimpleTimer.getInstance().addEvent(_cleaner, CLEAN_TIME);
+ }
+
+ public void stop() {
+ SimpleTimer.getInstance().removeEvent(_cleaner);
+ }
+
+ public void updateOptsFromShare(Connection con) {
+ Destination dest = con.getRemotePeer();
+ if (dest == null)
+ return;
+ ConnectionOptions opts = con.getOptions();
+ if (opts == null)
+ return;
+ Entry e = _cache.get(dest);
+ if (e == null || e.isExpired())
+ return;
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("From cache: " +
+ con.getSession().getMyDestination().calculateHash().toBase64().substring(0, 4) +
+ '-' +
+ dest.calculateHash().toBase64().substring(0, 4) +
+ " RTT: " + e.getRTT() + " wdw: " + e.getWindowSize());
+ opts.setRTT(e.getRTT());
+ opts.setWindowSize(e.getWindowSize());
+ }
+
+ public void updateShareOpts(Connection con) {
+ Destination dest = con.getRemotePeer();
+ if (dest == null)
+ return;
+ if (con.getAckedPackets() <= 0)
+ return;
+ ConnectionOptions opts = con.getOptions();
+ if (opts == null)
+ return;
+ int old = -1;
+ int oldw = -1;
+ Entry e = _cache.get(dest);
+ if (e == null || e.isExpired()) {
+ e = new Entry(opts.getRTT(), opts.getWindowSize());
+ _cache.put(dest, e);
+ } else {
+ old = e.getRTT();
+ oldw = e.getWindowSize();
+ e.setRTT(opts.getRTT());
+ e.setWindowSize(opts.getWindowSize());
+ }
+ if (_log.shouldLog(Log.DEBUG))
+ _log.debug("To cache: " +
+ con.getSession().getMyDestination().calculateHash().toBase64().substring(0, 4) +
+ '-' +
+ dest.calculateHash().toBase64().substring(0, 4) +
+ " old: " + old + " con: " + opts.getRTT() + " new: " + e.getRTT() +
+ " oldw: " + oldw + " conw: " + opts.getWindowSize() + " neww: " + e.getWindowSize());
+ }
+
+ private class Entry {
+ int _rtt;
+ int _wdw;
+ long _updated;
+
+ public Entry(int ms, int wdw) {
+ _rtt = ms;
+ _wdw = wdw;
+ _updated = _context.clock().now();
+ }
+ public int getRTT() { return _rtt; }
+ public void setRTT(int ms) {
+ _rtt = (int)(RTT_DAMPENING*_rtt + (1-RTT_DAMPENING)*ms);
+ if (_rtt > MAX_RTT)
+ _rtt = MAX_RTT;
+ _updated = _context.clock().now();
+ }
+ public int getWindowSize() { return _wdw; }
+ public void setWindowSize(int wdw) {
+ _wdw = (int)(0.5 + WDW_DAMPENING*_wdw + (1-WDW_DAMPENING)*wdw);
+ if (_wdw > MAX_WINDOW_SIZE)
+ _wdw = MAX_WINDOW_SIZE;
+ _updated = _context.clock().now();
+ }
+ public boolean isExpired() {
+ return _updated < _context.clock().now() - EXPIRE_TIME;
+ }
+ }
+
+ private class CleanEvent implements SimpleTimer.TimedEvent {
+ public CleanEvent() {}
+ public void timeReached() {
+ for (Iterator iter = _cache.keySet().iterator(); iter.hasNext(); ) {
+ if (_cache.get(iter.next()).isExpired())
+ iter.remove();
+ }
+ SimpleTimer.getInstance().addEvent(CleanEvent.this, CLEAN_TIME);
+ }
+ }
+}
From 6be54942ec9a07ca59de6ee4d223268710189a6e Mon Sep 17 00:00:00 2001
From: zzz
Date: Tue, 20 Jan 2009 17:22:56 +0000
Subject: [PATCH 04/16] * Streaming, I2CP, Client Message sending:
Pass message timeout through new I2CP message
SendMessageExpiresMessage, so that the router uses the same expiration
as the streaming lib. Should help reliability. * I2CP:
Implement new I2CP message ReconfigureSessionMessage. Will be used for
tunnel reduction.
---
.../net/i2p/client/streaming/PacketQueue.java | 11 +-
.../net/i2p/client/I2CPMessageProducer.java | 11 +-
core/java/src/net/i2p/client/I2PSession.java | 1 +
.../src/net/i2p/client/I2PSessionImpl.java | 8 +-
.../src/net/i2p/client/I2PSessionImpl2.java | 30 +++--
.../client/RequestLeaseSetMessageHandler.java | 14 ++-
.../net/i2p/data/i2cp/I2CPMessageHandler.java | 4 +-
.../data/i2cp/ReconfigureSessionMessage.java | 103 +++++++++++++++
.../data/i2cp/SendMessageExpiresMessage.java | 117 ++++++++++++++++++
.../src/net/i2p/router/ClientMessage.java | 10 ++
.../router/client/ClientConnectionRunner.java | 6 +-
.../net/i2p/router/client/ClientManager.java | 3 +-
.../client/ClientMessageEventListener.java | 16 +++
.../OutboundClientMessageOneShotJob.java | 48 ++++---
14 files changed, 343 insertions(+), 39 deletions(-)
create mode 100644 core/java/src/net/i2p/data/i2cp/ReconfigureSessionMessage.java
create mode 100644 core/java/src/net/i2p/data/i2cp/SendMessageExpiresMessage.java
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java b/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java
index 2d22226d3..a56e7753d 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java
@@ -82,7 +82,16 @@ class PacketQueue {
// this should not block!
begin = _context.clock().now();
- sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent);
+ long expires = 0;
+ Connection.ResendPacketEvent rpe = (Connection.ResendPacketEvent) packet.getResendEvent();
+ if (rpe != null)
+ // we want the router to expire it a little before we do,
+ // so if we retransmit it will use a new tunnel/lease combo
+ expires = rpe.getNextSendTime() - 500;
+ if (expires > 0)
+ sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent, expires);
+ else
+ sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent);
end = _context.clock().now();
if ( (end-begin > 1000) && (_log.shouldLog(Log.WARN)) )
diff --git a/core/java/src/net/i2p/client/I2CPMessageProducer.java b/core/java/src/net/i2p/client/I2CPMessageProducer.java
index 9af1fbd19..5b45ee7a3 100644
--- a/core/java/src/net/i2p/client/I2CPMessageProducer.java
+++ b/core/java/src/net/i2p/client/I2CPMessageProducer.java
@@ -9,6 +9,7 @@ package net.i2p.client;
*
*/
+import java.util.Date;
import java.util.Set;
import net.i2p.I2PAppContext;
@@ -28,6 +29,7 @@ import net.i2p.data.i2cp.DestroySessionMessage;
import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.ReportAbuseMessage;
import net.i2p.data.i2cp.SendMessageMessage;
+import net.i2p.data.i2cp.SendMessageExpiresMessage;
import net.i2p.data.i2cp.SessionConfig;
import net.i2p.util.Log;
@@ -91,8 +93,13 @@ class I2CPMessageProducer {
*
*/
public void sendMessage(I2PSessionImpl session, Destination dest, long nonce, byte[] payload, SessionTag tag,
- SessionKey key, Set tags, SessionKey newKey) throws I2PSessionException {
- SendMessageMessage msg = new SendMessageMessage();
+ SessionKey key, Set tags, SessionKey newKey, long expires) throws I2PSessionException {
+ SendMessageMessage msg;
+ if (expires > 0) {
+ msg = new SendMessageExpiresMessage();
+ ((SendMessageExpiresMessage)msg).setExpiration(new Date(expires));
+ } else
+ msg = new SendMessageMessage();
msg.setDestination(dest);
msg.setSessionId(session.getSessionId());
msg.setNonce(nonce);
diff --git a/core/java/src/net/i2p/client/I2PSession.java b/core/java/src/net/i2p/client/I2PSession.java
index 627d1775a..d8c64f222 100644
--- a/core/java/src/net/i2p/client/I2PSession.java
+++ b/core/java/src/net/i2p/client/I2PSession.java
@@ -70,6 +70,7 @@ public interface I2PSession {
*/
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException;
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent) throws I2PSessionException;
+ public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expire) throws I2PSessionException;
/** Receive a message that the router has notified the client about, returning
* the payload.
diff --git a/core/java/src/net/i2p/client/I2PSessionImpl.java b/core/java/src/net/i2p/client/I2PSessionImpl.java
index 78f4ba763..a57957107 100644
--- a/core/java/src/net/i2p/client/I2PSessionImpl.java
+++ b/core/java/src/net/i2p/client/I2PSessionImpl.java
@@ -550,10 +550,10 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
* Pass off the error to the listener
*/
void propogateError(String msg, Throwable error) {
- if (_log.shouldLog(Log.WARN))
- _log.warn(getPrefix() + "Error occurred: " + msg + " - " + error.getMessage());
- if (_log.shouldLog(Log.WARN))
- _log.warn(getPrefix() + " cause", error);
+ if (_log.shouldLog(Log.ERROR))
+ _log.error(getPrefix() + "Error occurred: " + msg + " - " + error.getMessage());
+ if (_log.shouldLog(Log.ERROR))
+ _log.error(getPrefix() + " cause", error);
if (_sessionListener != null) _sessionListener.errorOccurred(this, msg, error);
}
diff --git a/core/java/src/net/i2p/client/I2PSessionImpl2.java b/core/java/src/net/i2p/client/I2PSessionImpl2.java
index 81c6ef22f..6a90952a5 100644
--- a/core/java/src/net/i2p/client/I2PSessionImpl2.java
+++ b/core/java/src/net/i2p/client/I2PSessionImpl2.java
@@ -107,15 +107,19 @@ class I2PSessionImpl2 extends I2PSessionImpl {
return sendMessage(dest, payload, 0, payload.length);
}
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size) throws I2PSessionException {
- return sendMessage(dest, payload, offset, size, new SessionKey(), new HashSet(64));
+ return sendMessage(dest, payload, offset, size, new SessionKey(), new HashSet(64), 0);
}
@Override
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException {
- return sendMessage(dest, payload, 0, payload.length, keyUsed, tagsSent);
+ return sendMessage(dest, payload, 0, payload.length, keyUsed, tagsSent, 0);
}
public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent)
throws I2PSessionException {
+ return sendMessage(dest, payload, offset, size, keyUsed, tagsSent, 0);
+ }
+ public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expires)
+ throws I2PSessionException {
if (_log.shouldLog(Log.DEBUG)) _log.debug("sending message");
if (isClosed()) throw new I2PSessionException("Already closed");
@@ -142,7 +146,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
}
_context.statManager().addRateData("i2cp.tx.msgCompressed", compressed, 0);
_context.statManager().addRateData("i2cp.tx.msgExpanded", size, 0);
- return sendBestEffort(dest, payload, keyUsed, tagsSent);
+ return sendBestEffort(dest, payload, keyUsed, tagsSent, expires);
}
/**
@@ -168,7 +172,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
private static final int NUM_TAGS = 50;
- private boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent)
+ private boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent, long expires)
throws I2PSessionException {
SessionKey key = null;
SessionKey newKey = null;
@@ -176,6 +180,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
Set sentTags = null;
int oldTags = 0;
long begin = _context.clock().now();
+ /***********
if (I2CPMessageProducer.END_TO_END_CRYPTO) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("begin sendBestEffort");
key = _context.sessionKeyManager().getCurrentKey(dest.getPublicKey());
@@ -220,6 +225,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
} else {
// not using end to end crypto, so don't ever bundle any tags
}
+ **********/
if (_log.shouldLog(Log.DEBUG)) _log.debug("before creating nonce");
@@ -233,14 +239,14 @@ class I2PSessionImpl2 extends I2PSessionImpl {
if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Setting key = " + key);
if (keyUsed != null) {
- if (I2CPMessageProducer.END_TO_END_CRYPTO) {
- if (newKey != null)
- keyUsed.setData(newKey.getData());
- else
- keyUsed.setData(key.getData());
- } else {
+ //if (I2CPMessageProducer.END_TO_END_CRYPTO) {
+ // if (newKey != null)
+ // keyUsed.setData(newKey.getData());
+ // else
+ // keyUsed.setData(key.getData());
+ //} else {
keyUsed.setData(SessionKey.INVALID_KEY.getData());
- }
+ //}
}
if (tagsSent != null) {
if (sentTags != null) {
@@ -261,7 +267,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
+ state.getNonce() + " for best effort "
+ " sync took " + (inSendingSync-beforeSendingSync)
+ " add took " + (afterSendingSync-inSendingSync));
- _producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey);
+ _producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey, expires);
// since this is 'best effort', all we're waiting for is a status update
// saying that the router received it - in theory, that should come back
diff --git a/core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java b/core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java
index 7d6d816c1..6163771e3 100644
--- a/core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java
+++ b/core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java
@@ -21,6 +21,7 @@ import net.i2p.data.Lease;
import net.i2p.data.LeaseSet;
import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey;
+import net.i2p.data.SessionKey;
import net.i2p.data.SigningPrivateKey;
import net.i2p.data.SigningPublicKey;
import net.i2p.data.i2cp.I2CPMessage;
@@ -78,6 +79,17 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
leaseSet.setEncryptionKey(li.getPublicKey());
leaseSet.setSigningKey(li.getSigningPublicKey());
+ String sk = session.getOptions().getProperty("i2cp.sessionKey");
+ if (sk != null) {
+ SessionKey key = new SessionKey();
+ try {
+ key.fromBase64(sk);
+ leaseSet.encrypt(key);
+ _context.keyRing().put(session.getMyDestination().calculateHash(), key);
+ } catch (DataFormatException dfe) {
+ _log.error("Bad session key: " + sk);
+ }
+ }
try {
leaseSet.sign(session.getPrivateKey());
session.getProducer().createLeaseSet(session, leaseSet, li.getSigningPrivateKey(), li.getPrivateKey());
@@ -137,4 +149,4 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
&& DataHelper.eq(_signingPrivKey, li.getSigningPrivateKey());
}
}
-}
\ No newline at end of file
+}
diff --git a/core/java/src/net/i2p/data/i2cp/I2CPMessageHandler.java b/core/java/src/net/i2p/data/i2cp/I2CPMessageHandler.java
index 128c312dc..15045028a 100644
--- a/core/java/src/net/i2p/data/i2cp/I2CPMessageHandler.java
+++ b/core/java/src/net/i2p/data/i2cp/I2CPMessageHandler.java
@@ -18,7 +18,7 @@ import net.i2p.data.DataHelper;
import net.i2p.util.Log;
/**
- * Handle messages from the server for the client
+ * Handle messages from the server for the client or vice versa
*
*/
public class I2CPMessageHandler {
@@ -75,6 +75,8 @@ public class I2CPMessageHandler {
return new RequestLeaseSetMessage();
case SendMessageMessage.MESSAGE_TYPE:
return new SendMessageMessage();
+ case SendMessageExpiresMessage.MESSAGE_TYPE:
+ return new SendMessageExpiresMessage();
case SessionStatusMessage.MESSAGE_TYPE:
return new SessionStatusMessage();
case GetDateMessage.MESSAGE_TYPE:
diff --git a/core/java/src/net/i2p/data/i2cp/ReconfigureSessionMessage.java b/core/java/src/net/i2p/data/i2cp/ReconfigureSessionMessage.java
new file mode 100644
index 000000000..7165f6d32
--- /dev/null
+++ b/core/java/src/net/i2p/data/i2cp/ReconfigureSessionMessage.java
@@ -0,0 +1,103 @@
+package net.i2p.data.i2cp;
+
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain
+ * with no warranty of any kind, either expressed or implied.
+ * It probably won't make your computer catch on fire, or eat
+ * your children, but it might. Use at your own risk.
+ *
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataHelper;
+import net.i2p.util.Log;
+
+/**
+ * Defines the message a client sends to a router when
+ * updating the config on an existing session.
+ *
+ * @author zzz
+ */
+public class ReconfigureSessionMessage extends I2CPMessageImpl {
+ private final static Log _log = new Log(ReconfigureSessionMessage.class);
+ public final static int MESSAGE_TYPE = 2;
+ private SessionId _sessionId;
+ private SessionConfig _sessionConfig;
+
+ public ReconfigureSessionMessage() {
+ _sessionId = null;
+ _sessionConfig = null;
+ }
+
+ public SessionId getSessionId() {
+ return _sessionId;
+ }
+
+ public void setSessionId(SessionId id) {
+ _sessionId = id;
+ }
+
+ public SessionConfig getSessionConfig() {
+ return _sessionConfig;
+ }
+
+ public void setSessionConfig(SessionConfig config) {
+ _sessionConfig = config;
+ }
+
+ @Override
+ protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
+ try {
+ _sessionId = new SessionId();
+ _sessionId.readBytes(in);
+ _sessionConfig = new SessionConfig();
+ _sessionConfig.readBytes(in);
+ } catch (DataFormatException dfe) {
+ throw new I2CPMessageException("Unable to load the message data", dfe);
+ }
+ }
+
+ @Override
+ protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
+ if (_sessionId == null || _sessionConfig == null)
+ throw new I2CPMessageException("Unable to write out the message as there is not enough data");
+ ByteArrayOutputStream os = new ByteArrayOutputStream(64);
+ try {
+ _sessionId.writeBytes(os);
+ _sessionConfig.writeBytes(os);
+ } catch (DataFormatException dfe) {
+ throw new I2CPMessageException("Error writing out the message data", dfe);
+ }
+ return os.toByteArray();
+ }
+
+ public int getType() {
+ return MESSAGE_TYPE;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if ((object != null) && (object instanceof ReconfigureSessionMessage)) {
+ ReconfigureSessionMessage msg = (ReconfigureSessionMessage) object;
+ return DataHelper.eq(getSessionId(), msg.getSessionId())
+ && DataHelper.eq(getSessionConfig(), msg.getSessionConfig());
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ buf.append("[ReconfigureSessionMessage: ");
+ buf.append("\n\tSessionId: ").append(getSessionId());
+ buf.append("\n\tSessionConfig: ").append(getSessionConfig());
+ buf.append("]");
+ return buf.toString();
+ }
+}
diff --git a/core/java/src/net/i2p/data/i2cp/SendMessageExpiresMessage.java b/core/java/src/net/i2p/data/i2cp/SendMessageExpiresMessage.java
new file mode 100644
index 000000000..d15c1979c
--- /dev/null
+++ b/core/java/src/net/i2p/data/i2cp/SendMessageExpiresMessage.java
@@ -0,0 +1,117 @@
+package net.i2p.data.i2cp;
+
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain
+ * with no warranty of any kind, either expressed or implied.
+ * It probably won't make your computer catch on fire, or eat
+ * your children, but it might. Use at your own risk.
+ *
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Date;
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataHelper;
+import net.i2p.data.Destination;
+import net.i2p.data.Payload;
+import net.i2p.util.Log;
+
+/**
+ * Same as SendMessageMessage, but with an expiration to be passed to the router
+ *
+ * @author zzz
+ */
+public class SendMessageExpiresMessage extends SendMessageMessage {
+ private final static Log _log = new Log(SendMessageExpiresMessage.class);
+ public final static int MESSAGE_TYPE = 36;
+ private SessionId _sessionId;
+ private Destination _destination;
+ private Payload _payload;
+ private Date _expiration;
+
+ public SendMessageExpiresMessage() {
+ super();
+ setExpiration(null);
+ }
+
+ public Date getExpiration() {
+ return _expiration;
+ }
+
+ public void setExpiration(Date d) {
+ _expiration = d;
+ }
+
+ /**
+ * Read the body into the data structures
+ *
+ * @throws IOException
+ */
+ @Override
+ public void readMessage(InputStream in, int length, int type) throws I2CPMessageException, IOException {
+ super.readMessage(in, length, type);
+
+ try {
+ _expiration = DataHelper.readDate(in);
+ } catch (DataFormatException dfe) {
+ throw new I2CPMessageException("Unable to load the message data", dfe);
+ }
+ }
+
+ /**
+ * Write out the full message to the stream, including the 4 byte size and 1
+ * byte type header. Override the parent so we can be more mem efficient
+ *
+ * @throws IOException
+ */
+ @Override
+ public void writeMessage(OutputStream out) throws I2CPMessageException, IOException {
+ if ((getSessionId() == null) || (getDestination() == null) || (getPayload() == null) || (getNonce() <= 0) || (_expiration == null))
+ throw new I2CPMessageException("Unable to write out the message as there is not enough data");
+ int len = 2 + getDestination().size() + getPayload().getSize() + 4 + 4 + DataHelper.DATE_LENGTH;
+
+ try {
+ DataHelper.writeLong(out, 4, len);
+ DataHelper.writeLong(out, 1, getType());
+ getSessionId().writeBytes(out);
+ getDestination().writeBytes(out);
+ getPayload().writeBytes(out);
+ DataHelper.writeLong(out, 4, getNonce());
+ DataHelper.writeDate(out, _expiration);
+ } catch (DataFormatException dfe) {
+ throw new I2CPMessageException("Error writing the msg", dfe);
+ }
+ }
+
+ public int getType() {
+ return MESSAGE_TYPE;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if ((object != null) && (object instanceof SendMessageExpiresMessage)) {
+ SendMessageExpiresMessage msg = (SendMessageExpiresMessage) object;
+ return super.equals(object)
+ && DataHelper.eq(getExpiration(), msg.getExpiration());
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ buf.append("[SendMessageMessage: ");
+ buf.append("\n\tSessionId: ").append(getSessionId());
+ buf.append("\n\tNonce: ").append(getNonce());
+ buf.append("\n\tDestination: ").append(getDestination());
+ buf.append("\n\tExpiration: ").append(getExpiration());
+ buf.append("\n\tPayload: ").append(getPayload());
+ buf.append("]");
+ return buf.toString();
+ }
+}
diff --git a/router/java/src/net/i2p/router/ClientMessage.java b/router/java/src/net/i2p/router/ClientMessage.java
index 005f69a2d..ec7820d69 100644
--- a/router/java/src/net/i2p/router/ClientMessage.java
+++ b/router/java/src/net/i2p/router/ClientMessage.java
@@ -27,6 +27,7 @@ public class ClientMessage {
private SessionConfig _senderConfig;
private Hash _destinationHash;
private MessageId _messageId;
+ private long _expiration;
public ClientMessage() {
setPayload(null);
@@ -36,6 +37,7 @@ public class ClientMessage {
setSenderConfig(null);
setDestinationHash(null);
setMessageId(null);
+ setExpiration(0);
}
/**
@@ -91,4 +93,12 @@ public class ClientMessage {
*/
public SessionConfig getSenderConfig() { return _senderConfig; }
public void setSenderConfig(SessionConfig config) { _senderConfig = config; }
+
+ /**
+ * Expiration requested by the client that sent the message. This will only be available
+ * for locally originated messages.
+ *
+ */
+ public long getExpiration() { return _expiration; }
+ public void setExpiration(long e) { _expiration = e; }
}
diff --git a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java
index 544badcad..133ad142c 100644
--- a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java
+++ b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java
@@ -29,6 +29,7 @@ import net.i2p.data.i2cp.I2CPMessageReader;
import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.data.i2cp.SendMessageMessage;
+import net.i2p.data.i2cp.SendMessageExpiresMessage;
import net.i2p.data.i2cp.SessionConfig;
import net.i2p.data.i2cp.SessionId;
import net.i2p.router.Job;
@@ -270,6 +271,9 @@ public class ClientConnectionRunner {
Destination dest = message.getDestination();
MessageId id = new MessageId();
id.setMessageId(getNextMessageId());
+ long expiration = 0;
+ if (message instanceof SendMessageExpiresMessage)
+ expiration = ((SendMessageExpiresMessage) message).getExpiration().getTime();
long beforeLock = _context.clock().now();
long inLock = 0;
synchronized (_acceptedPending) {
@@ -291,7 +295,7 @@ public class ClientConnectionRunner {
// the following blocks as described above
SessionConfig cfg = _config;
if (cfg != null)
- _manager.distributeMessage(cfg.getDestination(), dest, payload, id);
+ _manager.distributeMessage(cfg.getDestination(), dest, payload, id, expiration);
long timeToDistribute = _context.clock().now() - beforeDistribute;
if (_log.shouldLog(Log.DEBUG))
_log.warn("Time to distribute in the manager to "
diff --git a/router/java/src/net/i2p/router/client/ClientManager.java b/router/java/src/net/i2p/router/client/ClientManager.java
index d9838ef7b..18c9c7742 100644
--- a/router/java/src/net/i2p/router/client/ClientManager.java
+++ b/router/java/src/net/i2p/router/client/ClientManager.java
@@ -140,7 +140,7 @@ public class ClientManager {
}
}
- void distributeMessage(Destination fromDest, Destination toDest, Payload payload, MessageId msgId) {
+ void distributeMessage(Destination fromDest, Destination toDest, Payload payload, MessageId msgId, long expiration) {
// check if there is a runner for it
ClientConnectionRunner runner = getRunner(toDest);
if (runner != null) {
@@ -168,6 +168,7 @@ public class ClientManager {
msg.setSenderConfig(runner.getConfig());
msg.setFromDestination(runner.getConfig().getDestination());
msg.setMessageId(msgId);
+ msg.setExpiration(expiration);
_ctx.clientMessagePool().add(msg, true);
}
}
diff --git a/router/java/src/net/i2p/router/client/ClientMessageEventListener.java b/router/java/src/net/i2p/router/client/ClientMessageEventListener.java
index 033e28f2f..d36d26401 100644
--- a/router/java/src/net/i2p/router/client/ClientMessageEventListener.java
+++ b/router/java/src/net/i2p/router/client/ClientMessageEventListener.java
@@ -21,7 +21,9 @@ import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.MessagePayloadMessage;
import net.i2p.data.i2cp.ReceiveMessageBeginMessage;
import net.i2p.data.i2cp.ReceiveMessageEndMessage;
+import net.i2p.data.i2cp.ReconfigureSessionMessage;
import net.i2p.data.i2cp.SendMessageMessage;
+import net.i2p.data.i2cp.SendMessageExpiresMessage;
import net.i2p.data.i2cp.SessionId;
import net.i2p.data.i2cp.SessionStatusMessage;
import net.i2p.data.i2cp.SetDateMessage;
@@ -67,6 +69,9 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
case SendMessageMessage.MESSAGE_TYPE:
handleSendMessage(reader, (SendMessageMessage)message);
break;
+ case SendMessageExpiresMessage.MESSAGE_TYPE:
+ handleSendMessage(reader, (SendMessageExpiresMessage)message);
+ break;
case ReceiveMessageBeginMessage.MESSAGE_TYPE:
handleReceiveBegin(reader, (ReceiveMessageBeginMessage)message);
break;
@@ -237,6 +242,17 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
_context.jobQueue().addJob(new LookupDestJob(_context, _runner, message.getHash()));
}
+ /**
+ * Message's Session ID ignored. This doesn't support removing previously set options.
+ * Nor do we bother with message.getSessionConfig().verifySignature() ... should we?
+ *
+ */
+ private void handleReconfigureSession(I2CPMessageReader reader, ReconfigureSessionMessage message) {
+ if (_log.shouldLog(Log.INFO))
+ _log.info("Updating options - session " + _runner.getSessionId());
+ _runner.getConfig().getOptions().putAll(message.getSessionConfig().getOptions());
+ }
+
// this *should* be mod 65536, but UnsignedInteger is still b0rked. FIXME
private final static int MAX_SESSION_ID = 32767;
diff --git a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java
index ccef8192a..e7c369b1e 100644
--- a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java
+++ b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java
@@ -69,6 +69,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
*/
public final static String OVERALL_TIMEOUT_MS_PARAM = "clientMessageTimeout";
private final static long OVERALL_TIMEOUT_MS_DEFAULT = 60*1000;
+ private final static long OVERALL_TIMEOUT_MS_MIN = 5*1000;
/** priority of messages, that might get honored some day... */
private final static int SEND_PRIORITY = 500;
@@ -125,23 +126,34 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
_to = msg.getDestination();
_toString = _to.calculateHash().toBase64().substring(0,4);
_leaseSetLookupBegin = -1;
-
- String param = msg.getSenderConfig().getOptions().getProperty(OVERALL_TIMEOUT_MS_PARAM);
- if (param == null)
- param = ctx.router().getConfigSetting(OVERALL_TIMEOUT_MS_PARAM);
- if (param != null) {
- try {
- timeoutMs = Long.parseLong(param);
- } catch (NumberFormatException nfe) {
- if (_log.shouldLog(Log.WARN))
- _log.warn("Invalid client message timeout specified [" + param
- + "], defaulting to " + OVERALL_TIMEOUT_MS_DEFAULT, nfe);
- timeoutMs = OVERALL_TIMEOUT_MS_DEFAULT;
- }
- }
-
_start = getContext().clock().now();
- _overallExpiration = timeoutMs + _start;
+
+ // use expiration requested by client if available, otherwise session config,
+ // otherwise router config, otherwise default
+ _overallExpiration = msg.getExpiration();
+ if (_overallExpiration > 0) {
+ _overallExpiration = Math.max(_overallExpiration, _start + OVERALL_TIMEOUT_MS_MIN);
+ _overallExpiration = Math.min(_overallExpiration, _start + OVERALL_TIMEOUT_MS_DEFAULT);
+ if (_log.shouldLog(Log.WARN))
+ _log.warn("Message Expiration (ms): " + (_overallExpiration - _start));
+ } else {
+ String param = msg.getSenderConfig().getOptions().getProperty(OVERALL_TIMEOUT_MS_PARAM);
+ if (param == null)
+ param = ctx.router().getConfigSetting(OVERALL_TIMEOUT_MS_PARAM);
+ if (param != null) {
+ try {
+ timeoutMs = Long.parseLong(param);
+ } catch (NumberFormatException nfe) {
+ if (_log.shouldLog(Log.WARN))
+ _log.warn("Invalid client message timeout specified [" + param
+ + "], defaulting to " + OVERALL_TIMEOUT_MS_DEFAULT, nfe);
+ timeoutMs = OVERALL_TIMEOUT_MS_DEFAULT;
+ }
+ }
+ _overallExpiration = timeoutMs + _start;
+ if (_log.shouldLog(Log.WARN))
+ _log.warn("Default Expiration (ms): " + timeoutMs);
+ }
_finished = false;
}
@@ -445,6 +457,10 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
}
boolean wantACK = true;
int existingTags = GarlicMessageBuilder.estimateAvailableTags(getContext(), _leaseSet.getEncryptionKey());
+ // what's the point of 5% random? possible improvements or replacements:
+ // - wantACK if we changed their inbound lease
+ // - wantACK if we changed our outbound tunnel (requires moving selectOutboundTunnel() before this)
+ // - wantACK if we haven't in last 1m (requires a new static cache probably)
if ( (existingTags > 30) && (getContext().random().nextInt(100) >= 5) )
wantACK = false;
From c620420a6f86537913657102141fee764a48cc38 Mon Sep 17 00:00:00 2001
From: zzz
Date: Tue, 20 Jan 2009 17:24:28 +0000
Subject: [PATCH 05/16] * I2PTunnel Edit Pages: - Change default
length to 2+0 - Cleanup helper code - Stub out the following new
options (C=client, S=server): + Access list (S) + Certificate
type (S) + Encrypted LeaseSet (S) + New dest on idle restart
(C) + Tunnel closure on idle (C) + Tunnel reduction on idle
(C,S)
---
.../src/net/i2p/i2ptunnel/web/EditBean.java | 188 ++++++++----------
apps/i2ptunnel/jsp/editClient.jsp | 64 +++++-
apps/i2ptunnel/jsp/editServer.jsp | 134 ++++++++++++-
3 files changed, 276 insertions(+), 110 deletions(-)
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java
index 90e5f7e20..06a46b701 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java
@@ -8,9 +8,12 @@ package net.i2p.i2ptunnel.web;
*
*/
+import java.util.Arrays;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
+import java.util.Set;
import java.util.StringTokenizer;
import net.i2p.i2ptunnel.TunnelController;
@@ -82,119 +85,100 @@ public class EditBean extends IndexBean {
}
public boolean shouldDelay(int tunnel) {
- TunnelController tun = getController(tunnel);
- if (tun != null) {
- Properties opts = getOptions(tun);
- if (opts != null) {
- String delay = opts.getProperty("i2p.streaming.connectDelay");
- if ( (delay == null) || ("0".equals(delay)) )
- return false;
- else
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
+ return getProperty(tunnel, "i2p.streaming.connectDelay", 0) > 0;
}
public boolean isInteractive(int tunnel) {
- TunnelController tun = getController(tunnel);
- if (tun != null) {
- Properties opts = getOptions(tun);
- if (opts != null) {
- String wsiz = opts.getProperty("i2p.streaming.maxWindowSize");
- if ( (wsiz == null) || (!"1".equals(wsiz)) )
- return false;
- else
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
+ return getProperty(tunnel, "i2p.streaming.maxWindowSize", 128) == 12;
}
public int getTunnelDepth(int tunnel, int defaultLength) {
- TunnelController tun = getController(tunnel);
- if (tun != null) {
- Properties opts = getOptions(tun);
- if (opts != null) {
- String len = opts.getProperty("inbound.length");
- if (len == null) return defaultLength;
- try {
- return Integer.parseInt(len);
- } catch (NumberFormatException nfe) {
- return defaultLength;
- }
- } else {
- return defaultLength;
- }
- } else {
- return defaultLength;
- }
+ return getProperty(tunnel, "inbound.length", defaultLength);
}
public int getTunnelQuantity(int tunnel, int defaultQuantity) {
- TunnelController tun = getController(tunnel);
- if (tun != null) {
- Properties opts = getOptions(tun);
- if (opts != null) {
- String len = opts.getProperty("inbound.quantity");
- if (len == null) return defaultQuantity;
- try {
- return Integer.parseInt(len);
- } catch (NumberFormatException nfe) {
- return defaultQuantity;
- }
- } else {
- return defaultQuantity;
- }
- } else {
- return defaultQuantity;
- }
+ return getProperty(tunnel, "inbound.quantity", defaultQuantity);
}
public int getTunnelBackupQuantity(int tunnel, int defaultBackupQuantity) {
- TunnelController tun = getController(tunnel);
- if (tun != null) {
- Properties opts = getOptions(tun);
- if (opts != null) {
- String len = opts.getProperty("inbound.backupQuantity");
- if (len == null) return defaultBackupQuantity;
- try {
- return Integer.parseInt(len);
- } catch (NumberFormatException nfe) {
- return defaultBackupQuantity;
- }
- } else {
- return defaultBackupQuantity;
- }
- } else {
- return defaultBackupQuantity;
- }
+ return getProperty(tunnel, "inbound.backupQuantity", defaultBackupQuantity);
}
public int getTunnelVariance(int tunnel, int defaultVariance) {
+ return getProperty(tunnel, "inbound.lengthVariance", defaultVariance);
+ }
+
+ public boolean getReduce(int tunnel) {
+ return false;
+ }
+
+ public int getReduceCount(int tunnel) {
+ return getProperty(tunnel, "inbound.reduceQuantity", 1);
+ }
+
+ public int getReduceTime(int tunnel) {
+ return getProperty(tunnel, "reduceIdleTime", 20);
+ }
+
+ public int getCert(int tunnel) {
+ return 0;
+ }
+
+ public int getEffort(int tunnel) {
+ return 23;
+ }
+
+ public String getSigner(int tunnel) {
+ return "";
+ }
+
+ public boolean getEncrypt(int tunnel) {
+ return false;
+ }
+
+ public String getEncryptKey(int tunnel) {
+ return getProperty(tunnel, "encryptKey", "");
+ }
+
+ public boolean getAccess(int tunnel) {
+ return false;
+ }
+
+ public String getAccessList(int tunnel) {
+ return getProperty(tunnel, "accessList", "");
+ }
+
+ public boolean getClose(int tunnel) {
+ return false;
+ }
+
+ public boolean getNewDest(int tunnel) {
+ return false;
+ }
+
+ private int getProperty(int tunnel, String prop, int def) {
TunnelController tun = getController(tunnel);
if (tun != null) {
Properties opts = getOptions(tun);
if (opts != null) {
- String len = opts.getProperty("inbound.lengthVariance");
- if (len == null) return defaultVariance;
+ String s = opts.getProperty(prop);
+ if (s == null) return def;
try {
- return Integer.parseInt(len);
- } catch (NumberFormatException nfe) {
- return defaultVariance;
- }
- } else {
- return defaultVariance;
+ return Integer.parseInt(s);
+ } catch (NumberFormatException nfe) {}
}
- } else {
- return defaultVariance;
}
+ return def;
+ }
+
+ private String getProperty(int tunnel, String prop, String def) {
+ TunnelController tun = getController(tunnel);
+ if (tun != null) {
+ Properties opts = getOptions(tun);
+ if (opts != null)
+ return opts.getProperty(prop, def);
+ }
+ return def;
}
public String getI2CPHost(int tunnel) {
@@ -213,6 +197,14 @@ public class EditBean extends IndexBean {
return "7654";
}
+ private static final String noShowProps[] = {
+ "inbound.length", "outbound.length", "inbound.lengthVariance", "outbound.lengthVariance",
+ "inbound.backupQuantity", "outbound.backupQuantity", "inbound.quantity", "outbound.quantity",
+ "inbound.nickname", "outbound.nickname", "i2p.streaming.connectDelay", "i2p.streaming.maxWindowSize"
+ };
+ private static final Set noShowSet = new HashSet(noShowProps.length);
+ static { noShowSet.addAll(Arrays.asList(noShowProps)); }
+
public String getCustomOptions(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun != null) {
@@ -222,19 +214,9 @@ public class EditBean extends IndexBean {
int i = 0;
for (Iterator iter = opts.keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
+ if (noShowSet.contains(key))
+ continue;
String val = opts.getProperty(key);
- if ("inbound.length".equals(key)) continue;
- if ("outbound.length".equals(key)) continue;
- if ("inbound.lengthVariance".equals(key)) continue;
- if ("outbound.lengthVariance".equals(key)) continue;
- if ("inbound.backupQuantity".equals(key)) continue;
- if ("outbound.backupQuantity".equals(key)) continue;
- if ("inbound.quantity".equals(key)) continue;
- if ("outbound.quantity".equals(key)) continue;
- if ("inbound.nickname".equals(key)) continue;
- if ("outbound.nickname".equals(key)) continue;
- if ("i2p.streaming.connectDelay".equals(key)) continue;
- if ("i2p.streaming.maxWindowSize".equals(key)) continue;
if (i != 0) buf.append(' ');
buf.append(key).append('=').append(val);
i++;
diff --git a/apps/i2ptunnel/jsp/editClient.jsp b/apps/i2ptunnel/jsp/editClient.jsp
index 97f8adbe5..2f06e1779 100644
--- a/apps/i2ptunnel/jsp/editClient.jsp
+++ b/apps/i2ptunnel/jsp/editClient.jsp
@@ -207,10 +207,10 @@
+
+
+
+
+
+
+ class="tickbox" />
+
+
+
+ class="tickbox" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ class="tickbox" />
+
+
+
+
+
+
+
+
+
@@ -284,8 +340,10 @@
diff --git a/apps/i2ptunnel/jsp/editServer.jsp b/apps/i2ptunnel/jsp/editServer.jsp
index 0cdf5e0e9..5b7ad9105 100644
--- a/apps/i2ptunnel/jsp/editServer.jsp
+++ b/apps/i2ptunnel/jsp/editServer.jsp
@@ -177,12 +177,12 @@
Variance:
Another application you can see on this webpage is I2PTunnel
diff --git a/build.xml b/build.xml
index 9bff4c5c9..a07c41053 100644
--- a/build.xml
+++ b/build.xml
@@ -60,7 +60,6 @@
-
@@ -87,7 +86,7 @@
-
+
@@ -219,7 +218,6 @@
-
@@ -368,17 +366,16 @@
-
-
+
-
+
-
+
@@ -449,7 +446,7 @@
-
+
diff --git a/installer/resources/wrapper.config b/installer/resources/wrapper.config
index 2550f4e3d..177bc1c0b 100644
--- a/installer/resources/wrapper.config
+++ b/installer/resources/wrapper.config
@@ -47,14 +47,13 @@ wrapper.java.classpath.12=lib/jasper-runtime.jar
wrapper.java.classpath.13=lib/commons-logging.jar
wrapper.java.classpath.14=lib/commons-el.jar
wrapper.java.classpath.15=lib/ant.jar
-wrapper.java.classpath.16=lib/xercesImpl.jar
# java service wrapper, BSD
-wrapper.java.classpath.17=lib/wrapper.jar
+wrapper.java.classpath.16=lib/wrapper.jar
# systray, LGPL
-wrapper.java.classpath.18=lib/systray.jar
-wrapper.java.classpath.19=lib/systray4j.jar
+wrapper.java.classpath.17=lib/systray.jar
+wrapper.java.classpath.18=lib/systray4j.jar
# BOB
-wrapper.java.classpath.20=lib/BOB.jar
+wrapper.java.classpath.19=lib/BOB.jar
# Java Library Path (location of Wrapper.DLL or libwrapper.so)
wrapper.java.library.path.1=.
From d8298c63abe61456f6d6653c5c81442871b6a295 Mon Sep 17 00:00:00 2001
From: zzz
Date: Sat, 24 Jan 2009 17:27:06 +0000
Subject: [PATCH 13/16] http error message
---
.../i2p/i2ptunnel/socks/SOCKSServerFactory.java | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServerFactory.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServerFactory.java
index b9b04c57a..67a52d688 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServerFactory.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/socks/SOCKSServerFactory.java
@@ -7,6 +7,7 @@
package net.i2p.i2ptunnel.socks;
import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
@@ -18,6 +19,15 @@ import net.i2p.util.Log;
public class SOCKSServerFactory {
private final static Log _log = new Log(SOCKSServerFactory.class);
+ private final static String ERR_REQUEST_DENIED =
+ "HTTP/1.1 403 Access Denied\r\n" +
+ "Content-Type: text/html; charset=iso-8859-1\r\n" +
+ "Cache-control: no-cache\r\n" +
+ "\r\n" +
+ "I2P SOCKS PROXY ERROR: REQUEST DENIED
" +
+ "Your browser is misconfigured. This is a SOCKS proxy, not a HTTP proxy" +
+ "";
+
/**
* Create a new SOCKS server, using the provided socket (that must
* be connected to a client) to select the proper SOCKS protocol
@@ -38,6 +48,13 @@ public class SOCKSServerFactory {
// SOCKS version 5
serv = new SOCKS5Server(s);
break;
+ case 'C':
+ case 'G':
+ case 'H':
+ case 'P':
+ DataOutputStream out = new DataOutputStream(s.getOutputStream());
+ out.write(ERR_REQUEST_DENIED.getBytes());
+ throw new SOCKSException("HTTP request to socks");
default:
throw new SOCKSException("SOCKS protocol version not supported (" + Integer.toHexString(socksVer) + ")");
}
From ae0bcc492da8f0a2abf8be92d4e0413d8955629a Mon Sep 17 00:00:00 2001
From: zzz
Date: Sat, 24 Jan 2009 20:07:41 +0000
Subject: [PATCH 14/16] * netdb.jsp: Don't show stats by default *
RebuildRouterInfoJob: Don't run it * PublishLocalRouterInfoJob: -
Delay for 5m at startup - Run every 20m (was 7.5m)
---
.../src/net/i2p/router/web/NetDbHelper.java | 6 ++-
apps/routerconsole/jsp/netdb.jsp | 1 +
.../net/i2p/router/NetworkDatabaseFacade.java | 1 +
.../networkdb/PublishLocalRouterInfoJob.java | 6 +--
.../KademliaNetworkDatabaseFacade.java | 48 +++++++++++++------
.../router/startup/RebuildRouterInfoJob.java | 7 +++
.../startup/StartAcceptingClientsJob.java | 3 +-
7 files changed, 52 insertions(+), 20 deletions(-)
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NetDbHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/NetDbHelper.java
index 41f0ad90c..114579a27 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/NetDbHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/NetDbHelper.java
@@ -11,6 +11,7 @@ public class NetDbHelper {
private RouterContext _context;
private Writer _out;
private String _routerPrefix;
+ private boolean _full = false;
/**
* Configure this bean to query a particular router context
@@ -30,6 +31,7 @@ public class NetDbHelper {
public void setWriter(Writer writer) { _out = writer; }
public void setRouter(String r) { _routerPrefix = r; }
+ public void setFull(String f) { _full = "1".equals(f); };
public String getNetDbSummary() {
try {
@@ -37,14 +39,14 @@ public class NetDbHelper {
if (_routerPrefix != null)
_context.netDb().renderRouterInfoHTML(_out, _routerPrefix);
else
- _context.netDb().renderStatusHTML(_out);
+ _context.netDb().renderStatusHTML(_out, _full);
return "";
} else {
ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
if (_routerPrefix != null)
_context.netDb().renderRouterInfoHTML(new OutputStreamWriter(baos), _routerPrefix);
else
- _context.netDb().renderStatusHTML(new OutputStreamWriter(baos));
+ _context.netDb().renderStatusHTML(new OutputStreamWriter(baos), _full);
return new String(baos.toByteArray());
}
} catch (IOException ioe) {
diff --git a/apps/routerconsole/jsp/netdb.jsp b/apps/routerconsole/jsp/netdb.jsp
index 89c2bdec2..08a1377d3 100644
--- a/apps/routerconsole/jsp/netdb.jsp
+++ b/apps/routerconsole/jsp/netdb.jsp
@@ -14,6 +14,7 @@
" />
+ " />
" />
diff --git a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java
index ed4fe1555..e4a5ce08b 100644
--- a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java
+++ b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java
@@ -62,4 +62,5 @@ public abstract class NetworkDatabaseFacade implements Service {
public int getKnownRouters() { return 0; }
public int getKnownLeaseSets() { return 0; }
public void renderRouterInfoHTML(Writer out, String s) throws IOException {}
+ public void renderStatusHTML(Writer out, boolean b) throws IOException {}
}
diff --git a/router/java/src/net/i2p/router/networkdb/PublishLocalRouterInfoJob.java b/router/java/src/net/i2p/router/networkdb/PublishLocalRouterInfoJob.java
index 96dbadd6c..8fa729d63 100644
--- a/router/java/src/net/i2p/router/networkdb/PublishLocalRouterInfoJob.java
+++ b/router/java/src/net/i2p/router/networkdb/PublishLocalRouterInfoJob.java
@@ -20,12 +20,12 @@ import net.i2p.router.RouterContext;
import net.i2p.util.Log;
/**
- * Publish the local router's RouterInfo every 5 to 10 minutes
+ * Publish the local router's RouterInfo periodically
*
*/
public class PublishLocalRouterInfoJob extends JobImpl {
private Log _log;
- final static long PUBLISH_DELAY = 5*60*1000; // every 5 to 10 minutes (since we randomize)
+ final static long PUBLISH_DELAY = 20*60*1000;
public PublishLocalRouterInfoJob(RouterContext ctx) {
super(ctx);
@@ -67,6 +67,6 @@ public class PublishLocalRouterInfoJob extends JobImpl {
} catch (DataFormatException dfe) {
_log.error("Error signing the updated local router info!", dfe);
}
- requeue(PUBLISH_DELAY + getContext().random().nextInt((int)PUBLISH_DELAY));
+ requeue((PUBLISH_DELAY/2) + getContext().random().nextInt((int)PUBLISH_DELAY));
}
}
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
index c74499f95..23ef78d86 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
@@ -125,6 +125,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
private final static long ROUTER_INFO_EXPIRATION_SHORT = 90*60*1000l;
private final static long EXPLORE_JOB_DELAY = 10*60*1000l;
+ private final static long PUBLISH_JOB_DELAY = 5*60*1000l;
public KademliaNetworkDatabaseFacade(RouterContext context) {
_context = context;
@@ -326,7 +327,9 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
}
// periodically update and resign the router's 'published date', which basically
// serves as a version
- _context.jobQueue().addJob(new PublishLocalRouterInfoJob(_context));
+ Job plrij = new PublishLocalRouterInfoJob(_context);
+ plrij.getTiming().setStartAfter(_context.clock().now() + PUBLISH_JOB_DELAY);
+ _context.jobQueue().addJob(plrij);
try {
publish(ri);
} catch (IllegalArgumentException iae) {
@@ -970,7 +973,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
StringBuffer buf = new StringBuffer(4*1024);
buf.append("Network Database RouterInfo Lookup
\n");
if (".".equals(routerPrefix)) {
- renderRouterInfo(buf, _context.router().getRouterInfo(), true);
+ renderRouterInfo(buf, _context.router().getRouterInfo(), true, true);
} else {
boolean notFound = true;
Set routers = getRouters();
@@ -978,7 +981,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
RouterInfo ri = (RouterInfo)iter.next();
Hash key = ri.getIdentity().getHash();
if (key.toBase64().startsWith(routerPrefix)) {
- renderRouterInfo(buf, ri, false);
+ renderRouterInfo(buf, ri, false, true);
notFound = false;
}
}
@@ -990,7 +993,14 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
}
public void renderStatusHTML(Writer out) throws IOException {
- StringBuffer buf = new StringBuffer(getKnownRouters() * 2048);
+ renderStatusHTML(out, true);
+ }
+
+ public void renderStatusHTML(Writer out, boolean full) throws IOException {
+ int size = getKnownRouters() * 512;
+ if (full)
+ size *= 4;
+ StringBuffer buf = new StringBuffer(size);
buf.append("Network Database Contents
\n");
if (!_initialized) {
buf.append("Not initialized\n");
@@ -1044,10 +1054,15 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
}
Hash us = _context.routerHash();
- out.write("Routers
\n");
+ out.write("\n");
RouterInfo ourInfo = _context.router().getRouterInfo();
- renderRouterInfo(buf, ourInfo, true);
+ renderRouterInfo(buf, ourInfo, true, true);
out.write(buf.toString());
buf.setLength(0);
@@ -1061,7 +1076,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
Hash key = ri.getIdentity().getHash();
boolean isUs = key.equals(us);
if (!isUs) {
- renderRouterInfo(buf, ri, false);
+ renderRouterInfo(buf, ri, false, full);
out.write(buf.toString());
buf.setLength(0);
String coreVersion = ri.getOption("coreVersion");
@@ -1102,7 +1117,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
out.flush();
}
- private void renderRouterInfo(StringBuffer buf, RouterInfo info, boolean isUs) {
+ private void renderRouterInfo(StringBuffer buf, RouterInfo info, boolean isUs, boolean full) {
String hash = info.getIdentity().getHash().toBase64();
buf.append("");
if (isUs) {
@@ -1129,13 +1144,18 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
}
}
buf.append("
\n");
- buf.append("Stats:
\n");
- for (Iterator iter = info.getOptions().keySet().iterator(); iter.hasNext(); ) {
- String key = (String)iter.next();
- String val = info.getOption(key);
- buf.append(DataHelper.stripHTML(key)).append(" = ").append(DataHelper.stripHTML(val)).append("
\n");
+ if (full) {
+ buf.append("Stats:
\n");
+ for (Iterator iter = info.getOptions().keySet().iterator(); iter.hasNext(); ) {
+ String key = (String)iter.next();
+ String val = info.getOption(key);
+ buf.append(DataHelper.stripHTML(key)).append(" = ").append(DataHelper.stripHTML(val)).append("
\n");
+ }
+ buf.append("\n");
+ } else {
+ buf.append("Full entry\n");
}
- buf.append("
\n");
+ buf.append("
\n");
}
}
diff --git a/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java b/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java
index a43fc6311..967bc7a79 100644
--- a/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java
+++ b/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java
@@ -28,6 +28,13 @@ import net.i2p.router.RouterContext;
import net.i2p.util.Log;
/**
+ * This used be called from StartAcceptingClientsJob but is now disabled.
+ * It is still called once from LoadRouterInfoJob (but not run as a Job).
+ *
+ * The following comments appear to be incorrect...
+ * it rebuilds if the router.info file does not exist.
+ * There is no check for a router.info.rebuild file.
+ *
* If the file router.info.rebuild exists, rebuild the router info and republish.
* This is useful for dhcp or other situations where the router addresses change -
* simply create the router.info.rebuild file after modifying router.config and within
diff --git a/router/java/src/net/i2p/router/startup/StartAcceptingClientsJob.java b/router/java/src/net/i2p/router/startup/StartAcceptingClientsJob.java
index 7ad54299f..727d06ac6 100644
--- a/router/java/src/net/i2p/router/startup/StartAcceptingClientsJob.java
+++ b/router/java/src/net/i2p/router/startup/StartAcceptingClientsJob.java
@@ -28,7 +28,8 @@ public class StartAcceptingClientsJob extends JobImpl {
getContext().clientManager().startup();
getContext().jobQueue().addJob(new ReadConfigJob(getContext()));
- getContext().jobQueue().addJob(new RebuildRouterInfoJob(getContext()));
+ // pointless
+ //getContext().jobQueue().addJob(new RebuildRouterInfoJob(getContext()));
getContext().jobQueue().allowParallelOperation();
}
}
From 6ed17c1a5f5e8569d874f6534696d10cc238e951 Mon Sep 17 00:00:00 2001
From: zzz
Date: Sat, 24 Jan 2009 23:42:31 +0000
Subject: [PATCH 15/16] prevent null spoofhost
---
apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java | 4 ++--
apps/i2ptunnel/jsp/editServer.jsp | 3 ++-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java
index 07730718a..627024b67 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java
@@ -39,7 +39,7 @@ public class EditBean extends IndexBean {
public String getTargetHost(int tunnel) {
TunnelController tun = getController(tunnel);
- if (tun != null)
+ if (tun != null && tun.getTargetHost() != null)
return tun.getTargetHost();
else
return "127.0.0.1";
@@ -53,7 +53,7 @@ public class EditBean extends IndexBean {
}
public String getSpoofedHost(int tunnel) {
TunnelController tun = getController(tunnel);
- if (tun != null)
+ if (tun != null && tun.getSpoofedHost() != null)
return tun.getSpoofedHost();
else
return "";
diff --git a/apps/i2ptunnel/jsp/editServer.jsp b/apps/i2ptunnel/jsp/editServer.jsp
index 5b7ad9105..e6cc0497d 100644
--- a/apps/i2ptunnel/jsp/editServer.jsp
+++ b/apps/i2ptunnel/jsp/editServer.jsp
@@ -110,7 +110,8 @@
-
+
+
<% }
%>
From 6235b49300f83264d692a80320d6945587b658f4 Mon Sep 17 00:00:00 2001
From: zzz
Date: Sun, 25 Jan 2009 01:01:48 +0000
Subject: [PATCH 16/16] cleanup of lease stuff
---
.../kademlia/PersistentDataStore.java | 116 ------------------
1 file changed, 116 deletions(-)
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java
index 77d2427cd..5d0d219db 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java
@@ -65,10 +65,6 @@ class PersistentDataStore extends TransientDataStore {
return super.remove(key);
}
- public DataStructure removeLease(Hash key) {
- return super.removeLease(key);
- }
-
public void put(Hash key, DataStructure data) {
if ( (data == null) || (key == null) ) return;
super.put(key, data);
@@ -77,26 +73,6 @@ class PersistentDataStore extends TransientDataStore {
_writer.queue(key, data);
}
-/*
- * We don't store leasesets here anymore, use the TransientDataStore count
- *
- public int countLeaseSets() {
- File dbDir = null;
- try {
- dbDir = getDbDir();
- } catch (IOException ioe) {
- return 0;
- }
- if (dbDir == null)
- return 0;
- File leaseSetFiles[] = dbDir.listFiles(LeaseSetFilter.getInstance());
- if (leaseSetFiles == null)
- return 0;
- else
- return leaseSetFiles.length;
- }
-*/
-
private void accept(LeaseSet ls) {
super.put(ls.getDestination().calculateHash(), ls);
}
@@ -249,18 +225,6 @@ class PersistentDataStore extends TransientDataStore {
int routerCount = 0;
try {
File dbDir = getDbDir();
-/****
- if (getContext().router().getUptime() < 10*60*1000) {
- File leaseSetFiles[] = dbDir.listFiles(LeaseSetFilter.getInstance());
- if (leaseSetFiles != null) {
- for (int i = 0; i < leaseSetFiles.length; i++) {
- Hash key = getLeaseSetHash(leaseSetFiles[i].getName());
- if ( (key != null) && (!isKnown(key)) )
- PersistentDataStore.this._context.jobQueue().addJob(new ReadLeaseJob(leaseSetFiles[i], key));
- }
- }
- }
-****/
File routerInfoFiles[] = dbDir.listFiles(RouterInfoFilter.getInstance());
if (routerInfoFiles != null) {
routerCount += routerInfoFiles.length;
@@ -283,63 +247,6 @@ class PersistentDataStore extends TransientDataStore {
}
}
-/****
- private class ReadLeaseJob extends JobImpl {
- private File _leaseFile;
- private Hash _key;
- public ReadLeaseJob(File leaseFile, Hash key) {
- super(PersistentDataStore.this._context);
- _leaseFile = leaseFile;
- _key = key;
- }
- public String getName() { return "Read LeaseSet"; }
- private boolean shouldRead() {
- DataStructure data = get(_key);
- if (data == null) return true;
- if (data instanceof LeaseSet) {
- long knownDate = ((LeaseSet)data).getEarliestLeaseDate();
- long fileDate = _leaseFile.lastModified();
- if (fileDate > knownDate)
- return true;
- else
- return false;
- } else {
- // wtf
- return true;
- }
- }
- public void runJob() {
- if (!shouldRead()) return;
- try {
- FileInputStream fis = null;
- boolean corrupt = false;
- try {
- fis = new FileInputStream(_leaseFile);
- LeaseSet ls = new LeaseSet();
- ls.readBytes(fis);
- try {
- _facade.store(ls.getDestination().calculateHash(), ls);
- } catch (IllegalArgumentException iae) {
- _log.info("Refused locally loaded leaseSet - deleting");
- corrupt = true;
- }
- } catch (DataFormatException dfe) {
- _log.warn("Error reading the leaseSet from " + _leaseFile.getAbsolutePath(), dfe);
- corrupt = true;
- } catch (FileNotFoundException fnfe) {
- _log.debug("Deleted prior to read.. a race during expiration / load");
- corrupt = false;
- } finally {
- if (fis != null) try { fis.close(); } catch (IOException ioe) {}
- }
- if (corrupt) _leaseFile.delete();
- } catch (IOException ioe) {
- _log.warn("Error reading the leaseSet from " + _leaseFile.getAbsolutePath(), ioe);
- }
- }
- }
-****/
-
private class ReadRouterJob extends JobImpl {
private File _routerFile;
private Hash _key;
@@ -464,31 +371,8 @@ class PersistentDataStore extends TransientDataStore {
_log.info("Removed router info at " + f.getAbsolutePath());
return;
}
-/***
- String lsName = getLeaseSetName(key);
- File f = new File(dir, lsName);
- if (f.exists()) {
- boolean removed = f.delete();
- if (!removed)
- _log.warn("Unable to remove lease set at " + f.getAbsolutePath());
- else
- _log.info("Removed lease set at " + f.getAbsolutePath());
- return;
- }
-***/
}
-/***
- private final static class LeaseSetFilter implements FilenameFilter {
- private static final FilenameFilter _instance = new LeaseSetFilter();
- public static final FilenameFilter getInstance() { return _instance; }
- public boolean accept(File dir, String name) {
- if (name == null) return false;
- name = name.toUpperCase();
- return (name.startsWith(LEASESET_PREFIX.toUpperCase()) && name.endsWith(LEASESET_SUFFIX.toUpperCase()));
- }
- }
-***/
private final static class RouterInfoFilter implements FilenameFilter {
private static final FilenameFilter _instance = new RouterInfoFilter();
public static final FilenameFilter getInstance() { return _instance; }