예제 #1
0
    def __watchdog_job(self):
        while not self.status == Link.CLOSED:
            while (self.watchdog_lock):
                sleep(max(self.rtt, 0.025))

            if not self.status == Link.CLOSED:
                # Link was initiated, but no response
                # from destination yet
                if self.status == Link.PENDING:
                    next_check = self.request_time + self.proof_timeout
                    sleep_time = next_check - time.time()
                    if time.time() >= self.request_time + self.proof_timeout:
                        RNS.log("Link establishment timed out",
                                RNS.LOG_VERBOSE)
                        self.status = Link.CLOSED
                        self.teardown_reason = Link.TIMEOUT
                        self.link_closed()
                        sleep_time = 0.001

                elif self.status == Link.HANDSHAKE:
                    next_check = self.request_time + self.proof_timeout
                    sleep_time = next_check - time.time()
                    if time.time() >= self.request_time + self.proof_timeout:
                        RNS.log(
                            "Timeout waiting for RTT packet from link initiator",
                            RNS.LOG_DEBUG)
                        self.status = Link.CLOSED
                        self.teardown_reason = Link.TIMEOUT
                        self.link_closed()
                        sleep_time = 0.001

                elif self.status == Link.ACTIVE:
                    if time.time() >= self.last_inbound + self.keepalive:
                        sleep_time = self.rtt * self.timeout_factor
                        self.status = Link.STALE
                        if self.initiator:
                            self.send_keepalive()
                    else:
                        sleep_time = (self.last_inbound +
                                      self.keepalive) - time.time()

                elif self.status == Link.STALE:
                    sleep_time = 0.001
                    self.status = Link.CLOSED
                    self.teardown_reason = Link.TIMEOUT
                    self.link_closed()

                if sleep_time == 0:
                    RNS.log("Warning! Link watchdog sleep time of 0!",
                            RNS.LOG_ERROR)
                if sleep_time == None or sleep_time < 0:
                    RNS.log("Timing error! Closing Reticulum now.",
                            RNS.LOG_CRITICAL)
                    RNS.panic()

                sleep(sleep_time)
예제 #2
0
 def disableEncryption(self):
     if (RNS.Reticulum.should_allow_unencrypted()):
         RNS.log(
             "The link " + str(self) +
             " was downgraded to an encryptionless link", RNS.LOG_NOTICE)
         self.__encryption_disabled = True
     else:
         RNS.log(
             "Attempt to disable encryption on link, but encryptionless links are not allowed by config.",
             RNS.LOG_CRITICAL)
         RNS.log("Shutting down Reticulum now!", RNS.LOG_CRITICAL)
         RNS.panic()
예제 #3
0
    def __init__(self,configdir=None):
        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.__allow_unencrypted = False
        Reticulum.__transport_enabled = False
        Reticulum.__use_implicit_proof = True

        self.local_interface_port = 37428
        self.share_instance = True

        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)
                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()
            RNS.log("Default config file created. Make any necessary changes in "+Reticulum.configdir+"/config and start Reticulum again.")
            RNS.log("Exiting now!")
            exit(1)

        self.applyConfig()
        RNS.Identity.loadKnownDestinations()

        RNS.Transport.start(self)

        atexit.register(Reticulum.exit_handler)
예제 #4
0
    def disable_encryption(self):
        """
        HAZARDOUS. This will downgrade the link to encryptionless. All
        information over the link will be sent in plaintext. Never use
        this in production applications. Should only be used for debugging
        purposes, and will disappear in a future version.

        If encryptionless links are not explicitly allowed in the users
        configuration file, Reticulum will terminate itself along with the
        client application and throw an error message to the user.
        """
        if (RNS.Reticulum.should_allow_unencrypted()):
            RNS.log(
                "The link " + str(self) +
                " was downgraded to an encryptionless link", RNS.LOG_NOTICE)
            self.__encryption_disabled = True
        else:
            RNS.log(
                "Attempt to disable encryption on link, but encryptionless links are not allowed by config.",
                RNS.LOG_CRITICAL)
            RNS.log("Shutting down Reticulum now!", RNS.LOG_CRITICAL)
            RNS.panic()
예제 #5
0
	def __watchdog_job(self):
		self.__watchdog_job_id += 1
		this_job_id = self.__watchdog_job_id

		while self.status < Resource.ASSEMBLING and this_job_id == self.__watchdog_job_id:
			while self.watchdog_lock:
				sleep(0.025)

			sleep_time = None

			if self.status == Resource.ADVERTISED:
				sleep_time = (self.adv_sent+self.default_timeout)-time.time()
				if sleep_time < 0:
					if self.retries_left <= 0:
						RNS.log("Resource transfer timeout after sending advertisement", RNS.LOG_DEBUG)
						self.cancel()
						sleep_time = 0.001
					else:
						RNS.log("No part requests received, retrying resource advertisement...", RNS.LOG_DEBUG)
						self.retries_left -= 1
						self.advertisement_packet.resend()
						self.last_activity = time.time()
						self.adv_sent = self.last_activity
						sleep_time = 0.001
					

			elif self.status == Resource.TRANSFERRING:
				if not self.initiator:
					rtt = self.link.rtt if self.rtt == None else self.rtt
					sleep_time = self.last_activity + (rtt*self.timeout_factor) - time.time()

					if sleep_time < 0:
						if self.retries_left > 0:
							RNS.log("Timeout waiting for parts, requesting retry", RNS.LOG_DEBUG)
							sleep_time = 0.001
							self.retries_left -= 1
							self.waiting_for_hmu = False
							self.request_next()
						else:
							self.cancel()
							sleep_time = 0.001
				else:
					max_wait = self.rtt * self.timeout_factor * self.max_retries + self.sender_grace_time
					sleep_time = self.last_activity + max_wait - time.time()
					if sleep_time < 0:
						RNS.log("Resource timed out waiting for part requests", RNS.LOG_DEBUG)
						self.cancel()
						sleep_time = 0.001

			elif self.status == Resource.AWAITING_PROOF:
				sleep_time = self.last_part_sent + (self.rtt*self.timeout_factor+self.sender_grace_time) - time.time()
				if sleep_time < 0:
					if self.retries_left <= 0:
						RNS.log("Resource timed out waiting for proof", RNS.LOG_DEBUG)
						self.cancel()
						sleep_time = 0.001
					else:
						RNS.log("All parts sent, but no resource proof received, querying network cache...", RNS.LOG_DEBUG)
						self.retries_left -= 1
						expected_data = self.hash + self.expected_proof
						expected_proof_packet = RNS.Packet(self.link, expected_data, packet_type=RNS.Packet.PROOF, context=RNS.Packet.RESOURCE_PRF)
						expected_proof_packet.pack()
						expected_proof_packet.updateHash()
						RNS.Transport.cache_request(expected_proof_packet.packet_hash)
						self.last_part_sent = time.time()
						sleep_time = 0.001

			if sleep_time == 0:
				RNS.log("Warning! Link watchdog sleep time of 0!", RNS.LOG_WARNING)
			if sleep_time == None or sleep_time < 0:
				# TODO: This should probably not be here forever
				RNS.log("Timing error! Closing Reticulum now.", RNS.LOG_CRITICAL)
				RNS.panic()

			sleep(sleep_time)
예제 #6
0
    def applyConfig(self):
        if "logging" in self.config:
            for option in self.config["logging"]:
                value = self.config["logging"][option]
                if option == "loglevel":
                    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 == "enable_transport":
                    v = self.config["reticulum"].as_bool(option)
                    if v == True:
                        Reticulum.__transport_enabled = 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
                if option == "allow_unencrypted":
                    v = self.config["reticulum"].as_bool(option)
                    if v == True:
                        RNS.log("", RNS.LOG_CRITICAL)
                        RNS.log("! ! !     ! ! !     ! ! !", RNS.LOG_CRITICAL)
                        RNS.log("", RNS.LOG_CRITICAL)
                        RNS.log("Danger! Encryptionless links have been allowed in the config file!", RNS.LOG_CRITICAL)
                        RNS.log("Beware of the consequences! Any data sent over a link can potentially be intercepted,", RNS.LOG_CRITICAL)
                        RNS.log("read and modified! If you are not absolutely sure that you want this,", RNS.LOG_CRITICAL)
                        RNS.log("you should exit Reticulum NOW and change your config file!", RNS.LOG_CRITICAL)
                        RNS.log("", RNS.LOG_CRITICAL)
                        RNS.log("! ! !     ! ! !     ! ! !", RNS.LOG_CRITICAL)
                        RNS.log("", RNS.LOG_CRITICAL)
                        Reticulum.__allow_unencrypted = True

        self.start_local_interface()

        if self.is_shared_instance or self.is_standalone_instance:
            interface_names = []
            for name in self.config["interfaces"]:
                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"])
                                )

                                if "outgoing" in c and c.as_bool("outgoing") == True:
                                    interface.OUT = True
                                else:
                                    interface.OUT = False

                                RNS.Transport.interfaces.append(interface)


                            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

                                RNS.Transport.interfaces.append(interface)


                            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

                                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 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["outgoing"].lower() == "true":
                                    interface.OUT = True
                                else:
                                    interface.OUT = False

                                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 = 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["outgoing"].lower() == "true":
                                    interface.OUT = True
                                else:
                                    interface.OUT = False

                                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 = 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["outgoing"].lower() == "true":
                                    interface.OUT = True
                                else:
                                    interface.OUT = False

                                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 = 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["outgoing"].lower() == "true":
                                    interface.OUT = True
                                else:
                                    interface.OUT = False

                                RNS.Transport.interfaces.append(interface)
                        else:
                            RNS.log("Skipping disabled interface \""+name+"\"", RNS.LOG_NOTICE)

                    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()
예제 #7
0
    def __init__(self, configdir=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.
        """

        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.__allow_unencrypted = False
        Reticulum.__transport_enabled = False
        Reticulum.__use_implicit_proof = True

        self.local_interface_port = 37428
        self.share_instance = True

        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)
                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.__create_default_config()
            RNS.log(
                "Default config file created. Make any necessary changes in " +
                Reticulum.configdir + "/config and start Reticulum again.")
            RNS.log("Exiting now!")
            exit(1)

        self.__apply_config()
        RNS.Identity.load_known_destinations()

        RNS.Transport.start(self)

        atexit.register(Reticulum.exit_handler)