diff --git a/RNS/Reticulum.py b/RNS/Reticulum.py index 8dce164..7abd988 100755 --- a/RNS/Reticulum.py +++ b/RNS/Reticulum.py @@ -738,6 +738,9 @@ class Reticulum: if path == "interface_stats": rpc_connection.send(self.get_interface_stats()) + if path == "path_table": + rpc_connection.send(self.get_path_table()) + if path == "next_hop_if_name": rpc_connection.send(self.get_next_hop_if_name(call["destination_hash"])) @@ -809,6 +812,28 @@ class Reticulum: return stats + def get_path_table(self): + if self.is_connected_to_shared_instance: + rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key) + rpc_connection.send({"get": "path_table"}) + response = rpc_connection.recv() + return response + + else: + path_table = [] + for dst_hash in RNS.Transport.destination_table: + entry = { + "hash": dst_hash, + "timestamp": RNS.Transport.destination_table[dst_hash][0], + "via": RNS.Transport.destination_table[dst_hash][1], + "hops": RNS.Transport.destination_table[dst_hash][2], + "expires": RNS.Transport.destination_table[dst_hash][3], + "interface": str(RNS.Transport.destination_table[dst_hash][5]), + } + path_table.append(entry) + + return path_table + def get_next_hop_if_name(self, destination): if self.is_connected_to_shared_instance: rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key) diff --git a/RNS/Utilities/rnpath.py b/RNS/Utilities/rnpath.py index 74748f8..bddba02 100644 --- a/RNS/Utilities/rnpath.py +++ b/RNS/Utilities/rnpath.py @@ -30,44 +30,53 @@ import argparse from RNS._version import __version__ -def program_setup(configdir, destination_hexhash, verbosity): - try: - dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 - if len(destination_hexhash) != dest_len: - raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)) - try: - destination_hash = bytes.fromhex(destination_hexhash) - except Exception as e: - raise ValueError("Invalid destination entered. Check your input.") - except Exception as e: - print(str(e)) - exit() +def program_setup(configdir, table, destination_hexhash, verbosity): + if table: + reticulum = RNS.Reticulum(configdir = configdir, loglevel = 3+verbosity) + table = sorted(reticulum.get_path_table(), key=lambda e: (e["interface"], e["hops"]) ) - reticulum = RNS.Reticulum(configdir = configdir, loglevel = 3+verbosity) + for path in table: + exp_str = RNS.timestamp_str(path["expires"]) + print(RNS.prettyhexrep(path["hash"])+" is "+str(path["hops"])+" hops away via "+RNS.prettyhexrep(path["via"])+" on "+path["interface"]+" expires "+RNS.timestamp_str(path["expires"])) - if not RNS.Transport.has_path(destination_hash): - RNS.Transport.request_path(destination_hash) - print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=" ") - sys.stdout.flush() - - i = 0 - syms = "⢄⢂⢁⡁⡈⡐⡠" - while not RNS.Transport.has_path(destination_hash): - time.sleep(0.1) - print(("\b\b"+syms[i]+" "), end="") - sys.stdout.flush() - i = (i+1)%len(syms) - - hops = RNS.Transport.hops_to(destination_hash) - next_hop = RNS.prettyhexrep(reticulum.get_next_hop(destination_hash)) - next_hop_interface = reticulum.get_next_hop_if_name(destination_hash) - - if hops != 1: - ms = "s" else: - ms = "" + try: + dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2 + if len(destination_hexhash) != dest_len: + raise ValueError("Destination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2)) + try: + destination_hash = bytes.fromhex(destination_hexhash) + except Exception as e: + raise ValueError("Invalid destination entered. Check your input.") + except Exception as e: + print(str(e)) + exit() - print("\rPath found, destination "+RNS.prettyhexrep(destination_hash)+" is "+str(hops)+" hop"+ms+" away via "+next_hop+" on "+next_hop_interface) + reticulum = RNS.Reticulum(configdir = configdir, loglevel = 3+verbosity) + + if not RNS.Transport.has_path(destination_hash): + RNS.Transport.request_path(destination_hash) + print("Path to "+RNS.prettyhexrep(destination_hash)+" requested ", end=" ") + sys.stdout.flush() + + i = 0 + syms = "⢄⢂⢁⡁⡈⡐⡠" + while not RNS.Transport.has_path(destination_hash): + time.sleep(0.1) + print(("\b\b"+syms[i]+" "), end="") + sys.stdout.flush() + i = (i+1)%len(syms) + + hops = RNS.Transport.hops_to(destination_hash) + next_hop = RNS.prettyhexrep(reticulum.get_next_hop(destination_hash)) + next_hop_interface = reticulum.get_next_hop_if_name(destination_hash) + + if hops != 1: + ms = "s" + else: + ms = "" + + print("\rPath found, destination "+RNS.prettyhexrep(destination_hash)+" is "+str(hops)+" hop"+ms+" away via "+next_hop+" on "+next_hop_interface) def main(): @@ -87,6 +96,14 @@ def main(): version="rnpath {version}".format(version=__version__) ) + parser.add_argument( + "-t", + "--table", + action="store_true", + help="show all known paths", + default=False + ) + parser.add_argument( "destination", nargs="?", @@ -104,12 +121,12 @@ def main(): else: configarg = None - if not args.destination: + if not args.table and not args.destination: print("") parser.print_help() print("") else: - program_setup(configdir = configarg, destination_hexhash = args.destination, verbosity = args.verbose) + program_setup(configdir = configarg, table = args.table, destination_hexhash = args.destination, verbosity = args.verbose) except KeyboardInterrupt: print("") diff --git a/RNS/__init__.py b/RNS/__init__.py index 877c588..657a0b2 100755 --- a/RNS/__init__.py +++ b/RNS/__init__.py @@ -93,12 +93,15 @@ def host_os(): from .vendor.platformutils import get_platform return get_platform() +def timestamp_str(time_s): + timestamp = time.localtime(time_s) + return time.strftime(logtimefmt, timestamp) + def log(msg, level=3, _override_destination = False): global _always_override_destination if loglevel >= level: - timestamp = time.time() - logstring = "["+time.strftime(logtimefmt)+"] ["+loglevelname(level)+"] "+msg + logstring = "["+timestamp_str(time.time())+"] ["+loglevelname(level)+"] "+msg logging_lock.acquire() if (logdest == LOG_STDOUT or _always_override_destination or _override_destination):