package lbms.plugins.mldht.kad; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; import lbms.plugins.mldht.kad.DHT.DHTtype; import lbms.plugins.mldht.kad.messages.FindNodeRequest; import lbms.plugins.mldht.kad.messages.FindNodeResponse; import lbms.plugins.mldht.kad.messages.MessageBase; import lbms.plugins.mldht.kad.messages.MessageBase.Method; import lbms.plugins.mldht.kad.messages.MessageBase.Type; /** * @author Damokles * */ public class NodeLookup extends Task { private int validReponsesSinceLastClosestSetModification; public SortedSet<Key> closestSet; public Map<MessageBase, Key> lookupMap; private boolean fillWithAllBuckets = false; NodeLookup (Key node_id, RPCServerBase rpc, Node node, boolean isBootstrap) { super(node_id, rpc, node); fillWithAllBuckets = !isBootstrap; this.closestSet = new TreeSet<Key>(new Key.DistanceOrder(targetKey)); this.lookupMap = new HashMap<MessageBase, Key>(); } @Override synchronized void update () { // go over the todo list and send find node calls // until we have nothing left synchronized (todo) { while (todo.size() > 0 && canDoRequest()) { KBucketEntry e = todo.first(); todo.remove(e); // only send a findNode if we haven't allready visited the node if (!visited.contains(e)) { // send a findNode to the node FindNodeRequest fnr = new FindNodeRequest(node.getOurID(), targetKey); fnr.setWant4(rpc.getDHT().getType() == DHTtype.IPV4_DHT || DHT.getDHT(DHTtype.IPV4_DHT).getNode() != null && DHT.getDHT(DHTtype.IPV4_DHT).getNode().getNumEntriesInRoutingTable() < DHTConstants.BOOTSTRAP_IF_LESS_THAN_X_PEERS); fnr.setWant6(rpc.getDHT().getType() == DHTtype.IPV6_DHT || DHT.getDHT(DHTtype.IPV6_DHT).getNode() != null && DHT.getDHT(DHTtype.IPV6_DHT).getNode().getNumEntriesInRoutingTable() < DHTConstants.BOOTSTRAP_IF_LESS_THAN_X_PEERS); fnr.setDestination(e.getAddress()); synchronized (lookupMap) { lookupMap.put(fnr, e.getID()); } rpcCall(fnr); synchronized (visited) { visited.add(e); } } // remove the entry from the todo list } } if (todo.size() == 0 && getNumOutstandingRequests() == 0 && !isFinished()) { done(); } else if (validReponsesSinceLastClosestSetModification >= DHTConstants.MAX_CONCURRENT_REQUESTS) { done(); // quit after 15 nodes responses } } @Override void callFinished (RPCCallBase c, MessageBase rsp) { if (isFinished()) { return; } // check the response and see if it is a good one if (rsp.getMethod() == Method.FIND_NODE && rsp.getType() == Type.RSP_MSG) { MessageBase b = c.getRequest(); synchronized (lookupMap) { if (lookupMap.containsKey(b)) { synchronized (closestSet) { Key toAdd = lookupMap.remove(b); closestSet.add(toAdd); if (closestSet.size() > DHTConstants.MAX_ENTRIES_PER_BUCKET) { Key last = closestSet.last(); closestSet.remove(last); if (toAdd == last) { validReponsesSinceLastClosestSetModification++; } else { validReponsesSinceLastClosestSetModification = 0; } } } } } FindNodeResponse fnr = (FindNodeResponse) rsp; for (DHTtype type : DHTtype.values()) { byte[] nodes = fnr.getNodes(type); if (nodes == null) continue; int nval = nodes.length / type.NODES_ENTRY_LENGTH; if (type == rpc.getDHT().getType()) { synchronized (todo) { for (int i = 0; i < nval; i++) { // add node to todo list KBucketEntry e = PackUtil.UnpackBucketEntry(nodes, i * type.NODES_ENTRY_LENGTH, type); if (!e.getID().equals(node.getOurID()) && !todo.contains(e) && !visited.contains(e)) { todo.add(e); } } } } else { for (int i = 0; i < nval; i++) { KBucketEntry e = PackUtil.UnpackBucketEntry(nodes, i * type.NODES_ENTRY_LENGTH, type); DHT.getDHT(type).addDHTNode(e.getAddress().getAddress().getHostAddress(), e.getAddress().getPort()); } } } } } @Override void callTimeout (RPCCallBase c) { } /* (non-Javadoc) * @see lbms.plugins.mldht.kad.Task#start() */ @Override void start () { int added = 0; // delay the filling of the todo list until we actually start the task KBucket[] buckets = node.getBuckets(); outer: for (int i = buckets.length -1; i >= 1; i--) if (buckets[i] != null) for (KBucketEntry e : buckets[i].getEntries()) if (!e.isBad()) { todo.add(e); added++; if (!fillWithAllBuckets && added >= 2 * DHTConstants.MAX_ENTRIES_PER_BUCKET) break outer; } super.start(); } @Override protected void done () { super.done(); rpc.getDHT().getEstimator().update(new TreeSet<Key>(closestSet)); } }