mirror of
https://github.com/markqvist/Reticulum.git
synced 2024-11-26 15:30:18 +00:00
Echo example
This commit is contained in:
parent
4c7d20e15b
commit
d4be407821
@ -15,12 +15,17 @@ class Destination:
|
|||||||
PADDINGSIZE= FPE.Identity.PADDINGSIZE;
|
PADDINGSIZE= FPE.Identity.PADDINGSIZE;
|
||||||
|
|
||||||
# Constants
|
# Constants
|
||||||
SINGLE = 0x00;
|
SINGLE = 0x00
|
||||||
GROUP = 0x01;
|
GROUP = 0x01
|
||||||
PLAIN = 0x02;
|
PLAIN = 0x02
|
||||||
LINK = 0x03;
|
LINK = 0x03
|
||||||
types = [SINGLE, GROUP, PLAIN, LINK]
|
types = [SINGLE, GROUP, PLAIN, LINK]
|
||||||
|
|
||||||
|
PROVE_NONE = 0x21
|
||||||
|
PROVE_APP = 0x22
|
||||||
|
PROVE_ALL = 0x23
|
||||||
|
proof_strategies = [PROVE_NONE, PROVE_APP, PROVE_ALL]
|
||||||
|
|
||||||
IN = 0x11;
|
IN = 0x11;
|
||||||
OUT = 0x12;
|
OUT = 0x12;
|
||||||
directions = [IN, OUT]
|
directions = [IN, OUT]
|
||||||
@ -56,20 +61,24 @@ class Destination:
|
|||||||
if not direction in Destination.directions: raise ValueError("Unknown destination direction")
|
if not direction in Destination.directions: raise ValueError("Unknown destination direction")
|
||||||
self.type = type
|
self.type = type
|
||||||
self.direction = direction
|
self.direction = direction
|
||||||
|
self.proof_strategy = Destination.PROVE_NONE
|
||||||
self.mtu = 0
|
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 = Identity()
|
||||||
identity.createKeys()
|
aspects = aspects+(identity.hexhash,)
|
||||||
|
|
||||||
self.identity = identity
|
self.identity = identity
|
||||||
aspects = aspects+(identity.hexhash,)
|
|
||||||
|
|
||||||
self.name = Destination.getDestinationName(app_name, *aspects)
|
self.name = Destination.getDestinationName(app_name, *aspects)
|
||||||
self.hash = Destination.getDestinationHash(app_name, *aspects)
|
self.hash = Destination.getDestinationHash(app_name, *aspects)
|
||||||
self.hexhash = self.hash.encode("hex_codec")
|
self.hexhash = self.hash.encode("hex_codec")
|
||||||
|
|
||||||
self.callback = None
|
self.callback = None
|
||||||
|
self.proofcallback = None
|
||||||
|
|
||||||
FPE.Transport.registerDestination(self)
|
FPE.Transport.registerDestination(self)
|
||||||
|
|
||||||
@ -81,11 +90,19 @@ class Destination:
|
|||||||
def setCallback(self, callback):
|
def setCallback(self, callback):
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
|
|
||||||
|
def setProofCallback(self, callback):
|
||||||
|
self.proofcallback = callback
|
||||||
|
|
||||||
def receive(self, data):
|
def setProofStrategy(self, proof_strategy):
|
||||||
plaintext = self.decrypt(data)
|
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:
|
if plaintext != None and self.callback != None:
|
||||||
self.callback(plaintext, self)
|
self.callback(plaintext, packet)
|
||||||
|
|
||||||
|
|
||||||
def createKeys(self):
|
def createKeys(self):
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from Interfaces import *
|
from Interfaces import *
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
import jsonpickle
|
|
||||||
from vendor.configobj import ConfigObj
|
from vendor.configobj import ConfigObj
|
||||||
|
import atexit
|
||||||
import struct
|
import struct
|
||||||
import array
|
import array
|
||||||
import os.path
|
import os.path
|
||||||
@ -13,16 +13,12 @@ class FlexPE:
|
|||||||
MTU = 500
|
MTU = 500
|
||||||
router = None
|
router = None
|
||||||
config = None
|
config = None
|
||||||
destinations = []
|
|
||||||
interfaces = []
|
|
||||||
configdir = os.path.expanduser("~")+"/.flexpe"
|
configdir = os.path.expanduser("~")+"/.flexpe"
|
||||||
configpath = ""
|
configpath = ""
|
||||||
storagepath = ""
|
storagepath = ""
|
||||||
cachepath = ""
|
cachepath = ""
|
||||||
|
|
||||||
# TODO: Move this to Transport
|
|
||||||
packetlist = []
|
|
||||||
|
|
||||||
def __init__(self,configdir=None):
|
def __init__(self,configdir=None):
|
||||||
if configdir != None:
|
if configdir != None:
|
||||||
FlexPE.configdir = configdir
|
FlexPE.configdir = configdir
|
||||||
@ -48,19 +44,10 @@ class FlexPE:
|
|||||||
FPE.Identity.loadKnownDestinations()
|
FPE.Identity.loadKnownDestinations()
|
||||||
FlexPE.router = self
|
FlexPE.router = self
|
||||||
|
|
||||||
@staticmethod
|
atexit.register(FPE.Identity.exitHandler)
|
||||||
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)
|
|
||||||
|
|
||||||
def applyConfig(self):
|
def applyConfig(self):
|
||||||
|
if "logging" in self.config:
|
||||||
for option in self.config["logging"]:
|
for option in self.config["logging"]:
|
||||||
value = self.config["logging"][option]
|
value = self.config["logging"][option]
|
||||||
if option == "loglevel":
|
if option == "loglevel":
|
||||||
@ -82,7 +69,7 @@ class FlexPE:
|
|||||||
interface.OUT = True
|
interface.OUT = True
|
||||||
|
|
||||||
interface.name = name
|
interface.name = name
|
||||||
FlexPE.interfaces.append(interface)
|
FPE.Transport.interfaces.append(interface)
|
||||||
|
|
||||||
if c["type"] == "SerialInterface":
|
if c["type"] == "SerialInterface":
|
||||||
interface = SerialInterface.SerialInterface(
|
interface = SerialInterface.SerialInterface(
|
||||||
@ -98,7 +85,7 @@ class FlexPE:
|
|||||||
interface.OUT = True
|
interface.OUT = True
|
||||||
|
|
||||||
interface.name = name
|
interface.name = name
|
||||||
FlexPE.interfaces.append(interface)
|
FPE.Transport.interfaces.append(interface)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
FPE.log("The interface \""+name+"\" could not be created. Check your configuration file for errors!", FPE.LOG_ERROR)
|
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()
|
self.createKeys()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def remember(hash, public_key, app_data = None):
|
def remember(packet_hash, destination_hash, public_key, app_data = None):
|
||||||
FPE.log("Remembering "+FPE.hexrep(hash, False), FPE.LOG_VERBOSE)
|
FPE.log("Remembering "+FPE.prettyhexrep(destination_hash), FPE.LOG_VERBOSE)
|
||||||
Identity.known_destinations[hash] = [time.time(), public_key, app_data]
|
Identity.known_destinations[destination_hash] = [time.time(), packet_hash, public_key, app_data]
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def recall(identity):
|
def recall(destination_hash):
|
||||||
pass
|
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
|
@staticmethod
|
||||||
def saveKnownDestinations():
|
def saveKnownDestinations():
|
||||||
@ -80,7 +90,7 @@ class Identity:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def validateAnnounce(packet):
|
def validateAnnounce(packet):
|
||||||
if packet.packet_type == FPE.Packet.ANNOUNCE:
|
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
|
destination_hash = packet.destination_hash
|
||||||
public_key = packet.data[10:Identity.DERKEYSIZE/8+10]
|
public_key = packet.data[10:Identity.DERKEYSIZE/8+10]
|
||||||
random_hash = packet.data[Identity.DERKEYSIZE/8+10:Identity.DERKEYSIZE/8+20]
|
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):
|
if announced_identity.validate(signature, signed_data):
|
||||||
FPE.log("Announce is valid", FPE.LOG_VERBOSE)
|
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:
|
else:
|
||||||
FPE.log("Announce is invalid", FPE.LOG_VERBOSE)
|
FPE.log("Announce is invalid", FPE.LOG_VERBOSE)
|
||||||
|
|
||||||
del announced_identity
|
del announced_identity
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def exitHandler():
|
||||||
|
Identity.saveKnownDestinations()
|
||||||
|
|
||||||
|
|
||||||
def createKeys(self):
|
def createKeys(self):
|
||||||
self.prv = rsa.generate_private_key(
|
self.prv = rsa.generate_private_key(
|
||||||
@ -119,10 +137,9 @@ class Identity:
|
|||||||
format=serialization.PublicFormat.SubjectPublicKeyInfo
|
format=serialization.PublicFormat.SubjectPublicKeyInfo
|
||||||
)
|
)
|
||||||
|
|
||||||
self.hash = Identity.truncatedHash(self.pub_bytes)
|
self.updateHashes()
|
||||||
self.hexhash = self.hash.encode("hex_codec")
|
|
||||||
|
|
||||||
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):
|
def getPrivateKey(self):
|
||||||
return self.prv_bytes
|
return self.prv_bytes
|
||||||
@ -138,10 +155,16 @@ class Identity:
|
|||||||
encoding=serialization.Encoding.DER,
|
encoding=serialization.Encoding.DER,
|
||||||
format=serialization.PublicFormat.SubjectPublicKeyInfo
|
format=serialization.PublicFormat.SubjectPublicKeyInfo
|
||||||
)
|
)
|
||||||
|
self.updateHashes()
|
||||||
|
|
||||||
def loadPublicKey(self, key):
|
def loadPublicKey(self, key):
|
||||||
self.pub_bytes = key
|
self.pub_bytes = key
|
||||||
self.pub = load_der_public_key(self.pub_bytes, backend=default_backend())
|
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):
|
def saveIdentity(self):
|
||||||
pass
|
pass
|
||||||
@ -150,7 +173,7 @@ class Identity:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def encrypt(self, plaintext):
|
def encrypt(self, plaintext):
|
||||||
if self.prv != None:
|
if self.pub != None:
|
||||||
chunksize = (Identity.KEYSIZE-Identity.PADDINGSIZE)/8
|
chunksize = (Identity.KEYSIZE-Identity.PADDINGSIZE)/8
|
||||||
chunks = int(math.ceil(len(plaintext)/(float(chunksize))))
|
chunks = int(math.ceil(len(plaintext)/(float(chunksize))))
|
||||||
# TODO: Remove debug output print("Plaintext size is "+str(len(plaintext))+", with "+str(chunks)+" chunks")
|
# 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.")
|
# TODO: Remove debug output print("Plaintext encrypted, ciphertext length is "+str(len(ciphertext))+" bytes.")
|
||||||
return ciphertext
|
return ciphertext
|
||||||
else:
|
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):
|
def decrypt(self, ciphertext):
|
||||||
@ -238,11 +261,12 @@ class Identity:
|
|||||||
else:
|
else:
|
||||||
raise KeyError("Signature validation failed because identity does not hold a public key")
|
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):
|
def getRandomHash(self):
|
||||||
return self.truncatedHash(os.urandom(10))
|
return self.truncatedHash(os.urandom(10))
|
||||||
|
|
||||||
|
|
||||||
def identityExithandler():
|
|
||||||
Identity.saveKnownDestinations()
|
|
||||||
|
|
||||||
atexit.register(identityExithandler)
|
|
@ -3,6 +3,7 @@ import SocketServer
|
|||||||
import threading
|
import threading
|
||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
|
import FPE
|
||||||
|
|
||||||
class UdpInterface(Interface):
|
class UdpInterface(Interface):
|
||||||
bind_ip = None
|
bind_ip = None
|
||||||
@ -36,9 +37,13 @@ class UdpInterface(Interface):
|
|||||||
|
|
||||||
|
|
||||||
def processIncoming(self, data):
|
def processIncoming(self, data):
|
||||||
|
# TODO: remove
|
||||||
|
#FPE.log("IN: "+FPE.prettyhexrep(data))
|
||||||
self.owner.inbound(data)
|
self.owner.inbound(data)
|
||||||
|
|
||||||
def processOutgoing(self,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 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||||
udp_socket.sendto(data, (self.forward_ip, self.forward_port))
|
udp_socket.sendto(data, (self.forward_ip, self.forward_port))
|
||||||
@ -52,5 +57,7 @@ class UdpInterfaceHandler(SocketServer.BaseRequestHandler):
|
|||||||
|
|
||||||
def handle(self):
|
def handle(self):
|
||||||
if (UdpInterfaceHandler.interface != None):
|
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)
|
UdpInterfaceHandler.interface.processIncoming(data)
|
@ -1,4 +1,5 @@
|
|||||||
import struct
|
import struct
|
||||||
|
import time
|
||||||
import FPE
|
import FPE
|
||||||
|
|
||||||
class Packet:
|
class Packet:
|
||||||
@ -29,14 +30,19 @@ class Packet:
|
|||||||
self.transport_id = transport_id
|
self.transport_id = transport_id
|
||||||
self.data = data
|
self.data = data
|
||||||
self.flags = self.getPackedFlags()
|
self.flags = self.getPackedFlags()
|
||||||
|
self.MTU = self.destination.MTU
|
||||||
|
|
||||||
self.raw = None
|
self.raw = None
|
||||||
self.packed = False
|
self.packed = False
|
||||||
self.sent = False
|
self.sent = False
|
||||||
self.MTU = self.destination.MTU
|
self.fromPacked = False
|
||||||
else:
|
else:
|
||||||
self.raw = data
|
self.raw = data
|
||||||
self.packed = True
|
self.packed = True
|
||||||
|
self.fromPacked = True
|
||||||
|
|
||||||
|
self.sent_at = None
|
||||||
|
self.packet_hash = None
|
||||||
|
|
||||||
def getPackedFlags(self):
|
def getPackedFlags(self):
|
||||||
packed_flags = (self.header_type << 6) | (self.transport_type << 4) | (self.destination.type << 2) | self.packet_type
|
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()
|
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.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)
|
FPE.Transport.outbound(self.raw)
|
||||||
|
self.packet_hash = FPE.Identity.fullHash(self.raw)
|
||||||
|
self.sent_at = time.time()
|
||||||
self.sent = True
|
self.sent = True
|
||||||
else:
|
else:
|
||||||
raise IOError("Packet was already sent")
|
raise IOError("Packet was already sent")
|
||||||
@ -99,3 +107,22 @@ class Packet:
|
|||||||
Transport.outbound(self.raw)
|
Transport.outbound(self.raw)
|
||||||
else:
|
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;
|
TUNNEL = 0x03;
|
||||||
types = [BROADCAST, TRANSPORT, RELAY, TUNNEL]
|
types = [BROADCAST, TRANSPORT, RELAY, TUNNEL]
|
||||||
|
|
||||||
|
interfaces = []
|
||||||
|
destinations = []
|
||||||
packet_hashlist = []
|
packet_hashlist = []
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def outbound(raw):
|
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
|
@staticmethod
|
||||||
def inbound(raw):
|
def inbound(raw):
|
||||||
@ -22,15 +28,43 @@ class Transport:
|
|||||||
Transport.packet_hashlist.append(packet_hash)
|
Transport.packet_hashlist.append(packet_hash)
|
||||||
packet = FPE.Packet(None, raw)
|
packet = FPE.Packet(None, raw)
|
||||||
packet.unpack()
|
packet.unpack()
|
||||||
|
packet.packet_hash = packet_hash
|
||||||
|
|
||||||
if packet.packet_type == FPE.Packet.ANNOUNCE:
|
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:
|
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:
|
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
|
@staticmethod
|
||||||
def registerDestination(destination):
|
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()
|
@ -68,3 +68,8 @@ def hexrep(data, delimit=True):
|
|||||||
delimiter = ""
|
delimiter = ""
|
||||||
hexrep = delimiter.join("{:02x}".format(ord(c)) for c in data)
|
hexrep = delimiter.join("{:02x}".format(ord(c)) for c in data)
|
||||||
return hexrep
|
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()
|
identity = Identity()
|
||||||
|
|
||||||
d1=Destination(identity, Destination.IN, Destination.SINGLE, "messenger", "user")
|
d1=Destination(identity, Destination.IN, Destination.SINGLE, "messenger", "user")
|
||||||
|
#d1.setProofStrategy(Destination.PROVE_ALL)
|
||||||
d1.setCallback(testCallback)
|
d1.setCallback(testCallback)
|
||||||
|
|
||||||
msg=""
|
msg=""
|
||||||
@ -28,7 +29,7 @@ pl = len(d1.identity.pub_bytes)
|
|||||||
|
|
||||||
d1.announce()
|
d1.announce()
|
||||||
p1=Packet(d1, msg)
|
p1=Packet(d1, msg)
|
||||||
#p1.send()
|
p1.send()
|
||||||
|
|
||||||
# p2=Packet(d2,"Test af msg")
|
# p2=Packet(d2,"Test af msg")
|
||||||
# p2.send()
|
# p2.send()
|
||||||
|
Loading…
Reference in New Issue
Block a user