mirror of
https://github.com/markqvist/Reticulum.git
synced 2024-11-26 15:30:18 +00:00
Serial interface implemented
This commit is contained in:
parent
9a9630cfd2
commit
be8fa4f7bb
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.pyc
|
@ -1,4 +1,5 @@
|
|||||||
import base64
|
import base64
|
||||||
|
from Transport import Transport
|
||||||
from cryptography.fernet import Fernet
|
from cryptography.fernet import Fernet
|
||||||
from cryptography.hazmat.primitives import hashes
|
from cryptography.hazmat.primitives import hashes
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
@ -41,13 +42,13 @@ class Destination:
|
|||||||
|
|
||||||
|
|
||||||
def __init__(self, direction, type, app_name, *aspects):
|
def __init__(self, direction, type, app_name, *aspects):
|
||||||
|
|
||||||
# Check input values and build name string
|
# Check input values and build name string
|
||||||
if "." in app_name: raise ValueError("Dots can't be used in app names")
|
if "." in app_name: raise ValueError("Dots can't be used in app names")
|
||||||
if not type in Destination.types: raise ValueError("Unknown destination type")
|
if not type in Destination.types: raise ValueError("Unknown destination type")
|
||||||
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.mtu = 0
|
||||||
|
|
||||||
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)
|
||||||
@ -61,6 +62,8 @@ class Destination:
|
|||||||
self.prv_bytes = None
|
self.prv_bytes = None
|
||||||
self.pub_bytes = None
|
self.pub_bytes = None
|
||||||
|
|
||||||
|
Transport.registerDestination(self)
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "<"+self.name+"/"+self.hexhash+">"
|
return "<"+self.name+"/"+self.hexhash+">"
|
||||||
@ -158,7 +161,7 @@ class Destination:
|
|||||||
|
|
||||||
def decrypt(self, ciphertext):
|
def decrypt(self, ciphertext):
|
||||||
if self.type == Destination.PLAIN:
|
if self.type == Destination.PLAIN:
|
||||||
return plaintext
|
return ciphertext
|
||||||
|
|
||||||
if self.type == Destination.SINGLE and self.prv != None:
|
if self.type == Destination.SINGLE and self.prv != None:
|
||||||
plaintext = self.prv.decrypt(
|
plaintext = self.prv.decrypt(
|
111
FPE/FlexPE.py
Normal file
111
FPE/FlexPE.py
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
from Interfaces import *
|
||||||
|
import ConfigParser
|
||||||
|
import jsonpickle
|
||||||
|
from vendor.configobj import ConfigObj
|
||||||
|
import struct
|
||||||
|
import array
|
||||||
|
import os.path
|
||||||
|
import os
|
||||||
|
|
||||||
|
class FlexPE:
|
||||||
|
MTU = 700
|
||||||
|
router = None
|
||||||
|
config = None
|
||||||
|
destinations = []
|
||||||
|
interfaces = []
|
||||||
|
configdir = os.path.expanduser("~")+"/.flexpe"
|
||||||
|
configpath = configdir+"/config"
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
if os.path.isfile(FlexPE.configpath):
|
||||||
|
self.config = ConfigObj(FlexPE.configpath)
|
||||||
|
else:
|
||||||
|
print("Could not load config file, creating default configuration...")
|
||||||
|
self.createDefaultConfig()
|
||||||
|
|
||||||
|
self.applyConfig()
|
||||||
|
print FlexPE.interfaces
|
||||||
|
|
||||||
|
FlexPE.router = self
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def addDestination(destination):
|
||||||
|
destination.MTU = FlexPE.MTU
|
||||||
|
FlexPE.destinations.append(destination)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def incoming(data):
|
||||||
|
|
||||||
|
header = struct.unpack("B", data[0])
|
||||||
|
|
||||||
|
hash = data[1:11]
|
||||||
|
type = header[0] & 0x03
|
||||||
|
|
||||||
|
for destination in FlexPE.destinations:
|
||||||
|
if destination.hash == hash and destination.type == type:
|
||||||
|
destination.receive(data[11:])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def outbound(raw):
|
||||||
|
for interface in FlexPE.interfaces:
|
||||||
|
if interface.OUT:
|
||||||
|
interface.processOutgoing(raw)
|
||||||
|
|
||||||
|
def applyConfig(self):
|
||||||
|
for name in self.config["interfaces"]:
|
||||||
|
c = self.config["interfaces"][name]
|
||||||
|
try:
|
||||||
|
if c["type"] == "UdpInterface":
|
||||||
|
interface = UdpInterface.UdpInterface(
|
||||||
|
self,
|
||||||
|
c["listen_ip"],
|
||||||
|
int(c["listen_port"]),
|
||||||
|
c["forward_ip"],
|
||||||
|
int(c["forward_port"])
|
||||||
|
)
|
||||||
|
|
||||||
|
if c["use_as_outgoing"].lower() == "true":
|
||||||
|
interface.OUT = True
|
||||||
|
|
||||||
|
interface.name = name
|
||||||
|
FlexPE.interfaces.append(interface)
|
||||||
|
|
||||||
|
if c["type"] == "SerialInterface":
|
||||||
|
interface = SerialInterface.SerialInterface(
|
||||||
|
self,
|
||||||
|
c["port"],
|
||||||
|
int(c["speed"]),
|
||||||
|
int(c["databits"]),
|
||||||
|
c["parity"],
|
||||||
|
int(c["stopbits"])
|
||||||
|
)
|
||||||
|
|
||||||
|
if c["use_as_outgoing"].lower() == "true":
|
||||||
|
interface.OUT = True
|
||||||
|
|
||||||
|
interface.name = name
|
||||||
|
FlexPE.interfaces.append(interface)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print("The interface \""+name+"\" could not be created. Check your configuration file for errors!")
|
||||||
|
print("The contained error was: "+str(e))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def createDefaultConfig(self):
|
||||||
|
self.config = ConfigObj()
|
||||||
|
self.config.filename = FlexPE.configpath
|
||||||
|
self.config["interfaces"] = {}
|
||||||
|
self.config["interfaces"]["Default UDP Interface"] = {}
|
||||||
|
self.config["interfaces"]["Default UDP Interface"]["type"] = "UdpInterface"
|
||||||
|
self.config["interfaces"]["Default UDP Interface"]["listen_ip"] = "0.0.0.0"
|
||||||
|
self.config["interfaces"]["Default UDP Interface"]["listen_port"] = 7777
|
||||||
|
self.config["interfaces"]["Default UDP Interface"]["forward_ip"] = "255.255.255.255"
|
||||||
|
self.config["interfaces"]["Default UDP Interface"]["forward_port"] = 7777
|
||||||
|
self.config["interfaces"]["Default UDP Interface"]["use_as_outgoing"] = "true"
|
||||||
|
if not os.path.isdir(FlexPE.configdir):
|
||||||
|
os.makedirs(FlexPE.configdir)
|
||||||
|
self.config.write()
|
||||||
|
self.applyConfig()
|
9
FPE/Interfaces/Interface.py
Normal file
9
FPE/Interfaces/Interface.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
class Interface:
|
||||||
|
IN = False
|
||||||
|
OUT = False
|
||||||
|
FWD = False
|
||||||
|
RPT = False
|
||||||
|
name = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
76
FPE/Interfaces/SerialInterface.py
Normal file
76
FPE/Interfaces/SerialInterface.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
from __future__ import print_function
|
||||||
|
from Interface import Interface
|
||||||
|
import sys
|
||||||
|
import serial
|
||||||
|
import threading
|
||||||
|
|
||||||
|
class SerialInterface(Interface):
|
||||||
|
MAX_CHUNK = 32768
|
||||||
|
TIMEOUT_SECONDS = 0.15
|
||||||
|
|
||||||
|
owner = None
|
||||||
|
port = None
|
||||||
|
speed = None
|
||||||
|
databits = None
|
||||||
|
parity = None
|
||||||
|
stopbits = None
|
||||||
|
serial = None
|
||||||
|
|
||||||
|
def __init__(self, owner, port, speed, databits, parity, stopbits):
|
||||||
|
self.owner = owner
|
||||||
|
self.port = port
|
||||||
|
self.speed = speed
|
||||||
|
self.databits = databits
|
||||||
|
self.parity = serial.PARITY_NONE
|
||||||
|
self.stopbits = stopbits
|
||||||
|
|
||||||
|
if parity.lower() == "e" or parity.lower() == "even":
|
||||||
|
self.parity = serial.PARITY_EVEN
|
||||||
|
|
||||||
|
if parity.lower() == "o" or parity.lower() == "odd":
|
||||||
|
self.parity = serial.PARITY_ODD
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.serial = serial.Serial(
|
||||||
|
port = self.port,
|
||||||
|
baudrate = self.speed,
|
||||||
|
bytesize = self.databits,
|
||||||
|
parity = self.parity,
|
||||||
|
stopbits = self.stopbits,
|
||||||
|
timeout = SerialInterface.TIMEOUT_SECONDS,
|
||||||
|
xonxoff = False,
|
||||||
|
rtscts = False,
|
||||||
|
write_timeout = None,
|
||||||
|
dsrdtr = False,
|
||||||
|
)
|
||||||
|
print(self.serial.inter_byte_timeout)
|
||||||
|
except Exception as e:
|
||||||
|
print("Could not create serial port", file=sys.stderr)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#self.serial.open()
|
||||||
|
if self.serial.is_open:
|
||||||
|
thread = threading.Thread(target=self.readLoop)
|
||||||
|
thread.setDaemon(True)
|
||||||
|
thread.start()
|
||||||
|
else:
|
||||||
|
raise IOError("Could not open serial port")
|
||||||
|
|
||||||
|
|
||||||
|
def processIncoming(self, data):
|
||||||
|
self.owner.__class__.incoming(data)
|
||||||
|
|
||||||
|
|
||||||
|
def processOutgoing(self,data):
|
||||||
|
self.serial.write(data)
|
||||||
|
|
||||||
|
|
||||||
|
def readLoop(self):
|
||||||
|
while self.serial.is_open:
|
||||||
|
data = self.serial.read(size=self.owner.__class__.MTU)
|
||||||
|
if not data == "":
|
||||||
|
self.processIncoming(data)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
56
FPE/Interfaces/UdpInterface.py
Normal file
56
FPE/Interfaces/UdpInterface.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
from Interface import Interface
|
||||||
|
import SocketServer
|
||||||
|
import threading
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
|
||||||
|
class UdpInterface(Interface):
|
||||||
|
bind_ip = None
|
||||||
|
bind_port = None
|
||||||
|
forward_ip = None
|
||||||
|
forward_port = None
|
||||||
|
owner = None
|
||||||
|
|
||||||
|
def __init__(self, owner, bindip=None, bindport=None, forwardip=None, forwardport=None):
|
||||||
|
self.IN = True
|
||||||
|
self.OUT = False
|
||||||
|
|
||||||
|
if (bindip != None and bindport != None):
|
||||||
|
self.receives = True
|
||||||
|
self.bind_ip = bindip
|
||||||
|
self.bind_port = bindport
|
||||||
|
|
||||||
|
UdpInterfaceHandler.interface = self
|
||||||
|
self.owner = owner
|
||||||
|
address = (self.bind_ip, self.bind_port)
|
||||||
|
self.server = SocketServer.UDPServer(address, UdpInterfaceHandler)
|
||||||
|
|
||||||
|
thread = threading.Thread(target=self.server.serve_forever)
|
||||||
|
thread.setDaemon(True)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
if (forwardip != None and forwardport != None):
|
||||||
|
self.forwards = True
|
||||||
|
self.forward_ip = forwardip
|
||||||
|
self.forward_port = forwardport
|
||||||
|
|
||||||
|
|
||||||
|
def processIncoming(self, data):
|
||||||
|
self.owner.__class__.incoming(data)
|
||||||
|
|
||||||
|
def processOutgoing(self,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))
|
||||||
|
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "UdpInterface["+self.bind_ip+":"+str(self.bind_port)+"]"
|
||||||
|
|
||||||
|
class UdpInterfaceHandler(SocketServer.BaseRequestHandler):
|
||||||
|
interface = None
|
||||||
|
|
||||||
|
def handle(self):
|
||||||
|
if (UdpInterfaceHandler.interface != None):
|
||||||
|
data = self.request[0].strip()
|
||||||
|
UdpInterfaceHandler.interface.processIncoming(data)
|
5
FPE/Interfaces/__init__.py
Normal file
5
FPE/Interfaces/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import os
|
||||||
|
import glob
|
||||||
|
|
||||||
|
modules = glob.glob(os.path.dirname(__file__)+"/*.py")
|
||||||
|
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]
|
35
FPE/Packet.py
Normal file
35
FPE/Packet.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import struct
|
||||||
|
from Transport import *
|
||||||
|
|
||||||
|
class Packet:
|
||||||
|
|
||||||
|
def __init__(self, destination, data):
|
||||||
|
self.destination = destination
|
||||||
|
self.data = data
|
||||||
|
self.flags = 0x00
|
||||||
|
self.header = 0x00
|
||||||
|
self.raw = None
|
||||||
|
self.sent = False
|
||||||
|
self.mtu = 0
|
||||||
|
|
||||||
|
def send(self):
|
||||||
|
if not self.sent:
|
||||||
|
self.MTU = self.destination.MTU
|
||||||
|
self.header = struct.pack("!B", self.header ^ self.destination.type ^ self.flags)
|
||||||
|
self.header += self.destination.hash
|
||||||
|
self.ciphertext = self.destination.encrypt(self.data)
|
||||||
|
self.raw = self.header + self.ciphertext
|
||||||
|
|
||||||
|
if len(self.raw) > self.MTU:
|
||||||
|
raise IOError("Packet size exceeds MTU of "+Packet.MTU+" bytes")
|
||||||
|
|
||||||
|
Transport.outbound(self.raw)
|
||||||
|
self.sent = True
|
||||||
|
else:
|
||||||
|
raise IOError("Packet was already sent")
|
||||||
|
|
||||||
|
def resend(self):
|
||||||
|
if self.sent:
|
||||||
|
Transport.outbound(self.raw)
|
||||||
|
else:
|
||||||
|
raise IOError("Packet was not sent yet")
|
10
FPE/Transport.py
Normal file
10
FPE/Transport.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
from FlexPE import FlexPE
|
||||||
|
|
||||||
|
class Transport:
|
||||||
|
@staticmethod
|
||||||
|
def outbound(raw):
|
||||||
|
FlexPE.outbound(raw)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def registerDestination(destination):
|
||||||
|
FlexPE.addDestination(destination)
|
5
FPE/__init__.py
Normal file
5
FPE/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import os
|
||||||
|
import glob
|
||||||
|
|
||||||
|
modules = glob.glob(os.path.dirname(__file__)+"/*.py")
|
||||||
|
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]
|
5
FPE/vendor/__init__.py
vendored
Normal file
5
FPE/vendor/__init__.py
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import os
|
||||||
|
import glob
|
||||||
|
|
||||||
|
modules = glob.glob(os.path.dirname(__file__)+"/*.py")
|
||||||
|
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]
|
2468
FPE/vendor/configobj.py
vendored
Normal file
2468
FPE/vendor/configobj.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
24
Packet.py
24
Packet.py
@ -1,24 +0,0 @@
|
|||||||
import struct
|
|
||||||
from Transport import *
|
|
||||||
|
|
||||||
class Packet:
|
|
||||||
MTU = 960
|
|
||||||
|
|
||||||
def __init__(self, destination, data):
|
|
||||||
self.destination = destination
|
|
||||||
self.data = data
|
|
||||||
self.flags = 0x00
|
|
||||||
self.header = 0x00
|
|
||||||
self.raw = None
|
|
||||||
self.sent = False
|
|
||||||
|
|
||||||
def send(self):
|
|
||||||
self.header = struct.pack("!B", self.header ^ self.destination.type ^ self.flags)
|
|
||||||
self.header += self.destination.hash
|
|
||||||
self.ciphertext = self.destination.encrypt(self.data)
|
|
||||||
self.raw = self.header + self.ciphertext
|
|
||||||
|
|
||||||
if len(self.raw) > Packet.MTU:
|
|
||||||
raise IOError("Packet size exceeds MTU of "+Packet.MTU+" bytes")
|
|
||||||
|
|
||||||
Transport.outbound(self.raw)
|
|
@ -1,4 +0,0 @@
|
|||||||
class Transport:
|
|
||||||
@staticmethod
|
|
||||||
def outbound(raw):
|
|
||||||
pass
|
|
Loading…
Reference in New Issue
Block a user