Updated Echo example
This commit is contained in:
parent
0c49ca8458
commit
8082338657
@ -104,7 +104,7 @@ class Destination:
|
|||||||
def proof_requested_callback(self, callback):
|
def proof_requested_callback(self, callback):
|
||||||
self.callbacks.proof_requested = 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:
|
if not proof_strategy in Destination.proof_strategies:
|
||||||
raise TypeError("Unsupported proof strategy")
|
raise TypeError("Unsupported proof strategy")
|
||||||
else:
|
else:
|
||||||
|
@ -99,13 +99,12 @@ class Identity:
|
|||||||
announced_identity.loadPublicKey(public_key)
|
announced_identity.loadPublicKey(public_key)
|
||||||
|
|
||||||
if announced_identity.pub != None and announced_identity.validate(signature, signed_data):
|
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.Identity.remember(RNS.Identity.fullHash(packet.raw), destination_hash, public_key)
|
||||||
RNS.log("Stored valid announce from "+RNS.prettyhexrep(destination_hash), RNS.LOG_INFO)
|
RNS.log("Stored valid announce from "+RNS.prettyhexrep(destination_hash), RNS.LOG_INFO)
|
||||||
del announced_identity
|
del announced_identity
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
RNS.log("Announce is invalid", RNS.LOG_VERBOSE)
|
RNS.log("Received invalid announce", RNS.LOG_DEBUG)
|
||||||
del announced_identity
|
del announced_identity
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -253,6 +253,7 @@ class PacketReceipt:
|
|||||||
if proof_valid:
|
if proof_valid:
|
||||||
self.status = PacketReceipt.DELIVERED
|
self.status = PacketReceipt.DELIVERED
|
||||||
self.proved = True
|
self.proved = True
|
||||||
|
self.concluded_at = time.time()
|
||||||
if self.callbacks.delivery != None:
|
if self.callbacks.delivery != None:
|
||||||
self.callbacks.delivery(self)
|
self.callbacks.delivery(self)
|
||||||
return True
|
return True
|
||||||
@ -267,6 +268,7 @@ class PacketReceipt:
|
|||||||
if proof_valid:
|
if proof_valid:
|
||||||
self.status = PacketReceipt.DELIVERED
|
self.status = PacketReceipt.DELIVERED
|
||||||
self.proved = True
|
self.proved = True
|
||||||
|
self.concluded_at = time.time()
|
||||||
if self.callbacks.delivery != None:
|
if self.callbacks.delivery != None:
|
||||||
self.callbacks.delivery(self)
|
self.callbacks.delivery(self)
|
||||||
return True
|
return True
|
||||||
@ -275,6 +277,8 @@ class PacketReceipt:
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def rtt(self):
|
||||||
|
return self.concluded_at - self.sent_at
|
||||||
|
|
||||||
def isTimedOut(self):
|
def isTimedOut(self):
|
||||||
return (self.sent_at+self.timeout < time.time())
|
return (self.sent_at+self.timeout < time.time())
|
||||||
|
@ -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 argparse
|
||||||
import time
|
|
||||||
import RNS
|
import RNS
|
||||||
|
|
||||||
# Let's define an app name. We'll use this for all
|
# 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
|
# certain that no-one else than this destination was able
|
||||||
# to read it.
|
# to read it.
|
||||||
echo_destination = RNS.Destination(server_identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "echo", "request")
|
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
|
# Tell the destination which function in our program to
|
||||||
# run when a packet is received.
|
# run when a packet is received. We do this so we can
|
||||||
echo_destination.setCallback(serverCallback)
|
# print a log message when the server receives a request
|
||||||
|
echo_destination.packet_callback(server_callback)
|
||||||
|
|
||||||
# Everything's ready!
|
# Everything's ready!
|
||||||
# Let's Wait for client requests or user input
|
# Let's Wait for client requests or user input
|
||||||
@ -36,58 +49,26 @@ def server(configpath):
|
|||||||
|
|
||||||
def announceLoop(destination):
|
def announceLoop(destination):
|
||||||
# Let the user know that everything is ready
|
# 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.
|
# We enter a loop that runs until the users exits.
|
||||||
# If the user just hits enter, we will announce our server
|
# If the user hits enter, we will announce our server
|
||||||
# destination on the network, which will let clients know
|
# destination on the network, which will let clients
|
||||||
# how to create messages directed towards it.
|
# know how to create messages directed towards it.
|
||||||
while True:
|
while True:
|
||||||
entered = raw_input()
|
entered = raw_input()
|
||||||
destination.announce()
|
destination.announce()
|
||||||
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
|
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
|
||||||
|
|
||||||
|
|
||||||
def serverCallback(message, packet):
|
def server_callback(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")
|
|
||||||
|
|
||||||
# Tell the user that we received an echo request, and
|
# Tell the user that we received an echo request, and
|
||||||
# that we are going to send a reply to the requester.
|
# that we are going to send a reply to the requester.
|
||||||
RNS.log("Received packet from <"+reply_destination_hexhash+">, sending reply")
|
# Sending the proof is handled automatically, since we
|
||||||
|
# set up the destination to prove all incoming packets.
|
||||||
# To let the client know that we got the echo request,
|
RNS.log("Received packet from echo client, proof sent")
|
||||||
# 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)
|
|
||||||
|
|
||||||
|
|
||||||
# We need a global list to hold sent echo requests
|
|
||||||
sent_requests = []
|
|
||||||
# This initialisation is executed when the users chooses
|
# This initialisation is executed when the users chooses
|
||||||
# to run as a client
|
# to run as a client
|
||||||
def client(destination_hexhash, configpath):
|
def client(destination_hexhash, configpath):
|
||||||
@ -104,36 +85,12 @@ def client(destination_hexhash, configpath):
|
|||||||
# We must first initialise Reticulum
|
# We must first initialise Reticulum
|
||||||
reticulum = RNS.Reticulum(configpath)
|
reticulum = RNS.Reticulum(configpath)
|
||||||
|
|
||||||
# Randomly create a new identity for our echo server
|
# We override the loglevel to provide feedback when
|
||||||
client_identity = RNS.Identity()
|
# an announce is received
|
||||||
|
RNS.loglevel = RNS.LOG_INFO
|
||||||
# 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.<IDENTITY_HASH>
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# Tell the user that the client is ready!
|
# 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.
|
# We enter a loop that runs until the user exits.
|
||||||
# If the user hits enter, we will try to send an
|
# 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
|
# recall method, so let's create an outgoing
|
||||||
# destination. We use the naming convention:
|
# destination. We use the naming convention:
|
||||||
# example_utilities.echo.request
|
# example_utilities.echo.request
|
||||||
# Since this is a "single" destination, the identity
|
# This matches the naming we specified in the
|
||||||
# hash will be automatically added to the end of
|
# server part of the code.
|
||||||
# the name.
|
|
||||||
request_destination = RNS.Destination(server_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "echo", "request")
|
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.
|
# The destination is ready, so let's create a packet.
|
||||||
# We set the destination to the request_destination
|
# We set the destination to the request_destination
|
||||||
# that was just created, and the only data we add
|
# that was just created, and the only data we add
|
||||||
# is the identity hash of our client identity.
|
# is a random hash.
|
||||||
# Including that information will let the server
|
echo_request = RNS.Packet(request_destination, RNS.Identity.getRandomHash())
|
||||||
# create a destination to send replies to.
|
|
||||||
echo_request = RNS.Packet(request_destination, client_identity.hash)
|
|
||||||
|
|
||||||
# Send the packet!
|
# Send the packet! If the packet is successfully
|
||||||
echo_request.send()
|
# sent, it will return a PacketReceipt instance.
|
||||||
|
packet_receipt = echo_request.send()
|
||||||
|
|
||||||
# Add the request to our list of sent packets
|
# We can then set a delivery callback on the receipt.
|
||||||
sent_requests.append(echo_request)
|
# 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
|
# Tell the user that the echo request was sent
|
||||||
RNS.log("Sent echo request to "+RNS.prettyhexrep(request_destination.hash))
|
RNS.log("Sent echo request to "+RNS.prettyhexrep(request_destination.hash))
|
||||||
else:
|
else:
|
||||||
# If we do not know this destination, tell the
|
# If we do not know this destination, tell the
|
||||||
# user to wait for an announce to arrive.
|
# 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
|
# This method is called when our reply destination
|
||||||
# receives a proof packet.
|
# receives a proof packet.
|
||||||
def clientProofCallback(proof_packet):
|
def packet_delivered(receipt):
|
||||||
# We save the current time so we can calculate
|
if receipt.status == RNS.PacketReceipt.DELIVERED:
|
||||||
# round-trip time for the packet
|
rtt = receipt.rtt()
|
||||||
now = time.time()
|
if (rtt >= 1):
|
||||||
|
rtt = round(rtt, 3)
|
||||||
# Let's look through our list of sent requests,
|
rttstring = str(rtt)+" seconds"
|
||||||
# and see if we can find one that matches the
|
else:
|
||||||
# proof we just received.
|
rtt = round(rtt*1000, 3)
|
||||||
for unproven_packet in sent_requests:
|
rttstring = str(rtt)+" milliseconds"
|
||||||
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")
|
|
||||||
|
|
||||||
|
RNS.log("Valid reply received from "+RNS.prettyhexrep(receipt.destination.hash)+", round-trip time is "+rttstring)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@ -248,6 +174,7 @@ if __name__ == "__main__":
|
|||||||
print("")
|
print("")
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
print("")
|
print("")
|
||||||
client(args.destination, configarg)
|
else:
|
||||||
|
client(args.destination, configarg)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
exit()
|
exit()
|
Loading…
Reference in New Issue
Block a user