diff --git a/RNS/Destination.py b/RNS/Destination.py index d9b5f60..96fc587 100755 --- a/RNS/Destination.py +++ b/RNS/Destination.py @@ -104,7 +104,7 @@ class Destination: def proof_requested_callback(self, callback): self.callbacks.proof_requested = callback - def setProofStrategy(self, proof_strategy): + def set_proof_strategy(self, proof_strategy): if not proof_strategy in Destination.proof_strategies: raise TypeError("Unsupported proof strategy") else: diff --git a/RNS/Identity.py b/RNS/Identity.py index 73bb7d9..d078eef 100644 --- a/RNS/Identity.py +++ b/RNS/Identity.py @@ -99,13 +99,12 @@ class Identity: announced_identity.loadPublicKey(public_key) if announced_identity.pub != None and announced_identity.validate(signature, signed_data): - RNS.log("Announce is valid", RNS.LOG_VERBOSE) RNS.Identity.remember(RNS.Identity.fullHash(packet.raw), destination_hash, public_key) RNS.log("Stored valid announce from "+RNS.prettyhexrep(destination_hash), RNS.LOG_INFO) del announced_identity return True else: - RNS.log("Announce is invalid", RNS.LOG_VERBOSE) + RNS.log("Received invalid announce", RNS.LOG_DEBUG) del announced_identity return False diff --git a/RNS/Packet.py b/RNS/Packet.py index c12569c..692a333 100755 --- a/RNS/Packet.py +++ b/RNS/Packet.py @@ -253,6 +253,7 @@ class PacketReceipt: if proof_valid: self.status = PacketReceipt.DELIVERED self.proved = True + self.concluded_at = time.time() if self.callbacks.delivery != None: self.callbacks.delivery(self) return True @@ -267,6 +268,7 @@ class PacketReceipt: if proof_valid: self.status = PacketReceipt.DELIVERED self.proved = True + self.concluded_at = time.time() if self.callbacks.delivery != None: self.callbacks.delivery(self) return True @@ -275,6 +277,8 @@ class PacketReceipt: else: return False + def rtt(self): + return self.concluded_at - self.sent_at def isTimedOut(self): return (self.sent_at+self.timeout < time.time()) diff --git a/RNS/Utilities/Echo.py b/RNS/Utilities/Echo.py index 69256f0..419de61 100644 --- a/RNS/Utilities/Echo.py +++ b/RNS/Utilities/Echo.py @@ -1,5 +1,11 @@ +########################################################## +# This RNS example demonstrates a simple client/server # +# echo utility. A client can send an echo request to the # +# server, and the server will respond by proving receipt # +# of the packet. # +########################################################## + import argparse -import time import RNS # Let's define an app name. We'll use this for all @@ -24,10 +30,17 @@ def server(configpath): # certain that no-one else than this destination was able # to read it. echo_destination = RNS.Destination(server_identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "echo", "request") + + # We configure the destination to automatically prove all + # packets adressed to it. By doing this, RNS will automatically + # generate a proof for each incoming packet and transmit it + # back to the sender of that packet. + echo_destination.set_proof_strategy(RNS.Destination.PROVE_ALL) # Tell the destination which function in our program to - # run when a packet is received. - echo_destination.setCallback(serverCallback) + # run when a packet is received. We do this so we can + # print a log message when the server receives a request + echo_destination.packet_callback(server_callback) # Everything's ready! # Let's Wait for client requests or user input @@ -36,58 +49,26 @@ def server(configpath): def announceLoop(destination): # Let the user know that everything is ready - RNS.log("Echo server "+RNS.prettyhexrep(destination.hash)+" running, hit enter to send announce (Ctrl-C to quit)") + RNS.log("Echo server "+RNS.prettyhexrep(destination.hash)+" running, hit enter to manually send an announce (Ctrl-C to quit)") # We enter a loop that runs until the users exits. - # If the user just hits enter, we will announce our server - # destination on the network, which will let clients know - # how to create messages directed towards it. + # If the user hits enter, we will announce our server + # destination on the network, which will let clients + # know how to create messages directed towards it. while True: entered = raw_input() destination.announce() RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash)) -def serverCallback(message, packet): - # We have received am echo request from a client! When - # a client sends a request, it will include the hash of - # it's identity in the message. Since we know that the - # client has created a listening destination using this - # identity hash, we can construct an outgoing destination - # to direct our response to. The hash is sent in binary - # format, so we encode it as printable hexadecimal first, - # since aspect names need to in printable text. - client_identity_hexhash = message.encode("hex_codec") - - # We can now create a destination that will let us reach - # the client which send the echo request. - reply_destination = RNS.Destination(None, RNS.Destination.OUT, RNS.Destination.PLAIN, APP_NAME, "echo", "reply", client_identity_hexhash) - - # Let's encode the reply destination hash in a readable - # way, so we can output some info to the user. - reply_destination_hexhash = reply_destination.hash.encode("hex_codec") - +def server_callback(message, packet): # Tell the user that we received an echo request, and # that we are going to send a reply to the requester. - RNS.log("Received packet from <"+reply_destination_hexhash+">, sending reply") - - # To let the client know that we got the echo request, - # we will use the "proof" functions of Reticulum. In most - # applications, the proving of packets will occur fully - # automatically, but in some cases like this, it can be - # beneficial to use the functions manually, since it - # neatly provides functionality that can unequivocally - # prove the receipt of the request to the client. - # - # Using the proof functionality is very simple, we just - # need to call the "prove" method on the packet we wish - # to prove, and specify which destination it should be - # directed to. - packet.prove(reply_destination) + # Sending the proof is handled automatically, since we + # set up the destination to prove all incoming packets. + RNS.log("Received packet from echo client, proof sent") -# We need a global list to hold sent echo requests -sent_requests = [] # This initialisation is executed when the users chooses # to run as a client def client(destination_hexhash, configpath): @@ -104,36 +85,12 @@ def client(destination_hexhash, configpath): # We must first initialise Reticulum reticulum = RNS.Reticulum(configpath) - # Randomly create a new identity for our echo server - client_identity = RNS.Identity() - - # Let's set up a destination for replies to our echo - # requests. This destination will be used by the server - # to direct replies to. We're going to use a "plain" - # destination, so the server can send replies back - # without knowing any public keys of the client. In this - # case, such a design is benificial, since any client - # can send echo requests directly to the server, without - # first having to announce it's destination, or include - # public keys in the echo request - # - # We will use the destination naming convention of: - # example_utilities.echo.reply. - # where the last part is a hex representation of the hash - # of our "client_identity". We need to include this to - # create a unique destination for the server to respond to. - # If we had used a "single" destination, something equivalent - # to this process would have happened automatically. - reply_destination = RNS.Destination(client_identity, RNS.Destination.IN, RNS.Destination.PLAIN, APP_NAME, "echo", "reply", client_identity.hexhash) - - # Since we are only expecting packets of the "proof" - # type to reach our reply destination, we just set the - # proof callback (and in this case not the normal - # message callback) - reply_destination.setProofCallback(clientProofCallback) + # We override the loglevel to provide feedback when + # an announce is received + RNS.loglevel = RNS.LOG_INFO # Tell the user that the client is ready! - RNS.log("Echo client "+RNS.prettyhexrep(reply_destination.hash)+" ready, hit enter to send echo request (Ctrl-C to quit)") + RNS.log("Echo client ready, hit enter to send echo request to "+destination_hexhash+" (Ctrl-C to quit)") # We enter a loop that runs until the user exits. # If the user hits enter, we will try to send an @@ -153,76 +110,45 @@ def client(destination_hexhash, configpath): # recall method, so let's create an outgoing # destination. We use the naming convention: # example_utilities.echo.request - # Since this is a "single" destination, the identity - # hash will be automatically added to the end of - # the name. + # This matches the naming we specified in the + # server part of the code. request_destination = RNS.Destination(server_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "echo", "request") # The destination is ready, so let's create a packet. # We set the destination to the request_destination # that was just created, and the only data we add - # is the identity hash of our client identity. - # Including that information will let the server - # create a destination to send replies to. - echo_request = RNS.Packet(request_destination, client_identity.hash) + # is a random hash. + echo_request = RNS.Packet(request_destination, RNS.Identity.getRandomHash()) - # Send the packet! - echo_request.send() + # Send the packet! If the packet is successfully + # sent, it will return a PacketReceipt instance. + packet_receipt = echo_request.send() - # Add the request to our list of sent packets - sent_requests.append(echo_request) + # We can then set a delivery callback on the receipt. + # This will get automatically called when a proof for + # this specific packet is received from the destination. + packet_receipt.delivery_callback(packet_delivered) # Tell the user that the echo request was sent RNS.log("Sent echo request to "+RNS.prettyhexrep(request_destination.hash)) else: # If we do not know this destination, tell the # user to wait for an announce to arrive. - RNS.log("Destination is not yet known. Wait for an announce to arrive.") + RNS.log("Destination is not yet known. Wait for an announce to arrive and try again.") # This method is called when our reply destination # receives a proof packet. -def clientProofCallback(proof_packet): - # We save the current time so we can calculate - # round-trip time for the packet - now = time.time() - - # Let's look through our list of sent requests, - # and see if we can find one that matches the - # proof we just received. - for unproven_packet in sent_requests: - try: - # Check that the proof hash matches the - # hash of the packet we sent earlier - if unproven_packet.packet_hash == proof_packet.data[:32]: - # We need to actually calidate the proof. - # This is simply done by calling the - # validateProofPacket method on the packet - # we sent earlier. - if unproven_packet.validateProofPacket(proof_packet): - # If the proof is valid, we will calculate - # the round-trip time, and inform the user. - rtt = now - unproven_packet.sent_at - if (rtt >= 1): - rtt = round(rtt, 3) - rttstring = str(rtt)+" seconds" - else: - rtt = round(rtt*1000, 3) - rttstring = str(rtt)+" milliseconds" - - RNS.log( - "Valid echo reply, proved by "+RNS.prettyhexrep(unproven_packet.destination.hash)+ - ", round-trip time was "+rttstring - ) - # Perform some cleanup - sent_requests.remove(unproven_packet) - del unproven_packet - else: - # If the proof was invalid, we inform - # the user of this. - RNS.log("Echo reply received, but proof was invalid") - except: - RNS.log("Proof packet received, but packet contained invalid or unparsable data") +def packet_delivered(receipt): + if receipt.status == RNS.PacketReceipt.DELIVERED: + rtt = receipt.rtt() + if (rtt >= 1): + rtt = round(rtt, 3) + rttstring = str(rtt)+" seconds" + else: + rtt = round(rtt*1000, 3) + rttstring = str(rtt)+" milliseconds" + RNS.log("Valid reply received from "+RNS.prettyhexrep(receipt.destination.hash)+", round-trip time is "+rttstring) if __name__ == "__main__": @@ -248,6 +174,7 @@ if __name__ == "__main__": print("") parser.print_help() print("") - client(args.destination, configarg) + else: + client(args.destination, configarg) except KeyboardInterrupt: exit() \ No newline at end of file