def neighbours_within_distance(self, id, distance): """ naive correct version simply compares all nodes """ assert is_integer(id) nodes = list(n for n in self if n.id_distance(id) <= distance) return sorted(nodes, key=operator.methodcaller('id_distance', id))
def recv_find_node(self, remote, targetid): # FIXME, amplification attack (need to ping pong ping pong first) assert isinstance(remote, Node) assert is_integer(targetid) self.update(remote) found = self.routing.neighbours(targetid) log.debug('recv find_node', remoteid=remote, found=len(found)) self.wire.send_neighbours(remote, found)
def find_node(self, targetid, via_node=None): # FIXME, amplification attack (need to ping pong ping pong first) assert is_integer(targetid) assert not via_node or isinstance(via_node, Node) self._find_requests[targetid] = time.time() + k_request_timeout if via_node: self.wire.send_find_node(via_node, targetid) else: self._query_neighbours(targetid)
def __init__(self, ip, udp_port, tcp_port=0, from_binary=False): tcp_port = tcp_port or udp_port if from_binary: self.udp_port = dec_port(udp_port) self.tcp_port = dec_port(tcp_port) else: assert is_integer(udp_port) assert is_integer(tcp_port) self.udp_port = udp_port self.tcp_port = tcp_port try: # `ip` could be in binary or ascii format, independent of # from_binary's truthy. We use ad-hoc regexp to determine format _ip = str_to_bytes(ip) _ip = (bytes_to_str(ip) if PY3 else unicode(ip)) if ip_pattern.match(_ip) else _ip self._ip = ipaddress.ip_address(_ip) except ipaddress.AddressValueError as e: log.debug("failed to parse ip", error=e, ip=ip) raise e
def neighbours(self, node, k=k_bucket_size): """ sorting by bucket.midpoint does not work in edge cases build a short list of k * 2 nodes and sort and shorten it """ assert isinstance(node, Node) or is_integer(node) if isinstance(node, Node): node = node.id nodes = [] for bucket in self.buckets_by_id_distance(node): for n in bucket.nodes_by_id_distance(node): if n is not node: nodes.append(n) if len(nodes) == k * 2: break return sorted(nodes, key=operator.methodcaller('id_distance', node))[:k]
def __init__(self, protocol_id, cmd_id, payload, sequence_id, window_size, is_chunked_n=False, frames=None, frame_cipher=None): payload = memoryview(payload) assert is_integer(window_size) assert window_size % self.padding == 0 assert isinstance(cmd_id, int) and cmd_id < 256 self.cmd_id = cmd_id self.payload = payload if frame_cipher: self.frame_cipher = frame_cipher self.frames = frames or [] assert protocol_id < 2**16 self.protocol_id = protocol_id assert sequence_id is None or sequence_id < 2**16 self.sequence_id = sequence_id self.is_chunked_n = is_chunked_n self.frames.append(self) # chunk payloads resulting in frames exceeding window_size fs = self.frame_size() if fs > window_size: if not is_chunked_n: self.is_chunked_0 = True self.total_payload_size = self.body_size() # chunk payload self.payload = payload[:window_size - fs] assert self.frame_size() <= window_size remain = payload[len(self.payload):] assert len(remain) + len(self.payload) == len(payload) Frame(protocol_id, cmd_id, remain, sequence_id, window_size, is_chunked_n=True, frames=self.frames, frame_cipher=frame_cipher) assert self.frame_size() <= window_size
def __init__(self, proto, targetid, via_node=None, timeout=k_request_timeout, callback=None): assert isinstance(proto, KademliaProtocol) assert is_integer(targetid) assert not via_node or isinstance(via_node, Node) self.proto = proto self.targetid = targetid self.via_node = via_node self.timeout = time.time() + timeout self.callback = callback if via_node: self.wire.send_find_node(via_node, targetid) else: self._query_neighbours(targetid)
def send_find_node(self, node, target_node_id): """ ### Find Node (type 0x03) Find Node packets are sent to locate nodes close to a given target ID. The receiver should reply with a Neighbors packet containing the `k` nodes closest to target that it knows about. FindNode packet-type: 0x03 struct FindNode <= 76 bytes { NodeId target; // Id of a node. The responding node will send back nodes closest to the target. unsigned expiration; }; """ assert is_integer(target_node_id) target_node_id = utils.int_to_big_endian(target_node_id).rjust(kademlia.k_pubkey_size // 8, b'\0') assert len(target_node_id) == kademlia.k_pubkey_size // 8 log.debug('>>> find_node', remoteid=node) message = self.pack(self.cmd_id_map['find_node'], [target_node_id]) self.send(node, message)
def recv_neighbours(self, remote, neighbours): """ if one of the neighbours is closer than the closest known neighbour if not timed out query closest node for neighbours add all nodes to the list """ assert isinstance(neighbours, list) log.debug('recv neighbours', remoteid=remote, num=len(neighbours), local=self.this_node, neighbours=neighbours) neighbours = [n for n in neighbours if n != self.this_node] neighbours = [n for n in neighbours if n not in self.routing] # we don't map requests to responses, thus forwarding to all FIXME for nodeid, timeout in self._find_requests.items(): assert is_integer(nodeid) closest = sorted(neighbours, key=operator.methodcaller('id_distance', nodeid)) if time.time() < timeout: closest_known = self.routing.neighbours(nodeid) closest_known = closest_known[0] if closest_known else None assert closest_known != self.this_node # send find_node requests to k_find_concurrency closests for close_node in closest[:k_find_concurrency]: if not closest_known or \ close_node.id_distance(nodeid) < closest_known.id_distance(nodeid): log.debug('forwarding find request', closest=close_node, closest_known=closest_known) self.wire.send_find_node(close_node, nodeid) # add all nodes to the list for node in neighbours: if node != self.this_node: self.ping(node)
def buckets_by_id_distance(self, id): assert is_integer(id) return sorted(self.buckets, key=operator.methodcaller('id_distance', id))
def __init__(self, counter=0, sender=''): assert is_integer(counter) assert isinstance(sender, bytes) super(Token, self).__init__(counter, sender)