This commit is contained in:
Mark Qvist
2026-06-27 21:59:34 +02:00
parent c99ec922aa
commit c0bc19d998
6 changed files with 309 additions and 435 deletions
+5 -8
View File
@@ -602,21 +602,18 @@ class Identity:
signal_str = " ["
if packet.rssi != None:
signal_str += "RSSI "+str(packet.rssi)+"dBm"
if packet.snr != None:
signal_str += ", "
if packet.snr != None:
signal_str += "SNR "+str(packet.snr)+"dB"
if packet.snr != None: signal_str += ", "
if packet.snr != None: signal_str += "SNR "+str(packet.snr)+"dB"
signal_str += "]"
else:
signal_str = ""
else: signal_str = ""
if hasattr(packet, "transport_id") and packet.transport_id != None:
RNS.log("Valid announce for "+RNS.prettyhexrep(destination_hash)+" "+str(packet.hops)+" hops away, received via "+RNS.prettyhexrep(packet.transport_id)+" on "+str(packet.receiving_interface)+signal_str, RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None
else:
RNS.log("Valid announce for "+RNS.prettyhexrep(destination_hash)+" "+str(packet.hops)+" hops away, received on "+str(packet.receiving_interface)+signal_str, RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None
if ratchet:
Identity._remember_ratchet(destination_hash, ratchet)
if ratchet: Identity._remember_ratchet(destination_hash, ratchet)
return True
+72 -152
View File
@@ -191,8 +191,7 @@ class Link:
if len(data) == Link.ECPUBSIZE+Link.LINK_MTU_SIZE:
RNS.log("Link request includes MTU signalling", RNS.LOG_DEBUG) # TODO: Remove debug
try:
link.mtu = Link.mtu_from_lr_packet(packet) or Reticulum.MTU
try: link.mtu = Link.mtu_from_lr_packet(packet) or Reticulum.MTU
except Exception as e:
RNS.trace_exception(e)
link.mtu = RNS.Reticulum.MTU
@@ -277,6 +276,7 @@ class Link:
self.initiator = False
self.prv = X25519PrivateKey.generate()
self.sig_prv = self.owner.identity.sig_prv
else:
self.initiator = True
self.expected_hops = RNS.Transport.hops_to(self.destination.hash)
@@ -296,14 +296,11 @@ class Link:
if peer_pub_bytes == None:
self.peer_pub = None
self.peer_pub_bytes = None
else:
self.load_peer(peer_pub_bytes, peer_sig_pub_bytes)
if established_callback != None:
self.set_link_established_callback(established_callback)
else: self.load_peer(peer_pub_bytes, peer_sig_pub_bytes)
if closed_callback != None:
self.set_link_closed_callback(closed_callback)
if established_callback != None: self.set_link_established_callback(established_callback)
if closed_callback != None: self.set_link_closed_callback(closed_callback)
if self.initiator:
signalling_bytes = b""
@@ -334,8 +331,7 @@ class Link:
self.peer_sig_pub_bytes = peer_sig_pub_bytes
self.peer_sig_pub = Ed25519PublicKey.from_public_bytes(self.peer_sig_pub_bytes)
if not hasattr(self.peer_pub, "curve"):
self.peer_pub.curve = Link.CURVE
if not hasattr(self.peer_pub, "curve"): self.peer_pub.curve = Link.CURVE
@staticmethod
def link_id_from_lr_packet(packet):
@@ -447,8 +443,8 @@ class Link:
thread = threading.Thread(target=self.callbacks.link_established, args=(self,))
thread.daemon = True
thread.start()
else:
RNS.log("Invalid link proof signature received by "+str(self)+". Ignoring.", RNS.LOG_DEBUG)
else: RNS.log("Invalid link proof signature received by "+str(self)+". Ignoring.", RNS.LOG_DEBUG)
except Exception as e:
self.status = Link.CLOSED
@@ -497,35 +493,21 @@ class Link:
request_packet = RNS.Packet(self, packed_request, RNS.Packet.DATA, context = RNS.Packet.REQUEST)
packet_receipt = request_packet.send()
if packet_receipt == False:
return False
if packet_receipt == False: return False
else:
packet_receipt.set_timeout(timeout)
return RequestReceipt(
self,
packet_receipt = packet_receipt,
response_callback = response_callback,
failed_callback = failed_callback,
progress_callback = progress_callback,
timeout = timeout,
request_size = len(packed_request),
)
return RequestReceipt(self, packet_receipt = packet_receipt, response_callback = response_callback,
failed_callback = failed_callback, progress_callback = progress_callback,
timeout = timeout, request_size = len(packed_request))
else:
request_id = RNS.Identity.truncated_hash(packed_request)
RNS.log("Sending request "+RNS.prettyhexrep(request_id)+" as resource.", RNS.LOG_DEBUG)
request_resource = RNS.Resource(packed_request, self, request_id = request_id, is_response = False, timeout = timeout)
return RequestReceipt(
self,
resource = request_resource,
response_callback = response_callback,
failed_callback = failed_callback,
progress_callback = progress_callback,
timeout = timeout,
request_size = len(packed_request),
)
return RequestReceipt(self, resource = request_resource, response_callback = response_callback,
failed_callback = failed_callback, progress_callback = progress_callback,
timeout = timeout, request_size = len(packed_request))
def update_mdu(self):
self.mdu = self.mtu - RNS.Reticulum.HEADER_MAXSIZE - RNS.Reticulum.IFAC_MIN_SIZE
@@ -547,10 +529,8 @@ class Link:
self.__update_keepalive()
try:
if self.owner.callbacks.link_established != None:
self.owner.callbacks.link_established(self)
except Exception as e:
RNS.log("Error occurred in external link establishment callback. The contained exception was: "+str(e), RNS.LOG_ERROR)
if self.owner.callbacks.link_established != None: self.owner.callbacks.link_established(self)
except Exception as e: RNS.log("Error occurred in external link establishment callback. The contained exception was: "+str(e), RNS.LOG_ERROR)
except Exception as e:
RNS.log("Error occurred while processing RTT packet, tearing down link. The contained exception was: "+str(e), RNS.LOG_ERROR)
@@ -565,73 +545,57 @@ class Link:
:param track: Whether or not to keep track of physical layer statistics. Value must be ``True`` or ``False``.
"""
if track:
self.__track_phy_stats = True
else:
self.__track_phy_stats = False
if track: self.__track_phy_stats = True
else: self.__track_phy_stats = False
def get_rssi(self):
"""
:returns: The physical layer *Received Signal Strength Indication* if available, otherwise ``None``. Physical layer statistics must be enabled on the link for this method to return a value.
"""
if self.__track_phy_stats:
return self.rssi
else:
return None
if self.__track_phy_stats: return self.rssi
else: return None
def get_snr(self):
"""
:returns: The physical layer *Signal-to-Noise Ratio* if available, otherwise ``None``. Physical layer statistics must be enabled on the link for this method to return a value.
"""
if self.__track_phy_stats:
return self.snr
else:
return None
if self.__track_phy_stats: return self.snr
else: return None
def get_q(self):
"""
:returns: The physical layer *Link Quality* if available, otherwise ``None``. Physical layer statistics must be enabled on the link for this method to return a value.
"""
if self.__track_phy_stats:
return self.q
else:
return None
if self.__track_phy_stats: return self.q
else: return None
def get_establishment_rate(self):
"""
:returns: The data transfer rate at which the link establishment procedure ocurred, in bits per second.
"""
if self.establishment_rate != None:
return self.establishment_rate*8
else:
return None
if self.establishment_rate != None: return self.establishment_rate*8
else: return None
def get_mtu(self):
"""
:returns: The MTU of an established link.
"""
if self.status == Link.ACTIVE:
return self.mtu
else:
return None
if self.status == Link.ACTIVE: return self.mtu
else: return None
def get_mdu(self):
"""
:returns: The packet MDU of an established link.
"""
if self.status == Link.ACTIVE:
return self.mdu
else:
return None
if self.status == Link.ACTIVE: return self.mdu
else: return None
def get_expected_rate(self):
"""
:returns: The packet expected in-flight data rate of an established link.
"""
if self.status == Link.ACTIVE:
return self.expected_rate
else:
return None
if self.status == Link.ACTIVE: return self.expected_rate
else: return None
def get_mode(self):
"""
@@ -649,10 +613,8 @@ class Link:
"""
:returns: The time in seconds since this link was established.
"""
if self.activated_at:
return time.time() - self.activated_at
else:
return None
if self.activated_at: return time.time() - self.activated_at
else: return None
def no_inbound_for(self):
"""
@@ -712,14 +674,11 @@ class Link:
plaintext = self.decrypt(packet.data)
if plaintext == self.link_id:
self.status = Link.CLOSED
if self.initiator:
self.teardown_reason = Link.DESTINATION_CLOSED
else:
self.teardown_reason = Link.INITIATOR_CLOSED
if self.initiator: self.teardown_reason = Link.DESTINATION_CLOSED
else: self.teardown_reason = Link.INITIATOR_CLOSED
self.__update_phy_stats(packet)
self.link_closed()
except Exception as e:
pass
except Exception as e: pass
def link_closed(self):
for resource in self.incoming_resources: resource.cancel()
@@ -752,9 +711,7 @@ class Link:
while not self.status == Link.CLOSED:
while (self.watchdog_lock):
rtt_wait = 0.025
if hasattr(self, "rtt") and self.rtt:
rtt_wait = self.rtt
if hasattr(self, "rtt") and self.rtt: rtt_wait = self.rtt
sleep(max(rtt_wait, 0.025))
if not self.status == Link.CLOSED:
@@ -779,10 +736,8 @@ class Link:
self.link_closed()
sleep_time = 0.001
if self.initiator:
RNS.log("Timeout waiting for link request proof", RNS.LOG_DEBUG)
else:
RNS.log("Timeout waiting for RTT packet from link initiator", RNS.LOG_DEBUG)
if self.initiator: RNS.log("Timeout waiting for link request proof", RNS.LOG_DEBUG)
else: RNS.log("Timeout waiting for RTT packet from link initiator", RNS.LOG_DEBUG)
elif self.status == Link.ACTIVE:
activated_at = self.activated_at if self.activated_at != None else 0
@@ -796,11 +751,10 @@ class Link:
if time.time() >= last_inbound + self.stale_time:
sleep_time = self.rtt * self.keepalive_timeout_factor + Link.STALE_GRACE
self.status = Link.STALE
else:
sleep_time = self.keepalive
else: sleep_time = self.keepalive
else:
sleep_time = (last_inbound + self.keepalive) - time.time()
else: sleep_time = (last_inbound + self.keepalive) - time.time()
elif self.status == Link.STALE:
sleep_time = 0.001
@@ -810,8 +764,7 @@ class Link:
self.link_closed()
if sleep_time == 0:
RNS.log("Warning! Link watchdog sleep time of 0!", RNS.LOG_ERROR)
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! Tearing down link "+str(self)+" now.", RNS.LOG_ERROR)
self.teardown()
@@ -825,7 +778,6 @@ class Link:
self.snr = None
self.q = None
def __update_phy_stats(self, packet, query_shared = True, force_update = False):
if self.__track_phy_stats or force_update:
if query_shared:
@@ -834,12 +786,9 @@ class Link:
if packet.snr == None: packet.snr = reticulum.get_packet_snr(packet.packet_hash)
if packet.q == None: packet.q = reticulum.get_packet_q(packet.packet_hash)
if packet.rssi != None:
self.rssi = packet.rssi
if packet.snr != None:
self.snr = packet.snr
if packet.q != None:
self.q = packet.q
if packet.rssi != None: self.rssi = packet.rssi
if packet.snr != None: self.snr = packet.snr
if packet.q != None: self.q = packet.q
def __update_keepalive(self):
self.keepalive = max(min(self.rtt*(Link.KEEPALIVE_MAX/Link.KEEPALIVE_MAX_RTT), Link.KEEPALIVE_MAX), Link.KEEPALIVE_MIN)
@@ -933,8 +882,8 @@ class Link:
def job(): self.handle_request(request_id, request_data)
threading.Thread(target=job, daemon=True).start()
else:
RNS.log("Incoming request resource failed with status: "+RNS.hexrep([resource.status]), RNS.LOG_DEBUG)
else: RNS.log("Incoming request resource failed with status: "+RNS.hexrep([resource.status]), RNS.LOG_DEBUG)
def response_resource_concluded(self, resource):
if resource.status == RNS.Resource.COMPLETE:
@@ -965,8 +914,7 @@ class Link:
:return: ``Channel`` object
"""
if self._channel is None:
self._channel = Channel(LinkChannelOutlet(self))
if self._channel is None: self._channel = Channel(LinkChannelOutlet(self))
return self._channel
def receive(self, packet):
@@ -976,12 +924,10 @@ class Link:
RNS.log(f"Link-associated packet received on unexpected interface {packet.receiving_interface} instead of {self.attached_interface}! Someone might be trying to manipulate your communication!", RNS.LOG_ERROR)
else:
self.last_inbound = time.time()
if packet.context != RNS.Packet.KEEPALIVE:
self.last_data = self.last_inbound
if packet.context != RNS.Packet.KEEPALIVE: self.last_data = self.last_inbound
self.rx += 1
self.rxbytes += len(packet.data)
if self.status == Link.STALE:
self.status = Link.ACTIVE
if self.status == Link.STALE: self.status = Link.ACTIVE
if packet.packet_type == RNS.Packet.DATA:
should_query = False
@@ -1002,10 +948,8 @@ class Link:
elif self.destination.proof_strategy == RNS.Destination.PROVE_APP:
if self.destination.callbacks.proof_requested:
try:
if self.destination.callbacks.proof_requested(packet):
packet.prove()
except Exception as e:
RNS.log("Error while executing proof request callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
if self.destination.callbacks.proof_requested(packet): packet.prove()
except Exception as e: RNS.log("Error while executing proof request callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
elif packet.context == RNS.Packet.LINKIDENTIFY:
plaintext = self.decrypt(packet.data)
@@ -1039,8 +983,7 @@ class Link:
def job(): self.handle_request(request_id, unpacked_request)
threading.Thread(target=job, daemon=True).start()
self.__update_phy_stats(packet, query_shared=True)
except Exception as e:
RNS.log("Error occurred while handling request. The contained exception was: "+str(e), RNS.LOG_ERROR)
except Exception as e: RNS.log("Error occurred while handling request. The contained exception was: "+str(e), RNS.LOG_ERROR)
elif packet.context == RNS.Packet.RESPONSE:
try:
@@ -1053,8 +996,7 @@ class Link:
def job(): self.handle_response(request_id, response_data, transfer_size, transfer_size, update_sizes=True)
threading.Thread(target=job, daemon=True).start()
self.__update_phy_stats(packet, query_shared=True)
except Exception as e:
RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR)
except Exception as e: RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR)
elif packet.context == RNS.Packet.LRRTT:
if not self.initiator:
@@ -1186,7 +1128,6 @@ class Link:
self.watchdog_lock = False
def encrypt(self, plaintext):
try:
if not self.token:
@@ -1201,7 +1142,6 @@ class Link:
RNS.log("Encryption on link "+str(self)+" failed. The contained exception was: "+str(e), RNS.LOG_ERROR)
raise e
def decrypt(self, ciphertext):
try:
if not self.token: self.token = Token(self.derived_key)
@@ -1211,7 +1151,6 @@ class Link:
RNS.log("Decryption failed on link "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
return None
def sign(self, message):
return self.sig_prv.sign(message)
@@ -1219,8 +1158,7 @@ class Link:
try:
self.peer_sig_pub.verify(signature, message)
return True
except Exception as e:
return False
except Exception as e: return False
def set_link_established_callback(self, callback):
self.callbacks.link_established = callback
@@ -1388,7 +1326,6 @@ class RequestReceipt():
self.link.pending_requests.append(self)
def request_resource_concluded(self, resource):
if resource.status == RNS.Resource.COMPLETE:
RNS.log("Request "+RNS.prettyhexrep(self.request_id)+" successfully sent as resource.", RNS.LOG_DEBUG)
@@ -1411,7 +1348,6 @@ class RequestReceipt():
except Exception as e:
RNS.log("Error while executing request failed callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
def __response_timeout_job(self):
while self.status == RequestReceipt.DELIVERED:
now = time.time()
@@ -1421,7 +1357,6 @@ class RequestReceipt():
time.sleep(0.1)
def request_timed_out(self, packet_receipt):
if self in self.link.pending_requests and self.status == RequestReceipt.DELIVERED:
self.status = RequestReceipt.FAILED
@@ -1430,9 +1365,7 @@ class RequestReceipt():
if self.callbacks.failed != None:
try: self.callbacks.failed(self)
except Exception as e:
RNS.log("Error while executing request timed out callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
except Exception as e: RNS.log("Error while executing request timed out callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
def response_resource_progress(self, resource):
if resource != None:
@@ -1449,13 +1382,10 @@ class RequestReceipt():
self.progress = resource.get_progress()
if self.callbacks.progress != None:
try:
self.callbacks.progress(self)
except Exception as e:
RNS.log("Error while executing response progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
else:
resource.cancel()
try: self.callbacks.progress(self)
except Exception as e: RNS.log("Error while executing response progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
else: resource.cancel()
def response_received(self, response, metadata=None):
if not self.status == RequestReceipt.FAILED:
@@ -1474,13 +1404,11 @@ class RequestReceipt():
if self.callbacks.progress != None:
try: self.callbacks.progress(self)
except Exception as e:
RNS.log("Error while executing response progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
except Exception as e: RNS.log("Error while executing response progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
if self.callbacks.response != None:
try: self.callbacks.response(self)
except Exception as e:
RNS.log("Error while executing response received callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
except Exception as e: RNS.log("Error while executing response received callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
def get_request_id(self):
"""
@@ -1504,31 +1432,23 @@ class RequestReceipt():
"""
:returns: The response as *bytes* if it is ready, otherwise *None*.
"""
if self.status == RequestReceipt.READY:
return self.response
else:
return None
if self.status == RequestReceipt.READY: return self.response
else: return None
def get_response_time(self):
"""
:returns: The response time of the request in seconds.
"""
if self.status == RequestReceipt.READY:
return self.response_concluded_at - self.started_at
else:
return None
if self.status == RequestReceipt.READY: return self.response_concluded_at - self.started_at
else: return None
def concluded(self):
"""
:returns: True if the associated request has concluded (successfully or with a failure), otherwise False.
"""
if self.status == RequestReceipt.READY:
return True
elif self.status == RequestReceipt.FAILED:
return True
else:
return False
if self.status == RequestReceipt.READY: return True
elif self.status == RequestReceipt.FAILED: return True
else: return False
class RequestReceiptCallbacks:
+40 -69
View File
@@ -313,51 +313,40 @@ class Packet:
# encrypted destinations
self.pack()
if RNS.Transport.outbound(self):
return self.receipt
if RNS.Transport.outbound(self): return self.receipt
else:
RNS.log("Re-send failed. No interfaces could process the outbound packet", RNS.LOG_WARNING)
self.sent = False
self.receipt = None
return False
else:
raise IOError("Packet was not sent yet")
else: raise IOError("Packet was not sent yet")
def prove(self, destination=None):
if self.fromPacked and hasattr(self, "destination") and self.destination:
if self.destination.identity and self.destination.identity.prv:
self.destination.identity.prove(self, destination)
elif self.fromPacked and hasattr(self, "link") and self.link:
self.link.prove_packet(self)
else:
RNS.log("Could not prove packet associated with neither a destination nor a link", RNS.LOG_ERROR)
elif self.fromPacked and hasattr(self, "link") and self.link: self.link.prove_packet(self)
else: RNS.log("Could not prove packet associated with neither a destination nor a link", RNS.LOG_ERROR)
# Generates a special destination that allows Reticulum
# to direct the proof back to the proved packet's sender
def generate_proof_destination(self):
return ProofDestination(self)
def generate_proof_destination(self): return ProofDestination(self)
def validate_proof_packet(self, proof_packet):
return self.receipt.validate_proof_packet(proof_packet)
def validate_proof_packet(self, proof_packet): return self.receipt.validate_proof_packet(proof_packet)
def validate_proof(self, proof):
return self.receipt.validate_proof(proof)
def validate_proof(self, proof): return self.receipt.validate_proof(proof)
def update_hash(self):
self.packet_hash = self.get_hash()
def update_hash(self): self.packet_hash = self.get_hash()
def get_hash(self):
return RNS.Identity.full_hash(self.get_hashable_part())
def get_hash(self): return RNS.Identity.full_hash(self.get_hashable_part())
def getTruncatedHash(self):
return RNS.Identity.truncated_hash(self.get_hashable_part())
def getTruncatedHash(self): return RNS.Identity.truncated_hash(self.get_hashable_part())
def get_hashable_part(self):
hashable_part = bytes([self.raw[0] & 0b00001111])
if self.header_type == Packet.HEADER_2:
hashable_part += self.raw[(RNS.Identity.TRUNCATED_HASHLENGTH//8)+2:]
else:
hashable_part += self.raw[2:]
if self.header_type == Packet.HEADER_2: hashable_part += self.raw[(RNS.Identity.TRUNCATED_HASHLENGTH//8)+2:]
else: hashable_part += self.raw[2:]
return hashable_part
@@ -365,36 +354,29 @@ class Packet:
"""
:returns: The physical layer *Received Signal Strength Indication* if available, otherwise ``None``.
"""
if self.rssi != None:
return self.rssi
else:
return reticulum.get_packet_rssi(self.packet_hash)
if self.rssi != None: return self.rssi
else: return reticulum.get_packet_rssi(self.packet_hash)
def get_snr(self):
"""
:returns: The physical layer *Signal-to-Noise Ratio* if available, otherwise ``None``.
"""
if self.snr != None:
return self.snr
else:
return reticulum.get_packet_snr(self.packet_hash)
if self.snr != None: return self.snr
else: return reticulum.get_packet_snr(self.packet_hash)
def get_q(self):
"""
:returns: The physical layer *Link Quality* if available, otherwise ``None``.
"""
if self.q != None:
return self.q
else:
return reticulum.get_packet_q(self.packet_hash)
if self.q != None: return self.q
else: return reticulum.get_packet_q(self.packet_hash)
class ProofDestination:
def __init__(self, packet):
self.hash = packet.get_hash()[:RNS.Reticulum.TRUNCATED_HASHLENGTH//8];
self.type = RNS.Destination.SINGLE
def encrypt(self, plaintext):
return plaintext
def encrypt(self, plaintext): return plaintext
class PacketReceipt:
@@ -463,18 +445,17 @@ class PacketReceipt:
link.last_proof = self.concluded_at
if self.callbacks.delivery != None:
try:
self.callbacks.delivery(self)
try: self.callbacks.delivery(self)
except Exception as e:
RNS.log("An error occurred while evaluating external delivery callback for "+str(link), RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.trace_exception(e)
return True
else:
return False
else:
return False
else: return False
else: return False
elif len(proof) == PacketReceipt.IMPL_LENGTH:
pass
# TODO: Why is this disabled?
@@ -491,8 +472,7 @@ class PacketReceipt:
# else:
# RNS.log("invalid")
# return False
else:
return False
else: return False
# Validate a raw proof
def validate_proof(self, proof, proof_packet=None):
@@ -509,24 +489,20 @@ class PacketReceipt:
self.proof_packet = proof_packet
if self.callbacks.delivery != None:
try:
self.callbacks.delivery(self)
try: self.callbacks.delivery(self)
except Exception as e:
RNS.log("Error while executing proof validated callback. The contained exception was: "+str(e), RNS.LOG_ERROR)
return True
else:
return False
else:
return False
else: return False
else: return False
elif len(proof) == PacketReceipt.IMPL_LENGTH:
# This is an implicit proof
if not hasattr(self.destination, "identity"):
return False
if self.destination.identity == None:
return False
if not hasattr(self.destination, "identity"): return False
if self.destination.identity == None: return False
signature = proof[:RNS.Identity.SIGLENGTH//8]
proof_valid = self.destination.identity.validate(signature, self.hash)
@@ -537,16 +513,13 @@ class PacketReceipt:
self.proof_packet = proof_packet
if self.callbacks.delivery != None:
try:
self.callbacks.delivery(self)
except Exception as e:
RNS.log("Error while executing proof validated callback. The contained exception was: "+str(e), RNS.LOG_ERROR)
try: self.callbacks.delivery(self)
except Exception as e: RNS.log("Error while executing proof validated callback. The contained exception was: "+str(e), RNS.LOG_ERROR)
return True
else:
return False
else:
return False
else: return False
else: return False
def get_rtt(self):
"""
@@ -559,10 +532,8 @@ class PacketReceipt:
def check_timeout(self):
if self.status == PacketReceipt.SENT and self.is_timed_out():
if self.timeout == -1:
self.status = PacketReceipt.CULLED
else:
self.status = PacketReceipt.FAILED
if self.timeout == -1: self.status = PacketReceipt.CULLED
else: self.status = PacketReceipt.FAILED
self.concluded_at = time.time()
+1 -2
View File
@@ -31,5 +31,4 @@
class Resolver:
@staticmethod
def resolve_identity(full_name):
pass
def resolve_identity(full_name): pass
+129 -186
View File
@@ -253,6 +253,7 @@ class Reticulum:
Reticulum.__network_identity = None
Reticulum.__transport_enabled = False
Reticulum.__static_transport_identity = False
Reticulum.__local_hops_delta = False
Reticulum.__link_mtu_discovery = Reticulum.LINK_MTU_DISCOVERY
Reticulum.__remote_management_enabled = False
Reticulum.__use_implicit_proof = True
@@ -387,11 +388,7 @@ class Reticulum:
def __start_local_interface(self):
if self.share_instance:
try:
interface = LocalInterface.LocalServerInterface(
RNS.Transport,
self.local_interface_port,
socket_path=self.local_socket_path
)
interface = LocalInterface.LocalServerInterface(RNS.Transport, self.local_interface_port, socket_path=self.local_socket_path)
interface.OUT = True
if hasattr(Reticulum, "_force_shared_instance_bitrate"):
interface.bitrate = Reticulum._force_shared_instance_bitrate
@@ -433,6 +430,7 @@ class Reticulum:
Reticulum.__remote_management_enabled = False
Reticulum.__allow_probes = False
RNS.log("Connected to locally available Reticulum 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)
@@ -505,6 +503,10 @@ class Reticulum:
v = self.config["reticulum"].as_bool(option)
if v == True: Reticulum.__static_transport_identity = True
if option == "local_hops_delta":
v = self.config["reticulum"].as_bool(option)
if v == True: Reticulum.__local_hops_delta = True
if option == "network_identity":
if Reticulum.__network_identity == None:
path = self.config["reticulum"][option]
@@ -737,24 +739,19 @@ class Reticulum:
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
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 c["networkname"] != "": ifac_netname = c["networkname"]
if "network_name" in c:
if c["network_name"] != "":
ifac_netname = c["network_name"]
if c["network_name"] != "": ifac_netname = c["network_name"]
ifac_netkey = None
if "passphrase" in c:
if c["passphrase"] != "":
ifac_netkey = c["passphrase"]
if c["passphrase"] != "": ifac_netkey = c["passphrase"]
if "pass_phrase" in c:
if c["pass_phrase"] != "":
ifac_netkey = c["pass_phrase"]
if c["pass_phrase"] != "": ifac_netkey = c["pass_phrase"]
ingress_control = True
if "ingress_control" in c: ingress_control = c.as_bool("ingress_control")
@@ -783,29 +780,22 @@ class Reticulum:
configured_bitrate = None
if "bitrate" in c:
if c.as_int("bitrate") >= Reticulum.MINIMUM_BITRATE:
configured_bitrate = c.as_int("bitrate")
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")
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")
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 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
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:
@@ -887,40 +877,41 @@ class Reticulum:
if ifac_size != None: interface.ifac_size = ifac_size
else: interface.ifac_size = interface.DEFAULT_IFAC_SIZE
interface.discoverable = discoverable
interface.discovery_announce_interval = discovery_announce_interval
interface.discovery_publish_ifac = publish_ifac
interface.reachable_on = reachable_on
interface.discovery_name = discovery_name
interface.discovery_encrypt = discovery_encrypt
interface.discovery_stamp_value = discovery_stamp_value
interface.discovery_latitude = latitude
interface.discovery_longitude = longitude
interface.discovery_height = height
interface.discovery_frequency = discovery_frequency
interface.discovery_bandwidth = discovery_bandwidth
interface.discovery_modulation = discovery_modulation
interface.discoverable = discoverable
interface.discovery_announce_interval = discovery_announce_interval
interface.discovery_publish_ifac = publish_ifac
interface.reachable_on = reachable_on
interface.discovery_name = discovery_name
interface.discovery_encrypt = discovery_encrypt
interface.discovery_stamp_value = discovery_stamp_value
interface.discovery_latitude = latitude
interface.discovery_longitude = longitude
interface.discovery_height = height
interface.discovery_frequency = discovery_frequency
interface.discovery_bandwidth = discovery_bandwidth
interface.discovery_modulation = discovery_modulation
interface.recursive_prs = recursive_prs
interface.announces_from_internal = announces_from_internal
interface.announce_rate_target = announce_rate_target
interface.announce_rate_grace = announce_rate_grace
interface.announce_rate_penalty = announce_rate_penalty
interface.ingress_control = ingress_control
if egress_control != None: interface.egress_control = egress_control
if ic_max_held_announces != None: interface.ic_max_held_announces = ic_max_held_announces
if ic_burst_hold != None: interface.ic_burst_hold = ic_burst_hold
if ic_burst_freq_new != None: interface.ic_burst_freq_new = ic_burst_freq_new
if ic_burst_freq != None: interface.ic_burst_freq = ic_burst_freq
if ic_pr_burst_freq_new != None: interface.ic_pr_burst_freq_new = ic_pr_burst_freq_new
if ic_pr_burst_freq != None: interface.ic_pr_burst_freq = ic_pr_burst_freq
if ec_pr_freq != None: interface.ec_pr_freq = ec_pr_freq
if ic_new_time != None: interface.ic_new_time = ic_new_time
if ic_burst_penalty != None: interface.ic_burst_penalty = ic_burst_penalty
interface.recursive_prs = recursive_prs
interface.announces_from_internal = announces_from_internal
interface.announce_rate_target = announce_rate_target
interface.announce_rate_grace = announce_rate_grace
interface.announce_rate_penalty = announce_rate_penalty
interface.ingress_control = ingress_control
if egress_control != None: interface.egress_control = egress_control
if ic_max_held_announces != None: interface.ic_max_held_announces = ic_max_held_announces
if ic_burst_hold != None: interface.ic_burst_hold = ic_burst_hold
if ic_burst_freq_new != None: interface.ic_burst_freq_new = ic_burst_freq_new
if ic_burst_freq != None: interface.ic_burst_freq = ic_burst_freq
if ic_pr_burst_freq_new != None: interface.ic_pr_burst_freq_new = ic_pr_burst_freq_new
if ic_pr_burst_freq != None: interface.ic_pr_burst_freq = ic_pr_burst_freq
if ec_pr_freq != None: interface.ec_pr_freq = ec_pr_freq
if ic_new_time != None: interface.ic_new_time = ic_new_time
if ic_burst_penalty != None: interface.ic_burst_penalty = ic_burst_penalty
if ic_held_release_interval != None: interface.ic_held_release_interval = ic_held_release_interval
interface.ifac_netname = ifac_netname
interface.ifac_netkey = ifac_netkey
interface.ifac_netkey = ifac_netkey
if interface.ifac_netname != None or interface.ifac_netkey != None:
ifac_origin = b""
@@ -932,12 +923,8 @@ class Reticulum:
ifac_origin += RNS.Identity.full_hash(interface.ifac_netkey.encode("utf-8"))
ifac_origin_hash = RNS.Identity.full_hash(ifac_origin)
interface.ifac_key = RNS.Cryptography.hkdf(
length=64,
derive_from=ifac_origin_hash,
salt=self.ifac_salt,
context=None
)
interface.ifac_key = RNS.Cryptography.hkdf(length=64, derive_from=ifac_origin_hash,
salt=self.ifac_salt, context=None)
interface.ifac_identity = RNS.Identity.from_bytes(interface.ifac_key)
interface.ifac_signature = interface.ifac_identity.sign(RNS.Identity.full_hash(interface.ifac_key))
@@ -1063,6 +1050,7 @@ class Reticulum:
def _add_interface(self, interface, mode = None, configured_bitrate=None, ifac_size=None, ifac_netname=None, ifac_netkey=None,
announce_cap=None, announce_rate_target=None, announce_rate_grace=None, announce_rate_penalty=None,
bootstrap_only=False, recursive_prs=False, announces_from_internal=True):
if not self.is_connected_to_shared_instance:
if interface != None and issubclass(type(interface), RNS.Interfaces.Interface.Interface):
@@ -1077,15 +1065,15 @@ class Reticulum:
if ifac_size != None: interface.ifac_size = ifac_size
else: interface.ifac_size = interface.DEFAULT_IFAC_SIZE
interface.recursive_prs = recursive_prs
interface.recursive_prs = recursive_prs
interface.announces_from_internal = announces_from_internal
interface.announce_cap = announce_cap if announce_cap != None else Reticulum.ANNOUNCE_CAP/100.0
interface.announce_rate_target = announce_rate_target
interface.announce_rate_grace = announce_rate_grace
interface.announce_rate_penalty = announce_rate_penalty
interface.announce_cap = announce_cap if announce_cap != None else Reticulum.ANNOUNCE_CAP/100.0
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
interface.ifac_netkey = ifac_netkey
if interface.ifac_netname != None or interface.ifac_netkey != None:
ifac_origin = b""
@@ -1097,12 +1085,8 @@ class Reticulum:
ifac_origin += RNS.Identity.full_hash(interface.ifac_netkey.encode("utf-8"))
ifac_origin_hash = RNS.Identity.full_hash(ifac_origin)
interface.ifac_key = RNS.Cryptography.hkdf(
length=64,
derive_from=ifac_origin_hash,
salt=self.ifac_salt,
context=None
)
interface.ifac_key = RNS.Cryptography.hkdf(length=64, derive_from=ifac_origin_hash,
salt=self.ifac_salt, context=None)
interface.ifac_identity = RNS.Identity.from_bytes(interface.ifac_key)
interface.ifac_signature = interface.ifac_identity.sign(RNS.Identity.full_hash(interface.ifac_key))
@@ -1199,8 +1183,7 @@ class Reticulum:
self.config = ConfigObj(__default_rns_config__)
self.config.filename = Reticulum.configpath
if not os.path.isdir(Reticulum.configdir):
os.makedirs(Reticulum.configdir)
if not os.path.isdir(Reticulum.configdir): os.makedirs(Reticulum.configdir)
self.config.write()
def rpc_return(self, connection, response):
@@ -1331,31 +1314,26 @@ class Reticulum:
rpc_connection.send_bytes(mp.packb({"get": "interface_stats"}))
response = mp.unpackb(rpc_connection.recv_bytes())
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, "clients"): ifstats["clients"] = interface.clients
else: ifstats["clients"] = None
if hasattr(interface, "parent_interface") and interface.parent_interface != None:
ifstats["parent_interface_name"] = str(interface.parent_interface)
ifstats["parent_interface_hash"] = interface.parent_interface.get_hash()
if hasattr(interface, "i2p") and hasattr(interface, "connectable"):
if interface.connectable:
ifstats["i2p_connectable"] = True
else:
ifstats["i2p_connectable"] = False
if interface.connectable: ifstats["i2p_connectable"] = True
else: ifstats["i2p_connectable"] = False
if hasattr(interface, "b32"):
if interface.b32 != None:
ifstats["i2p_b32"] = interface.b32+".b32.i2p"
else:
ifstats["i2p_b32"] = None
if interface.b32 != None: ifstats["i2p_b32"] = interface.b32+".b32.i2p"
else: ifstats["i2p_b32"] = None
if hasattr(interface, "i2p_tunnel_state"):
if interface.i2p_tunnel_state != None:
@@ -1370,37 +1348,20 @@ class Reticulum:
else:
ifstats["tunnelstate"] = None
if hasattr(interface, "r_airtime_short"):
ifstats["airtime_short"] = interface.r_airtime_short
if hasattr(interface, "r_airtime_long"):
ifstats["airtime_long"] = interface.r_airtime_long
if hasattr(interface, "r_channel_load_short"):
ifstats["channel_load_short"] = interface.r_channel_load_short
if hasattr(interface, "r_channel_load_long"):
ifstats["channel_load_long"] = interface.r_channel_load_long
if hasattr(interface, "r_noise_floor"):
ifstats["noise_floor"] = interface.r_noise_floor
if hasattr(interface, "r_interference"):
ifstats["interference"] = interface.r_interference
if hasattr(interface, "r_airtime_short"): ifstats["airtime_short"] = interface.r_airtime_short
if hasattr(interface, "r_airtime_long"): ifstats["airtime_long"] = interface.r_airtime_long
if hasattr(interface, "r_channel_load_short"): ifstats["channel_load_short"] = interface.r_channel_load_short
if hasattr(interface, "r_channel_load_long"): ifstats["channel_load_long"] = interface.r_channel_load_long
if hasattr(interface, "r_noise_floor"): ifstats["noise_floor"] = interface.r_noise_floor
if hasattr(interface, "r_interference"): ifstats["interference"] = interface.r_interference
if hasattr(interface, "cpu_temp"): ifstats["cpu_temp"] = interface.cpu_temp
if hasattr(interface, "cpu_load"): ifstats["cpu_load"] = interface.cpu_load
if hasattr(interface, "mem_load"): ifstats["mem_load"] = interface.mem_load
if hasattr(interface, "r_interference_l") and type(interface.r_interference_l) == list:
ifstats["interference_last_ts"] = interface.r_interference_l[0]
ifstats["interference_last_dbm"] = interface.r_interference_l[1]
if hasattr(interface, "cpu_temp"):
ifstats["cpu_temp"] = interface.cpu_temp
if hasattr(interface, "cpu_load"):
ifstats["cpu_load"] = interface.cpu_load
if hasattr(interface, "mem_load"):
ifstats["mem_load"] = interface.mem_load
if hasattr(interface, "switch_id"):
if interface.switch_id != None: ifstats["switch_id"] = RNS.hexrep(interface.switch_id)
else: ifstats["switch_id"] = None
@@ -1414,39 +1375,26 @@ class Reticulum:
else: ifstats["endpoint_id"] = None
if hasattr(interface, "r_battery_state"):
if interface.r_battery_state != 0x00:
ifstats["battery_state"] = interface.get_battery_state_string()
if hasattr(interface, "r_battery_percent"):
ifstats["battery_percent"] = interface.r_battery_percent
if interface.r_battery_state != 0x00: ifstats["battery_state"] = interface.get_battery_state_string()
if hasattr(interface, "r_battery_percent"): ifstats["battery_percent"] = interface.r_battery_percent
if hasattr(interface, "bitrate"):
if interface.bitrate != None:
ifstats["bitrate"] = interface.bitrate
else:
ifstats["bitrate"] = None
if interface.bitrate != None: ifstats["bitrate"] = interface.bitrate
else: ifstats["bitrate"] = None
if hasattr(interface, "current_rx_speed"):
if interface.current_rx_speed != None:
ifstats["rxs"] = interface.current_rx_speed
else:
ifstats["rxs"] = 0
else:
ifstats["rxs"] = 0
if interface.current_rx_speed != None: ifstats["rxs"] = interface.current_rx_speed
else: ifstats["rxs"] = 0
else: ifstats["rxs"] = 0
if hasattr(interface, "current_tx_speed"):
if interface.current_tx_speed != None:
ifstats["txs"] = interface.current_tx_speed
else:
ifstats["txs"] = 0
else:
ifstats["txs"] = 0
if interface.current_tx_speed != None: ifstats["txs"] = interface.current_tx_speed
else: ifstats["txs"] = 0
else: ifstats["txs"] = 0
if hasattr(interface, "peers"):
if interface.peers != None:
ifstats["peers"] = len(interface.peers)
else:
ifstats["peers"] = None
if interface.peers != None: ifstats["peers"] = len(interface.peers)
else: ifstats["peers"] = None
if hasattr(interface, "ifac_signature"):
ifstats["ifac_signature"] = interface.ifac_signature
@@ -1454,57 +1402,52 @@ class Reticulum:
ifstats["ifac_netname"] = interface.ifac_netname
else:
ifstats["ifac_signature"] = None
ifstats["ifac_size"] = None
ifstats["ifac_netname"] = None
ifstats["ifac_size"] = None
ifstats["ifac_netname"] = None
if hasattr(interface, "autoconnect_source"):
ifstats["autoconnect_source"] = interface.autoconnect_source
else:
ifstats["autoconnect_source"] = None
if hasattr(interface, "autoconnect_source"): ifstats["autoconnect_source"] = interface.autoconnect_source
else: ifstats["autoconnect_source"] = None
if hasattr(interface, "announce_queue"):
if interface.announce_queue != None:
ifstats["announce_queue"] = len(interface.announce_queue)
else:
ifstats["announce_queue"] = None
if interface.announce_queue != None: ifstats["announce_queue"] = len(interface.announce_queue)
else: ifstats["announce_queue"] = None
ifstats["name"] = str(interface)
ifstats["short_name"] = str(interface.name)
ifstats["hash"] = interface.get_hash()
ifstats["type"] = str(type(interface).__name__)
ifstats["rxb"] = interface.rxb
ifstats["txb"] = interface.txb
ifstats["name"] = str(interface)
ifstats["short_name"] = str(interface.name)
ifstats["hash"] = interface.get_hash()
ifstats["type"] = str(type(interface).__name__)
ifstats["rxb"] = interface.rxb
ifstats["txb"] = interface.txb
ifstats["incoming_announce_frequency"] = interface.incoming_announce_frequency()
ifstats["outgoing_announce_frequency"] = interface.outgoing_announce_frequency()
ifstats["incoming_pr_frequency"] = interface.incoming_pr_frequency()
ifstats["outgoing_pr_frequency"] = interface.outgoing_pr_frequency()
ifstats["announce_rate_target"] = interface.announce_rate_target
ifstats["announce_rate_penalty"] = interface.announce_rate_penalty
ifstats["announce_rate_grace"] = interface.announce_rate_grace
ifstats["held_announces"] = len(interface.held_announces)
ifstats["burst_active"] = interface.ic_burst_active
ifstats["burst_activated"] = interface.ic_burst_activated
ifstats["pr_burst_active"] = interface.ic_pr_burst_active
ifstats["pr_burst_activated"] = interface.ic_pr_burst_activated
ifstats["status"] = interface.online
ifstats["mode"] = interface.mode
ifstats["incoming_pr_frequency"] = interface.incoming_pr_frequency()
ifstats["outgoing_pr_frequency"] = interface.outgoing_pr_frequency()
ifstats["announce_rate_target"] = interface.announce_rate_target
ifstats["announce_rate_penalty"] = interface.announce_rate_penalty
ifstats["announce_rate_grace"] = interface.announce_rate_grace
ifstats["held_announces"] = len(interface.held_announces)
ifstats["burst_active"] = interface.ic_burst_active
ifstats["burst_activated"] = interface.ic_burst_activated
ifstats["pr_burst_active"] = interface.ic_pr_burst_active
ifstats["pr_burst_activated"] = interface.ic_pr_burst_activated
ifstats["status"] = interface.online
ifstats["mode"] = interface.mode
interfaces.append(ifstats)
stats = {}
stats = {}
stats["interfaces"] = interfaces
stats["rxb"] = RNS.Transport.traffic_rxb
stats["txb"] = RNS.Transport.traffic_txb
stats["rxs"] = RNS.Transport.speed_rx
stats["txs"] = RNS.Transport.speed_tx
stats["rxb"] = RNS.Transport.traffic_rxb
stats["txb"] = RNS.Transport.traffic_txb
stats["rxs"] = RNS.Transport.speed_rx
stats["txs"] = RNS.Transport.speed_tx
if Reticulum.transport_enabled():
stats["transport_id"] = RNS.Transport.identity.hash
stats["network_id"] = RNS.Transport.network_identity.hash if RNS.Transport.network_identity else None
stats["transport_uptime"] = time.time()-RNS.Transport.start_time
if Reticulum.probe_destination_enabled():
stats["probe_responder"] = RNS.Transport.probe_destination.hash
else:
stats["probe_responder"] = None
if Reticulum.probe_destination_enabled(): stats["probe_responder"] = RNS.Transport.probe_destination.hash
else: stats["probe_responder"] = None
if importlib.util.find_spec('psutil') != None:
import psutil
@@ -1527,14 +1470,10 @@ class Reticulum:
for dst_hash in RNS.Transport.path_table:
path_hops = RNS.Transport.path_table[dst_hash][2]
if max_hops == None or path_hops <= max_hops:
entry = {
"hash": dst_hash,
"timestamp": RNS.Transport.path_table[dst_hash][0],
"via": RNS.Transport.path_table[dst_hash][1],
"hops": path_hops,
"expires": RNS.Transport.path_table[dst_hash][3],
"interface": str(RNS.Transport.path_table[dst_hash][5]),
}
entry = { "hash": dst_hash, "timestamp": RNS.Transport.path_table[dst_hash][0],
"via": RNS.Transport.path_table[dst_hash][1], "hops": path_hops,
"expires": RNS.Transport.path_table[dst_hash][3],
"interface": str(RNS.Transport.path_table[dst_hash][5]) }
path_table.append(entry)
return path_table
@@ -1862,6 +1801,10 @@ class Reticulum:
def static_transport_identity():
return Reticulum.__static_transport_identity
@staticmethod
def local_hops_delta():
return Reticulum.__local_hops_delta
# Default configuration file:
__default_rns_config__ = '''# This is the default Reticulum config file.
# You should probably edit it to include any additional,
+62 -18
View File
@@ -166,6 +166,7 @@ class Transport:
local_client_rssi_cache = []
local_client_snr_cache = []
local_client_q_cache = []
local_hops_delta = 0
LOCAL_CLIENT_CACHE_MAXSIZE = 512
pending_local_path_requests = {}
@@ -236,6 +237,8 @@ class Transport:
Transport.identity = RNS.Identity()
RNS.log(f"Initialized ephemeral transport identity {RNS.prettyhexrep(Transport.identity.hash)}", RNS.LOG_VERBOSE) if RNS.sl(RNS.LOG_VERBOSE) else None
if RNS.Reticulum.local_hops_delta(): Transport.local_hops_delta = (ord(os.urandom(1))%6)+2
packet_hashlist_path = RNS.Reticulum.storagepath+"/packet_hashlist"
if not Transport.owner.is_connected_to_shared_instance:
if os.path.isfile(packet_hashlist_path):
@@ -1147,7 +1150,7 @@ class Transport:
# Insert packet into transport
new_flags = (RNS.Packet.HEADER_2) << 6 | (Transport.TRANSPORT) << 4 | (packet.flags & 0b00001111)
new_raw = struct.pack("!B", new_flags)
new_raw += packet.raw[1:2]
new_raw += packet.raw[1:2] if not Transport.should_apply_delta(packet, outbound_interface) else struct.pack("!B", Transport.local_hops_delta)
new_raw += path_entry[IDX_PT_NEXT_HOP]
new_raw += packet.raw[2:]
packet_sent(packet)
@@ -1167,7 +1170,7 @@ class Transport:
# Insert packet into transport
new_flags = (RNS.Packet.HEADER_2) << 6 | (Transport.TRANSPORT) << 4 | (packet.flags & 0b00001111)
new_raw = struct.pack("!B", new_flags)
new_raw += packet.raw[1:2]
new_raw += packet.raw[1:2] if not Transport.should_apply_delta(packet, outbound_interface) else struct.pack("!B", Transport.local_hops_delta)
new_raw += path_entry[IDX_PT_NEXT_HOP]
new_raw += packet.raw[2:]
packet_sent(packet)
@@ -1180,7 +1183,9 @@ class Transport:
# simply transmit the packet directly on that one.
else:
packet_sent(packet)
Transport.transmit(outbound_interface, packet.raw)
if not Transport.should_apply_delta(packet, outbound_interface): raw = packet.raw
else: raw = Transport.mangle_hops(packet.raw, Transport.local_hops_delta)
Transport.transmit(outbound_interface, raw)
sent = True
# If we don't have a known path for the destination, we'll
@@ -1194,11 +1199,10 @@ class Transport:
should_transmit = True
if packet.destination.type == RNS.Destination.LINK:
if packet.destination.status == RNS.Link.CLOSED: should_transmit = False
if interface != packet.destination.attached_interface: should_transmit = False
if packet.destination.status == RNS.Link.CLOSED: continue
if interface != packet.destination.attached_interface: continue
if packet.attached_interface != None and interface != packet.attached_interface:
should_transmit = False
if packet.attached_interface != None and interface != packet.attached_interface: continue
if packet.packet_type == RNS.Packet.ANNOUNCE:
if packet.attached_interface == None:
@@ -1335,7 +1339,12 @@ class Transport:
Transport.add_packet_hash(packet.packet_hash)
stored_hash = True
Transport.transmit(interface, packet.raw)
if not Transport.should_apply_delta(packet, interface): raw = packet.raw
else:
if packet.packet_type != RNS.Packet.ANNOUNCE: raw = Transport.mangle_hops(packet.raw, Transport.local_hops_delta)
else: raw = Transport.mangle_hops(packet.raw, Transport.local_hops_delta, transport_insert=packet.header_type==RNS.Packet.HEADER_1)
Transport.transmit(interface, raw)
if packet.packet_type == RNS.Packet.ANNOUNCE: interface.sent_announce()
if packet.destination.type == RNS.Destination.PLAIN and packet.is_outbound_pr: interface.sent_path_request()
packet_sent(packet)
@@ -1343,6 +1352,17 @@ class Transport:
return sent
@staticmethod
def should_apply_delta(packet, interface):
return not Transport.owner.is_connected_to_shared_instance and packet.hops == 0 and Transport.local_hops_delta != 0 \
and not packet.destination.type == RNS.Destination.PLAIN and not packet.destination.type == RNS.Destination.GROUP \
and not interface in Transport.local_client_interfaces and not Transport.is_shared_instance(interface)
@staticmethod
def mangle_hops(raw, hops, transport_insert=False):
if not transport_insert: return raw[0:1]+struct.pack("!B", hops)+raw[2:]
else: return struct.pack("!B", ord(raw[0:1]) | (RNS.Packet.HEADER_2<<6) | (Transport.TRANSPORT<<4))+struct.pack("!B", hops)+Transport.identity.hash+raw[2:]
@staticmethod
def add_packet_hash(packet_hash):
if not Transport.owner.is_connected_to_shared_instance:
@@ -1530,6 +1550,8 @@ class Transport:
for_local_client_link = (packet.packet_type != RNS.Packet.ANNOUNCE) and (packet.destination_hash in Transport.link_table and Transport.link_table[packet.destination_hash][IDX_LT_RCVD_IF] in Transport.local_client_interfaces)
for_local_client_link |= (packet.packet_type != RNS.Packet.ANNOUNCE) and (packet.destination_hash in Transport.link_table and Transport.link_table[packet.destination_hash][IDX_LT_NH_IF] in Transport.local_client_interfaces)
proof_for_local_client = (packet.destination_hash in Transport.reverse_table) and (Transport.reverse_table[packet.destination_hash][IDX_RT_RCVD_IF] in Transport.local_client_interfaces)
to_local_client = for_local_client or for_local_client_link or proof_for_local_client
link_request_handled = False
# Plain broadcast packets from local clients are sent
# directly on all attached interfaces, since they are
@@ -1591,10 +1613,23 @@ class Transport:
new_raw += struct.pack("!B", packet.hops)
new_raw += packet.raw[(RNS.Identity.TRUNCATED_HASHLENGTH//8)+2:]
elif remaining_hops == 0:
# Just increase hop count and transmit
new_raw = packet.raw[0:1]
new_raw += struct.pack("!B", packet.hops)
new_raw += packet.raw[2:]
if to_local_client and RNS.Transport.local_hops_delta != 0:
if packet.header_type == RNS.Packet.HEADER_2:
# Strip transport headers and transmit
new_flags = (RNS.Packet.HEADER_1) << 6 | (Transport.BROADCAST) << 4 | (packet.flags & 0b00001111)
new_raw = struct.pack("!B", new_flags)
new_raw += struct.pack("!B", packet.hops)
new_raw += packet.raw[(RNS.Identity.TRUNCATED_HASHLENGTH//8)+2:]
else:
# Just increase hop count and transmit
new_raw = packet.raw[0:1]
new_raw += struct.pack("!B", packet.hops)
new_raw += packet.raw[2:]
else:
# Just increase hop count and transmit
new_raw = packet.raw[0:1]
new_raw += struct.pack("!B", packet.hops)
new_raw += packet.raw[2:]
outbound_interface = Transport.path_table[packet.destination_hash][IDX_PT_RVCD_IF]
@@ -1639,6 +1674,7 @@ class Transport:
proof_timeout ] # 8: Proof timeout timestamp
with Transport.link_table_lock: Transport.link_table[RNS.Link.link_id_from_lr_packet(packet)] = link_entry
link_request_handled = True
else:
# Entry format is
@@ -1648,6 +1684,7 @@ class Transport:
with Transport.reverse_table_lock: Transport.reverse_table[packet.getTruncatedHash()] = reverse_entry
if Transport.local_hops_delta != 0 and from_local_client and not to_local_client: new_raw = Transport.mangle_hops(new_raw, Transport.local_hops_delta)
Transport.transmit(outbound_interface, new_raw)
with Transport.path_table_lock: Transport.path_table[packet.destination_hash][IDX_PT_TIMESTAMP] = time.time()
@@ -1691,7 +1728,7 @@ class Transport:
Transport.add_packet_hash(packet.packet_hash)
new_raw = packet.raw[0:1]
new_raw += struct.pack("!B", packet.hops)
new_raw += struct.pack("!B", packet.hops if not from_local_client or Transport.local_hops_delta == 0 else Transport.local_hops_delta)
new_raw += packet.raw[2:]
Transport.transmit(outbound_interface, new_raw)
Transport.link_table[packet.destination_hash][IDX_LT_TIMESTAMP] = time.time()
@@ -2104,7 +2141,7 @@ class Transport:
RNS.trace_exception(e)
# Handling for link requests to local destinations
elif packet.packet_type == RNS.Packet.LINKREQUEST:
elif packet.packet_type == RNS.Packet.LINKREQUEST and not link_request_handled:
if packet.transport_id == None or packet.transport_id == Transport.identity.hash:
destination = None
with Transport.destinations_map_lock:
@@ -2207,7 +2244,7 @@ class Transport:
if peer_identity.validate(signature, signed_data):
RNS.log("Link request proof validated for transport via "+str(link_entry[IDX_LT_RCVD_IF]), RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None
new_raw = packet.raw[0:1]
new_raw += struct.pack("!B", packet.hops)
new_raw += struct.pack("!B", packet.hops if not from_local_client or Transport.local_hops_delta == 0 else Transport.local_hops_delta)
new_raw += packet.raw[2:]
Transport.link_table[packet.destination_hash][IDX_LT_VALIDATED] = True
Transport.transmit(link_entry[IDX_LT_RCVD_IF], new_raw)
@@ -2274,7 +2311,7 @@ class Transport:
if packet.receiving_interface == reverse_entry[IDX_RT_OUTB_IF]:
RNS.log("Proof received on correct interface, transporting it via "+str(reverse_entry[IDX_RT_RCVD_IF]), RNS.LOG_EXTREME) if RNS.sl(RNS.LOG_EXTREME) else None
new_raw = packet.raw[0:1]
new_raw += struct.pack("!B", packet.hops)
new_raw += struct.pack("!B", packet.hops if not from_local_client or Transport.local_hops_delta == 0 else Transport.local_hops_delta)
new_raw += packet.raw[2:]
Transport.transmit(reverse_entry[IDX_RT_RCVD_IF], new_raw)
else:
@@ -2672,7 +2709,8 @@ class Transport:
:returns: The destination hash as *bytes* for the next hop to the specified destination, or *None* if the next hop is unknown.
"""
with Transport.path_table_lock:
if destination_hash in Transport.path_table: return Transport.path_table[destination_hash][IDX_PT_NEXT_HOP]
if destination_hash in Transport.path_table: return Transport.path_table[destination_hash][IDX_PT_NEXT_HOP]
elif destination_hash in Transport.destinations_map: return Transport.identity.hash
else: return None
@staticmethod
@@ -2682,7 +2720,8 @@ class Transport:
:returns: The interface for the next hop to the specified destination, or *None* if the interface is unknown.
"""
with Transport.path_table_lock:
if destination_hash in Transport.path_table: return Transport.path_table[destination_hash][IDX_PT_RVCD_IF]
if destination_hash in Transport.path_table: return Transport.path_table[destination_hash][IDX_PT_RVCD_IF]
elif destination_hash in Transport.destinations_map: return RNS.Reticulum.get_instance().shared_instance_interface
else: return None
@staticmethod
@@ -3080,6 +3119,11 @@ class Transport:
else: return False
else: return False
@staticmethod
def is_shared_instance(interface):
if hasattr(interface, "is_local_shared_instance"): return True
else: return False
@staticmethod
def interface_to_shared_instance(interface):
if hasattr(interface, "is_connected_to_shared_instance"): return True