def _get_maintenance_query(self, node_): if not node_.id: return Query(self.find_closest_msg, node_) elif random.choice((False, True)): return Query(self.find_closest_msg, node_) target_log_distance = self.table.find_next_bucket_with_room_index( node_=node_) if target_log_distance: target = self.my_node.id.generate_close_id(target_log_distance) return Query( message.OutgoingFindNodeQuery(self.my_node.id, target), node_) else: return Query(self.ping_msg, node_)
def announce(self): if not self._bt_port: return [], False nodes_to_announce = self._lookup_queue.get_closest_responded_qnodes() announce_to_myself = False #TODO: is is worth it to announce to self? The problem is that I don't #know my own IP number. Maybe if 127.0.0.1 translates into "I (the #node returning 127.0.0.1) am in the swarm". ''' if len(nodes_to_announce) < ANNOUNCE_REDUNDANCY: announce_to_myself = True elif (self._my_id.log_distance(self._info_hash) < nodes_to_announce[ANNOUNCE_REDUNDANCY-1].id.log_distance( self._info_hash)): nodes_to_announce = nodes_to_announce[:-1] announce_to_myself = True ''' queries_to_send = [] for qnode in nodes_to_announce: logger.debug('announcing to %r' % qnode.node) msg = message.OutgoingAnnouncePeerQuery(self._my_id, self._info_hash, self._bt_port, qnode.token) queries_to_send.append(Query(msg, qnode.node, self)) return queries_to_send, announce_to_myself
def on_query_received(self, node_): ''' Return None when nothing to do Return a list of queries when queries need to be sent (the queries will be sent out by the caller) ''' if self._maintenance_mode != NORMAL_MODE: return log_distance = self.my_node.log_distance(node_) try: sbucket = self.table.get_sbucket(log_distance) except (IndexError): return # Got a query from myself. Just ignore it. m_bucket = sbucket.main rnode = m_bucket.get_rnode(node_) if rnode: # node in routing table: inform rnode self._update_rnode_on_query_received(rnode) return # node is not in the routing table if m_bucket.there_is_room(): # There is room in the bucket. Just add the new node. rnode = node_.get_rnode(log_distance) m_bucket.add(rnode) self.table.update_lowest_index(log_distance) self.table.num_rnodes += 1 self._update_rnode_on_query_received(rnode) return # No room in the main routing table # Check whether there is a bad node to be replaced. bad_rnode = self._pop_bad_rnode(m_bucket) if bad_rnode: # We have a bad node in the bucket. Replace it with the new node. rnode = node_.get_rnode(log_distance) m_bucket.add(rnode) self._update_rnode_on_query_received(rnode) self.table.update_lowest_index(log_distance) self.table.num_rnodes += 0 return # No bad nodes. Check for questionable nodes q_rnodes = self._get_questionable_rnodes(m_bucket) queries_to_send = [] # if q_rnodes: # print time.time(), '-----pinging questionable nodes in', # print log_distance # print q_rnodes for q_rnode in q_rnodes: # Ping questinable nodes to check whether they are still alive. # (0 timeouts so far, candidate node) c_rnode = node_.get_rnode(log_distance) self._update_rnode_on_query_received(c_rnode) self._pinged_q_rnodes[q_rnode] = [0, c_rnode] queries_to_send.append(Query(self.ping_msg, q_rnode)) return queries_to_send
def _get_lookup_queries(self, nodes): queries = [] for node_ in nodes: if node_.id == self._my_id: continue self._num_parallel_queries += 1 self.num_queries += len(nodes) queries.append(Query(self._get_peers_msg, node_, self)) return queries
def _get_maintenance_query(self, node_): if not node_.id: # Bootstrap nodes don't have id return Query(self.find_closest_msg, node_) if random.choice((False, True)): # 50% chance to send find_node with my id as target return Query(self.find_closest_msg, node_) # 50% chance to send a find_node to fill up a non-full bucket target_log_distance = self.table.find_next_bucket_with_room_index( node_=node_) if target_log_distance: target = self.my_node.id.generate_close_id(target_log_distance) return Query( message.OutgoingFindNodeQuery(self.my_node.id, target), node_) else: # Every bucket is full. We send a ping instead. return Query(self.ping_msg, node_)
def on_response_received(self, node_, rtt, nodes): log_distance = self.my_node.log_distance(node_) try: sbucket = self.table.get_sbucket(log_distance) except (IndexError): return # Got a response from myself. Just ignore it. m_bucket = sbucket.main rnode = m_bucket.get_rnode(node_) if rnode: # node in routing table: update self._update_rnode_on_response_received(rnode, rtt) if self._maintenance_mode == NORMAL_MODE: m_bucket.last_changed_ts = time.time() if node_ in self._pinged_q_rnodes: # This node is questionable. This response proves that it is # alive. Remove it from the questionable dict. del self._pinged_q_rnodes[node_] return # The node is not in main if m_bucket.there_is_room(): rnode = node_.get_rnode(log_distance) m_bucket.add(rnode) self.table.update_lowest_index(log_distance) self.table.num_rnodes += 1 self._update_rnode_on_response_received(rnode, rtt) if self._maintenance_mode == NORMAL_MODE: m_bucket.last_changed_ts = time.time() return # The main bucket is full # if there is a bad node inside the bucket, # replace it with the sending node_ bad_rnode = self._pop_bad_rnode(m_bucket) if bad_rnode: rnode = node_.get_rnode(log_distance) m_bucket.add(rnode) self._update_rnode_on_response_received(rnode, rtt) if self._maintenance_mode == NORMAL_MODE: m_bucket.last_changed_ts = time.time() self.table.update_lowest_index(log_distance) self.table.num_rnodes += 0 return # There are no bad nodes. Ping questionable nodes (if any) q_rnodes = self._get_questionable_rnodes(m_bucket) queries_to_send = [] for q_rnode in q_rnodes: # (0 timeouts so far, candidate node) c_rnode = node_.get_rnode(log_distance) self._update_rnode_on_response_received(c_rnode, rtt) self._pinged_q_rnodes[q_rnode] = [0, c_rnode] queries_to_send.append(Query(self.ping_msg, q_rnode)) return queries_to_send
def announce(self): if not self._bt_port: return ([], False) nodes_to_announce = self._lookup_queue.get_closest_responded_qnodes() announce_to_myself = False queries_to_send = [] for qnode in nodes_to_announce: logger.debug('announcing to %r' % qnode.node) msg = message.OutgoingAnnouncePeerQuery(self._my_id, self._info_hash, self._bt_port, qnode.token) queries_to_send.append(Query(msg, qnode.node, self)) return (queries_to_send, announce_to_myself)
def on_timeout(self, node_): if not node_.id: return # This is a bootstrap node (just addr, no id) log_distance = self.my_node.log_distance(node_) try: sbucket = self.table.get_sbucket(log_distance) except (IndexError): return # Got a timeout from myself, WTF? Just ignore. m_bucket = sbucket.main rnode = m_bucket.get_rnode(node_) if not rnode: # This node is not in the table. Nothing to do here return # The node is in the table. Update it self._update_rnode_on_timeout(rnode) t_strikes, c_rnode = self._pinged_q_rnodes.get(node_, (None, None)) if t_strikes is None: # The node is not being checked by a "questinable ping". return elif t_strikes == 0: # This is the first timeout self._pinged_q_rnodes[node_] = (1, c_rnode) # Let's give it another chance return [Query(self.ping_msg, rnode)] elif t_strikes == 1: # Second timeout. You're a bad node, replace if possible # check if the candidate node is in the routing table log_distance = self.my_node.log_distance(c_rnode) m_bucket = self.table.get_sbucket(log_distance).main c_rnode_in_table = m_bucket.get_rnode(c_rnode) if c_rnode_in_table: print 'questionable node replaced' # replace m_bucket.remove(rnode) m_bucket.add(c_rnode) self.table.update_lowest_index(log_distance) self.table.num_rnodes += 0
def _get_maintenance_query(self, node_): return Query(self.ping_msg, node_)