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();
 }
  /**
   * 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);
    }
  }
 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;
 }
  public boolean handleMessage(Message m) {
    PeerNode source = (PeerNode) m.getSource();
    if (source == null) {
      // Node has been disconnected and garbage collected already! Ouch.
      return true;
    }
    if (logMINOR) Logger.minor(this, "Dispatching " + m + " from " + source);
    if (callback != null) {
      try {
        callback.snoop(m, node);
      } catch (Throwable t) {
        Logger.error(this, "Callback threw " + t, t);
      }
    }
    MessageType spec = m.getSpec();
    if (spec == DMT.FNPPing) {
      // Send an FNPPong
      Message reply = DMT.createFNPPong(m.getInt(DMT.PING_SEQNO));
      try {
        source.sendAsync(reply, null, pingCounter); // nothing we can do if can't contact source
      } catch (NotConnectedException e) {
        if (logMINOR) Logger.minor(this, "Lost connection replying to " + m);
      }
      return true;
    } else if (spec == DMT.FNPStoreSecret) {
      return node.netid.handleStoreSecret(m);
    } else if (spec == DMT.FNPSecretPing) {
      return node.netid.handleSecretPing(m);
    } else if (spec == DMT.FNPDetectedIPAddress) {
      Peer p = (Peer) m.getObject(DMT.EXTERNAL_ADDRESS);
      source.setRemoteDetectedPeer(p);
      node.ipDetector.redetectAddress();
      return true;
    } else if (spec == DMT.FNPTime) {
      return handleTime(m, source);
    } else if (spec == DMT.FNPUptime) {
      return handleUptime(m, source);
    } else if (spec == DMT.FNPSentPackets) {
      source.handleSentPackets(m);
      return true;
    } else if (spec == DMT.FNPVoid) {
      return true;
    } else if (spec == DMT.FNPDisconnect) {
      handleDisconnect(m, source);
      return true;
    } else if (spec == DMT.nodeToNodeMessage) {
      node.receivedNodeToNodeMessage(m, source);
      return true;
    } else if (spec == DMT.UOMAnnounce && source.isRealConnection()) {
      return node.nodeUpdater.uom.handleAnnounce(m, source);
    } else if (spec == DMT.UOMRequestRevocation && source.isRealConnection()) {
      return node.nodeUpdater.uom.handleRequestRevocation(m, source);
    } else if (spec == DMT.UOMSendingRevocation && source.isRealConnection()) {
      return node.nodeUpdater.uom.handleSendingRevocation(m, source);
    } else if (spec == DMT.UOMRequestMain && source.isRealConnection()) {
      return node.nodeUpdater.uom.handleRequestJar(m, source, false);
    } else if (spec == DMT.UOMRequestExtra && source.isRealConnection()) {
      return node.nodeUpdater.uom.handleRequestJar(m, source, true);
    } else if (spec == DMT.UOMSendingMain && source.isRealConnection()) {
      return node.nodeUpdater.uom.handleSendingMain(m, source);
    } else if (spec == DMT.UOMSendingExtra && source.isRealConnection()) {
      return node.nodeUpdater.uom.handleSendingExt(m, source);
    } else if (spec == DMT.FNPOpennetAnnounceRequest) {
      return handleAnnounceRequest(m, source);
    } else if (spec == DMT.FNPRoutingStatus) {
      if (source instanceof DarknetPeerNode) {
        boolean value = m.getBoolean(DMT.ROUTING_ENABLED);
        if (logMINOR)
          Logger.minor(this, "The peer (" + source + ") asked us to set routing=" + value);
        ((DarknetPeerNode) source).setRoutingStatus(value, false);
      }
      // We claim it in any case
      return true;
    } else if (source.isRealConnection() && spec == DMT.FNPLocChangeNotificationNew) {
      double newLoc = m.getDouble(DMT.LOCATION);
      ShortBuffer buffer = ((ShortBuffer) m.getObject(DMT.PEER_LOCATIONS));
      double[] locs = Fields.bytesToDoubles(buffer.getData());

      /**
       * Do *NOT* remove the sanity check below!
       *
       * @see http://archives.freenetproject.org/message/20080718.144240.359e16d3.en.html
       */
      if ((OpennetManager.MAX_PEERS_FOR_SCALING < locs.length) && (source.isOpennet())) {
        if (locs.length > OpennetManager.PANIC_MAX_PEERS) {
          // This can't happen by accident
          Logger.error(
              this,
              "We received "
                  + locs.length
                  + " locations from "
                  + source.toString()
                  + "! That should *NOT* happen! Possible attack!");
          source.forceDisconnect(true);
          return true;
        } else {
          // A few extra can happen by accident. Just use the first 20.
          Logger.normal(
              this,
              "Too many locations from "
                  + source.toString()
                  + " : "
                  + locs.length
                  + " could be an accident, using the first "
                  + OpennetManager.MAX_PEERS_FOR_SCALING);
          double[] firstLocs = new double[OpennetManager.MAX_PEERS_FOR_SCALING];
          System.arraycopy(locs, 0, firstLocs, 0, OpennetManager.MAX_PEERS_FOR_SCALING);
          locs = firstLocs;
        }
      }
      // We are on darknet and we trust our peers OR we are on opennet
      // and the amount of locations sent to us seems reasonable
      source.updateLocation(newLoc, locs);

      return true;
    }

    if (!source.isRoutable()) return false;
    if (logDEBUG) Logger.debug(this, "Not routable");

    if (spec == DMT.FNPNetworkID) {
      source.handleFNPNetworkID(m);
      return true;
    } else if (spec == DMT.FNPSwapRequest) {
      return node.lm.handleSwapRequest(m, source);
    } else if (spec == DMT.FNPSwapReply) {
      return node.lm.handleSwapReply(m, source);
    } else if (spec == DMT.FNPSwapRejected) {
      return node.lm.handleSwapRejected(m, source);
    } else if (spec == DMT.FNPSwapCommit) {
      return node.lm.handleSwapCommit(m, source);
    } else if (spec == DMT.FNPSwapComplete) {
      return node.lm.handleSwapComplete(m, source);
    } else if (spec == DMT.FNPCHKDataRequest) {
      return handleDataRequest(m, source, false);
    } else if (spec == DMT.FNPSSKDataRequest) {
      return handleDataRequest(m, source, true);
    } else if (spec == DMT.FNPInsertRequest) {
      return handleInsertRequest(m, source, false);
    } else if (spec == DMT.FNPSSKInsertRequest) {
      return handleInsertRequest(m, source, true);
    } else if (spec == DMT.FNPSSKInsertRequestNew) {
      return handleInsertRequest(m, source, true);
    } else if (spec == DMT.FNPRHProbeRequest) {
      return handleProbeRequest(m, source);
    } else if (spec == DMT.FNPRoutedPing) {
      return handleRouted(m, source);
    } else if (spec == DMT.FNPRoutedPong) {
      return handleRoutedReply(m);
    } else if (spec == DMT.FNPRoutedRejected) {
      return handleRoutedRejected(m);
      // FIXME implement threaded probe requests of various kinds.
      // Old probe request code was a major pain, never really worked.
      // We should have threaded probe requests (for simple code),
      // and one for each routing strategy.
      //		} else if(spec == DMT.FNPProbeRequest) {
      //			return handleProbeRequest(m, source);
      //		} else if(spec == DMT.FNPProbeReply) {
      //			return handleProbeReply(m, source);
      //		} else if(spec == DMT.FNPProbeRejected) {
      //			return handleProbeRejected(m, source);
      //		} else if(spec == DMT.FNPProbeTrace) {
      //			return handleProbeTrace(m, source);
    } else if (spec == DMT.FNPOfferKey) {
      return handleOfferKey(m, source);
    } else if (spec == DMT.FNPGetOfferedKey) {
      return handleGetOfferedKey(m, source);
    }
    return false;
  }