remove dangling spaces
This commit is contained in:
parent
ce9dc56048
commit
06b9555462
|
@ -1247,7 +1247,7 @@ This beta release brings a range of improvements and bugfixes.
|
||||||
- Improved documentation.
|
- Improved documentation.
|
||||||
- Improved request timeouts and handling.
|
- Improved request timeouts and handling.
|
||||||
- Improved link establishment.
|
- Improved link establishment.
|
||||||
- Improved resource transfer timing.
|
- Improved resource transfer timing.
|
||||||
|
|
||||||
**Fixed bugs**
|
**Fixed bugs**
|
||||||
- Fixed a race condition in inbound proof handling.
|
- Fixed a race condition in inbound proof handling.
|
||||||
|
|
|
@ -22,7 +22,7 @@ noble_gases = ["Helium", "Neon", "Argon", "Krypton", "Xenon", "Radon", "Oganesso
|
||||||
def program_setup(configpath):
|
def program_setup(configpath):
|
||||||
# We must first initialise Reticulum
|
# We must first initialise Reticulum
|
||||||
reticulum = RNS.Reticulum(configpath)
|
reticulum = RNS.Reticulum(configpath)
|
||||||
|
|
||||||
# Randomly create a new identity for our example
|
# Randomly create a new identity for our example
|
||||||
identity = RNS.Identity()
|
identity = RNS.Identity()
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ def program_setup(configpath):
|
||||||
|
|
||||||
# We register the announce handler with Reticulum
|
# We register the announce handler with Reticulum
|
||||||
RNS.Transport.register_announce_handler(announce_handler)
|
RNS.Transport.register_announce_handler(announce_handler)
|
||||||
|
|
||||||
# Everything's ready!
|
# Everything's ready!
|
||||||
# Let's hand over control to the announce loop
|
# Let's hand over control to the announce loop
|
||||||
announceLoop(destination_1, destination_2)
|
announceLoop(destination_1, destination_2)
|
||||||
|
@ -86,7 +86,7 @@ def announceLoop(destination_1, destination_2):
|
||||||
# know how to create messages directed towards it.
|
# know how to create messages directed towards it.
|
||||||
while True:
|
while True:
|
||||||
entered = input()
|
entered = input()
|
||||||
|
|
||||||
# Randomly select a fruit
|
# Randomly select a fruit
|
||||||
fruit = fruits[random.randint(0,len(fruits)-1)]
|
fruit = fruits[random.randint(0,len(fruits)-1)]
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ APP_NAME = "example_utilities"
|
||||||
def program_setup(configpath, channel=None):
|
def program_setup(configpath, channel=None):
|
||||||
# We must first initialise Reticulum
|
# We must first initialise Reticulum
|
||||||
reticulum = RNS.Reticulum(configpath)
|
reticulum = RNS.Reticulum(configpath)
|
||||||
|
|
||||||
# If the user did not select a "channel" we use
|
# If the user did not select a "channel" we use
|
||||||
# a default one called "public_information".
|
# a default one called "public_information".
|
||||||
# This "channel" is added to the destination name-
|
# This "channel" is added to the destination name-
|
||||||
|
@ -40,7 +40,7 @@ def program_setup(configpath, channel=None):
|
||||||
# We specify a callback that will get called every time
|
# We specify a callback that will get called every time
|
||||||
# the destination receives data.
|
# the destination receives data.
|
||||||
broadcast_destination.set_packet_callback(packet_callback)
|
broadcast_destination.set_packet_callback(packet_callback)
|
||||||
|
|
||||||
# Everything's ready!
|
# Everything's ready!
|
||||||
# Let's hand over control to the main loop
|
# Let's hand over control to the main loop
|
||||||
broadcastLoop(broadcast_destination)
|
broadcastLoop(broadcast_destination)
|
||||||
|
|
|
@ -35,7 +35,7 @@ latest_buffer = None
|
||||||
def server(configpath):
|
def server(configpath):
|
||||||
# We must first initialise Reticulum
|
# We must first initialise Reticulum
|
||||||
reticulum = RNS.Reticulum(configpath)
|
reticulum = RNS.Reticulum(configpath)
|
||||||
|
|
||||||
# Randomly create a new identity for our example
|
# Randomly create a new identity for our example
|
||||||
server_identity = RNS.Identity()
|
server_identity = RNS.Identity()
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ def client(destination_hexhash, configpath):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
|
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
|
||||||
)
|
)
|
||||||
|
|
||||||
destination_hash = bytes.fromhex(destination_hexhash)
|
destination_hash = bytes.fromhex(destination_hexhash)
|
||||||
except:
|
except:
|
||||||
RNS.log("Invalid destination entered. Check your input!\n")
|
RNS.log("Invalid destination entered. Check your input!\n")
|
||||||
|
@ -251,7 +251,7 @@ def link_closed(link):
|
||||||
RNS.log("The link was closed by the server, exiting now")
|
RNS.log("The link was closed by the server, exiting now")
|
||||||
else:
|
else:
|
||||||
RNS.log("Link closed, exiting now")
|
RNS.log("Link closed, exiting now")
|
||||||
|
|
||||||
RNS.Reticulum.exit_handler()
|
RNS.Reticulum.exit_handler()
|
||||||
time.sleep(1.5)
|
time.sleep(1.5)
|
||||||
os._exit(0)
|
os._exit(0)
|
||||||
|
|
|
@ -98,7 +98,7 @@ latest_client_link = None
|
||||||
def server(configpath):
|
def server(configpath):
|
||||||
# We must first initialise Reticulum
|
# We must first initialise Reticulum
|
||||||
reticulum = RNS.Reticulum(configpath)
|
reticulum = RNS.Reticulum(configpath)
|
||||||
|
|
||||||
# Randomly create a new identity for our link example
|
# Randomly create a new identity for our link example
|
||||||
server_identity = RNS.Identity()
|
server_identity = RNS.Identity()
|
||||||
|
|
||||||
|
@ -206,7 +206,7 @@ def client(destination_hexhash, configpath):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
|
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
|
||||||
)
|
)
|
||||||
|
|
||||||
destination_hash = bytes.fromhex(destination_hexhash)
|
destination_hash = bytes.fromhex(destination_hexhash)
|
||||||
except:
|
except:
|
||||||
RNS.log("Invalid destination entered. Check your input!\n")
|
RNS.log("Invalid destination entered. Check your input!\n")
|
||||||
|
@ -315,7 +315,7 @@ def link_closed(link):
|
||||||
RNS.log("The link was closed by the server, exiting now")
|
RNS.log("The link was closed by the server, exiting now")
|
||||||
else:
|
else:
|
||||||
RNS.log("Link closed, exiting now")
|
RNS.log("Link closed, exiting now")
|
||||||
|
|
||||||
RNS.Reticulum.exit_handler()
|
RNS.Reticulum.exit_handler()
|
||||||
time.sleep(1.5)
|
time.sleep(1.5)
|
||||||
os._exit(0)
|
os._exit(0)
|
||||||
|
|
|
@ -26,7 +26,7 @@ def server(configpath):
|
||||||
|
|
||||||
# We must first initialise Reticulum
|
# We must first initialise Reticulum
|
||||||
reticulum = RNS.Reticulum(configpath)
|
reticulum = RNS.Reticulum(configpath)
|
||||||
|
|
||||||
# Randomly create a new identity for our echo server
|
# Randomly create a new identity for our echo server
|
||||||
server_identity = RNS.Identity()
|
server_identity = RNS.Identity()
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ def server(configpath):
|
||||||
# create a "single" destination that can receive encrypted
|
# create a "single" destination that can receive encrypted
|
||||||
# messages. This way the client can send a request and be
|
# messages. This way the client can send a request and be
|
||||||
# certain that no-one else than this destination was able
|
# certain that no-one else than this destination was able
|
||||||
# to read it.
|
# to read it.
|
||||||
echo_destination = RNS.Destination(
|
echo_destination = RNS.Destination(
|
||||||
server_identity,
|
server_identity,
|
||||||
RNS.Destination.IN,
|
RNS.Destination.IN,
|
||||||
|
@ -50,7 +50,7 @@ def server(configpath):
|
||||||
# generate a proof for each incoming packet and transmit it
|
# generate a proof for each incoming packet and transmit it
|
||||||
# back to the sender of that packet.
|
# back to the sender of that packet.
|
||||||
echo_destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
|
echo_destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
|
||||||
|
|
||||||
# Tell the destination which function in our program to
|
# Tell the destination which function in our program to
|
||||||
# run when a packet is received. We do this so we can
|
# run when a packet is received. We do this so we can
|
||||||
# print a log message when the server receives a request
|
# print a log message when the server receives a request
|
||||||
|
@ -79,7 +79,7 @@ def announceLoop(destination):
|
||||||
|
|
||||||
def server_callback(message, packet):
|
def server_callback(message, packet):
|
||||||
global reticulum
|
global reticulum
|
||||||
|
|
||||||
# Tell the user that we received an echo request, and
|
# Tell the user that we received an echo request, and
|
||||||
# that we are going to send a reply to the requester.
|
# that we are going to send a reply to the requester.
|
||||||
# Sending the proof is handled automatically, since we
|
# Sending the proof is handled automatically, since we
|
||||||
|
@ -92,14 +92,14 @@ def server_callback(message, packet):
|
||||||
|
|
||||||
if reception_rssi != None:
|
if reception_rssi != None:
|
||||||
reception_stats += f" [RSSI {reception_rssi} dBm]"
|
reception_stats += f" [RSSI {reception_rssi} dBm]"
|
||||||
|
|
||||||
if reception_snr != None:
|
if reception_snr != None:
|
||||||
reception_stats += f" [SNR {reception_snr} dBm]"
|
reception_stats += f" [SNR {reception_snr} dBm]"
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if packet.rssi != None:
|
if packet.rssi != None:
|
||||||
reception_stats += f" [RSSI {packet.rssi} dBm]"
|
reception_stats += f" [RSSI {packet.rssi} dBm]"
|
||||||
|
|
||||||
if packet.snr != None:
|
if packet.snr != None:
|
||||||
reception_stats += f" [SNR {packet.snr} dB]"
|
reception_stats += f" [SNR {packet.snr} dB]"
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ def server_callback(message, packet):
|
||||||
# to run as a client
|
# to run as a client
|
||||||
def client(destination_hexhash, configpath, timeout=None):
|
def client(destination_hexhash, configpath, timeout=None):
|
||||||
global reticulum
|
global reticulum
|
||||||
|
|
||||||
# We need a binary representation of the destination
|
# We need a binary representation of the destination
|
||||||
# hash that was entered on the command line
|
# hash that was entered on the command line
|
||||||
try:
|
try:
|
||||||
|
@ -149,7 +149,7 @@ def client(destination_hexhash, configpath, timeout=None):
|
||||||
# command line.
|
# command line.
|
||||||
while True:
|
while True:
|
||||||
input()
|
input()
|
||||||
|
|
||||||
# Let's first check if RNS knows a path to the destination.
|
# Let's first check if RNS knows a path to the destination.
|
||||||
# If it does, we'll load the server identity and create a packet
|
# If it does, we'll load the server identity and create a packet
|
||||||
if RNS.Transport.has_path(destination_hash):
|
if RNS.Transport.has_path(destination_hash):
|
||||||
|
@ -230,7 +230,7 @@ def packet_delivered(receipt):
|
||||||
|
|
||||||
if reception_rssi != None:
|
if reception_rssi != None:
|
||||||
reception_stats += f" [RSSI {reception_rssi} dBm]"
|
reception_stats += f" [RSSI {reception_rssi} dBm]"
|
||||||
|
|
||||||
if reception_snr != None:
|
if reception_snr != None:
|
||||||
reception_stats += f" [SNR {reception_snr} dB]"
|
reception_stats += f" [SNR {reception_snr} dB]"
|
||||||
|
|
||||||
|
@ -238,7 +238,7 @@ def packet_delivered(receipt):
|
||||||
if receipt.proof_packet != None:
|
if receipt.proof_packet != None:
|
||||||
if receipt.proof_packet.rssi != None:
|
if receipt.proof_packet.rssi != None:
|
||||||
reception_stats += f" [RSSI {receipt.proof_packet.rssi} dBm]"
|
reception_stats += f" [RSSI {receipt.proof_packet.rssi} dBm]"
|
||||||
|
|
||||||
if receipt.proof_packet.snr != None:
|
if receipt.proof_packet.snr != None:
|
||||||
reception_stats += f" [SNR {receipt.proof_packet.snr} dB]"
|
reception_stats += f" [SNR {receipt.proof_packet.snr} dB]"
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ serve_path = None
|
||||||
def server(configpath, path):
|
def server(configpath, path):
|
||||||
# We must first initialise Reticulum
|
# We must first initialise Reticulum
|
||||||
reticulum = RNS.Reticulum(configpath)
|
reticulum = RNS.Reticulum(configpath)
|
||||||
|
|
||||||
# Randomly create a new identity for our file server
|
# Randomly create a new identity for our file server
|
||||||
server_identity = RNS.Identity()
|
server_identity = RNS.Identity()
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ def client_connected(link):
|
||||||
RNS.log("Too many files in served directory!", RNS.LOG_ERROR)
|
RNS.log("Too many files in served directory!", RNS.LOG_ERROR)
|
||||||
RNS.log("You should implement a function to split the filelist over multiple packets.", RNS.LOG_ERROR)
|
RNS.log("You should implement a function to split the filelist over multiple packets.", RNS.LOG_ERROR)
|
||||||
RNS.log("Hint: The client already supports it :)", RNS.LOG_ERROR)
|
RNS.log("Hint: The client already supports it :)", RNS.LOG_ERROR)
|
||||||
|
|
||||||
# After this, we're just going to keep the link
|
# After this, we're just going to keep the link
|
||||||
# open until the client requests a file. We'll
|
# open until the client requests a file. We'll
|
||||||
# configure a function that get's called when
|
# configure a function that get's called when
|
||||||
|
@ -147,7 +147,7 @@ def client_request(message, packet):
|
||||||
# read it and pack it as a resource
|
# read it and pack it as a resource
|
||||||
RNS.log(f"Client requested \"{filename}\"")
|
RNS.log(f"Client requested \"{filename}\"")
|
||||||
file = open(os.path.join(serve_path, filename), "rb")
|
file = open(os.path.join(serve_path, filename), "rb")
|
||||||
|
|
||||||
file_resource = RNS.Resource(
|
file_resource = RNS.Resource(
|
||||||
file,
|
file,
|
||||||
packet.link,
|
packet.link,
|
||||||
|
@ -220,7 +220,7 @@ def client(destination_hexhash, configpath):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
|
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
|
||||||
)
|
)
|
||||||
|
|
||||||
destination_hash = bytes.fromhex(destination_hexhash)
|
destination_hash = bytes.fromhex(destination_hexhash)
|
||||||
except:
|
except:
|
||||||
RNS.log("Invalid destination entered. Check your input!\n")
|
RNS.log("Invalid destination entered. Check your input!\n")
|
||||||
|
@ -291,7 +291,7 @@ def download(filename):
|
||||||
# packet receipt.
|
# packet receipt.
|
||||||
request_packet = RNS.Packet(server_link, filename.encode("utf-8"), create_receipt=False)
|
request_packet = RNS.Packet(server_link, filename.encode("utf-8"), create_receipt=False)
|
||||||
request_packet.send()
|
request_packet.send()
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
print(f"Requested \"{filename}\" from server, waiting for download to begin...")
|
print(f"Requested \"{filename}\" from server, waiting for download to begin...")
|
||||||
menu_mode = "download_started"
|
menu_mode = "download_started"
|
||||||
|
@ -474,7 +474,7 @@ def link_closed(link):
|
||||||
RNS.log("The link was closed by the server, exiting now")
|
RNS.log("The link was closed by the server, exiting now")
|
||||||
else:
|
else:
|
||||||
RNS.log("Link closed, exiting now")
|
RNS.log("Link closed, exiting now")
|
||||||
|
|
||||||
RNS.Reticulum.exit_handler()
|
RNS.Reticulum.exit_handler()
|
||||||
time.sleep(1.5)
|
time.sleep(1.5)
|
||||||
os._exit(0)
|
os._exit(0)
|
||||||
|
@ -486,17 +486,17 @@ def link_closed(link):
|
||||||
def download_began(resource):
|
def download_began(resource):
|
||||||
global menu_mode, current_download, download_started, transfer_size, file_size
|
global menu_mode, current_download, download_started, transfer_size, file_size
|
||||||
current_download = resource
|
current_download = resource
|
||||||
|
|
||||||
if download_started == 0:
|
if download_started == 0:
|
||||||
download_started = time.time()
|
download_started = time.time()
|
||||||
|
|
||||||
transfer_size += resource.size
|
transfer_size += resource.size
|
||||||
file_size = resource.total_size
|
file_size = resource.total_size
|
||||||
|
|
||||||
menu_mode = "downloading"
|
menu_mode = "downloading"
|
||||||
|
|
||||||
# When the download concludes, successfully
|
# When the download concludes, successfully
|
||||||
# or not, we'll update our menu state and
|
# or not, we'll update our menu state and
|
||||||
# inform the user about how it all went.
|
# inform the user about how it all went.
|
||||||
def download_concluded(resource):
|
def download_concluded(resource):
|
||||||
global menu_mode, current_filename, download_started, download_finished, download_time
|
global menu_mode, current_filename, download_started, download_finished, download_time
|
||||||
|
|
|
@ -27,7 +27,7 @@ latest_client_link = None
|
||||||
def server(configpath):
|
def server(configpath):
|
||||||
# We must first initialise Reticulum
|
# We must first initialise Reticulum
|
||||||
reticulum = RNS.Reticulum(configpath)
|
reticulum = RNS.Reticulum(configpath)
|
||||||
|
|
||||||
# Randomly create a new identity for our link example
|
# Randomly create a new identity for our link example
|
||||||
server_identity = RNS.Identity()
|
server_identity = RNS.Identity()
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ def server_packet_received(message, packet):
|
||||||
text = message.decode("utf-8")
|
text = message.decode("utf-8")
|
||||||
|
|
||||||
RNS.log(f"Received data from {remote_peer}: {text}")
|
RNS.log(f"Received data from {remote_peer}: {text}")
|
||||||
|
|
||||||
reply_text = f"I received \"{text}\" over the link from {remote_peer}"
|
reply_text = f"I received \"{text}\" over the link from {remote_peer}"
|
||||||
reply_data = reply_text.encode("utf-8")
|
reply_data = reply_text.encode("utf-8")
|
||||||
RNS.Packet(latest_client_link, reply_data).send()
|
RNS.Packet(latest_client_link, reply_data).send()
|
||||||
|
@ -239,7 +239,7 @@ def link_closed(link):
|
||||||
RNS.log("The link was closed by the server, exiting now")
|
RNS.log("The link was closed by the server, exiting now")
|
||||||
else:
|
else:
|
||||||
RNS.log("Link closed, exiting now")
|
RNS.log("Link closed, exiting now")
|
||||||
|
|
||||||
RNS.Reticulum.exit_handler()
|
RNS.Reticulum.exit_handler()
|
||||||
time.sleep(1.5)
|
time.sleep(1.5)
|
||||||
os._exit(0)
|
os._exit(0)
|
||||||
|
|
|
@ -27,7 +27,7 @@ latest_client_link = None
|
||||||
def server(configpath):
|
def server(configpath):
|
||||||
# We must first initialise Reticulum
|
# We must first initialise Reticulum
|
||||||
reticulum = RNS.Reticulum(configpath)
|
reticulum = RNS.Reticulum(configpath)
|
||||||
|
|
||||||
# Randomly create a new identity for our link example
|
# Randomly create a new identity for our link example
|
||||||
server_identity = RNS.Identity()
|
server_identity = RNS.Identity()
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ def server_packet_received(message, packet):
|
||||||
# that connected.
|
# that connected.
|
||||||
text = message.decode("utf-8")
|
text = message.decode("utf-8")
|
||||||
RNS.log(f"Received data on the link: {text}")
|
RNS.log(f"Received data on the link: {text}")
|
||||||
|
|
||||||
reply_text = f"I received \"{text}\" over the link"
|
reply_text = f"I received \"{text}\" over the link"
|
||||||
reply_data = reply_text.encode("utf-8")
|
reply_data = reply_text.encode("utf-8")
|
||||||
RNS.Packet(latest_client_link, reply_data).send()
|
RNS.Packet(latest_client_link, reply_data).send()
|
||||||
|
@ -113,7 +113,7 @@ def client(destination_hexhash, configpath):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
|
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
|
||||||
)
|
)
|
||||||
|
|
||||||
destination_hash = bytes.fromhex(destination_hexhash)
|
destination_hash = bytes.fromhex(destination_hexhash)
|
||||||
except:
|
except:
|
||||||
RNS.log("Invalid destination entered. Check your input!\n")
|
RNS.log("Invalid destination entered. Check your input!\n")
|
||||||
|
@ -217,7 +217,7 @@ def link_closed(link):
|
||||||
RNS.log("The link was closed by the server, exiting now")
|
RNS.log("The link was closed by the server, exiting now")
|
||||||
else:
|
else:
|
||||||
RNS.log("Link closed, exiting now")
|
RNS.log("Link closed, exiting now")
|
||||||
|
|
||||||
RNS.Reticulum.exit_handler()
|
RNS.Reticulum.exit_handler()
|
||||||
time.sleep(1.5)
|
time.sleep(1.5)
|
||||||
os._exit(0)
|
os._exit(0)
|
||||||
|
|
|
@ -17,7 +17,7 @@ APP_NAME = "example_utilities"
|
||||||
def program_setup(configpath):
|
def program_setup(configpath):
|
||||||
# We must first initialise Reticulum
|
# We must first initialise Reticulum
|
||||||
reticulum = RNS.Reticulum(configpath)
|
reticulum = RNS.Reticulum(configpath)
|
||||||
|
|
||||||
# Randomly create a new identity for our example
|
# Randomly create a new identity for our example
|
||||||
identity = RNS.Identity()
|
identity = RNS.Identity()
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ def program_setup(configpath):
|
||||||
# tries to communicate with the destination know whether their
|
# tries to communicate with the destination know whether their
|
||||||
# communication was received correctly.
|
# communication was received correctly.
|
||||||
destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
|
destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
|
||||||
|
|
||||||
# Everything's ready!
|
# Everything's ready!
|
||||||
# Let's hand over control to the announce loop
|
# Let's hand over control to the announce loop
|
||||||
announceLoop(destination)
|
announceLoop(destination)
|
||||||
|
|
|
@ -28,7 +28,7 @@ def server(configpath):
|
||||||
|
|
||||||
# TODO: Remove
|
# TODO: Remove
|
||||||
RNS.loglevel = RNS.LOG_DEBUG
|
RNS.loglevel = RNS.LOG_DEBUG
|
||||||
|
|
||||||
# Randomly create a new identity for our echo server
|
# Randomly create a new identity for our echo server
|
||||||
server_identity = RNS.Identity()
|
server_identity = RNS.Identity()
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ def server(configpath):
|
||||||
# create a "single" destination that can receive encrypted
|
# create a "single" destination that can receive encrypted
|
||||||
# messages. This way the client can send a request and be
|
# messages. This way the client can send a request and be
|
||||||
# certain that no-one else than this destination was able
|
# certain that no-one else than this destination was able
|
||||||
# to read it.
|
# to read it.
|
||||||
echo_destination = RNS.Destination(
|
echo_destination = RNS.Destination(
|
||||||
server_identity,
|
server_identity,
|
||||||
RNS.Destination.IN,
|
RNS.Destination.IN,
|
||||||
|
@ -61,7 +61,7 @@ def server(configpath):
|
||||||
# generate a proof for each incoming packet and transmit it
|
# generate a proof for each incoming packet and transmit it
|
||||||
# back to the sender of that packet.
|
# back to the sender of that packet.
|
||||||
echo_destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
|
echo_destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
|
||||||
|
|
||||||
# Tell the destination which function in our program to
|
# Tell the destination which function in our program to
|
||||||
# run when a packet is received. We do this so we can
|
# run when a packet is received. We do this so we can
|
||||||
# print a log message when the server receives a request
|
# print a log message when the server receives a request
|
||||||
|
@ -90,7 +90,7 @@ def announceLoop(destination):
|
||||||
|
|
||||||
def server_callback(message, packet):
|
def server_callback(message, packet):
|
||||||
global reticulum
|
global reticulum
|
||||||
|
|
||||||
# Tell the user that we received an echo request, and
|
# Tell the user that we received an echo request, and
|
||||||
# that we are going to send a reply to the requester.
|
# that we are going to send a reply to the requester.
|
||||||
# Sending the proof is handled automatically, since we
|
# Sending the proof is handled automatically, since we
|
||||||
|
@ -103,14 +103,14 @@ def server_callback(message, packet):
|
||||||
|
|
||||||
if reception_rssi != None:
|
if reception_rssi != None:
|
||||||
reception_stats += f" [RSSI {reception_rssi} dBm]"
|
reception_stats += f" [RSSI {reception_rssi} dBm]"
|
||||||
|
|
||||||
if reception_snr != None:
|
if reception_snr != None:
|
||||||
reception_stats += f" [SNR {reception_snr} dBm]"
|
reception_stats += f" [SNR {reception_snr} dBm]"
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if packet.rssi != None:
|
if packet.rssi != None:
|
||||||
reception_stats += f" [RSSI {packet.rssi} dBm]"
|
reception_stats += f" [RSSI {packet.rssi} dBm]"
|
||||||
|
|
||||||
if packet.snr != None:
|
if packet.snr != None:
|
||||||
reception_stats += f" [SNR {packet.snr} dB]"
|
reception_stats += f" [SNR {packet.snr} dB]"
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ def server_callback(message, packet):
|
||||||
# to run as a client
|
# to run as a client
|
||||||
def client(destination_hexhash, configpath, timeout=None):
|
def client(destination_hexhash, configpath, timeout=None):
|
||||||
global reticulum
|
global reticulum
|
||||||
|
|
||||||
# We need a binary representation of the destination
|
# We need a binary representation of the destination
|
||||||
# hash that was entered on the command line
|
# hash that was entered on the command line
|
||||||
try:
|
try:
|
||||||
|
@ -160,7 +160,7 @@ def client(destination_hexhash, configpath, timeout=None):
|
||||||
# command line.
|
# command line.
|
||||||
while True:
|
while True:
|
||||||
input()
|
input()
|
||||||
|
|
||||||
# Let's first check if RNS knows a path to the destination.
|
# Let's first check if RNS knows a path to the destination.
|
||||||
# If it does, we'll load the server identity and create a packet
|
# If it does, we'll load the server identity and create a packet
|
||||||
if RNS.Transport.has_path(destination_hash):
|
if RNS.Transport.has_path(destination_hash):
|
||||||
|
@ -242,7 +242,7 @@ def packet_delivered(receipt):
|
||||||
|
|
||||||
if reception_rssi != None:
|
if reception_rssi != None:
|
||||||
reception_stats += f" [RSSI {reception_rssi} dBm]"
|
reception_stats += f" [RSSI {reception_rssi} dBm]"
|
||||||
|
|
||||||
if reception_snr != None:
|
if reception_snr != None:
|
||||||
reception_stats += f" [SNR {reception_snr} dB]"
|
reception_stats += f" [SNR {reception_snr} dB]"
|
||||||
|
|
||||||
|
@ -250,7 +250,7 @@ def packet_delivered(receipt):
|
||||||
if receipt.proof_packet != None:
|
if receipt.proof_packet != None:
|
||||||
if receipt.proof_packet.rssi != None:
|
if receipt.proof_packet.rssi != None:
|
||||||
reception_stats += f" [RSSI {receipt.proof_packet.rssi} dBm]"
|
reception_stats += f" [RSSI {receipt.proof_packet.rssi} dBm]"
|
||||||
|
|
||||||
if receipt.proof_packet.snr != None:
|
if receipt.proof_packet.snr != None:
|
||||||
reception_stats += f" [SNR {receipt.proof_packet.snr} dB]"
|
reception_stats += f" [SNR {receipt.proof_packet.snr} dB]"
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ def random_text_generator(path, data, request_id, link_id, remote_identity, requ
|
||||||
def server(configpath):
|
def server(configpath):
|
||||||
# We must first initialise Reticulum
|
# We must first initialise Reticulum
|
||||||
reticulum = RNS.Reticulum(configpath)
|
reticulum = RNS.Reticulum(configpath)
|
||||||
|
|
||||||
# Randomly create a new identity for our link example
|
# Randomly create a new identity for our link example
|
||||||
server_identity = RNS.Identity()
|
server_identity = RNS.Identity()
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ def client(destination_hexhash, configpath):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
|
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
|
||||||
)
|
)
|
||||||
|
|
||||||
destination_hash = bytes.fromhex(destination_hexhash)
|
destination_hash = bytes.fromhex(destination_hexhash)
|
||||||
except:
|
except:
|
||||||
RNS.log("Invalid destination entered. Check your input!\n")
|
RNS.log("Invalid destination entered. Check your input!\n")
|
||||||
|
@ -223,7 +223,7 @@ def link_closed(link):
|
||||||
RNS.log("The link was closed by the server, exiting now")
|
RNS.log("The link was closed by the server, exiting now")
|
||||||
else:
|
else:
|
||||||
RNS.log("Link closed, exiting now")
|
RNS.log("Link closed, exiting now")
|
||||||
|
|
||||||
RNS.Reticulum.exit_handler()
|
RNS.Reticulum.exit_handler()
|
||||||
time.sleep(1.5)
|
time.sleep(1.5)
|
||||||
os._exit(0)
|
os._exit(0)
|
||||||
|
|
|
@ -36,7 +36,7 @@ printed = False
|
||||||
def server(configpath):
|
def server(configpath):
|
||||||
# We must first initialise Reticulum
|
# We must first initialise Reticulum
|
||||||
reticulum = RNS.Reticulum(configpath)
|
reticulum = RNS.Reticulum(configpath)
|
||||||
|
|
||||||
# Randomly create a new identity for our link example
|
# Randomly create a new identity for our link example
|
||||||
server_identity = RNS.Identity()
|
server_identity = RNS.Identity()
|
||||||
|
|
||||||
|
@ -113,9 +113,9 @@ def size_str(num, suffix='B'):
|
||||||
|
|
||||||
def server_packet_received(message, packet):
|
def server_packet_received(message, packet):
|
||||||
global latest_client_link, first_packet_at, last_packet_at, received_data, rc, data_cap
|
global latest_client_link, first_packet_at, last_packet_at, received_data, rc, data_cap
|
||||||
|
|
||||||
received_data += len(packet.data)
|
received_data += len(packet.data)
|
||||||
|
|
||||||
rc += 1
|
rc += 1
|
||||||
if rc >= 50:
|
if rc >= 50:
|
||||||
RNS.log(size_str(received_data))
|
RNS.log(size_str(received_data))
|
||||||
|
@ -127,7 +127,7 @@ def server_packet_received(message, packet):
|
||||||
rc = 0
|
rc = 0
|
||||||
|
|
||||||
last_packet_at = time.time()
|
last_packet_at = time.time()
|
||||||
|
|
||||||
# Print statistics
|
# Print statistics
|
||||||
download_time = last_packet_at-first_packet_at
|
download_time = last_packet_at-first_packet_at
|
||||||
hours, rem = divmod(download_time, 3600)
|
hours, rem = divmod(download_time, 3600)
|
||||||
|
@ -169,7 +169,7 @@ def client(destination_hexhash, configpath):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
|
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
|
||||||
)
|
)
|
||||||
|
|
||||||
destination_hash = bytes.fromhex(destination_hexhash)
|
destination_hash = bytes.fromhex(destination_hexhash)
|
||||||
except:
|
except:
|
||||||
RNS.log("Invalid destination entered. Check your input!\n")
|
RNS.log("Invalid destination entered. Check your input!\n")
|
||||||
|
@ -280,7 +280,7 @@ def link_closed(link):
|
||||||
RNS.log("The link was closed by the server, exiting now")
|
RNS.log("The link was closed by the server, exiting now")
|
||||||
else:
|
else:
|
||||||
RNS.log("Link closed, exiting now")
|
RNS.log("Link closed, exiting now")
|
||||||
|
|
||||||
RNS.Reticulum.exit_handler()
|
RNS.Reticulum.exit_handler()
|
||||||
|
|
||||||
time.sleep(1.5)
|
time.sleep(1.5)
|
||||||
|
|
18
README.md
18
README.md
|
@ -109,8 +109,8 @@ network, and vice versa.
|
||||||
|
|
||||||
## How do I get started?
|
## How do I get started?
|
||||||
The best way to get started with the Reticulum Network Stack depends on what
|
The best way to get started with the Reticulum Network Stack depends on what
|
||||||
you want to do. For full details and examples, have a look at the
|
you want to do. For full details and examples, have a look at the
|
||||||
[Getting Started Fast](https://markqvist.github.io/Reticulum/manual/gettingstartedfast.html)
|
[Getting Started Fast](https://markqvist.github.io/Reticulum/manual/gettingstartedfast.html)
|
||||||
section of the [Reticulum Manual](https://markqvist.github.io/Reticulum/manual/).
|
section of the [Reticulum Manual](https://markqvist.github.io/Reticulum/manual/).
|
||||||
|
|
||||||
To simply install Reticulum and related utilities on your system, the easiest way is via `pip`.
|
To simply install Reticulum and related utilities on your system, the easiest way is via `pip`.
|
||||||
|
@ -143,15 +143,15 @@ creating a more complex configuration.
|
||||||
|
|
||||||
If you have an old version of `pip` on your system, you may need to upgrade it first with `pip install pip --upgrade`. If you no not already have `pip` installed, you can install it using the package manager of your system with `sudo apt install python3-pip` or similar.
|
If you have an old version of `pip` on your system, you may need to upgrade it first with `pip install pip --upgrade`. If you no not already have `pip` installed, you can install it using the package manager of your system with `sudo apt install python3-pip` or similar.
|
||||||
|
|
||||||
For more detailed examples on how to expand communication over many mediums such
|
For more detailed examples on how to expand communication over many mediums such
|
||||||
as packet radio or LoRa, serial ports, or over fast IP links and the Internet using
|
as packet radio or LoRa, serial ports, or over fast IP links and the Internet using
|
||||||
the UDP and TCP interfaces, take a look at the [Supported Interfaces](https://markqvist.github.io/Reticulum/manual/interfaces.html)
|
the UDP and TCP interfaces, take a look at the [Supported Interfaces](https://markqvist.github.io/Reticulum/manual/interfaces.html)
|
||||||
section of the [Reticulum Manual](https://markqvist.github.io/Reticulum/manual/).
|
section of the [Reticulum Manual](https://markqvist.github.io/Reticulum/manual/).
|
||||||
|
|
||||||
## Included Utilities
|
## Included Utilities
|
||||||
Reticulum includes a range of useful utilities for managing your networks,
|
Reticulum includes a range of useful utilities for managing your networks,
|
||||||
viewing status and information, and other tasks. You can read more about these
|
viewing status and information, and other tasks. You can read more about these
|
||||||
programs in the [Included Utility Programs](https://markqvist.github.io/Reticulum/manual/using.html#included-utility-programs)
|
programs in the [Included Utility Programs](https://markqvist.github.io/Reticulum/manual/using.html#included-utility-programs)
|
||||||
section of the [Reticulum Manual](https://markqvist.github.io/Reticulum/manual/).
|
section of the [Reticulum Manual](https://markqvist.github.io/Reticulum/manual/).
|
||||||
|
|
||||||
- The system daemon `rnsd` for running Reticulum as an always-available service
|
- The system daemon `rnsd` for running Reticulum as an always-available service
|
||||||
|
@ -242,7 +242,7 @@ The testnet is just that, an informal network for testing and experimenting.
|
||||||
It will be up most of the time, and anyone can join, but it also means that
|
It will be up most of the time, and anyone can join, but it also means that
|
||||||
there's no guarantees for service availability.
|
there's no guarantees for service availability.
|
||||||
|
|
||||||
It probably goes without saying, but *don't use the testnet entry-points as
|
It probably goes without saying, but *don't use the testnet entry-points as
|
||||||
hardcoded or default interfaces in any applications you ship to users*. When
|
hardcoded or default interfaces in any applications you ship to users*. When
|
||||||
shipping applications, the best practice is to provide your own default
|
shipping applications, the best practice is to provide your own default
|
||||||
connectivity solutions, if needed and applicable, or in most cases, simply
|
connectivity solutions, if needed and applicable, or in most cases, simply
|
||||||
|
|
|
@ -244,7 +244,7 @@ class RawChannelWriter(RawIOBase, AbstractContextManager):
|
||||||
processed_length = len(chunk)
|
processed_length = len(chunk)
|
||||||
|
|
||||||
message = StreamDataMessage(self._stream_id, chunk, self._eof, comp_success)
|
message = StreamDataMessage(self._stream_id, chunk, self._eof, comp_success)
|
||||||
|
|
||||||
self._channel.send(message)
|
self._channel.send(message)
|
||||||
return processed_length
|
return processed_length
|
||||||
|
|
||||||
|
|
|
@ -136,7 +136,7 @@ class MessageBase(abc.ABC):
|
||||||
MSGTYPE = None
|
MSGTYPE = None
|
||||||
"""
|
"""
|
||||||
Defines a unique identifier for a message class.
|
Defines a unique identifier for a message class.
|
||||||
|
|
||||||
* Must be unique within all classes registered with a ``Channel``
|
* Must be unique within all classes registered with a ``Channel``
|
||||||
* Must be less than ``0xf000``. Values greater than or equal to ``0xf000`` are reserved.
|
* Must be less than ``0xf000``. Values greater than or equal to ``0xf000`` are reserved.
|
||||||
"""
|
"""
|
||||||
|
@ -247,11 +247,11 @@ class Channel(contextlib.AbstractContextManager):
|
||||||
|
|
||||||
# The maximum window size for transfers on fast links
|
# The maximum window size for transfers on fast links
|
||||||
WINDOW_MAX_FAST = 48
|
WINDOW_MAX_FAST = 48
|
||||||
|
|
||||||
# For calculating maps and guard segments, this
|
# For calculating maps and guard segments, this
|
||||||
# must be set to the global maximum window.
|
# must be set to the global maximum window.
|
||||||
WINDOW_MAX = WINDOW_MAX_FAST
|
WINDOW_MAX = WINDOW_MAX_FAST
|
||||||
|
|
||||||
# If the fast rate is sustained for this many request
|
# If the fast rate is sustained for this many request
|
||||||
# rounds, the fast link window size will be allowed.
|
# rounds, the fast link window size will be allowed.
|
||||||
FAST_RATE_THRESHOLD = 10
|
FAST_RATE_THRESHOLD = 10
|
||||||
|
@ -380,21 +380,21 @@ class Channel(contextlib.AbstractContextManager):
|
||||||
def _emplace_envelope(self, envelope: Envelope, ring: collections.deque[Envelope]) -> bool:
|
def _emplace_envelope(self, envelope: Envelope, ring: collections.deque[Envelope]) -> bool:
|
||||||
with self._lock:
|
with self._lock:
|
||||||
i = 0
|
i = 0
|
||||||
|
|
||||||
for existing in ring:
|
for existing in ring:
|
||||||
|
|
||||||
if envelope.sequence == existing.sequence:
|
if envelope.sequence == existing.sequence:
|
||||||
RNS.log(f"Envelope: Emplacement of duplicate envelope with sequence {envelope.sequence}", RNS.LOG_EXTREME)
|
RNS.log(f"Envelope: Emplacement of duplicate envelope with sequence {envelope.sequence}", RNS.LOG_EXTREME)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if envelope.sequence < existing.sequence and not (self._next_rx_sequence - envelope.sequence) > (Channel.SEQ_MAX//2):
|
if envelope.sequence < existing.sequence and not (self._next_rx_sequence - envelope.sequence) > (Channel.SEQ_MAX//2):
|
||||||
ring.insert(i, envelope)
|
ring.insert(i, envelope)
|
||||||
|
|
||||||
envelope.tracked = True
|
envelope.tracked = True
|
||||||
return True
|
return True
|
||||||
|
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
envelope.tracked = True
|
envelope.tracked = True
|
||||||
ring.append(envelope)
|
ring.append(envelope)
|
||||||
|
|
||||||
|
@ -449,7 +449,7 @@ class Channel(contextlib.AbstractContextManager):
|
||||||
m = e.unpack(self._message_factories)
|
m = e.unpack(self._message_factories)
|
||||||
else:
|
else:
|
||||||
m = e.message
|
m = e.message
|
||||||
|
|
||||||
self._rx_ring.remove(e)
|
self._rx_ring.remove(e)
|
||||||
self._run_callbacks(m)
|
self._run_callbacks(m)
|
||||||
|
|
||||||
|
@ -468,7 +468,7 @@ class Channel(contextlib.AbstractContextManager):
|
||||||
with self._lock:
|
with self._lock:
|
||||||
outstanding = 0
|
outstanding = 0
|
||||||
for envelope in self._tx_ring:
|
for envelope in self._tx_ring:
|
||||||
if envelope.outlet == self._outlet:
|
if envelope.outlet == self._outlet:
|
||||||
if not envelope.packet or not self._outlet.get_packet_state(envelope.packet) == MessageState.MSGSTATE_DELIVERED:
|
if not envelope.packet or not self._outlet.get_packet_state(envelope.packet) == MessageState.MSGSTATE_DELIVERED:
|
||||||
outstanding += 1
|
outstanding += 1
|
||||||
|
|
||||||
|
@ -508,7 +508,7 @@ class Channel(contextlib.AbstractContextManager):
|
||||||
# TODO: Remove at some point
|
# TODO: Remove at some point
|
||||||
# RNS.log("Increased "+str(self)+" max window to "+str(self.window_max), RNS.LOG_DEBUG)
|
# RNS.log("Increased "+str(self)+" max window to "+str(self.window_max), RNS.LOG_DEBUG)
|
||||||
# RNS.log("Increased "+str(self)+" min window to "+str(self.window_min), RNS.LOG_DEBUG)
|
# RNS.log("Increased "+str(self)+" min window to "+str(self.window_min), RNS.LOG_DEBUG)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.fast_rate_rounds += 1
|
self.fast_rate_rounds += 1
|
||||||
if self.window_max < Channel.WINDOW_MAX_FAST and self.fast_rate_rounds == Channel.FAST_RATE_THRESHOLD:
|
if self.window_max < Channel.WINDOW_MAX_FAST and self.fast_rate_rounds == Channel.FAST_RATE_THRESHOLD:
|
||||||
|
@ -581,7 +581,7 @@ class Channel(contextlib.AbstractContextManager):
|
||||||
with self._lock:
|
with self._lock:
|
||||||
if not self.is_ready_to_send():
|
if not self.is_ready_to_send():
|
||||||
raise ChannelException(CEType.ME_LINK_NOT_READY, f"Link is not ready")
|
raise ChannelException(CEType.ME_LINK_NOT_READY, f"Link is not ready")
|
||||||
|
|
||||||
envelope = Envelope(self._outlet, message=message, sequence=self._next_sequence)
|
envelope = Envelope(self._outlet, message=message, sequence=self._next_sequence)
|
||||||
self._next_sequence = (self._next_sequence + 1) % Channel.SEQ_MODULUS
|
self._next_sequence = (self._next_sequence + 1) % Channel.SEQ_MODULUS
|
||||||
self._emplace_envelope(envelope, self._tx_ring)
|
self._emplace_envelope(envelope, self._tx_ring)
|
||||||
|
@ -592,7 +592,7 @@ class Channel(contextlib.AbstractContextManager):
|
||||||
envelope.pack()
|
envelope.pack()
|
||||||
if len(envelope.raw) > self._outlet.mdu:
|
if len(envelope.raw) > self._outlet.mdu:
|
||||||
raise ChannelException(CEType.ME_TOO_BIG, f"Packed message too big for packet: {len(envelope.raw)} > {self._outlet.mdu}")
|
raise ChannelException(CEType.ME_TOO_BIG, f"Packed message too big for packet: {len(envelope.raw)} > {self._outlet.mdu}")
|
||||||
|
|
||||||
envelope.packet = self._outlet.send(envelope.raw)
|
envelope.packet = self._outlet.send(envelope.raw)
|
||||||
envelope.tries += 1
|
envelope.tries += 1
|
||||||
self._outlet.set_packet_delivered_callback(envelope.packet, self._packet_delivered)
|
self._outlet.set_packet_delivered_callback(envelope.packet, self._packet_delivered)
|
||||||
|
|
|
@ -25,7 +25,7 @@ import RNS.vendor.platformutils as pu
|
||||||
|
|
||||||
if cp.PROVIDER == cp.PROVIDER_INTERNAL:
|
if cp.PROVIDER == cp.PROVIDER_INTERNAL:
|
||||||
from .aes import AES
|
from .aes import AES
|
||||||
|
|
||||||
elif cp.PROVIDER == cp.PROVIDER_PYCA:
|
elif cp.PROVIDER == cp.PROVIDER_PYCA:
|
||||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ class AES_128_CBC:
|
||||||
cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
|
cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
|
||||||
else:
|
else:
|
||||||
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
|
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
|
||||||
|
|
||||||
encryptor = cipher.encryptor()
|
encryptor = cipher.encryptor()
|
||||||
ciphertext = encryptor.update(plaintext) + encryptor.finalize()
|
ciphertext = encryptor.update(plaintext) + encryptor.finalize()
|
||||||
return ciphertext
|
return ciphertext
|
||||||
|
|
|
@ -49,7 +49,7 @@ class Fernet():
|
||||||
|
|
||||||
if len(key) != 32:
|
if len(key) != 32:
|
||||||
raise ValueError(f"Token key must be 32 bytes, not {len(key)}")
|
raise ValueError(f"Token key must be 32 bytes, not {len(key)}")
|
||||||
|
|
||||||
self._signing_key = key[:16]
|
self._signing_key = key[:16]
|
||||||
self._encryption_key = key[16:]
|
self._encryption_key = key[16:]
|
||||||
|
|
||||||
|
|
|
@ -48,34 +48,34 @@ class sha256:
|
||||||
_h = (0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
_h = (0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
||||||
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19)
|
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19)
|
||||||
_output_size = 8
|
_output_size = 8
|
||||||
|
|
||||||
blocksize = 1
|
blocksize = 1
|
||||||
block_size = 64
|
block_size = 64
|
||||||
digest_size = 32
|
digest_size = 32
|
||||||
|
|
||||||
def __init__(self, m=None):
|
def __init__(self, m=None):
|
||||||
self._buffer = b""
|
self._buffer = b""
|
||||||
self._counter = 0
|
self._counter = 0
|
||||||
|
|
||||||
if m is not None:
|
if m is not None:
|
||||||
if type(m) is not bytes:
|
if type(m) is not bytes:
|
||||||
raise TypeError(f'{self.__class__.__name__}() argument 1 must be bytes, not {type(m).__name__}')
|
raise TypeError(f'{self.__class__.__name__}() argument 1 must be bytes, not {type(m).__name__}')
|
||||||
self.update(m)
|
self.update(m)
|
||||||
|
|
||||||
def _rotr(self, x, y):
|
def _rotr(self, x, y):
|
||||||
return ((x >> y) | (x << (32-y))) & 0xFFFFFFFF
|
return ((x >> y) | (x << (32-y))) & 0xFFFFFFFF
|
||||||
|
|
||||||
def _sha256_process(self, c):
|
def _sha256_process(self, c):
|
||||||
w = [0]*64
|
w = [0]*64
|
||||||
w[0:16] = struct.unpack('!16L', c)
|
w[0:16] = struct.unpack('!16L', c)
|
||||||
|
|
||||||
for i in range(16, 64):
|
for i in range(16, 64):
|
||||||
s0 = self._rotr(w[i-15], 7) ^ self._rotr(w[i-15], 18) ^ (w[i-15] >> 3)
|
s0 = self._rotr(w[i-15], 7) ^ self._rotr(w[i-15], 18) ^ (w[i-15] >> 3)
|
||||||
s1 = self._rotr(w[i-2], 17) ^ self._rotr(w[i-2], 19) ^ (w[i-2] >> 10)
|
s1 = self._rotr(w[i-2], 17) ^ self._rotr(w[i-2], 19) ^ (w[i-2] >> 10)
|
||||||
w[i] = (w[i-16] + s0 + w[i-7] + s1) & 0xFFFFFFFF
|
w[i] = (w[i-16] + s0 + w[i-7] + s1) & 0xFFFFFFFF
|
||||||
|
|
||||||
a,b,c,d,e,f,g,h = self._h
|
a,b,c,d,e,f,g,h = self._h
|
||||||
|
|
||||||
for i in range(64):
|
for i in range(64):
|
||||||
s0 = self._rotr(a, 2) ^ self._rotr(a, 13) ^ self._rotr(a, 22)
|
s0 = self._rotr(a, 2) ^ self._rotr(a, 13) ^ self._rotr(a, 22)
|
||||||
maj = (a & b) ^ (a & c) ^ (b & c)
|
maj = (a & b) ^ (a & c) ^ (b & c)
|
||||||
|
@ -83,7 +83,7 @@ class sha256:
|
||||||
s1 = self._rotr(e, 6) ^ self._rotr(e, 11) ^ self._rotr(e, 25)
|
s1 = self._rotr(e, 6) ^ self._rotr(e, 11) ^ self._rotr(e, 25)
|
||||||
ch = (e & f) ^ ((~e) & g)
|
ch = (e & f) ^ ((~e) & g)
|
||||||
t1 = h + s1 + ch + self._k[i] + w[i]
|
t1 = h + s1 + ch + self._k[i] + w[i]
|
||||||
|
|
||||||
h = g
|
h = g
|
||||||
g = f
|
g = f
|
||||||
f = e
|
f = e
|
||||||
|
@ -92,38 +92,38 @@ class sha256:
|
||||||
c = b
|
c = b
|
||||||
b = a
|
b = a
|
||||||
a = (t1 + t2) & 0xFFFFFFFF
|
a = (t1 + t2) & 0xFFFFFFFF
|
||||||
|
|
||||||
self._h = [(x+y) & 0xFFFFFFFF for x,y in zip(self._h, [a,b,c,d,e,f,g,h])]
|
self._h = [(x+y) & 0xFFFFFFFF for x,y in zip(self._h, [a,b,c,d,e,f,g,h])]
|
||||||
|
|
||||||
def update(self, m):
|
def update(self, m):
|
||||||
if not m:
|
if not m:
|
||||||
return
|
return
|
||||||
|
|
||||||
if type(m) is not bytes:
|
if type(m) is not bytes:
|
||||||
raise TypeError(f'{sys._getframe().f_code.co_name}() argument 1 must be bytes, not {type(m).__name__}')
|
raise TypeError(f'{sys._getframe().f_code.co_name}() argument 1 must be bytes, not {type(m).__name__}')
|
||||||
|
|
||||||
self._buffer += m
|
self._buffer += m
|
||||||
self._counter += len(m)
|
self._counter += len(m)
|
||||||
|
|
||||||
while len(self._buffer) >= 64:
|
while len(self._buffer) >= 64:
|
||||||
self._sha256_process(self._buffer[:64])
|
self._sha256_process(self._buffer[:64])
|
||||||
self._buffer = self._buffer[64:]
|
self._buffer = self._buffer[64:]
|
||||||
|
|
||||||
def digest(self):
|
def digest(self):
|
||||||
mdi = self._counter & 0x3F
|
mdi = self._counter & 0x3F
|
||||||
length = struct.pack('!Q', self._counter<<3)
|
length = struct.pack('!Q', self._counter<<3)
|
||||||
|
|
||||||
if mdi < 56:
|
if mdi < 56:
|
||||||
padlen = 55-mdi
|
padlen = 55-mdi
|
||||||
else:
|
else:
|
||||||
padlen = 119-mdi
|
padlen = 119-mdi
|
||||||
|
|
||||||
r = self.copy()
|
r = self.copy()
|
||||||
r.update(b'\x80'+(b'\x00'*padlen)+length)
|
r.update(b'\x80'+(b'\x00'*padlen)+length)
|
||||||
return b''.join([struct.pack('!L', i) for i in r._h[:self._output_size]])
|
return b''.join([struct.pack('!L', i) for i in r._h[:self._output_size]])
|
||||||
|
|
||||||
def hexdigest(self):
|
def hexdigest(self):
|
||||||
return self.digest().encode('hex')
|
return self.digest().encode('hex')
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
return copy.deepcopy(self)
|
return copy.deepcopy(self)
|
||||||
|
|
|
@ -138,9 +138,9 @@ class X25519PrivateKey:
|
||||||
peer_public_key = X25519PublicKey.from_public_bytes(peer_public_key)
|
peer_public_key = X25519PublicKey.from_public_bytes(peer_public_key)
|
||||||
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
|
||||||
shared = _pack_number(_raw_curve25519(peer_public_key.x, self.a))
|
shared = _pack_number(_raw_curve25519(peer_public_key.x, self.a))
|
||||||
|
|
||||||
end = time.time()
|
end = time.time()
|
||||||
duration = end-start
|
duration = end-start
|
||||||
|
|
||||||
|
@ -150,7 +150,7 @@ class X25519PrivateKey:
|
||||||
if end > X25519PrivateKey.T_CLEAR:
|
if end > X25519PrivateKey.T_CLEAR:
|
||||||
X25519PrivateKey.T_CLEAR = end + X25519PrivateKey.DELAY_WINDOW
|
X25519PrivateKey.T_CLEAR = end + X25519PrivateKey.DELAY_WINDOW
|
||||||
X25519PrivateKey.T_MAX = 0
|
X25519PrivateKey.T_MAX = 0
|
||||||
|
|
||||||
if duration < X25519PrivateKey.T_MAX or duration < X25519PrivateKey.MIN_EXEC_TIME:
|
if duration < X25519PrivateKey.T_MAX or duration < X25519PrivateKey.MIN_EXEC_TIME:
|
||||||
target = start+X25519PrivateKey.T_MAX
|
target = start+X25519PrivateKey.T_MAX
|
||||||
|
|
||||||
|
|
|
@ -144,7 +144,7 @@ class AES:
|
||||||
return matrix2bytes(state)
|
return matrix2bytes(state)
|
||||||
|
|
||||||
|
|
||||||
# will encrypt the entire data
|
# will encrypt the entire data
|
||||||
def encrypt(self, plaintext, iv):
|
def encrypt(self, plaintext, iv):
|
||||||
"""
|
"""
|
||||||
Encrypts `plaintext` using CBC mode and PKCS#7 padding, with the given
|
Encrypts `plaintext` using CBC mode and PKCS#7 padding, with the given
|
||||||
|
@ -173,7 +173,7 @@ class AES:
|
||||||
return b''.join(ciphertext_blocks)
|
return b''.join(ciphertext_blocks)
|
||||||
|
|
||||||
|
|
||||||
# will decrypt the entire data
|
# will decrypt the entire data
|
||||||
def decrypt(self, ciphertext, iv):
|
def decrypt(self, ciphertext, iv):
|
||||||
"""
|
"""
|
||||||
Decrypts `ciphertext` using CBC mode and PKCS#7 padding, with the given
|
Decrypts `ciphertext` using CBC mode and PKCS#7 padding, with the given
|
||||||
|
@ -188,7 +188,7 @@ class AES:
|
||||||
for ciphertext_block in split_blocks(ciphertext):
|
for ciphertext_block in split_blocks(ciphertext):
|
||||||
# in CBC mode every block is XOR'd with the previous block
|
# in CBC mode every block is XOR'd with the previous block
|
||||||
xorred = xor_bytes(previous, self._decrypt_block(ciphertext_block))
|
xorred = xor_bytes(previous, self._decrypt_block(ciphertext_block))
|
||||||
|
|
||||||
# append plaintext
|
# append plaintext
|
||||||
plaintext_blocks.append(xorred)
|
plaintext_blocks.append(xorred)
|
||||||
previous = ciphertext_block
|
previous = ciphertext_block
|
||||||
|
@ -223,7 +223,7 @@ def test():
|
||||||
print("Single Block Tests")
|
print("Single Block Tests")
|
||||||
print("------------------")
|
print("------------------")
|
||||||
print(f"iv: {iv.hex()}")
|
print(f"iv: {iv.hex()}")
|
||||||
|
|
||||||
print(f"plain text: '{single_block_text.decode()}'")
|
print(f"plain text: '{single_block_text.decode()}'")
|
||||||
ciphertext_block = _aes._encrypt_block(single_block_text)
|
ciphertext_block = _aes._encrypt_block(single_block_text)
|
||||||
plaintext_block = _aes._decrypt_block(ciphertext_block)
|
plaintext_block = _aes._decrypt_block(ciphertext_block)
|
||||||
|
@ -268,4 +268,4 @@ def test():
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# test AES class
|
# test AES class
|
||||||
test()
|
test()
|
||||||
|
|
|
@ -140,7 +140,7 @@ class Destination:
|
||||||
|
|
||||||
def __init__(self, identity, direction, type, app_name, *aspects):
|
def __init__(self, identity, direction, type, app_name, *aspects):
|
||||||
# Check input values and build name string
|
# Check input values and build name string
|
||||||
if "." in app_name: raise ValueError("Dots can't be used in app names")
|
if "." in app_name: raise ValueError("Dots can't be used in app names")
|
||||||
if not type in Destination.types: raise ValueError("Unknown destination type")
|
if not type in Destination.types: raise ValueError("Unknown destination type")
|
||||||
if not direction in Destination.directions: raise ValueError("Unknown destination direction")
|
if not direction in Destination.directions: raise ValueError("Unknown destination direction")
|
||||||
|
|
||||||
|
@ -241,7 +241,7 @@ class Destination:
|
||||||
|
|
||||||
if self.direction != Destination.IN:
|
if self.direction != Destination.IN:
|
||||||
raise TypeError("Only IN destination types can be announced")
|
raise TypeError("Only IN destination types can be announced")
|
||||||
|
|
||||||
ratchet = b""
|
ratchet = b""
|
||||||
now = time.time()
|
now = time.time()
|
||||||
stale_responses = []
|
stale_responses = []
|
||||||
|
@ -264,7 +264,7 @@ class Destination:
|
||||||
# multiple available paths, and to choose the best one.
|
# multiple available paths, and to choose the best one.
|
||||||
RNS.log(f"Using cached announce data for answering path request with tag {RNS.prettyhexrep(tag)}", RNS.LOG_EXTREME)
|
RNS.log(f"Using cached announce data for answering path request with tag {RNS.prettyhexrep(tag)}", RNS.LOG_EXTREME)
|
||||||
announce_data = self.path_responses[tag][1]
|
announce_data = self.path_responses[tag][1]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
destination_hash = self.hash
|
destination_hash = self.hash
|
||||||
random_hash = RNS.Identity.get_random_hash()[0:5]+int(time.time()).to_bytes(5, "big")
|
random_hash = RNS.Identity.get_random_hash()[0:5]+int(time.time()).to_bytes(5, "big")
|
||||||
|
@ -281,7 +281,7 @@ class Destination:
|
||||||
returned_app_data = self.default_app_data()
|
returned_app_data = self.default_app_data()
|
||||||
if isinstance(returned_app_data, bytes):
|
if isinstance(returned_app_data, bytes):
|
||||||
app_data = returned_app_data
|
app_data = returned_app_data
|
||||||
|
|
||||||
signed_data = self.hash+self.identity.get_public_key()+self.name_hash+random_hash+ratchet
|
signed_data = self.hash+self.identity.get_public_key()+self.name_hash+random_hash+ratchet
|
||||||
if app_data != None:
|
if app_data != None:
|
||||||
signed_data += app_data
|
signed_data += app_data
|
||||||
|
|
|
@ -137,7 +137,7 @@ class Identity:
|
||||||
# save, but the only changes. It might be possible to
|
# save, but the only changes. It might be possible to
|
||||||
# simply overwrite on exit now that every local client
|
# simply overwrite on exit now that every local client
|
||||||
# disconnect triggers a data persist.
|
# disconnect triggers a data persist.
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if hasattr(Identity, "saving_known_destinations"):
|
if hasattr(Identity, "saving_known_destinations"):
|
||||||
wait_interval = 0.2
|
wait_interval = 0.2
|
||||||
|
@ -279,7 +279,7 @@ class Identity:
|
||||||
ratchet_data = {"ratchet": ratchet, "received": time.time()}
|
ratchet_data = {"ratchet": ratchet, "received": time.time()}
|
||||||
|
|
||||||
ratchetdir = f"{RNS.Reticulum.storagepath}/ratchets"
|
ratchetdir = f"{RNS.Reticulum.storagepath}/ratchets"
|
||||||
|
|
||||||
if not os.path.isdir(ratchetdir):
|
if not os.path.isdir(ratchetdir):
|
||||||
os.makedirs(ratchetdir)
|
os.makedirs(ratchetdir)
|
||||||
|
|
||||||
|
@ -290,7 +290,7 @@ class Identity:
|
||||||
ratchet_file.close()
|
ratchet_file.close()
|
||||||
os.replace(outpath, finalpath)
|
os.replace(outpath, finalpath)
|
||||||
|
|
||||||
|
|
||||||
threading.Thread(target=persist_job, daemon=True).start()
|
threading.Thread(target=persist_job, daemon=True).start()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -337,7 +337,7 @@ class Identity:
|
||||||
Identity.known_ratchets[destination_hash] = ratchet_data["ratchet"]
|
Identity.known_ratchets[destination_hash] = ratchet_data["ratchet"]
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log(f"An error occurred while loading ratchet data for {RNS.prettyhexrep(destination_hash)} from storage.", RNS.LOG_ERROR)
|
RNS.log(f"An error occurred while loading ratchet data for {RNS.prettyhexrep(destination_hash)} from storage.", RNS.LOG_ERROR)
|
||||||
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
|
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
|
@ -444,7 +444,7 @@ class Identity:
|
||||||
RNS.log(f"Received invalid announce for {RNS.prettyhexrep(destination_hash)}: Invalid signature.", RNS.LOG_DEBUG)
|
RNS.log(f"Received invalid announce for {RNS.prettyhexrep(destination_hash)}: Invalid signature.", RNS.LOG_DEBUG)
|
||||||
del announced_identity
|
del announced_identity
|
||||||
return False
|
return False
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log(f"Error occurred while validating announce. The contained exception was: {e}", RNS.LOG_ERROR)
|
RNS.log(f"Error occurred while validating announce. The contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
return False
|
return False
|
||||||
|
@ -567,7 +567,7 @@ class Identity:
|
||||||
self.prv = X25519PrivateKey.from_private_bytes(self.prv_bytes)
|
self.prv = X25519PrivateKey.from_private_bytes(self.prv_bytes)
|
||||||
self.sig_prv_bytes = prv_bytes[Identity.KEYSIZE//8//2:]
|
self.sig_prv_bytes = prv_bytes[Identity.KEYSIZE//8//2:]
|
||||||
self.sig_prv = Ed25519PrivateKey.from_private_bytes(self.sig_prv_bytes)
|
self.sig_prv = Ed25519PrivateKey.from_private_bytes(self.sig_prv_bytes)
|
||||||
|
|
||||||
self.pub = self.prv.public_key()
|
self.pub = self.prv.public_key()
|
||||||
self.pub_bytes = self.pub.public_bytes()
|
self.pub_bytes = self.pub.public_bytes()
|
||||||
|
|
||||||
|
@ -640,7 +640,7 @@ class Identity:
|
||||||
target_public_key = self.pub
|
target_public_key = self.pub
|
||||||
|
|
||||||
shared_key = ephemeral_key.exchange(target_public_key)
|
shared_key = ephemeral_key.exchange(target_public_key)
|
||||||
|
|
||||||
derived_key = RNS.Cryptography.hkdf(
|
derived_key = RNS.Cryptography.hkdf(
|
||||||
length=32,
|
length=32,
|
||||||
derive_from=shared_key,
|
derive_from=shared_key,
|
||||||
|
@ -690,9 +690,9 @@ class Identity:
|
||||||
plaintext = fernet.decrypt(ciphertext)
|
plaintext = fernet.decrypt(ciphertext)
|
||||||
if ratchet_id_receiver:
|
if ratchet_id_receiver:
|
||||||
ratchet_id_receiver.latest_ratchet_id = ratchet_id
|
ratchet_id_receiver.latest_ratchet_id = ratchet_id
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -720,7 +720,7 @@ class Identity:
|
||||||
RNS.log(f"Decryption by {RNS.prettyhexrep(self.hash)} failed: {e}", RNS.LOG_DEBUG)
|
RNS.log(f"Decryption by {RNS.prettyhexrep(self.hash)} failed: {e}", RNS.LOG_DEBUG)
|
||||||
if ratchet_id_receiver:
|
if ratchet_id_receiver:
|
||||||
ratchet_id_receiver.latest_ratchet_id = None
|
ratchet_id_receiver.latest_ratchet_id = None
|
||||||
|
|
||||||
return plaintext;
|
return plaintext;
|
||||||
else:
|
else:
|
||||||
RNS.log("Decryption failed because the token size was invalid.", RNS.LOG_DEBUG)
|
RNS.log("Decryption failed because the token size was invalid.", RNS.LOG_DEBUG)
|
||||||
|
@ -739,7 +739,7 @@ class Identity:
|
||||||
"""
|
"""
|
||||||
if self.sig_prv != None:
|
if self.sig_prv != None:
|
||||||
try:
|
try:
|
||||||
return self.sig_prv.sign(message)
|
return self.sig_prv.sign(message)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log(f"The identity {self} could not sign the requested message. The contained exception was: {e}", RNS.LOG_ERROR)
|
RNS.log(f"The identity {self} could not sign the requested message. The contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
raise e
|
raise e
|
||||||
|
@ -770,7 +770,7 @@ class Identity:
|
||||||
proof_data = signature
|
proof_data = signature
|
||||||
else:
|
else:
|
||||||
proof_data = packet.packet_hash + signature
|
proof_data = packet.packet_hash + signature
|
||||||
|
|
||||||
if destination == None:
|
if destination == None:
|
||||||
destination = packet.generate_proof_destination()
|
destination = packet.generate_proof_destination()
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,7 @@ class AX25KISSInterface(Interface):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.HW_MTU = 564
|
self.HW_MTU = 564
|
||||||
|
|
||||||
self.pyserial = serial
|
self.pyserial = serial
|
||||||
self.serial = None
|
self.serial = None
|
||||||
self.owner = owner
|
self.owner = owner
|
||||||
|
@ -343,7 +343,7 @@ class AX25KISSInterface(Interface):
|
||||||
self.online = False
|
self.online = False
|
||||||
RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
|
RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
|
RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
|
||||||
|
|
||||||
if RNS.Reticulum.panic_on_interface_error:
|
if RNS.Reticulum.panic_on_interface_error:
|
||||||
RNS.panic()
|
RNS.panic()
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ class KISSInterface(Interface):
|
||||||
|
|
||||||
from usbserial4a import serial4a as serial
|
from usbserial4a import serial4a as serial
|
||||||
self.parity = "N"
|
self.parity = "N"
|
||||||
|
|
||||||
else:
|
else:
|
||||||
RNS.log("Could not load USB serial module for Android, KISS interface cannot be created.", RNS.LOG_CRITICAL)
|
RNS.log("Could not load USB serial module for Android, KISS interface cannot be created.", RNS.LOG_CRITICAL)
|
||||||
RNS.log("You can install this module by issuing: pip install usbserial4a", RNS.LOG_CRITICAL)
|
RNS.log("You can install this module by issuing: pip install usbserial4a", RNS.LOG_CRITICAL)
|
||||||
|
@ -83,9 +83,9 @@ class KISSInterface(Interface):
|
||||||
raise SystemError("Android-specific interface was used on non-Android OS")
|
raise SystemError("Android-specific interface was used on non-Android OS")
|
||||||
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.HW_MTU = 564
|
self.HW_MTU = 564
|
||||||
|
|
||||||
if beacon_data == None:
|
if beacon_data == None:
|
||||||
beacon_data = ""
|
beacon_data = ""
|
||||||
|
|
||||||
|
@ -172,7 +172,7 @@ class KISSInterface(Interface):
|
||||||
self.serial.timeout = 0.1
|
self.serial.timeout = 0.1
|
||||||
elif vid == 0x10C4:
|
elif vid == 0x10C4:
|
||||||
# Hardware parameters for SiLabs CP210x @ 115200 baud
|
# Hardware parameters for SiLabs CP210x @ 115200 baud
|
||||||
self.serial.DEFAULT_READ_BUFFER_SIZE = 64
|
self.serial.DEFAULT_READ_BUFFER_SIZE = 64
|
||||||
self.serial.USB_READ_TIMEOUT_MILLIS = 12
|
self.serial.USB_READ_TIMEOUT_MILLIS = 12
|
||||||
self.serial.timeout = 0.012
|
self.serial.timeout = 0.012
|
||||||
elif vid == 0x1A86 and pid == 0x55D4:
|
elif vid == 0x1A86 and pid == 0x55D4:
|
||||||
|
@ -352,7 +352,7 @@ class KISSInterface(Interface):
|
||||||
data_buffer = data_buffer+bytes([byte])
|
data_buffer = data_buffer+bytes([byte])
|
||||||
elif (command == KISS.CMD_READY):
|
elif (command == KISS.CMD_READY):
|
||||||
self.process_queue()
|
self.process_queue()
|
||||||
|
|
||||||
if got == 0:
|
if got == 0:
|
||||||
time_since_last = int(time.time()*1000) - last_read_ms
|
time_since_last = int(time.time()*1000) - last_read_ms
|
||||||
if len(data_buffer) > 0 and time_since_last > self.timeout:
|
if len(data_buffer) > 0 and time_since_last > self.timeout:
|
||||||
|
@ -379,7 +379,7 @@ class KISSInterface(Interface):
|
||||||
self.online = False
|
self.online = False
|
||||||
RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
|
RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
|
RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
|
||||||
|
|
||||||
if RNS.Reticulum.panic_on_interface_error:
|
if RNS.Reticulum.panic_on_interface_error:
|
||||||
RNS.panic()
|
RNS.panic()
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ class KISS():
|
||||||
FESC = 0xDB
|
FESC = 0xDB
|
||||||
TFEND = 0xDC
|
TFEND = 0xDC
|
||||||
TFESC = 0xDD
|
TFESC = 0xDD
|
||||||
|
|
||||||
CMD_UNKNOWN = 0xFE
|
CMD_UNKNOWN = 0xFE
|
||||||
CMD_DATA = 0x00
|
CMD_DATA = 0x00
|
||||||
CMD_FREQUENCY = 0x01
|
CMD_FREQUENCY = 0x01
|
||||||
|
@ -78,11 +78,11 @@ class KISS():
|
||||||
|
|
||||||
DETECT_REQ = 0x73
|
DETECT_REQ = 0x73
|
||||||
DETECT_RESP = 0x46
|
DETECT_RESP = 0x46
|
||||||
|
|
||||||
RADIO_STATE_OFF = 0x00
|
RADIO_STATE_OFF = 0x00
|
||||||
RADIO_STATE_ON = 0x01
|
RADIO_STATE_ON = 0x01
|
||||||
RADIO_STATE_ASK = 0xFF
|
RADIO_STATE_ASK = 0xFF
|
||||||
|
|
||||||
CMD_ERROR = 0x90
|
CMD_ERROR = 0x90
|
||||||
ERROR_INITRADIO = 0x01
|
ERROR_INITRADIO = 0x01
|
||||||
ERROR_TXFAILED = 0x02
|
ERROR_TXFAILED = 0x02
|
||||||
|
@ -194,7 +194,7 @@ class AndroidBluetoothManager():
|
||||||
if self.rfcomm_reader != None:
|
if self.rfcomm_reader != None:
|
||||||
self.rfcomm_reader.close()
|
self.rfcomm_reader.close()
|
||||||
self.rfcomm_reader = None
|
self.rfcomm_reader = None
|
||||||
|
|
||||||
if self.rfcomm_writer != None:
|
if self.rfcomm_writer != None:
|
||||||
self.rfcomm_writer.close()
|
self.rfcomm_writer.close()
|
||||||
self.rfcomm_writer = None
|
self.rfcomm_writer = None
|
||||||
|
@ -371,7 +371,7 @@ class RNodeInterface(Interface):
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.bt_manager = None
|
self.bt_manager = None
|
||||||
|
|
||||||
else:
|
else:
|
||||||
RNS.log("Could not load USB serial module for Android, RNode interface cannot be created.", RNS.LOG_CRITICAL)
|
RNS.log("Could not load USB serial module for Android, RNode interface cannot be created.", RNS.LOG_CRITICAL)
|
||||||
RNS.log("You can install this module by issuing: pip install usbserial4a", RNS.LOG_CRITICAL)
|
RNS.log("You can install this module by issuing: pip install usbserial4a", RNS.LOG_CRITICAL)
|
||||||
|
@ -382,7 +382,7 @@ class RNodeInterface(Interface):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.HW_MTU = 508
|
self.HW_MTU = 508
|
||||||
|
|
||||||
self.pyserial = serial
|
self.pyserial = serial
|
||||||
self.serial = None
|
self.serial = None
|
||||||
self.owner = owner
|
self.owner = owner
|
||||||
|
@ -561,7 +561,7 @@ class RNodeInterface(Interface):
|
||||||
# self.ble = BLEConnection(owner=self, target_name=self.ble_name, target_bt_addr=self.ble_addr)
|
# self.ble = BLEConnection(owner=self, target_name=self.ble_name, target_bt_addr=self.ble_addr)
|
||||||
# self.serial = self.ble
|
# self.serial = self.ble
|
||||||
# RNS.log(f"New connection instance: "+str(self.ble))
|
# RNS.log(f"New connection instance: "+str(self.ble))
|
||||||
|
|
||||||
def open_port(self):
|
def open_port(self):
|
||||||
if not self.use_ble:
|
if not self.use_ble:
|
||||||
if self.port != None:
|
if self.port != None:
|
||||||
|
@ -602,7 +602,7 @@ class RNodeInterface(Interface):
|
||||||
self.serial.timeout = 0.1
|
self.serial.timeout = 0.1
|
||||||
elif vid == 0x10C4:
|
elif vid == 0x10C4:
|
||||||
# Hardware parameters for SiLabs CP210x @ 115200 baud
|
# Hardware parameters for SiLabs CP210x @ 115200 baud
|
||||||
self.serial.DEFAULT_READ_BUFFER_SIZE = 64
|
self.serial.DEFAULT_READ_BUFFER_SIZE = 64
|
||||||
self.serial.USB_READ_TIMEOUT_MILLIS = 12
|
self.serial.USB_READ_TIMEOUT_MILLIS = 12
|
||||||
self.serial.timeout = 0.012
|
self.serial.timeout = 0.012
|
||||||
elif vid == 0x1A86 and pid == 0x55D4:
|
elif vid == 0x1A86 and pid == 0x55D4:
|
||||||
|
@ -687,14 +687,14 @@ class RNodeInterface(Interface):
|
||||||
RNS.log(f"After configuring {self}, the reported radio parameters did not match your configuration.", RNS.LOG_ERROR)
|
RNS.log(f"After configuring {self}, the reported radio parameters did not match your configuration.", RNS.LOG_ERROR)
|
||||||
RNS.log("Make sure that your hardware actually supports the parameters specified in the configuration", RNS.LOG_ERROR)
|
RNS.log("Make sure that your hardware actually supports the parameters specified in the configuration", RNS.LOG_ERROR)
|
||||||
RNS.log("Aborting RNode startup", RNS.LOG_ERROR)
|
RNS.log("Aborting RNode startup", RNS.LOG_ERROR)
|
||||||
|
|
||||||
if self.serial != None:
|
if self.serial != None:
|
||||||
self.serial.close()
|
self.serial.close()
|
||||||
if self.bt_manager != None:
|
if self.bt_manager != None:
|
||||||
self.bt_manager.close()
|
self.bt_manager.close()
|
||||||
|
|
||||||
raise OSError("RNode interface did not pass configuration validation")
|
raise OSError("RNode interface did not pass configuration validation")
|
||||||
|
|
||||||
|
|
||||||
def initRadio(self):
|
def initRadio(self):
|
||||||
self.setFrequency()
|
self.setFrequency()
|
||||||
|
@ -702,22 +702,22 @@ class RNodeInterface(Interface):
|
||||||
|
|
||||||
self.setBandwidth()
|
self.setBandwidth()
|
||||||
time.sleep(0.15)
|
time.sleep(0.15)
|
||||||
|
|
||||||
self.setTXPower()
|
self.setTXPower()
|
||||||
time.sleep(0.15)
|
time.sleep(0.15)
|
||||||
|
|
||||||
self.setSpreadingFactor()
|
self.setSpreadingFactor()
|
||||||
time.sleep(0.15)
|
time.sleep(0.15)
|
||||||
|
|
||||||
self.setCodingRate()
|
self.setCodingRate()
|
||||||
time.sleep(0.15)
|
time.sleep(0.15)
|
||||||
|
|
||||||
self.setSTALock()
|
self.setSTALock()
|
||||||
time.sleep(0.15)
|
time.sleep(0.15)
|
||||||
|
|
||||||
self.setLTALock()
|
self.setLTALock()
|
||||||
time.sleep(0.15)
|
time.sleep(0.15)
|
||||||
|
|
||||||
self.setRadioState(KISS.RADIO_STATE_ON)
|
self.setRadioState(KISS.RADIO_STATE_ON)
|
||||||
time.sleep(0.15)
|
time.sleep(0.15)
|
||||||
|
|
||||||
|
@ -735,7 +735,7 @@ class RNodeInterface(Interface):
|
||||||
written = self.write_mux(kiss_command)
|
written = self.write_mux(kiss_command)
|
||||||
if written != len(kiss_command):
|
if written != len(kiss_command):
|
||||||
raise OSError("An IO error occurred while sending host left command to device")
|
raise OSError("An IO error occurred while sending host left command to device")
|
||||||
|
|
||||||
def enable_bluetooth(self):
|
def enable_bluetooth(self):
|
||||||
kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x01, KISS.FEND])
|
kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x01, KISS.FEND])
|
||||||
written = self.write_mux(kiss_command)
|
written = self.write_mux(kiss_command)
|
||||||
|
@ -788,7 +788,7 @@ class RNodeInterface(Interface):
|
||||||
data = line_byte+line_data
|
data = line_byte+line_data
|
||||||
escaped_data = KISS.escape(data)
|
escaped_data = KISS.escape(data)
|
||||||
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FB_WRITE])+escaped_data+bytes([KISS.FEND])
|
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FB_WRITE])+escaped_data+bytes([KISS.FEND])
|
||||||
|
|
||||||
written = self.write_mux(kiss_command)
|
written = self.write_mux(kiss_command)
|
||||||
if written != len(kiss_command):
|
if written != len(kiss_command):
|
||||||
raise OSError("An IO error occurred while writing framebuffer data device")
|
raise OSError("An IO error occurred while writing framebuffer data device")
|
||||||
|
@ -883,7 +883,7 @@ class RNodeInterface(Interface):
|
||||||
if (self.maj_version >= RNodeInterface.REQUIRED_FW_VER_MAJ):
|
if (self.maj_version >= RNodeInterface.REQUIRED_FW_VER_MAJ):
|
||||||
if (self.min_version >= RNodeInterface.REQUIRED_FW_VER_MIN):
|
if (self.min_version >= RNodeInterface.REQUIRED_FW_VER_MIN):
|
||||||
self.firmware_ok = True
|
self.firmware_ok = True
|
||||||
|
|
||||||
if self.firmware_ok:
|
if self.firmware_ok:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -1188,7 +1188,7 @@ class RNodeInterface(Interface):
|
||||||
atl = command_buffer[2] << 8 | command_buffer[3]
|
atl = command_buffer[2] << 8 | command_buffer[3]
|
||||||
cus = command_buffer[4] << 8 | command_buffer[5]
|
cus = command_buffer[4] << 8 | command_buffer[5]
|
||||||
cul = command_buffer[6] << 8 | command_buffer[7]
|
cul = command_buffer[6] << 8 | command_buffer[7]
|
||||||
|
|
||||||
self.r_airtime_short = ats/100.0
|
self.r_airtime_short = ats/100.0
|
||||||
self.r_airtime_long = atl/100.0
|
self.r_airtime_long = atl/100.0
|
||||||
self.r_channel_load_short = cus/100.0
|
self.r_channel_load_short = cus/100.0
|
||||||
|
@ -1289,10 +1289,10 @@ class RNodeInterface(Interface):
|
||||||
if time.time() > self.first_tx + self.id_interval:
|
if time.time() > self.first_tx + self.id_interval:
|
||||||
RNS.log(f"Interface {self} is transmitting beacon data: {self.id_callsign.decode('utf-8')}", RNS.LOG_DEBUG)
|
RNS.log(f"Interface {self} is transmitting beacon data: {self.id_callsign.decode('utf-8')}", RNS.LOG_DEBUG)
|
||||||
self.processOutgoing(self.id_callsign)
|
self.processOutgoing(self.id_callsign)
|
||||||
|
|
||||||
if (time.time() - self.last_port_io > self.port_io_timeout):
|
if (time.time() - self.last_port_io > self.port_io_timeout):
|
||||||
self.detect()
|
self.detect()
|
||||||
|
|
||||||
if (time.time() - self.last_port_io > self.port_io_timeout*3):
|
if (time.time() - self.last_port_io > self.port_io_timeout*3):
|
||||||
raise OSError(f"Connected port for {self} became unresponsive")
|
raise OSError(f"Connected port for {self} became unresponsive")
|
||||||
|
|
||||||
|
@ -1343,7 +1343,7 @@ class RNodeInterface(Interface):
|
||||||
if self.last_imagedata != None:
|
if self.last_imagedata != None:
|
||||||
self.display_image(self.last_imagedata)
|
self.display_image(self.last_imagedata)
|
||||||
self.enable_external_framebuffer()
|
self.enable_external_framebuffer()
|
||||||
|
|
||||||
elif hasattr(self, "bt_manager") and self.bt_manager != None and self.bt_manager.connected:
|
elif hasattr(self, "bt_manager") and self.bt_manager != None and self.bt_manager.connected:
|
||||||
self.configure_device()
|
self.configure_device()
|
||||||
if self.online:
|
if self.online:
|
||||||
|
@ -1504,7 +1504,7 @@ class BLEConnection(BluetoothDispatcher):
|
||||||
self.write_characteristic(self.rx_char, data)
|
self.write_characteristic(self.rx_char, data)
|
||||||
else:
|
else:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("An error occurred in {self} write loop: {e}", RNS.LOG_ERROR)
|
RNS.log("An error occurred in {self} write loop: {e}", RNS.LOG_ERROR)
|
||||||
RNS.trace_exception(e)
|
RNS.trace_exception(e)
|
||||||
|
@ -1552,7 +1552,7 @@ class BLEConnection(BluetoothDispatcher):
|
||||||
self.owner.hw_errors.append({"error": KISS.ERROR_INVALID_BLE_MTU, "description": "The Bluetooth Low Energy transfer MTU could not be configured for the connected device, and communication has failed. Restart Reticulum and any connected applications to retry connecting."})
|
self.owner.hw_errors.append({"error": KISS.ERROR_INVALID_BLE_MTU, "description": "The Bluetooth Low Energy transfer MTU could not be configured for the connected device, and communication has failed. Restart Reticulum and any connected applications to retry connecting."})
|
||||||
self.close()
|
self.close()
|
||||||
self.should_run = False
|
self.should_run = False
|
||||||
|
|
||||||
self.close_gatt()
|
self.close_gatt()
|
||||||
|
|
||||||
self.connect_job_running = False
|
self.connect_job_running = False
|
||||||
|
@ -1599,14 +1599,14 @@ class BLEConnection(BluetoothDispatcher):
|
||||||
def on_services(self, status, services):
|
def on_services(self, status, services):
|
||||||
if status == GATT_SUCCESS:
|
if status == GATT_SUCCESS:
|
||||||
self.rx_char = services.search(BLEConnection.UART_RX_CHAR_UUID)
|
self.rx_char = services.search(BLEConnection.UART_RX_CHAR_UUID)
|
||||||
|
|
||||||
if self.rx_char is not None:
|
if self.rx_char is not None:
|
||||||
self.tx_char = services.search(BLEConnection.UART_TX_CHAR_UUID)
|
self.tx_char = services.search(BLEConnection.UART_TX_CHAR_UUID)
|
||||||
|
|
||||||
if self.tx_char is not None:
|
if self.tx_char is not None:
|
||||||
if self.enable_notifications(self.tx_char):
|
if self.enable_notifications(self.tx_char):
|
||||||
RNS.log("Enabled notifications for BLE TX characteristic", RNS.LOG_DEBUG)
|
RNS.log("Enabled notifications for BLE TX characteristic", RNS.LOG_DEBUG)
|
||||||
|
|
||||||
RNS.log(f"Requesting BLE connection MTU update to {self.target_mtu}", RNS.LOG_DEBUG)
|
RNS.log(f"Requesting BLE connection MTU update to {self.target_mtu}", RNS.LOG_DEBUG)
|
||||||
self.mtu_requested_time = time.time()
|
self.mtu_requested_time = time.time()
|
||||||
self.request_mtu(self.target_mtu)
|
self.request_mtu(self.target_mtu)
|
||||||
|
|
|
@ -64,7 +64,7 @@ class SerialInterface(Interface):
|
||||||
|
|
||||||
from usbserial4a import serial4a as serial
|
from usbserial4a import serial4a as serial
|
||||||
self.parity = "N"
|
self.parity = "N"
|
||||||
|
|
||||||
else:
|
else:
|
||||||
RNS.log("Could not load USB serial module for Android, Serial interface cannot be created.", RNS.LOG_CRITICAL)
|
RNS.log("Could not load USB serial module for Android, Serial interface cannot be created.", RNS.LOG_CRITICAL)
|
||||||
RNS.log("You can install this module by issuing: pip install usbserial4a", RNS.LOG_CRITICAL)
|
RNS.log("You can install this module by issuing: pip install usbserial4a", RNS.LOG_CRITICAL)
|
||||||
|
@ -75,7 +75,7 @@ class SerialInterface(Interface):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.HW_MTU = 564
|
self.HW_MTU = 564
|
||||||
|
|
||||||
self.pyserial = serial
|
self.pyserial = serial
|
||||||
self.serial = None
|
self.serial = None
|
||||||
self.owner = owner
|
self.owner = owner
|
||||||
|
@ -145,7 +145,7 @@ class SerialInterface(Interface):
|
||||||
self.serial.timeout = 0.1
|
self.serial.timeout = 0.1
|
||||||
elif vid == 0x10C4:
|
elif vid == 0x10C4:
|
||||||
# Hardware parameters for SiLabs CP210x @ 115200 baud
|
# Hardware parameters for SiLabs CP210x @ 115200 baud
|
||||||
self.serial.DEFAULT_READ_BUFFER_SIZE = 64
|
self.serial.DEFAULT_READ_BUFFER_SIZE = 64
|
||||||
self.serial.USB_READ_TIMEOUT_MILLIS = 12
|
self.serial.USB_READ_TIMEOUT_MILLIS = 12
|
||||||
self.serial.timeout = 0.012
|
self.serial.timeout = 0.012
|
||||||
elif vid == 0x1A86 and pid == 0x55D4:
|
elif vid == 0x1A86 and pid == 0x55D4:
|
||||||
|
@ -182,7 +182,7 @@ class SerialInterface(Interface):
|
||||||
if self.online:
|
if self.online:
|
||||||
data = bytes([HDLC.FLAG])+HDLC.escape(data)+bytes([HDLC.FLAG])
|
data = bytes([HDLC.FLAG])+HDLC.escape(data)+bytes([HDLC.FLAG])
|
||||||
written = self.serial.write(data)
|
written = self.serial.write(data)
|
||||||
self.txb += len(data)
|
self.txb += len(data)
|
||||||
if written != len(data):
|
if written != len(data):
|
||||||
raise OSError(f"Serial interface only wrote {written} bytes of {len(data)}")
|
raise OSError(f"Serial interface only wrote {written} bytes of {len(data)}")
|
||||||
|
|
||||||
|
@ -217,7 +217,7 @@ class SerialInterface(Interface):
|
||||||
byte = HDLC.ESC
|
byte = HDLC.ESC
|
||||||
escape = False
|
escape = False
|
||||||
data_buffer = data_buffer+bytes([byte])
|
data_buffer = data_buffer+bytes([byte])
|
||||||
|
|
||||||
if got == 0:
|
if got == 0:
|
||||||
time_since_last = int(time.time()*1000) - last_read_ms
|
time_since_last = int(time.time()*1000) - last_read_ms
|
||||||
if len(data_buffer) > 0 and time_since_last > self.timeout:
|
if len(data_buffer) > 0 and time_since_last > self.timeout:
|
||||||
|
@ -225,12 +225,12 @@ class SerialInterface(Interface):
|
||||||
in_frame = False
|
in_frame = False
|
||||||
escape = False
|
escape = False
|
||||||
# sleep(0.08)
|
# sleep(0.08)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.online = False
|
self.online = False
|
||||||
RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
|
RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
|
RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
|
||||||
|
|
||||||
if RNS.Reticulum.panic_on_interface_error:
|
if RNS.Reticulum.panic_on_interface_error:
|
||||||
RNS.panic()
|
RNS.panic()
|
||||||
|
|
||||||
|
|
|
@ -289,7 +289,7 @@ class AutoInterface(Interface):
|
||||||
|
|
||||||
udp_server = socketserver.UDPServer(address, self.handler_factory(self.processIncoming))
|
udp_server = socketserver.UDPServer(address, self.handler_factory(self.processIncoming))
|
||||||
self.interface_servers[ifname] = udp_server
|
self.interface_servers[ifname] = udp_server
|
||||||
|
|
||||||
thread = threading.Thread(target=udp_server.serve_forever)
|
thread = threading.Thread(target=udp_server.serve_forever)
|
||||||
thread.daemon = True
|
thread.daemon = True
|
||||||
thread.start()
|
thread.start()
|
||||||
|
@ -306,11 +306,11 @@ class AutoInterface(Interface):
|
||||||
def discovery_handler(self, socket, ifname):
|
def discovery_handler(self, socket, ifname):
|
||||||
def announce_loop():
|
def announce_loop():
|
||||||
self.announce_handler(ifname)
|
self.announce_handler(ifname)
|
||||||
|
|
||||||
thread = threading.Thread(target=announce_loop)
|
thread = threading.Thread(target=announce_loop)
|
||||||
thread.daemon = True
|
thread.daemon = True
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
data, ipv6_src = socket.recvfrom(1024)
|
data, ipv6_src = socket.recvfrom(1024)
|
||||||
expected_hash = RNS.Identity.full_hash(self.group_id+ipv6_src[0].encode("utf-8"))
|
expected_hash = RNS.Identity.full_hash(self.group_id+ipv6_src[0].encode("utf-8"))
|
||||||
|
@ -396,13 +396,13 @@ class AutoInterface(Interface):
|
||||||
self.carrier_changed = True
|
self.carrier_changed = True
|
||||||
RNS.log(f"{self} Carrier recovered on {ifname}", RNS.LOG_WARNING)
|
RNS.log(f"{self} Carrier recovered on {ifname}", RNS.LOG_WARNING)
|
||||||
self.timed_out_interfaces[ifname] = False
|
self.timed_out_interfaces[ifname] = False
|
||||||
|
|
||||||
|
|
||||||
def announce_handler(self, ifname):
|
def announce_handler(self, ifname):
|
||||||
while True:
|
while True:
|
||||||
self.peer_announce(ifname)
|
self.peer_announce(ifname)
|
||||||
time.sleep(self.announce_interval)
|
time.sleep(self.announce_interval)
|
||||||
|
|
||||||
def peer_announce(self, ifname):
|
def peer_announce(self, ifname):
|
||||||
try:
|
try:
|
||||||
link_local_address = self.adopted_interfaces[ifname]
|
link_local_address = self.adopted_interfaces[ifname]
|
||||||
|
@ -414,7 +414,7 @@ class AutoInterface(Interface):
|
||||||
announce_socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, ifis)
|
announce_socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, ifis)
|
||||||
announce_socket.sendto(discovery_token, addr_info[0][4])
|
announce_socket.sendto(discovery_token, addr_info[0][4])
|
||||||
announce_socket.close()
|
announce_socket.close()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if (ifname in self.timed_out_interfaces and self.timed_out_interfaces[ifname] == False) or not ifname in self.timed_out_interfaces:
|
if (ifname in self.timed_out_interfaces and self.timed_out_interfaces[ifname] == False) or not ifname in self.timed_out_interfaces:
|
||||||
RNS.log(f"{self} Detected possible carrier loss on {ifname}: {e}", RNS.LOG_WARNING)
|
RNS.log(f"{self} Detected possible carrier loss on {ifname}: {e}", RNS.LOG_WARNING)
|
||||||
|
@ -471,9 +471,9 @@ class AutoInterface(Interface):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log(f"Could not transmit on {self}. The contained exception was: {e}", RNS.LOG_ERROR)
|
RNS.log(f"Could not transmit on {self}. The contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
|
||||||
self.txb += len(data)
|
self.txb += len(data)
|
||||||
|
|
||||||
|
|
||||||
# Until per-device sub-interfacing is implemented,
|
# Until per-device sub-interfacing is implemented,
|
||||||
# ingress limiting should be disabled on AutoInterface
|
# ingress limiting should be disabled on AutoInterface
|
||||||
|
|
|
@ -143,11 +143,11 @@ class I2PController:
|
||||||
|
|
||||||
self.loop.ext_owner = self
|
self.loop.ext_owner = self
|
||||||
result = asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop).result()
|
result = asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop).result()
|
||||||
|
|
||||||
if not i2p_destination in self.i2plib_tunnels:
|
if not i2p_destination in self.i2plib_tunnels:
|
||||||
raise OSError("No tunnel control instance was created")
|
raise OSError("No tunnel control instance was created")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
tn = self.i2plib_tunnels[i2p_destination]
|
tn = self.i2plib_tunnels[i2p_destination]
|
||||||
if tn != None and hasattr(tn, "status"):
|
if tn != None and hasattr(tn, "status"):
|
||||||
|
|
||||||
|
@ -183,7 +183,7 @@ class I2PController:
|
||||||
|
|
||||||
except ConnectionRefusedError as e:
|
except ConnectionRefusedError as e:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
except ConnectionAbortedError as e:
|
except ConnectionAbortedError as e:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
@ -222,13 +222,13 @@ class I2PController:
|
||||||
|
|
||||||
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.KeyNotFound):
|
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.KeyNotFound):
|
||||||
RNS.log(f"The I2P daemon could not find the key for {i2p_destination}", RNS.LOG_ERROR)
|
RNS.log(f"The I2P daemon could not find the key for {i2p_destination}", RNS.LOG_ERROR)
|
||||||
|
|
||||||
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.PeerNotFound):
|
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.PeerNotFound):
|
||||||
RNS.log(f"The I2P daemon mould not find the peer {i2p_destination}", RNS.LOG_ERROR)
|
RNS.log(f"The I2P daemon mould not find the peer {i2p_destination}", RNS.LOG_ERROR)
|
||||||
|
|
||||||
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.I2PError):
|
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.I2PError):
|
||||||
RNS.log("The I2P daemon experienced an unspecified error", RNS.LOG_ERROR)
|
RNS.log("The I2P daemon experienced an unspecified error", RNS.LOG_ERROR)
|
||||||
|
|
||||||
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.Timeout):
|
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.Timeout):
|
||||||
RNS.log(f"I2P daemon timed out while setting up client tunnel to {i2p_destination}", RNS.LOG_ERROR)
|
RNS.log(f"I2P daemon timed out while setting up client tunnel to {i2p_destination}", RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
@ -320,7 +320,7 @@ class I2PController:
|
||||||
|
|
||||||
elif i2p_exception != None:
|
elif i2p_exception != None:
|
||||||
RNS.log("An error ocurred while setting up I2P tunnel", RNS.LOG_ERROR)
|
RNS.log("An error ocurred while setting up I2P tunnel", RNS.LOG_ERROR)
|
||||||
|
|
||||||
if isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.CantReachPeer):
|
if isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.CantReachPeer):
|
||||||
RNS.log(f"The I2P daemon can't reach peer {i2p_destination}", RNS.LOG_ERROR)
|
RNS.log(f"The I2P daemon can't reach peer {i2p_destination}", RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
@ -338,13 +338,13 @@ class I2PController:
|
||||||
|
|
||||||
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.KeyNotFound):
|
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.KeyNotFound):
|
||||||
RNS.log(f"The I2P daemon could not find the key for {i2p_destination}", RNS.LOG_ERROR)
|
RNS.log(f"The I2P daemon could not find the key for {i2p_destination}", RNS.LOG_ERROR)
|
||||||
|
|
||||||
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.PeerNotFound):
|
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.PeerNotFound):
|
||||||
RNS.log(f"The I2P daemon mould not find the peer {i2p_destination}", RNS.LOG_ERROR)
|
RNS.log(f"The I2P daemon mould not find the peer {i2p_destination}", RNS.LOG_ERROR)
|
||||||
|
|
||||||
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.I2PError):
|
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.I2PError):
|
||||||
RNS.log("The I2P daemon experienced an unspecified error", RNS.LOG_ERROR)
|
RNS.log("The I2P daemon experienced an unspecified error", RNS.LOG_ERROR)
|
||||||
|
|
||||||
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.Timeout):
|
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.Timeout):
|
||||||
RNS.log(f"I2P daemon timed out while setting up client tunnel to {i2p_destination}", RNS.LOG_ERROR)
|
RNS.log(f"I2P daemon timed out while setting up client tunnel to {i2p_destination}", RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
@ -393,7 +393,7 @@ class I2PInterfacePeer(Interface):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.HW_MTU = 1064
|
self.HW_MTU = 1064
|
||||||
|
|
||||||
self.IN = True
|
self.IN = True
|
||||||
self.OUT = False
|
self.OUT = False
|
||||||
self.socket = None
|
self.socket = None
|
||||||
|
@ -492,7 +492,7 @@ class I2PInterfacePeer(Interface):
|
||||||
while self.awaiting_i2p_tunnel:
|
while self.awaiting_i2p_tunnel:
|
||||||
time.sleep(0.25)
|
time.sleep(0.25)
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
if not self.kiss_framing:
|
if not self.kiss_framing:
|
||||||
self.wants_tunnel = True
|
self.wants_tunnel = True
|
||||||
|
|
||||||
|
@ -525,7 +525,7 @@ class I2PInterfacePeer(Interface):
|
||||||
|
|
||||||
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
||||||
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(I2PInterfacePeer.I2P_PROBE_AFTER))
|
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(I2PInterfacePeer.I2P_PROBE_AFTER))
|
||||||
|
|
||||||
def shutdown_socket(self, target_socket):
|
def shutdown_socket(self, target_socket):
|
||||||
if callable(target_socket.close):
|
if callable(target_socket.close):
|
||||||
try:
|
try:
|
||||||
|
@ -538,15 +538,15 @@ class I2PInterfacePeer(Interface):
|
||||||
if socket != None:
|
if socket != None:
|
||||||
target_socket.close()
|
target_socket.close()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log(f"Error while closing socket for {self}: {e}")
|
RNS.log(f"Error while closing socket for {self}: {e}")
|
||||||
|
|
||||||
def detach(self):
|
def detach(self):
|
||||||
RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
|
RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
|
||||||
if self.socket != None:
|
if self.socket != None:
|
||||||
if hasattr(self.socket, "close"):
|
if hasattr(self.socket, "close"):
|
||||||
if callable(self.socket.close):
|
if callable(self.socket.close):
|
||||||
self.detached = True
|
self.detached = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.socket.shutdown(socket.SHUT_RDWR)
|
self.socket.shutdown(socket.SHUT_RDWR)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -564,7 +564,7 @@ class I2PInterfacePeer(Interface):
|
||||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
self.socket.connect((self.target_ip, self.target_port))
|
self.socket.connect((self.target_ip, self.target_port))
|
||||||
self.online = True
|
self.online = True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if initial:
|
if initial:
|
||||||
if not self.awaiting_i2p_tunnel:
|
if not self.awaiting_i2p_tunnel:
|
||||||
|
@ -572,7 +572,7 @@ class I2PInterfacePeer(Interface):
|
||||||
RNS.log(f"Leaving unconnected and retrying connection in {I2PInterfacePeer.RECONNECT_WAIT} seconds.", RNS.LOG_ERROR)
|
RNS.log(f"Leaving unconnected and retrying connection in {I2PInterfacePeer.RECONNECT_WAIT} seconds.", RNS.LOG_ERROR)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
@ -580,7 +580,7 @@ class I2PInterfacePeer(Interface):
|
||||||
self.set_timeouts_linux()
|
self.set_timeouts_linux()
|
||||||
elif platform.system() == "Darwin":
|
elif platform.system() == "Darwin":
|
||||||
self.set_timeouts_osx()
|
self.set_timeouts_osx()
|
||||||
|
|
||||||
self.online = True
|
self.online = True
|
||||||
self.writing = False
|
self.writing = False
|
||||||
self.never_connected = False
|
self.never_connected = False
|
||||||
|
@ -631,7 +631,7 @@ class I2PInterfacePeer(Interface):
|
||||||
self.rxb += len(data)
|
self.rxb += len(data)
|
||||||
if hasattr(self, "parent_interface") and self.parent_interface != None and self.parent_count:
|
if hasattr(self, "parent_interface") and self.parent_interface != None and self.parent_count:
|
||||||
self.parent_interface.rxb += len(data)
|
self.parent_interface.rxb += len(data)
|
||||||
|
|
||||||
self.owner.inbound(data, self)
|
self.owner.inbound(data, self)
|
||||||
|
|
||||||
def processOutgoing(self, data):
|
def processOutgoing(self, data):
|
||||||
|
@ -651,7 +651,7 @@ class I2PInterfacePeer(Interface):
|
||||||
self.writing = False
|
self.writing = False
|
||||||
self.txb += len(data)
|
self.txb += len(data)
|
||||||
self.last_write = time.time()
|
self.last_write = time.time()
|
||||||
|
|
||||||
if hasattr(self, "parent_interface") and self.parent_interface != None and self.parent_count:
|
if hasattr(self, "parent_interface") and self.parent_interface != None and self.parent_count:
|
||||||
self.parent_interface.txb += len(data)
|
self.parent_interface.txb += len(data)
|
||||||
|
|
||||||
|
@ -686,7 +686,7 @@ class I2PInterfacePeer(Interface):
|
||||||
RNS.log(f"An error ocurred while sending I2P keepalive. The contained exception was: {e}", RNS.LOG_ERROR)
|
RNS.log(f"An error ocurred while sending I2P keepalive. The contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
self.shutdown_socket(self.socket)
|
self.shutdown_socket(self.socket)
|
||||||
should_run = False
|
should_run = False
|
||||||
|
|
||||||
if (time.time()-self.last_read > I2PInterfacePeer.I2P_READ_TIMEOUT):
|
if (time.time()-self.last_read > I2PInterfacePeer.I2P_READ_TIMEOUT):
|
||||||
RNS.log("I2P socket is unresponsive, restarting...", RNS.LOG_WARNING)
|
RNS.log("I2P socket is unresponsive, restarting...", RNS.LOG_WARNING)
|
||||||
if self.socket != None:
|
if self.socket != None:
|
||||||
|
@ -790,7 +790,7 @@ class I2PInterfacePeer(Interface):
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.online = False
|
self.online = False
|
||||||
RNS.log(f"An interface error occurred for {self}, the contained exception was: {e}", RNS.LOG_WARNING)
|
RNS.log(f"An interface error occurred for {self}, the contained exception was: {e}", RNS.LOG_WARNING)
|
||||||
|
@ -832,7 +832,7 @@ class I2PInterface(Interface):
|
||||||
|
|
||||||
def __init__(self, owner, name, rns_storagepath, peers, connectable = False, ifac_size = 16, ifac_netname = None, ifac_netkey = None):
|
def __init__(self, owner, name, rns_storagepath, peers, connectable = False, ifac_size = 16, ifac_netname = None, ifac_netkey = None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.HW_MTU = 1064
|
self.HW_MTU = 1064
|
||||||
|
|
||||||
self.online = False
|
self.online = False
|
||||||
|
@ -882,7 +882,7 @@ class I2PInterface(Interface):
|
||||||
def createHandler(*args, **keys):
|
def createHandler(*args, **keys):
|
||||||
return I2PInterfaceHandler(callback, *args, **keys)
|
return I2PInterfaceHandler(callback, *args, **keys)
|
||||||
return createHandler
|
return createHandler
|
||||||
|
|
||||||
ThreadingI2PServer.allow_reuse_address = True
|
ThreadingI2PServer.allow_reuse_address = True
|
||||||
self.server = ThreadingI2PServer(self.address, handlerFactory(self.incoming_connection))
|
self.server = ThreadingI2PServer(self.address, handlerFactory(self.incoming_connection))
|
||||||
|
|
||||||
|
|
|
@ -146,7 +146,7 @@ class Interface:
|
||||||
def release():
|
def release():
|
||||||
RNS.Transport.inbound(selected_announce_packet.raw, selected_announce_packet.receiving_interface)
|
RNS.Transport.inbound(selected_announce_packet.raw, selected_announce_packet.receiving_interface)
|
||||||
threading.Thread(target=release, daemon=True).start()
|
threading.Thread(target=release, daemon=True).start()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log(f"An error occurred while processing held announces for {self}", RNS.LOG_ERROR)
|
RNS.log(f"An error occurred while processing held announces for {self}", RNS.LOG_ERROR)
|
||||||
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
|
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
|
@ -170,7 +170,7 @@ class Interface:
|
||||||
for i in range(1,dq_len):
|
for i in range(1,dq_len):
|
||||||
delta_sum += self.ia_freq_deque[i]-self.ia_freq_deque[i-1]
|
delta_sum += self.ia_freq_deque[i]-self.ia_freq_deque[i-1]
|
||||||
delta_sum += time.time() - self.ia_freq_deque[dq_len-1]
|
delta_sum += time.time() - self.ia_freq_deque[dq_len-1]
|
||||||
|
|
||||||
if delta_sum == 0:
|
if delta_sum == 0:
|
||||||
avg = 0
|
avg = 0
|
||||||
else:
|
else:
|
||||||
|
@ -187,7 +187,7 @@ class Interface:
|
||||||
for i in range(1,dq_len):
|
for i in range(1,dq_len):
|
||||||
delta_sum += self.oa_freq_deque[i]-self.oa_freq_deque[i-1]
|
delta_sum += self.oa_freq_deque[i]-self.oa_freq_deque[i-1]
|
||||||
delta_sum += time.time() - self.oa_freq_deque[dq_len-1]
|
delta_sum += time.time() - self.oa_freq_deque[dq_len-1]
|
||||||
|
|
||||||
if delta_sum == 0:
|
if delta_sum == 0:
|
||||||
avg = 0
|
avg = 0
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -71,9 +71,9 @@ class KISSInterface(Interface):
|
||||||
RNS.panic()
|
RNS.panic()
|
||||||
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.HW_MTU = 564
|
self.HW_MTU = 564
|
||||||
|
|
||||||
if beacon_data == None:
|
if beacon_data == None:
|
||||||
beacon_data = ""
|
beacon_data = ""
|
||||||
|
|
||||||
|
@ -218,7 +218,7 @@ class KISSInterface(Interface):
|
||||||
|
|
||||||
|
|
||||||
def processIncoming(self, data):
|
def processIncoming(self, data):
|
||||||
self.rxb += len(data)
|
self.rxb += len(data)
|
||||||
self.owner.inbound(data, self)
|
self.owner.inbound(data, self)
|
||||||
|
|
||||||
|
|
||||||
|
@ -325,7 +325,7 @@ class KISSInterface(Interface):
|
||||||
self.online = False
|
self.online = False
|
||||||
RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
|
RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
|
RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
|
||||||
|
|
||||||
if RNS.Reticulum.panic_on_interface_error:
|
if RNS.Reticulum.panic_on_interface_error:
|
||||||
RNS.panic()
|
RNS.panic()
|
||||||
|
|
||||||
|
|
|
@ -58,11 +58,11 @@ class LocalClientInterface(Interface):
|
||||||
|
|
||||||
# TODO: Remove at some point
|
# TODO: Remove at some point
|
||||||
# self.rxptime = 0
|
# self.rxptime = 0
|
||||||
|
|
||||||
self.HW_MTU = 1064
|
self.HW_MTU = 1064
|
||||||
|
|
||||||
self.online = False
|
self.online = False
|
||||||
|
|
||||||
self.IN = True
|
self.IN = True
|
||||||
self.OUT = False
|
self.OUT = False
|
||||||
self.socket = None
|
self.socket = None
|
||||||
|
@ -146,7 +146,7 @@ class LocalClientInterface(Interface):
|
||||||
time.sleep(LocalClientInterface.RECONNECT_WAIT+2)
|
time.sleep(LocalClientInterface.RECONNECT_WAIT+2)
|
||||||
RNS.Transport.shared_connection_reappeared()
|
RNS.Transport.shared_connection_reappeared()
|
||||||
threading.Thread(target=job, daemon=True).start()
|
threading.Thread(target=job, daemon=True).start()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
RNS.log("Attempt to reconnect on a non-initiator shared local interface. This should not happen.", RNS.LOG_ERROR)
|
RNS.log("Attempt to reconnect on a non-initiator shared local interface. This should not happen.", RNS.LOG_ERROR)
|
||||||
raise OSError("Attempt to reconnect on a non-initiator local interface")
|
raise OSError("Attempt to reconnect on a non-initiator local interface")
|
||||||
|
@ -156,10 +156,10 @@ class LocalClientInterface(Interface):
|
||||||
self.rxb += len(data)
|
self.rxb += len(data)
|
||||||
if hasattr(self, "parent_interface") and self.parent_interface != None:
|
if hasattr(self, "parent_interface") and self.parent_interface != None:
|
||||||
self.parent_interface.rxb += len(data)
|
self.parent_interface.rxb += len(data)
|
||||||
|
|
||||||
# TODO: Remove at some point
|
# TODO: Remove at some point
|
||||||
# processing_start = time.time()
|
# processing_start = time.time()
|
||||||
|
|
||||||
self.owner.inbound(data, self)
|
self.owner.inbound(data, self)
|
||||||
|
|
||||||
# TODO: Remove at some point
|
# TODO: Remove at some point
|
||||||
|
@ -234,7 +234,7 @@ class LocalClientInterface(Interface):
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.online = False
|
self.online = False
|
||||||
RNS.log(f"An interface error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
|
RNS.log(f"An interface error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
|
@ -247,7 +247,7 @@ class LocalClientInterface(Interface):
|
||||||
if callable(self.socket.close):
|
if callable(self.socket.close):
|
||||||
RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
|
RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
|
||||||
self.detached = True
|
self.detached = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.socket.shutdown(socket.SHUT_RDWR)
|
self.socket.shutdown(socket.SHUT_RDWR)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -283,7 +283,7 @@ class LocalClientInterface(Interface):
|
||||||
if self.is_connected_to_shared_instance:
|
if self.is_connected_to_shared_instance:
|
||||||
if nowarning == False:
|
if nowarning == False:
|
||||||
RNS.log("Permanently lost connection to local shared RNS instance. Exiting now.", RNS.LOG_CRITICAL)
|
RNS.log("Permanently lost connection to local shared RNS instance. Exiting now.", RNS.LOG_CRITICAL)
|
||||||
|
|
||||||
RNS.exit()
|
RNS.exit()
|
||||||
|
|
||||||
|
|
||||||
|
@ -297,7 +297,7 @@ class LocalServerInterface(Interface):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.online = False
|
self.online = False
|
||||||
self.clients = 0
|
self.clients = 0
|
||||||
|
|
||||||
self.IN = True
|
self.IN = True
|
||||||
self.OUT = False
|
self.OUT = False
|
||||||
self.name = "Reticulum"
|
self.name = "Reticulum"
|
||||||
|
|
|
@ -49,7 +49,7 @@ class PipeInterface(Interface):
|
||||||
|
|
||||||
owner = None
|
owner = None
|
||||||
command = None
|
command = None
|
||||||
|
|
||||||
def __init__(self, owner, name, command, respawn_delay):
|
def __init__(self, owner, name, command, respawn_delay):
|
||||||
if respawn_delay == None:
|
if respawn_delay == None:
|
||||||
respawn_delay = 5
|
respawn_delay = 5
|
||||||
|
@ -57,7 +57,7 @@ class PipeInterface(Interface):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.HW_MTU = 1064
|
self.HW_MTU = 1064
|
||||||
|
|
||||||
self.owner = owner
|
self.owner = owner
|
||||||
self.name = name
|
self.name = name
|
||||||
self.command = command
|
self.command = command
|
||||||
|
@ -83,7 +83,7 @@ class PipeInterface(Interface):
|
||||||
|
|
||||||
def open_pipe(self):
|
def open_pipe(self):
|
||||||
RNS.log(f"Connecting subprocess pipe for {self}...", RNS.LOG_VERBOSE)
|
RNS.log(f"Connecting subprocess pipe for {self}...", RNS.LOG_VERBOSE)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.process = subprocess.Popen(shlex.split(self.command), stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
self.process = subprocess.Popen(shlex.split(self.command), stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||||
self.pipe_is_open = True
|
self.pipe_is_open = True
|
||||||
|
@ -102,7 +102,7 @@ class PipeInterface(Interface):
|
||||||
|
|
||||||
|
|
||||||
def processIncoming(self, data):
|
def processIncoming(self, data):
|
||||||
self.rxb += len(data)
|
self.rxb += len(data)
|
||||||
self.owner.inbound(data, self)
|
self.owner.inbound(data, self)
|
||||||
|
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ class PipeInterface(Interface):
|
||||||
data = bytes([HDLC.FLAG])+HDLC.escape(data)+bytes([HDLC.FLAG])
|
data = bytes([HDLC.FLAG])+HDLC.escape(data)+bytes([HDLC.FLAG])
|
||||||
written = self.process.stdin.write(data)
|
written = self.process.stdin.write(data)
|
||||||
self.process.stdin.flush()
|
self.process.stdin.flush()
|
||||||
self.txb += len(data)
|
self.txb += len(data)
|
||||||
if written != len(data):
|
if written != len(data):
|
||||||
raise OSError(f"Pipe interface only wrote {written} bytes of {len(data)}")
|
raise OSError(f"Pipe interface only wrote {written} bytes of {len(data)}")
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ class PipeInterface(Interface):
|
||||||
|
|
||||||
RNS.log(f"Subprocess terminated on {self}")
|
RNS.log(f"Subprocess terminated on {self}")
|
||||||
self.process.kill()
|
self.process.kill()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.online = False
|
self.online = False
|
||||||
try:
|
try:
|
||||||
|
@ -162,7 +162,7 @@ class PipeInterface(Interface):
|
||||||
|
|
||||||
RNS.log(f"A pipe error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
|
RNS.log(f"A pipe error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
|
RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
|
||||||
|
|
||||||
if RNS.Reticulum.panic_on_interface_error:
|
if RNS.Reticulum.panic_on_interface_error:
|
||||||
RNS.panic()
|
RNS.panic()
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ class KISS():
|
||||||
FESC = 0xDB
|
FESC = 0xDB
|
||||||
TFEND = 0xDC
|
TFEND = 0xDC
|
||||||
TFESC = 0xDD
|
TFESC = 0xDD
|
||||||
|
|
||||||
CMD_UNKNOWN = 0xFE
|
CMD_UNKNOWN = 0xFE
|
||||||
CMD_DATA = 0x00
|
CMD_DATA = 0x00
|
||||||
CMD_FREQUENCY = 0x01
|
CMD_FREQUENCY = 0x01
|
||||||
|
@ -69,11 +69,11 @@ class KISS():
|
||||||
|
|
||||||
DETECT_REQ = 0x73
|
DETECT_REQ = 0x73
|
||||||
DETECT_RESP = 0x46
|
DETECT_RESP = 0x46
|
||||||
|
|
||||||
RADIO_STATE_OFF = 0x00
|
RADIO_STATE_OFF = 0x00
|
||||||
RADIO_STATE_ON = 0x01
|
RADIO_STATE_ON = 0x01
|
||||||
RADIO_STATE_ASK = 0xFF
|
RADIO_STATE_ASK = 0xFF
|
||||||
|
|
||||||
CMD_ERROR = 0x90
|
CMD_ERROR = 0x90
|
||||||
ERROR_INITRADIO = 0x01
|
ERROR_INITRADIO = 0x01
|
||||||
ERROR_TXFAILED = 0x02
|
ERROR_TXFAILED = 0x02
|
||||||
|
@ -91,7 +91,7 @@ class KISS():
|
||||||
data = data.replace(bytes([0xdb]), bytes([0xdb, 0xdd]))
|
data = data.replace(bytes([0xdb]), bytes([0xdb, 0xdd]))
|
||||||
data = data.replace(bytes([0xc0]), bytes([0xdb, 0xdc]))
|
data = data.replace(bytes([0xc0]), bytes([0xdb, 0xdc]))
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
class RNodeInterface(Interface):
|
class RNodeInterface(Interface):
|
||||||
MAX_CHUNK = 32768
|
MAX_CHUNK = 32768
|
||||||
|
@ -132,7 +132,7 @@ class RNodeInterface(Interface):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.HW_MTU = 508
|
self.HW_MTU = 508
|
||||||
|
|
||||||
self.pyserial = serial
|
self.pyserial = serial
|
||||||
self.serial = None
|
self.serial = None
|
||||||
self.owner = owner
|
self.owner = owner
|
||||||
|
@ -287,7 +287,7 @@ class RNodeInterface(Interface):
|
||||||
write_timeout = None,
|
write_timeout = None,
|
||||||
dsrdtr = False,
|
dsrdtr = False,
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
RNS.log(f"Opening BLE connection for {self}...")
|
RNS.log(f"Opening BLE connection for {self}...")
|
||||||
if self.ble != None and self.ble.running == False:
|
if self.ble != None and self.ble.running == False:
|
||||||
|
@ -333,7 +333,7 @@ class RNodeInterface(Interface):
|
||||||
detect_time = RNS.prettytime(time.time()-detect_time)
|
detect_time = RNS.prettytime(time.time()-detect_time)
|
||||||
else:
|
else:
|
||||||
RNS.log(f"RNode detect timed out over {self.port}", RNS.LOG_ERROR)
|
RNS.log(f"RNode detect timed out over {self.port}", RNS.LOG_ERROR)
|
||||||
|
|
||||||
if not self.detected:
|
if not self.detected:
|
||||||
RNS.log(f"Could not detect device for {self}", RNS.LOG_ERROR)
|
RNS.log(f"Could not detect device for {self}", RNS.LOG_ERROR)
|
||||||
self.serial.close()
|
self.serial.close()
|
||||||
|
@ -354,7 +354,7 @@ class RNodeInterface(Interface):
|
||||||
RNS.log("Make sure that your hardware actually supports the parameters specified in the configuration", RNS.LOG_ERROR)
|
RNS.log("Make sure that your hardware actually supports the parameters specified in the configuration", RNS.LOG_ERROR)
|
||||||
RNS.log("Aborting RNode startup", RNS.LOG_ERROR)
|
RNS.log("Aborting RNode startup", RNS.LOG_ERROR)
|
||||||
self.serial.close()
|
self.serial.close()
|
||||||
|
|
||||||
|
|
||||||
def initRadio(self):
|
def initRadio(self):
|
||||||
self.setFrequency()
|
self.setFrequency()
|
||||||
|
@ -374,13 +374,13 @@ class RNodeInterface(Interface):
|
||||||
written = self.serial.write(kiss_command)
|
written = self.serial.write(kiss_command)
|
||||||
if written != len(kiss_command):
|
if written != len(kiss_command):
|
||||||
raise OSError(f"An IO error occurred while detecting hardware for {self}")
|
raise OSError(f"An IO error occurred while detecting hardware for {self}")
|
||||||
|
|
||||||
def leave(self):
|
def leave(self):
|
||||||
kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND])
|
kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND])
|
||||||
written = self.serial.write(kiss_command)
|
written = self.serial.write(kiss_command)
|
||||||
if written != len(kiss_command):
|
if written != len(kiss_command):
|
||||||
raise OSError("An IO error occurred while sending host left command to device")
|
raise OSError("An IO error occurred while sending host left command to device")
|
||||||
|
|
||||||
def enable_external_framebuffer(self):
|
def enable_external_framebuffer(self):
|
||||||
if self.display != None:
|
if self.display != None:
|
||||||
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x01, KISS.FEND])
|
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x01, KISS.FEND])
|
||||||
|
@ -414,7 +414,7 @@ class RNodeInterface(Interface):
|
||||||
data = line_byte+line_data
|
data = line_byte+line_data
|
||||||
escaped_data = KISS.escape(data)
|
escaped_data = KISS.escape(data)
|
||||||
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FB_WRITE])+escaped_data+bytes([KISS.FEND])
|
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FB_WRITE])+escaped_data+bytes([KISS.FEND])
|
||||||
|
|
||||||
written = self.serial.write(kiss_command)
|
written = self.serial.write(kiss_command)
|
||||||
if written != len(kiss_command):
|
if written != len(kiss_command):
|
||||||
raise OSError("An IO error occurred while writing framebuffer data device")
|
raise OSError("An IO error occurred while writing framebuffer data device")
|
||||||
|
@ -509,7 +509,7 @@ class RNodeInterface(Interface):
|
||||||
if (self.maj_version >= RNodeInterface.REQUIRED_FW_VER_MAJ):
|
if (self.maj_version >= RNodeInterface.REQUIRED_FW_VER_MAJ):
|
||||||
if (self.min_version >= RNodeInterface.REQUIRED_FW_VER_MIN):
|
if (self.min_version >= RNodeInterface.REQUIRED_FW_VER_MIN):
|
||||||
self.firmware_ok = True
|
self.firmware_ok = True
|
||||||
|
|
||||||
if self.firmware_ok:
|
if self.firmware_ok:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -799,7 +799,7 @@ class RNodeInterface(Interface):
|
||||||
atl = command_buffer[2] << 8 | command_buffer[3]
|
atl = command_buffer[2] << 8 | command_buffer[3]
|
||||||
cus = command_buffer[4] << 8 | command_buffer[5]
|
cus = command_buffer[4] << 8 | command_buffer[5]
|
||||||
cul = command_buffer[6] << 8 | command_buffer[7]
|
cul = command_buffer[6] << 8 | command_buffer[7]
|
||||||
|
|
||||||
self.r_airtime_short = ats/100.0
|
self.r_airtime_short = ats/100.0
|
||||||
self.r_airtime_long = atl/100.0
|
self.r_airtime_long = atl/100.0
|
||||||
self.r_channel_load_short = cus/100.0
|
self.r_channel_load_short = cus/100.0
|
||||||
|
@ -885,7 +885,7 @@ class RNodeInterface(Interface):
|
||||||
self.detected = True
|
self.detected = True
|
||||||
else:
|
else:
|
||||||
self.detected = False
|
self.detected = False
|
||||||
|
|
||||||
else:
|
else:
|
||||||
time_since_last = int(time.time()*1000) - last_read_ms
|
time_since_last = int(time.time()*1000) - last_read_ms
|
||||||
if len(data_buffer) > 0 and time_since_last > self.timeout:
|
if len(data_buffer) > 0 and time_since_last > self.timeout:
|
||||||
|
@ -943,7 +943,7 @@ class RNodeInterface(Interface):
|
||||||
self.disable_external_framebuffer()
|
self.disable_external_framebuffer()
|
||||||
self.setRadioState(KISS.RADIO_STATE_OFF)
|
self.setRadioState(KISS.RADIO_STATE_OFF)
|
||||||
self.leave()
|
self.leave()
|
||||||
|
|
||||||
if self.use_ble:
|
if self.use_ble:
|
||||||
self.ble.close()
|
self.ble.close()
|
||||||
|
|
||||||
|
@ -1038,7 +1038,7 @@ class BLEConnection():
|
||||||
if importlib.util.find_spec("bleak") != None:
|
if importlib.util.find_spec("bleak") != None:
|
||||||
import bleak
|
import bleak
|
||||||
BLEConnection.bleak = bleak
|
BLEConnection.bleak = bleak
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
BLEConnection.asyncio = asyncio
|
BLEConnection.asyncio = asyncio
|
||||||
else:
|
else:
|
||||||
|
@ -1133,7 +1133,7 @@ class BLEConnection():
|
||||||
else:
|
else:
|
||||||
if self.target_bt_addr != None and device.address == self.target_bt_addr:
|
if self.target_bt_addr != None and device.address == self.target_bt_addr:
|
||||||
RNS.log(f"Can't connect to target device {self.target_bt_addr} over BLE, device is not bonded", RNS.LOG_ERROR)
|
RNS.log(f"Can't connect to target device {self.target_bt_addr} over BLE, device is not bonded", RNS.LOG_ERROR)
|
||||||
|
|
||||||
elif self.target_name != None and device.name == self.target_name:
|
elif self.target_name != None and device.name == self.target_name:
|
||||||
RNS.log(f"Can't connect to target device {self.target_name} over BLE, device is not bonded", RNS.LOG_ERROR)
|
RNS.log(f"Can't connect to target device {self.target_name} over BLE, device is not bonded", RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
@ -1154,7 +1154,7 @@ class BLEConnection():
|
||||||
if "props" in device.details and "Bonded" in device.details["props"]:
|
if "props" in device.details and "Bonded" in device.details["props"]:
|
||||||
if device.details["props"]["Bonded"] == True:
|
if device.details["props"]["Bonded"] == True:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log(f"Error while determining device bond status for {device}, the contained exception was: {e}", RNS.LOG_ERROR)
|
RNS.log(f"Error while determining device bond status for {device}, the contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ class KISS():
|
||||||
FESC = 0xDB
|
FESC = 0xDB
|
||||||
TFEND = 0xDC
|
TFEND = 0xDC
|
||||||
TFESC = 0xDD
|
TFESC = 0xDD
|
||||||
|
|
||||||
CMD_UNKNOWN = 0xFE
|
CMD_UNKNOWN = 0xFE
|
||||||
CMD_FREQUENCY = 0x01
|
CMD_FREQUENCY = 0x01
|
||||||
CMD_BANDWIDTH = 0x02
|
CMD_BANDWIDTH = 0x02
|
||||||
|
@ -94,11 +94,11 @@ class KISS():
|
||||||
|
|
||||||
DETECT_REQ = 0x73
|
DETECT_REQ = 0x73
|
||||||
DETECT_RESP = 0x46
|
DETECT_RESP = 0x46
|
||||||
|
|
||||||
RADIO_STATE_OFF = 0x00
|
RADIO_STATE_OFF = 0x00
|
||||||
RADIO_STATE_ON = 0x01
|
RADIO_STATE_ON = 0x01
|
||||||
RADIO_STATE_ASK = 0xFF
|
RADIO_STATE_ASK = 0xFF
|
||||||
|
|
||||||
CMD_ERROR = 0x90
|
CMD_ERROR = 0x90
|
||||||
ERROR_INITRADIO = 0x01
|
ERROR_INITRADIO = 0x01
|
||||||
ERROR_TXFAILED = 0x02
|
ERROR_TXFAILED = 0x02
|
||||||
|
@ -159,7 +159,7 @@ class KISS():
|
||||||
data = data.replace(bytes([0xdb]), bytes([0xdb, 0xdd]))
|
data = data.replace(bytes([0xdb]), bytes([0xdb, 0xdd]))
|
||||||
data = data.replace(bytes([0xc0]), bytes([0xdb, 0xdc]))
|
data = data.replace(bytes([0xc0]), bytes([0xdb, 0xdc]))
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
class RNodeMultiInterface(Interface):
|
class RNodeMultiInterface(Interface):
|
||||||
MAX_CHUNK = 32768
|
MAX_CHUNK = 32768
|
||||||
|
@ -188,7 +188,7 @@ class RNodeMultiInterface(Interface):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.HW_MTU = 508
|
self.HW_MTU = 508
|
||||||
|
|
||||||
self.clients = 0
|
self.clients = 0
|
||||||
self.pyserial = serial
|
self.pyserial = serial
|
||||||
self.serial = None
|
self.serial = None
|
||||||
|
@ -294,7 +294,7 @@ class RNodeMultiInterface(Interface):
|
||||||
|
|
||||||
self.detect()
|
self.detect()
|
||||||
sleep(0.2)
|
sleep(0.2)
|
||||||
|
|
||||||
if not self.detected:
|
if not self.detected:
|
||||||
RNS.log(f"Could not detect device for {self}", RNS.LOG_ERROR)
|
RNS.log(f"Could not detect device for {self}", RNS.LOG_ERROR)
|
||||||
self.serial.close()
|
self.serial.close()
|
||||||
|
@ -327,7 +327,7 @@ class RNodeMultiInterface(Interface):
|
||||||
|
|
||||||
interface.OUT = subint[10]
|
interface.OUT = subint[10]
|
||||||
interface.IN = True
|
interface.IN = True
|
||||||
|
|
||||||
interface.announce_rate_target = self.announce_rate_target
|
interface.announce_rate_target = self.announce_rate_target
|
||||||
interface.mode = self.mode
|
interface.mode = self.mode
|
||||||
interface.HW_MTU = self.HW_MTU
|
interface.HW_MTU = self.HW_MTU
|
||||||
|
@ -345,13 +345,13 @@ class RNodeMultiInterface(Interface):
|
||||||
written = self.serial.write(kiss_command)
|
written = self.serial.write(kiss_command)
|
||||||
if written != len(kiss_command):
|
if written != len(kiss_command):
|
||||||
raise OSError(f"An IO error occurred while detecting hardware for {self}")
|
raise OSError(f"An IO error occurred while detecting hardware for {self}")
|
||||||
|
|
||||||
def leave(self):
|
def leave(self):
|
||||||
kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND])
|
kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND])
|
||||||
written = self.serial.write(kiss_command)
|
written = self.serial.write(kiss_command)
|
||||||
if written != len(kiss_command):
|
if written != len(kiss_command):
|
||||||
raise OSError("An IO error occurred while sending host left command to device")
|
raise OSError("An IO error occurred while sending host left command to device")
|
||||||
|
|
||||||
def enable_external_framebuffer(self):
|
def enable_external_framebuffer(self):
|
||||||
if self.display != None:
|
if self.display != None:
|
||||||
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x01, KISS.FEND])
|
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x01, KISS.FEND])
|
||||||
|
@ -385,7 +385,7 @@ class RNodeMultiInterface(Interface):
|
||||||
data = line_byte+line_data
|
data = line_byte+line_data
|
||||||
escaped_data = KISS.escape(data)
|
escaped_data = KISS.escape(data)
|
||||||
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FB_WRITE])+escaped_data+bytes([KISS.FEND])
|
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FB_WRITE])+escaped_data+bytes([KISS.FEND])
|
||||||
|
|
||||||
written = self.serial.write(kiss_command)
|
written = self.serial.write(kiss_command)
|
||||||
if written != len(kiss_command):
|
if written != len(kiss_command):
|
||||||
raise OSError("An IO error occurred while writing framebuffer data device")
|
raise OSError("An IO error occurred while writing framebuffer data device")
|
||||||
|
@ -485,7 +485,7 @@ class RNodeMultiInterface(Interface):
|
||||||
if (self.maj_version >= RNodeMultiInterface.REQUIRED_FW_VER_MAJ):
|
if (self.maj_version >= RNodeMultiInterface.REQUIRED_FW_VER_MAJ):
|
||||||
if (self.min_version >= RNodeMultiInterface.REQUIRED_FW_VER_MIN):
|
if (self.min_version >= RNodeMultiInterface.REQUIRED_FW_VER_MIN):
|
||||||
self.firmware_ok = True
|
self.firmware_ok = True
|
||||||
|
|
||||||
if self.firmware_ok:
|
if self.firmware_ok:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -737,7 +737,7 @@ class RNodeMultiInterface(Interface):
|
||||||
atl = command_buffer[2] << 8 | command_buffer[3]
|
atl = command_buffer[2] << 8 | command_buffer[3]
|
||||||
cus = command_buffer[4] << 8 | command_buffer[5]
|
cus = command_buffer[4] << 8 | command_buffer[5]
|
||||||
cul = command_buffer[6] << 8 | command_buffer[7]
|
cul = command_buffer[6] << 8 | command_buffer[7]
|
||||||
|
|
||||||
self.r_airtime_short = ats/100.0
|
self.r_airtime_short = ats/100.0
|
||||||
self.r_airtime_long = atl/100.0
|
self.r_airtime_long = atl/100.0
|
||||||
self.r_channel_load_short = cus/100.0
|
self.r_channel_load_short = cus/100.0
|
||||||
|
@ -804,7 +804,7 @@ class RNodeMultiInterface(Interface):
|
||||||
# add the interface to the back of the list, they're all given from vport 0 and up in order
|
# add the interface to the back of the list, they're all given from vport 0 and up in order
|
||||||
self.subinterface_types.append(KISS.interface_type_to_str(command_buffer[1]))
|
self.subinterface_types.append(KISS.interface_type_to_str(command_buffer[1]))
|
||||||
command_buffer = b""
|
command_buffer = b""
|
||||||
|
|
||||||
else:
|
else:
|
||||||
time_since_last = int(time.time()*1000) - last_read_ms
|
time_since_last = int(time.time()*1000) - last_read_ms
|
||||||
if len(data_buffer) > 0 and time_since_last > self.timeout:
|
if len(data_buffer) > 0 and time_since_last > self.timeout:
|
||||||
|
@ -918,7 +918,7 @@ class RNodeSubInterface(Interface):
|
||||||
RNS.panic()
|
RNS.panic()
|
||||||
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
if index == 0:
|
if index == 0:
|
||||||
sel_cmd = KISS.CMD_SEL_INT0
|
sel_cmd = KISS.CMD_SEL_INT0
|
||||||
data_cmd= KISS.CMD_INT0_DATA
|
data_cmd= KISS.CMD_INT0_DATA
|
||||||
|
@ -1079,7 +1079,7 @@ class RNodeSubInterface(Interface):
|
||||||
RNS.log(f"After configuring {self}, the reported radio parameters did not match your configuration.", RNS.LOG_ERROR)
|
RNS.log(f"After configuring {self}, the reported radio parameters did not match your configuration.", RNS.LOG_ERROR)
|
||||||
RNS.log("Make sure that your hardware actually supports the parameters specified in the configuration", RNS.LOG_ERROR)
|
RNS.log("Make sure that your hardware actually supports the parameters specified in the configuration", RNS.LOG_ERROR)
|
||||||
RNS.log("Aborting RNode startup", RNS.LOG_ERROR)
|
RNS.log("Aborting RNode startup", RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
|
||||||
def initRadio(self):
|
def initRadio(self):
|
||||||
self.parent_interface.setFrequency(self.frequency, self)
|
self.parent_interface.setFrequency(self.frequency, self)
|
||||||
|
|
|
@ -63,7 +63,7 @@ class SerialInterface(Interface):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.HW_MTU = 564
|
self.HW_MTU = 564
|
||||||
|
|
||||||
self.pyserial = serial
|
self.pyserial = serial
|
||||||
self.serial = None
|
self.serial = None
|
||||||
self.owner = owner
|
self.owner = owner
|
||||||
|
@ -122,7 +122,7 @@ class SerialInterface(Interface):
|
||||||
|
|
||||||
|
|
||||||
def processIncoming(self, data):
|
def processIncoming(self, data):
|
||||||
self.rxb += len(data)
|
self.rxb += len(data)
|
||||||
self.owner.inbound(data, self)
|
self.owner.inbound(data, self)
|
||||||
|
|
||||||
|
|
||||||
|
@ -130,7 +130,7 @@ class SerialInterface(Interface):
|
||||||
if self.online:
|
if self.online:
|
||||||
data = bytes([HDLC.FLAG])+HDLC.escape(data)+bytes([HDLC.FLAG])
|
data = bytes([HDLC.FLAG])+HDLC.escape(data)+bytes([HDLC.FLAG])
|
||||||
written = self.serial.write(data)
|
written = self.serial.write(data)
|
||||||
self.txb += len(data)
|
self.txb += len(data)
|
||||||
if written != len(data):
|
if written != len(data):
|
||||||
raise OSError(f"Serial interface only wrote {written} bytes of {len(data)}")
|
raise OSError(f"Serial interface only wrote {written} bytes of {len(data)}")
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ class SerialInterface(Interface):
|
||||||
byte = HDLC.ESC
|
byte = HDLC.ESC
|
||||||
escape = False
|
escape = False
|
||||||
data_buffer = data_buffer+bytes([byte])
|
data_buffer = data_buffer+bytes([byte])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
time_since_last = int(time.time()*1000) - last_read_ms
|
time_since_last = int(time.time()*1000) - last_read_ms
|
||||||
if len(data_buffer) > 0 and time_since_last > self.timeout:
|
if len(data_buffer) > 0 and time_since_last > self.timeout:
|
||||||
|
@ -172,12 +172,12 @@ class SerialInterface(Interface):
|
||||||
in_frame = False
|
in_frame = False
|
||||||
escape = False
|
escape = False
|
||||||
sleep(0.08)
|
sleep(0.08)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.online = False
|
self.online = False
|
||||||
RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
|
RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
|
RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
|
||||||
|
|
||||||
if RNS.Reticulum.panic_on_interface_error:
|
if RNS.Reticulum.panic_on_interface_error:
|
||||||
RNS.panic()
|
RNS.panic()
|
||||||
|
|
||||||
|
|
|
@ -80,9 +80,9 @@ class TCPClientInterface(Interface):
|
||||||
|
|
||||||
def __init__(self, owner, name, target_ip=None, target_port=None, connected_socket=None, max_reconnect_tries=None, kiss_framing=False, i2p_tunneled = False, connect_timeout = None):
|
def __init__(self, owner, name, target_ip=None, target_port=None, connected_socket=None, max_reconnect_tries=None, kiss_framing=False, i2p_tunneled = False, connect_timeout = None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.HW_MTU = 1064
|
self.HW_MTU = 1064
|
||||||
|
|
||||||
self.IN = True
|
self.IN = True
|
||||||
self.OUT = False
|
self.OUT = False
|
||||||
self.socket = None
|
self.socket = None
|
||||||
|
@ -99,7 +99,7 @@ class TCPClientInterface(Interface):
|
||||||
self.i2p_tunneled = i2p_tunneled
|
self.i2p_tunneled = i2p_tunneled
|
||||||
self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL
|
self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL
|
||||||
self.bitrate = TCPClientInterface.BITRATE_GUESS
|
self.bitrate = TCPClientInterface.BITRATE_GUESS
|
||||||
|
|
||||||
if max_reconnect_tries == None:
|
if max_reconnect_tries == None:
|
||||||
self.max_reconnect_tries = TCPClientInterface.RECONNECT_MAX_TRIES
|
self.max_reconnect_tries = TCPClientInterface.RECONNECT_MAX_TRIES
|
||||||
else:
|
else:
|
||||||
|
@ -128,14 +128,14 @@ class TCPClientInterface(Interface):
|
||||||
self.connect_timeout = connect_timeout
|
self.connect_timeout = connect_timeout
|
||||||
else:
|
else:
|
||||||
self.connect_timeout = TCPClientInterface.INITIAL_CONNECT_TIMEOUT
|
self.connect_timeout = TCPClientInterface.INITIAL_CONNECT_TIMEOUT
|
||||||
|
|
||||||
if TCPClientInterface.SYNCHRONOUS_START:
|
if TCPClientInterface.SYNCHRONOUS_START:
|
||||||
self.initial_connect()
|
self.initial_connect()
|
||||||
else:
|
else:
|
||||||
thread = threading.Thread(target=self.initial_connect)
|
thread = threading.Thread(target=self.initial_connect)
|
||||||
thread.daemon = True
|
thread.daemon = True
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
def initial_connect(self):
|
def initial_connect(self):
|
||||||
if not self.connect(initial=True):
|
if not self.connect(initial=True):
|
||||||
thread = threading.Thread(target=self.reconnect)
|
thread = threading.Thread(target=self.reconnect)
|
||||||
|
@ -170,19 +170,19 @@ class TCPClientInterface(Interface):
|
||||||
TCP_KEEPIDLE = 0x10
|
TCP_KEEPIDLE = 0x10
|
||||||
|
|
||||||
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
||||||
|
|
||||||
if not self.i2p_tunneled:
|
if not self.i2p_tunneled:
|
||||||
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(TCPClientInterface.TCP_PROBE_AFTER))
|
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(TCPClientInterface.TCP_PROBE_AFTER))
|
||||||
else:
|
else:
|
||||||
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(TCPClientInterface.I2P_PROBE_AFTER))
|
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(TCPClientInterface.I2P_PROBE_AFTER))
|
||||||
|
|
||||||
def detach(self):
|
def detach(self):
|
||||||
if self.socket != None:
|
if self.socket != None:
|
||||||
if hasattr(self.socket, "close"):
|
if hasattr(self.socket, "close"):
|
||||||
if callable(self.socket.close):
|
if callable(self.socket.close):
|
||||||
RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
|
RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
|
||||||
self.detached = True
|
self.detached = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.socket.shutdown(socket.SHUT_RDWR)
|
self.socket.shutdown(socket.SHUT_RDWR)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -209,13 +209,13 @@ class TCPClientInterface(Interface):
|
||||||
|
|
||||||
if initial:
|
if initial:
|
||||||
RNS.log(f"TCP connection for {self} established", RNS.LOG_DEBUG)
|
RNS.log(f"TCP connection for {self} established", RNS.LOG_DEBUG)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if initial:
|
if initial:
|
||||||
RNS.log(f"Initial connection for {self} could not be established: {e}", RNS.LOG_ERROR)
|
RNS.log(f"Initial connection for {self} could not be established: {e}", RNS.LOG_ERROR)
|
||||||
RNS.log(f"Leaving unconnected and retrying connection in {TCPClientInterface.RECONNECT_WAIT} seconds.", RNS.LOG_ERROR)
|
RNS.log(f"Leaving unconnected and retrying connection in {TCPClientInterface.RECONNECT_WAIT} seconds.", RNS.LOG_ERROR)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
@ -223,7 +223,7 @@ class TCPClientInterface(Interface):
|
||||||
self.set_timeouts_linux()
|
self.set_timeouts_linux()
|
||||||
elif platform.system() == "Darwin":
|
elif platform.system() == "Darwin":
|
||||||
self.set_timeouts_osx()
|
self.set_timeouts_osx()
|
||||||
|
|
||||||
self.online = True
|
self.online = True
|
||||||
self.writing = False
|
self.writing = False
|
||||||
self.never_connected = False
|
self.never_connected = False
|
||||||
|
@ -269,7 +269,7 @@ class TCPClientInterface(Interface):
|
||||||
self.rxb += len(data)
|
self.rxb += len(data)
|
||||||
if hasattr(self, "parent_interface") and self.parent_interface != None:
|
if hasattr(self, "parent_interface") and self.parent_interface != None:
|
||||||
self.parent_interface.rxb += len(data)
|
self.parent_interface.rxb += len(data)
|
||||||
|
|
||||||
self.owner.inbound(data, self)
|
self.owner.inbound(data, self)
|
||||||
|
|
||||||
def processOutgoing(self, data):
|
def processOutgoing(self, data):
|
||||||
|
@ -369,7 +369,7 @@ class TCPClientInterface(Interface):
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.online = False
|
self.online = False
|
||||||
RNS.log(f"An interface error occurred for {self}, the contained exception was: {e}", RNS.LOG_WARNING)
|
RNS.log(f"An interface error occurred for {self}, the contained exception was: {e}", RNS.LOG_WARNING)
|
||||||
|
@ -427,7 +427,7 @@ class TCPServerInterface(Interface):
|
||||||
|
|
||||||
self.online = False
|
self.online = False
|
||||||
self.clients = 0
|
self.clients = 0
|
||||||
|
|
||||||
self.IN = True
|
self.IN = True
|
||||||
self.OUT = False
|
self.OUT = False
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -474,7 +474,7 @@ class TCPServerInterface(Interface):
|
||||||
spawned_interface.target_port = str(handler.client_address[1])
|
spawned_interface.target_port = str(handler.client_address[1])
|
||||||
spawned_interface.parent_interface = self
|
spawned_interface.parent_interface = self
|
||||||
spawned_interface.bitrate = self.bitrate
|
spawned_interface.bitrate = self.bitrate
|
||||||
|
|
||||||
spawned_interface.ifac_size = self.ifac_size
|
spawned_interface.ifac_size = self.ifac_size
|
||||||
spawned_interface.ifac_netname = self.ifac_netname
|
spawned_interface.ifac_netname = self.ifac_netname
|
||||||
spawned_interface.ifac_netkey = self.ifac_netkey
|
spawned_interface.ifac_netkey = self.ifac_netkey
|
||||||
|
|
|
@ -99,7 +99,7 @@ class UDPInterface(Interface):
|
||||||
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||||
udp_socket.sendto(data, (self.forward_ip, self.forward_port))
|
udp_socket.sendto(data, (self.forward_ip, self.forward_port))
|
||||||
self.txb += len(data)
|
self.txb += len(data)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log(f"Could not transmit on {self}. The contained exception was: {e}", RNS.LOG_ERROR)
|
RNS.log(f"Could not transmit on {self}. The contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
|
32
RNS/Link.py
32
RNS/Link.py
|
@ -124,7 +124,7 @@ class Link:
|
||||||
RNS.Transport.register_link(link)
|
RNS.Transport.register_link(link)
|
||||||
link.last_inbound = time.time()
|
link.last_inbound = time.time()
|
||||||
link.start_watchdog()
|
link.start_watchdog()
|
||||||
|
|
||||||
RNS.log(f"Incoming link request {link} accepted on {link.attached_interface}", RNS.LOG_DEBUG)
|
RNS.log(f"Incoming link request {link} accepted on {link.attached_interface}", RNS.LOG_DEBUG)
|
||||||
return link
|
return link
|
||||||
|
|
||||||
|
@ -189,7 +189,7 @@ class Link:
|
||||||
self.sig_prv = Ed25519PrivateKey.generate()
|
self.sig_prv = Ed25519PrivateKey.generate()
|
||||||
|
|
||||||
self.fernet = None
|
self.fernet = None
|
||||||
|
|
||||||
self.pub = self.prv.public_key()
|
self.pub = self.prv.public_key()
|
||||||
self.pub_bytes = self.pub.public_bytes()
|
self.pub_bytes = self.pub.public_bytes()
|
||||||
|
|
||||||
|
@ -288,7 +288,7 @@ class Link:
|
||||||
self.establishment_cost += len(packet.raw)
|
self.establishment_cost += len(packet.raw)
|
||||||
signed_data = self.link_id+self.peer_pub_bytes+self.peer_sig_pub_bytes
|
signed_data = self.link_id+self.peer_pub_bytes+self.peer_sig_pub_bytes
|
||||||
signature = packet.data[:RNS.Identity.SIGLENGTH//8]
|
signature = packet.data[:RNS.Identity.SIGLENGTH//8]
|
||||||
|
|
||||||
if self.destination.identity.validate(signature, signed_data):
|
if self.destination.identity.validate(signature, signed_data):
|
||||||
if self.status != Link.HANDSHAKE:
|
if self.status != Link.HANDSHAKE:
|
||||||
raise OSError(f"Invalid link state for proof validation: {self.status}")
|
raise OSError(f"Invalid link state for proof validation: {self.status}")
|
||||||
|
@ -301,7 +301,7 @@ class Link:
|
||||||
self.last_proof = self.activated_at
|
self.last_proof = self.activated_at
|
||||||
RNS.Transport.activate_link(self)
|
RNS.Transport.activate_link(self)
|
||||||
RNS.log(f"Link {self} established with {self.destination}, RTT is {round(self.rtt, 3)}s", RNS.LOG_VERBOSE)
|
RNS.log(f"Link {self} established with {self.destination}, RTT is {round(self.rtt, 3)}s", RNS.LOG_VERBOSE)
|
||||||
|
|
||||||
if self.rtt != None and self.establishment_cost != None and self.rtt > 0 and self.establishment_cost > 0:
|
if self.rtt != None and self.establishment_cost != None and self.rtt > 0 and self.establishment_cost > 0:
|
||||||
self.establishment_rate = self.establishment_cost/self.rtt
|
self.establishment_rate = self.establishment_cost/self.rtt
|
||||||
|
|
||||||
|
@ -316,7 +316,7 @@ class Link:
|
||||||
thread.start()
|
thread.start()
|
||||||
else:
|
else:
|
||||||
RNS.log(f"Invalid link proof signature received by {self}. Ignoring.", RNS.LOG_DEBUG)
|
RNS.log(f"Invalid link proof signature received by {self}. Ignoring.", RNS.LOG_DEBUG)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.status = Link.CLOSED
|
self.status = Link.CLOSED
|
||||||
RNS.log(f"An error ocurred while validating link request proof on {self}.", RNS.LOG_ERROR)
|
RNS.log(f"An error ocurred while validating link request proof on {self}.", RNS.LOG_ERROR)
|
||||||
|
@ -377,7 +377,7 @@ class Link:
|
||||||
timeout = timeout,
|
timeout = timeout,
|
||||||
request_size = len(packed_request),
|
request_size = len(packed_request),
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
request_id = RNS.Identity.truncated_hash(packed_request)
|
request_id = RNS.Identity.truncated_hash(packed_request)
|
||||||
RNS.log(f"Sending request {RNS.prettyhexrep(request_id)} as resource.", RNS.LOG_DEBUG)
|
RNS.log(f"Sending request {RNS.prettyhexrep(request_id)} as resource.", RNS.LOG_DEBUG)
|
||||||
|
@ -547,7 +547,7 @@ class Link:
|
||||||
resource.cancel()
|
resource.cancel()
|
||||||
if self._channel:
|
if self._channel:
|
||||||
self._channel._shutdown()
|
self._channel._shutdown()
|
||||||
|
|
||||||
self.prv = None
|
self.prv = None
|
||||||
self.pub = None
|
self.pub = None
|
||||||
self.pub_bytes = None
|
self.pub_bytes = None
|
||||||
|
@ -620,7 +620,7 @@ class Link:
|
||||||
self.status = Link.STALE
|
self.status = Link.STALE
|
||||||
else:
|
else:
|
||||||
sleep_time = self.keepalive
|
sleep_time = self.keepalive
|
||||||
|
|
||||||
else:
|
else:
|
||||||
sleep_time = (last_inbound + self.keepalive) - time.time()
|
sleep_time = (last_inbound + self.keepalive) - time.time()
|
||||||
|
|
||||||
|
@ -655,7 +655,7 @@ class Link:
|
||||||
self.snr = packet.snr
|
self.snr = packet.snr
|
||||||
if packet.q != None:
|
if packet.q != None:
|
||||||
self.q = packet.q
|
self.q = packet.q
|
||||||
|
|
||||||
def send_keepalive(self):
|
def send_keepalive(self):
|
||||||
keepalive_packet = RNS.Packet(self, bytes([0xFF]), context=RNS.Packet.KEEPALIVE)
|
keepalive_packet = RNS.Packet(self, bytes([0xFF]), context=RNS.Packet.KEEPALIVE)
|
||||||
keepalive_packet.send()
|
keepalive_packet.send()
|
||||||
|
@ -782,7 +782,7 @@ class Link:
|
||||||
thread = threading.Thread(target=self.callbacks.packet, args=(plaintext, packet))
|
thread = threading.Thread(target=self.callbacks.packet, args=(plaintext, packet))
|
||||||
thread.daemon = True
|
thread.daemon = True
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
if self.destination.proof_strategy == RNS.Destination.PROVE_ALL:
|
if self.destination.proof_strategy == RNS.Destination.PROVE_ALL:
|
||||||
packet.prove()
|
packet.prove()
|
||||||
should_query = True
|
should_query = True
|
||||||
|
@ -815,7 +815,7 @@ class Link:
|
||||||
self.callbacks.remote_identified(self, self.__remote_identity)
|
self.callbacks.remote_identified(self, self.__remote_identity)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log(f"Error while executing remote identified callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
|
RNS.log(f"Error while executing remote identified callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
|
|
||||||
self.__update_phy_stats(packet, query_shared=True)
|
self.__update_phy_stats(packet, query_shared=True)
|
||||||
|
|
||||||
elif packet.context == RNS.Packet.REQUEST:
|
elif packet.context == RNS.Packet.REQUEST:
|
||||||
|
@ -903,7 +903,7 @@ class Link:
|
||||||
if not packet.packet_hash in resource.req_hashlist:
|
if not packet.packet_hash in resource.req_hashlist:
|
||||||
resource.req_hashlist.append(packet.packet_hash)
|
resource.req_hashlist.append(packet.packet_hash)
|
||||||
resource.request(plaintext)
|
resource.request(plaintext)
|
||||||
|
|
||||||
# TODO: Test and possibly enable this at some point
|
# TODO: Test and possibly enable this at some point
|
||||||
# def request_job():
|
# def request_job():
|
||||||
# resource.request(plaintext)
|
# resource.request(plaintext)
|
||||||
|
@ -984,7 +984,7 @@ class Link:
|
||||||
try:
|
try:
|
||||||
if not self.fernet:
|
if not self.fernet:
|
||||||
self.fernet = Fernet(self.derived_key)
|
self.fernet = Fernet(self.derived_key)
|
||||||
|
|
||||||
return self.fernet.decrypt(ciphertext)
|
return self.fernet.decrypt(ciphertext)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -1140,7 +1140,7 @@ class RequestReceipt():
|
||||||
elif self.resource != None:
|
elif self.resource != None:
|
||||||
self.hash = resource.request_id
|
self.hash = resource.request_id
|
||||||
resource.set_callback(self.request_resource_concluded)
|
resource.set_callback(self.request_resource_concluded)
|
||||||
|
|
||||||
self.link = link
|
self.link = link
|
||||||
self.request_id = self.hash
|
self.request_id = self.hash
|
||||||
self.request_size = request_size
|
self.request_size = request_size
|
||||||
|
@ -1224,7 +1224,7 @@ class RequestReceipt():
|
||||||
self.packet_receipt.callbacks.delivery(self.packet_receipt)
|
self.packet_receipt.callbacks.delivery(self.packet_receipt)
|
||||||
|
|
||||||
self.progress = resource.get_progress()
|
self.progress = resource.get_progress()
|
||||||
|
|
||||||
if self.callbacks.progress != None:
|
if self.callbacks.progress != None:
|
||||||
try:
|
try:
|
||||||
self.callbacks.progress(self)
|
self.callbacks.progress(self)
|
||||||
|
@ -1233,7 +1233,7 @@ class RequestReceipt():
|
||||||
else:
|
else:
|
||||||
resource.cancel()
|
resource.cancel()
|
||||||
|
|
||||||
|
|
||||||
def response_received(self, response):
|
def response_received(self, response):
|
||||||
if not self.status == RequestReceipt.FAILED:
|
if not self.status == RequestReceipt.FAILED:
|
||||||
self.progress = 1.0
|
self.progress = 1.0
|
||||||
|
|
|
@ -97,7 +97,7 @@ class Packet:
|
||||||
# the below calculation; 383 bytes.
|
# the below calculation; 383 bytes.
|
||||||
ENCRYPTED_MDU = math.floor((RNS.Reticulum.MDU-RNS.Identity.FERNET_OVERHEAD-RNS.Identity.KEYSIZE//16)/RNS.Identity.AES128_BLOCKSIZE)*RNS.Identity.AES128_BLOCKSIZE - 1
|
ENCRYPTED_MDU = math.floor((RNS.Reticulum.MDU-RNS.Identity.FERNET_OVERHEAD-RNS.Identity.KEYSIZE//16)/RNS.Identity.AES128_BLOCKSIZE)*RNS.Identity.AES128_BLOCKSIZE - 1
|
||||||
"""
|
"""
|
||||||
The maximum size of the payload data in a single encrypted packet
|
The maximum size of the payload data in a single encrypted packet
|
||||||
"""
|
"""
|
||||||
PLAIN_MDU = MDU
|
PLAIN_MDU = MDU
|
||||||
"""
|
"""
|
||||||
|
@ -256,7 +256,7 @@ class Packet:
|
||||||
def send(self):
|
def send(self):
|
||||||
"""
|
"""
|
||||||
Sends the packet.
|
Sends the packet.
|
||||||
|
|
||||||
:returns: A :ref:`RNS.PacketReceipt<api-packetreceipt>` instance if *create_receipt* was set to *True* when the packet was instantiated, if not returns *None*. If the packet could not be sent *False* is returned.
|
:returns: A :ref:`RNS.PacketReceipt<api-packetreceipt>` instance if *create_receipt* was set to *True* when the packet was instantiated, if not returns *None*. If the packet could not be sent *False* is returned.
|
||||||
"""
|
"""
|
||||||
if not self.sent:
|
if not self.sent:
|
||||||
|
@ -278,21 +278,21 @@ class Packet:
|
||||||
self.sent = False
|
self.sent = False
|
||||||
self.receipt = None
|
self.receipt = None
|
||||||
return False
|
return False
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise OSError("Packet was already sent")
|
raise OSError("Packet was already sent")
|
||||||
|
|
||||||
def resend(self):
|
def resend(self):
|
||||||
"""
|
"""
|
||||||
Re-sends the packet.
|
Re-sends the packet.
|
||||||
|
|
||||||
:returns: A :ref:`RNS.PacketReceipt<api-packetreceipt>` instance if *create_receipt* was set to *True* when the packet was instantiated, if not returns *None*. If the packet could not be sent *False* is returned.
|
:returns: A :ref:`RNS.PacketReceipt<api-packetreceipt>` instance if *create_receipt* was set to *True* when the packet was instantiated, if not returns *None*. If the packet could not be sent *False* is returned.
|
||||||
"""
|
"""
|
||||||
if self.sent:
|
if self.sent:
|
||||||
# Re-pack the packet to obtain new ciphertext for
|
# Re-pack the packet to obtain new ciphertext for
|
||||||
# encrypted destinations
|
# encrypted destinations
|
||||||
self.pack()
|
self.pack()
|
||||||
|
|
||||||
if RNS.Transport.outbound(self):
|
if RNS.Transport.outbound(self):
|
||||||
return self.receipt
|
return self.receipt
|
||||||
else:
|
else:
|
||||||
|
@ -388,7 +388,7 @@ class PacketReceipt:
|
||||||
|
|
||||||
def get_status(self):
|
def get_status(self):
|
||||||
"""
|
"""
|
||||||
:returns: The status of the associated :ref:`RNS.Packet<api-packet>` instance. Can be one of ``RNS.PacketReceipt.SENT``, ``RNS.PacketReceipt.DELIVERED``, ``RNS.PacketReceipt.FAILED`` or ``RNS.PacketReceipt.CULLED``.
|
:returns: The status of the associated :ref:`RNS.Packet<api-packet>` instance. Can be one of ``RNS.PacketReceipt.SENT``, ``RNS.PacketReceipt.DELIVERED``, ``RNS.PacketReceipt.FAILED`` or ``RNS.PacketReceipt.CULLED``.
|
||||||
"""
|
"""
|
||||||
return self.status
|
return self.status
|
||||||
|
|
||||||
|
@ -422,7 +422,7 @@ class PacketReceipt:
|
||||||
RNS.log(f"An error occurred while evaluating external delivery callback for {link}", RNS.LOG_ERROR)
|
RNS.log(f"An error occurred while evaluating external delivery callback for {link}", RNS.LOG_ERROR)
|
||||||
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
|
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
RNS.trace_exception(e)
|
RNS.trace_exception(e)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
@ -490,7 +490,7 @@ class PacketReceipt:
|
||||||
self.callbacks.delivery(self)
|
self.callbacks.delivery(self)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log(f"Error while executing proof validated callback. The contained exception was: {e}", RNS.LOG_ERROR)
|
RNS.log(f"Error while executing proof validated callback. The contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
@ -524,7 +524,7 @@ class PacketReceipt:
|
||||||
def set_timeout(self, timeout):
|
def set_timeout(self, timeout):
|
||||||
"""
|
"""
|
||||||
Sets a timeout in seconds
|
Sets a timeout in seconds
|
||||||
|
|
||||||
:param timeout: The timeout in seconds.
|
:param timeout: The timeout in seconds.
|
||||||
"""
|
"""
|
||||||
self.timeout = float(timeout)
|
self.timeout = float(timeout)
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
# SOFTWARE.
|
# SOFTWARE.
|
||||||
|
|
||||||
class Resolver:
|
class Resolver:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def resolve_identity(full_name):
|
def resolve_identity(full_name):
|
||||||
pass
|
pass
|
|
@ -59,11 +59,11 @@ class Resource:
|
||||||
|
|
||||||
# The maximum window size for transfers on fast links
|
# The maximum window size for transfers on fast links
|
||||||
WINDOW_MAX_FAST = 75
|
WINDOW_MAX_FAST = 75
|
||||||
|
|
||||||
# For calculating maps and guard segments, this
|
# For calculating maps and guard segments, this
|
||||||
# must be set to the global maximum window.
|
# must be set to the global maximum window.
|
||||||
WINDOW_MAX = WINDOW_MAX_FAST
|
WINDOW_MAX = WINDOW_MAX_FAST
|
||||||
|
|
||||||
# If the fast rate is sustained for this many request
|
# If the fast rate is sustained for this many request
|
||||||
# rounds, the fast link window size will be allowed.
|
# rounds, the fast link window size will be allowed.
|
||||||
FAST_RATE_THRESHOLD = WINDOW_MAX_SLOW - WINDOW - 2
|
FAST_RATE_THRESHOLD = WINDOW_MAX_SLOW - WINDOW - 2
|
||||||
|
@ -111,7 +111,7 @@ class Resource:
|
||||||
# fit in 3 bytes in resource advertisements.
|
# fit in 3 bytes in resource advertisements.
|
||||||
MAX_EFFICIENT_SIZE = 16 * 1024 * 1024 - 1
|
MAX_EFFICIENT_SIZE = 16 * 1024 * 1024 - 1
|
||||||
RESPONSE_MAX_GRACE_TIME = 10
|
RESPONSE_MAX_GRACE_TIME = 10
|
||||||
|
|
||||||
# The maximum size to auto-compress with
|
# The maximum size to auto-compress with
|
||||||
# bz2 before sending.
|
# bz2 before sending.
|
||||||
AUTO_COMPRESS_MAX_SIZE = MAX_EFFICIENT_SIZE
|
AUTO_COMPRESS_MAX_SIZE = MAX_EFFICIENT_SIZE
|
||||||
|
@ -186,7 +186,7 @@ class Resource:
|
||||||
resource.waiting_for_hmu = False
|
resource.waiting_for_hmu = False
|
||||||
resource.receiving_part = False
|
resource.receiving_part = False
|
||||||
resource.consecutive_completed_height = -1
|
resource.consecutive_completed_height = -1
|
||||||
|
|
||||||
if not resource.link.has_incoming_resource(resource):
|
if not resource.link.has_incoming_resource(resource):
|
||||||
resource.link.register_incoming_resource(resource)
|
resource.link.register_incoming_resource(resource)
|
||||||
|
|
||||||
|
@ -255,7 +255,7 @@ class Resource:
|
||||||
elif isinstance(data, bytes):
|
elif isinstance(data, bytes):
|
||||||
data_size = len(data)
|
data_size = len(data)
|
||||||
self.total_size = data_size
|
self.total_size = data_size
|
||||||
|
|
||||||
resource_data = data
|
resource_data = data
|
||||||
self.total_segments = 1
|
self.total_segments = 1
|
||||||
self.segment_index = 1
|
self.segment_index = 1
|
||||||
|
@ -322,7 +322,7 @@ class Resource:
|
||||||
self.data = b""
|
self.data = b""
|
||||||
self.data += RNS.Identity.get_random_hash()[:Resource.RANDOM_HASH_SIZE]
|
self.data += RNS.Identity.get_random_hash()[:Resource.RANDOM_HASH_SIZE]
|
||||||
self.data += self.compressed_data
|
self.data += self.compressed_data
|
||||||
|
|
||||||
self.compressed = True
|
self.compressed = True
|
||||||
self.uncompressed_data = None
|
self.uncompressed_data = None
|
||||||
|
|
||||||
|
@ -348,7 +348,7 @@ class Resource:
|
||||||
self.sent_parts = 0
|
self.sent_parts = 0
|
||||||
hashmap_entries = int(math.ceil(self.size/float(Resource.SDU)))
|
hashmap_entries = int(math.ceil(self.size/float(Resource.SDU)))
|
||||||
self.total_parts = hashmap_entries
|
self.total_parts = hashmap_entries
|
||||||
|
|
||||||
hashmap_ok = False
|
hashmap_ok = False
|
||||||
while not hashmap_ok:
|
while not hashmap_ok:
|
||||||
hashmap_computation_began = time.time()
|
hashmap_computation_began = time.time()
|
||||||
|
@ -389,12 +389,12 @@ class Resource:
|
||||||
self.parts.append(part)
|
self.parts.append(part)
|
||||||
|
|
||||||
RNS.log(f"Hashmap computation concluded in {round(time.time() - hashmap_computation_began, 3)} seconds", RNS.LOG_DEBUG)
|
RNS.log(f"Hashmap computation concluded in {round(time.time() - hashmap_computation_began, 3)} seconds", RNS.LOG_DEBUG)
|
||||||
|
|
||||||
if advertise:
|
if advertise:
|
||||||
self.advertise()
|
self.advertise()
|
||||||
else:
|
else:
|
||||||
self.receive_lock = Lock()
|
self.receive_lock = Lock()
|
||||||
|
|
||||||
|
|
||||||
def hashmap_update_packet(self, plaintext):
|
def hashmap_update_packet(self, plaintext):
|
||||||
if not self.status == Resource.FAILED:
|
if not self.status == Resource.FAILED:
|
||||||
|
@ -489,7 +489,7 @@ class Resource:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log(f"Could not resend advertisement packet, cancelling resource. The contained exception was: {e}", RNS.LOG_VERBOSE)
|
RNS.log(f"Could not resend advertisement packet, cancelling resource. The contained exception was: {e}", RNS.LOG_VERBOSE)
|
||||||
self.cancel()
|
self.cancel()
|
||||||
|
|
||||||
|
|
||||||
elif self.status == Resource.TRANSFERRING:
|
elif self.status == Resource.TRANSFERRING:
|
||||||
if not self.initiator:
|
if not self.initiator:
|
||||||
|
@ -504,7 +504,7 @@ class Resource:
|
||||||
retries_used = self.max_retries - self.retries_left
|
retries_used = self.max_retries - self.retries_left
|
||||||
extra_wait = retries_used * Resource.PER_RETRY_DELAY
|
extra_wait = retries_used * Resource.PER_RETRY_DELAY
|
||||||
sleep_time = self.last_activity + (rtt*(self.part_timeout_factor+window_remaining)) + Resource.RETRY_GRACE_TIME + extra_wait - time.time()
|
sleep_time = self.last_activity + (rtt*(self.part_timeout_factor+window_remaining)) + Resource.RETRY_GRACE_TIME + extra_wait - time.time()
|
||||||
|
|
||||||
if sleep_time < 0:
|
if sleep_time < 0:
|
||||||
if self.retries_left > 0:
|
if self.retries_left > 0:
|
||||||
ms = "" if self.outstanding_parts == 1 else "s"
|
ms = "" if self.outstanding_parts == 1 else "s"
|
||||||
|
@ -558,7 +558,7 @@ class Resource:
|
||||||
if sleep_time == None or sleep_time < 0:
|
if sleep_time == None or sleep_time < 0:
|
||||||
RNS.log("Timing error, cancelling resource transfer.", RNS.LOG_ERROR)
|
RNS.log("Timing error, cancelling resource transfer.", RNS.LOG_ERROR)
|
||||||
self.cancel()
|
self.cancel()
|
||||||
|
|
||||||
if sleep_time != None:
|
if sleep_time != None:
|
||||||
sleep(min(sleep_time, Resource.WATCHDOG_MAX_SLEEP))
|
sleep(min(sleep_time, Resource.WATCHDOG_MAX_SLEEP))
|
||||||
|
|
||||||
|
@ -697,7 +697,7 @@ class Resource:
|
||||||
if self.req_resp == None:
|
if self.req_resp == None:
|
||||||
self.req_resp = self.last_activity
|
self.req_resp = self.last_activity
|
||||||
rtt = self.req_resp-self.req_sent
|
rtt = self.req_resp-self.req_sent
|
||||||
|
|
||||||
self.part_timeout_factor = Resource.PART_TIMEOUT_FACTOR_AFTER_RTT
|
self.part_timeout_factor = Resource.PART_TIMEOUT_FACTOR_AFTER_RTT
|
||||||
if self.rtt == None:
|
if self.rtt == None:
|
||||||
self.rtt = self.link.rtt
|
self.rtt = self.link.rtt
|
||||||
|
@ -737,7 +737,7 @@ class Resource:
|
||||||
# Update consecutive completed pointer
|
# Update consecutive completed pointer
|
||||||
if i == self.consecutive_completed_height + 1:
|
if i == self.consecutive_completed_height + 1:
|
||||||
self.consecutive_completed_height = i
|
self.consecutive_completed_height = i
|
||||||
|
|
||||||
cp = self.consecutive_completed_height + 1
|
cp = self.consecutive_completed_height + 1
|
||||||
while cp < len(self.parts) and self.parts[cp] != None:
|
while cp < len(self.parts) and self.parts[cp] != None:
|
||||||
self.consecutive_completed_height = cp
|
self.consecutive_completed_height = cp
|
||||||
|
@ -802,7 +802,7 @@ class Resource:
|
||||||
i = 0; pn = self.consecutive_completed_height+1
|
i = 0; pn = self.consecutive_completed_height+1
|
||||||
search_start = pn
|
search_start = pn
|
||||||
search_size = self.window
|
search_size = self.window
|
||||||
|
|
||||||
for part in self.parts[search_start:search_start+search_size]:
|
for part in self.parts[search_start:search_start+search_size]:
|
||||||
if part == None:
|
if part == None:
|
||||||
part_hash = self.hashmap[pn]
|
part_hash = self.hashmap[pn]
|
||||||
|
@ -883,10 +883,10 @@ class Resource:
|
||||||
RNS.log("Resource could not send parts, cancelling transfer!", RNS.LOG_DEBUG)
|
RNS.log("Resource could not send parts, cancelling transfer!", RNS.LOG_DEBUG)
|
||||||
RNS.log(f"The contained exception was: {e}", RNS.LOG_DEBUG)
|
RNS.log(f"The contained exception was: {e}", RNS.LOG_DEBUG)
|
||||||
self.cancel()
|
self.cancel()
|
||||||
|
|
||||||
if wants_more_hashmap:
|
if wants_more_hashmap:
|
||||||
last_map_hash = request_data[1:Resource.MAPHASH_LEN+1]
|
last_map_hash = request_data[1:Resource.MAPHASH_LEN+1]
|
||||||
|
|
||||||
part_index = self.receiver_min_consecutive_height
|
part_index = self.receiver_min_consecutive_height
|
||||||
search_start = part_index
|
search_start = part_index
|
||||||
search_end = self.receiver_min_consecutive_height+ResourceAdvertisement.COLLISION_GUARD_SIZE
|
search_end = self.receiver_min_consecutive_height+ResourceAdvertisement.COLLISION_GUARD_SIZE
|
||||||
|
@ -904,7 +904,7 @@ class Resource:
|
||||||
else:
|
else:
|
||||||
segment = part_index // ResourceAdvertisement.HASHMAP_MAX_LEN
|
segment = part_index // ResourceAdvertisement.HASHMAP_MAX_LEN
|
||||||
|
|
||||||
|
|
||||||
hashmap_start = segment*ResourceAdvertisement.HASHMAP_MAX_LEN
|
hashmap_start = segment*ResourceAdvertisement.HASHMAP_MAX_LEN
|
||||||
hashmap_end = min((segment+1)*ResourceAdvertisement.HASHMAP_MAX_LEN, len(self.parts))
|
hashmap_end = min((segment+1)*ResourceAdvertisement.HASHMAP_MAX_LEN, len(self.parts))
|
||||||
|
|
||||||
|
@ -949,7 +949,7 @@ class Resource:
|
||||||
self.link.cancel_outgoing_resource(self)
|
self.link.cancel_outgoing_resource(self)
|
||||||
else:
|
else:
|
||||||
self.link.cancel_incoming_resource(self)
|
self.link.cancel_incoming_resource(self)
|
||||||
|
|
||||||
if self.callback != None:
|
if self.callback != None:
|
||||||
try:
|
try:
|
||||||
self.link.resource_concluded(self)
|
self.link.resource_concluded(self)
|
||||||
|
@ -969,7 +969,7 @@ class Resource:
|
||||||
"""
|
"""
|
||||||
if self.status == RNS.Resource.COMPLETE and self.segment_index == self.total_segments:
|
if self.status == RNS.Resource.COMPLETE and self.segment_index == self.total_segments:
|
||||||
return 1.0
|
return 1.0
|
||||||
|
|
||||||
elif self.initiator:
|
elif self.initiator:
|
||||||
if not self.split:
|
if not self.split:
|
||||||
self.processed_parts = self.sent_parts
|
self.processed_parts = self.sent_parts
|
||||||
|
@ -1194,7 +1194,7 @@ class ResourceAdvertisement:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def unpack(data):
|
def unpack(data):
|
||||||
dictionary = umsgpack.unpackb(data)
|
dictionary = umsgpack.unpackb(data)
|
||||||
|
|
||||||
adv = ResourceAdvertisement()
|
adv = ResourceAdvertisement()
|
||||||
adv.t = dictionary["t"]
|
adv.t = dictionary["t"]
|
||||||
adv.d = dictionary["d"]
|
adv.d = dictionary["d"]
|
||||||
|
@ -1213,4 +1213,4 @@ class ResourceAdvertisement:
|
||||||
adv.u = True if ((adv.f >> 3) & 0x01) == 0x01 else False
|
adv.u = True if ((adv.f >> 3) & 0x01) == 0x01 else False
|
||||||
adv.p = True if ((adv.f >> 4) & 0x01) == 0x01 else False
|
adv.p = True if ((adv.f >> 4) & 0x01) == 0x01 else False
|
||||||
|
|
||||||
return adv
|
return adv
|
||||||
|
|
|
@ -129,7 +129,7 @@ class Reticulum:
|
||||||
HEADER_MAXSIZE = 2+1+(TRUNCATED_HASHLENGTH//8)*2
|
HEADER_MAXSIZE = 2+1+(TRUNCATED_HASHLENGTH//8)*2
|
||||||
IFAC_MIN_SIZE = 1
|
IFAC_MIN_SIZE = 1
|
||||||
IFAC_SALT = bytes.fromhex("adf54d882c9a9b80771eb4995d702d4a3e733391b2a0f53f416d9f907e55cff8")
|
IFAC_SALT = bytes.fromhex("adf54d882c9a9b80771eb4995d702d4a3e733391b2a0f53f416d9f907e55cff8")
|
||||||
|
|
||||||
MDU = MTU - HEADER_MAXSIZE - IFAC_MIN_SIZE
|
MDU = MTU - HEADER_MAXSIZE - IFAC_MIN_SIZE
|
||||||
|
|
||||||
RESOURCE_CACHE = 24*60*60
|
RESOURCE_CACHE = 24*60*60
|
||||||
|
@ -140,7 +140,7 @@ class Reticulum:
|
||||||
|
|
||||||
router = None
|
router = None
|
||||||
config = None
|
config = None
|
||||||
|
|
||||||
# The default configuration path will be expanded to a directory
|
# The default configuration path will be expanded to a directory
|
||||||
# named ".reticulum" inside the current users home directory
|
# named ".reticulum" inside the current users home directory
|
||||||
userdir = os.path.expanduser("~")
|
userdir = os.path.expanduser("~")
|
||||||
|
@ -150,7 +150,7 @@ class Reticulum:
|
||||||
cachepath = ""
|
cachepath = ""
|
||||||
|
|
||||||
__instance = None
|
__instance = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def exit_handler():
|
def exit_handler():
|
||||||
# This exit handler is called whenever Reticulum is asked to
|
# This exit handler is called whenever Reticulum is asked to
|
||||||
|
@ -212,7 +212,7 @@ class Reticulum:
|
||||||
if logdest == RNS.LOG_FILE:
|
if logdest == RNS.LOG_FILE:
|
||||||
RNS.logdest = RNS.LOG_FILE
|
RNS.logdest = RNS.LOG_FILE
|
||||||
RNS.logfile = f"{Reticulum.configdir}/logfile"
|
RNS.logfile = f"{Reticulum.configdir}/logfile"
|
||||||
|
|
||||||
Reticulum.configpath = f"{Reticulum.configdir}/config"
|
Reticulum.configpath = f"{Reticulum.configdir}/config"
|
||||||
Reticulum.storagepath = f"{Reticulum.configdir}/storage"
|
Reticulum.storagepath = f"{Reticulum.configdir}/storage"
|
||||||
Reticulum.cachepath = f"{Reticulum.configdir}/storage/cache"
|
Reticulum.cachepath = f"{Reticulum.configdir}/storage/cache"
|
||||||
|
@ -278,7 +278,7 @@ class Reticulum:
|
||||||
|
|
||||||
self.__apply_config()
|
self.__apply_config()
|
||||||
RNS.log(f"Configuration loaded from {self.configpath}", RNS.LOG_VERBOSE)
|
RNS.log(f"Configuration loaded from {self.configpath}", RNS.LOG_VERBOSE)
|
||||||
|
|
||||||
RNS.Identity.load_known_destinations()
|
RNS.Identity.load_known_destinations()
|
||||||
|
|
||||||
RNS.Transport.start(self)
|
RNS.Transport.start(self)
|
||||||
|
@ -286,7 +286,7 @@ class Reticulum:
|
||||||
self.rpc_addr = ("127.0.0.1", self.local_control_port)
|
self.rpc_addr = ("127.0.0.1", self.local_control_port)
|
||||||
if self.rpc_key == None:
|
if self.rpc_key == None:
|
||||||
self.rpc_key = RNS.Identity.full_hash(RNS.Transport.identity.get_private_key())
|
self.rpc_key = RNS.Identity.full_hash(RNS.Transport.identity.get_private_key())
|
||||||
|
|
||||||
if self.is_shared_instance:
|
if self.is_shared_instance:
|
||||||
self.rpc_listener = multiprocessing.connection.Listener(self.rpc_addr, authkey=self.rpc_key)
|
self.rpc_listener = multiprocessing.connection.Listener(self.rpc_addr, authkey=self.rpc_key)
|
||||||
thread = threading.Thread(target=self.rpc_loop)
|
thread = threading.Thread(target=self.rpc_loop)
|
||||||
|
@ -314,7 +314,7 @@ class Reticulum:
|
||||||
|
|
||||||
if now > self.last_data_persist+Reticulum.PERSIST_INTERVAL:
|
if now > self.last_data_persist+Reticulum.PERSIST_INTERVAL:
|
||||||
self.__persist_data()
|
self.__persist_data()
|
||||||
|
|
||||||
time.sleep(Reticulum.JOB_INTERVAL)
|
time.sleep(Reticulum.JOB_INTERVAL)
|
||||||
|
|
||||||
def __start_local_interface(self):
|
def __start_local_interface(self):
|
||||||
|
@ -330,7 +330,7 @@ class Reticulum:
|
||||||
interface._force_bitrate = Reticulum._force_shared_instance_bitrate
|
interface._force_bitrate = Reticulum._force_shared_instance_bitrate
|
||||||
RNS.log(f"Forcing shared instance bitrate of {RNS.prettyspeed(interface.bitrate)}", RNS.LOG_WARNING)
|
RNS.log(f"Forcing shared instance bitrate of {RNS.prettyspeed(interface.bitrate)}", RNS.LOG_WARNING)
|
||||||
RNS.Transport.interfaces.append(interface)
|
RNS.Transport.interfaces.append(interface)
|
||||||
|
|
||||||
self.is_shared_instance = True
|
self.is_shared_instance = True
|
||||||
RNS.log(f"Started shared instance interface: {interface}", RNS.LOG_DEBUG)
|
RNS.log(f"Started shared instance interface: {interface}", RNS.LOG_DEBUG)
|
||||||
self.__start_jobs()
|
self.__start_jobs()
|
||||||
|
@ -455,7 +455,7 @@ class Reticulum:
|
||||||
c = self.config["interfaces"][name]
|
c = self.config["interfaces"][name]
|
||||||
|
|
||||||
interface_mode = Interface.Interface.MODE_FULL
|
interface_mode = Interface.Interface.MODE_FULL
|
||||||
|
|
||||||
if "interface_mode" in c:
|
if "interface_mode" in c:
|
||||||
c["interface_mode"] = str(c["interface_mode"]).lower()
|
c["interface_mode"] = str(c["interface_mode"]).lower()
|
||||||
if c["interface_mode"] == "full":
|
if c["interface_mode"] == "full":
|
||||||
|
@ -490,7 +490,7 @@ class Reticulum:
|
||||||
if "ifac_size" in c:
|
if "ifac_size" in c:
|
||||||
if c.as_int("ifac_size") >= Reticulum.IFAC_MIN_SIZE*8:
|
if c.as_int("ifac_size") >= Reticulum.IFAC_MIN_SIZE*8:
|
||||||
ifac_size = c.as_int("ifac_size")//8
|
ifac_size = c.as_int("ifac_size")//8
|
||||||
|
|
||||||
ifac_netname = None
|
ifac_netname = None
|
||||||
if "networkname" in c:
|
if "networkname" in c:
|
||||||
if c["networkname"] != "":
|
if c["networkname"] != "":
|
||||||
|
@ -506,7 +506,7 @@ class Reticulum:
|
||||||
if "pass_phrase" in c:
|
if "pass_phrase" in c:
|
||||||
if c["pass_phrase"] != "":
|
if c["pass_phrase"] != "":
|
||||||
ifac_netkey = c["pass_phrase"]
|
ifac_netkey = c["pass_phrase"]
|
||||||
|
|
||||||
ingress_control = True
|
ingress_control = True
|
||||||
if "ingress_control" in c: ingress_control = c.as_bool("ingress_control")
|
if "ingress_control" in c: ingress_control = c.as_bool("ingress_control")
|
||||||
ic_max_held_announces = None
|
ic_max_held_announces = None
|
||||||
|
@ -533,12 +533,12 @@ class Reticulum:
|
||||||
if "announce_rate_target" in c:
|
if "announce_rate_target" in c:
|
||||||
if c.as_int("announce_rate_target") > 0:
|
if c.as_int("announce_rate_target") > 0:
|
||||||
announce_rate_target = c.as_int("announce_rate_target")
|
announce_rate_target = c.as_int("announce_rate_target")
|
||||||
|
|
||||||
announce_rate_grace = None
|
announce_rate_grace = None
|
||||||
if "announce_rate_grace" in c:
|
if "announce_rate_grace" in c:
|
||||||
if c.as_int("announce_rate_grace") >= 0:
|
if c.as_int("announce_rate_grace") >= 0:
|
||||||
announce_rate_grace = c.as_int("announce_rate_grace")
|
announce_rate_grace = c.as_int("announce_rate_grace")
|
||||||
|
|
||||||
announce_rate_penalty = None
|
announce_rate_penalty = None
|
||||||
if "announce_rate_penalty" in c:
|
if "announce_rate_penalty" in c:
|
||||||
if c.as_int("announce_rate_penalty") >= 0:
|
if c.as_int("announce_rate_penalty") >= 0:
|
||||||
|
@ -554,7 +554,7 @@ class Reticulum:
|
||||||
if "announce_cap" in c:
|
if "announce_cap" in c:
|
||||||
if c.as_float("announce_cap") > 0 and c.as_float("announce_cap") <= 100:
|
if c.as_float("announce_cap") > 0 and c.as_float("announce_cap") <= 100:
|
||||||
announce_cap = c.as_float("announce_cap")/100.0
|
announce_cap = c.as_float("announce_cap")/100.0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
interface = None
|
interface = None
|
||||||
|
|
||||||
|
@ -661,7 +661,7 @@ class Reticulum:
|
||||||
if interface_mode == Interface.Interface.MODE_ACCESS_POINT:
|
if interface_mode == Interface.Interface.MODE_ACCESS_POINT:
|
||||||
RNS.log(f"{interface} does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING)
|
RNS.log(f"{interface} does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING)
|
||||||
interface_mode = Interface.Interface.MODE_FULL
|
interface_mode = Interface.Interface.MODE_FULL
|
||||||
|
|
||||||
interface.mode = interface_mode
|
interface.mode = interface_mode
|
||||||
|
|
||||||
interface.announce_cap = announce_cap
|
interface.announce_cap = announce_cap
|
||||||
|
@ -698,7 +698,7 @@ class Reticulum:
|
||||||
if interface_mode == Interface.Interface.MODE_ACCESS_POINT:
|
if interface_mode == Interface.Interface.MODE_ACCESS_POINT:
|
||||||
RNS.log(f"{interface} does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING)
|
RNS.log(f"{interface} does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING)
|
||||||
interface_mode = Interface.Interface.MODE_FULL
|
interface_mode = Interface.Interface.MODE_FULL
|
||||||
|
|
||||||
interface.mode = interface_mode
|
interface.mode = interface_mode
|
||||||
|
|
||||||
interface.announce_cap = announce_cap
|
interface.announce_cap = announce_cap
|
||||||
|
@ -735,7 +735,7 @@ class Reticulum:
|
||||||
if interface_mode == Interface.Interface.MODE_ACCESS_POINT:
|
if interface_mode == Interface.Interface.MODE_ACCESS_POINT:
|
||||||
RNS.log(f"{interface} does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING)
|
RNS.log(f"{interface} does not support Access Point mode, reverting to default mode: Full", RNS.LOG_WARNING)
|
||||||
interface_mode = Interface.Interface.MODE_FULL
|
interface_mode = Interface.Interface.MODE_FULL
|
||||||
|
|
||||||
interface.mode = interface_mode
|
interface.mode = interface_mode
|
||||||
|
|
||||||
interface.announce_cap = announce_cap
|
interface.announce_cap = announce_cap
|
||||||
|
@ -938,7 +938,7 @@ class Reticulum:
|
||||||
ble_addr = ble_string
|
ble_addr = ble_string
|
||||||
else:
|
else:
|
||||||
ble_name = ble_string
|
ble_name = ble_string
|
||||||
|
|
||||||
interface = RNodeInterface.RNodeInterface(
|
interface = RNodeInterface.RNodeInterface(
|
||||||
RNS.Transport,
|
RNS.Transport,
|
||||||
name,
|
name,
|
||||||
|
@ -1012,11 +1012,11 @@ class Reticulum:
|
||||||
txpower = int(subinterface_config["txpower"]) if "txpower" in subinterface_config else None
|
txpower = int(subinterface_config["txpower"]) if "txpower" in subinterface_config else None
|
||||||
subint_config[subint_index][4] = txpower
|
subint_config[subint_index][4] = txpower
|
||||||
spreadingfactor = int(subinterface_config["spreadingfactor"]) if "spreadingfactor" in subinterface_config else None
|
spreadingfactor = int(subinterface_config["spreadingfactor"]) if "spreadingfactor" in subinterface_config else None
|
||||||
subint_config[subint_index][5] = spreadingfactor
|
subint_config[subint_index][5] = spreadingfactor
|
||||||
codingrate = int(subinterface_config["codingrate"]) if "codingrate" in subinterface_config else None
|
codingrate = int(subinterface_config["codingrate"]) if "codingrate" in subinterface_config else None
|
||||||
subint_config[subint_index][6] = codingrate
|
subint_config[subint_index][6] = codingrate
|
||||||
flow_control = subinterface_config.as_bool("flow_control") if "flow_control" in subinterface_config else False
|
flow_control = subinterface_config.as_bool("flow_control") if "flow_control" in subinterface_config else False
|
||||||
subint_config[subint_index][7] = flow_control
|
subint_config[subint_index][7] = flow_control
|
||||||
st_alock = float(subinterface_config["airtime_limit_short"]) if "airtime_limit_short" in subinterface_config else None
|
st_alock = float(subinterface_config["airtime_limit_short"]) if "airtime_limit_short" in subinterface_config else None
|
||||||
subint_config[subint_index][8] = st_alock
|
subint_config[subint_index][8] = st_alock
|
||||||
lt_alock = float(subinterface_config["airtime_limit_long"]) if "airtime_limit_long" in subinterface_config else None
|
lt_alock = float(subinterface_config["airtime_limit_long"]) if "airtime_limit_long" in subinterface_config else None
|
||||||
|
@ -1038,7 +1038,7 @@ class Reticulum:
|
||||||
id_interval = int(c["id_interval"]) if "id_interval" in c else None
|
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
|
id_callsign = c["id_callsign"] if "id_callsign" in c else None
|
||||||
port = c["port"] if "port" in c else None
|
port = c["port"] if "port" in c else None
|
||||||
|
|
||||||
if port == None:
|
if port == None:
|
||||||
raise ValueError(f"No port specified for {name}")
|
raise ValueError(f"No port specified for {name}")
|
||||||
|
|
||||||
|
@ -1122,7 +1122,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):
|
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):
|
||||||
if not self.is_connected_to_shared_instance:
|
if not self.is_connected_to_shared_instance:
|
||||||
if interface != None and issubclass(type(interface), RNS.Interfaces.Interface.Interface):
|
if interface != None and issubclass(type(interface), RNS.Interfaces.Interface.Interface):
|
||||||
|
|
||||||
if mode == None:
|
if mode == None:
|
||||||
mode = Interface.Interface.MODE_FULL
|
mode = Interface.Interface.MODE_FULL
|
||||||
interface.mode = mode
|
interface.mode = mode
|
||||||
|
@ -1200,14 +1200,14 @@ class Reticulum:
|
||||||
age = now - mtime
|
age = now - mtime
|
||||||
if age > RNS.Transport.DESTINATION_TIMEOUT:
|
if age > RNS.Transport.DESTINATION_TIMEOUT:
|
||||||
os.unlink(filepath)
|
os.unlink(filepath)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log(f"Error while cleaning resources cache, the contained exception was: {e}", RNS.LOG_ERROR)
|
RNS.log(f"Error while cleaning resources cache, the contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
|
|
||||||
def __create_default_config(self):
|
def __create_default_config(self):
|
||||||
self.config = ConfigObj(__default_rns_config__)
|
self.config = ConfigObj(__default_rns_config__)
|
||||||
self.config.filename = Reticulum.configpath
|
self.config.filename = Reticulum.configpath
|
||||||
|
|
||||||
if not os.path.isdir(Reticulum.configdir):
|
if not os.path.isdir(Reticulum.configdir):
|
||||||
os.makedirs(Reticulum.configdir)
|
os.makedirs(Reticulum.configdir)
|
||||||
self.config.write()
|
self.config.write()
|
||||||
|
@ -1279,7 +1279,7 @@ class Reticulum:
|
||||||
interfaces = []
|
interfaces = []
|
||||||
for interface in RNS.Transport.interfaces:
|
for interface in RNS.Transport.interfaces:
|
||||||
ifstats = {}
|
ifstats = {}
|
||||||
|
|
||||||
if hasattr(interface, "clients"):
|
if hasattr(interface, "clients"):
|
||||||
ifstats["clients"] = interface.clients
|
ifstats["clients"] = interface.clients
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -51,7 +51,7 @@ class Transport:
|
||||||
"""
|
"""
|
||||||
Maximum amount of hops that Reticulum will transport a packet.
|
Maximum amount of hops that Reticulum will transport a packet.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
PATHFINDER_R = 1 # Retransmit retries
|
PATHFINDER_R = 1 # Retransmit retries
|
||||||
PATHFINDER_G = 5 # Retry grace period
|
PATHFINDER_G = 5 # Retry grace period
|
||||||
PATHFINDER_RW = 0.5 # Random window for announce rebroadcast
|
PATHFINDER_RW = 0.5 # Random window for announce rebroadcast
|
||||||
|
@ -101,7 +101,7 @@ class Transport:
|
||||||
announce_rate_table = {} # A table for keeping track of announce rates
|
announce_rate_table = {} # A table for keeping track of announce rates
|
||||||
path_requests = {} # A table for storing path request timestamps
|
path_requests = {} # A table for storing path request timestamps
|
||||||
path_states = {} # A table for keeping track of path states
|
path_states = {} # A table for keeping track of path states
|
||||||
|
|
||||||
discovery_path_requests = {} # A table for keeping track of path requests on behalf of other nodes
|
discovery_path_requests = {} # A table for keeping track of path requests on behalf of other nodes
|
||||||
discovery_pr_tags = [] # A table for keeping track of tagged path requests
|
discovery_pr_tags = [] # A table for keeping track of tagged path requests
|
||||||
max_pr_tags = 32000 # Maximum amount of unique path request tags to remember
|
max_pr_tags = 32000 # Maximum amount of unique path request tags to remember
|
||||||
|
@ -150,7 +150,7 @@ class Transport:
|
||||||
if Transport.identity == None:
|
if Transport.identity == None:
|
||||||
transport_identity_path = f"{RNS.Reticulum.storagepath}/transport_identity"
|
transport_identity_path = f"{RNS.Reticulum.storagepath}/transport_identity"
|
||||||
if os.path.isfile(transport_identity_path):
|
if os.path.isfile(transport_identity_path):
|
||||||
Transport.identity = RNS.Identity.from_file(transport_identity_path)
|
Transport.identity = RNS.Identity.from_file(transport_identity_path)
|
||||||
|
|
||||||
if Transport.identity == None:
|
if Transport.identity == None:
|
||||||
RNS.log("No valid Transport Identity in storage, creating...", RNS.LOG_VERBOSE)
|
RNS.log("No valid Transport Identity in storage, creating...", RNS.LOG_VERBOSE)
|
||||||
|
@ -187,7 +187,7 @@ class Transport:
|
||||||
Transport.control_destinations.append(Transport.remote_management_destination)
|
Transport.control_destinations.append(Transport.remote_management_destination)
|
||||||
Transport.control_hashes.append(Transport.remote_management_destination.hash)
|
Transport.control_hashes.append(Transport.remote_management_destination.hash)
|
||||||
RNS.log(f"Enabled remote management on {Transport.remote_management_destination}", RNS.LOG_NOTICE)
|
RNS.log(f"Enabled remote management on {Transport.remote_management_destination}", RNS.LOG_NOTICE)
|
||||||
|
|
||||||
Transport.jobs_running = False
|
Transport.jobs_running = False
|
||||||
thread = threading.Thread(target=Transport.jobloop, daemon=True)
|
thread = threading.Thread(target=Transport.jobloop, daemon=True)
|
||||||
thread.start()
|
thread.start()
|
||||||
|
@ -405,7 +405,7 @@ class Transport:
|
||||||
announce_destination = RNS.Destination(announce_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "unknown", "unknown");
|
announce_destination = RNS.Destination(announce_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "unknown", "unknown");
|
||||||
announce_destination.hash = packet.destination_hash
|
announce_destination.hash = packet.destination_hash
|
||||||
announce_destination.hexhash = announce_destination.hash.hex()
|
announce_destination.hexhash = announce_destination.hash.hex()
|
||||||
|
|
||||||
new_packet = RNS.Packet(
|
new_packet = RNS.Packet(
|
||||||
announce_destination,
|
announce_destination,
|
||||||
announce_data,
|
announce_data,
|
||||||
|
@ -423,7 +423,7 @@ class Transport:
|
||||||
RNS.log(f"Rebroadcasting announce as path response for {RNS.prettyhexrep(announce_destination.hash)} with hop count {new_packet.hops}", RNS.LOG_DEBUG)
|
RNS.log(f"Rebroadcasting announce as path response for {RNS.prettyhexrep(announce_destination.hash)} with hop count {new_packet.hops}", RNS.LOG_DEBUG)
|
||||||
else:
|
else:
|
||||||
RNS.log(f"Rebroadcasting announce for {RNS.prettyhexrep(announce_destination.hash)} with hop count {new_packet.hops}", RNS.LOG_DEBUG)
|
RNS.log(f"Rebroadcasting announce for {RNS.prettyhexrep(announce_destination.hash)} with hop count {new_packet.hops}", RNS.LOG_DEBUG)
|
||||||
|
|
||||||
outgoing.append(new_packet)
|
outgoing.append(new_packet)
|
||||||
|
|
||||||
# This handles an edge case where a peer sends a past
|
# This handles an edge case where a peer sends a past
|
||||||
|
@ -486,7 +486,7 @@ class Transport:
|
||||||
|
|
||||||
path_request_throttle = time.time() - last_path_request < Transport.PATH_REQUEST_MI
|
path_request_throttle = time.time() - last_path_request < Transport.PATH_REQUEST_MI
|
||||||
path_request_conditions = False
|
path_request_conditions = False
|
||||||
|
|
||||||
# If the path has been invalidated between the time of
|
# If the path has been invalidated between the time of
|
||||||
# making the link request and now, try to rediscover it
|
# making the link request and now, try to rediscover it
|
||||||
if not Transport.has_path(link_entry[6]):
|
if not Transport.has_path(link_entry[6]):
|
||||||
|
@ -726,7 +726,7 @@ class Transport:
|
||||||
|
|
||||||
# Assemble new payload with IFAC
|
# Assemble new payload with IFAC
|
||||||
new_raw = new_header+ifac+raw[2:]
|
new_raw = new_header+ifac+raw[2:]
|
||||||
|
|
||||||
# Mask payload
|
# Mask payload
|
||||||
i = 0; masked_raw = b""
|
i = 0; masked_raw = b""
|
||||||
for byte in new_raw:
|
for byte in new_raw:
|
||||||
|
@ -781,7 +781,7 @@ class Transport:
|
||||||
if generate_receipt:
|
if generate_receipt:
|
||||||
packet.receipt = RNS.PacketReceipt(packet)
|
packet.receipt = RNS.PacketReceipt(packet)
|
||||||
Transport.receipts.append(packet.receipt)
|
Transport.receipts.append(packet.receipt)
|
||||||
|
|
||||||
# TODO: Enable when caching has been redesigned
|
# TODO: Enable when caching has been redesigned
|
||||||
# Transport.cache(packet)
|
# Transport.cache(packet)
|
||||||
|
|
||||||
|
@ -850,7 +850,7 @@ class Transport:
|
||||||
should_transmit = False
|
should_transmit = False
|
||||||
if interface != packet.destination.attached_interface:
|
if interface != packet.destination.attached_interface:
|
||||||
should_transmit = False
|
should_transmit = False
|
||||||
|
|
||||||
if packet.attached_interface != None and interface != packet.attached_interface:
|
if packet.attached_interface != None and interface != packet.attached_interface:
|
||||||
should_transmit = False
|
should_transmit = False
|
||||||
|
|
||||||
|
@ -919,7 +919,7 @@ class Transport:
|
||||||
tx_time = (len(packet.raw)*8) / interface.bitrate
|
tx_time = (len(packet.raw)*8) / interface.bitrate
|
||||||
wait_time = (tx_time / interface.announce_cap)
|
wait_time = (tx_time / interface.announce_cap)
|
||||||
interface.announce_allowed_at = outbound_time + wait_time
|
interface.announce_allowed_at = outbound_time + wait_time
|
||||||
|
|
||||||
else:
|
else:
|
||||||
should_transmit = False
|
should_transmit = False
|
||||||
if not len(interface.announce_queue) >= RNS.Reticulum.MAX_QUEUED_ANNOUNCES:
|
if not len(interface.announce_queue) >= RNS.Reticulum.MAX_QUEUED_ANNOUNCES:
|
||||||
|
@ -979,10 +979,10 @@ class Transport:
|
||||||
|
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if should_transmit:
|
if should_transmit:
|
||||||
if not stored_hash:
|
if not stored_hash:
|
||||||
Transport.packet_hashlist.append(packet.packet_hash)
|
Transport.packet_hashlist.append(packet.packet_hash)
|
||||||
|
@ -1134,14 +1134,14 @@ class Transport:
|
||||||
|
|
||||||
if Transport.identity == None:
|
if Transport.identity == None:
|
||||||
return
|
return
|
||||||
|
|
||||||
Transport.jobs_locked = True
|
Transport.jobs_locked = True
|
||||||
|
|
||||||
packet = RNS.Packet(None, raw)
|
packet = RNS.Packet(None, raw)
|
||||||
if not packet.unpack():
|
if not packet.unpack():
|
||||||
Transport.jobs_locked = False
|
Transport.jobs_locked = False
|
||||||
return
|
return
|
||||||
|
|
||||||
packet.receiving_interface = interface
|
packet.receiving_interface = interface
|
||||||
packet.hops += 1
|
packet.hops += 1
|
||||||
|
|
||||||
|
@ -1205,7 +1205,7 @@ class Transport:
|
||||||
Transport.packet_hashlist.append(packet.packet_hash)
|
Transport.packet_hashlist.append(packet.packet_hash)
|
||||||
# TODO: Enable when caching has been redesigned
|
# TODO: Enable when caching has been redesigned
|
||||||
# Transport.cache(packet)
|
# Transport.cache(packet)
|
||||||
|
|
||||||
# Check special conditions for local clients connected
|
# Check special conditions for local clients connected
|
||||||
# through a shared Reticulum instance
|
# through a shared Reticulum instance
|
||||||
from_local_client = (packet.receiving_interface in Transport.local_client_interfaces)
|
from_local_client = (packet.receiving_interface in Transport.local_client_interfaces)
|
||||||
|
@ -1262,7 +1262,7 @@ class Transport:
|
||||||
if packet.destination_hash in Transport.destination_table:
|
if packet.destination_hash in Transport.destination_table:
|
||||||
next_hop = Transport.destination_table[packet.destination_hash][1]
|
next_hop = Transport.destination_table[packet.destination_hash][1]
|
||||||
remaining_hops = Transport.destination_table[packet.destination_hash][2]
|
remaining_hops = Transport.destination_table[packet.destination_hash][2]
|
||||||
|
|
||||||
if remaining_hops > 1:
|
if remaining_hops > 1:
|
||||||
# Just increase hop count and transmit
|
# Just increase hop count and transmit
|
||||||
new_raw = packet.raw[0:1]
|
new_raw = packet.raw[0:1]
|
||||||
|
@ -1356,7 +1356,7 @@ class Transport:
|
||||||
new_raw += packet.raw[2:]
|
new_raw += packet.raw[2:]
|
||||||
Transport.transmit(outbound_interface, new_raw)
|
Transport.transmit(outbound_interface, new_raw)
|
||||||
Transport.link_table[packet.destination_hash][0] = time.time()
|
Transport.link_table[packet.destination_hash][0] = time.time()
|
||||||
|
|
||||||
# TODO: Test and possibly enable this at some point
|
# TODO: Test and possibly enable this at some point
|
||||||
# Transport.jobs_locked = False
|
# Transport.jobs_locked = False
|
||||||
# return
|
# return
|
||||||
|
@ -1383,13 +1383,13 @@ class Transport:
|
||||||
if local_destination == None and RNS.Identity.validate_announce(packet):
|
if local_destination == None and RNS.Identity.validate_announce(packet):
|
||||||
if packet.transport_id != None:
|
if packet.transport_id != None:
|
||||||
received_from = packet.transport_id
|
received_from = packet.transport_id
|
||||||
|
|
||||||
# Check if this is a next retransmission from
|
# Check if this is a next retransmission from
|
||||||
# another node. If it is, we're removing the
|
# another node. If it is, we're removing the
|
||||||
# announce in question from our pending table
|
# announce in question from our pending table
|
||||||
if RNS.Reticulum.transport_enabled() and packet.destination_hash in Transport.announce_table:
|
if RNS.Reticulum.transport_enabled() and packet.destination_hash in Transport.announce_table:
|
||||||
announce_entry = Transport.announce_table[packet.destination_hash]
|
announce_entry = Transport.announce_table[packet.destination_hash]
|
||||||
|
|
||||||
if packet.hops-1 == announce_entry[4]:
|
if packet.hops-1 == announce_entry[4]:
|
||||||
RNS.log(f"Heard a local rebroadcast of announce for {RNS.prettyhexrep(packet.destination_hash)}", RNS.LOG_DEBUG)
|
RNS.log(f"Heard a local rebroadcast of announce for {RNS.prettyhexrep(packet.destination_hash)}", RNS.LOG_DEBUG)
|
||||||
announce_entry[6] += 1
|
announce_entry[6] += 1
|
||||||
|
@ -1415,7 +1415,7 @@ class Transport:
|
||||||
# local to this system, and that hops are less than the max
|
# local to this system, and that hops are less than the max
|
||||||
if (not any(packet.destination_hash == d.hash for d in Transport.destinations) and packet.hops < Transport.PATHFINDER_M+1):
|
if (not any(packet.destination_hash == d.hash for d in Transport.destinations) and packet.hops < Transport.PATHFINDER_M+1):
|
||||||
announce_emitted = Transport.announce_emitted(packet)
|
announce_emitted = Transport.announce_emitted(packet)
|
||||||
|
|
||||||
random_blob = packet.data[RNS.Identity.KEYSIZE//8+RNS.Identity.NAME_HASH_LENGTH//8:RNS.Identity.KEYSIZE//8+RNS.Identity.NAME_HASH_LENGTH//8+10]
|
random_blob = packet.data[RNS.Identity.KEYSIZE//8+RNS.Identity.NAME_HASH_LENGTH//8:RNS.Identity.KEYSIZE//8+RNS.Identity.NAME_HASH_LENGTH//8+10]
|
||||||
random_blobs = []
|
random_blobs = []
|
||||||
if packet.destination_hash in Transport.destination_table:
|
if packet.destination_hash in Transport.destination_table:
|
||||||
|
@ -1442,7 +1442,7 @@ class Transport:
|
||||||
# the emission timestamp is more recent.
|
# the emission timestamp is more recent.
|
||||||
now = time.time()
|
now = time.time()
|
||||||
path_expires = Transport.destination_table[packet.destination_hash][3]
|
path_expires = Transport.destination_table[packet.destination_hash][3]
|
||||||
|
|
||||||
path_announce_emitted = 0
|
path_announce_emitted = 0
|
||||||
for path_random_blob in random_blobs:
|
for path_random_blob in random_blobs:
|
||||||
path_announce_emitted = max(path_announce_emitted, int.from_bytes(path_random_blob[5:10], "big"))
|
path_announce_emitted = max(path_announce_emitted, int.from_bytes(path_random_blob[5:10], "big"))
|
||||||
|
@ -1474,7 +1474,7 @@ class Transport:
|
||||||
should_add = True
|
should_add = True
|
||||||
else:
|
else:
|
||||||
should_add = False
|
should_add = False
|
||||||
|
|
||||||
# If we have already heard this announce before,
|
# If we have already heard this announce before,
|
||||||
# but the path has been marked as unresponsive
|
# but the path has been marked as unresponsive
|
||||||
# by a failed communications attempt or similar,
|
# by a failed communications attempt or similar,
|
||||||
|
@ -1527,14 +1527,14 @@ class Transport:
|
||||||
|
|
||||||
else:
|
else:
|
||||||
rate_blocked = True
|
rate_blocked = True
|
||||||
|
|
||||||
|
|
||||||
retries = 0
|
retries = 0
|
||||||
announce_hops = packet.hops
|
announce_hops = packet.hops
|
||||||
local_rebroadcasts = 0
|
local_rebroadcasts = 0
|
||||||
block_rebroadcasts = False
|
block_rebroadcasts = False
|
||||||
attached_interface = None
|
attached_interface = None
|
||||||
|
|
||||||
retransmit_timeout = now + (RNS.rand() * Transport.PATHFINDER_RW)
|
retransmit_timeout = now + (RNS.rand() * Transport.PATHFINDER_RW)
|
||||||
|
|
||||||
if hasattr(packet.receiving_interface, "mode") and packet.receiving_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ACCESS_POINT:
|
if hasattr(packet.receiving_interface, "mode") and packet.receiving_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ACCESS_POINT:
|
||||||
|
@ -1543,7 +1543,7 @@ class Transport:
|
||||||
expires = now + Transport.ROAMING_PATH_TIME
|
expires = now + Transport.ROAMING_PATH_TIME
|
||||||
else:
|
else:
|
||||||
expires = now + Transport.PATHFINDER_E
|
expires = now + Transport.PATHFINDER_E
|
||||||
|
|
||||||
random_blobs.append(random_blob)
|
random_blobs.append(random_blob)
|
||||||
random_blobs = random_blobs[-Transport.MAX_RANDOM_BLOBS:]
|
random_blobs = random_blobs[-Transport.MAX_RANDOM_BLOBS:]
|
||||||
|
|
||||||
|
@ -1552,7 +1552,7 @@ class Transport:
|
||||||
|
|
||||||
if rate_blocked:
|
if rate_blocked:
|
||||||
RNS.log(f"Blocking rebroadcast of announce from {RNS.prettyhexrep(packet.destination_hash)} due to excessive announce rate", RNS.LOG_DEBUG)
|
RNS.log(f"Blocking rebroadcast of announce from {RNS.prettyhexrep(packet.destination_hash)} due to excessive announce rate", RNS.LOG_DEBUG)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if Transport.from_local_client(packet):
|
if Transport.from_local_client(packet):
|
||||||
# If the announce is from a local client,
|
# If the announce is from a local client,
|
||||||
|
@ -1619,7 +1619,7 @@ class Transport:
|
||||||
attached_interface = local_interface,
|
attached_interface = local_interface,
|
||||||
context_flag = packet.context_flag,
|
context_flag = packet.context_flag,
|
||||||
)
|
)
|
||||||
|
|
||||||
new_announce.hops = packet.hops
|
new_announce.hops = packet.hops
|
||||||
new_announce.send()
|
new_announce.send()
|
||||||
|
|
||||||
|
@ -1730,7 +1730,7 @@ class Transport:
|
||||||
if destination.hash == packet.destination_hash and destination.type == packet.destination_type:
|
if destination.hash == packet.destination_hash and destination.type == packet.destination_type:
|
||||||
packet.destination = destination
|
packet.destination = destination
|
||||||
destination.receive(packet)
|
destination.receive(packet)
|
||||||
|
|
||||||
# Handling for local data packets
|
# Handling for local data packets
|
||||||
elif packet.packet_type == RNS.Packet.DATA:
|
elif packet.packet_type == RNS.Packet.DATA:
|
||||||
if packet.destination_type == RNS.Destination.LINK:
|
if packet.destination_type == RNS.Destination.LINK:
|
||||||
|
@ -1844,7 +1844,7 @@ class Transport:
|
||||||
for link in Transport.active_links:
|
for link in Transport.active_links:
|
||||||
if link.link_id == packet.destination_hash:
|
if link.link_id == packet.destination_hash:
|
||||||
packet.link = link
|
packet.link = link
|
||||||
|
|
||||||
if len(packet.data) == RNS.PacketReceipt.EXPL_LENGTH:
|
if len(packet.data) == RNS.PacketReceipt.EXPL_LENGTH:
|
||||||
proof_hash = packet.data[:RNS.Identity.HASHLENGTH//8]
|
proof_hash = packet.data[:RNS.Identity.HASHLENGTH//8]
|
||||||
else:
|
else:
|
||||||
|
@ -1884,13 +1884,13 @@ class Transport:
|
||||||
interface_hash = interface.get_hash()
|
interface_hash = interface.get_hash()
|
||||||
public_key = RNS.Transport.identity.get_public_key()
|
public_key = RNS.Transport.identity.get_public_key()
|
||||||
random_hash = RNS.Identity.get_random_hash()
|
random_hash = RNS.Identity.get_random_hash()
|
||||||
|
|
||||||
tunnel_id_data = public_key+interface_hash
|
tunnel_id_data = public_key+interface_hash
|
||||||
tunnel_id = RNS.Identity.full_hash(tunnel_id_data)
|
tunnel_id = RNS.Identity.full_hash(tunnel_id_data)
|
||||||
|
|
||||||
signed_data = tunnel_id_data+random_hash
|
signed_data = tunnel_id_data+random_hash
|
||||||
signature = Transport.identity.sign(signed_data)
|
signature = Transport.identity.sign(signed_data)
|
||||||
|
|
||||||
data = signed_data+signature
|
data = signed_data+signature
|
||||||
|
|
||||||
tnl_snth_dst = RNS.Destination(None, RNS.Destination.OUT, RNS.Destination.PLAIN, Transport.APP_NAME, "tunnel", "synthesize")
|
tnl_snth_dst = RNS.Destination(None, RNS.Destination.OUT, RNS.Destination.PLAIN, Transport.APP_NAME, "tunnel", "synthesize")
|
||||||
|
@ -1910,7 +1910,7 @@ class Transport:
|
||||||
tunnel_id_data = public_key+interface_hash
|
tunnel_id_data = public_key+interface_hash
|
||||||
tunnel_id = RNS.Identity.full_hash(tunnel_id_data)
|
tunnel_id = RNS.Identity.full_hash(tunnel_id_data)
|
||||||
random_hash = data[RNS.Identity.KEYSIZE//8+RNS.Identity.HASHLENGTH//8:RNS.Identity.KEYSIZE//8+RNS.Identity.HASHLENGTH//8+RNS.Reticulum.TRUNCATED_HASHLENGTH//8]
|
random_hash = data[RNS.Identity.KEYSIZE//8+RNS.Identity.HASHLENGTH//8:RNS.Identity.KEYSIZE//8+RNS.Identity.HASHLENGTH//8+RNS.Reticulum.TRUNCATED_HASHLENGTH//8]
|
||||||
|
|
||||||
signature = data[RNS.Identity.KEYSIZE//8+RNS.Identity.HASHLENGTH//8+RNS.Reticulum.TRUNCATED_HASHLENGTH//8:expected_length]
|
signature = data[RNS.Identity.KEYSIZE//8+RNS.Identity.HASHLENGTH//8+RNS.Reticulum.TRUNCATED_HASHLENGTH//8:expected_length]
|
||||||
signed_data = tunnel_id_data+random_hash
|
signed_data = tunnel_id_data+random_hash
|
||||||
|
|
||||||
|
@ -1983,7 +1983,7 @@ class Transport:
|
||||||
for registered_destination in Transport.destinations:
|
for registered_destination in Transport.destinations:
|
||||||
if destination.hash == registered_destination.hash:
|
if destination.hash == registered_destination.hash:
|
||||||
raise KeyError("Attempt to register an already registered destination.")
|
raise KeyError("Attempt to register an already registered destination.")
|
||||||
|
|
||||||
Transport.destinations.append(destination)
|
Transport.destinations.append(destination)
|
||||||
|
|
||||||
if Transport.owner.is_connected_to_shared_instance:
|
if Transport.owner.is_connected_to_shared_instance:
|
||||||
|
@ -2070,7 +2070,7 @@ class Transport:
|
||||||
if packet.receiving_interface != None:
|
if packet.receiving_interface != None:
|
||||||
interface_reference = str(packet.receiving_interface)
|
interface_reference = str(packet.receiving_interface)
|
||||||
|
|
||||||
file = open(f"{RNS.Reticulum.cachepath}/{packet_hash}", "wb")
|
file = open(f"{RNS.Reticulum.cachepath}/{packet_hash}", "wb")
|
||||||
file.write(umsgpack.packb([packet.raw, interface_reference]))
|
file.write(umsgpack.packb([packet.raw, interface_reference]))
|
||||||
file.close()
|
file.close()
|
||||||
|
|
||||||
|
@ -2428,11 +2428,11 @@ class Transport:
|
||||||
if len(Transport.local_client_interfaces) > 0:
|
if len(Transport.local_client_interfaces) > 0:
|
||||||
if destination_hash in Transport.destination_table:
|
if destination_hash in Transport.destination_table:
|
||||||
destination_interface = Transport.destination_table[destination_hash][5]
|
destination_interface = Transport.destination_table[destination_hash][5]
|
||||||
|
|
||||||
if Transport.is_local_client_interface(destination_interface):
|
if Transport.is_local_client_interface(destination_interface):
|
||||||
destination_exists_on_local_client = True
|
destination_exists_on_local_client = True
|
||||||
Transport.pending_local_path_requests[destination_hash] = attached_interface
|
Transport.pending_local_path_requests[destination_hash] = attached_interface
|
||||||
|
|
||||||
local_destination = next((d for d in Transport.destinations if d.hash == destination_hash), None)
|
local_destination = next((d for d in Transport.destinations if d.hash == destination_hash), None)
|
||||||
if local_destination != None:
|
if local_destination != None:
|
||||||
local_destination.announce(path_response=True, tag=tag, attached_interface=attached_interface)
|
local_destination.announce(path_response=True, tag=tag, attached_interface=attached_interface)
|
||||||
|
@ -2445,7 +2445,7 @@ class Transport:
|
||||||
|
|
||||||
if attached_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING and attached_interface == received_from:
|
if attached_interface.mode == RNS.Interfaces.Interface.Interface.MODE_ROAMING and attached_interface == received_from:
|
||||||
RNS.log("Not answering path request on roaming-mode interface, since next hop is on same roaming-mode interface", RNS.LOG_DEBUG)
|
RNS.log("Not answering path request on roaming-mode interface, since next hop is on same roaming-mode interface", RNS.LOG_DEBUG)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if requestor_transport_id != None and next_hop == requestor_transport_id:
|
if requestor_transport_id != None and next_hop == requestor_transport_id:
|
||||||
# TODO: Find a bandwidth efficient way to invalidate our
|
# TODO: Find a bandwidth efficient way to invalidate our
|
||||||
|
@ -2489,7 +2489,7 @@ class Transport:
|
||||||
if packet.destination_hash in Transport.announce_table:
|
if packet.destination_hash in Transport.announce_table:
|
||||||
held_entry = Transport.announce_table[packet.destination_hash]
|
held_entry = Transport.announce_table[packet.destination_hash]
|
||||||
Transport.held_announces[packet.destination_hash] = held_entry
|
Transport.held_announces[packet.destination_hash] = held_entry
|
||||||
|
|
||||||
Transport.announce_table[packet.destination_hash] = [now, retransmit_timeout, retries, received_from, announce_hops, packet, local_rebroadcasts, block_rebroadcasts, attached_interface]
|
Transport.announce_table[packet.destination_hash] = [now, retransmit_timeout, retries, received_from, announce_hops, packet, local_rebroadcasts, block_rebroadcasts, attached_interface]
|
||||||
|
|
||||||
elif is_from_local_client:
|
elif is_from_local_client:
|
||||||
|
@ -2563,7 +2563,7 @@ class Transport:
|
||||||
detachable_interfaces.append(interface)
|
detachable_interfaces.append(interface)
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
for interface in Transport.local_client_interfaces:
|
for interface in Transport.local_client_interfaces:
|
||||||
# Currently no rules are being applied
|
# Currently no rules are being applied
|
||||||
# here, and all interfaces will be sent
|
# here, and all interfaces will be sent
|
||||||
|
|
|
@ -77,7 +77,7 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
|
||||||
|
|
||||||
identity_path = f"{RNS.Reticulum.identitypath}/{APP_NAME}"
|
identity_path = f"{RNS.Reticulum.identitypath}/{APP_NAME}"
|
||||||
if os.path.isfile(identity_path):
|
if os.path.isfile(identity_path):
|
||||||
identity = RNS.Identity.from_file(identity_path)
|
identity = RNS.Identity.from_file(identity_path)
|
||||||
|
|
||||||
if identity == None:
|
if identity == None:
|
||||||
RNS.log("No valid saved identity found, creating new...", RNS.LOG_INFO)
|
RNS.log("No valid saved identity found, creating new...", RNS.LOG_INFO)
|
||||||
|
@ -121,7 +121,7 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
|
||||||
ms = "y"
|
ms = "y"
|
||||||
else:
|
else:
|
||||||
ms = "ies"
|
ms = "ies"
|
||||||
|
|
||||||
RNS.log(f"Loaded {len(ali)} allowed identit{ms} from {allowed_file}", RNS.LOG_VERBOSE)
|
RNS.log(f"Loaded {len(ali)} allowed identit{ms} from {allowed_file}", RNS.LOG_VERBOSE)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -210,7 +210,7 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
|
||||||
destination.announce()
|
destination.announce()
|
||||||
|
|
||||||
threading.Thread(target=job, daemon=True).start()
|
threading.Thread(target=job, daemon=True).start()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
|
@ -236,7 +236,7 @@ def receive_sender_identified(link, identity):
|
||||||
|
|
||||||
def receive_resource_callback(resource):
|
def receive_resource_callback(resource):
|
||||||
global allow_all
|
global allow_all
|
||||||
|
|
||||||
sender_identity = resource.link.get_remote_identity()
|
sender_identity = resource.link.get_remote_identity()
|
||||||
|
|
||||||
if sender_identity != None:
|
if sender_identity != None:
|
||||||
|
@ -278,7 +278,7 @@ def receive_resource_concluded(resource):
|
||||||
while os.path.isfile(full_save_path):
|
while os.path.isfile(full_save_path):
|
||||||
counter += 1
|
counter += 1
|
||||||
full_save_path = f"{saved_filename}.{counter}"
|
full_save_path = f"{saved_filename}.{counter}"
|
||||||
|
|
||||||
file = open(full_save_path, "wb")
|
file = open(full_save_path, "wb")
|
||||||
file.write(resource.data.read())
|
file.write(resource.data.read())
|
||||||
file.close()
|
file.close()
|
||||||
|
@ -599,7 +599,7 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
|
||||||
print(str(e))
|
print(str(e))
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
file_path = os.path.expanduser(file)
|
file_path = os.path.expanduser(file)
|
||||||
if not os.path.isfile(file_path):
|
if not os.path.isfile(file_path):
|
||||||
print("File not found")
|
print("File not found")
|
||||||
|
@ -715,7 +715,7 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
i = (i+1)%len(syms)
|
i = (i+1)%len(syms)
|
||||||
|
|
||||||
|
|
||||||
if resource.status > RNS.Resource.COMPLETE:
|
if resource.status > RNS.Resource.COMPLETE:
|
||||||
if silent:
|
if silent:
|
||||||
print(f"File was not accepted by {RNS.prettyhexrep(destination_hash)}")
|
print(f"File was not accepted by {RNS.prettyhexrep(destination_hash)}")
|
||||||
|
@ -796,7 +796,7 @@ def main():
|
||||||
parser.add_argument('-P', '--phy-rates', action='store_true', default=False, help="display physical layer transfer rates")
|
parser.add_argument('-P', '--phy-rates', action='store_true', default=False, help="display physical layer transfer rates")
|
||||||
# parser.add_argument("--limit", action="store", metavar="files", type=float, help="maximum number of files to accept", default=None)
|
# parser.add_argument("--limit", action="store", metavar="files", type=float, help="maximum number of files to accept", default=None)
|
||||||
parser.add_argument("--version", action="version", version=f"rncp {__version__}")
|
parser.add_argument("--version", action="version", version=f"rncp {__version__}")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.listen or args.print_identity:
|
if args.listen or args.print_identity:
|
||||||
|
|
|
@ -82,7 +82,7 @@ def main():
|
||||||
parser.add_argument("-w", "--write", metavar="file", action="store", default=None, help="output file path", type=str)
|
parser.add_argument("-w", "--write", metavar="file", action="store", default=None, help="output file path", type=str)
|
||||||
parser.add_argument("-f", "--force", action="store_true", default=None, help="write output even if it overwrites existing files")
|
parser.add_argument("-f", "--force", action="store_true", default=None, help="write output even if it overwrites existing files")
|
||||||
parser.add_argument("-I", "--stdin", action="store_true", default=False, help=argparse.SUPPRESS) # "read input from STDIN instead of file"
|
parser.add_argument("-I", "--stdin", action="store_true", default=False, help=argparse.SUPPRESS) # "read input from STDIN instead of file"
|
||||||
parser.add_argument("-O", "--stdout", action="store_true", default=False, help=argparse.SUPPRESS) # help="write output to STDOUT instead of file",
|
parser.add_argument("-O", "--stdout", action="store_true", default=False, help=argparse.SUPPRESS) # help="write output to STDOUT instead of file",
|
||||||
|
|
||||||
parser.add_argument("-R", "--request", action="store_true", default=False, help="request unknown Identities from the network")
|
parser.add_argument("-R", "--request", action="store_true", default=False, help="request unknown Identities from the network")
|
||||||
parser.add_argument("-t", action="store", metavar="seconds", type=float, help="identity request timeout before giving up", default=RNS.Transport.PATH_REQUEST_TIMEOUT)
|
parser.add_argument("-t", action="store", metavar="seconds", type=float, help="identity request timeout before giving up", default=RNS.Transport.PATH_REQUEST_TIMEOUT)
|
||||||
|
@ -93,14 +93,14 @@ def main():
|
||||||
parser.add_argument("-B", "--base32", action="store_true", default=False, help="Use base32-encoded input and output")
|
parser.add_argument("-B", "--base32", action="store_true", default=False, help="Use base32-encoded input and output")
|
||||||
|
|
||||||
parser.add_argument("--version", action="version", version=f"rnid {__version__}")
|
parser.add_argument("--version", action="version", version=f"rnid {__version__}")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
ops = 0;
|
ops = 0;
|
||||||
for t in [args.encrypt, args.decrypt, args.validate, args.sign]:
|
for t in [args.encrypt, args.decrypt, args.validate, args.sign]:
|
||||||
if t:
|
if t:
|
||||||
ops += 1
|
ops += 1
|
||||||
|
|
||||||
if ops > 1:
|
if ops > 1:
|
||||||
RNS.log("This utility currently only supports one of the encrypt, decrypt, sign or verify operations per invocation", RNS.LOG_ERROR)
|
RNS.log("This utility currently only supports one of the encrypt, decrypt, sign or verify operations per invocation", RNS.LOG_ERROR)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
@ -179,7 +179,7 @@ def main():
|
||||||
quietness = args.quiet
|
quietness = args.quiet
|
||||||
if verbosity != 0 or quietness != 0:
|
if verbosity != 0 or quietness != 0:
|
||||||
targetloglevel = targetloglevel+verbosity-quietness
|
targetloglevel = targetloglevel+verbosity-quietness
|
||||||
|
|
||||||
# Start Reticulum
|
# Start Reticulum
|
||||||
reticulum = RNS.Reticulum(configdir=args.config, loglevel=targetloglevel)
|
reticulum = RNS.Reticulum(configdir=args.config, loglevel=targetloglevel)
|
||||||
RNS.compact_log_fmt = True
|
RNS.compact_log_fmt = True
|
||||||
|
@ -234,7 +234,7 @@ def main():
|
||||||
RNS.log("Invalid hexadecimal hash provided", RNS.LOG_ERROR)
|
RNS.log("Invalid hexadecimal hash provided", RNS.LOG_ERROR)
|
||||||
exit(7)
|
exit(7)
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Try loading Identity from file
|
# Try loading Identity from file
|
||||||
if not os.path.isfile(identity_str):
|
if not os.path.isfile(identity_str):
|
||||||
|
@ -391,7 +391,7 @@ def main():
|
||||||
RNS.log("Could not open output file for writing", RNS.LOG_ERROR)
|
RNS.log("Could not open output file for writing", RNS.LOG_ERROR)
|
||||||
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
|
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
exit(15)
|
exit(15)
|
||||||
|
|
||||||
# TODO: Actually expand this to a good solution
|
# TODO: Actually expand this to a good solution
|
||||||
# probably need to create a wrapper that takes
|
# probably need to create a wrapper that takes
|
||||||
# into account not closing stdout when done
|
# into account not closing stdout when done
|
||||||
|
@ -415,12 +415,12 @@ def main():
|
||||||
|
|
||||||
if not args.stdout:
|
if not args.stdout:
|
||||||
RNS.log(f"Signing {args.read}")
|
RNS.log(f"Signing {args.read}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data_output.write(identity.sign(data_input.read()))
|
data_output.write(identity.sign(data_input.read()))
|
||||||
data_output.close()
|
data_output.close()
|
||||||
data_input.close()
|
data_input.close()
|
||||||
|
|
||||||
if not args.stdout:
|
if not args.stdout:
|
||||||
if args.read:
|
if args.read:
|
||||||
RNS.log(f"File {args.read} signed with {identity} to {args.write}")
|
RNS.log(f"File {args.read} signed with {identity} to {args.write}")
|
||||||
|
@ -448,7 +448,7 @@ def main():
|
||||||
else:
|
else:
|
||||||
# if not args.stdout:
|
# if not args.stdout:
|
||||||
# RNS.log("Verifying "+str(args.validate)+" for "+str(args.read))
|
# RNS.log("Verifying "+str(args.validate)+" for "+str(args.read))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
sig_input = open(args.validate, "rb")
|
sig_input = open(args.validate, "rb")
|
||||||
|
@ -498,7 +498,7 @@ def main():
|
||||||
|
|
||||||
if not args.stdout:
|
if not args.stdout:
|
||||||
RNS.log(f"Encrypting {args.read}")
|
RNS.log(f"Encrypting {args.read}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
more_data = True
|
more_data = True
|
||||||
while more_data:
|
while more_data:
|
||||||
|
@ -545,7 +545,7 @@ def main():
|
||||||
|
|
||||||
if not args.stdout:
|
if not args.stdout:
|
||||||
RNS.log(f"Decrypting {args.read}...")
|
RNS.log(f"Decrypting {args.read}...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
more_data = True
|
more_data = True
|
||||||
while more_data:
|
while more_data:
|
||||||
|
|
|
@ -49,7 +49,7 @@ def main():
|
||||||
parser.add_argument('-q', '--quiet', action='count', default=0)
|
parser.add_argument('-q', '--quiet', action='count', default=0)
|
||||||
parser.add_argument("--exampleconfig", action='store_true', default=False, help="print verbose configuration example to stdout and exit")
|
parser.add_argument("--exampleconfig", action='store_true', default=False, help="print verbose configuration example to stdout and exit")
|
||||||
parser.add_argument("--version", action="version", version=f"ir {__version__}")
|
parser.add_argument("--version", action="version", version=f"ir {__version__}")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.exampleconfig:
|
if args.exampleconfig:
|
||||||
|
|
|
@ -61,7 +61,7 @@ class KISS():
|
||||||
FESC = 0xDB
|
FESC = 0xDB
|
||||||
TFEND = 0xDC
|
TFEND = 0xDC
|
||||||
TFESC = 0xDD
|
TFESC = 0xDD
|
||||||
|
|
||||||
CMD_UNKNOWN = 0xFE
|
CMD_UNKNOWN = 0xFE
|
||||||
CMD_DATA = 0x00
|
CMD_DATA = 0x00
|
||||||
CMD_FREQUENCY = 0x01
|
CMD_FREQUENCY = 0x01
|
||||||
|
@ -104,11 +104,11 @@ class KISS():
|
||||||
|
|
||||||
DETECT_REQ = 0x73
|
DETECT_REQ = 0x73
|
||||||
DETECT_RESP = 0x46
|
DETECT_RESP = 0x46
|
||||||
|
|
||||||
RADIO_STATE_OFF = 0x00
|
RADIO_STATE_OFF = 0x00
|
||||||
RADIO_STATE_ON = 0x01
|
RADIO_STATE_ON = 0x01
|
||||||
RADIO_STATE_ASK = 0xFF
|
RADIO_STATE_ASK = 0xFF
|
||||||
|
|
||||||
CMD_ERROR = 0x90
|
CMD_ERROR = 0x90
|
||||||
ERROR_INITRADIO = 0x01
|
ERROR_INITRADIO = 0x01
|
||||||
ERROR_TXFAILED = 0x02
|
ERROR_TXFAILED = 0x02
|
||||||
|
@ -191,7 +191,7 @@ class ROM():
|
||||||
PRODUCT_TECHO = 0x15
|
PRODUCT_TECHO = 0x15
|
||||||
MODEL_T4 = 0x16
|
MODEL_T4 = 0x16
|
||||||
MODEL_T9 = 0x17
|
MODEL_T9 = 0x17
|
||||||
|
|
||||||
PRODUCT_HMBRW = 0xF0
|
PRODUCT_HMBRW = 0xF0
|
||||||
MODEL_FF = 0xFF
|
MODEL_FF = 0xFF
|
||||||
MODEL_FE = 0xFE
|
MODEL_FE = 0xFE
|
||||||
|
@ -610,7 +610,7 @@ class RNode():
|
||||||
self.detected = True
|
self.detected = True
|
||||||
else:
|
else:
|
||||||
self.detected = False
|
self.detected = False
|
||||||
|
|
||||||
else:
|
else:
|
||||||
time_since_last = int(time.time()*1000) - last_read_ms
|
time_since_last = int(time.time()*1000) - last_read_ms
|
||||||
if len(data_buffer) > 0 and time_since_last > self.timeout:
|
if len(data_buffer) > 0 and time_since_last > self.timeout:
|
||||||
|
@ -886,7 +886,7 @@ class RNode():
|
||||||
from cryptography.hazmat.primitives.serialization import load_der_private_key
|
from cryptography.hazmat.primitives.serialization import load_der_private_key
|
||||||
from cryptography.hazmat.primitives.asymmetric import padding
|
from cryptography.hazmat.primitives.asymmetric import padding
|
||||||
|
|
||||||
# Try loading local signing key for
|
# Try loading local signing key for
|
||||||
# validation of self-signed devices
|
# validation of self-signed devices
|
||||||
if os.path.isdir(FWD_DIR) and os.path.isfile(FWD_DIR+"/signing.key"):
|
if os.path.isdir(FWD_DIR) and os.path.isfile(FWD_DIR+"/signing.key"):
|
||||||
private_bytes = None
|
private_bytes = None
|
||||||
|
@ -922,7 +922,7 @@ class RNode():
|
||||||
RNS.log("Could not deserialize local signing key")
|
RNS.log("Could not deserialize local signing key")
|
||||||
RNS.log(str(e))
|
RNS.log(str(e))
|
||||||
|
|
||||||
# Try loading trusted signing key for
|
# Try loading trusted signing key for
|
||||||
# validation of devices
|
# validation of devices
|
||||||
if os.path.isdir(TK_DIR):
|
if os.path.isdir(TK_DIR):
|
||||||
for f in os.listdir(TK_DIR):
|
for f in os.listdir(TK_DIR):
|
||||||
|
@ -1230,11 +1230,11 @@ def rnode_open_serial(port):
|
||||||
write_timeout = None,
|
write_timeout = None,
|
||||||
dsrdtr = False
|
dsrdtr = False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def graceful_exit(C=0):
|
def graceful_exit(C=0):
|
||||||
if RNS.vendor.platformutils.is_windows():
|
if RNS.vendor.platformutils.is_windows():
|
||||||
RNS.log("Windows detected; delaying DTR",RNS.LOG_VERBOSE)
|
RNS.log("Windows detected; delaying DTR",RNS.LOG_VERBOSE)
|
||||||
if rnode:
|
if rnode:
|
||||||
RNS.log("Sending \"Leave\" to Rnode",RNS.LOG_VERBOSE)
|
RNS.log("Sending \"Leave\" to Rnode",RNS.LOG_VERBOSE)
|
||||||
rnode.leave() # Leave has wait built in
|
rnode.leave() # Leave has wait built in
|
||||||
|
@ -1319,13 +1319,13 @@ def main():
|
||||||
|
|
||||||
parser.add_argument("-f", "--flash", action="store_true", help="Flash firmware and bootstrap EEPROM")
|
parser.add_argument("-f", "--flash", action="store_true", help="Flash firmware and bootstrap EEPROM")
|
||||||
parser.add_argument("-r", "--rom", action="store_true", help="Bootstrap EEPROM without flashing firmware")
|
parser.add_argument("-r", "--rom", action="store_true", help="Bootstrap EEPROM without flashing firmware")
|
||||||
parser.add_argument("-k", "--key", action="store_true", help="Generate a new signing key and exit") #
|
parser.add_argument("-k", "--key", action="store_true", help="Generate a new signing key and exit") #
|
||||||
parser.add_argument("-S", "--sign", action="store_true", help="Display public part of signing key")
|
parser.add_argument("-S", "--sign", action="store_true", help="Display public part of signing key")
|
||||||
parser.add_argument("-H", "--firmware-hash", action="store", help="Display installed firmware hash")
|
parser.add_argument("-H", "--firmware-hash", action="store", help="Display installed firmware hash")
|
||||||
parser.add_argument("-K", "--get-target-firmware-hash", action="store_true", help=argparse.SUPPRESS) # Get target firmware hash from device
|
parser.add_argument("-K", "--get-target-firmware-hash", action="store_true", help=argparse.SUPPRESS) # Get target firmware hash from device
|
||||||
parser.add_argument("-L", "--get-firmware-hash", action="store_true", help=argparse.SUPPRESS) # Get calculated firmware hash from device
|
parser.add_argument("-L", "--get-firmware-hash", action="store_true", help=argparse.SUPPRESS) # Get calculated firmware hash from device
|
||||||
parser.add_argument("--platform", action="store", metavar="platform", type=str, default=None, help="Platform specification for device bootstrap")
|
parser.add_argument("--platform", action="store", metavar="platform", type=str, default=None, help="Platform specification for device bootstrap")
|
||||||
parser.add_argument("--product", action="store", metavar="product", type=str, default=None, help="Product specification for device bootstrap") #
|
parser.add_argument("--product", action="store", metavar="product", type=str, default=None, help="Product specification for device bootstrap") #
|
||||||
parser.add_argument("--model", action="store", metavar="model", type=str, default=None, help="Model code for device bootstrap")
|
parser.add_argument("--model", action="store", metavar="model", type=str, default=None, help="Model code for device bootstrap")
|
||||||
parser.add_argument("--hwrev", action="store", metavar="revision", type=int, default=None, help="Hardware revision for device bootstrap")
|
parser.add_argument("--hwrev", action="store", metavar="revision", type=int, default=None, help="Hardware revision for device bootstrap")
|
||||||
|
|
||||||
|
@ -1354,7 +1354,7 @@ def main():
|
||||||
|
|
||||||
if args.fw_version != None:
|
if args.fw_version != None:
|
||||||
selected_version = args.fw_version
|
selected_version = args.fw_version
|
||||||
try:
|
try:
|
||||||
check_float = float(selected_version)
|
check_float = float(selected_version)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
RNS.log("Selected version \""+selected_version+"\" does not appear to be a number.")
|
RNS.log("Selected version \""+selected_version+"\" does not appear to be a number.")
|
||||||
|
@ -1368,7 +1368,7 @@ def main():
|
||||||
|
|
||||||
if args.nocheck:
|
if args.nocheck:
|
||||||
upd_nocheck = True
|
upd_nocheck = True
|
||||||
|
|
||||||
if args.public or args.key or args.flash or args.rom or args.autoinstall or args.trust_key:
|
if args.public or args.key or args.flash or args.rom or args.autoinstall or args.trust_key:
|
||||||
from cryptography.hazmat.primitives import hashes
|
from cryptography.hazmat.primitives import hashes
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
|
@ -1419,8 +1419,8 @@ def main():
|
||||||
ports = list_ports.comports()
|
ports = list_ports.comports()
|
||||||
portlist = []
|
portlist = []
|
||||||
for port in ports:
|
for port in ports:
|
||||||
portlist.insert(0, port)
|
portlist.insert(0, port)
|
||||||
|
|
||||||
pi = 1
|
pi = 1
|
||||||
print("Detected serial ports:")
|
print("Detected serial ports:")
|
||||||
for port in portlist:
|
for port in portlist:
|
||||||
|
@ -1556,8 +1556,8 @@ def main():
|
||||||
ports = list_ports.comports()
|
ports = list_ports.comports()
|
||||||
portlist = []
|
portlist = []
|
||||||
for port in ports:
|
for port in ports:
|
||||||
portlist.insert(0, port)
|
portlist.insert(0, port)
|
||||||
|
|
||||||
pi = 1
|
pi = 1
|
||||||
print("Detected serial ports:")
|
print("Detected serial ports:")
|
||||||
for port in portlist:
|
for port in portlist:
|
||||||
|
@ -1638,7 +1638,7 @@ def main():
|
||||||
print("correct firmware and provision it.")
|
print("correct firmware and provision it.")
|
||||||
else:
|
else:
|
||||||
print("\nIt looks like this is a fresh device with no RNode firmware.")
|
print("\nIt looks like this is a fresh device with no RNode firmware.")
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
print("What kind of device is this?\n")
|
print("What kind of device is this?\n")
|
||||||
print("[1] A specific kind of RNode")
|
print("[1] A specific kind of RNode")
|
||||||
|
@ -2224,7 +2224,7 @@ def main():
|
||||||
fw_filename = "rnode_firmware.hex"
|
fw_filename = "rnode_firmware.hex"
|
||||||
elif selected_mcu == ROM.MCU_2560:
|
elif selected_mcu == ROM.MCU_2560:
|
||||||
fw_filename = "rnode_firmware_m2560.hex"
|
fw_filename = "rnode_firmware_m2560.hex"
|
||||||
|
|
||||||
elif selected_platform == ROM.PLATFORM_ESP32:
|
elif selected_platform == ROM.PLATFORM_ESP32:
|
||||||
fw_filename = None
|
fw_filename = None
|
||||||
print("\nWhat kind of ESP32 board is this?\n")
|
print("\nWhat kind of ESP32 board is this?\n")
|
||||||
|
@ -2337,7 +2337,7 @@ def main():
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Could not load device signing key")
|
RNS.log("Could not load device signing key")
|
||||||
|
|
||||||
|
|
||||||
graceful_exit()
|
graceful_exit()
|
||||||
|
|
||||||
|
@ -2413,7 +2413,7 @@ def main():
|
||||||
return part_hash
|
return part_hash
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
elif platform == ROM.PLATFORM_NRF52:
|
elif platform == ROM.PLATFORM_NRF52:
|
||||||
# Calculate digest manually, as it is not included in the image.
|
# Calculate digest manually, as it is not included in the image.
|
||||||
firmware_data = open(partition_file, "rb")
|
firmware_data = open(partition_file, "rb")
|
||||||
|
@ -2994,7 +2994,7 @@ def main():
|
||||||
wants_fw_provision = False
|
wants_fw_provision = False
|
||||||
if args.flash:
|
if args.flash:
|
||||||
from subprocess import call
|
from subprocess import call
|
||||||
|
|
||||||
if fw_filename == None:
|
if fw_filename == None:
|
||||||
fw_filename = "rnode_firmware.hex"
|
fw_filename = "rnode_firmware.hex"
|
||||||
|
|
||||||
|
@ -3032,7 +3032,7 @@ def main():
|
||||||
RNS.log("Error while flashing")
|
RNS.log("Error while flashing")
|
||||||
RNS.log(str(e))
|
RNS.log(str(e))
|
||||||
graceful_exit(1)
|
graceful_exit(1)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
fw_src = UPD_DIR+"/"+selected_version+"/"
|
fw_src = UPD_DIR+"/"+selected_version+"/"
|
||||||
if os.path.isfile(fw_src+fw_filename):
|
if os.path.isfile(fw_src+fw_filename):
|
||||||
|
@ -3215,7 +3215,7 @@ def main():
|
||||||
update_full_path = EXT_DIR+"/extracted_rnode_firmware.version"
|
update_full_path = EXT_DIR+"/extracted_rnode_firmware.version"
|
||||||
else:
|
else:
|
||||||
update_full_path = UPD_DIR+"/"+selected_version+"/"+fw_filename
|
update_full_path = UPD_DIR+"/"+selected_version+"/"+fw_filename
|
||||||
if os.path.isfile(update_full_path):
|
if os.path.isfile(update_full_path):
|
||||||
try:
|
try:
|
||||||
args.info = False
|
args.info = False
|
||||||
RNS.log("Updating RNode firmware for device on "+args.port)
|
RNS.log("Updating RNode firmware for device on "+args.port)
|
||||||
|
@ -3468,7 +3468,7 @@ def main():
|
||||||
if args.autoinstall:
|
if args.autoinstall:
|
||||||
RNS.log("Clearing old EEPROM, this will take about 15 seconds...")
|
RNS.log("Clearing old EEPROM, this will take about 15 seconds...")
|
||||||
rnode.wipe_eeprom()
|
rnode.wipe_eeprom()
|
||||||
|
|
||||||
if rnode.platform == ROM.PLATFORM_ESP32:
|
if rnode.platform == ROM.PLATFORM_ESP32:
|
||||||
RNS.log("Waiting for ESP32 reset...")
|
RNS.log("Waiting for ESP32 reset...")
|
||||||
time.sleep(6)
|
time.sleep(6)
|
||||||
|
@ -3849,7 +3849,7 @@ def main():
|
||||||
else:
|
else:
|
||||||
RNS.log("This device has not been provisioned yet, cannot get firmware hash")
|
RNS.log("This device has not been provisioned yet, cannot get firmware hash")
|
||||||
exit(77)
|
exit(77)
|
||||||
|
|
||||||
if args.get_firmware_hash:
|
if args.get_firmware_hash:
|
||||||
if rnode.provisioned:
|
if rnode.provisioned:
|
||||||
RNS.log(f"The actual firmware hash is: {rnode.firmware_hash.hex()}")
|
RNS.log(f"The actual firmware hash is: {rnode.firmware_hash.hex()}")
|
||||||
|
@ -3923,7 +3923,7 @@ def main():
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("")
|
print("")
|
||||||
graceful_exit()
|
graceful_exit()
|
||||||
|
|
||||||
graceful_exit()
|
graceful_exit()
|
||||||
|
|
||||||
def extract_recovery_esptool():
|
def extract_recovery_esptool():
|
||||||
|
|
|
@ -235,7 +235,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
|
||||||
hour_rate = round(len(entry["timestamps"])/span_hours, 3)
|
hour_rate = round(len(entry["timestamps"])/span_hours, 3)
|
||||||
if hour_rate-int(hour_rate) == 0:
|
if hour_rate-int(hour_rate) == 0:
|
||||||
hour_rate = int(hour_rate)
|
hour_rate = int(hour_rate)
|
||||||
|
|
||||||
if entry["rate_violations"] > 0:
|
if entry["rate_violations"] > 0:
|
||||||
if entry["rate_violations"] == 1:
|
if entry["rate_violations"] == 1:
|
||||||
s_str = ""
|
s_str = ""
|
||||||
|
@ -245,14 +245,14 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
|
||||||
rv_str = f", {entry['rate_violations']} active rate violation{s_str}"
|
rv_str = f", {entry['rate_violations']} active rate violation{s_str}"
|
||||||
else:
|
else:
|
||||||
rv_str = ""
|
rv_str = ""
|
||||||
|
|
||||||
if entry["blocked_until"] > time.time():
|
if entry["blocked_until"] > time.time():
|
||||||
bli = time.time()-(int(entry["blocked_until"])-time.time())
|
bli = time.time()-(int(entry["blocked_until"])-time.time())
|
||||||
bl_str = f", new announces allowed in {pretty_date(int(bli))}"
|
bl_str = f", new announces allowed in {pretty_date(int(bli))}"
|
||||||
else:
|
else:
|
||||||
bl_str = ""
|
bl_str = ""
|
||||||
|
|
||||||
|
|
||||||
print(f"{RNS.prettyhexrep(entry['hash'])} last heard {last_str} ago, {hour_rate} announces/hour in the last {span_str}{rv_str}{bl_str}")
|
print(f"{RNS.prettyhexrep(entry['hash'])} last heard {last_str} ago, {hour_rate} announces/hour in the last {span_str}{rv_str}{bl_str}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -272,7 +272,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
|
||||||
|
|
||||||
print("Dropping announce queues on all interfaces...")
|
print("Dropping announce queues on all interfaces...")
|
||||||
reticulum.drop_announce_queues()
|
reticulum.drop_announce_queues()
|
||||||
|
|
||||||
elif drop:
|
elif drop:
|
||||||
if remote_link:
|
if remote_link:
|
||||||
if not no_output:
|
if not no_output:
|
||||||
|
@ -341,7 +341,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(str(e))
|
print(str(e))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if not RNS.Transport.has_path(destination_hash):
|
if not RNS.Transport.has_path(destination_hash):
|
||||||
RNS.Transport.request_path(destination_hash)
|
RNS.Transport.request_path(destination_hash)
|
||||||
print(f"Path to {RNS.prettyhexrep(destination_hash)} requested ", end=" ")
|
print(f"Path to {RNS.prettyhexrep(destination_hash)} requested ", end=" ")
|
||||||
|
@ -376,7 +376,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
|
||||||
print("\r \rPath not found")
|
print("\r \rPath not found")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
try:
|
try:
|
||||||
|
@ -479,7 +479,7 @@ def main():
|
||||||
help="timeout before giving up on remote queries",
|
help="timeout before giving up on remote queries",
|
||||||
default=RNS.Transport.PATH_REQUEST_TIMEOUT
|
default=RNS.Transport.PATH_REQUEST_TIMEOUT
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-j",
|
"-j",
|
||||||
"--json",
|
"--json",
|
||||||
|
@ -497,7 +497,7 @@ def main():
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument('-v', '--verbose', action='count', default=0)
|
parser.add_argument('-v', '--verbose', action='count', default=0)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.config:
|
if args.config:
|
||||||
|
|
|
@ -38,7 +38,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
|
||||||
if full_name == None:
|
if full_name == None:
|
||||||
print("The full destination name including application name aspects must be specified for the destination")
|
print("The full destination name including application name aspects must be specified for the destination")
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
app_name, aspects = RNS.Destination.app_and_aspects_from_name(full_name)
|
app_name, aspects = RNS.Destination.app_and_aspects_from_name(full_name)
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
|
||||||
|
|
||||||
if time.time() > _timeout:
|
if time.time() > _timeout:
|
||||||
print("\r \rProbe timed out")
|
print("\r \rProbe timed out")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print("\b\b ")
|
print("\b\b ")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
@ -162,10 +162,10 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
|
||||||
|
|
||||||
if reception_rssi != None:
|
if reception_rssi != None:
|
||||||
reception_stats += f" [RSSI {reception_rssi} dBm]"
|
reception_stats += f" [RSSI {reception_rssi} dBm]"
|
||||||
|
|
||||||
if reception_snr != None:
|
if reception_snr != None:
|
||||||
reception_stats += f" [SNR {reception_snr} dB]"
|
reception_stats += f" [SNR {reception_snr} dB]"
|
||||||
|
|
||||||
if reception_q != None:
|
if reception_q != None:
|
||||||
reception_stats += f" [Link Quality {reception_q}%]"
|
reception_stats += f" [Link Quality {reception_q}%]"
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
|
||||||
if receipt.proof_packet != None:
|
if receipt.proof_packet != None:
|
||||||
if receipt.proof_packet.rssi != None:
|
if receipt.proof_packet.rssi != None:
|
||||||
reception_stats += f" [RSSI {receipt.proof_packet.rssi} dBm]"
|
reception_stats += f" [RSSI {receipt.proof_packet.rssi} dBm]"
|
||||||
|
|
||||||
if receipt.proof_packet.snr != None:
|
if receipt.proof_packet.snr != None:
|
||||||
reception_stats += f" [SNR {receipt.proof_packet.snr} dB]"
|
reception_stats += f" [SNR {receipt.proof_packet.snr} dB]"
|
||||||
|
|
||||||
|
@ -192,7 +192,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
|
||||||
exit(2)
|
exit(2)
|
||||||
else:
|
else:
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -56,7 +56,7 @@ def main():
|
||||||
parser.add_argument('-s', '--service', action='store_true', default=False, help="rnsd is running as a service and should log to file")
|
parser.add_argument('-s', '--service', action='store_true', default=False, help="rnsd is running as a service and should log to file")
|
||||||
parser.add_argument("--exampleconfig", action='store_true', default=False, help="print verbose configuration example to stdout and exit")
|
parser.add_argument("--exampleconfig", action='store_true', default=False, help="print verbose configuration example to stdout and exit")
|
||||||
parser.add_argument("--version", action="version", version=f"rnsd {__version__}")
|
parser.add_argument("--version", action="version", version=f"rnsd {__version__}")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.exampleconfig:
|
if args.exampleconfig:
|
||||||
|
@ -192,7 +192,7 @@ loglevel = 4
|
||||||
|
|
||||||
# The following example enables communication with other
|
# The following example enables communication with other
|
||||||
# local Reticulum peers using UDP broadcasts.
|
# local Reticulum peers using UDP broadcasts.
|
||||||
|
|
||||||
[[UDP Interface]]
|
[[UDP Interface]]
|
||||||
type = UDPInterface
|
type = UDPInterface
|
||||||
enabled = no
|
enabled = no
|
||||||
|
@ -235,24 +235,24 @@ loglevel = 4
|
||||||
# This example demonstrates a TCP server interface.
|
# This example demonstrates a TCP server interface.
|
||||||
# It will listen for incoming connections on the
|
# It will listen for incoming connections on the
|
||||||
# specified IP address and port number.
|
# specified IP address and port number.
|
||||||
|
|
||||||
[[TCP Server Interface]]
|
[[TCP Server Interface]]
|
||||||
type = TCPServerInterface
|
type = TCPServerInterface
|
||||||
enabled = no
|
enabled = no
|
||||||
|
|
||||||
# This configuration will listen on all IP
|
# This configuration will listen on all IP
|
||||||
# interfaces on port 4242
|
# interfaces on port 4242
|
||||||
|
|
||||||
listen_ip = 0.0.0.0
|
listen_ip = 0.0.0.0
|
||||||
listen_port = 4242
|
listen_port = 4242
|
||||||
|
|
||||||
# Alternatively you can bind to a specific IP
|
# Alternatively you can bind to a specific IP
|
||||||
|
|
||||||
# listen_ip = 10.0.0.88
|
# listen_ip = 10.0.0.88
|
||||||
# listen_port = 4242
|
# listen_port = 4242
|
||||||
|
|
||||||
# Or a specific network device
|
# Or a specific network device
|
||||||
|
|
||||||
# device = eth0
|
# device = eth0
|
||||||
# port = 4242
|
# port = 4242
|
||||||
|
|
||||||
|
@ -300,7 +300,7 @@ loglevel = 4
|
||||||
# host device before connecting. BLE
|
# host device before connecting. BLE
|
||||||
# devices can be connected by name,
|
# devices can be connected by name,
|
||||||
# BLE MAC address or by any available.
|
# BLE MAC address or by any available.
|
||||||
|
|
||||||
# Connect to specific device by name
|
# Connect to specific device by name
|
||||||
# port = ble://RNode 3B87
|
# port = ble://RNode 3B87
|
||||||
|
|
||||||
|
@ -320,7 +320,7 @@ loglevel = 4
|
||||||
# Set TX power to 7 dBm (5 mW)
|
# Set TX power to 7 dBm (5 mW)
|
||||||
txpower = 7
|
txpower = 7
|
||||||
|
|
||||||
# Select spreading factor 8. Valid
|
# Select spreading factor 8. Valid
|
||||||
# range is 7 through 12, with 7
|
# range is 7 through 12, with 7
|
||||||
# being the fastest and 12 having
|
# being the fastest and 12 having
|
||||||
# the longest range.
|
# the longest range.
|
||||||
|
@ -349,8 +349,8 @@ loglevel = 4
|
||||||
# flow control can be useful. By default
|
# flow control can be useful. By default
|
||||||
# it is disabled.
|
# it is disabled.
|
||||||
flow_control = False
|
flow_control = False
|
||||||
|
|
||||||
|
|
||||||
# An example KISS modem interface. Useful for running
|
# An example KISS modem interface. Useful for running
|
||||||
# Reticulum over packet radio hardware.
|
# Reticulum over packet radio hardware.
|
||||||
|
|
||||||
|
@ -365,7 +365,7 @@ loglevel = 4
|
||||||
|
|
||||||
# Set the serial baud-rate and other
|
# Set the serial baud-rate and other
|
||||||
# configuration parameters.
|
# configuration parameters.
|
||||||
speed = 115200
|
speed = 115200
|
||||||
databits = 8
|
databits = 8
|
||||||
parity = none
|
parity = none
|
||||||
stopbits = 1
|
stopbits = 1
|
||||||
|
@ -413,7 +413,7 @@ loglevel = 4
|
||||||
# way, Reticulum will automatically encapsulate it's
|
# way, Reticulum will automatically encapsulate it's
|
||||||
# traffic in AX.25 and also identify your stations
|
# traffic in AX.25 and also identify your stations
|
||||||
# transmissions with your callsign and SSID.
|
# transmissions with your callsign and SSID.
|
||||||
#
|
#
|
||||||
# Only do this if you really need to! Reticulum doesn't
|
# Only do this if you really need to! Reticulum doesn't
|
||||||
# need the AX.25 layer for anything, and it incurs extra
|
# need the AX.25 layer for anything, and it incurs extra
|
||||||
# overhead on every packet to encapsulate in AX.25.
|
# overhead on every packet to encapsulate in AX.25.
|
||||||
|
@ -436,7 +436,7 @@ loglevel = 4
|
||||||
|
|
||||||
# Set the serial baud-rate and other
|
# Set the serial baud-rate and other
|
||||||
# configuration parameters.
|
# configuration parameters.
|
||||||
speed = 115200
|
speed = 115200
|
||||||
databits = 8
|
databits = 8
|
||||||
parity = none
|
parity = none
|
||||||
stopbits = 1
|
stopbits = 1
|
||||||
|
|
|
@ -161,7 +161,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
|
||||||
stats, link_count = remote_status
|
stats, link_count = remote_status
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(str(e))
|
print(str(e))
|
||||||
exit(20)
|
exit(20)
|
||||||
|
@ -215,7 +215,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
|
||||||
if sorting == "held":
|
if sorting == "held":
|
||||||
interfaces.sort(key=lambda i: i["held_announces"], reverse=not sort_reverse)
|
interfaces.sort(key=lambda i: i["held_announces"], reverse=not sort_reverse)
|
||||||
|
|
||||||
|
|
||||||
for ifstat in interfaces:
|
for ifstat in interfaces:
|
||||||
name = ifstat["name"]
|
name = ifstat["name"]
|
||||||
|
|
||||||
|
@ -302,10 +302,10 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
|
||||||
|
|
||||||
if "airtime_short" in ifstat and "airtime_long" in ifstat:
|
if "airtime_short" in ifstat and "airtime_long" in ifstat:
|
||||||
print(" Airtime : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["airtime_short"]),atl=str(ifstat["airtime_long"])))
|
print(" Airtime : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["airtime_short"]),atl=str(ifstat["airtime_long"])))
|
||||||
|
|
||||||
if "channel_load_short" in ifstat and "channel_load_long" in ifstat:
|
if "channel_load_short" in ifstat and "channel_load_long" in ifstat:
|
||||||
print(" Ch.Load : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["channel_load_short"]),atl=str(ifstat["channel_load_long"])))
|
print(" Ch.Load : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["channel_load_short"]),atl=str(ifstat["channel_load_long"])))
|
||||||
|
|
||||||
if "peers" in ifstat and ifstat["peers"] != None:
|
if "peers" in ifstat and ifstat["peers"] != None:
|
||||||
print(" Peers : {np} reachable".format(np=ifstat["peers"]))
|
print(" Peers : {np} reachable".format(np=ifstat["peers"]))
|
||||||
|
|
||||||
|
@ -315,7 +315,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
|
||||||
if "ifac_signature" in ifstat and ifstat["ifac_signature"] != None:
|
if "ifac_signature" in ifstat and ifstat["ifac_signature"] != None:
|
||||||
sigstr = "<…"+RNS.hexrep(ifstat["ifac_signature"][-5:], delimit=False)+">"
|
sigstr = "<…"+RNS.hexrep(ifstat["ifac_signature"][-5:], delimit=False)+">"
|
||||||
print(" Access : {nb}-bit IFAC by {sig}".format(nb=ifstat["ifac_size"]*8, sig=sigstr))
|
print(" Access : {nb}-bit IFAC by {sig}".format(nb=ifstat["ifac_size"]*8, sig=sigstr))
|
||||||
|
|
||||||
if "i2p_b32" in ifstat and ifstat["i2p_b32"] != None:
|
if "i2p_b32" in ifstat and ifstat["i2p_b32"] != None:
|
||||||
print(" I2P B32 : {ep}".format(ep=str(ifstat["i2p_b32"])))
|
print(" I2P B32 : {ep}".format(ep=str(ifstat["i2p_b32"])))
|
||||||
|
|
||||||
|
@ -325,14 +325,14 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
|
||||||
print(" Queued : {np} announce".format(np=aqn))
|
print(" Queued : {np} announce".format(np=aqn))
|
||||||
else:
|
else:
|
||||||
print(" Queued : {np} announces".format(np=aqn))
|
print(" Queued : {np} announces".format(np=aqn))
|
||||||
|
|
||||||
if astats and "held_announces" in ifstat and ifstat["held_announces"] != None and ifstat["held_announces"] > 0:
|
if astats and "held_announces" in ifstat and ifstat["held_announces"] != None and ifstat["held_announces"] > 0:
|
||||||
aqn = ifstat["held_announces"]
|
aqn = ifstat["held_announces"]
|
||||||
if aqn == 1:
|
if aqn == 1:
|
||||||
print(" Held : {np} announce".format(np=aqn))
|
print(" Held : {np} announce".format(np=aqn))
|
||||||
else:
|
else:
|
||||||
print(" Held : {np} announces".format(np=aqn))
|
print(" Held : {np} announces".format(np=aqn))
|
||||||
|
|
||||||
if astats and "incoming_announce_frequency" in ifstat and ifstat["incoming_announce_frequency"] != None:
|
if astats and "incoming_announce_frequency" in ifstat and ifstat["incoming_announce_frequency"] != None:
|
||||||
print(" Announces : {iaf}↑".format(iaf=RNS.prettyfrequency(ifstat["outgoing_announce_frequency"])))
|
print(" Announces : {iaf}↑".format(iaf=RNS.prettyfrequency(ifstat["outgoing_announce_frequency"])))
|
||||||
print(" {iaf}↓".format(iaf=RNS.prettyfrequency(ifstat["incoming_announce_frequency"])))
|
print(" {iaf}↓".format(iaf=RNS.prettyfrequency(ifstat["incoming_announce_frequency"])))
|
||||||
|
@ -358,7 +358,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
|
||||||
print(f"\n{lstr}")
|
print(f"\n{lstr}")
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if not remote:
|
if not remote:
|
||||||
print("Could not get RNS status")
|
print("Could not get RNS status")
|
||||||
|
@ -379,7 +379,7 @@ def main():
|
||||||
help="show all interfaces",
|
help="show all interfaces",
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-A",
|
"-A",
|
||||||
"--announce-stats",
|
"--announce-stats",
|
||||||
|
@ -387,7 +387,7 @@ def main():
|
||||||
help="show announce stats",
|
help="show announce stats",
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-l",
|
"-l",
|
||||||
"--link-stats",
|
"--link-stats",
|
||||||
|
@ -395,7 +395,7 @@ def main():
|
||||||
help="show link stats",
|
help="show link stats",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-s",
|
"-s",
|
||||||
"--sort",
|
"--sort",
|
||||||
|
@ -404,7 +404,7 @@ def main():
|
||||||
default=None,
|
default=None,
|
||||||
type=str
|
type=str
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-r",
|
"-r",
|
||||||
"--reverse",
|
"--reverse",
|
||||||
|
@ -412,7 +412,7 @@ def main():
|
||||||
help="reverse sorting",
|
help="reverse sorting",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-j",
|
"-j",
|
||||||
"--json",
|
"--json",
|
||||||
|
@ -451,7 +451,7 @@ def main():
|
||||||
parser.add_argument('-v', '--verbose', action='count', default=0)
|
parser.add_argument('-v', '--verbose', action='count', default=0)
|
||||||
|
|
||||||
parser.add_argument("filter", nargs="?", default=None, help="only display interfaces with names including filter", type=str)
|
parser.add_argument("filter", nargs="?", default=None, help="only display interfaces with names including filter", type=str)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.config:
|
if args.config:
|
||||||
|
|
|
@ -45,7 +45,7 @@ def prepare_identity(identity_path):
|
||||||
identity_path = f"{RNS.Reticulum.identitypath}/{APP_NAME}"
|
identity_path = f"{RNS.Reticulum.identitypath}/{APP_NAME}"
|
||||||
|
|
||||||
if os.path.isfile(identity_path):
|
if os.path.isfile(identity_path):
|
||||||
identity = RNS.Identity.from_file(identity_path)
|
identity = RNS.Identity.from_file(identity_path)
|
||||||
|
|
||||||
if identity == None:
|
if identity == None:
|
||||||
RNS.log("No valid saved identity found, creating new...", RNS.LOG_INFO)
|
RNS.log("No valid saved identity found, creating new...", RNS.LOG_INFO)
|
||||||
|
@ -57,7 +57,7 @@ def listen(configdir, identitypath = None, verbosity = 0, quietness = 0, allowed
|
||||||
|
|
||||||
targetloglevel = 3+verbosity-quietness
|
targetloglevel = 3+verbosity-quietness
|
||||||
reticulum = RNS.Reticulum(configdir=configdir, loglevel=targetloglevel)
|
reticulum = RNS.Reticulum(configdir=configdir, loglevel=targetloglevel)
|
||||||
|
|
||||||
prepare_identity(identitypath)
|
prepare_identity(identitypath)
|
||||||
destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "execute")
|
destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "execute")
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ def listen(configdir, identitypath = None, verbosity = 0, quietness = 0, allowed
|
||||||
|
|
||||||
if not disable_announce:
|
if not disable_announce:
|
||||||
destination.announce()
|
destination.announce()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
|
@ -338,7 +338,7 @@ def execute(configdir, identitypath = None, verbosity = 0, quietness = 0, detail
|
||||||
if link == None or link.status == RNS.Link.CLOSED or link.status == RNS.Link.PENDING:
|
if link == None or link.status == RNS.Link.CLOSED or link.status == RNS.Link.PENDING:
|
||||||
link = RNS.Link(listener_destination)
|
link = RNS.Link(listener_destination)
|
||||||
link.did_identify = False
|
link.did_identify = False
|
||||||
|
|
||||||
if not spin(until=lambda: link.status == RNS.Link.ACTIVE, msg=f"Establishing link with {RNS.prettyhexrep(destination_hash)}", timeout=timeout):
|
if not spin(until=lambda: link.status == RNS.Link.ACTIVE, msg=f"Establishing link with {RNS.prettyhexrep(destination_hash)}", timeout=timeout):
|
||||||
print(f"Could not establish link with {RNS.prettyhexrep(destination_hash)}")
|
print(f"Could not establish link with {RNS.prettyhexrep(destination_hash)}")
|
||||||
exit(243)
|
exit(243)
|
||||||
|
@ -467,7 +467,7 @@ def execute(configdir, identitypath = None, verbosity = 0, quietness = 0, detail
|
||||||
else:
|
else:
|
||||||
tstr = ""
|
tstr = ""
|
||||||
print(f"Remote wrote {outlen} bytes to stdout{tstr}")
|
print(f"Remote wrote {outlen} bytes to stdout{tstr}")
|
||||||
|
|
||||||
if errlen != None and stderr != None:
|
if errlen != None and stderr != None:
|
||||||
if len(stderr) < errlen:
|
if len(stderr) < errlen:
|
||||||
tstr = f", {len(stderr)} bytes displayed"
|
tstr = f", {len(stderr)} bytes displayed"
|
||||||
|
@ -548,7 +548,7 @@ def main():
|
||||||
parser.add_argument("--stdout", action='store', default=None, help="max size in bytes of returned stdout", type=int)
|
parser.add_argument("--stdout", action='store', default=None, help="max size in bytes of returned stdout", type=int)
|
||||||
parser.add_argument("--stderr", action='store', default=None, help="max size in bytes of returned stderr", type=int)
|
parser.add_argument("--stderr", action='store', default=None, help="max size in bytes of returned stderr", type=int)
|
||||||
parser.add_argument("--version", action="version", version=f"rnx {__version__}")
|
parser.add_argument("--version", action="version", version=f"rnx {__version__}")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.listen or args.print_identity:
|
if args.listen or args.print_identity:
|
||||||
|
@ -600,8 +600,8 @@ def main():
|
||||||
# while True:
|
# while True:
|
||||||
# ch = sys.stdin.read(1)
|
# ch = sys.stdin.read(1)
|
||||||
# cmdbuf += ch.encode("utf-8")
|
# cmdbuf += ch.encode("utf-8")
|
||||||
# print("\r"+prompt+cmdbuf.decode("utf-8"), end="")
|
# print("\r"+prompt+cmdbuf.decode("utf-8"), end="")
|
||||||
|
|
||||||
command = input()
|
command = input()
|
||||||
if command.lower() == "exit" or command.lower() == "quit":
|
if command.lower() == "exit" or command.lower() == "quit":
|
||||||
exit(0)
|
exit(0)
|
||||||
|
@ -676,7 +676,7 @@ def pretty_time(time, verbose=False):
|
||||||
minutes = int(time // 60)
|
minutes = int(time // 60)
|
||||||
time %= 60
|
time %= 60
|
||||||
seconds = round(time, 2)
|
seconds = round(time, 2)
|
||||||
|
|
||||||
ss = "" if seconds == 1 else "s"
|
ss = "" if seconds == 1 else "s"
|
||||||
sm = "" if minutes == 1 else "s"
|
sm = "" if minutes == 1 else "s"
|
||||||
sh = "" if hours == 1 else "s"
|
sh = "" if hours == 1 else "s"
|
||||||
|
|
|
@ -90,7 +90,7 @@ def loglevelname(level):
|
||||||
return "Debug"
|
return "Debug"
|
||||||
if (level == LOG_EXTREME):
|
if (level == LOG_EXTREME):
|
||||||
return "Extra"
|
return "Extra"
|
||||||
|
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
|
|
||||||
def version():
|
def version():
|
||||||
|
@ -124,7 +124,7 @@ def log(msg, level=3, _override_destination = False):
|
||||||
file = open(logfile, "a")
|
file = open(logfile, "a")
|
||||||
file.write(logstring+"\n")
|
file.write(logstring+"\n")
|
||||||
file.close()
|
file.close()
|
||||||
|
|
||||||
if os.path.getsize(logfile) > LOG_MAXSIZE:
|
if os.path.getsize(logfile) > LOG_MAXSIZE:
|
||||||
prevfile = logfile+".1"
|
prevfile = logfile+".1"
|
||||||
if os.path.isfile(prevfile):
|
if os.path.isfile(prevfile):
|
||||||
|
@ -138,7 +138,7 @@ def log(msg, level=3, _override_destination = False):
|
||||||
log("Exception occurred while writing log message to log file: "+str(e), LOG_CRITICAL)
|
log("Exception occurred while writing log message to log file: "+str(e), LOG_CRITICAL)
|
||||||
log("Dumping future log events to console!", LOG_CRITICAL)
|
log("Dumping future log events to console!", LOG_CRITICAL)
|
||||||
log(msg, level)
|
log(msg, level)
|
||||||
|
|
||||||
|
|
||||||
def rand():
|
def rand():
|
||||||
result = instance_random.random()
|
result = instance_random.random()
|
||||||
|
@ -155,7 +155,7 @@ def hexrep(data, delimit=True):
|
||||||
iter(data)
|
iter(data)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
data = [data]
|
data = [data]
|
||||||
|
|
||||||
delimiter = ":"
|
delimiter = ":"
|
||||||
if not delimit:
|
if not delimit:
|
||||||
delimiter = ""
|
delimiter = ""
|
||||||
|
@ -228,7 +228,7 @@ def prettytime(time, verbose=False, compact=False):
|
||||||
seconds = int(time)
|
seconds = int(time)
|
||||||
else:
|
else:
|
||||||
seconds = round(time, 2)
|
seconds = round(time, 2)
|
||||||
|
|
||||||
ss = "" if seconds == 1 else "s"
|
ss = "" if seconds == 1 else "s"
|
||||||
sm = "" if minutes == 1 else "s"
|
sm = "" if minutes == 1 else "s"
|
||||||
sh = "" if hours == 1 else "s"
|
sh = "" if hours == 1 else "s"
|
||||||
|
@ -272,7 +272,7 @@ def prettytime(time, verbose=False, compact=False):
|
||||||
|
|
||||||
def prettyshorttime(time, verbose=False, compact=False):
|
def prettyshorttime(time, verbose=False, compact=False):
|
||||||
time = time*1e6
|
time = time*1e6
|
||||||
|
|
||||||
seconds = int(time // 1e6); time %= 1e6
|
seconds = int(time // 1e6); time %= 1e6
|
||||||
milliseconds = int(time // 1e3); time %= 1e3
|
milliseconds = int(time // 1e3); time %= 1e3
|
||||||
|
|
||||||
|
@ -280,7 +280,7 @@ def prettyshorttime(time, verbose=False, compact=False):
|
||||||
microseconds = int(time)
|
microseconds = int(time)
|
||||||
else:
|
else:
|
||||||
microseconds = round(time, 2)
|
microseconds = round(time, 2)
|
||||||
|
|
||||||
ss = "" if seconds == 1 else "s"
|
ss = "" if seconds == 1 else "s"
|
||||||
sms = "" if milliseconds == 1 else "s"
|
sms = "" if milliseconds == 1 else "s"
|
||||||
sus = "" if microseconds == 1 else "s"
|
sus = "" if microseconds == 1 else "s"
|
||||||
|
@ -365,16 +365,16 @@ def profiler(tag=None, capture=False, super_tag=None):
|
||||||
def profiler_results():
|
def profiler_results():
|
||||||
from statistics import mean, median, stdev
|
from statistics import mean, median, stdev
|
||||||
results = {}
|
results = {}
|
||||||
|
|
||||||
for tag in profiler_tags:
|
for tag in profiler_tags:
|
||||||
tag_captures = []
|
tag_captures = []
|
||||||
tag_entry = profiler_tags[tag]
|
tag_entry = profiler_tags[tag]
|
||||||
|
|
||||||
for thread_ident in tag_entry["threads"]:
|
for thread_ident in tag_entry["threads"]:
|
||||||
thread_entry = tag_entry["threads"][thread_ident]
|
thread_entry = tag_entry["threads"][thread_ident]
|
||||||
thread_captures = thread_entry["captures"]
|
thread_captures = thread_entry["captures"]
|
||||||
sample_count = len(thread_captures)
|
sample_count = len(thread_captures)
|
||||||
|
|
||||||
if sample_count > 2:
|
if sample_count > 2:
|
||||||
thread_results = {
|
thread_results = {
|
||||||
"count": sample_count,
|
"count": sample_count,
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
A modern asynchronous library for building I2P applications.
|
A modern asynchronous library for building I2P applications.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .__version__ import (
|
from .__version__ import (
|
||||||
|
@ -10,7 +10,7 @@ from .__version__ import (
|
||||||
from .sam import Destination, PrivateKey
|
from .sam import Destination, PrivateKey
|
||||||
|
|
||||||
from .aiosam import (
|
from .aiosam import (
|
||||||
get_sam_socket, dest_lookup, new_destination,
|
get_sam_socket, dest_lookup, new_destination,
|
||||||
create_session, stream_connect, stream_accept,
|
create_session, stream_connect, stream_accept,
|
||||||
Session, StreamConnection, StreamAcceptor
|
Session, StreamConnection, StreamAcceptor
|
||||||
)
|
)
|
||||||
|
|
|
@ -34,12 +34,12 @@ async def get_sam_socket(sam_address=sam.DEFAULT_ADDRESS, loop=None):
|
||||||
writer.close()
|
writer.close()
|
||||||
raise exceptions.SAM_EXCEPTIONS[reply["RESULT"]]()
|
raise exceptions.SAM_EXCEPTIONS[reply["RESULT"]]()
|
||||||
|
|
||||||
async def dest_lookup(domain, sam_address=sam.DEFAULT_ADDRESS,
|
async def dest_lookup(domain, sam_address=sam.DEFAULT_ADDRESS,
|
||||||
loop=None):
|
loop=None):
|
||||||
"""A coroutine used to lookup a full I2P destination by .i2p domain or
|
"""A coroutine used to lookup a full I2P destination by .i2p domain or
|
||||||
.b32.i2p address.
|
.b32.i2p address.
|
||||||
|
|
||||||
:param domain: Address to be resolved, can be a .i2p domain or a .b32.i2p
|
:param domain: Address to be resolved, can be a .i2p domain or a .b32.i2p
|
||||||
address.
|
address.
|
||||||
:param sam_address: (optional) SAM API address
|
:param sam_address: (optional) SAM API address
|
||||||
:param loop: (optional) Event loop instance
|
:param loop: (optional) Event loop instance
|
||||||
|
@ -56,7 +56,7 @@ async def dest_lookup(domain, sam_address=sam.DEFAULT_ADDRESS,
|
||||||
|
|
||||||
async def new_destination(sam_address=sam.DEFAULT_ADDRESS, loop=None,
|
async def new_destination(sam_address=sam.DEFAULT_ADDRESS, loop=None,
|
||||||
sig_type=sam.Destination.default_sig_type):
|
sig_type=sam.Destination.default_sig_type):
|
||||||
"""A coroutine used to generate a new destination with a private key of a
|
"""A coroutine used to generate a new destination with a private key of a
|
||||||
chosen signature type.
|
chosen signature type.
|
||||||
|
|
||||||
:param sam_address: (optional) SAM API address
|
:param sam_address: (optional) SAM API address
|
||||||
|
@ -70,7 +70,7 @@ async def new_destination(sam_address=sam.DEFAULT_ADDRESS, loop=None,
|
||||||
writer.close()
|
writer.close()
|
||||||
return sam.Destination(reply["PRIV"], has_private_key=True)
|
return sam.Destination(reply["PRIV"], has_private_key=True)
|
||||||
|
|
||||||
async def create_session(session_name, sam_address=sam.DEFAULT_ADDRESS,
|
async def create_session(session_name, sam_address=sam.DEFAULT_ADDRESS,
|
||||||
loop=None, style="STREAM",
|
loop=None, style="STREAM",
|
||||||
signature_type=sam.Destination.default_sig_type,
|
signature_type=sam.Destination.default_sig_type,
|
||||||
destination=None, options={}):
|
destination=None, options={}):
|
||||||
|
@ -80,10 +80,10 @@ async def create_session(session_name, sam_address=sam.DEFAULT_ADDRESS,
|
||||||
:param sam_address: (optional) SAM API address
|
:param sam_address: (optional) SAM API address
|
||||||
:param loop: (optional) Event loop instance
|
:param loop: (optional) Event loop instance
|
||||||
:param style: (optional) Session style, can be STREAM, DATAGRAM, RAW
|
:param style: (optional) Session style, can be STREAM, DATAGRAM, RAW
|
||||||
:param signature_type: (optional) If the destination is TRANSIENT, this
|
:param signature_type: (optional) If the destination is TRANSIENT, this
|
||||||
signature type is used
|
signature type is used
|
||||||
:param destination: (optional) Destination to use in this session. Can be
|
:param destination: (optional) Destination to use in this session. Can be
|
||||||
a base64 encoded string, :class:`Destination`
|
a base64 encoded string, :class:`Destination`
|
||||||
instance or None. TRANSIENT destination is used when it
|
instance or None. TRANSIENT destination is used when it
|
||||||
is None.
|
is None.
|
||||||
:param options: (optional) A dict object with i2cp options
|
:param options: (optional) A dict object with i2cp options
|
||||||
|
@ -111,7 +111,7 @@ async def create_session(session_name, sam_address=sam.DEFAULT_ADDRESS,
|
||||||
if reply.ok:
|
if reply.ok:
|
||||||
if not destination:
|
if not destination:
|
||||||
destination = sam.Destination(
|
destination = sam.Destination(
|
||||||
reply["DESTINATION"], has_private_key=True)
|
reply["DESTINATION"], has_private_key=True)
|
||||||
logger.debug(destination.base32)
|
logger.debug(destination.base32)
|
||||||
logger.debug(f"Session created {session_name}")
|
logger.debug(f"Session created {session_name}")
|
||||||
return (reader, writer)
|
return (reader, writer)
|
||||||
|
@ -119,7 +119,7 @@ async def create_session(session_name, sam_address=sam.DEFAULT_ADDRESS,
|
||||||
writer.close()
|
writer.close()
|
||||||
raise exceptions.SAM_EXCEPTIONS[reply["RESULT"]]()
|
raise exceptions.SAM_EXCEPTIONS[reply["RESULT"]]()
|
||||||
|
|
||||||
async def stream_connect(session_name, destination,
|
async def stream_connect(session_name, destination,
|
||||||
sam_address=sam.DEFAULT_ADDRESS, loop=None):
|
sam_address=sam.DEFAULT_ADDRESS, loop=None):
|
||||||
"""A coroutine used to connect to a remote I2P destination.
|
"""A coroutine used to connect to a remote I2P destination.
|
||||||
|
|
||||||
|
@ -173,16 +173,16 @@ class Session:
|
||||||
:param sam_address: (optional) SAM API address
|
:param sam_address: (optional) SAM API address
|
||||||
:param loop: (optional) Event loop instance
|
:param loop: (optional) Event loop instance
|
||||||
:param style: (optional) Session style, can be STREAM, DATAGRAM, RAW
|
:param style: (optional) Session style, can be STREAM, DATAGRAM, RAW
|
||||||
:param signature_type: (optional) If the destination is TRANSIENT, this
|
:param signature_type: (optional) If the destination is TRANSIENT, this
|
||||||
signature type is used
|
signature type is used
|
||||||
:param destination: (optional) Destination to use in this session. Can be
|
:param destination: (optional) Destination to use in this session. Can be
|
||||||
a base64 encoded string, :class:`Destination`
|
a base64 encoded string, :class:`Destination`
|
||||||
instance or None. TRANSIENT destination is used when it
|
instance or None. TRANSIENT destination is used when it
|
||||||
is None.
|
is None.
|
||||||
:param options: (optional) A dict object with i2cp options
|
:param options: (optional) A dict object with i2cp options
|
||||||
:return: :class:`Session` object
|
:return: :class:`Session` object
|
||||||
"""
|
"""
|
||||||
def __init__(self, session_name, sam_address=sam.DEFAULT_ADDRESS,
|
def __init__(self, session_name, sam_address=sam.DEFAULT_ADDRESS,
|
||||||
loop=None, style="STREAM",
|
loop=None, style="STREAM",
|
||||||
signature_type=sam.Destination.default_sig_type,
|
signature_type=sam.Destination.default_sig_type,
|
||||||
destination=None, options={}):
|
destination=None, options={}):
|
||||||
|
@ -195,9 +195,9 @@ class Session:
|
||||||
self.options = options
|
self.options = options
|
||||||
|
|
||||||
async def __aenter__(self):
|
async def __aenter__(self):
|
||||||
self.reader, self.writer = await create_session(self.session_name,
|
self.reader, self.writer = await create_session(self.session_name,
|
||||||
sam_address=self.sam_address, loop=self.loop, style=self.style,
|
sam_address=self.sam_address, loop=self.loop, style=self.style,
|
||||||
signature_type=self.signature_type,
|
signature_type=self.signature_type,
|
||||||
destination=self.destination, options=self.options)
|
destination=self.destination, options=self.options)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -214,7 +214,7 @@ class StreamConnection:
|
||||||
:param loop: (optional) Event loop instance
|
:param loop: (optional) Event loop instance
|
||||||
:return: :class:`StreamConnection` object
|
:return: :class:`StreamConnection` object
|
||||||
"""
|
"""
|
||||||
def __init__(self, session_name, destination,
|
def __init__(self, session_name, destination,
|
||||||
sam_address=sam.DEFAULT_ADDRESS, loop=None):
|
sam_address=sam.DEFAULT_ADDRESS, loop=None):
|
||||||
self.session_name = session_name
|
self.session_name = session_name
|
||||||
self.sam_address = sam_address
|
self.sam_address = sam_address
|
||||||
|
@ -222,7 +222,7 @@ class StreamConnection:
|
||||||
self.destination = destination
|
self.destination = destination
|
||||||
|
|
||||||
async def __aenter__(self):
|
async def __aenter__(self):
|
||||||
self.reader, self.writer = await stream_connect(self.session_name,
|
self.reader, self.writer = await stream_connect(self.session_name,
|
||||||
self.destination, sam_address=self.sam_address, loop=self.loop)
|
self.destination, sam_address=self.sam_address, loop=self.loop)
|
||||||
self.read = self.reader.read
|
self.read = self.reader.read
|
||||||
self.write = self.writer.write
|
self.write = self.writer.write
|
||||||
|
@ -240,14 +240,14 @@ class StreamAcceptor:
|
||||||
:param loop: (optional) Event loop instance
|
:param loop: (optional) Event loop instance
|
||||||
:return: :class:`StreamAcceptor` object
|
:return: :class:`StreamAcceptor` object
|
||||||
"""
|
"""
|
||||||
def __init__(self, session_name, sam_address=sam.DEFAULT_ADDRESS,
|
def __init__(self, session_name, sam_address=sam.DEFAULT_ADDRESS,
|
||||||
loop=None):
|
loop=None):
|
||||||
self.session_name = session_name
|
self.session_name = session_name
|
||||||
self.sam_address = sam_address
|
self.sam_address = sam_address
|
||||||
self.loop = loop
|
self.loop = loop
|
||||||
|
|
||||||
async def __aenter__(self):
|
async def __aenter__(self):
|
||||||
self.reader, self.writer = await stream_accept(self.session_name,
|
self.reader, self.writer = await stream_accept(self.session_name,
|
||||||
sam_address=self.sam_address, loop=self.loop)
|
sam_address=self.sam_address, loop=self.loop)
|
||||||
self.read = self.reader.read
|
self.read = self.reader.read
|
||||||
self.write = self.writer.write
|
self.write = self.writer.write
|
||||||
|
|
|
@ -8,7 +8,7 @@ I2P_B64_CHARS = "-~"
|
||||||
|
|
||||||
def i2p_b64encode(x):
|
def i2p_b64encode(x):
|
||||||
"""Encode I2P destination"""
|
"""Encode I2P destination"""
|
||||||
return b64encode(x, altchars=I2P_B64_CHARS.encode()).decode()
|
return b64encode(x, altchars=I2P_B64_CHARS.encode()).decode()
|
||||||
|
|
||||||
def i2p_b64decode(x):
|
def i2p_b64decode(x):
|
||||||
"""Decode I2P destination"""
|
"""Decode I2P destination"""
|
||||||
|
@ -79,9 +79,9 @@ class Destination:
|
||||||
|
|
||||||
https://geti2p.net/spec/common-structures#destination
|
https://geti2p.net/spec/common-structures#destination
|
||||||
|
|
||||||
:param data: (optional) Base64 encoded data or binary data
|
:param data: (optional) Base64 encoded data or binary data
|
||||||
:param path: (optional) A path to a file with binary data
|
:param path: (optional) A path to a file with binary data
|
||||||
:param has_private_key: (optional) Does data have a private key?
|
:param has_private_key: (optional) Does data have a private key?
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ECDSA_SHA256_P256 = 1
|
ECDSA_SHA256_P256 = 1
|
||||||
|
@ -97,12 +97,12 @@ class Destination:
|
||||||
|
|
||||||
def __init__(self, data=None, path=None, has_private_key=False):
|
def __init__(self, data=None, path=None, has_private_key=False):
|
||||||
#: Binary destination
|
#: Binary destination
|
||||||
self.data = b''
|
self.data = b''
|
||||||
#: Base64 encoded destination
|
#: Base64 encoded destination
|
||||||
self.base64 = ""
|
self.base64 = ""
|
||||||
#: :class:`RNS.vendor.i2plib.PrivateKey` instance or None
|
#: :class:`RNS.vendor.i2plib.PrivateKey` instance or None
|
||||||
self.private_key = None
|
self.private_key = None
|
||||||
|
|
||||||
if path:
|
if path:
|
||||||
with open(path, "rb") as f: data = f.read()
|
with open(path, "rb") as f: data = f.read()
|
||||||
|
|
||||||
|
@ -126,13 +126,13 @@ class Destination:
|
||||||
"""Base32 destination hash of this destination"""
|
"""Base32 destination hash of this destination"""
|
||||||
desthash = sha256(self.data).digest()
|
desthash = sha256(self.data).digest()
|
||||||
return b32encode(desthash).decode()[:52].lower()
|
return b32encode(desthash).decode()[:52].lower()
|
||||||
|
|
||||||
class PrivateKey:
|
class PrivateKey:
|
||||||
"""I2P private key
|
"""I2P private key
|
||||||
|
|
||||||
https://geti2p.net/spec/common-structures#keysandcert
|
https://geti2p.net/spec/common-structures#keysandcert
|
||||||
|
|
||||||
:param data: Base64 encoded data or binary data
|
:param data: Base64 encoded data or binary data
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
|
|
|
@ -2,7 +2,7 @@ import logging
|
||||||
import asyncio
|
import asyncio
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
from . import sam
|
from . import sam
|
||||||
from . import aiosam
|
from . import aiosam
|
||||||
from . import utils
|
from . import utils
|
||||||
from .log import logger
|
from .log import logger
|
||||||
|
@ -29,9 +29,9 @@ async def proxy_data(reader, writer):
|
||||||
class I2PTunnel:
|
class I2PTunnel:
|
||||||
"""Base I2P Tunnel object, not to be used directly
|
"""Base I2P Tunnel object, not to be used directly
|
||||||
|
|
||||||
:param local_address: A local address to use for a tunnel.
|
:param local_address: A local address to use for a tunnel.
|
||||||
E.g. ("127.0.0.1", 6668)
|
E.g. ("127.0.0.1", 6668)
|
||||||
:param destination: (optional) Destination to use for this tunnel. Can be
|
:param destination: (optional) Destination to use for this tunnel. Can be
|
||||||
a base64 encoded string, :class:`Destination`
|
a base64 encoded string, :class:`Destination`
|
||||||
instance or None. A new destination is created when it
|
instance or None. A new destination is created when it
|
||||||
is None.
|
is None.
|
||||||
|
@ -42,7 +42,7 @@ class I2PTunnel:
|
||||||
:param sam_address: (optional) SAM API address
|
:param sam_address: (optional) SAM API address
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, local_address, destination=None, session_name=None,
|
def __init__(self, local_address, destination=None, session_name=None,
|
||||||
options={}, loop=None, sam_address=sam.DEFAULT_ADDRESS):
|
options={}, loop=None, sam_address=sam.DEFAULT_ADDRESS):
|
||||||
self.local_address = local_address
|
self.local_address = local_address
|
||||||
self.destination = destination
|
self.destination = destination
|
||||||
|
@ -57,7 +57,7 @@ class I2PTunnel:
|
||||||
sam_address=self.sam_address, loop=self.loop)
|
sam_address=self.sam_address, loop=self.loop)
|
||||||
_, self.session_writer = await aiosam.create_session(
|
_, self.session_writer = await aiosam.create_session(
|
||||||
self.session_name, style=self.style, options=self.options,
|
self.session_name, style=self.style, options=self.options,
|
||||||
sam_address=self.sam_address,
|
sam_address=self.sam_address,
|
||||||
loop=self.loop, destination=self.destination)
|
loop=self.loop, destination=self.destination)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
@ -68,11 +68,11 @@ class ClientTunnel(I2PTunnel):
|
||||||
"""Client tunnel, a subclass of tunnel.I2PTunnel
|
"""Client tunnel, a subclass of tunnel.I2PTunnel
|
||||||
|
|
||||||
If you run a client tunnel with a local address ("127.0.0.1", 6668) and
|
If you run a client tunnel with a local address ("127.0.0.1", 6668) and
|
||||||
a remote destination "irc.echelon.i2p", all connections to 127.0.0.1:6668
|
a remote destination "irc.echelon.i2p", all connections to 127.0.0.1:6668
|
||||||
will be proxied to irc.echelon.i2p.
|
will be proxied to irc.echelon.i2p.
|
||||||
|
|
||||||
:param remote_destination: Remote I2P destination, can be either .i2p
|
:param remote_destination: Remote I2P destination, can be either .i2p
|
||||||
domain, .b32.i2p address, base64 destination or
|
domain, .b32.i2p address, base64 destination or
|
||||||
:class:`Destination` instance
|
:class:`Destination` instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -90,12 +90,12 @@ class ClientTunnel(I2PTunnel):
|
||||||
"""Handle local client connection"""
|
"""Handle local client connection"""
|
||||||
try:
|
try:
|
||||||
sc_task = aiosam.stream_connect(
|
sc_task = aiosam.stream_connect(
|
||||||
self.session_name, self.remote_destination,
|
self.session_name, self.remote_destination,
|
||||||
sam_address=self.sam_address, loop=self.loop)
|
sam_address=self.sam_address, loop=self.loop)
|
||||||
self.status["connect_tasks"].append(sc_task)
|
self.status["connect_tasks"].append(sc_task)
|
||||||
|
|
||||||
remote_reader, remote_writer = await sc_task
|
remote_reader, remote_writer = await sc_task
|
||||||
asyncio.ensure_future(proxy_data(remote_reader, client_writer),
|
asyncio.ensure_future(proxy_data(remote_reader, client_writer),
|
||||||
loop=self.loop)
|
loop=self.loop)
|
||||||
asyncio.ensure_future(proxy_data(client_reader, remote_writer),
|
asyncio.ensure_future(proxy_data(client_reader, remote_writer),
|
||||||
loop=self.loop)
|
loop=self.loop)
|
||||||
|
@ -123,8 +123,8 @@ class ServerTunnel(I2PTunnel):
|
||||||
"""Server tunnel, a subclass of tunnel.I2PTunnel
|
"""Server tunnel, a subclass of tunnel.I2PTunnel
|
||||||
|
|
||||||
If you want to expose a local service 127.0.0.1:80 to the I2P network, run
|
If you want to expose a local service 127.0.0.1:80 to the I2P network, run
|
||||||
a server tunnel with a local address ("127.0.0.1", 80). If you don't
|
a server tunnel with a local address ("127.0.0.1", 80). If you don't
|
||||||
provide a private key or a session name, it will use a TRANSIENT
|
provide a private key or a session name, it will use a TRANSIENT
|
||||||
destination.
|
destination.
|
||||||
"""
|
"""
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -139,7 +139,7 @@ class ServerTunnel(I2PTunnel):
|
||||||
async def handle_client(incoming, client_reader, client_writer):
|
async def handle_client(incoming, client_reader, client_writer):
|
||||||
try:
|
try:
|
||||||
# data and dest may come in one chunk
|
# data and dest may come in one chunk
|
||||||
dest, data = incoming.split(b"\n", 1)
|
dest, data = incoming.split(b"\n", 1)
|
||||||
remote_destination = sam.Destination(dest.decode())
|
remote_destination = sam.Destination(dest.decode())
|
||||||
logger.debug(f"{self.session_name} client connected: {remote_destination.base32}.b32.i2p")
|
logger.debug(f"{self.session_name} client connected: {remote_destination.base32}.b32.i2p")
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ class ServerTunnel(I2PTunnel):
|
||||||
try:
|
try:
|
||||||
sc_task = asyncio.wait_for(
|
sc_task = asyncio.wait_for(
|
||||||
asyncio.open_connection(
|
asyncio.open_connection(
|
||||||
host=self.local_address[0],
|
host=self.local_address[0],
|
||||||
port=self.local_address[1]),
|
port=self.local_address[1]),
|
||||||
timeout=5)
|
timeout=5)
|
||||||
self.status["connect_tasks"].append(sc_task)
|
self.status["connect_tasks"].append(sc_task)
|
||||||
|
@ -172,7 +172,7 @@ class ServerTunnel(I2PTunnel):
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
client_reader, client_writer = await aiosam.stream_accept(
|
client_reader, client_writer = await aiosam.stream_accept(
|
||||||
self.session_name, sam_address=self.sam_address,
|
self.session_name, sam_address=self.sam_address,
|
||||||
loop=self.loop)
|
loop=self.loop)
|
||||||
incoming = await client_reader.read(BUFFER_SIZE)
|
incoming = await client_reader.read(BUFFER_SIZE)
|
||||||
asyncio.ensure_future(handle_client(
|
asyncio.ensure_future(handle_client(
|
||||||
|
@ -192,13 +192,13 @@ if __name__ == '__main__':
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('type', metavar="TYPE", choices=('server', 'client'),
|
parser.add_argument('type', metavar="TYPE", choices=('server', 'client'),
|
||||||
help="Tunnel type (server or client)")
|
help="Tunnel type (server or client)")
|
||||||
parser.add_argument('address', metavar="ADDRESS",
|
parser.add_argument('address', metavar="ADDRESS",
|
||||||
help="Local address (e.g. 127.0.0.1:8000)")
|
help="Local address (e.g. 127.0.0.1:8000)")
|
||||||
parser.add_argument('--debug', '-d', action='store_true',
|
parser.add_argument('--debug', '-d', action='store_true',
|
||||||
help='Debugging')
|
help='Debugging')
|
||||||
parser.add_argument('--key', '-k', default='', metavar='PRIVATE_KEY',
|
parser.add_argument('--key', '-k', default='', metavar='PRIVATE_KEY',
|
||||||
help='Path to private key file')
|
help='Path to private key file')
|
||||||
parser.add_argument('--destination', '-D', default='',
|
parser.add_argument('--destination', '-D', default='',
|
||||||
metavar='DESTINATION', help='Remote destination')
|
metavar='DESTINATION', help='Remote destination')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
@ -216,10 +216,10 @@ if __name__ == '__main__':
|
||||||
local_address = utils.address_from_string(args.address)
|
local_address = utils.address_from_string(args.address)
|
||||||
|
|
||||||
if args.type == "client":
|
if args.type == "client":
|
||||||
tunnel = ClientTunnel(args.destination, local_address, loop=loop,
|
tunnel = ClientTunnel(args.destination, local_address, loop=loop,
|
||||||
destination=destination, sam_address=SAM_ADDRESS)
|
destination=destination, sam_address=SAM_ADDRESS)
|
||||||
elif args.type == "server":
|
elif args.type == "server":
|
||||||
tunnel = ServerTunnel(local_address, loop=loop, destination=destination,
|
tunnel = ServerTunnel(local_address, loop=loop, destination=destination,
|
||||||
sam_address=SAM_ADDRESS)
|
sam_address=SAM_ADDRESS)
|
||||||
|
|
||||||
asyncio.ensure_future(tunnel.run(), loop=loop)
|
asyncio.ensure_future(tunnel.run(), loop=loop)
|
||||||
|
|
|
@ -62,7 +62,7 @@ class Packet:
|
||||||
|
|
||||||
def set_delivered_callback(self, callback: Callable[[Packet], None]):
|
def set_delivered_callback(self, callback: Callable[[Packet], None]):
|
||||||
self.delivered_callback = callback
|
self.delivered_callback = callback
|
||||||
|
|
||||||
def delivered(self):
|
def delivered(self):
|
||||||
with self.lock:
|
with self.lock:
|
||||||
self.state = MessageState.MSGSTATE_DELIVERED
|
self.state = MessageState.MSGSTATE_DELIVERED
|
||||||
|
|
|
@ -187,7 +187,7 @@ class TestIdentity(unittest.TestCase):
|
||||||
lb = 1
|
lb = 1
|
||||||
else:
|
else:
|
||||||
lb = 8
|
lb = 8
|
||||||
|
|
||||||
for i in range(1, lb):
|
for i in range(1, lb):
|
||||||
msg = os.urandom(mlen)
|
msg = os.urandom(mlen)
|
||||||
b += mlen
|
b += mlen
|
||||||
|
|
|
@ -105,7 +105,7 @@ class TestLink(unittest.TestCase):
|
||||||
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
||||||
|
|
||||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
|
||||||
l1 = RNS.Link(dest)
|
l1 = RNS.Link(dest)
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
self.assertEqual(l1.status, RNS.Link.ACTIVE)
|
self.assertEqual(l1.status, RNS.Link.ACTIVE)
|
||||||
|
@ -129,7 +129,7 @@ class TestLink(unittest.TestCase):
|
||||||
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
||||||
|
|
||||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
|
||||||
l1 = RNS.Link(dest)
|
l1 = RNS.Link(dest)
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
self.assertEqual(l1.status, RNS.Link.ACTIVE)
|
self.assertEqual(l1.status, RNS.Link.ACTIVE)
|
||||||
|
@ -176,7 +176,7 @@ class TestLink(unittest.TestCase):
|
||||||
if n_failed > 0:
|
if n_failed > 0:
|
||||||
ns = "s" if n_failed != 1 else ""
|
ns = "s" if n_failed != 1 else ""
|
||||||
print(f"Failed to receive proof for {n_failed} packet{ns}")
|
print(f"Failed to receive proof for {n_failed} packet{ns}")
|
||||||
|
|
||||||
self.assertEqual(all_ok, True)
|
self.assertEqual(all_ok, True)
|
||||||
print("OK!")
|
print("OK!")
|
||||||
print(f"Single packet and proof round-trip throughput is {self.size_str(b / pduration, 'b')}ps")
|
print(f"Single packet and proof round-trip throughput is {self.size_str(b / pduration, 'b')}ps")
|
||||||
|
@ -201,7 +201,7 @@ class TestLink(unittest.TestCase):
|
||||||
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
||||||
|
|
||||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
|
||||||
l1 = RNS.Link(dest)
|
l1 = RNS.Link(dest)
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
self.assertEqual(l1.status, RNS.Link.ACTIVE)
|
self.assertEqual(l1.status, RNS.Link.ACTIVE)
|
||||||
|
@ -241,7 +241,7 @@ class TestLink(unittest.TestCase):
|
||||||
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
||||||
|
|
||||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
|
||||||
l1 = RNS.Link(dest)
|
l1 = RNS.Link(dest)
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
self.assertEqual(l1.status, RNS.Link.ACTIVE)
|
self.assertEqual(l1.status, RNS.Link.ACTIVE)
|
||||||
|
@ -280,7 +280,7 @@ class TestLink(unittest.TestCase):
|
||||||
|
|
||||||
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
||||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
|
||||||
l1 = RNS.Link(dest)
|
l1 = RNS.Link(dest)
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
self.assertEqual(l1.status, RNS.Link.ACTIVE)
|
self.assertEqual(l1.status, RNS.Link.ACTIVE)
|
||||||
|
@ -310,7 +310,7 @@ class TestLink(unittest.TestCase):
|
||||||
if RNS.Cryptography.backend() == "internal":
|
if RNS.Cryptography.backend() == "internal":
|
||||||
print("Skipping medium resource test...")
|
print("Skipping medium resource test...")
|
||||||
return
|
return
|
||||||
|
|
||||||
init_rns(self)
|
init_rns(self)
|
||||||
print("")
|
print("")
|
||||||
print("Medium resource test")
|
print("Medium resource test")
|
||||||
|
@ -324,7 +324,7 @@ class TestLink(unittest.TestCase):
|
||||||
|
|
||||||
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
||||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
|
||||||
l1 = RNS.Link(dest)
|
l1 = RNS.Link(dest)
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
self.assertEqual(l1.status, RNS.Link.ACTIVE)
|
self.assertEqual(l1.status, RNS.Link.ACTIVE)
|
||||||
|
@ -371,7 +371,7 @@ class TestLink(unittest.TestCase):
|
||||||
|
|
||||||
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
|
||||||
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
|
||||||
|
|
||||||
l1 = RNS.Link(dest)
|
l1 = RNS.Link(dest)
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
self.assertEqual(l1.status, RNS.Link.ACTIVE)
|
self.assertEqual(l1.status, RNS.Link.ACTIVE)
|
||||||
|
@ -553,11 +553,11 @@ class TestLink(unittest.TestCase):
|
||||||
target_bytes = 3000
|
target_bytes = 3000
|
||||||
else:
|
else:
|
||||||
target_bytes = BUFFER_TEST_TARGET
|
target_bytes = BUFFER_TEST_TARGET
|
||||||
|
|
||||||
random.seed(154889)
|
random.seed(154889)
|
||||||
message = random.randbytes(target_bytes)
|
message = random.randbytes(target_bytes)
|
||||||
buffer_read_target = len(message)
|
buffer_read_target = len(message)
|
||||||
|
|
||||||
# the return message will have an appendage string " back at you"
|
# the return message will have an appendage string " back at you"
|
||||||
# for every StreamDataMessage that arrives. To verify, we need
|
# for every StreamDataMessage that arrives. To verify, we need
|
||||||
# to insert that string every MAX_DATA_LEN and also at the end.
|
# to insert that string every MAX_DATA_LEN and also at the end.
|
||||||
|
@ -572,7 +572,7 @@ class TestLink(unittest.TestCase):
|
||||||
# StreamDataMessage, the appended text will end up in a
|
# StreamDataMessage, the appended text will end up in a
|
||||||
# separate packet.
|
# separate packet.
|
||||||
print(f"Sending {len(message)} bytes, receiving {len(expected_rx_message)} bytes, ")
|
print(f"Sending {len(message)} bytes, receiving {len(expected_rx_message)} bytes, ")
|
||||||
|
|
||||||
buffer.write(message)
|
buffer.write(message)
|
||||||
buffer.flush()
|
buffer.flush()
|
||||||
|
|
||||||
|
@ -723,7 +723,7 @@ def profile_resource():
|
||||||
resource_profiling()
|
resource_profiling()
|
||||||
|
|
||||||
def profile_targets():
|
def profile_targets():
|
||||||
|
|
||||||
targets_profiling(yp=True)
|
targets_profiling(yp=True)
|
||||||
# cProfile.runctx("entry()", {"entry": targets_profiling, "size_str": size_str}, {}, "profile-targets.data")
|
# cProfile.runctx("entry()", {"entry": targets_profiling, "size_str": size_str}, {}, "profile-targets.data")
|
||||||
# p = pstats.Stats("profile-targets.data")
|
# p = pstats.Stats("profile-targets.data")
|
||||||
|
@ -749,7 +749,7 @@ def resource_profiling():
|
||||||
|
|
||||||
import yappi
|
import yappi
|
||||||
yappi.start()
|
yappi.start()
|
||||||
|
|
||||||
resource = RNS.Resource(data, l1, timeout=resource_timeout)
|
resource = RNS.Resource(data, l1, timeout=resource_timeout)
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue