/** Handle an FNPRoutedRejected message. */ private boolean handleRoutedRejected(Message m) { long id = m.getLong(DMT.UID); Long lid = Long.valueOf(id); RoutedContext rc = routedContexts.get(lid); if (rc == null) { // Gah Logger.error(this, "Unrecognized FNPRoutedRejected"); return false; // locally originated?? } short htl = rc.lastHtl; if (rc.source != null) htl = rc.source.decrementHTL(htl); short ohtl = m.getShort(DMT.HTL); if (ohtl < htl) htl = ohtl; if (htl == 0) { // Equivalent to DNF. // Relay. if (rc.source != null) { try { rc.source.sendAsync( DMT.createFNPRoutedRejected(id, (short) 0), null, nodeStats.routedMessageCtr); } catch (NotConnectedException e) { // Ouch. Logger.error(this, "Unable to relay probe DNF: peer disconnected: " + rc.source); } } } else { // Try routing to the next node forward(rc.msg, id, rc.source, htl, rc.msg.getDouble(DMT.TARGET_LOCATION), rc, rc.identity); } return true; }
private boolean handleGetOfferedKey(Message m, PeerNode source) { Key key = (Key) m.getObject(DMT.KEY); byte[] authenticator = ((ShortBuffer) m.getObject(DMT.OFFER_AUTHENTICATOR)).getData(); long uid = m.getLong(DMT.UID); if (!HMAC.verifyWithSHA256( node.failureTable.offerAuthenticatorKey, key.getFullKey(), authenticator)) { Logger.error( this, "Invalid offer request from " + source + " : authenticator did not verify"); try { source.sendAsync( DMT.createFNPGetOfferedKeyInvalid(uid, DMT.GET_OFFERED_KEY_REJECTED_BAD_AUTHENTICATOR), null, node.failureTable.senderCounter); } catch (NotConnectedException e) { // Too bad. } return true; } if (logMINOR) Logger.minor(this, "Valid GetOfferedKey for " + key + " from " + source); // Do we want it? We can RejectOverload if we don't have the bandwidth... boolean isSSK = key instanceof NodeSSK; OfferReplyTag tag = new OfferReplyTag(isSSK); node.lockUID(uid, isSSK, false, true, false, tag); boolean needPubKey; try { needPubKey = m.getBoolean(DMT.NEED_PUB_KEY); String reject = nodeStats.shouldRejectRequest(true, false, isSSK, false, true, source, false); if (reject != null) { Logger.normal( this, "Rejecting FNPGetOfferedKey from " + source + " for " + key + " : " + reject); Message rejected = DMT.createFNPRejectedOverload(uid, true); try { source.sendAsync(rejected, null, node.failureTable.senderCounter); } catch (NotConnectedException e) { Logger.normal( this, "Rejecting (overload) data request from " + source.getPeer() + ": " + e); } node.unlockUID(uid, isSSK, false, false, true, false, tag); return true; } } catch (Error e) { node.unlockUID(uid, isSSK, false, false, true, false, tag); throw e; } catch (RuntimeException e) { node.unlockUID(uid, isSSK, false, false, true, false, tag); throw e; } // Otherwise, sendOfferedKey is responsible for unlocking. // Accept it. try { node.failureTable.sendOfferedKey(key, isSSK, needPubKey, uid, source, tag); } catch (NotConnectedException e) { // Too bad. } return true; }
/** * Handle a routed-to-a-specific-node message. * * @param m * @return False if we want the message put back on the queue. */ boolean handleRouted(Message m, PeerNode source) { if (logMINOR) Logger.minor(this, "handleRouted(" + m + ')'); long id = m.getLong(DMT.UID); Long lid = Long.valueOf(id); short htl = m.getShort(DMT.HTL); byte[] identity = ((ShortBuffer) m.getObject(DMT.NODE_IDENTITY)).getData(); if (source != null) htl = source.decrementHTL(htl); RoutedContext ctx; ctx = routedContexts.get(lid); if (ctx != null) { try { source.sendAsync(DMT.createFNPRoutedRejected(id, htl), null, nodeStats.routedMessageCtr); } catch (NotConnectedException e) { if (logMINOR) Logger.minor(this, "Lost connection rejecting " + m); } return true; } ctx = new RoutedContext(m, source, identity); synchronized (routedContexts) { routedContexts.put(lid, ctx); } // source == null => originated locally, keep full htl double target = m.getDouble(DMT.TARGET_LOCATION); if (logMINOR) Logger.minor(this, "id " + id + " from " + source + " htl " + htl + " target " + target); if (Math.abs(node.lm.getLocation() - target) <= Double.MIN_VALUE) { if (logMINOR) Logger.minor(this, "Dispatching " + m.getSpec() + " on " + node.getDarknetPortNumber()); // Handle locally // Message type specific processing dispatchRoutedMessage(m, source, id); return true; } else if (htl == 0) { Message reject = DMT.createFNPRoutedRejected(id, (short) 0); if (source != null) try { source.sendAsync(reject, null, nodeStats.routedMessageCtr); } catch (NotConnectedException e) { if (logMINOR) Logger.minor(this, "Lost connection rejecting " + m); } return true; } else { return forward(m, id, source, htl, target, ctx, identity); } }
boolean handleRoutedReply(Message m) { long id = m.getLong(DMT.UID); if (logMINOR) Logger.minor(this, "Got reply: " + m); Long lid = Long.valueOf(id); RoutedContext ctx = routedContexts.get(lid); if (ctx == null) { Logger.error(this, "Unrecognized routed reply: " + m); return false; } PeerNode pn = ctx.source; if (pn == null) return false; try { pn.sendAsync(m, null, nodeStats.routedMessageCtr); } catch (NotConnectedException e) { if (logMINOR) Logger.minor(this, "Lost connection forwarding " + m + " to " + pn); } return true; }
private boolean handleAnnounceRequest(Message m, PeerNode source) { long uid = m.getLong(DMT.UID); OpennetManager om = node.getOpennet(); if (om == null || !source.canAcceptAnnouncements()) { Message msg = DMT.createFNPOpennetDisabled(uid); try { source.sendAsync(msg, null, node.nodeStats.announceByteCounter); } catch (NotConnectedException e) { // Ok } return true; } if (node.recentlyCompleted(uid)) { Message msg = DMT.createFNPRejectedLoop(uid); try { source.sendAsync(msg, null, node.nodeStats.announceByteCounter); } catch (NotConnectedException e) { // Ok } return true; } boolean success = false; // No way to check whether it's actually running atm, so lets report it to the completed list // immediately. // FIXME we should probably keep a list! node.completed(uid); try { if (!source.shouldAcceptAnnounce(uid)) { Message msg = DMT.createFNPRejectedOverload(uid, true); try { source.sendAsync(msg, null, node.nodeStats.announceByteCounter); } catch (NotConnectedException e) { // Ok } return true; } AnnounceSender sender = new AnnounceSender(m, uid, source, om, node); node.executor.execute(sender, "Announcement sender for " + uid); success = true; return true; } finally { if (!success) source.completedAnnounce(uid); } }
private static long getSecretPingResponse(Node source, PeerNode pathway, long uid) throws DisconnectedException { // wait for a reject or pong MessageFilter mfPong = MessageFilter.create() .setSource(pathway) .setField(DMT.UID, uid) .setTimeout(SECRETPONG_TIMEOUT) .setType(DMT.FNPSecretPong); MessageFilter mfRejectLoop = MessageFilter.create() .setSource(pathway) .setField(DMT.UID, uid) .setTimeout(SECRETPONG_TIMEOUT) .setType(DMT.FNPRejectedLoop); Message msg = source.getUSM().waitFor(mfPong.or(mfRejectLoop), null); if (msg == null) { Logger.error( source, "fatal timeout in waiting for secretpong from " + getPortNumber(pathway)); return -2; } if (msg.getSpec() == DMT.FNPSecretPong) { int suppliedCounter = msg.getInt(DMT.COUNTER); long secret = msg.getLong(DMT.SECRET); Logger.normal(source, "got secret, counter=" + suppliedCounter); return secret; } if (msg.getSpec() == DMT.FNPRejectedLoop) { Logger.error( source, "top level secret ping should not reject!: " + getPortNumber(source) + " -> " + getPortNumber(pathway)); return -1; } return -3; }
private boolean handleProbeRequest(Message m, PeerNode source) { long id = m.getLong(DMT.UID); if (node.recentlyCompleted(id)) { Message rejected = DMT.createFNPRejectedLoop(id); try { source.sendAsync(rejected, null, node.nodeStats.probeRequestCtr); } catch (NotConnectedException e) { Logger.normal(this, "Rejecting probe request from " + source.getPeer() + ": " + e); } return true; } // Lets not bother with full lockUID, just add it to the recently completed list. node.completed(id); // SSKs don't fix bwlimitDelayTime so shouldn't be accepted when overloaded. if (source.shouldRejectProbeRequest()) { Logger.normal(this, "Rejecting probe request from " + source.getPeer()); Message rejected = DMT.createFNPRejectedOverload(id, true); try { source.sendAsync(rejected, null, node.nodeStats.probeRequestCtr); } catch (NotConnectedException e) { Logger.normal( this, "Rejecting (overload) insert request from " + source.getPeer() + ": " + e); } return true; } double target = m.getDouble(DMT.TARGET_LOCATION); if (target > 1.0 || target < 0.0) { Logger.normal( this, "Rejecting invalid (target=" + target + ") probe request from " + source.getPeer()); Message rejected = DMT.createFNPRejectedOverload(id, true); try { source.sendAsync(rejected, null, node.nodeStats.probeRequestCtr); } catch (NotConnectedException e) { Logger.normal( this, "Rejecting (invalid) insert request from " + source.getPeer() + ": " + e); } return true; } ProbeRequestHandler.start(m, source, node, target); return true; }
static void start(Message m, PeerNode source, Node n, double target) { long uid = m.getLong(DMT.UID); double nearestLoc = m.getDouble(DMT.NEAREST_LOCATION); double best = m.getDouble(DMT.BEST_LOCATION); short htl = m.getShort(DMT.HTL); ProbeRequestSender sender = new ProbeRequestSender(target, htl, uid, n, nearestLoc, source, best); ProbeRequestHandler handler = new ProbeRequestHandler(source, uid, sender); sender.addListener(handler); PeerNode[] peers = n.peers.connectedPeers; Message accepted = DMT.createFNPAccepted(uid); Message trace = DMT.createFNPRHProbeTrace( uid, sender.getNearestLoc(), sender.getBest(), htl, (short) 1, (short) 1, n.getLocation(), n.swapIdentifier, LocationManager.extractLocs(peers, true), LocationManager.extractUIDs(peers), (short) 0, (short) 1, "", source.swapIdentifier); try { source.sendAsync(accepted, null, sender); source.sendAsync(trace, null, sender); } catch (NotConnectedException e) { // We completed(id), rather than locking it, so we don't need to unlock. return; // So all we need to do is not start the sender. } sender.start(); }
private boolean handleInsertRequest(Message m, PeerNode source, boolean isSSK) { ByteCounter ctr = isSSK ? node.nodeStats.sskInsertCtr : node.nodeStats.chkInsertCtr; long id = m.getLong(DMT.UID); if (node.recentlyCompleted(id)) { Message rejected = DMT.createFNPRejectedLoop(id); try { source.sendAsync(rejected, null, ctr); } catch (NotConnectedException e) { Logger.normal(this, "Rejecting insert request from " + source.getPeer() + ": " + e); } return true; } InsertTag tag = new InsertTag(isSSK, InsertTag.START.REMOTE); if (!node.lockUID(id, isSSK, true, false, false, tag)) { if (logMINOR) Logger.minor(this, "Could not lock ID " + id + " -> rejecting (already running)"); Message rejected = DMT.createFNPRejectedLoop(id); try { source.sendAsync(rejected, null, ctr); } catch (NotConnectedException e) { Logger.normal(this, "Rejecting insert request from " + source.getPeer() + ": " + e); } return true; } // SSKs don't fix bwlimitDelayTime so shouldn't be accepted when overloaded. String rejectReason = nodeStats.shouldRejectRequest(!isSSK, true, isSSK, false, false, source, false); if (rejectReason != null) { Logger.normal( this, "Rejecting insert from " + source.getPeer() + " preemptively because " + rejectReason); Message rejected = DMT.createFNPRejectedOverload(id, true); try { source.sendAsync(rejected, null, ctr); } catch (NotConnectedException e) { Logger.normal( this, "Rejecting (overload) insert request from " + source.getPeer() + ": " + e); } node.unlockUID(id, isSSK, true, false, false, false, tag); return true; } boolean forkOnCacheable = Node.FORK_ON_CACHEABLE_DEFAULT; Message forkControl = m.getSubMessage(DMT.FNPSubInsertForkControl); if (forkControl != null) forkOnCacheable = forkControl.getBoolean(DMT.ENABLE_INSERT_FORK_WHEN_CACHEABLE); long now = System.currentTimeMillis(); if (m.getSpec().equals(DMT.FNPSSKInsertRequest)) { NodeSSK key = (NodeSSK) m.getObject(DMT.FREENET_ROUTING_KEY); byte[] data = ((ShortBuffer) m.getObject(DMT.DATA)).getData(); byte[] headers = ((ShortBuffer) m.getObject(DMT.BLOCK_HEADERS)).getData(); short htl = m.getShort(DMT.HTL); SSKInsertHandler rh = new SSKInsertHandler( key, data, headers, htl, source, id, node, now, tag, node.canWriteDatastoreInsert(htl), forkOnCacheable); rh.receivedBytes(m.receivedByteCount()); node.executor.execute( rh, "SSKInsertHandler for " + id + " on " + node.getDarknetPortNumber()); } else if (m.getSpec().equals(DMT.FNPSSKInsertRequestNew)) { NodeSSK key = (NodeSSK) m.getObject(DMT.FREENET_ROUTING_KEY); short htl = m.getShort(DMT.HTL); SSKInsertHandler rh = new SSKInsertHandler( key, null, null, htl, source, id, node, now, tag, node.canWriteDatastoreInsert(htl), forkOnCacheable); rh.receivedBytes(m.receivedByteCount()); node.executor.execute( rh, "SSKInsertHandler for " + id + " on " + node.getDarknetPortNumber()); } else { CHKInsertHandler rh = new CHKInsertHandler(m, source, id, node, now, tag, forkOnCacheable); node.executor.execute( rh, "CHKInsertHandler for " + id + " on " + node.getDarknetPortNumber()); } if (logMINOR) Logger.minor(this, "Started InsertHandler for " + id); return true; }
/** Handle an incoming FNPDataRequest. */ private boolean handleDataRequest(Message m, PeerNode source, boolean isSSK) { long id = m.getLong(DMT.UID); ByteCounter ctr = isSSK ? node.nodeStats.sskRequestCtr : node.nodeStats.chkRequestCtr; if (node.recentlyCompleted(id)) { Message rejected = DMT.createFNPRejectedLoop(id); try { source.sendAsync(rejected, null, ctr); } catch (NotConnectedException e) { Logger.normal(this, "Rejecting data request (loop, finished): " + e); } return true; } short htl = m.getShort(DMT.HTL); Key key = (Key) m.getObject(DMT.FREENET_ROUTING_KEY); final RequestTag tag = new RequestTag(isSSK, RequestTag.START.REMOTE); if (!node.lockUID(id, isSSK, false, false, false, tag)) { if (logMINOR) Logger.minor(this, "Could not lock ID " + id + " -> rejecting (already running)"); Message rejected = DMT.createFNPRejectedLoop(id); try { source.sendAsync(rejected, null, ctr); } catch (NotConnectedException e) { Logger.normal(this, "Rejecting request from " + source.getPeer() + ": " + e); } node.failureTable.onFinalFailure(key, null, htl, htl, -1, source); return true; } else { if (logMINOR) Logger.minor(this, "Locked " + id); } // There are at least 2 threads that call this function. // DO NOT reuse the meta object, unless on a per-thread basis. // Object allocation is pretty cheap in modern Java anyway... // If we do reuse it, call reset(). BlockMetadata meta = new BlockMetadata(); KeyBlock block = node.fetch(key, false, false, false, false, meta); String rejectReason = nodeStats.shouldRejectRequest( !isSSK, false, isSSK, false, false, source, block != null && !meta.isOldBlock()); if (rejectReason != null) { // can accept 1 CHK request every so often, but not with SSKs because they aren't throttled so // won't sort out bwlimitDelayTime, which was the whole reason for accepting them when // overloaded... Logger.normal( this, "Rejecting " + (isSSK ? "SSK" : "CHK") + " request from " + source.getPeer() + " preemptively because " + rejectReason); Message rejected = DMT.createFNPRejectedOverload(id, true); try { source.sendAsync(rejected, null, ctr); } catch (NotConnectedException e) { Logger.normal( this, "Rejecting (overload) data request from " + source.getPeer() + ": " + e); } tag.setRejected(); node.unlockUID(id, isSSK, false, false, false, false, tag); // Do not tell failure table. // Otherwise an attacker can flood us with requests very cheaply and purge our // failure table even though we didn't accept any of them. return true; } nodeStats.reportIncomingRequestLocation(key.toNormalizedDouble()); // if(!node.lockUID(id)) return false; RequestHandler rh = new RequestHandler(m, source, id, node, htl, key, tag, block); node.executor.execute( rh, "RequestHandler for UID " + id + " on " + node.getDarknetPortNumber()); return true; }
private boolean handleTime(Message m, PeerNode source) { long delta = m.getLong(DMT.TIME) - System.currentTimeMillis(); source.setTimeDelta(delta); return true; }