mirror of
https://github.com/markqvist/Reticulum.git
synced 2024-12-23 04:10:18 +00:00
Echo example
This commit is contained in:
parent
4c7d20e15b
commit
d4be407821
@ -15,12 +15,17 @@ class Destination:
|
||||
PADDINGSIZE= FPE.Identity.PADDINGSIZE;
|
||||
|
||||
# Constants
|
||||
SINGLE = 0x00;
|
||||
GROUP = 0x01;
|
||||
PLAIN = 0x02;
|
||||
LINK = 0x03;
|
||||
SINGLE = 0x00
|
||||
GROUP = 0x01
|
||||
PLAIN = 0x02
|
||||
LINK = 0x03
|
||||
types = [SINGLE, GROUP, PLAIN, LINK]
|
||||
|
||||
PROVE_NONE = 0x21
|
||||
PROVE_APP = 0x22
|
||||
PROVE_ALL = 0x23
|
||||
proof_strategies = [PROVE_NONE, PROVE_APP, PROVE_ALL]
|
||||
|
||||
IN = 0x11;
|
||||
OUT = 0x12;
|
||||
directions = [IN, OUT]
|
||||
@ -56,20 +61,24 @@ class Destination:
|
||||
if not direction in Destination.directions: raise ValueError("Unknown destination direction")
|
||||
self.type = type
|
||||
self.direction = direction
|
||||
self.proof_strategy = Destination.PROVE_NONE
|
||||
self.mtu = 0
|
||||
|
||||
if identity == None:
|
||||
if identity != None and type == Destination.SINGLE:
|
||||
aspects = aspects+(identity.hexhash,)
|
||||
|
||||
if identity == None and direction == Destination.IN:
|
||||
identity = Identity()
|
||||
identity.createKeys()
|
||||
aspects = aspects+(identity.hexhash,)
|
||||
|
||||
self.identity = identity
|
||||
aspects = aspects+(identity.hexhash,)
|
||||
|
||||
self.name = Destination.getDestinationName(app_name, *aspects)
|
||||
self.hash = Destination.getDestinationHash(app_name, *aspects)
|
||||
self.hexhash = self.hash.encode("hex_codec")
|
||||
|
||||
self.callback = None
|
||||
self.proofcallback = None
|
||||
|
||||
FPE.Transport.registerDestination(self)
|
||||
|
||||
@ -81,11 +90,19 @@ class Destination:
|
||||
def setCallback(self, callback):
|
||||
self.callback = callback
|
||||
|
||||
def setProofCallback(self, callback):
|
||||
self.proofcallback = callback
|
||||
|
||||
def receive(self, data):
|
||||
plaintext = self.decrypt(data)
|
||||
def setProofStrategy(self, proof_strategy):
|
||||
if not proof_strategy in Destination.proof_strategies:
|
||||
raise TypeError("Unsupported proof strategy")
|
||||
else:
|
||||
self.proof_strategy = proof_strategy
|
||||
|
||||
def receive(self, packet):
|
||||
plaintext = self.decrypt(packet.data)
|
||||
if plaintext != None and self.callback != None:
|
||||
self.callback(plaintext, self)
|
||||
self.callback(plaintext, packet)
|
||||
|
||||
|
||||
def createKeys(self):
|
||||
|
@ -1,7 +1,7 @@
|
||||
from Interfaces import *
|
||||
import ConfigParser
|
||||
import jsonpickle
|
||||
from vendor.configobj import ConfigObj
|
||||
import atexit
|
||||
import struct
|
||||
import array
|
||||
import os.path
|
||||
@ -13,16 +13,12 @@ class FlexPE:
|
||||
MTU = 500
|
||||
router = None
|
||||
config = None
|
||||
destinations = []
|
||||
interfaces = []
|
||||
|
||||
configdir = os.path.expanduser("~")+"/.flexpe"
|
||||
configpath = ""
|
||||
storagepath = ""
|
||||
cachepath = ""
|
||||
|
||||
# TODO: Move this to Transport
|
||||
packetlist = []
|
||||
|
||||
def __init__(self,configdir=None):
|
||||
if configdir != None:
|
||||
FlexPE.configdir = configdir
|
||||
@ -48,23 +44,14 @@ class FlexPE:
|
||||
FPE.Identity.loadKnownDestinations()
|
||||
FlexPE.router = self
|
||||
|
||||
@staticmethod
|
||||
def addDestination(destination):
|
||||
destination.MTU = FlexPE.MTU
|
||||
FlexPE.destinations.append(destination)
|
||||
|
||||
@staticmethod
|
||||
def outbound(raw):
|
||||
for interface in FlexPE.interfaces:
|
||||
if interface.OUT:
|
||||
FPE.log("Transmitting via: "+str(interface), FPE.LOG_DEBUG)
|
||||
interface.processOutgoing(raw)
|
||||
atexit.register(FPE.Identity.exitHandler)
|
||||
|
||||
def applyConfig(self):
|
||||
for option in self.config["logging"]:
|
||||
value = self.config["logging"][option]
|
||||
if option == "loglevel":
|
||||
FPE.loglevel = int(value)
|
||||
if "logging" in self.config:
|
||||
for option in self.config["logging"]:
|
||||
value = self.config["logging"][option]
|
||||
if option == "loglevel":
|
||||
FPE.loglevel = int(value)
|
||||
|
||||
for name in self.config["interfaces"]:
|
||||
c = self.config["interfaces"][name]
|
||||
@ -82,7 +69,7 @@ class FlexPE:
|
||||
interface.OUT = True
|
||||
|
||||
interface.name = name
|
||||
FlexPE.interfaces.append(interface)
|
||||
FPE.Transport.interfaces.append(interface)
|
||||
|
||||
if c["type"] == "SerialInterface":
|
||||
interface = SerialInterface.SerialInterface(
|
||||
@ -98,7 +85,7 @@ class FlexPE:
|
||||
interface.OUT = True
|
||||
|
||||
interface.name = name
|
||||
FlexPE.interfaces.append(interface)
|
||||
FPE.Transport.interfaces.append(interface)
|
||||
|
||||
except Exception as e:
|
||||
FPE.log("The interface \""+name+"\" could not be created. Check your configuration file for errors!", FPE.LOG_ERROR)
|
||||
|
@ -37,13 +37,23 @@ class Identity:
|
||||
self.createKeys()
|
||||
|
||||
@staticmethod
|
||||
def remember(hash, public_key, app_data = None):
|
||||
FPE.log("Remembering "+FPE.hexrep(hash, False), FPE.LOG_VERBOSE)
|
||||
Identity.known_destinations[hash] = [time.time(), public_key, app_data]
|
||||
def remember(packet_hash, destination_hash, public_key, app_data = None):
|
||||
FPE.log("Remembering "+FPE.prettyhexrep(destination_hash), FPE.LOG_VERBOSE)
|
||||
Identity.known_destinations[destination_hash] = [time.time(), packet_hash, public_key, app_data]
|
||||
|
||||
|
||||
@staticmethod
|
||||
def recall(identity):
|
||||
pass
|
||||
def recall(destination_hash):
|
||||
FPE.log("Searching for "+FPE.prettyhexrep(destination_hash)+"...", FPE.LOG_DEBUG)
|
||||
if destination_hash in Identity.known_destinations:
|
||||
identity_data = Identity.known_destinations[destination_hash]
|
||||
identity = Identity(public_only=True)
|
||||
identity.loadPublicKey(identity_data[2])
|
||||
FPE.log("Found "+FPE.prettyhexrep(destination_hash)+" in known destinations", FPE.LOG_DEBUG)
|
||||
return identity
|
||||
else:
|
||||
FPE.log("Could not find "+FPE.prettyhexrep(destination_hash)+" in known destinations", FPE.LOG_DEBUG)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def saveKnownDestinations():
|
||||
@ -80,7 +90,7 @@ class Identity:
|
||||
@staticmethod
|
||||
def validateAnnounce(packet):
|
||||
if packet.packet_type == FPE.Packet.ANNOUNCE:
|
||||
FPE.log("Validating announce from "+FPE.hexrep(packet.destination_hash), FPE.LOG_VERBOSE)
|
||||
FPE.log("Validating announce from "+FPE.prettyhexrep(packet.destination_hash), FPE.LOG_VERBOSE)
|
||||
destination_hash = packet.destination_hash
|
||||
public_key = packet.data[10:Identity.DERKEYSIZE/8+10]
|
||||
random_hash = packet.data[Identity.DERKEYSIZE/8+10:Identity.DERKEYSIZE/8+20]
|
||||
@ -96,11 +106,19 @@ class Identity:
|
||||
|
||||
if announced_identity.validate(signature, signed_data):
|
||||
FPE.log("Announce is valid", FPE.LOG_VERBOSE)
|
||||
FPE.Identity.remember(destination_hash, public_key)
|
||||
FPE.Identity.remember(FPE.Identity.fullHash(packet.raw), destination_hash, public_key)
|
||||
FPE.log("Stored valid announce from "+FPE.prettyhexrep(destination_hash), FPE.LOG_INFO)
|
||||
del announced_identity
|
||||
return True
|
||||
else:
|
||||
FPE.log("Announce is invalid", FPE.LOG_VERBOSE)
|
||||
del announced_identity
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def exitHandler():
|
||||
Identity.saveKnownDestinations()
|
||||
|
||||
del announced_identity
|
||||
|
||||
def createKeys(self):
|
||||
self.prv = rsa.generate_private_key(
|
||||
@ -119,10 +137,9 @@ class Identity:
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo
|
||||
)
|
||||
|
||||
self.hash = Identity.truncatedHash(self.pub_bytes)
|
||||
self.hexhash = self.hash.encode("hex_codec")
|
||||
self.updateHashes()
|
||||
|
||||
FPE.log("Identity keys created, private length is "+str(len(self.prv_bytes))+" public length is "+str(len(self.pub_bytes)), FPE.LOG_INFO)
|
||||
FPE.log("Identity keys created for "+FPE.prettyhexrep(self.hash), FPE.LOG_INFO)
|
||||
|
||||
def getPrivateKey(self):
|
||||
return self.prv_bytes
|
||||
@ -138,10 +155,16 @@ class Identity:
|
||||
encoding=serialization.Encoding.DER,
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo
|
||||
)
|
||||
self.updateHashes()
|
||||
|
||||
def loadPublicKey(self, key):
|
||||
self.pub_bytes = key
|
||||
self.pub = load_der_public_key(self.pub_bytes, backend=default_backend())
|
||||
self.updateHashes()
|
||||
|
||||
def updateHashes(self):
|
||||
self.hash = Identity.truncatedHash(self.pub_bytes)
|
||||
self.hexhash = self.hash.encode("hex_codec")
|
||||
|
||||
def saveIdentity(self):
|
||||
pass
|
||||
@ -150,7 +173,7 @@ class Identity:
|
||||
pass
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
if self.prv != None:
|
||||
if self.pub != None:
|
||||
chunksize = (Identity.KEYSIZE-Identity.PADDINGSIZE)/8
|
||||
chunks = int(math.ceil(len(plaintext)/(float(chunksize))))
|
||||
# TODO: Remove debug output print("Plaintext size is "+str(len(plaintext))+", with "+str(chunks)+" chunks")
|
||||
@ -175,7 +198,7 @@ class Identity:
|
||||
# TODO: Remove debug output print("Plaintext encrypted, ciphertext length is "+str(len(ciphertext))+" bytes.")
|
||||
return ciphertext
|
||||
else:
|
||||
raise KeyError("Encryption failed because identity does not hold a private key")
|
||||
raise KeyError("Encryption failed because identity does not hold a public key")
|
||||
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
@ -238,11 +261,12 @@ class Identity:
|
||||
else:
|
||||
raise KeyError("Signature validation failed because identity does not hold a public key")
|
||||
|
||||
def prove(self, packet, destination):
|
||||
proof_data = packet.packet_hash + self.sign(packet.packet_hash)
|
||||
proof = FPE.Packet(destination, proof_data, FPE.Packet.PROOF)
|
||||
proof.send()
|
||||
|
||||
|
||||
def getRandomHash(self):
|
||||
return self.truncatedHash(os.urandom(10))
|
||||
|
||||
|
||||
def identityExithandler():
|
||||
Identity.saveKnownDestinations()
|
||||
|
||||
atexit.register(identityExithandler)
|
@ -3,6 +3,7 @@ import SocketServer
|
||||
import threading
|
||||
import socket
|
||||
import sys
|
||||
import FPE
|
||||
|
||||
class UdpInterface(Interface):
|
||||
bind_ip = None
|
||||
@ -36,9 +37,13 @@ class UdpInterface(Interface):
|
||||
|
||||
|
||||
def processIncoming(self, data):
|
||||
# TODO: remove
|
||||
#FPE.log("IN: "+FPE.prettyhexrep(data))
|
||||
self.owner.inbound(data)
|
||||
|
||||
def processOutgoing(self,data):
|
||||
# TODO: remove
|
||||
#FPE.log("OUT: "+FPE.prettyhexrep(" "+data))
|
||||
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||
udp_socket.sendto(data, (self.forward_ip, self.forward_port))
|
||||
@ -52,5 +57,7 @@ class UdpInterfaceHandler(SocketServer.BaseRequestHandler):
|
||||
|
||||
def handle(self):
|
||||
if (UdpInterfaceHandler.interface != None):
|
||||
data = self.request[0].strip()
|
||||
# TODO: remove
|
||||
#FPE.log("Datagram contents: "+FPE.prettyhexrep(self.request[0]))
|
||||
data = self.request[0]
|
||||
UdpInterfaceHandler.interface.processIncoming(data)
|
@ -1,4 +1,5 @@
|
||||
import struct
|
||||
import time
|
||||
import FPE
|
||||
|
||||
class Packet:
|
||||
@ -29,14 +30,19 @@ class Packet:
|
||||
self.transport_id = transport_id
|
||||
self.data = data
|
||||
self.flags = self.getPackedFlags()
|
||||
self.MTU = self.destination.MTU
|
||||
|
||||
self.raw = None
|
||||
self.packed = False
|
||||
self.sent = False
|
||||
self.MTU = self.destination.MTU
|
||||
self.fromPacked = False
|
||||
else:
|
||||
self.raw = data
|
||||
self.packed = True
|
||||
self.raw = data
|
||||
self.packed = True
|
||||
self.fromPacked = True
|
||||
|
||||
self.sent_at = None
|
||||
self.packet_hash = None
|
||||
|
||||
def getPackedFlags(self):
|
||||
packed_flags = (self.header_type << 6) | (self.transport_type << 4) | (self.destination.type << 2) | self.packet_type
|
||||
@ -90,6 +96,8 @@ class Packet:
|
||||
self.pack()
|
||||
FPE.log("Size: "+str(len(self.raw))+" header is "+str(len(self.header))+" payload is "+str(len(self.ciphertext)), FPE.LOG_DEBUG)
|
||||
FPE.Transport.outbound(self.raw)
|
||||
self.packet_hash = FPE.Identity.fullHash(self.raw)
|
||||
self.sent_at = time.time()
|
||||
self.sent = True
|
||||
else:
|
||||
raise IOError("Packet was already sent")
|
||||
@ -98,4 +106,23 @@ class Packet:
|
||||
if self.sent:
|
||||
Transport.outbound(self.raw)
|
||||
else:
|
||||
raise IOError("Packet was not sent yet")
|
||||
raise IOError("Packet was not sent yet")
|
||||
|
||||
def prove(self, destination):
|
||||
if self.fromPacked and self.destination:
|
||||
if self.destination.identity and self.destination.identity.prv:
|
||||
self.destination.identity.prove(self, destination)
|
||||
|
||||
def validateProofPacket(self, proof_packet):
|
||||
return self.validateProof(proof_packet.data)
|
||||
|
||||
def validateProof(self, proof):
|
||||
proof_hash = proof[:32]
|
||||
signature = proof[32:]
|
||||
if proof_hash == self.packet_hash:
|
||||
return self.destination.identity.validate(signature, proof_hash)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
|
||||
|
@ -8,11 +8,17 @@ class Transport:
|
||||
TUNNEL = 0x03;
|
||||
types = [BROADCAST, TRANSPORT, RELAY, TUNNEL]
|
||||
|
||||
interfaces = []
|
||||
destinations = []
|
||||
packet_hashlist = []
|
||||
|
||||
@staticmethod
|
||||
def outbound(raw):
|
||||
FPE.FlexPE.outbound(raw)
|
||||
Transport.cacheRaw(raw)
|
||||
for interface in Transport.interfaces:
|
||||
if interface.OUT:
|
||||
FPE.log("Transmitting via: "+str(interface), FPE.LOG_DEBUG)
|
||||
interface.processOutgoing(raw)
|
||||
|
||||
@staticmethod
|
||||
def inbound(raw):
|
||||
@ -22,15 +28,43 @@ class Transport:
|
||||
Transport.packet_hashlist.append(packet_hash)
|
||||
packet = FPE.Packet(None, raw)
|
||||
packet.unpack()
|
||||
packet.packet_hash = packet_hash
|
||||
|
||||
if packet.packet_type == FPE.Packet.ANNOUNCE:
|
||||
FPE.Identity.validateAnnounce(packet)
|
||||
if FPE.Identity.validateAnnounce(packet):
|
||||
Transport.cache(packet)
|
||||
|
||||
if packet.packet_type == FPE.Packet.RESOURCE:
|
||||
for destination in FlexPE.destinations:
|
||||
for destination in Transport.destinations:
|
||||
if destination.hash == packet.destination_hash and destination.type == packet.destination_type:
|
||||
destination.receive(packet.data)
|
||||
packet.destination = destination
|
||||
destination.receive(packet)
|
||||
Transport.cache(packet)
|
||||
|
||||
if packet.packet_type == FPE.Packet.PROOF:
|
||||
for destination in Transport.destinations:
|
||||
if destination.hash == packet.destination_hash:
|
||||
if destination.proofcallback != None:
|
||||
destination.proofcallback(packet)
|
||||
# TODO: add universal proof handling
|
||||
|
||||
@staticmethod
|
||||
def registerDestination(destination):
|
||||
FPE.FlexPE.addDestination(destination)
|
||||
destination.MTU = FPE.FlexPE.MTU
|
||||
if destination.direction == FPE.Destination.IN:
|
||||
Transport.destinations.append(destination)
|
||||
|
||||
@staticmethod
|
||||
def cache(packet):
|
||||
FPE.Transport.cacheRaw(packet.raw)
|
||||
|
||||
@staticmethod
|
||||
def cacheRaw(raw):
|
||||
try:
|
||||
file = open(FPE.FlexPE.cachepath+"/"+FPE.hexrep(FPE.Identity.fullHash(raw), delimit=False), "w")
|
||||
file.write(raw)
|
||||
file.close()
|
||||
FPE.log("Wrote packet "+FPE.prettyhexrep(FPE.Identity.fullHash(raw))+" to cache", FPE.LOG_DEBUG)
|
||||
except Exception as e:
|
||||
FPE.log("Error writing packet to cache", FPE.LOG_ERROR)
|
||||
FPE.log("The contained exception was: "+str(e))
|
222
FPE/Utilities/Echo.py
Normal file
222
FPE/Utilities/Echo.py
Normal file
@ -0,0 +1,222 @@
|
||||
import argparse
|
||||
import time
|
||||
import FPE
|
||||
|
||||
# Let's define an app name. We'll use this for all
|
||||
# destinations we create. Since this echo example
|
||||
# is part of a range of example utilities, we'll put
|
||||
# them all within the app namespace "example_utilities"
|
||||
APP_NAME = "example_utilitites"
|
||||
|
||||
# This initialisation is executed when the users chooses
|
||||
# to run as a server
|
||||
def server(configpath):
|
||||
# We must first initialise FlexPE
|
||||
fpe = FPE.FlexPE(configpath)
|
||||
|
||||
# Randomly create a new identity for our echo server
|
||||
server_identity = FPE.Identity()
|
||||
|
||||
# We create a destination that clients can query. We want
|
||||
# to be able to verify echo replies to our clients, so we
|
||||
# create a "single" destination that can receive encrypted
|
||||
# messages. This way the client can send a request and be
|
||||
# certain that no-one else than this destination was able
|
||||
# to read it.
|
||||
echo_destination = FPE.Destination(server_identity, FPE.Destination.IN, FPE.Destination.SINGLE, APP_NAME, "echo", "request")
|
||||
|
||||
# Tell the destination which function in our program to
|
||||
# run when a packet is received.
|
||||
echo_destination.setCallback(serverCallback)
|
||||
|
||||
# Everything's ready!
|
||||
# Let's Wait for client requests or user input
|
||||
announceLoop(echo_destination)
|
||||
|
||||
|
||||
def announceLoop(destination):
|
||||
# Let the user know that everything is ready
|
||||
FPE.log("Echo server running, hit enter to send 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.
|
||||
while True:
|
||||
entered = raw_input()
|
||||
destination.announce()
|
||||
FPE.log("Sent announce from "+FPE.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 = FPE.Destination(None, FPE.Destination.OUT, FPE.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
|
||||
# that we are going to send a reply to the requester.
|
||||
FPE.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 FlexPE. 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
|
||||
# to run as a client
|
||||
def client(destination_hexhash, configpath):
|
||||
# We need a binary representation of the destination
|
||||
# hash that was entered on the command line
|
||||
try:
|
||||
if len(destination_hexhash) != 20:
|
||||
raise ValueError("Destination length is invalid, must be 20 hexadecimal characters (10 bytes)")
|
||||
destination_hash = destination_hexhash.decode("hex")
|
||||
except:
|
||||
FPE.log("Invalid destination entered. Check your input!")
|
||||
exit()
|
||||
|
||||
# We must first initialise FlexPE
|
||||
fpe = FPE.FlexPE()
|
||||
|
||||
# Randomly create a new identity for our echo server
|
||||
client_identity = FPE.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.<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 = FPE.Destination(client_identity, FPE.Destination.IN, FPE.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!
|
||||
FPE.log("Echo client "+FPE.prettyhexrep(reply_destination.hash)+" ready, hit enter to send echo request (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
|
||||
# echo request to the destination specified on the
|
||||
# command line.
|
||||
while True:
|
||||
raw_input()
|
||||
# To address the server, we need to know it's public
|
||||
# key, so we check if FlexPE knows this destination.
|
||||
# This is done by calling the "recall" method of the
|
||||
# Identity module. If the destination is known, it will
|
||||
# return an Identity instance that can be used in
|
||||
# outgoing destinations.
|
||||
server_identity = FPE.Identity.recall(destination_hash)
|
||||
if server_identity != None:
|
||||
# We got the correct identity instance from the
|
||||
# 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.
|
||||
request_destination = FPE.Destination(server_identity, FPE.Destination.OUT, FPE.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 = FPE.Packet(request_destination, client_identity.hash)
|
||||
|
||||
# Send the packet!
|
||||
echo_request.send()
|
||||
|
||||
# Add the request to our list of sent packets
|
||||
sent_requests.append(echo_request)
|
||||
|
||||
# Tell the user that the echo request was sent
|
||||
FPE.log("Sent echo request to "+FPE.prettyhexrep(request_destination.hash))
|
||||
else:
|
||||
# If we do not know this destination, tell the
|
||||
# user to wait for an announce to arrive.
|
||||
FPE.log("Destination is not yet known. Wait for an announce to arrive.")
|
||||
|
||||
def clientProofCallback(proof_packet):
|
||||
now = time.time()
|
||||
for unproven_packet in sent_requests:
|
||||
if unproven_packet.packet_hash == proof_packet.data[:32]:
|
||||
if unproven_packet.validateProofPacket(proof_packet):
|
||||
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"
|
||||
FPE.log(
|
||||
"Valid echo reply, proved by "+FPE.prettyhexrep(unproven_packet.destination.hash)+
|
||||
", round-trip time was "+rttstring
|
||||
)
|
||||
else:
|
||||
FPE.log("Proof invalid")
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
parser = argparse.ArgumentParser(description="Simple echo server and client utility")
|
||||
parser.add_argument("-s", "--server", action="store_true", help="wait for incoming packets from clients")
|
||||
parser.add_argument("--config", action="store", default=None, help="path to alternative FlexPE config directory", type=str)
|
||||
parser.add_argument("destination", nargs="?", default=None, help="hexadecimal hash of the server destination", type=str)
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.server:
|
||||
configarg=None
|
||||
if args.config:
|
||||
configarg = args.config
|
||||
server(configarg)
|
||||
else:
|
||||
configarg=None
|
||||
if args.config:
|
||||
configarg = args.config
|
||||
client(args.destination, configarg)
|
||||
except KeyboardInterrupt:
|
||||
exit()
|
@ -67,4 +67,9 @@ def hexrep(data, delimit=True):
|
||||
if not delimit:
|
||||
delimiter = ""
|
||||
hexrep = delimiter.join("{:02x}".format(ord(c)) for c in data)
|
||||
return hexrep
|
||||
|
||||
def prettyhexrep(data):
|
||||
delimiter = ""
|
||||
hexrep = "<"+delimiter.join("{:02x}".format(ord(c)) for c in data)+">"
|
||||
return hexrep
|
3
t.py
3
t.py
@ -17,6 +17,7 @@ fpe = FlexPE()
|
||||
identity = Identity()
|
||||
|
||||
d1=Destination(identity, Destination.IN, Destination.SINGLE, "messenger", "user")
|
||||
#d1.setProofStrategy(Destination.PROVE_ALL)
|
||||
d1.setCallback(testCallback)
|
||||
|
||||
msg=""
|
||||
@ -28,7 +29,7 @@ pl = len(d1.identity.pub_bytes)
|
||||
|
||||
d1.announce()
|
||||
p1=Packet(d1, msg)
|
||||
#p1.send()
|
||||
p1.send()
|
||||
|
||||
# p2=Packet(d2,"Test af msg")
|
||||
# p2.send()
|
||||
|
Loading…
Reference in New Issue
Block a user