mirror of
https://github.com/markqvist/Reticulum.git
synced 2024-11-22 21:50:18 +00:00
Compare commits
30 Commits
a291598ac1
...
9bad8370da
Author | SHA1 | Date | |
---|---|---|---|
|
9bad8370da | ||
|
ba2feaa211 | ||
|
097d2b0dd9 | ||
|
bb0ce4faca | ||
|
5915228f5b | ||
|
0b66649158 | ||
|
e28dd6e14a | ||
|
0a15b4c6c1 | ||
|
62db09571d | ||
|
444ae0206b | ||
|
4b07e30b9d | ||
|
583e65419e | ||
|
1564930a51 | ||
|
b81b1de4eb | ||
|
746a38f818 | ||
|
c230eceaa6 | ||
|
3551662187 | ||
|
f7f34e0ea3 | ||
|
221493d816 | ||
|
e47dcae54e | ||
|
acd893a6e6 | ||
|
c032a2e438 | ||
|
1f6a494e14 | ||
|
059c2381b5 | ||
|
541d3cde48 | ||
|
87ff1808a2 | ||
|
8d1a000378 | ||
|
794a5f4401 | ||
|
7159e6a523 | ||
|
cf41187e2f |
19
Changelog.md
19
Changelog.md
@ -1,3 +1,22 @@
|
|||||||
|
### 2024-05-18: RNS β 0.7.5
|
||||||
|
|
||||||
|
This release adds support for AutoInterface on Windows platforms, fixes a number of bugs and adds several new supported boards to `rnodeconf`. Thanks to @faragher, @jacobeva and @liamcottle who contributed to this release!
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
- Added support for AutoInterface on Windows
|
||||||
|
- Added support for recursive path resolution for clients on roaming-mode interfaces
|
||||||
|
- Added RAK4631 support to `rnodeconf`
|
||||||
|
- Added LilyGO T3S3 support to `rnodeconf`
|
||||||
|
- Added ability to get target and calculated hashes via `rnodeconf`
|
||||||
|
- Fixed DTR timing making flashing fail on Windows in `rnodeconf`
|
||||||
|
- Fixed various output and menu bugs in `rnodeconf`
|
||||||
|
|
||||||
|
**Release Hashes**
|
||||||
|
```
|
||||||
|
99ec876966afdea45fcf164242c8e76c284f9e3edf09fb907638fba76e1324b1 rns-0.7.5-py3-none-any.whl
|
||||||
|
11156f6301707e4d17ff2ca6d58059bc8ba6fe1bbc4dc3de165dd96dc41ee75f rnspure-0.7.5-py3-none-any.whl
|
||||||
|
```
|
||||||
|
|
||||||
### 2024-05-05: RNS β 0.7.4
|
### 2024-05-05: RNS β 0.7.4
|
||||||
|
|
||||||
This maintenance release fixes a number of bugs, improves path requests and responses, and adds several useful features and capabilities. Thanks to @cobraPA, @jschulthess, @thiaguetz and @nothingbutlucas who contributed to this release!
|
This maintenance release fixes a number of bugs, improves path requests and responses, and adds several useful features and capabilities. Thanks to @cobraPA, @jschulthess, @thiaguetz and @nothingbutlucas who contributed to this release!
|
||||||
|
@ -77,6 +77,15 @@ class AutoInterface(Interface):
|
|||||||
ifas = self.netinfo.ifaddresses(ifname)
|
ifas = self.netinfo.ifaddresses(ifname)
|
||||||
return ifas
|
return ifas
|
||||||
|
|
||||||
|
def interface_name_to_index(self, ifname):
|
||||||
|
|
||||||
|
# socket.if_nametoindex doesn't work with uuid interface names on windows, it wants the ethernet_0 style
|
||||||
|
# we will just get the index from netinfo instead as it seems to work
|
||||||
|
if RNS.vendor.platformutils.is_windows():
|
||||||
|
return self.netinfo.interface_names_to_indexes()[ifname]
|
||||||
|
|
||||||
|
return socket.if_nametoindex(ifname)
|
||||||
|
|
||||||
def __init__(self, owner, name, group_id=None, discovery_scope=None, discovery_port=None, multicast_address_type=None, data_port=None, allowed_interfaces=None, ignored_interfaces=None, configured_bitrate=None):
|
def __init__(self, owner, name, group_id=None, discovery_scope=None, discovery_port=None, multicast_address_type=None, data_port=None, allowed_interfaces=None, ignored_interfaces=None, configured_bitrate=None):
|
||||||
from RNS.vendor.ifaddr import niwrapper
|
from RNS.vendor.ifaddr import niwrapper
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -172,69 +181,90 @@ class AutoInterface(Interface):
|
|||||||
|
|
||||||
suitable_interfaces = 0
|
suitable_interfaces = 0
|
||||||
for ifname in self.list_interfaces():
|
for ifname in self.list_interfaces():
|
||||||
if RNS.vendor.platformutils.is_darwin() and ifname in AutoInterface.DARWIN_IGNORE_IFS and not ifname in self.allowed_interfaces:
|
try:
|
||||||
RNS.log(str(self)+" skipping Darwin AWDL or tethering interface "+str(ifname), RNS.LOG_EXTREME)
|
if RNS.vendor.platformutils.is_darwin() and ifname in AutoInterface.DARWIN_IGNORE_IFS and not ifname in self.allowed_interfaces:
|
||||||
elif RNS.vendor.platformutils.is_darwin() and ifname == "lo0":
|
RNS.log(str(self)+" skipping Darwin AWDL or tethering interface "+str(ifname), RNS.LOG_EXTREME)
|
||||||
RNS.log(str(self)+" skipping Darwin loopback interface "+str(ifname), RNS.LOG_EXTREME)
|
elif RNS.vendor.platformutils.is_darwin() and ifname == "lo0":
|
||||||
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 Darwin loopback interface "+str(ifname), RNS.LOG_EXTREME)
|
||||||
RNS.log(str(self)+" skipping Android system interface "+str(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 ifname in self.ignored_interfaces:
|
RNS.log(str(self)+" skipping Android system interface "+str(ifname), RNS.LOG_EXTREME)
|
||||||
RNS.log(str(self)+" ignoring disallowed interface "+str(ifname), RNS.LOG_EXTREME)
|
elif ifname in self.ignored_interfaces:
|
||||||
elif ifname in AutoInterface.ALL_IGNORE_IFS:
|
RNS.log(str(self)+" ignoring disallowed interface "+str(ifname), RNS.LOG_EXTREME)
|
||||||
RNS.log(str(self)+" skipping interface "+str(ifname), RNS.LOG_EXTREME)
|
elif ifname in AutoInterface.ALL_IGNORE_IFS:
|
||||||
else:
|
RNS.log(str(self)+" skipping interface "+str(ifname), RNS.LOG_EXTREME)
|
||||||
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)
|
|
||||||
else:
|
else:
|
||||||
addresses = self.list_addresses(ifname)
|
if len(self.allowed_interfaces) > 0 and not ifname in self.allowed_interfaces:
|
||||||
if self.netinfo.AF_INET6 in addresses:
|
RNS.log(str(self)+" ignoring interface "+str(ifname)+" since it was not allowed", RNS.LOG_EXTREME)
|
||||||
link_local_addr = None
|
else:
|
||||||
for address in addresses[self.netinfo.AF_INET6]:
|
addresses = self.list_addresses(ifname)
|
||||||
if "addr" in address:
|
if self.netinfo.AF_INET6 in addresses:
|
||||||
if address["addr"].startswith("fe80:"):
|
link_local_addr = None
|
||||||
link_local_addr = self.descope_linklocal(address["addr"])
|
for address in addresses[self.netinfo.AF_INET6]:
|
||||||
self.link_local_addresses.append(link_local_addr)
|
if "addr" in address:
|
||||||
self.adopted_interfaces[ifname] = link_local_addr
|
if address["addr"].startswith("fe80:"):
|
||||||
self.multicast_echoes[ifname] = time.time()
|
link_local_addr = self.descope_linklocal(address["addr"])
|
||||||
RNS.log(str(self)+" Selecting link-local address "+str(link_local_addr)+" for interface "+str(ifname), RNS.LOG_EXTREME)
|
self.link_local_addresses.append(link_local_addr)
|
||||||
|
self.adopted_interfaces[ifname] = link_local_addr
|
||||||
|
self.multicast_echoes[ifname] = time.time()
|
||||||
|
nice_name = self.netinfo.interface_name_to_nice_name(ifname)
|
||||||
|
if nice_name != None and nice_name != ifname:
|
||||||
|
RNS.log(f"{self} Selecting link-local address {link_local_addr} for interface {nice_name} / {ifname}", RNS.LOG_EXTREME)
|
||||||
|
else:
|
||||||
|
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(str(self)+" No link-local IPv6 address configured for "+str(ifname)+", skipping interface", RNS.LOG_EXTREME)
|
||||||
else:
|
|
||||||
mcast_addr = self.mcast_discovery_address
|
|
||||||
RNS.log(str(self)+" Creating multicast discovery listener on "+str(ifname)+" with address "+str(mcast_addr), RNS.LOG_EXTREME)
|
|
||||||
|
|
||||||
# Struct with interface index
|
|
||||||
if_struct = struct.pack("I", socket.if_nametoindex(ifname))
|
|
||||||
|
|
||||||
# Set up multicast socket
|
|
||||||
discovery_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
|
||||||
discovery_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
||||||
if hasattr(socket, "SO_REUSEPORT"):
|
|
||||||
discovery_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
|
||||||
discovery_socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, if_struct)
|
|
||||||
|
|
||||||
# Join multicast group
|
|
||||||
mcast_group = socket.inet_pton(socket.AF_INET6, mcast_addr) + if_struct
|
|
||||||
discovery_socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mcast_group)
|
|
||||||
|
|
||||||
# Bind socket
|
|
||||||
if self.discovery_scope == AutoInterface.SCOPE_LINK:
|
|
||||||
addr_info = socket.getaddrinfo(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)
|
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)
|
||||||
|
|
||||||
discovery_socket.bind(addr_info[0][4])
|
# Struct with interface index
|
||||||
|
if_struct = struct.pack("I", self.interface_name_to_index(ifname))
|
||||||
|
|
||||||
# Set up thread for discovery packets
|
# Set up multicast socket
|
||||||
def discovery_loop():
|
discovery_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
||||||
self.discovery_handler(discovery_socket, ifname)
|
discovery_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
if hasattr(socket, "SO_REUSEPORT"):
|
||||||
|
discovery_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
||||||
|
discovery_socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, if_struct)
|
||||||
|
|
||||||
thread = threading.Thread(target=discovery_loop)
|
# Join multicast group
|
||||||
thread.daemon = True
|
mcast_group = socket.inet_pton(socket.AF_INET6, mcast_addr) + if_struct
|
||||||
thread.start()
|
discovery_socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mcast_group)
|
||||||
|
|
||||||
suitable_interfaces += 1
|
# Bind socket
|
||||||
|
if RNS.vendor.platformutils.is_windows():
|
||||||
|
|
||||||
|
# window throws "[WinError 10049] The requested address is not valid in its context"
|
||||||
|
# when trying to use the multicast address as host, or when providing interface index
|
||||||
|
# passing an empty host appears to work, but probably not exactly how we want it to...
|
||||||
|
discovery_socket.bind(('', self.discovery_port))
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
if self.discovery_scope == AutoInterface.SCOPE_LINK:
|
||||||
|
addr_info = socket.getaddrinfo(mcast_addr+"%"+ifname, self.discovery_port, socket.AF_INET6, socket.SOCK_DGRAM)
|
||||||
|
else:
|
||||||
|
addr_info = socket.getaddrinfo(mcast_addr, self.discovery_port, socket.AF_INET6, socket.SOCK_DGRAM)
|
||||||
|
|
||||||
|
discovery_socket.bind(addr_info[0][4])
|
||||||
|
|
||||||
|
# Set up thread for discovery packets
|
||||||
|
def discovery_loop():
|
||||||
|
self.discovery_handler(discovery_socket, ifname)
|
||||||
|
|
||||||
|
thread = threading.Thread(target=discovery_loop)
|
||||||
|
thread.daemon = True
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
suitable_interfaces += 1
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
nice_name = self.netinfo.interface_name_to_nice_name(ifname)
|
||||||
|
if nice_name != None and nice_name != ifname:
|
||||||
|
RNS.log(f"Could not configure the system interface {nice_name} / {ifname} for use with {self}, skipping it. The contained exception was: {e}", RNS.LOG_ERROR)
|
||||||
|
else:
|
||||||
|
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(str(self)+" could not autoconfigure. This interface currently provides no connectivity.", RNS.LOG_WARNING)
|
||||||
@ -253,7 +283,7 @@ class AutoInterface(Interface):
|
|||||||
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]+"%"+ifname
|
local_addr = self.adopted_interfaces[ifname]+"%"+str(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]
|
||||||
|
|
||||||
@ -380,7 +410,7 @@ class AutoInterface(Interface):
|
|||||||
announce_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
announce_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
||||||
addr_info = socket.getaddrinfo(self.mcast_discovery_address, self.discovery_port, socket.AF_INET6, socket.SOCK_DGRAM)
|
addr_info = socket.getaddrinfo(self.mcast_discovery_address, self.discovery_port, socket.AF_INET6, socket.SOCK_DGRAM)
|
||||||
|
|
||||||
ifis = struct.pack("I", socket.if_nametoindex(ifname))
|
ifis = struct.pack("I", self.interface_name_to_index(ifname))
|
||||||
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()
|
||||||
@ -433,8 +463,8 @@ class AutoInterface(Interface):
|
|||||||
try:
|
try:
|
||||||
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.peers[peer][0])
|
peer_addr = str(peer)+"%"+str(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])
|
||||||
|
|
||||||
|
@ -536,46 +536,40 @@ class Reticulum:
|
|||||||
|
|
||||||
if (("interface_enabled" in c) and c.as_bool("interface_enabled") == True) or (("enabled" in c) and c.as_bool("enabled") == True):
|
if (("interface_enabled" in c) and c.as_bool("interface_enabled") == True) or (("enabled" in c) and c.as_bool("enabled") == True):
|
||||||
if c["type"] == "AutoInterface":
|
if c["type"] == "AutoInterface":
|
||||||
if not RNS.vendor.platformutils.is_windows():
|
group_id = c["group_id"] if "group_id" in c else None
|
||||||
group_id = c["group_id"] if "group_id" in c else None
|
discovery_scope = c["discovery_scope"] if "discovery_scope" in c else None
|
||||||
discovery_scope = c["discovery_scope"] if "discovery_scope" in c else None
|
discovery_port = int(c["discovery_port"]) if "discovery_port" in c else None
|
||||||
discovery_port = int(c["discovery_port"]) if "discovery_port" in c else None
|
multicast_address_type = c["multicast_address_type"] if "multicast_address_type" in c else None
|
||||||
multicast_address_type = c["multicast_address_type"] if "multicast_address_type" in c else None
|
data_port = int(c["data_port"]) if "data_port" in c else None
|
||||||
data_port = int(c["data_port"]) if "data_port" in c else None
|
allowed_interfaces = c.as_list("devices") if "devices" in c else None
|
||||||
allowed_interfaces = c.as_list("devices") if "devices" in c else None
|
ignored_interfaces = c.as_list("ignored_devices") if "ignored_devices" in c else None
|
||||||
ignored_interfaces = c.as_list("ignored_devices") if "ignored_devices" in c else None
|
|
||||||
|
|
||||||
interface = AutoInterface.AutoInterface(
|
interface = AutoInterface.AutoInterface(
|
||||||
RNS.Transport,
|
RNS.Transport,
|
||||||
name,
|
name,
|
||||||
group_id,
|
group_id,
|
||||||
discovery_scope,
|
discovery_scope,
|
||||||
discovery_port,
|
discovery_port,
|
||||||
multicast_address_type,
|
multicast_address_type,
|
||||||
data_port,
|
data_port,
|
||||||
allowed_interfaces,
|
allowed_interfaces,
|
||||||
ignored_interfaces
|
ignored_interfaces
|
||||||
)
|
)
|
||||||
|
|
||||||
if "outgoing" in c and c.as_bool("outgoing") == False:
|
|
||||||
interface.OUT = False
|
|
||||||
else:
|
|
||||||
interface.OUT = True
|
|
||||||
|
|
||||||
interface.mode = interface_mode
|
|
||||||
|
|
||||||
interface.announce_cap = announce_cap
|
|
||||||
if configured_bitrate:
|
|
||||||
interface.bitrate = configured_bitrate
|
|
||||||
if ifac_size != None:
|
|
||||||
interface.ifac_size = ifac_size
|
|
||||||
else:
|
|
||||||
interface.ifac_size = 16
|
|
||||||
|
|
||||||
|
if "outgoing" in c and c.as_bool("outgoing") == False:
|
||||||
|
interface.OUT = False
|
||||||
else:
|
else:
|
||||||
RNS.log("AutoInterface is not currently supported on Windows, disabling interface.", RNS.LOG_ERROR);
|
interface.OUT = True
|
||||||
RNS.log("Please remove this AutoInterface instance from your configuration file.", RNS.LOG_ERROR);
|
|
||||||
RNS.log("You will have to manually configure other interfaces for connectivity.", RNS.LOG_ERROR);
|
interface.mode = interface_mode
|
||||||
|
|
||||||
|
interface.announce_cap = announce_cap
|
||||||
|
if configured_bitrate:
|
||||||
|
interface.bitrate = configured_bitrate
|
||||||
|
if ifac_size != None:
|
||||||
|
interface.ifac_size = ifac_size
|
||||||
|
else:
|
||||||
|
interface.ifac_size = 16
|
||||||
|
|
||||||
if c["type"] == "UDPInterface":
|
if c["type"] == "UDPInterface":
|
||||||
device = c["device"] if "device" in c else None
|
device = c["device"] if "device" in c else None
|
||||||
|
@ -37,26 +37,48 @@ SIG_EXT = "rsg"
|
|||||||
ENCRYPT_EXT = "rfe"
|
ENCRYPT_EXT = "rfe"
|
||||||
CHUNK_SIZE = 16*1024*1024
|
CHUNK_SIZE = 16*1024*1024
|
||||||
|
|
||||||
def spin(until=None, msg=None, timeout=None):
|
|
||||||
|
def spin(until=None, msg="", timeout=None):
|
||||||
i = 0
|
i = 0
|
||||||
syms = "⢄⢂⢁⡁⡈⡐⡠"
|
syms = "⢄⢂⢁⡁⡈⡐⡠"
|
||||||
if timeout != None:
|
if timeout is not None:
|
||||||
timeout = time.time()+timeout
|
timeout = time.time()+timeout
|
||||||
|
|
||||||
print(msg+" ", end=" ")
|
print(msg, end=" ")
|
||||||
while (timeout == None or time.time()<timeout) and not until():
|
while (timeout is None or time.time()<timeout) and not until():
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
print(("\b\b"+syms[i]+" "), end="")
|
print(("\b\b", syms[i]), end=" ", sep="")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
i = (i+1)%len(syms)
|
i = (i+1)%len(syms)
|
||||||
|
|
||||||
print("\r"+" "*len(msg)+" \r", end="")
|
print("\r", *len(msg), "\r", end="", sep="")
|
||||||
|
|
||||||
if timeout != None and time.time() > timeout:
|
if timeout is not None and time.time() > timeout:
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def get_keys(args: argparse.Namespace, identity) -> None:
|
||||||
|
"""Get public or/and private keys"""
|
||||||
|
|
||||||
|
if args.base64:
|
||||||
|
RNS.log("Public Key : " + base64.urlsafe_b64encode(identity.get_public_key()).decode("utf-8"))
|
||||||
|
elif args.base32:
|
||||||
|
RNS.log("Public Key : " + base64.b32encode(identity.get_public_key()).decode("utf-8"))
|
||||||
|
else:
|
||||||
|
RNS.log("Public Key : " + RNS.hexrep(identity.get_public_key(), delimit=False))
|
||||||
|
if identity.prv:
|
||||||
|
if args.print_private:
|
||||||
|
if args.base64:
|
||||||
|
RNS.log("Private Key : " + base64.urlsafe_b64encode(identity.get_private_key()).decode("utf-8"))
|
||||||
|
elif args.base32:
|
||||||
|
RNS.log("Private Key : " + base64.b32encode(identity.get_private_key()).decode("utf-8"))
|
||||||
|
else:
|
||||||
|
RNS.log("Private Key : " + RNS.hexrep(identity.get_private_key(), delimit=False))
|
||||||
|
else:
|
||||||
|
RNS.log("Private Key : Hidden")
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
try:
|
try:
|
||||||
parser = argparse.ArgumentParser(description="Reticulum Identity & Encryption Utility")
|
parser = argparse.ArgumentParser(description="Reticulum Identity & Encryption Utility")
|
||||||
@ -82,7 +104,7 @@ def main():
|
|||||||
parser.add_argument("-w", "--write", metavar="file", action="store", default=None, help="output file path", type=str)
|
parser.add_argument("-w", "--write", metavar="file", action="store", default=None, help="output file path", type=str)
|
||||||
parser.add_argument("-f", "--force", action="store_true", default=None, help="write output even if it overwrites existing files")
|
parser.add_argument("-f", "--force", action="store_true", default=None, help="write output even if it overwrites existing files")
|
||||||
parser.add_argument("-I", "--stdin", action="store_true", default=False, help=argparse.SUPPRESS) # "read input from STDIN instead of file"
|
parser.add_argument("-I", "--stdin", action="store_true", default=False, help=argparse.SUPPRESS) # "read input from STDIN instead of file"
|
||||||
parser.add_argument("-O", "--stdout", action="store_true", default=False, help=argparse.SUPPRESS) # help="write output to STDOUT instead of file",
|
parser.add_argument("-O", "--stdout", action="store_true", default=False, help=argparse.SUPPRESS) # help="write output to STDOUT instead of file",
|
||||||
|
|
||||||
parser.add_argument("-R", "--request", action="store_true", default=False, help="request unknown Identities from the network")
|
parser.add_argument("-R", "--request", action="store_true", default=False, help="request unknown Identities from the network")
|
||||||
parser.add_argument("-t", action="store", metavar="seconds", type=float, help="identity request timeout before giving up", default=RNS.Transport.PATH_REQUEST_TIMEOUT)
|
parser.add_argument("-t", action="store", metavar="seconds", type=float, help="identity request timeout before giving up", default=RNS.Transport.PATH_REQUEST_TIMEOUT)
|
||||||
@ -93,14 +115,10 @@ def main():
|
|||||||
parser.add_argument("-B", "--base32", action="store_true", default=False, help="Use base32-encoded input and output")
|
parser.add_argument("-B", "--base32", action="store_true", default=False, help="Use base32-encoded input and output")
|
||||||
|
|
||||||
parser.add_argument("--version", action="version", version="rnid {version}".format(version=__version__))
|
parser.add_argument("--version", action="version", version="rnid {version}".format(version=__version__))
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
ops = 0;
|
ops = sum(1 for t in [args.encrypt, args.decrypt, args.validate, args.sign] if t)
|
||||||
for t in [args.encrypt, args.decrypt, args.validate, args.sign]:
|
|
||||||
if t:
|
|
||||||
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)
|
||||||
@ -134,22 +152,8 @@ def main():
|
|||||||
exit(42)
|
exit(42)
|
||||||
|
|
||||||
RNS.log("Identity imported")
|
RNS.log("Identity imported")
|
||||||
if args.base64:
|
|
||||||
RNS.log("Public Key : "+base64.urlsafe_b64encode(identity.get_public_key()).decode("utf-8"))
|
get_keys(args, identity)
|
||||||
elif args.base32:
|
|
||||||
RNS.log("Public Key : "+base64.b32encode(identity.get_public_key()).decode("utf-8"))
|
|
||||||
else:
|
|
||||||
RNS.log("Public Key : "+RNS.hexrep(identity.get_public_key(), delimit=False))
|
|
||||||
if identity.prv:
|
|
||||||
if args.print_private:
|
|
||||||
if args.base64:
|
|
||||||
RNS.log("Private Key : "+base64.urlsafe_b64encode(identity.get_private_key()).decode("utf-8"))
|
|
||||||
elif args.base32:
|
|
||||||
RNS.log("Private Key : "+base64.b32encode(identity.get_private_key()).decode("utf-8"))
|
|
||||||
else:
|
|
||||||
RNS.log("Private Key : "+RNS.hexrep(identity.get_private_key(), delimit=False))
|
|
||||||
else:
|
|
||||||
RNS.log("Private Key : Hidden")
|
|
||||||
|
|
||||||
if args.write:
|
if args.write:
|
||||||
try:
|
try:
|
||||||
@ -179,7 +183,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
|
||||||
@ -208,7 +212,7 @@ def main():
|
|||||||
destination_hash = bytes.fromhex(identity_str)
|
destination_hash = bytes.fromhex(identity_str)
|
||||||
identity = RNS.Identity.recall(destination_hash)
|
identity = RNS.Identity.recall(destination_hash)
|
||||||
|
|
||||||
if identity == None:
|
if identity is 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("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)
|
||||||
@ -216,7 +220,7 @@ def main():
|
|||||||
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) is not None
|
||||||
spin(spincheck, "Requesting unknown Identity for "+RNS.prettyhexrep(destination_hash), args.t)
|
spin(spincheck, "Requesting unknown Identity for "+RNS.prettyhexrep(destination_hash), args.t)
|
||||||
|
|
||||||
if not spincheck():
|
if not spincheck():
|
||||||
@ -224,17 +228,15 @@ def main():
|
|||||||
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(" ".join(["Received Identity", str(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(" ".join(["Recalled Identity", str(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,13 +245,13 @@ 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(" ".join(["Loaded Identity", str(identity), "from", str(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")
|
||||||
exit(9)
|
exit(9)
|
||||||
|
|
||||||
if identity != None:
|
if identity is not None:
|
||||||
if args.hash:
|
if args.hash:
|
||||||
try:
|
try:
|
||||||
aspects = args.hash.split(".")
|
aspects = args.hash.split(".")
|
||||||
@ -259,9 +261,9 @@ def main():
|
|||||||
else:
|
else:
|
||||||
app_name = aspects[0]
|
app_name = aspects[0]
|
||||||
aspects = aspects[1:]
|
aspects = aspects[1:]
|
||||||
if identity.pub != None:
|
if identity.pub is not 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(" ".join(["The", str(args.hash), "destination for this Identity is", RNS.prettyhexrep(destination.hash)]))
|
||||||
RNS.log("The full destination specifier is "+str(destination))
|
RNS.log("The full destination specifier is "+str(destination))
|
||||||
time.sleep(0.25)
|
time.sleep(0.25)
|
||||||
exit(0)
|
exit(0)
|
||||||
@ -282,7 +284,7 @@ def main():
|
|||||||
else:
|
else:
|
||||||
app_name = aspects[0]
|
app_name = aspects[0]
|
||||||
aspects = aspects[1:]
|
aspects = aspects[1:]
|
||||||
if identity.prv != None:
|
if identity.prv is not 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("Created destination "+str(destination))
|
||||||
RNS.log("Announcing destination "+RNS.prettyhexrep(destination.hash))
|
RNS.log("Announcing destination "+RNS.prettyhexrep(destination.hash))
|
||||||
@ -291,7 +293,7 @@ def main():
|
|||||||
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(" ".join(["The", str(args.announce), "destination for this Identity is", RNS.prettyhexrep(destination.hash)]))
|
||||||
RNS.log("The full destination specifier is "+str(destination))
|
RNS.log("The full destination specifier is "+str(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)
|
||||||
@ -303,22 +305,7 @@ def main():
|
|||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
if args.print_identity:
|
if args.print_identity:
|
||||||
if args.base64:
|
get_keys(args, identity)
|
||||||
RNS.log("Public Key : "+base64.urlsafe_b64encode(identity.get_public_key()).decode("utf-8"))
|
|
||||||
elif args.base32:
|
|
||||||
RNS.log("Public Key : "+base64.b32encode(identity.get_public_key()).decode("utf-8"))
|
|
||||||
else:
|
|
||||||
RNS.log("Public Key : "+RNS.hexrep(identity.get_public_key(), delimit=False))
|
|
||||||
if identity.prv:
|
|
||||||
if args.print_private:
|
|
||||||
if args.base64:
|
|
||||||
RNS.log("Private Key : "+base64.urlsafe_b64encode(identity.get_private_key()).decode("utf-8"))
|
|
||||||
elif args.base32:
|
|
||||||
RNS.log("Private Key : "+base64.b32encode(identity.get_private_key()).decode("utf-8"))
|
|
||||||
else:
|
|
||||||
RNS.log("Private Key : "+RNS.hexrep(identity.get_private_key(), delimit=False))
|
|
||||||
else:
|
|
||||||
RNS.log("Private Key : Hidden")
|
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
if args.export:
|
if args.export:
|
||||||
@ -373,7 +360,7 @@ def main():
|
|||||||
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("."+ENCRYPT_EXT):
|
||||||
args.write = str(args.read).replace("."+ENCRYPT_EXT, "")
|
args.write = str(args.read).replace("."+ENCRYPT_EXT, "")
|
||||||
|
|
||||||
if args.sign and identity.prv == None:
|
if args.sign and identity.prv is 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)
|
||||||
|
|
||||||
@ -391,7 +378,7 @@ def main():
|
|||||||
RNS.log("Could not open output file for writing", RNS.LOG_ERROR)
|
RNS.log("Could not open output file for writing", RNS.LOG_ERROR)
|
||||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("The contained exception was: "+str(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
|
||||||
@ -399,7 +386,7 @@ def main():
|
|||||||
# data_output = sys.stdout
|
# data_output = sys.stdout
|
||||||
|
|
||||||
if args.sign:
|
if args.sign:
|
||||||
if identity.prv == None:
|
if identity.prv is 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(16)
|
exit(16)
|
||||||
|
|
||||||
@ -415,15 +402,15 @@ def main():
|
|||||||
|
|
||||||
if not args.stdout:
|
if not args.stdout:
|
||||||
RNS.log("Signing "+str(args.read))
|
RNS.log("Signing "+str(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(" ".join([ "File", str(args.read), "signed with", str(identity), "to", str(args.write) ]))
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -448,7 +435,7 @@ def main():
|
|||||||
else:
|
else:
|
||||||
# if not args.stdout:
|
# if not args.stdout:
|
||||||
# RNS.log("Verifying "+str(args.validate)+" for "+str(args.read))
|
# RNS.log("Verifying "+str(args.validate)+" for "+str(args.read))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
sig_input = open(args.validate, "rb")
|
sig_input = open(args.validate, "rb")
|
||||||
@ -457,18 +444,17 @@ def main():
|
|||||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
exit(21)
|
exit(21)
|
||||||
|
|
||||||
|
|
||||||
validated = identity.validate(sig_input.read(), data_input.read())
|
validated = identity.validate(sig_input.read(), data_input.read())
|
||||||
sig_input.close()
|
sig_input.close()
|
||||||
data_input.close()
|
data_input.close()
|
||||||
|
|
||||||
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(" ".join(["Signature", str(args.validate), "for file", str(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(" ".join(["Signature", str(args.validate), "for file", str(args.read), "made by Identity", str(identity), "is valid"]))
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -498,7 +484,7 @@ def main():
|
|||||||
|
|
||||||
if not args.stdout:
|
if not args.stdout:
|
||||||
RNS.log("Encrypting "+str(args.read))
|
RNS.log("Encrypting "+str(args.read))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
more_data = True
|
more_data = True
|
||||||
while more_data:
|
while more_data:
|
||||||
@ -511,7 +497,7 @@ 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(" ".join(["File", str(args.read), "encrypted for", str(identity), "to", str(args.write)]))
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -529,7 +515,7 @@ def main():
|
|||||||
exit(26)
|
exit(26)
|
||||||
|
|
||||||
if args.decrypt:
|
if args.decrypt:
|
||||||
if identity.prv == None:
|
if identity.prv is None:
|
||||||
RNS.log("Specified Identity does not hold a private key. Cannot decrypt.", RNS.LOG_ERROR)
|
RNS.log("Specified Identity does not hold a private key. Cannot decrypt.", RNS.LOG_ERROR)
|
||||||
exit(27)
|
exit(27)
|
||||||
|
|
||||||
@ -544,15 +530,15 @@ def main():
|
|||||||
exit(29)
|
exit(29)
|
||||||
|
|
||||||
if not args.stdout:
|
if not args.stdout:
|
||||||
RNS.log("Decrypting "+str(args.read)+"...")
|
RNS.log("".join(["Decrypting ", str(args.read), "..."]))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
more_data = True
|
more_data = True
|
||||||
while more_data:
|
while more_data:
|
||||||
chunk = data_input.read(CHUNK_SIZE)
|
chunk = data_input.read(CHUNK_SIZE)
|
||||||
if chunk:
|
if chunk:
|
||||||
plaintext = identity.decrypt(chunk)
|
plaintext = identity.decrypt(chunk)
|
||||||
if plaintext == None:
|
if plaintext is None:
|
||||||
if not args.stdout:
|
if not args.stdout:
|
||||||
RNS.log("Data could not be decrypted with the specified Identity")
|
RNS.log("Data could not be decrypted with the specified Identity")
|
||||||
exit(30)
|
exit(30)
|
||||||
@ -564,7 +550,7 @@ 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(" ".join(["File", str(args.read), "decrypted with", str(identity), "to", str(args.write)]))
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -581,20 +567,10 @@ def main():
|
|||||||
pass
|
pass
|
||||||
exit(31)
|
exit(31)
|
||||||
|
|
||||||
if True:
|
|
||||||
pass
|
|
||||||
|
|
||||||
elif False:
|
|
||||||
pass
|
|
||||||
|
|
||||||
else:
|
|
||||||
print("")
|
|
||||||
parser.print_help()
|
|
||||||
print("")
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("")
|
print("")
|
||||||
exit(255)
|
exit(255)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
@ -24,23 +24,23 @@
|
|||||||
|
|
||||||
import RNS
|
import RNS
|
||||||
import argparse
|
import argparse
|
||||||
import time
|
|
||||||
|
|
||||||
from RNS._version import __version__
|
from RNS._version import __version__
|
||||||
|
|
||||||
|
|
||||||
def program_setup(configdir, verbosity = 0, quietness = 0, service = False):
|
def program_setup(configdir, verbosity=0, quietness=0, service=False):
|
||||||
targetverbosity = verbosity-quietness
|
targetverbosity = verbosity - quietness
|
||||||
|
|
||||||
if service:
|
if service:
|
||||||
targetlogdest = RNS.LOG_FILE
|
targetlogdest = RNS.LOG_FILE
|
||||||
targetverbosity = None
|
targetverbosity = None
|
||||||
else:
|
else:
|
||||||
targetlogdest = RNS.LOG_STDOUT
|
targetlogdest = RNS.LOG_STDOUT
|
||||||
|
|
||||||
reticulum = RNS.Reticulum(configdir=configdir, verbosity=targetverbosity, logdest=targetlogdest)
|
RNS.Reticulum(configdir=configdir, verbosity=targetverbosity, logdest=targetlogdest)
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
try:
|
try:
|
||||||
parser = argparse.ArgumentParser(description="Reticulum Distributed Identity Resolver")
|
parser = argparse.ArgumentParser(description="Reticulum Distributed Identity Resolver")
|
||||||
@ -49,24 +49,23 @@ def main():
|
|||||||
parser.add_argument('-q', '--quiet', action='count', default=0)
|
parser.add_argument('-q', '--quiet', action='count', default=0)
|
||||||
parser.add_argument("--exampleconfig", action='store_true', default=False, help="print verbose configuration example to stdout and exit")
|
parser.add_argument("--exampleconfig", action='store_true', default=False, help="print verbose configuration example to stdout and exit")
|
||||||
parser.add_argument("--version", action="version", version="ir {version}".format(version=__version__))
|
parser.add_argument("--version", action="version", version="ir {version}".format(version=__version__))
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.exampleconfig:
|
if args.exampleconfig:
|
||||||
print(__example_rns_config__)
|
print(__example_rns_config__)
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
configarg = None
|
||||||
if args.config:
|
if args.config:
|
||||||
configarg = args.config
|
configarg = args.config
|
||||||
else:
|
|
||||||
configarg = None
|
|
||||||
|
|
||||||
program_setup(configdir = configarg, verbosity=args.verbose, quietness=args.quiet)
|
program_setup(configdir=configarg, verbosity=args.verbose, quietness=args.quiet)
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("")
|
print("")
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
__example_rns_config__ = '''# This is an example Identity Resolver file.
|
__example_rns_config__ = '''# This is an example Identity Resolver file.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -27,13 +27,14 @@ import argparse
|
|||||||
|
|
||||||
from RNS._version import __version__
|
from RNS._version import __version__
|
||||||
|
|
||||||
|
|
||||||
def size_str(num, suffix='B'):
|
def size_str(num, suffix='B'):
|
||||||
units = ['','K','M','G','T','P','E','Z']
|
units = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']
|
||||||
last_unit = 'Y'
|
last_unit = 'Y'
|
||||||
|
|
||||||
if suffix == 'b':
|
if suffix == 'b':
|
||||||
num *= 8
|
num *= 8
|
||||||
units = ['','K','M','G','T','P','E','Z']
|
units = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']
|
||||||
last_unit = 'Y'
|
last_unit = 'Y'
|
||||||
|
|
||||||
for unit in units:
|
for unit in units:
|
||||||
@ -46,6 +47,14 @@ def size_str(num, suffix='B'):
|
|||||||
|
|
||||||
return "%.2f%s%s" % (num, last_unit, suffix)
|
return "%.2f%s%s" % (num, last_unit, suffix)
|
||||||
|
|
||||||
|
|
||||||
|
def convert_bytes_to_hex(value_obj):
|
||||||
|
if isinstance(value_obj, bytes):
|
||||||
|
return RNS.hexrep(value_obj, delimit=False)
|
||||||
|
elif isinstance(value_obj, dict):
|
||||||
|
return {key: convert_bytes_to_hex(value) for key, value in value_obj.items()}
|
||||||
|
return value_obj
|
||||||
|
|
||||||
def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=False, astats=False, sorting=None, sort_reverse=False):
|
def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=False, astats=False, sorting=None, sort_reverse=False):
|
||||||
reticulum = RNS.Reticulum(configdir = configdir, loglevel = 3+verbosity)
|
reticulum = RNS.Reticulum(configdir = configdir, loglevel = 3+verbosity)
|
||||||
|
|
||||||
@ -55,169 +64,159 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if stats != None:
|
if stats is None:
|
||||||
if json:
|
|
||||||
import json
|
|
||||||
for s in stats:
|
|
||||||
if isinstance(stats[s], bytes):
|
|
||||||
stats[s] = RNS.hexrep(stats[s], delimit=False)
|
|
||||||
|
|
||||||
if isinstance(stats[s], dict):
|
|
||||||
for i in stats[s]:
|
|
||||||
if isinstance(i, dict):
|
|
||||||
for k in i:
|
|
||||||
if isinstance(i[k], bytes):
|
|
||||||
i[k] = RNS.hexrep(i[k], delimit=False)
|
|
||||||
|
|
||||||
print(json.dumps(stats))
|
|
||||||
exit()
|
|
||||||
|
|
||||||
interfaces = stats["interfaces"]
|
|
||||||
if sorting != None and isinstance(sorting, str):
|
|
||||||
sorting = sorting.lower()
|
|
||||||
if sorting == "rate" or sorting == "bitrate":
|
|
||||||
interfaces.sort(key=lambda i: i["bitrate"], reverse=not sort_reverse)
|
|
||||||
if sorting == "rx":
|
|
||||||
interfaces.sort(key=lambda i: i["rxb"], reverse=not sort_reverse)
|
|
||||||
if sorting == "tx":
|
|
||||||
interfaces.sort(key=lambda i: i["txb"], reverse=not sort_reverse)
|
|
||||||
if sorting == "traffic":
|
|
||||||
interfaces.sort(key=lambda i: i["rxb"]+i["txb"], reverse=not sort_reverse)
|
|
||||||
if sorting == "announces" or sorting == "announce":
|
|
||||||
interfaces.sort(key=lambda i: i["incoming_announce_frequency"]+i["outgoing_announce_frequency"], reverse=not sort_reverse)
|
|
||||||
if sorting == "arx":
|
|
||||||
interfaces.sort(key=lambda i: i["incoming_announce_frequency"], reverse=not sort_reverse)
|
|
||||||
if sorting == "atx":
|
|
||||||
interfaces.sort(key=lambda i: i["outgoing_announce_frequency"], reverse=not sort_reverse)
|
|
||||||
if sorting == "held":
|
|
||||||
interfaces.sort(key=lambda i: i["held_announces"], reverse=not sort_reverse)
|
|
||||||
|
|
||||||
|
|
||||||
for ifstat in interfaces:
|
|
||||||
name = ifstat["name"]
|
|
||||||
|
|
||||||
if dispall or not (
|
|
||||||
name.startswith("LocalInterface[") or
|
|
||||||
name.startswith("TCPInterface[Client") or
|
|
||||||
name.startswith("I2PInterfacePeer[Connected peer") or
|
|
||||||
(name.startswith("I2PInterface[") and ("i2p_connectable" in ifstat and ifstat["i2p_connectable"] == False))
|
|
||||||
):
|
|
||||||
|
|
||||||
if not (name.startswith("I2PInterface[") and ("i2p_connectable" in ifstat and ifstat["i2p_connectable"] == False)):
|
|
||||||
if name_filter == None or name_filter.lower() in name.lower():
|
|
||||||
print("")
|
|
||||||
|
|
||||||
if ifstat["status"]:
|
|
||||||
ss = "Up"
|
|
||||||
else:
|
|
||||||
ss = "Down"
|
|
||||||
|
|
||||||
if ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_ACCESS_POINT:
|
|
||||||
modestr = "Access Point"
|
|
||||||
elif ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_POINT_TO_POINT:
|
|
||||||
modestr = "Point-to-Point"
|
|
||||||
elif ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_ROAMING:
|
|
||||||
modestr = "Roaming"
|
|
||||||
elif ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_BOUNDARY:
|
|
||||||
modestr = "Boundary"
|
|
||||||
elif ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_GATEWAY:
|
|
||||||
modestr = "Gateway"
|
|
||||||
else:
|
|
||||||
modestr = "Full"
|
|
||||||
|
|
||||||
|
|
||||||
if ifstat["clients"] != None:
|
|
||||||
clients = ifstat["clients"]
|
|
||||||
if name.startswith("Shared Instance["):
|
|
||||||
cnum = max(clients-1,0)
|
|
||||||
if cnum == 1:
|
|
||||||
spec_str = " program"
|
|
||||||
else:
|
|
||||||
spec_str = " programs"
|
|
||||||
|
|
||||||
clients_string = "Serving : "+str(cnum)+spec_str
|
|
||||||
elif name.startswith("I2PInterface["):
|
|
||||||
if "i2p_connectable" in ifstat and ifstat["i2p_connectable"] == True:
|
|
||||||
cnum = clients
|
|
||||||
if cnum == 1:
|
|
||||||
spec_str = " connected I2P endpoint"
|
|
||||||
else:
|
|
||||||
spec_str = " connected I2P endpoints"
|
|
||||||
|
|
||||||
clients_string = "Peers : "+str(cnum)+spec_str
|
|
||||||
else:
|
|
||||||
clients_string = ""
|
|
||||||
else:
|
|
||||||
clients_string = "Clients : "+str(clients)
|
|
||||||
|
|
||||||
else:
|
|
||||||
clients = None
|
|
||||||
|
|
||||||
print(" {n}".format(n=ifstat["name"]))
|
|
||||||
|
|
||||||
if "ifac_netname" in ifstat and ifstat["ifac_netname"] != None:
|
|
||||||
print(" Network : {nn}".format(nn=ifstat["ifac_netname"]))
|
|
||||||
|
|
||||||
print(" Status : {ss}".format(ss=ss))
|
|
||||||
|
|
||||||
if clients != None and clients_string != "":
|
|
||||||
print(" "+clients_string)
|
|
||||||
|
|
||||||
if not (name.startswith("Shared Instance[") or name.startswith("TCPInterface[Client") or name.startswith("LocalInterface[")):
|
|
||||||
print(" Mode : {mode}".format(mode=modestr))
|
|
||||||
|
|
||||||
if "bitrate" in ifstat and ifstat["bitrate"] != None:
|
|
||||||
print(" Rate : {ss}".format(ss=speed_str(ifstat["bitrate"])))
|
|
||||||
|
|
||||||
if "airtime_short" in ifstat and "airtime_long" in ifstat:
|
|
||||||
print(" Airtime : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["airtime_short"]),atl=str(ifstat["airtime_long"])))
|
|
||||||
|
|
||||||
if "channel_load_short" in ifstat and "channel_load_long" in ifstat:
|
|
||||||
print(" Ch.Load : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["channel_load_short"]),atl=str(ifstat["channel_load_long"])))
|
|
||||||
|
|
||||||
if "peers" in ifstat and ifstat["peers"] != None:
|
|
||||||
print(" Peers : {np} reachable".format(np=ifstat["peers"]))
|
|
||||||
|
|
||||||
if "tunnelstate" in ifstat and ifstat["tunnelstate"] != None:
|
|
||||||
print(" I2P : {ts}".format(ts=ifstat["tunnelstate"]))
|
|
||||||
|
|
||||||
if "ifac_signature" in ifstat and ifstat["ifac_signature"] != None:
|
|
||||||
sigstr = "<…"+RNS.hexrep(ifstat["ifac_signature"][-5:], delimit=False)+">"
|
|
||||||
print(" Access : {nb}-bit IFAC by {sig}".format(nb=ifstat["ifac_size"]*8, sig=sigstr))
|
|
||||||
|
|
||||||
if "i2p_b32" in ifstat and ifstat["i2p_b32"] != None:
|
|
||||||
print(" I2P B32 : {ep}".format(ep=str(ifstat["i2p_b32"])))
|
|
||||||
|
|
||||||
if astats and "announce_queue" in ifstat and ifstat["announce_queue"] != None and ifstat["announce_queue"] > 0:
|
|
||||||
aqn = ifstat["announce_queue"]
|
|
||||||
if aqn == 1:
|
|
||||||
print(" Queued : {np} announce".format(np=aqn))
|
|
||||||
else:
|
|
||||||
print(" Queued : {np} announces".format(np=aqn))
|
|
||||||
|
|
||||||
if astats and "held_announces" in ifstat and ifstat["held_announces"] != None and ifstat["held_announces"] > 0:
|
|
||||||
aqn = ifstat["held_announces"]
|
|
||||||
if aqn == 1:
|
|
||||||
print(" Held : {np} announce".format(np=aqn))
|
|
||||||
else:
|
|
||||||
print(" Held : {np} announces".format(np=aqn))
|
|
||||||
|
|
||||||
if astats and "incoming_announce_frequency" in ifstat and ifstat["incoming_announce_frequency"] != None:
|
|
||||||
print(" Announces : {iaf}↑".format(iaf=RNS.prettyfrequency(ifstat["outgoing_announce_frequency"])))
|
|
||||||
print(" {iaf}↓".format(iaf=RNS.prettyfrequency(ifstat["incoming_announce_frequency"])))
|
|
||||||
|
|
||||||
print(" Traffic : {txb}↑\n {rxb}↓".format(rxb=size_str(ifstat["rxb"]), txb=size_str(ifstat["txb"])))
|
|
||||||
|
|
||||||
if "transport_id" in stats and stats["transport_id"] != None:
|
|
||||||
print("\n Transport Instance "+RNS.prettyhexrep(stats["transport_id"])+" running")
|
|
||||||
if "probe_responder" in stats and stats["probe_responder"] != None:
|
|
||||||
print(" Probe responder at "+RNS.prettyhexrep(stats["probe_responder"])+ " active")
|
|
||||||
print(" Uptime is "+RNS.prettytime(stats["transport_uptime"]))
|
|
||||||
|
|
||||||
print("")
|
|
||||||
|
|
||||||
else:
|
|
||||||
print("Could not get RNS status")
|
print("Could not get RNS status")
|
||||||
|
return
|
||||||
|
|
||||||
|
if json:
|
||||||
|
import json
|
||||||
|
for s, value in stats.items():
|
||||||
|
stats[s] = convert_bytes_to_hex(value)
|
||||||
|
print(json.dumps(stats))
|
||||||
|
exit()
|
||||||
|
|
||||||
|
interfaces = stats["interfaces"]
|
||||||
|
if sorting is not None and isinstance(sorting, str):
|
||||||
|
sorting = sorting.lower()
|
||||||
|
if sorting == "rate" or sorting == "bitrate":
|
||||||
|
interfaces.sort(key=lambda i: i["bitrate"], reverse=not sort_reverse)
|
||||||
|
elif sorting == "rx":
|
||||||
|
interfaces.sort(key=lambda i: i["rxb"], reverse=not sort_reverse)
|
||||||
|
elif sorting == "tx":
|
||||||
|
interfaces.sort(key=lambda i: i["txb"], reverse=not sort_reverse)
|
||||||
|
elif sorting == "traffic":
|
||||||
|
interfaces.sort(key=lambda i: i["rxb"]+i["txb"], reverse=not sort_reverse)
|
||||||
|
elif sorting == "announces" or sorting == "announce":
|
||||||
|
interfaces.sort(key=lambda i: i["incoming_announce_frequency"]+i["outgoing_announce_frequency"], reverse=not sort_reverse)
|
||||||
|
elif sorting == "arx":
|
||||||
|
interfaces.sort(key=lambda i: i["incoming_announce_frequency"], reverse=not sort_reverse)
|
||||||
|
elif sorting == "atx":
|
||||||
|
interfaces.sort(key=lambda i: i["outgoing_announce_frequency"], reverse=not sort_reverse)
|
||||||
|
elif sorting == "held":
|
||||||
|
interfaces.sort(key=lambda i: i["held_announces"], reverse=not sort_reverse)
|
||||||
|
|
||||||
|
for ifstat in interfaces:
|
||||||
|
name = ifstat["name"]
|
||||||
|
|
||||||
|
if dispall or not (
|
||||||
|
name.startswith("LocalInterface[") or
|
||||||
|
name.startswith("TCPInterface[Client") or
|
||||||
|
name.startswith("I2PInterfacePeer[Connected peer") or
|
||||||
|
(name.startswith("I2PInterface[") and ("i2p_connectable" in ifstat and ifstat["i2p_connectable"] == False))
|
||||||
|
):
|
||||||
|
|
||||||
|
if not (name.startswith("I2PInterface[") and ("i2p_connectable" in ifstat and ifstat["i2p_connectable"] == False)):
|
||||||
|
if name_filter == None or name_filter.lower() in name.lower():
|
||||||
|
print("")
|
||||||
|
|
||||||
|
if ifstat["status"]:
|
||||||
|
ss = "Up"
|
||||||
|
else:
|
||||||
|
ss = "Down"
|
||||||
|
|
||||||
|
if ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_ACCESS_POINT:
|
||||||
|
modestr = "Access Point"
|
||||||
|
elif ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_POINT_TO_POINT:
|
||||||
|
modestr = "Point-to-Point"
|
||||||
|
elif ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_ROAMING:
|
||||||
|
modestr = "Roaming"
|
||||||
|
elif ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_BOUNDARY:
|
||||||
|
modestr = "Boundary"
|
||||||
|
elif ifstat["mode"] == RNS.Interfaces.Interface.Interface.MODE_GATEWAY:
|
||||||
|
modestr = "Gateway"
|
||||||
|
else:
|
||||||
|
modestr = "Full"
|
||||||
|
|
||||||
|
if ifstat["clients"] is not None:
|
||||||
|
clients = ifstat["clients"]
|
||||||
|
if name.startswith("Shared Instance["):
|
||||||
|
cnum = max(clients-1,0)
|
||||||
|
if cnum == 1:
|
||||||
|
spec_str = " program"
|
||||||
|
else:
|
||||||
|
spec_str = " programs"
|
||||||
|
|
||||||
|
clients_string = "Serving : "+str(cnum)+spec_str
|
||||||
|
elif name.startswith("I2PInterface["):
|
||||||
|
if "i2p_connectable" in ifstat and ifstat["i2p_connectable"] == True:
|
||||||
|
cnum = clients
|
||||||
|
if cnum == 1:
|
||||||
|
spec_str = " connected I2P endpoint"
|
||||||
|
else:
|
||||||
|
spec_str = " connected I2P endpoints"
|
||||||
|
|
||||||
|
clients_string = "Peers : "+str(cnum)+spec_str
|
||||||
|
else:
|
||||||
|
clients_string = ""
|
||||||
|
else:
|
||||||
|
clients_string = "Clients : "+str(clients)
|
||||||
|
|
||||||
|
else:
|
||||||
|
clients = None
|
||||||
|
|
||||||
|
print(" {n}".format(n=ifstat["name"]))
|
||||||
|
|
||||||
|
if "ifac_netname" in ifstat and ifstat["ifac_netname"] != None:
|
||||||
|
print(" Network : {nn}".format(nn=ifstat["ifac_netname"]))
|
||||||
|
|
||||||
|
print(" Status : {ss}".format(ss=ss))
|
||||||
|
|
||||||
|
if clients != None and clients_string != "":
|
||||||
|
print(" "+clients_string)
|
||||||
|
|
||||||
|
if not (name.startswith("Shared Instance[") or name.startswith("TCPInterface[Client") or name.startswith("LocalInterface[")):
|
||||||
|
print(" Mode : {mode}".format(mode=modestr))
|
||||||
|
|
||||||
|
if "bitrate" in ifstat and ifstat["bitrate"] != None:
|
||||||
|
print(" Rate : {ss}".format(ss=speed_str(ifstat["bitrate"])))
|
||||||
|
|
||||||
|
if "airtime_short" in ifstat and "airtime_long" in ifstat:
|
||||||
|
print(" Airtime : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["airtime_short"]),atl=str(ifstat["airtime_long"])))
|
||||||
|
|
||||||
|
if "channel_load_short" in ifstat and "channel_load_long" in ifstat:
|
||||||
|
print(" Ch.Load : {ats}% (15s), {atl}% (1h)".format(ats=str(ifstat["channel_load_short"]),atl=str(ifstat["channel_load_long"])))
|
||||||
|
|
||||||
|
if "peers" in ifstat and ifstat["peers"] != None:
|
||||||
|
print(" Peers : {np} reachable".format(np=ifstat["peers"]))
|
||||||
|
|
||||||
|
if "tunnelstate" in ifstat and ifstat["tunnelstate"] != None:
|
||||||
|
print(" I2P : {ts}".format(ts=ifstat["tunnelstate"]))
|
||||||
|
|
||||||
|
if "ifac_signature" in ifstat and ifstat["ifac_signature"] != None:
|
||||||
|
sigstr = "<…"+RNS.hexrep(ifstat["ifac_signature"][-5:], delimit=False)+">"
|
||||||
|
print(" Access : {nb}-bit IFAC by {sig}".format(nb=ifstat["ifac_size"]*8, sig=sigstr))
|
||||||
|
|
||||||
|
if "i2p_b32" in ifstat and ifstat["i2p_b32"] != None:
|
||||||
|
print(" I2P B32 : {ep}".format(ep=str(ifstat["i2p_b32"])))
|
||||||
|
|
||||||
|
if astats and "announce_queue" in ifstat and ifstat["announce_queue"] != None and ifstat["announce_queue"] > 0:
|
||||||
|
aqn = ifstat["announce_queue"]
|
||||||
|
if aqn == 1:
|
||||||
|
print(" Queued : {np} announce".format(np=aqn))
|
||||||
|
else:
|
||||||
|
print(" Queued : {np} announces".format(np=aqn))
|
||||||
|
|
||||||
|
if astats and "held_announces" in ifstat and ifstat["held_announces"] != None and ifstat["held_announces"] > 0:
|
||||||
|
aqn = ifstat["held_announces"]
|
||||||
|
if aqn == 1:
|
||||||
|
print(" Held : {np} announce".format(np=aqn))
|
||||||
|
else:
|
||||||
|
print(" Held : {np} announces".format(np=aqn))
|
||||||
|
|
||||||
|
if astats and "incoming_announce_frequency" in ifstat and ifstat["incoming_announce_frequency"] != None:
|
||||||
|
print(" Announces : {iaf}↑".format(iaf=RNS.prettyfrequency(ifstat["outgoing_announce_frequency"])))
|
||||||
|
print(" {iaf}↓".format(iaf=RNS.prettyfrequency(ifstat["incoming_announce_frequency"])))
|
||||||
|
|
||||||
|
print(" Traffic : {txb}↑\n {rxb}↓".format(rxb=size_str(ifstat["rxb"]), txb=size_str(ifstat["txb"])))
|
||||||
|
|
||||||
|
if "transport_id" in stats and stats["transport_id"] != None:
|
||||||
|
print("\n Transport Instance "+RNS.prettyhexrep(stats["transport_id"])+" running")
|
||||||
|
if "probe_responder" in stats and stats["probe_responder"] != None:
|
||||||
|
print(" Probe responder at "+RNS.prettyhexrep(stats["probe_responder"])+ " active")
|
||||||
|
print(" Uptime is "+RNS.prettytime(stats["transport_uptime"]))
|
||||||
|
|
||||||
|
print("")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
try:
|
try:
|
||||||
@ -232,7 +231,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",
|
||||||
@ -240,7 +239,7 @@ def main():
|
|||||||
help="show announce stats",
|
help="show announce stats",
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-s",
|
"-s",
|
||||||
"--sort",
|
"--sort",
|
||||||
@ -249,7 +248,7 @@ def main():
|
|||||||
default=None,
|
default=None,
|
||||||
type=str
|
type=str
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-r",
|
"-r",
|
||||||
"--reverse",
|
"--reverse",
|
||||||
@ -257,7 +256,7 @@ def main():
|
|||||||
help="reverse sorting",
|
help="reverse sorting",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-j",
|
"-j",
|
||||||
"--json",
|
"--json",
|
||||||
@ -269,7 +268,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:
|
||||||
@ -292,6 +291,7 @@ def main():
|
|||||||
print("")
|
print("")
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
def speed_str(num, suffix='bps'):
|
def speed_str(num, suffix='bps'):
|
||||||
units = ['','k','M','G','T','P','E','Z']
|
units = ['','k','M','G','T','P','E','Z']
|
||||||
last_unit = 'Y'
|
last_unit = 'Y'
|
||||||
@ -308,5 +308,6 @@ def speed_str(num, suffix='bps'):
|
|||||||
|
|
||||||
return "%.2f %s%s" % (num, last_unit, suffix)
|
return "%.2f %s%s" % (num, last_unit, suffix)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
@ -28,7 +28,7 @@ import argparse
|
|||||||
import shlex
|
import shlex
|
||||||
import time
|
import time
|
||||||
import sys
|
import sys
|
||||||
import tty
|
# import tty # Commented this because commented lines below use it
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from RNS._version import __version__
|
from RNS._version import __version__
|
||||||
@ -39,25 +39,27 @@ reticulum = None
|
|||||||
allow_all = False
|
allow_all = False
|
||||||
allowed_identity_hashes = []
|
allowed_identity_hashes = []
|
||||||
|
|
||||||
|
|
||||||
def prepare_identity(identity_path):
|
def prepare_identity(identity_path):
|
||||||
global identity
|
global identity
|
||||||
if identity_path == None:
|
if identity_path is None:
|
||||||
identity_path = RNS.Reticulum.identitypath+"/"+APP_NAME
|
identity_path = 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)
|
||||||
identity = RNS.Identity()
|
identity = RNS.Identity()
|
||||||
identity.to_file(identity_path)
|
identity.to_file(identity_path)
|
||||||
|
|
||||||
|
|
||||||
def listen(configdir, identitypath = None, verbosity = 0, quietness = 0, allowed = [], print_identity = False, disable_auth = None, disable_announce=False):
|
def listen(configdir, identitypath = None, verbosity = 0, quietness = 0, allowed = [], print_identity = False, disable_auth = None, disable_announce=False):
|
||||||
global identity, allow_all, allowed_identity_hashes, reticulum
|
global identity, allow_all, allowed_identity_hashes, reticulum
|
||||||
|
|
||||||
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")
|
||||||
|
|
||||||
@ -69,7 +71,7 @@ def listen(configdir, identitypath = None, verbosity = 0, quietness = 0, allowed
|
|||||||
if disable_auth:
|
if disable_auth:
|
||||||
allow_all = True
|
allow_all = True
|
||||||
else:
|
else:
|
||||||
if allowed != None:
|
if allowed is not None:
|
||||||
for a in allowed:
|
for a in allowed:
|
||||||
try:
|
try:
|
||||||
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
dest_len = (RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2
|
||||||
@ -107,7 +109,7 @@ def listen(configdir, identitypath = None, verbosity = 0, quietness = 0, allowed
|
|||||||
|
|
||||||
if not disable_announce:
|
if not disable_announce:
|
||||||
destination.announce()
|
destination.announce()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
@ -297,6 +299,7 @@ def remote_execution_progress(request_receipt):
|
|||||||
link = None
|
link = None
|
||||||
listener_destination = None
|
listener_destination = None
|
||||||
remote_exec_grace = 2.0
|
remote_exec_grace = 2.0
|
||||||
|
|
||||||
def execute(configdir, identitypath = None, verbosity = 0, quietness = 0, detailed = False, mirror = False, noid = False, destination = None, command = None, stdin = None, stdoutl = None, stderrl = None, timeout = RNS.Transport.PATH_REQUEST_TIMEOUT, result_timeout = None, interactive = False):
|
def execute(configdir, identitypath = None, verbosity = 0, quietness = 0, detailed = False, mirror = False, noid = False, destination = None, command = None, stdin = None, stdoutl = None, stderrl = None, timeout = RNS.Transport.PATH_REQUEST_TIMEOUT, result_timeout = None, interactive = False):
|
||||||
global identity, reticulum, link, listener_destination, remote_exec_grace
|
global identity, reticulum, link, listener_destination, remote_exec_grace
|
||||||
|
|
||||||
@ -338,7 +341,7 @@ def execute(configdir, identitypath = None, verbosity = 0, quietness = 0, detail
|
|||||||
if link == None or link.status == RNS.Link.CLOSED or link.status == RNS.Link.PENDING:
|
if link == None or link.status == RNS.Link.CLOSED or link.status == RNS.Link.PENDING:
|
||||||
link = RNS.Link(listener_destination)
|
link = RNS.Link(listener_destination)
|
||||||
link.did_identify = False
|
link.did_identify = False
|
||||||
|
|
||||||
if not spin(until=lambda: link.status == RNS.Link.ACTIVE, msg="Establishing link with "+RNS.prettyhexrep(destination_hash), timeout=timeout):
|
if not spin(until=lambda: link.status == RNS.Link.ACTIVE, msg="Establishing link with "+RNS.prettyhexrep(destination_hash), timeout=timeout):
|
||||||
print("Could not establish link with "+RNS.prettyhexrep(destination_hash))
|
print("Could not establish link with "+RNS.prettyhexrep(destination_hash))
|
||||||
exit(243)
|
exit(243)
|
||||||
@ -467,7 +470,7 @@ def execute(configdir, identitypath = None, verbosity = 0, quietness = 0, detail
|
|||||||
else:
|
else:
|
||||||
tstr = ""
|
tstr = ""
|
||||||
print("Remote wrote "+str(outlen)+" bytes to stdout"+tstr)
|
print("Remote wrote "+str(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 = ", "+str(len(stderr))+" bytes displayed"
|
||||||
@ -548,7 +551,6 @@ def main():
|
|||||||
parser.add_argument("--stdout", action='store', default=None, help="max size in bytes of returned stdout", type=int)
|
parser.add_argument("--stdout", action='store', default=None, help="max size in bytes of returned stdout", type=int)
|
||||||
parser.add_argument("--stderr", action='store', default=None, help="max size in bytes of returned stderr", type=int)
|
parser.add_argument("--stderr", action='store', default=None, help="max size in bytes of returned stderr", type=int)
|
||||||
parser.add_argument("--version", action="version", version="rnx {version}".format(version=__version__))
|
parser.add_argument("--version", action="version", version="rnx {version}".format(version=__version__))
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.listen or args.print_identity:
|
if args.listen or args.print_identity:
|
||||||
@ -600,8 +602,8 @@ def main():
|
|||||||
# while True:
|
# while True:
|
||||||
# ch = sys.stdin.read(1)
|
# ch = sys.stdin.read(1)
|
||||||
# cmdbuf += ch.encode("utf-8")
|
# cmdbuf += ch.encode("utf-8")
|
||||||
# print("\r"+prompt+cmdbuf.decode("utf-8"), end="")
|
# print("\r"+prompt+cmdbuf.decode("utf-8"), end="")
|
||||||
|
|
||||||
command = input()
|
command = input()
|
||||||
if command.lower() == "exit" or command.lower() == "quit":
|
if command.lower() == "exit" or command.lower() == "quit":
|
||||||
exit(0)
|
exit(0)
|
||||||
@ -668,6 +670,7 @@ def size_str(num, suffix='B'):
|
|||||||
|
|
||||||
return "%.2f%s%s" % (num, last_unit, suffix)
|
return "%.2f%s%s" % (num, 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))
|
||||||
time = time % (24 * 3600)
|
time = time % (24 * 3600)
|
||||||
@ -676,7 +679,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,31 +687,24 @@ 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("".join([str(days), " day", sd if verbose else str(days), "d"]))
|
||||||
|
|
||||||
if hours > 0:
|
if hours > 0:
|
||||||
components.append(str(hours)+" hour"+sh if verbose else str(hours)+"h")
|
components.append("".join([str(hours), " hour", sh if verbose else str(hours), "h"]))
|
||||||
|
|
||||||
if minutes > 0:
|
if minutes > 0:
|
||||||
components.append(str(minutes)+" minute"+sm if verbose else str(minutes)+"m")
|
components.append("".join([str(minutes), " minute", sm if verbose else str(minutes), "m"]))
|
||||||
|
|
||||||
if seconds > 0:
|
if seconds > 0:
|
||||||
components.append(str(seconds)+" second"+ss if verbose else str(seconds)+"s")
|
components.append("".join([str(seconds), " second", ss if verbose else str(seconds), "s"]))
|
||||||
|
|
||||||
i = 0
|
if len(components) > 1:
|
||||||
tstr = ""
|
time_string = "".join([", ".join(components[:-1]), " and ", components[-1]])
|
||||||
for c in components:
|
else:
|
||||||
i += 1
|
time_string = components[0] if components else ""
|
||||||
if i == 1:
|
|
||||||
pass
|
|
||||||
elif i < len(components):
|
|
||||||
tstr += ", "
|
|
||||||
elif i == len(components):
|
|
||||||
tstr += " and "
|
|
||||||
|
|
||||||
tstr += c
|
return time_string
|
||||||
|
|
||||||
return tstr
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
@ -1 +1 @@
|
|||||||
__version__ = "0.7.4"
|
__version__ = "0.7.5"
|
||||||
|
19
RNS/vendor/ifaddr/niwrapper.py
vendored
19
RNS/vendor/ifaddr/niwrapper.py
vendored
@ -11,6 +11,25 @@ def interfaces() -> List[str]:
|
|||||||
adapters = RNS.vendor.ifaddr.get_adapters(include_unconfigured=True)
|
adapters = RNS.vendor.ifaddr.get_adapters(include_unconfigured=True)
|
||||||
return [a.name for a in adapters]
|
return [a.name for a in adapters]
|
||||||
|
|
||||||
|
def interface_names_to_indexes() -> dict:
|
||||||
|
adapters = RNS.vendor.ifaddr.get_adapters(include_unconfigured=True)
|
||||||
|
results = {}
|
||||||
|
for adapter in adapters:
|
||||||
|
results[adapter.name] = adapter.index
|
||||||
|
return results
|
||||||
|
|
||||||
|
def interface_name_to_nice_name(ifname) -> str:
|
||||||
|
try:
|
||||||
|
adapters = RNS.vendor.ifaddr.get_adapters(include_unconfigured=True)
|
||||||
|
for adapter in adapters:
|
||||||
|
if adapter.name == ifname:
|
||||||
|
if hasattr(adapter, "nice_name"):
|
||||||
|
return adapter.nice_name
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
def ifaddresses(ifname) -> dict:
|
def ifaddresses(ifname) -> dict:
|
||||||
adapters = RNS.vendor.ifaddr.get_adapters(include_unconfigured=True)
|
adapters = RNS.vendor.ifaddr.get_adapters(include_unconfigured=True)
|
||||||
ifa = {}
|
ifa = {}
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -1,4 +1,4 @@
|
|||||||
# Sphinx build info version 1
|
# Sphinx build info version 1
|
||||||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
||||||
config: 541171817b95d649201365804faf729c
|
config: e6cf914f5d96347d4f64d2e8bcefb841
|
||||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
var DOCUMENTATION_OPTIONS = {
|
var DOCUMENTATION_OPTIONS = {
|
||||||
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
||||||
VERSION: '0.7.4 beta',
|
VERSION: '0.7.5 beta',
|
||||||
LANGUAGE: 'en',
|
LANGUAGE: 'en',
|
||||||
COLLAPSE_INDEX: false,
|
COLLAPSE_INDEX: false,
|
||||||
BUILDER: 'html',
|
BUILDER: 'html',
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Support Reticulum" href="support.html" /><link rel="prev" title="Building Networks" href="networks.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Support Reticulum" href="support.html" /><link rel="prev" title="Building Networks" href="networks.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>Code Examples - Reticulum Network Stack 0.7.4 beta documentation</title>
|
<title>Code Examples - Reticulum Network Stack 0.7.5 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.5 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@ -167,7 +167,7 @@
|
|||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.5 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 0.7.4 beta documentation</title>
|
<title>An Explanation of Reticulum for Human Beings - Reticulum Network Stack 0.7.5 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.5 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@ -167,7 +167,7 @@
|
|||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.5 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" />
|
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="#" /><link rel="search" title="Search" href="search.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/><title>Index - Reticulum Network Stack 0.7.4 beta documentation</title>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/><title>Index - Reticulum Network Stack 0.7.5 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@ -139,7 +139,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.5 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@ -165,7 +165,7 @@
|
|||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.5 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Using Reticulum on Your System" href="using.html" /><link rel="prev" title="What is Reticulum?" href="whatis.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Using Reticulum on Your System" href="using.html" /><link rel="prev" title="What is Reticulum?" href="whatis.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>Getting Started Fast - Reticulum Network Stack 0.7.4 beta documentation</title>
|
<title>Getting Started Fast - Reticulum Network Stack 0.7.5 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.5 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@ -167,7 +167,7 @@
|
|||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.5 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Configuring Interfaces" href="interfaces.html" /><link rel="prev" title="Understanding Reticulum" href="understanding.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Configuring Interfaces" href="interfaces.html" /><link rel="prev" title="Understanding Reticulum" href="understanding.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>Communications Hardware - Reticulum Network Stack 0.7.4 beta documentation</title>
|
<title>Communications Hardware - Reticulum Network Stack 0.7.5 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.5 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@ -167,7 +167,7 @@
|
|||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.5 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="What is Reticulum?" href="whatis.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="What is Reticulum?" href="whatis.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>Reticulum Network Stack 0.7.4 beta documentation</title>
|
<title>Reticulum Network Stack 0.7.5 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="#"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
<a href="#"><div class="brand">Reticulum Network Stack 0.7.5 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@ -167,7 +167,7 @@
|
|||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.5 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Building Networks" href="networks.html" /><link rel="prev" title="Communications Hardware" href="hardware.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Building Networks" href="networks.html" /><link rel="prev" title="Communications Hardware" href="hardware.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>Configuring Interfaces - Reticulum Network Stack 0.7.4 beta documentation</title>
|
<title>Configuring Interfaces - Reticulum Network Stack 0.7.5 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.5 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@ -167,7 +167,7 @@
|
|||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.5 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Code Examples" href="examples.html" /><link rel="prev" title="Configuring Interfaces" href="interfaces.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Code Examples" href="examples.html" /><link rel="prev" title="Configuring Interfaces" href="interfaces.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>Building Networks - Reticulum Network Stack 0.7.4 beta documentation</title>
|
<title>Building Networks - Reticulum Network Stack 0.7.5 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.5 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@ -167,7 +167,7 @@
|
|||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.5 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
|
Binary file not shown.
@ -6,7 +6,7 @@
|
|||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="prev" title="Support Reticulum" href="support.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="prev" title="Support Reticulum" href="support.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>API Reference - Reticulum Network Stack 0.7.4 beta documentation</title>
|
<title>API Reference - Reticulum Network Stack 0.7.5 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.5 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@ -167,7 +167,7 @@
|
|||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.5 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||||
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="#" />
|
<meta name="color-scheme" content="light dark"><link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="#" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/><title>Search - Reticulum Network Stack 0.7.4 beta documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/><title>Search - Reticulum Network Stack 0.7.5 beta documentation</title><link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo-extensions.css?digest=30d1aed668e5c3a91c3e3bf6a60b675221979f0e" />
|
||||||
@ -138,7 +138,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.5 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@ -164,7 +164,7 @@
|
|||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.5 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="#" role="search">
|
</a><form class="sidebar-search-container" method="get" action="#" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="API Reference" href="reference.html" /><link rel="prev" title="Code Examples" href="examples.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="API Reference" href="reference.html" /><link rel="prev" title="Code Examples" href="examples.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>Support Reticulum - Reticulum Network Stack 0.7.4 beta documentation</title>
|
<title>Support Reticulum - Reticulum Network Stack 0.7.5 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.5 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@ -167,7 +167,7 @@
|
|||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.5 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Communications Hardware" href="hardware.html" /><link rel="prev" title="Using Reticulum on Your System" href="using.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Communications Hardware" href="hardware.html" /><link rel="prev" title="Using Reticulum on Your System" href="using.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>Understanding Reticulum - Reticulum Network Stack 0.7.4 beta documentation</title>
|
<title>Understanding Reticulum - Reticulum Network Stack 0.7.5 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.5 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@ -167,7 +167,7 @@
|
|||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.5 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Understanding Reticulum" href="understanding.html" /><link rel="prev" title="Getting Started Fast" href="gettingstartedfast.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Understanding Reticulum" href="understanding.html" /><link rel="prev" title="Getting Started Fast" href="gettingstartedfast.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>Using Reticulum on Your System - Reticulum Network Stack 0.7.4 beta documentation</title>
|
<title>Using Reticulum on Your System - Reticulum Network Stack 0.7.5 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.5 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@ -167,7 +167,7 @@
|
|||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.5 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Getting Started Fast" href="gettingstartedfast.html" /><link rel="prev" title="Reticulum Network Stack Manual" href="index.html" />
|
<link rel="index" title="Index" href="genindex.html" /><link rel="search" title="Search" href="search.html" /><link rel="next" title="Getting Started Fast" href="gettingstartedfast.html" /><link rel="prev" title="Reticulum Network Stack Manual" href="index.html" />
|
||||||
|
|
||||||
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
<meta name="generator" content="sphinx-5.3.0, furo 2022.09.29.dev1"/>
|
||||||
<title>What is Reticulum? - Reticulum Network Stack 0.7.4 beta documentation</title>
|
<title>What is Reticulum? - Reticulum Network Stack 0.7.5 beta documentation</title>
|
||||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
<link rel="stylesheet" type="text/css" href="_static/styles/furo.css?digest=189ec851f9bb375a2509b67be1f64f0cf212b702" />
|
||||||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||||||
@ -141,7 +141,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.4 beta documentation</div></a>
|
<a href="index.html"><div class="brand">Reticulum Network Stack 0.7.5 beta documentation</div></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="theme-toggle-container theme-toggle-header">
|
<div class="theme-toggle-container theme-toggle-header">
|
||||||
@ -167,7 +167,7 @@
|
|||||||
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
<img class="sidebar-logo" src="_static/rns_logo_512.png" alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.4 beta documentation</span>
|
<span class="sidebar-brand-text">Reticulum Network Stack 0.7.5 beta documentation</span>
|
||||||
|
|
||||||
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
</a><form class="sidebar-search-container" method="get" action="search.html" role="search">
|
||||||
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
<input class="sidebar-search" placeholder="Search" name="q" aria-label="Search">
|
||||||
|
Loading…
Reference in New Issue
Block a user