kopia lustrzana https://github.com/markqvist/reticulum
				
				
				
			
		
			
				
	
	
		
			1179 wiersze
		
	
	
		
			54 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
			
		
		
	
	
			1179 wiersze
		
	
	
		
			54 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
| # MIT License
 | |
| #
 | |
| # Copyright (c) 2016-2022 Mark Qvist / unsigned.io
 | |
| #
 | |
| # Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
| # of this software and associated documentation files (the "Software"), to deal
 | |
| # in the Software without restriction, including without limitation the rights
 | |
| # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | |
| # copies of the Software, and to permit persons to whom the Software is
 | |
| # furnished to do so, subject to the following conditions:
 | |
| #
 | |
| # The above copyright notice and this permission notice shall be included in all
 | |
| # copies or substantial portions of the Software.
 | |
| #
 | |
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | |
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | |
| # SOFTWARE.
 | |
| 
 | |
| from .vendor.platformutils import get_platform
 | |
| from cryptography.hazmat.primitives import hashes
 | |
| from cryptography.hazmat.primitives.kdf.hkdf import HKDF
 | |
| from cryptography.hazmat.backends import default_backend
 | |
| 
 | |
| cio_default_backend = default_backend()
 | |
| 
 | |
| if get_platform() == "android":
 | |
|     from .Interfaces import Interface
 | |
|     from .Interfaces import LocalInterface
 | |
|     from .Interfaces import AutoInterface
 | |
|     from .Interfaces import TCPInterface
 | |
|     from .Interfaces import UDPInterface
 | |
|     from .Interfaces import I2PInterface
 | |
| else:
 | |
|     from .Interfaces import *
 | |
| 
 | |
| from .vendor.configobj import ConfigObj
 | |
| import configparser
 | |
| import multiprocessing.connection
 | |
| import signal
 | |
| import threading
 | |
| import atexit
 | |
| import struct
 | |
| import array
 | |
| import os.path
 | |
| import os
 | |
| import RNS
 | |
| 
 | |
| class Reticulum:
 | |
|     """
 | |
|     This class is used to initialise access to Reticulum within a
 | |
|     program. You must create exactly one instance of this class before
 | |
|     carrying out any other RNS operations, such as creating destinations
 | |
|     or sending traffic. Every independently executed program must create
 | |
|     their own instance of the Reticulum class, but Reticulum will
 | |
|     automatically handle inter-program communication on the same system,
 | |
|     and expose all connected programs to external interfaces as well.
 | |
| 
 | |
|     As soon as an instance of this class is created, Reticulum will start
 | |
|     opening and configuring any hardware devices specified in the supplied
 | |
|     configuration.
 | |
| 
 | |
|     Currently the first running instance must be kept running while other
 | |
|     local instances are connected, as the first created instance will
 | |
|     act as a master instance that directly communicates with external
 | |
|     hardware such as modems, TNCs and radios. If a master instance is
 | |
|     asked to exit, it will not exit until all client processes have
 | |
|     terminated (unless killed forcibly).
 | |
| 
 | |
|     If you are running Reticulum on a system with several different
 | |
|     programs that use RNS starting and terminating at different times,
 | |
|     it will be advantageous to run a master RNS instance as a daemon for
 | |
|     other programs to use on demand.
 | |
|     """
 | |
| 
 | |
|     # Future minimum will probably be locked in at 251 bytes to support
 | |
|     # networks with segments of different MTUs. Absolute minimum is 219.
 | |
|     MTU            = 500
 | |
|     """
 | |
|     The MTU that Reticulum adheres to, and will expect other peers to
 | |
|     adhere to. By default, the MTU is 507 bytes. In custom RNS network
 | |
|     implementations, it is possible to change this value, but doing so will
 | |
|     completely break compatibility with all other RNS networks. An identical
 | |
|     MTU is a prerequisite for peers to communicate in the same network.
 | |
| 
 | |
|     Unless you really know what you are doing, the MTU should be left at
 | |
|     the default value.
 | |
|     """
 | |
| 
 | |
|     MAX_QUEUED_ANNOUNCES = 16384
 | |
|     QUEUED_ANNOUNCE_LIFE = 60*60*24
 | |
| 
 | |
|     ANNOUNCE_CAP = 2
 | |
|     """
 | |
|     The maximum percentage of interface bandwidth that, at any given time,
 | |
|     may be used to propagate announces. If an announce was scheduled for
 | |
|     broadcasting on an interface, but doing so would exceed the allowed
 | |
|     bandwidth allocation, the announce will be queued for transmission
 | |
|     when there is bandwidth available.
 | |
| 
 | |
|     Reticulum will always prioritise propagating announces with fewer
 | |
|     hops, ensuring that distant, large networks with many peers on fast
 | |
|     links don't overwhelm the capacity of smaller networks on slower
 | |
|     mediums. If an announce remains queued for an extended amount of time,
 | |
|     it will eventually be dropped.
 | |
| 
 | |
|     This value will be applied by default to all created interfaces,
 | |
|     but it can be configured individually on a per-interface basis.
 | |
|     """
 | |
| 
 | |
|     MINIMUM_BITRATE = 500
 | |
| 
 | |
|     # TODO: To reach the 300bps level without unreasonably impacting
 | |
|     # performance on faster links, we need a mechanism for setting
 | |
|     # this value more intelligently. One option could be inferring it
 | |
|     # from interface speed, but a better general approach would most
 | |
|     # probably be to let Reticulum somehow continously build a map of
 | |
|     # per-hop latencies and use this map for the timeout calculation. 
 | |
|     DEFAULT_PER_HOP_TIMEOUT = 5
 | |
| 
 | |
|     # Length of truncated hashes in bits.
 | |
|     TRUNCATED_HASHLENGTH = 80
 | |
| 
 | |
|     HEADER_MINSIZE = 2+1+(TRUNCATED_HASHLENGTH//8)*1
 | |
|     HEADER_MAXSIZE = 2+1+(TRUNCATED_HASHLENGTH//8)*2
 | |
|     IFAC_MIN_SIZE  = 1
 | |
|     IFAC_SALT      = bytes.fromhex("adf54d882c9a9b80771eb4995d702d4a3e733391b2a0f53f416d9f907e55cff8")
 | |
|     
 | |
|     MDU            = MTU - HEADER_MAXSIZE - IFAC_MIN_SIZE
 | |
| 
 | |
|     router         = None
 | |
|     config         = None
 | |
|     
 | |
|     # The default configuration path will be expanded to a directory
 | |
|     # named ".reticulum" inside the current users home directory
 | |
|     configdir    = os.path.expanduser("~")+"/.reticulum"
 | |
|     configpath   = ""
 | |
|     storagepath  = ""
 | |
|     cachepath    = ""
 | |
|     
 | |
|     @staticmethod
 | |
|     def exit_handler():
 | |
|         # This exit handler is called whenever Reticulum is asked to
 | |
|         # shut down, and will in turn call exit handlers in other
 | |
|         # classes, saving necessary information to disk and carrying
 | |
|         # out cleanup operations.
 | |
| 
 | |
|         RNS.Transport.exit_handler()
 | |
|         RNS.Identity.exit_handler()
 | |
| 
 | |
|     @staticmethod
 | |
|     def sigint_handler(signal, frame):
 | |
|         RNS.Transport.detach_interfaces()
 | |
|         RNS.exit()
 | |
| 
 | |
| 
 | |
|     @staticmethod
 | |
|     def sigterm_handler(signal, frame):
 | |
|         RNS.Transport.detach_interfaces()
 | |
|         RNS.exit()
 | |
| 
 | |
| 
 | |
|     def __init__(self,configdir=None, loglevel=None):
 | |
|         """
 | |
|         Initialises and starts a Reticulum instance. This must be
 | |
|         done before any other operations, and Reticulum will not
 | |
|         pass any traffic before being instantiated.
 | |
| 
 | |
|         :param configdir: Full path to a Reticulum configuration directory.
 | |
|         """
 | |
| 
 | |
|         RNS.vendor.platformutils.platform_checks()
 | |
| 
 | |
|         if configdir != None:
 | |
|             Reticulum.configdir = configdir
 | |
|         
 | |
|         Reticulum.configpath    = Reticulum.configdir+"/config"
 | |
|         Reticulum.storagepath   = Reticulum.configdir+"/storage"
 | |
|         Reticulum.cachepath     = Reticulum.configdir+"/storage/cache"
 | |
|         Reticulum.resourcepath  = Reticulum.configdir+"/storage/resources"
 | |
| 
 | |
|         Reticulum.__transport_enabled = False
 | |
|         Reticulum.__use_implicit_proof = True
 | |
| 
 | |
|         Reticulum.panic_on_interface_error = False
 | |
| 
 | |
|         self.local_interface_port = 37428
 | |
|         self.local_control_port   = 37429
 | |
|         self.share_instance       = True
 | |
|         self.rpc_listener         = None
 | |
| 
 | |
|         self.ifac_salt = Reticulum.IFAC_SALT
 | |
| 
 | |
|         self.requested_loglevel = loglevel
 | |
|         if self.requested_loglevel != None:
 | |
|             if self.requested_loglevel > RNS.LOG_EXTREME:
 | |
|                 self.requested_loglevel = RNS.LOG_EXTREME
 | |
|             if self.requested_loglevel < RNS.LOG_CRITICAL:
 | |
|                 self.requested_loglevel = RNS.LOG_CRITICAL
 | |
| 
 | |
|             RNS.loglevel = self.requested_loglevel
 | |
| 
 | |
|         self.is_shared_instance = False
 | |
|         self.is_connected_to_shared_instance = False
 | |
|         self.is_standalone_instance = False
 | |
| 
 | |
|         if not os.path.isdir(Reticulum.storagepath):
 | |
|             os.makedirs(Reticulum.storagepath)
 | |
| 
 | |
|         if not os.path.isdir(Reticulum.cachepath):
 | |
|             os.makedirs(Reticulum.cachepath)
 | |
| 
 | |
|         if not os.path.isdir(Reticulum.resourcepath):
 | |
|             os.makedirs(Reticulum.resourcepath)
 | |
| 
 | |
|         if os.path.isfile(self.configpath):
 | |
|             try:
 | |
|                 self.config = ConfigObj(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.__create_default_config()
 | |
|             RNS.log("Default config file created. Make any necessary changes in "+Reticulum.configdir+"/config and restart Reticulum if needed.")
 | |
|             import time
 | |
|             time.sleep(1.5)
 | |
| 
 | |
|         self.__apply_config()
 | |
|         RNS.log("Configuration loaded from "+self.configpath, RNS.LOG_VERBOSE)
 | |
|         
 | |
|         RNS.Identity.load_known_destinations()
 | |
| 
 | |
|         RNS.Transport.start(self)
 | |
| 
 | |
|         self.rpc_addr = ("127.0.0.1", self.local_control_port)
 | |
|         self.rpc_key  = RNS.Identity.full_hash(RNS.Transport.identity.get_private_key())
 | |
|         
 | |
|         if self.is_shared_instance:
 | |
|             self.rpc_listener = multiprocessing.connection.Listener(self.rpc_addr, authkey=self.rpc_key)
 | |
|             thread = threading.Thread(target=self.rpc_loop)
 | |
|             thread.setDaemon(True)
 | |
|             thread.start()
 | |
| 
 | |
|         atexit.register(Reticulum.exit_handler)
 | |
|         signal.signal(signal.SIGINT, Reticulum.sigint_handler)
 | |
|         signal.signal(signal.SIGTERM, Reticulum.sigterm_handler)
 | |
| 
 | |
|     def __start_local_interface(self):
 | |
|         if self.share_instance:
 | |
|             try:
 | |
|                 interface = LocalInterface.LocalServerInterface(
 | |
|                     RNS.Transport,
 | |
|                     self.local_interface_port
 | |
|                 )
 | |
|                 interface.OUT = True
 | |
|                 RNS.Transport.interfaces.append(interface)
 | |
|                 
 | |
|                 self.is_shared_instance = True
 | |
|                 RNS.log("Started shared instance interface: "+str(interface), RNS.LOG_DEBUG)
 | |
|             except Exception as e:
 | |
|                 try:
 | |
|                     interface = LocalInterface.LocalClientInterface(
 | |
|                         RNS.Transport,
 | |
|                         "Local shared instance",
 | |
|                         self.local_interface_port)
 | |
|                     interface.target_port = self.local_interface_port
 | |
|                     interface.OUT = True
 | |
|                     RNS.Transport.interfaces.append(interface)
 | |
|                     self.is_shared_instance = False
 | |
|                     self.is_standalone_instance = False
 | |
|                     self.is_connected_to_shared_instance = True
 | |
|                     Reticulum.__transport_enabled = False
 | |
|                     RNS.log("Connected to local shared instance via: "+str(interface), RNS.LOG_DEBUG)
 | |
|                 except Exception as e:
 | |
|                     RNS.log("Local shared instance appears to be running, but it could not be connected", RNS.LOG_ERROR)
 | |
|                     RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
 | |
|                     self.is_shared_instance = False
 | |
|                     self.is_standalone_instance = True
 | |
|                     self.is_connected_to_shared_instance = False
 | |
|         else:
 | |
|             self.is_shared_instance = False
 | |
|             self.is_standalone_instance = True
 | |
|             self.is_connected_to_shared_instance = False
 | |
| 
 | |
|     def __apply_config(self):
 | |
|         if "logging" in self.config:
 | |
|             for option in self.config["logging"]:
 | |
|                 value = self.config["logging"][option]
 | |
|                 if option == "loglevel" and self.requested_loglevel == None:
 | |
|                     RNS.loglevel = int(value)
 | |
|                     if RNS.loglevel < 0:
 | |
|                         RNS.loglevel = 0
 | |
|                     if RNS.loglevel > 7:
 | |
|                         RNS.loglevel = 7
 | |
| 
 | |
|         if "reticulum" in self.config:
 | |
|             for option in self.config["reticulum"]:
 | |
|                 value = self.config["reticulum"][option]
 | |
|                 if option == "share_instance":
 | |
|                     value = self.config["reticulum"].as_bool(option)
 | |
|                     self.share_instance = value
 | |
|                 if option == "shared_instance_port":
 | |
|                     value = int(self.config["reticulum"][option])
 | |
|                     self.local_interface_port = value
 | |
|                 if option == "instance_control_port":
 | |
|                     value = int(self.config["reticulum"][option])
 | |
|                     self.local_control_port = value
 | |
|                 if option == "enable_transport":
 | |
|                     v = self.config["reticulum"].as_bool(option)
 | |
|                     if v == True:
 | |
|                         Reticulum.__transport_enabled = True
 | |
|                 if option == "panic_on_interface_error":
 | |
|                     v = self.config["reticulum"].as_bool(option)
 | |
|                     if v == True:
 | |
|                         Reticulum.panic_on_interface_error = True
 | |
|                 if option == "use_implicit_proof":
 | |
|                     v = self.config["reticulum"].as_bool(option)
 | |
|                     if v == True:
 | |
|                         Reticulum.__use_implicit_proof = True
 | |
|                     if v == False:
 | |
|                         Reticulum.__use_implicit_proof = False
 | |
| 
 | |
|         self.__start_local_interface()
 | |
| 
 | |
|         if self.is_shared_instance or self.is_standalone_instance:
 | |
|             RNS.log("Bringing up system interfaces...", RNS.LOG_VERBOSE)
 | |
|             interface_names = []
 | |
|             for name in self.config["interfaces"]:
 | |
|                 if not name in interface_names:
 | |
|                     # TODO: We really need to generalise this way of instantiating
 | |
|                     # and configuring interfaces. Ideally, interfaces should just
 | |
|                     # have a conrfig dict passed to their init method, and return
 | |
|                     # a ready interface, onto which this routine can configure any
 | |
|                     # generic or extra parameters.
 | |
| 
 | |
|                     c = self.config["interfaces"][name]
 | |
| 
 | |
|                     interface_mode = Interface.Interface.MODE_FULL
 | |
|                     
 | |
|                     if "interface_mode" in c:
 | |
|                         c["interface_mode"] = str(c["interface_mode"]).lower()
 | |
|                         if c["interface_mode"] == "full":
 | |
|                             interface_mode = Interface.Interface.MODE_FULL
 | |
|                         elif c["interface_mode"] == "access_point" or c["interface_mode"] == "accesspoint" or c["interface_mode"] == "ap":
 | |
|                             interface_mode = Interface.Interface.MODE_ACCESS_POINT
 | |
|                         elif c["interface_mode"] == "pointtopoint" or c["interface_mode"] == "ptp":
 | |
|                             interface_mode = Interface.Interface.MODE_POINT_TO_POINT
 | |
|                         elif c["interface_mode"] == "roaming":
 | |
|                             interface_mode = Interface.Interface.MODE_ROAMING
 | |
|                         elif c["interface_mode"] == "boundary":
 | |
|                             interface_mode = Interface.Interface.MODE_BOUNDARY
 | |
| 
 | |
|                     elif "mode" in c:
 | |
|                         c["mode"] = str(c["mode"]).lower()
 | |
|                         if c["mode"] == "full":
 | |
|                             interface_mode = Interface.Interface.MODE_FULL
 | |
|                         elif c["mode"] == "access_point" or c["mode"] == "accesspoint" or c["mode"] == "ap":
 | |
|                             interface_mode = Interface.Interface.MODE_ACCESS_POINT
 | |
|                         elif c["mode"] == "pointtopoint" or c["mode"] == "ptp":
 | |
|                             interface_mode = Interface.Interface.MODE_POINT_TO_POINT
 | |
|                         elif c["mode"] == "roaming":
 | |
|                             interface_mode = Interface.Interface.MODE_ROAMING
 | |
|                         elif c["mode"] == "boundary":
 | |
|                             interface_mode = Interface.Interface.MODE_BOUNDARY
 | |
| 
 | |
|                     ifac_size = None
 | |
|                     if "ifac_size" in c:
 | |
|                         if c.as_int("ifac_size") >= Reticulum.IFAC_MIN_SIZE*8:
 | |
|                             ifac_size = c.as_int("ifac_size")//8
 | |
|                             
 | |
|                     ifac_netname = None
 | |
|                     if "networkname" in c:
 | |
|                         if c["networkname"] != "":
 | |
|                             ifac_netname = c["networkname"]
 | |
|                     if "network_name" in c:
 | |
|                         if c["network_name"] != "":
 | |
|                             ifac_netname = c["network_name"]
 | |
| 
 | |
|                     ifac_netkey = None
 | |
|                     if "passphrase" in c:
 | |
|                         if c["passphrase"] != "":
 | |
|                             ifac_netkey = c["passphrase"]
 | |
|                     if "pass_phrase" in c:
 | |
|                         if c["pass_phrase"] != "":
 | |
|                             ifac_netkey = c["pass_phrase"]
 | |
|                             
 | |
|                     configured_bitrate = None
 | |
|                     if "bitrate" in c:
 | |
|                         if c.as_int("bitrate") >= Reticulum.MINIMUM_BITRATE:
 | |
|                             configured_bitrate = c.as_int("bitrate")
 | |
| 
 | |
|                     announce_rate_target = None
 | |
|                     if "announce_rate_target" in c:
 | |
|                         if c.as_int("announce_rate_target") > 0:
 | |
|                             announce_rate_target = c.as_int("announce_rate_target")
 | |
|                             
 | |
|                     announce_rate_grace = None
 | |
|                     if "announce_rate_grace" in c:
 | |
|                         if c.as_int("announce_rate_grace") >= 0:
 | |
|                             announce_rate_grace = c.as_int("announce_rate_grace")
 | |
|                             
 | |
|                     announce_rate_penalty = None
 | |
|                     if "announce_rate_penalty" in c:
 | |
|                         if c.as_int("announce_rate_penalty") >= 0:
 | |
|                             announce_rate_penalty = c.as_int("announce_rate_penalty")
 | |
| 
 | |
|                     if announce_rate_target != None and announce_rate_grace == None:
 | |
|                         announce_rate_grace = 0
 | |
| 
 | |
|                     if announce_rate_target != None and announce_rate_penalty == None:
 | |
|                         announce_rate_penalty = 0
 | |
|                             
 | |
|                     announce_cap = Reticulum.ANNOUNCE_CAP/100.0
 | |
|                     if "announce_cap" in c:
 | |
|                         if c.as_float("announce_cap") > 0 and c.as_float("announce_cap") <= 100:
 | |
|                             announce_cap = c.as_float("announce_cap")/100.0
 | |
|                             
 | |
|                     try:
 | |
|                         interface = None
 | |
| 
 | |
|                         if (("interface_enabled" in c) and c.as_bool("interface_enabled") == True) or (("enabled" in c) and c.as_bool("enabled") == True):
 | |
|                             if c["type"] == "AutoInterface":
 | |
|                                 if not RNS.vendor.platformutils.is_windows():
 | |
|                                     group_id        = c["group_id"] if "group_id" in c else None
 | |
|                                     discovery_scope = c["discovery_scope"] if "discovery_scope" in c else None
 | |
|                                     discovery_port  = int(c["discovery_port"]) if "discovery_port" in c else None
 | |
|                                     data_port  = int(c["data_port"]) if "data_port" in c else None
 | |
|                                     allowed_interfaces = c.as_list("devices") if "devices" in c else None
 | |
|                                     ignored_interfaces = c.as_list("ignored_devices") if "ignored_devices" in c else None
 | |
| 
 | |
|                                     interface = AutoInterface.AutoInterface(
 | |
|                                         RNS.Transport,
 | |
|                                         name,
 | |
|                                         group_id,
 | |
|                                         discovery_scope,
 | |
|                                         discovery_port,
 | |
|                                         data_port,
 | |
|                                         allowed_interfaces,
 | |
|                                         ignored_interfaces
 | |
|                                     )
 | |
| 
 | |
|                                     if "outgoing" in c and c.as_bool("outgoing") == False:
 | |
|                                         interface.OUT = False
 | |
|                                     else:
 | |
|                                         interface.OUT = True
 | |
| 
 | |
|                                     interface.mode = interface_mode
 | |
| 
 | |
|                                     interface.announce_cap = announce_cap
 | |
|                                     if configured_bitrate:
 | |
|                                         interface.bitrate = configured_bitrate
 | |
|                                     if ifac_size != None:
 | |
|                                         interface.ifac_size = ifac_size
 | |
|                                     else:
 | |
|                                         interface.ifac_size = 16
 | |
| 
 | |
|                                 else:
 | |
|                                     RNS.log("AutoInterface is not currently supported on Windows, disabling interface.", RNS.LOG_ERROR);
 | |
|                                     RNS.log("Please remove this AutoInterface instance from your configuration file.", RNS.LOG_ERROR);
 | |
|                                     RNS.log("You will have to manually configure other interfaces for connectivity.", RNS.LOG_ERROR);
 | |
| 
 | |
|                             if c["type"] == "UDPInterface":
 | |
|                                 device       = c["device"] if "device" in c else None
 | |
|                                 port         = int(c["port"]) if "port" in c else None
 | |
|                                 listen_ip    = c["listen_ip"] if "listen_ip" in c else None
 | |
|                                 listen_port  = int(c["listen_port"]) if "listen_port" in c else None
 | |
|                                 forward_ip   = c["forward_ip"] if "forward_ip" in c else None
 | |
|                                 forward_port = int(c["forward_port"]) if "forward_port" in c else None
 | |
| 
 | |
|                                 if port != None:
 | |
|                                     if listen_port == None:
 | |
|                                         listen_port = port
 | |
|                                     if forward_port == None:
 | |
|                                         forward_port = port
 | |
| 
 | |
|                                 interface = UDPInterface.UDPInterface(
 | |
|                                     RNS.Transport,
 | |
|                                     name,
 | |
|                                     device,
 | |
|                                     listen_ip,
 | |
|                                     listen_port,
 | |
|                                     forward_ip,
 | |
|                                     forward_port
 | |
|                                 )
 | |
| 
 | |
|                                 if "outgoing" in c and c.as_bool("outgoing") == False:
 | |
|                                     interface.OUT = False
 | |
|                                 else:
 | |
|                                     interface.OUT = True
 | |
| 
 | |
|                                 interface.mode = interface_mode
 | |
| 
 | |
|                                 interface.announce_cap = announce_cap
 | |
|                                 if configured_bitrate:
 | |
|                                     interface.bitrate = configured_bitrate
 | |
|                                 if ifac_size != None:
 | |
|                                     interface.ifac_size = ifac_size
 | |
|                                 else:
 | |
|                                     interface.ifac_size = 16
 | |
| 
 | |
|                             if c["type"] == "TCPServerInterface":
 | |
|                                 device       = c["device"] if "device" in c else None
 | |
|                                 port         = int(c["port"]) if "port" in c else None
 | |
|                                 listen_ip    = c["listen_ip"] if "listen_ip" in c else None
 | |
|                                 listen_port  = int(c["listen_port"]) if "listen_port" in c else None
 | |
|                                 i2p_tunneled = c.as_bool("i2p_tunneled") if "i2p_tunneled" in c else False
 | |
| 
 | |
|                                 if port != None:
 | |
|                                     listen_port = port
 | |
| 
 | |
|                                 interface = TCPInterface.TCPServerInterface(
 | |
|                                     RNS.Transport,
 | |
|                                     name,
 | |
|                                     device,
 | |
|                                     listen_ip,
 | |
|                                     listen_port,
 | |
|                                     i2p_tunneled
 | |
|                                 )
 | |
| 
 | |
|                                 if "outgoing" in c and c.as_bool("outgoing") == False:
 | |
|                                     interface.OUT = False
 | |
|                                 else:
 | |
|                                     interface.OUT = True
 | |
| 
 | |
|                                 if interface_mode != Interface.Interface.MODE_FULL:
 | |
|                                     RNS.log(str(interface)+" does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING)
 | |
|                                     interface_mode = Interface.Interface.MODE_FULL
 | |
|                                 
 | |
|                                 interface.mode = interface_mode
 | |
| 
 | |
|                                 interface.announce_cap = announce_cap
 | |
|                                 if configured_bitrate:
 | |
|                                     interface.bitrate = configured_bitrate
 | |
|                                 if ifac_size != None:
 | |
|                                     interface.ifac_size = ifac_size
 | |
|                                 else:
 | |
|                                     interface.ifac_size = 16
 | |
| 
 | |
|                             if c["type"] == "TCPClientInterface":
 | |
|                                 kiss_framing = False
 | |
|                                 if "kiss_framing" in c and c.as_bool("kiss_framing") == True:
 | |
|                                     kiss_framing = True
 | |
|                                 i2p_tunneled = c.as_bool("i2p_tunneled") if "i2p_tunneled" in c else False
 | |
| 
 | |
| 
 | |
|                                 interface = TCPInterface.TCPClientInterface(
 | |
|                                     RNS.Transport,
 | |
|                                     name,
 | |
|                                     c["target_host"],
 | |
|                                     int(c["target_port"]),
 | |
|                                     kiss_framing = kiss_framing,
 | |
|                                     i2p_tunneled = i2p_tunneled
 | |
|                                 )
 | |
| 
 | |
|                                 if "outgoing" in c and c.as_bool("outgoing") == False:
 | |
|                                     interface.OUT = False
 | |
|                                 else:
 | |
|                                     interface.OUT = True
 | |
| 
 | |
|                                 if interface_mode != Interface.Interface.MODE_FULL:
 | |
|                                     RNS.log(str(interface)+" does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING)
 | |
|                                     interface_mode = Interface.Interface.MODE_FULL
 | |
|                                 
 | |
|                                 interface.mode = interface_mode
 | |
| 
 | |
|                                 interface.announce_cap = announce_cap
 | |
|                                 if configured_bitrate:
 | |
|                                     interface.bitrate = configured_bitrate
 | |
|                                 if ifac_size != None:
 | |
|                                     interface.ifac_size = ifac_size
 | |
|                                 else:
 | |
|                                     interface.ifac_size = 16
 | |
| 
 | |
|                             if c["type"] == "I2PInterface":
 | |
|                                 i2p_peers = c.as_list("peers") if "peers" in c else None
 | |
|                                 connectable = c.as_bool("connectable") if "connectable" in c else False
 | |
| 
 | |
|                                 interface = I2PInterface.I2PInterface(
 | |
|                                     RNS.Transport,
 | |
|                                     name,
 | |
|                                     Reticulum.storagepath,
 | |
|                                     i2p_peers,
 | |
|                                     connectable = connectable,
 | |
|                                 )
 | |
| 
 | |
|                                 if "outgoing" in c and c.as_bool("outgoing") == False:
 | |
|                                     interface.OUT = False
 | |
|                                 else:
 | |
|                                     interface.OUT = True
 | |
| 
 | |
|                                 if interface_mode != Interface.Interface.MODE_FULL:
 | |
|                                     RNS.log(str(interface)+" does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING)
 | |
|                                     interface_mode = Interface.Interface.MODE_FULL
 | |
|                                 
 | |
|                                 interface.mode = interface_mode
 | |
| 
 | |
|                                 interface.announce_cap = announce_cap
 | |
|                                 if configured_bitrate:
 | |
|                                     interface.bitrate = configured_bitrate
 | |
|                                 if ifac_size != None:
 | |
|                                     interface.ifac_size = ifac_size
 | |
|                                 else:
 | |
|                                     interface.ifac_size = 16
 | |
| 
 | |
|                             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")
 | |
| 
 | |
|                                 interface = SerialInterface.SerialInterface(
 | |
|                                     RNS.Transport,
 | |
|                                     name,
 | |
|                                     port,
 | |
|                                     speed,
 | |
|                                     databits,
 | |
|                                     parity,
 | |
|                                     stopbits
 | |
|                                 )
 | |
| 
 | |
|                                 if "outgoing" in c and c.as_bool("outgoing") == False:
 | |
|                                     interface.OUT = False
 | |
|                                 else:
 | |
|                                     interface.OUT = True
 | |
| 
 | |
|                                 interface.mode = interface_mode
 | |
| 
 | |
|                                 interface.announce_cap = announce_cap
 | |
|                                 if configured_bitrate:
 | |
|                                     interface.bitrate = configured_bitrate
 | |
|                                 if ifac_size != None:
 | |
|                                     interface.ifac_size = ifac_size
 | |
|                                 else:
 | |
|                                     interface.ifac_size = 8
 | |
| 
 | |
|                             if c["type"] == "PipeInterface":
 | |
|                                 command = c["command"] if "command" in c else None
 | |
|                                 respawn_delay = c.as_float("respawn_delay") if "respawn_delay" in c else None
 | |
| 
 | |
|                                 if command == None:
 | |
|                                     raise ValueError("No command specified for PipeInterface")
 | |
| 
 | |
|                                 interface = PipeInterface.PipeInterface(
 | |
|                                     RNS.Transport,
 | |
|                                     name,
 | |
|                                     command,
 | |
|                                     respawn_delay,
 | |
|                                 )
 | |
| 
 | |
|                                 if "outgoing" in c and c.as_bool("outgoing") == False:
 | |
|                                     interface.OUT = False
 | |
|                                 else:
 | |
|                                     interface.OUT = True
 | |
| 
 | |
|                                 interface.mode = interface_mode
 | |
| 
 | |
|                                 interface.announce_cap = announce_cap
 | |
|                                 if configured_bitrate:
 | |
|                                     interface.bitrate = configured_bitrate
 | |
|                                 if ifac_size != None:
 | |
|                                     interface.ifac_size = ifac_size
 | |
|                                 else:
 | |
|                                     interface.ifac_size = 8
 | |
| 
 | |
|                             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 = c.as_bool("flow_control") 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
 | |
|                                 beacon_interval = int(c["id_interval"]) if "id_interval" in c else None
 | |
|                                 beacon_data = c["id_callsign"] if "id_callsign" in c else None
 | |
| 
 | |
|                                 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,
 | |
|                                     beacon_interval,
 | |
|                                     beacon_data
 | |
|                                 )
 | |
| 
 | |
|                                 if "outgoing" in c and c.as_bool("outgoing") == False:
 | |
|                                     interface.OUT = False
 | |
|                                 else:
 | |
|                                     interface.OUT = True
 | |
| 
 | |
|                                 interface.mode = interface_mode
 | |
| 
 | |
|                                 interface.announce_cap = announce_cap
 | |
|                                 if configured_bitrate:
 | |
|                                     interface.bitrate = configured_bitrate
 | |
|                                 if ifac_size != None:
 | |
|                                     interface.ifac_size = ifac_size
 | |
|                                 else:
 | |
|                                     interface.ifac_size = 8
 | |
| 
 | |
|                             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 = c.as_bool("flow_control") 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
 | |
| 
 | |
|                                 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")
 | |
| 
 | |
|                                 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.as_bool("outgoing") == False:
 | |
|                                     interface.OUT = False
 | |
|                                 else:
 | |
|                                     interface.OUT = True
 | |
| 
 | |
|                                 interface.mode = interface_mode
 | |
| 
 | |
|                                 interface.announce_cap = announce_cap
 | |
|                                 if configured_bitrate:
 | |
|                                     interface.bitrate = configured_bitrate
 | |
|                                 if ifac_size != None:
 | |
|                                     interface.ifac_size = ifac_size
 | |
|                                 else:
 | |
|                                     interface.ifac_size = 8
 | |
| 
 | |
|                             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 = c.as_bool("flow_control") if "flow_control" in c else False
 | |
|                                 id_interval = int(c["id_interval"]) if "id_interval" in c else None
 | |
|                                 id_callsign = c["id_callsign"] if "id_callsign" in c else None
 | |
| 
 | |
|                                 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 = frequency,
 | |
|                                     bandwidth = bandwidth,
 | |
|                                     txpower = txpower,
 | |
|                                     sf = spreadingfactor,
 | |
|                                     cr = codingrate,
 | |
|                                     flow_control = flow_control,
 | |
|                                     id_interval = id_interval,
 | |
|                                     id_callsign = id_callsign
 | |
|                                 )
 | |
| 
 | |
|                                 if "outgoing" in c and c.as_bool("outgoing") == False:
 | |
|                                     interface.OUT = False
 | |
|                                 else:
 | |
|                                     interface.OUT = True
 | |
| 
 | |
|                                 interface.mode = interface_mode
 | |
| 
 | |
|                                 interface.announce_cap = announce_cap
 | |
|                                 if configured_bitrate:
 | |
|                                     interface.bitrate = configured_bitrate
 | |
|                                 if ifac_size != None:
 | |
|                                     interface.ifac_size = ifac_size
 | |
|                                 else:
 | |
|                                     interface.ifac_size = 8
 | |
| 
 | |
|                             if interface != None:
 | |
|                                 interface.announce_rate_target = announce_rate_target
 | |
|                                 interface.announce_rate_grace = announce_rate_grace
 | |
|                                 interface.announce_rate_penalty = announce_rate_penalty
 | |
| 
 | |
|                                 interface.ifac_netname = ifac_netname
 | |
|                                 interface.ifac_netkey = ifac_netkey
 | |
| 
 | |
|                                 if interface.ifac_netname != None or interface.ifac_netkey != None:
 | |
|                                     ifac_origin = b""
 | |
| 
 | |
|                                     if interface.ifac_netname != None:
 | |
|                                         ifac_origin += RNS.Identity.full_hash(interface.ifac_netname.encode("utf-8"))
 | |
| 
 | |
|                                     if interface.ifac_netkey != None:
 | |
|                                         ifac_origin += RNS.Identity.full_hash(interface.ifac_netkey.encode("utf-8"))
 | |
| 
 | |
|                                     ifac_origin_hash = RNS.Identity.full_hash(ifac_origin)
 | |
|                                     interface.ifac_key = HKDF(
 | |
|                                         algorithm=hashes.SHA256(),
 | |
|                                         length=64,
 | |
|                                         salt=self.ifac_salt,
 | |
|                                         info=None,
 | |
|                                         backend=cio_default_backend,
 | |
|                                     ).derive(ifac_origin_hash)
 | |
| 
 | |
|                                     interface.ifac_identity = RNS.Identity.from_bytes(interface.ifac_key)
 | |
|                                     interface.ifac_signature = interface.ifac_identity.sign(RNS.Identity.full_hash(interface.ifac_key))
 | |
| 
 | |
|                                 RNS.Transport.interfaces.append(interface)
 | |
| 
 | |
|                         else:
 | |
|                             RNS.log("Skipping disabled interface \""+name+"\"", RNS.LOG_DEBUG)
 | |
| 
 | |
|                     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()
 | |
| 
 | |
|             RNS.log("System interfaces are ready", RNS.LOG_VERBOSE)
 | |
| 
 | |
|                 
 | |
| 
 | |
|     def __create_default_config(self):
 | |
|         self.config = ConfigObj(__default_rns_config__)
 | |
|         self.config.filename = Reticulum.configpath
 | |
|         
 | |
|         if not os.path.isdir(Reticulum.configdir):
 | |
|             os.makedirs(Reticulum.configdir)
 | |
|         self.config.write()
 | |
| 
 | |
|     def rpc_loop(self):
 | |
|         while True:
 | |
|             try:
 | |
|                 rpc_connection = self.rpc_listener.accept()
 | |
|                 call = rpc_connection.recv()
 | |
| 
 | |
|                 if "get" in call:
 | |
|                     path = call["get"]
 | |
| 
 | |
|                     if path == "interface_stats":
 | |
|                         rpc_connection.send(self.get_interface_stats())
 | |
| 
 | |
|                     if path == "path_table":
 | |
|                         rpc_connection.send(self.get_path_table())
 | |
| 
 | |
|                     if path == "next_hop_if_name":
 | |
|                         rpc_connection.send(self.get_next_hop_if_name(call["destination_hash"]))
 | |
| 
 | |
|                     if path == "next_hop":
 | |
|                         rpc_connection.send(self.get_next_hop(call["destination_hash"]))
 | |
| 
 | |
|                     if path == "packet_rssi":
 | |
|                         rpc_connection.send(self.get_packet_rssi(call["packet_hash"]))
 | |
| 
 | |
|                     if path == "packet_snr":
 | |
|                         rpc_connection.send(self.get_packet_snr(call["packet_hash"]))
 | |
| 
 | |
|                 if "drop" in call:
 | |
|                     path = call["drop"]
 | |
| 
 | |
|                     if path == "path":
 | |
|                         rpc_connection.send(self.drop_path(call["destination_hash"]))
 | |
| 
 | |
|                     if path == "announce_queues":
 | |
|                         rpc_connection.send(self.drop_announce_queues())
 | |
| 
 | |
|                 rpc_connection.close()
 | |
| 
 | |
|             except Exception as e:
 | |
|                 RNS.log("An error ocurred while handling RPC call from local client: "+str(e), RNS.LOG_ERROR)
 | |
| 
 | |
|     def get_interface_stats(self):
 | |
|         if self.is_connected_to_shared_instance:
 | |
|             rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
 | |
|             rpc_connection.send({"get": "interface_stats"})
 | |
|             response = rpc_connection.recv()
 | |
|             return response
 | |
|         else:
 | |
|             interfaces = []
 | |
|             for interface in RNS.Transport.interfaces:
 | |
|                 ifstats = {}
 | |
|                 
 | |
|                 if hasattr(interface, "clients"):
 | |
|                     ifstats["clients"] = interface.clients
 | |
|                 else:
 | |
|                     ifstats["clients"] = None
 | |
| 
 | |
|                 if hasattr(interface, "b32"):
 | |
|                     if interface.b32 != None:
 | |
|                         ifstats["i2p_b32"] = interface.b32+".b32.i2p"
 | |
|                     else:
 | |
|                         ifstats["i2p_b32"] = None
 | |
| 
 | |
|                 if hasattr(interface, "bitrate"):
 | |
|                     if interface.bitrate != None:
 | |
|                         ifstats["bitrate"] = interface.bitrate
 | |
|                     else:
 | |
|                         ifstats["bitrate"] = None
 | |
| 
 | |
|                 if hasattr(interface, "peers"):
 | |
|                     if interface.peers != None:
 | |
|                         ifstats["peers"] = len(interface.peers)
 | |
|                     else:
 | |
|                         ifstats["peers"] = None
 | |
| 
 | |
|                 if hasattr(interface, "ifac_signature"):
 | |
|                     ifstats["ifac_signature"] = interface.ifac_signature
 | |
|                     ifstats["ifac_size"] = interface.ifac_size
 | |
|                     ifstats["ifac_netname"] = interface.ifac_netname
 | |
|                 else:
 | |
|                     ifstats["ifac_signature"] = None
 | |
|                     ifstats["ifac_size"] = None
 | |
|                     ifstats["ifac_netname"] = None
 | |
| 
 | |
|                 if hasattr(interface, "announce_queue"):
 | |
|                     if interface.announce_queue != None:
 | |
|                         ifstats["announce_queue"] = len(interface.announce_queue)
 | |
|                     else:
 | |
|                         ifstats["announce_queue"] = None
 | |
| 
 | |
|                 ifstats["name"] = str(interface)
 | |
|                 ifstats["rxb"] = interface.rxb
 | |
|                 ifstats["txb"] = interface.txb
 | |
|                 ifstats["status"] = interface.online
 | |
|                 ifstats["mode"] = interface.mode
 | |
| 
 | |
|                 interfaces.append(ifstats)
 | |
| 
 | |
|             stats = {}
 | |
|             stats["interfaces"] = interfaces
 | |
|             if Reticulum.transport_enabled():
 | |
|                 stats["transport_id"] = RNS.Transport.identity.hash
 | |
| 
 | |
|             return stats
 | |
| 
 | |
|     def get_path_table(self):
 | |
|         if self.is_connected_to_shared_instance:
 | |
|             rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
 | |
|             rpc_connection.send({"get": "path_table"})
 | |
|             response = rpc_connection.recv()
 | |
|             return response
 | |
| 
 | |
|         else:
 | |
|             path_table = []
 | |
|             for dst_hash in RNS.Transport.destination_table:
 | |
|                 entry = {
 | |
|                     "hash": dst_hash,
 | |
|                     "timestamp": RNS.Transport.destination_table[dst_hash][0],
 | |
|                     "via": RNS.Transport.destination_table[dst_hash][1],
 | |
|                     "hops": RNS.Transport.destination_table[dst_hash][2],
 | |
|                     "expires": RNS.Transport.destination_table[dst_hash][3],
 | |
|                     "interface": str(RNS.Transport.destination_table[dst_hash][5]),
 | |
|                 }
 | |
|                 path_table.append(entry)
 | |
| 
 | |
|             return path_table
 | |
| 
 | |
|     def drop_path(self, destination):
 | |
|         if self.is_connected_to_shared_instance:
 | |
|             rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
 | |
|             rpc_connection.send({"drop": "path", "destination_hash": destination})
 | |
|             response = rpc_connection.recv()
 | |
|             return response
 | |
| 
 | |
|         else:
 | |
|             return RNS.Transport.expire_path(destination)
 | |
| 
 | |
|     def drop_announce_queues(self):
 | |
|         if self.is_connected_to_shared_instance:
 | |
|             rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
 | |
|             rpc_connection.send({"drop": "announce_queues"})
 | |
|             response = rpc_connection.recv()
 | |
|             return response
 | |
| 
 | |
|         else:
 | |
|             return RNS.Transport.drop_announce_queues()
 | |
| 
 | |
|     def get_next_hop_if_name(self, destination):
 | |
|         if self.is_connected_to_shared_instance:
 | |
|             rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
 | |
|             rpc_connection.send({"get": "next_hop_if_name", "destination_hash": destination})
 | |
|             response = rpc_connection.recv()
 | |
|             return response
 | |
| 
 | |
|         else:
 | |
|             return str(RNS.Transport.next_hop_interface(destination))
 | |
| 
 | |
|     def get_next_hop(self, destination):
 | |
|         if self.is_connected_to_shared_instance:
 | |
|             rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
 | |
|             rpc_connection.send({"get": "next_hop", "destination_hash": destination})
 | |
|             response = rpc_connection.recv()
 | |
|             return response
 | |
| 
 | |
|         else:
 | |
|             return RNS.Transport.next_hop(destination)
 | |
| 
 | |
|     def get_packet_rssi(self, packet_hash):
 | |
|         if self.is_connected_to_shared_instance:
 | |
|             rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
 | |
|             rpc_connection.send({"get": "packet_rssi", "packet_hash": packet_hash})
 | |
|             response = rpc_connection.recv()
 | |
|             return response
 | |
| 
 | |
|         else:
 | |
|             for entry in RNS.Transport.local_client_rssi_cache:
 | |
|                 if entry[0] == packet_hash:
 | |
|                     return entry[1]
 | |
| 
 | |
|             return None
 | |
| 
 | |
|     def get_packet_snr(self, packet_hash):
 | |
|         if self.is_connected_to_shared_instance:
 | |
|             rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key)
 | |
|             rpc_connection.send({"get": "packet_snr", "packet_hash": packet_hash})
 | |
|             response = rpc_connection.recv()
 | |
|             return response
 | |
| 
 | |
|         else:
 | |
|             for entry in RNS.Transport.local_client_snr_cache:
 | |
|                 if entry[0] == packet_hash:
 | |
|                     return entry[1]
 | |
| 
 | |
|             return None
 | |
| 
 | |
| 
 | |
|     @staticmethod
 | |
|     def should_use_implicit_proof():
 | |
|         """
 | |
|         Returns whether proofs sent are explicit or implicit.
 | |
| 
 | |
|         :returns: True if the current running configuration specifies to use implicit proofs. False if not.
 | |
|         """
 | |
|         return Reticulum.__use_implicit_proof
 | |
| 
 | |
|     @staticmethod
 | |
|     def transport_enabled():
 | |
|         """
 | |
|         Returns whether Transport is enabled for the running
 | |
|         instance.
 | |
| 
 | |
|         When Transport is enabled, Reticulum will
 | |
|         route traffic for other peers, respond to path requests
 | |
|         and pass announces over the network.
 | |
| 
 | |
|         :returns: True if Transport is enabled, False if not.
 | |
|         """
 | |
|         return Reticulum.__transport_enabled
 | |
| 
 | |
| # Default configuration file:
 | |
| __default_rns_config__ = '''# This is the default Reticulum config file.
 | |
| # You should probably edit it to include any additional,
 | |
| # interfaces and settings you might need.
 | |
| 
 | |
| # Only the most basic options are included in this default
 | |
| # configuration. To see a more verbose, and much longer,
 | |
| # configuration example, you can run the command:
 | |
| # rnsd --exampleconfig
 | |
| 
 | |
| 
 | |
| [reticulum]
 | |
| 
 | |
| # If you enable Transport, your system will route traffic
 | |
| # for other peers, pass announces and serve path requests.
 | |
| # This should only be done for systems that are suited to
 | |
| # act as transport nodes, ie. if they are stationary and
 | |
| # always-on. This directive is optional and can be removed
 | |
| # for brevity.
 | |
| 
 | |
| enable_transport = False
 | |
| 
 | |
| 
 | |
| # By default, the first program to launch the Reticulum
 | |
| # Network Stack will create a shared instance, that other
 | |
| # programs can communicate with. Only the shared instance
 | |
| # opens all the configured interfaces directly, and other
 | |
| # local programs communicate with the shared instance over
 | |
| # a local socket. This is completely transparent to the
 | |
| # user, and should generally be turned on. This directive
 | |
| # is optional and can be removed for brevity.
 | |
| 
 | |
| share_instance = Yes
 | |
| 
 | |
| 
 | |
| # If you want to run multiple *different* shared instances
 | |
| # on the same system, you will need to specify different
 | |
| # shared instance ports for each. The defaults are given
 | |
| # below, and again, these options can be left out if you
 | |
| # don't need them.
 | |
| 
 | |
| shared_instance_port = 37428
 | |
| instance_control_port = 37429
 | |
| 
 | |
| 
 | |
| # You can configure Reticulum to panic and forcibly close
 | |
| # if an unrecoverable interface error occurs, such as the
 | |
| # hardware device for an interface disappearing. This is
 | |
| # an optional directive, and can be left out for brevity.
 | |
| # This behaviour is disabled by default.
 | |
| 
 | |
| panic_on_interface_error = No
 | |
| 
 | |
| 
 | |
| [logging]
 | |
| # Valid log levels are 0 through 7:
 | |
| #   0: Log only critical information
 | |
| #   1: Log errors and lower log levels
 | |
| #   2: Log warnings and lower log levels
 | |
| #   3: Log notices and lower log levels
 | |
| #   4: Log info and lower (this is the default)
 | |
| #   5: Verbose logging
 | |
| #   6: Debug logging
 | |
| #   7: Extreme logging
 | |
| 
 | |
| loglevel = 4
 | |
| 
 | |
| 
 | |
| # The interfaces section defines the physical and virtual
 | |
| # interfaces Reticulum will use to communicate on. This
 | |
| # section will contain examples for a variety of interface
 | |
| # types. You can modify these or use them as a basis for
 | |
| # your own config, or simply remove the unused ones.
 | |
| 
 | |
| [interfaces]
 | |
| 
 | |
|   # This interface enables communication with other
 | |
|   # link-local Reticulum nodes over UDP. It does not
 | |
|   # need any functional IP infrastructure like routers
 | |
|   # or DHCP servers, but will require that at least link-
 | |
|   # local IPv6 is enabled in your operating system, which
 | |
|   # should be enabled by default in almost any OS. See
 | |
|   # the Reticulum Manual for more configuration options.
 | |
| 
 | |
|   [[Default Interface]]
 | |
|     type = AutoInterface
 | |
|     interface_enabled = True
 | |
| 
 | |
| '''.splitlines()
 |