diff --git a/RNS/Interfaces/Interface.py b/RNS/Interfaces/Interface.py index 493c571..a04462b 100755 --- a/RNS/Interfaces/Interface.py +++ b/RNS/Interfaces/Interface.py @@ -1,3 +1,5 @@ +import RNS + class Interface: IN = False OUT = False @@ -6,4 +8,8 @@ class Interface: name = None def __init__(self): - pass \ No newline at end of file + pass + + def get_hash(self): + # TODO: Maybe expand this to something more unique + return RNS.Identity.fullHash(str(self).encode("utf-8")) \ No newline at end of file diff --git a/RNS/Reticulum.py b/RNS/Reticulum.py index a2bd30e..a0a44bd 100755 --- a/RNS/Reticulum.py +++ b/RNS/Reticulum.py @@ -48,8 +48,13 @@ class Reticulum: os.makedirs(Reticulum.cachepath) if os.path.isfile(self.configpath): - self.config = ConfigObj(self.configpath) - RNS.log("Configuration loaded from "+self.configpath) + try: + self.config = ConfigObj(self.configpath) + RNS.log("Configuration loaded from "+self.configpath) + except Exception as e: + RNS.log("Could not parse the configuration at "+self.configpath, RNS.LOG_ERROR) + RNS.log("Check your configuration file for errors!", RNS.LOG_ERROR) + RNS.panic() else: RNS.log("Could not load config file, creating default configuration file...") self.createDefaultConfig() @@ -103,207 +108,211 @@ class Reticulum: RNS.log("", RNS.LOG_CRITICAL) Reticulum.__allow_unencrypted = True - + interface_names = [] for name in self.config["interfaces"]: - c = self.config["interfaces"][name] + if not name in interface_names: + c = self.config["interfaces"][name] - try: - if ("interface_enabled" in c) and c.as_bool("interface_enabled") == True: - if c["type"] == "UdpInterface": - interface = UdpInterface.UdpInterface( - RNS.Transport, - name, - c["listen_ip"], - int(c["listen_port"]), - c["forward_ip"], - int(c["forward_port"]) - ) + try: + if ("interface_enabled" in c) and c.as_bool("interface_enabled") == True: + if c["type"] == "UdpInterface": + interface = UdpInterface.UdpInterface( + RNS.Transport, + name, + c["listen_ip"], + int(c["listen_port"]), + c["forward_ip"], + int(c["forward_port"]) + ) - if "outgoing" in c and c.as_bool("outgoing") == True: - interface.OUT = True - else: - interface.OUT = False + if "outgoing" in c and c.as_bool("outgoing") == True: + interface.OUT = True + else: + interface.OUT = False - RNS.Transport.interfaces.append(interface) + RNS.Transport.interfaces.append(interface) - if c["type"] == "TCPServerInterface": - interface = TCPInterface.TCPServerInterface( - RNS.Transport, - name, - c["listen_ip"], - int(c["listen_port"]) - ) + if c["type"] == "TCPServerInterface": + interface = TCPInterface.TCPServerInterface( + RNS.Transport, + name, + c["listen_ip"], + int(c["listen_port"]) + ) - if "outgoing" in c and c.as_bool("outgoing") == True: - interface.OUT = True - else: - interface.OUT = False + if "outgoing" in c and c.as_bool("outgoing") == True: + interface.OUT = True + else: + interface.OUT = False - RNS.Transport.interfaces.append(interface) + RNS.Transport.interfaces.append(interface) - if c["type"] == "TCPClientInterface": - interface = TCPInterface.TCPClientInterface( - RNS.Transport, - name, - c["target_host"], - int(c["target_port"]) - ) + if c["type"] == "TCPClientInterface": + interface = TCPInterface.TCPClientInterface( + RNS.Transport, + name, + c["target_host"], + int(c["target_port"]) + ) - if "outgoing" in c and c.as_bool("outgoing") == True: - interface.OUT = True - else: - interface.OUT = False + if "outgoing" in c and c.as_bool("outgoing") == True: + interface.OUT = True + else: + interface.OUT = False - RNS.Transport.interfaces.append(interface) + RNS.Transport.interfaces.append(interface) - if c["type"] == "SerialInterface": - port = c["port"] if "port" in c else None - speed = int(c["speed"]) if "speed" in c else 9600 - databits = int(c["databits"]) if "databits" in c else 8 - parity = c["parity"] if "parity" in c else "N" - stopbits = int(c["stopbits"]) if "stopbits" in c else 1 + if c["type"] == "SerialInterface": + port = c["port"] if "port" in c else None + speed = int(c["speed"]) if "speed" in c else 9600 + databits = int(c["databits"]) if "databits" in c else 8 + parity = c["parity"] if "parity" in c else "N" + stopbits = int(c["stopbits"]) if "stopbits" in c else 1 - if port == None: - raise ValueError("No port specified for serial interface") + if port == None: + raise ValueError("No port specified for serial interface") - interface = SerialInterface.SerialInterface( - RNS.Transport, - name, - port, - speed, - databits, - parity, - stopbits - ) + interface = SerialInterface.SerialInterface( + RNS.Transport, + name, + port, + speed, + databits, + parity, + stopbits + ) - if "outgoing" in c and c["outgoing"].lower() == "true": - interface.OUT = True - else: - interface.OUT = False + if "outgoing" in c and c["outgoing"].lower() == "true": + interface.OUT = True + else: + interface.OUT = False - RNS.Transport.interfaces.append(interface) + RNS.Transport.interfaces.append(interface) - if c["type"] == "KISSInterface": - preamble = int(c["preamble"]) if "preamble" in c else None - txtail = int(c["txtail"]) if "txtail" in c else None - persistence = int(c["persistence"]) if "persistence" in c else None - slottime = int(c["slottime"]) if "slottime" in c else None - flow_control = (True if c["flow_control"] == "true" else False) if "flow_control" in c else False + if c["type"] == "KISSInterface": + preamble = int(c["preamble"]) if "preamble" in c else None + txtail = int(c["txtail"]) if "txtail" in c else None + persistence = int(c["persistence"]) if "persistence" in c else None + slottime = int(c["slottime"]) if "slottime" in c else None + flow_control = (True if c["flow_control"] == "true" else False) if "flow_control" in c else False - port = c["port"] if "port" in c else None - speed = int(c["speed"]) if "speed" in c else 9600 - databits = int(c["databits"]) if "databits" in c else 8 - parity = c["parity"] if "parity" in c else "N" - stopbits = int(c["stopbits"]) if "stopbits" in c else 1 + port = c["port"] if "port" in c else None + speed = int(c["speed"]) if "speed" in c else 9600 + databits = int(c["databits"]) if "databits" in c else 8 + parity = c["parity"] if "parity" in c else "N" + stopbits = int(c["stopbits"]) if "stopbits" in c else 1 - if port == None: - raise ValueError("No port specified for serial interface") + if port == None: + raise ValueError("No port specified for serial interface") - interface = KISSInterface.KISSInterface( - RNS.Transport, - name, - port, - speed, - databits, - parity, - stopbits, - preamble, - txtail, - persistence, - slottime, - flow_control - ) + interface = KISSInterface.KISSInterface( + RNS.Transport, + name, + port, + speed, + databits, + parity, + stopbits, + preamble, + txtail, + persistence, + slottime, + flow_control + ) - if "outgoing" in c and c["outgoing"].lower() == "true": - interface.OUT = True - else: - interface.OUT = False + if "outgoing" in c and c["outgoing"].lower() == "true": + interface.OUT = True + else: + interface.OUT = False - RNS.Transport.interfaces.append(interface) + RNS.Transport.interfaces.append(interface) - if c["type"] == "AX25KISSInterface": - preamble = int(c["preamble"]) if "preamble" in c else None - txtail = int(c["txtail"]) if "txtail" in c else None - persistence = int(c["persistence"]) if "persistence" in c else None - slottime = int(c["slottime"]) if "slottime" in c else None - flow_control = (True if c["flow_control"] == "true" else False) if "flow_control" in c else False + if c["type"] == "AX25KISSInterface": + preamble = int(c["preamble"]) if "preamble" in c else None + txtail = int(c["txtail"]) if "txtail" in c else None + persistence = int(c["persistence"]) if "persistence" in c else None + slottime = int(c["slottime"]) if "slottime" in c else None + flow_control = (True if c["flow_control"] == "true" else False) if "flow_control" in c else False - port = c["port"] if "port" in c else None - speed = int(c["speed"]) if "speed" in c else 9600 - databits = int(c["databits"]) if "databits" in c else 8 - parity = c["parity"] if "parity" in c else "N" - stopbits = int(c["stopbits"]) if "stopbits" in c else 1 + port = c["port"] if "port" in c else None + speed = int(c["speed"]) if "speed" in c else 9600 + databits = int(c["databits"]) if "databits" in c else 8 + parity = c["parity"] if "parity" in c else "N" + stopbits = int(c["stopbits"]) if "stopbits" in c else 1 - callsign = c["callsign"] if "callsign" in c else "" - ssid = int(c["ssid"]) if "ssid" in c else -1 + callsign = c["callsign"] if "callsign" in c else "" + ssid = int(c["ssid"]) if "ssid" in c else -1 - if port == None: - raise ValueError("No port specified for serial interface") + if port == None: + raise ValueError("No port specified for serial interface") - interface = AX25KISSInterface.AX25KISSInterface( - RNS.Transport, - name, - callsign, - ssid, - port, - speed, - databits, - parity, - stopbits, - preamble, - txtail, - persistence, - slottime, - flow_control - ) + interface = AX25KISSInterface.AX25KISSInterface( + RNS.Transport, + name, + callsign, + ssid, + port, + speed, + databits, + parity, + stopbits, + preamble, + txtail, + persistence, + slottime, + flow_control + ) - if "outgoing" in c and c["outgoing"].lower() == "true": - interface.OUT = True - else: - interface.OUT = False + if "outgoing" in c and c["outgoing"].lower() == "true": + interface.OUT = True + else: + interface.OUT = False - RNS.Transport.interfaces.append(interface) + RNS.Transport.interfaces.append(interface) - if c["type"] == "RNodeInterface": - frequency = int(c["frequency"]) if "frequency" in c else None - bandwidth = int(c["bandwidth"]) if "bandwidth" in c else None - txpower = int(c["txpower"]) if "txpower" in c else None - spreadingfactor = int(c["spreadingfactor"]) if "spreadingfactor" in c else None - codingrate = int(c["codingrate"]) if "codingrate" in c else None - flow_control = (True if c["flow_control"] == "true" else False) if "flow_control" in c else False + if c["type"] == "RNodeInterface": + frequency = int(c["frequency"]) if "frequency" in c else None + bandwidth = int(c["bandwidth"]) if "bandwidth" in c else None + txpower = int(c["txpower"]) if "txpower" in c else None + spreadingfactor = int(c["spreadingfactor"]) if "spreadingfactor" in c else None + codingrate = int(c["codingrate"]) if "codingrate" in c else None + flow_control = (True if c["flow_control"] == "true" else False) if "flow_control" in c else False - port = c["port"] if "port" in c else None - - if port == None: - raise ValueError("No port specified for RNode interface") + port = c["port"] if "port" in c else None + + if port == None: + raise ValueError("No port specified for RNode interface") - interface = RNodeInterface.RNodeInterface( - RNS.Transport, - name, - port, - frequency, - bandwidth, - txpower, - spreadingfactor, - flow_control - ) + interface = RNodeInterface.RNodeInterface( + RNS.Transport, + name, + port, + frequency, + bandwidth, + txpower, + spreadingfactor, + flow_control + ) - if "outgoing" in c and c["outgoing"].lower() == "true": - interface.OUT = True - else: - interface.OUT = False + if "outgoing" in c and c["outgoing"].lower() == "true": + interface.OUT = True + else: + interface.OUT = False - RNS.Transport.interfaces.append(interface) - else: - RNS.log("Skipping disabled interface \""+name+"\"", RNS.LOG_VERBOSE) + RNS.Transport.interfaces.append(interface) + else: + RNS.log("Skipping disabled interface \""+name+"\"", RNS.LOG_VERBOSE) - except Exception as e: - RNS.log("The interface \""+name+"\" could not be created. Check your configuration file for errors!", RNS.LOG_ERROR) - RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) - raise e + except Exception as e: + RNS.log("The interface \""+name+"\" could not be created. Check your configuration file for errors!", RNS.LOG_ERROR) + RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) + RNS.panic() + else: + RNS.log("The interface name \""+name+"\" was already used. Check your configuration file for errors!", RNS.LOG_ERROR) + RNS.panic() def createDefaultConfig(self): diff --git a/RNS/Transport.py b/RNS/Transport.py index 2821a36..b3e08a8 100755 --- a/RNS/Transport.py +++ b/RNS/Transport.py @@ -70,25 +70,25 @@ class Transport: @staticmethod def start(): if Transport.identity == None: - transport_identity_path = RNS.Reticulum.configdir+"/transportidentity" + transport_identity_path = RNS.Reticulum.storagepath+"/transport_identity" if os.path.isfile(transport_identity_path): Transport.identity = RNS.Identity.from_file(transport_identity_path) if Transport.identity == None: - RNS.log("No valid Transport Identity on disk, creating...", RNS.LOG_VERBOSE) + RNS.log("No valid Transport Identity in storage, creating...", RNS.LOG_VERBOSE) Transport.identity = RNS.Identity() Transport.identity.save(transport_identity_path) else: - RNS.log("Loaded Transport Identity from disk", RNS.LOG_VERBOSE) + RNS.log("Loaded Transport Identity from storage", RNS.LOG_VERBOSE) - packet_hashlist_path = RNS.Reticulum.configdir+"/packet_hashlist" + packet_hashlist_path = RNS.Reticulum.storagepath+"/packet_hashlist" if os.path.isfile(packet_hashlist_path): try: file = open(packet_hashlist_path, "rb") Transport.packet_hashlist = umsgpack.unpackb(file.read()) file.close() except Exception as e: - RNS.log("Could not load packet hashlist from disk, the contained exception was: "+str(e), RNS.LOG_ERROR) + RNS.log("Could not load packet hashlist from storage, the contained exception was: "+str(e), RNS.LOG_ERROR) # Create transport-specific destinations path_request_destination = RNS.Destination(None, RNS.Destination.IN, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request") @@ -99,6 +99,38 @@ class Transport: thread.start() if RNS.Reticulum.transport_enabled(): + destination_table_path = RNS.Reticulum.storagepath+"/destination_table" + if os.path.isfile(destination_table_path): + serialised_destinations = [] + try: + file = open(destination_table_path, "rb") + serialised_destinations = umsgpack.unpackb(file.read()) + file.close() + + for serialised_entry in serialised_destinations: + destination_hash = serialised_entry[0] + receiving_interface = Transport.find_interface_from_hash(serialised_entry[6]) + announce_packet = Transport.get_cached_packet(serialised_entry[7]) + + if announce_packet != None and receiving_interface != None: + announce_packet.unpack() + timestamp = serialised_entry[1] + received_from = serialised_entry[2] + hops = serialised_entry[3] + expires = serialised_entry[4] + random_blobs = serialised_entry[5] + Transport.destination_table[destination_hash] = [timestamp, received_from, hops, expires, random_blobs, receiving_interface, announce_packet] + RNS.log("Loaded destination table entry for "+RNS.prettyhexrep(destination_hash)+" from storage", RNS.LOG_DEBUG) + else: + RNS.log("Could not reconstruct destination table entry from storage for "+RNS.prettyhexrep(destination_hash), RNS.LOG_DEBUG) + if announce_packet == None: + RNS.log("The announce packet could not be loaded from cache", RNS.LOG_DEBUG) + if receiving_interface == None: + RNS.log("The interface is no longer available", RNS.LOG_DEBUG) + + except Exception as e: + RNS.log("Could not load destination table from storage, the contained exception was: "+str(e), RNS.LOG_ERROR) + RNS.log("Transport instance "+str(Transport.identity)+" started") @staticmethod @@ -599,6 +631,13 @@ class Transport: else: RNS.log("Attempted to activate a link that was not in the pending table", RNS.LOG_ERROR) + @staticmethod + def find_interface_from_hash(interface_hash): + for interface in Transport.interfaces: + if interface.get_hash() == interface_hash: + return interface + + return None @staticmethod def shouldCache(packet): @@ -610,8 +649,8 @@ class Transport: return False @staticmethod - def cache(packet): - if RNS.Transport.shouldCache(packet): + def cache(packet, force_cache=False): + if RNS.Transport.shouldCache(packet) or force_cache: try: packet_hash = RNS.hexrep(packet.getHash(), delimit=False) file = open(RNS.Reticulum.cachepath+"/"+packet_hash, "wb") @@ -622,20 +661,36 @@ class Transport: RNS.log("Error writing packet to cache", RNS.LOG_ERROR) RNS.log("The contained exception was: "+str(e)) - # TODO: Implement cache requests. Needs methodology - # rethinking. This is skeleton code. @staticmethod - def cache_request_packet(packet): - if len(packet.data) == RNS.Identity.HASHLENGTH/8: - packet_hash = RNS.hexrep(packet.data, delimit=False) + def get_cached_packet(packet_hash): + try: + packet_hash = RNS.hexrep(packet_hash, delimit=False) path = RNS.Reticulum.cachepath+"/"+packet_hash if os.path.isfile(path): file = open(path, "rb") raw = file.read() file.close() packet = RNS.Packet(None, raw) - # TODO: Implement outbound for this + return packet + else: + return None + except Exception as e: + RNS.log("Exception occurred while getting cached packet.", RNS.LOG_ERROR) + RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) + # TODO: Implement cache requests. Needs methodology + # rethinking. This is skeleton code. + @staticmethod + def cache_request_packet(packet): + if len(packet.data) == RNS.Identity.HASHLENGTH/8: + packet_hash = RNS.hexrep(packet.data, delimit=False) + packet = Transport.get_cached_packet(packet_hash) + + if packet != None: + # TODO: Implement outbound for this + pass + else: + pass # TODO: Implement cache requests. Needs methodology # rethinking. This is skeleton code. @@ -704,10 +759,33 @@ class Transport: @staticmethod def exitHandler(): + RNS.log("Saving packet hashlist to storage...", RNS.LOG_VERBOSE) try: - packet_hashlist_path = RNS.Reticulum.configdir+"/packet_hashlist" + packet_hashlist_path = RNS.Reticulum.storagepath+"/packet_hashlist" file = open(packet_hashlist_path, "wb") file.write(umsgpack.packb(Transport.packet_hashlist)) file.close() + RNS.log("Done packet hashlist to storage", RNS.LOG_VERBOSE) except Exception as e: - RNS.log("Could not save packet hashlist to disk, the contained exception was: "+str(e), RNS.LOG_ERROR) + RNS.log("Could not save packet hashlist to storage, the contained exception was: "+str(e), RNS.LOG_ERROR) + + RNS.log("Saving destination table to storage...", RNS.LOG_VERBOSE) + try: + serialised_destinations = [] + for destination_hash in Transport.destination_table: + destination_entry = Transport.destination_table[destination_hash] + + de = destination_entry + Transport.cache(de[6], force_cache=True) + interface_hash = de[5].get_hash() + packet_hash = de[6].getHash() + serialised_entry = [destination_hash, de[0], de[1], de[2], de[3], de[4], interface_hash, packet_hash] + serialised_destinations.append(serialised_entry) + + destination_table_path = RNS.Reticulum.storagepath+"/destination_table" + file = open(destination_table_path, "wb") + file.write(umsgpack.packb(serialised_destinations)) + file.close() + RNS.log("Done saving destination table to storage", RNS.LOG_VERBOSE) + except Exception as e: + RNS.log("Could not save destination table to storage, the contained exception was: "+str(e), RNS.LOG_ERROR)