From d73b327fd04cfa0dd0974142bfb32ff4ff03d4a0 Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Sat, 1 Aug 2020 12:36:31 +0000 Subject: [PATCH] NetDB: Track client that requested LS OCMOSJ: Don't send to a RAP LS --- core/java/src/net/i2p/data/LeaseSet.java | 24 ++++++++++++++- .../net/i2p/router/NetworkDatabaseFacade.java | 11 +++++++ .../dummy/DummyNetworkDatabaseFacade.java | 2 ++ .../OutboundClientMessageOneShotJob.java | 29 ++++++++++++++++--- ...andleFloodfillDatabaseStoreMessageJob.java | 8 ++--- .../KademliaNetworkDatabaseFacade.java | 29 +++++++++++++++++++ .../tunnel/InboundMessageDistributor.java | 6 ++-- 7 files changed, 97 insertions(+), 12 deletions(-) diff --git a/core/java/src/net/i2p/data/LeaseSet.java b/core/java/src/net/i2p/data/LeaseSet.java index 601acdcc92..1d67c3f195 100644 --- a/core/java/src/net/i2p/data/LeaseSet.java +++ b/core/java/src/net/i2p/data/LeaseSet.java @@ -70,6 +70,7 @@ public class LeaseSet extends DatabaseEntry { protected final List<Lease> _leases; protected boolean _receivedAsPublished; private boolean _receivedAsReply; + private Hash _receivedBy; // Store these since isCurrent() and getEarliestLeaseDate() are called frequently private long _firstExpiration; protected long _lastExpiration; @@ -198,9 +199,30 @@ public class LeaseSet extends DatabaseEntry { */ public boolean getReceivedAsReply() { return _receivedAsReply; } - /** set to true @since 0.7.14 */ + /** + * set to true + * @since 0.7.14 + */ public void setReceivedAsReply() { _receivedAsReply = true; } + /** + * The Hash of the local client that received this LS, + * null if the router or unknown. + * + * @since 0.9.47 + */ + public Hash getReceivedBy() { return _receivedBy; } + + /** + * Also sets receivedAsReply to true + * @param localClient may be null + * @since 0.9.47 + */ + public void setReceivedBy(Hash localClient) { + _receivedAsReply = true; + _receivedBy = localClient; + } + /** * @throws IllegalStateException if already signed */ diff --git a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java index d2402de825..591971f353 100644 --- a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java @@ -74,6 +74,17 @@ public abstract class NetworkDatabaseFacade implements Service { */ public abstract void lookupLeaseSetRemotely(Hash key, Hash fromLocalDest); + /** + * Unconditionally lookup using the client's tunnels. + * + * @param fromLocalDest use these tunnels for the lookup, or null for exploratory + * @param onFindJob may be null + * @param onFailedLookupJob may be null + * @since 0.9.47 + */ + public abstract void lookupLeaseSetRemotely(Hash key, Job onFindJob, Job onFailedLookupJob, + long timeoutMs, Hash fromLocalDest); + /** * Lookup using the client's tunnels * Succeeds even if LS validation fails due to unsupported sig type diff --git a/router/java/src/net/i2p/router/dummy/DummyNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/dummy/DummyNetworkDatabaseFacade.java index 71b4a5a40d..9fbea9b812 100644 --- a/router/java/src/net/i2p/router/dummy/DummyNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/dummy/DummyNetworkDatabaseFacade.java @@ -45,6 +45,8 @@ public class DummyNetworkDatabaseFacade extends NetworkDatabaseFacade { public void lookupLeaseSet(Hash key, Job onFindJob, Job onFailedLookupJob, long timeoutMs, Hash fromLocalDest) {} public LeaseSet lookupLeaseSetLocally(Hash key) { return null; } public void lookupLeaseSetRemotely(Hash key, Hash fromLocalDest) {} + public void lookupLeaseSetRemotely(Hash key, Job onFindJob, Job onFailedLookupJob, + long timeoutMs, Hash fromLocalDest) {} public void lookupDestination(Hash key, Job onFinishedJob, long timeoutMs, Hash fromLocalDest) {} diff --git a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java index 8a40d9b3bc..7250c08945 100644 --- a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java +++ b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java @@ -207,7 +207,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { _to = msg.getDestination(); Hash toHash = _to.calculateHash(); _hashPair = new OutboundCache.HashPair(_from.calculateHash(), toHash); - _toString = toHash.toBase64().substring(0,4); + _toString = toHash.toBase32(); // we look up here rather than runJob() so we may adjust the timeout _leaseSet = ctx.netDb().lookupLeaseSetLocally(toHash); @@ -297,6 +297,23 @@ public class OutboundClientMessageOneShotJob extends JobImpl { SendJob success = new SendJob(getContext()); // set in constructor if (_leaseSet != null) { + if (!_leaseSet.getReceivedAsReply()) { + boolean shouldFetch = true; + if (_leaseSet.getType() != DatabaseEntry.KEY_TYPE_LEASESET) { + LeaseSet2 ls2 = (LeaseSet2) _leaseSet; + shouldFetch = !ls2.isUnpublished() || ls2.isBlindedWhenPublished(); + } + if (shouldFetch) { + if (_log.shouldInfo()) + _log.info(getJobId() + ": RAP LS, firing search: " + _leaseSet.getHash().toBase32()); + LookupLeaseSetFailedJob failed = new LookupLeaseSetFailedJob(getContext()); + getContext().netDb().lookupLeaseSetRemotely(_leaseSet.getHash(), success, failed, + LS_LOOKUP_TIMEOUT, _from.calculateHash()); + } else { + dieFatal(MessageStatusMessage.STATUS_SEND_FAILURE_NO_LEASESET); + } + return; + } if (_log.shouldLog(Log.DEBUG)) _log.debug(getJobId() + ": Send outbound client message - leaseSet found locally for " + _toString); if (!_leaseSet.isCurrent(Router.CLOCK_FUDGE_FACTOR / 4)) { @@ -405,13 +422,17 @@ public class OutboundClientMessageOneShotJob extends JobImpl { */ private int getNextLease() { // set in runJob if found locally - if (_leaseSet == null) { + if (_leaseSet == null || !_leaseSet.getReceivedAsReply()) { _leaseSet = getContext().netDb().lookupLeaseSetLocally(_to.calculateHash()); if (_leaseSet == null) { // shouldn't happen if (_log.shouldLog(Log.WARN)) _log.warn(getJobId() + ": Lookup locally didn't find the leaseSet for " + _toString); return MessageStatusMessage.STATUS_SEND_FAILURE_NO_LEASESET; + } else if (_leaseSet.getReceivedAsPublished()) { + if (_log.shouldLog(Log.WARN)) + _log.warn(getJobId() + ": Only have RAP LS for " + _toString); + return MessageStatusMessage.STATUS_SEND_FAILURE_NO_LEASESET; } } @@ -571,8 +592,8 @@ public class OutboundClientMessageOneShotJob extends JobImpl { _log.warn("Unable to send to " + _toString + " because the sig type is unsupported"); cause = MessageStatusMessage.STATUS_SEND_FAILURE_UNSUPPORTED_ENCRYPTION; } else { - if (_log.shouldLog(Log.WARN)) - _log.warn("Unable to send to " + _toString + " because we couldn't find their leaseSet"); + if (_log.shouldInfo()) + _log.info("Unable to send to " + _toString + ", no LS found"); cause = MessageStatusMessage.STATUS_SEND_FAILURE_NO_LEASESET; } diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java index 3915c5c849..4a7dafbb97 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/HandleFloodfillDatabaseStoreMessageJob.java @@ -116,16 +116,16 @@ class HandleFloodfillDatabaseStoreMessageJob extends JobImpl { } else if (match.getEarliestLeaseDate() < ls.getEarliestLeaseDate()) { wasNew = true; // If it is in our keyspace and we are talking to it - if (match.getReceivedAsPublished()) - ls.setReceivedAsPublished(true); + //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); + //if (match.getReceivedAsPublished()) + // ls.setReceivedAsPublished(true); } else { wasNew = false; } 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 d248494e2b..460b557260 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java @@ -602,8 +602,27 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad public void lookupLeaseSetRemotely(Hash key, Hash fromLocalDest) { if (!_initialized) return; key = _blindCache.getHash(key); + if (isNegativeCached(key)) + return; search(key, null, null, 20*1000, true, fromLocalDest); } + + /** + * Unconditionally lookup using the client's tunnels. + * + * @param fromLocalDest use these tunnels for the lookup, or null for exploratory + * @param onFindJob may be null + * @param onFailedLookupJob may be null + * @since 0.9.47 + */ + public void lookupLeaseSetRemotely(Hash key, Job onFindJob, Job onFailedLookupJob, + long timeoutMs, Hash fromLocalDest) { + if (!_initialized) return; + key = _blindCache.getHash(key); + if (isNegativeCached(key)) + return; + search(key, onFindJob, onFailedLookupJob, timeoutMs, true, fromLocalDest); + } /** * Use lookupDestination() if you don't need the LS or don't need it validated. @@ -936,6 +955,16 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad rv = (LeaseSet)_ds.get(key); if ( (rv != null) && (rv.equals(leaseSet)) ) { // if it hasn't changed, no need to do anything + // except copy over the flags + Hash to = leaseSet.getReceivedBy(); + if (to != null) { + rv.setReceivedBy(to); + } else if (leaseSet.getReceivedAsReply()) { + rv.setReceivedAsReply(); + } + if (leaseSet.getReceivedAsPublished()) { + rv.setReceivedAsPublished(true); + } return rv; } } catch (ClassCastException cce) { diff --git a/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java b/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java index ad39d4fda7..ff13ce889a 100644 --- a/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java +++ b/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java @@ -125,7 +125,7 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver { // allow DSM of our own key (used by FloodfillVerifyStoreJob) // or other keys (used by IterativeSearchJob) // as long as there's no reply token (we will never set a reply token but an attacker might) - ((LeaseSet)dsm.getEntry()).setReceivedAsReply(); + ((LeaseSet)dsm.getEntry()).setReceivedBy(_client); } break; @@ -155,7 +155,7 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver { return; } if (dsm.getEntry().isLeaseSet()) - ((LeaseSet)dsm.getEntry()).setReceivedAsReply(); + ((LeaseSet)dsm.getEntry()).setReceivedBy(_client); break; case DatabaseSearchReplyMessage.MESSAGE_TYPE: @@ -264,7 +264,7 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver { // Or, it's a normal LS bundled with data and a MessageStatusMessage. // ... and inject it. - ((LeaseSet)dsm.getEntry()).setReceivedAsReply(); + ((LeaseSet)dsm.getEntry()).setReceivedBy(_client); if (_log.shouldLog(Log.INFO)) _log.info("Storing garlic LS down tunnel for: " + dsm.getKey() + " sent to: " + (_client != null ? _client.toBase32() : "router")); -- GitLab