package net.i2p.router.networkdb.kademlia; import net.i2p.data.Hash; import net.i2p.data.i2np.DatabaseSearchReplyMessage; import net.i2p.data.router.RouterInfo; import net.i2p.router.JobImpl; import net.i2p.router.RouterContext; import net.i2p.util.Log; /* * 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. * */ class SearchReplyJob extends JobImpl { private DatabaseSearchReplyMessage _msg; private Log _log; /** * Peer who we think sent us the reply. Note: could be spoofed! If the * attacker knew we were searching for a particular key from a * particular peer, they could send us some searchReply messages with * bad values, trying to get us to consider that peer unreliable. * Potential fixes include either authenticated 'from' address or use a * nonce in the search + searchReply (and check for it in the selector). * */ private Hash _peer; private int _curIndex; private int _invalidPeers; private int _seenPeers; private int _newPeers; private int _duplicatePeers; private int _repliesPendingVerification; private long _duration; private SearchJob _searchJob; public SearchReplyJob(RouterContext enclosingContext, SearchJob job, DatabaseSearchReplyMessage message, Hash peer, long duration) { super(enclosingContext); _log = enclosingContext.logManager().getLog(getClass()); _searchJob = job; _msg = message; _peer = peer; _curIndex = 0; _invalidPeers = 0; _seenPeers = 0; _newPeers = 0; _duplicatePeers = 0; _repliesPendingVerification = 0; if (duration > 0) _duration = duration; else _duration = 0; } public String getName() { return "Process Reply for Kademlia Search"; } public void runJob() { if (_curIndex >= _msg.getNumReplies()) { if (_log.shouldLog(Log.DEBUG) && _msg.getNumReplies() == 0) _log.debug(getJobId() + ": dbSearchReply received with no routers referenced"); if (_repliesPendingVerification > 0) { // we received new references from the peer, but still // haven't verified all of them, so lets give it more time requeue(_searchJob.timeoutMs()); } else { // either they didn't tell us anything new or we have verified // (or failed to verify) all of them. we're done getContext().profileManager().dbLookupReply(_peer, _newPeers, _seenPeers, _invalidPeers, _duplicatePeers, _duration); if (_newPeers > 0) _searchJob.newPeersFound(_newPeers); } } else { Hash peer = _msg.getReply(_curIndex); boolean shouldAdd = false; RouterInfo info = getContext().netDb().lookupRouterInfoLocally(peer); if (info == null) { // if the peer is giving us lots of bad peer references, // dont try to fetch them. boolean sendsBadInfo = getContext().profileOrganizer().peerSendsBadReplies(_peer); if (!sendsBadInfo) { // we don't need to search for everthing we're given here - only ones that // are next in our search path... // note: no need to think about banlisted targets in the netdb search, given // the floodfill's behavior // This keeps us from continually chasing blocklisted floodfills if (getContext().banlist().isBanlisted(peer)) { // if (_log.shouldLog(Log.INFO)) // _log.info("Not looking for a banlisted peer..."); // getContext().statManager().addRateData("netDb.searchReplyValidationSkipped", 1, 0); } else { //getContext().netDb().lookupRouterInfo(peer, new ReplyVerifiedJob(getContext(), peer), new ReplyNotVerifiedJob(getContext(), peer), _timeoutMs); //_repliesPendingVerification++; shouldAdd = true; } } else { if (_log.shouldLog(Log.INFO)) _log.info("Peer " + _peer.toBase64() + " sends us bad replies, so not verifying " + peer.toBase64()); getContext().statManager().addRateData("netDb.searchReplyValidationSkipped", 1); } } if (_searchJob.wasAttempted(peer)) { _duplicatePeers++; } if (_log.shouldLog(Log.DEBUG)) _log.debug(getJobId() + ": dbSearchReply received on search referencing router " + peer + " already known? " + (info != null)); if (shouldAdd) { if (_searchJob.add(peer)) _newPeers++; else _seenPeers++; } _curIndex++; requeue(0); } } void replyVerified() { if (_log.shouldLog(Log.INFO)) _log.info("Peer reply from " + _peer.toBase64()); _repliesPendingVerification--; getContext().statManager().addRateData("netDb.searchReplyValidated", 1); } void replyNotVerified() { if (_log.shouldLog(Log.INFO)) _log.info("Peer reply from " + _peer.toBase64()); _repliesPendingVerification--; _invalidPeers++; getContext().statManager().addRateData("netDb.searchReplyNotValidated", 1); } } /** the peer gave us a reference to a new router, and we were able to fetch it */ /*** class ReplyVerifiedJob extends JobImpl { private Hash _key; private SearchReplyJob _replyJob; public ReplyVerifiedJob(RouterContext enclosingContext, SearchReplyJob srj, Hash key) { super(enclosingContext); _replyJob = srj; _key = key; } public String getName() { return "Search reply value verified"; } public void runJob() { _replyJob.replyVerified(); } } ***/ /** the peer gave us a reference to a new router, and we were NOT able to fetch it */ /*** class ReplyNotVerifiedJob extends JobImpl { private Hash _key; private SearchReplyJob _replyJob; public ReplyNotVerifiedJob(RouterContext enclosingContext, SearchReplyJob srj, Hash key) { super(enclosingContext); _key = key; _replyJob = srj; } public String getName() { return "Search reply value NOT verified"; } public void runJob() { _replyJob.replyNotVerified(); } } ***/