mirror of
https://github.com/i2p/i2p.i2p.git
synced 2024-12-06 19:27:00 +01:00
Merge branch '487-ls2' into 'master'
NetDB, I2CP: Fix tunnels going yellow for several minutes (Gitlab #487) Closes #487 See merge request i2p-hackers/i2p.i2p!211
This commit is contained in:
@@ -105,6 +105,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
||||
private long _offlineExpiration;
|
||||
private Signature _offlineSignature;
|
||||
protected SigningPublicKey _transientSigningPublicKey;
|
||||
private long _lastLS2SignTime;
|
||||
|
||||
// subsession stuff
|
||||
// registered subsessions
|
||||
@@ -1185,6 +1186,21 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
|
||||
SessionId getSessionId() { return _sessionId; }
|
||||
void setSessionId(SessionId id) { _sessionId = id; }
|
||||
|
||||
/**
|
||||
* The published timestamp of the last LS2 we signed
|
||||
*
|
||||
* @return 0 if never
|
||||
* @since 0.9.64
|
||||
*/
|
||||
long getLastLS2SignTime() { return _lastLS2SignTime; };
|
||||
|
||||
/**
|
||||
* The published timestamp of the last LS2 we signed
|
||||
*
|
||||
* @since 0.9.64
|
||||
*/
|
||||
void setLastLS2SignTime(long now) { _lastLS2SignTime = now; };
|
||||
|
||||
/** configure the listener */
|
||||
public void setSessionListener(I2PSessionListener lsnr) { _sessionListener = lsnr; }
|
||||
|
||||
|
||||
@@ -130,19 +130,25 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
|
||||
boolean isLS2 = requiresLS2(session);
|
||||
LeaseSet leaseSet;
|
||||
if (isLS2) {
|
||||
LeaseSet2 ls2;
|
||||
if (_ls2Type == DatabaseEntry.KEY_TYPE_LS2) {
|
||||
leaseSet = new LeaseSet2();
|
||||
ls2 = new LeaseSet2();
|
||||
} else if (_ls2Type == DatabaseEntry.KEY_TYPE_ENCRYPTED_LS2) {
|
||||
leaseSet = new EncryptedLeaseSet();
|
||||
ls2 = new EncryptedLeaseSet();
|
||||
} else if (_ls2Type == DatabaseEntry.KEY_TYPE_META_LS2) {
|
||||
leaseSet = new MetaLeaseSet();
|
||||
ls2= new MetaLeaseSet();
|
||||
} else {
|
||||
session.propogateError("Unsupported LS2 type", new Exception());
|
||||
session.destroySession();
|
||||
return;
|
||||
}
|
||||
if (Boolean.parseBoolean(session.getOptions().getProperty("i2cp.dontPublishLeaseSet")))
|
||||
((LeaseSet2)leaseSet).setUnpublished();
|
||||
ls2.setUnpublished();
|
||||
// ensure 1-second resolution timestamp is higher than last one
|
||||
long now = Math.max(_context.clock().now(), session.getLastLS2SignTime() + 1000);
|
||||
ls2.setPublished(now);
|
||||
session.setLastLS2SignTime(now);
|
||||
leaseSet = ls2;
|
||||
} else {
|
||||
leaseSet = new LeaseSet();
|
||||
}
|
||||
|
||||
@@ -42,19 +42,25 @@ class RequestVariableLeaseSetMessageHandler extends RequestLeaseSetMessageHandle
|
||||
boolean isLS2 = requiresLS2(session);
|
||||
LeaseSet leaseSet;
|
||||
if (isLS2) {
|
||||
LeaseSet2 ls2;
|
||||
if (_ls2Type == DatabaseEntry.KEY_TYPE_LS2) {
|
||||
leaseSet = new LeaseSet2();
|
||||
ls2 = new LeaseSet2();
|
||||
} else if (_ls2Type == DatabaseEntry.KEY_TYPE_ENCRYPTED_LS2) {
|
||||
leaseSet = new EncryptedLeaseSet();
|
||||
ls2 = new EncryptedLeaseSet();
|
||||
} else if (_ls2Type == DatabaseEntry.KEY_TYPE_META_LS2) {
|
||||
leaseSet = new MetaLeaseSet();
|
||||
ls2 = new MetaLeaseSet();
|
||||
} else {
|
||||
session.propogateError("Unsupported LS2 type", new Exception());
|
||||
session.destroySession();
|
||||
return;
|
||||
}
|
||||
if (Boolean.parseBoolean(session.getOptions().getProperty("i2cp.dontPublishLeaseSet")))
|
||||
((LeaseSet2)leaseSet).setUnpublished();
|
||||
ls2.setUnpublished();
|
||||
// ensure 1-second resolution timestamp is higher than last one
|
||||
long now = Math.max(_context.clock().now(), session.getLastLS2SignTime() + 1000);
|
||||
ls2.setPublished(now);
|
||||
session.setLastLS2SignTime(now);
|
||||
leaseSet = ls2;
|
||||
} else {
|
||||
leaseSet = new LeaseSet();
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
||||
DataHelper.writeLong(out, 2, _signingKey.getType().getCode());
|
||||
_signingKey.writeBytes(out);
|
||||
if (_published <= 0)
|
||||
_published = Clock.getInstance().now();
|
||||
setPublished(Clock.getInstance().now());
|
||||
DataHelper.writeLong(out, 4, _published / 1000);
|
||||
DataHelper.writeLong(out, 2, (_expires - _published) / 1000);
|
||||
DataHelper.writeLong(out, 2, _flags);
|
||||
|
||||
@@ -238,6 +238,9 @@ public class LeaseSet extends DatabaseEntry {
|
||||
* determine which LeaseSet was published more recently (later earliestLeaseSetDate
|
||||
* means it was published later)
|
||||
*
|
||||
* Warning - do not use this for version comparison for LeaseSet2.
|
||||
* Use LeaseSet2.getPublished() instead.
|
||||
*
|
||||
* @return earliest end date of any lease in the set, or -1 if there are no leases
|
||||
*/
|
||||
public long getEarliestLeaseDate() {
|
||||
|
||||
@@ -66,6 +66,9 @@ public class LeaseSet2 extends LeaseSet {
|
||||
* Published timestamp, as received.
|
||||
* Different than getDate() or getEarliestLeaseDate(), which are the earliest lease expiration.
|
||||
*
|
||||
* Use this for LS2 version comparison, NOT getEarliestLeaseDate(), because
|
||||
* that will return -1 for EncryptedLS and MetaLS.
|
||||
*
|
||||
* @return in ms, with 1 second resolution
|
||||
* @since 0.9.39
|
||||
*/
|
||||
@@ -73,6 +76,18 @@ public class LeaseSet2 extends LeaseSet {
|
||||
return _published;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set published timestamp.
|
||||
* Will be rounded to nearest second.
|
||||
* If not called, will be set on write.
|
||||
*
|
||||
* @since 0.9.64
|
||||
*/
|
||||
public void setPublished(long now) {
|
||||
// we round it here, so comparisons during verifies aren't wrong
|
||||
_published = ((now + 500) / 1000) * 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Published expiration, as received.
|
||||
* May be different than getLatestLeaseDate(), which is the latest lease expiration.
|
||||
@@ -524,8 +539,7 @@ public class LeaseSet2 extends LeaseSet {
|
||||
protected void writeHeader(OutputStream out) throws DataFormatException, IOException {
|
||||
_destination.writeBytes(out);
|
||||
if (_published <= 0) {
|
||||
// we round it here, so comparisons during verifies aren't wrong
|
||||
_published = ((Clock.getInstance().now() + 500) / 1000) * 1000;
|
||||
setPublished(Clock.getInstance().now());
|
||||
}
|
||||
long pub1k = _published / 1000;
|
||||
DataHelper.writeLong(out, 4, pub1k);
|
||||
|
||||
@@ -22,6 +22,17 @@ public interface DataStore {
|
||||
public DatabaseEntry get(Hash key, boolean persist);
|
||||
public boolean put(Hash key, DatabaseEntry data);
|
||||
public boolean put(Hash key, DatabaseEntry data, boolean persist);
|
||||
|
||||
/*
|
||||
* Unconditionally store, bypass all newer/older checks
|
||||
*
|
||||
* @return success
|
||||
* @param key non-null
|
||||
* @param data non-null
|
||||
* @since 0.9.64
|
||||
*/
|
||||
public boolean forcePut(Hash key, DatabaseEntry data);
|
||||
|
||||
public DatabaseEntry remove(Hash key);
|
||||
public DatabaseEntry remove(Hash key, boolean persist);
|
||||
public Set<Hash> getKeys();
|
||||
|
||||
@@ -61,15 +61,18 @@ class ExpireLeasesJob extends JobImpl {
|
||||
*
|
||||
*/
|
||||
private List<Hash> selectKeysToExpire() {
|
||||
RouterContext ctx = getContext();
|
||||
List<Hash> toExpire = new ArrayList<Hash>(128);
|
||||
for (Map.Entry<Hash, DatabaseEntry> entry : _facade.getDataStore().getMapEntries()) {
|
||||
DatabaseEntry obj = entry.getValue();
|
||||
if (obj.isLeaseSet()) {
|
||||
LeaseSet ls = (LeaseSet)obj;
|
||||
if (!ls.isCurrent(Router.CLOCK_FUDGE_FACTOR))
|
||||
toExpire.add(entry.getKey());
|
||||
else if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Lease " + ls.getDestination().calculateHash() + " is current, no need to expire");
|
||||
if (!ls.isCurrent(Router.CLOCK_FUDGE_FACTOR)) {
|
||||
Hash h = entry.getKey();
|
||||
toExpire.add(h);
|
||||
if (ctx.clientManager().isLocal(h))
|
||||
_log.logAlways(Log.WARN, "Expired local leaseset " + h.toBase32());
|
||||
}
|
||||
}
|
||||
}
|
||||
return toExpire;
|
||||
|
||||
+1
-12
@@ -146,22 +146,11 @@ class HandleFloodfillDatabaseStoreMessageJob extends JobImpl {
|
||||
LeaseSet match = _facade.store(key, ls);
|
||||
if (match == null) {
|
||||
wasNew = true;
|
||||
} else if (match.getEarliestLeaseDate() < ls.getEarliestLeaseDate()) {
|
||||
} else if (KademliaNetworkDatabaseFacade.isNewer(ls, match)) {
|
||||
wasNew = true;
|
||||
// If it is in our keyspace and we are talking to it
|
||||
//if (match.getReceivedAsPublished())
|
||||
// ls.setReceivedAsPublished(true);
|
||||
} else if (type != DatabaseEntry.KEY_TYPE_LEASESET &&
|
||||
match.getType() != DatabaseEntry.KEY_TYPE_LEASESET) {
|
||||
LeaseSet2 ls2 = (LeaseSet2) ls;
|
||||
LeaseSet2 match2 = (LeaseSet2) match;
|
||||
if (match2.getPublished() < ls2.getPublished()) {
|
||||
wasNew = true;
|
||||
//if (match.getReceivedAsPublished())
|
||||
// ls.setReceivedAsPublished(true);
|
||||
} else {
|
||||
wasNew = false;
|
||||
}
|
||||
} else {
|
||||
wasNew = false;
|
||||
// The FloodOnlyLookupSelector goes away after the first good reply
|
||||
|
||||
+45
-7
@@ -881,6 +881,8 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
||||
private static final long PUBLISH_DELAY = 3*1000;
|
||||
|
||||
/**
|
||||
* Stores in local netdb, and publishes to floodfill if client manager says to
|
||||
*
|
||||
* @throws IllegalArgumentException if the leaseSet is not valid
|
||||
*/
|
||||
public void publish(LeaseSet localLeaseSet) throws IllegalArgumentException {
|
||||
@@ -891,7 +893,8 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
||||
}
|
||||
Hash h = localLeaseSet.getHash();
|
||||
try {
|
||||
store(h, localLeaseSet);
|
||||
// force overwrite of previous entry
|
||||
store(h, localLeaseSet, true);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
_log.error("locally published leaseSet is not valid?", iae);
|
||||
throw iae;
|
||||
@@ -1078,7 +1081,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Store the leaseSet.
|
||||
*
|
||||
@@ -1090,12 +1093,28 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
||||
* @return previous entry or null
|
||||
*/
|
||||
public LeaseSet store(Hash key, LeaseSet leaseSet) throws IllegalArgumentException {
|
||||
return store(key, leaseSet, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the leaseSet.
|
||||
*
|
||||
* If the store fails due to unsupported crypto, it will negative cache
|
||||
* the hash until restart.
|
||||
*
|
||||
* @param force always store even if not newer
|
||||
* @throws IllegalArgumentException if the leaseSet is not valid
|
||||
* @throws UnsupportedCryptoException if that's why it failed.
|
||||
* @return previous entry or null
|
||||
* @since 0.9.64
|
||||
*/
|
||||
public LeaseSet store(Hash key, LeaseSet leaseSet, boolean force) throws IllegalArgumentException {
|
||||
if (!_initialized) return null;
|
||||
|
||||
LeaseSet rv;
|
||||
try {
|
||||
rv = (LeaseSet)_ds.get(key);
|
||||
if (rv != null && rv.getEarliestLeaseDate() >= leaseSet.getEarliestLeaseDate()) {
|
||||
if (rv != null && !force && !isNewer(leaseSet, rv)) {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Not storing older " + key);
|
||||
// if it hasn't changed, no need to do anything
|
||||
@@ -1117,7 +1136,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
||||
|
||||
// spoof / hash collision detection
|
||||
// todo allow non-exp to overwrite exp
|
||||
if (rv != null) {
|
||||
if (rv != null && !force) {
|
||||
Destination d1 = leaseSet.getDestination();
|
||||
Destination d2 = rv.getDestination();
|
||||
if (d1 != null && d2 != null && !d1.equals(d2))
|
||||
@@ -1159,9 +1178,10 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
||||
if (err != null)
|
||||
throw new IllegalArgumentException("Invalid store attempt - " + err);
|
||||
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Storing LS to the data store...");
|
||||
_ds.put(key, leaseSet);
|
||||
if (force)
|
||||
_ds.forcePut(key, leaseSet);
|
||||
else
|
||||
_ds.put(key, leaseSet);
|
||||
|
||||
if (encls != null) {
|
||||
// we now have decrypted it, store it as well
|
||||
@@ -1205,6 +1225,24 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility to determine if a is newer than b.
|
||||
* Uses publish date if a and b are both LS2, else earliest lease date.
|
||||
*
|
||||
* @param a non-null
|
||||
* @param b non-null
|
||||
* @return if a is newer than b
|
||||
* @since 0.9.64
|
||||
*/
|
||||
public static boolean isNewer(LeaseSet a, LeaseSet b) {
|
||||
if (a.getType() != DatabaseEntry.KEY_TYPE_LEASESET &&
|
||||
b.getType() != DatabaseEntry.KEY_TYPE_LEASESET) {
|
||||
return ((LeaseSet2) a).getPublished() > ((LeaseSet2) b).getPublished();
|
||||
} else {
|
||||
return a.getEarliestLeaseDate() > b.getEarliestLeaseDate();
|
||||
}
|
||||
}
|
||||
|
||||
private static final int MIN_ROUTERS = 90;
|
||||
|
||||
|
||||
@@ -164,6 +164,24 @@ public class PersistentDataStore extends TransientDataStore {
|
||||
_writer.queue(key, data);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unconditionally store, bypass all newer/older checks.
|
||||
* Persists for RI only.
|
||||
*
|
||||
* @param persist if false, call super only, don't access disk
|
||||
* @return success
|
||||
* @param key non-null
|
||||
* @param data non-null
|
||||
* @since 0.9.64
|
||||
*/
|
||||
@Override
|
||||
public boolean forcePut(Hash key, DatabaseEntry data) {
|
||||
boolean rv = super.forcePut(key, data);
|
||||
if (rv && data.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO)
|
||||
_writer.queue(key, data);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/** How many files to write every 10 minutes. Doesn't make sense to limit it,
|
||||
* they just back up in the queue hogging memory.
|
||||
@@ -329,10 +347,11 @@ public class PersistentDataStore extends TransientDataStore {
|
||||
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
private long getPublishDate(DatabaseEntry data) {
|
||||
|
||||
private static long getPublishDate(DatabaseEntry data) {
|
||||
return data.getDate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This was mostly for manual reseeding, i.e. the user manually
|
||||
* copies RI files to the directory. Nobody does this,
|
||||
|
||||
@@ -190,6 +190,20 @@ class TransientDataStore implements DataStore {
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unconditionally store, bypass all newer/older checks
|
||||
*
|
||||
* @return success
|
||||
* @param key non-null
|
||||
* @param data non-null
|
||||
* @since 0.9.64
|
||||
*/
|
||||
@Override
|
||||
public boolean forcePut(Hash key, DatabaseEntry data) {
|
||||
_data.put(key, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
Reference in New Issue
Block a user