Compare commits

...

13 Commits

Author SHA1 Message Date
Pavol Rusnak
06b9555462
remove dangling spaces 2024-10-11 23:14:32 +02:00
Pavol Rusnak
ce9dc56048
modernize 2024-10-11 23:13:52 +02:00
Mark Qvist
30248854ed Updated changelog 2024-10-11 17:13:03 +02:00
Mark Qvist
f34bc75588 Updated docs 2024-10-11 16:47:53 +02:00
Mark Qvist
3b23e2f37d Improved RNode BLE reconnection reliability 2024-10-11 13:38:16 +02:00
Mark Qvist
7417cf5947 Add rnode battery state to rnstatus output 2024-10-11 10:14:10 +02:00
Mark Qvist
60d8da843c Disable tty module dependency for rnx, since it is currently unused 2024-10-11 09:54:09 +02:00
Mark Qvist
f9667fd684 Fixed missing import on Android 2024-10-10 23:49:20 +02:00
Mark Qvist
d9269c6047 Updated version 2024-10-10 23:32:09 +02:00
Mark Qvist
6521f839cd Fixed resource transfers hanging for a long time over slow links if proof packet is lost 2024-10-10 17:06:43 +02:00
Mark Qvist
d63bbcdc0a Updated changelog 2024-10-10 00:45:09 +02:00
Mark Qvist
c36c7186de Updated docs 2024-10-10 00:44:33 +02:00
Mark Qvist
6fec76205c Added save directory option to rncp 2024-10-10 00:41:57 +02:00
104 changed files with 2373 additions and 2316 deletions

View File

@ -1,3 +1,36 @@
### 2024-10-11: RNS β 0.8.4
This release fixes a number of bugs and improves reliability of automatic reconnection when BLE-connected RNodes unexpectedly disappear or lose connection.
**Changes**
- Improved RNode BLE reconnection realiability
- Added RNode battery state to `rnstatus` output
- Fixed resource transfer hanging for a long time over slow links if proof packet is lost
- Fixed missing import on Android
**Release Hashes**
```
d3f7a9fddc6c1e59b1e4895756fe602408ac6ef09de377ee65ec62d09fff97a3 dist/rns-0.8.4-py3-none-any.whl
eb3843bcab1428be0adb097988991229a4c03156ab40cc9c6e2d9c590d8b850b dist/rnspure-0.8.4-py3-none-any.whl
```
### 2024-10-10: RNS β 0.8.3
This release fixes a bug in resource transfer progress calculation, improves RNode error handling, and brings minor improvements to the `rncp` utility.
**Changes**
- Fixed a bug in resource transfer progress calculations
- Added physical layer transfer rate output option to `rncp`
- Added save directory option to `rncp`
- Improved path handling for the fetch-jail option of of `rncp`
- Added error detection for modem communication timeouts on connected RNode devices
**Release Hashes**
```
54ddab32769081045db5fe45b27492cc012bf2fad64bc65ed37011f3651469fb rns-0.8.3-py3-none-any.whl
a04915111d65b05a5f2ef2687ed208813034196c0c5e711cb01e6db72faa23ef rnspure-0.8.3-py3-none-any.whl
```
### 2024-10-06: RNS β 0.8.2
This release adds several new boards to `rnodeconf`, fixes a range of bugs and improves transport reliability.
@ -1214,7 +1247,7 @@ This beta release brings a range of improvements and bugfixes.
- Improved documentation.
- Improved request timeouts and handling.
- Improved link establishment.
- Improved resource transfer timing.
- Improved resource transfer timing.
**Fixed bugs**
- Fixed a race condition in inbound proof handling.

View File

@ -22,7 +22,7 @@ noble_gases = ["Helium", "Neon", "Argon", "Krypton", "Xenon", "Radon", "Oganesso
def program_setup(configpath):
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# Randomly create a new identity for our example
identity = RNS.Identity()
@ -70,7 +70,7 @@ def program_setup(configpath):
# We register the announce handler with Reticulum
RNS.Transport.register_announce_handler(announce_handler)
# Everything's ready!
# Let's hand over control to the announce loop
announceLoop(destination_1, destination_2)
@ -86,16 +86,14 @@ def announceLoop(destination_1, destination_2):
# know how to create messages directed towards it.
while True:
entered = input()
# Randomly select a fruit
fruit = fruits[random.randint(0,len(fruits)-1)]
# Send the announce including the app data
destination_1.announce(app_data=fruit.encode("utf-8"))
RNS.log(
"Sent announce from "+
RNS.prettyhexrep(destination_1.hash)+
" ("+destination_1.name+")"
f"Sent announce from {RNS.prettyhexrep(destination_1.hash)} ({destination_1.name})"
)
# Randomly select a noble gas
@ -104,9 +102,7 @@ def announceLoop(destination_1, destination_2):
# Send the announce including the app data
destination_2.announce(app_data=noble_gas.encode("utf-8"))
RNS.log(
"Sent announce from "+
RNS.prettyhexrep(destination_2.hash)+
" ("+destination_2.name+")"
f"Sent announce from {RNS.prettyhexrep(destination_2.hash)} ({destination_2.name})"
)
# We will need to define an announce handler class that
@ -126,14 +122,12 @@ class ExampleAnnounceHandler:
# and cannot use wildcards.
def received_announce(self, destination_hash, announced_identity, app_data):
RNS.log(
"Received an announce from "+
RNS.prettyhexrep(destination_hash)
f"Received an announce from {RNS.prettyhexrep(destination_hash)}"
)
if app_data:
RNS.log(
"The announce contained the following app data: "+
app_data.decode("utf-8")
f"The announce contained the following app data: {app_data.decode('utf-8')}"
)
##########################################################

View File

@ -17,7 +17,7 @@ APP_NAME = "example_utilities"
def program_setup(configpath, channel=None):
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# If the user did not select a "channel" we use
# a default one called "public_information".
# 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
# the destination receives data.
broadcast_destination.set_packet_callback(packet_callback)
# Everything's ready!
# Let's hand over control to the main loop
broadcastLoop(broadcast_destination)
@ -48,15 +48,13 @@ def program_setup(configpath, channel=None):
def packet_callback(data, packet):
# Simply print out the received data
print("")
print("Received data: "+data.decode("utf-8")+"\r\n> ", end="")
print(f"Received data: {data.decode('utf-8')}\r\n> ", end="")
sys.stdout.flush()
def broadcastLoop(destination):
# Let the user know that everything is ready
RNS.log(
"Broadcast example "+
RNS.prettyhexrep(destination.hash)+
" running, enter text and hit enter to broadcast (Ctrl-C to quit)"
f"Broadcast example {RNS.prettyhexrep(destination.hash)} running, enter text and hit enter to broadcast (Ctrl-C to quit)"
)
# We enter a loop that runs until the users exits.

View File

@ -35,7 +35,7 @@ latest_buffer = None
def server(configpath):
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# Randomly create a new identity for our example
server_identity = RNS.Identity()
@ -61,9 +61,7 @@ def server(configpath):
def server_loop(destination):
# Let the user know that everything is ready
RNS.log(
"Link buffer example "+
RNS.prettyhexrep(destination.hash)+
" running, waiting for a connection."
f"Link buffer example {RNS.prettyhexrep(destination.hash)} running, waiting for a connection."
)
RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
@ -75,7 +73,7 @@ def server_loop(destination):
while True:
entered = input()
destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
RNS.log(f"Sent announce from {RNS.prettyhexrep(destination.hash)}")
# When a client establishes a link to our server
# destination, this function will be called with
@ -120,9 +118,9 @@ def server_buffer_ready(ready_bytes: int):
data = latest_buffer.read(ready_bytes)
data = data.decode("utf-8")
RNS.log("Received data over the buffer: " + data)
RNS.log(f"Received data over the buffer: {data}")
reply_message = "I received \""+data+"\" over the buffer"
reply_message = f"I received \"{data}\" over the buffer"
reply_message = reply_message.encode("utf-8")
latest_buffer.write(reply_message)
latest_buffer.flush()
@ -151,9 +149,9 @@ def client(destination_hexhash, configpath):
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len:
raise ValueError(
"Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
)
destination_hash = bytes.fromhex(destination_hexhash)
except:
RNS.log("Invalid destination entered. Check your input!\n")
@ -223,7 +221,7 @@ def client_loop():
except Exception as e:
RNS.log("Error while sending data over the link buffer: "+str(e))
RNS.log(f"Error while sending data over the link buffer: {e}")
should_quit = True
server_link.teardown()
@ -253,7 +251,7 @@ def link_closed(link):
RNS.log("The link was closed by the server, exiting now")
else:
RNS.log("Link closed, exiting now")
RNS.Reticulum.exit_handler()
time.sleep(1.5)
os._exit(0)
@ -262,7 +260,7 @@ def link_closed(link):
def client_buffer_ready(ready_bytes: int):
global buffer
data = buffer.read(ready_bytes)
RNS.log("Received data over the link buffer: " + data.decode("utf-8"))
RNS.log(f"Received data over the link buffer: {data.decode('utf-8')}")
print("> ", end=" ")
sys.stdout.flush()

View File

@ -98,7 +98,7 @@ latest_client_link = None
def server(configpath):
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# Randomly create a new identity for our link example
server_identity = RNS.Identity()
@ -124,9 +124,7 @@ def server(configpath):
def server_loop(destination):
# Let the user know that everything is ready
RNS.log(
"Link example "+
RNS.prettyhexrep(destination.hash)+
" running, waiting for a connection."
f"Link example {RNS.prettyhexrep(destination.hash)} running, waiting for a connection."
)
RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
@ -138,7 +136,7 @@ def server_loop(destination):
while True:
entered = input()
destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
RNS.log(f"Sent announce from {RNS.prettyhexrep(destination.hash)}")
# When a client establishes a link to our server
# destination, this function will be called with
@ -176,9 +174,9 @@ def server_message_received(message):
#
#
if isinstance(message, StringMessage):
RNS.log("Received data on the link: " + message.data + " (message created at " + str(message.timestamp) + ")")
RNS.log(f"Received data on the link: {message.data} (message created at {message.timestamp})")
reply_message = StringMessage("I received \""+message.data+"\" over the link")
reply_message = StringMessage(f"I received \"{message.data}\" over the link")
latest_client_link.get_channel().send(reply_message)
# Incoming messages are sent to each message
@ -206,9 +204,9 @@ def client(destination_hexhash, configpath):
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len:
raise ValueError(
"Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
)
destination_hash = bytes.fromhex(destination_hexhash)
except:
RNS.log("Invalid destination entered. Check your input!\n")
@ -280,17 +278,14 @@ def client_loop():
channel.send(message)
else:
RNS.log(
"Cannot send this packet, the data size of "+
str(packed_size)+" bytes exceeds the link packet MDU of "+
str(channel.MDU)+" bytes",
f"Cannot send this packet, the data size of {packed_size} bytes exceeds the link packet MDU of {channel.MDU} bytes",
RNS.LOG_ERROR
)
else:
RNS.log("Channel is not ready to send, please wait for " +
"pending messages to complete.", RNS.LOG_ERROR)
RNS.log(f"Channel is not ready to send, please wait for pending messages to complete.", RNS.LOG_ERROR)
except Exception as e:
RNS.log("Error while sending data over the link: "+str(e))
RNS.log(f"Error while sending data over the link: {e}")
should_quit = True
server_link.teardown()
@ -320,7 +315,7 @@ def link_closed(link):
RNS.log("The link was closed by the server, exiting now")
else:
RNS.log("Link closed, exiting now")
RNS.Reticulum.exit_handler()
time.sleep(1.5)
os._exit(0)
@ -329,7 +324,7 @@ def link_closed(link):
# simply print out the data.
def client_message_received(message):
if isinstance(message, StringMessage):
RNS.log("Received data on the link: " + message.data + " (message created at " + str(message.timestamp) + ")")
RNS.log(f"Received data on the link: {message.data} (message created at {message.timestamp})")
print("> ", end=" ")
sys.stdout.flush()

View File

@ -26,7 +26,7 @@ def server(configpath):
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# Randomly create a new identity for our echo server
server_identity = RNS.Identity()
@ -35,7 +35,7 @@ def server(configpath):
# create a "single" destination that can receive encrypted
# messages. This way the client can send a request and be
# certain that no-one else than this destination was able
# to read it.
# to read it.
echo_destination = RNS.Destination(
server_identity,
RNS.Destination.IN,
@ -50,7 +50,7 @@ def server(configpath):
# generate a proof for each incoming packet and transmit it
# back to the sender of that packet.
echo_destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
# Tell the destination which function in our program to
# run when a packet is received. We do this so we can
# print a log message when the server receives a request
@ -64,9 +64,7 @@ def server(configpath):
def announceLoop(destination):
# Let the user know that everything is ready
RNS.log(
"Echo server "+
RNS.prettyhexrep(destination.hash)+
" running, hit enter to manually send an announce (Ctrl-C to quit)"
f"Echo server {RNS.prettyhexrep(destination.hash)} running, hit enter to manually send an announce (Ctrl-C to quit)"
)
# We enter a loop that runs until the users exits.
@ -76,12 +74,12 @@ def announceLoop(destination):
while True:
entered = input()
destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
RNS.log(f"Sent announce from {RNS.prettyhexrep(destination.hash)}")
def server_callback(message, packet):
global reticulum
# Tell the user that we received an echo request, and
# that we are going to send a reply to the requester.
# Sending the proof is handled automatically, since we
@ -93,19 +91,19 @@ def server_callback(message, packet):
reception_snr = reticulum.get_packet_snr(packet.packet_hash)
if reception_rssi != None:
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]"
reception_stats += f" [RSSI {reception_rssi} dBm]"
if reception_snr != None:
reception_stats += " [SNR "+str(reception_snr)+" dBm]"
reception_stats += f" [SNR {reception_snr} dBm]"
else:
if packet.rssi != None:
reception_stats += " [RSSI "+str(packet.rssi)+" dBm]"
if packet.snr != None:
reception_stats += " [SNR "+str(packet.snr)+" dB]"
reception_stats += f" [RSSI {packet.rssi} dBm]"
RNS.log("Received packet from echo client, proof sent"+reception_stats)
if packet.snr != None:
reception_stats += f" [SNR {packet.snr} dB]"
RNS.log(f"Received packet from echo client, proof sent{reception_stats}")
##########################################################
@ -116,20 +114,20 @@ def server_callback(message, packet):
# to run as a client
def client(destination_hexhash, configpath, timeout=None):
global reticulum
# We need a binary representation of the destination
# hash that was entered on the command line
try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len:
raise ValueError(
"Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
)
destination_hash = bytes.fromhex(destination_hexhash)
except Exception as e:
RNS.log("Invalid destination entered. Check your input!")
RNS.log(str(e)+"\n")
RNS.log(f"{e}\n")
exit()
# We must first initialise Reticulum
@ -142,9 +140,7 @@ def client(destination_hexhash, configpath, timeout=None):
# Tell the user that the client is ready!
RNS.log(
"Echo client ready, hit enter to send echo request to "+
destination_hexhash+
" (Ctrl-C to quit)"
f"Echo client ready, hit enter to send echo request to {destination_hexhash} (Ctrl-C to quit)"
)
# We enter a loop that runs until the user exits.
@ -153,7 +149,7 @@ def client(destination_hexhash, configpath, timeout=None):
# command line.
while True:
input()
# 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 RNS.Transport.has_path(destination_hash):
@ -205,7 +201,7 @@ def client(destination_hexhash, configpath, timeout=None):
packet_receipt.set_delivery_callback(packet_delivered)
# Tell the user that the echo request was sent
RNS.log("Sent echo request to "+RNS.prettyhexrep(request_destination.hash))
RNS.log(f"Sent echo request to {RNS.prettyhexrep(request_destination.hash)}")
else:
# If we do not know this destination, tell the
# user to wait for an announce to arrive.
@ -222,10 +218,10 @@ def packet_delivered(receipt):
rtt = receipt.get_rtt()
if (rtt >= 1):
rtt = round(rtt, 3)
rttstring = str(rtt)+" seconds"
rttstring = f"{rtt} seconds"
else:
rtt = round(rtt*1000, 3)
rttstring = str(rtt)+" milliseconds"
rttstring = f"{rtt} milliseconds"
reception_stats = ""
if reticulum.is_connected_to_shared_instance:
@ -233,30 +229,27 @@ def packet_delivered(receipt):
reception_snr = reticulum.get_packet_snr(receipt.proof_packet.packet_hash)
if reception_rssi != None:
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]"
reception_stats += f" [RSSI {reception_rssi} dBm]"
if reception_snr != None:
reception_stats += " [SNR "+str(reception_snr)+" dB]"
reception_stats += f" [SNR {reception_snr} dB]"
else:
if receipt.proof_packet != None:
if receipt.proof_packet.rssi != None:
reception_stats += " [RSSI "+str(receipt.proof_packet.rssi)+" dBm]"
reception_stats += f" [RSSI {receipt.proof_packet.rssi} dBm]"
if receipt.proof_packet.snr != None:
reception_stats += " [SNR "+str(receipt.proof_packet.snr)+" dB]"
reception_stats += f" [SNR {receipt.proof_packet.snr} dB]"
RNS.log(
"Valid reply received from "+
RNS.prettyhexrep(receipt.destination.hash)+
", round-trip time is "+rttstring+
reception_stats
f"Valid reply received from {RNS.prettyhexrep(receipt.destination.hash)}, round-trip time is {rttstring}{reception_stats}"
)
# This function is called if a packet times out.
def packet_timed_out(receipt):
if receipt.status == RNS.PacketReceipt.FAILED:
RNS.log("Packet "+RNS.prettyhexrep(receipt.hash)+" timed out")
RNS.log(f"Packet {RNS.prettyhexrep(receipt.hash)} timed out")
##########################################################

View File

@ -44,7 +44,7 @@ serve_path = None
def server(configpath, path):
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# Randomly create a new identity for our file server
server_identity = RNS.Identity()
@ -73,7 +73,7 @@ def server(configpath, path):
def announceLoop(destination):
# Let the user know that everything is ready
RNS.log("File server "+RNS.prettyhexrep(destination.hash)+" running")
RNS.log(f"File server {RNS.prettyhexrep(destination.hash)} running")
RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
# We enter a loop that runs until the users exits.
@ -83,7 +83,7 @@ def announceLoop(destination):
while True:
entered = input()
destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
RNS.log(f"Sent announce from {RNS.prettyhexrep(destination.hash)}")
# Here's a convenience function for listing all files
# in our served directory
@ -120,7 +120,7 @@ def client_connected(link):
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("Hint: The client already supports it :)", RNS.LOG_ERROR)
# After this, we're just going to keep the link
# open until the client requests a file. We'll
# configure a function that get's called when
@ -145,9 +145,9 @@ def client_request(message, packet):
try:
# If we have the requested file, we'll
# read it and pack it as a resource
RNS.log("Client requested \""+filename+"\"")
RNS.log(f"Client requested \"{filename}\"")
file = open(os.path.join(serve_path, filename), "rb")
file_resource = RNS.Resource(
file,
packet.link,
@ -158,7 +158,7 @@ def client_request(message, packet):
except Exception as e:
# If somethign went wrong, we close
# the link
RNS.log("Error while reading file \""+filename+"\"", RNS.LOG_ERROR)
RNS.log(f"Error while reading file \"{filename}\"", RNS.LOG_ERROR)
packet.link.teardown()
raise e
else:
@ -175,9 +175,9 @@ def resource_sending_concluded(resource):
name = "resource"
if resource.status == RNS.Resource.COMPLETE:
RNS.log("Done sending \""+name+"\" to client")
RNS.log(f"Done sending \"{name}\" to client")
elif resource.status == RNS.Resource.FAILED:
RNS.log("Sending \""+name+"\" to client failed")
RNS.log(f"Sending \"{name}\" to client failed")
def list_delivered(receipt):
RNS.log("The file list was received by the client")
@ -218,9 +218,9 @@ def client(destination_hexhash, configpath):
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len:
raise ValueError(
"Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
)
destination_hash = bytes.fromhex(destination_hexhash)
except:
RNS.log("Invalid destination entered. Check your input!\n")
@ -291,9 +291,9 @@ def download(filename):
# packet receipt.
request_packet = RNS.Packet(server_link, filename.encode("utf-8"), create_receipt=False)
request_packet.send()
print("")
print(("Requested \""+filename+"\" from server, waiting for download to begin..."))
print(f"Requested \"{filename}\" from server, waiting for download to begin...")
menu_mode = "download_started"
# This function runs a simple menu for the user
@ -363,7 +363,7 @@ def print_menu():
while menu_mode == "downloading":
global current_download
percent = round(current_download.get_progress() * 100.0, 1)
print(("\rProgress: "+str(percent)+" % "), end=' ')
print(f'\rProgress: {percent} % ', end=' ')
sys.stdout.flush()
time.sleep(0.1)
@ -383,15 +383,15 @@ def print_menu():
# Print statistics
hours, rem = divmod(download_time, 3600)
minutes, seconds = divmod(rem, 60)
timestring = "{:0>2}:{:0>2}:{:05.2f}".format(int(hours),int(minutes),seconds)
timestring = f"{int(hours):0>2}:{int(minutes):0>2}:{seconds:05.2f}"
print("")
print("")
print("--- Statistics -----")
print("\tTime taken : "+timestring)
print("\tFile size : "+size_str(file_size))
print("\tData transferred : "+size_str(transfer_size))
print("\tEffective rate : "+size_str(file_size/download_time, suffix='b')+"/s")
print("\tTransfer rate : "+size_str(transfer_size/download_time, suffix='b')+"/s")
print(f"\tTime taken : {timestring}")
print(f"\tFile size : {size_str(file_size)}")
print(f"\tData transferred : {size_str(transfer_size)}")
print(f"\tEffective rate : {size_str(file_size / download_time, suffix='b')}/s")
print(f"\tTransfer rate : {size_str(transfer_size / download_time, suffix='b')}/s")
print("")
print("The download completed! Press enter to return to the menu.")
print("")
@ -412,7 +412,7 @@ def print_filelist():
global server_files
print("Files on server:")
for index,file in enumerate(server_files):
print("\t("+str(index)+")\t"+file)
print(f"\t({index})\t{file}")
def filelist_received(filelist_data, packet):
global server_files, menu_mode
@ -474,7 +474,7 @@ def link_closed(link):
RNS.log("The link was closed by the server, exiting now")
else:
RNS.log("Link closed, exiting now")
RNS.Reticulum.exit_handler()
time.sleep(1.5)
os._exit(0)
@ -486,17 +486,17 @@ def link_closed(link):
def download_began(resource):
global menu_mode, current_download, download_started, transfer_size, file_size
current_download = resource
if download_started == 0:
download_started = time.time()
transfer_size += resource.size
file_size = resource.total_size
menu_mode = "downloading"
# 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.
def download_concluded(resource):
global menu_mode, current_filename, download_started, download_finished, download_time
@ -509,7 +509,7 @@ def download_concluded(resource):
counter = 0
while os.path.isfile(saved_filename):
counter += 1
saved_filename = current_filename+"."+str(counter)
saved_filename = f"{current_filename}.{counter}"
try:
file = open(saved_filename, "wb")
@ -534,9 +534,9 @@ def size_str(num, suffix='B'):
for unit in units:
if abs(num) < 1024.0:
return "%3.2f %s%s" % (num, unit, suffix)
return f"{num:3.2f} {unit}{suffix}"
num /= 1024.0
return "%.2f %s%s" % (num, last_unit, suffix)
return f"{num:.2f} {last_unit}{suffix}"
# A convenience function for clearing the screen
def clear_screen():

View File

@ -27,7 +27,7 @@ latest_client_link = None
def server(configpath):
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# Randomly create a new identity for our link example
server_identity = RNS.Identity()
@ -53,9 +53,7 @@ def server(configpath):
def server_loop(destination):
# Let the user know that everything is ready
RNS.log(
"Link identification example "+
RNS.prettyhexrep(destination.hash)+
" running, waiting for a connection."
f"Link identification example {RNS.prettyhexrep(destination.hash)} running, waiting for a connection."
)
RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
@ -67,7 +65,7 @@ def server_loop(destination):
while True:
entered = input()
destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
RNS.log(f"Sent announce from {RNS.prettyhexrep(destination.hash)}")
# When a client establishes a link to our server
# destination, this function will be called with
@ -85,7 +83,7 @@ def client_disconnected(link):
RNS.log("Client disconnected")
def remote_identified(link, identity):
RNS.log("Remote identified as: "+str(identity))
RNS.log(f"Remote identified as: {identity}")
def server_packet_received(message, packet):
global latest_client_link
@ -100,9 +98,9 @@ def server_packet_received(message, packet):
# that connected.
text = message.decode("utf-8")
RNS.log("Received data from "+remote_peer+": "+text)
reply_text = "I received \""+text+"\" over the link from "+remote_peer
RNS.log(f"Received data from {remote_peer}: {text}")
reply_text = f"I received \"{text}\" over the link from {remote_peer}"
reply_data = reply_text.encode("utf-8")
RNS.Packet(latest_client_link, reply_data).send()
@ -127,7 +125,7 @@ def client(destination_hexhash, configpath):
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len:
raise ValueError(
"Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
)
destination_hash = bytes.fromhex(destination_hexhash)
@ -141,8 +139,7 @@ def client(destination_hexhash, configpath):
# Create a new client identity
client_identity = RNS.Identity()
RNS.log(
"Client created new identity "+
str(client_identity)
f"Client created new identity {client_identity}"
)
# Check if we know a path to the destination
@ -210,14 +207,12 @@ def client_loop():
RNS.Packet(server_link, data).send()
else:
RNS.log(
"Cannot send this packet, the data size of "+
str(len(data))+" bytes exceeds the link packet MDU of "+
str(RNS.Link.MDU)+" bytes",
f"Cannot send this packet, the data size of {len(data)} bytes exceeds the link packet MDU of {RNS.Link.MDU} bytes",
RNS.LOG_ERROR
)
except Exception as e:
RNS.log("Error while sending data over the link: "+str(e))
RNS.log(f"Error while sending data over the link: {e}")
should_quit = True
server_link.teardown()
@ -244,7 +239,7 @@ def link_closed(link):
RNS.log("The link was closed by the server, exiting now")
else:
RNS.log("Link closed, exiting now")
RNS.Reticulum.exit_handler()
time.sleep(1.5)
os._exit(0)
@ -253,7 +248,7 @@ def link_closed(link):
# simply print out the data.
def client_packet_received(message, packet):
text = message.decode("utf-8")
RNS.log("Received data on the link: "+text)
RNS.log(f"Received data on the link: {text}")
print("> ", end=" ")
sys.stdout.flush()

View File

@ -27,7 +27,7 @@ latest_client_link = None
def server(configpath):
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# Randomly create a new identity for our link example
server_identity = RNS.Identity()
@ -53,9 +53,7 @@ def server(configpath):
def server_loop(destination):
# Let the user know that everything is ready
RNS.log(
"Link example "+
RNS.prettyhexrep(destination.hash)+
" running, waiting for a connection."
f"Link example {RNS.prettyhexrep(destination.hash)} running, waiting for a connection."
)
RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
@ -67,7 +65,7 @@ def server_loop(destination):
while True:
entered = input()
destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
RNS.log(f"Sent announce from {RNS.prettyhexrep(destination.hash)}")
# When a client establishes a link to our server
# destination, this function will be called with
@ -90,9 +88,9 @@ def server_packet_received(message, packet):
# it will all be directed to the last client
# that connected.
text = message.decode("utf-8")
RNS.log("Received data on the link: "+text)
reply_text = "I received \""+text+"\" over the link"
RNS.log(f"Received data on the link: {text}")
reply_text = f"I received \"{text}\" over the link"
reply_data = reply_text.encode("utf-8")
RNS.Packet(latest_client_link, reply_data).send()
@ -113,9 +111,9 @@ def client(destination_hexhash, configpath):
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len:
raise ValueError(
"Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
)
destination_hash = bytes.fromhex(destination_hexhash)
except:
RNS.log("Invalid destination entered. Check your input!\n")
@ -189,14 +187,12 @@ def client_loop():
RNS.Packet(server_link, data).send()
else:
RNS.log(
"Cannot send this packet, the data size of "+
str(len(data))+" bytes exceeds the link packet MDU of "+
str(RNS.Link.MDU)+" bytes",
f"Cannot send this packet, the data size of {len(data)} bytes exceeds the link packet MDU of {RNS.Link.MDU} bytes",
RNS.LOG_ERROR
)
except Exception as e:
RNS.log("Error while sending data over the link: "+str(e))
RNS.log(f"Error while sending data over the link: {e}")
should_quit = True
server_link.teardown()
@ -221,7 +217,7 @@ def link_closed(link):
RNS.log("The link was closed by the server, exiting now")
else:
RNS.log("Link closed, exiting now")
RNS.Reticulum.exit_handler()
time.sleep(1.5)
os._exit(0)
@ -230,7 +226,7 @@ def link_closed(link):
# simply print out the data.
def client_packet_received(message, packet):
text = message.decode("utf-8")
RNS.log("Received data on the link: "+text)
RNS.log(f"Received data on the link: {text}")
print("> ", end=" ")
sys.stdout.flush()

View File

@ -17,7 +17,7 @@ APP_NAME = "example_utilities"
def program_setup(configpath):
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# Randomly create a new identity for our example
identity = RNS.Identity()
@ -42,7 +42,7 @@ def program_setup(configpath):
# tries to communicate with the destination know whether their
# communication was received correctly.
destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
# Everything's ready!
# Let's hand over control to the announce loop
announceLoop(destination)
@ -51,9 +51,7 @@ def program_setup(configpath):
def announceLoop(destination):
# Let the user know that everything is ready
RNS.log(
"Minimal example "+
RNS.prettyhexrep(destination.hash)+
" running, hit enter to manually send an announce (Ctrl-C to quit)"
f"Minimal example {RNS.prettyhexrep(destination.hash)} running, hit enter to manually send an announce (Ctrl-C to quit)"
)
# We enter a loop that runs until the users exits.
@ -63,7 +61,7 @@ def announceLoop(destination):
while True:
entered = input()
destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
RNS.log(f"Sent announce from {RNS.prettyhexrep(destination.hash)}")
##########################################################

View File

@ -28,7 +28,7 @@ def server(configpath):
# TODO: Remove
RNS.loglevel = RNS.LOG_DEBUG
# Randomly create a new identity for our echo server
server_identity = RNS.Identity()
@ -37,7 +37,7 @@ def server(configpath):
# create a "single" destination that can receive encrypted
# messages. This way the client can send a request and be
# certain that no-one else than this destination was able
# to read it.
# to read it.
echo_destination = RNS.Destination(
server_identity,
RNS.Destination.IN,
@ -61,7 +61,7 @@ def server(configpath):
# generate a proof for each incoming packet and transmit it
# back to the sender of that packet.
echo_destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
# Tell the destination which function in our program to
# run when a packet is received. We do this so we can
# print a log message when the server receives a request
@ -75,9 +75,7 @@ def server(configpath):
def announceLoop(destination):
# Let the user know that everything is ready
RNS.log(
"Ratcheted echo server "+
RNS.prettyhexrep(destination.hash)+
" running, hit enter to manually send an announce (Ctrl-C to quit)"
f"Ratcheted echo server {RNS.prettyhexrep(destination.hash)} running, hit enter to manually send an announce (Ctrl-C to quit)"
)
# We enter a loop that runs until the users exits.
@ -87,12 +85,12 @@ def announceLoop(destination):
while True:
entered = input()
destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
RNS.log(f"Sent announce from {RNS.prettyhexrep(destination.hash)}")
def server_callback(message, packet):
global reticulum
# Tell the user that we received an echo request, and
# that we are going to send a reply to the requester.
# Sending the proof is handled automatically, since we
@ -104,19 +102,19 @@ def server_callback(message, packet):
reception_snr = reticulum.get_packet_snr(packet.packet_hash)
if reception_rssi != None:
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]"
reception_stats += f" [RSSI {reception_rssi} dBm]"
if reception_snr != None:
reception_stats += " [SNR "+str(reception_snr)+" dBm]"
reception_stats += f" [SNR {reception_snr} dBm]"
else:
if packet.rssi != None:
reception_stats += " [RSSI "+str(packet.rssi)+" dBm]"
if packet.snr != None:
reception_stats += " [SNR "+str(packet.snr)+" dB]"
reception_stats += f" [RSSI {packet.rssi} dBm]"
RNS.log("Received packet from echo client, proof sent"+reception_stats)
if packet.snr != None:
reception_stats += f" [SNR {packet.snr} dB]"
RNS.log(f"Received packet from echo client, proof sent{reception_stats}")
##########################################################
@ -127,20 +125,20 @@ def server_callback(message, packet):
# to run as a client
def client(destination_hexhash, configpath, timeout=None):
global reticulum
# We need a binary representation of the destination
# hash that was entered on the command line
try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len:
raise ValueError(
"Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
)
destination_hash = bytes.fromhex(destination_hexhash)
except Exception as e:
RNS.log("Invalid destination entered. Check your input!")
RNS.log(str(e)+"\n")
RNS.log(f"{e}\n")
exit()
# We must first initialise Reticulum
@ -153,9 +151,7 @@ def client(destination_hexhash, configpath, timeout=None):
# Tell the user that the client is ready!
RNS.log(
"Echo client ready, hit enter to send echo request to "+
destination_hexhash+
" (Ctrl-C to quit)"
f"Echo client ready, hit enter to send echo request to {destination_hexhash} (Ctrl-C to quit)"
)
# We enter a loop that runs until the user exits.
@ -164,7 +160,7 @@ def client(destination_hexhash, configpath, timeout=None):
# command line.
while True:
input()
# 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 RNS.Transport.has_path(destination_hash):
@ -217,7 +213,7 @@ def client(destination_hexhash, configpath, timeout=None):
packet_receipt.set_delivery_callback(packet_delivered)
# Tell the user that the echo request was sent
RNS.log("Sent echo request to "+RNS.prettyhexrep(request_destination.hash))
RNS.log(f"Sent echo request to {RNS.prettyhexrep(request_destination.hash)}")
else:
# If we do not know this destination, tell the
# user to wait for an announce to arrive.
@ -234,10 +230,10 @@ def packet_delivered(receipt):
rtt = receipt.get_rtt()
if (rtt >= 1):
rtt = round(rtt, 3)
rttstring = str(rtt)+" seconds"
rttstring = f"{rtt} seconds"
else:
rtt = round(rtt*1000, 3)
rttstring = str(rtt)+" milliseconds"
rttstring = f"{rtt} milliseconds"
reception_stats = ""
if reticulum.is_connected_to_shared_instance:
@ -245,30 +241,27 @@ def packet_delivered(receipt):
reception_snr = reticulum.get_packet_snr(receipt.proof_packet.packet_hash)
if reception_rssi != None:
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]"
reception_stats += f" [RSSI {reception_rssi} dBm]"
if reception_snr != None:
reception_stats += " [SNR "+str(reception_snr)+" dB]"
reception_stats += f" [SNR {reception_snr} dB]"
else:
if receipt.proof_packet != None:
if receipt.proof_packet.rssi != None:
reception_stats += " [RSSI "+str(receipt.proof_packet.rssi)+" dBm]"
reception_stats += f" [RSSI {receipt.proof_packet.rssi} dBm]"
if receipt.proof_packet.snr != None:
reception_stats += " [SNR "+str(receipt.proof_packet.snr)+" dB]"
reception_stats += f" [SNR {receipt.proof_packet.snr} dB]"
RNS.log(
"Valid reply received from "+
RNS.prettyhexrep(receipt.destination.hash)+
", round-trip time is "+rttstring+
reception_stats
f"Valid reply received from {RNS.prettyhexrep(receipt.destination.hash)}, round-trip time is {rttstring}{reception_stats}"
)
# This function is called if a packet times out.
def packet_timed_out(receipt):
if receipt.status == RNS.PacketReceipt.FAILED:
RNS.log("Packet "+RNS.prettyhexrep(receipt.hash)+" timed out")
RNS.log(f"Packet {RNS.prettyhexrep(receipt.hash)} timed out")
##########################################################

View File

@ -24,7 +24,7 @@ APP_NAME = "example_utilities"
latest_client_link = None
def random_text_generator(path, data, request_id, link_id, remote_identity, requested_at):
RNS.log("Generating response to request "+RNS.prettyhexrep(request_id)+" on link "+RNS.prettyhexrep(link_id))
RNS.log(f"Generating response to request {RNS.prettyhexrep(request_id)} on link {RNS.prettyhexrep(link_id)}")
texts = ["They looked up", "On each full moon", "Becky was upset", "Ill stay away from it", "The pet shop stocks everything"]
return texts[random.randint(0, len(texts)-1)]
@ -33,7 +33,7 @@ def random_text_generator(path, data, request_id, link_id, remote_identity, requ
def server(configpath):
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# Randomly create a new identity for our link example
server_identity = RNS.Identity()
@ -67,9 +67,7 @@ def server(configpath):
def server_loop(destination):
# Let the user know that everything is ready
RNS.log(
"Request example "+
RNS.prettyhexrep(destination.hash)+
" running, waiting for a connection."
f"Request example {RNS.prettyhexrep(destination.hash)} running, waiting for a connection."
)
RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
@ -81,7 +79,7 @@ def server_loop(destination):
while True:
entered = input()
destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
RNS.log(f"Sent announce from {RNS.prettyhexrep(destination.hash)}")
# When a client establishes a link to our server
# destination, this function will be called with
@ -113,9 +111,9 @@ def client(destination_hexhash, configpath):
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len:
raise ValueError(
"Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
)
destination_hash = bytes.fromhex(destination_hexhash)
except:
RNS.log("Invalid destination entered. Check your input!\n")
@ -187,7 +185,7 @@ def client_loop():
except Exception as e:
RNS.log("Error while sending request over the link: "+str(e))
RNS.log(f"Error while sending request over the link: {e}")
should_quit = True
server_link.teardown()
@ -195,13 +193,13 @@ def got_response(request_receipt):
request_id = request_receipt.request_id
response = request_receipt.response
RNS.log("Got response for request "+RNS.prettyhexrep(request_id)+": "+str(response))
RNS.log(f"Got response for request {RNS.prettyhexrep(request_id)}: {response}")
def request_received(request_receipt):
RNS.log("The request "+RNS.prettyhexrep(request_receipt.request_id)+" was received by the remote peer.")
RNS.log(f"The request {RNS.prettyhexrep(request_receipt.request_id)} was received by the remote peer.")
def request_failed(request_receipt):
RNS.log("The request "+RNS.prettyhexrep(request_receipt.request_id)+" failed.")
RNS.log(f"The request {RNS.prettyhexrep(request_receipt.request_id)} failed.")
# This function is called when a link
@ -225,7 +223,7 @@ def link_closed(link):
RNS.log("The link was closed by the server, exiting now")
else:
RNS.log("Link closed, exiting now")
RNS.Reticulum.exit_handler()
time.sleep(1.5)
os._exit(0)

View File

@ -36,7 +36,7 @@ printed = False
def server(configpath):
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# Randomly create a new identity for our link example
server_identity = RNS.Identity()
@ -62,9 +62,7 @@ def server(configpath):
def server_loop(destination):
# Let the user know that everything is ready
RNS.log(
"Speedtest "+
RNS.prettyhexrep(destination.hash)+
" running, waiting for a connection."
f"Speedtest {RNS.prettyhexrep(destination.hash)} running, waiting for a connection."
)
RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
@ -76,7 +74,7 @@ def server_loop(destination):
while True:
entered = input()
destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
RNS.log(f"Sent announce from {RNS.prettyhexrep(destination.hash)}")
# When a client establishes a link to our server
# destination, this function will be called with
@ -108,16 +106,16 @@ def size_str(num, suffix='B'):
for unit in units:
if abs(num) < 1024.0:
return "%3.2f %s%s" % (num, unit, suffix)
return f"{num:3.2f} {unit}{suffix}"
num /= 1024.0
return "%.2f %s%s" % (num, last_unit, suffix)
return f"{num:.2f} {last_unit}{suffix}"
def server_packet_received(message, packet):
global latest_client_link, first_packet_at, last_packet_at, received_data, rc, data_cap
received_data += len(packet.data)
rc += 1
if rc >= 50:
RNS.log(size_str(received_data))
@ -129,19 +127,19 @@ def server_packet_received(message, packet):
rc = 0
last_packet_at = time.time()
# Print statistics
download_time = last_packet_at-first_packet_at
hours, rem = divmod(download_time, 3600)
minutes, seconds = divmod(rem, 60)
timestring = "{:0>2}:{:0>2}:{:05.2f}".format(int(hours),int(minutes),seconds)
timestring = f"{int(hours):0>2}:{int(minutes):0>2}:{seconds:05.2f}"
print("")
print("")
print("--- Statistics -----")
print("\tTime taken : "+timestring)
print("\tData transferred : "+size_str(rcv_d))
print("\tTransfer rate : "+size_str(rcv_d/download_time, suffix='b')+"/s")
print(f"\tTime taken : {timestring}")
print(f"\tData transferred : {size_str(rcv_d)}")
print(f"\tTransfer rate : {size_str(rcv_d / download_time, suffix='b')}/s")
print("")
sys.stdout.flush()
@ -169,9 +167,9 @@ def client(destination_hexhash, configpath):
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len:
raise ValueError(
"Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)
f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes)."
)
destination_hash = bytes.fromhex(destination_hexhash)
except:
RNS.log("Invalid destination entered. Check your input!\n")
@ -260,13 +258,13 @@ def link_established(link):
download_time = ended-started
hours, rem = divmod(download_time, 3600)
minutes, seconds = divmod(rem, 60)
timestring = "{:0>2}:{:0>2}:{:05.2f}".format(int(hours),int(minutes),seconds)
timestring = f"{int(hours):0>2}:{int(minutes):0>2}:{seconds:05.2f}"
print("")
print("")
print("--- Statistics -----")
print("\tTime taken : "+timestring)
print("\tData transferred : "+size_str(data_sent))
print("\tTransfer rate : "+size_str(data_sent/download_time, suffix='b')+"/s")
print(f"\tTime taken : {timestring}")
print(f"\tData transferred : {size_str(data_sent)}")
print(f"\tTransfer rate : {size_str(data_sent / download_time, suffix='b')}/s")
print("")
sys.stdout.flush()
@ -282,7 +280,7 @@ def link_closed(link):
RNS.log("The link was closed by the server, exiting now")
else:
RNS.log("Link closed, exiting now")
RNS.Reticulum.exit_handler()
time.sleep(1.5)

View File

@ -109,8 +109,8 @@ network, and vice versa.
## How do I get started?
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
[Getting Started Fast](https://markqvist.github.io/Reticulum/manual/gettingstartedfast.html)
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)
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`.
@ -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.
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
the UDP and TCP interfaces, take a look at the [Supported Interfaces](https://markqvist.github.io/Reticulum/manual/interfaces.html)
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
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/).
## Included Utilities
Reticulum includes a range of useful utilities for managing your networks,
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)
Reticulum includes a range of useful utilities for managing your networks,
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)
section of the [Reticulum Manual](https://markqvist.github.io/Reticulum/manual/).
- 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
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
shipping applications, the best practice is to provide your own default
connectivity solutions, if needed and applicable, or in most cases, simply

View File

@ -65,7 +65,7 @@ class StreamDataMessage(MessageBase):
raise ValueError("stream_id must be 0-16383")
self.stream_id = stream_id
self.compressed = compressed
self.data = data or bytes()
self.data = data or b''
self.eof = eof
def pack(self) -> bytes:
@ -73,7 +73,7 @@ class StreamDataMessage(MessageBase):
raise ValueError("stream_id")
header_val = (0x3fff & self.stream_id) | (0x8000 if self.eof else 0x0000) | (0x4000 if self.compressed > 0 else 0x0000)
return bytes(struct.pack(">H", header_val) + (self.data if self.data else bytes()))
return bytes(struct.pack(">H", header_val) + (self.data if self.data else b''))
def unpack(self, raw):
self.stream_id = struct.unpack(">H", raw[:2])[0]
@ -148,7 +148,7 @@ class RawChannelReader(RawIOBase, AbstractContextManager):
try:
threading.Thread(target=listener, name="Message Callback", args=[len(self._buffer)], daemon=True).start()
except Exception as ex:
RNS.log("Error calling RawChannelReader(" + str(self._stream_id) + ") callback: " + str(ex), RNS.LOG_ERROR)
RNS.log(f"Error calling RawChannelReader({self._stream_id}) callback: {ex}", RNS.LOG_ERROR)
return True
return False
@ -244,7 +244,7 @@ class RawChannelWriter(RawIOBase, AbstractContextManager):
processed_length = len(chunk)
message = StreamDataMessage(self._stream_id, chunk, self._eof, comp_success)
self._channel.send(message)
return processed_length
@ -264,7 +264,7 @@ class RawChannelWriter(RawIOBase, AbstractContextManager):
time.sleep(0.05)
self._eof = True
self.write(bytes())
self.write(b'')
def __enter__(self):
return self

View File

@ -136,7 +136,7 @@ class MessageBase(abc.ABC):
MSGTYPE = None
"""
Defines a unique identifier for a message class.
* Must be unique within all classes registered with a ``Channel``
* Must be less than ``0xf000``. Values greater than or equal to ``0xf000`` are reserved.
"""
@ -168,7 +168,7 @@ class Envelope:
Internal wrapper used to transport messages over a channel and
track its state within the channel framework.
"""
def unpack(self, message_factories: dict[int, Type]) -> MessageBase:
def unpack(self, message_factories: dict[int, type]) -> MessageBase:
msgtype, self.sequence, length = struct.unpack(">HHH", self.raw[:6])
raw = self.raw[6:]
ctor = message_factories.get(msgtype, None)
@ -247,11 +247,11 @@ class Channel(contextlib.AbstractContextManager):
# The maximum window size for transfers on fast links
WINDOW_MAX_FAST = 48
# For calculating maps and guard segments, this
# must be set to the global maximum window.
WINDOW_MAX = WINDOW_MAX_FAST
# If the fast rate is sustained for this many request
# rounds, the fast link window size will be allowed.
FAST_RATE_THRESHOLD = 10
@ -282,7 +282,7 @@ class Channel(contextlib.AbstractContextManager):
self._message_callbacks: [MessageCallbackType] = []
self._next_sequence = 0
self._next_rx_sequence = 0
self._message_factories: dict[int, Type[MessageBase]] = {}
self._message_factories: dict[int, type[MessageBase]] = {}
self._max_tries = 5
self.fast_rate_rounds = 0
self.medium_rate_rounds = 0
@ -301,12 +301,12 @@ class Channel(contextlib.AbstractContextManager):
def __enter__(self) -> Channel:
return self
def __exit__(self, __exc_type: Type[BaseException] | None, __exc_value: BaseException | None,
def __exit__(self, __exc_type: type[BaseException] | None, __exc_value: BaseException | None,
__traceback: TracebackType | None) -> bool | None:
self._shutdown()
return False
def register_message_type(self, message_class: Type[MessageBase]):
def register_message_type(self, message_class: type[MessageBase]):
"""
Register a message class for reception over a ``Channel``.
@ -316,7 +316,7 @@ class Channel(contextlib.AbstractContextManager):
"""
self._register_message_type(message_class, is_system_type=False)
def _register_message_type(self, message_class: Type[MessageBase], *, is_system_type: bool = False):
def _register_message_type(self, message_class: type[MessageBase], *, is_system_type: bool = False):
with self._lock:
if not issubclass(message_class, MessageBase):
raise ChannelException(CEType.ME_INVALID_MSG_TYPE,
@ -380,21 +380,21 @@ class Channel(contextlib.AbstractContextManager):
def _emplace_envelope(self, envelope: Envelope, ring: collections.deque[Envelope]) -> bool:
with self._lock:
i = 0
for existing in ring:
if envelope.sequence == existing.sequence:
RNS.log(f"Envelope: Emplacement of duplicate envelope with sequence "+str(envelope.sequence), RNS.LOG_EXTREME)
RNS.log(f"Envelope: Emplacement of duplicate envelope with sequence {envelope.sequence}", RNS.LOG_EXTREME)
return False
if envelope.sequence < existing.sequence and not (self._next_rx_sequence - envelope.sequence) > (Channel.SEQ_MAX//2):
ring.insert(i, envelope)
envelope.tracked = True
return True
i += 1
envelope.tracked = True
ring.append(envelope)
@ -408,7 +408,7 @@ class Channel(contextlib.AbstractContextManager):
if cb(message):
return
except Exception as e:
RNS.log("Channel "+str(self)+" experienced an error while running a message callback. The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Channel {self} experienced an error while running a message callback. The contained exception was: {e}", RNS.LOG_ERROR)
def _receive(self, raw: bytes):
try:
@ -420,16 +420,16 @@ class Channel(contextlib.AbstractContextManager):
window_overflow = (self._next_rx_sequence+Channel.WINDOW_MAX) % Channel.SEQ_MODULUS
if window_overflow < self._next_rx_sequence:
if envelope.sequence > window_overflow:
RNS.log("Invalid packet sequence ("+str(envelope.sequence)+") received on channel "+str(self), RNS.LOG_EXTREME)
RNS.log(f"Invalid packet sequence ({envelope.sequence}) received on channel {self}", RNS.LOG_EXTREME)
return
else:
RNS.log("Invalid packet sequence ("+str(envelope.sequence)+") received on channel "+str(self), RNS.LOG_EXTREME)
RNS.log(f"Invalid packet sequence ({envelope.sequence}) received on channel {self}", RNS.LOG_EXTREME)
return
is_new = self._emplace_envelope(envelope, self._rx_ring)
if not is_new:
RNS.log("Duplicate message received on channel "+str(self), RNS.LOG_EXTREME)
RNS.log(f"Duplicate message received on channel {self}", RNS.LOG_EXTREME)
return
else:
with self._lock:
@ -449,12 +449,12 @@ class Channel(contextlib.AbstractContextManager):
m = e.unpack(self._message_factories)
else:
m = e.message
self._rx_ring.remove(e)
self._run_callbacks(m)
except Exception as e:
RNS.log("An error ocurred while receiving data on "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"An error ocurred while receiving data on {self}. The contained exception was: {e}", RNS.LOG_ERROR)
def is_ready_to_send(self) -> bool:
"""
@ -468,7 +468,7 @@ class Channel(contextlib.AbstractContextManager):
with self._lock:
outstanding = 0
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:
outstanding += 1
@ -508,7 +508,7 @@ class Channel(contextlib.AbstractContextManager):
# 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)+" min window to "+str(self.window_min), RNS.LOG_DEBUG)
else:
self.fast_rate_rounds += 1
if self.window_max < Channel.WINDOW_MAX_FAST and self.fast_rate_rounds == Channel.FAST_RATE_THRESHOLD:
@ -520,9 +520,9 @@ class Channel(contextlib.AbstractContextManager):
else:
RNS.log("Envelope not found in TX ring for "+str(self), RNS.LOG_EXTREME)
RNS.log(f"Envelope not found in TX ring for {self}", RNS.LOG_EXTREME)
if not envelope:
RNS.log("Spurious message received on "+str(self), RNS.LOG_EXTREME)
RNS.log(f"Spurious message received on {self}", RNS.LOG_EXTREME)
def _packet_delivered(self, packet: TPacket):
self._packet_tx_op(packet, lambda env: True)
@ -541,7 +541,7 @@ class Channel(contextlib.AbstractContextManager):
def _packet_timeout(self, packet: TPacket):
def retry_envelope(envelope: Envelope) -> bool:
if envelope.tries >= self._max_tries:
RNS.log("Retry count exceeded on "+str(self)+", tearing down Link.", RNS.LOG_ERROR)
RNS.log(f"Retry count exceeded on {self}, tearing down Link.", RNS.LOG_ERROR)
self._shutdown() # start on separate thread?
self._outlet.timed_out()
return True
@ -581,7 +581,7 @@ class Channel(contextlib.AbstractContextManager):
with self._lock:
if not self.is_ready_to_send():
raise ChannelException(CEType.ME_LINK_NOT_READY, f"Link is not ready")
envelope = Envelope(self._outlet, message=message, sequence=self._next_sequence)
self._next_sequence = (self._next_sequence + 1) % Channel.SEQ_MODULUS
self._emplace_envelope(envelope, self._tx_ring)
@ -592,7 +592,7 @@ class Channel(contextlib.AbstractContextManager):
envelope.pack()
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}")
envelope.packet = self._outlet.send(envelope.raw)
envelope.tries += 1
self._outlet.set_packet_delivered_callback(envelope.packet, self._packet_delivered)

View File

@ -25,7 +25,7 @@ import RNS.vendor.platformutils as pu
if cp.PROVIDER == cp.PROVIDER_INTERNAL:
from .aes import AES
elif cp.PROVIDER == cp.PROVIDER_PYCA:
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))
else:
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor()
ciphertext = encryptor.update(plaintext) + encryptor.finalize()
return ciphertext

View File

@ -48,15 +48,15 @@ class Fernet():
raise ValueError("Token key cannot be None")
if len(key) != 32:
raise ValueError("Token key must be 32 bytes, not "+str(len(key)))
raise ValueError(f"Token key must be 32 bytes, not {len(key)}")
self._signing_key = key[:16]
self._encryption_key = key[16:]
def verify_hmac(self, token):
if len(token) <= 32:
raise ValueError("Cannot verify HMAC on token of only "+str(len(token))+" bytes")
raise ValueError(f"Cannot verify HMAC on token of only {len(token)} bytes")
else:
received_hmac = token[-32:]
expected_hmac = HMAC.new(self._signing_key, token[:-32]).digest()

View File

@ -37,7 +37,7 @@ class HMAC:
"""
if not isinstance(key, (bytes, bytearray)):
raise TypeError("key: expected bytes or bytearray, but got %r" % type(key).__name__)
raise TypeError(f"key: expected bytes or bytearray, but got {type(key).__name__!r}")
if not digestmod:
raise TypeError("Missing required parameter 'digestmod'.")
@ -60,13 +60,11 @@ class HMAC:
if hasattr(self._inner, 'block_size'):
blocksize = self._inner.block_size
if blocksize < 16:
_warnings.warn('block_size of %d seems too small; using our '
'default of %d.' % (blocksize, self.blocksize),
_warnings.warn(f'block_size of {int(blocksize)} seems too small; using our default of {int(self.blocksize)}.',
RuntimeWarning, 2)
blocksize = self.blocksize
else:
_warnings.warn('No block_size attribute on given digest object; '
'Assuming %d.' % (self.blocksize),
_warnings.warn(f'No block_size attribute on given digest object; Assuming {int(self.blocksize)}.',
RuntimeWarning, 2)
blocksize = self.blocksize

View File

@ -35,6 +35,6 @@ class PKCS7:
l = len(data)
n = data[-1]
if n > bs:
raise ValueError("Cannot unpad, invalid padding length of "+str(n)+" bytes")
raise ValueError(f"Cannot unpad, invalid padding length of {n} bytes")
else:
return data[:l-n]

View File

@ -35,4 +35,4 @@ def backend():
elif PROVIDER == PROVIDER_INTERNAL:
return "internal"
elif PROVIDER == PROVIDER_PYCA:
return "openssl, PyCA "+str(pyca_v)
return f"openssl, PyCA {pyca_v}"

View File

@ -28,7 +28,7 @@ import sys
def new(m=None):
return sha256(m)
class sha256(object):
class sha256:
_k = (0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
@ -48,34 +48,34 @@ class sha256(object):
_h = (0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19)
_output_size = 8
blocksize = 1
block_size = 64
digest_size = 32
def __init__(self, m=None):
def __init__(self, m=None):
self._buffer = b""
self._counter = 0
if m is not None:
if type(m) is not bytes:
raise TypeError('%s() argument 1 must be bytes, not %s' % (self.__class__.__name__, type(m).__name__))
raise TypeError(f'{self.__class__.__name__}() argument 1 must be bytes, not {type(m).__name__}')
self.update(m)
def _rotr(self, x, y):
return ((x >> y) | (x << (32-y))) & 0xFFFFFFFF
def _sha256_process(self, c):
w = [0]*64
w[0:16] = struct.unpack('!16L', c)
for i in range(16, 64):
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)
w[i] = (w[i-16] + s0 + w[i-7] + s1) & 0xFFFFFFFF
a,b,c,d,e,f,g,h = self._h
for i in range(64):
s0 = self._rotr(a, 2) ^ self._rotr(a, 13) ^ self._rotr(a, 22)
maj = (a & b) ^ (a & c) ^ (b & c)
@ -83,7 +83,7 @@ class sha256(object):
s1 = self._rotr(e, 6) ^ self._rotr(e, 11) ^ self._rotr(e, 25)
ch = (e & f) ^ ((~e) & g)
t1 = h + s1 + ch + self._k[i] + w[i]
h = g
g = f
f = e
@ -92,38 +92,38 @@ class sha256(object):
c = b
b = a
a = (t1 + t2) & 0xFFFFFFFF
self._h = [(x+y) & 0xFFFFFFFF for x,y in zip(self._h, [a,b,c,d,e,f,g,h])]
def update(self, m):
if not m:
return
if type(m) is not bytes:
raise TypeError('%s() argument 1 must be bytes, not %s' % (sys._getframe().f_code.co_name, 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._counter += len(m)
while len(self._buffer) >= 64:
self._sha256_process(self._buffer[:64])
self._buffer = self._buffer[64:]
def digest(self):
mdi = self._counter & 0x3F
length = struct.pack('!Q', self._counter<<3)
if mdi < 56:
padlen = 55-mdi
else:
padlen = 119-mdi
r = self.copy()
r.update(b'\x80'+(b'\x00'*padlen)+length)
return b''.join([struct.pack('!L', i) for i in r._h[:self._output_size]])
def hexdigest(self):
return self.digest().encode('hex')
def copy(self):
return copy.deepcopy(self)

View File

@ -25,7 +25,7 @@ import copy, struct, sys
def new(m=None):
return sha512(m)
class sha512(object):
class sha512:
_k = (0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
@ -60,7 +60,7 @@ class sha512(object):
if m is not None:
if type(m) is not bytes:
raise TypeError('%s() argument 1 must be bytes, not %s' % (self.__class__.__name__, type(m).__name__))
raise TypeError(f'{self.__class__.__name__}() argument 1 must be bytes, not {type(m).__name__}')
self.update(m)
def _rotr(self, x, y):
@ -100,7 +100,7 @@ class sha512(object):
if not m:
return
if type(m) is not bytes:
raise TypeError('%s() argument 1 must be bytes, not %s' % (sys._getframe().f_code.co_name, 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._counter += len(m)

View File

@ -138,9 +138,9 @@ class X25519PrivateKey:
peer_public_key = X25519PublicKey.from_public_bytes(peer_public_key)
start = time.time()
shared = _pack_number(_raw_curve25519(peer_public_key.x, self.a))
end = time.time()
duration = end-start
@ -150,7 +150,7 @@ class X25519PrivateKey:
if end > X25519PrivateKey.T_CLEAR:
X25519PrivateKey.T_CLEAR = end + X25519PrivateKey.DELAY_WINDOW
X25519PrivateKey.T_MAX = 0
if duration < X25519PrivateKey.T_MAX or duration < X25519PrivateKey.MIN_EXEC_TIME:
target = start+X25519PrivateKey.T_MAX

View File

@ -20,5 +20,5 @@ elif cp.PROVIDER == cp.PROVIDER_PYCA:
from RNS.Cryptography.Proxies import Ed25519PrivateKeyProxy as Ed25519PrivateKey
from RNS.Cryptography.Proxies import Ed25519PublicKeyProxy as Ed25519PublicKey
modules = glob.glob(os.path.dirname(__file__)+"/*.py")
modules = glob.glob(f"{os.path.dirname(__file__)}/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]

View File

@ -144,7 +144,7 @@ class AES:
return matrix2bytes(state)
# will encrypt the entire data
# will encrypt the entire data
def encrypt(self, plaintext, iv):
"""
Encrypts `plaintext` using CBC mode and PKCS#7 padding, with the given
@ -173,7 +173,7 @@ class AES:
return b''.join(ciphertext_blocks)
# will decrypt the entire data
# will decrypt the entire data
def decrypt(self, ciphertext, iv):
"""
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):
# in CBC mode every block is XOR'd with the previous block
xorred = xor_bytes(previous, self._decrypt_block(ciphertext_block))
# append plaintext
plaintext_blocks.append(xorred)
previous = ciphertext_block
@ -223,14 +223,14 @@ def test():
print("Single Block Tests")
print("------------------")
print(f"iv: {iv.hex()}")
print(f"plain text: '{single_block_text.decode()}'")
ciphertext_block = _aes._encrypt_block(single_block_text)
plaintext_block = _aes._decrypt_block(ciphertext_block)
print(f"Ciphertext Hex: {ciphertext_block.hex()}")
print(f"Plaintext: {plaintext_block.decode()}")
assert plaintext_block == single_block_text
print(bcolors.OK + "Single Block Test Passed Successfully" + bcolors.RESET)
print(f"{bcolors.OK}Single Block Test Passed Successfully{bcolors.RESET}")
print()
# test a less than a block length phrase
@ -246,7 +246,7 @@ def test():
print(f"Ciphertext Hex: {ciphertext_short.hex()}")
print(f"Plaintext: {plaintext_short.decode()}")
assert short_text == plaintext_short
print(bcolors.OK + "Short Text Test Passed Successfully" + bcolors.RESET)
print(f"{bcolors.OK}Short Text Test Passed Successfully{bcolors.RESET}")
print()
# test an arbitrary length phrase
@ -262,10 +262,10 @@ def test():
print(f"Ciphertext Hex: {ciphertext.hex()}")
print(f"Plaintext: {plaintext.decode()}")
assert text == plaintext
print(bcolors.OK + "Arbitrary Length Text Test Passed Successfully" + bcolors.RESET)
print(f"{bcolors.OK}Arbitrary Length Text Test Passed Successfully{bcolors.RESET}")
print()
if __name__ == "__main__":
# test AES class
test()
test()

View File

@ -141,7 +141,7 @@ def encodepoint(P):
assert 0 <= y < (1<<255) # always < 0x7fff..ff
if x & 1:
y += 1<<255
return binascii.unhexlify("%064x" % y)[::-1]
return binascii.unhexlify(f"{y:064x}")[::-1]
def isoncurve(P):
x = P[0]
@ -192,7 +192,7 @@ def password_to_scalar(pw):
def scalar_to_bytes(y):
y = y % L
assert 0 <= y < 2**256
return binascii.unhexlify("%064x" % y)[::-1]
return binascii.unhexlify(f"{y:064x}")[::-1]
# Elements, of various orders

View File

@ -39,7 +39,7 @@ class BadPrefixError(Exception):
def remove_prefix(s_bytes, prefix):
assert(type(s_bytes) == type(prefix))
if s_bytes[:len(prefix)] != prefix:
raise BadPrefixError("did not see expected '%s' prefix" % (prefix,))
raise BadPrefixError(f"did not see expected '{prefix}' prefix")
return s_bytes[len(prefix):]
def to_ascii(s_bytes, prefix="", encoding="base64"):
@ -93,7 +93,7 @@ def from_ascii(s_ascii, prefix="", encoding="base64"):
raise NotImplementedError
return s_bytes
class SigningKey(object):
class SigningKey:
# this can only be used to reconstruct a key created by create_keypair().
def __init__(self, sk_s, prefix="", encoding=None):
assert isinstance(sk_s, bytes)
@ -150,7 +150,7 @@ class SigningKey(object):
return to_ascii(sig_out, prefix, encoding)
return prefix+sig_out
class VerifyingKey(object):
class VerifyingKey:
def __init__(self, vk_s, prefix="", encoding=None):
if not isinstance(prefix, bytes):
prefix = prefix.encode('ascii')

View File

@ -78,16 +78,16 @@ def sign(skbytes, msg):
"""Return just the signature, given the message and just the secret
key."""
if len(skbytes) != 32:
raise ValueError("Bad signing key length %d" % len(skbytes))
raise ValueError(f"Bad signing key length {len(skbytes)}")
vkbytes = create_verifying_key(skbytes)
sig = signature(msg, skbytes, vkbytes)
return sig
def verify(vkbytes, sig, msg):
if len(vkbytes) != 32:
raise ValueError("Bad verifying key length %d" % len(vkbytes))
raise ValueError(f"Bad verifying key length {len(vkbytes)}")
if len(sig) != 64:
raise ValueError("Bad signature length %d" % len(sig))
raise ValueError(f"Bad signature length {len(sig)}")
rc = checkvalid(sig, msg, vkbytes)
if not rc:
raise ValueError("rc != 0", rc)

View File

@ -96,10 +96,10 @@ class Destination:
name = app_name
for aspect in aspects:
if "." in aspect: raise ValueError("Dots can't be used in aspects")
name += "." + aspect
name += f".{aspect}"
if identity != None:
name += "." + identity.hexhash
name += f".{identity.hexhash}"
return name
@ -140,7 +140,7 @@ class Destination:
def __init__(self, identity, direction, type, app_name, *aspects):
# 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 direction in Destination.directions: raise ValueError("Unknown destination direction")
@ -192,7 +192,7 @@ class Destination:
"""
:returns: A human-readable representation of the destination including addressable hash and full name.
"""
return "<"+self.name+"/"+self.hexhash+">"
return f"<{self.name}/{self.hexhash}>"
def _clean_ratchets(self):
if self.ratchets != None:
@ -210,13 +210,13 @@ class Destination:
except Exception as e:
self.ratchets = None
self.ratchets_path = None
raise OSError("Could not write ratchet file contents for "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
raise OSError(f"Could not write ratchet file contents for {self}. The contained exception was: {e}", RNS.LOG_ERROR)
def rotate_ratchets(self):
if self.ratchets != None:
now = time.time()
if now > self.latest_ratchet_time+self.ratchet_interval:
RNS.log("Rotating ratchets for "+str(self), RNS.LOG_DEBUG)
RNS.log(f"Rotating ratchets for {self}", RNS.LOG_DEBUG)
new_ratchet = RNS.Identity._generate_ratchet()
self.ratchets.insert(0, new_ratchet)
self.latest_ratchet_time = now
@ -224,7 +224,7 @@ class Destination:
self._persist_ratchets()
return True
else:
raise SystemError("Cannot rotate ratchet on "+str(self)+", ratchets are not enabled")
raise SystemError(f"Cannot rotate ratchet on {self}, ratchets are not enabled")
return False
@ -241,7 +241,7 @@ class Destination:
if self.direction != Destination.IN:
raise TypeError("Only IN destination types can be announced")
ratchet = b""
now = time.time()
stale_responses = []
@ -262,9 +262,9 @@ class Destination:
# received via multiple paths. The difference in reception time will
# potentially also be useful in determining characteristics of the
# multiple available paths, and to choose the best one.
RNS.log("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]
else:
destination_hash = self.hash
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()
if isinstance(returned_app_data, bytes):
app_data = returned_app_data
signed_data = self.hash+self.identity.get_public_key()+self.name_hash+random_hash+ratchet
if app_data != None:
signed_data += app_data
@ -413,7 +413,7 @@ class Destination:
try:
self.callbacks.packet(plaintext, packet)
except Exception as e:
RNS.log("Error while executing receive callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while executing receive callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
def incoming_link_request(self, data, packet):
if self.accept_link_requests:
@ -437,9 +437,9 @@ class Destination:
except Exception as e:
self.ratchets = None
self.ratchets_path = None
raise OSError("Could not read ratchet file contents for "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
raise OSError(f"Could not read ratchet file contents for {self}. The contained exception was: {e}", RNS.LOG_ERROR)
else:
RNS.log("No existing ratchet data found, initialising new ratchet file for "+str(self), RNS.LOG_DEBUG)
RNS.log(f"No existing ratchet data found, initialising new ratchet file for {self}", RNS.LOG_DEBUG)
self.ratchets = []
self.ratchets_path = ratchets_path
self._persist_ratchets()
@ -464,11 +464,11 @@ class Destination:
self._reload_ratchets(ratchets_path)
# TODO: Remove at some point
RNS.log("Ratchets enabled on "+str(self), RNS.LOG_DEBUG)
RNS.log(f"Ratchets enabled on {self}", RNS.LOG_DEBUG)
return True
else:
raise ValueError("No ratchet file path specified for "+str(self))
raise ValueError(f"No ratchet file path specified for {self}")
def enforce_ratchets(self):
"""
@ -478,7 +478,7 @@ class Destination:
"""
if self.ratchets != None:
self.__enforce_ratchets = True
RNS.log("Ratchets enforced on "+str(self), RNS.LOG_DEBUG)
RNS.log(f"Ratchets enforced on {self}", RNS.LOG_DEBUG)
return True
else:
return False
@ -586,7 +586,7 @@ class Destination:
return self.prv.encrypt(plaintext)
except Exception as e:
RNS.log("The GROUP destination could not encrypt data", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
else:
raise ValueError("No private key held by GROUP destination. Did you create or load one?")
@ -630,7 +630,7 @@ class Destination:
return self.prv.decrypt(ciphertext)
except Exception as e:
RNS.log("The GROUP destination could not decrypt data", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
else:
raise ValueError("No private key held by GROUP destination. Did you create or load one?")

View File

@ -87,7 +87,7 @@ class Identity:
@staticmethod
def remember(packet_hash, destination_hash, public_key, app_data = None):
if len(public_key) != Identity.KEYSIZE//8:
raise TypeError("Can't remember "+RNS.prettyhexrep(destination_hash)+", the public key size of "+str(len(public_key))+" is not valid.", RNS.LOG_ERROR)
raise TypeError(f"Can't remember {RNS.prettyhexrep(destination_hash)}, the public key size of {len(public_key)} is not valid.", RNS.LOG_ERROR)
else:
Identity.known_destinations[destination_hash] = [time.time(), packet_hash, public_key, app_data]
@ -137,7 +137,7 @@ class Identity:
# save, but the only changes. It might be possible to
# simply overwrite on exit now that every local client
# disconnect triggers a data persist.
try:
if hasattr(Identity, "saving_known_destinations"):
wait_interval = 0.2
@ -153,9 +153,9 @@ class Identity:
save_start = time.time()
storage_known_destinations = {}
if os.path.isfile(RNS.Reticulum.storagepath+"/known_destinations"):
if os.path.isfile(f"{RNS.Reticulum.storagepath}/known_destinations"):
try:
file = open(RNS.Reticulum.storagepath+"/known_destinations","rb")
file = open(f"{RNS.Reticulum.storagepath}/known_destinations","rb")
storage_known_destinations = umsgpack.load(file)
file.close()
except:
@ -166,32 +166,32 @@ class Identity:
if not destination_hash in Identity.known_destinations:
Identity.known_destinations[destination_hash] = storage_known_destinations[destination_hash]
except Exception as e:
RNS.log("Skipped recombining known destinations from disk, since an error occurred: "+str(e), RNS.LOG_WARNING)
RNS.log(f"Skipped recombining known destinations from disk, since an error occurred: {e}", RNS.LOG_WARNING)
RNS.log("Saving "+str(len(Identity.known_destinations))+" known destinations to storage...", RNS.LOG_DEBUG)
file = open(RNS.Reticulum.storagepath+"/known_destinations","wb")
RNS.log(f"Saving {len(Identity.known_destinations)} known destinations to storage...", RNS.LOG_DEBUG)
file = open(f"{RNS.Reticulum.storagepath}/known_destinations","wb")
umsgpack.dump(Identity.known_destinations, file)
file.close()
save_time = time.time() - save_start
if save_time < 1:
time_str = str(round(save_time*1000,2))+"ms"
time_str = f"{round(save_time * 1000, 2)}ms"
else:
time_str = str(round(save_time,2))+"s"
time_str = f"{round(save_time, 2)}s"
RNS.log("Saved known destinations to storage in "+time_str, RNS.LOG_DEBUG)
RNS.log(f"Saved known destinations to storage in {time_str}", RNS.LOG_DEBUG)
except Exception as e:
RNS.log("Error while saving known destinations to disk, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while saving known destinations to disk, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.trace_exception(e)
Identity.saving_known_destinations = False
@staticmethod
def load_known_destinations():
if os.path.isfile(RNS.Reticulum.storagepath+"/known_destinations"):
if os.path.isfile(f"{RNS.Reticulum.storagepath}/known_destinations"):
try:
file = open(RNS.Reticulum.storagepath+"/known_destinations","rb")
file = open(f"{RNS.Reticulum.storagepath}/known_destinations","rb")
loaded_known_destinations = umsgpack.load(file)
file.close()
@ -200,7 +200,7 @@ class Identity:
if len(known_destination) == RNS.Reticulum.TRUNCATED_HASHLENGTH//8:
Identity.known_destinations[known_destination] = loaded_known_destinations[known_destination]
RNS.log("Loaded "+str(len(Identity.known_destinations))+" known destination from storage", RNS.LOG_VERBOSE)
RNS.log(f"Loaded {len(Identity.known_destinations)} known destination from storage", RNS.LOG_VERBOSE)
except Exception as e:
RNS.log("Error loading known destinations from disk, file will be recreated on exit", RNS.LOG_ERROR)
@ -278,8 +278,8 @@ class Identity:
hexhash = RNS.hexrep(destination_hash, delimit=False)
ratchet_data = {"ratchet": ratchet, "received": time.time()}
ratchetdir = RNS.Reticulum.storagepath+"/ratchets"
ratchetdir = f"{RNS.Reticulum.storagepath}/ratchets"
if not os.path.isdir(ratchetdir):
os.makedirs(ratchetdir)
@ -290,7 +290,7 @@ class Identity:
ratchet_file.close()
os.replace(outpath, finalpath)
threading.Thread(target=persist_job, daemon=True).start()
except Exception as e:
@ -303,7 +303,7 @@ class Identity:
RNS.log("Cleaning ratchets...", RNS.LOG_DEBUG)
try:
now = time.time()
ratchetdir = RNS.Reticulum.storagepath+"/ratchets"
ratchetdir = f"{RNS.Reticulum.storagepath}/ratchets"
if os.path.isdir(ratchetdir):
for filename in os.listdir(ratchetdir):
try:
@ -326,7 +326,7 @@ class Identity:
@staticmethod
def get_ratchet(destination_hash):
if not destination_hash in Identity.known_ratchets:
ratchetdir = RNS.Reticulum.storagepath+"/ratchets"
ratchetdir = f"{RNS.Reticulum.storagepath}/ratchets"
hexhash = RNS.hexrep(destination_hash, delimit=False)
ratchet_path = f"{ratchetdir}/{hexhash}"
if os.path.isfile(ratchet_path):
@ -337,7 +337,7 @@ class Identity:
Identity.known_ratchets[destination_hash] = ratchet_data["ratchet"]
else:
return None
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"The contained exception was: {e}", RNS.LOG_ERROR)
@ -417,19 +417,19 @@ class Identity:
if packet.rssi != None or packet.snr != None:
signal_str = " ["
if packet.rssi != None:
signal_str += "RSSI "+str(packet.rssi)+"dBm"
signal_str += f"RSSI {packet.rssi}dBm"
if packet.snr != None:
signal_str += ", "
if packet.snr != None:
signal_str += "SNR "+str(packet.snr)+"dB"
signal_str += f"SNR {packet.snr}dB"
signal_str += "]"
else:
signal_str = ""
if hasattr(packet, "transport_id") and packet.transport_id != None:
RNS.log("Valid announce for "+RNS.prettyhexrep(destination_hash)+" "+str(packet.hops)+" hops away, received via "+RNS.prettyhexrep(packet.transport_id)+" on "+str(packet.receiving_interface)+signal_str, RNS.LOG_EXTREME)
RNS.log(f"Valid announce for {RNS.prettyhexrep(destination_hash)} {packet.hops} hops away, received via {RNS.prettyhexrep(packet.transport_id)} on {packet.receiving_interface}{signal_str}", RNS.LOG_EXTREME)
else:
RNS.log("Valid announce for "+RNS.prettyhexrep(destination_hash)+" "+str(packet.hops)+" hops away, received on "+str(packet.receiving_interface)+signal_str, RNS.LOG_EXTREME)
RNS.log(f"Valid announce for {RNS.prettyhexrep(destination_hash)} {packet.hops} hops away, received on {packet.receiving_interface}{signal_str}", RNS.LOG_EXTREME)
if ratchet:
Identity._remember_ratchet(destination_hash, ratchet)
@ -437,16 +437,16 @@ class Identity:
return True
else:
RNS.log("Received invalid announce for "+RNS.prettyhexrep(destination_hash)+": Destination mismatch.", RNS.LOG_DEBUG)
RNS.log(f"Received invalid announce for {RNS.prettyhexrep(destination_hash)}: Destination mismatch.", RNS.LOG_DEBUG)
return False
else:
RNS.log("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
return False
except Exception as e:
RNS.log("Error occurred while validating announce. The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error occurred while validating announce. The contained exception was: {e}", RNS.LOG_ERROR)
return False
@staticmethod
@ -505,8 +505,8 @@ class Identity:
return True
return False
except Exception as e:
RNS.log("Error while saving identity to "+str(path), RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e))
RNS.log(f"Error while saving identity to {path}", RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}")
def __init__(self,create_keys=True):
# Initialize keys to none
@ -541,7 +541,7 @@ class Identity:
self.update_hashes()
RNS.log("Identity keys created for "+RNS.prettyhexrep(self.hash), RNS.LOG_VERBOSE)
RNS.log(f"Identity keys created for {RNS.prettyhexrep(self.hash)}", RNS.LOG_VERBOSE)
def get_private_key(self):
"""
@ -567,7 +567,7 @@ class Identity:
self.prv = X25519PrivateKey.from_private_bytes(self.prv_bytes)
self.sig_prv_bytes = prv_bytes[Identity.KEYSIZE//8//2:]
self.sig_prv = Ed25519PrivateKey.from_private_bytes(self.sig_prv_bytes)
self.pub = self.prv.public_key()
self.pub_bytes = self.pub.public_bytes()
@ -581,7 +581,7 @@ class Identity:
except Exception as e:
raise e
RNS.log("Failed to load identity key", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
return False
def load_public_key(self, pub_bytes):
@ -600,7 +600,7 @@ class Identity:
self.update_hashes()
except Exception as e:
RNS.log("Error while loading public key, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while loading public key, the contained exception was: {e}", RNS.LOG_ERROR)
def update_hashes(self):
self.hash = Identity.truncated_hash(self.get_public_key())
@ -613,8 +613,8 @@ class Identity:
return self.load_private_key(prv_bytes)
return False
except Exception as e:
RNS.log("Error while loading identity from "+str(path), RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while loading identity from {path}", RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
def get_salt(self):
return self.hash
@ -640,7 +640,7 @@ class Identity:
target_public_key = self.pub
shared_key = ephemeral_key.exchange(target_public_key)
derived_key = RNS.Cryptography.hkdf(
length=32,
derive_from=shared_key,
@ -690,14 +690,14 @@ class Identity:
plaintext = fernet.decrypt(ciphertext)
if ratchet_id_receiver:
ratchet_id_receiver.latest_ratchet_id = ratchet_id
break
except Exception as e:
pass
if enforce_ratchets and plaintext == None:
RNS.log("Decryption with ratchet enforcement by "+RNS.prettyhexrep(self.hash)+" failed. Dropping packet.", RNS.LOG_DEBUG)
RNS.log(f"Decryption with ratchet enforcement by {RNS.prettyhexrep(self.hash)} failed. Dropping packet.", RNS.LOG_DEBUG)
if ratchet_id_receiver:
ratchet_id_receiver.latest_ratchet_id = None
return None
@ -717,10 +717,10 @@ class Identity:
ratchet_id_receiver.latest_ratchet_id = None
except Exception as e:
RNS.log("Decryption by "+RNS.prettyhexrep(self.hash)+" failed: "+str(e), RNS.LOG_DEBUG)
RNS.log(f"Decryption by {RNS.prettyhexrep(self.hash)} failed: {e}", RNS.LOG_DEBUG)
if ratchet_id_receiver:
ratchet_id_receiver.latest_ratchet_id = None
return plaintext;
else:
RNS.log("Decryption failed because the token size was invalid.", RNS.LOG_DEBUG)
@ -739,9 +739,9 @@ class Identity:
"""
if self.sig_prv != None:
try:
return self.sig_prv.sign(message)
return self.sig_prv.sign(message)
except Exception as e:
RNS.log("The identity "+str(self)+" could not sign the requested message. The contained exception was: "+str(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
else:
raise KeyError("Signing failed because identity does not hold a private key")
@ -770,7 +770,7 @@ class Identity:
proof_data = signature
else:
proof_data = packet.packet_hash + signature
if destination == None:
destination = packet.generate_proof_destination()

View File

@ -80,14 +80,14 @@ class AX25KISSInterface(Interface):
super().__init__()
self.HW_MTU = 564
self.pyserial = serial
self.serial = None
self.owner = owner
self.name = name
self.src_call = callsign.upper().encode("ascii")
self.src_ssid = ssid
self.dst_call = "APZRNS".encode("ascii")
self.dst_call = b"APZRNS"
self.dst_ssid = 0
self.port = port
self.speed = speed
@ -105,10 +105,10 @@ class AX25KISSInterface(Interface):
self.flow_control_locked = time.time()
if (len(self.src_call) < 3 or len(self.src_call) > 6):
raise ValueError("Invalid callsign for "+str(self))
raise ValueError(f"Invalid callsign for {self}")
if (self.src_ssid < 0 or self.src_ssid > 15):
raise ValueError("Invalid SSID for "+str(self))
raise ValueError(f"Invalid SSID for {self}")
self.preamble = preamble if preamble != None else 350;
self.txtail = txtail if txtail != None else 20;
@ -124,16 +124,16 @@ class AX25KISSInterface(Interface):
try:
self.open_port()
except Exception as e:
RNS.log("Could not open serial port for interface "+str(self), RNS.LOG_ERROR)
RNS.log(f"Could not open serial port for interface {self}", RNS.LOG_ERROR)
raise e
if self.serial.is_open:
self.configure_device()
else:
raise IOError("Could not open serial port")
raise OSError("Could not open serial port")
def open_port(self):
RNS.log("Opening serial port "+self.port+"...", RNS.LOG_VERBOSE)
RNS.log(f"Opening serial port {self.port}...", RNS.LOG_VERBOSE)
self.serial = self.pyserial.Serial(
port = self.port,
baudrate = self.speed,
@ -155,7 +155,7 @@ class AX25KISSInterface(Interface):
thread.daemon = True
thread.start()
self.online = True
RNS.log("Serial port "+self.port+" is now open")
RNS.log(f"Serial port {self.port} is now open")
RNS.log("Configuring AX.25 KISS interface parameters...")
self.setPreamble(self.preamble)
self.setTxTail(self.txtail)
@ -176,7 +176,7 @@ class AX25KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXDELAY])+bytes([preamble])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure AX.25 KISS interface preamble to "+str(preamble_ms)+" (command value "+str(preamble)+")")
raise OSError(f"Could not configure AX.25 KISS interface preamble to {preamble_ms} (command value {preamble})")
def setTxTail(self, txtail):
txtail_ms = txtail
@ -189,7 +189,7 @@ class AX25KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXTAIL])+bytes([txtail])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure AX.25 KISS interface TX tail to "+str(txtail_ms)+" (command value "+str(txtail)+")")
raise OSError(f"Could not configure AX.25 KISS interface TX tail to {txtail_ms} (command value {txtail})")
def setPersistence(self, persistence):
if persistence < 0:
@ -200,7 +200,7 @@ class AX25KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_P])+bytes([persistence])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure AX.25 KISS interface persistence to "+str(persistence))
raise OSError(f"Could not configure AX.25 KISS interface persistence to {persistence}")
def setSlotTime(self, slottime):
slottime_ms = slottime
@ -213,16 +213,16 @@ class AX25KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SLOTTIME])+bytes([slottime])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure AX.25 KISS interface slot time to "+str(slottime_ms)+" (command value "+str(slottime)+")")
raise OSError(f"Could not configure AX.25 KISS interface slot time to {slottime_ms} (command value {slottime})")
def setFlowControl(self, flow_control):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_READY])+bytes([0x01])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
if (flow_control):
raise IOError("Could not enable AX.25 KISS interface flow control")
raise OSError("Could not enable AX.25 KISS interface flow control")
else:
raise IOError("Could not enable AX.25 KISS interface flow control")
raise OSError("Could not enable AX.25 KISS interface flow control")
def processIncoming(self, data):
@ -270,7 +270,7 @@ class AX25KISSInterface(Interface):
if written != len(kiss_frame):
if self.flow_control:
self.interface_ready = True
raise IOError("AX.25 interface only wrote "+str(written)+" bytes of "+str(len(kiss_frame)))
raise OSError(f"AX.25 interface only wrote {written} bytes of {len(kiss_frame)}")
else:
self.queue(data)
@ -336,14 +336,14 @@ class AX25KISSInterface(Interface):
if self.flow_control:
if not self.interface_ready:
if time.time() > self.flow_control_locked + self.flow_control_timeout:
RNS.log("Interface "+str(self)+" is unlocking flow control due to time-out. This should not happen. Your hardware might have missed a flow-control READY command, or maybe it does not support flow-control.", RNS.LOG_WARNING)
RNS.log(f"Interface {self} is unlocking flow control due to time-out. This should not happen. Your hardware might have missed a flow-control READY command, or maybe it does not support flow-control.", RNS.LOG_WARNING)
self.process_queue()
except Exception as e:
self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", 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)
if RNS.Reticulum.panic_on_interface_error:
RNS.panic()
@ -357,17 +357,17 @@ class AX25KISSInterface(Interface):
while not self.online:
try:
time.sleep(5)
RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_VERBOSE)
RNS.log(f"Attempting to reconnect serial port {self.port} for {self}...", RNS.LOG_VERBOSE)
self.open_port()
if self.serial.is_open:
self.configure_device()
except Exception as e:
RNS.log("Error while reconnecting port, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while reconnecting port, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("Reconnected serial port for "+str(self))
RNS.log(f"Reconnected serial port for {self}")
def should_ingress_limit(self):
return False
def __str__(self):
return "AX25KISSInterface["+self.name+"]"
return f"AX25KISSInterface[{self.name}]"

View File

@ -74,7 +74,7 @@ class KISSInterface(Interface):
from usbserial4a import serial4a as serial
self.parity = "N"
else:
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)
@ -83,9 +83,9 @@ class KISSInterface(Interface):
raise SystemError("Android-specific interface was used on non-Android OS")
super().__init__()
self.HW_MTU = 564
if beacon_data == None:
beacon_data = ""
@ -125,17 +125,17 @@ class KISSInterface(Interface):
try:
self.open_port()
except Exception as e:
RNS.log("Could not open serial port "+self.port, RNS.LOG_ERROR)
RNS.log(f"Could not open serial port {self.port}", RNS.LOG_ERROR)
raise e
if self.serial.is_open:
self.configure_device()
else:
raise IOError("Could not open serial port")
raise OSError("Could not open serial port")
def open_port(self):
RNS.log("Opening serial port "+self.port+"...")
RNS.log(f"Opening serial port {self.port}...")
# Get device parameters
from usb4a import usb
device = usb.get_usb_device(self.port)
@ -147,7 +147,7 @@ class KISSInterface(Interface):
proxy = self.pyserial.get_serial_port
if vid == 0x1A86 and pid == 0x55D4:
# Force CDC driver for Qinheng CH34x
RNS.log(str(self)+" using CDC driver for "+RNS.hexrep(vid)+":"+RNS.hexrep(pid), RNS.LOG_DEBUG)
RNS.log(f"{self} using CDC driver for {RNS.hexrep(vid)}:{RNS.hexrep(pid)}", RNS.LOG_DEBUG)
from usbserial4a.cdcacmserial4a import CdcAcmSerial
proxy = CdcAcmSerial
@ -172,7 +172,7 @@ class KISSInterface(Interface):
self.serial.timeout = 0.1
elif vid == 0x10C4:
# 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.timeout = 0.012
elif vid == 0x1A86 and pid == 0x55D4:
@ -186,9 +186,9 @@ class KISSInterface(Interface):
self.serial.USB_READ_TIMEOUT_MILLIS = 100
self.serial.timeout = 0.1
RNS.log(str(self)+" USB read buffer size set to "+RNS.prettysize(self.serial.DEFAULT_READ_BUFFER_SIZE), RNS.LOG_DEBUG)
RNS.log(str(self)+" USB read timeout set to "+str(self.serial.USB_READ_TIMEOUT_MILLIS)+"ms", RNS.LOG_DEBUG)
RNS.log(str(self)+" USB write timeout set to "+str(self.serial.USB_WRITE_TIMEOUT_MILLIS)+"ms", RNS.LOG_DEBUG)
RNS.log(f"{self} USB read buffer size set to {RNS.prettysize(self.serial.DEFAULT_READ_BUFFER_SIZE)}", RNS.LOG_DEBUG)
RNS.log(f"{self} USB read timeout set to {self.serial.USB_READ_TIMEOUT_MILLIS}ms", RNS.LOG_DEBUG)
RNS.log(f"{self} USB write timeout set to {self.serial.USB_WRITE_TIMEOUT_MILLIS}ms", RNS.LOG_DEBUG)
def configure_device(self):
# Allow time for interface to initialise before config
@ -197,7 +197,7 @@ class KISSInterface(Interface):
thread.daemon = True
thread.start()
self.online = True
RNS.log("Serial port "+self.port+" is now open")
RNS.log(f"Serial port {self.port} is now open")
RNS.log("Configuring KISS interface parameters...")
self.setPreamble(self.preamble)
self.setTxTail(self.txtail)
@ -218,7 +218,7 @@ class KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXDELAY])+bytes([preamble])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure KISS interface preamble to "+str(preamble_ms)+" (command value "+str(preamble)+")")
raise OSError(f"Could not configure KISS interface preamble to {preamble_ms} (command value {preamble})")
def setTxTail(self, txtail):
txtail_ms = txtail
@ -231,7 +231,7 @@ class KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXTAIL])+bytes([txtail])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure KISS interface TX tail to "+str(txtail_ms)+" (command value "+str(txtail)+")")
raise OSError(f"Could not configure KISS interface TX tail to {txtail_ms} (command value {txtail})")
def setPersistence(self, persistence):
if persistence < 0:
@ -242,7 +242,7 @@ class KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_P])+bytes([persistence])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure KISS interface persistence to "+str(persistence))
raise OSError(f"Could not configure KISS interface persistence to {persistence}")
def setSlotTime(self, slottime):
slottime_ms = slottime
@ -255,16 +255,16 @@ class KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SLOTTIME])+bytes([slottime])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure KISS interface slot time to "+str(slottime_ms)+" (command value "+str(slottime)+")")
raise OSError(f"Could not configure KISS interface slot time to {slottime_ms} (command value {slottime})")
def setFlowControl(self, flow_control):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_READY])+bytes([0x01])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
if (flow_control):
raise IOError("Could not enable KISS interface flow control")
raise OSError("Could not enable KISS interface flow control")
else:
raise IOError("Could not enable KISS interface flow control")
raise OSError("Could not enable KISS interface flow control")
def processIncoming(self, data):
@ -295,7 +295,7 @@ class KISSInterface(Interface):
self.first_tx = time.time()
if written != len(frame):
raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data)))
raise OSError(f"Serial interface only wrote {written} bytes of {len(data)}")
else:
self.queue(data)
@ -352,7 +352,7 @@ class KISSInterface(Interface):
data_buffer = data_buffer+bytes([byte])
elif (command == KISS.CMD_READY):
self.process_queue()
if got == 0:
time_since_last = int(time.time()*1000) - last_read_ms
if len(data_buffer) > 0 and time_since_last > self.timeout:
@ -365,21 +365,21 @@ class KISSInterface(Interface):
if self.flow_control:
if not self.interface_ready:
if time.time() > self.flow_control_locked + self.flow_control_timeout:
RNS.log("Interface "+str(self)+" is unlocking flow control due to time-out. This should not happen. Your hardware might have missed a flow-control READY command, or maybe it does not support flow-control.", RNS.LOG_WARNING)
RNS.log(f"Interface {self} is unlocking flow control due to time-out. This should not happen. Your hardware might have missed a flow-control READY command, or maybe it does not support flow-control.", RNS.LOG_WARNING)
self.process_queue()
if self.beacon_i != None and self.beacon_d != None:
if self.first_tx != None:
if time.time() > self.first_tx + self.beacon_i:
RNS.log("Interface "+str(self)+" is transmitting beacon data: "+str(self.beacon_d.decode("utf-8")), RNS.LOG_DEBUG)
RNS.log(f"Interface {self} is transmitting beacon data: {self.beacon_d.decode('utf-8')}", RNS.LOG_DEBUG)
self.first_tx = None
self.processOutgoing(self.beacon_d)
except Exception as e:
self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", 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)
if RNS.Reticulum.panic_on_interface_error:
RNS.panic()
@ -393,17 +393,17 @@ class KISSInterface(Interface):
while not self.online:
try:
time.sleep(5)
RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_VERBOSE)
RNS.log(f"Attempting to reconnect serial port {self.port} for {self}...", RNS.LOG_VERBOSE)
self.open_port()
if self.serial.is_open:
self.configure_device()
except Exception as e:
RNS.log("Error while reconnecting port, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while reconnecting port, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("Reconnected serial port for "+str(self))
RNS.log(f"Reconnected serial port for {self}")
def should_ingress_limit(self):
return False
def __str__(self):
return "KISSInterface["+self.name+"]"
return f"KISSInterface[{self.name}]"

View File

@ -42,7 +42,7 @@ class KISS():
FESC = 0xDB
TFEND = 0xDC
TFESC = 0xDD
CMD_UNKNOWN = 0xFE
CMD_DATA = 0x00
CMD_FREQUENCY = 0x01
@ -78,11 +78,11 @@ class KISS():
DETECT_REQ = 0x73
DETECT_RESP = 0x46
RADIO_STATE_OFF = 0x00
RADIO_STATE_ON = 0x01
RADIO_STATE_ASK = 0xFF
CMD_ERROR = 0x90
ERROR_INITRADIO = 0x01
ERROR_TXFAILED = 0x02
@ -172,7 +172,7 @@ class AndroidBluetoothManager():
try:
self.rfcomm_socket = device.createRfcommSocketToServiceRecord(self.bt_rfcomm_service_record)
if self.rfcomm_socket == None:
raise IOError("Bluetooth stack returned no socket object")
raise OSError("Bluetooth stack returned no socket object")
else:
if not self.rfcomm_socket.isConnected():
try:
@ -181,20 +181,20 @@ class AndroidBluetoothManager():
self.rfcomm_writer = self.rfcomm_socket.getOutputStream()
self.connected = True
self.connected_device = device
RNS.log("Bluetooth device "+str(self.connected_device.getName())+" "+str(self.connected_device.getAddress())+" connected.")
RNS.log(f"Bluetooth device {self.connected_device.getName()} {self.connected_device.getAddress()} connected.")
except Exception as e:
raise IOError("The Bluetooth RFcomm socket could not be connected: "+str(e))
raise OSError(f"The Bluetooth RFcomm socket could not be connected: {e}")
except Exception as e:
RNS.log("Could not create and connect Bluetooth RFcomm socket for "+str(device.getName())+" "+str(device.getAddress()), RNS.LOG_EXTREME)
RNS.log("The contained exception was: "+str(e), RNS.LOG_EXTREME)
RNS.log(f"Could not create and connect Bluetooth RFcomm socket for {device.getName()} {device.getAddress()}", RNS.LOG_EXTREME)
RNS.log(f"The contained exception was: {e}", RNS.LOG_EXTREME)
def close(self):
if self.connected:
if self.rfcomm_reader != None:
self.rfcomm_reader.close()
self.rfcomm_reader = None
if self.rfcomm_writer != None:
self.rfcomm_writer.close()
self.rfcomm_writer = None
@ -209,7 +209,7 @@ class AndroidBluetoothManager():
def read(self, len = None):
if self.connection_failed:
raise IOError("Bluetooth connection failed")
raise OSError("Bluetooth connection failed")
else:
if self.connected and self.rfcomm_reader != None:
available = self.rfcomm_reader.available()
@ -223,7 +223,7 @@ class AndroidBluetoothManager():
else:
return bytes([])
else:
raise IOError("No RFcomm socket available")
raise OSError("No RFcomm socket available")
def write(self, data):
try:
@ -231,7 +231,7 @@ class AndroidBluetoothManager():
self.rfcomm_writer.flush()
return len(data)
except Exception as e:
RNS.log("Bluetooth connection failed for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Bluetooth connection failed for {self}", RNS.LOG_ERROR)
self.connection_failed = True
return 0
@ -274,7 +274,7 @@ class RNodeInterface(Interface):
bluetooth_state = 0x00
if port != None:
RNS.log("Opening serial port "+port+"...")
RNS.log(f"Opening serial port {port}...")
# Get device parameters
from usb4a import usb
device = usb.get_usb_device(port)
@ -287,7 +287,7 @@ class RNodeInterface(Interface):
proxy = pyserial.get_serial_port
if vid == 0x1A86 and pid == 0x55D4:
# Force CDC driver for Qinheng CH34x
RNS.log("Using CDC driver for "+RNS.hexrep(vid)+":"+RNS.hexrep(pid), RNS.LOG_DEBUG)
RNS.log(f"Using CDC driver for {RNS.hexrep(vid)}:{RNS.hexrep(pid)}", RNS.LOG_DEBUG)
from usbserial4a.cdcacmserial4a import CdcAcmSerial
proxy = CdcAcmSerial
@ -371,7 +371,7 @@ class RNodeInterface(Interface):
else:
self.bt_manager = None
else:
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)
@ -382,7 +382,7 @@ class RNodeInterface(Interface):
super().__init__()
self.HW_MTU = 508
self.pyserial = serial
self.serial = None
self.owner = owner
@ -466,31 +466,31 @@ class RNodeInterface(Interface):
self.validcfg = True
if (self.frequency < RNodeInterface.FREQ_MIN or self.frequency > RNodeInterface.FREQ_MAX):
RNS.log("Invalid frequency configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid frequency configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if (self.txpower < 0 or self.txpower > 22):
RNS.log("Invalid TX power configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid TX power configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if (self.bandwidth < 7800 or self.bandwidth > 500000):
RNS.log("Invalid bandwidth configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid bandwidth configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if (self.sf < 5 or self.sf > 12):
RNS.log("Invalid spreading factor configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid spreading factor configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if (self.cr < 5 or self.cr > 8):
RNS.log("Invalid coding rate configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid coding rate configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if (self.st_alock and (self.st_alock < 0.0 or self.st_alock > 100.0)):
RNS.log("Invalid short-term airtime limit configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid short-term airtime limit configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if (self.lt_alock and (self.lt_alock < 0.0 or self.lt_alock > 100.0)):
RNS.log("Invalid long-term airtime limit configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid long-term airtime limit configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if id_interval != None and id_callsign != None:
@ -499,14 +499,14 @@ class RNodeInterface(Interface):
self.id_callsign = id_callsign.encode("utf-8")
self.id_interval = id_interval
else:
RNS.log("The encoded ID callsign for "+str(self)+" exceeds the max length of "+str(RNodeInterface.CALLSIGN_MAX_LEN)+" bytes.", RNS.LOG_ERROR)
RNS.log(f"The encoded ID callsign for {self} exceeds the max length of {RNodeInterface.CALLSIGN_MAX_LEN} bytes.", RNS.LOG_ERROR)
self.validcfg = False
else:
self.id_interval = None
self.id_callsign = None
if (not self.validcfg):
raise ValueError("The configuration for "+str(self)+" contains errors, interface is offline")
raise ValueError(f"The configuration for {self} contains errors, interface is offline")
try:
self.open_port()
@ -515,18 +515,18 @@ class RNodeInterface(Interface):
if self.serial.is_open:
self.configure_device()
else:
raise IOError("Could not open serial port")
raise OSError("Could not open serial port")
elif self.bt_manager != None:
if self.bt_manager.connected:
self.configure_device()
else:
raise IOError("Could not connect to any Bluetooth devices")
raise OSError("Could not connect to any Bluetooth devices")
else:
raise IOError("Neither serial port nor Bluetooth devices available")
raise OSError("Neither serial port nor Bluetooth devices available")
except Exception as e:
RNS.log("Could not open serial port for interface "+str(self), RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Could not open serial port for interface {self}", RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
if len(self.hw_errors) == 0:
RNS.log("Reticulum will attempt to bring up this interface periodically", RNS.LOG_ERROR)
thread = threading.Thread(target=self.reconnect_port, daemon=True).start()
@ -538,7 +538,7 @@ class RNodeInterface(Interface):
elif self.bt_manager != None:
return self.bt_manager.read()
else:
raise IOError("No ports available for reading")
raise OSError("No ports available for reading")
def write_mux(self, data):
if self.serial != None:
@ -551,7 +551,7 @@ class RNodeInterface(Interface):
self.last_port_io = time.time()
return written
else:
raise IOError("No ports available for writing")
raise OSError("No ports available for writing")
# def reset_ble(self):
# RNS.log(f"Clearing previous connection instance: "+str(self.ble))
@ -561,11 +561,11 @@ class RNodeInterface(Interface):
# self.ble = BLEConnection(owner=self, target_name=self.ble_name, target_bt_addr=self.ble_addr)
# self.serial = self.ble
# RNS.log(f"New connection instance: "+str(self.ble))
def open_port(self):
if not self.use_ble:
if self.port != None:
RNS.log("Opening serial port "+self.port+"...")
RNS.log(f"Opening serial port {self.port}...")
# Get device parameters
from usb4a import usb
device = usb.get_usb_device(self.port)
@ -577,7 +577,7 @@ class RNodeInterface(Interface):
proxy = self.pyserial.get_serial_port
if vid == 0x1A86 and pid == 0x55D4:
# Force CDC driver for Qinheng CH34x
RNS.log(str(self)+" using CDC driver for "+RNS.hexrep(vid)+":"+RNS.hexrep(pid), RNS.LOG_DEBUG)
RNS.log(f"{self} using CDC driver for {RNS.hexrep(vid)}:{RNS.hexrep(pid)}", RNS.LOG_DEBUG)
from usbserial4a.cdcacmserial4a import CdcAcmSerial
proxy = CdcAcmSerial
@ -602,7 +602,7 @@ class RNodeInterface(Interface):
self.serial.timeout = 0.1
elif vid == 0x10C4:
# 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.timeout = 0.012
elif vid == 0x1A86 and pid == 0x55D4:
@ -616,9 +616,9 @@ class RNodeInterface(Interface):
self.serial.USB_READ_TIMEOUT_MILLIS = 100
self.serial.timeout = 0.1
RNS.log(str(self)+" USB read buffer size set to "+RNS.prettysize(self.serial.DEFAULT_READ_BUFFER_SIZE), RNS.LOG_DEBUG)
RNS.log(str(self)+" USB read timeout set to "+str(self.serial.USB_READ_TIMEOUT_MILLIS)+"ms", RNS.LOG_DEBUG)
RNS.log(str(self)+" USB write timeout set to "+str(self.serial.USB_WRITE_TIMEOUT_MILLIS)+"ms", RNS.LOG_DEBUG)
RNS.log(f"{self} USB read buffer size set to {RNS.prettysize(self.serial.DEFAULT_READ_BUFFER_SIZE)}", RNS.LOG_DEBUG)
RNS.log(f"{self} USB read timeout set to {self.serial.USB_READ_TIMEOUT_MILLIS}ms", RNS.LOG_DEBUG)
RNS.log(f"{self} USB write timeout set to {self.serial.USB_WRITE_TIMEOUT_MILLIS}ms", RNS.LOG_DEBUG)
elif self.allow_bluetooth:
if self.bt_manager == None:
@ -660,17 +660,17 @@ class RNodeInterface(Interface):
RNS.log(f"RNode detect timed out over {self.port}", RNS.LOG_ERROR)
if not self.detected:
raise IOError("Could not detect device")
raise OSError("Could not detect device")
else:
if self.platform == KISS.PLATFORM_ESP32 or self.platform == KISS.PLATFORM_NRF52:
self.display = True
if not self.firmware_ok:
raise IOError("Invalid device firmware")
raise OSError("Invalid device firmware")
if self.serial != None and self.port != None:
self.timeout = 200
RNS.log("Serial port "+self.port+" is now open")
RNS.log(f"Serial port {self.port} is now open")
if self.bt_manager != None and self.bt_manager.connected:
self.timeout = 1500
@ -680,21 +680,21 @@ class RNodeInterface(Interface):
self.initRadio()
if (self.validateRadioState()):
self.interface_ready = True
RNS.log(str(self)+" is configured and powered up")
RNS.log(f"{self} is configured and powered up")
sleep(0.3)
self.online = True
else:
RNS.log("After configuring "+str(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("Aborting RNode startup", RNS.LOG_ERROR)
if self.serial != None:
self.serial.close()
if self.bt_manager != None:
self.bt_manager.close()
raise IOError("RNode interface did not pass configuration validation")
raise OSError("RNode interface did not pass configuration validation")
def initRadio(self):
self.setFrequency()
@ -702,22 +702,22 @@ class RNodeInterface(Interface):
self.setBandwidth()
time.sleep(0.15)
self.setTXPower()
time.sleep(0.15)
self.setSpreadingFactor()
time.sleep(0.15)
self.setCodingRate()
time.sleep(0.15)
self.setSTALock()
time.sleep(0.15)
self.setLTALock()
time.sleep(0.15)
self.setRadioState(KISS.RADIO_STATE_ON)
time.sleep(0.15)
@ -728,45 +728,45 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND, KISS.CMD_DETECT, KISS.DETECT_REQ, KISS.FEND, KISS.CMD_FW_VERSION, 0x00, KISS.FEND, KISS.CMD_PLATFORM, 0x00, KISS.FEND, KISS.CMD_MCU, 0x00, KISS.FEND])
written = self.write_mux(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while detecting hardware for "+str(self))
raise OSError(f"An IO error occurred while detecting hardware for {self}")
def leave(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND])
written = self.write_mux(kiss_command)
if written != len(kiss_command):
raise IOError("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):
kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x01, KISS.FEND])
written = self.write_mux(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while sending bluetooth enable command to device")
raise OSError("An IO error occurred while sending bluetooth enable command to device")
def disable_bluetooth(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x00, KISS.FEND])
written = self.write_mux(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while sending bluetooth disable command to device")
raise OSError("An IO error occurred while sending bluetooth disable command to device")
def bluetooth_pair(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x02, KISS.FEND])
written = self.write_mux(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while sending bluetooth pair command to device")
raise OSError("An IO error occurred while sending bluetooth pair command to device")
def enable_external_framebuffer(self):
if self.display != None:
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x01, KISS.FEND])
written = self.write_mux(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while enabling external framebuffer on device")
raise OSError("An IO error occurred while enabling external framebuffer on device")
def disable_external_framebuffer(self):
if self.display != None:
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x00, KISS.FEND])
written = self.write_mux(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while disabling external framebuffer on device")
raise OSError("An IO error occurred while disabling external framebuffer on device")
FB_PIXEL_WIDTH = 64
FB_BITS_PER_PIXEL = 1
@ -788,16 +788,16 @@ class RNodeInterface(Interface):
data = line_byte+line_data
escaped_data = KISS.escape(data)
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FB_WRITE])+escaped_data+bytes([KISS.FEND])
written = self.write_mux(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while writing framebuffer data device")
raise OSError("An IO error occurred while writing framebuffer data device")
def hard_reset(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND])
written = self.write_mux(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while restarting device")
raise OSError("An IO error occurred while restarting device")
sleep(4.0);
def setFrequency(self):
@ -810,7 +810,7 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FREQUENCY])+data+bytes([KISS.FEND])
written = self.write_mux(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring frequency for "+str(self))
raise OSError(f"An IO error occurred while configuring frequency for {self}")
def setBandwidth(self):
c1 = self.bandwidth >> 24
@ -822,28 +822,28 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_BANDWIDTH])+data+bytes([KISS.FEND])
written = self.write_mux(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring bandwidth for "+str(self))
raise OSError(f"An IO error occurred while configuring bandwidth for {self}")
def setTXPower(self):
txp = bytes([self.txpower])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXPOWER])+txp+bytes([KISS.FEND])
written = self.write_mux(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring TX power for "+str(self))
raise OSError(f"An IO error occurred while configuring TX power for {self}")
def setSpreadingFactor(self):
sf = bytes([self.sf])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SF])+sf+bytes([KISS.FEND])
written = self.write_mux(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring spreading factor for "+str(self))
raise OSError(f"An IO error occurred while configuring spreading factor for {self}")
def setCodingRate(self):
cr = bytes([self.cr])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_CR])+cr+bytes([KISS.FEND])
written = self.write_mux(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring coding rate for "+str(self))
raise OSError(f"An IO error occurred while configuring coding rate for {self}")
def setSTALock(self):
if self.st_alock != None:
@ -855,7 +855,7 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_ST_ALOCK])+data+bytes([KISS.FEND])
written = self.write_mux(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring short-term airtime limit for "+str(self))
raise OSError(f"An IO error occurred while configuring short-term airtime limit for {self}")
def setLTALock(self):
if self.lt_alock != None:
@ -867,14 +867,14 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_LT_ALOCK])+data+bytes([KISS.FEND])
written = self.write_mux(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring long-term airtime limit for "+str(self))
raise OSError(f"An IO error occurred while configuring long-term airtime limit for {self}")
def setRadioState(self, state):
self.state = state
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND])
written = self.write_mux(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring radio state for "+str(self))
raise OSError(f"An IO error occurred while configuring radio state for {self}")
def validate_firmware(self):
if (self.maj_version > RNodeInterface.REQUIRED_FW_VER_MAJ):
@ -883,21 +883,21 @@ class RNodeInterface(Interface):
if (self.maj_version >= RNodeInterface.REQUIRED_FW_VER_MAJ):
if (self.min_version >= RNodeInterface.REQUIRED_FW_VER_MIN):
self.firmware_ok = True
if self.firmware_ok:
return
RNS.log("The firmware version of the connected RNode is "+str(self.maj_version)+"."+str(self.min_version), RNS.LOG_ERROR)
RNS.log("This version of Reticulum requires at least version "+str(RNodeInterface.REQUIRED_FW_VER_MAJ)+"."+str(RNodeInterface.REQUIRED_FW_VER_MIN), RNS.LOG_ERROR)
RNS.log(f"The firmware version of the connected RNode is {self.maj_version}.{self.min_version}", RNS.LOG_ERROR)
RNS.log(f"This version of Reticulum requires at least version {RNodeInterface.REQUIRED_FW_VER_MAJ}.{RNodeInterface.REQUIRED_FW_VER_MIN}", RNS.LOG_ERROR)
RNS.log("Please update your RNode firmware with rnodeconf from https://github.com/markqvist/reticulum/")
error_description = "The firmware version of the connected RNode is "+str(self.maj_version)+"."+str(self.min_version)+". "
error_description += "This version of Reticulum requires at least version "+str(RNodeInterface.REQUIRED_FW_VER_MAJ)+"."+str(RNodeInterface.REQUIRED_FW_VER_MIN)+". "
error_description = f"The firmware version of the connected RNode is {self.maj_version}.{self.min_version}. "
error_description += f"This version of Reticulum requires at least version {RNodeInterface.REQUIRED_FW_VER_MAJ}.{RNodeInterface.REQUIRED_FW_VER_MIN}. "
error_description += "Please update your RNode firmware with rnodeconf from: https://github.com/markqvist/rnodeconfigutil/"
self.hw_errors.append({"error": KISS.ERROR_INVALID_FIRMWARE, "description": error_description})
def validateRadioState(self):
RNS.log("Waiting for radio configuration validation for "+str(self)+"...", RNS.LOG_VERBOSE)
RNS.log(f"Waiting for radio configuration validation for {self}...", RNS.LOG_VERBOSE)
if not self.platform == KISS.PLATFORM_ESP32:
sleep(1.00);
else:
@ -937,7 +937,7 @@ class RNodeInterface(Interface):
try:
self.bitrate = self.r_sf * ( (4.0/self.r_cr) / (math.pow(2,self.r_sf)/(self.r_bandwidth/1000)) ) * 1000
self.bitrate_kbps = round(self.bitrate/1000.0, 2)
RNS.log(str(self)+" On-air bitrate is now "+str(self.bitrate_kbps)+ " kbps", RNS.LOG_VERBOSE)
RNS.log(f"{self} On-air bitrate is now {self.bitrate_kbps} kbps", RNS.LOG_VERBOSE)
except:
self.bitrate = 0
@ -969,7 +969,7 @@ class RNodeInterface(Interface):
self.txb += datalen
if written != len(frame):
raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data)))
raise OSError(f"Serial interface only wrote {written} bytes of {len(data)}")
else:
self.queue(data)
@ -1042,7 +1042,7 @@ class RNodeInterface(Interface):
command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4):
self.r_frequency = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3]
RNS.log(str(self)+" Radio reporting frequency is "+str(self.r_frequency/1000000.0)+" MHz", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting frequency is {self.r_frequency / 1000000.0} MHz", RNS.LOG_DEBUG)
self.updateBitrate()
elif (command == KISS.CMD_BANDWIDTH):
@ -1058,26 +1058,26 @@ class RNodeInterface(Interface):
command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4):
self.r_bandwidth = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3]
RNS.log(str(self)+" Radio reporting bandwidth is "+str(self.r_bandwidth/1000.0)+" KHz", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting bandwidth is {self.r_bandwidth / 1000.0} KHz", RNS.LOG_DEBUG)
self.updateBitrate()
elif (command == KISS.CMD_TXPOWER):
self.r_txpower = byte
RNS.log(str(self)+" Radio reporting TX power is "+str(self.r_txpower)+" dBm", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting TX power is {self.r_txpower} dBm", RNS.LOG_DEBUG)
elif (command == KISS.CMD_SF):
self.r_sf = byte
RNS.log(str(self)+" Radio reporting spreading factor is "+str(self.r_sf), RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting spreading factor is {self.r_sf}", RNS.LOG_DEBUG)
self.updateBitrate()
elif (command == KISS.CMD_CR):
self.r_cr = byte
RNS.log(str(self)+" Radio reporting coding rate is "+str(self.r_cr), RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting coding rate is {self.r_cr}", RNS.LOG_DEBUG)
self.updateBitrate()
elif (command == KISS.CMD_RADIO_STATE):
self.r_state = byte
if self.r_state:
RNS.log(str(self)+" Radio reporting state is online", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting state is online", RNS.LOG_DEBUG)
else:
RNS.log(str(self)+" Radio reporting state is offline", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting state is offline", RNS.LOG_DEBUG)
elif (command == KISS.CMD_RADIO_LOCK):
self.r_lock = byte
@ -1156,7 +1156,7 @@ class RNodeInterface(Interface):
if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1]
self.r_st_alock = at/100.0
RNS.log(str(self)+" Radio reporting short-term airtime limit is "+str(self.r_st_alock)+"%", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting short-term airtime limit is {self.r_st_alock}%", RNS.LOG_DEBUG)
elif (command == KISS.CMD_LT_ALOCK):
if (byte == KISS.FESC):
escape = True
@ -1171,7 +1171,7 @@ class RNodeInterface(Interface):
if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1]
self.r_lt_alock = at/100.0
RNS.log(str(self)+" Radio reporting long-term airtime limit is "+str(self.r_lt_alock)+"%", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting long-term airtime limit is {self.r_lt_alock}%", RNS.LOG_DEBUG)
elif (command == KISS.CMD_STAT_CHTM):
if (byte == KISS.FESC):
escape = True
@ -1188,7 +1188,7 @@ class RNodeInterface(Interface):
atl = command_buffer[2] << 8 | command_buffer[3]
cus = command_buffer[4] << 8 | command_buffer[5]
cul = command_buffer[6] << 8 | command_buffer[7]
self.r_airtime_short = ats/100.0
self.r_airtime_long = atl/100.0
self.r_channel_load_short = cus/100.0
@ -1217,9 +1217,9 @@ class RNodeInterface(Interface):
self.r_preamble_symbols = prs
self.r_premable_time_ms = prt
self.r_csma_slot_time_ms = cst
RNS.log(str(self)+" Radio reporting symbol time is "+str(round(self.r_symbol_time_ms,2))+"ms (at "+str(self.r_symbol_rate)+" baud)", RNS.LOG_DEBUG)
RNS.log(str(self)+" Radio reporting preamble is "+str(self.r_preamble_symbols)+" symbols ("+str(self.r_premable_time_ms)+"ms)", RNS.LOG_DEBUG)
RNS.log(str(self)+" Radio reporting CSMA slot time is "+str(self.r_csma_slot_time_ms)+"ms", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting symbol time is {round(self.r_symbol_time_ms, 2)}ms (at {self.r_symbol_rate} baud)", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting preamble is {self.r_preamble_symbols} symbols ({self.r_premable_time_ms}ms)", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting CSMA slot time is {self.r_csma_slot_time_ms}ms", RNS.LOG_DEBUG)
elif (command == KISS.CMD_STAT_BAT):
if (byte == KISS.FESC):
escape = True
@ -1247,26 +1247,26 @@ class RNodeInterface(Interface):
self.mcu = byte
elif (command == KISS.CMD_ERROR):
if (byte == KISS.ERROR_INITRADIO):
RNS.log(str(self)+" hardware initialisation error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR)
raise IOError("Radio initialisation failure")
RNS.log(f"{self} hardware initialisation error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise OSError("Radio initialisation failure")
elif (byte == KISS.ERROR_TXFAILED):
RNS.log(str(self)+" hardware TX error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR)
raise IOError("Hardware transmit failure")
RNS.log(f"{self} hardware TX error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise OSError("Hardware transmit failure")
elif (byte == KISS.ERROR_MEMORY_LOW):
RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+"): Memory exhausted", RNS.LOG_ERROR)
RNS.log(f"{self} hardware error (code {RNS.hexrep(byte)}): Memory exhausted", RNS.LOG_ERROR)
self.hw_errors.append({"error": KISS.ERROR_MEMORY_LOW, "description": "Memory exhausted on connected device"})
elif (byte == KISS.ERROR_MODEM_TIMEOUT):
RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+"): Modem communication timed out", RNS.LOG_ERROR)
RNS.log(f"{self} hardware error (code {RNS.hexrep(byte)}): Modem communication timed out", RNS.LOG_ERROR)
self.hw_errors.append({"error": KISS.ERROR_MODEM_TIMEOUT, "description": "Modem communication timed out on connected device"})
else:
RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR)
raise IOError("Unknown hardware failure")
RNS.log(f"{self} hardware error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise OSError("Unknown hardware failure")
elif (command == KISS.CMD_RESET):
if (byte == 0xF8):
if self.platform == KISS.PLATFORM_ESP32:
if self.online:
RNS.log("Detected reset while device was online, reinitialising device...", RNS.LOG_ERROR)
raise IOError("ESP32 reset")
raise OSError("ESP32 reset")
elif (command == KISS.CMD_READY):
self.process_queue()
elif (command == KISS.CMD_DETECT):
@ -1278,7 +1278,7 @@ class RNodeInterface(Interface):
if got == 0:
time_since_last = int(time.time()*1000) - last_read_ms
if len(data_buffer) > 0 and time_since_last > self.timeout:
RNS.log(str(self)+" serial read timeout in command "+str(command), RNS.LOG_WARNING)
RNS.log(f"{self} serial read timeout in command {command}", RNS.LOG_WARNING)
data_buffer = b""
in_frame = False
command = KISS.CMD_UNKNOWN
@ -1287,22 +1287,22 @@ class RNodeInterface(Interface):
if self.id_interval != None and self.id_callsign != None:
if self.first_tx != None:
if time.time() > self.first_tx + self.id_interval:
RNS.log("Interface "+str(self)+" is transmitting beacon data: "+str(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)
if (time.time() - self.last_port_io > self.port_io_timeout):
self.detect()
if (time.time() - self.last_port_io > self.port_io_timeout*3):
raise IOError("Connected port for "+str(self)+" became unresponsive")
raise OSError(f"Connected port for {self} became unresponsive")
if self.bt_manager != None:
sleep(0.08)
except Exception as e:
self.online = False
RNS.log("A serial port occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
RNS.log(f"A serial port 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)
if RNS.Reticulum.panic_on_interface_error:
RNS.panic()
@ -1330,10 +1330,10 @@ class RNodeInterface(Interface):
try:
time.sleep(self.reconnect_w)
if self.serial != None and self.port != None:
RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_EXTREME)
RNS.log(f"Attempting to reconnect serial port {self.port} for {self}...", RNS.LOG_EXTREME)
if self.bt_manager != None:
RNS.log("Attempting to reconnect Bluetooth device for "+str(self)+"...", RNS.LOG_EXTREME)
RNS.log(f"Attempting to reconnect Bluetooth device for {self}...", RNS.LOG_EXTREME)
self.open_port()
@ -1343,7 +1343,7 @@ class RNodeInterface(Interface):
if self.last_imagedata != None:
self.display_image(self.last_imagedata)
self.enable_external_framebuffer()
elif hasattr(self, "bt_manager") and self.bt_manager != None and self.bt_manager.connected:
self.configure_device()
if self.online:
@ -1352,10 +1352,10 @@ class RNodeInterface(Interface):
self.enable_external_framebuffer()
except Exception as e:
RNS.log("Error while reconnecting RNode, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while reconnecting RNode, the contained exception was: {e}", RNS.LOG_ERROR)
if self.online:
RNS.log("Reconnected serial port for "+str(self))
RNS.log(f"Reconnected serial port for {self}")
def detach(self):
self.detached = True
@ -1399,7 +1399,7 @@ class RNodeInterface(Interface):
return data
def __str__(self):
return "RNodeInterface["+str(self.name)+"]"
return f"RNodeInterface[{self.name}]"
class BLEConnection(BluetoothDispatcher):
UART_SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"
@ -1472,7 +1472,7 @@ class BLEConnection(BluetoothDispatcher):
RNS.trace_exception(e)
def __init__(self, owner=None, target_name=None, target_bt_addr=None):
super(BLEConnection, self).__init__()
super().__init__()
self.owner = owner
self.target_name = target_name
self.target_bt_addr = target_bt_addr
@ -1504,7 +1504,7 @@ class BLEConnection(BluetoothDispatcher):
self.write_characteristic(self.rx_char, data)
else:
time.sleep(0.1)
except Exception as e:
RNS.log("An error occurred in {self} write loop: {e}", RNS.LOG_ERROR)
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.close()
self.should_run = False
self.close_gatt()
self.connect_job_running = False
@ -1599,14 +1599,14 @@ class BLEConnection(BluetoothDispatcher):
def on_services(self, status, services):
if status == GATT_SUCCESS:
self.rx_char = services.search(BLEConnection.UART_RX_CHAR_UUID)
if self.rx_char is not None:
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):
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)
self.mtu_requested_time = time.time()
self.request_mtu(self.target_mtu)

View File

@ -64,7 +64,7 @@ class SerialInterface(Interface):
from usbserial4a import serial4a as serial
self.parity = "N"
else:
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)
@ -75,7 +75,7 @@ class SerialInterface(Interface):
super().__init__()
self.HW_MTU = 564
self.pyserial = serial
self.serial = None
self.owner = owner
@ -98,17 +98,17 @@ class SerialInterface(Interface):
try:
self.open_port()
except Exception as e:
RNS.log("Could not open serial port for interface "+str(self), RNS.LOG_ERROR)
RNS.log(f"Could not open serial port for interface {self}", RNS.LOG_ERROR)
raise e
if self.serial.is_open:
self.configure_device()
else:
raise IOError("Could not open serial port")
raise OSError("Could not open serial port")
def open_port(self):
RNS.log("Opening serial port "+self.port+"...")
RNS.log(f"Opening serial port {self.port}...")
# Get device parameters
from usb4a import usb
device = usb.get_usb_device(self.port)
@ -120,7 +120,7 @@ class SerialInterface(Interface):
proxy = self.pyserial.get_serial_port
if vid == 0x1A86 and pid == 0x55D4:
# Force CDC driver for Qinheng CH34x
RNS.log(str(self)+" using CDC driver for "+RNS.hexrep(vid)+":"+RNS.hexrep(pid), RNS.LOG_DEBUG)
RNS.log(f"{self} using CDC driver for {RNS.hexrep(vid)}:{RNS.hexrep(pid)}", RNS.LOG_DEBUG)
from usbserial4a.cdcacmserial4a import CdcAcmSerial
proxy = CdcAcmSerial
@ -145,7 +145,7 @@ class SerialInterface(Interface):
self.serial.timeout = 0.1
elif vid == 0x10C4:
# 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.timeout = 0.012
elif vid == 0x1A86 and pid == 0x55D4:
@ -159,9 +159,9 @@ class SerialInterface(Interface):
self.serial.USB_READ_TIMEOUT_MILLIS = 100
self.serial.timeout = 0.1
RNS.log(str(self)+" USB read buffer size set to "+RNS.prettysize(self.serial.DEFAULT_READ_BUFFER_SIZE), RNS.LOG_DEBUG)
RNS.log(str(self)+" USB read timeout set to "+str(self.serial.USB_READ_TIMEOUT_MILLIS)+"ms", RNS.LOG_DEBUG)
RNS.log(str(self)+" USB write timeout set to "+str(self.serial.USB_WRITE_TIMEOUT_MILLIS)+"ms", RNS.LOG_DEBUG)
RNS.log(f"{self} USB read buffer size set to {RNS.prettysize(self.serial.DEFAULT_READ_BUFFER_SIZE)}", RNS.LOG_DEBUG)
RNS.log(f"{self} USB read timeout set to {self.serial.USB_READ_TIMEOUT_MILLIS}ms", RNS.LOG_DEBUG)
RNS.log(f"{self} USB write timeout set to {self.serial.USB_WRITE_TIMEOUT_MILLIS}ms", RNS.LOG_DEBUG)
def configure_device(self):
sleep(0.5)
@ -169,7 +169,7 @@ class SerialInterface(Interface):
thread.daemon = True
thread.start()
self.online = True
RNS.log("Serial port "+self.port+" is now open", RNS.LOG_VERBOSE)
RNS.log(f"Serial port {self.port} is now open", RNS.LOG_VERBOSE)
def processIncoming(self, data):
@ -182,9 +182,9 @@ class SerialInterface(Interface):
if self.online:
data = bytes([HDLC.FLAG])+HDLC.escape(data)+bytes([HDLC.FLAG])
written = self.serial.write(data)
self.txb += len(data)
self.txb += len(data)
if written != len(data):
raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data)))
raise OSError(f"Serial interface only wrote {written} bytes of {len(data)}")
def readLoop(self):
try:
@ -217,7 +217,7 @@ class SerialInterface(Interface):
byte = HDLC.ESC
escape = False
data_buffer = data_buffer+bytes([byte])
if got == 0:
time_since_last = int(time.time()*1000) - last_read_ms
if len(data_buffer) > 0 and time_since_last > self.timeout:
@ -225,12 +225,12 @@ class SerialInterface(Interface):
in_frame = False
escape = False
# sleep(0.08)
except Exception as e:
self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", 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)
if RNS.Reticulum.panic_on_interface_error:
RNS.panic()
@ -244,17 +244,17 @@ class SerialInterface(Interface):
while not self.online:
try:
time.sleep(5)
RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_VERBOSE)
RNS.log(f"Attempting to reconnect serial port {self.port} for {self}...", RNS.LOG_VERBOSE)
self.open_port()
if self.serial.is_open:
self.configure_device()
except Exception as e:
RNS.log("Error while reconnecting port, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while reconnecting port, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("Reconnected serial port for "+str(self))
RNS.log(f"Reconnected serial port for {self}")
def should_ingress_limit(self):
return False
def __str__(self):
return "SerialInterface["+self.name+"]"
return f"SerialInterface[{self.name}]"

View File

@ -23,5 +23,5 @@
import os
import glob
modules = glob.glob(os.path.dirname(__file__)+"/*.py")
modules = glob.glob(f"{os.path.dirname(__file__)}/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]

View File

@ -35,7 +35,7 @@ import RNS
class AutoInterface(Interface):
DEFAULT_DISCOVERY_PORT = 29716
DEFAULT_DATA_PORT = 42671
DEFAULT_GROUP_ID = "reticulum".encode("utf-8")
DEFAULT_GROUP_ID = b"reticulum"
SCOPE_LINK = "2"
SCOPE_ADMIN = "4"
@ -169,32 +169,32 @@ class AutoInterface(Interface):
self.group_hash = RNS.Identity.full_hash(self.group_id)
g = self.group_hash
#gt = "{:02x}".format(g[1]+(g[0]<<8))
#gt = f"{g[1] + (g[0] << 8):02x}"
gt = "0"
gt += ":"+"{:02x}".format(g[3]+(g[2]<<8))
gt += ":"+"{:02x}".format(g[5]+(g[4]<<8))
gt += ":"+"{:02x}".format(g[7]+(g[6]<<8))
gt += ":"+"{:02x}".format(g[9]+(g[8]<<8))
gt += ":"+"{:02x}".format(g[11]+(g[10]<<8))
gt += ":"+"{:02x}".format(g[13]+(g[12]<<8))
self.mcast_discovery_address = "ff"+self.multicast_address_type+self.discovery_scope+":"+gt
gt += f":{g[3] + (g[2] << 8):02x}"
gt += f":{g[5] + (g[4] << 8):02x}"
gt += f":{g[7] + (g[6] << 8):02x}"
gt += f":{g[9] + (g[8] << 8):02x}"
gt += f":{g[11] + (g[10] << 8):02x}"
gt += f":{g[13] + (g[12] << 8):02x}"
self.mcast_discovery_address = f"ff{self.multicast_address_type}{self.discovery_scope}:{gt}"
suitable_interfaces = 0
for ifname in self.list_interfaces():
try:
if RNS.vendor.platformutils.is_darwin() and ifname in AutoInterface.DARWIN_IGNORE_IFS and not ifname in self.allowed_interfaces:
RNS.log(str(self)+" skipping Darwin AWDL or tethering interface "+str(ifname), RNS.LOG_EXTREME)
RNS.log(f"{self} skipping Darwin AWDL or tethering interface {ifname}", RNS.LOG_EXTREME)
elif RNS.vendor.platformutils.is_darwin() and ifname == "lo0":
RNS.log(str(self)+" skipping Darwin loopback interface "+str(ifname), RNS.LOG_EXTREME)
RNS.log(f"{self} skipping Darwin loopback interface {ifname}", RNS.LOG_EXTREME)
elif RNS.vendor.platformutils.is_android() and ifname in AutoInterface.ANDROID_IGNORE_IFS and not ifname in self.allowed_interfaces:
RNS.log(str(self)+" skipping Android system interface "+str(ifname), RNS.LOG_EXTREME)
RNS.log(f"{self} skipping Android system interface {ifname}", RNS.LOG_EXTREME)
elif ifname in self.ignored_interfaces:
RNS.log(str(self)+" ignoring disallowed interface "+str(ifname), RNS.LOG_EXTREME)
RNS.log(f"{self} ignoring disallowed interface {ifname}", RNS.LOG_EXTREME)
elif ifname in AutoInterface.ALL_IGNORE_IFS:
RNS.log(str(self)+" skipping interface "+str(ifname), RNS.LOG_EXTREME)
RNS.log(f"{self} skipping interface {ifname}", RNS.LOG_EXTREME)
else:
if len(self.allowed_interfaces) > 0 and not ifname in self.allowed_interfaces:
RNS.log(str(self)+" ignoring interface "+str(ifname)+" since it was not allowed", RNS.LOG_EXTREME)
RNS.log(f"{self} ignoring interface {ifname} since it was not allowed", RNS.LOG_EXTREME)
else:
addresses = self.list_addresses(ifname)
if self.netinfo.AF_INET6 in addresses:
@ -213,10 +213,10 @@ class AutoInterface(Interface):
RNS.log(f"{self} Selecting link-local address {link_local_addr} for interface {ifname}", RNS.LOG_EXTREME)
if link_local_addr == None:
RNS.log(str(self)+" No link-local IPv6 address configured for "+str(ifname)+", skipping interface", RNS.LOG_EXTREME)
RNS.log(f"{self} No link-local IPv6 address configured for {ifname}, skipping interface", RNS.LOG_EXTREME)
else:
mcast_addr = self.mcast_discovery_address
RNS.log(str(self)+" Creating multicast discovery listener on "+str(ifname)+" with address "+str(mcast_addr), RNS.LOG_EXTREME)
RNS.log(f"{self} Creating multicast discovery listener on {ifname} with address {mcast_addr}", RNS.LOG_EXTREME)
# Struct with interface index
if_struct = struct.pack("I", self.interface_name_to_index(ifname))
@ -243,7 +243,7 @@ class AutoInterface(Interface):
else:
if self.discovery_scope == AutoInterface.SCOPE_LINK:
addr_info = socket.getaddrinfo(mcast_addr+"%"+ifname, self.discovery_port, socket.AF_INET6, socket.SOCK_DGRAM)
addr_info = socket.getaddrinfo(f"{mcast_addr}%{ifname}", self.discovery_port, socket.AF_INET6, socket.SOCK_DGRAM)
else:
addr_info = socket.getaddrinfo(mcast_addr, self.discovery_port, socket.AF_INET6, socket.SOCK_DGRAM)
@ -267,7 +267,7 @@ class AutoInterface(Interface):
RNS.log(f"Could not configure the system interface {ifname} for use with {self}, skipping it. The contained exception was: {e}", RNS.LOG_ERROR)
if suitable_interfaces == 0:
RNS.log(str(self)+" could not autoconfigure. This interface currently provides no connectivity.", RNS.LOG_WARNING)
RNS.log(f"{self} could not autoconfigure. This interface currently provides no connectivity.", RNS.LOG_WARNING)
else:
self.receives = True
@ -277,19 +277,19 @@ class AutoInterface(Interface):
self.bitrate = AutoInterface.BITRATE_GUESS
peering_wait = self.announce_interval*1.2
RNS.log(str(self)+" discovering peers for "+str(round(peering_wait, 2))+" seconds...", RNS.LOG_VERBOSE)
RNS.log(f"{self} discovering peers for {round(peering_wait, 2)} seconds...", RNS.LOG_VERBOSE)
self.owner = owner
socketserver.UDPServer.address_family = socket.AF_INET6
for ifname in self.adopted_interfaces:
local_addr = self.adopted_interfaces[ifname]+"%"+str(self.interface_name_to_index(ifname))
local_addr = f"{self.adopted_interfaces[ifname]}%{self.interface_name_to_index(ifname)}"
addr_info = socket.getaddrinfo(local_addr, self.data_port, socket.AF_INET6, socket.SOCK_DGRAM)
address = addr_info[0][4]
udp_server = socketserver.UDPServer(address, self.handler_factory(self.processIncoming))
self.interface_servers[ifname] = udp_server
thread = threading.Thread(target=udp_server.serve_forever)
thread.daemon = True
thread.start()
@ -306,18 +306,18 @@ class AutoInterface(Interface):
def discovery_handler(self, socket, ifname):
def announce_loop():
self.announce_handler(ifname)
thread = threading.Thread(target=announce_loop)
thread.daemon = True
thread.start()
while True:
data, ipv6_src = socket.recvfrom(1024)
expected_hash = RNS.Identity.full_hash(self.group_id+ipv6_src[0].encode("utf-8"))
if data == expected_hash:
self.add_peer(ipv6_src[0], ifname)
else:
RNS.log(str(self)+" received peering packet on "+str(ifname)+" from "+str(ipv6_src[0])+", but authentication hash was incorrect.", RNS.LOG_DEBUG)
RNS.log(f"{self} received peering packet on {ifname} from {ipv6_src[0]}, but authentication hash was incorrect.", RNS.LOG_DEBUG)
def peer_jobs(self):
while True:
@ -335,7 +335,7 @@ class AutoInterface(Interface):
# Remove any timed out peers
for peer_addr in timed_out_peers:
removed_peer = self.peers.pop(peer_addr)
RNS.log(str(self)+" removed peer "+str(peer_addr)+" on "+str(removed_peer[0]), RNS.LOG_DEBUG)
RNS.log(f"{self} removed peer {peer_addr} on {removed_peer[0]}", RNS.LOG_DEBUG)
for ifname in self.adopted_interfaces:
# Check that the link-local address has not changed
@ -349,25 +349,25 @@ class AutoInterface(Interface):
link_local_addr = self.descope_linklocal(address["addr"])
if link_local_addr != self.adopted_interfaces[ifname]:
old_link_local_address = self.adopted_interfaces[ifname]
RNS.log("Replacing link-local address "+str(old_link_local_address)+" for "+str(ifname)+" with "+str(link_local_addr), RNS.LOG_DEBUG)
RNS.log(f"Replacing link-local address {old_link_local_address} for {ifname} with {link_local_addr}", RNS.LOG_DEBUG)
self.adopted_interfaces[ifname] = link_local_addr
self.link_local_addresses.append(link_local_addr)
if old_link_local_address in self.link_local_addresses:
self.link_local_addresses.remove(old_link_local_address)
local_addr = link_local_addr+"%"+ifname
local_addr = f"{link_local_addr}%{ifname}"
addr_info = socket.getaddrinfo(local_addr, self.data_port, socket.AF_INET6, socket.SOCK_DGRAM)
listen_address = addr_info[0][4]
if ifname in self.interface_servers:
RNS.log("Shutting down previous UDP listener for "+str(self)+" "+str(ifname), RNS.LOG_DEBUG)
RNS.log(f"Shutting down previous UDP listener for {self} {ifname}", RNS.LOG_DEBUG)
previous_server = self.interface_servers[ifname]
def shutdown_server():
previous_server.shutdown()
threading.Thread(target=shutdown_server, daemon=True).start()
RNS.log("Starting new UDP listener for "+str(self)+" "+str(ifname), RNS.LOG_DEBUG)
RNS.log(f"Starting new UDP listener for {self} {ifname}", RNS.LOG_DEBUG)
udp_server = socketserver.UDPServer(listen_address, self.handler_factory(self.processIncoming))
self.interface_servers[ifname] = udp_server
@ -379,7 +379,7 @@ class AutoInterface(Interface):
self.carrier_changed = True
except Exception as e:
RNS.log("Could not get device information while updating link-local addresses for "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Could not get device information while updating link-local addresses for {self}. The contained exception was: {e}", RNS.LOG_ERROR)
# Check multicast echo timeouts
last_multicast_echo = 0
@ -389,20 +389,20 @@ class AutoInterface(Interface):
if now - last_multicast_echo > self.multicast_echo_timeout:
if ifname in self.timed_out_interfaces and self.timed_out_interfaces[ifname] == False:
self.carrier_changed = True
RNS.log("Multicast echo timeout for "+str(ifname)+". Carrier lost.", RNS.LOG_WARNING)
RNS.log(f"Multicast echo timeout for {ifname}. Carrier lost.", RNS.LOG_WARNING)
self.timed_out_interfaces[ifname] = True
else:
if ifname in self.timed_out_interfaces and self.timed_out_interfaces[ifname] == True:
self.carrier_changed = True
RNS.log(str(self)+" Carrier recovered on "+str(ifname), RNS.LOG_WARNING)
RNS.log(f"{self} Carrier recovered on {ifname}", RNS.LOG_WARNING)
self.timed_out_interfaces[ifname] = False
def announce_handler(self, ifname):
while True:
self.peer_announce(ifname)
time.sleep(self.announce_interval)
def peer_announce(self, ifname):
try:
link_local_address = self.adopted_interfaces[ifname]
@ -414,10 +414,10 @@ class AutoInterface(Interface):
announce_socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, ifis)
announce_socket.sendto(discovery_token, addr_info[0][4])
announce_socket.close()
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:
RNS.log(str(self)+" Detected possible carrier loss on "+str(ifname)+": "+str(e), RNS.LOG_WARNING)
RNS.log(f"{self} Detected possible carrier loss on {ifname}: {e}", RNS.LOG_WARNING)
else:
pass
@ -431,12 +431,12 @@ class AutoInterface(Interface):
if ifname != None:
self.multicast_echoes[ifname] = time.time()
else:
RNS.log(str(self)+" received multicast echo on unexpected interface "+str(ifname), RNS.LOG_WARNING)
RNS.log(f"{self} received multicast echo on unexpected interface {ifname}", RNS.LOG_WARNING)
else:
if not addr in self.peers:
self.peers[addr] = [ifname, time.time()]
RNS.log(str(self)+" added peer "+str(addr)+" on "+str(ifname), RNS.LOG_DEBUG)
RNS.log(f"{self} added peer {addr} on {ifname}", RNS.LOG_DEBUG)
else:
self.refresh_peer(addr)
@ -464,16 +464,16 @@ class AutoInterface(Interface):
if self.outbound_udp_socket == None:
self.outbound_udp_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
peer_addr = str(peer)+"%"+str(self.interface_name_to_index(self.peers[peer][0]))
peer_addr = f"{peer}%{self.interface_name_to_index(self.peers[peer][0])}"
addr_info = socket.getaddrinfo(peer_addr, self.data_port, socket.AF_INET6, socket.SOCK_DGRAM)
self.outbound_udp_socket.sendto(data, addr_info[0][4])
except Exception as e:
RNS.log("Could not transmit on "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Could not transmit on {self}. The contained exception was: {e}", RNS.LOG_ERROR)
self.txb += len(data)
# Until per-device sub-interfacing is implemented,
# ingress limiting should be disabled on AutoInterface
@ -481,7 +481,7 @@ class AutoInterface(Interface):
return False
def __str__(self):
return "AutoInterface["+self.name+"]"
return f"AutoInterface[{self.name}]"
class AutoInterfaceHandler(socketserver.BaseRequestHandler):
def __init__(self, callback, *args, **keys):

View File

@ -90,7 +90,7 @@ class I2PController:
time.sleep(0.10)
if self.loop == None:
RNS.log("Could not get event loop for "+str(self)+", waiting for event loop to appear", RNS.LOG_VERBOSE)
RNS.log(f"Could not get event loop for {self}, waiting for event loop to appear", RNS.LOG_VERBOSE)
while self.loop == None:
self.loop = asyncio.get_event_loop()
@ -101,7 +101,7 @@ class I2PController:
self.loop.run_forever()
except Exception as e:
self.ready = False
RNS.log("Exception on event loop for "+str(self)+": "+str(e), RNS.LOG_ERROR)
RNS.log(f"Exception on event loop for {self}: {e}", RNS.LOG_ERROR)
finally:
self.loop.close()
@ -136,18 +136,18 @@ class I2PController:
if not self.client_tunnels[i2p_destination]:
try:
async def tunnel_up():
RNS.log("Bringing up I2P tunnel to "+str(owner)+", this may take a while...", RNS.LOG_INFO)
RNS.log(f"Bringing up I2P tunnel to {owner}, this may take a while...", RNS.LOG_INFO)
tunnel = self.i2plib.ClientTunnel(i2p_destination, owner.local_addr, sam_address=self.sam_address, loop=self.loop)
self.i2plib_tunnels[i2p_destination] = tunnel
await tunnel.run()
self.loop.ext_owner = self
result = asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop).result()
if not i2p_destination in self.i2plib_tunnels:
raise IOError("No tunnel control instance was created")
else:
if not i2p_destination in self.i2plib_tunnels:
raise OSError("No tunnel control instance was created")
else:
tn = self.i2plib_tunnels[i2p_destination]
if tn != None and hasattr(tn, "status"):
@ -167,28 +167,28 @@ class I2PController:
try:
owner.socket.shutdown(socket.SHUT_RDWR)
except Exception as e:
RNS.log("Error while shutting down socket for "+str(owner)+": "+str(e))
RNS.log(f"Error while shutting down socket for {owner}: {e}")
try:
owner.socket.close()
except Exception as e:
RNS.log("Error while closing socket for "+str(owner)+": "+str(e))
RNS.log(f"Error while closing socket for {owner}: {e}")
self.client_tunnels[i2p_destination] = True
owner.awaiting_i2p_tunnel = False
RNS.log(str(owner)+" tunnel setup complete", RNS.LOG_VERBOSE)
RNS.log(f"{owner} tunnel setup complete", RNS.LOG_VERBOSE)
else:
raise IOError("Got no status response from SAM API")
raise OSError("Got no status response from SAM API")
except ConnectionRefusedError as e:
raise e
except ConnectionAbortedError as e:
raise e
except Exception as e:
RNS.log("Unexpected error type from I2P SAM: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Unexpected error type from I2P SAM: {e}", RNS.LOG_ERROR)
raise e
else:
@ -197,16 +197,16 @@ class I2PController:
i2p_exception = i2ptunnel.status["exception"]
if i2ptunnel.status["setup_ran"] == False:
RNS.log(str(self)+" I2P tunnel setup did not complete", RNS.LOG_ERROR)
RNS.log(f"{self} I2P tunnel setup did not complete", RNS.LOG_ERROR)
self.stop_tunnel(i2ptunnel)
return False
elif i2p_exception != None:
RNS.log("An error ocurred while setting up I2P tunnel to "+str(i2p_destination), RNS.LOG_ERROR)
RNS.log(f"An error ocurred while setting up I2P tunnel to {i2p_destination}", RNS.LOG_ERROR)
if isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.CantReachPeer):
RNS.log("The I2P daemon can't reach peer "+str(i2p_destination), RNS.LOG_ERROR)
RNS.log(f"The I2P daemon can't reach peer {i2p_destination}", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.DuplicatedDest):
RNS.log("The I2P daemon reported that the destination is already in use", RNS.LOG_ERROR)
@ -218,19 +218,19 @@ class I2PController:
RNS.log("The I2P daemon reported that the stream session ID doesn't exist", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.InvalidKey):
RNS.log("The I2P daemon reported that the key for "+str(i2p_destination)+" is invalid", RNS.LOG_ERROR)
RNS.log(f"The I2P daemon reported that the key for {i2p_destination} is invalid", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.KeyNotFound):
RNS.log("The I2P daemon could not find the key for "+str(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):
RNS.log("The I2P daemon mould not find the peer "+str(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):
RNS.log("The I2P daemon experienced an unspecified error", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.Timeout):
RNS.log("I2P daemon timed out while setting up client tunnel to "+str(i2p_destination), RNS.LOG_ERROR)
RNS.log(f"I2P daemon timed out while setting up client tunnel to {i2p_destination}", RNS.LOG_ERROR)
RNS.log("Resetting I2P tunnel and retrying later", RNS.LOG_ERROR)
@ -238,13 +238,13 @@ class I2PController:
return False
elif i2ptunnel.status["setup_failed"] == True:
RNS.log(str(self)+" Unspecified I2P tunnel setup error, resetting I2P tunnel", RNS.LOG_ERROR)
RNS.log(f"{self} Unspecified I2P tunnel setup error, resetting I2P tunnel", RNS.LOG_ERROR)
self.stop_tunnel(i2ptunnel)
return False
else:
RNS.log(str(self)+" Got no status from SAM API, resetting I2P tunnel", RNS.LOG_ERROR)
RNS.log(f"{self} Got no status from SAM API, resetting I2P tunnel", RNS.LOG_ERROR)
self.stop_tunnel(i2ptunnel)
return False
@ -259,11 +259,11 @@ class I2PController:
# Old format
i2p_dest_hash_of = RNS.Identity.full_hash(RNS.Identity.full_hash(owner.name.encode("utf-8")))
i2p_keyfile_of = self.storagepath+"/"+RNS.hexrep(i2p_dest_hash_of, delimit=False)+".i2p"
i2p_keyfile_of = f"{self.storagepath}/{RNS.hexrep(i2p_dest_hash_of, delimit=False)}.i2p"
# New format
i2p_dest_hash_nf = RNS.Identity.full_hash(RNS.Identity.full_hash(owner.name.encode("utf-8"))+RNS.Identity.full_hash(RNS.Transport.identity.hash))
i2p_keyfile_nf = self.storagepath+"/"+RNS.hexrep(i2p_dest_hash_nf, delimit=False)+".i2p"
i2p_keyfile_nf = f"{self.storagepath}/{RNS.hexrep(i2p_dest_hash_nf, delimit=False)}.i2p"
# Use old format if a key is already present
if os.path.isfile(i2p_keyfile_of):
@ -279,7 +279,7 @@ class I2PController:
key_file.write(i2p_dest.private_key.base64)
key_file.close()
else:
key_file = open(i2p_keyfile, "r")
key_file = open(i2p_keyfile)
prvd = key_file.read()
key_file.close()
i2p_dest = self.i2plib.Destination(data=prvd, has_private_key=True)
@ -294,12 +294,12 @@ class I2PController:
if self.server_tunnels[i2p_b32] == False:
try:
async def tunnel_up():
RNS.log(str(owner)+" Bringing up I2P endpoint, this may take a while...", RNS.LOG_INFO)
RNS.log(f"{owner} Bringing up I2P endpoint, this may take a while...", RNS.LOG_INFO)
tunnel = self.i2plib.ServerTunnel((owner.bind_ip, owner.bind_port), loop=self.loop, destination=i2p_dest, sam_address=self.sam_address)
self.i2plib_tunnels[i2p_b32] = tunnel
await tunnel.run()
owner.online = True
RNS.log(str(owner)+ " endpoint setup complete. Now reachable at: "+str(i2p_dest.base32)+".b32.i2p", RNS.LOG_VERBOSE)
RNS.log(f"{owner} endpoint setup complete. Now reachable at: {i2p_dest.base32}.b32.i2p", RNS.LOG_VERBOSE)
asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop).result()
self.server_tunnels[i2p_b32] = True
@ -313,16 +313,16 @@ class I2PController:
i2p_exception = i2ptunnel.status["exception"]
if i2ptunnel.status["setup_ran"] == False:
RNS.log(str(self)+" I2P tunnel setup did not complete", RNS.LOG_ERROR)
RNS.log(f"{self} I2P tunnel setup did not complete", RNS.LOG_ERROR)
self.stop_tunnel(i2ptunnel)
return False
elif i2p_exception != None:
RNS.log("An error ocurred while setting up I2P tunnel", RNS.LOG_ERROR)
if isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.CantReachPeer):
RNS.log("The I2P daemon can't reach peer "+str(i2p_destination), RNS.LOG_ERROR)
RNS.log(f"The I2P daemon can't reach peer {i2p_destination}", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.DuplicatedDest):
RNS.log("The I2P daemon reported that the destination is already in use", RNS.LOG_ERROR)
@ -334,19 +334,19 @@ class I2PController:
RNS.log("The I2P daemon reported that the stream session ID doesn't exist", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.InvalidKey):
RNS.log("The I2P daemon reported that the key for "+str(i2p_destination)+" is invalid", RNS.LOG_ERROR)
RNS.log(f"The I2P daemon reported that the key for {i2p_destination} is invalid", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.KeyNotFound):
RNS.log("The I2P daemon could not find the key for "+str(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):
RNS.log("The I2P daemon mould not find the peer "+str(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):
RNS.log("The I2P daemon experienced an unspecified error", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.Timeout):
RNS.log("I2P daemon timed out while setting up client tunnel to "+str(i2p_destination), RNS.LOG_ERROR)
RNS.log(f"I2P daemon timed out while setting up client tunnel to {i2p_destination}", RNS.LOG_ERROR)
RNS.log("Resetting I2P tunnel and retrying later", RNS.LOG_ERROR)
@ -354,13 +354,13 @@ class I2PController:
return False
elif i2ptunnel.status["setup_failed"] == True:
RNS.log(str(self)+" Unspecified I2P tunnel setup error, resetting I2P tunnel", RNS.LOG_ERROR)
RNS.log(f"{self} Unspecified I2P tunnel setup error, resetting I2P tunnel", RNS.LOG_ERROR)
self.stop_tunnel(i2ptunnel)
return False
else:
RNS.log(str(self)+" Got no status from SAM API, resetting I2P tunnel", RNS.LOG_ERROR)
RNS.log(f"{self} Got no status from SAM API, resetting I2P tunnel", RNS.LOG_ERROR)
self.stop_tunnel(i2ptunnel)
return False
@ -393,7 +393,7 @@ class I2PInterfacePeer(Interface):
super().__init__()
self.HW_MTU = 1064
self.IN = True
self.OUT = False
self.socket = None
@ -475,11 +475,11 @@ class I2PInterfacePeer(Interface):
self.target_port = self.bind_port
if not self.parent_interface.i2p.client_tunnel(self, target_i2p_dest):
RNS.log(str(self)+" I2P control process experienced an error, requesting new tunnel...", RNS.LOG_ERROR)
RNS.log(f"{self} I2P control process experienced an error, requesting new tunnel...", RNS.LOG_ERROR)
self.awaiting_i2p_tunnel = True
except Exception as e:
RNS.log("Error while while configuring "+str(self)+": "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while while configuring {self}: {e}", RNS.LOG_ERROR)
RNS.log("Check that I2P is installed and running, and that SAM is enabled. Retrying tunnel setup later.", RNS.LOG_ERROR)
time.sleep(8)
@ -492,7 +492,7 @@ class I2PInterfacePeer(Interface):
while self.awaiting_i2p_tunnel:
time.sleep(0.25)
time.sleep(2)
if not self.kiss_framing:
self.wants_tunnel = True
@ -525,37 +525,37 @@ class I2PInterfacePeer(Interface):
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(I2PInterfacePeer.I2P_PROBE_AFTER))
def shutdown_socket(self, target_socket):
if callable(target_socket.close):
try:
if socket != None:
target_socket.shutdown(socket.SHUT_RDWR)
except Exception as e:
RNS.log("Error while shutting down socket for "+str(self)+": "+str(e))
RNS.log(f"Error while shutting down socket for {self}: {e}")
try:
if socket != None:
target_socket.close()
except Exception as e:
RNS.log("Error while closing socket for "+str(self)+": "+str(e))
RNS.log(f"Error while closing socket for {self}: {e}")
def detach(self):
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG)
RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
if self.socket != None:
if hasattr(self.socket, "close"):
if callable(self.socket.close):
self.detached = True
try:
self.socket.shutdown(socket.SHUT_RDWR)
except Exception as e:
RNS.log("Error while shutting down socket for "+str(self)+": "+str(e))
RNS.log(f"Error while shutting down socket for {self}: {e}")
try:
self.socket.close()
except Exception as e:
RNS.log("Error while closing socket for "+str(self)+": "+str(e))
RNS.log(f"Error while closing socket for {self}: {e}")
self.socket = None
@ -564,15 +564,15 @@ class I2PInterfacePeer(Interface):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect((self.target_ip, self.target_port))
self.online = True
except Exception as e:
if initial:
if not self.awaiting_i2p_tunnel:
RNS.log("Initial connection for "+str(self)+" could not be established: "+str(e), RNS.LOG_ERROR)
RNS.log("Leaving unconnected and retrying connection in "+str(I2PInterfacePeer.RECONNECT_WAIT)+" seconds.", 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 {I2PInterfacePeer.RECONNECT_WAIT} seconds.", RNS.LOG_ERROR)
return False
else:
raise e
@ -580,7 +580,7 @@ class I2PInterfacePeer(Interface):
self.set_timeouts_linux()
elif platform.system() == "Darwin":
self.set_timeouts_osx()
self.online = True
self.writing = False
self.never_connected = False
@ -600,7 +600,7 @@ class I2PInterfacePeer(Interface):
attempts += 1
if self.max_reconnect_tries != None and attempts > self.max_reconnect_tries:
RNS.log("Max reconnection attempts reached for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Max reconnection attempts reached for {self}", RNS.LOG_ERROR)
self.teardown()
break
@ -609,12 +609,12 @@ class I2PInterfacePeer(Interface):
except Exception as e:
if not self.awaiting_i2p_tunnel:
RNS.log("Connection attempt for "+str(self)+" failed: "+str(e), RNS.LOG_DEBUG)
RNS.log(f"Connection attempt for {self} failed: {e}", RNS.LOG_DEBUG)
else:
RNS.log(str(self)+" still waiting for I2P tunnel to appear", RNS.LOG_VERBOSE)
RNS.log(f"{self} still waiting for I2P tunnel to appear", RNS.LOG_VERBOSE)
if not self.never_connected:
RNS.log(str(self)+" Re-established connection via I2P tunnel", RNS.LOG_INFO)
RNS.log(f"{self} Re-established connection via I2P tunnel", RNS.LOG_INFO)
self.reconnecting = False
thread = threading.Thread(target=self.read_loop)
@ -625,13 +625,13 @@ class I2PInterfacePeer(Interface):
else:
RNS.log("Attempt to reconnect on a non-initiator I2P interface. This should not happen.", RNS.LOG_ERROR)
raise IOError("Attempt to reconnect on a non-initiator I2P interface")
raise OSError("Attempt to reconnect on a non-initiator I2P interface")
def processIncoming(self, data):
self.rxb += len(data)
if hasattr(self, "parent_interface") and self.parent_interface != None and self.parent_count:
self.parent_interface.rxb += len(data)
self.owner.inbound(data, self)
def processOutgoing(self, data):
@ -651,13 +651,13 @@ class I2PInterfacePeer(Interface):
self.writing = False
self.txb += len(data)
self.last_write = time.time()
if hasattr(self, "parent_interface") and self.parent_interface != None and self.parent_count:
self.parent_interface.txb += len(data)
except Exception as e:
RNS.log("Exception occurred while transmitting via "+str(self)+", tearing down interface", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Exception occurred while transmitting via {self}, tearing down interface", RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
self.teardown()
@ -683,22 +683,22 @@ class I2PInterfacePeer(Interface):
if self.socket != None:
self.socket.sendall(bytes([HDLC.FLAG, HDLC.FLAG]))
except Exception as e:
RNS.log("An error ocurred while sending I2P keepalive. The contained exception was: "+str(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)
should_run = False
if (time.time()-self.last_read > I2PInterfacePeer.I2P_READ_TIMEOUT):
RNS.log("I2P socket is unresponsive, restarting...", RNS.LOG_WARNING)
if self.socket != None:
try:
self.socket.shutdown(socket.SHUT_RDWR)
except Exception as e:
RNS.log("Error while shutting down socket for "+str(self)+": "+str(e))
RNS.log(f"Error while shutting down socket for {self}: {e}")
try:
self.socket.close()
except Exception as e:
RNS.log("Error while closing socket for "+str(self)+": "+str(e))
RNS.log(f"Error while closing socket for {self}: {e}")
should_run = False
@ -782,18 +782,18 @@ class I2PInterfacePeer(Interface):
self.wd_reset = False
if self.initiator and not self.detached:
RNS.log("Socket for "+str(self)+" was closed, attempting to reconnect...", RNS.LOG_WARNING)
RNS.log(f"Socket for {self} was closed, attempting to reconnect...", RNS.LOG_WARNING)
self.reconnect()
else:
RNS.log("Socket for remote client "+str(self)+" was closed.", RNS.LOG_VERBOSE)
RNS.log(f"Socket for remote client {self} was closed.", RNS.LOG_VERBOSE)
self.teardown()
break
except Exception as e:
self.online = False
RNS.log("An interface error occurred for "+str(self)+", the contained exception was: "+str(e), RNS.LOG_WARNING)
RNS.log(f"An interface error occurred for {self}, the contained exception was: {e}", RNS.LOG_WARNING)
if self.initiator:
RNS.log("Attempting to reconnect...", RNS.LOG_WARNING)
@ -803,12 +803,12 @@ class I2PInterfacePeer(Interface):
def teardown(self):
if self.initiator and not self.detached:
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR)
RNS.log(f"The interface {self} experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error:
RNS.panic()
else:
RNS.log("The interface "+str(self)+" is being torn down.", RNS.LOG_VERBOSE)
RNS.log(f"The interface {self} is being torn down.", RNS.LOG_VERBOSE)
self.online = False
self.OUT = False
@ -824,7 +824,7 @@ class I2PInterfacePeer(Interface):
def __str__(self):
return "I2PInterfacePeer["+str(self.name)+"]"
return f"I2PInterfacePeer[{self.name}]"
class I2PInterface(Interface):
@ -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):
super().__init__()
self.HW_MTU = 1064
self.online = False
@ -882,7 +882,7 @@ class I2PInterface(Interface):
def createHandler(*args, **keys):
return I2PInterfaceHandler(callback, *args, **keys)
return createHandler
ThreadingI2PServer.allow_reuse_address = True
self.server = ThreadingI2PServer(self.address, handlerFactory(self.incoming_connection))
@ -895,11 +895,11 @@ class I2PInterface(Interface):
while True:
try:
if not self.i2p.server_tunnel(self):
RNS.log(str(self)+" I2P control process experienced an error, requesting new tunnel...", RNS.LOG_ERROR)
RNS.log(f"{self} I2P control process experienced an error, requesting new tunnel...", RNS.LOG_ERROR)
self.online = False
except Exception as e:
RNS.log("Error while while configuring "+str(self)+": "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while while configuring {self}: {e}", RNS.LOG_ERROR)
RNS.log("Check that I2P is installed and running, and that SAM is enabled. Retrying tunnel setup later.", RNS.LOG_ERROR)
time.sleep(15)
@ -911,7 +911,7 @@ class I2PInterface(Interface):
if peers != None:
for peer_addr in peers:
interface_name = self.name+" to "+peer_addr
interface_name = f"{self.name} to {peer_addr}"
peer_interface = I2PInterfacePeer(self, self.owner, interface_name, peer_addr)
peer_interface.OUT = True
peer_interface.IN = True
@ -921,7 +921,7 @@ class I2PInterface(Interface):
def incoming_connection(self, handler):
RNS.log("Accepting incoming I2P connection", RNS.LOG_VERBOSE)
interface_name = "Connected peer on "+self.name
interface_name = f"Connected peer on {self.name}"
spawned_interface = I2PInterfacePeer(self, self.owner, interface_name, connected_socket=handler.request)
spawned_interface.OUT = True
spawned_interface.IN = True
@ -954,7 +954,7 @@ class I2PInterface(Interface):
spawned_interface.announce_rate_penalty = self.announce_rate_penalty
spawned_interface.mode = self.mode
spawned_interface.HW_MTU = self.HW_MTU
RNS.log("Spawned new I2PInterface Peer: "+str(spawned_interface), RNS.LOG_VERBOSE)
RNS.log(f"Spawned new I2PInterface Peer: {spawned_interface}", RNS.LOG_VERBOSE)
RNS.Transport.interfaces.append(spawned_interface)
self.clients += 1
spawned_interface.read_loop()
@ -969,11 +969,11 @@ class I2PInterface(Interface):
if from_spawned: self.oa_freq_deque.append(time.time())
def detach(self):
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG)
RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
self.i2p.stop()
def __str__(self):
return "I2PInterface["+self.name+"]"
return f"I2PInterface[{self.name}]"
class I2PInterfaceHandler(socketserver.BaseRequestHandler):
def __init__(self, callback, *args, **keys):

View File

@ -140,16 +140,16 @@ class Interface:
selected_announce_packet = announce_packet
if selected_announce_packet != None:
RNS.log("Releasing held announce packet "+str(selected_announce_packet)+" from "+str(self), RNS.LOG_EXTREME)
RNS.log(f"Releasing held announce packet {selected_announce_packet} from {self}", RNS.LOG_EXTREME)
self.ic_held_release = time.time() + self.ic_held_release_interval
self.held_announces.pop(selected_announce_packet.destination_hash)
def release():
RNS.Transport.inbound(selected_announce_packet.raw, selected_announce_packet.receiving_interface)
threading.Thread(target=release, daemon=True).start()
except Exception as e:
RNS.log("An error occurred while processing held announces for "+str(self), RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), 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)
def received_announce(self):
self.ia_freq_deque.append(time.time())
@ -170,7 +170,7 @@ class Interface:
for i in range(1,dq_len):
delta_sum += self.ia_freq_deque[i]-self.ia_freq_deque[i-1]
delta_sum += time.time() - self.ia_freq_deque[dq_len-1]
if delta_sum == 0:
avg = 0
else:
@ -187,7 +187,7 @@ class Interface:
for i in range(1,dq_len):
delta_sum += self.oa_freq_deque[i]-self.oa_freq_deque[i-1]
delta_sum += time.time() - self.oa_freq_deque[dq_len-1]
if delta_sum == 0:
avg = 0
else:
@ -234,7 +234,7 @@ class Interface:
except Exception as e:
self.announce_queue = []
RNS.log("Error while processing announce queue on "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while processing announce queue on {self}. The contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("The announce queue for this interface has been cleared.", RNS.LOG_ERROR)
def detach(self):

View File

@ -71,9 +71,9 @@ class KISSInterface(Interface):
RNS.panic()
super().__init__()
self.HW_MTU = 564
if beacon_data == None:
beacon_data = ""
@ -113,17 +113,17 @@ class KISSInterface(Interface):
try:
self.open_port()
except Exception as e:
RNS.log("Could not open serial port "+self.port, RNS.LOG_ERROR)
RNS.log(f"Could not open serial port {self.port}", RNS.LOG_ERROR)
raise e
if self.serial.is_open:
self.configure_device()
else:
raise IOError("Could not open serial port")
raise OSError("Could not open serial port")
def open_port(self):
RNS.log("Opening serial port "+self.port+"...", RNS.LOG_VERBOSE)
RNS.log(f"Opening serial port {self.port}...", RNS.LOG_VERBOSE)
self.serial = self.pyserial.Serial(
port = self.port,
baudrate = self.speed,
@ -146,7 +146,7 @@ class KISSInterface(Interface):
thread.daemon = True
thread.start()
self.online = True
RNS.log("Serial port "+self.port+" is now open")
RNS.log(f"Serial port {self.port} is now open")
RNS.log("Configuring KISS interface parameters...")
self.setPreamble(self.preamble)
self.setTxTail(self.txtail)
@ -168,7 +168,7 @@ class KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXDELAY])+bytes([preamble])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure KISS interface preamble to "+str(preamble_ms)+" (command value "+str(preamble)+")")
raise OSError(f"Could not configure KISS interface preamble to {preamble_ms} (command value {preamble})")
def setTxTail(self, txtail):
txtail_ms = txtail
@ -181,7 +181,7 @@ class KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXTAIL])+bytes([txtail])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure KISS interface TX tail to "+str(txtail_ms)+" (command value "+str(txtail)+")")
raise OSError(f"Could not configure KISS interface TX tail to {txtail_ms} (command value {txtail})")
def setPersistence(self, persistence):
if persistence < 0:
@ -192,7 +192,7 @@ class KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_P])+bytes([persistence])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure KISS interface persistence to "+str(persistence))
raise OSError(f"Could not configure KISS interface persistence to {persistence}")
def setSlotTime(self, slottime):
slottime_ms = slottime
@ -205,20 +205,20 @@ class KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SLOTTIME])+bytes([slottime])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("Could not configure KISS interface slot time to "+str(slottime_ms)+" (command value "+str(slottime)+")")
raise OSError(f"Could not configure KISS interface slot time to {slottime_ms} (command value {slottime})")
def setFlowControl(self, flow_control):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_READY])+bytes([0x01])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
if (flow_control):
raise IOError("Could not enable KISS interface flow control")
raise OSError("Could not enable KISS interface flow control")
else:
raise IOError("Could not enable KISS interface flow control")
raise OSError("Could not enable KISS interface flow control")
def processIncoming(self, data):
self.rxb += len(data)
self.rxb += len(data)
self.owner.inbound(data, self)
@ -244,7 +244,7 @@ class KISSInterface(Interface):
self.first_tx = time.time()
if written != len(frame):
raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data)))
raise OSError(f"Serial interface only wrote {written} bytes of {len(data)}")
else:
self.queue(data)
@ -311,21 +311,21 @@ class KISSInterface(Interface):
if self.flow_control:
if not self.interface_ready:
if time.time() > self.flow_control_locked + self.flow_control_timeout:
RNS.log("Interface "+str(self)+" is unlocking flow control due to time-out. This should not happen. Your hardware might have missed a flow-control READY command, or maybe it does not support flow-control.", RNS.LOG_WARNING)
RNS.log(f"Interface {self} is unlocking flow control due to time-out. This should not happen. Your hardware might have missed a flow-control READY command, or maybe it does not support flow-control.", RNS.LOG_WARNING)
self.process_queue()
if self.beacon_i != None and self.beacon_d != None:
if self.first_tx != None:
if time.time() > self.first_tx + self.beacon_i:
RNS.log("Interface "+str(self)+" is transmitting beacon data: "+str(self.beacon_d.decode("utf-8")), RNS.LOG_DEBUG)
RNS.log(f"Interface {self} is transmitting beacon data: {self.beacon_d.decode('utf-8')}", RNS.LOG_DEBUG)
self.first_tx = None
self.processOutgoing(self.beacon_d)
except Exception as e:
self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", 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)
if RNS.Reticulum.panic_on_interface_error:
RNS.panic()
@ -339,17 +339,17 @@ class KISSInterface(Interface):
while not self.online:
try:
time.sleep(5)
RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_VERBOSE)
RNS.log(f"Attempting to reconnect serial port {self.port} for {self}...", RNS.LOG_VERBOSE)
self.open_port()
if self.serial.is_open:
self.configure_device()
except Exception as e:
RNS.log("Error while reconnecting port, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while reconnecting port, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("Reconnected serial port for "+str(self))
RNS.log(f"Reconnected serial port for {self}")
def should_ingress_limit(self):
return False
def __str__(self):
return "KISSInterface["+self.name+"]"
return f"KISSInterface[{self.name}]"

View File

@ -58,11 +58,11 @@ class LocalClientInterface(Interface):
# TODO: Remove at some point
# self.rxptime = 0
self.HW_MTU = 1064
self.online = False
self.IN = True
self.OUT = False
self.socket = None
@ -133,10 +133,10 @@ class LocalClientInterface(Interface):
self.connect()
except Exception as e:
RNS.log("Connection attempt for "+str(self)+" failed: "+str(e), RNS.LOG_DEBUG)
RNS.log(f"Connection attempt for {self} failed: {e}", RNS.LOG_DEBUG)
if not self.never_connected:
RNS.log("Reconnected socket for "+str(self)+".", RNS.LOG_INFO)
RNS.log(f"Reconnected socket for {self}.", RNS.LOG_INFO)
self.reconnecting = False
thread = threading.Thread(target=self.read_loop)
@ -146,20 +146,20 @@ class LocalClientInterface(Interface):
time.sleep(LocalClientInterface.RECONNECT_WAIT+2)
RNS.Transport.shared_connection_reappeared()
threading.Thread(target=job, daemon=True).start()
else:
RNS.log("Attempt to reconnect on a non-initiator shared local interface. This should not happen.", RNS.LOG_ERROR)
raise IOError("Attempt to reconnect on a non-initiator local interface")
raise OSError("Attempt to reconnect on a non-initiator local interface")
def processIncoming(self, data):
self.rxb += len(data)
if hasattr(self, "parent_interface") and self.parent_interface != None:
self.parent_interface.rxb += len(data)
# TODO: Remove at some point
# processing_start = time.time()
self.owner.inbound(data, self)
# TODO: Remove at some point
@ -188,8 +188,8 @@ class LocalClientInterface(Interface):
self.parent_interface.txb += len(data)
except Exception as e:
RNS.log("Exception occurred while transmitting via "+str(self)+", tearing down interface", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Exception occurred while transmitting via {self}, tearing down interface", RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
self.teardown()
@ -226,7 +226,7 @@ class LocalClientInterface(Interface):
else:
self.online = False
if self.is_connected_to_shared_instance and not self.detached:
RNS.log("Socket for "+str(self)+" was closed, attempting to reconnect...", RNS.LOG_WARNING)
RNS.log(f"Socket for {self} was closed, attempting to reconnect...", RNS.LOG_WARNING)
RNS.Transport.shared_connection_disappeared()
self.reconnect()
else:
@ -234,29 +234,29 @@ class LocalClientInterface(Interface):
break
except Exception as e:
self.online = False
RNS.log("An interface error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log("Tearing down "+str(self), RNS.LOG_ERROR)
RNS.log(f"An interface error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log(f"Tearing down {self}", RNS.LOG_ERROR)
self.teardown()
def detach(self):
if self.socket != None:
if hasattr(self.socket, "close"):
if callable(self.socket.close):
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG)
RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
self.detached = True
try:
self.socket.shutdown(socket.SHUT_RDWR)
except Exception as e:
RNS.log("Error while shutting down socket for "+str(self)+": "+str(e))
RNS.log(f"Error while shutting down socket for {self}: {e}")
try:
self.socket.close()
except Exception as e:
RNS.log("Error while closing socket for "+str(self)+": "+str(e))
RNS.log(f"Error while closing socket for {self}: {e}")
self.socket = None
@ -276,19 +276,19 @@ class LocalClientInterface(Interface):
RNS.Transport.owner._should_persist_data()
if nowarning == False:
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR)
RNS.log(f"The interface {self} experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error:
RNS.panic()
if self.is_connected_to_shared_instance:
if nowarning == False:
RNS.log("Permanently lost connection to local shared RNS instance. Exiting now.", RNS.LOG_CRITICAL)
RNS.exit()
def __str__(self):
return "LocalInterface["+str(self.target_port)+"]"
return f"LocalInterface[{self.target_port}]"
class LocalServerInterface(Interface):
@ -297,7 +297,7 @@ class LocalServerInterface(Interface):
super().__init__()
self.online = False
self.clients = 0
self.IN = True
self.OUT = False
self.name = "Reticulum"
@ -360,7 +360,7 @@ class LocalServerInterface(Interface):
if from_spawned: self.oa_freq_deque.append(time.time())
def __str__(self):
return "Shared Instance["+str(self.bind_port)+"]"
return f"Shared Instance[{self.bind_port}]"
class LocalInterfaceHandler(socketserver.BaseRequestHandler):
def __init__(self, callback, *args, **keys):

View File

@ -49,7 +49,7 @@ class PipeInterface(Interface):
owner = None
command = None
def __init__(self, owner, name, command, respawn_delay):
if respawn_delay == None:
respawn_delay = 5
@ -57,7 +57,7 @@ class PipeInterface(Interface):
super().__init__()
self.HW_MTU = 1064
self.owner = owner
self.name = name
self.command = command
@ -72,18 +72,18 @@ class PipeInterface(Interface):
self.open_pipe()
except Exception as e:
RNS.log("Could connect pipe for interface "+str(self), RNS.LOG_ERROR)
RNS.log(f"Could connect pipe for interface {self}", RNS.LOG_ERROR)
raise e
if self.pipe_is_open:
self.configure_pipe()
else:
raise IOError("Could not connect pipe")
raise OSError("Could not connect pipe")
def open_pipe(self):
RNS.log("Connecting subprocess pipe for "+str(self)+"...", RNS.LOG_VERBOSE)
RNS.log(f"Connecting subprocess pipe for {self}...", RNS.LOG_VERBOSE)
try:
self.process = subprocess.Popen(shlex.split(self.command), stdin=subprocess.PIPE, stdout=subprocess.PIPE)
self.pipe_is_open = True
@ -98,11 +98,11 @@ class PipeInterface(Interface):
thread.daemon = True
thread.start()
self.online = True
RNS.log("Subprocess pipe for "+str(self)+" is now connected", RNS.LOG_VERBOSE)
RNS.log(f"Subprocess pipe for {self} is now connected", RNS.LOG_VERBOSE)
def processIncoming(self, data):
self.rxb += len(data)
self.rxb += len(data)
self.owner.inbound(data, self)
@ -111,9 +111,9 @@ class PipeInterface(Interface):
data = bytes([HDLC.FLAG])+HDLC.escape(data)+bytes([HDLC.FLAG])
written = self.process.stdin.write(data)
self.process.stdin.flush()
self.txb += len(data)
self.txb += len(data)
if written != len(data):
raise IOError("Pipe interface only wrote "+str(written)+" bytes of "+str(len(data)))
raise OSError(f"Pipe interface only wrote {written} bytes of {len(data)}")
def readLoop(self):
@ -150,9 +150,9 @@ class PipeInterface(Interface):
escape = False
data_buffer = data_buffer+bytes([byte])
RNS.log("Subprocess terminated on "+str(self))
RNS.log(f"Subprocess terminated on {self}")
self.process.kill()
except Exception as e:
self.online = False
try:
@ -160,9 +160,9 @@ class PipeInterface(Interface):
except Exception as e:
pass
RNS.log("A pipe error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", 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)
if RNS.Reticulum.panic_on_interface_error:
RNS.panic()
@ -175,14 +175,14 @@ class PipeInterface(Interface):
while not self.online:
try:
time.sleep(self.respawn_delay)
RNS.log("Attempting to respawn subprocess for "+str(self)+"...", RNS.LOG_VERBOSE)
RNS.log(f"Attempting to respawn subprocess for {self}...", RNS.LOG_VERBOSE)
self.open_pipe()
if self.pipe_is_open:
self.configure_pipe()
except Exception as e:
RNS.log("Error while spawning subprocess, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while spawning subprocess, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("Reconnected pipe for "+str(self))
RNS.log(f"Reconnected pipe for {self}")
def __str__(self):
return "PipeInterface["+self.name+"]"
return f"PipeInterface[{self.name}]"

View File

@ -33,7 +33,7 @@ class KISS():
FESC = 0xDB
TFEND = 0xDC
TFESC = 0xDD
CMD_UNKNOWN = 0xFE
CMD_DATA = 0x00
CMD_FREQUENCY = 0x01
@ -69,11 +69,11 @@ class KISS():
DETECT_REQ = 0x73
DETECT_RESP = 0x46
RADIO_STATE_OFF = 0x00
RADIO_STATE_ON = 0x01
RADIO_STATE_ASK = 0xFF
CMD_ERROR = 0x90
ERROR_INITRADIO = 0x01
ERROR_TXFAILED = 0x02
@ -91,7 +91,7 @@ class KISS():
data = data.replace(bytes([0xdb]), bytes([0xdb, 0xdd]))
data = data.replace(bytes([0xc0]), bytes([0xdb, 0xdc]))
return data
class RNodeInterface(Interface):
MAX_CHUNK = 32768
@ -132,7 +132,7 @@ class RNodeInterface(Interface):
super().__init__()
self.HW_MTU = 508
self.pyserial = serial
self.serial = None
self.owner = owner
@ -211,31 +211,31 @@ class RNodeInterface(Interface):
self.validcfg = True
if (self.frequency < RNodeInterface.FREQ_MIN or self.frequency > RNodeInterface.FREQ_MAX):
RNS.log("Invalid frequency configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid frequency configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if (self.txpower < 0 or self.txpower > 22):
RNS.log("Invalid TX power configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid TX power configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if (self.bandwidth < 7800 or self.bandwidth > 1625000):
RNS.log("Invalid bandwidth configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid bandwidth configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if (self.sf < 5 or self.sf > 12):
RNS.log("Invalid spreading factor configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid spreading factor configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if (self.cr < 5 or self.cr > 8):
RNS.log("Invalid coding rate configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid coding rate configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if (self.st_alock and (self.st_alock < 0.0 or self.st_alock > 100.0)):
RNS.log("Invalid short-term airtime limit configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid short-term airtime limit configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if (self.lt_alock and (self.lt_alock < 0.0 or self.lt_alock > 100.0)):
RNS.log("Invalid long-term airtime limit configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid long-term airtime limit configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if id_interval != None and id_callsign != None:
@ -244,14 +244,14 @@ class RNodeInterface(Interface):
self.id_callsign = id_callsign.encode("utf-8")
self.id_interval = id_interval
else:
RNS.log("The encoded ID callsign for "+str(self)+" exceeds the max length of "+str(RNodeInterface.CALLSIGN_MAX_LEN)+" bytes.", RNS.LOG_ERROR)
RNS.log(f"The encoded ID callsign for {self} exceeds the max length of {RNodeInterface.CALLSIGN_MAX_LEN} bytes.", RNS.LOG_ERROR)
self.validcfg = False
else:
self.id_interval = None
self.id_callsign = None
if (not self.validcfg):
raise ValueError("The configuration for "+str(self)+" contains errors, interface is offline")
raise ValueError(f"The configuration for {self} contains errors, interface is offline")
try:
self.open_port()
@ -259,11 +259,11 @@ class RNodeInterface(Interface):
if self.serial.is_open:
self.configure_device()
else:
raise IOError("Could not open serial port")
raise OSError("Could not open serial port")
except Exception as e:
RNS.log("Could not open serial port for interface "+str(self), RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Could not open serial port for interface {self}", RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("Reticulum will attempt to bring up this interface periodically", RNS.LOG_ERROR)
if not self.detached and not self.reconnecting:
thread = threading.Thread(target=self.reconnect_port)
@ -273,7 +273,7 @@ class RNodeInterface(Interface):
def open_port(self):
if not self.use_ble:
RNS.log("Opening serial port "+self.port+"...")
RNS.log(f"Opening serial port {self.port}...")
self.serial = self.pyserial.Serial(
port = self.port,
baudrate = self.speed,
@ -287,9 +287,14 @@ class RNodeInterface(Interface):
write_timeout = None,
dsrdtr = False,
)
else:
RNS.log(f"Opening BLE connection for {self}...")
if self.ble != None and self.ble.running == False:
self.ble.close()
self.ble.cleanup()
self.ble = None
if self.ble == None:
self.ble = BLEConnection(owner=self, target_name=self.ble_name, target_bt_addr=self.ble_addr)
self.serial = self.ble
@ -298,8 +303,7 @@ class RNodeInterface(Interface):
while not self.ble.connected and time.time() < open_time + self.ble.CONNECT_TIMEOUT:
time.sleep(1)
def configure_device(self):
def reset_radio_state(self):
self.r_frequency = None
self.r_bandwidth = None
self.r_txpower = None
@ -307,6 +311,10 @@ class RNodeInterface(Interface):
self.r_cr = None
self.r_state = None
self.r_lock = None
self.detected = False
def configure_device(self):
self.reset_radio_state()
sleep(2.0)
thread = threading.Thread(target=self.readLoop)
@ -325,28 +333,28 @@ class RNodeInterface(Interface):
detect_time = RNS.prettytime(time.time()-detect_time)
else:
RNS.log(f"RNode detect timed out over {self.port}", RNS.LOG_ERROR)
if not self.detected:
RNS.log("Could not detect device for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Could not detect device for {self}", RNS.LOG_ERROR)
self.serial.close()
else:
if self.platform == KISS.PLATFORM_ESP32 or self.platform == KISS.PLATFORM_NRF52:
self.display = True
RNS.log("Serial port "+self.port+" is now open")
RNS.log(f"Serial port {self.port} is now open")
RNS.log("Configuring RNode interface...", RNS.LOG_VERBOSE)
self.initRadio()
if (self.validateRadioState()):
self.interface_ready = True
RNS.log(str(self)+" is configured and powered up")
RNS.log(f"{self} is configured and powered up")
sleep(0.3)
self.online = True
else:
RNS.log("After configuring "+str(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("Aborting RNode startup", RNS.LOG_ERROR)
self.serial.close()
def initRadio(self):
self.setFrequency()
@ -365,27 +373,27 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND, KISS.CMD_DETECT, KISS.DETECT_REQ, KISS.FEND, KISS.CMD_FW_VERSION, 0x00, KISS.FEND, KISS.CMD_PLATFORM, 0x00, KISS.FEND, KISS.CMD_MCU, 0x00, KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while detecting hardware for "+str(self))
raise OSError(f"An IO error occurred while detecting hardware for {self}")
def leave(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("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):
if self.display != None:
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x01, KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while enabling external framebuffer on device")
raise OSError("An IO error occurred while enabling external framebuffer on device")
def disable_external_framebuffer(self):
if self.display != None:
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x00, KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while disabling external framebuffer on device")
raise OSError("An IO error occurred while disabling external framebuffer on device")
FB_PIXEL_WIDTH = 64
FB_BITS_PER_PIXEL = 1
@ -406,16 +414,16 @@ class RNodeInterface(Interface):
data = line_byte+line_data
escaped_data = KISS.escape(data)
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FB_WRITE])+escaped_data+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while writing framebuffer data device")
raise OSError("An IO error occurred while writing framebuffer data device")
def hard_reset(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while restarting device")
raise OSError("An IO error occurred while restarting device")
sleep(2.25);
def setFrequency(self):
@ -428,7 +436,7 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FREQUENCY])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring frequency for "+str(self))
raise OSError(f"An IO error occurred while configuring frequency for {self}")
def setBandwidth(self):
c1 = self.bandwidth >> 24
@ -440,28 +448,28 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_BANDWIDTH])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring bandwidth for "+str(self))
raise OSError(f"An IO error occurred while configuring bandwidth for {self}")
def setTXPower(self):
txp = bytes([self.txpower])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXPOWER])+txp+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring TX power for "+str(self))
raise OSError(f"An IO error occurred while configuring TX power for {self}")
def setSpreadingFactor(self):
sf = bytes([self.sf])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SF])+sf+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring spreading factor for "+str(self))
raise OSError(f"An IO error occurred while configuring spreading factor for {self}")
def setCodingRate(self):
cr = bytes([self.cr])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_CR])+cr+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring coding rate for "+str(self))
raise OSError(f"An IO error occurred while configuring coding rate for {self}")
def setSTALock(self):
if self.st_alock != None:
@ -473,7 +481,7 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_ST_ALOCK])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring short-term airtime limit for "+str(self))
raise OSError(f"An IO error occurred while configuring short-term airtime limit for {self}")
def setLTALock(self):
if self.lt_alock != None:
@ -485,14 +493,14 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_LT_ALOCK])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring long-term airtime limit for "+str(self))
raise OSError(f"An IO error occurred while configuring long-term airtime limit for {self}")
def setRadioState(self, state):
self.state = state
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring radio state for "+str(self))
raise OSError(f"An IO error occurred while configuring radio state for {self}")
def validate_firmware(self):
if (self.maj_version > RNodeInterface.REQUIRED_FW_VER_MAJ):
@ -501,19 +509,26 @@ class RNodeInterface(Interface):
if (self.maj_version >= RNodeInterface.REQUIRED_FW_VER_MAJ):
if (self.min_version >= RNodeInterface.REQUIRED_FW_VER_MIN):
self.firmware_ok = True
if self.firmware_ok:
return
RNS.log("The firmware version of the connected RNode is "+str(self.maj_version)+"."+str(self.min_version), RNS.LOG_ERROR)
RNS.log("This version of Reticulum requires at least version "+str(RNodeInterface.REQUIRED_FW_VER_MAJ)+"."+str(RNodeInterface.REQUIRED_FW_VER_MIN), RNS.LOG_ERROR)
RNS.log(f"The firmware version of the connected RNode is {self.maj_version}.{self.min_version}", RNS.LOG_ERROR)
RNS.log(f"This version of Reticulum requires at least version {RNodeInterface.REQUIRED_FW_VER_MAJ}.{RNodeInterface.REQUIRED_FW_VER_MIN}", RNS.LOG_ERROR)
RNS.log("Please update your RNode firmware with rnodeconf from https://github.com/markqvist/rnodeconfigutil/")
RNS.panic()
def validateRadioState(self):
RNS.log("Waiting for radio configuration validation for "+str(self)+"...", RNS.LOG_VERBOSE)
sleep(0.25);
RNS.log(f"Waiting for radio configuration validation for {self}...", RNS.LOG_VERBOSE)
if self.use_ble:
sleep(1.00)
else:
sleep(0.25)
if self.use_ble and self.ble != None and self.ble.device_disappeared:
RNS.log(f"Device disappeared during radio state validation for {self}", RNS.LOG_ERROR)
return False
self.validcfg = True
if (self.r_frequency != None and abs(self.frequency - int(self.r_frequency)) > 100):
@ -542,7 +557,7 @@ class RNodeInterface(Interface):
try:
self.bitrate = self.r_sf * ( (4.0/self.r_cr) / (math.pow(2,self.r_sf)/(self.r_bandwidth/1000)) ) * 1000
self.bitrate_kbps = round(self.bitrate/1000.0, 2)
RNS.log(str(self)+" On-air bitrate is now "+str(self.bitrate_kbps)+ " kbps", RNS.LOG_VERBOSE)
RNS.log(f"{self} On-air bitrate is now {self.bitrate_kbps} kbps", RNS.LOG_VERBOSE)
except:
self.bitrate = 0
@ -573,7 +588,7 @@ class RNodeInterface(Interface):
self.txb += datalen
if written != len(frame):
raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data)))
raise OSError(f"Serial interface only wrote {written} bytes of {len(data)}")
else:
self.queue(data)
@ -639,7 +654,7 @@ class RNodeInterface(Interface):
command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4):
self.r_frequency = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3]
RNS.log(str(self)+" Radio reporting frequency is "+str(self.r_frequency/1000000.0)+" MHz", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting frequency is {self.r_frequency / 1000000.0} MHz", RNS.LOG_DEBUG)
self.updateBitrate()
elif (command == KISS.CMD_BANDWIDTH):
@ -655,26 +670,26 @@ class RNodeInterface(Interface):
command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4):
self.r_bandwidth = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3]
RNS.log(str(self)+" Radio reporting bandwidth is "+str(self.r_bandwidth/1000.0)+" KHz", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting bandwidth is {self.r_bandwidth / 1000.0} KHz", RNS.LOG_DEBUG)
self.updateBitrate()
elif (command == KISS.CMD_TXPOWER):
self.r_txpower = byte
RNS.log(str(self)+" Radio reporting TX power is "+str(self.r_txpower)+" dBm", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting TX power is {self.r_txpower} dBm", RNS.LOG_DEBUG)
elif (command == KISS.CMD_SF):
self.r_sf = byte
RNS.log(str(self)+" Radio reporting spreading factor is "+str(self.r_sf), RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting spreading factor is {self.r_sf}", RNS.LOG_DEBUG)
self.updateBitrate()
elif (command == KISS.CMD_CR):
self.r_cr = byte
RNS.log(str(self)+" Radio reporting coding rate is "+str(self.r_cr), RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting coding rate is {self.r_cr}", RNS.LOG_DEBUG)
self.updateBitrate()
elif (command == KISS.CMD_RADIO_STATE):
self.r_state = byte
if self.r_state:
pass
else:
RNS.log(str(self)+" Radio reporting state is offline", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting state is offline", RNS.LOG_DEBUG)
elif (command == KISS.CMD_RADIO_LOCK):
self.r_lock = byte
@ -752,7 +767,7 @@ class RNodeInterface(Interface):
if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1]
self.r_st_alock = at/100.0
RNS.log(str(self)+" Radio reporting short-term airtime limit is "+str(self.r_st_alock)+"%", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting short-term airtime limit is {self.r_st_alock}%", RNS.LOG_DEBUG)
elif (command == KISS.CMD_LT_ALOCK):
if (byte == KISS.FESC):
escape = True
@ -767,7 +782,7 @@ class RNodeInterface(Interface):
if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1]
self.r_lt_alock = at/100.0
RNS.log(str(self)+" Radio reporting long-term airtime limit is "+str(self.r_lt_alock)+"%", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting long-term airtime limit is {self.r_lt_alock}%", RNS.LOG_DEBUG)
elif (command == KISS.CMD_STAT_CHTM):
if (byte == KISS.FESC):
escape = True
@ -784,7 +799,7 @@ class RNodeInterface(Interface):
atl = command_buffer[2] << 8 | command_buffer[3]
cus = command_buffer[4] << 8 | command_buffer[5]
cul = command_buffer[6] << 8 | command_buffer[7]
self.r_airtime_short = ats/100.0
self.r_airtime_long = atl/100.0
self.r_channel_load_short = cus/100.0
@ -813,9 +828,9 @@ class RNodeInterface(Interface):
self.r_preamble_symbols = prs
self.r_premable_time_ms = prt
self.r_csma_slot_time_ms = cst
RNS.log(str(self)+" Radio reporting symbol time is "+str(round(self.r_symbol_time_ms,2))+"ms (at "+str(self.r_symbol_rate)+" baud)", RNS.LOG_DEBUG)
RNS.log(str(self)+" Radio reporting preamble is "+str(self.r_preamble_symbols)+" symbols ("+str(self.r_premable_time_ms)+"ms)", RNS.LOG_DEBUG)
RNS.log(str(self)+" Radio reporting CSMA slot time is "+str(self.r_csma_slot_time_ms)+"ms", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting symbol time is {round(self.r_symbol_time_ms, 2)}ms (at {self.r_symbol_rate} baud)", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting preamble is {self.r_preamble_symbols} symbols ({self.r_premable_time_ms}ms)", RNS.LOG_DEBUG)
RNS.log(f"{self} Radio reporting CSMA slot time is {self.r_csma_slot_time_ms}ms", RNS.LOG_DEBUG)
elif (command == KISS.CMD_STAT_BAT):
if (byte == KISS.FESC):
escape = True
@ -843,26 +858,26 @@ class RNodeInterface(Interface):
self.mcu = byte
elif (command == KISS.CMD_ERROR):
if (byte == KISS.ERROR_INITRADIO):
RNS.log(str(self)+" hardware initialisation error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR)
raise IOError("Radio initialisation failure")
RNS.log(f"{self} hardware initialisation error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise OSError("Radio initialisation failure")
elif (byte == KISS.ERROR_TXFAILED):
RNS.log(str(self)+" hardware TX error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR)
raise IOError("Hardware transmit failure")
RNS.log(f"{self} hardware TX error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise OSError("Hardware transmit failure")
elif (byte == KISS.ERROR_MEMORY_LOW):
RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+"): Memory exhausted", RNS.LOG_ERROR)
RNS.log(f"{self} hardware error (code {RNS.hexrep(byte)}): Memory exhausted", RNS.LOG_ERROR)
self.hw_errors.append({"error": KISS.ERROR_MEMORY_LOW, "description": "Memory exhausted on connected device"})
elif (byte == KISS.ERROR_MODEM_TIMEOUT):
RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+"): Modem communication timed out", RNS.LOG_ERROR)
RNS.log(f"{self} hardware error (code {RNS.hexrep(byte)}): Modem communication timed out", RNS.LOG_ERROR)
self.hw_errors.append({"error": KISS.ERROR_MODEM_TIMEOUT, "description": "Modem communication timed out on connected device"})
else:
RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR)
raise IOError("Unknown hardware failure")
RNS.log(f"{self} hardware error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise OSError("Unknown hardware failure")
elif (command == KISS.CMD_RESET):
if (byte == 0xF8):
if self.platform == KISS.PLATFORM_ESP32:
if self.online:
RNS.log("Detected reset while device was online, reinitialising device...", RNS.LOG_ERROR)
raise IOError("ESP32 reset")
raise OSError("ESP32 reset")
elif (command == KISS.CMD_READY):
self.process_queue()
elif (command == KISS.CMD_DETECT):
@ -870,11 +885,11 @@ class RNodeInterface(Interface):
self.detected = True
else:
self.detected = False
else:
time_since_last = int(time.time()*1000) - last_read_ms
if len(data_buffer) > 0 and time_since_last > self.timeout:
RNS.log(str(self)+" serial read timeout in command "+str(command), RNS.LOG_WARNING)
RNS.log(f"{self} serial read timeout in command {command}", RNS.LOG_WARNING)
data_buffer = b""
in_frame = False
command = KISS.CMD_UNKNOWN
@ -883,15 +898,15 @@ class RNodeInterface(Interface):
if self.id_interval != None and self.id_callsign != None:
if self.first_tx != None:
if time.time() > self.first_tx + self.id_interval:
RNS.log("Interface "+str(self)+" is transmitting beacon data: "+str(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)
sleep(0.08)
except Exception as e:
self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", 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)
if RNS.Reticulum.panic_on_interface_error:
RNS.panic()
@ -912,23 +927,23 @@ class RNodeInterface(Interface):
while not self.online and not self.detached:
try:
time.sleep(5)
RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_VERBOSE)
RNS.log(f"Attempting to reconnect serial port {self.port} for {self}...", RNS.LOG_VERBOSE)
self.open_port()
if self.serial.is_open:
self.configure_device()
except Exception as e:
RNS.log("Error while reconnecting port, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while reconnecting port, the contained exception was: {e}", RNS.LOG_ERROR)
self.reconnecting = False
if self.online:
RNS.log("Reconnected serial port for "+str(self))
RNS.log(f"Reconnected serial port for {self}")
def detach(self):
self.detached = True
self.disable_external_framebuffer()
self.setRadioState(KISS.RADIO_STATE_OFF)
self.leave()
if self.use_ble:
self.ble.close()
@ -965,7 +980,7 @@ class RNodeInterface(Interface):
return data
def __str__(self):
return "RNodeInterface["+str(self.name)+"]"
return f"RNodeInterface[{self.name}]"
class BLEConnection():
UART_SERVICE_UUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
@ -1010,18 +1025,20 @@ class BLEConnection():
self.target_bt_addr = target_bt_addr
self.scan_timeout = BLEConnection.SCAN_TIMEOUT
self.ble_device = None
self.last_client = None
self.connected = False
self.running = False
self.should_run = False
self.must_disconnect = False
self.connect_job_running = False
self.device_disappeared = False
import importlib
if BLEConnection.bleak == None:
if importlib.util.find_spec("bleak") != None:
import bleak
BLEConnection.bleak = bleak
import asyncio
BLEConnection.asyncio = asyncio
else:
@ -1032,8 +1049,17 @@ class BLEConnection():
self.should_run = True
self.connection_thread = threading.Thread(target=self.connection_job, daemon=True).start()
def cleanup(self):
try:
if self.last_client != None:
self.asyncio.run(self.last_client.disconnect())
except Exception as e:
RNS.log(f"Error while disconnecting BLE device on cleanup for {self.owner}", RNS.LOG_ERROR)
self.should_run = False
def connection_job(self):
while (self.should_run):
while self.should_run:
if self.ble_device == None:
self.ble_device = self.find_target_device()
@ -1043,6 +1069,10 @@ class BLEConnection():
time.sleep(1)
self.cleanup()
self.running = False
RNS.log(f"BLE connection job for {self.owner} ended", RNS.LOG_DEBUG)
def connect_device(self):
if self.ble_device != None and type(self.ble_device) == self.bleak.backends.device.BLEDevice:
RNS.log(f"Connecting BLE device {self.ble_device} for {self.owner}...", RNS.LOG_DEBUG)
@ -1056,6 +1086,7 @@ class BLEConnection():
self.connected = True
self.ble_device = ble_client
self.last_client = ble_client
self.owner.port = str(f"ble://{ble_client.address}")
loop = self.asyncio.get_running_loop()
@ -1077,12 +1108,14 @@ class BLEConnection():
self.asyncio.run(connect_job())
except Exception as e:
RNS.log(f"Could not connect BLE device {self.ble_device} for {self.owner}. Possibly missing authentication.", RNS.LOG_ERROR)
self.connect_job_running = False
def device_disconnected(self, device):
RNS.log(f"BLE device for {self.owner} disconnected", RNS.LOG_NOTICE)
self.connected = False
self.ble_device = None
self.device_disappeared = True
def find_target_device(self):
RNS.log(f"Searching for attachable BLE device for {self.owner}...", RNS.LOG_EXTREME)
@ -1100,13 +1133,19 @@ class BLEConnection():
else:
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)
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)
return False
device = self.asyncio.run(self.bleak.BleakScanner.find_device_by_filter(device_filter, timeout=self.scan_timeout))
device = None
try:
device = self.asyncio.run(self.bleak.BleakScanner.find_device_by_filter(device_filter, timeout=self.scan_timeout))
except Exception as e:
RNS.log(f"Error while finding BLE device for {self.owner}: {e}", RNS.LOG_ERROR)
self.should_run = False
return device
def device_bonded(self, device):
@ -1115,7 +1154,7 @@ class BLEConnection():
if "props" in device.details and "Bonded" in device.details["props"]:
if device.details["props"]["Bonded"] == True:
return True
except Exception as e:
RNS.log(f"Error while determining device bond status for {device}, the contained exception was: {e}", RNS.LOG_ERROR)

View File

@ -33,7 +33,7 @@ class KISS():
FESC = 0xDB
TFEND = 0xDC
TFESC = 0xDD
CMD_UNKNOWN = 0xFE
CMD_FREQUENCY = 0x01
CMD_BANDWIDTH = 0x02
@ -94,11 +94,11 @@ class KISS():
DETECT_REQ = 0x73
DETECT_RESP = 0x46
RADIO_STATE_OFF = 0x00
RADIO_STATE_ON = 0x01
RADIO_STATE_ASK = 0xFF
CMD_ERROR = 0x90
ERROR_INITRADIO = 0x01
ERROR_TXFAILED = 0x02
@ -159,7 +159,7 @@ class KISS():
data = data.replace(bytes([0xdb]), bytes([0xdb, 0xdd]))
data = data.replace(bytes([0xc0]), bytes([0xdb, 0xdc]))
return data
class RNodeMultiInterface(Interface):
MAX_CHUNK = 32768
@ -188,7 +188,7 @@ class RNodeMultiInterface(Interface):
super().__init__()
self.HW_MTU = 508
self.clients = 0
self.pyserial = serial
self.serial = None
@ -241,14 +241,14 @@ class RNodeMultiInterface(Interface):
self.id_callsign = id_callsign.encode("utf-8")
self.id_interval = id_interval
else:
RNS.log("The encoded ID callsign for "+str(self)+" exceeds the max length of "+str(RNodeMultiInterface.CALLSIGN_MAX_LEN)+" bytes.", RNS.LOG_ERROR)
RNS.log(f"The encoded ID callsign for {self} exceeds the max length of {RNodeMultiInterface.CALLSIGN_MAX_LEN} bytes.", RNS.LOG_ERROR)
self.validcfg = False
else:
self.id_interval = None
self.id_callsign = None
if (not self.validcfg):
raise ValueError("The configuration for "+str(self)+" contains errors, interface is offline")
raise ValueError(f"The configuration for {self} contains errors, interface is offline")
def start(self):
try:
@ -257,11 +257,11 @@ class RNodeMultiInterface(Interface):
if self.serial.is_open:
self.configure_device()
else:
raise IOError("Could not open serial port")
raise OSError("Could not open serial port")
except Exception as e:
RNS.log("Could not open serial port for interface "+str(self), RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Could not open serial port for interface {self}", RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("Reticulum will attempt to bring up this interface periodically", RNS.LOG_ERROR)
if not self.detached and not self.reconnecting:
thread = threading.Thread(target=self.reconnect_port)
@ -269,7 +269,7 @@ class RNodeMultiInterface(Interface):
thread.start()
def open_port(self):
RNS.log("Opening serial port "+self.port+"...")
RNS.log(f"Opening serial port {self.port}...")
self.serial = self.pyserial.Serial(
port = self.port,
baudrate = self.speed,
@ -294,15 +294,15 @@ class RNodeMultiInterface(Interface):
self.detect()
sleep(0.2)
if not self.detected:
RNS.log("Could not detect device for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Could not detect device for {self}", RNS.LOG_ERROR)
self.serial.close()
else:
if self.platform == KISS.PLATFORM_ESP32 or self.platform == KISS.PLATFORM_NRF52:
self.display = True
RNS.log("Serial port "+self.port+" is now open")
RNS.log(f"Serial port {self.port} is now open")
RNS.log("Creating subinterfaces...", RNS.LOG_VERBOSE)
for subint in self.subint_config:
subint_vport = int(subint[1])
@ -327,44 +327,44 @@ class RNodeMultiInterface(Interface):
interface.OUT = subint[10]
interface.IN = True
interface.announce_rate_target = self.announce_rate_target
interface.mode = self.mode
interface.HW_MTU = self.HW_MTU
interface.detected = True
RNS.Transport.interfaces.append(interface)
RNS.log("Spawned new RNode subinterface: "+str(interface), RNS.LOG_VERBOSE)
RNS.log(f"Spawned new RNode subinterface: {interface}", RNS.LOG_VERBOSE)
self.clients += 1
else:
raise ValueError("Virtual port \""+subint[1]+"\" for subinterface "+subint[0]+" does not exist on "+self.name)
raise ValueError(f"Virtual port \"{subint[1]}\" for subinterface {subint[0]} does not exist on {self.name}")
self.online = True
def detect(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_DETECT, KISS.DETECT_REQ, KISS.FEND, KISS.CMD_FW_VERSION, 0x00, KISS.FEND, KISS.CMD_PLATFORM, 0x00, KISS.FEND, KISS.CMD_MCU, 0x00, KISS.FEND, KISS.CMD_INTERFACES, 0x00, KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while detecting hardware for "+str(self))
raise OSError(f"An IO error occurred while detecting hardware for {self}")
def leave(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("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):
if self.display != None:
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x01, KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while enabling external framebuffer on device")
raise OSError("An IO error occurred while enabling external framebuffer on device")
def disable_external_framebuffer(self):
if self.display != None:
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x00, KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while disabling external framebuffer on device")
raise OSError("An IO error occurred while disabling external framebuffer on device")
FB_PIXEL_WIDTH = 64
FB_BITS_PER_PIXEL = 1
@ -385,16 +385,16 @@ class RNodeMultiInterface(Interface):
data = line_byte+line_data
escaped_data = KISS.escape(data)
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FB_WRITE])+escaped_data+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while writing framebuffer data device")
raise OSError("An IO error occurred while writing framebuffer data device")
def hard_reset(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while restarting device")
raise OSError("An IO error occurred while restarting device")
sleep(2.25);
def setFrequency(self, frequency, interface):
@ -407,7 +407,7 @@ class RNodeMultiInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_FREQUENCY])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring frequency for "+str(self))
raise OSError(f"An IO error occurred while configuring frequency for {self}")
self.selected_index = interface.index
def setBandwidth(self, bandwidth, interface):
@ -420,7 +420,7 @@ class RNodeMultiInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_BANDWIDTH])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring bandwidth for "+str(self))
raise OSError(f"An IO error occurred while configuring bandwidth for {self}")
self.selected_index = interface.index
def setTXPower(self, txpower, interface):
@ -428,7 +428,7 @@ class RNodeMultiInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_TXPOWER])+txp+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring TX power for "+str(self))
raise OSError(f"An IO error occurred while configuring TX power for {self}")
self.selected_index = interface.index
def setSpreadingFactor(self, sf, interface):
@ -436,7 +436,7 @@ class RNodeMultiInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_SF])+sf+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring spreading factor for "+str(self))
raise OSError(f"An IO error occurred while configuring spreading factor for {self}")
self.selected_index = interface.index
def setCodingRate(self, cr, interface):
@ -444,7 +444,7 @@ class RNodeMultiInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_CR])+cr+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring coding rate for "+str(self))
raise OSError(f"An IO error occurred while configuring coding rate for {self}")
self.selected_index = interface.index
def setSTALock(self, st_alock, interface):
@ -457,7 +457,7 @@ class RNodeMultiInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_ST_ALOCK])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring short-term airtime limit for "+str(self))
raise OSError(f"An IO error occurred while configuring short-term airtime limit for {self}")
self.selected_index = interface.index
def setLTALock(self, lt_alock, interface):
@ -470,7 +470,7 @@ class RNodeMultiInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_LT_ALOCK])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring long-term airtime limit for "+str(self))
raise OSError(f"An IO error occurred while configuring long-term airtime limit for {self}")
self.selected_index = interface.index
def setRadioState(self, state, interface):
@ -478,19 +478,19 @@ class RNodeMultiInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([interface.sel_cmd])+bytes([KISS.FEND])+bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND])
written = self.serial.write(kiss_command)
if written != len(kiss_command):
raise IOError("An IO error occurred while configuring radio state for "+str(self))
raise OSError(f"An IO error occurred while configuring radio state for {self}")
self.selected_index = interface.index
def validate_firmware(self):
if (self.maj_version >= RNodeMultiInterface.REQUIRED_FW_VER_MAJ):
if (self.min_version >= RNodeMultiInterface.REQUIRED_FW_VER_MIN):
self.firmware_ok = True
if self.firmware_ok:
return
RNS.log("The firmware version of the connected RNode is "+str(self.maj_version)+"."+str(self.min_version), RNS.LOG_ERROR)
RNS.log("This version of Reticulum requires at least version "+str(RNodeMultiInterface.REQUIRED_FW_VER_MAJ)+"."+str(RNodeMultiInterface.REQUIRED_FW_VER_MIN), RNS.LOG_ERROR)
RNS.log(f"The firmware version of the connected RNode is {self.maj_version}.{self.min_version}", RNS.LOG_ERROR)
RNS.log(f"This version of Reticulum requires at least version {RNodeMultiInterface.REQUIRED_FW_VER_MAJ}.{RNodeMultiInterface.REQUIRED_FW_VER_MIN}", RNS.LOG_ERROR)
RNS.log("Please update your RNode firmware with rnodeconf from https://github.com/markqvist/Reticulum/RNS/Utilities/rnodeconf.py")
RNS.panic()
@ -506,7 +506,7 @@ class RNodeMultiInterface(Interface):
self.txb += len(data)
if written != len(frame):
raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data)))
raise OSError(f"Serial interface only wrote {written} bytes of {len(data)}")
def received_announce(self, from_spawned=False):
if from_spawned: self.ia_freq_deque.append(time.time())
@ -589,7 +589,7 @@ class RNodeMultiInterface(Interface):
command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4):
self.subinterfaces[self.selected_index].r_frequency = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3]
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting frequency is "+str(self.subinterfaces[self.selected_index].r_frequency/1000000.0)+" MHz", RNS.LOG_DEBUG)
RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting frequency is {self.subinterfaces[self.selected_index].r_frequency / 1000000.0} MHz", RNS.LOG_DEBUG)
self.subinterfaces[self.selected_index].updateBitrate()
elif (command == KISS.CMD_BANDWIDTH):
@ -605,20 +605,20 @@ class RNodeMultiInterface(Interface):
command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4):
self.subinterfaces[self.selected_index].r_bandwidth = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3]
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting bandwidth is "+str(self.subinterfaces[self.selected_index].r_bandwidth/1000.0)+" KHz", RNS.LOG_DEBUG)
RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting bandwidth is {self.subinterfaces[self.selected_index].r_bandwidth / 1000.0} KHz", RNS.LOG_DEBUG)
self.subinterfaces[self.selected_index].updateBitrate()
elif (command == KISS.CMD_TXPOWER):
txp = byte - 256 if byte > 127 else byte
self.subinterfaces[self.selected_index].r_txpower = txp
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting TX power is "+str(self.subinterfaces[self.selected_index].r_txpower)+" dBm", RNS.LOG_DEBUG)
RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting TX power is {self.subinterfaces[self.selected_index].r_txpower} dBm", RNS.LOG_DEBUG)
elif (command == KISS.CMD_SF):
self.subinterfaces[self.selected_index].r_sf = byte
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting spreading factor is "+str(self.subinterfaces[self.selected_index].r_sf), RNS.LOG_DEBUG)
RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting spreading factor is {self.subinterfaces[self.selected_index].r_sf}", RNS.LOG_DEBUG)
self.subinterfaces[self.selected_index].updateBitrate()
elif (command == KISS.CMD_CR):
self.subinterfaces[self.selected_index].r_cr = byte
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting coding rate is "+str(self.subinterfaces[self.selected_index].r_cr), RNS.LOG_DEBUG)
RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting coding rate is {self.subinterfaces[self.selected_index].r_cr}", RNS.LOG_DEBUG)
self.subinterfaces[self.selected_index].updateBitrate()
elif (command == KISS.CMD_RADIO_STATE):
self.subinterfaces[self.selected_index].r_state = byte
@ -626,7 +626,7 @@ class RNodeMultiInterface(Interface):
pass
#RNS.log(str(self)+" Radio reporting state is online", RNS.LOG_DEBUG)
else:
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting state is offline", RNS.LOG_DEBUG)
RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting state is offline", RNS.LOG_DEBUG)
elif (command == KISS.CMD_RADIO_LOCK):
self.subinterfaces[self.selected_index].r_lock = byte
@ -705,7 +705,7 @@ class RNodeMultiInterface(Interface):
if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1]
self.subinterfaces[self.selected_index].r_st_alock = at/100.0
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting short-term airtime limit is "+str(self.subinterfaces[self.selected_index].r_st_alock)+"%", RNS.LOG_DEBUG)
RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting short-term airtime limit is {self.subinterfaces[self.selected_index].r_st_alock}%", RNS.LOG_DEBUG)
elif (command == KISS.CMD_LT_ALOCK):
if (byte == KISS.FESC):
escape = True
@ -720,7 +720,7 @@ class RNodeMultiInterface(Interface):
if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1]
self.subinterfaces[self.selected_index].r_lt_alock = at/100.0
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting long-term airtime limit is "+str(self.subinterfaces[self.selected_index].r_lt_alock)+"%", RNS.LOG_DEBUG)
RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting long-term airtime limit is {self.subinterfaces[self.selected_index].r_lt_alock}%", RNS.LOG_DEBUG)
elif (command == KISS.CMD_STAT_CHTM):
if (byte == KISS.FESC):
escape = True
@ -737,7 +737,7 @@ class RNodeMultiInterface(Interface):
atl = command_buffer[2] << 8 | command_buffer[3]
cus = command_buffer[4] << 8 | command_buffer[5]
cul = command_buffer[6] << 8 | command_buffer[7]
self.r_airtime_short = ats/100.0
self.r_airtime_long = atl/100.0
self.r_channel_load_short = cus/100.0
@ -766,9 +766,9 @@ class RNodeMultiInterface(Interface):
self.subinterfaces[self.selected_index].r_preamble_symbols = prs
self.subinterfaces[self.selected_index].r_premable_time_ms = prt
self.subinterfaces[self.selected_index].r_csma_slot_time_ms = cst
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting symbol time is "+str(round(self.subinterfaces[self.selected_index].r_symbol_time_ms,2))+"ms (at "+str(self.subinterfaces[self.selected_index].r_symbol_rate)+" baud)", RNS.LOG_DEBUG)
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting preamble is "+str(self.subinterfaces[self.selected_index].r_preamble_symbols)+" symbols ("+str(self.subinterfaces[self.selected_index].r_premable_time_ms)+"ms)", RNS.LOG_DEBUG)
RNS.log(str(self.subinterfaces[self.selected_index])+" Radio reporting CSMA slot time is "+str(self.subinterfaces[self.selected_index].r_csma_slot_time_ms)+"ms", RNS.LOG_DEBUG)
RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting symbol time is {round(self.subinterfaces[self.selected_index].r_symbol_time_ms, 2)}ms (at {self.subinterfaces[self.selected_index].r_symbol_rate} baud)", RNS.LOG_DEBUG)
RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting preamble is {self.subinterfaces[self.selected_index].r_preamble_symbols} symbols ({self.subinterfaces[self.selected_index].r_premable_time_ms}ms)", RNS.LOG_DEBUG)
RNS.log(f"{self.subinterfaces[self.selected_index]} Radio reporting CSMA slot time is {self.subinterfaces[self.selected_index].r_csma_slot_time_ms}ms", RNS.LOG_DEBUG)
elif (command == KISS.CMD_RANDOM):
self.r_random = byte
elif (command == KISS.CMD_PLATFORM):
@ -777,20 +777,20 @@ class RNodeMultiInterface(Interface):
self.mcu = byte
elif (command == KISS.CMD_ERROR):
if (byte == KISS.ERROR_INITRADIO):
RNS.log(str(self)+" hardware initialisation error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR)
raise IOError("Radio initialisation failure")
RNS.log(f"{self} hardware initialisation error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise OSError("Radio initialisation failure")
elif (byte == KISS.ERROR_TXFAILED):
RNS.log(str(self)+" hardware TX error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR)
raise IOError("Hardware transmit failure")
RNS.log(f"{self} hardware TX error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise OSError("Hardware transmit failure")
else:
RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR)
raise IOError("Unknown hardware failure")
RNS.log(f"{self} hardware error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise OSError("Unknown hardware failure")
elif (command == KISS.CMD_RESET):
if (byte == 0xF8):
if self.platform == KISS.PLATFORM_ESP32:
if self.online:
RNS.log("Detected reset while device was online, reinitialising device...", RNS.LOG_ERROR)
raise IOError("ESP32 reset")
raise OSError("ESP32 reset")
elif (command == KISS.CMD_READY):
self.process_queue()
elif (command == KISS.CMD_DETECT):
@ -804,11 +804,11 @@ class RNodeMultiInterface(Interface):
# 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]))
command_buffer = b""
else:
time_since_last = int(time.time()*1000) - last_read_ms
if len(data_buffer) > 0 and time_since_last > self.timeout:
RNS.log(str(self)+" serial read timeout in command "+str(command), RNS.LOG_WARNING)
RNS.log(f"{self} serial read timeout in command {command}", RNS.LOG_WARNING)
data_buffer = b""
in_frame = False
command = KISS.CMD_UNKNOWN
@ -824,14 +824,14 @@ class RNodeMultiInterface(Interface):
self.subinterfaces[interface.index].processOutgoing(self.id_callsign)
if interface_available:
RNS.log("Interface "+str(self)+" is transmitting beacon data on all subinterfaces: "+str(self.id_callsign.decode("utf-8")), RNS.LOG_DEBUG)
RNS.log(f"Interface {self} is transmitting beacon data on all subinterfaces: {self.id_callsign.decode('utf-8')}", RNS.LOG_DEBUG)
sleep(0.08)
except Exception as e:
self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", 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)
if RNS.Reticulum.panic_on_interface_error:
RNS.panic()
@ -854,16 +854,16 @@ class RNodeMultiInterface(Interface):
while not self.online and not self.detached:
try:
time.sleep(5)
RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_VERBOSE)
RNS.log(f"Attempting to reconnect serial port {self.port} for {self}...", RNS.LOG_VERBOSE)
self.open_port()
if self.serial.is_open:
self.configure_device()
except Exception as e:
RNS.log("Error while reconnecting port, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while reconnecting port, the contained exception was: {e}", RNS.LOG_ERROR)
self.reconnecting = False
if self.online:
RNS.log("Reconnected serial port for "+str(self))
RNS.log(f"Reconnected serial port for {self}")
def detach(self):
self.detached = True
@ -890,7 +890,7 @@ class RNodeMultiInterface(Interface):
interface.process_queue()
def __str__(self):
return "RNodeMultiInterface["+str(self.name)+"]"
return f"RNodeMultiInterface[{self.name}]"
class RNodeSubInterface(Interface):
LOW_FREQ_MIN = 137000000
@ -918,7 +918,7 @@ class RNodeSubInterface(Interface):
RNS.panic()
super().__init__()
if index == 0:
sel_cmd = KISS.CMD_SEL_INT0
data_cmd= KISS.CMD_INT0_DATA
@ -1019,42 +1019,42 @@ class RNodeSubInterface(Interface):
self.validcfg = True
if (self.interface_type == "SX126X" or self.interface_type == "SX127X"):
if (self.frequency < RNodeSubInterface.LOW_FREQ_MIN or self.frequency > RNodeSubInterface.LOW_FREQ_MAX):
RNS.log("Invalid frequency configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid frequency configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
elif (self.interface_type == "SX128X"):
if (self.frequency < RNodeSubInterface.HIGH_FREQ_MIN or self.frequency > RNodeSubInterface.HIGH_FREQ_MAX):
RNS.log("Invalid frequency configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid frequency configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
else:
RNS.log("Invalid interface type configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid interface type configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if (self.txpower < -9 or self.txpower > 27):
RNS.log("Invalid TX power configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid TX power configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if (self.bandwidth < 7800 or self.bandwidth > 1625000):
RNS.log("Invalid bandwidth configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid bandwidth configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if (self.sf < 5 or self.sf > 12):
RNS.log("Invalid spreading factor configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid spreading factor configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if (self.cr < 5 or self.cr > 8):
RNS.log("Invalid coding rate configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid coding rate configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if (self.st_alock and (self.st_alock < 0.0 or self.st_alock > 100.0)):
RNS.log("Invalid short-term airtime limit configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid short-term airtime limit configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if (self.lt_alock and (self.lt_alock < 0.0 or self.lt_alock > 100.0)):
RNS.log("Invalid long-term airtime limit configured for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Invalid long-term airtime limit configured for {self}", RNS.LOG_ERROR)
self.validcfg = False
if (not self.validcfg):
raise ValueError("The configuration for "+str(self)+" contains errors, interface is offline")
raise ValueError(f"The configuration for {self} contains errors, interface is offline")
self.configure_device()
@ -1068,18 +1068,18 @@ class RNodeSubInterface(Interface):
self.r_lock = None
sleep(2.0)
RNS.log("Configuring RNode subinterface "+str(self)+"...", RNS.LOG_VERBOSE)
RNS.log(f"Configuring RNode subinterface {self}...", RNS.LOG_VERBOSE)
self.initRadio()
if (self.validateRadioState()):
self.interface_ready = True
RNS.log(str(self)+" is configured and powered up")
RNS.log(f"{self} is configured and powered up")
sleep(0.3)
self.online = True
else:
RNS.log("After configuring "+str(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("Aborting RNode startup", RNS.LOG_ERROR)
def initRadio(self):
self.parent_interface.setFrequency(self.frequency, self)
@ -1093,7 +1093,7 @@ class RNodeSubInterface(Interface):
self.state = KISS.RADIO_STATE_ON
def validateRadioState(self):
RNS.log("Waiting for radio configuration validation for "+str(self)+"...", RNS.LOG_VERBOSE)
RNS.log(f"Waiting for radio configuration validation for {self}...", RNS.LOG_VERBOSE)
sleep(0.25);
self.validcfg = True
@ -1123,7 +1123,7 @@ class RNodeSubInterface(Interface):
try:
self.bitrate = self.r_sf * ( (4.0/self.r_cr) / (math.pow(2,self.r_sf)/(self.r_bandwidth/1000)) ) * 1000
self.bitrate_kbps = round(self.bitrate/1000.0, 2)
RNS.log(str(self)+" On-air bitrate is now "+str(self.bitrate_kbps)+ " kbps", RNS.LOG_VERBOSE)
RNS.log(f"{self} On-air bitrate is now {self.bitrate_kbps} kbps", RNS.LOG_VERBOSE)
except:
self.bitrate = 0
@ -1162,4 +1162,4 @@ class RNodeSubInterface(Interface):
self.interface_ready = True
def __str__(self):
return self.parent_interface.name+"["+self.name+"]"
return f"{self.parent_interface.name}[{self.name}]"

View File

@ -63,7 +63,7 @@ class SerialInterface(Interface):
super().__init__()
self.HW_MTU = 564
self.pyserial = serial
self.serial = None
self.owner = owner
@ -86,17 +86,17 @@ class SerialInterface(Interface):
try:
self.open_port()
except Exception as e:
RNS.log("Could not open serial port for interface "+str(self), RNS.LOG_ERROR)
RNS.log(f"Could not open serial port for interface {self}", RNS.LOG_ERROR)
raise e
if self.serial.is_open:
self.configure_device()
else:
raise IOError("Could not open serial port")
raise OSError("Could not open serial port")
def open_port(self):
RNS.log("Opening serial port "+self.port+"...", RNS.LOG_VERBOSE)
RNS.log(f"Opening serial port {self.port}...", RNS.LOG_VERBOSE)
self.serial = self.pyserial.Serial(
port = self.port,
baudrate = self.speed,
@ -118,11 +118,11 @@ class SerialInterface(Interface):
thread.daemon = True
thread.start()
self.online = True
RNS.log("Serial port "+self.port+" is now open", RNS.LOG_VERBOSE)
RNS.log(f"Serial port {self.port} is now open", RNS.LOG_VERBOSE)
def processIncoming(self, data):
self.rxb += len(data)
self.rxb += len(data)
self.owner.inbound(data, self)
@ -130,9 +130,9 @@ class SerialInterface(Interface):
if self.online:
data = bytes([HDLC.FLAG])+HDLC.escape(data)+bytes([HDLC.FLAG])
written = self.serial.write(data)
self.txb += len(data)
self.txb += len(data)
if written != len(data):
raise IOError("Serial interface only wrote "+str(written)+" bytes of "+str(len(data)))
raise OSError(f"Serial interface only wrote {written} bytes of {len(data)}")
def readLoop(self):
@ -164,7 +164,7 @@ class SerialInterface(Interface):
byte = HDLC.ESC
escape = False
data_buffer = data_buffer+bytes([byte])
else:
time_since_last = int(time.time()*1000) - last_read_ms
if len(data_buffer) > 0 and time_since_last > self.timeout:
@ -172,12 +172,12 @@ class SerialInterface(Interface):
in_frame = False
escape = False
sleep(0.08)
except Exception as e:
self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", 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)
if RNS.Reticulum.panic_on_interface_error:
RNS.panic()
@ -191,17 +191,17 @@ class SerialInterface(Interface):
while not self.online:
try:
time.sleep(5)
RNS.log("Attempting to reconnect serial port "+str(self.port)+" for "+str(self)+"...", RNS.LOG_VERBOSE)
RNS.log(f"Attempting to reconnect serial port {self.port} for {self}...", RNS.LOG_VERBOSE)
self.open_port()
if self.serial.is_open:
self.configure_device()
except Exception as e:
RNS.log("Error while reconnecting port, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while reconnecting port, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("Reconnected serial port for "+str(self))
RNS.log(f"Reconnected serial port for {self}")
def should_ingress_limit(self):
return False
def __str__(self):
return "SerialInterface["+self.name+"]"
return f"SerialInterface[{self.name}]"

View File

@ -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):
super().__init__()
self.HW_MTU = 1064
self.IN = True
self.OUT = False
self.socket = None
@ -99,7 +99,7 @@ class TCPClientInterface(Interface):
self.i2p_tunneled = i2p_tunneled
self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL
self.bitrate = TCPClientInterface.BITRATE_GUESS
if max_reconnect_tries == None:
self.max_reconnect_tries = TCPClientInterface.RECONNECT_MAX_TRIES
else:
@ -128,14 +128,14 @@ class TCPClientInterface(Interface):
self.connect_timeout = connect_timeout
else:
self.connect_timeout = TCPClientInterface.INITIAL_CONNECT_TIMEOUT
if TCPClientInterface.SYNCHRONOUS_START:
self.initial_connect()
else:
thread = threading.Thread(target=self.initial_connect)
thread.daemon = True
thread.start()
def initial_connect(self):
if not self.connect(initial=True):
thread = threading.Thread(target=self.reconnect)
@ -170,35 +170,35 @@ class TCPClientInterface(Interface):
TCP_KEEPIDLE = 0x10
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
if not self.i2p_tunneled:
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(TCPClientInterface.TCP_PROBE_AFTER))
else:
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(TCPClientInterface.I2P_PROBE_AFTER))
def detach(self):
if self.socket != None:
if hasattr(self.socket, "close"):
if callable(self.socket.close):
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG)
RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
self.detached = True
try:
self.socket.shutdown(socket.SHUT_RDWR)
except Exception as e:
RNS.log("Error while shutting down socket for "+str(self)+": "+str(e))
RNS.log(f"Error while shutting down socket for {self}: {e}")
try:
self.socket.close()
except Exception as e:
RNS.log("Error while closing socket for "+str(self)+": "+str(e))
RNS.log(f"Error while closing socket for {self}: {e}")
self.socket = None
def connect(self, initial=False):
try:
if initial:
RNS.log("Establishing TCP connection for "+str(self)+"...", RNS.LOG_DEBUG)
RNS.log(f"Establishing TCP connection for {self}...", RNS.LOG_DEBUG)
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.settimeout(TCPClientInterface.INITIAL_CONNECT_TIMEOUT)
@ -208,14 +208,14 @@ class TCPClientInterface(Interface):
self.online = True
if initial:
RNS.log("TCP connection for "+str(self)+" established", RNS.LOG_DEBUG)
RNS.log(f"TCP connection for {self} established", RNS.LOG_DEBUG)
except Exception as e:
if initial:
RNS.log("Initial connection for "+str(self)+" could not be established: "+str(e), RNS.LOG_ERROR)
RNS.log("Leaving unconnected and retrying connection in "+str(TCPClientInterface.RECONNECT_WAIT)+" seconds.", 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)
return False
else:
raise e
@ -223,7 +223,7 @@ class TCPClientInterface(Interface):
self.set_timeouts_linux()
elif platform.system() == "Darwin":
self.set_timeouts_osx()
self.online = True
self.writing = False
self.never_connected = False
@ -241,7 +241,7 @@ class TCPClientInterface(Interface):
attempts += 1
if self.max_reconnect_tries != None and attempts > self.max_reconnect_tries:
RNS.log("Max reconnection attempts reached for "+str(self), RNS.LOG_ERROR)
RNS.log(f"Max reconnection attempts reached for {self}", RNS.LOG_ERROR)
self.teardown()
break
@ -249,10 +249,10 @@ class TCPClientInterface(Interface):
self.connect()
except Exception as e:
RNS.log("Connection attempt for "+str(self)+" failed: "+str(e), RNS.LOG_DEBUG)
RNS.log(f"Connection attempt for {self} failed: {e}", RNS.LOG_DEBUG)
if not self.never_connected:
RNS.log("Reconnected socket for "+str(self)+".", RNS.LOG_INFO)
RNS.log(f"Reconnected socket for {self}.", RNS.LOG_INFO)
self.reconnecting = False
thread = threading.Thread(target=self.read_loop)
@ -263,13 +263,13 @@ class TCPClientInterface(Interface):
else:
RNS.log("Attempt to reconnect on a non-initiator TCP interface. This should not happen.", RNS.LOG_ERROR)
raise IOError("Attempt to reconnect on a non-initiator TCP interface")
raise OSError("Attempt to reconnect on a non-initiator TCP interface")
def processIncoming(self, data):
self.rxb += len(data)
if hasattr(self, "parent_interface") and self.parent_interface != None:
self.parent_interface.rxb += len(data)
self.owner.inbound(data, self)
def processOutgoing(self, data):
@ -292,8 +292,8 @@ class TCPClientInterface(Interface):
self.parent_interface.txb += len(data)
except Exception as e:
RNS.log("Exception occurred while transmitting via "+str(self)+", tearing down interface", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Exception occurred while transmitting via {self}, tearing down interface", RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
self.teardown()
@ -361,18 +361,18 @@ class TCPClientInterface(Interface):
else:
self.online = False
if self.initiator and not self.detached:
RNS.log("The socket for "+str(self)+" was closed, attempting to reconnect...", RNS.LOG_WARNING)
RNS.log(f"The socket for {self} was closed, attempting to reconnect...", RNS.LOG_WARNING)
self.reconnect()
else:
RNS.log("The socket for remote client "+str(self)+" was closed.", RNS.LOG_VERBOSE)
RNS.log(f"The socket for remote client {self} was closed.", RNS.LOG_VERBOSE)
self.teardown()
break
except Exception as e:
self.online = False
RNS.log("An interface error occurred for "+str(self)+", the contained exception was: "+str(e), RNS.LOG_WARNING)
RNS.log(f"An interface error occurred for {self}, the contained exception was: {e}", RNS.LOG_WARNING)
if self.initiator:
RNS.log("Attempting to reconnect...", RNS.LOG_WARNING)
@ -382,12 +382,12 @@ class TCPClientInterface(Interface):
def teardown(self):
if self.initiator and not self.detached:
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR)
RNS.log(f"The interface {self} experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error:
RNS.panic()
else:
RNS.log("The interface "+str(self)+" is being torn down.", RNS.LOG_VERBOSE)
RNS.log(f"The interface {self} is being torn down.", RNS.LOG_VERBOSE)
self.online = False
self.OUT = False
@ -402,7 +402,7 @@ class TCPClientInterface(Interface):
def __str__(self):
return "TCPInterface["+str(self.name)+"/"+str(self.target_ip)+":"+str(self.target_port)+"]"
return f"TCPInterface[{self.name}/{self.target_ip}:{self.target_port}]"
class TCPServerInterface(Interface):
@ -427,7 +427,7 @@ class TCPServerInterface(Interface):
self.online = False
self.clients = 0
self.IN = True
self.OUT = False
self.name = name
@ -466,7 +466,7 @@ class TCPServerInterface(Interface):
def incoming_connection(self, handler):
RNS.log("Accepting incoming TCP connection", RNS.LOG_VERBOSE)
interface_name = "Client on "+self.name
interface_name = f"Client on {self.name}"
spawned_interface = TCPClientInterface(self.owner, interface_name, target_ip=None, target_port=None, connected_socket=handler.request, i2p_tunneled=self.i2p_tunneled)
spawned_interface.OUT = self.OUT
spawned_interface.IN = self.IN
@ -474,7 +474,7 @@ class TCPServerInterface(Interface):
spawned_interface.target_port = str(handler.client_address[1])
spawned_interface.parent_interface = self
spawned_interface.bitrate = self.bitrate
spawned_interface.ifac_size = self.ifac_size
spawned_interface.ifac_netname = self.ifac_netname
spawned_interface.ifac_netkey = self.ifac_netkey
@ -501,7 +501,7 @@ class TCPServerInterface(Interface):
spawned_interface.mode = self.mode
spawned_interface.HW_MTU = self.HW_MTU
spawned_interface.online = True
RNS.log("Spawned new TCPClient Interface: "+str(spawned_interface), RNS.LOG_VERBOSE)
RNS.log(f"Spawned new TCPClient Interface: {spawned_interface}", RNS.LOG_VERBOSE)
RNS.Transport.interfaces.append(spawned_interface)
self.clients += 1
spawned_interface.read_loop()
@ -521,17 +521,17 @@ class TCPServerInterface(Interface):
if hasattr(self.server, "shutdown"):
if callable(self.server.shutdown):
try:
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG)
RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
self.server.shutdown()
self.detached = True
self.server = None
except Exception as e:
RNS.log("Error while shutting down server for "+str(self)+": "+str(e))
RNS.log(f"Error while shutting down server for {self}: {e}")
def __str__(self):
return "TCPServerInterface["+self.name+"/"+self.bind_ip+":"+str(self.bind_port)+"]"
return f"TCPServerInterface[{self.name}/{self.bind_ip}:{self.bind_port}]"
class TCPInterfaceHandler(socketserver.BaseRequestHandler):

View File

@ -99,13 +99,13 @@ class UDPInterface(Interface):
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
udp_socket.sendto(data, (self.forward_ip, self.forward_port))
self.txb += len(data)
except Exception as e:
RNS.log("Could not transmit on "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Could not transmit on {self}. The contained exception was: {e}", RNS.LOG_ERROR)
def __str__(self):
return "UDPInterface["+self.name+"/"+self.bind_ip+":"+str(self.bind_port)+"]"
return f"UDPInterface[{self.name}/{self.bind_ip}:{self.bind_port}]"
class UDPInterfaceHandler(socketserver.BaseRequestHandler):
def __init__(self, callback, *args, **keys):

View File

@ -24,5 +24,5 @@ import os
import glob
import RNS.Interfaces.Android
modules = glob.glob(os.path.dirname(__file__)+"/*.py")
modules = glob.glob(f"{os.path.dirname(__file__)}/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]

View File

@ -115,8 +115,8 @@ class Link:
link.destination = packet.destination
link.establishment_timeout = Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, packet.hops) + Link.KEEPALIVE
link.establishment_cost += len(packet.raw)
RNS.log("Validating link request "+RNS.prettyhexrep(link.link_id), RNS.LOG_VERBOSE)
RNS.log(f"Establishment timeout is {RNS.prettytime(link.establishment_timeout)} for incoming link request "+RNS.prettyhexrep(link.link_id), RNS.LOG_EXTREME)
RNS.log(f"Validating link request {RNS.prettyhexrep(link.link_id)}", RNS.LOG_VERBOSE)
RNS.log(f"Establishment timeout is {RNS.prettytime(link.establishment_timeout)} for incoming link request {RNS.prettyhexrep(link.link_id)}", RNS.LOG_EXTREME)
link.handshake()
link.attached_interface = packet.receiving_interface
link.prove()
@ -124,13 +124,13 @@ class Link:
RNS.Transport.register_link(link)
link.last_inbound = time.time()
link.start_watchdog()
RNS.log("Incoming link request "+str(link)+" accepted on "+str(link.attached_interface), RNS.LOG_DEBUG)
RNS.log(f"Incoming link request {link} accepted on {link.attached_interface}", RNS.LOG_DEBUG)
return link
except Exception as e:
RNS.log("Validating link request failed", RNS.LOG_VERBOSE)
RNS.log("exc: "+str(e))
RNS.log(f"exc: {e}")
return None
else:
@ -189,7 +189,7 @@ class Link:
self.sig_prv = Ed25519PrivateKey.generate()
self.fernet = None
self.pub = self.prv.public_key()
self.pub_bytes = self.pub.public_bytes()
@ -219,8 +219,8 @@ class Link:
self.start_watchdog()
self.packet.send()
self.had_outbound()
RNS.log("Link request "+RNS.prettyhexrep(self.link_id)+" sent to "+str(self.destination), RNS.LOG_DEBUG)
RNS.log(f"Establishment timeout is {RNS.prettytime(self.establishment_timeout)} for link request "+RNS.prettyhexrep(self.link_id), RNS.LOG_EXTREME)
RNS.log(f"Link request {RNS.prettyhexrep(self.link_id)} sent to {self.destination}", RNS.LOG_DEBUG)
RNS.log(f"Establishment timeout is {RNS.prettytime(self.establishment_timeout)} for link request {RNS.prettyhexrep(self.link_id)}", RNS.LOG_EXTREME)
def load_peer(self, peer_pub_bytes, peer_sig_pub_bytes):
@ -249,7 +249,7 @@ class Link:
context=self.get_context(),
)
else:
RNS.log("Handshake attempt on "+str(self)+" with invalid state "+str(self.status), RNS.LOG_ERROR)
RNS.log(f"Handshake attempt on {self} with invalid state {self.status}", RNS.LOG_ERROR)
def prove(self):
@ -288,10 +288,10 @@ class Link:
self.establishment_cost += len(packet.raw)
signed_data = self.link_id+self.peer_pub_bytes+self.peer_sig_pub_bytes
signature = packet.data[:RNS.Identity.SIGLENGTH//8]
if self.destination.identity.validate(signature, signed_data):
if self.status != Link.HANDSHAKE:
raise IOError("Invalid link state for proof validation: "+str(self.status))
raise OSError(f"Invalid link state for proof validation: {self.status}")
self.rtt = time.time() - self.request_time
self.attached_interface = packet.receiving_interface
@ -300,8 +300,8 @@ class Link:
self.activated_at = time.time()
self.last_proof = self.activated_at
RNS.Transport.activate_link(self)
RNS.log("Link "+str(self)+" established with "+str(self.destination)+", RTT is "+str(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:
self.establishment_rate = self.establishment_cost/self.rtt
@ -315,12 +315,12 @@ class Link:
thread.daemon = True
thread.start()
else:
RNS.log("Invalid link proof signature received by "+str(self)+". Ignoring.", RNS.LOG_DEBUG)
RNS.log(f"Invalid link proof signature received by {self}. Ignoring.", RNS.LOG_DEBUG)
except Exception as e:
self.status = Link.CLOSED
RNS.log("An error ocurred while validating link request proof on "+str(self)+".", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"An error ocurred while validating link request proof on {self}.", RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
def identify(self, identity):
@ -377,10 +377,10 @@ class Link:
timeout = timeout,
request_size = len(packed_request),
)
else:
request_id = RNS.Identity.truncated_hash(packed_request)
RNS.log("Sending request "+RNS.prettyhexrep(request_id)+" as resource.", RNS.LOG_DEBUG)
RNS.log(f"Sending request {RNS.prettyhexrep(request_id)} as resource.", RNS.LOG_DEBUG)
request_resource = RNS.Resource(packed_request, self, request_id = request_id, is_response = False, timeout = timeout)
return RequestReceipt(
@ -411,10 +411,10 @@ class Link:
if self.owner.callbacks.link_established != None:
self.owner.callbacks.link_established(self)
except Exception as e:
RNS.log("Error occurred in external link establishment callback. The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error occurred in external link establishment callback. The contained exception was: {e}", RNS.LOG_ERROR)
except Exception as e:
RNS.log("Error occurred while processing RTT packet, tearing down link. The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error occurred while processing RTT packet, tearing down link. The contained exception was: {e}", RNS.LOG_ERROR)
self.teardown()
def track_phy_stats(self, track):
@ -547,7 +547,7 @@ class Link:
resource.cancel()
if self._channel:
self._channel._shutdown()
self.prv = None
self.pub = None
self.pub_bytes = None
@ -563,7 +563,7 @@ class Link:
try:
self.callbacks.link_closed(self)
except Exception as e:
RNS.log("Error while executing link closed callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while executing link closed callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
def start_watchdog(self):
@ -620,7 +620,7 @@ class Link:
self.status = Link.STALE
else:
sleep_time = self.keepalive
else:
sleep_time = (last_inbound + self.keepalive) - time.time()
@ -634,7 +634,7 @@ class Link:
if sleep_time == 0:
RNS.log("Warning! Link watchdog sleep time of 0!", RNS.LOG_ERROR)
if sleep_time == None or sleep_time < 0:
RNS.log("Timing error! Tearing down link "+str(self)+" now.", RNS.LOG_ERROR)
RNS.log(f"Timing error! Tearing down link {self} now.", RNS.LOG_ERROR)
self.teardown()
sleep_time = 0.1
@ -655,7 +655,7 @@ class Link:
self.snr = packet.snr
if packet.q != None:
self.q = packet.q
def send_keepalive(self):
keepalive_packet = RNS.Packet(self, bytes([0xFF]), context=RNS.Packet.KEEPALIVE)
keepalive_packet.send()
@ -683,7 +683,7 @@ class Link:
allowed = True
if allowed:
RNS.log("Handling request "+RNS.prettyhexrep(request_id)+" for: "+str(path), RNS.LOG_DEBUG)
RNS.log(f"Handling request {RNS.prettyhexrep(request_id)} for: {path}", RNS.LOG_DEBUG)
if len(inspect.signature(response_generator).parameters) == 5:
response = response_generator(path, request_data, request_id, self.__remote_identity, requested_at)
elif len(inspect.signature(response_generator).parameters) == 6:
@ -700,7 +700,7 @@ class Link:
response_resource = RNS.Resource(packed_response, self, request_id = request_id, is_response = True)
else:
identity_string = str(self.get_remote_identity()) if self.get_remote_identity() != None else "<Unknown>"
RNS.log("Request "+RNS.prettyhexrep(request_id)+" from "+identity_string+" not allowed for: "+str(path), RNS.LOG_DEBUG)
RNS.log(f"Request {RNS.prettyhexrep(request_id)} from {identity_string} not allowed for: {path}", RNS.LOG_DEBUG)
def handle_response(self, request_id, response_data, response_size, response_transfer_size):
if self.status == Link.ACTIVE:
@ -715,7 +715,7 @@ class Link:
pending_request.response_transfer_size += response_transfer_size
pending_request.response_received(response_data)
except Exception as e:
RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error occurred while handling response. The contained exception was: {e}", RNS.LOG_ERROR)
break
@ -732,7 +732,7 @@ class Link:
self.handle_request(request_id, request_data)
else:
RNS.log("Incoming request resource failed with status: "+RNS.hexrep([resource.status]), RNS.LOG_DEBUG)
RNS.log(f"Incoming request resource failed with status: {RNS.hexrep([resource.status])}", RNS.LOG_DEBUG)
def response_resource_concluded(self, resource):
if resource.status == RNS.Resource.COMPLETE:
@ -743,7 +743,7 @@ class Link:
self.handle_response(request_id, response_data, resource.total_size, resource.size)
else:
RNS.log("Incoming response resource failed with status: "+RNS.hexrep([resource.status]), RNS.LOG_DEBUG)
RNS.log(f"Incoming response resource failed with status: {RNS.hexrep([resource.status])}", RNS.LOG_DEBUG)
for pending_request in self.pending_requests:
if pending_request.request_id == resource.request_id:
pending_request.request_timed_out(None)
@ -782,7 +782,7 @@ class Link:
thread = threading.Thread(target=self.callbacks.packet, args=(plaintext, packet))
thread.daemon = True
thread.start()
if self.destination.proof_strategy == RNS.Destination.PROVE_ALL:
packet.prove()
should_query = True
@ -794,7 +794,7 @@ class Link:
packet.prove()
should_query = True
except Exception as e:
RNS.log("Error while executing proof request callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while executing proof request callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
self.__update_phy_stats(packet, query_shared=should_query)
@ -814,8 +814,8 @@ class Link:
try:
self.callbacks.remote_identified(self, self.__remote_identity)
except Exception as e:
RNS.log("Error while executing remote identified callback from "+str(self)+". The contained exception was: "+str(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)
elif packet.context == RNS.Packet.REQUEST:
@ -827,7 +827,7 @@ class Link:
self.handle_request(request_id, unpacked_request)
self.__update_phy_stats(packet, query_shared=True)
except Exception as e:
RNS.log("Error occurred while handling request. The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error occurred while handling request. The contained exception was: {e}", RNS.LOG_ERROR)
elif packet.context == RNS.Packet.RESPONSE:
try:
@ -840,7 +840,7 @@ class Link:
self.handle_response(request_id, response_data, transfer_size, transfer_size)
self.__update_phy_stats(packet, query_shared=True)
except Exception as e:
RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error occurred while handling response. The contained exception was: {e}", RNS.LOG_ERROR)
elif packet.context == RNS.Packet.LRRTT:
if not self.initiator:
@ -883,7 +883,7 @@ class Link:
if self.callbacks.resource(resource_advertisement):
RNS.Resource.accept(packet, self.callbacks.resource_concluded)
except Exception as e:
RNS.log("Error while executing resource accept callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while executing resource accept callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
elif self.resource_strategy == Link.ACCEPT_ALL:
RNS.Resource.accept(packet, self.callbacks.resource_concluded)
@ -903,7 +903,7 @@ class Link:
if not packet.packet_hash in resource.req_hashlist:
resource.req_hashlist.append(packet.packet_hash)
resource.request(plaintext)
# TODO: Test and possibly enable this at some point
# def request_job():
# resource.request(plaintext)
@ -970,13 +970,13 @@ class Link:
try:
self.fernet = Fernet(self.derived_key)
except Exception as e:
RNS.log("Could not instantiate Fernet while performin encryption on link "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Could not instantiate Fernet while performin encryption on link {self}. The contained exception was: {e}", RNS.LOG_ERROR)
raise e
return self.fernet.encrypt(plaintext)
except Exception as e:
RNS.log("Encryption on link "+str(self)+" failed. The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Encryption on link {self} failed. The contained exception was: {e}", RNS.LOG_ERROR)
raise e
@ -984,11 +984,11 @@ class Link:
try:
if not self.fernet:
self.fernet = Fernet(self.derived_key)
return self.fernet.decrypt(ciphertext)
except Exception as e:
RNS.log("Decryption failed on link "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Decryption failed on link {self}. The contained exception was: {e}", RNS.LOG_ERROR)
return None
@ -1140,7 +1140,7 @@ class RequestReceipt():
elif self.resource != None:
self.hash = resource.request_id
resource.set_callback(self.request_resource_concluded)
self.link = link
self.request_id = self.hash
self.request_size = request_size
@ -1169,7 +1169,7 @@ class RequestReceipt():
def request_resource_concluded(self, resource):
if resource.status == RNS.Resource.COMPLETE:
RNS.log("Request "+RNS.prettyhexrep(self.request_id)+" successfully sent as resource.", RNS.LOG_DEBUG)
RNS.log(f"Request {RNS.prettyhexrep(self.request_id)} successfully sent as resource.", RNS.LOG_DEBUG)
if self.started_at == None:
self.started_at = time.time()
self.status = RequestReceipt.DELIVERED
@ -1178,7 +1178,7 @@ class RequestReceipt():
response_timeout_thread.daemon = True
response_timeout_thread.start()
else:
RNS.log("Sending request "+RNS.prettyhexrep(self.request_id)+" as resource failed with status: "+RNS.hexrep([resource.status]), RNS.LOG_DEBUG)
RNS.log(f"Sending request {RNS.prettyhexrep(self.request_id)} as resource failed with status: {RNS.hexrep([resource.status])}", RNS.LOG_DEBUG)
self.status = RequestReceipt.FAILED
self.concluded_at = time.time()
self.link.pending_requests.remove(self)
@ -1187,7 +1187,7 @@ class RequestReceipt():
try:
self.callbacks.failed(self)
except Exception as e:
RNS.log("Error while executing request failed callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while executing request failed callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
def __response_timeout_job(self):
@ -1208,7 +1208,7 @@ class RequestReceipt():
try:
self.callbacks.failed(self)
except Exception as e:
RNS.log("Error while executing request timed out callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while executing request timed out callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
def response_resource_progress(self, resource):
@ -1224,16 +1224,16 @@ class RequestReceipt():
self.packet_receipt.callbacks.delivery(self.packet_receipt)
self.progress = resource.get_progress()
if self.callbacks.progress != None:
try:
self.callbacks.progress(self)
except Exception as e:
RNS.log("Error while executing response progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while executing response progress callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
else:
resource.cancel()
def response_received(self, response):
if not self.status == RequestReceipt.FAILED:
self.progress = 1.0
@ -1252,13 +1252,13 @@ class RequestReceipt():
try:
self.callbacks.progress(self)
except Exception as e:
RNS.log("Error while executing response progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while executing response progress callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
if self.callbacks.response != None:
try:
self.callbacks.response(self)
except Exception as e:
RNS.log("Error while executing response received callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while executing response received callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
def get_request_id(self):
"""

View File

@ -97,7 +97,7 @@ class Packet:
# 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
"""
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
"""
@ -208,14 +208,14 @@ class Packet:
# Announce packets are not encrypted
self.ciphertext = self.data
else:
raise IOError("Packet with header type 2 must have a transport ID")
raise OSError("Packet with header type 2 must have a transport ID")
self.header += bytes([self.context])
self.raw = self.header + self.ciphertext
if len(self.raw) > self.MTU:
raise IOError("Packet size of "+str(len(self.raw))+" exceeds MTU of "+str(self.MTU)+" bytes")
raise OSError(f"Packet size of {len(self.raw)} exceeds MTU of {self.MTU} bytes")
self.packed = True
self.update_hash()
@ -250,19 +250,19 @@ class Packet:
return True
except Exception as e:
RNS.log("Received malformed packet, dropping it. The contained exception was: "+str(e), RNS.LOG_EXTREME)
RNS.log(f"Received malformed packet, dropping it. The contained exception was: {e}", RNS.LOG_EXTREME)
return False
def send(self):
"""
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.
"""
if not self.sent:
if self.destination.type == RNS.Destination.LINK:
if self.destination.status == RNS.Link.CLOSED:
raise IOError("Attempt to transmit over a closed link")
raise OSError("Attempt to transmit over a closed link")
else:
self.destination.last_outbound = time.time()
self.destination.tx += 1
@ -278,21 +278,21 @@ class Packet:
self.sent = False
self.receipt = None
return False
else:
raise IOError("Packet was already sent")
raise OSError("Packet was already sent")
def resend(self):
"""
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.
"""
if self.sent:
# Re-pack the packet to obtain new ciphertext for
# encrypted destinations
self.pack()
if RNS.Transport.outbound(self):
return self.receipt
else:
@ -301,7 +301,7 @@ class Packet:
self.receipt = None
return False
else:
raise IOError("Packet was not sent yet")
raise OSError("Packet was not sent yet")
def prove(self, destination=None):
if self.fromPacked and hasattr(self, "destination") and self.destination:
@ -388,7 +388,7 @@ class PacketReceipt:
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
@ -419,10 +419,10 @@ class PacketReceipt:
try:
self.callbacks.delivery(self)
except Exception as e:
RNS.log("An error occurred while evaluating external delivery callback for "+str(link), RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.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.trace_exception(e)
return True
else:
return False
@ -465,7 +465,7 @@ class PacketReceipt:
try:
self.callbacks.delivery(self)
except Exception as e:
RNS.log("Error while executing proof validated callback. The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while executing proof validated callback. The contained exception was: {e}", RNS.LOG_ERROR)
return True
else:
@ -489,8 +489,8 @@ class PacketReceipt:
try:
self.callbacks.delivery(self)
except Exception as e:
RNS.log("Error while executing proof validated callback. The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while executing proof validated callback. The contained exception was: {e}", RNS.LOG_ERROR)
return True
else:
return False
@ -524,7 +524,7 @@ class PacketReceipt:
def set_timeout(self, timeout):
"""
Sets a timeout in seconds
:param timeout: The timeout in seconds.
"""
self.timeout = float(timeout)

View File

@ -21,7 +21,7 @@
# SOFTWARE.
class Resolver:
@staticmethod
def resolve_identity(full_name):
pass

View File

@ -59,11 +59,11 @@ class Resource:
# The maximum window size for transfers on fast links
WINDOW_MAX_FAST = 75
# For calculating maps and guard segments, this
# must be set to the global maximum window.
WINDOW_MAX = WINDOW_MAX_FAST
# If the fast rate is sustained for this many request
# rounds, the fast link window size will be allowed.
FAST_RATE_THRESHOLD = WINDOW_MAX_SLOW - WINDOW - 2
@ -111,13 +111,14 @@ class Resource:
# fit in 3 bytes in resource advertisements.
MAX_EFFICIENT_SIZE = 16 * 1024 * 1024 - 1
RESPONSE_MAX_GRACE_TIME = 10
# The maximum size to auto-compress with
# bz2 before sending.
AUTO_COMPRESS_MAX_SIZE = MAX_EFFICIENT_SIZE
PART_TIMEOUT_FACTOR = 4
PART_TIMEOUT_FACTOR_AFTER_RTT = 2
PROOF_TIMEOUT_FACTOR = 3
MAX_RETRIES = 16
MAX_ADV_RETRIES = 4
SENDER_GRACE_TIME = 10.0
@ -172,7 +173,7 @@ class Resource:
resource.window_flexibility = Resource.WINDOW_FLEXIBILITY
resource.last_activity = time.time()
resource.storagepath = RNS.Reticulum.resourcepath+"/"+resource.original_hash.hex()
resource.storagepath = f"{RNS.Reticulum.resourcepath}/{resource.original_hash.hex()}"
resource.segment_index = adv.i
resource.total_segments = adv.l
if adv.l > 1:
@ -185,7 +186,7 @@ class Resource:
resource.waiting_for_hmu = False
resource.receiving_part = False
resource.consecutive_completed_height = -1
if not resource.link.has_incoming_resource(resource):
resource.link.register_incoming_resource(resource)
@ -194,7 +195,7 @@ class Resource:
try:
resource.link.callbacks.resource_started(resource)
except Exception as e:
RNS.log("Error while executing resource started callback from "+str(resource)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while executing resource started callback from {resource}. The contained exception was: {e}", RNS.LOG_ERROR)
resource.hashmap_update(0, resource.hashmap_raw)
@ -203,7 +204,7 @@ class Resource:
return resource
else:
RNS.log("Ignoring resource advertisement for "+RNS.prettyhexrep(resource.hash)+", resource already transferring", RNS.LOG_DEBUG)
RNS.log(f"Ignoring resource advertisement for {RNS.prettyhexrep(resource.hash)}, resource already transferring", RNS.LOG_DEBUG)
return None
except Exception as e:
@ -254,7 +255,7 @@ class Resource:
elif isinstance(data, bytes):
data_size = len(data)
self.total_size = data_size
resource_data = data
self.total_segments = 1
self.segment_index = 1
@ -307,7 +308,7 @@ class Resource:
if (auto_compress and len(self.uncompressed_data) <= Resource.AUTO_COMPRESS_MAX_SIZE):
RNS.log("Compressing resource data...", RNS.LOG_DEBUG)
self.compressed_data = bz2.compress(self.uncompressed_data)
RNS.log("Compression completed in "+str(round(time.time()-compression_began, 3))+" seconds", RNS.LOG_DEBUG)
RNS.log(f"Compression completed in {round(time.time() - compression_began, 3)} seconds", RNS.LOG_DEBUG)
else:
self.compressed_data = self.uncompressed_data
@ -316,12 +317,12 @@ class Resource:
if (self.compressed_size < self.uncompressed_size and auto_compress):
saved_bytes = len(self.uncompressed_data) - len(self.compressed_data)
RNS.log("Compression saved "+str(saved_bytes)+" bytes, sending compressed", RNS.LOG_DEBUG)
RNS.log(f"Compression saved {saved_bytes} bytes, sending compressed", RNS.LOG_DEBUG)
self.data = b""
self.data += RNS.Identity.get_random_hash()[:Resource.RANDOM_HASH_SIZE]
self.data += self.compressed_data
self.compressed = True
self.uncompressed_data = None
@ -347,11 +348,11 @@ class Resource:
self.sent_parts = 0
hashmap_entries = int(math.ceil(self.size/float(Resource.SDU)))
self.total_parts = hashmap_entries
hashmap_ok = False
while not hashmap_ok:
hashmap_computation_began = time.time()
RNS.log("Starting resource hashmap computation with "+str(hashmap_entries)+" entries...", RNS.LOG_DEBUG)
RNS.log(f"Starting resource hashmap computation with {hashmap_entries} entries...", RNS.LOG_DEBUG)
self.random_hash = RNS.Identity.get_random_hash()[:Resource.RANDOM_HASH_SIZE]
self.hash = RNS.Identity.full_hash(data+self.random_hash)
@ -387,13 +388,13 @@ class Resource:
self.hashmap += part.map_hash
self.parts.append(part)
RNS.log("Hashmap computation concluded in "+str(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:
self.advertise()
else:
self.receive_lock = Lock()
def hashmap_update_packet(self, plaintext):
if not self.status == Resource.FAILED:
@ -446,9 +447,9 @@ class Resource:
self.status = Resource.ADVERTISED
self.retries_left = self.max_adv_retries
self.link.register_outgoing_resource(self)
RNS.log("Sent resource advertisement for "+RNS.prettyhexrep(self.hash), RNS.LOG_DEBUG)
RNS.log(f"Sent resource advertisement for {RNS.prettyhexrep(self.hash)}", RNS.LOG_DEBUG)
except Exception as e:
RNS.log("Could not advertise resource, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Could not advertise resource, the contained exception was: {e}", RNS.LOG_ERROR)
self.cancel()
return
@ -486,9 +487,9 @@ class Resource:
self.adv_sent = self.last_activity
sleep_time = 0.001
except Exception as e:
RNS.log("Could not resend advertisement packet, cancelling resource. The contained exception was: "+str(e), RNS.LOG_VERBOSE)
RNS.log(f"Could not resend advertisement packet, cancelling resource. The contained exception was: {e}", RNS.LOG_VERBOSE)
self.cancel()
elif self.status == Resource.TRANSFERRING:
if not self.initiator:
@ -503,11 +504,11 @@ class Resource:
retries_used = self.max_retries - self.retries_left
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()
if sleep_time < 0:
if self.retries_left > 0:
ms = "" if self.outstanding_parts == 1 else "s"
RNS.log("Timed out waiting for "+str(self.outstanding_parts)+" part"+ms+", requesting retry", RNS.LOG_DEBUG)
RNS.log(f"Timed out waiting for {self.outstanding_parts} part{ms}, requesting retry", RNS.LOG_DEBUG)
if self.window > self.window_min:
self.window -= 1
if self.window_max > self.window_min:
@ -532,6 +533,10 @@ class Resource:
sleep_time = 0.001
elif self.status == Resource.AWAITING_PROOF:
# Decrease timeout factor since proof packets are
# significantly smaller than full req/resp roundtrip
self.timeout_factor = Resource.PROOF_TIMEOUT_FACTOR
sleep_time = self.last_part_sent + (self.rtt*self.timeout_factor+self.sender_grace_time) - time.time()
if sleep_time < 0:
if self.retries_left <= 0:
@ -553,7 +558,7 @@ class Resource:
if sleep_time == None or sleep_time < 0:
RNS.log("Timing error, cancelling resource transfer.", RNS.LOG_ERROR)
self.cancel()
if sleep_time != None:
sleep(min(sleep_time, Resource.WATCHDOG_MAX_SLEEP))
@ -590,7 +595,7 @@ class Resource:
except Exception as e:
RNS.log("Error while assembling received resource.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
self.status = Resource.CORRUPT
self.link.resource_concluded(self)
@ -601,7 +606,7 @@ class Resource:
try:
self.callback(self)
except Exception as e:
RNS.log("Error while executing resource assembled callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while executing resource assembled callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
try:
if hasattr(self.data, "close") and callable(self.data.close):
@ -613,7 +618,7 @@ class Resource:
RNS.log("Error while cleaning up resource files, the contained exception was:", RNS.LOG_ERROR)
RNS.log(str(e))
else:
RNS.log("Resource segment "+str(self.segment_index)+" of "+str(self.total_segments)+" received, waiting for next segment to be announced", RNS.LOG_DEBUG)
RNS.log(f"Resource segment {self.segment_index} of {self.total_segments} received, waiting for next segment to be announced", RNS.LOG_DEBUG)
def prove(self):
@ -623,9 +628,10 @@ class Resource:
proof_data = self.hash+proof
proof_packet = RNS.Packet(self.link, proof_data, packet_type=RNS.Packet.PROOF, context=RNS.Packet.RESOURCE_PRF)
proof_packet.send()
RNS.Transport.cache(proof_packet, force_cache=True)
except Exception as e:
RNS.log("Could not send proof packet, cancelling resource", RNS.LOG_DEBUG)
RNS.log("The contained exception was: "+str(e), RNS.LOG_DEBUG)
RNS.log(f"The contained exception was: {e}", RNS.LOG_DEBUG)
self.cancel()
def __prepare_next_segment(self):
@ -656,7 +662,7 @@ class Resource:
try:
self.callback(self)
except Exception as e:
RNS.log("Error while executing resource concluded callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while executing resource concluded callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
finally:
try:
if hasattr(self, "input_file"):
@ -664,7 +670,7 @@ class Resource:
self.input_file.close()
except Exception as e:
RNS.log("Error while closing resource input file: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while closing resource input file: {e}", RNS.LOG_ERROR)
else:
# Otherwise we'll recursively create the
# next segment of the resource
@ -691,7 +697,7 @@ class Resource:
if self.req_resp == None:
self.req_resp = self.last_activity
rtt = self.req_resp-self.req_sent
self.part_timeout_factor = Resource.PART_TIMEOUT_FACTOR_AFTER_RTT
if self.rtt == None:
self.rtt = self.link.rtt
@ -731,7 +737,7 @@ class Resource:
# Update consecutive completed pointer
if i == self.consecutive_completed_height + 1:
self.consecutive_completed_height = i
cp = self.consecutive_completed_height + 1
while cp < len(self.parts) and self.parts[cp] != None:
self.consecutive_completed_height = cp
@ -741,7 +747,7 @@ class Resource:
try:
self.__progress_callback(self)
except Exception as e:
RNS.log("Error while executing progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while executing progress callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
i += 1
@ -796,7 +802,7 @@ class Resource:
i = 0; pn = self.consecutive_completed_height+1
search_start = pn
search_size = self.window
for part in self.parts[search_start:search_start+search_size]:
if part == None:
part_hash = self.hashmap[pn]
@ -829,7 +835,7 @@ class Resource:
except Exception as e:
RNS.log("Could not send resource request packet, cancelling resource", RNS.LOG_DEBUG)
RNS.log("The contained exception was: "+str(e), RNS.LOG_DEBUG)
RNS.log(f"The contained exception was: {e}", RNS.LOG_DEBUG)
self.cancel()
# Called on outgoing resource to make it send more data
@ -875,12 +881,12 @@ class Resource:
except Exception as e:
RNS.log("Resource could not send parts, cancelling transfer!", RNS.LOG_DEBUG)
RNS.log("The contained exception was: "+str(e), RNS.LOG_DEBUG)
RNS.log(f"The contained exception was: {e}", RNS.LOG_DEBUG)
self.cancel()
if wants_more_hashmap:
last_map_hash = request_data[1:Resource.MAPHASH_LEN+1]
part_index = self.receiver_min_consecutive_height
search_start = part_index
search_end = self.receiver_min_consecutive_height+ResourceAdvertisement.COLLISION_GUARD_SIZE
@ -898,7 +904,7 @@ class Resource:
else:
segment = part_index // ResourceAdvertisement.HASHMAP_MAX_LEN
hashmap_start = segment*ResourceAdvertisement.HASHMAP_MAX_LEN
hashmap_end = min((segment+1)*ResourceAdvertisement.HASHMAP_MAX_LEN, len(self.parts))
@ -914,17 +920,18 @@ class Resource:
self.last_activity = time.time()
except Exception as e:
RNS.log("Could not send resource HMU packet, cancelling resource", RNS.LOG_DEBUG)
RNS.log("The contained exception was: "+str(e), RNS.LOG_DEBUG)
RNS.log(f"The contained exception was: {e}", RNS.LOG_DEBUG)
self.cancel()
if self.sent_parts == len(self.parts):
self.status = Resource.AWAITING_PROOF
self.retries_left = 3
if self.__progress_callback != None:
try:
self.__progress_callback(self)
except Exception as e:
RNS.log("Error while executing progress callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while executing progress callback from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
def cancel(self):
"""
@ -938,17 +945,17 @@ class Resource:
cancel_packet = RNS.Packet(self.link, self.hash, context=RNS.Packet.RESOURCE_ICL)
cancel_packet.send()
except Exception as e:
RNS.log("Could not send resource cancel packet, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Could not send resource cancel packet, the contained exception was: {e}", RNS.LOG_ERROR)
self.link.cancel_outgoing_resource(self)
else:
self.link.cancel_incoming_resource(self)
if self.callback != None:
try:
self.link.resource_concluded(self)
self.callback(self)
except Exception as e:
RNS.log("Error while executing callbacks on resource cancel from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while executing callbacks on resource cancel from {self}. The contained exception was: {e}", RNS.LOG_ERROR)
def set_callback(self, callback):
self.callback = callback
@ -962,7 +969,7 @@ class Resource:
"""
if self.status == RNS.Resource.COMPLETE and self.segment_index == self.total_segments:
return 1.0
elif self.initiator:
if not self.split:
self.processed_parts = self.sent_parts
@ -1061,7 +1068,7 @@ class Resource:
return self.compressed
def __str__(self):
return "<"+RNS.hexrep(self.hash,delimit=False)+"/"+RNS.hexrep(self.link.link_id,delimit=False)+">"
return f"<{RNS.hexrep(self.hash, delimit=False)}/{RNS.hexrep(self.link.link_id, delimit=False)}>"
class ResourceAdvertisement:
@ -1187,7 +1194,7 @@ class ResourceAdvertisement:
@staticmethod
def unpack(data):
dictionary = umsgpack.unpackb(data)
adv = ResourceAdvertisement()
adv.t = dictionary["t"]
adv.d = dictionary["d"]
@ -1206,4 +1213,4 @@ class ResourceAdvertisement:
adv.u = True if ((adv.f >> 3) & 0x01) == 0x01 else False
adv.p = True if ((adv.f >> 4) & 0x01) == 0x01 else False
return adv
return adv

View File

@ -29,6 +29,7 @@ if get_platform() == "android":
from .Interfaces import TCPInterface
from .Interfaces import UDPInterface
from .Interfaces import I2PInterface
from .Interfaces import RNodeMultiInterface
from .Interfaces.Android import RNodeInterface
from .Interfaces.Android import SerialInterface
from .Interfaces.Android import KISSInterface
@ -128,7 +129,7 @@ class Reticulum:
HEADER_MAXSIZE = 2+1+(TRUNCATED_HASHLENGTH//8)*2
IFAC_MIN_SIZE = 1
IFAC_SALT = bytes.fromhex("adf54d882c9a9b80771eb4995d702d4a3e733391b2a0f53f416d9f907e55cff8")
MDU = MTU - HEADER_MAXSIZE - IFAC_MIN_SIZE
RESOURCE_CACHE = 24*60*60
@ -139,7 +140,7 @@ class Reticulum:
router = None
config = None
# The default configuration path will be expanded to a directory
# named ".reticulum" inside the current users home directory
userdir = os.path.expanduser("~")
@ -149,7 +150,7 @@ class Reticulum:
cachepath = ""
__instance = None
@staticmethod
def exit_handler():
# This exit handler is called whenever Reticulum is asked to
@ -203,20 +204,20 @@ class Reticulum:
else:
if os.path.isdir("/etc/reticulum") and os.path.isfile("/etc/reticulum/config"):
Reticulum.configdir = "/etc/reticulum"
elif os.path.isdir(Reticulum.userdir+"/.config/reticulum") and os.path.isfile(Reticulum.userdir+"/.config/reticulum/config"):
Reticulum.configdir = Reticulum.userdir+"/.config/reticulum"
elif os.path.isdir(f"{Reticulum.userdir}/.config/reticulum") and os.path.isfile(f"{Reticulum.userdir}/.config/reticulum/config"):
Reticulum.configdir = f"{Reticulum.userdir}/.config/reticulum"
else:
Reticulum.configdir = Reticulum.userdir+"/.reticulum"
Reticulum.configdir = f"{Reticulum.userdir}/.reticulum"
if logdest == RNS.LOG_FILE:
RNS.logdest = RNS.LOG_FILE
RNS.logfile = Reticulum.configdir+"/logfile"
Reticulum.configpath = Reticulum.configdir+"/config"
Reticulum.storagepath = Reticulum.configdir+"/storage"
Reticulum.cachepath = Reticulum.configdir+"/storage/cache"
Reticulum.resourcepath = Reticulum.configdir+"/storage/resources"
Reticulum.identitypath = Reticulum.configdir+"/storage/identities"
RNS.logfile = f"{Reticulum.configdir}/logfile"
Reticulum.configpath = f"{Reticulum.configdir}/config"
Reticulum.storagepath = f"{Reticulum.configdir}/storage"
Reticulum.cachepath = f"{Reticulum.configdir}/storage/cache"
Reticulum.resourcepath = f"{Reticulum.configdir}/storage/resources"
Reticulum.identitypath = f"{Reticulum.configdir}/storage/identities"
Reticulum.__transport_enabled = False
Reticulum.__remote_management_enabled = False
@ -266,18 +267,18 @@ class Reticulum:
try:
self.config = ConfigObj(self.configpath)
except Exception as e:
RNS.log("Could not parse the configuration at "+self.configpath, RNS.LOG_ERROR)
RNS.log(f"Could not parse the configuration at {self.configpath}", RNS.LOG_ERROR)
RNS.log("Check your configuration file for errors!", RNS.LOG_ERROR)
RNS.panic()
else:
RNS.log("Could not load config file, creating default configuration file...")
self.__create_default_config()
RNS.log("Default config file created. Make any necessary changes in "+Reticulum.configdir+"/config and restart Reticulum if needed.")
RNS.log(f"Default config file created. Make any necessary changes in {Reticulum.configdir}/config and restart Reticulum if needed.")
time.sleep(1.5)
self.__apply_config()
RNS.log("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.Transport.start(self)
@ -285,7 +286,7 @@ class Reticulum:
self.rpc_addr = ("127.0.0.1", self.local_control_port)
if self.rpc_key == None:
self.rpc_key = RNS.Identity.full_hash(RNS.Transport.identity.get_private_key())
if self.is_shared_instance:
self.rpc_listener = multiprocessing.connection.Listener(self.rpc_addr, authkey=self.rpc_key)
thread = threading.Thread(target=self.rpc_loop)
@ -313,7 +314,7 @@ class Reticulum:
if now > self.last_data_persist+Reticulum.PERSIST_INTERVAL:
self.__persist_data()
time.sleep(Reticulum.JOB_INTERVAL)
def __start_local_interface(self):
@ -329,9 +330,9 @@ class Reticulum:
interface._force_bitrate = Reticulum._force_shared_instance_bitrate
RNS.log(f"Forcing shared instance bitrate of {RNS.prettyspeed(interface.bitrate)}", RNS.LOG_WARNING)
RNS.Transport.interfaces.append(interface)
self.is_shared_instance = True
RNS.log("Started shared instance interface: "+str(interface), RNS.LOG_DEBUG)
RNS.log(f"Started shared instance interface: {interface}", RNS.LOG_DEBUG)
self.__start_jobs()
except Exception as e:
@ -353,10 +354,10 @@ class Reticulum:
Reticulum.__transport_enabled = False
Reticulum.__remote_management_enabled = False
Reticulum.__allow_probes = False
RNS.log("Connected to locally available Reticulum instance via: "+str(interface), RNS.LOG_DEBUG)
RNS.log(f"Connected to locally available Reticulum instance via: {interface}", RNS.LOG_DEBUG)
except Exception as e:
RNS.log("Local shared instance appears to be running, but it could not be connected", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
self.is_shared_instance = False
self.is_standalone_instance = True
self.is_connected_to_shared_instance = False
@ -411,11 +412,11 @@ class Reticulum:
for hexhash in v:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(hexhash) != dest_len:
raise ValueError("Identity hash length for remote management ACL "+str(hexhash)+" is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
raise ValueError(f"Identity hash length for remote management ACL {hexhash} is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try:
allowed_hash = bytes.fromhex(hexhash)
except Exception as e:
raise ValueError("Invalid identity hash for remote management ACL: "+str(hexhash))
raise ValueError(f"Invalid identity hash for remote management ACL: {hexhash}")
if not allowed_hash in RNS.Transport.remote_management_allowed:
RNS.Transport.remote_management_allowed.append(allowed_hash)
@ -454,7 +455,7 @@ class Reticulum:
c = self.config["interfaces"][name]
interface_mode = Interface.Interface.MODE_FULL
if "interface_mode" in c:
c["interface_mode"] = str(c["interface_mode"]).lower()
if c["interface_mode"] == "full":
@ -489,7 +490,7 @@ class Reticulum:
if "ifac_size" in c:
if c.as_int("ifac_size") >= Reticulum.IFAC_MIN_SIZE*8:
ifac_size = c.as_int("ifac_size")//8
ifac_netname = None
if "networkname" in c:
if c["networkname"] != "":
@ -505,7 +506,7 @@ class Reticulum:
if "pass_phrase" in c:
if c["pass_phrase"] != "":
ifac_netkey = c["pass_phrase"]
ingress_control = True
if "ingress_control" in c: ingress_control = c.as_bool("ingress_control")
ic_max_held_announces = None
@ -532,12 +533,12 @@ class Reticulum:
if "announce_rate_target" in c:
if c.as_int("announce_rate_target") > 0:
announce_rate_target = c.as_int("announce_rate_target")
announce_rate_grace = None
if "announce_rate_grace" in c:
if c.as_int("announce_rate_grace") >= 0:
announce_rate_grace = c.as_int("announce_rate_grace")
announce_rate_penalty = None
if "announce_rate_penalty" in c:
if c.as_int("announce_rate_penalty") >= 0:
@ -553,7 +554,7 @@ class Reticulum:
if "announce_cap" in c:
if c.as_float("announce_cap") > 0 and c.as_float("announce_cap") <= 100:
announce_cap = c.as_float("announce_cap")/100.0
try:
interface = None
@ -658,9 +659,9 @@ class Reticulum:
interface.OUT = True
if interface_mode == Interface.Interface.MODE_ACCESS_POINT:
RNS.log(str(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_mode
interface.announce_cap = announce_cap
@ -695,9 +696,9 @@ class Reticulum:
interface.OUT = True
if interface_mode == Interface.Interface.MODE_ACCESS_POINT:
RNS.log(str(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_mode
interface.announce_cap = announce_cap
@ -732,9 +733,9 @@ class Reticulum:
interface.OUT = True
if interface_mode == Interface.Interface.MODE_ACCESS_POINT:
RNS.log(str(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_mode
interface.announce_cap = announce_cap
@ -937,7 +938,7 @@ class Reticulum:
ble_addr = ble_string
else:
ble_name = ble_string
interface = RNodeInterface.RNodeInterface(
RNS.Transport,
name,
@ -1011,11 +1012,11 @@ class Reticulum:
txpower = int(subinterface_config["txpower"]) if "txpower" in subinterface_config else None
subint_config[subint_index][4] = txpower
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
subint_config[subint_index][6] = codingrate
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
subint_config[subint_index][8] = st_alock
lt_alock = float(subinterface_config["airtime_limit_long"]) if "airtime_limit_long" in subinterface_config else None
@ -1029,17 +1030,17 @@ class Reticulum:
# if no subinterfaces are defined
if count == 0:
raise ValueError("No subinterfaces configured for "+name)
raise ValueError(f"No subinterfaces configured for {name}")
# if no subinterfaces are enabled
elif enabled_count == 0:
raise ValueError("No subinterfaces enabled for "+name)
raise ValueError(f"No subinterfaces enabled for {name}")
id_interval = int(c["id_interval"]) if "id_interval" in c else None
id_callsign = c["id_callsign"] if "id_callsign" in c else None
port = c["port"] if "port" in c else None
if port == None:
raise ValueError("No port specified for "+name)
raise ValueError(f"No port specified for {name}")
interface = RNodeMultiInterface.RNodeMultiInterface(
RNS.Transport,
@ -1106,14 +1107,14 @@ class Reticulum:
interface.start()
else:
RNS.log("Skipping disabled interface \""+name+"\"", RNS.LOG_DEBUG)
RNS.log(f"Skipping disabled interface \"{name}\"", RNS.LOG_DEBUG)
except Exception as e:
RNS.log("The interface \""+name+"\" could not be created. Check your configuration file for errors!", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"The interface \"{name}\" could not be created. Check your configuration file for errors!", RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
RNS.panic()
else:
RNS.log("The interface name \""+name+"\" was already used. Check your configuration file for errors!", RNS.LOG_ERROR)
RNS.log(f"The interface name \"{name}\" was already used. Check your configuration file for errors!", RNS.LOG_ERROR)
RNS.panic()
RNS.log("System interfaces are ready", RNS.LOG_VERBOSE)
@ -1121,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):
if not self.is_connected_to_shared_instance:
if interface != None and issubclass(type(interface), RNS.Interfaces.Interface.Interface):
if mode == None:
mode = Interface.Interface.MODE_FULL
interface.mode = mode
@ -1181,32 +1182,32 @@ class Reticulum:
for filename in os.listdir(self.resourcepath):
try:
if len(filename) == (RNS.Identity.HASHLENGTH//8)*2:
filepath = self.resourcepath + "/" + filename
filepath = f"{self.resourcepath}/{filename}"
mtime = os.path.getmtime(filepath)
age = now - mtime
if age > Reticulum.RESOURCE_CACHE:
os.unlink(filepath)
except Exception as e:
RNS.log("Error while cleaning resources cache, the contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while cleaning resources cache, the contained exception was: {e}", RNS.LOG_ERROR)
# Clean packet caches
for filename in os.listdir(self.cachepath):
try:
if len(filename) == (RNS.Identity.HASHLENGTH//8)*2:
filepath = self.cachepath + "/" + filename
filepath = f"{self.cachepath}/{filename}"
mtime = os.path.getmtime(filepath)
age = now - mtime
if age > RNS.Transport.DESTINATION_TIMEOUT:
os.unlink(filepath)
except Exception as e:
RNS.log("Error while cleaning resources cache, the contained exception was: "+str(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):
self.config = ConfigObj(__default_rns_config__)
self.config.filename = Reticulum.configpath
if not os.path.isdir(Reticulum.configdir):
os.makedirs(Reticulum.configdir)
self.config.write()
@ -1266,7 +1267,7 @@ class Reticulum:
rpc_connection.close()
except Exception as e:
RNS.log("An error ocurred while handling RPC call from local client: "+str(e), RNS.LOG_ERROR)
RNS.log(f"An error ocurred while handling RPC call from local client: {e}", RNS.LOG_ERROR)
def get_interface_stats(self):
if self.is_connected_to_shared_instance:
@ -1278,7 +1279,7 @@ class Reticulum:
interfaces = []
for interface in RNS.Transport.interfaces:
ifstats = {}
if hasattr(interface, "clients"):
ifstats["clients"] = interface.clients
else:
@ -1296,7 +1297,7 @@ class Reticulum:
if hasattr(interface, "b32"):
if interface.b32 != None:
ifstats["i2p_b32"] = interface.b32+".b32.i2p"
ifstats["i2p_b32"] = f"{interface.b32}.b32.i2p"
else:
ifstats["i2p_b32"] = None
@ -1327,7 +1328,7 @@ class Reticulum:
if hasattr(interface, "r_battery_state"):
if interface.r_battery_state != 0x00:
ifstats["battery_state"] = interface.r_battery_state
ifstats["battery_state"] = interface.get_battery_state_string()
if hasattr(interface, "r_battery_percent"):
ifstats["battery_percent"] = interface.r_battery_percent
@ -1485,12 +1486,12 @@ class Reticulum:
if self.is_connected_to_shared_instance and hasattr(self, "_force_shared_instance_bitrate") and self._force_shared_instance_bitrate:
simulated_latency = ((1/self._force_shared_instance_bitrate)*8)*RNS.Reticulum.MTU
RNS.log("Adding simulated latency of "+RNS.prettytime(simulated_latency)+" to first hop timeout", RNS.LOG_DEBUG)
RNS.log(f"Adding simulated latency of {RNS.prettytime(simulated_latency)} to first hop timeout", RNS.LOG_DEBUG)
response += simulated_latency
return response
except Exception as e:
RNS.log("An error occurred while getting first hop timeout from shared instance: "+str(e), RNS.LOG_ERROR)
RNS.log(f"An error occurred while getting first hop timeout from shared instance: {e}", RNS.LOG_ERROR)
return RNS.Reticulum.DEFAULT_PER_HOP_TIMEOUT
else:

File diff suppressed because it is too large Load Diff

View File

@ -23,5 +23,5 @@
import os
import glob
modules = glob.glob(os.path.dirname(__file__)+"/*.py")
modules = glob.glob(f"{os.path.dirname(__file__)}/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]

View File

@ -35,6 +35,7 @@ APP_NAME = "rncp"
allow_all = False
allow_fetch = False
fetch_jail = None
save_path = None
show_phy_rates = False
allowed_identity_hashes = []
@ -44,8 +45,8 @@ es = " "
erase_str = "\33[2K\r"
def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identity = False,
limit = None, disable_auth = None, fetch_allowed = False, jail = None, announce = False):
global allow_all, allow_fetch, allowed_identity_hashes, fetch_jail
limit = None, disable_auth = None, fetch_allowed = False, jail = None, save = None, announce = False):
global allow_all, allow_fetch, allowed_identity_hashes, fetch_jail, save_path
from tempfile import TemporaryFile
allow_fetch = fetch_allowed
@ -58,11 +59,25 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
if jail != None:
fetch_jail = os.path.abspath(os.path.expanduser(jail))
RNS.log("Restricting fetch requests to paths under \""+fetch_jail+"\"", RNS.LOG_VERBOSE)
RNS.log(f"Restricting fetch requests to paths under \"{fetch_jail}\"", RNS.LOG_VERBOSE)
identity_path = RNS.Reticulum.identitypath+"/"+APP_NAME
if save != None:
sp = os.path.abspath(os.path.expanduser(save))
if os.path.isdir(sp):
if os.access(sp, os.W_OK):
save_path = sp
else:
RNS.log("Output directory not writable", RNS.LOG_ERROR)
exit(4)
else:
RNS.log("Output directory not found", RNS.LOG_ERROR)
exit(3)
RNS.log(f"Saving received files in \"{save_path}\"", RNS.LOG_VERBOSE)
identity_path = f"{RNS.Reticulum.identitypath}/{APP_NAME}"
if os.path.isfile(identity_path):
identity = RNS.Identity.from_file(identity_path)
identity = RNS.Identity.from_file(identity_path)
if identity == None:
RNS.log("No valid saved identity found, creating new...", RNS.LOG_INFO)
@ -72,8 +87,8 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "receive")
if display_identity:
print("Identity : "+str(identity))
print("Listening on : "+RNS.prettyhexrep(destination.hash))
print(f"Identity : {identity}")
print(f"Listening on : {RNS.prettyhexrep(destination.hash)}")
exit(0)
if disable_auth:
@ -83,14 +98,14 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
try:
allowed_file_name = "allowed_identities"
allowed_file = None
if os.path.isfile(os.path.expanduser("/etc/rncp/"+allowed_file_name)):
allowed_file = os.path.expanduser("/etc/rncp/"+allowed_file_name)
elif os.path.isfile(os.path.expanduser("~/.config/rncp/"+allowed_file_name)):
allowed_file = os.path.expanduser("~/.config/rncp/"+allowed_file_name)
elif os.path.isfile(os.path.expanduser("~/.rncp/"+allowed_file_name)):
allowed_file = os.path.expanduser("~/.rncp/"+allowed_file_name)
if os.path.isfile(os.path.expanduser(f"/etc/rncp/{allowed_file_name}")):
allowed_file = os.path.expanduser(f"/etc/rncp/{allowed_file_name}")
elif os.path.isfile(os.path.expanduser(f"~/.config/rncp/{allowed_file_name}")):
allowed_file = os.path.expanduser(f"~/.config/rncp/{allowed_file_name}")
elif os.path.isfile(os.path.expanduser(f"~/.rncp/{allowed_file_name}")):
allowed_file = os.path.expanduser(f"~/.rncp/{allowed_file_name}")
if allowed_file != None:
af = open(allowed_file, "r")
af = open(allowed_file)
al = af.read().replace("\r", "").split("\n")
ali = []
for a in al:
@ -106,17 +121,17 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
ms = "y"
else:
ms = "ies"
RNS.log("Loaded "+str(len(ali))+" allowed identit"+ms+" from "+str(allowed_file), RNS.LOG_VERBOSE)
RNS.log(f"Loaded {len(ali)} allowed identit{ms} from {allowed_file}", RNS.LOG_VERBOSE)
except Exception as e:
RNS.log("Error while parsing allowed_identities file. The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"Error while parsing allowed_identities file. The contained exception was: {e}", RNS.LOG_ERROR)
if allowed != None:
for a in allowed:
try:
if len(a) != dest_len:
raise ValueError("Allowed destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
raise ValueError(f"Allowed destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try:
destination_hash = bytes.fromhex(a)
allowed_identity_hashes.append(destination_hash)
@ -135,9 +150,11 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
return REQ_FETCH_NOT_ALLOWED
if fetch_jail:
if data.startswith(fetch_jail+"/"):
data = data.replace(fetch_jail+"/", "")
file_path = os.path.abspath(os.path.expanduser(f"{fetch_jail}/{data}"))
RNS.log(f"Disallowing fetch request for {file_path} outside of fetch jail {fetch_jail}", RNS.LOG_WARNING)
if not file_path.startswith(fetch_jail):
if not file_path.startswith(fetch_jail+"/"):
RNS.log(f"Disallowing fetch request for {file_path} outside of fetch jail {fetch_jail}", RNS.LOG_WARNING)
return REQ_FETCH_NOT_ALLOWED
else:
file_path = os.path.abspath(os.path.expanduser(f"{data}"))
@ -148,11 +165,11 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
target_link = link
if not os.path.isfile(file_path):
RNS.log("Client-requested file not found: "+str(file_path), RNS.LOG_VERBOSE)
RNS.log(f"Client-requested file not found: {file_path}", RNS.LOG_VERBOSE)
return False
else:
if target_link != None:
RNS.log("Sending file "+str(file_path)+" to client", RNS.LOG_VERBOSE)
RNS.log(f"Sending file {file_path} to client", RNS.LOG_VERBOSE)
temp_file = TemporaryFile()
real_file = open(file_path, "rb")
@ -182,7 +199,7 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
else:
destination.register_request_handler("fetch_file", response_generator=fetch_request, allow=RNS.Destination.ALLOW_LIST, allowed_list=allowed_identity_hashes)
print("rncp listening on "+RNS.prettyhexrep(destination.hash))
print(f"rncp listening on {RNS.prettyhexrep(destination.hash)}")
if announce >= 0:
def job():
@ -193,7 +210,7 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
destination.announce()
threading.Thread(target=job, daemon=True).start()
while True:
time.sleep(1)
@ -219,7 +236,7 @@ def receive_sender_identified(link, identity):
def receive_resource_callback(resource):
global allow_all
sender_identity = resource.link.get_remote_identity()
if sender_identity != None:
@ -233,25 +250,25 @@ def receive_resource_callback(resource):
def receive_resource_started(resource):
if resource.link.get_remote_identity():
id_str = " from "+RNS.prettyhexrep(resource.link.get_remote_identity().hash)
id_str = f" from {RNS.prettyhexrep(resource.link.get_remote_identity().hash)}"
else:
id_str = ""
print("Starting resource transfer "+RNS.prettyhexrep(resource.hash)+id_str)
print(f"Starting resource transfer {RNS.prettyhexrep(resource.hash)}{id_str}")
def receive_resource_concluded(resource):
global fetch_jail
global save_path
if resource.status == RNS.Resource.COMPLETE:
print(str(resource)+" completed")
print(f"{resource} completed")
if resource.total_size > 4:
filename_len = int.from_bytes(resource.data.read(2), "big")
filename = resource.data.read(filename_len).decode("utf-8")
counter = 0
if fetch_jail:
saved_filename = os.path.abspath(os.path.expanduser(fetch_jail+"/"+filename))
if not saved_filename.startswith(fetch_jail):
if save_path:
saved_filename = os.path.abspath(os.path.expanduser(save_path+"/"+filename))
if not saved_filename.startswith(save_path+"/"):
RNS.log(f"Invalid save path {saved_filename}, ignoring", RNS.LOG_ERROR)
return
else:
@ -260,8 +277,8 @@ def receive_resource_concluded(resource):
full_save_path = saved_filename
while os.path.isfile(full_save_path):
counter += 1
full_save_path = saved_filename+"."+str(counter)
full_save_path = f"{saved_filename}.{counter}"
file = open(full_save_path, "wb")
file.write(resource.data.read())
file.close()
@ -311,15 +328,27 @@ def sender_progress(resource):
resource_done = True
link = None
def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = None, timeout = RNS.Transport.PATH_REQUEST_TIMEOUT, silent=False, phy_rates=False):
global current_resource, resource_done, link, speed, show_phy_rates
def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = None, timeout = RNS.Transport.PATH_REQUEST_TIMEOUT, silent=False, phy_rates=False, save=None):
global current_resource, resource_done, link, speed, show_phy_rates, save_path
targetloglevel = 3+verbosity-quietness
show_phy_rates = phy_rates
if save:
sp = os.path.abspath(os.path.expanduser(save))
if os.path.isdir(sp):
if os.access(sp, os.W_OK):
save_path = sp
else:
RNS.log("Output directory not writable", RNS.LOG_ERROR)
exit(4)
else:
RNS.log("Output directory not found", RNS.LOG_ERROR)
exit(3)
try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination) != dest_len:
raise ValueError("Allowed destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
raise ValueError(f"Allowed destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try:
destination_hash = bytes.fromhex(destination)
except Exception as e:
@ -330,11 +359,11 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
reticulum = RNS.Reticulum(configdir=configdir, loglevel=targetloglevel)
identity_path = RNS.Reticulum.identitypath+"/"+APP_NAME
identity_path = f"{RNS.Reticulum.identitypath}/{APP_NAME}"
if os.path.isfile(identity_path):
identity = RNS.Identity.from_file(identity_path)
if identity == None:
RNS.log("Could not load identity for rncp. The identity file at \""+str(identity_path)+"\" may be corrupt or unreadable.", RNS.LOG_ERROR)
RNS.log(f"Could not load identity for rncp. The identity file at \"{identity_path}\" may be corrupt or unreadable.", RNS.LOG_ERROR)
exit(2)
else:
identity = None
@ -347,9 +376,9 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
if not RNS.Transport.has_path(destination_hash):
RNS.Transport.request_path(destination_hash)
if silent:
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested")
print(f"Path to {RNS.prettyhexrep(destination_hash)} requested")
else:
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=es)
print(f"Path to {RNS.prettyhexrep(destination_hash)} requested ", end=es)
sys.stdout.flush()
i = 0
@ -358,7 +387,7 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
while not RNS.Transport.has_path(destination_hash) and time.time() < estab_timeout:
if not silent:
time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="")
print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush()
i = (i+1)%len(syms)
@ -370,9 +399,9 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
exit(1)
else:
if silent:
print("Establishing link with "+RNS.prettyhexrep(destination_hash))
print(f"Establishing link with {RNS.prettyhexrep(destination_hash)}")
else:
print(f"{erase_str}Establishing link with "+RNS.prettyhexrep(destination_hash)+" ", end=es)
print(f"{erase_str}Establishing link with {RNS.prettyhexrep(destination_hash)} ", end=es)
listener_identity = RNS.Identity.recall(destination_hash)
listener_destination = RNS.Destination(
@ -387,15 +416,15 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
while link.status != RNS.Link.ACTIVE and time.time() < estab_timeout:
if not silent:
time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="")
print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush()
i = (i+1)%len(syms)
if not RNS.Transport.has_path(destination_hash):
if silent:
print("Could not establish link with "+RNS.prettyhexrep(destination_hash))
print(f"Could not establish link with {RNS.prettyhexrep(destination_hash)}")
else:
print(f"{erase_str}Could not establish link with "+RNS.prettyhexrep(destination_hash))
print(f"{erase_str}Could not establish link with {RNS.prettyhexrep(destination_hash)}")
exit(1)
else:
if silent:
@ -436,18 +465,25 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
def fetch_resource_concluded(resource):
nonlocal resource_resolved, resource_status
global save_path
if resource.status == RNS.Resource.COMPLETE:
if resource.total_size > 4:
filename_len = int.from_bytes(resource.data.read(2), "big")
filename = resource.data.read(filename_len).decode("utf-8")
counter = 0
saved_filename = filename
while os.path.isfile(saved_filename):
if save_path:
saved_filename = os.path.abspath(os.path.expanduser(save_path+"/"+filename))
else:
saved_filename = filename
full_save_path = saved_filename
while os.path.isfile(full_save_path):
counter += 1
saved_filename = filename+"."+str(counter)
file = open(saved_filename, "wb")
full_save_path = f"{saved_filename}.{counter)"
file = open(full_save_path, "wb")
file.write(resource.data.read())
file.close()
resource_status = "completed"
@ -471,19 +507,19 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
while not request_resolved:
if not silent:
time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="")
print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush()
i = (i+1)%len(syms)
if request_status == "fetch_not_allowed":
if not silent: print(f"{erase_str}", end="")
print("Fetch request failed, fetching the file "+str(file)+" was not allowed by the remote")
print(f"Fetch request failed, fetching the file {file} was not allowed by the remote")
link.teardown()
time.sleep(0.15)
exit(0)
elif request_status == "not_found":
if not silent: print(f"{erase_str}", end="")
print("Fetch request failed, the file "+str(file)+" was not found on the remote")
print(f"Fetch request failed, the file {file} was not found on the remote")
link.teardown()
time.sleep(0.15)
exit(0)
@ -534,9 +570,9 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
exit(1)
else:
if silent:
print(str(file)+" fetched from "+RNS.prettyhexrep(destination_hash))
print(f"{file} fetched from {RNS.prettyhexrep(destination_hash)}")
else:
print("\n"+str(file)+" fetched from "+RNS.prettyhexrep(destination_hash))
print(f"\n{file} fetched from {RNS.prettyhexrep(destination_hash)}")
link.teardown()
time.sleep(0.15)
exit(0)
@ -554,7 +590,7 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination) != dest_len:
raise ValueError("Allowed destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
raise ValueError(f"Allowed destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try:
destination_hash = bytes.fromhex(destination)
except Exception as e:
@ -563,7 +599,7 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
print(str(e))
exit(1)
file_path = os.path.expanduser(file)
if not os.path.isfile(file_path):
print("File not found")
@ -589,11 +625,11 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
reticulum = RNS.Reticulum(configdir=configdir, loglevel=targetloglevel)
identity_path = RNS.Reticulum.identitypath+"/"+APP_NAME
identity_path = f"{RNS.Reticulum.identitypath}/{APP_NAME}"
if os.path.isfile(identity_path):
identity = RNS.Identity.from_file(identity_path)
if identity == None:
RNS.log("Could not load identity for rncp. The identity file at \""+str(identity_path)+"\" may be corrupt or unreadable.", RNS.LOG_ERROR)
RNS.log(f"Could not load identity for rncp. The identity file at \"{identity_path}\" may be corrupt or unreadable.", RNS.LOG_ERROR)
exit(2)
else:
identity = None
@ -606,9 +642,9 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
if not RNS.Transport.has_path(destination_hash):
RNS.Transport.request_path(destination_hash)
if silent:
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested")
print(f"Path to {RNS.prettyhexrep(destination_hash)} requested")
else:
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=es)
print(f"Path to {RNS.prettyhexrep(destination_hash)} requested ", end=es)
sys.stdout.flush()
i = 0
@ -617,7 +653,7 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
while not RNS.Transport.has_path(destination_hash) and time.time() < estab_timeout:
if not silent:
time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="")
print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush()
i = (i+1)%len(syms)
@ -629,9 +665,9 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
exit(1)
else:
if silent:
print("Establishing link with "+RNS.prettyhexrep(destination_hash))
print(f"Establishing link with {RNS.prettyhexrep(destination_hash)}")
else:
print(f"{erase_str}Establishing link with "+RNS.prettyhexrep(destination_hash)+" ", end=es)
print(f"{erase_str}Establishing link with {RNS.prettyhexrep(destination_hash)} ", end=es)
receiver_identity = RNS.Identity.recall(destination_hash)
receiver_destination = RNS.Destination(
@ -646,21 +682,21 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
while link.status != RNS.Link.ACTIVE and time.time() < estab_timeout:
if not silent:
time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="")
print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush()
i = (i+1)%len(syms)
if time.time() > estab_timeout:
if silent:
print("Link establishment with "+RNS.prettyhexrep(destination_hash)+" timed out")
print(f"Link establishment with {RNS.prettyhexrep(destination_hash)} timed out")
else:
print(f"{erase_str}Link establishment with "+RNS.prettyhexrep(destination_hash)+" timed out")
print(f"{erase_str}Link establishment with {RNS.prettyhexrep(destination_hash)} timed out")
exit(1)
elif not RNS.Transport.has_path(destination_hash):
if silent:
print("No path found to "+RNS.prettyhexrep(destination_hash))
print(f"No path found to {RNS.prettyhexrep(destination_hash)}")
else:
print(f"{erase_str}No path found to "+RNS.prettyhexrep(destination_hash))
print(f"{erase_str}No path found to {RNS.prettyhexrep(destination_hash)}")
exit(1)
else:
if silent:
@ -675,16 +711,16 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
while resource.status < RNS.Resource.TRANSFERRING:
if not silent:
time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="")
print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush()
i = (i+1)%len(syms)
if resource.status > RNS.Resource.COMPLETE:
if silent:
print("File was not accepted by "+RNS.prettyhexrep(destination_hash))
print(f"File was not accepted by {RNS.prettyhexrep(destination_hash)}")
else:
print(f"{erase_str}File was not accepted by "+RNS.prettyhexrep(destination_hash))
print(f"{erase_str}File was not accepted by {RNS.prettyhexrep(destination_hash)}")
exit(1)
else:
if silent:
@ -707,9 +743,9 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
ss = size_str(speed, "b")
stat_str = f"{percent}% - {cs} of {ts} - {ss}ps{phy_str}"
if not done:
print(f"{erase_str}Transferring file "+syms[i]+" "+stat_str, end=es)
print(f"{erase_str}Transferring file {syms[i]} {stat_str}", end=es)
else:
print(f"{erase_str}Transfer complete "+stat_str, end=es)
print(f"{erase_str}Transfer complete {stat_str}", end=es)
sys.stdout.flush()
i = (i+1)%len(syms)
return i
@ -729,9 +765,9 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
exit(1)
else:
if silent:
print(str(file_path)+" copied to "+RNS.prettyhexrep(destination_hash))
print(f"{file_path} copied to {RNS.prettyhexrep(destination_hash)}")
else:
print("\n"+str(file_path)+" copied to "+RNS.prettyhexrep(destination_hash))
print(f"\n{file_path} copied to {RNS.prettyhexrep(destination_hash)}")
link.teardown()
time.sleep(0.25)
real_file.close()
@ -751,15 +787,16 @@ def main():
parser.add_argument("-F", '--allow-fetch', action='store_true', default=False, help="allow authenticated clients to fetch files")
parser.add_argument("-f", '--fetch', action='store_true', default=False, help="fetch file from remote listener instead of sending")
parser.add_argument("-j", "--jail", metavar="path", action="store", default=None, help="restrict fetch requests to specified path", type=str)
parser.add_argument("-s", "--save", metavar="path", action="store", default=None, help="save received files in specified path", type=str)
parser.add_argument("-b", action='store', metavar="seconds", default=-1, help="announce interval, 0 to only announce at startup", type=int)
parser.add_argument('-a', metavar="allowed_hash", dest="allowed", action='append', help="allow this identity", type=str)
parser.add_argument('-a', metavar="allowed_hash", dest="allowed", action='append', help="allow this identity (or add in ~/.rncp/allowed_identities)", type=str)
parser.add_argument('-n', '--no-auth', action='store_true', default=False, help="accept requests from anyone")
parser.add_argument('-p', '--print-identity', action='store_true', default=False, help="print identity and destination info and exit")
parser.add_argument("-w", action="store", metavar="seconds", type=float, help="sender timeout before giving up", default=RNS.Transport.PATH_REQUEST_TIMEOUT)
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("--version", action="version", version="rncp {version}".format(version=__version__))
parser.add_argument("--version", action="version", version=f"rncp {__version__}")
args = parser.parse_args()
if args.listen or args.print_identity:
@ -770,6 +807,7 @@ def main():
allowed = args.allowed,
fetch_allowed = args.allow_fetch,
jail = args.jail,
save = args.save,
display_identity=args.print_identity,
# limit=args.limit,
disable_auth=args.no_auth,
@ -787,6 +825,7 @@ def main():
timeout = args.w,
silent = args.silent,
phy_rates = args.phy_rates,
save = args.save,
)
else:
print("")
@ -830,12 +869,12 @@ def size_str(num, suffix='B'):
for unit in units:
if abs(num) < 1000.0:
if unit == "":
return "%.0f %s%s" % (num, unit, suffix)
return f"{num:.0f} {unit}{suffix}"
else:
return "%.2f %s%s" % (num, unit, suffix)
return f"{num:.2f} {unit}{suffix}"
num /= 1000.0
return "%.2f%s%s" % (num, last_unit, suffix)
return f"{num:.2f}{last_unit}{suffix}"
if __name__ == "__main__":
main()

View File

@ -43,14 +43,14 @@ def spin(until=None, msg=None, timeout=None):
if timeout != None:
timeout = time.time()+timeout
print(msg+" ", end=" ")
print(f"{msg} ", end=" ")
while (timeout == None or time.time()<timeout) and not until():
time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="")
print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush()
i = (i+1)%len(syms)
print("\r"+" "*len(msg)+" \r", end="")
print(f"\r{' ' * len(msg)} \r", end="")
if timeout != None and time.time() > timeout:
return False
@ -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("-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("-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("-t", action="store", metavar="seconds", type=float, help="identity request timeout before giving up", default=RNS.Transport.PATH_REQUEST_TIMEOUT)
@ -92,15 +92,15 @@ def main():
parser.add_argument("-b", "--base64", action="store_true", default=False, help="Use base64-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="rnid {version}".format(version=__version__))
parser.add_argument("--version", action="version", version=f"rnid {__version__}")
args = parser.parse_args()
ops = 0;
for t in [args.encrypt, args.decrypt, args.validate, args.sign]:
if t:
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)
exit(1)
@ -124,30 +124,30 @@ def main():
else:
identity_bytes = bytes.fromhex(args.import_str)
except Exception as e:
print("Invalid identity data specified for import: "+str(e))
print(f"Invalid identity data specified for import: {e}")
exit(41)
try:
identity = RNS.Identity.from_bytes(identity_bytes)
except Exception as e:
print("Could not create Reticulum identity from specified data: "+str(e))
print(f"Could not create Reticulum identity from specified data: {e}")
exit(42)
RNS.log("Identity imported")
if args.base64:
RNS.log("Public Key : "+base64.urlsafe_b64encode(identity.get_public_key()).decode("utf-8"))
RNS.log(f"Public Key : {base64.urlsafe_b64encode(identity.get_public_key()).decode('utf-8')}")
elif args.base32:
RNS.log("Public Key : "+base64.b32encode(identity.get_public_key()).decode("utf-8"))
RNS.log(f"Public Key : {base64.b32encode(identity.get_public_key()).decode('utf-8')}")
else:
RNS.log("Public Key : "+RNS.hexrep(identity.get_public_key(), delimit=False))
RNS.log(f"Public Key : {RNS.hexrep(identity.get_public_key(), delimit=False)}")
if identity.prv:
if args.print_private:
if args.base64:
RNS.log("Private Key : "+base64.urlsafe_b64encode(identity.get_private_key()).decode("utf-8"))
RNS.log(f"Private Key : {base64.urlsafe_b64encode(identity.get_private_key()).decode('utf-8')}")
elif args.base32:
RNS.log("Private Key : "+base64.b32encode(identity.get_private_key()).decode("utf-8"))
RNS.log(f"Private Key : {base64.b32encode(identity.get_private_key()).decode('utf-8')}")
else:
RNS.log("Private Key : "+RNS.hexrep(identity.get_private_key(), delimit=False))
RNS.log(f"Private Key : {RNS.hexrep(identity.get_private_key(), delimit=False)}")
else:
RNS.log("Private Key : Hidden")
@ -156,13 +156,13 @@ def main():
wp = os.path.expanduser(args.write)
if not os.path.isfile(wp) or args.force:
identity.to_file(wp)
RNS.log("Wrote imported identity to "+str(args.write))
RNS.log(f"Wrote imported identity to {args.write}")
else:
print("File "+str(wp)+" already exists, not overwriting")
print(f"File {wp} already exists, not overwriting")
exit(43)
except Exception as e:
print("Error while writing imported identity to file: "+str(e))
print(f"Error while writing imported identity to file: {e}")
exit(44)
exit(0)
@ -179,7 +179,7 @@ def main():
quietness = args.quiet
if verbosity != 0 or quietness != 0:
targetloglevel = targetloglevel+verbosity-quietness
# Start Reticulum
reticulum = RNS.Reticulum(configdir=args.config, loglevel=targetloglevel)
RNS.compact_log_fmt = True
@ -189,16 +189,16 @@ def main():
if args.generate:
identity = RNS.Identity()
if not args.force and os.path.isfile(args.generate):
RNS.log("Identity file "+str(args.generate)+" already exists. Not overwriting.", RNS.LOG_ERROR)
RNS.log(f"Identity file {args.generate} already exists. Not overwriting.", RNS.LOG_ERROR)
exit(3)
else:
try:
identity.to_file(args.generate)
RNS.log("New identity written to "+str(args.generate))
RNS.log(f"New identity written to {args.generate}")
exit(0)
except Exception as e:
RNS.log("An error ocurred while saving the generated Identity.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
exit(4)
identity = None
@ -210,31 +210,31 @@ def main():
if identity == None:
if not args.request:
RNS.log("Could not recall Identity for "+RNS.prettyhexrep(destination_hash)+".", RNS.LOG_ERROR)
RNS.log(f"Could not recall Identity for {RNS.prettyhexrep(destination_hash)}.", RNS.LOG_ERROR)
RNS.log("You can query the network for unknown Identities with the -R option.", RNS.LOG_ERROR)
exit(5)
else:
RNS.Transport.request_path(destination_hash)
def spincheck():
return RNS.Identity.recall(destination_hash) != None
spin(spincheck, "Requesting unknown Identity for "+RNS.prettyhexrep(destination_hash), args.t)
spin(spincheck, f"Requesting unknown Identity for {RNS.prettyhexrep(destination_hash)}", args.t)
if not spincheck():
RNS.log("Identity request timed out", RNS.LOG_ERROR)
exit(6)
else:
identity = RNS.Identity.recall(destination_hash)
RNS.log("Received Identity "+str(identity)+" for destination "+RNS.prettyhexrep(destination_hash)+" from the network")
RNS.log(f"Received Identity {identity} for destination {RNS.prettyhexrep(destination_hash)} from the network")
else:
RNS.log("Recalled Identity "+str(identity)+" for destination "+RNS.prettyhexrep(destination_hash))
RNS.log(f"Recalled Identity {identity} for destination {RNS.prettyhexrep(destination_hash)}")
except Exception as e:
RNS.log("Invalid hexadecimal hash provided", RNS.LOG_ERROR)
exit(7)
else:
# Try loading Identity from file
if not os.path.isfile(identity_str):
@ -243,7 +243,7 @@ def main():
else:
try:
identity = RNS.Identity.from_file(identity_str)
RNS.log("Loaded Identity "+str(identity)+" from "+str(identity_str))
RNS.log(f"Loaded Identity {identity} from {identity_str}")
except Exception as e:
RNS.log("Could not decode Identity from specified file")
@ -261,15 +261,15 @@ def main():
aspects = aspects[1:]
if identity.pub != None:
destination = RNS.Destination(identity, RNS.Destination.OUT, RNS.Destination.SINGLE, app_name, *aspects)
RNS.log("The "+str(args.hash)+" destination for this Identity is "+RNS.prettyhexrep(destination.hash))
RNS.log("The full destination specifier is "+str(destination))
RNS.log(f"The {args.hash} destination for this Identity is {RNS.prettyhexrep(destination.hash)}")
RNS.log(f"The full destination specifier is {destination}")
time.sleep(0.25)
exit(0)
else:
raise KeyError("No public key known")
except Exception as e:
RNS.log("An error ocurred while attempting to send the announce.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
exit(0)
@ -284,39 +284,39 @@ def main():
aspects = aspects[1:]
if identity.prv != None:
destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, app_name, *aspects)
RNS.log("Created destination "+str(destination))
RNS.log("Announcing destination "+RNS.prettyhexrep(destination.hash))
RNS.log(f"Created destination {destination}")
RNS.log(f"Announcing destination {RNS.prettyhexrep(destination.hash)}")
destination.announce()
time.sleep(0.25)
exit(0)
else:
destination = RNS.Destination(identity, RNS.Destination.OUT, RNS.Destination.SINGLE, app_name, *aspects)
RNS.log("The "+str(args.announce)+" destination for this Identity is "+RNS.prettyhexrep(destination.hash))
RNS.log("The full destination specifier is "+str(destination))
RNS.log(f"The {args.announce} destination for this Identity is {RNS.prettyhexrep(destination.hash)}")
RNS.log(f"The full destination specifier is {destination}")
RNS.log("Cannot announce this destination, since the private key is not held")
time.sleep(0.25)
exit(33)
except Exception as e:
RNS.log("An error ocurred while attempting to send the announce.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
exit(0)
if args.print_identity:
if args.base64:
RNS.log("Public Key : "+base64.urlsafe_b64encode(identity.get_public_key()).decode("utf-8"))
RNS.log(f"Public Key : {base64.urlsafe_b64encode(identity.get_public_key()).decode('utf-8')}")
elif args.base32:
RNS.log("Public Key : "+base64.b32encode(identity.get_public_key()).decode("utf-8"))
RNS.log(f"Public Key : {base64.b32encode(identity.get_public_key()).decode('utf-8')}")
else:
RNS.log("Public Key : "+RNS.hexrep(identity.get_public_key(), delimit=False))
RNS.log(f"Public Key : {RNS.hexrep(identity.get_public_key(), delimit=False)}")
if identity.prv:
if args.print_private:
if args.base64:
RNS.log("Private Key : "+base64.urlsafe_b64encode(identity.get_private_key()).decode("utf-8"))
RNS.log(f"Private Key : {base64.urlsafe_b64encode(identity.get_private_key()).decode('utf-8')}")
elif args.base32:
RNS.log("Private Key : "+base64.b32encode(identity.get_private_key()).decode("utf-8"))
RNS.log(f"Private Key : {base64.b32encode(identity.get_private_key()).decode('utf-8')}")
else:
RNS.log("Private Key : "+RNS.hexrep(identity.get_private_key(), delimit=False))
RNS.log(f"Private Key : {RNS.hexrep(identity.get_private_key(), delimit=False)}")
else:
RNS.log("Private Key : Hidden")
exit(0)
@ -324,11 +324,11 @@ def main():
if args.export:
if identity.prv:
if args.base64:
RNS.log("Exported Identity : "+base64.urlsafe_b64encode(identity.get_private_key()).decode("utf-8"))
RNS.log(f"Exported Identity : {base64.urlsafe_b64encode(identity.get_private_key()).decode('utf-8')}")
elif args.base32:
RNS.log("Exported Identity : "+base64.b32encode(identity.get_private_key()).decode("utf-8"))
RNS.log(f"Exported Identity : {base64.b32encode(identity.get_private_key()).decode('utf-8')}")
else:
RNS.log("Exported Identity : "+RNS.hexrep(identity.get_private_key(), delimit=False))
RNS.log(f"Exported Identity : {RNS.hexrep(identity.get_private_key(), delimit=False)}")
else:
RNS.log("Identity doesn't hold a private key, cannot export")
exit(50)
@ -336,28 +336,28 @@ def main():
exit(0)
if args.validate:
if not args.read and args.validate.lower().endswith("."+SIG_EXT):
args.read = str(args.validate).replace("."+SIG_EXT, "")
if not args.read and args.validate.lower().endswith(f".{SIG_EXT}"):
args.read = str(args.validate).replace(f".{SIG_EXT}", "")
if not os.path.isfile(args.validate):
RNS.log("Signature file "+str(args.read)+" not found", RNS.LOG_ERROR)
RNS.log(f"Signature file {args.read} not found", RNS.LOG_ERROR)
exit(10)
if not os.path.isfile(args.read):
RNS.log("Input file "+str(args.read)+" not found", RNS.LOG_ERROR)
RNS.log(f"Input file {args.read} not found", RNS.LOG_ERROR)
exit(11)
data_input = None
if args.read:
if not os.path.isfile(args.read):
RNS.log("Input file "+str(args.read)+" not found", RNS.LOG_ERROR)
RNS.log(f"Input file {args.read} not found", RNS.LOG_ERROR)
exit(12)
else:
try:
data_input = open(args.read, "rb")
except Exception as e:
RNS.log("Could not open input file for reading", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
exit(13)
# TODO: Actually expand this to a good solution
@ -368,30 +368,30 @@ def main():
data_output = None
if args.encrypt and not args.write and not args.stdout and args.read:
args.write = str(args.read)+"."+ENCRYPT_EXT
args.write = f"{args.read}.{ENCRYPT_EXT}"
if args.decrypt and not args.write and not args.stdout and args.read and args.read.lower().endswith("."+ENCRYPT_EXT):
args.write = str(args.read).replace("."+ENCRYPT_EXT, "")
if args.decrypt and not args.write and not args.stdout and args.read and args.read.lower().endswith(f".{ENCRYPT_EXT}"):
args.write = str(args.read).replace(f".{ENCRYPT_EXT}", "")
if args.sign and identity.prv == None:
RNS.log("Specified Identity does not hold a private key. Cannot sign.", RNS.LOG_ERROR)
exit(14)
if args.sign and not args.write and not args.stdout and args.read:
args.write = str(args.read)+"."+SIG_EXT
args.write = f"{args.read}.{SIG_EXT}"
if args.write:
if not args.force and os.path.isfile(args.write):
RNS.log("Output file "+str(args.write)+" already exists. Not overwriting.", RNS.LOG_ERROR)
RNS.log(f"Output file {args.write} already exists. Not overwriting.", RNS.LOG_ERROR)
exit(15)
else:
try:
data_output = open(args.write, "wb")
except Exception as e:
RNS.log("Could not open output file for writing", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
exit(15)
# TODO: Actually expand this to a good solution
# probably need to create a wrapper that takes
# into account not closing stdout when done
@ -414,22 +414,22 @@ def main():
exit(18)
if not args.stdout:
RNS.log("Signing "+str(args.read))
RNS.log(f"Signing {args.read}")
try:
data_output.write(identity.sign(data_input.read()))
data_output.close()
data_input.close()
if not args.stdout:
if args.read:
RNS.log("File "+str(args.read)+" signed with "+str(identity)+" to "+str(args.write))
RNS.log(f"File {args.read} signed with {identity} to {args.write}")
exit(0)
except Exception as e:
if not args.stdout:
RNS.log("An error ocurred while encrypting data.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
try:
data_output.close()
except:
@ -448,13 +448,13 @@ def main():
else:
# if not args.stdout:
# RNS.log("Verifying "+str(args.validate)+" for "+str(args.read))
try:
try:
sig_input = open(args.validate, "rb")
except Exception as e:
RNS.log("An error ocurred while opening "+str(args.validate)+".", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"An error ocurred while opening {args.validate}.", RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
exit(21)
@ -464,17 +464,17 @@ def main():
if not validated:
if not args.stdout:
RNS.log("Signature "+str(args.validate)+" for file "+str(args.read)+" is invalid", RNS.LOG_ERROR)
RNS.log(f"Signature {args.validate} for file {args.read} is invalid", RNS.LOG_ERROR)
exit(22)
else:
if not args.stdout:
RNS.log("Signature "+str(args.validate)+" for file "+str(args.read)+" made by Identity "+str(identity)+" is valid")
RNS.log(f"Signature {args.validate} for file {args.read} made by Identity {identity} is valid")
exit(0)
except Exception as e:
if not args.stdout:
RNS.log("An error ocurred while validating signature.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
try:
data_output.close()
except:
@ -497,8 +497,8 @@ def main():
exit(25)
if not args.stdout:
RNS.log("Encrypting "+str(args.read))
RNS.log(f"Encrypting {args.read}")
try:
more_data = True
while more_data:
@ -511,13 +511,13 @@ def main():
data_input.close()
if not args.stdout:
if args.read:
RNS.log("File "+str(args.read)+" encrypted for "+str(identity)+" to "+str(args.write))
RNS.log(f"File {args.read} encrypted for {identity} to {args.write}")
exit(0)
except Exception as e:
if not args.stdout:
RNS.log("An error ocurred while encrypting data.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
try:
data_output.close()
except:
@ -544,8 +544,8 @@ def main():
exit(29)
if not args.stdout:
RNS.log("Decrypting "+str(args.read)+"...")
RNS.log(f"Decrypting {args.read}...")
try:
more_data = True
while more_data:
@ -564,13 +564,13 @@ def main():
data_input.close()
if not args.stdout:
if args.read:
RNS.log("File "+str(args.read)+" decrypted with "+str(identity)+" to "+str(args.write))
RNS.log(f"File {args.read} decrypted with {identity} to {args.write}")
exit(0)
except Exception as e:
if not args.stdout:
RNS.log("An error ocurred while decrypting data.", RNS.LOG_ERROR)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
try:
data_output.close()
except:

View File

@ -48,8 +48,8 @@ def main():
parser.add_argument('-v', '--verbose', 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("--version", action="version", version="ir {version}".format(version=__version__))
parser.add_argument("--version", action="version", version=f"ir {__version__}")
args = parser.parse_args()
if args.exampleconfig:

File diff suppressed because one or more lines are too long

View File

@ -35,7 +35,7 @@ def connect_remote(destination_hash, auth_identity, timeout, no_output = False):
global remote_link, reticulum
if not RNS.Transport.has_path(destination_hash):
if not no_output:
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested", end=" ")
print(f"Path to {RNS.prettyhexrep(destination_hash)} requested", end=" ")
sys.stdout.flush()
RNS.Transport.request_path(destination_hash)
pr_time = time.time()
@ -88,7 +88,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(remote) != dest_len:
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
raise ValueError(f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try:
identity_hash = bytes.fromhex(remote)
remote_hash = RNS.Destination.hash_from_name_and_identity("rnstransport.remote.management", identity_hash)
@ -97,7 +97,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
identity = RNS.Identity.from_file(os.path.expanduser(management_identity))
if identity == None:
raise ValueError("Could not load management identity from "+str(management_identity))
raise ValueError(f"Could not load management identity from {management_identity}")
try:
connect_remote(remote_hash, identity, remote_timeout, no_output)
@ -118,7 +118,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len:
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
raise ValueError(f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try:
destination_hash = bytes.fromhex(destination_hexhash)
except Exception as e:
@ -166,7 +166,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
m_str = " "
else:
m_str = "s"
print(RNS.prettyhexrep(path["hash"])+" is "+str(path["hops"])+" hop"+m_str+" away via "+RNS.prettyhexrep(path["via"])+" on "+path["interface"]+" expires "+RNS.timestamp_str(path["expires"]))
print(f"{RNS.prettyhexrep(path['hash'])} is {path['hops']} hop{m_str} away via {RNS.prettyhexrep(path['via'])} on {path['interface']} expires {RNS.timestamp_str(path['expires'])}")
if destination_hash != None and displayed == 0:
print("No path known")
@ -178,7 +178,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len:
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
raise ValueError(f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try:
destination_hash = bytes.fromhex(destination_hexhash)
except Exception as e:
@ -235,28 +235,28 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
hour_rate = round(len(entry["timestamps"])/span_hours, 3)
if hour_rate-int(hour_rate) == 0:
hour_rate = int(hour_rate)
if entry["rate_violations"] > 0:
if entry["rate_violations"] == 1:
s_str = ""
else:
s_str = "s"
rv_str = ", "+str(entry["rate_violations"])+" active rate violation"+s_str
rv_str = f", {entry['rate_violations']} active rate violation{s_str}"
else:
rv_str = ""
if entry["blocked_until"] > time.time():
bli = time.time()-(int(entry["blocked_until"])-time.time())
bl_str = ", new announces allowed in "+pretty_date(int(bli))
bl_str = f", new announces allowed in {pretty_date(int(bli))}"
else:
bl_str = ""
print(RNS.prettyhexrep(entry["hash"])+" last heard "+last_str+" ago, "+str(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:
print("Error while processing entry for "+RNS.prettyhexrep(entry["hash"]))
print(f"Error while processing entry for {RNS.prettyhexrep(entry['hash'])}")
print(str(e))
if destination_hash != None and displayed == 0:
@ -272,7 +272,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
print("Dropping announce queues on all interfaces...")
reticulum.drop_announce_queues()
elif drop:
if remote_link:
if not no_output:
@ -283,7 +283,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len:
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
raise ValueError(f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try:
destination_hash = bytes.fromhex(destination_hexhash)
except Exception as e:
@ -293,9 +293,9 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
sys.exit(1)
if reticulum.drop_path(destination_hash):
print("Dropped path to "+RNS.prettyhexrep(destination_hash))
print(f"Dropped path to {RNS.prettyhexrep(destination_hash)}")
else:
print("Unable to drop path to "+RNS.prettyhexrep(destination_hash)+". Does it exist?")
print(f"Unable to drop path to {RNS.prettyhexrep(destination_hash)}. Does it exist?")
sys.exit(1)
elif drop_via:
@ -308,7 +308,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len:
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
raise ValueError(f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try:
destination_hash = bytes.fromhex(destination_hexhash)
except Exception as e:
@ -318,9 +318,9 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
sys.exit(1)
if reticulum.drop_all_via(destination_hash):
print("Dropped all paths via "+RNS.prettyhexrep(destination_hash))
print(f"Dropped all paths via {RNS.prettyhexrep(destination_hash)}")
else:
print("Unable to drop paths via "+RNS.prettyhexrep(destination_hash)+". Does the transport instance exist?")
print(f"Unable to drop paths via {RNS.prettyhexrep(destination_hash)}. Does the transport instance exist?")
sys.exit(1)
else:
@ -333,7 +333,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len:
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
raise ValueError(f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try:
destination_hash = bytes.fromhex(destination_hexhash)
except Exception as e:
@ -341,10 +341,10 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
except Exception as e:
print(str(e))
sys.exit(1)
if not RNS.Transport.has_path(destination_hash):
RNS.Transport.request_path(destination_hash)
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=" ")
print(f"Path to {RNS.prettyhexrep(destination_hash)} requested ", end=" ")
sys.stdout.flush()
i = 0
@ -352,7 +352,7 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
limit = time.time()+timeout
while not RNS.Transport.has_path(destination_hash) and time.time()<limit:
time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="")
print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush()
i = (i+1)%len(syms)
@ -371,12 +371,12 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
else:
ms = ""
print("\rPath found, destination "+RNS.prettyhexrep(destination_hash)+" is "+str(hops)+" hop"+ms+" away via "+next_hop+" on "+next_hop_interface)
print(f'\rPath found, destination {RNS.prettyhexrep(destination_hash)} is {hops} hop{ms} away via {next_hop} on {next_hop_interface}')
else:
print("\r \rPath not found")
sys.exit(1)
def main():
try:
@ -392,7 +392,7 @@ def main():
parser.add_argument(
"--version",
action="version",
version="rnpath {version}".format(version=__version__)
version=f"rnpath {__version__}"
)
parser.add_argument(
@ -479,7 +479,7 @@ def main():
help="timeout before giving up on remote queries",
default=RNS.Transport.PATH_REQUEST_TIMEOUT
)
parser.add_argument(
"-j",
"--json",
@ -497,7 +497,7 @@ def main():
)
parser.add_argument('-v', '--verbose', action='count', default=0)
args = parser.parse_args()
if args.config:
@ -547,26 +547,26 @@ def pretty_date(time=False):
return ''
if day_diff == 0:
if second_diff < 10:
return str(second_diff) + " seconds"
return f"{second_diff} seconds"
if second_diff < 60:
return str(second_diff) + " seconds"
return f"{second_diff} seconds"
if second_diff < 120:
return "1 minute"
if second_diff < 3600:
return str(int(second_diff / 60)) + " minutes"
return f"{int(second_diff / 60)} minutes"
if second_diff < 7200:
return "an hour"
if second_diff < 86400:
return str(int(second_diff / 3600)) + " hours"
return f"{int(second_diff / 3600)} hours"
if day_diff == 1:
return "1 day"
if day_diff < 7:
return str(day_diff) + " days"
return f"{day_diff} days"
if day_diff < 31:
return str(int(day_diff / 7)) + " weeks"
return f"{int(day_diff / 7)} weeks"
if day_diff < 365:
return str(int(day_diff / 30)) + " months"
return str(int(day_diff / 365)) + " years"
return f"{int(day_diff / 30)} months"
return f"{int(day_diff / 365)} years"
if __name__ == "__main__":
main()

View File

@ -38,7 +38,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
if full_name == None:
print("The full destination name including application name aspects must be specified for the destination")
exit()
try:
app_name, aspects = RNS.Destination.app_and_aspects_from_name(full_name)
@ -49,7 +49,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len:
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
raise ValueError(f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try:
destination_hash = bytes.fromhex(destination_hexhash)
except Exception as e:
@ -70,7 +70,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
if not RNS.Transport.has_path(destination_hash):
RNS.Transport.request_path(destination_hash)
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=" ")
print(f"Path to {RNS.prettyhexrep(destination_hash)} requested ", end=" ")
sys.stdout.flush()
_timeout = time.time() + (timeout or DEFAULT_TIMEOUT+reticulum.get_first_hop_timeout(destination_hash))
@ -78,7 +78,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
syms = "⢄⢂⢁⡁⡈⡐⡠"
while not RNS.Transport.has_path(destination_hash) and not time.time() > _timeout:
time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="")
print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush()
i = (i+1)%len(syms)
@ -107,7 +107,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
probe = RNS.Packet(request_destination, os.urandom(size))
probe.pack()
except OSError:
print("Error: Probe packet size of "+str(len(probe.raw))+" bytes exceed MTU of "+str(RNS.Reticulum.MTU)+" bytes")
print(f"Error: Probe packet size of {len(probe.raw)} bytes exceed MTU of {RNS.Reticulum.MTU} bytes")
exit(3)
receipt = probe.send()
@ -115,25 +115,25 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
if more_output:
nhd = reticulum.get_next_hop(destination_hash)
via_str = " via "+RNS.prettyhexrep(nhd) if nhd != None else ""
if_str = " on "+str(reticulum.get_next_hop_if_name(destination_hash)) if reticulum.get_next_hop_if_name(destination_hash) != "None" else ""
via_str = f" via {RNS.prettyhexrep(nhd)}" if nhd != None else ""
if_str = f" on {reticulum.get_next_hop_if_name(destination_hash)}" if reticulum.get_next_hop_if_name(destination_hash) != "None" else ""
more = via_str+if_str
else:
more = ""
print("\rSent probe "+str(sent)+" ("+str(size)+" bytes) to "+RNS.prettyhexrep(destination_hash)+more+" ", end=" ")
print(f'\rSent probe {sent} ({size} bytes) to {RNS.prettyhexrep(destination_hash)}{more} ', end=" ")
_timeout = time.time() + (timeout or DEFAULT_TIMEOUT+reticulum.get_first_hop_timeout(destination_hash))
i = 0
while receipt.status == RNS.PacketReceipt.SENT and not time.time() > _timeout:
time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="")
print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush()
i = (i+1)%len(syms)
if time.time() > _timeout:
print("\r \rProbe timed out")
else:
print("\b\b ")
sys.stdout.flush()
@ -149,10 +149,10 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
rtt = receipt.get_rtt()
if (rtt >= 1):
rtt = round(rtt, 3)
rttstring = str(rtt)+" seconds"
rttstring = f"{rtt} seconds"
else:
rtt = round(rtt*1000, 3)
rttstring = str(rtt)+" milliseconds"
rttstring = f"{rtt} milliseconds"
reception_stats = ""
if reticulum.is_connected_to_shared_instance:
@ -161,28 +161,24 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
reception_q = reticulum.get_packet_q(receipt.proof_packet.packet_hash)
if reception_rssi != None:
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]"
reception_stats += f" [RSSI {reception_rssi} dBm]"
if reception_snr != None:
reception_stats += " [SNR "+str(reception_snr)+" dB]"
reception_stats += f" [SNR {reception_snr} dB]"
if reception_q != None:
reception_stats += " [Link Quality "+str(reception_q)+"%]"
reception_stats += f" [Link Quality {reception_q}%]"
else:
if receipt.proof_packet != None:
if receipt.proof_packet.rssi != None:
reception_stats += " [RSSI "+str(receipt.proof_packet.rssi)+" dBm]"
reception_stats += f" [RSSI {receipt.proof_packet.rssi} dBm]"
if receipt.proof_packet.snr != None:
reception_stats += " [SNR "+str(receipt.proof_packet.snr)+" dB]"
reception_stats += f" [SNR {receipt.proof_packet.snr} dB]"
print(
"Valid reply from "+
RNS.prettyhexrep(receipt.destination.hash)+
"\nRound-trip time is "+rttstring+
" over "+str(hops)+" hop"+ms+
reception_stats+"\n"
f"Valid reply from {RNS.prettyhexrep(receipt.destination.hash)}\nRound-trip time is {rttstring} over {hops} hop{ms}{reception_stats}\n"
)
else:
@ -196,7 +192,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
exit(2)
else:
exit(0)
def main():
try:
@ -207,7 +203,7 @@ def main():
parser.add_argument("-n", "--probes", action="store", default=1, help="number of probes to send", type=int)
parser.add_argument("-t", "--timeout", metavar="seconds", action="store", default=None, help="timeout before giving up", type=float)
parser.add_argument("-w", "--wait", metavar="seconds", action="store", default=0, help="time between each probe", type=float)
parser.add_argument("--version", action="version", version="rnprobe {version}".format(version=__version__))
parser.add_argument("--version", action="version", version=f"rnprobe {__version__}")
parser.add_argument("full_name", nargs="?", default=None, help="full destination name in dotted notation", type=str)
parser.add_argument("destination_hash", nargs="?", default=None, help="hexadecimal hash of the destination", type=str)

View File

@ -40,9 +40,9 @@ def program_setup(configdir, verbosity = 0, quietness = 0, service = False):
reticulum = RNS.Reticulum(configdir=configdir, verbosity=targetverbosity, logdest=targetlogdest)
if reticulum.is_connected_to_shared_instance:
RNS.log("Started rnsd version {version} connected to another shared local instance, this is probably NOT what you want!".format(version=__version__), RNS.LOG_WARNING)
RNS.log(f"Started rnsd version {__version__} connected to another shared local instance, this is probably NOT what you want!", RNS.LOG_WARNING)
else:
RNS.log("Started rnsd version {version}".format(version=__version__), RNS.LOG_NOTICE)
RNS.log(f"Started rnsd version {__version__}", RNS.LOG_NOTICE)
while True:
time.sleep(1)
@ -55,8 +55,8 @@ def main():
parser.add_argument('-q', '--quiet', action='count', default=0)
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("--version", action="version", version="rnsd {version}".format(version=__version__))
parser.add_argument("--version", action="version", version=f"rnsd {__version__}")
args = parser.parse_args()
if args.exampleconfig:
@ -192,7 +192,7 @@ loglevel = 4
# The following example enables communication with other
# local Reticulum peers using UDP broadcasts.
[[UDP Interface]]
type = UDPInterface
enabled = no
@ -235,24 +235,24 @@ loglevel = 4
# This example demonstrates a TCP server interface.
# It will listen for incoming connections on the
# specified IP address and port number.
[[TCP Server Interface]]
type = TCPServerInterface
enabled = no
# This configuration will listen on all IP
# interfaces on port 4242
listen_ip = 0.0.0.0
listen_port = 4242
# Alternatively you can bind to a specific IP
# listen_ip = 10.0.0.88
# listen_port = 4242
# Or a specific network device
# device = eth0
# port = 4242
@ -300,7 +300,7 @@ loglevel = 4
# host device before connecting. BLE
# devices can be connected by name,
# BLE MAC address or by any available.
# Connect to specific device by name
# port = ble://RNode 3B87
@ -320,7 +320,7 @@ loglevel = 4
# Set TX power to 7 dBm (5 mW)
txpower = 7
# Select spreading factor 8. Valid
# Select spreading factor 8. Valid
# range is 7 through 12, with 7
# being the fastest and 12 having
# the longest range.
@ -349,8 +349,8 @@ loglevel = 4
# flow control can be useful. By default
# it is disabled.
flow_control = False
# An example KISS modem interface. Useful for running
# Reticulum over packet radio hardware.
@ -365,7 +365,7 @@ loglevel = 4
# Set the serial baud-rate and other
# configuration parameters.
speed = 115200
speed = 115200
databits = 8
parity = none
stopbits = 1
@ -413,7 +413,7 @@ loglevel = 4
# way, Reticulum will automatically encapsulate it's
# traffic in AX.25 and also identify your stations
# transmissions with your callsign and SSID.
#
#
# Only do this if you really need to! Reticulum doesn't
# need the AX.25 layer for anything, and it incurs extra
# overhead on every packet to encapsulate in AX.25.
@ -436,7 +436,7 @@ loglevel = 4
# Set the serial baud-rate and other
# configuration parameters.
speed = 115200
speed = 115200
databits = 8
parity = none
stopbits = 1

View File

@ -42,12 +42,12 @@ def size_str(num, suffix='B'):
for unit in units:
if abs(num) < 1000.0:
if unit == "":
return "%.0f %s%s" % (num, unit, suffix)
return f"{num:.0f} {unit}{suffix}"
else:
return "%.2f %s%s" % (num, unit, suffix)
return f"{num:.2f} {unit}{suffix}"
num /= 1000.0
return "%.2f%s%s" % (num, last_unit, suffix)
return f"{num:.2f}{last_unit}{suffix}"
request_result = None
request_concluded = False
@ -144,7 +144,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(remote) != dest_len:
raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
raise ValueError(f"Destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try:
identity_hash = bytes.fromhex(remote)
destination_hash = RNS.Destination.hash_from_name_and_identity("rnstransport.remote.management", identity_hash)
@ -161,7 +161,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
stats, link_count = remote_status
except Exception as e:
raise e
except Exception as e:
print(str(e))
exit(20)
@ -215,7 +215,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
if sorting == "held":
interfaces.sort(key=lambda i: i["held_announces"], reverse=not sort_reverse)
for ifstat in interfaces:
name = ifstat["name"]
@ -281,13 +281,13 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
if "ifac_netname" in ifstat and ifstat["ifac_netname"] != None:
print(" Network : {nn}".format(nn=ifstat["ifac_netname"]))
print(" Status : {ss}".format(ss=ss))
print(f" Status : {ss}")
if clients != None and clients_string != "":
print(" "+clients_string)
if not (name.startswith("Shared Instance[") or name.startswith("TCPInterface[Client") or name.startswith("LocalInterface[")):
print(" Mode : {mode}".format(mode=modestr))
print(f" Mode : {modestr}")
if "bitrate" in ifstat and ifstat["bitrate"] != None:
print(" Rate : {ss}".format(ss=speed_str(ifstat["bitrate"])))
@ -295,16 +295,17 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
if "battery_percent" in ifstat and ifstat["battery_percent"] != None:
try:
bpi = int(ifstat["battery_percent"])
print(" Battery : {bp}%".format(bp=bpi))
bss = ifstat["battery_state"]
print(f" Battery : {bpi}% ({bss})")
except:
pass
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"])))
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"])))
if "peers" in ifstat and ifstat["peers"] != None:
print(" Peers : {np} reachable".format(np=ifstat["peers"]))
@ -314,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:
sigstr = "<…"+RNS.hexrep(ifstat["ifac_signature"][-5:], delimit=False)+">"
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:
print(" I2P B32 : {ep}".format(ep=str(ifstat["i2p_b32"])))
@ -324,14 +325,14 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
print(" Queued : {np} announce".format(np=aqn))
else:
print(" Queued : {np} announces".format(np=aqn))
if astats and "held_announces" in ifstat and ifstat["held_announces"] != None and ifstat["held_announces"] > 0:
aqn = ifstat["held_announces"]
if aqn == 1:
print(" Held : {np} announce".format(np=aqn))
else:
print(" Held : {np} announces".format(np=aqn))
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(" {iaf}".format(iaf=RNS.prettyfrequency(ifstat["incoming_announce_frequency"])))
@ -357,7 +358,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
print(f"\n{lstr}")
print("")
else:
if not remote:
print("Could not get RNS status")
@ -378,7 +379,7 @@ def main():
help="show all interfaces",
default=False
)
parser.add_argument(
"-A",
"--announce-stats",
@ -386,7 +387,7 @@ def main():
help="show announce stats",
default=False
)
parser.add_argument(
"-l",
"--link-stats",
@ -394,7 +395,7 @@ def main():
help="show link stats",
default=False,
)
parser.add_argument(
"-s",
"--sort",
@ -403,7 +404,7 @@ def main():
default=None,
type=str
)
parser.add_argument(
"-r",
"--reverse",
@ -411,7 +412,7 @@ def main():
help="reverse sorting",
default=False,
)
parser.add_argument(
"-j",
"--json",
@ -450,7 +451,7 @@ def main():
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)
args = parser.parse_args()
if args.config:
@ -488,10 +489,10 @@ def speed_str(num, suffix='bps'):
for unit in units:
if abs(num) < 1000.0:
return "%3.2f %s%s" % (num, unit, suffix)
return f"{num:3.2f} {unit}{suffix}"
num /= 1000.0
return "%.2f %s%s" % (num, last_unit, suffix)
return f"{num:.2f} {last_unit}{suffix}"
if __name__ == "__main__":
main()

View File

@ -28,8 +28,8 @@ import argparse
import shlex
import time
import sys
import tty
import os
#import tty
from RNS._version import __version__
@ -42,10 +42,10 @@ allowed_identity_hashes = []
def prepare_identity(identity_path):
global identity
if identity_path == None:
identity_path = RNS.Reticulum.identitypath+"/"+APP_NAME
identity_path = f"{RNS.Reticulum.identitypath}/{APP_NAME}"
if os.path.isfile(identity_path):
identity = RNS.Identity.from_file(identity_path)
identity = RNS.Identity.from_file(identity_path)
if identity == None:
RNS.log("No valid saved identity found, creating new...", RNS.LOG_INFO)
@ -57,13 +57,13 @@ def listen(configdir, identitypath = None, verbosity = 0, quietness = 0, allowed
targetloglevel = 3+verbosity-quietness
reticulum = RNS.Reticulum(configdir=configdir, loglevel=targetloglevel)
prepare_identity(identitypath)
destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "execute")
if print_identity:
print("Identity : "+str(identity))
print("Listening on : "+RNS.prettyhexrep(destination.hash))
print(f"Identity : {identity}")
print(f"Listening on : {RNS.prettyhexrep(destination.hash)}")
exit(0)
if disable_auth:
@ -74,7 +74,7 @@ def listen(configdir, identitypath = None, verbosity = 0, quietness = 0, allowed
try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(a) != dest_len:
raise ValueError("Allowed destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
raise ValueError(f"Allowed destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try:
destination_hash = bytes.fromhex(a)
allowed_identity_hashes.append(destination_hash)
@ -103,27 +103,27 @@ def listen(configdir, identitypath = None, verbosity = 0, quietness = 0, allowed
allow = RNS.Destination.ALLOW_ALL,
)
RNS.log("rnx listening for commands on "+RNS.prettyhexrep(destination.hash))
RNS.log(f"rnx listening for commands on {RNS.prettyhexrep(destination.hash)}")
if not disable_announce:
destination.announce()
while True:
time.sleep(1)
def command_link_established(link):
link.set_remote_identified_callback(initiator_identified)
link.set_link_closed_callback(command_link_closed)
RNS.log("Command link "+str(link)+" established")
RNS.log(f"Command link {link} established")
def command_link_closed(link):
RNS.log("Command link "+str(link)+" closed")
RNS.log(f"Command link {link} closed")
def initiator_identified(link, identity):
global allow_all
RNS.log("Initiator of link "+str(link)+" identified as "+RNS.prettyhexrep(identity.hash))
RNS.log(f"Initiator of link {link} identified as {RNS.prettyhexrep(identity.hash)}")
if not allow_all and not identity.hash in allowed_identity_hashes:
RNS.log("Identity "+RNS.prettyhexrep(identity.hash)+" not allowed, tearing down link")
RNS.log(f"Identity {RNS.prettyhexrep(identity.hash)} not allowed, tearing down link")
link.teardown()
def execute_received_command(path, data, request_id, remote_identity, requested_at):
@ -134,9 +134,9 @@ def execute_received_command(path, data, request_id, remote_identity, requested_
stdin = data[4] # Data passed to stdin
if remote_identity != None:
RNS.log("Executing command ["+command+"] for "+RNS.prettyhexrep(remote_identity.hash))
RNS.log(f"Executing command [{command}] for {RNS.prettyhexrep(remote_identity.hash)}")
else:
RNS.log("Executing command ["+command+"] for unknown requestor")
RNS.log(f"Executing command [{command}] for unknown requestor")
result = [
False, # 0: Command was executed
@ -178,7 +178,7 @@ def execute_received_command(path, data, request_id, remote_identity, requested_
pass
if timeout != None and time.time() > result[6]+timeout:
RNS.log("Command ["+command+"] timed out and is being killed...")
RNS.log(f"Command [{command}] timed out and is being killed...")
process.terminate()
process.wait()
if process.poll() != None:
@ -219,9 +219,9 @@ def execute_received_command(path, data, request_id, remote_identity, requested_
return result
if remote_identity != None:
RNS.log("Delivering result of command ["+str(command)+"] to "+RNS.prettyhexrep(remote_identity.hash))
RNS.log(f"Delivering result of command [{command}] to {RNS.prettyhexrep(remote_identity.hash)}")
else:
RNS.log("Delivering result of command ["+str(command)+"] to unknown requestor")
RNS.log(f"Delivering result of command [{command}] to unknown requestor")
return result
@ -231,14 +231,14 @@ def spin(until=None, msg=None, timeout=None):
if timeout != None:
timeout = time.time()+timeout
print(msg+" ", end=" ")
print(f"{msg} ", end=" ")
while (timeout == None or time.time()<timeout) and not until():
time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="")
print(f"\b\b{syms[i]} ", end="")
sys.stdout.flush()
i = (i+1)%len(syms)
print("\r"+" "*len(msg)+" \r", end="")
print(f"\r{' ' * len(msg)} \r", end="")
if timeout != None and time.time() > timeout:
return False
@ -259,8 +259,8 @@ def spin_stat(until=None, timeout=None):
time.sleep(0.1)
prg = current_progress
percent = round(prg * 100.0, 1)
stat_str = str(percent)+"% - " + size_str(int(prg*response_transfer_size)) + " of " + size_str(response_transfer_size) + " - " +size_str(speed, "b")+"ps"
print("\r \rReceiving result "+syms[i]+" "+stat_str, end=" ")
stat_str = f"{percent}% - {size_str(int(prg * response_transfer_size))} of {size_str(response_transfer_size)} - {size_str(speed, 'b')}ps"
print(f'\r \rReceiving result {syms[i]} {stat_str}', end=" ")
sys.stdout.flush()
i = (i+1)%len(syms)
@ -303,7 +303,7 @@ def execute(configdir, identitypath = None, verbosity = 0, quietness = 0, detail
try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination) != dest_len:
raise ValueError("Allowed destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
raise ValueError(f"Allowed destination length is invalid, must be {dest_len} hexadecimal characters ({dest_len // 2} bytes).")
try:
destination_hash = bytes.fromhex(destination)
except Exception as e:
@ -321,7 +321,7 @@ def execute(configdir, identitypath = None, verbosity = 0, quietness = 0, detail
if not RNS.Transport.has_path(destination_hash):
RNS.Transport.request_path(destination_hash)
if not spin(until=lambda: RNS.Transport.has_path(destination_hash), msg="Path to "+RNS.prettyhexrep(destination_hash)+" requested", timeout=timeout):
if not spin(until=lambda: RNS.Transport.has_path(destination_hash), msg=f"Path to {RNS.prettyhexrep(destination_hash)} requested", timeout=timeout):
print("Path not found")
exit(242)
@ -338,9 +338,9 @@ 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:
link = RNS.Link(listener_destination)
link.did_identify = False
if not spin(until=lambda: link.status == RNS.Link.ACTIVE, msg="Establishing link with "+RNS.prettyhexrep(destination_hash), timeout=timeout):
print("Could not establish link with "+RNS.prettyhexrep(destination_hash))
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)}")
exit(243)
if not noid and not link.did_identify:
@ -443,7 +443,7 @@ def execute(configdir, identitypath = None, verbosity = 0, quietness = 0, detail
print("\n--- End of remote output, rnx done ---")
if started != None and concluded != None:
cmd_duration = round(concluded - started, 3)
print("Remote command execution took "+str(cmd_duration)+" seconds")
print(f"Remote command execution took {cmd_duration} seconds")
total_size = request_receipt.response_size
if request_receipt.request_size != None:
@ -453,27 +453,27 @@ def execute(configdir, identitypath = None, verbosity = 0, quietness = 0, detail
if transfer_duration == 1:
tdstr = " in 1 second"
elif transfer_duration < 10:
tdstr = " in "+str(transfer_duration)+" seconds"
tdstr = f" in {transfer_duration} seconds"
else:
tdstr = " in "+pretty_time(transfer_duration)
tdstr = f" in {pretty_time(transfer_duration)}"
spdstr = ", effective rate "+size_str(total_size/transfer_duration, "b")+"ps"
spdstr = f", effective rate {size_str(total_size / transfer_duration, 'b')}ps"
print("Transferred "+size_str(total_size)+tdstr+spdstr)
print(f"Transferred {size_str(total_size)}{tdstr}{spdstr}")
if outlen != None and stdout != None:
if len(stdout) < outlen:
tstr = ", "+str(len(stdout))+" bytes displayed"
tstr = f", {len(stdout)} bytes displayed"
else:
tstr = ""
print("Remote wrote "+str(outlen)+" bytes to stdout"+tstr)
print(f"Remote wrote {outlen} bytes to stdout{tstr}")
if errlen != None and stderr != None:
if len(stderr) < errlen:
tstr = ", "+str(len(stderr))+" bytes displayed"
tstr = f", {len(stderr)} bytes displayed"
else:
tstr = ""
print("Remote wrote "+str(errlen)+" bytes to stderr"+tstr)
print(f"Remote wrote {errlen} bytes to stderr{tstr}")
else:
if stdout != None and len(stdout) > 0:
@ -487,9 +487,9 @@ def execute(configdir, identitypath = None, verbosity = 0, quietness = 0, detail
sys.stderr.flush()
print("\nOutput truncated before being returned:")
if len(stdout) != 0 and len(stdout) < outlen:
print(" stdout truncated to "+str(len(stdout))+" bytes")
print(f" stdout truncated to {len(stdout)} bytes")
if len(stderr) != 0 and len(stderr) < errlen:
print(" stderr truncated to "+str(len(stderr))+" bytes")
print(f" stderr truncated to {len(stderr)} bytes")
else:
print("Remote could not execute command")
if interactive:
@ -547,8 +547,8 @@ def main():
parser.add_argument("--stdin", action='store', default=None, help="pass input to stdin", type=str)
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("--version", action="version", version="rnx {version}".format(version=__version__))
parser.add_argument("--version", action="version", version=f"rnx {__version__}")
args = parser.parse_args()
if args.listen or args.print_identity:
@ -593,15 +593,15 @@ def main():
while True:
try:
cstr = str(code) if code and code != 0 else ""
prompt = cstr+"> "
prompt = f"{cstr}> "
print(prompt,end="")
# cmdbuf = b""
# while True:
# ch = sys.stdin.read(1)
# cmdbuf += ch.encode("utf-8")
# print("\r"+prompt+cmdbuf.decode("utf-8"), end="")
# print("\r"+prompt+cmdbuf.decode("utf-8"), end="")
command = input()
if command.lower() == "exit" or command.lower() == "quit":
exit(0)
@ -661,12 +661,12 @@ def size_str(num, suffix='B'):
for unit in units:
if abs(num) < 1000.0:
if unit == "":
return "%.0f %s%s" % (num, unit, suffix)
return f"{num:.0f} {unit}{suffix}"
else:
return "%.2f %s%s" % (num, unit, suffix)
return f"{num:.2f} {unit}{suffix}"
num /= 1000.0
return "%.2f%s%s" % (num, last_unit, suffix)
return f"{num:.2f}{last_unit}{suffix}"
def pretty_time(time, verbose=False):
days = int(time // (24 * 3600))
@ -676,7 +676,7 @@ def pretty_time(time, verbose=False):
minutes = int(time // 60)
time %= 60
seconds = round(time, 2)
ss = "" if seconds == 1 else "s"
sm = "" if minutes == 1 else "s"
sh = "" if hours == 1 else "s"
@ -684,16 +684,16 @@ def pretty_time(time, verbose=False):
components = []
if days > 0:
components.append(str(days)+" day"+sd if verbose else str(days)+"d")
components.append(f"{days} day{sd}" if verbose else f"{days}d")
if hours > 0:
components.append(str(hours)+" hour"+sh if verbose else str(hours)+"h")
components.append(f"{hours} hour{sh}" if verbose else f"{hours}h")
if minutes > 0:
components.append(str(minutes)+" minute"+sm if verbose else str(minutes)+"m")
components.append(f"{minutes} minute{sm}" if verbose else f"{minutes}m")
if seconds > 0:
components.append(str(seconds)+" second"+ss if verbose else str(seconds)+"s")
components.append(f"{seconds} second{ss}" if verbose else f"{seconds}s")
i = 0
tstr = ""

View File

@ -90,7 +90,7 @@ def loglevelname(level):
return "Debug"
if (level == LOG_EXTREME):
return "Extra"
return "Unknown"
def version():
@ -124,7 +124,7 @@ def log(msg, level=3, _override_destination = False):
file = open(logfile, "a")
file.write(logstring+"\n")
file.close()
if os.path.getsize(logfile) > LOG_MAXSIZE:
prevfile = logfile+".1"
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("Dumping future log events to console!", LOG_CRITICAL)
log(msg, level)
def rand():
result = instance_random.random()
@ -147,7 +147,7 @@ def rand():
def trace_exception(e):
import traceback
exception_info = "".join(traceback.TracebackException.from_exception(e).format())
log(f"An unhandled {str(type(e))} exception occurred: {str(e)}", LOG_ERROR)
log(f"An unhandled {type(e)} exception occurred: {e}", LOG_ERROR)
log(exception_info, LOG_ERROR)
def hexrep(data, delimit=True):
@ -155,16 +155,16 @@ def hexrep(data, delimit=True):
iter(data)
except TypeError:
data = [data]
delimiter = ":"
if not delimit:
delimiter = ""
hexrep = delimiter.join("{:02x}".format(c) for c in data)
hexrep = delimiter.join(f"{c:02x}" for c in data)
return hexrep
def prettyhexrep(data):
delimiter = ""
hexrep = "<"+delimiter.join("{:02x}".format(c) for c in data)+">"
hexrep = "<"+delimiter.join(f"{c:02x}" for c in data)+">"
return hexrep
def prettyspeed(num, suffix="b"):
@ -182,12 +182,12 @@ def prettysize(num, suffix='B'):
for unit in units:
if abs(num) < 1000.0:
if unit == "":
return "%.0f %s%s" % (num, unit, suffix)
return f"{num:.0f} {unit}{suffix}"
else:
return "%.2f %s%s" % (num, unit, suffix)
return f"{num:.2f} {unit}{suffix}"
num /= 1000.0
return "%.2f%s%s" % (num, last_unit, suffix)
return f"{num:.2f}{last_unit}{suffix}"
def prettyfrequency(hz, suffix="Hz"):
num = hz*1e6
@ -196,10 +196,10 @@ def prettyfrequency(hz, suffix="Hz"):
for unit in units:
if abs(num) < 1000.0:
return "%.2f %s%s" % (num, unit, suffix)
return f"{num:.2f} {unit}{suffix}"
num /= 1000.0
return "%.2f%s%s" % (num, last_unit, suffix)
return f"{num:.2f}{last_unit}{suffix}"
def prettydistance(m, suffix="m"):
num = m*1e6
@ -212,10 +212,10 @@ def prettydistance(m, suffix="m"):
if unit == "c": divisor = 100
if abs(num) < divisor:
return "%.2f %s%s" % (num, unit, suffix)
return f"{num:.2f} {unit}{suffix}"
num /= divisor
return "%.2f %s%s" % (num, last_unit, suffix)
return f"{num:.2f} {last_unit}{suffix}"
def prettytime(time, verbose=False, compact=False):
days = int(time // (24 * 3600))
@ -228,7 +228,7 @@ def prettytime(time, verbose=False, compact=False):
seconds = int(time)
else:
seconds = round(time, 2)
ss = "" if seconds == 1 else "s"
sm = "" if minutes == 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):
time = time*1e6
seconds = int(time // 1e6); time %= 1e6
milliseconds = int(time // 1e3); time %= 1e3
@ -280,7 +280,7 @@ def prettyshorttime(time, verbose=False, compact=False):
microseconds = int(time)
else:
microseconds = round(time, 2)
ss = "" if seconds == 1 else "s"
sms = "" if milliseconds == 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():
from statistics import mean, median, stdev
results = {}
for tag in profiler_tags:
tag_captures = []
tag_entry = profiler_tags[tag]
for thread_ident in tag_entry["threads"]:
thread_entry = tag_entry["threads"][thread_ident]
thread_captures = thread_entry["captures"]
sample_count = len(thread_captures)
if sample_count > 2:
thread_results = {
"count": sample_count,

View File

@ -1 +1 @@
__version__ = "0.8.3"
__version__ = "0.8.4"

View File

@ -1,5 +1,5 @@
import os
import glob
modules = glob.glob(os.path.dirname(__file__)+"/*.py")
modules = glob.glob(f"{os.path.dirname(__file__)}/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
"""
A modern asynchronous library for building I2P applications.
A modern asynchronous library for building I2P applications.
"""
from .__version__ import (
@ -10,7 +10,7 @@ from .__version__ import (
from .sam import Destination, PrivateKey
from .aiosam import (
get_sam_socket, dest_lookup, new_destination,
get_sam_socket, dest_lookup, new_destination,
create_session, stream_connect, stream_accept,
Session, StreamConnection, StreamAcceptor
)

View File

@ -11,7 +11,7 @@ def parse_reply(data):
try:
msg = sam.Message(data.decode().strip())
logger.debug("SAM reply: "+str(msg))
logger.debug(f"SAM reply: {msg}")
except:
raise ConnectionAbortedError("Invalid SAM response")
@ -34,12 +34,12 @@ async def get_sam_socket(sam_address=sam.DEFAULT_ADDRESS, loop=None):
writer.close()
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):
"""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.
: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.
:param sam_address: (optional) SAM API address
: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,
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.
:param sam_address: (optional) SAM API address
@ -70,7 +70,7 @@ async def new_destination(sam_address=sam.DEFAULT_ADDRESS, loop=None,
writer.close()
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",
signature_type=sam.Destination.default_sig_type,
destination=None, options={}):
@ -80,16 +80,16 @@ async def create_session(session_name, sam_address=sam.DEFAULT_ADDRESS,
:param sam_address: (optional) SAM API address
:param loop: (optional) Event loop instance
: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
:param destination: (optional) Destination to use in this session. Can be
a base64 encoded string, :class:`Destination`
:param destination: (optional) Destination to use in this session. Can be
a base64 encoded string, :class:`Destination`
instance or None. TRANSIENT destination is used when it
is None.
:param options: (optional) A dict object with i2cp options
:return: A (reader, writer) pair
"""
logger.debug("Creating session {}".format(session_name))
logger.debug(f"Creating session {session_name}")
if destination:
if type(destination) == sam.Destination:
destination = destination
@ -101,7 +101,7 @@ async def create_session(session_name, sam_address=sam.DEFAULT_ADDRESS,
else:
dest_string = sam.TRANSIENT_DESTINATION
options = " ".join(["{}={}".format(k, v) for k, v in options.items()])
options = " ".join([f"{k}={v}" for k, v in options.items()])
reader, writer = await get_sam_socket(sam_address, loop)
writer.write(sam.session_create(
@ -111,15 +111,15 @@ async def create_session(session_name, sam_address=sam.DEFAULT_ADDRESS,
if reply.ok:
if not destination:
destination = sam.Destination(
reply["DESTINATION"], has_private_key=True)
reply["DESTINATION"], has_private_key=True)
logger.debug(destination.base32)
logger.debug("Session created {}".format(session_name))
logger.debug(f"Session created {session_name}")
return (reader, writer)
else:
writer.close()
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):
"""A coroutine used to connect to a remote I2P destination.
@ -129,7 +129,7 @@ async def stream_connect(session_name, destination,
:param loop: (optional) Event loop instance
:return: A (reader, writer) pair
"""
logger.debug("Connecting stream {}".format(session_name))
logger.debug(f"Connecting stream {session_name}")
if isinstance(destination, str) and not destination.endswith(".i2p"):
destination = sam.Destination(destination)
elif isinstance(destination, str):
@ -140,7 +140,7 @@ async def stream_connect(session_name, destination,
silent="false"))
reply = parse_reply(await reader.readline())
if reply.ok:
logger.debug("Stream connected {}".format(session_name))
logger.debug(f"Stream connected {session_name}")
return (reader, writer)
else:
writer.close()
@ -173,16 +173,16 @@ class Session:
:param sam_address: (optional) SAM API address
:param loop: (optional) Event loop instance
: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
:param destination: (optional) Destination to use in this session. Can be
a base64 encoded string, :class:`Destination`
:param destination: (optional) Destination to use in this session. Can be
a base64 encoded string, :class:`Destination`
instance or None. TRANSIENT destination is used when it
is None.
:param options: (optional) A dict object with i2cp options
: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",
signature_type=sam.Destination.default_sig_type,
destination=None, options={}):
@ -195,9 +195,9 @@ class Session:
self.options = options
async def __aenter__(self):
self.reader, self.writer = await create_session(self.session_name,
sam_address=self.sam_address, loop=self.loop, style=self.style,
signature_type=self.signature_type,
self.reader, self.writer = await create_session(self.session_name,
sam_address=self.sam_address, loop=self.loop, style=self.style,
signature_type=self.signature_type,
destination=self.destination, options=self.options)
return self
@ -214,7 +214,7 @@ class StreamConnection:
:param loop: (optional) Event loop instance
:return: :class:`StreamConnection` object
"""
def __init__(self, session_name, destination,
def __init__(self, session_name, destination,
sam_address=sam.DEFAULT_ADDRESS, loop=None):
self.session_name = session_name
self.sam_address = sam_address
@ -222,7 +222,7 @@ class StreamConnection:
self.destination = destination
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.read = self.reader.read
self.write = self.writer.write
@ -240,14 +240,14 @@ class StreamAcceptor:
:param loop: (optional) Event loop instance
: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):
self.session_name = session_name
self.sam_address = sam_address
self.loop = loop
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)
self.read = self.reader.read
self.write = self.writer.write

View File

@ -8,7 +8,7 @@ I2P_B64_CHARS = "-~"
def i2p_b64encode(x):
"""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):
"""Decode I2P destination"""
@ -23,7 +23,7 @@ TRANSIENT_DESTINATION = "TRANSIENT"
VALID_BASE32_ADDRESS = re.compile(r"^([a-zA-Z0-9]{52}).b32.i2p$")
VALID_BASE64_ADDRESS = re.compile(r"^([a-zA-Z0-9-~=]{516,528})$")
class Message(object):
class Message:
"""Parse SAM message to an object"""
def __init__(self, s):
self.opts = {}
@ -51,41 +51,37 @@ class Message(object):
# SAM request messages
def hello(min_version, max_version):
return "HELLO VERSION MIN={} MAX={}\n".format(min_version,
max_version).encode()
return f"HELLO VERSION MIN={min_version} MAX={max_version}\n".encode()
def session_create(style, session_id, destination, options=""):
return "SESSION CREATE STYLE={} ID={} DESTINATION={} {}\n".format(
style, session_id, destination, options).encode()
return f"SESSION CREATE STYLE={style} ID={session_id} DESTINATION={destination} {options}\n".encode()
def stream_connect(session_id, destination, silent="false"):
return "STREAM CONNECT ID={} DESTINATION={} SILENT={}\n".format(
session_id, destination, silent).encode()
return f"STREAM CONNECT ID={session_id} DESTINATION={destination} SILENT={silent}\n".encode()
def stream_accept(session_id, silent="false"):
return "STREAM ACCEPT ID={} SILENT={}\n".format(session_id, silent).encode()
return f"STREAM ACCEPT ID={session_id} SILENT={silent}\n".encode()
def stream_forward(session_id, port, options=""):
return "STREAM FORWARD ID={} PORT={} {}\n".format(
session_id, port, options).encode()
return f"STREAM FORWARD ID={session_id} PORT={port} {options}\n".encode()
def naming_lookup(name):
return "NAMING LOOKUP NAME={}\n".format(name).encode()
return f"NAMING LOOKUP NAME={name}\n".encode()
def dest_generate(signature_type):
return "DEST GENERATE SIGNATURE_TYPE={}\n".format(signature_type).encode()
return f"DEST GENERATE SIGNATURE_TYPE={signature_type}\n".encode()
class Destination(object):
class Destination:
"""I2P destination
https://geti2p.net/spec/common-structures#destination
:param data: (optional) Base64 encoded data or 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 data: (optional) Base64 encoded data or binary data
:param path: (optional) A path to a file with binary data
:param has_private_key: (optional) Does data have a private key?
"""
ECDSA_SHA256_P256 = 1
@ -101,12 +97,12 @@ class Destination(object):
def __init__(self, data=None, path=None, has_private_key=False):
#: Binary destination
self.data = bytes()
self.data = b''
#: Base64 encoded destination
self.base64 = ""
self.base64 = ""
#: :class:`RNS.vendor.i2plib.PrivateKey` instance or None
self.private_key = None
self.private_key = None
if path:
with open(path, "rb") as f: data = f.read()
@ -123,20 +119,20 @@ class Destination(object):
self.base64 = data if type(data) == str else i2p_b64encode(data)
def __repr__(self):
return "<Destination: {}>".format(self.base32)
return f"<Destination: {self.base32}>"
@property
def base32(self):
"""Base32 destination hash of this destination"""
desthash = sha256(self.data).digest()
return b32encode(desthash).decode()[:52].lower()
class PrivateKey(object):
class PrivateKey:
"""I2P private key
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):

View File

@ -2,7 +2,7 @@ import logging
import asyncio
import argparse
from . import sam
from . import sam
from . import aiosam
from . import utils
from .log import logger
@ -18,7 +18,7 @@ async def proxy_data(reader, writer):
break
writer.write(data)
except Exception as e:
logger.debug('proxy_data_task exception {}'.format(e))
logger.debug(f'proxy_data_task exception {e}')
finally:
try:
writer.close()
@ -26,12 +26,12 @@ async def proxy_data(reader, writer):
pass
logger.debug('close connection')
class I2PTunnel(object):
class I2PTunnel:
"""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)
: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`
instance or None. A new destination is created when it
is None.
@ -42,7 +42,7 @@ class I2PTunnel(object):
: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):
self.local_address = local_address
self.destination = destination
@ -57,7 +57,7 @@ class I2PTunnel(object):
sam_address=self.sam_address, loop=self.loop)
_, self.session_writer = await aiosam.create_session(
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)
def stop(self):
@ -68,11 +68,11 @@ class ClientTunnel(I2PTunnel):
"""Client tunnel, a subclass of tunnel.I2PTunnel
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.
:param remote_destination: Remote I2P destination, can be either .i2p
domain, .b32.i2p address, base64 destination or
:param remote_destination: Remote I2P destination, can be either .i2p
domain, .b32.i2p address, base64 destination or
:class:`Destination` instance
"""
@ -90,12 +90,12 @@ class ClientTunnel(I2PTunnel):
"""Handle local client connection"""
try:
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)
self.status["connect_tasks"].append(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)
asyncio.ensure_future(proxy_data(client_reader, remote_writer),
loop=self.loop)
@ -123,8 +123,8 @@ class ServerTunnel(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
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
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
destination.
"""
def __init__(self, *args, **kwargs):
@ -139,11 +139,10 @@ class ServerTunnel(I2PTunnel):
async def handle_client(incoming, client_reader, client_writer):
try:
# 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())
logger.debug("{} client connected: {}.b32.i2p".format(
self.session_name, remote_destination.base32))
logger.debug(f"{self.session_name} client connected: {remote_destination.base32}.b32.i2p")
except Exception as e:
self.status["exception"] = e
self.status["setup_failed"] = True
@ -152,7 +151,7 @@ class ServerTunnel(I2PTunnel):
try:
sc_task = asyncio.wait_for(
asyncio.open_connection(
host=self.local_address[0],
host=self.local_address[0],
port=self.local_address[1]),
timeout=5)
self.status["connect_tasks"].append(sc_task)
@ -173,7 +172,7 @@ class ServerTunnel(I2PTunnel):
try:
while True:
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)
incoming = await client_reader.read(BUFFER_SIZE)
asyncio.ensure_future(handle_client(
@ -193,13 +192,13 @@ if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('type', metavar="TYPE", choices=('server', '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)")
parser.add_argument('--debug', '-d', action='store_true',
help='Debugging')
parser.add_argument('--key', '-k', default='', metavar='PRIVATE_KEY',
help='Path to private key file')
parser.add_argument('--destination', '-D', default='',
parser.add_argument('--destination', '-D', default='',
metavar='DESTINATION', help='Remote destination')
args = parser.parse_args()
@ -217,10 +216,10 @@ if __name__ == '__main__':
local_address = utils.address_from_string(args.address)
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)
elif args.type == "server":
tunnel = ServerTunnel(local_address, loop=loop, destination=destination,
tunnel = ServerTunnel(local_address, loop=loop, destination=destination,
sam_address=SAM_ADDRESS)
asyncio.ensure_future(tunnel.run(), loop=loop)

View File

@ -38,5 +38,5 @@ def generate_session_id(length=6):
"""Generate random session id"""
rand = random.SystemRandom()
sid = [rand.choice(string.ascii_letters) for _ in range(length)]
return "reticulum-" + "".join(sid)
return f"reticulum-{''.join(sid)}"

View File

@ -28,6 +28,6 @@ if os.name == "nt":
elif os.name == "posix":
from RNS.vendor.ifaddr._posix import get_adapters
else:
raise RuntimeError("Unsupported Operating System: %s" % os.name)
raise RuntimeError(f"Unsupported Operating System: {os.name}")
__all__ = ['Adapter', 'IP', 'get_adapters']

View File

@ -79,7 +79,7 @@ def get_adapters(include_unconfigured: bool = False) -> Iterable[shared.Adapter]
prefixlen = shared.ipv6_prefixlength(ipaddress.IPv6Address(netmaskStr))
else:
assert netmask is not None, f'sockaddr_to_ip({addr[0].ifa_netmask}) returned None'
netmaskStr = str('0.0.0.0/' + netmask)
netmaskStr = str(f"0.0.0.0/{netmask}")
prefixlen = ipaddress.IPv4Network(netmaskStr).prefixlen
ip = shared.IP(ip_addr, prefixlen, name)
add_ip(name, ip)

View File

@ -26,7 +26,7 @@ import platform
from typing import List, Optional, Tuple, Union
class Adapter(object):
class Adapter:
"""
Represents a network interface device controller (NIC), such as a
network card. An adapter can have multiple IPs.
@ -58,9 +58,7 @@ class Adapter(object):
self.index = index
def __repr__(self) -> str:
return "Adapter(name={name}, nice_name={nice_name}, ips={ips}, index={index})".format(
name=repr(self.name), nice_name=repr(self.nice_name), ips=repr(self.ips), index=repr(self.index)
)
return f"Adapter(name={self.name!r}, nice_name={self.nice_name!r}, ips={self.ips!r}, index={self.index!r})"
# Type of an IPv4 address (a string in "xxx.xxx.xxx.xxx" format)
@ -70,7 +68,7 @@ _IPv4Address = str
_IPv6Address = Tuple[str, int, int]
class IP(object):
class IP:
"""
Represents an IP address of an adapter.
"""
@ -112,9 +110,7 @@ class IP(object):
return isinstance(self.ip, tuple)
def __repr__(self) -> str:
return "IP(ip={ip}, network_prefix={network_prefix}, nice_name={nice_name})".format(
ip=repr(self.ip), network_prefix=repr(self.network_prefix), nice_name=repr(self.nice_name)
)
return f"IP(ip={self.ip!r}, network_prefix={self.network_prefix!r}, nice_name={self.nice_name!r})"
if platform.system() == "Darwin" or "BSD" in platform.system():

View File

@ -40,7 +40,7 @@ def ifaddresses(ifname) -> dict:
for ip in a.ips:
t = {}
if ip.is_IPv4:
net = ipaddress.ip_network(str(ip.ip)+"/"+str(ip.network_prefix), strict=False)
net = ipaddress.ip_network(f"{ip.ip}/{ip.network_prefix}", strict=False)
t["addr"] = ip.ip
t["prefix"] = ip.network_prefix
t["broadcast"] = str(net.broadcast_address)

55
RNS/vendor/six.py vendored
View File

@ -20,7 +20,6 @@
"""Utilities for writing code that runs on Python 2 and 3"""
from __future__ import absolute_import
import functools
import itertools
@ -57,7 +56,7 @@ else:
MAXSIZE = int((1 << 31) - 1)
else:
# It's possible to have sizeof(long) != sizeof(Py_ssize_t).
class X(object):
class X:
def __len__(self):
return 1 << 31
@ -88,7 +87,7 @@ def _import_module(name):
return sys.modules[name]
class _LazyDescr(object):
class _LazyDescr:
def __init__(self, name):
self.name = name
@ -108,7 +107,7 @@ class _LazyDescr(object):
class MovedModule(_LazyDescr):
def __init__(self, name, old, new=None):
super(MovedModule, self).__init__(name)
super().__init__(name)
if PY3:
if new is None:
new = name
@ -129,7 +128,7 @@ class MovedModule(_LazyDescr):
class _LazyModule(types.ModuleType):
def __init__(self, name):
super(_LazyModule, self).__init__(name)
super().__init__(name)
self.__doc__ = self.__class__.__doc__
def __dir__(self):
@ -144,7 +143,7 @@ class _LazyModule(types.ModuleType):
class MovedAttribute(_LazyDescr):
def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
super(MovedAttribute, self).__init__(name)
super().__init__(name)
if PY3:
if new_mod is None:
new_mod = name
@ -166,7 +165,7 @@ class MovedAttribute(_LazyDescr):
return getattr(module, self.attr)
class _SixMetaPathImporter(object):
class _SixMetaPathImporter:
"""
A meta path importer to import six.moves and its submodules.
@ -181,10 +180,10 @@ class _SixMetaPathImporter(object):
def _add_module(self, mod, *fullnames):
for fullname in fullnames:
self.known_modules[self.name + "." + fullname] = mod
self.known_modules[f"{self.name}.{fullname}"] = mod
def _get_module(self, fullname):
return self.known_modules[self.name + "." + fullname]
return self.known_modules[f"{self.name}.{fullname}"]
def find_module(self, fullname, path=None):
if fullname in self.known_modules:
@ -200,7 +199,7 @@ class _SixMetaPathImporter(object):
try:
return self.known_modules[fullname]
except KeyError:
raise ImportError("This loader does not know module " + fullname)
raise ImportError(f"This loader does not know module {fullname}")
def load_module(self, fullname):
try:
@ -312,9 +311,9 @@ _moved_attributes = [
MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
"tkinter.simpledialog"),
MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
MovedModule("urllib_parse", f"{__name__}.moves.urllib_parse", "urllib.parse"),
MovedModule("urllib_error", f"{__name__}.moves.urllib_error", "urllib.error"),
MovedModule("urllib", f"{__name__}.moves.urllib", f"{__name__}.moves.urllib"),
MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"),
@ -328,12 +327,12 @@ if sys.platform == "win32":
for attr in _moved_attributes:
setattr(_MovedItems, attr.name, attr)
if isinstance(attr, MovedModule):
_importer._add_module(attr, "moves." + attr.name)
_importer._add_module(attr, f"moves.{attr.name}")
del attr
_MovedItems._moved_attributes = _moved_attributes
moves = _MovedItems(__name__ + ".moves")
moves = _MovedItems(f"{__name__}.moves")
_importer._add_module(moves, "moves")
@ -375,7 +374,7 @@ del attr
Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"),
_importer._add_module(Module_six_moves_urllib_parse(f"{__name__}.moves.urllib_parse"),
"moves.urllib_parse", "moves.urllib.parse")
@ -395,7 +394,7 @@ del attr
Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"),
_importer._add_module(Module_six_moves_urllib_error(f"{__name__}.moves.urllib.error"),
"moves.urllib_error", "moves.urllib.error")
@ -447,7 +446,7 @@ del attr
Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"),
_importer._add_module(Module_six_moves_urllib_request(f"{__name__}.moves.urllib.request"),
"moves.urllib_request", "moves.urllib.request")
@ -468,7 +467,7 @@ del attr
Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"),
_importer._add_module(Module_six_moves_urllib_response(f"{__name__}.moves.urllib.response"),
"moves.urllib_response", "moves.urllib.response")
@ -486,7 +485,7 @@ del attr
Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"),
_importer._add_module(Module_six_moves_urllib_robotparser(f"{__name__}.moves.urllib.robotparser"),
"moves.urllib_robotparser", "moves.urllib.robotparser")
@ -503,7 +502,7 @@ class Module_six_moves_urllib(types.ModuleType):
def __dir__(self):
return ['parse', 'error', 'request', 'response', 'robotparser']
_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"),
_importer._add_module(Module_six_moves_urllib(f"{__name__}.moves.urllib"),
"moves.urllib")
@ -520,7 +519,7 @@ def remove_move(name):
try:
del moves.__dict__[name]
except KeyError:
raise AttributeError("no such move, %r" % (name,))
raise AttributeError(f"no such move, {name!r}")
if PY3:
@ -576,7 +575,7 @@ else:
def create_unbound_method(func, cls):
return types.MethodType(func, None, cls)
class Iterator(object):
class Iterator:
def next(self):
return type(self).__next__(self)
@ -910,7 +909,7 @@ def ensure_binary(s, encoding='utf-8', errors='strict'):
return s
if isinstance(s, text_type):
return s.encode(encoding, errors)
raise TypeError("not expecting type '%s'" % type(s))
raise TypeError(f"not expecting type '{type(s)}'")
def ensure_str(s, encoding='utf-8', errors='strict'):
@ -932,7 +931,7 @@ def ensure_str(s, encoding='utf-8', errors='strict'):
elif PY3 and isinstance(s, binary_type):
return s.decode(encoding, errors)
elif not isinstance(s, (text_type, binary_type)):
raise TypeError("not expecting type '%s'" % type(s))
raise TypeError(f"not expecting type '{type(s)}'")
return s
@ -952,7 +951,7 @@ def ensure_text(s, encoding='utf-8', errors='strict'):
elif isinstance(s, text_type):
return s
else:
raise TypeError("not expecting type '%s'" % type(s))
raise TypeError(f"not expecting type '{type(s)}'")
def python_2_unicode_compatible(klass):
@ -965,9 +964,7 @@ def python_2_unicode_compatible(klass):
"""
if PY2:
if '__str__' not in klass.__dict__:
raise ValueError("@python_2_unicode_compatible cannot be applied "
"to %s because it doesn't define __str__()." %
klass.__name__)
raise ValueError(f"@python_2_unicode_compatible cannot be applied to {klass.__name__} because it doesn't define __str__().")
klass.__unicode__ = klass.__str__
klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
return klass

View File

@ -52,7 +52,7 @@ import io
if sys.version_info[0:2] >= (3, 3):
from collections.abc import Hashable
else:
from collections import Hashable
from collections.abc import Hashable
__version__ = "2.7.1"
"Module version string"
@ -66,7 +66,7 @@ version = (2, 7, 1)
##############################################################################
# Extension type for application-defined types and data
class Ext(object):
class Ext:
"""
The Ext class facilitates creating a serializable extension object to store
an application-defined type and data byte array.
@ -100,7 +100,7 @@ class Ext(object):
if not isinstance(type, int):
raise TypeError("ext type is not type integer")
elif not (-2**7 <= type <= 2**7 - 1):
raise ValueError("ext type value {:d} is out of range (-128 to 127)".format(type))
raise ValueError(f"ext type value {type} is out of range (-128 to 127)")
# Check data is type bytes or str
elif sys.version_info[0] == 3 and not isinstance(data, bytes):
raise TypeError("ext data is not type \'bytes\'")
@ -127,8 +127,8 @@ class Ext(object):
"""
String representation of this Ext object.
"""
s = "Ext Object (Type: {:d}, Data: ".format(self.type)
s += " ".join(["0x{:02}".format(ord(self.data[i:i + 1]))
s = f"Ext Object (Type: {self.type}, Data: "
s += " ".join([f"0x{ord(self.data[i:i + 1]):02}"
for i in xrange(min(len(self.data), 8))])
if len(self.data) > 8:
s += " ..."
@ -177,11 +177,11 @@ def ext_serializable(ext_type):
if not isinstance(ext_type, int):
raise TypeError("Ext type is not type integer")
elif not (-2**7 <= ext_type <= 2**7 - 1):
raise ValueError("Ext type value {:d} is out of range of -128 to 127".format(ext_type))
raise ValueError(f"Ext type value {ext_type} is out of range of -128 to 127")
elif ext_type in _ext_type_to_class:
raise ValueError("Ext type {:d} already registered with class {:s}".format(ext_type, repr(_ext_type_to_class[ext_type])))
raise ValueError(f"Ext type {ext_type} already registered with class {_ext_type_to_class[ext_type]!r}")
elif cls in _ext_class_to_type:
raise ValueError("Class {:s} already registered with Ext type {:d}".format(repr(cls), ext_type))
raise ValueError(f"Class {cls!r} already registered with Ext type {ext_type}")
_ext_type_to_class[ext_type] = cls
_ext_class_to_type[cls] = ext_type
@ -495,7 +495,7 @@ def _pack2(obj, fp, **options):
try:
_pack_ext(Ext(_ext_class_to_type[obj.__class__], obj.packb()), fp, options)
except AttributeError:
raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(obj.__class__)))
raise NotImplementedError(f"Ext serializable class {obj.__class__!r} is missing implementation of packb()")
elif isinstance(obj, bool):
_pack_boolean(obj, fp, options)
elif isinstance(obj, (int, long)):
@ -525,7 +525,7 @@ def _pack2(obj, fp, **options):
_pack_ext(ext_handlers[t](obj), fp, options)
else:
raise UnsupportedTypeException(
"unsupported type: {:s}".format(str(type(obj))))
f"unsupported type: {type(obj)}")
elif _ext_class_to_type:
# Linear search for superclass
t = next((t for t in _ext_class_to_type if isinstance(obj, t)), None)
@ -533,11 +533,11 @@ def _pack2(obj, fp, **options):
try:
_pack_ext(Ext(_ext_class_to_type[t], obj.packb()), fp, options)
except AttributeError:
raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(t)))
raise NotImplementedError(f"Ext serializable class {t!r} is missing implementation of packb()")
else:
raise UnsupportedTypeException("unsupported type: {:s}".format(str(type(obj))))
raise UnsupportedTypeException(f"unsupported type: {type(obj)}")
else:
raise UnsupportedTypeException("unsupported type: {:s}".format(str(type(obj))))
raise UnsupportedTypeException(f"unsupported type: {type(obj)}")
# Pack for Python 3, with unicode 'str' type, 'bytes' type, and no 'long' type
@ -582,7 +582,7 @@ def _pack3(obj, fp, **options):
try:
_pack_ext(Ext(_ext_class_to_type[obj.__class__], obj.packb()), fp, options)
except AttributeError:
raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(obj.__class__)))
raise NotImplementedError(f"Ext serializable class {obj.__class__!r} is missing implementation of packb()")
elif isinstance(obj, bool):
_pack_boolean(obj, fp, options)
elif isinstance(obj, int):
@ -612,7 +612,7 @@ def _pack3(obj, fp, **options):
_pack_ext(ext_handlers[t](obj), fp, options)
else:
raise UnsupportedTypeException(
"unsupported type: {:s}".format(str(type(obj))))
f"unsupported type: {type(obj)}")
elif _ext_class_to_type:
# Linear search for superclass
t = next((t for t in _ext_class_to_type if isinstance(obj, t)), None)
@ -620,12 +620,12 @@ def _pack3(obj, fp, **options):
try:
_pack_ext(Ext(_ext_class_to_type[t], obj.packb()), fp, options)
except AttributeError:
raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(t)))
raise NotImplementedError(f"Ext serializable class {t!r} is missing implementation of packb()")
else:
raise UnsupportedTypeException("unsupported type: {:s}".format(str(type(obj))))
raise UnsupportedTypeException(f"unsupported type: {type(obj)}")
else:
raise UnsupportedTypeException(
"unsupported type: {:s}".format(str(type(obj))))
f"unsupported type: {type(obj)}")
def _packb2(obj, **options):
@ -737,21 +737,21 @@ def _unpack_integer(code, fp, options):
return struct.unpack(">I", _read_except(fp, 4))[0]
elif code == b'\xcf':
return struct.unpack(">Q", _read_except(fp, 8))[0]
raise Exception("logic error, not int: 0x{:02x}".format(ord(code)))
raise Exception(f"logic error, not int: 0x{ord(code):02x}")
def _unpack_reserved(code, fp, options):
if code == b'\xc1':
raise ReservedCodeException(
"encountered reserved code: 0x{:02x}".format(ord(code)))
f"encountered reserved code: 0x{ord(code):02x}")
raise Exception(
"logic error, not reserved code: 0x{:02x}".format(ord(code)))
f"logic error, not reserved code: 0x{ord(code):02x}")
def _unpack_nil(code, fp, options):
if code == b'\xc0':
return None
raise Exception("logic error, not nil: 0x{:02x}".format(ord(code)))
raise Exception(f"logic error, not nil: 0x{ord(code):02x}")
def _unpack_boolean(code, fp, options):
@ -759,7 +759,7 @@ def _unpack_boolean(code, fp, options):
return False
elif code == b'\xc3':
return True
raise Exception("logic error, not boolean: 0x{:02x}".format(ord(code)))
raise Exception(f"logic error, not boolean: 0x{ord(code):02x}")
def _unpack_float(code, fp, options):
@ -767,7 +767,7 @@ def _unpack_float(code, fp, options):
return struct.unpack(">f", _read_except(fp, 4))[0]
elif code == b'\xcb':
return struct.unpack(">d", _read_except(fp, 8))[0]
raise Exception("logic error, not float: 0x{:02x}".format(ord(code)))
raise Exception(f"logic error, not float: 0x{ord(code):02x}")
def _unpack_string(code, fp, options):
@ -780,7 +780,7 @@ def _unpack_string(code, fp, options):
elif code == b'\xdb':
length = struct.unpack(">I", _read_except(fp, 4))[0]
else:
raise Exception("logic error, not string: 0x{:02x}".format(ord(code)))
raise Exception(f"logic error, not string: 0x{ord(code):02x}")
# Always return raw bytes in compatibility mode
global compatibility
@ -804,7 +804,7 @@ def _unpack_binary(code, fp, options):
elif code == b'\xc6':
length = struct.unpack(">I", _read_except(fp, 4))[0]
else:
raise Exception("logic error, not binary: 0x{:02x}".format(ord(code)))
raise Exception(f"logic error, not binary: 0x{ord(code):02x}")
return _read_except(fp, length)
@ -827,7 +827,7 @@ def _unpack_ext(code, fp, options):
elif code == b'\xc9':
length = struct.unpack(">I", _read_except(fp, 4))[0]
else:
raise Exception("logic error, not ext: 0x{:02x}".format(ord(code)))
raise Exception(f"logic error, not ext: 0x{ord(code):02x}")
ext_type = struct.unpack("b", _read_except(fp, 1))[0]
ext_data = _read_except(fp, length)
@ -842,7 +842,7 @@ def _unpack_ext(code, fp, options):
try:
return _ext_type_to_class[ext_type].unpackb(ext_data)
except AttributeError:
raise NotImplementedError("Ext serializable class {:s} is missing implementation of unpackb()".format(repr(_ext_type_to_class[ext_type])))
raise NotImplementedError(f"Ext serializable class {_ext_type_to_class[ext_type]!r} is missing implementation of unpackb()")
# Timestamp extension
if ext_type == -1:
@ -868,7 +868,7 @@ def _unpack_ext_timestamp(ext_data, options):
microseconds = struct.unpack(">I", ext_data[0:4])[0] // 1000
else:
raise UnsupportedTimestampException(
"unsupported timestamp with data length {:d}".format(len(ext_data)))
f"unsupported timestamp with data length {len(ext_data)}")
return _epoch + datetime.timedelta(seconds=seconds,
microseconds=microseconds)
@ -882,10 +882,10 @@ def _unpack_array(code, fp, options):
elif code == b'\xdd':
length = struct.unpack(">I", _read_except(fp, 4))[0]
else:
raise Exception("logic error, not array: 0x{:02x}".format(ord(code)))
raise Exception(f"logic error, not array: 0x{ord(code):02x}")
if options.get('use_tuple'):
return tuple((_unpack(fp, options) for i in xrange(length)))
return tuple(_unpack(fp, options) for i in xrange(length))
return [_unpack(fp, options) for i in xrange(length)]
@ -904,7 +904,7 @@ def _unpack_map(code, fp, options):
elif code == b'\xdf':
length = struct.unpack(">I", _read_except(fp, 4))[0]
else:
raise Exception("logic error, not map: 0x{:02x}".format(ord(code)))
raise Exception(f"logic error, not map: 0x{ord(code):02x}")
d = {} if not options.get('use_ordered_dict') else collections.OrderedDict()
for _ in xrange(length):
@ -916,10 +916,10 @@ def _unpack_map(code, fp, options):
k = _deep_list_to_tuple(k)
elif not isinstance(k, Hashable):
raise UnhashableKeyException(
"encountered unhashable key: \"{:s}\" ({:s})".format(str(k), str(type(k))))
f"encountered unhashable key: \"{k}\" ({type(k)})")
elif k in d:
raise DuplicateKeyException(
"encountered duplicate key: \"{:s}\" ({:s})".format(str(k), str(type(k))))
f"encountered duplicate key: \"{k}\" ({type(k)})")
# Unpack value
v = _unpack(fp, options)
@ -928,7 +928,7 @@ def _unpack_map(code, fp, options):
d[k] = v
except TypeError:
raise UnhashableKeyException(
"encountered unhashable key: \"{:s}\"".format(str(k)))
f"encountered unhashable key: \"{k}\"")
return d

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: bd0a676b0f490cda5d841ab7cd7cade1
config: ab3a817981de13f74ede3061942f75d2
tags: 645f666f9bcd5a90fca523b33c5a78b7

View File

@ -1,6 +1,6 @@
var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '0.8.2 beta',
VERSION: '0.8.4 beta',
LANGUAGE: 'en',
COLLAPSE_INDEX: false,
BUILDER: 'html',

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Support Reticulum" href="support.html" /><link rel="prev" title="Building Networks" href="networks.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Code Examples - Reticulum Network Stack 0.8.2 beta documentation</title>
<title>Code Examples - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 0.8.2 beta documentation</title>
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/><title>Index - Reticulum Network Stack 0.8.2 beta documentation</title>
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/><title>Index - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -139,7 +139,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@ -165,7 +165,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Using Reticulum on Your System" href="using.html" /><link rel="prev" title="What is Reticulum?" href="whatis.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Getting Started Fast - Reticulum Network Stack 0.8.2 beta documentation</title>
<title>Getting Started Fast - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Configuring Interfaces" href="interfaces.html" /><link rel="prev" title="Understanding Reticulum" href="understanding.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Communications Hardware - Reticulum Network Stack 0.8.2 beta documentation</title>
<title>Communications Hardware - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="What is Reticulum?" href="whatis.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Reticulum Network Stack 0.8.2 beta documentation</title>
<title>Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="#"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a>
<a href="#"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Building Networks" href="networks.html" /><link rel="prev" title="Communications Hardware" href="hardware.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Configuring Interfaces - Reticulum Network Stack 0.8.2 beta documentation</title>
<title>Configuring Interfaces - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Code Examples" href="examples.html" /><link rel="prev" title="Configuring Interfaces" href="interfaces.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Building Networks - Reticulum Network Stack 0.8.2 beta documentation</title>
<title>Building Networks - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

Binary file not shown.

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="prev" title="Support Reticulum" href="support.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>API Reference - Reticulum Network Stack 0.8.2 beta documentation</title>
<title>API Reference - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="#" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/><title>Search - Reticulum Network Stack 0.8.2 beta documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/><title>Search - Reticulum Network Stack 0.8.4 beta documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
@ -138,7 +138,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@ -164,7 +164,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="#" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="API Reference" href="reference.html" /><link rel="prev" title="Code Examples" href="examples.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Support Reticulum - Reticulum Network Stack 0.8.2 beta documentation</title>
<title>Support Reticulum - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Communications Hardware" href="hardware.html" /><link rel="prev" title="Using Reticulum on Your System" href="using.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Understanding Reticulum - Reticulum Network Stack 0.8.2 beta documentation</title>
<title>Understanding Reticulum - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Understanding Reticulum" href="understanding.html" /><link rel="prev" title="Getting Started Fast" href="gettingstartedfast.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>Using Reticulum on Your System - Reticulum Network Stack 0.8.2 beta documentation</title>
<title>Using Reticulum on Your System - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -6,7 +6,7 @@
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Getting Started Fast" href="gettingstartedfast.html" /><link rel="prev" title="Reticulum Network Stack Manual" href="index.html" />
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
<title>What is Reticulum? - Reticulum Network Stack 0.8.2 beta documentation</title>
<title>What is Reticulum? - Reticulum Network Stack 0.8.4 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
@ -141,7 +141,7 @@
</label>
</div>
<div class="header-center">
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.2 beta documentation</div></a>
<a href="index.html"><div class="brand">Reticulum Network Stack 0.8.4 beta documentation</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
@ -167,7 +167,7 @@
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
</div>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.2 beta documentation</span>
<span class="sidebar-brand-text">Reticulum Network Stack 0.8.4 beta documentation</span>
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">

View File

@ -13,12 +13,12 @@ project = 'Reticulum Network Stack'
copyright = '2023, Mark Qvist'
author = 'Mark Qvist'
exec(open("../../RNS/_version.py", "r").read())
exec(open("../../RNS/_version.py").read())
version = __version__
# The full version, including alpha/beta/rc tags
import RNS
release = RNS._version.__version__+" beta"
release = f"{RNS._version.__version__} beta"
# -- General configuration ---------------------------------------------------
extensions = [

View File

@ -9,14 +9,14 @@ if '--pure' in sys.argv:
sys.argv.remove('--pure')
print("Building pure-python wheel")
exec(open("RNS/_version.py", "r").read())
with open("README.md", "r") as fh:
exec(open("RNS/_version.py").read())
with open("README.md") as fh:
long_description = fh.read()
if pure_python:
pkg_name = "rnspure"
requirements = []
long_description = long_description.replace("</p>", "</p>"+pure_notice)
long_description = long_description.replace("</p>", f"</p>{pure_notice}")
else:
pkg_name = "rns"
requirements = ['cryptography>=3.4.7', 'pyserial>=3.5']

Some files were not shown because too many files have changed in this diff Show More