mirror of
https://github.com/markqvist/Reticulum.git
synced 2026-04-27 22:25:37 +00:00
Implemented blackhole management
This commit is contained in:
parent
18e4e66db8
commit
c13412369a
5 changed files with 319 additions and 66 deletions
|
|
@ -430,6 +430,11 @@ class Identity:
|
|||
announced_identity = Identity(create_keys=False)
|
||||
announced_identity.load_public_key(public_key)
|
||||
|
||||
if len(RNS.Transport.blackholed_identities) > 0:
|
||||
if announced_identity.hash in RNS.Transport.blackholed_identities:
|
||||
RNS.log(f"Invalidated and dropped announce from blackholed identity {RNS.prettyhexrep(announced_identity.hash)}", RNS.LOG_EXTREME)
|
||||
return False
|
||||
|
||||
if announced_identity.pub != None and announced_identity.validate(signature, signed_data):
|
||||
if only_validate_signature:
|
||||
del announced_identity
|
||||
|
|
|
|||
180
RNS/Reticulum.py
180
RNS/Reticulum.py
|
|
@ -248,6 +248,7 @@ class Reticulum:
|
|||
Reticulum.cachepath = Reticulum.configdir+"/storage/cache"
|
||||
Reticulum.resourcepath = Reticulum.configdir+"/storage/resources"
|
||||
Reticulum.identitypath = Reticulum.configdir+"/storage/identities"
|
||||
Reticulum.blackholepath = Reticulum.configdir+"/storage/blackhole"
|
||||
Reticulum.interfacepath = Reticulum.configdir+"/interfaces"
|
||||
|
||||
Reticulum.__transport_enabled = False
|
||||
|
|
@ -258,6 +259,8 @@ class Reticulum:
|
|||
Reticulum.__discovery_enabled = False
|
||||
Reticulum.__discover_interfaces = False
|
||||
Reticulum.__required_discovery_value = None
|
||||
Reticulum.__publish_blackhole = False
|
||||
Reticulum.__blackhole_sources = []
|
||||
|
||||
Reticulum.panic_on_interface_error = False
|
||||
|
||||
|
|
@ -276,10 +279,8 @@ class Reticulum:
|
|||
self.requested_loglevel = loglevel
|
||||
self.requested_verbosity = verbosity
|
||||
if self.requested_loglevel != None:
|
||||
if self.requested_loglevel > RNS.LOG_EXTREME:
|
||||
self.requested_loglevel = RNS.LOG_EXTREME
|
||||
if self.requested_loglevel < RNS.LOG_CRITICAL:
|
||||
self.requested_loglevel = RNS.LOG_CRITICAL
|
||||
if self.requested_loglevel > RNS.LOG_EXTREME: self.requested_loglevel = RNS.LOG_EXTREME
|
||||
if self.requested_loglevel < RNS.LOG_CRITICAL: self.requested_loglevel = RNS.LOG_CRITICAL
|
||||
|
||||
RNS.loglevel = self.requested_loglevel
|
||||
|
||||
|
|
@ -292,27 +293,16 @@ class Reticulum:
|
|||
self.last_data_persist = time.time()
|
||||
self.last_cache_clean = 0
|
||||
|
||||
if not os.path.isdir(Reticulum.storagepath):
|
||||
os.makedirs(Reticulum.storagepath)
|
||||
|
||||
if not os.path.isdir(Reticulum.cachepath):
|
||||
os.makedirs(Reticulum.cachepath)
|
||||
|
||||
if not os.path.isdir(os.path.join(Reticulum.cachepath, "announces")):
|
||||
os.makedirs(os.path.join(Reticulum.cachepath, "announces"))
|
||||
|
||||
if not os.path.isdir(Reticulum.resourcepath):
|
||||
os.makedirs(Reticulum.resourcepath)
|
||||
|
||||
if not os.path.isdir(Reticulum.identitypath):
|
||||
os.makedirs(Reticulum.identitypath)
|
||||
|
||||
if not os.path.isdir(Reticulum.interfacepath):
|
||||
os.makedirs(Reticulum.interfacepath)
|
||||
if not os.path.isdir(Reticulum.storagepath): os.makedirs(Reticulum.storagepath)
|
||||
if not os.path.isdir(Reticulum.cachepath): os.makedirs(Reticulum.cachepath)
|
||||
if not os.path.isdir(Reticulum.resourcepath): os.makedirs(Reticulum.resourcepath)
|
||||
if not os.path.isdir(Reticulum.identitypath): os.makedirs(Reticulum.identitypath)
|
||||
if not os.path.isdir(Reticulum.blackholepath): os.makedirs(Reticulum.blackholepath)
|
||||
if not os.path.isdir(Reticulum.interfacepath): os.makedirs(Reticulum.interfacepath)
|
||||
if not os.path.isdir(os.path.join(Reticulum.cachepath, "announces")): os.makedirs(os.path.join(Reticulum.cachepath, "announces"))
|
||||
|
||||
if os.path.isfile(self.configpath):
|
||||
try:
|
||||
self.config = ConfigObj(self.configpath)
|
||||
try: self.config = ConfigObj(self.configpath)
|
||||
except Exception as e:
|
||||
RNS.log("Could not parse the configuration at "+self.configpath, RNS.LOG_ERROR)
|
||||
RNS.log("Check your configuration file for errors!", RNS.LOG_ERROR)
|
||||
|
|
@ -455,24 +445,30 @@ class Reticulum:
|
|||
if "reticulum" in self.config:
|
||||
for option in self.config["reticulum"]:
|
||||
value = self.config["reticulum"][option]
|
||||
|
||||
if option == "share_instance":
|
||||
value = self.config["reticulum"].as_bool(option)
|
||||
self.share_instance = value
|
||||
|
||||
if RNS.vendor.platformutils.use_af_unix():
|
||||
if option == "instance_name":
|
||||
value = self.config["reticulum"][option]
|
||||
self.local_socket_path = value
|
||||
|
||||
if option == "shared_instance_type":
|
||||
if self.shared_instance_type == None:
|
||||
value = self.config["reticulum"][option].lower()
|
||||
if value in ["tcp", "unix"]:
|
||||
self.shared_instance_type = value
|
||||
|
||||
if option == "shared_instance_port":
|
||||
value = int(self.config["reticulum"][option])
|
||||
self.local_interface_port = value
|
||||
|
||||
if option == "instance_control_port":
|
||||
value = int(self.config["reticulum"][option])
|
||||
self.local_control_port = value
|
||||
|
||||
if option == "rpc_key":
|
||||
try:
|
||||
value = bytes.fromhex(self.config["reticulum"][option])
|
||||
|
|
@ -480,49 +476,70 @@ class Reticulum:
|
|||
except Exception as e:
|
||||
RNS.log("Invalid shared instance RPC key specified, falling back to default key", RNS.LOG_ERROR)
|
||||
self.rpc_key = None
|
||||
|
||||
if option == "enable_transport":
|
||||
v = self.config["reticulum"].as_bool(option)
|
||||
if v == True: Reticulum.__transport_enabled = True
|
||||
|
||||
if option == "link_mtu_discovery":
|
||||
v = self.config["reticulum"].as_bool(option)
|
||||
if v == True: Reticulum.__link_mtu_discovery = True
|
||||
|
||||
if option == "enable_remote_management":
|
||||
v = self.config["reticulum"].as_bool(option)
|
||||
if v == True: Reticulum.__remote_management_enabled = True
|
||||
|
||||
if option == "remote_management_allowed":
|
||||
v = self.config["reticulum"].as_list(option)
|
||||
for hexhash in v:
|
||||
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||
if len(hexhash) != dest_len:
|
||||
raise ValueError("Identity hash length for remote management ACL "+str(hexhash)+" is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
|
||||
try:
|
||||
allowed_hash = bytes.fromhex(hexhash)
|
||||
except Exception as e:
|
||||
raise ValueError("Invalid identity hash for remote management ACL: "+str(hexhash))
|
||||
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))
|
||||
try: allowed_hash = bytes.fromhex(hexhash)
|
||||
except Exception as e: raise ValueError("Invalid identity hash for remote management ACL: "+str(hexhash))
|
||||
|
||||
if not allowed_hash in RNS.Transport.remote_management_allowed:
|
||||
RNS.Transport.remote_management_allowed.append(allowed_hash)
|
||||
|
||||
if option == "respond_to_probes":
|
||||
v = self.config["reticulum"].as_bool(option)
|
||||
if v == True: Reticulum.__allow_probes = True
|
||||
|
||||
if option == "force_shared_instance_bitrate":
|
||||
v = self.config["reticulum"].as_int(option)
|
||||
Reticulum._force_shared_instance_bitrate = v
|
||||
|
||||
if option == "panic_on_interface_error":
|
||||
v = self.config["reticulum"].as_bool(option)
|
||||
if v == True: Reticulum.panic_on_interface_error = True
|
||||
|
||||
if option == "use_implicit_proof":
|
||||
v = self.config["reticulum"].as_bool(option)
|
||||
if v == True: Reticulum.__use_implicit_proof = True
|
||||
if v == False: Reticulum.__use_implicit_proof = False
|
||||
|
||||
if option == "discover_interfaces":
|
||||
v = self.config["reticulum"].as_bool(option)
|
||||
if v == True: Reticulum.__discover_interfaces = True
|
||||
if v == False: Reticulum.__discover_interfaces = False
|
||||
|
||||
if option == "required_discovery_value":
|
||||
v = self.config["reticulum"].as_int(option)
|
||||
if v > 0: Reticulum.__required_discovery_value = v
|
||||
else: Reticulum.__required_discovery_value = None
|
||||
|
||||
if option == "publish_blackhole":
|
||||
v = self.config["reticulum"].as_bool(option)
|
||||
if v == True: Reticulum.__publish_blackhole = True
|
||||
if v == False: Reticulum.__publish_blackhole = False
|
||||
|
||||
if option == "blackhole_sources":
|
||||
v = self.config["reticulum"].as_list(option)
|
||||
for hexhash in v:
|
||||
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||
if len(hexhash) != dest_len: raise ValueError(f"Identity hash length for blackhole source {hexhash} is invalid, must be {dest_len} hexadecimal characters ({dest_len//2} bytes).")
|
||||
try: source_identity_hash = bytes.fromhex(hexhash)
|
||||
except Exception as e: raise ValueError(f"Invalid identity hash for remote blackhole source: {hexhash}")
|
||||
if not source_identity_hash in Reticulum.__blackhole_sources: Reticulum.__blackhole_sources.append(source_identity_hash)
|
||||
|
||||
if RNS.compiled: RNS.log("Reticulum running in compiled mode", RNS.LOG_DEBUG)
|
||||
else: RNS.log("Reticulum running in interpreted mode", RNS.LOG_DEBUG)
|
||||
|
|
@ -968,48 +985,34 @@ class Reticulum:
|
|||
if "get" in call:
|
||||
path = call["get"]
|
||||
|
||||
if path == "interface_stats":
|
||||
rpc_connection.send(self.get_interface_stats())
|
||||
|
||||
if path == "path_table":
|
||||
mh = call["max_hops"]
|
||||
rpc_connection.send(self.get_path_table(max_hops=mh))
|
||||
|
||||
if path == "rate_table":
|
||||
rpc_connection.send(self.get_rate_table())
|
||||
|
||||
if path == "next_hop_if_name":
|
||||
rpc_connection.send(self.get_next_hop_if_name(call["destination_hash"]))
|
||||
|
||||
if path == "next_hop":
|
||||
rpc_connection.send(self.get_next_hop(call["destination_hash"]))
|
||||
|
||||
if path == "first_hop_timeout":
|
||||
rpc_connection.send(self.get_first_hop_timeout(call["destination_hash"]))
|
||||
|
||||
if path == "link_count":
|
||||
rpc_connection.send(self.get_link_count())
|
||||
|
||||
if path == "packet_rssi":
|
||||
rpc_connection.send(self.get_packet_rssi(call["packet_hash"]))
|
||||
|
||||
if path == "packet_snr":
|
||||
rpc_connection.send(self.get_packet_snr(call["packet_hash"]))
|
||||
|
||||
if path == "packet_q":
|
||||
rpc_connection.send(self.get_packet_q(call["packet_hash"]))
|
||||
if path == "interface_stats": rpc_connection.send(self.get_interface_stats())
|
||||
if path == "rate_table": rpc_connection.send(self.get_rate_table())
|
||||
if path == "next_hop_if_name": rpc_connection.send(self.get_next_hop_if_name(call["destination_hash"]))
|
||||
if path == "next_hop": rpc_connection.send(self.get_next_hop(call["destination_hash"]))
|
||||
if path == "first_hop_timeout": rpc_connection.send(self.get_first_hop_timeout(call["destination_hash"]))
|
||||
if path == "link_count": rpc_connection.send(self.get_link_count())
|
||||
if path == "packet_rssi": rpc_connection.send(self.get_packet_rssi(call["packet_hash"]))
|
||||
if path == "packet_snr": rpc_connection.send(self.get_packet_snr(call["packet_hash"]))
|
||||
if path == "packet_q": rpc_connection.send(self.get_packet_q(call["packet_hash"]))
|
||||
if path == "blackholed_identities": rpc_connection.send(self.get_blackholed_identities())
|
||||
|
||||
if "drop" in call:
|
||||
path = call["drop"]
|
||||
if path == "path": rpc_connection.send(self.drop_path(call["destination_hash"]))
|
||||
if path == "all_via": rpc_connection.send(self.drop_all_via(call["destination_hash"]))
|
||||
if path == "announce_queues": rpc_connection.send(self.drop_announce_queues())
|
||||
|
||||
if path == "path":
|
||||
rpc_connection.send(self.drop_path(call["destination_hash"]))
|
||||
if "blackhole_identity" in call:
|
||||
identity_hash = call["blackhole_identity"]
|
||||
rpc_connection.send(self.blackhole_identity(identity_hash))
|
||||
|
||||
if path == "all_via":
|
||||
rpc_connection.send(self.drop_all_via(call["destination_hash"]))
|
||||
|
||||
if path == "announce_queues":
|
||||
rpc_connection.send(self.drop_announce_queues())
|
||||
if "unblackhole_identity" in call:
|
||||
identity_hash = call["unblackhole_identity"]
|
||||
rpc_connection.send(self.unblackhole_identity(identity_hash))
|
||||
|
||||
rpc_connection.close()
|
||||
|
||||
|
|
@ -1376,6 +1379,37 @@ class Reticulum:
|
|||
def reload_interface(self, interface):
|
||||
pass
|
||||
|
||||
def get_blackholed_identities(self):
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"get": "blackholed_identities"})
|
||||
response = rpc_connection.recv()
|
||||
return response
|
||||
|
||||
else: return RNS.Transport.blackholed_identities
|
||||
|
||||
def blackhole_identity(self, identity_hash):
|
||||
if len(identity_hash) != RNS.Reticulum.TRUNCATED_HASHLENGTH//8: return False
|
||||
else:
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"blackhole_identity": identity_hash})
|
||||
response = rpc_connection.recv()
|
||||
return response
|
||||
|
||||
else: return RNS.Transport.blackhole_identity(identity_hash)
|
||||
|
||||
def unblackhole_identity(self, identity_hash):
|
||||
if len(identity_hash) != RNS.Reticulum.TRUNCATED_HASHLENGTH//8: return False
|
||||
else:
|
||||
if self.is_connected_to_shared_instance:
|
||||
rpc_connection = self.get_rpc_client()
|
||||
rpc_connection.send({"unblackhole_identity": identity_hash})
|
||||
response = rpc_connection.recv()
|
||||
return response
|
||||
|
||||
else: return RNS.Transport.unblackhole_identity(identity_hash)
|
||||
|
||||
@staticmethod
|
||||
def should_use_implicit_proof():
|
||||
"""
|
||||
|
|
@ -1433,13 +1467,33 @@ class Reticulum:
|
|||
@staticmethod
|
||||
def required_discovery_value():
|
||||
"""
|
||||
Returns the required stam value for a discovered interface
|
||||
Returns the required stamp value for a discovered interface
|
||||
to be considered valid and remembered.
|
||||
|
||||
:returns: The required stamp value as an integer.
|
||||
"""
|
||||
return Reticulum.__required_discovery_value
|
||||
|
||||
@staticmethod
|
||||
def publish_blackhole_enabled():
|
||||
"""
|
||||
Returns whether blackhole list publishing is enabled for the
|
||||
running instance.
|
||||
|
||||
:returns: True if blackhole list publishing is enabled, False if not.
|
||||
"""
|
||||
return Reticulum.__publish_blackhole
|
||||
|
||||
@staticmethod
|
||||
def blackhole_sources():
|
||||
"""
|
||||
Returns the list of transport identity hashes from which
|
||||
blackhole lists are sourced.
|
||||
|
||||
:returns: A list of identity hashes.
|
||||
"""
|
||||
return Reticulum.__blackhole_sources
|
||||
|
||||
# Default configuration file:
|
||||
__default_rns_config__ = '''# This is the default Reticulum config file.
|
||||
# You should probably edit it to include any additional,
|
||||
|
|
|
|||
119
RNS/Transport.py
119
RNS/Transport.py
|
|
@ -113,6 +113,7 @@ class Transport:
|
|||
announce_rate_table = {} # A table for keeping track of announce rates
|
||||
path_requests = {} # A table for storing path request timestamps
|
||||
path_states = {} # A table for keeping track of path states
|
||||
blackholed_identities = {} # A table for keeping track of blackholed identities
|
||||
|
||||
discovery_path_requests = {} # A table for keeping track of path requests on behalf of other nodes
|
||||
discovery_pr_tags = [] # A table for keeping track of tagged path requests
|
||||
|
|
@ -195,6 +196,8 @@ class Transport:
|
|||
except Exception as e:
|
||||
RNS.log("Could not load packet hashlist from storage, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
Transport.reload_blackhole()
|
||||
|
||||
# Create transport-specific destinations
|
||||
Transport.path_request_destination = RNS.Destination(None, RNS.Destination.IN, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request")
|
||||
Transport.path_request_destination.set_packet_callback(Transport.path_request_handler)
|
||||
|
|
@ -245,8 +248,14 @@ class Transport:
|
|||
random_blobs = serialised_entry[5]
|
||||
receiving_interface = Transport.find_interface_from_hash(serialised_entry[6])
|
||||
announce_packet = Transport.get_cached_packet(serialised_entry[7], packet_type="announce")
|
||||
blackholed = False
|
||||
|
||||
if announce_packet != None and receiving_interface != None:
|
||||
if len(Transport.blackholed_identities) > 0:
|
||||
path_identity = RNS.Identity.recall(destination_hash)
|
||||
if path_identity in Transport.blackholed_identities: blackholed = True
|
||||
del path_identity
|
||||
|
||||
if announce_packet != None and receiving_interface != None and blackholed == False:
|
||||
announce_packet.unpack()
|
||||
# We increase the hops, since reading a packet
|
||||
# from cache is equivalent to receiving it again
|
||||
|
|
@ -261,6 +270,8 @@ class Transport:
|
|||
RNS.log("The announce packet could not be loaded from cache", RNS.LOG_DEBUG)
|
||||
if receiving_interface == None:
|
||||
RNS.log("The interface is no longer available", RNS.LOG_DEBUG)
|
||||
if blackholed:
|
||||
RNS.log("The associated identity is blackholed", RNS.LOG_DEBUG)
|
||||
|
||||
if len(Transport.path_table) == 1:
|
||||
specifier = "entry"
|
||||
|
|
@ -3040,6 +3051,112 @@ class Transport:
|
|||
if not Transport.owner.is_connected_to_shared_instance:
|
||||
Transport.persist_data()
|
||||
|
||||
@staticmethod
|
||||
def blackhole_identity(identity_hash):
|
||||
try:
|
||||
if not identity_hash in Transport.blackholed_identities:
|
||||
entry = {"source": Transport.identity.hash, "until": None}
|
||||
Transport.blackholed_identities[identity_hash] = entry
|
||||
Transport.persist_blackhole()
|
||||
Transport.remove_blackholed_paths()
|
||||
RNS.log(f"Blackholed identity {RNS.prettyhexrep(identity_hash)}", RNS.LOG_INFO)
|
||||
return True
|
||||
|
||||
else: return None
|
||||
|
||||
except Exception as e:
|
||||
RNS.log(f"Error while blackholing identity: {e}", RNS.LOG_ERROR)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def unblackhole_identity(identity_hash):
|
||||
try:
|
||||
if identity_hash in Transport.blackholed_identities:
|
||||
Transport.blackholed_identities.pop(identity_hash)
|
||||
Transport.persist_blackhole()
|
||||
RNS.log(f"Lifted blackhole for identity {RNS.prettyhexrep(identity_hash)}", RNS.LOG_INFO)
|
||||
return True
|
||||
|
||||
else: return None
|
||||
|
||||
except Exception as e:
|
||||
RNS.log(f"Error while unblackholing identity: {e}", RNS.LOG_ERROR)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def reload_blackhole():
|
||||
now = time.time()
|
||||
source_count = 0
|
||||
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||
for filename in os.listdir(RNS.Reticulum.blackholepath):
|
||||
try:
|
||||
if filename == "local": source_identity_hash = Transport.identity.hash
|
||||
else:
|
||||
if len(filename) != dest_len: raise ValueError(f"Identity hash length for blackhole source {filename} is invalid")
|
||||
source_identity_hash = bytes.fromhex(filename)
|
||||
|
||||
sourcepath = os.path.join(RNS.Reticulum.blackholepath, filename)
|
||||
with open(sourcepath, "rb") as f:
|
||||
packed = f.read()
|
||||
source_list = umsgpack.unpackb(packed)
|
||||
for identity_hash in source_list:
|
||||
if len(identity_hash) == RNS.Reticulum.TRUNCATED_HASHLENGTH//8:
|
||||
if identity_hash in Transport.blackholed_identities:
|
||||
if Transport.blackholed_identities[identity_hash]["source"] == Transport.identity.hash:
|
||||
continue
|
||||
|
||||
until = source_list[identity_hash]["until"]
|
||||
entry = {"source": source_identity_hash, "until": until}
|
||||
if until == None or now < until:
|
||||
Transport.blackholed_identities[identity_hash] = entry
|
||||
|
||||
source_count += 1
|
||||
|
||||
except Exception as e:
|
||||
RNS.log(f"Could not load blackholed identities from source file {filename}: {e}", RNS.LOG_ERROR)
|
||||
RNS.trace_exception(e)
|
||||
|
||||
Transport.remove_blackholed_paths()
|
||||
|
||||
def remove_blackholed_paths():
|
||||
drop_destinations = []
|
||||
for destination_hash in Transport.path_table.copy():
|
||||
try:
|
||||
associated_identity = RNS.Identity.recall(destination_hash)
|
||||
if associated_identity and associated_identity.hash in Transport.blackholed_identities:
|
||||
if not destination_hash in drop_destinations: drop_destinations.append(destination_hash)
|
||||
except Exception as e:
|
||||
RNS.log(f"Error while enumerating blackhole-associated destinations: {e}", RNS.LOG_ERROR)
|
||||
|
||||
for destination_hash in drop_destinations:
|
||||
try:
|
||||
if destination_hash in Transport.path_table: Transport.path_table.pop(destination_hash)
|
||||
except Exception as e:
|
||||
RNS.log(f"Error while dropping blackhole-associated destination from path table: {e}", RNS.LOG_ERROR)
|
||||
|
||||
if len(drop_destinations) > 0:
|
||||
ms = "" if len(drop_destinations) == 1 else "s"
|
||||
RNS.log(f"Removed {len(drop_destinations)} destination{ms} associated with blackholed identities from path table", RNS.LOG_INFO)
|
||||
|
||||
@staticmethod
|
||||
def persist_blackhole():
|
||||
try:
|
||||
local_blackhole = {}
|
||||
for identity_hash in Transport.blackholed_identities:
|
||||
if Transport.blackholed_identities[identity_hash]["source"] == Transport.identity.hash:
|
||||
local_blackhole[identity_hash] = Transport.blackholed_identities[identity_hash]
|
||||
|
||||
packed = umsgpack.packb(local_blackhole)
|
||||
localpath = os.path.join(RNS.Reticulum.blackholepath, "local")
|
||||
tmppath = f"{localpath}.tmp"
|
||||
with open(tmppath, "wb") as f: f.write(packed)
|
||||
if os.path.isfile(localpath): os.unlink(localpath)
|
||||
os.rename(tmppath, localpath)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log(f"Error while persisting blackhole list: {e}", RNS.LOG_ERROR)
|
||||
RNS.trace_exception(e)
|
||||
|
||||
|
||||
# Table entry indices
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,14 @@ def connect_remote(destination_hash, auth_identity, timeout, no_output = False):
|
|||
link.set_link_established_callback(remote_link_established)
|
||||
link.set_link_closed_callback(remote_link_closed)
|
||||
|
||||
def parse_hash(input_str):
|
||||
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||
if len(input_str) != dest_len: raise ValueError("Hash length is invalid, must be {hex} hexadecimal characters ({byte} bytes).".format(hex=dest_len, byte=dest_len//2))
|
||||
try:
|
||||
hash_bytes = bytes.fromhex(input_str)
|
||||
return hash_bytes
|
||||
except Exception as e: raise ValueError("Invalid hash entered. Check your input.")
|
||||
|
||||
def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity, timeout, drop_queues,
|
||||
drop_via, max_hops, remote=None, management_identity=None, remote_timeout=RNS.Transport.PATH_REQUEST_TIMEOUT,
|
||||
blackholed=False, blackhole=False, unblackhole=False, no_output=False, json=False):
|
||||
|
|
@ -114,7 +122,64 @@ def program_setup(configdir, table, rates, drop, destination_hexhash, verbosity,
|
|||
|
||||
while remote_link == None: time.sleep(0.1)
|
||||
|
||||
if table:
|
||||
if blackholed:
|
||||
if remote_link:
|
||||
if not no_output:
|
||||
print("\r \r", end="")
|
||||
print("Listing blackholed identities on remote instances not yet implemented")
|
||||
exit(255)
|
||||
|
||||
try:
|
||||
blackholed = reticulum.get_blackholed_identities()
|
||||
now = time.time()
|
||||
for identity_hash in blackholed:
|
||||
until = blackholed[identity_hash]["until"]
|
||||
if until == None: until_str = "indefinitely"
|
||||
else: until_str = f" for {RNS.prettytime(until-now)}"
|
||||
print(f"{RNS.prettyhexrep(identity_hash)} blackholed {until_str}")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print(f"Could not get blackholed identities from RNS instance: {e}")
|
||||
exit(20)
|
||||
|
||||
elif blackhole:
|
||||
if remote_link:
|
||||
if not no_output:
|
||||
print("\r \r", end="")
|
||||
print("Blackholing identity on remote instances not yet implemented")
|
||||
exit(255)
|
||||
|
||||
try:
|
||||
identity_hash = parse_hash(destination_hexhash)
|
||||
result = reticulum.blackhole_identity(identity_hash)
|
||||
if result == True: print(f"Blackholed identity {destination_hexhash}")
|
||||
elif result == None: print(f"Identity {destination_hexhash} already blackholed")
|
||||
else: print(f"Could not blackhole identity {destination_hexhash}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Could not blackhole identity: {e}")
|
||||
exit(20)
|
||||
|
||||
elif unblackhole:
|
||||
if remote_link:
|
||||
if not no_output:
|
||||
print("\r \r", end="")
|
||||
print("Blackholing identity on remote instances not yet implemented")
|
||||
exit(255)
|
||||
|
||||
try:
|
||||
identity_hash = parse_hash(destination_hexhash)
|
||||
result = reticulum.unblackhole_identity(identity_hash)
|
||||
if result == True: print(f"Lifted blackhole for identity {destination_hexhash}")
|
||||
elif result == None: print(f"Identity {destination_hexhash} not blackholed")
|
||||
else: print(f"Could not unblackhole identity {destination_hexhash}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Could not unblackhole identity: {e}")
|
||||
exit(20)
|
||||
|
||||
elif table:
|
||||
destination_hash = None
|
||||
if destination_hexhash != None:
|
||||
try:
|
||||
|
|
@ -379,7 +444,7 @@ def main():
|
|||
if args.config: configarg = args.config
|
||||
else: configarg = None
|
||||
|
||||
if not args.drop_announces and not args.table and not args.rates and not args.destination and not args.drop_via:
|
||||
if not args.drop_announces and not args.table and not args.rates and not args.destination and not args.drop_via and not args.blackholed:
|
||||
print("")
|
||||
parser.print_help()
|
||||
print("")
|
||||
|
|
|
|||
|
|
@ -196,6 +196,18 @@ instance_name = default
|
|||
# respond_to_probes = No
|
||||
|
||||
|
||||
# You can publish your local list of blackholed identities
|
||||
# for other transport instances to use for automatic,
|
||||
# network-wide blackhole management.
|
||||
|
||||
# publish_blackhole = No
|
||||
|
||||
# List of remote transport identities from which to auto-
|
||||
# matically source lists of blackholed identities.
|
||||
|
||||
# blackhole_sources = 521c87a83afb8f29e4455e77930b973b
|
||||
|
||||
|
||||
[logging]
|
||||
# Valid log levels are 0 through 7:
|
||||
# 0: Log only critical information
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue