diff --git a/history.txt b/history.txt index 2ab62f009b560d4dafe8a3681bf5b96f48bdcf46..80a39779cec5c24cbf7560fe2aaada82ac9b5b91 100644 --- a/history.txt +++ b/history.txt @@ -1,8 +1,11 @@ +2014-04-06 zzz + * NetDB: Iterative search improvements 2014-04-05 zzz * I2PTunnel: - Add server option for unique local address per-client - Fix changing outproxy without stopping tunnel (ticket #1164) + - Fix add-to-addressbook hostname link in i2ptunnel (ticket #688) * NetDB: Skip key cert LS stores and verifies for floodfills that don't support them 2014-04-01 zzz diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 0725033fa600cfb5180ac82283f5194ee35b34b0..f6e6df3a5c0d10fbf1efc2f7069c342c2fe16f4e 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 3; + public final static long BUILD = 4; /** for example "-test" */ public final static String EXTRA = ""; diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillStoreJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillStoreJob.java index b9acaca3e5669b17d5a58c2928d2a44e1cb2d01f..5618d256459f57a592b44571a4f339c8bb4aaa98 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillStoreJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillStoreJob.java @@ -56,12 +56,16 @@ class FloodfillStoreJob extends StoreJob { protected void succeed() { super.succeed(); - if (_state != null) { if (_facade.isVerifyInProgress(_state.getTarget())) { if (_log.shouldLog(Log.INFO)) _log.info("Skipping verify, one already in progress for: " + _state.getTarget()); return; } + if (getContext().router().gracefulShutdownInProgress()) { + if (_log.shouldLog(Log.INFO)) + _log.info("Skipping verify, shutdown in progress for: " + _state.getTarget()); + return; + } // Get the time stamp from the data we sent, so the Verify job can meke sure that // it finds something stamped with that time or newer. DatabaseEntry data = _state.getData(); @@ -81,7 +85,6 @@ class FloodfillStoreJob extends StoreJob { } catch (NoSuchElementException nsee) {} getContext().jobQueue().addJob(new FloodfillVerifyStoreJob(getContext(), _state.getTarget(), published, isRouterInfo, sentTo, _facade)); - } } @Override diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/IterativeLookupJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/IterativeLookupJob.java index d4c77dbfdf0ff65fde18a1de941b8433939c8361..13d76368877357176c371ba621fb8166b5aa454a 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/IterativeLookupJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/IterativeLookupJob.java @@ -3,6 +3,7 @@ package net.i2p.router.networkdb.kademlia; import net.i2p.data.Hash; import net.i2p.data.RouterInfo; import net.i2p.data.i2np.DatabaseSearchReplyMessage; +import net.i2p.util.Log; import net.i2p.router.JobImpl; import net.i2p.router.RouterContext; @@ -22,19 +23,26 @@ import net.i2p.router.RouterContext; * @since 0.8.9 */ class IterativeLookupJob extends JobImpl { + private final Log _log; private final DatabaseSearchReplyMessage _dsrm; private final IterativeSearchJob _search; public IterativeLookupJob(RouterContext ctx, DatabaseSearchReplyMessage dsrm, IterativeSearchJob search) { super(ctx); + _log = ctx.logManager().getLog(IterativeLookupJob.class); _dsrm = dsrm; _search = search; } public void runJob() { - // TODO - dsrm.getFromHash() can't be trusted - check against the list of - // those we sent the search to in _search ? Hash from = _dsrm.getFromHash(); + // dsrm.getFromHash() can't be trusted - check against the list of + // those we sent the search to in _search + if (!_search.wasQueried(from)) { + if (_log.shouldLog(Log.WARN)) + _log.warn(_search.getJobId() + ": ILJ DSRM from unqueried peer: " + _dsrm); + return; + } // Chase the hashes from the reply // 255 max, see comments in SingleLookupJob @@ -54,15 +62,35 @@ class IterativeLookupJob extends JobImpl { invalidPeers++; continue; } + if (getContext().banlist().isBanlistedForever(peer)) { + oldPeers++; + continue; + } RouterInfo ri = getContext().netDb().lookupRouterInfoLocally(peer); if (ri == null) { - // get the RI from the peer that told us about it - getContext().jobQueue().addJob(new IterativeFollowupJob(getContext(), peer, from, _search)); + // Take it on faith that it's ff to speed things up, we don't need the RI + // to query it. + // Zero-hop outbound tunnel will be failed in ISJ.sendQuery() + _search.newPeerToTry(peer); + if (_search.getFromHash() == null) { + // get the RI from the peer that told us about it + // Only if original search used expl. tunnels + getContext().jobQueue().addJob(new SingleSearchJob(getContext(), peer, from)); + } else { + // don't look it up as we don't have a good way to do it securely... + // add to expl. queue to look up later? no, probably not safe either + } newPeers++; } else if (ri.getPublished() < getContext().clock().now() - 60*60*1000 || !FloodfillNetworkDatabaseFacade.isFloodfill(ri)) { // get an updated RI from the (now ff?) peer - getContext().jobQueue().addJob(new IterativeFollowupJob(getContext(), peer, peer, _search)); + // Only if original search used expl. tunnels + if (_search.getFromHash() == null) { + getContext().jobQueue().addJob(new IterativeFollowupJob(getContext(), peer, peer, _search)); + } else { + // for now, don't believe him, don't call newPeerToTry() + // is IFJ safe if we use the client tunnels? + } oldPeers++; } else { // add it to the sorted queue @@ -73,6 +101,8 @@ class IterativeLookupJob extends JobImpl { oldPeers++; } } + if (_log.shouldLog(Log.INFO)) + _log.info(_search.getJobId() + ": ILJ DSRM processed " + newPeers + '/' + oldPeers + '/' + invalidPeers + " new/old/invalid hashes"); long timeSent = _search.timeSent(from); // assume 0 dup if (timeSent > 0) { diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/IterativeSearchJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/IterativeSearchJob.java index 919250b5dcc604080a3e534446bccd16e11cd3e9..7a2fa777096ae2d97e37b6e3efd13321b91f45de 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/IterativeSearchJob.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/IterativeSearchJob.java @@ -252,7 +252,6 @@ class IterativeSearchJob extends FloodSearchJob { * Send a DLM to the peer */ private void sendQuery(Hash peer) { - DatabaseLookupMessage dlm = new DatabaseLookupMessage(getContext(), true); TunnelManagerFacade tm = getContext().tunnelManager(); TunnelInfo outTunnel; TunnelInfo replyTunnel; @@ -281,11 +280,22 @@ class IterativeSearchJob extends FloodSearchJob { // if it happens to be closest to itself and we are using zero-hop exploratory tunnels. // If we don't, the OutboundMessageDistributor ends up logging erors for // not being able to send to the floodfill, if we don't have an older netdb entry. - if (outTunnel.getLength() <= 1 && peer.equals(_key)) { - failed(peer, false); - return; + if (outTunnel.getLength() <= 1) { + if (peer.equals(_key)) { + failed(peer, false); + if (_log.shouldLog(Log.WARN)) + _log.warn(getJobId() + ": not doing zero-hop self-lookup of " + peer); + return; + } + if (_facade.lookupLocallyWithoutValidation(peer) == null) { + failed(peer, false); + if (_log.shouldLog(Log.WARN)) + _log.warn(getJobId() + ": not doing zero-hop lookup to unknown " + peer); + return; + } } + DatabaseLookupMessage dlm = new DatabaseLookupMessage(getContext(), true); dlm.setFrom(replyTunnel.getPeer(0)); dlm.setMessageExpiration(getContext().clock().now() + SINGLE_SEARCH_MSG_TIME); dlm.setReplyTunnel(replyTunnel.getReceiveTunnelId(0)); @@ -386,11 +396,17 @@ class IterativeSearchJob extends FloodSearchJob { if (peer.equals(getContext().routerHash()) || peer.equals(_key)) return; - RouterInfo ri = getContext().netDb().lookupRouterInfoLocally(peer); - if (!FloodfillNetworkDatabaseFacade.isFloodfill(ri)) + if (getContext().banlist().isBanlistedForever(peer)) { + if (_log.shouldLog(Log.INFO)) + _log.info(getJobId() + ": banlisted peer from DSRM " + peer); return; - if (getContext().banlist().isBanlistedForever(peer)) + } + RouterInfo ri = getContext().netDb().lookupRouterInfoLocally(peer); + if (ri != null && !FloodfillNetworkDatabaseFacade.isFloodfill(ri)) { + if (_log.shouldLog(Log.INFO)) + _log.info(getJobId() + ": non-ff peer from DSRM " + peer); return; + } synchronized (this) { if (_failedPeers.contains(peer) || _unheardFrom.contains(peer)) @@ -399,10 +415,28 @@ class IterativeSearchJob extends FloodSearchJob { return; // already in the list } if (_log.shouldLog(Log.INFO)) - _log.info(getJobId() + ": new peer from DSRM " + peer); + _log.info(getJobId() + ": new peer from DSRM: known? " + (ri != null) + ' ' + peer); retry(); } + /** + * Hash of the dest this query is from + * @return null for router + * @since 0.9.13 + */ + public Hash getFromHash() { + return _fromLocalDest; + } + + /** + * Did we send a request to this peer? + * @since 0.9.13 + */ + public boolean wasQueried(Hash peer) { + synchronized (this) { + return _unheardFrom.contains(peer) || _failedPeers.contains(peer); + } + } /** * When did we send the query to the peer? diff --git a/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java b/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java index 9f202a6a2cf529a1806dfbe032fc8041b987cdb0..f769bc9bf9d9b70b4597a2c88cda6f429e197827 100644 --- a/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java +++ b/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java @@ -69,6 +69,7 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver { // LS or RI and client or expl., so that we can safely follow references // in a reply to a LS lookup over client tunnels. // ILJ would also have to follow references via client tunnels + /**** DatabaseSearchReplyMessage orig = (DatabaseSearchReplyMessage) msg; if (orig.getNumReplies() > 0) { if (_log.shouldLog(Log.INFO)) @@ -78,6 +79,7 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver { newMsg.setSearchKey(orig.getSearchKey()); msg = newMsg; } + ****/ } else if ( (_client != null) && (type == DatabaseStoreMessage.MESSAGE_TYPE)) { DatabaseStoreMessage dsm = (DatabaseStoreMessage) msg; @@ -164,8 +166,8 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver { _log.warn("no outbound tunnel to send the client message for " + _client + ": " + msg); return; } - if (_log.shouldLog(Log.INFO)) - _log.info("distributing inbound tunnel message back out " + out + if (_log.shouldLog(Log.DEBUG)) + _log.debug("distributing IB tunnel msg type " + type + " back out " + out + " targetting " + target); TunnelId outId = out.getSendTunnelId(0); if (outId == null) { @@ -174,8 +176,9 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver { + " failing to distribute " + msg); return; } - if (msg.getMessageExpiration() < _context.clock().now() + 10*1000) - msg.setMessageExpiration(_context.clock().now() + 10*1000); + long exp = _context.clock().now() + 20*1000; + if (msg.getMessageExpiration() < exp) + msg.setMessageExpiration(exp); _context.tunnelDispatcher().dispatchOutbound(msg, outId, tunnel, target); } } @@ -250,6 +253,7 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver { // in a reply to a LS lookup over client tunnels. // ILJ would also have to follow references via client tunnels DatabaseSearchReplyMessage orig = (DatabaseSearchReplyMessage) data; + /**** if (orig.getNumReplies() > 0) { if (_log.shouldLog(Log.INFO)) _log.info("Removing replies from a garlic DSRM down a tunnel for " + _client + ": " + data); @@ -258,6 +262,7 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver { newMsg.setSearchKey(orig.getSearchKey()); orig = newMsg; } + ****/ _context.inNetMessagePool().add(orig, null, null); } else if (type == DataMessage.MESSAGE_TYPE) { // a data message targetting the local router is how we send load tests (real