* NetDB: Prep for leasesets with different expire times

- Add new I2CP RequestVariableLeaseSetMessage
   - Send RVLSM if client supports it; handle on client side;
     disabled by default for the moment.
   - Add LeaseSet.getLatestLeaseDate()
   - Check latest, not earliest, date too far in future in KNDF.validate()
   - Check latest date too far in past in KNDF.validate()
   - Only check gateway and tunnel ID for equality in OCMOSJ lease caching to reduce churn
   - Split up KNDF.validate(RI) for efficiency, don't need to check
     signature, netid, etc. before lookups, only on store
   - Remove enforeNetID config
   - Fix major bug causing newer leasesets to be treated as older, and not stored or published
   - Increase max adjustment time of earliest lease
   - TransientDataStore cleanups
   - RouterInfo and LeaseSet equals() speedups
This commit is contained in:
zzz
2013-06-09 14:42:51 +00:00
parent 182fe900b8
commit 68d25afcba
18 changed files with 432 additions and 120 deletions

View File

@ -16,6 +16,7 @@ import net.i2p.data.i2cp.DisconnectMessage;
import net.i2p.data.i2cp.MessagePayloadMessage;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.data.i2cp.RequestLeaseSetMessage;
import net.i2p.data.i2cp.RequestVariableLeaseSetMessage;
import net.i2p.data.i2cp.SessionStatusMessage;
import net.i2p.data.i2cp.SetDateMessage;
@ -40,6 +41,7 @@ class I2PClientMessageHandlerMap {
highest = Math.max(highest, SetDateMessage.MESSAGE_TYPE);
highest = Math.max(highest, DestReplyMessage.MESSAGE_TYPE);
highest = Math.max(highest, BandwidthLimitsMessage.MESSAGE_TYPE);
highest = Math.max(highest, RequestVariableLeaseSetMessage.MESSAGE_TYPE);
_handlers = new I2CPMessageHandler[highest+1];
_handlers[DisconnectMessage.MESSAGE_TYPE] = new DisconnectMessageHandler(context);
@ -50,6 +52,7 @@ class I2PClientMessageHandlerMap {
_handlers[SetDateMessage.MESSAGE_TYPE] = new SetDateMessageHandler(context);
_handlers[DestReplyMessage.MESSAGE_TYPE] = new DestReplyMessageHandler(context);
_handlers[BandwidthLimitsMessage.MESSAGE_TYPE] = new BWLimitsMessageHandler(context);
_handlers[RequestVariableLeaseSetMessage.MESSAGE_TYPE] = new RequestVariableLeaseSetMessageHandler(context);
}
public I2CPMessageHandler getHandler(int messageTypeId) {

View File

@ -29,7 +29,8 @@ import net.i2p.data.i2cp.RequestLeaseSetMessage;
import net.i2p.util.Log;
/**
* Handle I2CP RequestLeaseSetMessage from the router by granting all leases
* Handle I2CP RequestLeaseSetMessage from the router by granting all leases,
* using the specified expiration time for each lease.
*
* @author jrandom
*/
@ -37,7 +38,15 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
private final Map<Destination, LeaseInfo> _existingLeaseSets;
public RequestLeaseSetMessageHandler(I2PAppContext context) {
super(context, RequestLeaseSetMessage.MESSAGE_TYPE);
this(context, RequestLeaseSetMessage.MESSAGE_TYPE);
}
/**
* For extension
* @since 0.9.7
*/
protected RequestLeaseSetMessageHandler(I2PAppContext context, int messageType) {
super(context, messageType);
// not clear why there would ever be more than one
_existingLeaseSets = new ConcurrentHashMap(4);
}
@ -55,6 +64,14 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
//lease.setStartDate(msg.getStartDate());
leaseSet.addLease(lease);
}
signLeaseSet(leaseSet, session);
}
/**
* Finish creating and signing the new LeaseSet
* @since 0.9.7
*/
protected void signLeaseSet(LeaseSet leaseSet, I2PSessionImpl session) {
// also, if this session is connected to multiple routers, include other leases here
leaseSet.setDestination(session.getMyDestination());

View File

@ -0,0 +1,41 @@
package net.i2p.client;
/*
* free (adj.): unencumbered; not under the control of others
* 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 net.i2p.I2PAppContext;
import net.i2p.data.LeaseSet;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.RequestVariableLeaseSetMessage;
import net.i2p.util.Log;
/**
* Handle I2CP RequestVariableLeaseSetMessage from the router by granting all leases,
* retaining the individual expiration time for each lease.
*
* @since 0.9.7
*/
class RequestVariableLeaseSetMessageHandler extends RequestLeaseSetMessageHandler {
public RequestVariableLeaseSetMessageHandler(I2PAppContext context) {
super(context, RequestVariableLeaseSetMessage.MESSAGE_TYPE);
}
@Override
public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Handle message " + message);
RequestVariableLeaseSetMessage msg = (RequestVariableLeaseSetMessage) message;
LeaseSet leaseSet = new LeaseSet();
for (int i = 0; i < msg.getEndpoints(); i++) {
leaseSet.addLease(msg.getEndpoint(i));
}
signLeaseSet(leaseSet, session);
}
}

View File

@ -93,6 +93,9 @@ public class LeaseSet extends DatabaseEntry {
_firstExpiration = Long.MAX_VALUE;
}
/**
* Same as getEarliestLeaseDate()
*/
public long getDate() {
return getEarliestLeaseDate();
}
@ -198,7 +201,7 @@ public class LeaseSet extends DatabaseEntry {
}
/**
* Retrieve the end date of the earliest lease include in this leaseSet.
* Retrieve the end date of the earliest lease included in this leaseSet.
* This is the date that should be used in comparisons for leaseSet age - to
* determine which LeaseSet was published more recently (later earliestLeaseSetDate
* means it was published later)
@ -211,6 +214,17 @@ public class LeaseSet extends DatabaseEntry {
return _firstExpiration;
}
/**
* Retrieve the end date of the latest lease included in this leaseSet.
* This is the date used in isCurrent().
*
* @return latest end date of any lease in the set, or 0 if there are no leases
* @since 0.9.7
*/
public long getLatestLeaseDate() {
return _lastExpiration;
}
/**
* Verify that the signature matches the lease set's destination's signing public key.
* OR the included revocation key.
@ -287,8 +301,12 @@ public class LeaseSet extends DatabaseEntry {
/**
* This does NOT validate the signature
*
* @throws IllegalStateException if called more than once or Destination already set
*/
public void readBytes(InputStream in) throws DataFormatException, IOException {
if (_destination != null)
throw new IllegalStateException();
_destination = new Destination();
_destination.readBytes(in);
_encryptionKey = PublicKey.create(in);
@ -297,7 +315,6 @@ public class LeaseSet extends DatabaseEntry {
if (numLeases > MAX_LEASES)
throw new DataFormatException("Too many leases - max is " + MAX_LEASES);
//_version = DataHelper.readLong(in, 4);
_leases.clear();
for (int i = 0; i < numLeases; i++) {
Lease lease = new Lease();
lease.readBytes(in);
@ -344,9 +361,10 @@ public class LeaseSet extends DatabaseEntry {
if (object == this) return true;
if ((object == null) || !(object instanceof LeaseSet)) return false;
LeaseSet ls = (LeaseSet) object;
return DataHelper.eq(getEncryptionKey(), ls.getEncryptionKey()) &&
//DataHelper.eq(getVersion(), ls.getVersion()) &&
DataHelper.eq(_leases, ls._leases) && DataHelper.eq(_signature, ls.getSignature())
return
DataHelper.eq(_signature, ls.getSignature())
&& DataHelper.eq(_leases, ls._leases)
&& DataHelper.eq(getEncryptionKey(), ls.getEncryptionKey())
&& DataHelper.eq(_signingKey, ls.getSigningKey())
&& DataHelper.eq(_destination, ls.getDestination());
@ -371,7 +389,7 @@ public class LeaseSet extends DatabaseEntry {
buf.append("\n\tSignature: ").append(_signature);
buf.append("\n\tLeases: #").append(getLeaseCount());
for (int i = 0; i < getLeaseCount(); i++)
buf.append("\n\t\tLease (").append(i).append("): ").append(getLease(i));
buf.append("\n\t\t").append(getLease(i));
buf.append("]");
return buf.toString();
}

View File

@ -592,9 +592,10 @@ public class RouterInfo extends DatabaseEntry {
if (object == this) return true;
if ((object == null) || !(object instanceof RouterInfo)) return false;
RouterInfo info = (RouterInfo) object;
return DataHelper.eq(_identity, info.getIdentity())
return
_published == info.getPublished()
&& DataHelper.eq(_signature, info.getSignature())
&& _published == info.getPublished();
&& DataHelper.eq(_identity, info.getIdentity());
// Let's speed up the NetDB
//&& DataHelper.eq(_addresses, info.getAddresses())
//&& DataHelper.eq(_options, info.getOptions())

View File

@ -77,6 +77,8 @@ public class I2CPMessageHandler {
return new ReportAbuseMessage();
case RequestLeaseSetMessage.MESSAGE_TYPE:
return new RequestLeaseSetMessage();
case RequestVariableLeaseSetMessage.MESSAGE_TYPE:
return new RequestVariableLeaseSetMessage();
case SendMessageMessage.MESSAGE_TYPE:
return new SendMessageMessage();
case SendMessageExpiresMessage.MESSAGE_TYPE:

View File

@ -50,18 +50,18 @@ public class RequestLeaseSetMessage extends I2CPMessageImpl {
}
public Hash getRouter(int endpoint) {
if ((endpoint < 0) || (_endpoints.size() < endpoint)) return null;
if ((endpoint < 0) || (_endpoints.size() <= endpoint)) return null;
return _endpoints.get(endpoint).getRouter();
}
public TunnelId getTunnelId(int endpoint) {
if ((endpoint < 0) || (_endpoints.size() < endpoint)) return null;
if ((endpoint < 0) || (_endpoints.size() <= endpoint)) return null;
return _endpoints.get(endpoint).getTunnelId();
}
/** @deprecated unused - presumably he meant remove? */
public void remoteEndpoint(int endpoint) {
if ((endpoint >= 0) && (endpoint < _endpoints.size())) _endpoints.remove(endpoint);
if ((endpoint >= 0) && (endpoint <= _endpoints.size())) _endpoints.remove(endpoint);
}
public void addEndpoint(Hash router, TunnelId tunnel) {

View File

@ -0,0 +1,128 @@
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 java.util.ArrayList;
import java.util.List;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Lease;
import net.i2p.util.VersionComparator;
/**
* Defines the message a router sends to a client to request that
* a leaseset be created and signed. The reply is a CreateLeaseSetMessage.
*
* This message has an expiration time for each lease, unlike RequestLeaseSetMessage,
* which has a single expiration time for all leases.
*
* @since 0.9.7
*/
public class RequestVariableLeaseSetMessage extends I2CPMessageImpl {
public final static int MESSAGE_TYPE = 37;
private SessionId _sessionId;
private final List<Lease> _endpoints;
private static final String MIN_VERSION = "0.9.7";
public RequestVariableLeaseSetMessage() {
_endpoints = new ArrayList();
}
/**
* Does the client support this message?
*
* @param clientVersion may be null
* @return version != null and version >= 0.9.7
*/
public static boolean isSupported(String clientVersion) {
return clientVersion != null &&
VersionComparator.comp(clientVersion, MIN_VERSION) >= 0;
}
public SessionId getSessionId() {
return _sessionId;
}
public void setSessionId(SessionId id) {
_sessionId = id;
}
public int getEndpoints() {
return _endpoints.size();
}
public Lease getEndpoint(int endpoint) {
if ((endpoint < 0) || (_endpoints.size() <= endpoint)) return null;
return _endpoints.get(endpoint);
}
public void addEndpoint(Lease lease) {
if (lease == null)
throw new IllegalArgumentException();
_endpoints.add(lease);
}
@Override
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
try {
if (_sessionId == null)
throw new IllegalStateException();
_sessionId = new SessionId();
_sessionId.readBytes(in);
int numTunnels = (int) DataHelper.readLong(in, 1);
for (int i = 0; i < numTunnels; i++) {
Lease lease = new Lease();
lease.readBytes(in);
_endpoints.add(lease);
}
} catch (DataFormatException dfe) {
throw new I2CPMessageException("Unable to load the message data", dfe);
}
}
@Override
protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
if (_sessionId == null)
throw new I2CPMessageException("No data");
ByteArrayOutputStream os = new ByteArrayOutputStream(256);
try {
_sessionId.writeBytes(os);
DataHelper.writeLong(os, 1, _endpoints.size());
for (int i = 0; i < _endpoints.size(); i++) {
_endpoints.get(i).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 String toString() {
StringBuilder buf = new StringBuilder();
buf.append("[RequestVariableLeaseSetMessage: ");
buf.append("\n\tSessionId: ").append(getSessionId());
buf.append("\n\tTunnels:");
for (int i = 0; i < getEndpoints(); i++) {
buf.append('\n').append(_endpoints.get(i));
}
buf.append("]");
return buf.toString();
}
}