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
|
||||
from Transport import Transport
|
||||
from cryptography.fernet import Fernet
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
@ -41,13 +42,13 @@ class Destination:
|
||||
|
||||
|
||||
def __init__(self, direction, type, app_name, *aspects):
|
||||
|
||||
# Check input values and build name string
|
||||
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 direction in Destination.directions: raise ValueError("Unknown destination direction")
|
||||
self.type = type
|
||||
self.direction = direction
|
||||
self.mtu = 0
|
||||
|
||||
self.name = Destination.getDestinationName(app_name, *aspects)
|
||||
self.hash = Destination.getDestinationHash(app_name, *aspects)
|
||||
@ -61,6 +62,8 @@ class Destination:
|
||||
self.prv_bytes = None
|
||||
self.pub_bytes = None
|
||||
|
||||
Transport.registerDestination(self)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return "<"+self.name+"/"+self.hexhash+">"
|
||||
@ -158,7 +161,7 @@ class Destination:
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
if self.type == Destination.PLAIN:
|
||||
return plaintext
|
||||
return ciphertext
|
||||
|
||||
if self.type == Destination.SINGLE and self.prv != None:
|
||||
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