Compare commits

...

3 Commits

Author SHA1 Message Date
Pavol Rusnak
c6cc2616e8
Merge 011448c22d into 527f6cc906 2024-10-08 20:04:00 +00:00
Pavol Rusnak
011448c22d
remove dangling spaces 2024-10-08 22:03:30 +02:00
Pavol Rusnak
2a5a439921
modernize 2024-10-08 22:03:09 +02:00
84 changed files with 2181 additions and 2253 deletions

View File

@ -1214,7 +1214,7 @@ This beta release brings a range of improvements and bugfixes.
- Improved documentation. - Improved documentation.
- Improved request timeouts and handling. - Improved request timeouts and handling.
- Improved link establishment. - Improved link establishment.
- Improved resource transfer timing. - Improved resource transfer timing.
**Fixed bugs** **Fixed bugs**
- Fixed a race condition in inbound proof handling. - Fixed a race condition in inbound proof handling.

View File

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

View File

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

View File

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

View File

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

View File

@ -26,7 +26,7 @@ def server(configpath):
# We must first initialise Reticulum # We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath) reticulum = RNS.Reticulum(configpath)
# Randomly create a new identity for our echo server # Randomly create a new identity for our echo server
server_identity = RNS.Identity() server_identity = RNS.Identity()
@ -35,7 +35,7 @@ def server(configpath):
# create a "single" destination that can receive encrypted # create a "single" destination that can receive encrypted
# messages. This way the client can send a request and be # messages. This way the client can send a request and be
# certain that no-one else than this destination was able # certain that no-one else than this destination was able
# to read it. # to read it.
echo_destination = RNS.Destination( echo_destination = RNS.Destination(
server_identity, server_identity,
RNS.Destination.IN, RNS.Destination.IN,
@ -50,7 +50,7 @@ def server(configpath):
# generate a proof for each incoming packet and transmit it # generate a proof for each incoming packet and transmit it
# back to the sender of that packet. # back to the sender of that packet.
echo_destination.set_proof_strategy(RNS.Destination.PROVE_ALL) echo_destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
# Tell the destination which function in our program to # Tell the destination which function in our program to
# run when a packet is received. We do this so we can # run when a packet is received. We do this so we can
# print a log message when the server receives a request # print a log message when the server receives a request
@ -64,9 +64,7 @@ def server(configpath):
def announceLoop(destination): def announceLoop(destination):
# Let the user know that everything is ready # Let the user know that everything is ready
RNS.log( RNS.log(
"Echo server "+ f"Echo server {RNS.prettyhexrep(destination.hash)} running, hit enter to manually send an announce (Ctrl-C to quit)"
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. # We enter a loop that runs until the users exits.
@ -76,12 +74,12 @@ def announceLoop(destination):
while True: while True:
entered = input() entered = input()
destination.announce() 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): def server_callback(message, packet):
global reticulum global reticulum
# Tell the user that we received an echo request, and # Tell the user that we received an echo request, and
# that we are going to send a reply to the requester. # that we are going to send a reply to the requester.
# Sending the proof is handled automatically, since we # Sending the proof is handled automatically, since we
@ -93,19 +91,19 @@ def server_callback(message, packet):
reception_snr = reticulum.get_packet_snr(packet.packet_hash) reception_snr = reticulum.get_packet_snr(packet.packet_hash)
if reception_rssi != None: if reception_rssi != None:
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]" reception_stats += f" [RSSI {reception_rssi} dBm]"
if reception_snr != None: if reception_snr != None:
reception_stats += " [SNR "+str(reception_snr)+" dBm]" reception_stats += f" [SNR {reception_snr} dBm]"
else: else:
if packet.rssi != None: if packet.rssi != None:
reception_stats += " [RSSI "+str(packet.rssi)+" dBm]" reception_stats += f" [RSSI {packet.rssi} dBm]"
if packet.snr != None:
reception_stats += " [SNR "+str(packet.snr)+" dB]"
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 # to run as a client
def client(destination_hexhash, configpath, timeout=None): def client(destination_hexhash, configpath, timeout=None):
global reticulum global reticulum
# We need a binary representation of the destination # We need a binary representation of the destination
# hash that was entered on the command line # hash that was entered on the command line
try: try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len: if len(destination_hexhash) != dest_len:
raise ValueError( 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) destination_hash = bytes.fromhex(destination_hexhash)
except Exception as e: except Exception as e:
RNS.log("Invalid destination entered. Check your input!") RNS.log("Invalid destination entered. Check your input!")
RNS.log(str(e)+"\n") RNS.log(f"{e}\n")
exit() exit()
# We must first initialise Reticulum # We must first initialise Reticulum
@ -142,9 +140,7 @@ def client(destination_hexhash, configpath, timeout=None):
# Tell the user that the client is ready! # Tell the user that the client is ready!
RNS.log( RNS.log(
"Echo client ready, hit enter to send echo request to "+ f"Echo client ready, hit enter to send echo request to {destination_hexhash} (Ctrl-C to quit)"
destination_hexhash+
" (Ctrl-C to quit)"
) )
# We enter a loop that runs until the user exits. # We enter a loop that runs until the user exits.
@ -153,7 +149,7 @@ def client(destination_hexhash, configpath, timeout=None):
# command line. # command line.
while True: while True:
input() input()
# Let's first check if RNS knows a path to the destination. # Let's first check if RNS knows a path to the destination.
# If it does, we'll load the server identity and create a packet # If it does, we'll load the server identity and create a packet
if RNS.Transport.has_path(destination_hash): if RNS.Transport.has_path(destination_hash):
@ -205,7 +201,7 @@ def client(destination_hexhash, configpath, timeout=None):
packet_receipt.set_delivery_callback(packet_delivered) packet_receipt.set_delivery_callback(packet_delivered)
# Tell the user that the echo request was sent # 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: else:
# If we do not know this destination, tell the # If we do not know this destination, tell the
# user to wait for an announce to arrive. # user to wait for an announce to arrive.
@ -222,10 +218,10 @@ def packet_delivered(receipt):
rtt = receipt.get_rtt() rtt = receipt.get_rtt()
if (rtt >= 1): if (rtt >= 1):
rtt = round(rtt, 3) rtt = round(rtt, 3)
rttstring = str(rtt)+" seconds" rttstring = f"{rtt} seconds"
else: else:
rtt = round(rtt*1000, 3) rtt = round(rtt*1000, 3)
rttstring = str(rtt)+" milliseconds" rttstring = f"{rtt} milliseconds"
reception_stats = "" reception_stats = ""
if reticulum.is_connected_to_shared_instance: 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) reception_snr = reticulum.get_packet_snr(receipt.proof_packet.packet_hash)
if reception_rssi != None: if reception_rssi != None:
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]" reception_stats += f" [RSSI {reception_rssi} dBm]"
if reception_snr != None: if reception_snr != None:
reception_stats += " [SNR "+str(reception_snr)+" dB]" reception_stats += f" [SNR {reception_snr} dB]"
else: else:
if receipt.proof_packet != None: if receipt.proof_packet != None:
if receipt.proof_packet.rssi != 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: 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( RNS.log(
"Valid reply received from "+ f"Valid reply received from {RNS.prettyhexrep(receipt.destination.hash)}, round-trip time is {rttstring}{reception_stats}"
RNS.prettyhexrep(receipt.destination.hash)+
", round-trip time is "+rttstring+
reception_stats
) )
# This function is called if a packet times out. # This function is called if a packet times out.
def packet_timed_out(receipt): def packet_timed_out(receipt):
if receipt.status == RNS.PacketReceipt.FAILED: 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): def server(configpath, path):
# We must first initialise Reticulum # We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath) reticulum = RNS.Reticulum(configpath)
# Randomly create a new identity for our file server # Randomly create a new identity for our file server
server_identity = RNS.Identity() server_identity = RNS.Identity()
@ -73,7 +73,7 @@ def server(configpath, path):
def announceLoop(destination): def announceLoop(destination):
# Let the user know that everything is ready # 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)") RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
# We enter a loop that runs until the users exits. # We enter a loop that runs until the users exits.
@ -83,7 +83,7 @@ def announceLoop(destination):
while True: while True:
entered = input() entered = input()
destination.announce() 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 # Here's a convenience function for listing all files
# in our served directory # 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("Too many files in served directory!", RNS.LOG_ERROR)
RNS.log("You should implement a function to split the filelist over multiple packets.", RNS.LOG_ERROR) RNS.log("You should implement a function to split the filelist over multiple packets.", RNS.LOG_ERROR)
RNS.log("Hint: The client already supports it :)", RNS.LOG_ERROR) RNS.log("Hint: The client already supports it :)", RNS.LOG_ERROR)
# After this, we're just going to keep the link # After this, we're just going to keep the link
# open until the client requests a file. We'll # open until the client requests a file. We'll
# configure a function that get's called when # configure a function that get's called when
@ -145,9 +145,9 @@ def client_request(message, packet):
try: try:
# If we have the requested file, we'll # If we have the requested file, we'll
# read it and pack it as a resource # 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 = open(os.path.join(serve_path, filename), "rb")
file_resource = RNS.Resource( file_resource = RNS.Resource(
file, file,
packet.link, packet.link,
@ -158,7 +158,7 @@ def client_request(message, packet):
except Exception as e: except Exception as e:
# If somethign went wrong, we close # If somethign went wrong, we close
# the link # 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() packet.link.teardown()
raise e raise e
else: else:
@ -175,9 +175,9 @@ def resource_sending_concluded(resource):
name = "resource" name = "resource"
if resource.status == RNS.Resource.COMPLETE: 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: 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): def list_delivered(receipt):
RNS.log("The file list was received by the client") 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 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len: if len(destination_hexhash) != dest_len:
raise ValueError( 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) destination_hash = bytes.fromhex(destination_hexhash)
except: except:
RNS.log("Invalid destination entered. Check your input!\n") RNS.log("Invalid destination entered. Check your input!\n")
@ -291,9 +291,9 @@ def download(filename):
# packet receipt. # packet receipt.
request_packet = RNS.Packet(server_link, filename.encode("utf-8"), create_receipt=False) request_packet = RNS.Packet(server_link, filename.encode("utf-8"), create_receipt=False)
request_packet.send() request_packet.send()
print("") print("")
print(("Requested \""+filename+"\" from server, waiting for download to begin...")) print(f"Requested \"{filename}\" from server, waiting for download to begin...")
menu_mode = "download_started" menu_mode = "download_started"
# This function runs a simple menu for the user # This function runs a simple menu for the user
@ -363,7 +363,7 @@ def print_menu():
while menu_mode == "downloading": while menu_mode == "downloading":
global current_download global current_download
percent = round(current_download.get_progress() * 100.0, 1) percent = round(current_download.get_progress() * 100.0, 1)
print(("\rProgress: "+str(percent)+" % "), end=' ') print(f'\rProgress: {percent} % ', end=' ')
sys.stdout.flush() sys.stdout.flush()
time.sleep(0.1) time.sleep(0.1)
@ -383,15 +383,15 @@ def print_menu():
# Print statistics # Print statistics
hours, rem = divmod(download_time, 3600) hours, rem = divmod(download_time, 3600)
minutes, seconds = divmod(rem, 60) 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("") print("")
print("--- Statistics -----") print("--- Statistics -----")
print("\tTime taken : "+timestring) print(f"\tTime taken : {timestring}")
print("\tFile size : "+size_str(file_size)) print(f"\tFile size : {size_str(file_size)}")
print("\tData transferred : "+size_str(transfer_size)) print(f"\tData transferred : {size_str(transfer_size)}")
print("\tEffective rate : "+size_str(file_size/download_time, suffix='b')+"/s") print(f"\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"\tTransfer rate : {size_str(transfer_size / download_time, suffix='b')}/s")
print("") print("")
print("The download completed! Press enter to return to the menu.") print("The download completed! Press enter to return to the menu.")
print("") print("")
@ -412,7 +412,7 @@ def print_filelist():
global server_files global server_files
print("Files on server:") print("Files on server:")
for index,file in enumerate(server_files): 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): def filelist_received(filelist_data, packet):
global server_files, menu_mode global server_files, menu_mode
@ -474,7 +474,7 @@ def link_closed(link):
RNS.log("The link was closed by the server, exiting now") RNS.log("The link was closed by the server, exiting now")
else: else:
RNS.log("Link closed, exiting now") RNS.log("Link closed, exiting now")
RNS.Reticulum.exit_handler() RNS.Reticulum.exit_handler()
time.sleep(1.5) time.sleep(1.5)
os._exit(0) os._exit(0)
@ -486,17 +486,17 @@ def link_closed(link):
def download_began(resource): def download_began(resource):
global menu_mode, current_download, download_started, transfer_size, file_size global menu_mode, current_download, download_started, transfer_size, file_size
current_download = resource current_download = resource
if download_started == 0: if download_started == 0:
download_started = time.time() download_started = time.time()
transfer_size += resource.size transfer_size += resource.size
file_size = resource.total_size file_size = resource.total_size
menu_mode = "downloading" menu_mode = "downloading"
# When the download concludes, successfully # When the download concludes, successfully
# or not, we'll update our menu state and # or not, we'll update our menu state and
# inform the user about how it all went. # inform the user about how it all went.
def download_concluded(resource): def download_concluded(resource):
global menu_mode, current_filename, download_started, download_finished, download_time global menu_mode, current_filename, download_started, download_finished, download_time
@ -509,7 +509,7 @@ def download_concluded(resource):
counter = 0 counter = 0
while os.path.isfile(saved_filename): while os.path.isfile(saved_filename):
counter += 1 counter += 1
saved_filename = current_filename+"."+str(counter) saved_filename = f"{current_filename}.{counter}"
try: try:
file = open(saved_filename, "wb") file = open(saved_filename, "wb")
@ -534,9 +534,9 @@ def size_str(num, suffix='B'):
for unit in units: for unit in units:
if abs(num) < 1024.0: if abs(num) < 1024.0:
return "%3.2f %s%s" % (num, unit, suffix) return f"{num:3.2f} {unit}{suffix}"
num /= 1024.0 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 # A convenience function for clearing the screen
def clear_screen(): def clear_screen():

View File

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

View File

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

View File

@ -17,7 +17,7 @@ APP_NAME = "example_utilities"
def program_setup(configpath): def program_setup(configpath):
# We must first initialise Reticulum # We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath) reticulum = RNS.Reticulum(configpath)
# Randomly create a new identity for our example # Randomly create a new identity for our example
identity = RNS.Identity() identity = RNS.Identity()
@ -42,7 +42,7 @@ def program_setup(configpath):
# tries to communicate with the destination know whether their # tries to communicate with the destination know whether their
# communication was received correctly. # communication was received correctly.
destination.set_proof_strategy(RNS.Destination.PROVE_ALL) destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
# Everything's ready! # Everything's ready!
# Let's hand over control to the announce loop # Let's hand over control to the announce loop
announceLoop(destination) announceLoop(destination)
@ -51,9 +51,7 @@ def program_setup(configpath):
def announceLoop(destination): def announceLoop(destination):
# Let the user know that everything is ready # Let the user know that everything is ready
RNS.log( RNS.log(
"Minimal example "+ f"Minimal example {RNS.prettyhexrep(destination.hash)} running, hit enter to manually send an announce (Ctrl-C to quit)"
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. # We enter a loop that runs until the users exits.
@ -63,7 +61,7 @@ def announceLoop(destination):
while True: while True:
entered = input() entered = input()
destination.announce() 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 # TODO: Remove
RNS.loglevel = RNS.LOG_DEBUG RNS.loglevel = RNS.LOG_DEBUG
# Randomly create a new identity for our echo server # Randomly create a new identity for our echo server
server_identity = RNS.Identity() server_identity = RNS.Identity()
@ -37,7 +37,7 @@ def server(configpath):
# create a "single" destination that can receive encrypted # create a "single" destination that can receive encrypted
# messages. This way the client can send a request and be # messages. This way the client can send a request and be
# certain that no-one else than this destination was able # certain that no-one else than this destination was able
# to read it. # to read it.
echo_destination = RNS.Destination( echo_destination = RNS.Destination(
server_identity, server_identity,
RNS.Destination.IN, RNS.Destination.IN,
@ -61,7 +61,7 @@ def server(configpath):
# generate a proof for each incoming packet and transmit it # generate a proof for each incoming packet and transmit it
# back to the sender of that packet. # back to the sender of that packet.
echo_destination.set_proof_strategy(RNS.Destination.PROVE_ALL) echo_destination.set_proof_strategy(RNS.Destination.PROVE_ALL)
# Tell the destination which function in our program to # Tell the destination which function in our program to
# run when a packet is received. We do this so we can # run when a packet is received. We do this so we can
# print a log message when the server receives a request # print a log message when the server receives a request
@ -75,9 +75,7 @@ def server(configpath):
def announceLoop(destination): def announceLoop(destination):
# Let the user know that everything is ready # Let the user know that everything is ready
RNS.log( RNS.log(
"Ratcheted echo server "+ f"Ratcheted echo server {RNS.prettyhexrep(destination.hash)} running, hit enter to manually send an announce (Ctrl-C to quit)"
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. # We enter a loop that runs until the users exits.
@ -87,12 +85,12 @@ def announceLoop(destination):
while True: while True:
entered = input() entered = input()
destination.announce() 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): def server_callback(message, packet):
global reticulum global reticulum
# Tell the user that we received an echo request, and # Tell the user that we received an echo request, and
# that we are going to send a reply to the requester. # that we are going to send a reply to the requester.
# Sending the proof is handled automatically, since we # Sending the proof is handled automatically, since we
@ -104,19 +102,19 @@ def server_callback(message, packet):
reception_snr = reticulum.get_packet_snr(packet.packet_hash) reception_snr = reticulum.get_packet_snr(packet.packet_hash)
if reception_rssi != None: if reception_rssi != None:
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]" reception_stats += f" [RSSI {reception_rssi} dBm]"
if reception_snr != None: if reception_snr != None:
reception_stats += " [SNR "+str(reception_snr)+" dBm]" reception_stats += f" [SNR {reception_snr} dBm]"
else: else:
if packet.rssi != None: if packet.rssi != None:
reception_stats += " [RSSI "+str(packet.rssi)+" dBm]" reception_stats += f" [RSSI {packet.rssi} dBm]"
if packet.snr != None:
reception_stats += " [SNR "+str(packet.snr)+" dB]"
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 # to run as a client
def client(destination_hexhash, configpath, timeout=None): def client(destination_hexhash, configpath, timeout=None):
global reticulum global reticulum
# We need a binary representation of the destination # We need a binary representation of the destination
# hash that was entered on the command line # hash that was entered on the command line
try: try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len: if len(destination_hexhash) != dest_len:
raise ValueError( 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) destination_hash = bytes.fromhex(destination_hexhash)
except Exception as e: except Exception as e:
RNS.log("Invalid destination entered. Check your input!") RNS.log("Invalid destination entered. Check your input!")
RNS.log(str(e)+"\n") RNS.log(f"{e}\n")
exit() exit()
# We must first initialise Reticulum # We must first initialise Reticulum
@ -153,9 +151,7 @@ def client(destination_hexhash, configpath, timeout=None):
# Tell the user that the client is ready! # Tell the user that the client is ready!
RNS.log( RNS.log(
"Echo client ready, hit enter to send echo request to "+ f"Echo client ready, hit enter to send echo request to {destination_hexhash} (Ctrl-C to quit)"
destination_hexhash+
" (Ctrl-C to quit)"
) )
# We enter a loop that runs until the user exits. # We enter a loop that runs until the user exits.
@ -164,7 +160,7 @@ def client(destination_hexhash, configpath, timeout=None):
# command line. # command line.
while True: while True:
input() input()
# Let's first check if RNS knows a path to the destination. # Let's first check if RNS knows a path to the destination.
# If it does, we'll load the server identity and create a packet # If it does, we'll load the server identity and create a packet
if RNS.Transport.has_path(destination_hash): if RNS.Transport.has_path(destination_hash):
@ -217,7 +213,7 @@ def client(destination_hexhash, configpath, timeout=None):
packet_receipt.set_delivery_callback(packet_delivered) packet_receipt.set_delivery_callback(packet_delivered)
# Tell the user that the echo request was sent # 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: else:
# If we do not know this destination, tell the # If we do not know this destination, tell the
# user to wait for an announce to arrive. # user to wait for an announce to arrive.
@ -234,10 +230,10 @@ def packet_delivered(receipt):
rtt = receipt.get_rtt() rtt = receipt.get_rtt()
if (rtt >= 1): if (rtt >= 1):
rtt = round(rtt, 3) rtt = round(rtt, 3)
rttstring = str(rtt)+" seconds" rttstring = f"{rtt} seconds"
else: else:
rtt = round(rtt*1000, 3) rtt = round(rtt*1000, 3)
rttstring = str(rtt)+" milliseconds" rttstring = f"{rtt} milliseconds"
reception_stats = "" reception_stats = ""
if reticulum.is_connected_to_shared_instance: 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) reception_snr = reticulum.get_packet_snr(receipt.proof_packet.packet_hash)
if reception_rssi != None: if reception_rssi != None:
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]" reception_stats += f" [RSSI {reception_rssi} dBm]"
if reception_snr != None: if reception_snr != None:
reception_stats += " [SNR "+str(reception_snr)+" dB]" reception_stats += f" [SNR {reception_snr} dB]"
else: else:
if receipt.proof_packet != None: if receipt.proof_packet != None:
if receipt.proof_packet.rssi != 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: 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( RNS.log(
"Valid reply received from "+ f"Valid reply received from {RNS.prettyhexrep(receipt.destination.hash)}, round-trip time is {rttstring}{reception_stats}"
RNS.prettyhexrep(receipt.destination.hash)+
", round-trip time is "+rttstring+
reception_stats
) )
# This function is called if a packet times out. # This function is called if a packet times out.
def packet_timed_out(receipt): def packet_timed_out(receipt):
if receipt.status == RNS.PacketReceipt.FAILED: 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 latest_client_link = None
def random_text_generator(path, data, request_id, link_id, remote_identity, requested_at): 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"] 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)] 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): def server(configpath):
# We must first initialise Reticulum # We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath) reticulum = RNS.Reticulum(configpath)
# Randomly create a new identity for our link example # Randomly create a new identity for our link example
server_identity = RNS.Identity() server_identity = RNS.Identity()
@ -67,9 +67,7 @@ def server(configpath):
def server_loop(destination): def server_loop(destination):
# Let the user know that everything is ready # Let the user know that everything is ready
RNS.log( RNS.log(
"Request example "+ f"Request example {RNS.prettyhexrep(destination.hash)} running, waiting for a connection."
RNS.prettyhexrep(destination.hash)+
" running, waiting for a connection."
) )
RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)") RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
@ -81,7 +79,7 @@ def server_loop(destination):
while True: while True:
entered = input() entered = input()
destination.announce() 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 # When a client establishes a link to our server
# destination, this function will be called with # destination, this function will be called with
@ -113,9 +111,9 @@ def client(destination_hexhash, configpath):
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len: if len(destination_hexhash) != dest_len:
raise ValueError( 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) destination_hash = bytes.fromhex(destination_hexhash)
except: except:
RNS.log("Invalid destination entered. Check your input!\n") RNS.log("Invalid destination entered. Check your input!\n")
@ -187,7 +185,7 @@ def client_loop():
except Exception as e: 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 should_quit = True
server_link.teardown() server_link.teardown()
@ -195,13 +193,13 @@ def got_response(request_receipt):
request_id = request_receipt.request_id request_id = request_receipt.request_id
response = request_receipt.response 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): 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): 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 # 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") RNS.log("The link was closed by the server, exiting now")
else: else:
RNS.log("Link closed, exiting now") RNS.log("Link closed, exiting now")
RNS.Reticulum.exit_handler() RNS.Reticulum.exit_handler()
time.sleep(1.5) time.sleep(1.5)
os._exit(0) os._exit(0)

View File

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

View File

@ -109,8 +109,8 @@ network, and vice versa.
## How do I get started? ## How do I get started?
The best way to get started with the Reticulum Network Stack depends on what The best way to get started with the Reticulum Network Stack depends on what
you want to do. For full details and examples, have a look at the you want to do. For full details and examples, have a look at the
[Getting Started Fast](https://markqvist.github.io/Reticulum/manual/gettingstartedfast.html) [Getting Started Fast](https://markqvist.github.io/Reticulum/manual/gettingstartedfast.html)
section of the [Reticulum Manual](https://markqvist.github.io/Reticulum/manual/). section of the [Reticulum Manual](https://markqvist.github.io/Reticulum/manual/).
To simply install Reticulum and related utilities on your system, the easiest way is via `pip`. To simply install Reticulum and related utilities on your system, the easiest way is via `pip`.
@ -143,15 +143,15 @@ creating a more complex configuration.
If you have an old version of `pip` on your system, you may need to upgrade it first with `pip install pip --upgrade`. If you no not already have `pip` installed, you can install it using the package manager of your system with `sudo apt install python3-pip` or similar. If you have an old version of `pip` on your system, you may need to upgrade it first with `pip install pip --upgrade`. If you no not already have `pip` installed, you can install it using the package manager of your system with `sudo apt install python3-pip` or similar.
For more detailed examples on how to expand communication over many mediums such For more detailed examples on how to expand communication over many mediums such
as packet radio or LoRa, serial ports, or over fast IP links and the Internet using as packet radio or LoRa, serial ports, or over fast IP links and the Internet using
the UDP and TCP interfaces, take a look at the [Supported Interfaces](https://markqvist.github.io/Reticulum/manual/interfaces.html) the UDP and TCP interfaces, take a look at the [Supported Interfaces](https://markqvist.github.io/Reticulum/manual/interfaces.html)
section of the [Reticulum Manual](https://markqvist.github.io/Reticulum/manual/). section of the [Reticulum Manual](https://markqvist.github.io/Reticulum/manual/).
## Included Utilities ## Included Utilities
Reticulum includes a range of useful utilities for managing your networks, Reticulum includes a range of useful utilities for managing your networks,
viewing status and information, and other tasks. You can read more about these viewing status and information, and other tasks. You can read more about these
programs in the [Included Utility Programs](https://markqvist.github.io/Reticulum/manual/using.html#included-utility-programs) programs in the [Included Utility Programs](https://markqvist.github.io/Reticulum/manual/using.html#included-utility-programs)
section of the [Reticulum Manual](https://markqvist.github.io/Reticulum/manual/). section of the [Reticulum Manual](https://markqvist.github.io/Reticulum/manual/).
- The system daemon `rnsd` for running Reticulum as an always-available service - The system daemon `rnsd` for running Reticulum as an always-available service
@ -242,7 +242,7 @@ The testnet is just that, an informal network for testing and experimenting.
It will be up most of the time, and anyone can join, but it also means that It will be up most of the time, and anyone can join, but it also means that
there's no guarantees for service availability. there's no guarantees for service availability.
It probably goes without saying, but *don't use the testnet entry-points as It probably goes without saying, but *don't use the testnet entry-points as
hardcoded or default interfaces in any applications you ship to users*. When hardcoded or default interfaces in any applications you ship to users*. When
shipping applications, the best practice is to provide your own default shipping applications, the best practice is to provide your own default
connectivity solutions, if needed and applicable, or in most cases, simply connectivity solutions, if needed and applicable, or in most cases, simply

View File

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

View File

@ -136,7 +136,7 @@ class MessageBase(abc.ABC):
MSGTYPE = None MSGTYPE = None
""" """
Defines a unique identifier for a message class. Defines a unique identifier for a message class.
* Must be unique within all classes registered with a ``Channel`` * Must be unique within all classes registered with a ``Channel``
* Must be less than ``0xf000``. Values greater than or equal to ``0xf000`` are reserved. * Must be less than ``0xf000``. Values greater than or equal to ``0xf000`` are reserved.
""" """
@ -168,7 +168,7 @@ class Envelope:
Internal wrapper used to transport messages over a channel and Internal wrapper used to transport messages over a channel and
track its state within the channel framework. 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]) msgtype, self.sequence, length = struct.unpack(">HHH", self.raw[:6])
raw = self.raw[6:] raw = self.raw[6:]
ctor = message_factories.get(msgtype, None) ctor = message_factories.get(msgtype, None)
@ -247,11 +247,11 @@ class Channel(contextlib.AbstractContextManager):
# The maximum window size for transfers on fast links # The maximum window size for transfers on fast links
WINDOW_MAX_FAST = 48 WINDOW_MAX_FAST = 48
# For calculating maps and guard segments, this # For calculating maps and guard segments, this
# must be set to the global maximum window. # must be set to the global maximum window.
WINDOW_MAX = WINDOW_MAX_FAST WINDOW_MAX = WINDOW_MAX_FAST
# If the fast rate is sustained for this many request # If the fast rate is sustained for this many request
# rounds, the fast link window size will be allowed. # rounds, the fast link window size will be allowed.
FAST_RATE_THRESHOLD = 10 FAST_RATE_THRESHOLD = 10
@ -282,7 +282,7 @@ class Channel(contextlib.AbstractContextManager):
self._message_callbacks: [MessageCallbackType] = [] self._message_callbacks: [MessageCallbackType] = []
self._next_sequence = 0 self._next_sequence = 0
self._next_rx_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._max_tries = 5
self.fast_rate_rounds = 0 self.fast_rate_rounds = 0
self.medium_rate_rounds = 0 self.medium_rate_rounds = 0
@ -301,12 +301,12 @@ class Channel(contextlib.AbstractContextManager):
def __enter__(self) -> Channel: def __enter__(self) -> Channel:
return self 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: __traceback: TracebackType | None) -> bool | None:
self._shutdown() self._shutdown()
return False 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``. 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) 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: with self._lock:
if not issubclass(message_class, MessageBase): if not issubclass(message_class, MessageBase):
raise ChannelException(CEType.ME_INVALID_MSG_TYPE, 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: def _emplace_envelope(self, envelope: Envelope, ring: collections.deque[Envelope]) -> bool:
with self._lock: with self._lock:
i = 0 i = 0
for existing in ring: for existing in ring:
if envelope.sequence == existing.sequence: if envelope.sequence == existing.sequence:
RNS.log(f"Envelope: Emplacement of duplicate envelope with sequence "+str(envelope.sequence), RNS.LOG_EXTREME) RNS.log(f"Envelope: Emplacement of duplicate envelope with sequence {envelope.sequence}", RNS.LOG_EXTREME)
return False return False
if envelope.sequence < existing.sequence and not (self._next_rx_sequence - envelope.sequence) > (Channel.SEQ_MAX//2): if envelope.sequence < existing.sequence and not (self._next_rx_sequence - envelope.sequence) > (Channel.SEQ_MAX//2):
ring.insert(i, envelope) ring.insert(i, envelope)
envelope.tracked = True envelope.tracked = True
return True return True
i += 1 i += 1
envelope.tracked = True envelope.tracked = True
ring.append(envelope) ring.append(envelope)
@ -408,7 +408,7 @@ class Channel(contextlib.AbstractContextManager):
if cb(message): if cb(message):
return return
except Exception as e: 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): def _receive(self, raw: bytes):
try: try:
@ -420,16 +420,16 @@ class Channel(contextlib.AbstractContextManager):
window_overflow = (self._next_rx_sequence+Channel.WINDOW_MAX) % Channel.SEQ_MODULUS window_overflow = (self._next_rx_sequence+Channel.WINDOW_MAX) % Channel.SEQ_MODULUS
if window_overflow < self._next_rx_sequence: if window_overflow < self._next_rx_sequence:
if envelope.sequence > window_overflow: 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 return
else: 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 return
is_new = self._emplace_envelope(envelope, self._rx_ring) is_new = self._emplace_envelope(envelope, self._rx_ring)
if not is_new: 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 return
else: else:
with self._lock: with self._lock:
@ -449,12 +449,12 @@ class Channel(contextlib.AbstractContextManager):
m = e.unpack(self._message_factories) m = e.unpack(self._message_factories)
else: else:
m = e.message m = e.message
self._rx_ring.remove(e) self._rx_ring.remove(e)
self._run_callbacks(m) self._run_callbacks(m)
except Exception as e: 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: def is_ready_to_send(self) -> bool:
""" """
@ -468,7 +468,7 @@ class Channel(contextlib.AbstractContextManager):
with self._lock: with self._lock:
outstanding = 0 outstanding = 0
for envelope in self._tx_ring: for envelope in self._tx_ring:
if envelope.outlet == self._outlet: if envelope.outlet == self._outlet:
if not envelope.packet or not self._outlet.get_packet_state(envelope.packet) == MessageState.MSGSTATE_DELIVERED: if not envelope.packet or not self._outlet.get_packet_state(envelope.packet) == MessageState.MSGSTATE_DELIVERED:
outstanding += 1 outstanding += 1
@ -508,7 +508,7 @@ class Channel(contextlib.AbstractContextManager):
# TODO: Remove at some point # TODO: Remove at some point
# RNS.log("Increased "+str(self)+" max window to "+str(self.window_max), RNS.LOG_DEBUG) # RNS.log("Increased "+str(self)+" max window to "+str(self.window_max), RNS.LOG_DEBUG)
# RNS.log("Increased "+str(self)+" min window to "+str(self.window_min), RNS.LOG_DEBUG) # RNS.log("Increased "+str(self)+" min window to "+str(self.window_min), RNS.LOG_DEBUG)
else: else:
self.fast_rate_rounds += 1 self.fast_rate_rounds += 1
if self.window_max < Channel.WINDOW_MAX_FAST and self.fast_rate_rounds == Channel.FAST_RATE_THRESHOLD: if self.window_max < Channel.WINDOW_MAX_FAST and self.fast_rate_rounds == Channel.FAST_RATE_THRESHOLD:
@ -520,9 +520,9 @@ class Channel(contextlib.AbstractContextManager):
else: 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: 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): def _packet_delivered(self, packet: TPacket):
self._packet_tx_op(packet, lambda env: True) self._packet_tx_op(packet, lambda env: True)
@ -541,7 +541,7 @@ class Channel(contextlib.AbstractContextManager):
def _packet_timeout(self, packet: TPacket): def _packet_timeout(self, packet: TPacket):
def retry_envelope(envelope: Envelope) -> bool: def retry_envelope(envelope: Envelope) -> bool:
if envelope.tries >= self._max_tries: 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._shutdown() # start on separate thread?
self._outlet.timed_out() self._outlet.timed_out()
return True return True
@ -581,7 +581,7 @@ class Channel(contextlib.AbstractContextManager):
with self._lock: with self._lock:
if not self.is_ready_to_send(): if not self.is_ready_to_send():
raise ChannelException(CEType.ME_LINK_NOT_READY, f"Link is not ready") raise ChannelException(CEType.ME_LINK_NOT_READY, f"Link is not ready")
envelope = Envelope(self._outlet, message=message, sequence=self._next_sequence) envelope = Envelope(self._outlet, message=message, sequence=self._next_sequence)
self._next_sequence = (self._next_sequence + 1) % Channel.SEQ_MODULUS self._next_sequence = (self._next_sequence + 1) % Channel.SEQ_MODULUS
self._emplace_envelope(envelope, self._tx_ring) self._emplace_envelope(envelope, self._tx_ring)
@ -592,7 +592,7 @@ class Channel(contextlib.AbstractContextManager):
envelope.pack() envelope.pack()
if len(envelope.raw) > self._outlet.mdu: if len(envelope.raw) > self._outlet.mdu:
raise ChannelException(CEType.ME_TOO_BIG, f"Packed message too big for packet: {len(envelope.raw)} > {self._outlet.mdu}") raise ChannelException(CEType.ME_TOO_BIG, f"Packed message too big for packet: {len(envelope.raw)} > {self._outlet.mdu}")
envelope.packet = self._outlet.send(envelope.raw) envelope.packet = self._outlet.send(envelope.raw)
envelope.tries += 1 envelope.tries += 1
self._outlet.set_packet_delivered_callback(envelope.packet, self._packet_delivered) self._outlet.set_packet_delivered_callback(envelope.packet, self._packet_delivered)

View File

@ -25,7 +25,7 @@ import RNS.vendor.platformutils as pu
if cp.PROVIDER == cp.PROVIDER_INTERNAL: if cp.PROVIDER == cp.PROVIDER_INTERNAL:
from .aes import AES from .aes import AES
elif cp.PROVIDER == cp.PROVIDER_PYCA: elif cp.PROVIDER == cp.PROVIDER_PYCA:
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
@ -46,7 +46,7 @@ class AES_128_CBC:
cipher = Cipher(algorithms.AES(key), modes.CBC(iv)) cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
else: else:
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()) cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor() encryptor = cipher.encryptor()
ciphertext = encryptor.update(plaintext) + encryptor.finalize() ciphertext = encryptor.update(plaintext) + encryptor.finalize()
return ciphertext return ciphertext

View File

@ -48,15 +48,15 @@ class Fernet():
raise ValueError("Token key cannot be None") raise ValueError("Token key cannot be None")
if len(key) != 32: 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._signing_key = key[:16]
self._encryption_key = key[16:] self._encryption_key = key[16:]
def verify_hmac(self, token): def verify_hmac(self, token):
if len(token) <= 32: 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: else:
received_hmac = token[-32:] received_hmac = token[-32:]
expected_hmac = HMAC.new(self._signing_key, token[:-32]).digest() expected_hmac = HMAC.new(self._signing_key, token[:-32]).digest()

View File

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

View File

@ -35,6 +35,6 @@ class PKCS7:
l = len(data) l = len(data)
n = data[-1] n = data[-1]
if n > bs: 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: else:
return data[:l-n] return data[:l-n]

View File

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

View File

@ -25,7 +25,7 @@ import copy, struct, sys
def new(m=None): def new(m=None):
return sha512(m) return sha512(m)
class sha512(object): class sha512:
_k = (0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, _k = (0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
@ -60,7 +60,7 @@ class sha512(object):
if m is not None: if m is not None:
if type(m) is not bytes: 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) self.update(m)
def _rotr(self, x, y): def _rotr(self, x, y):
@ -100,7 +100,7 @@ class sha512(object):
if not m: if not m:
return return
if type(m) is not bytes: 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._buffer += m
self._counter += len(m) self._counter += len(m)

View File

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

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 Ed25519PrivateKeyProxy as Ed25519PrivateKey
from RNS.Cryptography.Proxies import Ed25519PublicKeyProxy as Ed25519PublicKey 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')] __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) return matrix2bytes(state)
# will encrypt the entire data # will encrypt the entire data
def encrypt(self, plaintext, iv): def encrypt(self, plaintext, iv):
""" """
Encrypts `plaintext` using CBC mode and PKCS#7 padding, with the given Encrypts `plaintext` using CBC mode and PKCS#7 padding, with the given
@ -173,7 +173,7 @@ class AES:
return b''.join(ciphertext_blocks) return b''.join(ciphertext_blocks)
# will decrypt the entire data # will decrypt the entire data
def decrypt(self, ciphertext, iv): def decrypt(self, ciphertext, iv):
""" """
Decrypts `ciphertext` using CBC mode and PKCS#7 padding, with the given Decrypts `ciphertext` using CBC mode and PKCS#7 padding, with the given
@ -188,7 +188,7 @@ class AES:
for ciphertext_block in split_blocks(ciphertext): for ciphertext_block in split_blocks(ciphertext):
# in CBC mode every block is XOR'd with the previous block # in CBC mode every block is XOR'd with the previous block
xorred = xor_bytes(previous, self._decrypt_block(ciphertext_block)) xorred = xor_bytes(previous, self._decrypt_block(ciphertext_block))
# append plaintext # append plaintext
plaintext_blocks.append(xorred) plaintext_blocks.append(xorred)
previous = ciphertext_block previous = ciphertext_block
@ -223,14 +223,14 @@ def test():
print("Single Block Tests") print("Single Block Tests")
print("------------------") print("------------------")
print(f"iv: {iv.hex()}") print(f"iv: {iv.hex()}")
print(f"plain text: '{single_block_text.decode()}'") print(f"plain text: '{single_block_text.decode()}'")
ciphertext_block = _aes._encrypt_block(single_block_text) ciphertext_block = _aes._encrypt_block(single_block_text)
plaintext_block = _aes._decrypt_block(ciphertext_block) plaintext_block = _aes._decrypt_block(ciphertext_block)
print(f"Ciphertext Hex: {ciphertext_block.hex()}") print(f"Ciphertext Hex: {ciphertext_block.hex()}")
print(f"Plaintext: {plaintext_block.decode()}") print(f"Plaintext: {plaintext_block.decode()}")
assert plaintext_block == single_block_text 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() print()
# test a less than a block length phrase # test a less than a block length phrase
@ -246,7 +246,7 @@ def test():
print(f"Ciphertext Hex: {ciphertext_short.hex()}") print(f"Ciphertext Hex: {ciphertext_short.hex()}")
print(f"Plaintext: {plaintext_short.decode()}") print(f"Plaintext: {plaintext_short.decode()}")
assert short_text == plaintext_short 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() print()
# test an arbitrary length phrase # test an arbitrary length phrase
@ -262,10 +262,10 @@ def test():
print(f"Ciphertext Hex: {ciphertext.hex()}") print(f"Ciphertext Hex: {ciphertext.hex()}")
print(f"Plaintext: {plaintext.decode()}") print(f"Plaintext: {plaintext.decode()}")
assert text == plaintext 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() print()
if __name__ == "__main__": if __name__ == "__main__":
# test AES class # test AES class
test() test()

View File

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

View File

@ -39,7 +39,7 @@ class BadPrefixError(Exception):
def remove_prefix(s_bytes, prefix): def remove_prefix(s_bytes, prefix):
assert(type(s_bytes) == type(prefix)) assert(type(s_bytes) == type(prefix))
if s_bytes[:len(prefix)] != 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):] return s_bytes[len(prefix):]
def to_ascii(s_bytes, prefix="", encoding="base64"): def to_ascii(s_bytes, prefix="", encoding="base64"):
@ -93,7 +93,7 @@ def from_ascii(s_ascii, prefix="", encoding="base64"):
raise NotImplementedError raise NotImplementedError
return s_bytes return s_bytes
class SigningKey(object): class SigningKey:
# this can only be used to reconstruct a key created by create_keypair(). # this can only be used to reconstruct a key created by create_keypair().
def __init__(self, sk_s, prefix="", encoding=None): def __init__(self, sk_s, prefix="", encoding=None):
assert isinstance(sk_s, bytes) assert isinstance(sk_s, bytes)
@ -150,7 +150,7 @@ class SigningKey(object):
return to_ascii(sig_out, prefix, encoding) return to_ascii(sig_out, prefix, encoding)
return prefix+sig_out return prefix+sig_out
class VerifyingKey(object): class VerifyingKey:
def __init__(self, vk_s, prefix="", encoding=None): def __init__(self, vk_s, prefix="", encoding=None):
if not isinstance(prefix, bytes): if not isinstance(prefix, bytes):
prefix = prefix.encode('ascii') 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 """Return just the signature, given the message and just the secret
key.""" key."""
if len(skbytes) != 32: 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) vkbytes = create_verifying_key(skbytes)
sig = signature(msg, skbytes, vkbytes) sig = signature(msg, skbytes, vkbytes)
return sig return sig
def verify(vkbytes, sig, msg): def verify(vkbytes, sig, msg):
if len(vkbytes) != 32: 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: 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) rc = checkvalid(sig, msg, vkbytes)
if not rc: if not rc:
raise ValueError("rc != 0", rc) raise ValueError("rc != 0", rc)

View File

@ -96,10 +96,10 @@ class Destination:
name = app_name name = app_name
for aspect in aspects: for aspect in aspects:
if "." in aspect: raise ValueError("Dots can't be used in aspects") if "." in aspect: raise ValueError("Dots can't be used in aspects")
name += "." + aspect name += f".{aspect}"
if identity != None: if identity != None:
name += "." + identity.hexhash name += f".{identity.hexhash}"
return name return name
@ -140,7 +140,7 @@ class Destination:
def __init__(self, identity, direction, type, app_name, *aspects): def __init__(self, identity, direction, type, app_name, *aspects):
# Check input values and build name string # Check input values and build name string
if "." in app_name: raise ValueError("Dots can't be used in app names") if "." in app_name: raise ValueError("Dots can't be used in app names")
if not type in Destination.types: raise ValueError("Unknown destination type") if not type in Destination.types: raise ValueError("Unknown destination type")
if not direction in Destination.directions: raise ValueError("Unknown destination direction") if not direction in Destination.directions: raise ValueError("Unknown destination direction")
@ -192,7 +192,7 @@ class Destination:
""" """
:returns: A human-readable representation of the destination including addressable hash and full name. :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): def _clean_ratchets(self):
if self.ratchets != None: if self.ratchets != None:
@ -210,13 +210,13 @@ class Destination:
except Exception as e: except Exception as e:
self.ratchets = None self.ratchets = None
self.ratchets_path = 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): def rotate_ratchets(self):
if self.ratchets != None: if self.ratchets != None:
now = time.time() now = time.time()
if now > self.latest_ratchet_time+self.ratchet_interval: 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() new_ratchet = RNS.Identity._generate_ratchet()
self.ratchets.insert(0, new_ratchet) self.ratchets.insert(0, new_ratchet)
self.latest_ratchet_time = now self.latest_ratchet_time = now
@ -224,7 +224,7 @@ class Destination:
self._persist_ratchets() self._persist_ratchets()
return True return True
else: 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 return False
@ -241,7 +241,7 @@ class Destination:
if self.direction != Destination.IN: if self.direction != Destination.IN:
raise TypeError("Only IN destination types can be announced") raise TypeError("Only IN destination types can be announced")
ratchet = b"" ratchet = b""
now = time.time() now = time.time()
stale_responses = [] stale_responses = []
@ -262,9 +262,9 @@ class Destination:
# received via multiple paths. The difference in reception time will # received via multiple paths. The difference in reception time will
# potentially also be useful in determining characteristics of the # potentially also be useful in determining characteristics of the
# multiple available paths, and to choose the best one. # 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] announce_data = self.path_responses[tag][1]
else: else:
destination_hash = self.hash destination_hash = self.hash
random_hash = RNS.Identity.get_random_hash()[0:5]+int(time.time()).to_bytes(5, "big") random_hash = RNS.Identity.get_random_hash()[0:5]+int(time.time()).to_bytes(5, "big")
@ -281,7 +281,7 @@ class Destination:
returned_app_data = self.default_app_data() returned_app_data = self.default_app_data()
if isinstance(returned_app_data, bytes): if isinstance(returned_app_data, bytes):
app_data = returned_app_data app_data = returned_app_data
signed_data = self.hash+self.identity.get_public_key()+self.name_hash+random_hash+ratchet signed_data = self.hash+self.identity.get_public_key()+self.name_hash+random_hash+ratchet
if app_data != None: if app_data != None:
signed_data += app_data signed_data += app_data
@ -413,7 +413,7 @@ class Destination:
try: try:
self.callbacks.packet(plaintext, packet) self.callbacks.packet(plaintext, packet)
except Exception as e: 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): def incoming_link_request(self, data, packet):
if self.accept_link_requests: if self.accept_link_requests:
@ -437,9 +437,9 @@ class Destination:
except Exception as e: except Exception as e:
self.ratchets = None self.ratchets = None
self.ratchets_path = 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: 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 = []
self.ratchets_path = ratchets_path self.ratchets_path = ratchets_path
self._persist_ratchets() self._persist_ratchets()
@ -464,11 +464,11 @@ class Destination:
self._reload_ratchets(ratchets_path) self._reload_ratchets(ratchets_path)
# TODO: Remove at some point # 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 return True
else: 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): def enforce_ratchets(self):
""" """
@ -478,7 +478,7 @@ class Destination:
""" """
if self.ratchets != None: if self.ratchets != None:
self.__enforce_ratchets = True 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 return True
else: else:
return False return False
@ -586,7 +586,7 @@ class Destination:
return self.prv.encrypt(plaintext) return self.prv.encrypt(plaintext)
except Exception as e: except Exception as e:
RNS.log("The GROUP destination could not encrypt data", RNS.LOG_ERROR) 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: else:
raise ValueError("No private key held by GROUP destination. Did you create or load one?") 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) return self.prv.decrypt(ciphertext)
except Exception as e: except Exception as e:
RNS.log("The GROUP destination could not decrypt data", RNS.LOG_ERROR) 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: else:
raise ValueError("No private key held by GROUP destination. Did you create or load one?") raise ValueError("No private key held by GROUP destination. Did you create or load one?")

View File

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

View File

@ -80,14 +80,14 @@ class AX25KISSInterface(Interface):
super().__init__() super().__init__()
self.HW_MTU = 564 self.HW_MTU = 564
self.pyserial = serial self.pyserial = serial
self.serial = None self.serial = None
self.owner = owner self.owner = owner
self.name = name self.name = name
self.src_call = callsign.upper().encode("ascii") self.src_call = callsign.upper().encode("ascii")
self.src_ssid = ssid self.src_ssid = ssid
self.dst_call = "APZRNS".encode("ascii") self.dst_call = b"APZRNS"
self.dst_ssid = 0 self.dst_ssid = 0
self.port = port self.port = port
self.speed = speed self.speed = speed
@ -105,10 +105,10 @@ class AX25KISSInterface(Interface):
self.flow_control_locked = time.time() self.flow_control_locked = time.time()
if (len(self.src_call) < 3 or len(self.src_call) > 6): 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): 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.preamble = preamble if preamble != None else 350;
self.txtail = txtail if txtail != None else 20; self.txtail = txtail if txtail != None else 20;
@ -124,16 +124,16 @@ class AX25KISSInterface(Interface):
try: try:
self.open_port() self.open_port()
except Exception as e: 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 raise e
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
else: else:
raise IOError("Could not open serial port") raise OSError("Could not open serial port")
def open_port(self): 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( self.serial = self.pyserial.Serial(
port = self.port, port = self.port,
baudrate = self.speed, baudrate = self.speed,
@ -155,7 +155,7 @@ class AX25KISSInterface(Interface):
thread.daemon = True thread.daemon = True
thread.start() thread.start()
self.online = True 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...") RNS.log("Configuring AX.25 KISS interface parameters...")
self.setPreamble(self.preamble) self.setPreamble(self.preamble)
self.setTxTail(self.txtail) 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]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXDELAY])+bytes([preamble])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setTxTail(self, txtail):
txtail_ms = 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]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXTAIL])+bytes([txtail])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setPersistence(self, persistence):
if persistence < 0: if persistence < 0:
@ -200,7 +200,7 @@ class AX25KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_P])+bytes([persistence])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_P])+bytes([persistence])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setSlotTime(self, slottime):
slottime_ms = 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]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SLOTTIME])+bytes([slottime])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setFlowControl(self, flow_control):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_READY])+bytes([0x01])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_READY])+bytes([0x01])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
if (flow_control): 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: 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): def processIncoming(self, data):
@ -270,7 +270,7 @@ class AX25KISSInterface(Interface):
if written != len(kiss_frame): if written != len(kiss_frame):
if self.flow_control: if self.flow_control:
self.interface_ready = True 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: else:
self.queue(data) self.queue(data)
@ -336,14 +336,14 @@ class AX25KISSInterface(Interface):
if self.flow_control: if self.flow_control:
if not self.interface_ready: if not self.interface_ready:
if time.time() > self.flow_control_locked + self.flow_control_timeout: 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() self.process_queue()
except Exception as e: except Exception as e:
self.online = False self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR) RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
@ -357,17 +357,17 @@ class AX25KISSInterface(Interface):
while not self.online: while not self.online:
try: try:
time.sleep(5) 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() self.open_port()
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
except Exception as e: 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): def should_ingress_limit(self):
return False return False
def __str__(self): 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 from usbserial4a import serial4a as serial
self.parity = "N" self.parity = "N"
else: else:
RNS.log("Could not load USB serial module for Android, KISS interface cannot be created.", RNS.LOG_CRITICAL) RNS.log("Could not load USB serial module for Android, KISS interface cannot be created.", RNS.LOG_CRITICAL)
RNS.log("You can install this module by issuing: pip install usbserial4a", RNS.LOG_CRITICAL) RNS.log("You can install this module by issuing: pip install usbserial4a", RNS.LOG_CRITICAL)
@ -83,9 +83,9 @@ class KISSInterface(Interface):
raise SystemError("Android-specific interface was used on non-Android OS") raise SystemError("Android-specific interface was used on non-Android OS")
super().__init__() super().__init__()
self.HW_MTU = 564 self.HW_MTU = 564
if beacon_data == None: if beacon_data == None:
beacon_data = "" beacon_data = ""
@ -125,17 +125,17 @@ class KISSInterface(Interface):
try: try:
self.open_port() self.open_port()
except Exception as e: 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 raise e
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
else: else:
raise IOError("Could not open serial port") raise OSError("Could not open serial port")
def open_port(self): def open_port(self):
RNS.log("Opening serial port "+self.port+"...") RNS.log(f"Opening serial port {self.port}...")
# Get device parameters # Get device parameters
from usb4a import usb from usb4a import usb
device = usb.get_usb_device(self.port) device = usb.get_usb_device(self.port)
@ -147,7 +147,7 @@ class KISSInterface(Interface):
proxy = self.pyserial.get_serial_port proxy = self.pyserial.get_serial_port
if vid == 0x1A86 and pid == 0x55D4: if vid == 0x1A86 and pid == 0x55D4:
# Force CDC driver for Qinheng CH34x # 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 from usbserial4a.cdcacmserial4a import CdcAcmSerial
proxy = CdcAcmSerial proxy = CdcAcmSerial
@ -172,7 +172,7 @@ class KISSInterface(Interface):
self.serial.timeout = 0.1 self.serial.timeout = 0.1
elif vid == 0x10C4: elif vid == 0x10C4:
# Hardware parameters for SiLabs CP210x @ 115200 baud # Hardware parameters for SiLabs CP210x @ 115200 baud
self.serial.DEFAULT_READ_BUFFER_SIZE = 64 self.serial.DEFAULT_READ_BUFFER_SIZE = 64
self.serial.USB_READ_TIMEOUT_MILLIS = 12 self.serial.USB_READ_TIMEOUT_MILLIS = 12
self.serial.timeout = 0.012 self.serial.timeout = 0.012
elif vid == 0x1A86 and pid == 0x55D4: elif vid == 0x1A86 and pid == 0x55D4:
@ -186,9 +186,9 @@ class KISSInterface(Interface):
self.serial.USB_READ_TIMEOUT_MILLIS = 100 self.serial.USB_READ_TIMEOUT_MILLIS = 100
self.serial.timeout = 0.1 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(f"{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(f"{self} USB read timeout set to {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 write timeout set to {self.serial.USB_WRITE_TIMEOUT_MILLIS}ms", RNS.LOG_DEBUG)
def configure_device(self): def configure_device(self):
# Allow time for interface to initialise before config # Allow time for interface to initialise before config
@ -197,7 +197,7 @@ class KISSInterface(Interface):
thread.daemon = True thread.daemon = True
thread.start() thread.start()
self.online = True 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...") RNS.log("Configuring KISS interface parameters...")
self.setPreamble(self.preamble) self.setPreamble(self.preamble)
self.setTxTail(self.txtail) 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]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXDELAY])+bytes([preamble])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setTxTail(self, txtail):
txtail_ms = 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]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXTAIL])+bytes([txtail])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setPersistence(self, persistence):
if persistence < 0: if persistence < 0:
@ -242,7 +242,7 @@ class KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_P])+bytes([persistence])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_P])+bytes([persistence])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setSlotTime(self, slottime):
slottime_ms = 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]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SLOTTIME])+bytes([slottime])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setFlowControl(self, flow_control):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_READY])+bytes([0x01])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_READY])+bytes([0x01])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
if (flow_control): if (flow_control):
raise IOError("Could not enable KISS interface flow control") raise OSError("Could not enable KISS interface flow control")
else: else:
raise IOError("Could not enable KISS interface flow control") raise OSError("Could not enable KISS interface flow control")
def processIncoming(self, data): def processIncoming(self, data):
@ -295,7 +295,7 @@ class KISSInterface(Interface):
self.first_tx = time.time() self.first_tx = time.time()
if written != len(frame): 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: else:
self.queue(data) self.queue(data)
@ -352,7 +352,7 @@ class KISSInterface(Interface):
data_buffer = data_buffer+bytes([byte]) data_buffer = data_buffer+bytes([byte])
elif (command == KISS.CMD_READY): elif (command == KISS.CMD_READY):
self.process_queue() self.process_queue()
if got == 0: if got == 0:
time_since_last = int(time.time()*1000) - last_read_ms time_since_last = int(time.time()*1000) - last_read_ms
if len(data_buffer) > 0 and time_since_last > self.timeout: if len(data_buffer) > 0 and time_since_last > self.timeout:
@ -365,21 +365,21 @@ class KISSInterface(Interface):
if self.flow_control: if self.flow_control:
if not self.interface_ready: if not self.interface_ready:
if time.time() > self.flow_control_locked + self.flow_control_timeout: 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() self.process_queue()
if self.beacon_i != None and self.beacon_d != None: if self.beacon_i != None and self.beacon_d != None:
if self.first_tx != None: if self.first_tx != None:
if time.time() > self.first_tx + self.beacon_i: 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.first_tx = None
self.processOutgoing(self.beacon_d) self.processOutgoing(self.beacon_d)
except Exception as e: except Exception as e:
self.online = False self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR) RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
@ -393,17 +393,17 @@ class KISSInterface(Interface):
while not self.online: while not self.online:
try: try:
time.sleep(5) 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() self.open_port()
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
except Exception as e: 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): def should_ingress_limit(self):
return False return False
def __str__(self): def __str__(self):
return "KISSInterface["+self.name+"]" return f"KISSInterface[{self.name}]"

View File

@ -42,7 +42,7 @@ class KISS():
FESC = 0xDB FESC = 0xDB
TFEND = 0xDC TFEND = 0xDC
TFESC = 0xDD TFESC = 0xDD
CMD_UNKNOWN = 0xFE CMD_UNKNOWN = 0xFE
CMD_DATA = 0x00 CMD_DATA = 0x00
CMD_FREQUENCY = 0x01 CMD_FREQUENCY = 0x01
@ -78,11 +78,11 @@ class KISS():
DETECT_REQ = 0x73 DETECT_REQ = 0x73
DETECT_RESP = 0x46 DETECT_RESP = 0x46
RADIO_STATE_OFF = 0x00 RADIO_STATE_OFF = 0x00
RADIO_STATE_ON = 0x01 RADIO_STATE_ON = 0x01
RADIO_STATE_ASK = 0xFF RADIO_STATE_ASK = 0xFF
CMD_ERROR = 0x90 CMD_ERROR = 0x90
ERROR_INITRADIO = 0x01 ERROR_INITRADIO = 0x01
ERROR_TXFAILED = 0x02 ERROR_TXFAILED = 0x02
@ -172,7 +172,7 @@ class AndroidBluetoothManager():
try: try:
self.rfcomm_socket = device.createRfcommSocketToServiceRecord(self.bt_rfcomm_service_record) self.rfcomm_socket = device.createRfcommSocketToServiceRecord(self.bt_rfcomm_service_record)
if self.rfcomm_socket == None: if self.rfcomm_socket == None:
raise IOError("Bluetooth stack returned no socket object") raise OSError("Bluetooth stack returned no socket object")
else: else:
if not self.rfcomm_socket.isConnected(): if not self.rfcomm_socket.isConnected():
try: try:
@ -181,20 +181,20 @@ class AndroidBluetoothManager():
self.rfcomm_writer = self.rfcomm_socket.getOutputStream() self.rfcomm_writer = self.rfcomm_socket.getOutputStream()
self.connected = True self.connected = True
self.connected_device = device 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: 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: 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(f"Could not create and connect Bluetooth RFcomm socket for {device.getName()} {device.getAddress()}", RNS.LOG_EXTREME)
RNS.log("The contained exception was: "+str(e), RNS.LOG_EXTREME) RNS.log(f"The contained exception was: {e}", RNS.LOG_EXTREME)
def close(self): def close(self):
if self.connected: if self.connected:
if self.rfcomm_reader != None: if self.rfcomm_reader != None:
self.rfcomm_reader.close() self.rfcomm_reader.close()
self.rfcomm_reader = None self.rfcomm_reader = None
if self.rfcomm_writer != None: if self.rfcomm_writer != None:
self.rfcomm_writer.close() self.rfcomm_writer.close()
self.rfcomm_writer = None self.rfcomm_writer = None
@ -209,7 +209,7 @@ class AndroidBluetoothManager():
def read(self, len = None): def read(self, len = None):
if self.connection_failed: if self.connection_failed:
raise IOError("Bluetooth connection failed") raise OSError("Bluetooth connection failed")
else: else:
if self.connected and self.rfcomm_reader != None: if self.connected and self.rfcomm_reader != None:
available = self.rfcomm_reader.available() available = self.rfcomm_reader.available()
@ -223,7 +223,7 @@ class AndroidBluetoothManager():
else: else:
return bytes([]) return bytes([])
else: else:
raise IOError("No RFcomm socket available") raise OSError("No RFcomm socket available")
def write(self, data): def write(self, data):
try: try:
@ -231,7 +231,7 @@ class AndroidBluetoothManager():
self.rfcomm_writer.flush() self.rfcomm_writer.flush()
return len(data) return len(data)
except Exception as e: 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 self.connection_failed = True
return 0 return 0
@ -274,7 +274,7 @@ class RNodeInterface(Interface):
bluetooth_state = 0x00 bluetooth_state = 0x00
if port != None: if port != None:
RNS.log("Opening serial port "+port+"...") RNS.log(f"Opening serial port {port}...")
# Get device parameters # Get device parameters
from usb4a import usb from usb4a import usb
device = usb.get_usb_device(port) device = usb.get_usb_device(port)
@ -287,7 +287,7 @@ class RNodeInterface(Interface):
proxy = pyserial.get_serial_port proxy = pyserial.get_serial_port
if vid == 0x1A86 and pid == 0x55D4: if vid == 0x1A86 and pid == 0x55D4:
# Force CDC driver for Qinheng CH34x # 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 from usbserial4a.cdcacmserial4a import CdcAcmSerial
proxy = CdcAcmSerial proxy = CdcAcmSerial
@ -371,7 +371,7 @@ class RNodeInterface(Interface):
else: else:
self.bt_manager = None self.bt_manager = None
else: else:
RNS.log("Could not load USB serial module for Android, RNode interface cannot be created.", RNS.LOG_CRITICAL) RNS.log("Could not load USB serial module for Android, RNode interface cannot be created.", RNS.LOG_CRITICAL)
RNS.log("You can install this module by issuing: pip install usbserial4a", RNS.LOG_CRITICAL) RNS.log("You can install this module by issuing: pip install usbserial4a", RNS.LOG_CRITICAL)
@ -382,7 +382,7 @@ class RNodeInterface(Interface):
super().__init__() super().__init__()
self.HW_MTU = 508 self.HW_MTU = 508
self.pyserial = serial self.pyserial = serial
self.serial = None self.serial = None
self.owner = owner self.owner = owner
@ -466,31 +466,31 @@ class RNodeInterface(Interface):
self.validcfg = True self.validcfg = True
if (self.frequency < RNodeInterface.FREQ_MIN or self.frequency > RNodeInterface.FREQ_MAX): 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 self.validcfg = False
if (self.txpower < 0 or self.txpower > 22): 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 self.validcfg = False
if (self.bandwidth < 7800 or self.bandwidth > 500000): 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 self.validcfg = False
if (self.sf < 5 or self.sf > 12): 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 self.validcfg = False
if (self.cr < 5 or self.cr > 8): 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 self.validcfg = False
if (self.st_alock and (self.st_alock < 0.0 or self.st_alock > 100.0)): 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 self.validcfg = False
if (self.lt_alock and (self.lt_alock < 0.0 or self.lt_alock > 100.0)): 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 self.validcfg = False
if id_interval != None and id_callsign != None: 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_callsign = id_callsign.encode("utf-8")
self.id_interval = id_interval self.id_interval = id_interval
else: 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 self.validcfg = False
else: else:
self.id_interval = None self.id_interval = None
self.id_callsign = None self.id_callsign = None
if (not self.validcfg): 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: try:
self.open_port() self.open_port()
@ -515,18 +515,18 @@ class RNodeInterface(Interface):
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
else: else:
raise IOError("Could not open serial port") raise OSError("Could not open serial port")
elif self.bt_manager != None: elif self.bt_manager != None:
if self.bt_manager.connected: if self.bt_manager.connected:
self.configure_device() self.configure_device()
else: else:
raise IOError("Could not connect to any Bluetooth devices") raise OSError("Could not connect to any Bluetooth devices")
else: else:
raise IOError("Neither serial port nor Bluetooth devices available") raise OSError("Neither serial port nor Bluetooth devices available")
except Exception as e: 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)
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
if len(self.hw_errors) == 0: if len(self.hw_errors) == 0:
RNS.log("Reticulum will attempt to bring up this interface periodically", RNS.LOG_ERROR) RNS.log("Reticulum will attempt to bring up this interface periodically", RNS.LOG_ERROR)
thread = threading.Thread(target=self.reconnect_port, daemon=True).start() thread = threading.Thread(target=self.reconnect_port, daemon=True).start()
@ -538,7 +538,7 @@ class RNodeInterface(Interface):
elif self.bt_manager != None: elif self.bt_manager != None:
return self.bt_manager.read() return self.bt_manager.read()
else: else:
raise IOError("No ports available for reading") raise OSError("No ports available for reading")
def write_mux(self, data): def write_mux(self, data):
if self.serial != None: if self.serial != None:
@ -551,7 +551,7 @@ class RNodeInterface(Interface):
self.last_port_io = time.time() self.last_port_io = time.time()
return written return written
else: else:
raise IOError("No ports available for writing") raise OSError("No ports available for writing")
# def reset_ble(self): # def reset_ble(self):
# RNS.log(f"Clearing previous connection instance: "+str(self.ble)) # 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.ble = BLEConnection(owner=self, target_name=self.ble_name, target_bt_addr=self.ble_addr)
# self.serial = self.ble # self.serial = self.ble
# RNS.log(f"New connection instance: "+str(self.ble)) # RNS.log(f"New connection instance: "+str(self.ble))
def open_port(self): def open_port(self):
if not self.use_ble: if not self.use_ble:
if self.port != None: if self.port != None:
RNS.log("Opening serial port "+self.port+"...") RNS.log(f"Opening serial port {self.port}...")
# Get device parameters # Get device parameters
from usb4a import usb from usb4a import usb
device = usb.get_usb_device(self.port) device = usb.get_usb_device(self.port)
@ -577,7 +577,7 @@ class RNodeInterface(Interface):
proxy = self.pyserial.get_serial_port proxy = self.pyserial.get_serial_port
if vid == 0x1A86 and pid == 0x55D4: if vid == 0x1A86 and pid == 0x55D4:
# Force CDC driver for Qinheng CH34x # 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 from usbserial4a.cdcacmserial4a import CdcAcmSerial
proxy = CdcAcmSerial proxy = CdcAcmSerial
@ -602,7 +602,7 @@ class RNodeInterface(Interface):
self.serial.timeout = 0.1 self.serial.timeout = 0.1
elif vid == 0x10C4: elif vid == 0x10C4:
# Hardware parameters for SiLabs CP210x @ 115200 baud # Hardware parameters for SiLabs CP210x @ 115200 baud
self.serial.DEFAULT_READ_BUFFER_SIZE = 64 self.serial.DEFAULT_READ_BUFFER_SIZE = 64
self.serial.USB_READ_TIMEOUT_MILLIS = 12 self.serial.USB_READ_TIMEOUT_MILLIS = 12
self.serial.timeout = 0.012 self.serial.timeout = 0.012
elif vid == 0x1A86 and pid == 0x55D4: elif vid == 0x1A86 and pid == 0x55D4:
@ -616,9 +616,9 @@ class RNodeInterface(Interface):
self.serial.USB_READ_TIMEOUT_MILLIS = 100 self.serial.USB_READ_TIMEOUT_MILLIS = 100
self.serial.timeout = 0.1 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(f"{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(f"{self} USB read timeout set to {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 write timeout set to {self.serial.USB_WRITE_TIMEOUT_MILLIS}ms", RNS.LOG_DEBUG)
elif self.allow_bluetooth: elif self.allow_bluetooth:
if self.bt_manager == None: 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) RNS.log(f"RNode detect timed out over {self.port}", RNS.LOG_ERROR)
if not self.detected: if not self.detected:
raise IOError("Could not detect device") raise OSError("Could not detect device")
else: else:
if self.platform == KISS.PLATFORM_ESP32 or self.platform == KISS.PLATFORM_NRF52: if self.platform == KISS.PLATFORM_ESP32 or self.platform == KISS.PLATFORM_NRF52:
self.display = True self.display = True
if not self.firmware_ok: if not self.firmware_ok:
raise IOError("Invalid device firmware") raise OSError("Invalid device firmware")
if self.serial != None and self.port != None: if self.serial != None and self.port != None:
self.timeout = 200 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: if self.bt_manager != None and self.bt_manager.connected:
self.timeout = 1500 self.timeout = 1500
@ -680,21 +680,21 @@ class RNodeInterface(Interface):
self.initRadio() self.initRadio()
if (self.validateRadioState()): if (self.validateRadioState()):
self.interface_ready = True 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) sleep(0.3)
self.online = True self.online = True
else: 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("Make sure that your hardware actually supports the parameters specified in the configuration", RNS.LOG_ERROR)
RNS.log("Aborting RNode startup", RNS.LOG_ERROR) RNS.log("Aborting RNode startup", RNS.LOG_ERROR)
if self.serial != None: if self.serial != None:
self.serial.close() self.serial.close()
if self.bt_manager != None: if self.bt_manager != None:
self.bt_manager.close() self.bt_manager.close()
raise IOError("RNode interface did not pass configuration validation") raise OSError("RNode interface did not pass configuration validation")
def initRadio(self): def initRadio(self):
self.setFrequency() self.setFrequency()
@ -702,22 +702,22 @@ class RNodeInterface(Interface):
self.setBandwidth() self.setBandwidth()
time.sleep(0.15) time.sleep(0.15)
self.setTXPower() self.setTXPower()
time.sleep(0.15) time.sleep(0.15)
self.setSpreadingFactor() self.setSpreadingFactor()
time.sleep(0.15) time.sleep(0.15)
self.setCodingRate() self.setCodingRate()
time.sleep(0.15) time.sleep(0.15)
self.setSTALock() self.setSTALock()
time.sleep(0.15) time.sleep(0.15)
self.setLTALock() self.setLTALock()
time.sleep(0.15) time.sleep(0.15)
self.setRadioState(KISS.RADIO_STATE_ON) self.setRadioState(KISS.RADIO_STATE_ON)
time.sleep(0.15) time.sleep(0.15)
@ -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]) 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) written = self.write_mux(kiss_command)
if written != len(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): def leave(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(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): def enable_bluetooth(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x01, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x01, KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(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): def disable_bluetooth(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x00, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x00, KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def bluetooth_pair(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x02, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_BT_CTRL, 0x02, KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def enable_external_framebuffer(self):
if self.display != None: if self.display != None:
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x01, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x01, KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(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): def disable_external_framebuffer(self):
if self.display != None: if self.display != None:
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x00, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x00, KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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_PIXEL_WIDTH = 64
FB_BITS_PER_PIXEL = 1 FB_BITS_PER_PIXEL = 1
@ -788,16 +788,16 @@ class RNodeInterface(Interface):
data = line_byte+line_data data = line_byte+line_data
escaped_data = KISS.escape(data) escaped_data = KISS.escape(data)
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FB_WRITE])+escaped_data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FB_WRITE])+escaped_data+bytes([KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def hard_reset(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while restarting device") raise OSError("An IO error occurred while restarting device")
sleep(4.0); sleep(4.0);
def setFrequency(self): def setFrequency(self):
@ -810,7 +810,7 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FREQUENCY])+data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FREQUENCY])+data+bytes([KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setBandwidth(self):
c1 = self.bandwidth >> 24 c1 = self.bandwidth >> 24
@ -822,28 +822,28 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_BANDWIDTH])+data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_BANDWIDTH])+data+bytes([KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setTXPower(self):
txp = bytes([self.txpower]) txp = bytes([self.txpower])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXPOWER])+txp+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXPOWER])+txp+bytes([KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setSpreadingFactor(self):
sf = bytes([self.sf]) sf = bytes([self.sf])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SF])+sf+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SF])+sf+bytes([KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setCodingRate(self):
cr = bytes([self.cr]) cr = bytes([self.cr])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_CR])+cr+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_CR])+cr+bytes([KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setSTALock(self):
if self.st_alock != None: 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]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_ST_ALOCK])+data+bytes([KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setLTALock(self):
if self.lt_alock != None: 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]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_LT_ALOCK])+data+bytes([KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setRadioState(self, state):
self.state = state self.state = state
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND])
written = self.write_mux(kiss_command) written = self.write_mux(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def validate_firmware(self):
if (self.maj_version > RNodeInterface.REQUIRED_FW_VER_MAJ): 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.maj_version >= RNodeInterface.REQUIRED_FW_VER_MAJ):
if (self.min_version >= RNodeInterface.REQUIRED_FW_VER_MIN): if (self.min_version >= RNodeInterface.REQUIRED_FW_VER_MIN):
self.firmware_ok = True self.firmware_ok = True
if self.firmware_ok: if self.firmware_ok:
return return
RNS.log("The firmware version of the connected RNode is "+str(self.maj_version)+"."+str(self.min_version), 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("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"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/") 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 = f"The firmware version of the connected RNode is {self.maj_version}.{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"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/" 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}) self.hw_errors.append({"error": KISS.ERROR_INVALID_FIRMWARE, "description": error_description})
def validateRadioState(self): 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: if not self.platform == KISS.PLATFORM_ESP32:
sleep(1.00); sleep(1.00);
else: else:
@ -937,7 +937,7 @@ class RNodeInterface(Interface):
try: try:
self.bitrate = self.r_sf * ( (4.0/self.r_cr) / (math.pow(2,self.r_sf)/(self.r_bandwidth/1000)) ) * 1000 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) 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: except:
self.bitrate = 0 self.bitrate = 0
@ -969,7 +969,7 @@ class RNodeInterface(Interface):
self.txb += datalen self.txb += datalen
if written != len(frame): 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: else:
self.queue(data) self.queue(data)
@ -1042,7 +1042,7 @@ class RNodeInterface(Interface):
command_buffer = command_buffer+bytes([byte]) command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4): if (len(command_buffer) == 4):
self.r_frequency = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3] 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() self.updateBitrate()
elif (command == KISS.CMD_BANDWIDTH): elif (command == KISS.CMD_BANDWIDTH):
@ -1058,26 +1058,26 @@ class RNodeInterface(Interface):
command_buffer = command_buffer+bytes([byte]) command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4): if (len(command_buffer) == 4):
self.r_bandwidth = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3] 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() self.updateBitrate()
elif (command == KISS.CMD_TXPOWER): elif (command == KISS.CMD_TXPOWER):
self.r_txpower = byte 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): elif (command == KISS.CMD_SF):
self.r_sf = byte 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() self.updateBitrate()
elif (command == KISS.CMD_CR): elif (command == KISS.CMD_CR):
self.r_cr = byte 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() self.updateBitrate()
elif (command == KISS.CMD_RADIO_STATE): elif (command == KISS.CMD_RADIO_STATE):
self.r_state = byte self.r_state = byte
if self.r_state: 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: 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): elif (command == KISS.CMD_RADIO_LOCK):
self.r_lock = byte self.r_lock = byte
@ -1156,7 +1156,7 @@ class RNodeInterface(Interface):
if (len(command_buffer) == 2): if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1] at = command_buffer[0] << 8 | command_buffer[1]
self.r_st_alock = at/100.0 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): elif (command == KISS.CMD_LT_ALOCK):
if (byte == KISS.FESC): if (byte == KISS.FESC):
escape = True escape = True
@ -1171,7 +1171,7 @@ class RNodeInterface(Interface):
if (len(command_buffer) == 2): if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1] at = command_buffer[0] << 8 | command_buffer[1]
self.r_lt_alock = at/100.0 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): elif (command == KISS.CMD_STAT_CHTM):
if (byte == KISS.FESC): if (byte == KISS.FESC):
escape = True escape = True
@ -1188,7 +1188,7 @@ class RNodeInterface(Interface):
atl = command_buffer[2] << 8 | command_buffer[3] atl = command_buffer[2] << 8 | command_buffer[3]
cus = command_buffer[4] << 8 | command_buffer[5] cus = command_buffer[4] << 8 | command_buffer[5]
cul = command_buffer[6] << 8 | command_buffer[7] cul = command_buffer[6] << 8 | command_buffer[7]
self.r_airtime_short = ats/100.0 self.r_airtime_short = ats/100.0
self.r_airtime_long = atl/100.0 self.r_airtime_long = atl/100.0
self.r_channel_load_short = cus/100.0 self.r_channel_load_short = cus/100.0
@ -1217,9 +1217,9 @@ class RNodeInterface(Interface):
self.r_preamble_symbols = prs self.r_preamble_symbols = prs
self.r_premable_time_ms = prt self.r_premable_time_ms = prt
self.r_csma_slot_time_ms = cst 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(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(str(self)+" Radio reporting preamble is "+str(self.r_preamble_symbols)+" symbols ("+str(self.r_premable_time_ms)+"ms)", 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(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 CSMA slot time is {self.r_csma_slot_time_ms}ms", RNS.LOG_DEBUG)
elif (command == KISS.CMD_STAT_BAT): elif (command == KISS.CMD_STAT_BAT):
if (byte == KISS.FESC): if (byte == KISS.FESC):
escape = True escape = True
@ -1247,26 +1247,26 @@ class RNodeInterface(Interface):
self.mcu = byte self.mcu = byte
elif (command == KISS.CMD_ERROR): elif (command == KISS.CMD_ERROR):
if (byte == KISS.ERROR_INITRADIO): if (byte == KISS.ERROR_INITRADIO):
RNS.log(str(self)+" hardware initialisation error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) RNS.log(f"{self} hardware initialisation error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise IOError("Radio initialisation failure") raise OSError("Radio initialisation failure")
elif (byte == KISS.ERROR_TXFAILED): elif (byte == KISS.ERROR_TXFAILED):
RNS.log(str(self)+" hardware TX error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) RNS.log(f"{self} hardware TX error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise IOError("Hardware transmit failure") raise OSError("Hardware transmit failure")
elif (byte == KISS.ERROR_MEMORY_LOW): 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"}) self.hw_errors.append({"error": KISS.ERROR_MEMORY_LOW, "description": "Memory exhausted on connected device"})
elif (byte == KISS.ERROR_MODEM_TIMEOUT): 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"}) self.hw_errors.append({"error": KISS.ERROR_MODEM_TIMEOUT, "description": "Modem communication timed out on connected device"})
else: else:
RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) RNS.log(f"{self} hardware error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise IOError("Unknown hardware failure") raise OSError("Unknown hardware failure")
elif (command == KISS.CMD_RESET): elif (command == KISS.CMD_RESET):
if (byte == 0xF8): if (byte == 0xF8):
if self.platform == KISS.PLATFORM_ESP32: if self.platform == KISS.PLATFORM_ESP32:
if self.online: if self.online:
RNS.log("Detected reset while device was online, reinitialising device...", RNS.LOG_ERROR) 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): elif (command == KISS.CMD_READY):
self.process_queue() self.process_queue()
elif (command == KISS.CMD_DETECT): elif (command == KISS.CMD_DETECT):
@ -1278,7 +1278,7 @@ class RNodeInterface(Interface):
if got == 0: if got == 0:
time_since_last = int(time.time()*1000) - last_read_ms time_since_last = int(time.time()*1000) - last_read_ms
if len(data_buffer) > 0 and time_since_last > self.timeout: if len(data_buffer) > 0 and time_since_last > self.timeout:
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"" data_buffer = b""
in_frame = False in_frame = False
command = KISS.CMD_UNKNOWN command = KISS.CMD_UNKNOWN
@ -1287,22 +1287,22 @@ class RNodeInterface(Interface):
if self.id_interval != None and self.id_callsign != None: if self.id_interval != None and self.id_callsign != None:
if self.first_tx != None: if self.first_tx != None:
if time.time() > self.first_tx + self.id_interval: 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) self.processOutgoing(self.id_callsign)
if (time.time() - self.last_port_io > self.port_io_timeout): if (time.time() - self.last_port_io > self.port_io_timeout):
self.detect() self.detect()
if (time.time() - self.last_port_io > self.port_io_timeout*3): if (time.time() - self.last_port_io > self.port_io_timeout*3):
raise IOError("Connected port for "+str(self)+" became unresponsive") raise OSError(f"Connected port for {self} became unresponsive")
if self.bt_manager != None: if self.bt_manager != None:
sleep(0.08) sleep(0.08)
except Exception as e: except Exception as e:
self.online = False self.online = False
RNS.log("A serial port occurred, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"A serial port occurred, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR) RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
@ -1330,10 +1330,10 @@ class RNodeInterface(Interface):
try: try:
time.sleep(self.reconnect_w) time.sleep(self.reconnect_w)
if self.serial != None and self.port != None: 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: 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() self.open_port()
@ -1343,7 +1343,7 @@ class RNodeInterface(Interface):
if self.last_imagedata != None: if self.last_imagedata != None:
self.display_image(self.last_imagedata) self.display_image(self.last_imagedata)
self.enable_external_framebuffer() self.enable_external_framebuffer()
elif hasattr(self, "bt_manager") and self.bt_manager != None and self.bt_manager.connected: elif hasattr(self, "bt_manager") and self.bt_manager != None and self.bt_manager.connected:
self.configure_device() self.configure_device()
if self.online: if self.online:
@ -1352,10 +1352,10 @@ class RNodeInterface(Interface):
self.enable_external_framebuffer() self.enable_external_framebuffer()
except Exception as e: 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: if self.online:
RNS.log("Reconnected serial port for "+str(self)) RNS.log(f"Reconnected serial port for {self}")
def detach(self): def detach(self):
self.detached = True self.detached = True
@ -1399,7 +1399,7 @@ class RNodeInterface(Interface):
return data return data
def __str__(self): def __str__(self):
return "RNodeInterface["+str(self.name)+"]" return f"RNodeInterface[{self.name}]"
class BLEConnection(BluetoothDispatcher): class BLEConnection(BluetoothDispatcher):
UART_SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e" UART_SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"
@ -1472,7 +1472,7 @@ class BLEConnection(BluetoothDispatcher):
RNS.trace_exception(e) RNS.trace_exception(e)
def __init__(self, owner=None, target_name=None, target_bt_addr=None): def __init__(self, owner=None, target_name=None, target_bt_addr=None):
super(BLEConnection, self).__init__() super().__init__()
self.owner = owner self.owner = owner
self.target_name = target_name self.target_name = target_name
self.target_bt_addr = target_bt_addr self.target_bt_addr = target_bt_addr
@ -1504,7 +1504,7 @@ class BLEConnection(BluetoothDispatcher):
self.write_characteristic(self.rx_char, data) self.write_characteristic(self.rx_char, data)
else: else:
time.sleep(0.1) time.sleep(0.1)
except Exception as e: except Exception as e:
RNS.log("An error occurred in {self} write loop: {e}", RNS.LOG_ERROR) RNS.log("An error occurred in {self} write loop: {e}", RNS.LOG_ERROR)
RNS.trace_exception(e) RNS.trace_exception(e)
@ -1552,7 +1552,7 @@ class BLEConnection(BluetoothDispatcher):
self.owner.hw_errors.append({"error": KISS.ERROR_INVALID_BLE_MTU, "description": "The Bluetooth Low Energy transfer MTU could not be configured for the connected device, and communication has failed. Restart Reticulum and any connected applications to retry connecting."}) self.owner.hw_errors.append({"error": KISS.ERROR_INVALID_BLE_MTU, "description": "The Bluetooth Low Energy transfer MTU could not be configured for the connected device, and communication has failed. Restart Reticulum and any connected applications to retry connecting."})
self.close() self.close()
self.should_run = False self.should_run = False
self.close_gatt() self.close_gatt()
self.connect_job_running = False self.connect_job_running = False
@ -1599,14 +1599,14 @@ class BLEConnection(BluetoothDispatcher):
def on_services(self, status, services): def on_services(self, status, services):
if status == GATT_SUCCESS: if status == GATT_SUCCESS:
self.rx_char = services.search(BLEConnection.UART_RX_CHAR_UUID) self.rx_char = services.search(BLEConnection.UART_RX_CHAR_UUID)
if self.rx_char is not None: if self.rx_char is not None:
self.tx_char = services.search(BLEConnection.UART_TX_CHAR_UUID) self.tx_char = services.search(BLEConnection.UART_TX_CHAR_UUID)
if self.tx_char is not None: if self.tx_char is not None:
if self.enable_notifications(self.tx_char): if self.enable_notifications(self.tx_char):
RNS.log("Enabled notifications for BLE TX characteristic", RNS.LOG_DEBUG) RNS.log("Enabled notifications for BLE TX characteristic", RNS.LOG_DEBUG)
RNS.log(f"Requesting BLE connection MTU update to {self.target_mtu}", RNS.LOG_DEBUG) RNS.log(f"Requesting BLE connection MTU update to {self.target_mtu}", RNS.LOG_DEBUG)
self.mtu_requested_time = time.time() self.mtu_requested_time = time.time()
self.request_mtu(self.target_mtu) self.request_mtu(self.target_mtu)

View File

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

View File

@ -23,5 +23,5 @@
import os import os
import glob 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')] __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): class AutoInterface(Interface):
DEFAULT_DISCOVERY_PORT = 29716 DEFAULT_DISCOVERY_PORT = 29716
DEFAULT_DATA_PORT = 42671 DEFAULT_DATA_PORT = 42671
DEFAULT_GROUP_ID = "reticulum".encode("utf-8") DEFAULT_GROUP_ID = b"reticulum"
SCOPE_LINK = "2" SCOPE_LINK = "2"
SCOPE_ADMIN = "4" SCOPE_ADMIN = "4"
@ -169,32 +169,32 @@ class AutoInterface(Interface):
self.group_hash = RNS.Identity.full_hash(self.group_id) self.group_hash = RNS.Identity.full_hash(self.group_id)
g = self.group_hash g = self.group_hash
#gt = "{:02x}".format(g[1]+(g[0]<<8)) #gt = f"{g[1] + (g[0] << 8):02x}"
gt = "0" gt = "0"
gt += ":"+"{:02x}".format(g[3]+(g[2]<<8)) gt += f":{g[3] + (g[2] << 8):02x}"
gt += ":"+"{:02x}".format(g[5]+(g[4]<<8)) gt += f":{g[5] + (g[4] << 8):02x}"
gt += ":"+"{:02x}".format(g[7]+(g[6]<<8)) gt += f":{g[7] + (g[6] << 8):02x}"
gt += ":"+"{:02x}".format(g[9]+(g[8]<<8)) gt += f":{g[9] + (g[8] << 8):02x}"
gt += ":"+"{:02x}".format(g[11]+(g[10]<<8)) gt += f":{g[11] + (g[10] << 8):02x}"
gt += ":"+"{:02x}".format(g[13]+(g[12]<<8)) gt += f":{g[13] + (g[12] << 8):02x}"
self.mcast_discovery_address = "ff"+self.multicast_address_type+self.discovery_scope+":"+gt self.mcast_discovery_address = f"ff{self.multicast_address_type}{self.discovery_scope}:{gt}"
suitable_interfaces = 0 suitable_interfaces = 0
for ifname in self.list_interfaces(): for ifname in self.list_interfaces():
try: try:
if RNS.vendor.platformutils.is_darwin() and ifname in AutoInterface.DARWIN_IGNORE_IFS and not ifname in self.allowed_interfaces: 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": 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: 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: 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: 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: else:
if len(self.allowed_interfaces) > 0 and not ifname in self.allowed_interfaces: 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: else:
addresses = self.list_addresses(ifname) addresses = self.list_addresses(ifname)
if self.netinfo.AF_INET6 in addresses: 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) RNS.log(f"{self} Selecting link-local address {link_local_addr} for interface {ifname}", RNS.LOG_EXTREME)
if link_local_addr == None: 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: else:
mcast_addr = self.mcast_discovery_address 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 # Struct with interface index
if_struct = struct.pack("I", self.interface_name_to_index(ifname)) if_struct = struct.pack("I", self.interface_name_to_index(ifname))
@ -243,7 +243,7 @@ class AutoInterface(Interface):
else: else:
if self.discovery_scope == AutoInterface.SCOPE_LINK: 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: else:
addr_info = socket.getaddrinfo(mcast_addr, self.discovery_port, socket.AF_INET6, socket.SOCK_DGRAM) 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) 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: 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: else:
self.receives = True self.receives = True
@ -277,19 +277,19 @@ class AutoInterface(Interface):
self.bitrate = AutoInterface.BITRATE_GUESS self.bitrate = AutoInterface.BITRATE_GUESS
peering_wait = self.announce_interval*1.2 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 self.owner = owner
socketserver.UDPServer.address_family = socket.AF_INET6 socketserver.UDPServer.address_family = socket.AF_INET6
for ifname in self.adopted_interfaces: 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) addr_info = socket.getaddrinfo(local_addr, self.data_port, socket.AF_INET6, socket.SOCK_DGRAM)
address = addr_info[0][4] address = addr_info[0][4]
udp_server = socketserver.UDPServer(address, self.handler_factory(self.processIncoming)) udp_server = socketserver.UDPServer(address, self.handler_factory(self.processIncoming))
self.interface_servers[ifname] = udp_server self.interface_servers[ifname] = udp_server
thread = threading.Thread(target=udp_server.serve_forever) thread = threading.Thread(target=udp_server.serve_forever)
thread.daemon = True thread.daemon = True
thread.start() thread.start()
@ -306,18 +306,18 @@ class AutoInterface(Interface):
def discovery_handler(self, socket, ifname): def discovery_handler(self, socket, ifname):
def announce_loop(): def announce_loop():
self.announce_handler(ifname) self.announce_handler(ifname)
thread = threading.Thread(target=announce_loop) thread = threading.Thread(target=announce_loop)
thread.daemon = True thread.daemon = True
thread.start() thread.start()
while True: while True:
data, ipv6_src = socket.recvfrom(1024) data, ipv6_src = socket.recvfrom(1024)
expected_hash = RNS.Identity.full_hash(self.group_id+ipv6_src[0].encode("utf-8")) expected_hash = RNS.Identity.full_hash(self.group_id+ipv6_src[0].encode("utf-8"))
if data == expected_hash: if data == expected_hash:
self.add_peer(ipv6_src[0], ifname) self.add_peer(ipv6_src[0], ifname)
else: 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): def peer_jobs(self):
while True: while True:
@ -335,7 +335,7 @@ class AutoInterface(Interface):
# Remove any timed out peers # Remove any timed out peers
for peer_addr in timed_out_peers: for peer_addr in timed_out_peers:
removed_peer = self.peers.pop(peer_addr) 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: for ifname in self.adopted_interfaces:
# Check that the link-local address has not changed # Check that the link-local address has not changed
@ -349,25 +349,25 @@ class AutoInterface(Interface):
link_local_addr = self.descope_linklocal(address["addr"]) link_local_addr = self.descope_linklocal(address["addr"])
if link_local_addr != self.adopted_interfaces[ifname]: if link_local_addr != self.adopted_interfaces[ifname]:
old_link_local_address = 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.adopted_interfaces[ifname] = link_local_addr
self.link_local_addresses.append(link_local_addr) self.link_local_addresses.append(link_local_addr)
if old_link_local_address in self.link_local_addresses: if old_link_local_address in self.link_local_addresses:
self.link_local_addresses.remove(old_link_local_address) 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) addr_info = socket.getaddrinfo(local_addr, self.data_port, socket.AF_INET6, socket.SOCK_DGRAM)
listen_address = addr_info[0][4] listen_address = addr_info[0][4]
if ifname in self.interface_servers: 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] previous_server = self.interface_servers[ifname]
def shutdown_server(): def shutdown_server():
previous_server.shutdown() previous_server.shutdown()
threading.Thread(target=shutdown_server, daemon=True).start() 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)) udp_server = socketserver.UDPServer(listen_address, self.handler_factory(self.processIncoming))
self.interface_servers[ifname] = udp_server self.interface_servers[ifname] = udp_server
@ -379,7 +379,7 @@ class AutoInterface(Interface):
self.carrier_changed = True self.carrier_changed = True
except Exception as e: 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 # Check multicast echo timeouts
last_multicast_echo = 0 last_multicast_echo = 0
@ -389,20 +389,20 @@ class AutoInterface(Interface):
if now - last_multicast_echo > self.multicast_echo_timeout: if now - last_multicast_echo > self.multicast_echo_timeout:
if ifname in self.timed_out_interfaces and self.timed_out_interfaces[ifname] == False: if ifname in self.timed_out_interfaces and self.timed_out_interfaces[ifname] == False:
self.carrier_changed = True 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 self.timed_out_interfaces[ifname] = True
else: else:
if ifname in self.timed_out_interfaces and self.timed_out_interfaces[ifname] == True: if ifname in self.timed_out_interfaces and self.timed_out_interfaces[ifname] == True:
self.carrier_changed = 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 self.timed_out_interfaces[ifname] = False
def announce_handler(self, ifname): def announce_handler(self, ifname):
while True: while True:
self.peer_announce(ifname) self.peer_announce(ifname)
time.sleep(self.announce_interval) time.sleep(self.announce_interval)
def peer_announce(self, ifname): def peer_announce(self, ifname):
try: try:
link_local_address = self.adopted_interfaces[ifname] link_local_address = self.adopted_interfaces[ifname]
@ -414,10 +414,10 @@ class AutoInterface(Interface):
announce_socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, ifis) announce_socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, ifis)
announce_socket.sendto(discovery_token, addr_info[0][4]) announce_socket.sendto(discovery_token, addr_info[0][4])
announce_socket.close() announce_socket.close()
except Exception as e: except Exception as e:
if (ifname in self.timed_out_interfaces and self.timed_out_interfaces[ifname] == False) or not ifname in self.timed_out_interfaces: if (ifname in self.timed_out_interfaces and self.timed_out_interfaces[ifname] == False) or not ifname in self.timed_out_interfaces:
RNS.log(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: else:
pass pass
@ -431,12 +431,12 @@ class AutoInterface(Interface):
if ifname != None: if ifname != None:
self.multicast_echoes[ifname] = time.time() self.multicast_echoes[ifname] = time.time()
else: 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: else:
if not addr in self.peers: if not addr in self.peers:
self.peers[addr] = [ifname, time.time()] 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: else:
self.refresh_peer(addr) self.refresh_peer(addr)
@ -464,16 +464,16 @@ class AutoInterface(Interface):
if self.outbound_udp_socket == None: if self.outbound_udp_socket == None:
self.outbound_udp_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) 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) 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]) self.outbound_udp_socket.sendto(data, addr_info[0][4])
except Exception as e: 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) self.txb += len(data)
# Until per-device sub-interfacing is implemented, # Until per-device sub-interfacing is implemented,
# ingress limiting should be disabled on AutoInterface # ingress limiting should be disabled on AutoInterface
@ -481,7 +481,7 @@ class AutoInterface(Interface):
return False return False
def __str__(self): def __str__(self):
return "AutoInterface["+self.name+"]" return f"AutoInterface[{self.name}]"
class AutoInterfaceHandler(socketserver.BaseRequestHandler): class AutoInterfaceHandler(socketserver.BaseRequestHandler):
def __init__(self, callback, *args, **keys): def __init__(self, callback, *args, **keys):

View File

@ -90,7 +90,7 @@ class I2PController:
time.sleep(0.10) time.sleep(0.10)
if self.loop == None: 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: while self.loop == None:
self.loop = asyncio.get_event_loop() self.loop = asyncio.get_event_loop()
@ -101,7 +101,7 @@ class I2PController:
self.loop.run_forever() self.loop.run_forever()
except Exception as e: except Exception as e:
self.ready = False 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: finally:
self.loop.close() self.loop.close()
@ -136,18 +136,18 @@ class I2PController:
if not self.client_tunnels[i2p_destination]: if not self.client_tunnels[i2p_destination]:
try: try:
async def tunnel_up(): 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) tunnel = self.i2plib.ClientTunnel(i2p_destination, owner.local_addr, sam_address=self.sam_address, loop=self.loop)
self.i2plib_tunnels[i2p_destination] = tunnel self.i2plib_tunnels[i2p_destination] = tunnel
await tunnel.run() await tunnel.run()
self.loop.ext_owner = self self.loop.ext_owner = self
result = asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop).result() result = asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop).result()
if not i2p_destination in self.i2plib_tunnels:
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] tn = self.i2plib_tunnels[i2p_destination]
if tn != None and hasattr(tn, "status"): if tn != None and hasattr(tn, "status"):
@ -167,28 +167,28 @@ class I2PController:
try: try:
owner.socket.shutdown(socket.SHUT_RDWR) owner.socket.shutdown(socket.SHUT_RDWR)
except Exception as e: 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: try:
owner.socket.close() owner.socket.close()
except Exception as e: 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 self.client_tunnels[i2p_destination] = True
owner.awaiting_i2p_tunnel = False 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: else:
raise IOError("Got no status response from SAM API") raise OSError("Got no status response from SAM API")
except ConnectionRefusedError as e: except ConnectionRefusedError as e:
raise e raise e
except ConnectionAbortedError as e: except ConnectionAbortedError as e:
raise e raise e
except Exception as 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 raise e
else: else:
@ -197,16 +197,16 @@ class I2PController:
i2p_exception = i2ptunnel.status["exception"] i2p_exception = i2ptunnel.status["exception"]
if i2ptunnel.status["setup_ran"] == False: 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) self.stop_tunnel(i2ptunnel)
return False return False
elif i2p_exception != None: 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): 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): 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) 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) 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): 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): 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): 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): elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.I2PError):
RNS.log("The I2P daemon experienced an unspecified error", RNS.LOG_ERROR) RNS.log("The I2P daemon experienced an unspecified error", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.Timeout): elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.Timeout):
RNS.log("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) RNS.log("Resetting I2P tunnel and retrying later", RNS.LOG_ERROR)
@ -238,13 +238,13 @@ class I2PController:
return False return False
elif i2ptunnel.status["setup_failed"] == True: 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) self.stop_tunnel(i2ptunnel)
return False return False
else: 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) self.stop_tunnel(i2ptunnel)
return False return False
@ -259,11 +259,11 @@ class I2PController:
# Old format # Old format
i2p_dest_hash_of = RNS.Identity.full_hash(RNS.Identity.full_hash(owner.name.encode("utf-8"))) 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 # 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_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 # Use old format if a key is already present
if os.path.isfile(i2p_keyfile_of): if os.path.isfile(i2p_keyfile_of):
@ -279,7 +279,7 @@ class I2PController:
key_file.write(i2p_dest.private_key.base64) key_file.write(i2p_dest.private_key.base64)
key_file.close() key_file.close()
else: else:
key_file = open(i2p_keyfile, "r") key_file = open(i2p_keyfile)
prvd = key_file.read() prvd = key_file.read()
key_file.close() key_file.close()
i2p_dest = self.i2plib.Destination(data=prvd, has_private_key=True) i2p_dest = self.i2plib.Destination(data=prvd, has_private_key=True)
@ -294,12 +294,12 @@ class I2PController:
if self.server_tunnels[i2p_b32] == False: if self.server_tunnels[i2p_b32] == False:
try: try:
async def tunnel_up(): 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) 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 self.i2plib_tunnels[i2p_b32] = tunnel
await tunnel.run() await tunnel.run()
owner.online = True 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() asyncio.run_coroutine_threadsafe(tunnel_up(), self.loop).result()
self.server_tunnels[i2p_b32] = True self.server_tunnels[i2p_b32] = True
@ -313,16 +313,16 @@ class I2PController:
i2p_exception = i2ptunnel.status["exception"] i2p_exception = i2ptunnel.status["exception"]
if i2ptunnel.status["setup_ran"] == False: 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) self.stop_tunnel(i2ptunnel)
return False return False
elif i2p_exception != None: elif i2p_exception != None:
RNS.log("An error ocurred while setting up I2P tunnel", RNS.LOG_ERROR) RNS.log("An error ocurred while setting up I2P tunnel", RNS.LOG_ERROR)
if isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.CantReachPeer): if isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.CantReachPeer):
RNS.log("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): 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) 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) 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): 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): 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): 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): elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.I2PError):
RNS.log("The I2P daemon experienced an unspecified error", RNS.LOG_ERROR) RNS.log("The I2P daemon experienced an unspecified error", RNS.LOG_ERROR)
elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.Timeout): elif isinstance(i2p_exception, RNS.vendor.i2plib.exceptions.Timeout):
RNS.log("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) RNS.log("Resetting I2P tunnel and retrying later", RNS.LOG_ERROR)
@ -354,13 +354,13 @@ class I2PController:
return False return False
elif i2ptunnel.status["setup_failed"] == True: 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) self.stop_tunnel(i2ptunnel)
return False return False
else: 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) self.stop_tunnel(i2ptunnel)
return False return False
@ -393,7 +393,7 @@ class I2PInterfacePeer(Interface):
super().__init__() super().__init__()
self.HW_MTU = 1064 self.HW_MTU = 1064
self.IN = True self.IN = True
self.OUT = False self.OUT = False
self.socket = None self.socket = None
@ -475,11 +475,11 @@ class I2PInterfacePeer(Interface):
self.target_port = self.bind_port self.target_port = self.bind_port
if not self.parent_interface.i2p.client_tunnel(self, target_i2p_dest): 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 self.awaiting_i2p_tunnel = True
except Exception as e: 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) RNS.log("Check that I2P is installed and running, and that SAM is enabled. Retrying tunnel setup later.", RNS.LOG_ERROR)
time.sleep(8) time.sleep(8)
@ -492,7 +492,7 @@ class I2PInterfacePeer(Interface):
while self.awaiting_i2p_tunnel: while self.awaiting_i2p_tunnel:
time.sleep(0.25) time.sleep(0.25)
time.sleep(2) time.sleep(2)
if not self.kiss_framing: if not self.kiss_framing:
self.wants_tunnel = True self.wants_tunnel = True
@ -525,37 +525,37 @@ class I2PInterfacePeer(Interface):
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(I2PInterfacePeer.I2P_PROBE_AFTER)) self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(I2PInterfacePeer.I2P_PROBE_AFTER))
def shutdown_socket(self, target_socket): def shutdown_socket(self, target_socket):
if callable(target_socket.close): if callable(target_socket.close):
try: try:
if socket != None: if socket != None:
target_socket.shutdown(socket.SHUT_RDWR) target_socket.shutdown(socket.SHUT_RDWR)
except Exception as e: 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: try:
if socket != None: if socket != None:
target_socket.close() target_socket.close()
except Exception as e: 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): def detach(self):
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG) RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
if self.socket != None: if self.socket != None:
if hasattr(self.socket, "close"): if hasattr(self.socket, "close"):
if callable(self.socket.close): if callable(self.socket.close):
self.detached = True self.detached = True
try: try:
self.socket.shutdown(socket.SHUT_RDWR) self.socket.shutdown(socket.SHUT_RDWR)
except Exception as e: except Exception as e:
RNS.log("Error while shutting down socket for "+str(self)+": "+str(e)) RNS.log(f"Error while shutting down socket for {self}: {e}")
try: try:
self.socket.close() self.socket.close()
except Exception as e: 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 self.socket = None
@ -564,15 +564,15 @@ class I2PInterfacePeer(Interface):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect((self.target_ip, self.target_port)) self.socket.connect((self.target_ip, self.target_port))
self.online = True self.online = True
except Exception as e: except Exception as e:
if initial: if initial:
if not self.awaiting_i2p_tunnel: if not self.awaiting_i2p_tunnel:
RNS.log("Initial connection for "+str(self)+" could not be established: "+str(e), RNS.LOG_ERROR) RNS.log(f"Initial connection for {self} could not be established: {e}", RNS.LOG_ERROR)
RNS.log("Leaving unconnected and retrying connection in "+str(I2PInterfacePeer.RECONNECT_WAIT)+" seconds.", RNS.LOG_ERROR) RNS.log(f"Leaving unconnected and retrying connection in {I2PInterfacePeer.RECONNECT_WAIT} seconds.", RNS.LOG_ERROR)
return False return False
else: else:
raise e raise e
@ -580,7 +580,7 @@ class I2PInterfacePeer(Interface):
self.set_timeouts_linux() self.set_timeouts_linux()
elif platform.system() == "Darwin": elif platform.system() == "Darwin":
self.set_timeouts_osx() self.set_timeouts_osx()
self.online = True self.online = True
self.writing = False self.writing = False
self.never_connected = False self.never_connected = False
@ -600,7 +600,7 @@ class I2PInterfacePeer(Interface):
attempts += 1 attempts += 1
if self.max_reconnect_tries != None and attempts > self.max_reconnect_tries: 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() self.teardown()
break break
@ -609,12 +609,12 @@ class I2PInterfacePeer(Interface):
except Exception as e: except Exception as e:
if not self.awaiting_i2p_tunnel: 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: 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: 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 self.reconnecting = False
thread = threading.Thread(target=self.read_loop) thread = threading.Thread(target=self.read_loop)
@ -625,13 +625,13 @@ class I2PInterfacePeer(Interface):
else: else:
RNS.log("Attempt to reconnect on a non-initiator I2P interface. This should not happen.", RNS.LOG_ERROR) 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): def processIncoming(self, data):
self.rxb += len(data) self.rxb += len(data)
if hasattr(self, "parent_interface") and self.parent_interface != None and self.parent_count: if hasattr(self, "parent_interface") and self.parent_interface != None and self.parent_count:
self.parent_interface.rxb += len(data) self.parent_interface.rxb += len(data)
self.owner.inbound(data, self) self.owner.inbound(data, self)
def processOutgoing(self, data): def processOutgoing(self, data):
@ -651,13 +651,13 @@ class I2PInterfacePeer(Interface):
self.writing = False self.writing = False
self.txb += len(data) self.txb += len(data)
self.last_write = time.time() self.last_write = time.time()
if hasattr(self, "parent_interface") and self.parent_interface != None and self.parent_count: if hasattr(self, "parent_interface") and self.parent_interface != None and self.parent_count:
self.parent_interface.txb += len(data) self.parent_interface.txb += len(data)
except Exception as e: except Exception as e:
RNS.log("Exception occurred while transmitting via "+str(self)+", tearing down interface", RNS.LOG_ERROR) RNS.log(f"Exception occurred while transmitting via {self}, tearing down interface", 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.teardown() self.teardown()
@ -683,22 +683,22 @@ class I2PInterfacePeer(Interface):
if self.socket != None: if self.socket != None:
self.socket.sendall(bytes([HDLC.FLAG, HDLC.FLAG])) self.socket.sendall(bytes([HDLC.FLAG, HDLC.FLAG]))
except Exception as e: 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) self.shutdown_socket(self.socket)
should_run = False should_run = False
if (time.time()-self.last_read > I2PInterfacePeer.I2P_READ_TIMEOUT): if (time.time()-self.last_read > I2PInterfacePeer.I2P_READ_TIMEOUT):
RNS.log("I2P socket is unresponsive, restarting...", RNS.LOG_WARNING) RNS.log("I2P socket is unresponsive, restarting...", RNS.LOG_WARNING)
if self.socket != None: if self.socket != None:
try: try:
self.socket.shutdown(socket.SHUT_RDWR) self.socket.shutdown(socket.SHUT_RDWR)
except Exception as e: 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: try:
self.socket.close() self.socket.close()
except Exception as e: 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 should_run = False
@ -782,18 +782,18 @@ class I2PInterfacePeer(Interface):
self.wd_reset = False self.wd_reset = False
if self.initiator and not self.detached: 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() self.reconnect()
else: 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() self.teardown()
break break
except Exception as e: except Exception as e:
self.online = False 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: if self.initiator:
RNS.log("Attempting to reconnect...", RNS.LOG_WARNING) RNS.log("Attempting to reconnect...", RNS.LOG_WARNING)
@ -803,12 +803,12 @@ class I2PInterfacePeer(Interface):
def teardown(self): def teardown(self):
if self.initiator and not self.detached: 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: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
else: 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.online = False
self.OUT = False self.OUT = False
@ -824,7 +824,7 @@ class I2PInterfacePeer(Interface):
def __str__(self): def __str__(self):
return "I2PInterfacePeer["+str(self.name)+"]" return f"I2PInterfacePeer[{self.name}]"
class I2PInterface(Interface): 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): def __init__(self, owner, name, rns_storagepath, peers, connectable = False, ifac_size = 16, ifac_netname = None, ifac_netkey = None):
super().__init__() super().__init__()
self.HW_MTU = 1064 self.HW_MTU = 1064
self.online = False self.online = False
@ -882,7 +882,7 @@ class I2PInterface(Interface):
def createHandler(*args, **keys): def createHandler(*args, **keys):
return I2PInterfaceHandler(callback, *args, **keys) return I2PInterfaceHandler(callback, *args, **keys)
return createHandler return createHandler
ThreadingI2PServer.allow_reuse_address = True ThreadingI2PServer.allow_reuse_address = True
self.server = ThreadingI2PServer(self.address, handlerFactory(self.incoming_connection)) self.server = ThreadingI2PServer(self.address, handlerFactory(self.incoming_connection))
@ -895,11 +895,11 @@ class I2PInterface(Interface):
while True: while True:
try: try:
if not self.i2p.server_tunnel(self): 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 self.online = False
except Exception as e: 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) RNS.log("Check that I2P is installed and running, and that SAM is enabled. Retrying tunnel setup later.", RNS.LOG_ERROR)
time.sleep(15) time.sleep(15)
@ -911,7 +911,7 @@ class I2PInterface(Interface):
if peers != None: if peers != None:
for peer_addr in peers: 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 = I2PInterfacePeer(self, self.owner, interface_name, peer_addr)
peer_interface.OUT = True peer_interface.OUT = True
peer_interface.IN = True peer_interface.IN = True
@ -921,7 +921,7 @@ class I2PInterface(Interface):
def incoming_connection(self, handler): def incoming_connection(self, handler):
RNS.log("Accepting incoming I2P connection", RNS.LOG_VERBOSE) 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 = I2PInterfacePeer(self, self.owner, interface_name, connected_socket=handler.request)
spawned_interface.OUT = True spawned_interface.OUT = True
spawned_interface.IN = True spawned_interface.IN = True
@ -954,7 +954,7 @@ class I2PInterface(Interface):
spawned_interface.announce_rate_penalty = self.announce_rate_penalty spawned_interface.announce_rate_penalty = self.announce_rate_penalty
spawned_interface.mode = self.mode spawned_interface.mode = self.mode
spawned_interface.HW_MTU = self.HW_MTU 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) RNS.Transport.interfaces.append(spawned_interface)
self.clients += 1 self.clients += 1
spawned_interface.read_loop() spawned_interface.read_loop()
@ -969,11 +969,11 @@ class I2PInterface(Interface):
if from_spawned: self.oa_freq_deque.append(time.time()) if from_spawned: self.oa_freq_deque.append(time.time())
def detach(self): def detach(self):
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG) RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
self.i2p.stop() self.i2p.stop()
def __str__(self): def __str__(self):
return "I2PInterface["+self.name+"]" return f"I2PInterface[{self.name}]"
class I2PInterfaceHandler(socketserver.BaseRequestHandler): class I2PInterfaceHandler(socketserver.BaseRequestHandler):
def __init__(self, callback, *args, **keys): def __init__(self, callback, *args, **keys):

View File

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

View File

@ -71,9 +71,9 @@ class KISSInterface(Interface):
RNS.panic() RNS.panic()
super().__init__() super().__init__()
self.HW_MTU = 564 self.HW_MTU = 564
if beacon_data == None: if beacon_data == None:
beacon_data = "" beacon_data = ""
@ -113,17 +113,17 @@ class KISSInterface(Interface):
try: try:
self.open_port() self.open_port()
except Exception as e: 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 raise e
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
else: else:
raise IOError("Could not open serial port") raise OSError("Could not open serial port")
def open_port(self): 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( self.serial = self.pyserial.Serial(
port = self.port, port = self.port,
baudrate = self.speed, baudrate = self.speed,
@ -146,7 +146,7 @@ class KISSInterface(Interface):
thread.daemon = True thread.daemon = True
thread.start() thread.start()
self.online = True 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...") RNS.log("Configuring KISS interface parameters...")
self.setPreamble(self.preamble) self.setPreamble(self.preamble)
self.setTxTail(self.txtail) 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]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXDELAY])+bytes([preamble])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setTxTail(self, txtail):
txtail_ms = 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]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXTAIL])+bytes([txtail])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setPersistence(self, persistence):
if persistence < 0: if persistence < 0:
@ -192,7 +192,7 @@ class KISSInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_P])+bytes([persistence])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_P])+bytes([persistence])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setSlotTime(self, slottime):
slottime_ms = 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]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SLOTTIME])+bytes([slottime])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setFlowControl(self, flow_control):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_READY])+bytes([0x01])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_READY])+bytes([0x01])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
if (flow_control): if (flow_control):
raise IOError("Could not enable KISS interface flow control") raise OSError("Could not enable KISS interface flow control")
else: else:
raise IOError("Could not enable KISS interface flow control") raise OSError("Could not enable KISS interface flow control")
def processIncoming(self, data): def processIncoming(self, data):
self.rxb += len(data) self.rxb += len(data)
self.owner.inbound(data, self) self.owner.inbound(data, self)
@ -244,7 +244,7 @@ class KISSInterface(Interface):
self.first_tx = time.time() self.first_tx = time.time()
if written != len(frame): 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: else:
self.queue(data) self.queue(data)
@ -311,21 +311,21 @@ class KISSInterface(Interface):
if self.flow_control: if self.flow_control:
if not self.interface_ready: if not self.interface_ready:
if time.time() > self.flow_control_locked + self.flow_control_timeout: 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() self.process_queue()
if self.beacon_i != None and self.beacon_d != None: if self.beacon_i != None and self.beacon_d != None:
if self.first_tx != None: if self.first_tx != None:
if time.time() > self.first_tx + self.beacon_i: 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.first_tx = None
self.processOutgoing(self.beacon_d) self.processOutgoing(self.beacon_d)
except Exception as e: except Exception as e:
self.online = False self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR) RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
@ -339,17 +339,17 @@ class KISSInterface(Interface):
while not self.online: while not self.online:
try: try:
time.sleep(5) 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() self.open_port()
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
except Exception as e: 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): def should_ingress_limit(self):
return False return False
def __str__(self): 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 # TODO: Remove at some point
# self.rxptime = 0 # self.rxptime = 0
self.HW_MTU = 1064 self.HW_MTU = 1064
self.online = False self.online = False
self.IN = True self.IN = True
self.OUT = False self.OUT = False
self.socket = None self.socket = None
@ -133,10 +133,10 @@ class LocalClientInterface(Interface):
self.connect() self.connect()
except Exception as e: 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: 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 self.reconnecting = False
thread = threading.Thread(target=self.read_loop) thread = threading.Thread(target=self.read_loop)
@ -146,20 +146,20 @@ class LocalClientInterface(Interface):
time.sleep(LocalClientInterface.RECONNECT_WAIT+2) time.sleep(LocalClientInterface.RECONNECT_WAIT+2)
RNS.Transport.shared_connection_reappeared() RNS.Transport.shared_connection_reappeared()
threading.Thread(target=job, daemon=True).start() threading.Thread(target=job, daemon=True).start()
else: else:
RNS.log("Attempt to reconnect on a non-initiator shared local interface. This should not happen.", RNS.LOG_ERROR) RNS.log("Attempt to reconnect on a non-initiator shared local interface. This should not happen.", RNS.LOG_ERROR)
raise 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): def processIncoming(self, data):
self.rxb += len(data) self.rxb += len(data)
if hasattr(self, "parent_interface") and self.parent_interface != None: if hasattr(self, "parent_interface") and self.parent_interface != None:
self.parent_interface.rxb += len(data) self.parent_interface.rxb += len(data)
# TODO: Remove at some point # TODO: Remove at some point
# processing_start = time.time() # processing_start = time.time()
self.owner.inbound(data, self) self.owner.inbound(data, self)
# TODO: Remove at some point # TODO: Remove at some point
@ -188,8 +188,8 @@ class LocalClientInterface(Interface):
self.parent_interface.txb += len(data) self.parent_interface.txb += len(data)
except Exception as e: except Exception as e:
RNS.log("Exception occurred while transmitting via "+str(self)+", tearing down interface", RNS.LOG_ERROR) RNS.log(f"Exception occurred while transmitting via {self}, tearing down interface", 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.teardown() self.teardown()
@ -226,7 +226,7 @@ class LocalClientInterface(Interface):
else: else:
self.online = False self.online = False
if self.is_connected_to_shared_instance and not self.detached: 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() RNS.Transport.shared_connection_disappeared()
self.reconnect() self.reconnect()
else: else:
@ -234,29 +234,29 @@ class LocalClientInterface(Interface):
break break
except Exception as e: except Exception as e:
self.online = False self.online = False
RNS.log("An interface error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"An interface error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("Tearing down "+str(self), RNS.LOG_ERROR) RNS.log(f"Tearing down {self}", RNS.LOG_ERROR)
self.teardown() self.teardown()
def detach(self): def detach(self):
if self.socket != None: if self.socket != None:
if hasattr(self.socket, "close"): if hasattr(self.socket, "close"):
if callable(self.socket.close): if callable(self.socket.close):
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG) RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
self.detached = True self.detached = True
try: try:
self.socket.shutdown(socket.SHUT_RDWR) self.socket.shutdown(socket.SHUT_RDWR)
except Exception as e: except Exception as e:
RNS.log("Error while shutting down socket for "+str(self)+": "+str(e)) RNS.log(f"Error while shutting down socket for {self}: {e}")
try: try:
self.socket.close() self.socket.close()
except Exception as e: 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 self.socket = None
@ -276,19 +276,19 @@ class LocalClientInterface(Interface):
RNS.Transport.owner._should_persist_data() RNS.Transport.owner._should_persist_data()
if nowarning == False: 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: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
if self.is_connected_to_shared_instance: if self.is_connected_to_shared_instance:
if nowarning == False: if nowarning == False:
RNS.log("Permanently lost connection to local shared RNS instance. Exiting now.", RNS.LOG_CRITICAL) RNS.log("Permanently lost connection to local shared RNS instance. Exiting now.", RNS.LOG_CRITICAL)
RNS.exit() RNS.exit()
def __str__(self): def __str__(self):
return "LocalInterface["+str(self.target_port)+"]" return f"LocalInterface[{self.target_port}]"
class LocalServerInterface(Interface): class LocalServerInterface(Interface):
@ -297,7 +297,7 @@ class LocalServerInterface(Interface):
super().__init__() super().__init__()
self.online = False self.online = False
self.clients = 0 self.clients = 0
self.IN = True self.IN = True
self.OUT = False self.OUT = False
self.name = "Reticulum" self.name = "Reticulum"
@ -360,7 +360,7 @@ class LocalServerInterface(Interface):
if from_spawned: self.oa_freq_deque.append(time.time()) if from_spawned: self.oa_freq_deque.append(time.time())
def __str__(self): def __str__(self):
return "Shared Instance["+str(self.bind_port)+"]" return f"Shared Instance[{self.bind_port}]"
class LocalInterfaceHandler(socketserver.BaseRequestHandler): class LocalInterfaceHandler(socketserver.BaseRequestHandler):
def __init__(self, callback, *args, **keys): def __init__(self, callback, *args, **keys):

View File

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

View File

@ -33,7 +33,7 @@ class KISS():
FESC = 0xDB FESC = 0xDB
TFEND = 0xDC TFEND = 0xDC
TFESC = 0xDD TFESC = 0xDD
CMD_UNKNOWN = 0xFE CMD_UNKNOWN = 0xFE
CMD_DATA = 0x00 CMD_DATA = 0x00
CMD_FREQUENCY = 0x01 CMD_FREQUENCY = 0x01
@ -69,11 +69,11 @@ class KISS():
DETECT_REQ = 0x73 DETECT_REQ = 0x73
DETECT_RESP = 0x46 DETECT_RESP = 0x46
RADIO_STATE_OFF = 0x00 RADIO_STATE_OFF = 0x00
RADIO_STATE_ON = 0x01 RADIO_STATE_ON = 0x01
RADIO_STATE_ASK = 0xFF RADIO_STATE_ASK = 0xFF
CMD_ERROR = 0x90 CMD_ERROR = 0x90
ERROR_INITRADIO = 0x01 ERROR_INITRADIO = 0x01
ERROR_TXFAILED = 0x02 ERROR_TXFAILED = 0x02
@ -91,7 +91,7 @@ class KISS():
data = data.replace(bytes([0xdb]), bytes([0xdb, 0xdd])) data = data.replace(bytes([0xdb]), bytes([0xdb, 0xdd]))
data = data.replace(bytes([0xc0]), bytes([0xdb, 0xdc])) data = data.replace(bytes([0xc0]), bytes([0xdb, 0xdc]))
return data return data
class RNodeInterface(Interface): class RNodeInterface(Interface):
MAX_CHUNK = 32768 MAX_CHUNK = 32768
@ -132,7 +132,7 @@ class RNodeInterface(Interface):
super().__init__() super().__init__()
self.HW_MTU = 508 self.HW_MTU = 508
self.pyserial = serial self.pyserial = serial
self.serial = None self.serial = None
self.owner = owner self.owner = owner
@ -211,31 +211,31 @@ class RNodeInterface(Interface):
self.validcfg = True self.validcfg = True
if (self.frequency < RNodeInterface.FREQ_MIN or self.frequency > RNodeInterface.FREQ_MAX): 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 self.validcfg = False
if (self.txpower < 0 or self.txpower > 22): 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 self.validcfg = False
if (self.bandwidth < 7800 or self.bandwidth > 1625000): 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 self.validcfg = False
if (self.sf < 5 or self.sf > 12): 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 self.validcfg = False
if (self.cr < 5 or self.cr > 8): 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 self.validcfg = False
if (self.st_alock and (self.st_alock < 0.0 or self.st_alock > 100.0)): 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 self.validcfg = False
if (self.lt_alock and (self.lt_alock < 0.0 or self.lt_alock > 100.0)): 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 self.validcfg = False
if id_interval != None and id_callsign != None: 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_callsign = id_callsign.encode("utf-8")
self.id_interval = id_interval self.id_interval = id_interval
else: 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 self.validcfg = False
else: else:
self.id_interval = None self.id_interval = None
self.id_callsign = None self.id_callsign = None
if (not self.validcfg): 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: try:
self.open_port() self.open_port()
@ -259,11 +259,11 @@ class RNodeInterface(Interface):
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
else: else:
raise IOError("Could not open serial port") raise OSError("Could not open serial port")
except Exception as e: 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)
RNS.log("The contained exception was: "+str(e), 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) RNS.log("Reticulum will attempt to bring up this interface periodically", RNS.LOG_ERROR)
if not self.detached and not self.reconnecting: if not self.detached and not self.reconnecting:
thread = threading.Thread(target=self.reconnect_port) thread = threading.Thread(target=self.reconnect_port)
@ -273,7 +273,7 @@ class RNodeInterface(Interface):
def open_port(self): def open_port(self):
if not self.use_ble: 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( self.serial = self.pyserial.Serial(
port = self.port, port = self.port,
baudrate = self.speed, baudrate = self.speed,
@ -287,7 +287,7 @@ class RNodeInterface(Interface):
write_timeout = None, write_timeout = None,
dsrdtr = False, dsrdtr = False,
) )
else: else:
RNS.log(f"Opening BLE connection for {self}...") RNS.log(f"Opening BLE connection for {self}...")
if self.ble == None: if self.ble == None:
@ -325,28 +325,28 @@ class RNodeInterface(Interface):
detect_time = RNS.prettytime(time.time()-detect_time) detect_time = RNS.prettytime(time.time()-detect_time)
else: else:
RNS.log(f"RNode detect timed out over {self.port}", RNS.LOG_ERROR) RNS.log(f"RNode detect timed out over {self.port}", RNS.LOG_ERROR)
if not self.detected: if not self.detected:
RNS.log("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() self.serial.close()
else: else:
if self.platform == KISS.PLATFORM_ESP32 or self.platform == KISS.PLATFORM_NRF52: if self.platform == KISS.PLATFORM_ESP32 or self.platform == KISS.PLATFORM_NRF52:
self.display = True 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) RNS.log("Configuring RNode interface...", RNS.LOG_VERBOSE)
self.initRadio() self.initRadio()
if (self.validateRadioState()): if (self.validateRadioState()):
self.interface_ready = True 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) sleep(0.3)
self.online = True self.online = True
else: 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("Make sure that your hardware actually supports the parameters specified in the configuration", RNS.LOG_ERROR)
RNS.log("Aborting RNode startup", RNS.LOG_ERROR) RNS.log("Aborting RNode startup", RNS.LOG_ERROR)
self.serial.close() self.serial.close()
def initRadio(self): def initRadio(self):
self.setFrequency() self.setFrequency()
@ -365,27 +365,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]) 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) written = self.serial.write(kiss_command)
if written != len(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): def leave(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def enable_external_framebuffer(self):
if self.display != None: if self.display != None:
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x01, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x01, KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(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): def disable_external_framebuffer(self):
if self.display != None: if self.display != None:
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x00, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x00, KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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_PIXEL_WIDTH = 64
FB_BITS_PER_PIXEL = 1 FB_BITS_PER_PIXEL = 1
@ -406,16 +406,16 @@ class RNodeInterface(Interface):
data = line_byte+line_data data = line_byte+line_data
escaped_data = KISS.escape(data) escaped_data = KISS.escape(data)
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FB_WRITE])+escaped_data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FB_WRITE])+escaped_data+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def hard_reset(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while restarting device") raise OSError("An IO error occurred while restarting device")
sleep(2.25); sleep(2.25);
def setFrequency(self): def setFrequency(self):
@ -428,7 +428,7 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FREQUENCY])+data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FREQUENCY])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setBandwidth(self):
c1 = self.bandwidth >> 24 c1 = self.bandwidth >> 24
@ -440,28 +440,28 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_BANDWIDTH])+data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_BANDWIDTH])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setTXPower(self):
txp = bytes([self.txpower]) txp = bytes([self.txpower])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXPOWER])+txp+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_TXPOWER])+txp+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setSpreadingFactor(self):
sf = bytes([self.sf]) sf = bytes([self.sf])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SF])+sf+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_SF])+sf+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setCodingRate(self):
cr = bytes([self.cr]) cr = bytes([self.cr])
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_CR])+cr+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_CR])+cr+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setSTALock(self):
if self.st_alock != None: if self.st_alock != None:
@ -473,7 +473,7 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_ST_ALOCK])+data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_ST_ALOCK])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setLTALock(self):
if self.lt_alock != None: if self.lt_alock != None:
@ -485,14 +485,14 @@ class RNodeInterface(Interface):
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_LT_ALOCK])+data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_LT_ALOCK])+data+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def setRadioState(self, state):
self.state = state self.state = state
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_RADIO_STATE])+bytes([state])+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def validate_firmware(self):
if (self.maj_version > RNodeInterface.REQUIRED_FW_VER_MAJ): if (self.maj_version > RNodeInterface.REQUIRED_FW_VER_MAJ):
@ -501,18 +501,18 @@ class RNodeInterface(Interface):
if (self.maj_version >= RNodeInterface.REQUIRED_FW_VER_MAJ): if (self.maj_version >= RNodeInterface.REQUIRED_FW_VER_MAJ):
if (self.min_version >= RNodeInterface.REQUIRED_FW_VER_MIN): if (self.min_version >= RNodeInterface.REQUIRED_FW_VER_MIN):
self.firmware_ok = True self.firmware_ok = True
if self.firmware_ok: if self.firmware_ok:
return return
RNS.log("The firmware version of the connected RNode is "+str(self.maj_version)+"."+str(self.min_version), 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("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"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.log("Please update your RNode firmware with rnodeconf from https://github.com/markqvist/rnodeconfigutil/")
RNS.panic() RNS.panic()
def validateRadioState(self): 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); sleep(0.25);
self.validcfg = True self.validcfg = True
@ -542,7 +542,7 @@ class RNodeInterface(Interface):
try: try:
self.bitrate = self.r_sf * ( (4.0/self.r_cr) / (math.pow(2,self.r_sf)/(self.r_bandwidth/1000)) ) * 1000 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) 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: except:
self.bitrate = 0 self.bitrate = 0
@ -573,7 +573,7 @@ class RNodeInterface(Interface):
self.txb += datalen self.txb += datalen
if written != len(frame): 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: else:
self.queue(data) self.queue(data)
@ -639,7 +639,7 @@ class RNodeInterface(Interface):
command_buffer = command_buffer+bytes([byte]) command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4): if (len(command_buffer) == 4):
self.r_frequency = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3] 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() self.updateBitrate()
elif (command == KISS.CMD_BANDWIDTH): elif (command == KISS.CMD_BANDWIDTH):
@ -655,26 +655,26 @@ class RNodeInterface(Interface):
command_buffer = command_buffer+bytes([byte]) command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4): if (len(command_buffer) == 4):
self.r_bandwidth = command_buffer[0] << 24 | command_buffer[1] << 16 | command_buffer[2] << 8 | command_buffer[3] 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() self.updateBitrate()
elif (command == KISS.CMD_TXPOWER): elif (command == KISS.CMD_TXPOWER):
self.r_txpower = byte 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): elif (command == KISS.CMD_SF):
self.r_sf = byte 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() self.updateBitrate()
elif (command == KISS.CMD_CR): elif (command == KISS.CMD_CR):
self.r_cr = byte 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() self.updateBitrate()
elif (command == KISS.CMD_RADIO_STATE): elif (command == KISS.CMD_RADIO_STATE):
self.r_state = byte self.r_state = byte
if self.r_state: if self.r_state:
pass pass
else: 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): elif (command == KISS.CMD_RADIO_LOCK):
self.r_lock = byte self.r_lock = byte
@ -752,7 +752,7 @@ class RNodeInterface(Interface):
if (len(command_buffer) == 2): if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1] at = command_buffer[0] << 8 | command_buffer[1]
self.r_st_alock = at/100.0 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): elif (command == KISS.CMD_LT_ALOCK):
if (byte == KISS.FESC): if (byte == KISS.FESC):
escape = True escape = True
@ -767,7 +767,7 @@ class RNodeInterface(Interface):
if (len(command_buffer) == 2): if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1] at = command_buffer[0] << 8 | command_buffer[1]
self.r_lt_alock = at/100.0 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): elif (command == KISS.CMD_STAT_CHTM):
if (byte == KISS.FESC): if (byte == KISS.FESC):
escape = True escape = True
@ -784,7 +784,7 @@ class RNodeInterface(Interface):
atl = command_buffer[2] << 8 | command_buffer[3] atl = command_buffer[2] << 8 | command_buffer[3]
cus = command_buffer[4] << 8 | command_buffer[5] cus = command_buffer[4] << 8 | command_buffer[5]
cul = command_buffer[6] << 8 | command_buffer[7] cul = command_buffer[6] << 8 | command_buffer[7]
self.r_airtime_short = ats/100.0 self.r_airtime_short = ats/100.0
self.r_airtime_long = atl/100.0 self.r_airtime_long = atl/100.0
self.r_channel_load_short = cus/100.0 self.r_channel_load_short = cus/100.0
@ -813,9 +813,9 @@ class RNodeInterface(Interface):
self.r_preamble_symbols = prs self.r_preamble_symbols = prs
self.r_premable_time_ms = prt self.r_premable_time_ms = prt
self.r_csma_slot_time_ms = cst 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(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(str(self)+" Radio reporting preamble is "+str(self.r_preamble_symbols)+" symbols ("+str(self.r_premable_time_ms)+"ms)", 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(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 CSMA slot time is {self.r_csma_slot_time_ms}ms", RNS.LOG_DEBUG)
elif (command == KISS.CMD_STAT_BAT): elif (command == KISS.CMD_STAT_BAT):
if (byte == KISS.FESC): if (byte == KISS.FESC):
escape = True escape = True
@ -843,26 +843,26 @@ class RNodeInterface(Interface):
self.mcu = byte self.mcu = byte
elif (command == KISS.CMD_ERROR): elif (command == KISS.CMD_ERROR):
if (byte == KISS.ERROR_INITRADIO): if (byte == KISS.ERROR_INITRADIO):
RNS.log(str(self)+" hardware initialisation error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) RNS.log(f"{self} hardware initialisation error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise IOError("Radio initialisation failure") raise OSError("Radio initialisation failure")
elif (byte == KISS.ERROR_TXFAILED): elif (byte == KISS.ERROR_TXFAILED):
RNS.log(str(self)+" hardware TX error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) RNS.log(f"{self} hardware TX error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise IOError("Hardware transmit failure") raise OSError("Hardware transmit failure")
elif (byte == KISS.ERROR_MEMORY_LOW): 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"}) self.hw_errors.append({"error": KISS.ERROR_MEMORY_LOW, "description": "Memory exhausted on connected device"})
elif (byte == KISS.ERROR_MODEM_TIMEOUT): 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"}) self.hw_errors.append({"error": KISS.ERROR_MODEM_TIMEOUT, "description": "Modem communication timed out on connected device"})
else: else:
RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) RNS.log(f"{self} hardware error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise IOError("Unknown hardware failure") raise OSError("Unknown hardware failure")
elif (command == KISS.CMD_RESET): elif (command == KISS.CMD_RESET):
if (byte == 0xF8): if (byte == 0xF8):
if self.platform == KISS.PLATFORM_ESP32: if self.platform == KISS.PLATFORM_ESP32:
if self.online: if self.online:
RNS.log("Detected reset while device was online, reinitialising device...", RNS.LOG_ERROR) 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): elif (command == KISS.CMD_READY):
self.process_queue() self.process_queue()
elif (command == KISS.CMD_DETECT): elif (command == KISS.CMD_DETECT):
@ -870,11 +870,11 @@ class RNodeInterface(Interface):
self.detected = True self.detected = True
else: else:
self.detected = False self.detected = False
else: else:
time_since_last = int(time.time()*1000) - last_read_ms time_since_last = int(time.time()*1000) - last_read_ms
if len(data_buffer) > 0 and time_since_last > self.timeout: if len(data_buffer) > 0 and time_since_last > self.timeout:
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"" data_buffer = b""
in_frame = False in_frame = False
command = KISS.CMD_UNKNOWN command = KISS.CMD_UNKNOWN
@ -883,15 +883,15 @@ class RNodeInterface(Interface):
if self.id_interval != None and self.id_callsign != None: if self.id_interval != None and self.id_callsign != None:
if self.first_tx != None: if self.first_tx != None:
if time.time() > self.first_tx + self.id_interval: 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) self.processOutgoing(self.id_callsign)
sleep(0.08) sleep(0.08)
except Exception as e: except Exception as e:
self.online = False self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR) RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
@ -912,23 +912,23 @@ class RNodeInterface(Interface):
while not self.online and not self.detached: while not self.online and not self.detached:
try: try:
time.sleep(5) 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() self.open_port()
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
except Exception as e: 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 self.reconnecting = False
if self.online: if self.online:
RNS.log("Reconnected serial port for "+str(self)) RNS.log(f"Reconnected serial port for {self}")
def detach(self): def detach(self):
self.detached = True self.detached = True
self.disable_external_framebuffer() self.disable_external_framebuffer()
self.setRadioState(KISS.RADIO_STATE_OFF) self.setRadioState(KISS.RADIO_STATE_OFF)
self.leave() self.leave()
if self.use_ble: if self.use_ble:
self.ble.close() self.ble.close()
@ -965,7 +965,7 @@ class RNodeInterface(Interface):
return data return data
def __str__(self): def __str__(self):
return "RNodeInterface["+str(self.name)+"]" return f"RNodeInterface[{self.name}]"
class BLEConnection(): class BLEConnection():
UART_SERVICE_UUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" UART_SERVICE_UUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
@ -1021,7 +1021,7 @@ class BLEConnection():
if importlib.util.find_spec("bleak") != None: if importlib.util.find_spec("bleak") != None:
import bleak import bleak
BLEConnection.bleak = bleak BLEConnection.bleak = bleak
import asyncio import asyncio
BLEConnection.asyncio = asyncio BLEConnection.asyncio = asyncio
else: else:
@ -1100,7 +1100,7 @@ class BLEConnection():
else: else:
if self.target_bt_addr != None and device.address == self.target_bt_addr: if self.target_bt_addr != None and device.address == self.target_bt_addr:
RNS.log(f"Can't connect to target device {self.target_bt_addr} over BLE, device is not bonded", RNS.LOG_ERROR) RNS.log(f"Can't connect to target device {self.target_bt_addr} over BLE, device is not bonded", RNS.LOG_ERROR)
elif self.target_name != None and device.name == self.target_name: elif self.target_name != None and device.name == self.target_name:
RNS.log(f"Can't connect to target device {self.target_name} over BLE, device is not bonded", RNS.LOG_ERROR) RNS.log(f"Can't connect to target device {self.target_name} over BLE, device is not bonded", RNS.LOG_ERROR)
@ -1115,7 +1115,7 @@ class BLEConnection():
if "props" in device.details and "Bonded" in device.details["props"]: if "props" in device.details and "Bonded" in device.details["props"]:
if device.details["props"]["Bonded"] == True: if device.details["props"]["Bonded"] == True:
return True return True
except Exception as e: except Exception as e:
RNS.log(f"Error while determining device bond status for {device}, the contained exception was: {e}", RNS.LOG_ERROR) RNS.log(f"Error while determining device bond status for {device}, the contained exception was: {e}", RNS.LOG_ERROR)

View File

@ -33,7 +33,7 @@ class KISS():
FESC = 0xDB FESC = 0xDB
TFEND = 0xDC TFEND = 0xDC
TFESC = 0xDD TFESC = 0xDD
CMD_UNKNOWN = 0xFE CMD_UNKNOWN = 0xFE
CMD_FREQUENCY = 0x01 CMD_FREQUENCY = 0x01
CMD_BANDWIDTH = 0x02 CMD_BANDWIDTH = 0x02
@ -94,11 +94,11 @@ class KISS():
DETECT_REQ = 0x73 DETECT_REQ = 0x73
DETECT_RESP = 0x46 DETECT_RESP = 0x46
RADIO_STATE_OFF = 0x00 RADIO_STATE_OFF = 0x00
RADIO_STATE_ON = 0x01 RADIO_STATE_ON = 0x01
RADIO_STATE_ASK = 0xFF RADIO_STATE_ASK = 0xFF
CMD_ERROR = 0x90 CMD_ERROR = 0x90
ERROR_INITRADIO = 0x01 ERROR_INITRADIO = 0x01
ERROR_TXFAILED = 0x02 ERROR_TXFAILED = 0x02
@ -159,7 +159,7 @@ class KISS():
data = data.replace(bytes([0xdb]), bytes([0xdb, 0xdd])) data = data.replace(bytes([0xdb]), bytes([0xdb, 0xdd]))
data = data.replace(bytes([0xc0]), bytes([0xdb, 0xdc])) data = data.replace(bytes([0xc0]), bytes([0xdb, 0xdc]))
return data return data
class RNodeMultiInterface(Interface): class RNodeMultiInterface(Interface):
MAX_CHUNK = 32768 MAX_CHUNK = 32768
@ -188,7 +188,7 @@ class RNodeMultiInterface(Interface):
super().__init__() super().__init__()
self.HW_MTU = 508 self.HW_MTU = 508
self.clients = 0 self.clients = 0
self.pyserial = serial self.pyserial = serial
self.serial = None self.serial = None
@ -241,14 +241,14 @@ class RNodeMultiInterface(Interface):
self.id_callsign = id_callsign.encode("utf-8") self.id_callsign = id_callsign.encode("utf-8")
self.id_interval = id_interval self.id_interval = id_interval
else: 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 self.validcfg = False
else: else:
self.id_interval = None self.id_interval = None
self.id_callsign = None self.id_callsign = None
if (not self.validcfg): 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): def start(self):
try: try:
@ -257,11 +257,11 @@ class RNodeMultiInterface(Interface):
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
else: else:
raise IOError("Could not open serial port") raise OSError("Could not open serial port")
except Exception as e: 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)
RNS.log("The contained exception was: "+str(e), 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) RNS.log("Reticulum will attempt to bring up this interface periodically", RNS.LOG_ERROR)
if not self.detached and not self.reconnecting: if not self.detached and not self.reconnecting:
thread = threading.Thread(target=self.reconnect_port) thread = threading.Thread(target=self.reconnect_port)
@ -269,7 +269,7 @@ class RNodeMultiInterface(Interface):
thread.start() thread.start()
def open_port(self): def open_port(self):
RNS.log("Opening serial port "+self.port+"...") RNS.log(f"Opening serial port {self.port}...")
self.serial = self.pyserial.Serial( self.serial = self.pyserial.Serial(
port = self.port, port = self.port,
baudrate = self.speed, baudrate = self.speed,
@ -294,15 +294,15 @@ class RNodeMultiInterface(Interface):
self.detect() self.detect()
sleep(0.2) sleep(0.2)
if not self.detected: 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() self.serial.close()
else: else:
if self.platform == KISS.PLATFORM_ESP32 or self.platform == KISS.PLATFORM_NRF52: if self.platform == KISS.PLATFORM_ESP32 or self.platform == KISS.PLATFORM_NRF52:
self.display = True 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) RNS.log("Creating subinterfaces...", RNS.LOG_VERBOSE)
for subint in self.subint_config: for subint in self.subint_config:
subint_vport = int(subint[1]) subint_vport = int(subint[1])
@ -327,44 +327,44 @@ class RNodeMultiInterface(Interface):
interface.OUT = subint[10] interface.OUT = subint[10]
interface.IN = True interface.IN = True
interface.announce_rate_target = self.announce_rate_target interface.announce_rate_target = self.announce_rate_target
interface.mode = self.mode interface.mode = self.mode
interface.HW_MTU = self.HW_MTU interface.HW_MTU = self.HW_MTU
interface.detected = True interface.detected = True
RNS.Transport.interfaces.append(interface) 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 self.clients += 1
else: 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 self.online = True
def detect(self): 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]) 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) written = self.serial.write(kiss_command)
if written != len(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): def leave(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_LEAVE, 0xFF, KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def enable_external_framebuffer(self):
if self.display != None: if self.display != None:
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x01, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x01, KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(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): def disable_external_framebuffer(self):
if self.display != None: if self.display != None:
kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x00, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_FB_EXT, 0x00, KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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_PIXEL_WIDTH = 64
FB_BITS_PER_PIXEL = 1 FB_BITS_PER_PIXEL = 1
@ -385,16 +385,16 @@ class RNodeMultiInterface(Interface):
data = line_byte+line_data data = line_byte+line_data
escaped_data = KISS.escape(data) escaped_data = KISS.escape(data)
kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FB_WRITE])+escaped_data+bytes([KISS.FEND]) kiss_command = bytes([KISS.FEND])+bytes([KISS.CMD_FB_WRITE])+escaped_data+bytes([KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise 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): def hard_reset(self):
kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND]) kiss_command = bytes([KISS.FEND, KISS.CMD_RESET, 0xf8, KISS.FEND])
written = self.serial.write(kiss_command) written = self.serial.write(kiss_command)
if written != len(kiss_command): if written != len(kiss_command):
raise IOError("An IO error occurred while restarting device") raise OSError("An IO error occurred while restarting device")
sleep(2.25); sleep(2.25);
def setFrequency(self, frequency, interface): 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]) 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) written = self.serial.write(kiss_command)
if written != len(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 self.selected_index = interface.index
def setBandwidth(self, bandwidth, interface): 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]) 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) written = self.serial.write(kiss_command)
if written != len(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 self.selected_index = interface.index
def setTXPower(self, txpower, interface): 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]) 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) written = self.serial.write(kiss_command)
if written != len(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 self.selected_index = interface.index
def setSpreadingFactor(self, sf, interface): 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]) 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) written = self.serial.write(kiss_command)
if written != len(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 self.selected_index = interface.index
def setCodingRate(self, cr, interface): 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]) 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) written = self.serial.write(kiss_command)
if written != len(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 self.selected_index = interface.index
def setSTALock(self, st_alock, interface): 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]) 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) written = self.serial.write(kiss_command)
if written != len(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 self.selected_index = interface.index
def setLTALock(self, lt_alock, interface): 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]) 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) written = self.serial.write(kiss_command)
if written != len(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 self.selected_index = interface.index
def setRadioState(self, state, interface): 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]) 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) written = self.serial.write(kiss_command)
if written != len(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 self.selected_index = interface.index
def validate_firmware(self): def validate_firmware(self):
if (self.maj_version >= RNodeMultiInterface.REQUIRED_FW_VER_MAJ): if (self.maj_version >= RNodeMultiInterface.REQUIRED_FW_VER_MAJ):
if (self.min_version >= RNodeMultiInterface.REQUIRED_FW_VER_MIN): if (self.min_version >= RNodeMultiInterface.REQUIRED_FW_VER_MIN):
self.firmware_ok = True self.firmware_ok = True
if self.firmware_ok: if self.firmware_ok:
return return
RNS.log("The firmware version of the connected RNode is "+str(self.maj_version)+"."+str(self.min_version), 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("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"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.log("Please update your RNode firmware with rnodeconf from https://github.com/markqvist/Reticulum/RNS/Utilities/rnodeconf.py")
RNS.panic() RNS.panic()
@ -506,7 +506,7 @@ class RNodeMultiInterface(Interface):
self.txb += len(data) self.txb += len(data)
if written != len(frame): 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): def received_announce(self, from_spawned=False):
if from_spawned: self.ia_freq_deque.append(time.time()) if from_spawned: self.ia_freq_deque.append(time.time())
@ -589,7 +589,7 @@ class RNodeMultiInterface(Interface):
command_buffer = command_buffer+bytes([byte]) command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4): 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] 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() self.subinterfaces[self.selected_index].updateBitrate()
elif (command == KISS.CMD_BANDWIDTH): elif (command == KISS.CMD_BANDWIDTH):
@ -605,20 +605,20 @@ class RNodeMultiInterface(Interface):
command_buffer = command_buffer+bytes([byte]) command_buffer = command_buffer+bytes([byte])
if (len(command_buffer) == 4): 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] 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() self.subinterfaces[self.selected_index].updateBitrate()
elif (command == KISS.CMD_TXPOWER): elif (command == KISS.CMD_TXPOWER):
txp = byte - 256 if byte > 127 else byte txp = byte - 256 if byte > 127 else byte
self.subinterfaces[self.selected_index].r_txpower = txp 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): elif (command == KISS.CMD_SF):
self.subinterfaces[self.selected_index].r_sf = byte 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() self.subinterfaces[self.selected_index].updateBitrate()
elif (command == KISS.CMD_CR): elif (command == KISS.CMD_CR):
self.subinterfaces[self.selected_index].r_cr = byte 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() self.subinterfaces[self.selected_index].updateBitrate()
elif (command == KISS.CMD_RADIO_STATE): elif (command == KISS.CMD_RADIO_STATE):
self.subinterfaces[self.selected_index].r_state = byte self.subinterfaces[self.selected_index].r_state = byte
@ -626,7 +626,7 @@ class RNodeMultiInterface(Interface):
pass pass
#RNS.log(str(self)+" Radio reporting state is online", RNS.LOG_DEBUG) #RNS.log(str(self)+" Radio reporting state is online", RNS.LOG_DEBUG)
else: 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): elif (command == KISS.CMD_RADIO_LOCK):
self.subinterfaces[self.selected_index].r_lock = byte self.subinterfaces[self.selected_index].r_lock = byte
@ -705,7 +705,7 @@ class RNodeMultiInterface(Interface):
if (len(command_buffer) == 2): if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1] at = command_buffer[0] << 8 | command_buffer[1]
self.subinterfaces[self.selected_index].r_st_alock = at/100.0 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): elif (command == KISS.CMD_LT_ALOCK):
if (byte == KISS.FESC): if (byte == KISS.FESC):
escape = True escape = True
@ -720,7 +720,7 @@ class RNodeMultiInterface(Interface):
if (len(command_buffer) == 2): if (len(command_buffer) == 2):
at = command_buffer[0] << 8 | command_buffer[1] at = command_buffer[0] << 8 | command_buffer[1]
self.subinterfaces[self.selected_index].r_lt_alock = at/100.0 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): elif (command == KISS.CMD_STAT_CHTM):
if (byte == KISS.FESC): if (byte == KISS.FESC):
escape = True escape = True
@ -737,7 +737,7 @@ class RNodeMultiInterface(Interface):
atl = command_buffer[2] << 8 | command_buffer[3] atl = command_buffer[2] << 8 | command_buffer[3]
cus = command_buffer[4] << 8 | command_buffer[5] cus = command_buffer[4] << 8 | command_buffer[5]
cul = command_buffer[6] << 8 | command_buffer[7] cul = command_buffer[6] << 8 | command_buffer[7]
self.r_airtime_short = ats/100.0 self.r_airtime_short = ats/100.0
self.r_airtime_long = atl/100.0 self.r_airtime_long = atl/100.0
self.r_channel_load_short = cus/100.0 self.r_channel_load_short = cus/100.0
@ -766,9 +766,9 @@ class RNodeMultiInterface(Interface):
self.subinterfaces[self.selected_index].r_preamble_symbols = prs 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_premable_time_ms = prt
self.subinterfaces[self.selected_index].r_csma_slot_time_ms = cst 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(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(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(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(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 CSMA slot time is {self.subinterfaces[self.selected_index].r_csma_slot_time_ms}ms", RNS.LOG_DEBUG)
elif (command == KISS.CMD_RANDOM): elif (command == KISS.CMD_RANDOM):
self.r_random = byte self.r_random = byte
elif (command == KISS.CMD_PLATFORM): elif (command == KISS.CMD_PLATFORM):
@ -777,20 +777,20 @@ class RNodeMultiInterface(Interface):
self.mcu = byte self.mcu = byte
elif (command == KISS.CMD_ERROR): elif (command == KISS.CMD_ERROR):
if (byte == KISS.ERROR_INITRADIO): if (byte == KISS.ERROR_INITRADIO):
RNS.log(str(self)+" hardware initialisation error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) RNS.log(f"{self} hardware initialisation error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise IOError("Radio initialisation failure") raise OSError("Radio initialisation failure")
elif (byte == KISS.ERROR_TXFAILED): elif (byte == KISS.ERROR_TXFAILED):
RNS.log(str(self)+" hardware TX error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) RNS.log(f"{self} hardware TX error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise IOError("Hardware transmit failure") raise OSError("Hardware transmit failure")
else: else:
RNS.log(str(self)+" hardware error (code "+RNS.hexrep(byte)+")", RNS.LOG_ERROR) RNS.log(f"{self} hardware error (code {RNS.hexrep(byte)})", RNS.LOG_ERROR)
raise IOError("Unknown hardware failure") raise OSError("Unknown hardware failure")
elif (command == KISS.CMD_RESET): elif (command == KISS.CMD_RESET):
if (byte == 0xF8): if (byte == 0xF8):
if self.platform == KISS.PLATFORM_ESP32: if self.platform == KISS.PLATFORM_ESP32:
if self.online: if self.online:
RNS.log("Detected reset while device was online, reinitialising device...", RNS.LOG_ERROR) 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): elif (command == KISS.CMD_READY):
self.process_queue() self.process_queue()
elif (command == KISS.CMD_DETECT): 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 # add the interface to the back of the list, they're all given from vport 0 and up in order
self.subinterface_types.append(KISS.interface_type_to_str(command_buffer[1])) self.subinterface_types.append(KISS.interface_type_to_str(command_buffer[1]))
command_buffer = b"" command_buffer = b""
else: else:
time_since_last = int(time.time()*1000) - last_read_ms time_since_last = int(time.time()*1000) - last_read_ms
if len(data_buffer) > 0 and time_since_last > self.timeout: if len(data_buffer) > 0 and time_since_last > self.timeout:
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"" data_buffer = b""
in_frame = False in_frame = False
command = KISS.CMD_UNKNOWN command = KISS.CMD_UNKNOWN
@ -824,14 +824,14 @@ class RNodeMultiInterface(Interface):
self.subinterfaces[interface.index].processOutgoing(self.id_callsign) self.subinterfaces[interface.index].processOutgoing(self.id_callsign)
if interface_available: 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) sleep(0.08)
except Exception as e: except Exception as e:
self.online = False self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR) RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
@ -854,16 +854,16 @@ class RNodeMultiInterface(Interface):
while not self.online and not self.detached: while not self.online and not self.detached:
try: try:
time.sleep(5) 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() self.open_port()
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
except Exception as e: 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 self.reconnecting = False
if self.online: if self.online:
RNS.log("Reconnected serial port for "+str(self)) RNS.log(f"Reconnected serial port for {self}")
def detach(self): def detach(self):
self.detached = True self.detached = True
@ -890,7 +890,7 @@ class RNodeMultiInterface(Interface):
interface.process_queue() interface.process_queue()
def __str__(self): def __str__(self):
return "RNodeMultiInterface["+str(self.name)+"]" return f"RNodeMultiInterface[{self.name}]"
class RNodeSubInterface(Interface): class RNodeSubInterface(Interface):
LOW_FREQ_MIN = 137000000 LOW_FREQ_MIN = 137000000
@ -918,7 +918,7 @@ class RNodeSubInterface(Interface):
RNS.panic() RNS.panic()
super().__init__() super().__init__()
if index == 0: if index == 0:
sel_cmd = KISS.CMD_SEL_INT0 sel_cmd = KISS.CMD_SEL_INT0
data_cmd= KISS.CMD_INT0_DATA data_cmd= KISS.CMD_INT0_DATA
@ -1019,42 +1019,42 @@ class RNodeSubInterface(Interface):
self.validcfg = True self.validcfg = True
if (self.interface_type == "SX126X" or self.interface_type == "SX127X"): if (self.interface_type == "SX126X" or self.interface_type == "SX127X"):
if (self.frequency < RNodeSubInterface.LOW_FREQ_MIN or self.frequency > RNodeSubInterface.LOW_FREQ_MAX): 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 self.validcfg = False
elif (self.interface_type == "SX128X"): elif (self.interface_type == "SX128X"):
if (self.frequency < RNodeSubInterface.HIGH_FREQ_MIN or self.frequency > RNodeSubInterface.HIGH_FREQ_MAX): 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 self.validcfg = False
else: 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 self.validcfg = False
if (self.txpower < -9 or self.txpower > 27): 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 self.validcfg = False
if (self.bandwidth < 7800 or self.bandwidth > 1625000): 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 self.validcfg = False
if (self.sf < 5 or self.sf > 12): 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 self.validcfg = False
if (self.cr < 5 or self.cr > 8): 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 self.validcfg = False
if (self.st_alock and (self.st_alock < 0.0 or self.st_alock > 100.0)): 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 self.validcfg = False
if (self.lt_alock and (self.lt_alock < 0.0 or self.lt_alock > 100.0)): 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 self.validcfg = False
if (not self.validcfg): 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() self.configure_device()
@ -1068,18 +1068,18 @@ class RNodeSubInterface(Interface):
self.r_lock = None self.r_lock = None
sleep(2.0) 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() self.initRadio()
if (self.validateRadioState()): if (self.validateRadioState()):
self.interface_ready = True 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) sleep(0.3)
self.online = True self.online = True
else: 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("Make sure that your hardware actually supports the parameters specified in the configuration", RNS.LOG_ERROR)
RNS.log("Aborting RNode startup", RNS.LOG_ERROR) RNS.log("Aborting RNode startup", RNS.LOG_ERROR)
def initRadio(self): def initRadio(self):
self.parent_interface.setFrequency(self.frequency, self) self.parent_interface.setFrequency(self.frequency, self)
@ -1093,7 +1093,7 @@ class RNodeSubInterface(Interface):
self.state = KISS.RADIO_STATE_ON self.state = KISS.RADIO_STATE_ON
def validateRadioState(self): 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); sleep(0.25);
self.validcfg = True self.validcfg = True
@ -1123,7 +1123,7 @@ class RNodeSubInterface(Interface):
try: try:
self.bitrate = self.r_sf * ( (4.0/self.r_cr) / (math.pow(2,self.r_sf)/(self.r_bandwidth/1000)) ) * 1000 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) 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: except:
self.bitrate = 0 self.bitrate = 0
@ -1162,4 +1162,4 @@ class RNodeSubInterface(Interface):
self.interface_ready = True self.interface_ready = True
def __str__(self): 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__() super().__init__()
self.HW_MTU = 564 self.HW_MTU = 564
self.pyserial = serial self.pyserial = serial
self.serial = None self.serial = None
self.owner = owner self.owner = owner
@ -86,17 +86,17 @@ class SerialInterface(Interface):
try: try:
self.open_port() self.open_port()
except Exception as e: 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 raise e
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
else: else:
raise IOError("Could not open serial port") raise OSError("Could not open serial port")
def open_port(self): 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( self.serial = self.pyserial.Serial(
port = self.port, port = self.port,
baudrate = self.speed, baudrate = self.speed,
@ -118,11 +118,11 @@ class SerialInterface(Interface):
thread.daemon = True thread.daemon = True
thread.start() thread.start()
self.online = True 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): def processIncoming(self, data):
self.rxb += len(data) self.rxb += len(data)
self.owner.inbound(data, self) self.owner.inbound(data, self)
@ -130,9 +130,9 @@ class SerialInterface(Interface):
if self.online: if self.online:
data = bytes([HDLC.FLAG])+HDLC.escape(data)+bytes([HDLC.FLAG]) data = bytes([HDLC.FLAG])+HDLC.escape(data)+bytes([HDLC.FLAG])
written = self.serial.write(data) written = self.serial.write(data)
self.txb += len(data) self.txb += len(data)
if written != len(data): if written != len(data):
raise 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): def readLoop(self):
@ -164,7 +164,7 @@ class SerialInterface(Interface):
byte = HDLC.ESC byte = HDLC.ESC
escape = False escape = False
data_buffer = data_buffer+bytes([byte]) data_buffer = data_buffer+bytes([byte])
else: else:
time_since_last = int(time.time()*1000) - last_read_ms time_since_last = int(time.time()*1000) - last_read_ms
if len(data_buffer) > 0 and time_since_last > self.timeout: if len(data_buffer) > 0 and time_since_last > self.timeout:
@ -172,12 +172,12 @@ class SerialInterface(Interface):
in_frame = False in_frame = False
escape = False escape = False
sleep(0.08) sleep(0.08)
except Exception as e: except Exception as e:
self.online = False self.online = False
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR) RNS.log(f"A serial port error occurred, the contained exception was: {e}", RNS.LOG_ERROR)
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR) RNS.log(f"The interface {self} experienced an unrecoverable error and is now offline.", RNS.LOG_ERROR)
if RNS.Reticulum.panic_on_interface_error: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
@ -191,17 +191,17 @@ class SerialInterface(Interface):
while not self.online: while not self.online:
try: try:
time.sleep(5) 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() self.open_port()
if self.serial.is_open: if self.serial.is_open:
self.configure_device() self.configure_device()
except Exception as e: 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): def should_ingress_limit(self):
return False return False
def __str__(self): 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): def __init__(self, owner, name, target_ip=None, target_port=None, connected_socket=None, max_reconnect_tries=None, kiss_framing=False, i2p_tunneled = False, connect_timeout = None):
super().__init__() super().__init__()
self.HW_MTU = 1064 self.HW_MTU = 1064
self.IN = True self.IN = True
self.OUT = False self.OUT = False
self.socket = None self.socket = None
@ -99,7 +99,7 @@ class TCPClientInterface(Interface):
self.i2p_tunneled = i2p_tunneled self.i2p_tunneled = i2p_tunneled
self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL
self.bitrate = TCPClientInterface.BITRATE_GUESS self.bitrate = TCPClientInterface.BITRATE_GUESS
if max_reconnect_tries == None: if max_reconnect_tries == None:
self.max_reconnect_tries = TCPClientInterface.RECONNECT_MAX_TRIES self.max_reconnect_tries = TCPClientInterface.RECONNECT_MAX_TRIES
else: else:
@ -128,14 +128,14 @@ class TCPClientInterface(Interface):
self.connect_timeout = connect_timeout self.connect_timeout = connect_timeout
else: else:
self.connect_timeout = TCPClientInterface.INITIAL_CONNECT_TIMEOUT self.connect_timeout = TCPClientInterface.INITIAL_CONNECT_TIMEOUT
if TCPClientInterface.SYNCHRONOUS_START: if TCPClientInterface.SYNCHRONOUS_START:
self.initial_connect() self.initial_connect()
else: else:
thread = threading.Thread(target=self.initial_connect) thread = threading.Thread(target=self.initial_connect)
thread.daemon = True thread.daemon = True
thread.start() thread.start()
def initial_connect(self): def initial_connect(self):
if not self.connect(initial=True): if not self.connect(initial=True):
thread = threading.Thread(target=self.reconnect) thread = threading.Thread(target=self.reconnect)
@ -170,35 +170,35 @@ class TCPClientInterface(Interface):
TCP_KEEPIDLE = 0x10 TCP_KEEPIDLE = 0x10
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
if not self.i2p_tunneled: if not self.i2p_tunneled:
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(TCPClientInterface.TCP_PROBE_AFTER)) self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(TCPClientInterface.TCP_PROBE_AFTER))
else: else:
self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(TCPClientInterface.I2P_PROBE_AFTER)) self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(TCPClientInterface.I2P_PROBE_AFTER))
def detach(self): def detach(self):
if self.socket != None: if self.socket != None:
if hasattr(self.socket, "close"): if hasattr(self.socket, "close"):
if callable(self.socket.close): if callable(self.socket.close):
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG) RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
self.detached = True self.detached = True
try: try:
self.socket.shutdown(socket.SHUT_RDWR) self.socket.shutdown(socket.SHUT_RDWR)
except Exception as e: except Exception as e:
RNS.log("Error while shutting down socket for "+str(self)+": "+str(e)) RNS.log(f"Error while shutting down socket for {self}: {e}")
try: try:
self.socket.close() self.socket.close()
except Exception as e: 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 self.socket = None
def connect(self, initial=False): def connect(self, initial=False):
try: try:
if initial: 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 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.settimeout(TCPClientInterface.INITIAL_CONNECT_TIMEOUT) self.socket.settimeout(TCPClientInterface.INITIAL_CONNECT_TIMEOUT)
@ -208,14 +208,14 @@ class TCPClientInterface(Interface):
self.online = True self.online = True
if initial: 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: except Exception as e:
if initial: if initial:
RNS.log("Initial connection for "+str(self)+" could not be established: "+str(e), RNS.LOG_ERROR) RNS.log(f"Initial connection for {self} could not be established: {e}", RNS.LOG_ERROR)
RNS.log("Leaving unconnected and retrying connection in "+str(TCPClientInterface.RECONNECT_WAIT)+" seconds.", RNS.LOG_ERROR) RNS.log(f"Leaving unconnected and retrying connection in {TCPClientInterface.RECONNECT_WAIT} seconds.", RNS.LOG_ERROR)
return False return False
else: else:
raise e raise e
@ -223,7 +223,7 @@ class TCPClientInterface(Interface):
self.set_timeouts_linux() self.set_timeouts_linux()
elif platform.system() == "Darwin": elif platform.system() == "Darwin":
self.set_timeouts_osx() self.set_timeouts_osx()
self.online = True self.online = True
self.writing = False self.writing = False
self.never_connected = False self.never_connected = False
@ -241,7 +241,7 @@ class TCPClientInterface(Interface):
attempts += 1 attempts += 1
if self.max_reconnect_tries != None and attempts > self.max_reconnect_tries: 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() self.teardown()
break break
@ -249,10 +249,10 @@ class TCPClientInterface(Interface):
self.connect() self.connect()
except Exception as e: 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: 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 self.reconnecting = False
thread = threading.Thread(target=self.read_loop) thread = threading.Thread(target=self.read_loop)
@ -263,13 +263,13 @@ class TCPClientInterface(Interface):
else: else:
RNS.log("Attempt to reconnect on a non-initiator TCP interface. This should not happen.", RNS.LOG_ERROR) 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): def processIncoming(self, data):
self.rxb += len(data) self.rxb += len(data)
if hasattr(self, "parent_interface") and self.parent_interface != None: if hasattr(self, "parent_interface") and self.parent_interface != None:
self.parent_interface.rxb += len(data) self.parent_interface.rxb += len(data)
self.owner.inbound(data, self) self.owner.inbound(data, self)
def processOutgoing(self, data): def processOutgoing(self, data):
@ -292,8 +292,8 @@ class TCPClientInterface(Interface):
self.parent_interface.txb += len(data) self.parent_interface.txb += len(data)
except Exception as e: except Exception as e:
RNS.log("Exception occurred while transmitting via "+str(self)+", tearing down interface", RNS.LOG_ERROR) RNS.log(f"Exception occurred while transmitting via {self}, tearing down interface", 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.teardown() self.teardown()
@ -361,18 +361,18 @@ class TCPClientInterface(Interface):
else: else:
self.online = False self.online = False
if self.initiator and not self.detached: 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() self.reconnect()
else: 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() self.teardown()
break break
except Exception as e: except Exception as e:
self.online = False 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: if self.initiator:
RNS.log("Attempting to reconnect...", RNS.LOG_WARNING) RNS.log("Attempting to reconnect...", RNS.LOG_WARNING)
@ -382,12 +382,12 @@ class TCPClientInterface(Interface):
def teardown(self): def teardown(self):
if self.initiator and not self.detached: 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: if RNS.Reticulum.panic_on_interface_error:
RNS.panic() RNS.panic()
else: 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.online = False
self.OUT = False self.OUT = False
@ -402,7 +402,7 @@ class TCPClientInterface(Interface):
def __str__(self): 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): class TCPServerInterface(Interface):
@ -427,7 +427,7 @@ class TCPServerInterface(Interface):
self.online = False self.online = False
self.clients = 0 self.clients = 0
self.IN = True self.IN = True
self.OUT = False self.OUT = False
self.name = name self.name = name
@ -466,7 +466,7 @@ class TCPServerInterface(Interface):
def incoming_connection(self, handler): def incoming_connection(self, handler):
RNS.log("Accepting incoming TCP connection", RNS.LOG_VERBOSE) 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 = 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.OUT = self.OUT
spawned_interface.IN = self.IN spawned_interface.IN = self.IN
@ -474,7 +474,7 @@ class TCPServerInterface(Interface):
spawned_interface.target_port = str(handler.client_address[1]) spawned_interface.target_port = str(handler.client_address[1])
spawned_interface.parent_interface = self spawned_interface.parent_interface = self
spawned_interface.bitrate = self.bitrate spawned_interface.bitrate = self.bitrate
spawned_interface.ifac_size = self.ifac_size spawned_interface.ifac_size = self.ifac_size
spawned_interface.ifac_netname = self.ifac_netname spawned_interface.ifac_netname = self.ifac_netname
spawned_interface.ifac_netkey = self.ifac_netkey spawned_interface.ifac_netkey = self.ifac_netkey
@ -501,7 +501,7 @@ class TCPServerInterface(Interface):
spawned_interface.mode = self.mode spawned_interface.mode = self.mode
spawned_interface.HW_MTU = self.HW_MTU spawned_interface.HW_MTU = self.HW_MTU
spawned_interface.online = True 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) RNS.Transport.interfaces.append(spawned_interface)
self.clients += 1 self.clients += 1
spawned_interface.read_loop() spawned_interface.read_loop()
@ -521,17 +521,17 @@ class TCPServerInterface(Interface):
if hasattr(self.server, "shutdown"): if hasattr(self.server, "shutdown"):
if callable(self.server.shutdown): if callable(self.server.shutdown):
try: try:
RNS.log("Detaching "+str(self), RNS.LOG_DEBUG) RNS.log(f"Detaching {self}", RNS.LOG_DEBUG)
self.server.shutdown() self.server.shutdown()
self.detached = True self.detached = True
self.server = None self.server = None
except Exception as e: 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): 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): 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.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
udp_socket.sendto(data, (self.forward_ip, self.forward_port)) udp_socket.sendto(data, (self.forward_ip, self.forward_port))
self.txb += len(data) self.txb += len(data)
except Exception as e: except Exception as e:
RNS.log("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): 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): class UDPInterfaceHandler(socketserver.BaseRequestHandler):
def __init__(self, callback, *args, **keys): def __init__(self, callback, *args, **keys):

View File

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

View File

@ -97,7 +97,7 @@ class Packet:
# the below calculation; 383 bytes. # the below calculation; 383 bytes.
ENCRYPTED_MDU = math.floor((RNS.Reticulum.MDU-RNS.Identity.FERNET_OVERHEAD-RNS.Identity.KEYSIZE//16)/RNS.Identity.AES128_BLOCKSIZE)*RNS.Identity.AES128_BLOCKSIZE - 1 ENCRYPTED_MDU = math.floor((RNS.Reticulum.MDU-RNS.Identity.FERNET_OVERHEAD-RNS.Identity.KEYSIZE//16)/RNS.Identity.AES128_BLOCKSIZE)*RNS.Identity.AES128_BLOCKSIZE - 1
""" """
The maximum size of the payload data in a single encrypted packet The maximum size of the payload data in a single encrypted packet
""" """
PLAIN_MDU = MDU PLAIN_MDU = MDU
""" """
@ -208,14 +208,14 @@ class Packet:
# Announce packets are not encrypted # Announce packets are not encrypted
self.ciphertext = self.data self.ciphertext = self.data
else: 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.header += bytes([self.context])
self.raw = self.header + self.ciphertext self.raw = self.header + self.ciphertext
if len(self.raw) > self.MTU: 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.packed = True
self.update_hash() self.update_hash()
@ -250,19 +250,19 @@ class Packet:
return True return True
except Exception as e: 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 return False
def send(self): def send(self):
""" """
Sends the packet. Sends the packet.
:returns: A :ref:`RNS.PacketReceipt<api-packetreceipt>` instance if *create_receipt* was set to *True* when the packet was instantiated, if not returns *None*. If the packet could not be sent *False* is returned. :returns: A :ref:`RNS.PacketReceipt<api-packetreceipt>` instance if *create_receipt* was set to *True* when the packet was instantiated, if not returns *None*. If the packet could not be sent *False* is returned.
""" """
if not self.sent: if not self.sent:
if self.destination.type == RNS.Destination.LINK: if self.destination.type == RNS.Destination.LINK:
if self.destination.status == RNS.Link.CLOSED: 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: else:
self.destination.last_outbound = time.time() self.destination.last_outbound = time.time()
self.destination.tx += 1 self.destination.tx += 1
@ -278,21 +278,21 @@ class Packet:
self.sent = False self.sent = False
self.receipt = None self.receipt = None
return False return False
else: else:
raise IOError("Packet was already sent") raise OSError("Packet was already sent")
def resend(self): def resend(self):
""" """
Re-sends the packet. Re-sends the packet.
:returns: A :ref:`RNS.PacketReceipt<api-packetreceipt>` instance if *create_receipt* was set to *True* when the packet was instantiated, if not returns *None*. If the packet could not be sent *False* is returned. :returns: A :ref:`RNS.PacketReceipt<api-packetreceipt>` instance if *create_receipt* was set to *True* when the packet was instantiated, if not returns *None*. If the packet could not be sent *False* is returned.
""" """
if self.sent: if self.sent:
# Re-pack the packet to obtain new ciphertext for # Re-pack the packet to obtain new ciphertext for
# encrypted destinations # encrypted destinations
self.pack() self.pack()
if RNS.Transport.outbound(self): if RNS.Transport.outbound(self):
return self.receipt return self.receipt
else: else:
@ -301,7 +301,7 @@ class Packet:
self.receipt = None self.receipt = None
return False return False
else: else:
raise IOError("Packet was not sent yet") raise OSError("Packet was not sent yet")
def prove(self, destination=None): def prove(self, destination=None):
if self.fromPacked and hasattr(self, "destination") and self.destination: if self.fromPacked and hasattr(self, "destination") and self.destination:
@ -388,7 +388,7 @@ class PacketReceipt:
def get_status(self): def get_status(self):
""" """
:returns: The status of the associated :ref:`RNS.Packet<api-packet>` instance. Can be one of ``RNS.PacketReceipt.SENT``, ``RNS.PacketReceipt.DELIVERED``, ``RNS.PacketReceipt.FAILED`` or ``RNS.PacketReceipt.CULLED``. :returns: The status of the associated :ref:`RNS.Packet<api-packet>` instance. Can be one of ``RNS.PacketReceipt.SENT``, ``RNS.PacketReceipt.DELIVERED``, ``RNS.PacketReceipt.FAILED`` or ``RNS.PacketReceipt.CULLED``.
""" """
return self.status return self.status
@ -419,10 +419,10 @@ class PacketReceipt:
try: try:
self.callbacks.delivery(self) self.callbacks.delivery(self)
except Exception as e: except Exception as e:
RNS.log("An error occurred while evaluating external delivery callback for "+str(link), RNS.LOG_ERROR) RNS.log(f"An error occurred while evaluating external delivery callback for {link}", 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)
RNS.trace_exception(e) RNS.trace_exception(e)
return True return True
else: else:
return False return False
@ -465,7 +465,7 @@ class PacketReceipt:
try: try:
self.callbacks.delivery(self) self.callbacks.delivery(self)
except Exception as e: 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 return True
else: else:
@ -489,8 +489,8 @@ class PacketReceipt:
try: try:
self.callbacks.delivery(self) self.callbacks.delivery(self)
except Exception as e: 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 return True
else: else:
return False return False
@ -524,7 +524,7 @@ class PacketReceipt:
def set_timeout(self, timeout): def set_timeout(self, timeout):
""" """
Sets a timeout in seconds Sets a timeout in seconds
:param timeout: The timeout in seconds. :param timeout: The timeout in seconds.
""" """
self.timeout = float(timeout) self.timeout = float(timeout)

View File

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

View File

@ -59,11 +59,11 @@ class Resource:
# The maximum window size for transfers on fast links # The maximum window size for transfers on fast links
WINDOW_MAX_FAST = 75 WINDOW_MAX_FAST = 75
# For calculating maps and guard segments, this # For calculating maps and guard segments, this
# must be set to the global maximum window. # must be set to the global maximum window.
WINDOW_MAX = WINDOW_MAX_FAST WINDOW_MAX = WINDOW_MAX_FAST
# If the fast rate is sustained for this many request # If the fast rate is sustained for this many request
# rounds, the fast link window size will be allowed. # rounds, the fast link window size will be allowed.
FAST_RATE_THRESHOLD = WINDOW_MAX_SLOW - WINDOW - 2 FAST_RATE_THRESHOLD = WINDOW_MAX_SLOW - WINDOW - 2
@ -111,7 +111,7 @@ class Resource:
# fit in 3 bytes in resource advertisements. # fit in 3 bytes in resource advertisements.
MAX_EFFICIENT_SIZE = 16 * 1024 * 1024 - 1 MAX_EFFICIENT_SIZE = 16 * 1024 * 1024 - 1
RESPONSE_MAX_GRACE_TIME = 10 RESPONSE_MAX_GRACE_TIME = 10
# The maximum size to auto-compress with # The maximum size to auto-compress with
# bz2 before sending. # bz2 before sending.
AUTO_COMPRESS_MAX_SIZE = MAX_EFFICIENT_SIZE AUTO_COMPRESS_MAX_SIZE = MAX_EFFICIENT_SIZE
@ -172,7 +172,7 @@ class Resource:
resource.window_flexibility = Resource.WINDOW_FLEXIBILITY resource.window_flexibility = Resource.WINDOW_FLEXIBILITY
resource.last_activity = time.time() 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.segment_index = adv.i
resource.total_segments = adv.l resource.total_segments = adv.l
if adv.l > 1: if adv.l > 1:
@ -185,7 +185,7 @@ class Resource:
resource.waiting_for_hmu = False resource.waiting_for_hmu = False
resource.receiving_part = False resource.receiving_part = False
resource.consecutive_completed_height = -1 resource.consecutive_completed_height = -1
if not resource.link.has_incoming_resource(resource): if not resource.link.has_incoming_resource(resource):
resource.link.register_incoming_resource(resource) resource.link.register_incoming_resource(resource)
@ -194,7 +194,7 @@ class Resource:
try: try:
resource.link.callbacks.resource_started(resource) resource.link.callbacks.resource_started(resource)
except Exception as e: 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) resource.hashmap_update(0, resource.hashmap_raw)
@ -203,7 +203,7 @@ class Resource:
return resource return resource
else: 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 return None
except Exception as e: except Exception as e:
@ -256,7 +256,7 @@ class Resource:
data_size = len(data) data_size = len(data)
self.grand_total_parts = math.ceil(data_size/Resource.SDU) self.grand_total_parts = math.ceil(data_size/Resource.SDU)
self.total_size = data_size self.total_size = data_size
resource_data = data resource_data = data
self.total_segments = 1 self.total_segments = 1
self.segment_index = 1 self.segment_index = 1
@ -309,7 +309,7 @@ class Resource:
if (auto_compress and len(self.uncompressed_data) <= Resource.AUTO_COMPRESS_MAX_SIZE): if (auto_compress and len(self.uncompressed_data) <= Resource.AUTO_COMPRESS_MAX_SIZE):
RNS.log("Compressing resource data...", RNS.LOG_DEBUG) RNS.log("Compressing resource data...", RNS.LOG_DEBUG)
self.compressed_data = bz2.compress(self.uncompressed_data) 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: else:
self.compressed_data = self.uncompressed_data self.compressed_data = self.uncompressed_data
@ -318,12 +318,12 @@ class Resource:
if (self.compressed_size < self.uncompressed_size and auto_compress): if (self.compressed_size < self.uncompressed_size and auto_compress):
saved_bytes = len(self.uncompressed_data) - len(self.compressed_data) 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 = b""
self.data += RNS.Identity.get_random_hash()[:Resource.RANDOM_HASH_SIZE] self.data += RNS.Identity.get_random_hash()[:Resource.RANDOM_HASH_SIZE]
self.data += self.compressed_data self.data += self.compressed_data
self.compressed = True self.compressed = True
self.uncompressed_data = None self.uncompressed_data = None
@ -348,11 +348,11 @@ class Resource:
self.size = len(self.data) self.size = len(self.data)
self.sent_parts = 0 self.sent_parts = 0
hashmap_entries = int(math.ceil(self.size/float(Resource.SDU))) hashmap_entries = int(math.ceil(self.size/float(Resource.SDU)))
hashmap_ok = False hashmap_ok = False
while not hashmap_ok: while not hashmap_ok:
hashmap_computation_began = time.time() 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.random_hash = RNS.Identity.get_random_hash()[:Resource.RANDOM_HASH_SIZE]
self.hash = RNS.Identity.full_hash(data+self.random_hash) self.hash = RNS.Identity.full_hash(data+self.random_hash)
@ -388,13 +388,13 @@ class Resource:
self.hashmap += part.map_hash self.hashmap += part.map_hash
self.parts.append(part) 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: if advertise:
self.advertise() self.advertise()
else: else:
self.receive_lock = Lock() self.receive_lock = Lock()
def hashmap_update_packet(self, plaintext): def hashmap_update_packet(self, plaintext):
if not self.status == Resource.FAILED: if not self.status == Resource.FAILED:
@ -447,9 +447,9 @@ class Resource:
self.status = Resource.ADVERTISED self.status = Resource.ADVERTISED
self.retries_left = self.max_adv_retries self.retries_left = self.max_adv_retries
self.link.register_outgoing_resource(self) 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: 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() self.cancel()
return return
@ -487,9 +487,9 @@ class Resource:
self.adv_sent = self.last_activity self.adv_sent = self.last_activity
sleep_time = 0.001 sleep_time = 0.001
except Exception as e: 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() self.cancel()
elif self.status == Resource.TRANSFERRING: elif self.status == Resource.TRANSFERRING:
if not self.initiator: if not self.initiator:
@ -504,11 +504,11 @@ class Resource:
retries_used = self.max_retries - self.retries_left retries_used = self.max_retries - self.retries_left
extra_wait = retries_used * Resource.PER_RETRY_DELAY extra_wait = retries_used * Resource.PER_RETRY_DELAY
sleep_time = self.last_activity + (rtt*(self.part_timeout_factor+window_remaining)) + Resource.RETRY_GRACE_TIME + extra_wait - time.time() sleep_time = self.last_activity + (rtt*(self.part_timeout_factor+window_remaining)) + Resource.RETRY_GRACE_TIME + extra_wait - time.time()
if sleep_time < 0: if sleep_time < 0:
if self.retries_left > 0: if self.retries_left > 0:
ms = "" if self.outstanding_parts == 1 else "s" ms = "" if self.outstanding_parts == 1 else "s"
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: if self.window > self.window_min:
self.window -= 1 self.window -= 1
if self.window_max > self.window_min: if self.window_max > self.window_min:
@ -554,7 +554,7 @@ class Resource:
if sleep_time == None or sleep_time < 0: if sleep_time == None or sleep_time < 0:
RNS.log("Timing error, cancelling resource transfer.", RNS.LOG_ERROR) RNS.log("Timing error, cancelling resource transfer.", RNS.LOG_ERROR)
self.cancel() self.cancel()
if sleep_time != None: if sleep_time != None:
sleep(min(sleep_time, Resource.WATCHDOG_MAX_SLEEP)) sleep(min(sleep_time, Resource.WATCHDOG_MAX_SLEEP))
@ -591,7 +591,7 @@ class Resource:
except Exception as e: except Exception as e:
RNS.log("Error while assembling received resource.", RNS.LOG_ERROR) 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.status = Resource.CORRUPT
self.link.resource_concluded(self) self.link.resource_concluded(self)
@ -602,7 +602,7 @@ class Resource:
try: try:
self.callback(self) self.callback(self)
except Exception as e: 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: try:
if hasattr(self.data, "close") and callable(self.data.close): if hasattr(self.data, "close") and callable(self.data.close):
@ -614,7 +614,7 @@ class Resource:
RNS.log("Error while cleaning up resource files, the contained exception was:", RNS.LOG_ERROR) RNS.log("Error while cleaning up resource files, the contained exception was:", RNS.LOG_ERROR)
RNS.log(str(e)) RNS.log(str(e))
else: 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): def prove(self):
@ -626,7 +626,7 @@ class Resource:
proof_packet.send() proof_packet.send()
except Exception as e: except Exception as e:
RNS.log("Could not send proof packet, cancelling resource", RNS.LOG_DEBUG) 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() self.cancel()
def __prepare_next_segment(self): def __prepare_next_segment(self):
@ -657,7 +657,7 @@ class Resource:
try: try:
self.callback(self) self.callback(self)
except Exception as e: 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: finally:
try: try:
if hasattr(self, "input_file"): if hasattr(self, "input_file"):
@ -665,7 +665,7 @@ class Resource:
self.input_file.close() self.input_file.close()
except Exception as e: 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: else:
# Otherwise we'll recursively create the # Otherwise we'll recursively create the
# next segment of the resource # next segment of the resource
@ -692,7 +692,7 @@ class Resource:
if self.req_resp == None: if self.req_resp == None:
self.req_resp = self.last_activity self.req_resp = self.last_activity
rtt = self.req_resp-self.req_sent rtt = self.req_resp-self.req_sent
self.part_timeout_factor = Resource.PART_TIMEOUT_FACTOR_AFTER_RTT self.part_timeout_factor = Resource.PART_TIMEOUT_FACTOR_AFTER_RTT
if self.rtt == None: if self.rtt == None:
self.rtt = self.link.rtt self.rtt = self.link.rtt
@ -732,7 +732,7 @@ class Resource:
# Update consecutive completed pointer # Update consecutive completed pointer
if i == self.consecutive_completed_height + 1: if i == self.consecutive_completed_height + 1:
self.consecutive_completed_height = i self.consecutive_completed_height = i
cp = self.consecutive_completed_height + 1 cp = self.consecutive_completed_height + 1
while cp < len(self.parts) and self.parts[cp] != None: while cp < len(self.parts) and self.parts[cp] != None:
self.consecutive_completed_height = cp self.consecutive_completed_height = cp
@ -742,7 +742,7 @@ class Resource:
try: try:
self.__progress_callback(self) self.__progress_callback(self)
except Exception as e: 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 i += 1
@ -797,7 +797,7 @@ class Resource:
i = 0; pn = self.consecutive_completed_height+1 i = 0; pn = self.consecutive_completed_height+1
search_start = pn search_start = pn
search_size = self.window search_size = self.window
for part in self.parts[search_start:search_start+search_size]: for part in self.parts[search_start:search_start+search_size]:
if part == None: if part == None:
part_hash = self.hashmap[pn] part_hash = self.hashmap[pn]
@ -830,7 +830,7 @@ class Resource:
except Exception as e: except Exception as e:
RNS.log("Could not send resource request packet, cancelling resource", RNS.LOG_DEBUG) 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() self.cancel()
# Called on outgoing resource to make it send more data # Called on outgoing resource to make it send more data
@ -876,12 +876,12 @@ class Resource:
except Exception as e: except Exception as e:
RNS.log("Resource could not send parts, cancelling transfer!", RNS.LOG_DEBUG) RNS.log("Resource could not send parts, cancelling transfer!", RNS.LOG_DEBUG)
RNS.log("The contained exception was: "+str(e), RNS.LOG_DEBUG) RNS.log(f"The contained exception was: {e}", RNS.LOG_DEBUG)
self.cancel() self.cancel()
if wants_more_hashmap: if wants_more_hashmap:
last_map_hash = request_data[1:Resource.MAPHASH_LEN+1] last_map_hash = request_data[1:Resource.MAPHASH_LEN+1]
part_index = self.receiver_min_consecutive_height part_index = self.receiver_min_consecutive_height
search_start = part_index search_start = part_index
search_end = self.receiver_min_consecutive_height+ResourceAdvertisement.COLLISION_GUARD_SIZE search_end = self.receiver_min_consecutive_height+ResourceAdvertisement.COLLISION_GUARD_SIZE
@ -899,7 +899,7 @@ class Resource:
else: else:
segment = part_index // ResourceAdvertisement.HASHMAP_MAX_LEN segment = part_index // ResourceAdvertisement.HASHMAP_MAX_LEN
hashmap_start = segment*ResourceAdvertisement.HASHMAP_MAX_LEN hashmap_start = segment*ResourceAdvertisement.HASHMAP_MAX_LEN
hashmap_end = min((segment+1)*ResourceAdvertisement.HASHMAP_MAX_LEN, len(self.parts)) hashmap_end = min((segment+1)*ResourceAdvertisement.HASHMAP_MAX_LEN, len(self.parts))
@ -915,7 +915,7 @@ class Resource:
self.last_activity = time.time() self.last_activity = time.time()
except Exception as e: except Exception as e:
RNS.log("Could not send resource HMU packet, cancelling resource", RNS.LOG_DEBUG) 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() self.cancel()
if self.sent_parts == len(self.parts): if self.sent_parts == len(self.parts):
@ -925,7 +925,7 @@ class Resource:
try: try:
self.__progress_callback(self) self.__progress_callback(self)
except Exception as e: 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): def cancel(self):
""" """
@ -939,17 +939,17 @@ class Resource:
cancel_packet = RNS.Packet(self.link, self.hash, context=RNS.Packet.RESOURCE_ICL) cancel_packet = RNS.Packet(self.link, self.hash, context=RNS.Packet.RESOURCE_ICL)
cancel_packet.send() cancel_packet.send()
except Exception as e: 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) self.link.cancel_outgoing_resource(self)
else: else:
self.link.cancel_incoming_resource(self) self.link.cancel_incoming_resource(self)
if self.callback != None: if self.callback != None:
try: try:
self.link.resource_concluded(self) self.link.resource_concluded(self)
self.callback(self) self.callback(self)
except Exception as e: 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): def set_callback(self, callback):
self.callback = callback self.callback = callback
@ -968,7 +968,7 @@ class Resource:
self.processed_parts += self.sent_parts self.processed_parts += self.sent_parts
self.progress_total_parts = float(self.grand_total_parts) self.progress_total_parts = float(self.grand_total_parts)
else: else:
self.processed_parts = (self.segment_index-1)*math.ceil(Resource.MAX_EFFICIENT_SIZE/Resource.SDU) self.processed_parts = (self.segment_index-1)*math.ceil(Resource.MAX_EFFICIENT_SIZE/Resource.SDU)
self.processed_parts += self.received_count self.processed_parts += self.received_count
if self.split: if self.split:
self.progress_total_parts = float(math.ceil(self.total_size/Resource.SDU)) self.progress_total_parts = float(math.ceil(self.total_size/Resource.SDU))
@ -1015,7 +1015,7 @@ class Resource:
return self.compressed return self.compressed
def __str__(self): 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: class ResourceAdvertisement:
@ -1141,7 +1141,7 @@ class ResourceAdvertisement:
@staticmethod @staticmethod
def unpack(data): def unpack(data):
dictionary = umsgpack.unpackb(data) dictionary = umsgpack.unpackb(data)
adv = ResourceAdvertisement() adv = ResourceAdvertisement()
adv.t = dictionary["t"] adv.t = dictionary["t"]
adv.d = dictionary["d"] adv.d = dictionary["d"]

View File

@ -128,7 +128,7 @@ class Reticulum:
HEADER_MAXSIZE = 2+1+(TRUNCATED_HASHLENGTH//8)*2 HEADER_MAXSIZE = 2+1+(TRUNCATED_HASHLENGTH//8)*2
IFAC_MIN_SIZE = 1 IFAC_MIN_SIZE = 1
IFAC_SALT = bytes.fromhex("adf54d882c9a9b80771eb4995d702d4a3e733391b2a0f53f416d9f907e55cff8") IFAC_SALT = bytes.fromhex("adf54d882c9a9b80771eb4995d702d4a3e733391b2a0f53f416d9f907e55cff8")
MDU = MTU - HEADER_MAXSIZE - IFAC_MIN_SIZE MDU = MTU - HEADER_MAXSIZE - IFAC_MIN_SIZE
RESOURCE_CACHE = 24*60*60 RESOURCE_CACHE = 24*60*60
@ -139,7 +139,7 @@ class Reticulum:
router = None router = None
config = None config = None
# The default configuration path will be expanded to a directory # The default configuration path will be expanded to a directory
# named ".reticulum" inside the current users home directory # named ".reticulum" inside the current users home directory
userdir = os.path.expanduser("~") userdir = os.path.expanduser("~")
@ -149,7 +149,7 @@ class Reticulum:
cachepath = "" cachepath = ""
__instance = None __instance = None
@staticmethod @staticmethod
def exit_handler(): def exit_handler():
# This exit handler is called whenever Reticulum is asked to # This exit handler is called whenever Reticulum is asked to
@ -203,20 +203,20 @@ class Reticulum:
else: else:
if os.path.isdir("/etc/reticulum") and os.path.isfile("/etc/reticulum/config"): if os.path.isdir("/etc/reticulum") and os.path.isfile("/etc/reticulum/config"):
Reticulum.configdir = "/etc/reticulum" Reticulum.configdir = "/etc/reticulum"
elif os.path.isdir(Reticulum.userdir+"/.config/reticulum") and os.path.isfile(Reticulum.userdir+"/.config/reticulum/config"): elif os.path.isdir(f"{Reticulum.userdir}/.config/reticulum") and os.path.isfile(f"{Reticulum.userdir}/.config/reticulum/config"):
Reticulum.configdir = Reticulum.userdir+"/.config/reticulum" Reticulum.configdir = f"{Reticulum.userdir}/.config/reticulum"
else: else:
Reticulum.configdir = Reticulum.userdir+"/.reticulum" Reticulum.configdir = f"{Reticulum.userdir}/.reticulum"
if logdest == RNS.LOG_FILE: if logdest == RNS.LOG_FILE:
RNS.logdest = RNS.LOG_FILE RNS.logdest = RNS.LOG_FILE
RNS.logfile = Reticulum.configdir+"/logfile" RNS.logfile = f"{Reticulum.configdir}/logfile"
Reticulum.configpath = Reticulum.configdir+"/config" Reticulum.configpath = f"{Reticulum.configdir}/config"
Reticulum.storagepath = Reticulum.configdir+"/storage" Reticulum.storagepath = f"{Reticulum.configdir}/storage"
Reticulum.cachepath = Reticulum.configdir+"/storage/cache" Reticulum.cachepath = f"{Reticulum.configdir}/storage/cache"
Reticulum.resourcepath = Reticulum.configdir+"/storage/resources" Reticulum.resourcepath = f"{Reticulum.configdir}/storage/resources"
Reticulum.identitypath = Reticulum.configdir+"/storage/identities" Reticulum.identitypath = f"{Reticulum.configdir}/storage/identities"
Reticulum.__transport_enabled = False Reticulum.__transport_enabled = False
Reticulum.__remote_management_enabled = False Reticulum.__remote_management_enabled = False
@ -266,18 +266,18 @@ class Reticulum:
try: try:
self.config = ConfigObj(self.configpath) self.config = ConfigObj(self.configpath)
except Exception as e: 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.log("Check your configuration file for errors!", RNS.LOG_ERROR)
RNS.panic() RNS.panic()
else: else:
RNS.log("Could not load config file, creating default configuration file...") RNS.log("Could not load config file, creating default configuration file...")
self.__create_default_config() 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) time.sleep(1.5)
self.__apply_config() 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.Identity.load_known_destinations()
RNS.Transport.start(self) RNS.Transport.start(self)
@ -285,7 +285,7 @@ class Reticulum:
self.rpc_addr = ("127.0.0.1", self.local_control_port) self.rpc_addr = ("127.0.0.1", self.local_control_port)
if self.rpc_key == None: if self.rpc_key == None:
self.rpc_key = RNS.Identity.full_hash(RNS.Transport.identity.get_private_key()) self.rpc_key = RNS.Identity.full_hash(RNS.Transport.identity.get_private_key())
if self.is_shared_instance: if self.is_shared_instance:
self.rpc_listener = multiprocessing.connection.Listener(self.rpc_addr, authkey=self.rpc_key) self.rpc_listener = multiprocessing.connection.Listener(self.rpc_addr, authkey=self.rpc_key)
thread = threading.Thread(target=self.rpc_loop) thread = threading.Thread(target=self.rpc_loop)
@ -313,7 +313,7 @@ class Reticulum:
if now > self.last_data_persist+Reticulum.PERSIST_INTERVAL: if now > self.last_data_persist+Reticulum.PERSIST_INTERVAL:
self.__persist_data() self.__persist_data()
time.sleep(Reticulum.JOB_INTERVAL) time.sleep(Reticulum.JOB_INTERVAL)
def __start_local_interface(self): def __start_local_interface(self):
@ -329,9 +329,9 @@ class Reticulum:
interface._force_bitrate = Reticulum._force_shared_instance_bitrate interface._force_bitrate = Reticulum._force_shared_instance_bitrate
RNS.log(f"Forcing shared instance bitrate of {RNS.prettyspeed(interface.bitrate)}", RNS.LOG_WARNING) RNS.log(f"Forcing shared instance bitrate of {RNS.prettyspeed(interface.bitrate)}", RNS.LOG_WARNING)
RNS.Transport.interfaces.append(interface) RNS.Transport.interfaces.append(interface)
self.is_shared_instance = True self.is_shared_instance = True
RNS.log("Started shared instance interface: "+str(interface), RNS.LOG_DEBUG) RNS.log(f"Started shared instance interface: {interface}", RNS.LOG_DEBUG)
self.__start_jobs() self.__start_jobs()
except Exception as e: except Exception as e:
@ -353,10 +353,10 @@ class Reticulum:
Reticulum.__transport_enabled = False Reticulum.__transport_enabled = False
Reticulum.__remote_management_enabled = False Reticulum.__remote_management_enabled = False
Reticulum.__allow_probes = 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: except Exception as e:
RNS.log("Local shared instance appears to be running, but it could not be connected", RNS.LOG_ERROR) 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_shared_instance = False
self.is_standalone_instance = True self.is_standalone_instance = True
self.is_connected_to_shared_instance = False self.is_connected_to_shared_instance = False
@ -411,11 +411,11 @@ class Reticulum:
for hexhash in v: for hexhash in v:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(hexhash) != dest_len: 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: try:
allowed_hash = bytes.fromhex(hexhash) allowed_hash = bytes.fromhex(hexhash)
except Exception as e: 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: if not allowed_hash in RNS.Transport.remote_management_allowed:
RNS.Transport.remote_management_allowed.append(allowed_hash) RNS.Transport.remote_management_allowed.append(allowed_hash)
@ -454,7 +454,7 @@ class Reticulum:
c = self.config["interfaces"][name] c = self.config["interfaces"][name]
interface_mode = Interface.Interface.MODE_FULL interface_mode = Interface.Interface.MODE_FULL
if "interface_mode" in c: if "interface_mode" in c:
c["interface_mode"] = str(c["interface_mode"]).lower() c["interface_mode"] = str(c["interface_mode"]).lower()
if c["interface_mode"] == "full": if c["interface_mode"] == "full":
@ -489,7 +489,7 @@ class Reticulum:
if "ifac_size" in c: if "ifac_size" in c:
if c.as_int("ifac_size") >= Reticulum.IFAC_MIN_SIZE*8: if c.as_int("ifac_size") >= Reticulum.IFAC_MIN_SIZE*8:
ifac_size = c.as_int("ifac_size")//8 ifac_size = c.as_int("ifac_size")//8
ifac_netname = None ifac_netname = None
if "networkname" in c: if "networkname" in c:
if c["networkname"] != "": if c["networkname"] != "":
@ -505,7 +505,7 @@ class Reticulum:
if "pass_phrase" in c: if "pass_phrase" in c:
if c["pass_phrase"] != "": if c["pass_phrase"] != "":
ifac_netkey = c["pass_phrase"] ifac_netkey = c["pass_phrase"]
ingress_control = True ingress_control = True
if "ingress_control" in c: ingress_control = c.as_bool("ingress_control") if "ingress_control" in c: ingress_control = c.as_bool("ingress_control")
ic_max_held_announces = None ic_max_held_announces = None
@ -532,12 +532,12 @@ class Reticulum:
if "announce_rate_target" in c: if "announce_rate_target" in c:
if c.as_int("announce_rate_target") > 0: if c.as_int("announce_rate_target") > 0:
announce_rate_target = c.as_int("announce_rate_target") announce_rate_target = c.as_int("announce_rate_target")
announce_rate_grace = None announce_rate_grace = None
if "announce_rate_grace" in c: if "announce_rate_grace" in c:
if c.as_int("announce_rate_grace") >= 0: if c.as_int("announce_rate_grace") >= 0:
announce_rate_grace = c.as_int("announce_rate_grace") announce_rate_grace = c.as_int("announce_rate_grace")
announce_rate_penalty = None announce_rate_penalty = None
if "announce_rate_penalty" in c: if "announce_rate_penalty" in c:
if c.as_int("announce_rate_penalty") >= 0: if c.as_int("announce_rate_penalty") >= 0:
@ -553,7 +553,7 @@ class Reticulum:
if "announce_cap" in c: if "announce_cap" in c:
if c.as_float("announce_cap") > 0 and c.as_float("announce_cap") <= 100: if c.as_float("announce_cap") > 0 and c.as_float("announce_cap") <= 100:
announce_cap = c.as_float("announce_cap")/100.0 announce_cap = c.as_float("announce_cap")/100.0
try: try:
interface = None interface = None
@ -658,9 +658,9 @@ class Reticulum:
interface.OUT = True interface.OUT = True
if interface_mode == Interface.Interface.MODE_ACCESS_POINT: 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.Interface.MODE_FULL
interface.mode = interface_mode interface.mode = interface_mode
interface.announce_cap = announce_cap interface.announce_cap = announce_cap
@ -695,9 +695,9 @@ class Reticulum:
interface.OUT = True interface.OUT = True
if interface_mode == Interface.Interface.MODE_ACCESS_POINT: 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.Interface.MODE_FULL
interface.mode = interface_mode interface.mode = interface_mode
interface.announce_cap = announce_cap interface.announce_cap = announce_cap
@ -732,9 +732,9 @@ class Reticulum:
interface.OUT = True interface.OUT = True
if interface_mode == Interface.Interface.MODE_ACCESS_POINT: 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.Interface.MODE_FULL
interface.mode = interface_mode interface.mode = interface_mode
interface.announce_cap = announce_cap interface.announce_cap = announce_cap
@ -937,7 +937,7 @@ class Reticulum:
ble_addr = ble_string ble_addr = ble_string
else: else:
ble_name = ble_string ble_name = ble_string
interface = RNodeInterface.RNodeInterface( interface = RNodeInterface.RNodeInterface(
RNS.Transport, RNS.Transport,
name, name,
@ -1011,11 +1011,11 @@ class Reticulum:
txpower = int(subinterface_config["txpower"]) if "txpower" in subinterface_config else None txpower = int(subinterface_config["txpower"]) if "txpower" in subinterface_config else None
subint_config[subint_index][4] = txpower subint_config[subint_index][4] = txpower
spreadingfactor = int(subinterface_config["spreadingfactor"]) if "spreadingfactor" in subinterface_config else None spreadingfactor = int(subinterface_config["spreadingfactor"]) if "spreadingfactor" in subinterface_config else None
subint_config[subint_index][5] = spreadingfactor subint_config[subint_index][5] = spreadingfactor
codingrate = int(subinterface_config["codingrate"]) if "codingrate" in subinterface_config else None codingrate = int(subinterface_config["codingrate"]) if "codingrate" in subinterface_config else None
subint_config[subint_index][6] = codingrate subint_config[subint_index][6] = codingrate
flow_control = subinterface_config.as_bool("flow_control") if "flow_control" in subinterface_config else False flow_control = subinterface_config.as_bool("flow_control") if "flow_control" in subinterface_config else False
subint_config[subint_index][7] = flow_control subint_config[subint_index][7] = flow_control
st_alock = float(subinterface_config["airtime_limit_short"]) if "airtime_limit_short" in subinterface_config else None st_alock = float(subinterface_config["airtime_limit_short"]) if "airtime_limit_short" in subinterface_config else None
subint_config[subint_index][8] = st_alock subint_config[subint_index][8] = st_alock
lt_alock = float(subinterface_config["airtime_limit_long"]) if "airtime_limit_long" in subinterface_config else None lt_alock = float(subinterface_config["airtime_limit_long"]) if "airtime_limit_long" in subinterface_config else None
@ -1029,17 +1029,17 @@ class Reticulum:
# if no subinterfaces are defined # if no subinterfaces are defined
if count == 0: if count == 0:
raise ValueError("No subinterfaces configured for "+name) raise ValueError(f"No subinterfaces configured for {name}")
# if no subinterfaces are enabled # if no subinterfaces are enabled
elif enabled_count == 0: 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_interval = int(c["id_interval"]) if "id_interval" in c else None
id_callsign = c["id_callsign"] if "id_callsign" in c else None id_callsign = c["id_callsign"] if "id_callsign" in c else None
port = c["port"] if "port" in c else None port = c["port"] if "port" in c else None
if port == None: if port == None:
raise ValueError("No port specified for "+name) raise ValueError(f"No port specified for {name}")
interface = RNodeMultiInterface.RNodeMultiInterface( interface = RNodeMultiInterface.RNodeMultiInterface(
RNS.Transport, RNS.Transport,
@ -1106,14 +1106,14 @@ class Reticulum:
interface.start() interface.start()
else: else:
RNS.log("Skipping disabled interface \""+name+"\"", RNS.LOG_DEBUG) RNS.log(f"Skipping disabled interface \"{name}\"", RNS.LOG_DEBUG)
except Exception as e: except Exception as e:
RNS.log("The interface \""+name+"\" could not be created. Check your configuration file for errors!", RNS.LOG_ERROR) RNS.log(f"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 contained exception was: {e}", RNS.LOG_ERROR)
RNS.panic() RNS.panic()
else: 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.panic()
RNS.log("System interfaces are ready", RNS.LOG_VERBOSE) RNS.log("System interfaces are ready", RNS.LOG_VERBOSE)
@ -1121,7 +1121,7 @@ class Reticulum:
def _add_interface(self,interface, mode = None, configured_bitrate=None, ifac_size=None, ifac_netname=None, ifac_netkey=None, announce_cap=None, announce_rate_target=None, announce_rate_grace=None, announce_rate_penalty=None): def _add_interface(self,interface, mode = None, configured_bitrate=None, ifac_size=None, ifac_netname=None, ifac_netkey=None, announce_cap=None, announce_rate_target=None, announce_rate_grace=None, announce_rate_penalty=None):
if not self.is_connected_to_shared_instance: if not self.is_connected_to_shared_instance:
if interface != None and issubclass(type(interface), RNS.Interfaces.Interface.Interface): if interface != None and issubclass(type(interface), RNS.Interfaces.Interface.Interface):
if mode == None: if mode == None:
mode = Interface.Interface.MODE_FULL mode = Interface.Interface.MODE_FULL
interface.mode = mode interface.mode = mode
@ -1181,32 +1181,32 @@ class Reticulum:
for filename in os.listdir(self.resourcepath): for filename in os.listdir(self.resourcepath):
try: try:
if len(filename) == (RNS.Identity.HASHLENGTH//8)*2: if len(filename) == (RNS.Identity.HASHLENGTH//8)*2:
filepath = self.resourcepath + "/" + filename filepath = f"{self.resourcepath}/{filename}"
mtime = os.path.getmtime(filepath) mtime = os.path.getmtime(filepath)
age = now - mtime age = now - mtime
if age > Reticulum.RESOURCE_CACHE: if age > Reticulum.RESOURCE_CACHE:
os.unlink(filepath) os.unlink(filepath)
except Exception as e: 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 # Clean packet caches
for filename in os.listdir(self.cachepath): for filename in os.listdir(self.cachepath):
try: try:
if len(filename) == (RNS.Identity.HASHLENGTH//8)*2: if len(filename) == (RNS.Identity.HASHLENGTH//8)*2:
filepath = self.cachepath + "/" + filename filepath = f"{self.cachepath}/{filename}"
mtime = os.path.getmtime(filepath) mtime = os.path.getmtime(filepath)
age = now - mtime age = now - mtime
if age > RNS.Transport.DESTINATION_TIMEOUT: if age > RNS.Transport.DESTINATION_TIMEOUT:
os.unlink(filepath) os.unlink(filepath)
except Exception as e: except Exception as e:
RNS.log("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): def __create_default_config(self):
self.config = ConfigObj(__default_rns_config__) self.config = ConfigObj(__default_rns_config__)
self.config.filename = Reticulum.configpath self.config.filename = Reticulum.configpath
if not os.path.isdir(Reticulum.configdir): if not os.path.isdir(Reticulum.configdir):
os.makedirs(Reticulum.configdir) os.makedirs(Reticulum.configdir)
self.config.write() self.config.write()
@ -1266,7 +1266,7 @@ class Reticulum:
rpc_connection.close() rpc_connection.close()
except Exception as e: 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): def get_interface_stats(self):
if self.is_connected_to_shared_instance: if self.is_connected_to_shared_instance:
@ -1278,7 +1278,7 @@ class Reticulum:
interfaces = [] interfaces = []
for interface in RNS.Transport.interfaces: for interface in RNS.Transport.interfaces:
ifstats = {} ifstats = {}
if hasattr(interface, "clients"): if hasattr(interface, "clients"):
ifstats["clients"] = interface.clients ifstats["clients"] = interface.clients
else: else:
@ -1296,7 +1296,7 @@ class Reticulum:
if hasattr(interface, "b32"): if hasattr(interface, "b32"):
if interface.b32 != None: if interface.b32 != None:
ifstats["i2p_b32"] = interface.b32+".b32.i2p" ifstats["i2p_b32"] = f"{interface.b32}.b32.i2p"
else: else:
ifstats["i2p_b32"] = None ifstats["i2p_b32"] = None
@ -1485,12 +1485,12 @@ class Reticulum:
if self.is_connected_to_shared_instance and hasattr(self, "_force_shared_instance_bitrate") and self._force_shared_instance_bitrate: 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 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 response += simulated_latency
return response return response
except Exception as e: 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 return RNS.Reticulum.DEFAULT_PER_HOP_TIMEOUT
else: else:

File diff suppressed because it is too large Load Diff

View File

@ -23,5 +23,5 @@
import os import os
import glob 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')] __all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]

View File

@ -54,11 +54,11 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
if jail != None: if jail != None:
fetch_jail = os.path.abspath(os.path.expanduser(jail)) 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 identity_path = f"{RNS.Reticulum.identitypath}/{APP_NAME}"
if os.path.isfile(identity_path): if os.path.isfile(identity_path):
identity = RNS.Identity.from_file(identity_path) identity = RNS.Identity.from_file(identity_path)
if identity == None: if identity == None:
RNS.log("No valid saved identity found, creating new...", RNS.LOG_INFO) RNS.log("No valid saved identity found, creating new...", RNS.LOG_INFO)
@ -68,8 +68,8 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "receive") destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "receive")
if display_identity: if display_identity:
print("Identity : "+str(identity)) print(f"Identity : {identity}")
print("Listening on : "+RNS.prettyhexrep(destination.hash)) print(f"Listening on : {RNS.prettyhexrep(destination.hash)}")
exit(0) exit(0)
if disable_auth: if disable_auth:
@ -79,14 +79,14 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
try: try:
allowed_file_name = "allowed_identities" allowed_file_name = "allowed_identities"
allowed_file = None allowed_file = None
if os.path.isfile(os.path.expanduser("/etc/rncp/"+allowed_file_name)): if os.path.isfile(os.path.expanduser(f"/etc/rncp/{allowed_file_name}")):
allowed_file = os.path.expanduser("/etc/rncp/"+allowed_file_name) allowed_file = os.path.expanduser(f"/etc/rncp/{allowed_file_name}")
elif os.path.isfile(os.path.expanduser("~/.config/rncp/"+allowed_file_name)): elif os.path.isfile(os.path.expanduser(f"~/.config/rncp/{allowed_file_name}")):
allowed_file = os.path.expanduser("~/.config/rncp/"+allowed_file_name) allowed_file = os.path.expanduser(f"~/.config/rncp/{allowed_file_name}")
elif os.path.isfile(os.path.expanduser("~/.rncp/"+allowed_file_name)): elif os.path.isfile(os.path.expanduser(f"~/.rncp/{allowed_file_name}")):
allowed_file = os.path.expanduser("~/.rncp/"+allowed_file_name) allowed_file = os.path.expanduser(f"~/.rncp/{allowed_file_name}")
if allowed_file != None: if allowed_file != None:
af = open(allowed_file, "r") af = open(allowed_file)
al = af.read().replace("\r", "").split("\n") al = af.read().replace("\r", "").split("\n")
ali = [] ali = []
for a in al: for a in al:
@ -102,17 +102,17 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
ms = "y" ms = "y"
else: else:
ms = "ies" 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: 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: if allowed != None:
for a in allowed: for a in allowed:
try: try:
if len(a) != dest_len: 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: try:
destination_hash = bytes.fromhex(a) destination_hash = bytes.fromhex(a)
allowed_identity_hashes.append(destination_hash) allowed_identity_hashes.append(destination_hash)
@ -141,11 +141,11 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
target_link = link target_link = link
if not os.path.isfile(file_path): 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 return False
else: else:
if target_link != None: 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() temp_file = TemporaryFile()
real_file = open(file_path, "rb") real_file = open(file_path, "rb")
@ -169,7 +169,7 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
destination.set_link_established_callback(client_link_established) destination.set_link_established_callback(client_link_established)
destination.register_request_handler("fetch_file", response_generator=fetch_request, allow=RNS.Destination.ALLOW_LIST, allowed_list=allowed_identity_hashes) 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: if announce >= 0:
def job(): def job():
@ -180,7 +180,7 @@ def listen(configdir, verbosity = 0, quietness = 0, allowed = [], display_identi
destination.announce() destination.announce()
threading.Thread(target=job, daemon=True).start() threading.Thread(target=job, daemon=True).start()
while True: while True:
time.sleep(1) time.sleep(1)
@ -206,7 +206,7 @@ def receive_sender_identified(link, identity):
def receive_resource_callback(resource): def receive_resource_callback(resource):
global allow_all global allow_all
sender_identity = resource.link.get_remote_identity() sender_identity = resource.link.get_remote_identity()
if sender_identity != None: if sender_identity != None:
@ -220,15 +220,15 @@ def receive_resource_callback(resource):
def receive_resource_started(resource): def receive_resource_started(resource):
if resource.link.get_remote_identity(): 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: else:
id_str = "" 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): def receive_resource_concluded(resource):
if resource.status == RNS.Resource.COMPLETE: if resource.status == RNS.Resource.COMPLETE:
print(str(resource)+" completed") print(f"{resource} completed")
if resource.total_size > 4: if resource.total_size > 4:
filename_len = int.from_bytes(resource.data.read(2), "big") filename_len = int.from_bytes(resource.data.read(2), "big")
@ -238,8 +238,8 @@ def receive_resource_concluded(resource):
saved_filename = filename saved_filename = filename
while os.path.isfile(saved_filename): while os.path.isfile(saved_filename):
counter += 1 counter += 1
saved_filename = filename+"."+str(counter) saved_filename = f"{filename}.{counter}"
file = open(saved_filename, "wb") file = open(saved_filename, "wb")
file.write(resource.data.read()) file.write(resource.data.read())
file.close() file.close()
@ -285,7 +285,7 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
try: try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination) != dest_len: 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: try:
destination_hash = bytes.fromhex(destination) destination_hash = bytes.fromhex(destination)
except Exception as e: except Exception as e:
@ -296,11 +296,11 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
reticulum = RNS.Reticulum(configdir=configdir, loglevel=targetloglevel) 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): if os.path.isfile(identity_path):
identity = RNS.Identity.from_file(identity_path) identity = RNS.Identity.from_file(identity_path)
if identity == None: if identity == None:
RNS.log("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) exit(2)
else: else:
identity = None identity = None
@ -313,9 +313,9 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
if not RNS.Transport.has_path(destination_hash): if not RNS.Transport.has_path(destination_hash):
RNS.Transport.request_path(destination_hash) RNS.Transport.request_path(destination_hash)
if silent: if silent:
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested") print(f"Path to {RNS.prettyhexrep(destination_hash)} requested")
else: else:
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=" ") print(f"Path to {RNS.prettyhexrep(destination_hash)} requested ", end=" ")
sys.stdout.flush() sys.stdout.flush()
i = 0 i = 0
@ -324,7 +324,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: while not RNS.Transport.has_path(destination_hash) and time.time() < estab_timeout:
if not silent: if not silent:
time.sleep(0.1) time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="") print(f'\x08\x08{syms[i]} ', end="")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
@ -336,9 +336,9 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
exit(1) exit(1)
else: else:
if silent: if silent:
print("Establishing link with "+RNS.prettyhexrep(destination_hash)) print(f"Establishing link with {RNS.prettyhexrep(destination_hash)}")
else: else:
print("\r \rEstablishing link with "+RNS.prettyhexrep(destination_hash)+" ", end=" ") print(f'\r \rEstablishing link with {RNS.prettyhexrep(destination_hash)} ', end=" ")
listener_identity = RNS.Identity.recall(destination_hash) listener_identity = RNS.Identity.recall(destination_hash)
listener_destination = RNS.Destination( listener_destination = RNS.Destination(
@ -353,15 +353,15 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
while link.status != RNS.Link.ACTIVE and time.time() < estab_timeout: while link.status != RNS.Link.ACTIVE and time.time() < estab_timeout:
if not silent: if not silent:
time.sleep(0.1) time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="") print(f'\x08\x08{syms[i]} ', end="")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
if not RNS.Transport.has_path(destination_hash): if not RNS.Transport.has_path(destination_hash):
if silent: if silent:
print("Could not establish link with "+RNS.prettyhexrep(destination_hash)) print(f"Could not establish link with {RNS.prettyhexrep(destination_hash)}")
else: else:
print("\r \rCould not establish link with "+RNS.prettyhexrep(destination_hash)) print(f'\r \rCould not establish link with {RNS.prettyhexrep(destination_hash)}')
exit(1) exit(1)
else: else:
if silent: if silent:
@ -411,8 +411,8 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
saved_filename = filename saved_filename = filename
while os.path.isfile(saved_filename): while os.path.isfile(saved_filename):
counter += 1 counter += 1
saved_filename = filename+"."+str(counter) saved_filename = f"{filename}.{counter}"
file = open(saved_filename, "wb") file = open(saved_filename, "wb")
file.write(resource.data.read()) file.write(resource.data.read())
file.close() file.close()
@ -437,19 +437,19 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
while not request_resolved: while not request_resolved:
if not silent: if not silent:
time.sleep(0.1) time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="") print(f'\x08\x08{syms[i]} ', end="")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
if request_status == "fetch_not_allowed": if request_status == "fetch_not_allowed":
if not silent: print("\r \r", end="") if not silent: print("\r \r", 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() link.teardown()
time.sleep(0.15) time.sleep(0.15)
exit(0) exit(0)
elif request_status == "not_found": elif request_status == "not_found":
if not silent: print("\r \r", end="") if not silent: print("\r \r", 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() link.teardown()
time.sleep(0.15) time.sleep(0.15)
exit(0) exit(0)
@ -474,13 +474,13 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
if current_resource: if current_resource:
prg = current_resource.get_progress() prg = current_resource.get_progress()
percent = round(prg * 100.0, 1) percent = round(prg * 100.0, 1)
stat_str = str(percent)+"% - " + size_str(int(prg*current_resource.total_size)) + " of " + size_str(current_resource.total_size) + " - " +size_str(speed, "b")+"ps" stat_str = f"{percent}% - {size_str(int(prg * current_resource.total_size))} of {size_str(current_resource.total_size)} - {size_str(speed, 'b')}ps"
if prg != 1.0: if prg != 1.0:
print("\r \rTransferring file "+syms[i]+" "+stat_str, end=" ") print(f'\r \rTransferring file {syms[i]} {stat_str}', end=" ")
else: else:
print("\r \rTransfer complete "+stat_str, end=" ") print(f'\r \rTransfer complete {stat_str}', end=" ")
else: else:
print("\r \rWaiting for transfer to start "+syms[i]+" ", end=" ") print(f'\r \rWaiting for transfer to start {syms[i]} ', end=" ")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
@ -492,10 +492,10 @@ def fetch(configdir, verbosity = 0, quietness = 0, destination = None, file = No
exit(1) exit(1)
else: else:
if silent: if silent:
print(str(file)+" fetched from "+RNS.prettyhexrep(destination_hash)) print(f"{file} fetched from {RNS.prettyhexrep(destination_hash)}")
else: else:
#print("\r \r"+str(file)+" fetched from "+RNS.prettyhexrep(destination_hash)) #print("\r \r"+str(file)+" fetched from "+RNS.prettyhexrep(destination_hash))
print("\n"+str(file)+" fetched from "+RNS.prettyhexrep(destination_hash)) print(f"\n{file} fetched from {RNS.prettyhexrep(destination_hash)}")
link.teardown() link.teardown()
time.sleep(0.15) time.sleep(0.15)
exit(0) exit(0)
@ -512,7 +512,7 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
try: try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination) != dest_len: 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: try:
destination_hash = bytes.fromhex(destination) destination_hash = bytes.fromhex(destination)
except Exception as e: except Exception as e:
@ -521,7 +521,7 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
print(str(e)) print(str(e))
exit(1) exit(1)
file_path = os.path.expanduser(file) file_path = os.path.expanduser(file)
if not os.path.isfile(file_path): if not os.path.isfile(file_path):
print("File not found") print("File not found")
@ -547,11 +547,11 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
reticulum = RNS.Reticulum(configdir=configdir, loglevel=targetloglevel) 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): if os.path.isfile(identity_path):
identity = RNS.Identity.from_file(identity_path) identity = RNS.Identity.from_file(identity_path)
if identity == None: if identity == None:
RNS.log("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) exit(2)
else: else:
identity = None identity = None
@ -564,9 +564,9 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
if not RNS.Transport.has_path(destination_hash): if not RNS.Transport.has_path(destination_hash):
RNS.Transport.request_path(destination_hash) RNS.Transport.request_path(destination_hash)
if silent: if silent:
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested") print(f"Path to {RNS.prettyhexrep(destination_hash)} requested")
else: else:
print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=" ") print(f"Path to {RNS.prettyhexrep(destination_hash)} requested ", end=" ")
sys.stdout.flush() sys.stdout.flush()
i = 0 i = 0
@ -575,7 +575,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: while not RNS.Transport.has_path(destination_hash) and time.time() < estab_timeout:
if not silent: if not silent:
time.sleep(0.1) time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="") print(f'\x08\x08{syms[i]} ', end="")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
@ -587,9 +587,9 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
exit(1) exit(1)
else: else:
if silent: if silent:
print("Establishing link with "+RNS.prettyhexrep(destination_hash)) print(f"Establishing link with {RNS.prettyhexrep(destination_hash)}")
else: else:
print("\r \rEstablishing link with "+RNS.prettyhexrep(destination_hash)+" ", end=" ") print(f'\r \rEstablishing link with {RNS.prettyhexrep(destination_hash)} ', end=" ")
receiver_identity = RNS.Identity.recall(destination_hash) receiver_identity = RNS.Identity.recall(destination_hash)
receiver_destination = RNS.Destination( receiver_destination = RNS.Destination(
@ -604,21 +604,21 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
while link.status != RNS.Link.ACTIVE and time.time() < estab_timeout: while link.status != RNS.Link.ACTIVE and time.time() < estab_timeout:
if not silent: if not silent:
time.sleep(0.1) time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="") print(f'\x08\x08{syms[i]} ', end="")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
if time.time() > estab_timeout: if time.time() > estab_timeout:
if silent: if silent:
print("Link establishment with "+RNS.prettyhexrep(destination_hash)+" timed out") print(f"Link establishment with {RNS.prettyhexrep(destination_hash)} timed out")
else: else:
print("\r \rLink establishment with "+RNS.prettyhexrep(destination_hash)+" timed out") print(f'\r \rLink establishment with {RNS.prettyhexrep(destination_hash)} timed out')
exit(1) exit(1)
elif not RNS.Transport.has_path(destination_hash): elif not RNS.Transport.has_path(destination_hash):
if silent: if silent:
print("No path found to "+RNS.prettyhexrep(destination_hash)) print(f"No path found to {RNS.prettyhexrep(destination_hash)}")
else: else:
print("\r \rNo path found to "+RNS.prettyhexrep(destination_hash)) print(f'\r \rNo path found to {RNS.prettyhexrep(destination_hash)}')
exit(1) exit(1)
else: else:
if silent: if silent:
@ -633,16 +633,16 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
while resource.status < RNS.Resource.TRANSFERRING: while resource.status < RNS.Resource.TRANSFERRING:
if not silent: if not silent:
time.sleep(0.1) time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="") print(f'\x08\x08{syms[i]} ', end="")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
if resource.status > RNS.Resource.COMPLETE: if resource.status > RNS.Resource.COMPLETE:
if silent: if silent:
print("File was not accepted by "+RNS.prettyhexrep(destination_hash)) print(f"File was not accepted by {RNS.prettyhexrep(destination_hash)}")
else: else:
print("\r \rFile was not accepted by "+RNS.prettyhexrep(destination_hash)) print(f'\r \rFile was not accepted by {RNS.prettyhexrep(destination_hash)}')
exit(1) exit(1)
else: else:
if silent: if silent:
@ -654,11 +654,11 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
time.sleep(0.1) time.sleep(0.1)
prg = current_resource.get_progress() prg = current_resource.get_progress()
percent = round(prg * 100.0, 1) percent = round(prg * 100.0, 1)
stat_str = str(percent)+"% - " + size_str(int(prg*current_resource.total_size)) + " of " + size_str(current_resource.total_size) + " - " +size_str(speed, "b")+"ps" stat_str = f"{percent}% - {size_str(int(prg * current_resource.total_size))} of {size_str(current_resource.total_size)} - {size_str(speed, 'b')}ps"
if not done: if not done:
print("\r \rTransferring file "+syms[i]+" "+stat_str, end=" ") print(f'\r \rTransferring file {syms[i]} {stat_str}', end=" ")
else: else:
print("\r \rTransfer complete "+stat_str, end=" ") print(f'\r \rTransfer complete {stat_str}', end=" ")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
return i return i
@ -678,10 +678,10 @@ def send(configdir, verbosity = 0, quietness = 0, destination = None, file = Non
exit(1) exit(1)
else: else:
if silent: if silent:
print(str(file_path)+" copied to "+RNS.prettyhexrep(destination_hash)) print(f"{file_path} copied to {RNS.prettyhexrep(destination_hash)}")
else: else:
# print("\r \r"+str(file_path)+" copied to "+RNS.prettyhexrep(destination_hash)) # print("\r \r"+str(file_path)+" copied to "+RNS.prettyhexrep(destination_hash))
print("\n"+str(file_path)+" copied to "+RNS.prettyhexrep(destination_hash)) print(f"\n{file_path} copied to {RNS.prettyhexrep(destination_hash)}")
link.teardown() link.teardown()
time.sleep(0.25) time.sleep(0.25)
real_file.close() real_file.close()
@ -707,8 +707,8 @@ def main():
parser.add_argument('-p', '--print-identity', action='store_true', default=False, help="print identity and destination info and exit") 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("-w", action="store", metavar="seconds", type=float, help="sender timeout before giving up", default=RNS.Transport.PATH_REQUEST_TIMEOUT)
# parser.add_argument("--limit", action="store", metavar="files", type=float, help="maximum number of files to accept", default=None) # parser.add_argument("--limit", action="store", metavar="files", type=float, help="maximum number of files to accept", default=None)
parser.add_argument("--version", action="version", version="rncp {version}".format(version=__version__)) parser.add_argument("--version", action="version", version=f"rncp {__version__}")
args = parser.parse_args() args = parser.parse_args()
if args.listen or args.print_identity: if args.listen or args.print_identity:
@ -777,12 +777,12 @@ def size_str(num, suffix='B'):
for unit in units: for unit in units:
if abs(num) < 1000.0: if abs(num) < 1000.0:
if unit == "": if unit == "":
return "%.0f %s%s" % (num, unit, suffix) return f"{num:.0f} {unit}{suffix}"
else: else:
return "%.2f %s%s" % (num, unit, suffix) return f"{num:.2f} {unit}{suffix}"
num /= 1000.0 num /= 1000.0
return "%.2f%s%s" % (num, last_unit, suffix) return f"{num:.2f}{last_unit}{suffix}"
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

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

View File

@ -48,8 +48,8 @@ def main():
parser.add_argument('-v', '--verbose', action='count', default=0) parser.add_argument('-v', '--verbose', action='count', default=0)
parser.add_argument('-q', '--quiet', action='count', default=0) parser.add_argument('-q', '--quiet', action='count', default=0)
parser.add_argument("--exampleconfig", action='store_true', default=False, help="print verbose configuration example to stdout and exit") parser.add_argument("--exampleconfig", action='store_true', default=False, help="print verbose configuration example to stdout and exit")
parser.add_argument("--version", action="version", version="ir {version}".format(version=__version__)) parser.add_argument("--version", action="version", version=f"ir {__version__}")
args = parser.parse_args() args = parser.parse_args()
if args.exampleconfig: if args.exampleconfig:

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

View File

@ -38,7 +38,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
if full_name == None: if full_name == None:
print("The full destination name including application name aspects must be specified for the destination") print("The full destination name including application name aspects must be specified for the destination")
exit() exit()
try: try:
app_name, aspects = RNS.Destination.app_and_aspects_from_name(full_name) app_name, aspects = RNS.Destination.app_and_aspects_from_name(full_name)
@ -49,7 +49,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
try: try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(destination_hexhash) != dest_len: 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: try:
destination_hash = bytes.fromhex(destination_hexhash) destination_hash = bytes.fromhex(destination_hexhash)
except Exception as e: 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): if not RNS.Transport.has_path(destination_hash):
RNS.Transport.request_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() sys.stdout.flush()
_timeout = time.time() + (timeout or DEFAULT_TIMEOUT+reticulum.get_first_hop_timeout(destination_hash)) _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 = "⢄⢂⢁⡁⡈⡐⡠" syms = "⢄⢂⢁⡁⡈⡐⡠"
while not RNS.Transport.has_path(destination_hash) and not time.time() > _timeout: while not RNS.Transport.has_path(destination_hash) and not time.time() > _timeout:
time.sleep(0.1) time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="") print(f'\x08\x08{syms[i]} ', end="")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) 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 = RNS.Packet(request_destination, os.urandom(size))
probe.pack() probe.pack()
except OSError: 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) exit(3)
receipt = probe.send() receipt = probe.send()
@ -115,25 +115,25 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
if more_output: if more_output:
nhd = reticulum.get_next_hop(destination_hash) nhd = reticulum.get_next_hop(destination_hash)
via_str = " via "+RNS.prettyhexrep(nhd) if nhd != None else "" via_str = f" 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 "" 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 more = via_str+if_str
else: else:
more = "" 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)) _timeout = time.time() + (timeout or DEFAULT_TIMEOUT+reticulum.get_first_hop_timeout(destination_hash))
i = 0 i = 0
while receipt.status == RNS.PacketReceipt.SENT and not time.time() > _timeout: while receipt.status == RNS.PacketReceipt.SENT and not time.time() > _timeout:
time.sleep(0.1) time.sleep(0.1)
print(("\b\b"+syms[i]+" "), end="") print(f'\x08\x08{syms[i]} ', end="")
sys.stdout.flush() sys.stdout.flush()
i = (i+1)%len(syms) i = (i+1)%len(syms)
if time.time() > _timeout: if time.time() > _timeout:
print("\r \rProbe timed out") print("\r \rProbe timed out")
else: else:
print("\b\b ") print("\b\b ")
sys.stdout.flush() sys.stdout.flush()
@ -149,10 +149,10 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
rtt = receipt.get_rtt() rtt = receipt.get_rtt()
if (rtt >= 1): if (rtt >= 1):
rtt = round(rtt, 3) rtt = round(rtt, 3)
rttstring = str(rtt)+" seconds" rttstring = f"{rtt} seconds"
else: else:
rtt = round(rtt*1000, 3) rtt = round(rtt*1000, 3)
rttstring = str(rtt)+" milliseconds" rttstring = f"{rtt} milliseconds"
reception_stats = "" reception_stats = ""
if reticulum.is_connected_to_shared_instance: 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) reception_q = reticulum.get_packet_q(receipt.proof_packet.packet_hash)
if reception_rssi != None: if reception_rssi != None:
reception_stats += " [RSSI "+str(reception_rssi)+" dBm]" reception_stats += f" [RSSI {reception_rssi} dBm]"
if reception_snr != None: if reception_snr != None:
reception_stats += " [SNR "+str(reception_snr)+" dB]" reception_stats += f" [SNR {reception_snr} dB]"
if reception_q != None: if reception_q != None:
reception_stats += " [Link Quality "+str(reception_q)+"%]" reception_stats += f" [Link Quality {reception_q}%]"
else: else:
if receipt.proof_packet != None: if receipt.proof_packet != None:
if receipt.proof_packet.rssi != 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: 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( print(
"Valid reply from "+ f"Valid reply from {RNS.prettyhexrep(receipt.destination.hash)}\nRound-trip time is {rttstring} over {hops} hop{ms}{reception_stats}\n"
RNS.prettyhexrep(receipt.destination.hash)+
"\nRound-trip time is "+rttstring+
" over "+str(hops)+" hop"+ms+
reception_stats+"\n"
) )
else: else:
@ -196,7 +192,7 @@ def program_setup(configdir, destination_hexhash, size=None, full_name = None, v
exit(2) exit(2)
else: else:
exit(0) exit(0)
def main(): def main():
try: try:
@ -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("-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("-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("-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("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) 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) reticulum = RNS.Reticulum(configdir=configdir, verbosity=targetverbosity, logdest=targetlogdest)
if reticulum.is_connected_to_shared_instance: 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: 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: while True:
time.sleep(1) time.sleep(1)
@ -55,8 +55,8 @@ def main():
parser.add_argument('-q', '--quiet', action='count', default=0) 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('-s', '--service', action='store_true', default=False, help="rnsd is running as a service and should log to file")
parser.add_argument("--exampleconfig", action='store_true', default=False, help="print verbose configuration example to stdout and exit") parser.add_argument("--exampleconfig", action='store_true', default=False, help="print verbose configuration example to stdout and exit")
parser.add_argument("--version", action="version", version="rnsd {version}".format(version=__version__)) parser.add_argument("--version", action="version", version=f"rnsd {__version__}")
args = parser.parse_args() args = parser.parse_args()
if args.exampleconfig: if args.exampleconfig:
@ -192,7 +192,7 @@ loglevel = 4
# The following example enables communication with other # The following example enables communication with other
# local Reticulum peers using UDP broadcasts. # local Reticulum peers using UDP broadcasts.
[[UDP Interface]] [[UDP Interface]]
type = UDPInterface type = UDPInterface
enabled = no enabled = no
@ -235,24 +235,24 @@ loglevel = 4
# This example demonstrates a TCP server interface. # This example demonstrates a TCP server interface.
# It will listen for incoming connections on the # It will listen for incoming connections on the
# specified IP address and port number. # specified IP address and port number.
[[TCP Server Interface]] [[TCP Server Interface]]
type = TCPServerInterface type = TCPServerInterface
enabled = no enabled = no
# This configuration will listen on all IP # This configuration will listen on all IP
# interfaces on port 4242 # interfaces on port 4242
listen_ip = 0.0.0.0 listen_ip = 0.0.0.0
listen_port = 4242 listen_port = 4242
# Alternatively you can bind to a specific IP # Alternatively you can bind to a specific IP
# listen_ip = 10.0.0.88 # listen_ip = 10.0.0.88
# listen_port = 4242 # listen_port = 4242
# Or a specific network device # Or a specific network device
# device = eth0 # device = eth0
# port = 4242 # port = 4242
@ -300,7 +300,7 @@ loglevel = 4
# host device before connecting. BLE # host device before connecting. BLE
# devices can be connected by name, # devices can be connected by name,
# BLE MAC address or by any available. # BLE MAC address or by any available.
# Connect to specific device by name # Connect to specific device by name
# port = ble://RNode 3B87 # port = ble://RNode 3B87
@ -320,7 +320,7 @@ loglevel = 4
# Set TX power to 7 dBm (5 mW) # Set TX power to 7 dBm (5 mW)
txpower = 7 txpower = 7
# Select spreading factor 8. Valid # Select spreading factor 8. Valid
# range is 7 through 12, with 7 # range is 7 through 12, with 7
# being the fastest and 12 having # being the fastest and 12 having
# the longest range. # the longest range.
@ -349,8 +349,8 @@ loglevel = 4
# flow control can be useful. By default # flow control can be useful. By default
# it is disabled. # it is disabled.
flow_control = False flow_control = False
# An example KISS modem interface. Useful for running # An example KISS modem interface. Useful for running
# Reticulum over packet radio hardware. # Reticulum over packet radio hardware.
@ -365,7 +365,7 @@ loglevel = 4
# Set the serial baud-rate and other # Set the serial baud-rate and other
# configuration parameters. # configuration parameters.
speed = 115200 speed = 115200
databits = 8 databits = 8
parity = none parity = none
stopbits = 1 stopbits = 1
@ -413,7 +413,7 @@ loglevel = 4
# way, Reticulum will automatically encapsulate it's # way, Reticulum will automatically encapsulate it's
# traffic in AX.25 and also identify your stations # traffic in AX.25 and also identify your stations
# transmissions with your callsign and SSID. # transmissions with your callsign and SSID.
# #
# Only do this if you really need to! Reticulum doesn't # Only do this if you really need to! Reticulum doesn't
# need the AX.25 layer for anything, and it incurs extra # need the AX.25 layer for anything, and it incurs extra
# overhead on every packet to encapsulate in AX.25. # overhead on every packet to encapsulate in AX.25.
@ -436,7 +436,7 @@ loglevel = 4
# Set the serial baud-rate and other # Set the serial baud-rate and other
# configuration parameters. # configuration parameters.
speed = 115200 speed = 115200
databits = 8 databits = 8
parity = none parity = none
stopbits = 1 stopbits = 1

View File

@ -42,12 +42,12 @@ def size_str(num, suffix='B'):
for unit in units: for unit in units:
if abs(num) < 1000.0: if abs(num) < 1000.0:
if unit == "": if unit == "":
return "%.0f %s%s" % (num, unit, suffix) return f"{num:.0f} {unit}{suffix}"
else: else:
return "%.2f %s%s" % (num, unit, suffix) return f"{num:.2f} {unit}{suffix}"
num /= 1000.0 num /= 1000.0
return "%.2f%s%s" % (num, last_unit, suffix) return f"{num:.2f}{last_unit}{suffix}"
request_result = None request_result = None
request_concluded = False request_concluded = False
@ -144,7 +144,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
try: try:
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
if len(remote) != dest_len: 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: try:
identity_hash = bytes.fromhex(remote) identity_hash = bytes.fromhex(remote)
destination_hash = RNS.Destination.hash_from_name_and_identity("rnstransport.remote.management", identity_hash) 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 stats, link_count = remote_status
except Exception as e: except Exception as e:
raise e raise e
except Exception as e: except Exception as e:
print(str(e)) print(str(e))
exit(20) exit(20)
@ -215,7 +215,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
if sorting == "held": if sorting == "held":
interfaces.sort(key=lambda i: i["held_announces"], reverse=not sort_reverse) interfaces.sort(key=lambda i: i["held_announces"], reverse=not sort_reverse)
for ifstat in interfaces: for ifstat in interfaces:
name = ifstat["name"] name = ifstat["name"]
@ -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: if "ifac_netname" in ifstat and ifstat["ifac_netname"] != None:
print(" Network : {nn}".format(nn=ifstat["ifac_netname"])) print(" Network : {nn}".format(nn=ifstat["ifac_netname"]))
print(" Status : {ss}".format(ss=ss)) print(f" Status : {ss}")
if clients != None and clients_string != "": if clients != None and clients_string != "":
print(" "+clients_string) print(" "+clients_string)
if not (name.startswith("Shared Instance[") or name.startswith("TCPInterface[Client") or name.startswith("LocalInterface[")): 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: if "bitrate" in ifstat and ifstat["bitrate"] != None:
print(" Rate : {ss}".format(ss=speed_str(ifstat["bitrate"]))) print(" Rate : {ss}".format(ss=speed_str(ifstat["bitrate"])))
@ -295,16 +295,16 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
if "battery_percent" in ifstat and ifstat["battery_percent"] != None: if "battery_percent" in ifstat and ifstat["battery_percent"] != None:
try: try:
bpi = int(ifstat["battery_percent"]) bpi = int(ifstat["battery_percent"])
print(" Battery : {bp}%".format(bp=bpi)) print(f" Battery : {bpi}%")
except: except:
pass pass
if "airtime_short" in ifstat and "airtime_long" in ifstat: if "airtime_short" in ifstat and "airtime_long" in ifstat:
print(" Airtime : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["airtime_short"]),atl=str(ifstat["airtime_long"]))) print(" Airtime : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["airtime_short"]),atl=str(ifstat["airtime_long"])))
if "channel_load_short" in ifstat and "channel_load_long" in ifstat: if "channel_load_short" in ifstat and "channel_load_long" in ifstat:
print(" Ch.Load : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["channel_load_short"]),atl=str(ifstat["channel_load_long"]))) print(" Ch.Load : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["channel_load_short"]),atl=str(ifstat["channel_load_long"])))
if "peers" in ifstat and ifstat["peers"] != None: if "peers" in ifstat and ifstat["peers"] != None:
print(" Peers : {np} reachable".format(np=ifstat["peers"])) print(" Peers : {np} reachable".format(np=ifstat["peers"]))
@ -314,7 +314,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
if "ifac_signature" in ifstat and ifstat["ifac_signature"] != None: if "ifac_signature" in ifstat and ifstat["ifac_signature"] != None:
sigstr = "<…"+RNS.hexrep(ifstat["ifac_signature"][-5:], delimit=False)+">" sigstr = "<…"+RNS.hexrep(ifstat["ifac_signature"][-5:], delimit=False)+">"
print(" Access : {nb}-bit IFAC by {sig}".format(nb=ifstat["ifac_size"]*8, sig=sigstr)) print(" Access : {nb}-bit IFAC by {sig}".format(nb=ifstat["ifac_size"]*8, sig=sigstr))
if "i2p_b32" in ifstat and ifstat["i2p_b32"] != None: if "i2p_b32" in ifstat and ifstat["i2p_b32"] != None:
print(" I2P B32 : {ep}".format(ep=str(ifstat["i2p_b32"]))) print(" I2P B32 : {ep}".format(ep=str(ifstat["i2p_b32"])))
@ -324,14 +324,14 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
print(" Queued : {np} announce".format(np=aqn)) print(" Queued : {np} announce".format(np=aqn))
else: else:
print(" Queued : {np} announces".format(np=aqn)) print(" Queued : {np} announces".format(np=aqn))
if astats and "held_announces" in ifstat and ifstat["held_announces"] != None and ifstat["held_announces"] > 0: if astats and "held_announces" in ifstat and ifstat["held_announces"] != None and ifstat["held_announces"] > 0:
aqn = ifstat["held_announces"] aqn = ifstat["held_announces"]
if aqn == 1: if aqn == 1:
print(" Held : {np} announce".format(np=aqn)) print(" Held : {np} announce".format(np=aqn))
else: else:
print(" Held : {np} announces".format(np=aqn)) print(" Held : {np} announces".format(np=aqn))
if astats and "incoming_announce_frequency" in ifstat and ifstat["incoming_announce_frequency"] != None: if astats and "incoming_announce_frequency" in ifstat and ifstat["incoming_announce_frequency"] != None:
print(" Announces : {iaf}".format(iaf=RNS.prettyfrequency(ifstat["outgoing_announce_frequency"]))) print(" Announces : {iaf}".format(iaf=RNS.prettyfrequency(ifstat["outgoing_announce_frequency"])))
print(" {iaf}".format(iaf=RNS.prettyfrequency(ifstat["incoming_announce_frequency"]))) print(" {iaf}".format(iaf=RNS.prettyfrequency(ifstat["incoming_announce_frequency"])))
@ -357,7 +357,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
print(f"\n{lstr}") print(f"\n{lstr}")
print("") print("")
else: else:
if not remote: if not remote:
print("Could not get RNS status") print("Could not get RNS status")
@ -378,7 +378,7 @@ def main():
help="show all interfaces", help="show all interfaces",
default=False default=False
) )
parser.add_argument( parser.add_argument(
"-A", "-A",
"--announce-stats", "--announce-stats",
@ -386,7 +386,7 @@ def main():
help="show announce stats", help="show announce stats",
default=False default=False
) )
parser.add_argument( parser.add_argument(
"-l", "-l",
"--link-stats", "--link-stats",
@ -394,7 +394,7 @@ def main():
help="show link stats", help="show link stats",
default=False, default=False,
) )
parser.add_argument( parser.add_argument(
"-s", "-s",
"--sort", "--sort",
@ -403,7 +403,7 @@ def main():
default=None, default=None,
type=str type=str
) )
parser.add_argument( parser.add_argument(
"-r", "-r",
"--reverse", "--reverse",
@ -411,7 +411,7 @@ def main():
help="reverse sorting", help="reverse sorting",
default=False, default=False,
) )
parser.add_argument( parser.add_argument(
"-j", "-j",
"--json", "--json",
@ -450,7 +450,7 @@ def main():
parser.add_argument('-v', '--verbose', action='count', default=0) parser.add_argument('-v', '--verbose', action='count', default=0)
parser.add_argument("filter", nargs="?", default=None, help="only display interfaces with names including filter", type=str) parser.add_argument("filter", nargs="?", default=None, help="only display interfaces with names including filter", type=str)
args = parser.parse_args() args = parser.parse_args()
if args.config: if args.config:
@ -488,10 +488,10 @@ def speed_str(num, suffix='bps'):
for unit in units: for unit in units:
if abs(num) < 1000.0: if abs(num) < 1000.0:
return "%3.2f %s%s" % (num, unit, suffix) return f"{num:3.2f} {unit}{suffix}"
num /= 1000.0 num /= 1000.0
return "%.2f %s%s" % (num, last_unit, suffix) return f"{num:.2f} {last_unit}{suffix}"
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import os import os
import glob 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')] __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 ( from .__version__ import (
@ -10,7 +10,7 @@ from .__version__ import (
from .sam import Destination, PrivateKey from .sam import Destination, PrivateKey
from .aiosam import ( from .aiosam import (
get_sam_socket, dest_lookup, new_destination, get_sam_socket, dest_lookup, new_destination,
create_session, stream_connect, stream_accept, create_session, stream_connect, stream_accept,
Session, StreamConnection, StreamAcceptor Session, StreamConnection, StreamAcceptor
) )

View File

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

View File

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

View File

@ -2,7 +2,7 @@ import logging
import asyncio import asyncio
import argparse import argparse
from . import sam from . import sam
from . import aiosam from . import aiosam
from . import utils from . import utils
from .log import logger from .log import logger
@ -18,7 +18,7 @@ async def proxy_data(reader, writer):
break break
writer.write(data) writer.write(data)
except Exception as e: except Exception as e:
logger.debug('proxy_data_task exception {}'.format(e)) logger.debug(f'proxy_data_task exception {e}')
finally: finally:
try: try:
writer.close() writer.close()
@ -26,12 +26,12 @@ async def proxy_data(reader, writer):
pass pass
logger.debug('close connection') logger.debug('close connection')
class I2PTunnel(object): class I2PTunnel:
"""Base I2P Tunnel object, not to be used directly """Base I2P Tunnel object, not to be used directly
:param local_address: A local address to use for a tunnel. :param local_address: A local address to use for a tunnel.
E.g. ("127.0.0.1", 6668) E.g. ("127.0.0.1", 6668)
:param destination: (optional) Destination to use for this tunnel. Can be :param destination: (optional) Destination to use for this tunnel. Can be
a base64 encoded string, :class:`Destination` a base64 encoded string, :class:`Destination`
instance or None. A new destination is created when it instance or None. A new destination is created when it
is None. is None.
@ -42,7 +42,7 @@ class I2PTunnel(object):
:param sam_address: (optional) SAM API address :param sam_address: (optional) SAM API address
""" """
def __init__(self, local_address, destination=None, session_name=None, def __init__(self, local_address, destination=None, session_name=None,
options={}, loop=None, sam_address=sam.DEFAULT_ADDRESS): options={}, loop=None, sam_address=sam.DEFAULT_ADDRESS):
self.local_address = local_address self.local_address = local_address
self.destination = destination self.destination = destination
@ -57,7 +57,7 @@ class I2PTunnel(object):
sam_address=self.sam_address, loop=self.loop) sam_address=self.sam_address, loop=self.loop)
_, self.session_writer = await aiosam.create_session( _, self.session_writer = await aiosam.create_session(
self.session_name, style=self.style, options=self.options, self.session_name, style=self.style, options=self.options,
sam_address=self.sam_address, sam_address=self.sam_address,
loop=self.loop, destination=self.destination) loop=self.loop, destination=self.destination)
def stop(self): def stop(self):
@ -68,11 +68,11 @@ class ClientTunnel(I2PTunnel):
"""Client tunnel, a subclass of tunnel.I2PTunnel """Client tunnel, a subclass of tunnel.I2PTunnel
If you run a client tunnel with a local address ("127.0.0.1", 6668) and If you run a client tunnel with a local address ("127.0.0.1", 6668) and
a remote destination "irc.echelon.i2p", all connections to 127.0.0.1:6668 a remote destination "irc.echelon.i2p", all connections to 127.0.0.1:6668
will be proxied to irc.echelon.i2p. will be proxied to irc.echelon.i2p.
:param remote_destination: Remote I2P destination, can be either .i2p :param remote_destination: Remote I2P destination, can be either .i2p
domain, .b32.i2p address, base64 destination or domain, .b32.i2p address, base64 destination or
:class:`Destination` instance :class:`Destination` instance
""" """
@ -90,12 +90,12 @@ class ClientTunnel(I2PTunnel):
"""Handle local client connection""" """Handle local client connection"""
try: try:
sc_task = aiosam.stream_connect( sc_task = aiosam.stream_connect(
self.session_name, self.remote_destination, self.session_name, self.remote_destination,
sam_address=self.sam_address, loop=self.loop) sam_address=self.sam_address, loop=self.loop)
self.status["connect_tasks"].append(sc_task) self.status["connect_tasks"].append(sc_task)
remote_reader, remote_writer = await sc_task remote_reader, remote_writer = await sc_task
asyncio.ensure_future(proxy_data(remote_reader, client_writer), asyncio.ensure_future(proxy_data(remote_reader, client_writer),
loop=self.loop) loop=self.loop)
asyncio.ensure_future(proxy_data(client_reader, remote_writer), asyncio.ensure_future(proxy_data(client_reader, remote_writer),
loop=self.loop) loop=self.loop)
@ -123,8 +123,8 @@ class ServerTunnel(I2PTunnel):
"""Server tunnel, a subclass of tunnel.I2PTunnel """Server tunnel, a subclass of tunnel.I2PTunnel
If you want to expose a local service 127.0.0.1:80 to the I2P network, run If you want to expose a local service 127.0.0.1:80 to the I2P network, run
a server tunnel with a local address ("127.0.0.1", 80). If you don't a server tunnel with a local address ("127.0.0.1", 80). If you don't
provide a private key or a session name, it will use a TRANSIENT provide a private key or a session name, it will use a TRANSIENT
destination. destination.
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -139,11 +139,10 @@ class ServerTunnel(I2PTunnel):
async def handle_client(incoming, client_reader, client_writer): async def handle_client(incoming, client_reader, client_writer):
try: try:
# data and dest may come in one chunk # data and dest may come in one chunk
dest, data = incoming.split(b"\n", 1) dest, data = incoming.split(b"\n", 1)
remote_destination = sam.Destination(dest.decode()) remote_destination = sam.Destination(dest.decode())
logger.debug("{} client connected: {}.b32.i2p".format( logger.debug(f"{self.session_name} client connected: {remote_destination.base32}.b32.i2p")
self.session_name, remote_destination.base32))
except Exception as e: except Exception as e:
self.status["exception"] = e self.status["exception"] = e
self.status["setup_failed"] = True self.status["setup_failed"] = True
@ -152,7 +151,7 @@ class ServerTunnel(I2PTunnel):
try: try:
sc_task = asyncio.wait_for( sc_task = asyncio.wait_for(
asyncio.open_connection( asyncio.open_connection(
host=self.local_address[0], host=self.local_address[0],
port=self.local_address[1]), port=self.local_address[1]),
timeout=5) timeout=5)
self.status["connect_tasks"].append(sc_task) self.status["connect_tasks"].append(sc_task)
@ -173,7 +172,7 @@ class ServerTunnel(I2PTunnel):
try: try:
while True: while True:
client_reader, client_writer = await aiosam.stream_accept( client_reader, client_writer = await aiosam.stream_accept(
self.session_name, sam_address=self.sam_address, self.session_name, sam_address=self.sam_address,
loop=self.loop) loop=self.loop)
incoming = await client_reader.read(BUFFER_SIZE) incoming = await client_reader.read(BUFFER_SIZE)
asyncio.ensure_future(handle_client( asyncio.ensure_future(handle_client(
@ -193,13 +192,13 @@ if __name__ == '__main__':
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('type', metavar="TYPE", choices=('server', 'client'), parser.add_argument('type', metavar="TYPE", choices=('server', 'client'),
help="Tunnel type (server or client)") help="Tunnel type (server or client)")
parser.add_argument('address', metavar="ADDRESS", parser.add_argument('address', metavar="ADDRESS",
help="Local address (e.g. 127.0.0.1:8000)") help="Local address (e.g. 127.0.0.1:8000)")
parser.add_argument('--debug', '-d', action='store_true', parser.add_argument('--debug', '-d', action='store_true',
help='Debugging') help='Debugging')
parser.add_argument('--key', '-k', default='', metavar='PRIVATE_KEY', parser.add_argument('--key', '-k', default='', metavar='PRIVATE_KEY',
help='Path to private key file') help='Path to private key file')
parser.add_argument('--destination', '-D', default='', parser.add_argument('--destination', '-D', default='',
metavar='DESTINATION', help='Remote destination') metavar='DESTINATION', help='Remote destination')
args = parser.parse_args() args = parser.parse_args()
@ -217,10 +216,10 @@ if __name__ == '__main__':
local_address = utils.address_from_string(args.address) local_address = utils.address_from_string(args.address)
if args.type == "client": if args.type == "client":
tunnel = ClientTunnel(args.destination, local_address, loop=loop, tunnel = ClientTunnel(args.destination, local_address, loop=loop,
destination=destination, sam_address=SAM_ADDRESS) destination=destination, sam_address=SAM_ADDRESS)
elif args.type == "server": elif args.type == "server":
tunnel = ServerTunnel(local_address, loop=loop, destination=destination, tunnel = ServerTunnel(local_address, loop=loop, destination=destination,
sam_address=SAM_ADDRESS) sam_address=SAM_ADDRESS)
asyncio.ensure_future(tunnel.run(), loop=loop) asyncio.ensure_future(tunnel.run(), loop=loop)

View File

@ -38,5 +38,5 @@ def generate_session_id(length=6):
"""Generate random session id""" """Generate random session id"""
rand = random.SystemRandom() rand = random.SystemRandom()
sid = [rand.choice(string.ascii_letters) for _ in range(length)] 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": elif os.name == "posix":
from RNS.vendor.ifaddr._posix import get_adapters from RNS.vendor.ifaddr._posix import get_adapters
else: else:
raise RuntimeError("Unsupported Operating System: %s" % os.name) raise RuntimeError(f"Unsupported Operating System: {os.name}")
__all__ = ['Adapter', 'IP', 'get_adapters'] __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)) prefixlen = shared.ipv6_prefixlength(ipaddress.IPv6Address(netmaskStr))
else: else:
assert netmask is not None, f'sockaddr_to_ip({addr[0].ifa_netmask}) returned None' 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 prefixlen = ipaddress.IPv4Network(netmaskStr).prefixlen
ip = shared.IP(ip_addr, prefixlen, name) ip = shared.IP(ip_addr, prefixlen, name)
add_ip(name, ip) add_ip(name, ip)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -62,7 +62,7 @@ class Packet:
def set_delivered_callback(self, callback: Callable[[Packet], None]): def set_delivered_callback(self, callback: Callable[[Packet], None]):
self.delivered_callback = callback self.delivered_callback = callback
def delivered(self): def delivered(self):
with self.lock: with self.lock:
self.state = MessageState.MSGSTATE_DELIVERED self.state = MessageState.MSGSTATE_DELIVERED
@ -145,7 +145,7 @@ class SystemMessage(MessageBase):
MSGTYPE = 0xf000 MSGTYPE = 0xf000
def pack(self) -> bytes: def pack(self) -> bytes:
return bytes() return b''
def unpack(self, raw): def unpack(self, raw):
pass pass
@ -160,7 +160,7 @@ class ProtocolHarness(contextlib.AbstractContextManager):
def cleanup(self): def cleanup(self):
self.channel._shutdown() self.channel._shutdown()
def __exit__(self, __exc_type: typing.Type[BaseException], __exc_value: BaseException, def __exit__(self, __exc_type: type[BaseException], __exc_value: BaseException,
__traceback: types.TracebackType) -> bool: __traceback: types.TracebackType) -> bool:
# self._log.debug(f"__exit__({__exc_type}, {__exc_value}, {__traceback})") # self._log.debug(f"__exit__({__exc_type}, {__exc_value}, {__traceback})")
self.cleanup() self.cleanup()
@ -431,7 +431,7 @@ class TestChannel(unittest.TestCase):
self.assertEqual(len(data), count) self.assertEqual(len(data), count)
read_finished = False read_finished = False
result = bytes() result = b''
def read_thread(): def read_thread():
nonlocal read_finished, result nonlocal read_finished, result

View File

@ -11,22 +11,22 @@ class TestSHA256(unittest.TestCase):
def test_empty(self): def test_empty(self):
self.assertEqual( self.assertEqual(
self.f(''.encode("utf-8")), self.f(b''),
bytes.fromhex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")) bytes.fromhex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"))
def test_less_than_block_length(self): def test_less_than_block_length(self):
self.assertEqual( self.assertEqual(
self.f('abc'.encode("utf-8")), self.f(b'abc'),
bytes.fromhex("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad")) bytes.fromhex("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"))
def test_block_length(self): def test_block_length(self):
self.assertEqual( self.assertEqual(
self.f('a'.encode("utf-8")*64), self.f(b'a'*64),
bytes.fromhex("ffe054fe7ae0cb6dc65c3af9b61d5209f439851db43d0ba5997337df154668eb")) bytes.fromhex("ffe054fe7ae0cb6dc65c3af9b61d5209f439851db43d0ba5997337df154668eb"))
def test_several_blocks(self): def test_several_blocks(self):
self.assertEqual( self.assertEqual(
self.f('a'.encode("utf-8")*1000000), self.f(b'a'*1000000),
bytes.fromhex("cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0")) bytes.fromhex("cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"))
def test_random_blocks(self): def test_random_blocks(self):
@ -49,10 +49,10 @@ class TestSHA256(unittest.TestCase):
if (i%1000 == 0): if (i%1000 == 0):
gbytes = round(b/1000000000,3) gbytes = round(b/1000000000,3)
mbps = round((b*8/1000000)/(time.time()-start), 2) mbps = round((b*8/1000000)/(time.time()-start), 2)
print(str(i)+" rounds OK, total data: "+str(gbytes)+"GB, "+str(mbps)+"mbps") print(f"{i} rounds OK, total data: {gbytes}GB, {mbps}mbps")
if not ok: if not ok:
print("Failed at round "+str(i)) print(f"Failed at round {i}")
else: else:
print("SHA-256 test OK") print("SHA-256 test OK")
@ -65,25 +65,25 @@ class TestSHA512(unittest.TestCase):
def test_empty(self): def test_empty(self):
self.assertEqual( self.assertEqual(
self.f(''.encode("utf-8")), self.f(b''),
bytes.fromhex( bytes.fromhex(
'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce'+ 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce'+
'47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e')) '47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e'))
def test_less_than_block_length(self): def test_less_than_block_length(self):
self.assertEqual(self.f('abc'.encode("utf-8")), self.assertEqual(self.f(b'abc'),
bytes.fromhex( bytes.fromhex(
'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a'+ 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a'+
'2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f')) '2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f'))
def test_block_length(self): def test_block_length(self):
self.assertEqual(self.f('a'.encode("utf-8")*128), self.assertEqual(self.f(b'a'*128),
bytes.fromhex( bytes.fromhex(
'b73d1929aa615934e61a871596b3f3b33359f42b8175602e89f7e06e5f658a24'+ 'b73d1929aa615934e61a871596b3f3b33359f42b8175602e89f7e06e5f658a24'+
'3667807ed300314b95cacdd579f3e33abdfbe351909519a846d465c59582f321')) '3667807ed300314b95cacdd579f3e33abdfbe351909519a846d465c59582f321'))
def test_several_blocks(self): def test_several_blocks(self):
self.assertEqual(self.f('a'.encode("utf-8")*1000000), self.assertEqual(self.f(b'a'*1000000),
bytes.fromhex( bytes.fromhex(
'e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb'+ 'e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb'+
'de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b')) 'de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b'))
@ -108,10 +108,10 @@ class TestSHA512(unittest.TestCase):
if (i%1000 == 0): if (i%1000 == 0):
gbytes = round(b/1000000000,3) gbytes = round(b/1000000000,3)
mbps = round((b*8/1000000)/(time.time()-start), 2) mbps = round((b*8/1000000)/(time.time()-start), 2)
print(str(i)+" rounds OK, total data: "+str(gbytes)+"GB, "+str(mbps)+"mbps") print(f"{i} rounds OK, total data: {gbytes}GB, {mbps}mbps")
if not ok: if not ok:
print("Failed at round "+str(i)) print(f"Failed at round {i}")
else: else:
print("SHA-512 test OK") print("SHA-512 test OK")

View File

@ -62,8 +62,8 @@ class TestIdentity(unittest.TestCase):
tmdev = tmax - tmin tmdev = tmax - tmin
mpct = (tmax/tmed)*100 mpct = (tmax/tmed)*100
print("Random messages:") print("Random messages:")
print(" Signature timing min/avg/med/max/mdev: "+str(round(tmin, 3))+"/"+str(round(tmean, 3))+"/"+str(round(tmed, 3))+"/"+str(round(tmax, 3))+"/"+str(round(tmdev, 3))) print(f" Signature timing min/avg/med/max/mdev: {round(tmin, 3)}/{round(tmean, 3)}/{round(tmed, 3)}/{round(tmax, 3)}/{round(tmdev, 3)}")
print(" Max deviation from median: "+str(round(mpct, 1))+"%") print(f" Max deviation from median: {round(mpct, 1)}%")
print() print()
id1 = RNS.Identity() id1 = RNS.Identity()
@ -85,8 +85,8 @@ class TestIdentity(unittest.TestCase):
tmdev = tmax - tmin tmdev = tmax - tmin
mpct = (tmax/tmed)*100 mpct = (tmax/tmed)*100
print("All 0xff messages:") print("All 0xff messages:")
print(" Signature timing min/avg/med/max/mdev: "+str(round(tmin, 3))+"/"+str(round(tmean, 3))+"/"+str(round(tmed, 3))+"/"+str(round(tmax, 3))+"/"+str(round(tmdev, 3))) print(f" Signature timing min/avg/med/max/mdev: {round(tmin, 3)}/{round(tmean, 3)}/{round(tmed, 3)}/{round(tmax, 3)}/{round(tmdev, 3)}")
print(" Max deviation from median: "+str(round(mpct, 1))+"%") print(f" Max deviation from median: {round(mpct, 1)}%")
print() print()
id1 = RNS.Identity() id1 = RNS.Identity()
@ -108,8 +108,8 @@ class TestIdentity(unittest.TestCase):
tmdev = tmax - tmin tmdev = tmax - tmin
mpct = (tmax/tmed)*100 mpct = (tmax/tmed)*100
print("All 0x00 messages:") print("All 0x00 messages:")
print(" Signature timing min/avg/med/max/mdev: "+str(round(tmin, 3))+"/"+str(round(tmean, 3))+"/"+str(round(tmed, 3))+"/"+str(round(tmax, 3))+"/"+str(round(tmdev, 3))) print(f" Signature timing min/avg/med/max/mdev: {round(tmin, 3)}/{round(tmean, 3)}/{round(tmed, 3)}/{round(tmax, 3)}/{round(tmdev, 3)}")
print(" Max deviation from median: "+str(round(mpct, 1))+"%") print(f" Max deviation from median: {round(mpct, 1)}%")
print() print()
b = 0 b = 0
@ -127,7 +127,7 @@ class TestIdentity(unittest.TestCase):
self.assertEqual(True, id2.validate(signature, msg)) self.assertEqual(True, id2.validate(signature, msg))
t += time.time() - start t += time.time() - start
print("Sign/validate chunks < MTU: "+self.size_str(b/t, "b")+"ps") print(f"Sign/validate chunks < MTU: {self.size_str(b / t, 'b')}ps")
for i in range(1, 500): for i in range(1, 500):
mlen = 16*1024 mlen = 16*1024
@ -142,7 +142,7 @@ class TestIdentity(unittest.TestCase):
self.assertEqual(True, id2.validate(signature, msg)) self.assertEqual(True, id2.validate(signature, msg))
t += time.time() - start t += time.time() - start
print("Sign/validate 16KB chunks: "+self.size_str(b/t, "b")+"ps") print(f"Sign/validate 16KB chunks: {self.size_str(b / t, 'b')}ps")
def test_2_encrypt(self): def test_2_encrypt(self):
@ -176,8 +176,8 @@ class TestIdentity(unittest.TestCase):
self.assertEqual(msg, decrypted) self.assertEqual(msg, decrypted)
d_t += time.time() - d_start d_t += time.time() - d_start
print("Encrypt chunks < MTU: "+self.size_str(b/e_t, "b")+"ps") print(f"Encrypt chunks < MTU: {self.size_str(b / e_t, 'b')}ps")
print("Decrypt chunks < MTU: "+self.size_str(b/d_t, "b")+"ps") print(f"Decrypt chunks < MTU: {self.size_str(b / d_t, 'b')}ps")
print("") print("")
# Test encrypt and decrypt of large chunks # Test encrypt and decrypt of large chunks
@ -187,7 +187,7 @@ class TestIdentity(unittest.TestCase):
lb = 1 lb = 1
else: else:
lb = 8 lb = 8
for i in range(1, lb): for i in range(1, lb):
msg = os.urandom(mlen) msg = os.urandom(mlen)
b += mlen b += mlen
@ -203,8 +203,8 @@ class TestIdentity(unittest.TestCase):
self.assertEqual(msg, id1.decrypt(token)) self.assertEqual(msg, id1.decrypt(token))
d_t += time.time() - d_start d_t += time.time() - d_start
print("Encrypt "+self.size_str(mlen)+" chunks: "+self.size_str(b/e_t, "b")+"ps") print(f"Encrypt {self.size_str(mlen)} chunks: {self.size_str(b / e_t, 'b')}ps")
print("Decrypt "+self.size_str(mlen)+" chunks: "+self.size_str(b/d_t, "b")+"ps") print(f"Decrypt {self.size_str(mlen)} chunks: {self.size_str(b / d_t, 'b')}ps")
def size_str(self, num, suffix='B'): def size_str(self, num, suffix='B'):
units = ['','K','M','G','T','P','E','Z'] units = ['','K','M','G','T','P','E','Z']
@ -218,12 +218,12 @@ class TestIdentity(unittest.TestCase):
for unit in units: for unit in units:
if abs(num) < 1000.0: if abs(num) < 1000.0:
if unit == "": if unit == "":
return "%.0f %s%s" % (num, unit, suffix) return f"{num:.0f} {unit}{suffix}"
else: else:
return "%.2f %s%s" % (num, unit, suffix) return f"{num:.2f} {unit}{suffix}"
num /= 1000.0 num /= 1000.0
return "%.2f%s%s" % (num, last_unit, suffix) return f"{num:.2f}{last_unit}{suffix}"
if __name__ == '__main__': if __name__ == '__main__':
unittest.main(verbosity=2) unittest.main(verbosity=2)

View File

@ -28,7 +28,7 @@ BUFFER_TEST_TARGET = 32000
def targets_job(caller): def targets_job(caller):
cmd = "python -c \"from tests.link import targets; targets()\"" cmd = "python -c \"from tests.link import targets; targets()\""
print("Opening subprocess for "+str(cmd)+"...", RNS.LOG_VERBOSE) print(f"Opening subprocess for {cmd}...", RNS.LOG_VERBOSE)
ppath = os.getcwd() ppath = os.getcwd()
try: try:
@ -105,7 +105,7 @@ class TestLink(unittest.TestCase):
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish") dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d")) self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
l1 = RNS.Link(dest) l1 = RNS.Link(dest)
time.sleep(0.5) time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.ACTIVE) self.assertEqual(l1.status, RNS.Link.ACTIVE)
@ -129,7 +129,7 @@ class TestLink(unittest.TestCase):
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish") dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d")) self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
l1 = RNS.Link(dest) l1 = RNS.Link(dest)
time.sleep(0.5) time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.ACTIVE) self.assertEqual(l1.status, RNS.Link.ACTIVE)
@ -144,7 +144,7 @@ class TestLink(unittest.TestCase):
packet_size = RNS.Link.MDU packet_size = RNS.Link.MDU
pstart = time.time() pstart = time.time()
print("Sending "+str(num_packets)+" link packets of "+str(packet_size)+" bytes...") print(f"Sending {num_packets} link packets of {packet_size} bytes...")
for i in range(0, num_packets): for i in range(0, num_packets):
time.sleep(0.003) time.sleep(0.003)
b += packet_size b += packet_size
@ -154,7 +154,7 @@ class TestLink(unittest.TestCase):
receipts.append(p.send()) receipts.append(p.send())
pr_t += time.time() - start pr_t += time.time() - start
print("Sent "+self.size_str(b)+", "+self.size_str(b/pr_t, "b")+"ps") print(f"Sent {self.size_str(b)}, {self.size_str(b / pr_t, 'b')}ps")
print("Checking receipts...", end=" ") print("Checking receipts...", end=" ")
all_ok = False all_ok = False
@ -175,11 +175,11 @@ class TestLink(unittest.TestCase):
if n_failed > 0: if n_failed > 0:
ns = "s" if n_failed != 1 else "" ns = "s" if n_failed != 1 else ""
print("Failed to receive proof for "+str(n_failed)+" packet"+ns) print(f"Failed to receive proof for {n_failed} packet{ns}")
self.assertEqual(all_ok, True) self.assertEqual(all_ok, True)
print("OK!") print("OK!")
print("Single packet and proof round-trip throughput is "+self.size_str(b/pduration, "b")+"ps") print(f"Single packet and proof round-trip throughput is {self.size_str(b / pduration, 'b')}ps")
l1.teardown() l1.teardown()
time.sleep(0.5) time.sleep(0.5)
@ -201,7 +201,7 @@ class TestLink(unittest.TestCase):
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish") dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d")) self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
l1 = RNS.Link(dest) l1 = RNS.Link(dest)
time.sleep(0.5) time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.ACTIVE) self.assertEqual(l1.status, RNS.Link.ACTIVE)
@ -209,7 +209,7 @@ class TestLink(unittest.TestCase):
resource_timeout = 120 resource_timeout = 120
resource_size = 128 resource_size = 128
data = os.urandom(resource_size) data = os.urandom(resource_size)
print("Sending "+self.size_str(resource_size)+" resource...") print(f"Sending {self.size_str(resource_size)} resource...")
resource = RNS.Resource(data, l1, timeout=resource_timeout) resource = RNS.Resource(data, l1, timeout=resource_timeout)
start = time.time() start = time.time()
@ -219,7 +219,7 @@ class TestLink(unittest.TestCase):
t = time.time() - start t = time.time() - start
self.assertEqual(resource.status, RNS.Resource.COMPLETE) self.assertEqual(resource.status, RNS.Resource.COMPLETE)
print("Resource completed at "+self.size_str(resource_size/t, "b")+"ps") print(f"Resource completed at {self.size_str(resource_size / t, 'b')}ps")
l1.teardown() l1.teardown()
time.sleep(0.5) time.sleep(0.5)
@ -241,7 +241,7 @@ class TestLink(unittest.TestCase):
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish") dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d")) self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
l1 = RNS.Link(dest) l1 = RNS.Link(dest)
time.sleep(0.5) time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.ACTIVE) self.assertEqual(l1.status, RNS.Link.ACTIVE)
@ -249,7 +249,7 @@ class TestLink(unittest.TestCase):
resource_timeout = 120 resource_timeout = 120
resource_size = 256*1000 resource_size = 256*1000
data = os.urandom(resource_size) data = os.urandom(resource_size)
print("Sending "+self.size_str(resource_size)+" resource...") print(f"Sending {self.size_str(resource_size)} resource...")
resource = RNS.Resource(data, l1, timeout=resource_timeout) resource = RNS.Resource(data, l1, timeout=resource_timeout)
start = time.time() start = time.time()
@ -259,7 +259,7 @@ class TestLink(unittest.TestCase):
t = time.time() - start t = time.time() - start
self.assertEqual(resource.status, RNS.Resource.COMPLETE) self.assertEqual(resource.status, RNS.Resource.COMPLETE)
print("Resource completed at "+self.size_str(resource_size/t, "b")+"ps") print(f"Resource completed at {self.size_str(resource_size / t, 'b')}ps")
l1.teardown() l1.teardown()
time.sleep(0.5) time.sleep(0.5)
@ -280,7 +280,7 @@ class TestLink(unittest.TestCase):
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish") dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d")) self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
l1 = RNS.Link(dest) l1 = RNS.Link(dest)
time.sleep(0.5) time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.ACTIVE) self.assertEqual(l1.status, RNS.Link.ACTIVE)
@ -288,7 +288,7 @@ class TestLink(unittest.TestCase):
resource_timeout = 120 resource_timeout = 120
resource_size = 1000*1000 resource_size = 1000*1000
data = os.urandom(resource_size) data = os.urandom(resource_size)
print("Sending "+self.size_str(resource_size)+" resource...") print(f"Sending {self.size_str(resource_size)} resource...")
resource = RNS.Resource(data, l1, timeout=resource_timeout) resource = RNS.Resource(data, l1, timeout=resource_timeout)
start = time.time() start = time.time()
@ -298,7 +298,7 @@ class TestLink(unittest.TestCase):
t = time.time() - start t = time.time() - start
self.assertEqual(resource.status, RNS.Resource.COMPLETE) self.assertEqual(resource.status, RNS.Resource.COMPLETE)
print("Resource completed at "+self.size_str(resource_size/t, "b")+"ps") print(f"Resource completed at {self.size_str(resource_size / t, 'b')}ps")
l1.teardown() l1.teardown()
time.sleep(0.5) time.sleep(0.5)
@ -310,7 +310,7 @@ class TestLink(unittest.TestCase):
if RNS.Cryptography.backend() == "internal": if RNS.Cryptography.backend() == "internal":
print("Skipping medium resource test...") print("Skipping medium resource test...")
return return
init_rns(self) init_rns(self)
print("") print("")
print("Medium resource test") print("Medium resource test")
@ -324,7 +324,7 @@ class TestLink(unittest.TestCase):
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish") dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d")) self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
l1 = RNS.Link(dest) l1 = RNS.Link(dest)
time.sleep(0.5) time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.ACTIVE) self.assertEqual(l1.status, RNS.Link.ACTIVE)
@ -332,7 +332,7 @@ class TestLink(unittest.TestCase):
resource_timeout = 120 resource_timeout = 120
resource_size = 5*1000*1000 resource_size = 5*1000*1000
data = os.urandom(resource_size) data = os.urandom(resource_size)
print("Sending "+self.size_str(resource_size)+" resource...") print(f"Sending {self.size_str(resource_size)} resource...")
resource = RNS.Resource(data, l1, timeout=resource_timeout) resource = RNS.Resource(data, l1, timeout=resource_timeout)
start = time.time() start = time.time()
@ -342,7 +342,7 @@ class TestLink(unittest.TestCase):
t = time.time() - start t = time.time() - start
self.assertEqual(resource.status, RNS.Resource.COMPLETE) self.assertEqual(resource.status, RNS.Resource.COMPLETE)
print("Resource completed at "+self.size_str(resource_size/t, "b")+"ps") print(f"Resource completed at {self.size_str(resource_size / t, 'b')}ps")
l1.teardown() l1.teardown()
time.sleep(0.5) time.sleep(0.5)
@ -371,7 +371,7 @@ class TestLink(unittest.TestCase):
dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish") dest = RNS.Destination(id1, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "link", "establish")
self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d")) self.assertEqual(dest.hash, bytes.fromhex("fb48da0e82e6e01ba0c014513f74540d"))
l1 = RNS.Link(dest) l1 = RNS.Link(dest)
time.sleep(0.5) time.sleep(0.5)
self.assertEqual(l1.status, RNS.Link.ACTIVE) self.assertEqual(l1.status, RNS.Link.ACTIVE)
@ -379,7 +379,7 @@ class TestLink(unittest.TestCase):
resource_timeout = 120 resource_timeout = 120
resource_size = 50*1000*1000 resource_size = 50*1000*1000
data = os.urandom(resource_size) data = os.urandom(resource_size)
print("Sending "+self.size_str(resource_size)+" resource...") print(f"Sending {self.size_str(resource_size)} resource...")
resource = RNS.Resource(data, l1, timeout=resource_timeout, callback=self.lr_callback) resource = RNS.Resource(data, l1, timeout=resource_timeout, callback=self.lr_callback)
start = time.time() start = time.time()
@ -389,7 +389,7 @@ class TestLink(unittest.TestCase):
t = time.time() - start t = time.time() - start
self.assertEqual(TestLink.large_resource_status, RNS.Resource.COMPLETE) self.assertEqual(TestLink.large_resource_status, RNS.Resource.COMPLETE)
print("Resource completed at "+self.size_str(resource_size/t, "b")+"ps") print(f"Resource completed at {self.size_str(resource_size / t, 'b')}ps")
l1.teardown() l1.teardown()
time.sleep(0.5) time.sleep(0.5)
@ -478,7 +478,7 @@ class TestLink(unittest.TestCase):
channel = l1.get_channel() channel = l1.get_channel()
buffer = RNS.Buffer.create_bidirectional_buffer(0, 0, channel, handle_data) buffer = RNS.Buffer.create_bidirectional_buffer(0, 0, channel, handle_data)
buffer.write("Hi there".encode("utf-8")) buffer.write(b"Hi there")
buffer.flush() buffer.flush()
time.sleep(0.5) time.sleep(0.5)
@ -508,7 +508,7 @@ class TestLink(unittest.TestCase):
if local_bitrate is not None: if local_bitrate is not None:
local_interface.bitrate = local_bitrate local_interface.bitrate = local_bitrate
local_interface._force_bitrate = True local_interface._force_bitrate = True
print("Forcing local bitrate of " + str(local_bitrate) + " bps (" + str(round(local_bitrate/8, 0)) + " B/s)") print(f"Forcing local bitrate of {local_bitrate} bps ({round(local_bitrate / 8, 0)} B/s)")
# TODO: Load this from public bytes only # TODO: Load this from public bytes only
id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0])) id1 = RNS.Identity.from_bytes(bytes.fromhex(fixed_keys[0][0]))
@ -525,7 +525,7 @@ class TestLink(unittest.TestCase):
# delay a reasonable time for link to come up at current bitrate # delay a reasonable time for link to come up at current bitrate
link_sleep = max(RNS.Link.MDU * 3 / local_interface.bitrate * 8, 2) link_sleep = max(RNS.Link.MDU * 3 / local_interface.bitrate * 8, 2)
timeout_at = time.time() + link_sleep timeout_at = time.time() + link_sleep
print("Waiting " + str(round(link_sleep, 1)) + " sec for link to come up") print(f"Waiting {round(link_sleep, 1)} sec for link to come up")
while l1.status != RNS.Link.ACTIVE and time.time() < timeout_at: while l1.status != RNS.Link.ACTIVE and time.time() < timeout_at:
time.sleep(0.01) time.sleep(0.01)
@ -553,26 +553,26 @@ class TestLink(unittest.TestCase):
target_bytes = 3000 target_bytes = 3000
else: else:
target_bytes = BUFFER_TEST_TARGET target_bytes = BUFFER_TEST_TARGET
random.seed(154889) random.seed(154889)
message = random.randbytes(target_bytes) message = random.randbytes(target_bytes)
buffer_read_target = len(message) buffer_read_target = len(message)
# the return message will have an appendage string " back at you" # the return message will have an appendage string " back at you"
# for every StreamDataMessage that arrives. To verify, we need # for every StreamDataMessage that arrives. To verify, we need
# to insert that string every MAX_DATA_LEN and also at the end. # to insert that string every MAX_DATA_LEN and also at the end.
expected_rx_message = b"" expected_rx_message = b""
for i in range(0, len(message)): for i in range(0, len(message)):
if i > 0 and (i % StreamDataMessage.MAX_DATA_LEN) == 0: if i > 0 and (i % StreamDataMessage.MAX_DATA_LEN) == 0:
expected_rx_message += " back at you".encode("utf-8") expected_rx_message += b" back at you"
expected_rx_message += bytes([message[i]]) expected_rx_message += bytes([message[i]])
expected_rx_message += " back at you".encode("utf-8") expected_rx_message += b" back at you"
# since the segments will be received at max length for a # since the segments will be received at max length for a
# StreamDataMessage, the appended text will end up in a # StreamDataMessage, the appended text will end up in a
# separate packet. # separate packet.
print("Sending " + str(len(message)) + " bytes, receiving " + str(len(expected_rx_message)) + " bytes, ") print(f"Sending {len(message)} bytes, receiving {len(expected_rx_message)} bytes, ")
buffer.write(message) buffer.write(message)
buffer.flush() buffer.flush()
@ -621,12 +621,12 @@ class TestLink(unittest.TestCase):
for unit in units: for unit in units:
if abs(num) < 1000.0: if abs(num) < 1000.0:
if unit == "": if unit == "":
return "%.0f %s%s" % (num, unit, suffix) return f"{num:.0f} {unit}{suffix}"
else: else:
return "%.2f %s%s" % (num, unit, suffix) return f"{num:.2f} {unit}{suffix}"
num /= 1000.0 num /= 1000.0
return "%.2f%s%s" % (num, last_unit, suffix) return f"{num:.2f}{last_unit}{suffix}"
if __name__ == '__main__': if __name__ == '__main__':
unittest.main(verbosity=1) unittest.main(verbosity=1)
@ -650,16 +650,16 @@ def targets(yp=False):
threads = yappi.get_thread_stats() threads = yappi.get_thread_stats()
for thread in threads: for thread in threads:
print( print(
"Function stats for (%s) (%d)" % (thread.name, thread.id) f"Function stats for ({thread.name}) ({int(thread.id)})"
) # it is the Thread.__class__.__name__ ) # it is the Thread.__class__.__name__
yappi.get_func_stats(ctx_id=thread.id).save("receiver_thread_"+str(thread.id)+".data", type="pstat") yappi.get_func_stats(ctx_id=thread.id).save(f"receiver_thread_{thread.id}.data", type="pstat")
except Exception as e: except Exception as e:
print("Error: "+str(e)) print(f"Error: {e}")
if hasattr(resource.link.attached_interface, "rxptime"): if hasattr(resource.link.attached_interface, "rxptime"):
rx_pr = (resource.link.attached_interface.rxb*8)/resource.link.attached_interface.rxptime rx_pr = (resource.link.attached_interface.rxb*8)/resource.link.attached_interface.rxptime
print("Average RX proccessing rate: "+size_str(rx_pr, "b")+"ps") print(f"Average RX proccessing rate: {size_str(rx_pr, 'b')}ps")
def link_established(link): def link_established(link):
print("Link established") print("Link established")
@ -670,7 +670,7 @@ def targets(yp=False):
def handle_message(message): def handle_message(message):
if isinstance(message, MessageTest): if isinstance(message, MessageTest):
message.data = message.data + " back" message.data = f"{message.data} back"
channel.send(message) channel.send(message)
channel.register_message_type(MessageTest) channel.register_message_type(MessageTest)
@ -685,17 +685,17 @@ def targets(yp=False):
buffer_read_len += len(data) buffer_read_len += len(data)
response_data.append(data) response_data.append(data)
if data == "Hi there".encode("utf-8"): if data == b"Hi there":
RNS.log("Sending response") RNS.log("Sending response")
for data in response_data: for data in response_data:
buffer.write(data + " back at you".encode("utf-8")) buffer.write(data + b" back at you")
buffer.flush() buffer.flush()
buffer_read_len = 0 buffer_read_len = 0
if buffer_read_len == BUFFER_TEST_TARGET: if buffer_read_len == BUFFER_TEST_TARGET:
RNS.log("Sending response") RNS.log("Sending response")
for data in response_data: for data in response_data:
buffer.write(data + " back at you".encode("utf-8")) buffer.write(data + b" back at you")
buffer.flush() buffer.flush()
buffer_read_len = 0 buffer_read_len = 0
@ -723,7 +723,7 @@ def profile_resource():
resource_profiling() resource_profiling()
def profile_targets(): def profile_targets():
targets_profiling(yp=True) targets_profiling(yp=True)
# cProfile.runctx("entry()", {"entry": targets_profiling, "size_str": size_str}, {}, "profile-targets.data") # cProfile.runctx("entry()", {"entry": targets_profiling, "size_str": size_str}, {}, "profile-targets.data")
# p = pstats.Stats("profile-targets.data") # p = pstats.Stats("profile-targets.data")
@ -745,11 +745,11 @@ def resource_profiling():
resource_timeout = 120 resource_timeout = 120
resource_size = 5*1000*1000 resource_size = 5*1000*1000
data = os.urandom(resource_size) data = os.urandom(resource_size)
print("Sending "+size_str(resource_size)+" resource...") print(f"Sending {size_str(resource_size)} resource...")
import yappi import yappi
yappi.start() yappi.start()
resource = RNS.Resource(data, l1, timeout=resource_timeout) resource = RNS.Resource(data, l1, timeout=resource_timeout)
start = time.time() start = time.time()
@ -759,22 +759,22 @@ def resource_profiling():
time.sleep(0.01) time.sleep(0.01)
t = time.time() - start t = time.time() - start
print("Resource completed at "+size_str(resource_size/t, "b")+"ps") print(f"Resource completed at {size_str(resource_size / t, 'b')}ps")
yappi.get_func_stats().save("sender_main_calls.data", type="pstat") yappi.get_func_stats().save("sender_main_calls.data", type="pstat")
threads = yappi.get_thread_stats() threads = yappi.get_thread_stats()
for thread in threads: for thread in threads:
print( print(
"Function stats for (%s) (%d)" % (thread.name, thread.id) f"Function stats for ({thread.name}) ({int(thread.id)})"
) # it is the Thread.__class__.__name__ ) # it is the Thread.__class__.__name__
yappi.get_func_stats(ctx_id=thread.id).save("sender_thread_"+str(thread.id)+".data", type="pstat") yappi.get_func_stats(ctx_id=thread.id).save(f"sender_thread_{thread.id}.data", type="pstat")
# t_pstats = yappi.convert2pstats(tstats) # t_pstats = yappi.convert2pstats(tstats)
# t_pstats.save("resource_tstat.data", type="pstat") # t_pstats.save("resource_tstat.data", type="pstat")
if hasattr(resource.link.attached_interface, "rxptime"): if hasattr(resource.link.attached_interface, "rxptime"):
rx_pr = (resource.link.attached_interface.rxb*8)/resource.link.attached_interface.rxptime rx_pr = (resource.link.attached_interface.rxb*8)/resource.link.attached_interface.rxptime
print("Average RX proccessing rate: "+size_str(rx_pr, "b")+"ps") print(f"Average RX proccessing rate: {size_str(rx_pr, 'b')}ps")
l1.teardown() l1.teardown()
time.sleep(0.5) time.sleep(0.5)
@ -791,9 +791,9 @@ def size_str(num, suffix='B'):
for unit in units: for unit in units:
if abs(num) < 1000.0: if abs(num) < 1000.0:
if unit == "": if unit == "":
return "%.0f %s%s" % (num, unit, suffix) return f"{num:.0f} {unit}{suffix}"
else: else:
return "%.2f %s%s" % (num, unit, suffix) return f"{num:.2f} {unit}{suffix}"
num /= 1000.0 num /= 1000.0
return "%.2f%s%s" % (num, last_unit, suffix) return f"{num:.2f}{last_unit}{suffix}"