def __init__(self, node): self.node = node self.version = "EC\x00\x01" self.remote_nodes = NodeStore() self._started = False self._running_transactions = {} self.on_query_sent = [] self.on_query_received = [] self.on_response_sent = [] self.on_response_received = [] # Init handlers handlers = [ NodeStoreHandler, NodeInvalidater, PingHandler, FindNodeHandler, GetPeersHandler, AnnouncePeerHandler, MetaDataSpy] for handlerClass in handlers: handlerClass(self)
class DHTServer(object): def __init__(self, node): self.node = node self.version = "EC\x00\x01" self.remote_nodes = NodeStore() self._started = False self._running_transactions = {} self.on_query_sent = [] self.on_query_received = [] self.on_response_sent = [] self.on_response_received = [] # Init handlers handlers = [ NodeStoreHandler, NodeInvalidater, PingHandler, FindNodeHandler, GetPeersHandler, AnnouncePeerHandler, MetaDataSpy] for handlerClass in handlers: handlerClass(self) def start(self): if self._started: raise Exception self._started = True self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._sock.bind(("0.0.0.0", self.node.dht_port)) # bind all adapters Thread(target=self._receiver).start() def _receiver(self): """ Run in a different thread, and have as only goal to receive messages, parse them, and put them into a thread safe queue for further handling """ while True: try: # Receive message raw_message, connect_info = self._sock.recvfrom(4096) message = bdecode(raw_message) # print "Received f " + str(connect_info) + ":\t" + str(message) # This is a query from another peer. if message['y'] == 'q': # Retrieve or create source node. id_26 = message['a']['id'] + socket.inet_aton(connect_info[0]) + struct.pack(r'!H', connect_info[1]) source_node = self.remote_nodes.get_node(id_26) or Node.from_id_26(id_26) transaction = Transaction(query=message, query_node=source_node, response_node=self.node) # create transaction self._running_transactions[transaction.id] = transaction # Add it to running transactions for handler in self.on_query_received: handler(transaction) # Notify listeners elif message['y'] == 'e': raise Exception # This is a response to a transaction we started. elif message['y'] == 'r': transaction = self._running_transactions.pop(message["t"]) # pop transaction transaction.response = message for handler in self.on_response_received: handler(transaction) # Notify listeners else: raise Exception except socket.timeout: pass except BTFailure: pass except: print traceback.format_exc() def send_ping_query(self, target_node): return self.send_query(Transaction( query_node=self.node, response_node=target_node, query={ "y": "q", "t": urandom(2), "q": "ping", "a": {"id": self.node.id_20}, "v": self.version } )) def send_find_node(self, target_node, target_id): return self.send_query(Transaction( query_node=self.node, response_node=target_node, query={ "y": "q", "t": urandom(2), "q": "find_node", "a": {"id": self.node.id_20, "target": target_id}, "v": self.version } )) def send_get_peers(self, target_node, info_hash): return self.send_query(Transaction( query_node=self.node, response_node=target_node, query={ "y": "q", "t": urandom(2), "q": "get_peers", "a": {"id": self.node.id_20, "info_hash": info_hash}, "v": self.version } )) def send_announce_peer(self, target_node, info_hash, token, port): return self.send_query(Transaction( query_node=self.node, response_node=target_node, query={ "y": "q", "t": urandom(2), "q": "announce_peer", "a": {"id": self.node.id_20, "info_hash": info_hash, "token": token, "port": port}, "v": self.version } )) def send_response(self, transaction): # print "sending to " + str(transaction.query_node.dht_connect_info) + ":\t" + str(transaction.response) del self._running_transactions[transaction.id] self._sock.sendto( bencode(transaction.response), transaction.query_node.dht_connect_info ) for handler in self.on_response_sent: handler(transaction) def send_query(self, transaction): # print "sending to " + str(transaction.response_node.dht_connect_info) + ":\t" + str(transaction.query) self._running_transactions[transaction.id] = transaction self._sock.sendto( bencode(transaction.query), transaction.response_node.dht_connect_info ) for handler in self.on_query_sent: handler(transaction) return transaction