def test(self): # Sender doesn't use private flag ping_public = m.OutgoingPingQuery(tc.CLIENT_ID) bencoded_public = ping_public.encode(tc.TID) # Sender uses private flag PRIVATE1 m.private_dht_name = 'PRIVATE1' ping_private1 = m.OutgoingPingQuery(tc.CLIENT_ID) bencoded_private1 = ping_private1.encode(tc.TID) # Sender uses private flag PRIVATE1 m.private_dht_name = 'PRIVATE2' ping_private2 = m.OutgoingPingQuery(tc.CLIENT_ID) bencoded_private2 = ping_private2.encode(tc.TID) # Receiver in the public DHT accepts messages (ignores private flag) m.private_dht_name = None m.IncomingMsg(bencoded_public, tc.CLIENT_ADDR) m.IncomingMsg(bencoded_private1, tc.CLIENT_ADDR) m.IncomingMsg(bencoded_private2, tc.CLIENT_ADDR) # Receiver in the private DHT accepts ONLY messages from the # private DHT it belongs to m.private_dht_name = 'PRIVATE1' assert_raises(m.MsgError, m.IncomingMsg, bencoded_public, tc.CLIENT_ADDR) m.IncomingMsg(bencoded_private1, tc.CLIENT_ADDR) assert_raises(m.MsgError, m.IncomingMsg, bencoded_private2, tc.CLIENT_ADDR)
def _setup(self): global time time = querier.time = MockTime() self.ping_msg = message.OutgoingPingQuery(tc.CLIENT_ID) ping_r_out = message.OutgoingPingResponse(tc.SERVER_ID) self.ping_r_in = message.IncomingMsg(ping_r_out.encode(tc.TID), tc.SERVER_ADDR) fn_r_out = message.OutgoingFindNodeResponse(tc.SERVER_ID, nodes2=tc.NODES) self.fn_r_in = message.IncomingMsg(fn_r_out.encode(tc.TID), tc.SERVER_ADDR) self.got_response = False self.got_error = False self.got_timeout = False self.got_routing_response = False self.got_routing_error = False self.got_routing_timeout = False self.got_routing_nodes_found = False timeout_task = minitwisted.Task(1, self.on_timeout, tc.SERVER_NODE) self.query = querier.Query(tc.TID, self.ping_msg.query, tc.SERVER_NODE, timeout_task)
def _exchange_msgs(self, outgoing_query, outgoing_response): #client data = outgoing_query.encode(tc.TID) # querier.register_query() #server incoming_query = m.IncomingMsg(data, tc.CLIENT_ADDR) eq_(incoming_query.type, m.QUERY) data = outgoing_response.encode(incoming_query.tid) #client incoming_response = m.IncomingMsg(data, tc.SERVER_ADDR) assert incoming_response.type is m.RESPONSE
def test_ping(self): #client outgoing_query = m.OutgoingPingQuery(tc.CLIENT_ID) data = outgoing_query.encode(tc.TID) # query_manager would do it #server incoming_query = m.IncomingMsg(data) assert incoming_query.type is m.QUERY outgoing_response = m.OutgoingPingResponse(tc.SERVER_ID) data = outgoing_response.encode(incoming_query.tid) #client incoming_response = m.IncomingMsg(data) assert incoming_response.type is m.RESPONSE incoming_response.sanitize_response(outgoing_query.query)
def test_announce_peer(self): #client outgoing_query = m.OutgoingAnnouncePeerQuery(tc.CLIENT_ID, tc.INFO_HASH, tc.BT_PORT, tc.TOKEN) outgoing_query.tid = tc.TID data = outgoing_query.encode(tc.TID) #server incoming_query = m.IncomingMsg(data, tc.CLIENT_ADDR) assert incoming_query.type is m.QUERY outgoing_response = m.OutgoingAnnouncePeerResponse(tc.SERVER_ID) data = outgoing_response.encode(incoming_query.tid) #client incoming_response = m.IncomingMsg(data, tc.SERVER_ADDR) assert incoming_response.type is m.RESPONSE
def test_find_node(self): #client outgoing_query = m.OutgoingFindNodeQuery(tc.CLIENT_ID, tc.NODE_ID) data = outgoing_query.encode(tc.TID) #server incoming_query = m.IncomingMsg(data, tc.CLIENT_ADDR) assert incoming_query.type is m.QUERY outgoing_response = m.OutgoingFindNodeResponse(tc.SERVER_ID, tc.NODES) data = outgoing_response.encode(incoming_query.tid) #client incoming_response = m.IncomingMsg(data, tc.SERVER_ADDR) eq_(incoming_response.type, m.RESPONSE) #incoming_response.sanitize_response(outgoing_query.query) for n1, n2 in zip(tc.NODES, incoming_response.all_nodes): eq_(n1, n2)
def test_get_peers_nodes(self): #client outgoing_query = m.OutgoingGetPeersQuery(tc.CLIENT_ID, tc.INFO_HASH) data = outgoing_query.encode(tc.TID) #server incoming_query = m.IncomingMsg(data, tc.CLIENT_ADDR) assert incoming_query.type is m.QUERY outgoing_response = m.OutgoingGetPeersResponse(tc.SERVER_ID, tc.TOKEN, tc.NODES) data = outgoing_response.encode(incoming_query.tid) #client incoming_response = m.IncomingMsg(data, tc.SERVER_ADDR) assert incoming_response.type is m.RESPONSE #incoming_response.sanitize_response(outgoing_query.query) for n1, n2 in zip(tc.NODES, incoming_response.all_nodes): assert n1 == n2
def _gen_nodes_args(node_, nodes): out_msg = message.OutgoingGetPeersResponse(node_.id, tc.TOKEN, nodes2=nodes).encode(tc.TID) in_msg = message.IncomingMsg(out_msg) in_msg.sanitize_response(message.GET_PEERS) return in_msg, node_
def test_unknown_error(self): error_code = (999, "some weird error string") b_err = m.OutgoingErrorMsg(error_code).encode(tc.TID) logger.info( "TEST LOGGING ** IGNORE EXPECTED INFO ** Unknown error: %r", error_code) _ = m.IncomingMsg(b_err, tc.CLIENT_ADDR)
def test_get_peers_peers(self): #client outgoing_query = m.OutgoingGetPeersQuery(tc.CLIENT_ID, tc.INFO_HASH) data = outgoing_query.encode(tc.TID) #server incoming_query = m.IncomingMsg(data) assert incoming_query.type is m.QUERY outgoing_response = m.OutgoingGetPeersResponse(tc.SERVER_ID, tc.TOKEN, peers=tc.PEERS) data = outgoing_response.encode(incoming_query.tid) #client incoming_response = m.IncomingMsg(data) assert incoming_response.type is m.RESPONSE incoming_response.sanitize_response(outgoing_query.query) for p1, p2 in zip(tc.PEERS, incoming_response.peers): assert p1[0] == p2[0] assert p1[1] == p2[1]
def _test_fire_callback_on_error(self): # the server creates the response error_msg = message.OutgoingErrorMsg(message.GENERIC_E) error_data = error_msg.encode(tc.TID) # IncomingMsg decodes the response received error_msg = message.IncomingMsg(error_data, tc.SERVER_ADDR) # querier notifies of the message (callback) self.query.on_error_received(error_msg) assert not self.got_response and self.got_error
def _on_datagram_received(self, data, addr): try: msg = message.IncomingMsg(data, addr) except(message.MsgError): return # ignore message if msg.type == message.QUERY: response_msg = self._get_response(msg) if response_msg: bencoded_response = response_msg.encode(msg.tid) self._reactor.sendto(bencoded_response, addr) maintenance_queries_to_send = self._routing_m.on_query_received( msg.sender_node) elif msg.type in (message.RESPONSE, message.ERROR): related_query = self._querier.on_response_received(msg, addr) if not related_query: # Query timed out or unrequested response return # lookup related tasks if related_query.lookup_obj: if msg.type == message.RESPONSE: (lookup_queries_to_send, peers, num_parallel_queries, lookup_done ) = related_query.lookup_obj.on_response_received( msg, msg.sender_node) else: #ERROR peers = None # an error msg doesn't have peers (lookup_queries_to_send, num_parallel_queries, lookup_done ) = related_query.lookup_obj.on_error_received( msg, msg.sender_node) self._send_queries(lookup_queries_to_send) if related_query.lookup_obj.callback_f: lookup_id = related_query.lookup_obj.lookup_id if peers: related_query.lookup_obj.callback_f(lookup_id, peers) if lookup_done: related_query.lookup_obj.callback_f(lookup_id, None) # maintenance related tasks if msg.type == message.RESPONSE: maintenance_queries_to_send = \ self._routing_m.on_response_received( msg.sender_node, related_query.rtt, msg.all_nodes) else: maintenance_queries_to_send = \ self._routing_m.on_error_received( msg.sender_node) else: # unknown type return self._send_queries(maintenance_queries_to_send)
def test_unsolicited_response(self): # Server creates unsolicited response # It might well be that the server responds using another port, # and therefore, the addr is not matched # TODO: consider accepting responses from a different port ping_r_msg_out = message.OutgoingPingResponse(tc.SERVER_ID) bencoded_r = ping_r_msg_out.encode('zz') # The client receives the bencoded message ping_r_in = message.IncomingMsg(bencoded_r, tc.SERVER_ADDR) stored_q = self.querier.on_response_received(ping_r_in, tc.SERVER_ADDR) assert stored_q is None
def _test_fire_callback_on_response(self): # the server creates the response pong_msg = message.OutgoingPingResponse(tc.SERVER_ID) pong_data = pong_msg.encode(tc.TID) # IncomingMsg decodes the response received pong_msg = message.IncomingMsg(pong_data, tc.SERVER_ADDR) # querier notifies of the message (callback) self.query.on_response_received(pong_msg) ok_(self.got_response) ok_(not self.got_error) ok_(not self.got_timeout)
def _on_datagram_received(self, data, addr): try: msg = message.IncomingMsg(data, addr) except message.MsgError: return if msg.type == message.QUERY: if msg.sender_id == self._my_id: logger.debug('Got a msg from myself:\n%r', msg) return response_msg = self._get_response(msg) if response_msg: bencoded_response = response_msg.encode(msg.tid) self._reactor.sendto(bencoded_response, addr) maintenance_queries_to_send = self._routing_m.on_query_received( msg.sender_node) elif msg.type == message.RESPONSE: related_query = self._querier.on_response_received(msg, addr) if not related_query: return if related_query.lookup_obj: if msg.type == message.RESPONSE: lookup_queries_to_send, peers, num_parallel_queries, lookup_done = related_query.lookup_obj.on_response_received( msg, msg.sender_node) self._send_queries(lookup_queries_to_send) if related_query.lookup_obj.callback_f: lookup_id = related_query.lookup_obj.lookup_id if peers: related_query.lookup_obj.callback_f(lookup_id, peers) if lookup_done: self._announce(related_query.lookup_obj) related_query.lookup_obj.callback_f(lookup_id, None) maintenance_queries_to_send = self._routing_m.on_response_received( msg.sender_node, related_query.rtt, msg.all_nodes) elif msg.type == message.ERROR: related_query = self._querier.on_error_received(msg, addr) if not related_query: return if related_query.lookup_obj: peers = None lookup_queries_to_send, num_parallel_queries, lookup_done = related_query.lookup_obj.on_error_received( msg, addr) self._send_queries(lookup_queries_to_send) if related_query.lookup_obj.callback_f: lookup_id = related_query.lookup_obj.lookup_id if lookup_done: self._announce(related_query.lookup_obj) related_query.lookup_obj.callback_f(lookup_id, None) maintenance_queries_to_send = self._routing_m.on_error_received( addr) else: return self._send_queries(maintenance_queries_to_send)
def send_query_and_get_response(self, querier_, later_delay=0): ping_msg = message.OutgoingPingQuery(tc.CLIENT_ID) msg = message.OutgoingFindNodeQuery(tc.CLIENT_ID, tc.CLIENT_ID) if later_delay: task = querier_.send_query_later(later_delay, msg, tc.EXTERNAL_NODE, self.on_response, self.on_timeout, self.on_error, tc.TIMEOUT_DELAY) # This second query is just to have two elements # in the querier_.pending[tc.EXTERNAL_ADDR] list task = querier_.send_query_later(later_delay, msg, tc.EXTERNAL_NODE, self.on_response, self.on_timeout, self.on_error, tc.TIMEOUT_DELAY) else: node_ = (querier_ == self.querier_mock) and tc.SERVER_NODE query = querier_.send_query(ping_msg, node_ or tc.EXTERNAL_NODE, self.on_response, self.on_timeout, self.on_error, timeout_delay=tc.TIMEOUT_DELAY) # querier creates TID msg_tid = '\0\0' if querier_ is self.querier_mock: # server gets query # the server creates the response pong_msg = message.OutgoingPingResponse(tc.SERVER_ID) pong_msg_data = pong_msg.encode(msg_tid) # the client gets the response # rpc_m decodes msg and calls callback pong_msg = message.IncomingMsg(pong_msg_data) querier_.on_response_received(pong_msg, tc.SERVER_ADDR) if later_delay: ok_(not self.got_response) ok_(not self.got_timeout) time.sleep(later_delay*2) time.sleep(tc.TIMEOUT_DELAY+.1) ### It crashed (timeout_task.cancelled??????) #TODO2: move the 'real' tests to integration ############################################### ### A DHT node must be running on peer_addr ### ############################################### ok_(self.got_response) ok_(not self.got_timeout)
def test_fire_callback_on_late_response(self): self.query.timeout_task.fire_callbacks() self.query.timeout_task.cancel() # the server creates the response pong_msg = message.OutgoingPingResponse(tc.SERVER_ID) pong_data = pong_msg.encode(tc.TID) # IncomingMsg decodes the response received pong_msg = message.IncomingMsg(pong_data, tc.SERVER_ADDR) # querier notifies of the message (but it's too late) self.query.on_response_received(pong_msg) logger.warning("**IGNORE WARNING LOG**") assert not self.got_response and not self.got_error \ and self.got_timeout
def test_errors(self): # client side query_msg = message.OutgoingPingQuery(tc.CLIENT_ID) # querier.send_query() encodes query_data = query_msg.encode(tc.TID) # server side # rpc_manager.datagram_received() decodes and calls responder (callback) query_msg = message.IncomingMsg(query_data) ## 'xxxxxx' is not a valid QUERY query_msg.query = 'zzzzzzzz' assert not self.notification_callback_done ok_( self.responder.on_query_received(query_msg, tc.CLIENT_ADDR) is None )
def test_query(self): # no m.QUERY del self.ping_d[m.QUERY] assert_raises(m.MsgError, m.IncomingMsg, bencode.encode(self.ping_d)) # bad m.QUERY self.ping_d[m.QUERY] = 1 assert_raises(m.MsgError, m.IncomingMsg, bencode.encode(self.ping_d)) self.ping_d[m.QUERY] = [] assert_raises(m.MsgError, m.IncomingMsg, bencode.encode(self.ping_d)) self.ping_d[m.QUERY] = {} assert_raises(m.MsgError, m.IncomingMsg, bencode.encode(self.ping_d)) # unknown m.QUERY is not an error at this point # responder will process it and send an errror msg if necesary self.ping_d[m.QUERY] = 'a' m.IncomingMsg(bencode.encode(self.ping_d))
def test_error(self): # Client creates a query ping_msg = message.OutgoingPingQuery(tc.CLIENT_ID) q = Query(ping_msg, tc.SERVER_NODE) timeout_task = minitwisted.Task(TIMEOUT_DELAY, None) # Client registers query bencoded_msg = self.querier.register_query(q, timeout_task) # Client sends bencoded_msg time.sleep(1) # Server gets bencoded_msg and creates response ping_r_msg_out = message.OutgoingErrorMsg(message.GENERIC_E) bencoded_r = ping_r_msg_out.encode(tc.TID) # The client receives the bencoded message ping_r_in = message.IncomingMsg(bencoded_r, tc.SERVER_ADDR) stored_q = self.querier.on_error_received(ping_r_in, tc.SERVER_ADDR) assert stored_q is None
def test_return_response_for_find_node(self): # client side query_msg = message.OutgoingFindNodeQuery(tc.CLIENT_ID, tc.TARGET_ID) # querier encodes query_data = query_msg.encode(tc.TID) # server side # rpc_manager.datagram_received() decodes query_msg = message.IncomingMsg(query_data) # rpc calls responder assert not self.notification_callback_done response_msg = self.responder.on_query_received( query_msg, tc.CLIENT_ADDR) response_data = response_msg.encode(query_msg.tid) assert self.notification_callback_done expected_msg = message.OutgoingFindNodeResponse(tc.SERVER_ID, tc.NODES) expected_data = expected_msg.encode(tc.TID) eq_(response_data, expected_data)
def test_return_response_for_ping(self): # client side query_msg = message.OutgoingPingQuery(tc.CLIENT_ID) # rpc_manager.sendto() encodes query_data = query_msg.encode(tc.TID) # server side # rpc_manager.datagram_received() decodes query_msg = message.IncomingMsg(query_data) assert not self.notification_callback_done response_msg = self.responder.on_query_received( query_msg, tc.CLIENT_ADDR) response_data = response_msg.encode(query_msg.tid) assert self.notification_callback_done expected_msg = message.OutgoingPingResponse(tc.SERVER_ID) expected_data = expected_msg.encode(tc.TID) eq_(response_data, expected_data)
def test_return_response_for_get_peers_when_no_peers(self): # client side query_msg = message.OutgoingGetPeersQuery(tc.CLIENT_ID, tc.NODE_ID) # rpc_manager.sendto() encodes query_data = query_msg.encode(tc.TID) # server side # rpc_manager.datagram_received() decodes query_msg = message.IncomingMsg(query_data) assert not self.notification_callback_done response_msg = self.responder.on_query_received( query_msg, tc.CLIENT_ADDR) response_data = response_msg.encode(query_msg.tid) assert self.notification_callback_done expected_msg = message.OutgoingGetPeersResponse(tc.SERVER_ID, self.token_m.get(), nodes2=tc.NODES) expected_data = expected_msg.encode(query_msg.tid) eq_(response_data, expected_data)
def test_ping_with_response(self): # Client creates a query ping_msg = message.OutgoingPingQuery(tc.CLIENT_ID) q = Query(ping_msg, tc.SERVER_NODE) # Querier.register_query sets a TID and timeout_task q.tid = tc.TID q.timeout_task = minitwisted.Task(TIMEOUT_DELAY, None) q.query_ts = time.time() # The query is sent time.sleep(1) # The server creates a response ping_r_msg_out = message.OutgoingPingResponse(tc.SERVER_ID) bencoded_r = ping_r_msg_out.encode(tc.TID) # The client receives the bencoded message ping_r_in = message.IncomingMsg(bencoded_r, tc.SERVER_ADDR) q.on_response_received(ping_r_in) assert 1 < q.rtt < 1.1 assert q.lookup_obj is None
def send_query_and_get_error(self, querier): ping_msg = message.OutgoingPingQuery() query = querier.send_query(ping_msg, tc.EXTERNAL_NODE, self.on_response, self.on_timeout, self.on_error, timeout_delay=tc.TIMEOUT_DELAY) if querier is self.querier_mock: # the server creates the response error_msg = message.OutgoingErrorMsg(query.tid, message.GENERIC_E) error_data = error_msg.encode() # rpc_m decodes the response received _, _, error_msg_dict = message.IncomingMsg(error_data) # rpc_m notifies of the message (callback) querier.on_error_received(error_msg_dict, tc.EXTERNAL_NODE) time.sleep(tc.TIMEOUT_DELAY + .1)
def test_find_node_with_error(self): # Client creates a query fn_msg = message.OutgoingFindNodeQuery(tc.CLIENT_ID, tc.TARGET_ID) # the destination's ID is unknown q = Query(fn_msg, node.Node(tc.SERVER_ADDR)) # Querier.register_query sets a TID and timeout_task q.tid = tc.TID q.timeout_task = minitwisted.Task(TIMEOUT_DELAY, None) q.query_ts = time.time() # The query is sent # The server creates a response fn_r_out = message.OutgoingErrorMsg(message.GENERIC_E) bencoded_fn_r = fn_r_out.encode(tc.TID) time.sleep(1) # The client receives the bencoded message fn_r_in = message.IncomingMsg(bencoded_fn_r, tc.SERVER_ADDR) q.on_error_received(fn_r_in) assert 1 < q.rtt < 1.1 assert q.lookup_obj is None
def test_return_response_for_announce_peer_with_valid_tocken(self): # client side query_msg = message.OutgoingAnnouncePeerQuery(tc.CLIENT_ID, tc.INFO_HASH, tc.CLIENT_ADDR[1], self.token_m.get()) # querier.send_query() encodes query_data = query_msg.encode(tc.TID) # server side # rpc_manager.datagram_received() decodes and calls responder (callback) query_msg = message.IncomingMsg(query_data) assert not self.notification_callback_done response_msg = self.responder.on_query_received( query_msg, tc.CLIENT_ADDR) response_data = response_msg.encode(query_msg.tid) assert self.notification_callback_done # responder returns to querier expected_msg = message.OutgoingAnnouncePeerResponse(tc.SERVER_ID) expected_data = expected_msg.encode(tc.TID) assert response_data == expected_data
def test_unsolicited_response(self): # We have a pending response from NO_ADDR (TID \0\0) # but we get a response with different TID # client ping = message.OutgoingPingQuery(tc.CLIENT_ID) self.querier.send_query(ping, tc.SERVER_NODE, self.on_response, self.on_error, self.on_timeout, tc.TIMEOUT_DELAY) ok_(not self.got_response) ok_(not self.got_routing_response) ok_(not self.got_routing_nodes_found) # server pong = message.OutgoingPingResponse(tc.SERVER_ID) pong_in = message.IncomingMsg(pong.encode(tc.TID)) # client self.querier.on_response_received(pong_in, tc.SERVER_ADDR) ok_(not self.got_response) ok_(not self.got_routing_response) ok_(not self.got_routing_nodes_found)
def test_get_peers_with_response(self): # Client creates a query fn_msg = message.OutgoingGetPeersQuery(tc.CLIENT_ID, tc.INFO_HASH) # the destination's ID is unknown # This query belongs to a lookup q = Query(fn_msg, node.Node(tc.SERVER_ADDR), LOOKUP_OBJ) # Querier.register_query sets a TID and timeout_task q.tid = tc.TID q.timeout_task = minitwisted.Task(TIMEOUT_DELAY, None) q.query_ts = time.time() # The query is sent # The server creates a response fn_r_out = message.OutgoingGetPeersResponse(tc.SERVER_ID, nodes=tc.NODES) bencoded_fn_r = fn_r_out.encode(tc.TID) time.sleep(1) # The client receives the bencoded message fn_r_in = message.IncomingMsg(bencoded_fn_r, tc.SERVER_ADDR) q.on_response_received(fn_r_in) assert 1 < q.rtt < 1.1 assert q.lookup_obj is LOOKUP_OBJ
def _on_datagram_received(self, data, addr): # Sanitize bencode try: msg = message.IncomingMsg(data) except (message.MsgError): log.info('MsgError when decoding\n%s\nsouce: %s' % (data, addr)) return # ignore message try: # callback according to message's type callback_fs = self.msg_callbacks_d[msg.type] except (KeyError): log.info('Key TYPE has an invalid value\n%s\nsouce: %s' % (data, addr)) return #ignore message # Call the proper callback (selected according msg's TYPE) response_msg = None for callback_f in callback_fs: # if there is a response we should keep it response_msg = callback_f(msg, addr) or response_msg if response_msg: bencoded_response = response_msg.encode(msg.tid) self.send_msg_to(bencoded_response, addr)