Compare commits

..

16 Commits

Author SHA1 Message Date
Mark Qvist da13ee9cb9 Updated manual 2021-08-21 21:27:56 +02:00
Mark Qvist f719d44db5 Transport optimisations 2021-08-21 20:23:36 +02:00
Mark Qvist af890d91d2 Fixed race condition in outbound handling packet filter 2021-08-21 19:42:01 +02:00
Mark Qvist 242941fec4 Updated readme 2021-08-21 15:33:14 +02:00
Mark Qvist 0f79197945 Updated docs 2021-08-21 15:19:24 +02:00
Mark Qvist 212518a345 Implemented requests and responses of arbitrary sizes using resources. 2021-08-21 14:52:31 +02:00
Mark Qvist 1dc6655017 Implemented request and response API 2021-08-20 23:29:06 +02:00
Mark Qvist 69930e5652 Updated default config 2021-08-20 11:23:35 +02:00
Mark Qvist 2b8b95da2b Added config options for TCP server interface binding to network interface instead of IP. 2021-08-19 20:13:53 +02:00
Mark Qvist 6382409194 Added config options for UDP interface binding to network interface instead of IP. 2021-08-19 19:56:35 +02:00
Mark Qvist 4fd3d26714 Fixed UDP broadcast echo packets not being filtered. 2021-08-19 17:05:07 +02:00
Mark Qvist 8b6870fad8 Updated docs 2021-08-19 14:11:22 +02:00
Mark Qvist 384a7db974 Implemented link peer identification 2021-08-19 14:10:37 +02:00
Mark Qvist 772ae44ab8 Updated readme and docs 2021-07-25 23:48:18 +02:00
Mark Qvist d326df6c5a Cleanup 2021-05-20 23:31:26 +02:00
Mark Qvist 4269c48293 Updated readme 2021-05-20 23:16:19 +02:00
34 changed files with 1925 additions and 196 deletions
+6 -2
View File
@@ -135,7 +135,12 @@ def client_disconnected(link):
def client_request(message, packet):
global serve_path
filename = message.decode("utf-8")
try:
filename = message.decode("utf-8")
except Exception as e:
filename = None
if filename in list_files():
try:
# If we have the requested file, we'll
@@ -497,7 +502,6 @@ def download_concluded(resource):
saved_filename = current_filename
if resource.status == RNS.Resource.COMPLETE:
counter = 0
while os.path.isfile(saved_filename):
+310
View File
@@ -0,0 +1,310 @@
##########################################################
# This RNS example demonstrates how to set up a link to #
# a destination, and identify the initiator to it's peer #
##########################################################
import os
import sys
import time
import argparse
import RNS
# Let's define an app name. We'll use this for all
# destinations we create. Since this echo example
# is part of a range of example utilities, we'll put
# them all within the app namespace "example_utilities"
APP_NAME = "example_utilities"
##########################################################
#### Server Part #########################################
##########################################################
# A reference to the latest client link that connected
latest_client_link = None
# This initialisation is executed when the users chooses
# to run as a server
def server(configpath):
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# Randomly create a new identity for our link example
server_identity = RNS.Identity()
# We create a destination that clients can connect to. We
# want clients to create links to this destination, so we
# need to create a "single" destination type.
server_destination = RNS.Destination(
server_identity,
RNS.Destination.IN,
RNS.Destination.SINGLE,
APP_NAME,
"identifyexample"
)
# We configure a function that will get called every time
# a new client creates a link to this destination.
server_destination.set_link_established_callback(client_connected)
# Everything's ready!
# Let's Wait for client requests or user input
server_loop(server_destination)
def server_loop(destination):
# Let the user know that everything is ready
RNS.log(
"Link identification example "+
RNS.prettyhexrep(destination.hash)+
" running, waiting for a connection."
)
RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
# We enter a loop that runs until the users exits.
# If the user hits enter, we will announce our server
# destination on the network, which will let clients
# know how to create messages directed towards it.
while True:
entered = input()
destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
# When a client establishes a link to our server
# destination, this function will be called with
# a reference to the link.
def client_connected(link):
global latest_client_link
RNS.log("Client connected")
link.set_link_closed_callback(client_disconnected)
link.set_packet_callback(server_packet_received)
link.set_remote_identified_callback(remote_identified)
latest_client_link = link
def client_disconnected(link):
RNS.log("Client disconnected")
def remote_identified(identity):
RNS.log("Remote identified as: "+str(identity))
def server_packet_received(message, packet):
global latest_client_link
# Get the originating identity for display
remote_peer = "unidentified peer"
if packet.link.get_remote_identity() != None:
remote_peer = str(packet.link.get_remote_identity())
# When data is received over any active link,
# it will all be directed to the last client
# that connected.
text = message.decode("utf-8")
RNS.log("Received data from "+remote_peer+": "+text)
reply_text = "I received \""+text+"\" over the link from "+remote_peer
reply_data = reply_text.encode("utf-8")
RNS.Packet(latest_client_link, reply_data).send()
##########################################################
#### Client Part #########################################
##########################################################
# A reference to the server link
server_link = None
# A reference to the client identity
client_identity = None
# This initialisation is executed when the users chooses
# to run as a client
def client(destination_hexhash, configpath):
global client_identity
# We need a binary representation of the destination
# hash that was entered on the command line
try:
if len(destination_hexhash) != 20:
raise ValueError("Destination length is invalid, must be 20 hexadecimal characters (10 bytes)")
destination_hash = bytes.fromhex(destination_hexhash)
except:
RNS.log("Invalid destination entered. Check your input!\n")
exit()
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# Create a new client identity
client_identity = RNS.Identity()
RNS.log(
"Client created new identity "+
str(client_identity)
)
# Check if we know a path to the destination
if not RNS.Transport.has_path(destination_hash):
RNS.log("Destination is not yet known. Requesting path and waiting for announce to arrive...")
RNS.Transport.request_path(destination_hash)
while not RNS.Transport.has_path(destination_hash):
time.sleep(0.1)
# Recall the server identity
server_identity = RNS.Identity.recall(destination_hash)
# Inform the user that we'll begin connecting
RNS.log("Establishing link with server...")
# When the server identity is known, we set
# up a destination
server_destination = RNS.Destination(
server_identity,
RNS.Destination.OUT,
RNS.Destination.SINGLE,
APP_NAME,
"identifyexample"
)
# And create a link
link = RNS.Link(server_destination)
# We set a callback that will get executed
# every time a packet is received over the
# link
link.set_packet_callback(client_packet_received)
# We'll also set up functions to inform the
# user when the link is established or closed
link.set_link_established_callback(link_established)
link.set_link_closed_callback(link_closed)
# Everything is set up, so let's enter a loop
# for the user to interact with the example
client_loop()
def client_loop():
global server_link
# Wait for the link to become active
while not server_link:
time.sleep(0.1)
should_quit = False
while not should_quit:
try:
print("> ", end=" ")
text = input()
# Check if we should quit the example
if text == "quit" or text == "q" or text == "exit":
should_quit = True
server_link.teardown()
# If not, send the entered text over the link
if text != "":
data = text.encode("utf-8")
if len(data) <= RNS.Link.MDU:
RNS.Packet(server_link, data).send()
else:
RNS.log(
"Cannot send this packet, the data size of "+
str(len(data))+" bytes exceeds the link packet MDU of "+
str(RNS.Link.MDU)+" bytes",
RNS.LOG_ERROR
)
except Exception as e:
RNS.log("Error while sending data over the link: "+str(e))
should_quit = True
server_link.teardown()
# This function is called when a link
# has been established with the server
def link_established(link):
# We store a reference to the link
# instance for later use
global server_link, client_identity
server_link = link
# Inform the user that the server is
# connected
RNS.log("Link established with server, identifying to remote peer...")
link.identify(client_identity)
# When a link is closed, we'll inform the
# user, and exit the program
def link_closed(link):
if link.teardown_reason == RNS.Link.TIMEOUT:
RNS.log("The link timed out, exiting now")
elif link.teardown_reason == RNS.Link.DESTINATION_CLOSED:
RNS.log("The link was closed by the server, exiting now")
else:
RNS.log("Link closed, exiting now")
RNS.Reticulum.exit_handler()
time.sleep(1.5)
os._exit(0)
# When a packet is received over the link, we
# simply print out the data.
def client_packet_received(message, packet):
text = message.decode("utf-8")
RNS.log("Received data on the link: "+text)
print("> ", end=" ")
sys.stdout.flush()
##########################################################
#### Program Startup #####################################
##########################################################
# This part of the program runs at startup,
# and parses input of from the user, and then
# starts up the desired program mode.
if __name__ == "__main__":
try:
parser = argparse.ArgumentParser(description="Simple link example")
parser.add_argument(
"-s",
"--server",
action="store_true",
help="wait for incoming link requests from clients"
)
parser.add_argument(
"--config",
action="store",
default=None,
help="path to alternative Reticulum config directory",
type=str
)
parser.add_argument(
"destination",
nargs="?",
default=None,
help="hexadecimal hash of the server destination",
type=str
)
args = parser.parse_args()
if args.config:
configarg = args.config
else:
configarg = None
if args.server:
server(configarg)
else:
if (args.destination == None):
print("")
parser.print_help()
print("")
else:
client(args.destination, configarg)
except KeyboardInterrupt:
print("")
exit()
+283
View File
@@ -0,0 +1,283 @@
##########################################################
# This RNS example demonstrates how to set perform #
# requests and receive responses over a link. #
##########################################################
import os
import sys
import time
import random
import argparse
import RNS
# Let's define an app name. We'll use this for all
# destinations we create. Since this echo example
# is part of a range of example utilities, we'll put
# them all within the app namespace "example_utilities"
APP_NAME = "example_utilities"
##########################################################
#### Server Part #########################################
##########################################################
# A reference to the latest client link that connected
latest_client_link = None
def random_text_generator(path, data, request_id, remote_identity, requested_at):
RNS.log("Generating response to request "+RNS.prettyhexrep(request_id))
texts = ["They looked up", "On each full moon", "Becky was upset", "Ill stay away from it", "The pet shop stocks everything"]
return texts[random.randint(0, len(texts)-1)]
# This initialisation is executed when the users chooses
# to run as a server
def server(configpath):
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# Randomly create a new identity for our link example
server_identity = RNS.Identity()
# We create a destination that clients can connect to. We
# want clients to create links to this destination, so we
# need to create a "single" destination type.
server_destination = RNS.Destination(
server_identity,
RNS.Destination.IN,
RNS.Destination.SINGLE,
APP_NAME,
"requestexample"
)
# We configure a function that will get called every time
# a new client creates a link to this destination.
server_destination.set_link_established_callback(client_connected)
# We register a request handler for handling incoming
# requests over any established links.
server_destination.register_request_handler(
"/random/text",
response_generator = random_text_generator,
allow = RNS.Destination.ALLOW_ALL
)
# Everything's ready!
# Let's Wait for client requests or user input
server_loop(server_destination)
def server_loop(destination):
# Let the user know that everything is ready
RNS.log(
"Request example "+
RNS.prettyhexrep(destination.hash)+
" running, waiting for a connection."
)
RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)")
# We enter a loop that runs until the users exits.
# If the user hits enter, we will announce our server
# destination on the network, which will let clients
# know how to create messages directed towards it.
while True:
entered = input()
destination.announce()
RNS.log("Sent announce from "+RNS.prettyhexrep(destination.hash))
# When a client establishes a link to our server
# destination, this function will be called with
# a reference to the link.
def client_connected(link):
global latest_client_link
RNS.log("Client connected")
link.set_link_closed_callback(client_disconnected)
latest_client_link = link
def client_disconnected(link):
RNS.log("Client disconnected")
##########################################################
#### Client Part #########################################
##########################################################
# A reference to the server link
server_link = None
# This initialisation is executed when the users chooses
# to run as a client
def client(destination_hexhash, configpath):
# We need a binary representation of the destination
# hash that was entered on the command line
try:
if len(destination_hexhash) != 20:
raise ValueError("Destination length is invalid, must be 20 hexadecimal characters (10 bytes)")
destination_hash = bytes.fromhex(destination_hexhash)
except:
RNS.log("Invalid destination entered. Check your input!\n")
exit()
# We must first initialise Reticulum
reticulum = RNS.Reticulum(configpath)
# Check if we know a path to the destination
if not RNS.Transport.has_path(destination_hash):
RNS.log("Destination is not yet known. Requesting path and waiting for announce to arrive...")
RNS.Transport.request_path(destination_hash)
while not RNS.Transport.has_path(destination_hash):
time.sleep(0.1)
# Recall the server identity
server_identity = RNS.Identity.recall(destination_hash)
# Inform the user that we'll begin connecting
RNS.log("Establishing link with server...")
# When the server identity is known, we set
# up a destination
server_destination = RNS.Destination(
server_identity,
RNS.Destination.OUT,
RNS.Destination.SINGLE,
APP_NAME,
"requestexample"
)
# And create a link
link = RNS.Link(server_destination)
# We'll set up functions to inform the
# user when the link is established or closed
link.set_link_established_callback(link_established)
link.set_link_closed_callback(link_closed)
# Everything is set up, so let's enter a loop
# for the user to interact with the example
client_loop()
def client_loop():
global server_link
# Wait for the link to become active
while not server_link:
time.sleep(0.1)
should_quit = False
while not should_quit:
try:
print("> ", end=" ")
text = input()
# Check if we should quit the example
if text == "quit" or text == "q" or text == "exit":
should_quit = True
server_link.teardown()
else:
server_link.request(
"/random/text",
data = None,
response_callback = got_response,
failed_callback = request_failed
)
except Exception as e:
RNS.log("Error while sending request over the link: "+str(e))
should_quit = True
server_link.teardown()
def got_response(request_receipt):
request_id = request_receipt.request_id
response = request_receipt.response
RNS.log("Got response for request "+RNS.prettyhexrep(request_id)+": "+str(response))
def request_received(request_receipt):
RNS.log("The request "+RNS.prettyhexrep(request_receipt.request_id)+" was received by the remote peer.")
def request_failed(request_receipt):
RNS.log("The request "+RNS.prettyhexrep(request_receipt.request_id)+" failed.")
# This function is called when a link
# has been established with the server
def link_established(link):
# We store a reference to the link
# instance for later use
global server_link
server_link = link
# Inform the user that the server is
# connected
RNS.log("Link established with server, hit enter to perform a request, or type in \"quit\" to quit")
# When a link is closed, we'll inform the
# user, and exit the program
def link_closed(link):
if link.teardown_reason == RNS.Link.TIMEOUT:
RNS.log("The link timed out, exiting now")
elif link.teardown_reason == RNS.Link.DESTINATION_CLOSED:
RNS.log("The link was closed by the server, exiting now")
else:
RNS.log("Link closed, exiting now")
RNS.Reticulum.exit_handler()
time.sleep(1.5)
os._exit(0)
##########################################################
#### Program Startup #####################################
##########################################################
# This part of the program runs at startup,
# and parses input of from the user, and then
# starts up the desired program mode.
if __name__ == "__main__":
try:
parser = argparse.ArgumentParser(description="Simple request/response example")
parser.add_argument(
"-s",
"--server",
action="store_true",
help="wait for incoming requests from clients"
)
parser.add_argument(
"--config",
action="store",
default=None,
help="path to alternative Reticulum config directory",
type=str
)
parser.add_argument(
"destination",
nargs="?",
default=None,
help="hexadecimal hash of the server destination",
type=str
)
args = parser.parse_args()
if args.config:
configarg = args.config
else:
configarg = None
if args.server:
server(configarg)
else:
if (args.destination == None):
print("")
parser.print_help()
print("")
else:
client(args.destination, configarg)
except KeyboardInterrupt:
print("")
exit()
-54
View File
@@ -1,54 +0,0 @@
Reticulum Wire Format
Header Types
-----------------
type 1 00 Two byte header, one 10 byte address field
type 2 01 Two byte header, two 10 byte address fields
type 3 10 Reserved
type 4 11 Reserved
Propagation Types
-----------------
broadcast 00
transport 01
reserved 10
reserved 11
Destination Types
-----------------
single 00
group 01
plain 10
link 11
Packet Types
-----------------
data 00
announce 01
link request 10
proof 11
+- Packet Example -+
01010000 00000100 [ADDR1, 10 bytes] [ADDR2, 10 bytes] [CONTEXT, 1 byte] [DATA]
| | | | |
| | | | +-- Hops = 4
| | | +------- DATA packet
| | +--------- SINGLE destination
| +----------- TRANSPORT propagation type
+------------- HEADER_2, two byte header, two address fields
+- Packet Example -+
00000000 00000111 [ADDR1, 10 bytes] [CONTEXT, 1 byte] [DATA]
| | | | |
| | | | +-- Hops = 7
| | | +------- DATA packet
| | +--------- SINGLE destination
| +----------- BROADCAST propagation type
+------------- HEADER_1, two byte header, one address field
+4 -37
View File
@@ -32,6 +32,7 @@ For more info, see [unsigned.io/projects/reticulum](https://unsigned.io/projects
- Reticulum can handle a few bytes of data or files of many gigabytes
- Sequencing, transfer coordination and checksumming is automatic
- The API is very easy to use, and provides transfer progress
- Lightweight, flexible and expandable Request/Response mechanism
- Efficient link establishment
- Total bandwidth cost of setting up a link is 3 packets totalling 240 bytes
- Low cost of keeping links open at only 0.62 bits per second
@@ -74,49 +75,15 @@ Some countries still ban the use of encryption when operating under an amateur r
- pyserial
## How do I get started?
Full documentation and tutorials are coming with the stable alpha release. Until then, you are mostly on your own. If you want to experiment already, you could take a look in the "Examples" folder, for some well-documented example programs. The default configuration file created by Reticulum on the first run is also worth reading. Be sure to also read the [Reticulum Overview Document](http://unsigned.io/wp-content/uploads/2018/04/Reticulum_Overview_v0.4.pdf).
The best way to get started with the Reticulum Network Stack depends on what
you want to do. For full details and examples, have a look at the [Getting Started Fast](https://markqvist.github.io/Reticulum/manual/gettingstartedfast.html) section of the [Reticulum Manual](https://markqvist.github.io/Reticulum/manual/).
If you just need Reticulum as a dependency for another application, the easiest way is probably via pip:
If you just need Reticulum as a dependency for another application, the easiest way is via pip:
```bash
pip3 install rns
```
For Reticulum development, you might want to get the latest source from GitHub. In that case, don't use pip, but try this recipe:
```bash
# Install dependencies
pip3 install cryptography pyserial
# Clone repository
git clone https://github.com/markqvist/Reticulum.git
# Move into Reticulum folder and symlink library to examples folder
cd Reticulum
ln -s ../RNS ./Examples/
# Run an example
python3 Examples/Echo.py -s
# Unless you've manually created a config file, Reticulum will do so now,
# and immediately exit. Make any necessary changes to the file:
nano ~/.reticulum/config
# ... and launch the example again.
python3 Examples/Echo.py -s
# You can now repeat the process on another computer,
# and run the same example with -h to get command line options.
python3 Examples/Echo.py -h
# Run the example in client mode to "ping" the server.
# Replace the hash below with the actual destination hash of your server.
python3 Examples/Echo.py 3e12fc71692f8ec47bc5
# Have a look at another example
python3 Examples/Filetransfer.py -h
```
The default config file contains examples for using Reticulum with LoRa transceivers (specifically [RNode](https://unsigned.io/projects/rnode/)), packet radio TNCs/modems and UDP. By default a UDP interface is already enabled in the default config, which will enable Reticulum communication in your local ethernet broadcast domain.
You can use the examples in the config file to expand communication over other mediums such as packet radio or LoRa, or over fast IP links using the UDP interface. I'll add in-depth tutorials and explanations on these topics later. For now, the included examples will hopefully be enough to get started.
+45
View File
@@ -40,6 +40,11 @@ class Destination:
PROVE_ALL = 0x23
proof_strategies = [PROVE_NONE, PROVE_APP, PROVE_ALL]
ALLOW_NONE = 0x00
ALLOW_ALL = 0x01
ALLOW_LIST = 0x02
request_policies = [ALLOW_NONE, ALLOW_ALL, ALLOW_LIST]
IN = 0x11;
OUT = 0x12;
directions = [IN, OUT]
@@ -97,6 +102,7 @@ class Destination:
if not type in Destination.types: raise ValueError("Unknown destination type")
if not direction in Destination.directions: raise ValueError("Unknown destination direction")
self.callbacks = Callbacks()
self.request_handlers = {}
self.type = type
self.direction = direction
self.proof_strategy = Destination.PROVE_NONE
@@ -208,6 +214,45 @@ class Destination:
else:
self.proof_strategy = proof_strategy
def register_request_handler(self, path, response_generator = None, allow = ALLOW_NONE, allowed_list = None):
"""
Registers a request handler.
:param path: The path for the request handler to be registered.
:param response_generator: A function or method with the signature *response_generator(path, data, request_id, remote_identity, requested_at)* to be called. Whatever this funcion returns will be sent as a response to the requester. If the function returns ``None``, no response will be sent.
:param allow: One of ``RNS.Destination.ALLOW_NONE``, ``RNS.Destination.ALLOW_ALL`` or ``RNS.Destination.ALLOW_LIST``. If ``RNS.Destination.ALLOW_LIST`` is set, the request handler will only respond to requests for identified peers in the supplied list.
:param allowed_list: A list of *bytes-like* :ref:`RNS.Identity<api-identity>` hashes.
:raises: ``ValueError`` if any of the supplied arguments are invalid.
"""
if path == None or path == "":
raise ValueError("Invalid path specified")
elif not callable(response_generator):
raise ValueError("Invalid response generator specified")
elif not allow in Destination.request_policies:
raise ValueError("Invalid request policy")
else:
path_hash = RNS.Identity.truncated_hash(path.encode("utf-8"))
request_handler = [path, response_generator, allow, allowed_list]
self.request_handlers[path_hash] = request_handler
def deregister_request_handler(self, path):
"""
Deregisters a request handler.
:param path: The path for the request handler to be deregistered.
:returns: True if the handler was deregistered, otherwise False.
"""
path_hash = RNS.Identity.truncated_hash(path.encode("utf-8"))
if path_hash in self.request_handlers:
self.request_handlers.pop(path_hash)
return True
else:
return False
def receive(self, packet):
if packet.packet_type == RNS.Packet.LINKREQUEST:
plaintext = packet.data
+11 -1
View File
@@ -1,6 +1,7 @@
from .Interface import Interface
import socketserver
import threading
import netifaces
import socket
import time
import sys
@@ -127,12 +128,21 @@ class TCPClientInterface(Interface):
class TCPServerInterface(Interface):
@staticmethod
def get_address_for_if(name):
return netifaces.ifaddresses(name)[netifaces.AF_INET][0]['addr']
def __init__(self, owner, name, bindip=None, bindport=None):
def get_broadcast_for_if(name):
return netifaces.ifaddresses(name)[netifaces.AF_INET][0]['broadcast']
def __init__(self, owner, name, device=None, bindip=None, bindport=None):
self.IN = True
self.OUT = False
self.name = name
if device != None:
bindip = TCPServerInterface.get_address_for_if(device)
if (bindip != None and bindport != None):
self.receives = True
self.bind_ip = bindip
+17 -1
View File
@@ -1,18 +1,34 @@
from .Interface import Interface
import socketserver
import threading
import netifaces
import socket
import time
import sys
import RNS
class UDPInterface(Interface):
def __init__(self, owner, name, bindip=None, bindport=None, forwardip=None, forwardport=None):
@staticmethod
def get_address_for_if(name):
return netifaces.ifaddresses(name)[netifaces.AF_INET][0]['addr']
def get_broadcast_for_if(name):
return netifaces.ifaddresses(name)[netifaces.AF_INET][0]['broadcast']
def __init__(self, owner, name, device=None, bindip=None, bindport=None, forwardip=None, forwardport=None):
self.IN = True
self.OUT = False
self.name = name
if device != None:
if bindip == None:
bindip = UDPInterface.get_broadcast_for_if(device)
if forwardip == None:
forwardip = UDPInterface.get_broadcast_for_if(device)
if (bindip != None and bindport != None):
self.receives = True
self.bind_ip = bindip
+258 -2
View File
@@ -23,6 +23,7 @@ class LinkCallbacks:
self.resource = None
self.resource_started = None
self.resource_concluded = None
self.remote_identified = None
class Link:
"""
@@ -109,6 +110,7 @@ class Link:
self.resource_strategy = Link.ACCEPT_NONE
self.outgoing_resources = []
self.incoming_resources = []
self.pending_requests = []
self.last_inbound = 0
self.last_outbound = 0
self.tx = 0
@@ -125,6 +127,7 @@ class Link:
self.owner = owner
self.destination = destination
self.attached_interface = None
self.__remote_identity = None
self.__encryption_disabled = False
if self.destination == None:
self.initiator = False
@@ -226,6 +229,7 @@ class Link:
if self.destination.identity.validate(signature, signed_data):
self.rtt = time.time() - self.request_time
self.attached_interface = packet.receiving_interface
self.__remote_identity = self.destination.identity
RNS.Transport.activate_link(self)
RNS.log("Link "+str(self)+" established with "+str(self.destination)+", RTT is "+str(self.rtt), RNS.LOG_VERBOSE)
rtt_data = umsgpack.packb(self.rtt)
@@ -243,6 +247,60 @@ class Link:
RNS.log("Invalid link proof signature received by "+str(self)+". Ignoring.", RNS.LOG_DEBUG)
def identify(self, identity):
"""
Identifies the initiator of the link to the remote peer. This can only happen
once the link has been established, and is carried out over the encrypted link.
The identity is only revealed to the remote peer, and initiator anonymity is
thus preserved. This method can be used for authentication.
:param identity: An RNS.Identity instance to identify as.
"""
if self.initiator:
signed_data = self.link_id + identity.get_public_key()
signature = identity.sign(signed_data)
proof_data = identity.get_public_key() + signature
proof = RNS.Packet(self, proof_data, RNS.Packet.DATA, context = RNS.Packet.LINKIDENTIFY)
proof.send()
self.had_outbound()
def request(self, path, data = None, response_callback = None, failed_callback = None):
"""
Sends a request to the remote peer.
:param path: The request path.
:param response_callback: A function or method with the signature *response_callback(request_receipt)* to be called when a response is received. See the :ref:`Request Example<example-request>` for more info.
:param failed_callback: A function or method with the signature *failed_callback(request_receipt)* to be called when a request fails. See the :ref:`Request Example<example-request>` for more info.
"""
request_path_hash = RNS.Identity.truncated_hash(path.encode("utf-8"))
unpacked_request = [time.time(), request_path_hash, data]
packed_request = umsgpack.packb(unpacked_request)
if len(packed_request) <= Link.MDU:
request_packet = RNS.Packet(self, packed_request, RNS.Packet.DATA, context = RNS.Packet.REQUEST)
return RequestReceipt(
self,
packet_receipt = request_packet.send(),
response_callback = response_callback,
failed_callback = failed_callback
)
else:
request_id = RNS.Identity.truncated_hash(packed_request)
RNS.log("Sending request "+RNS.prettyhexrep(request_id)+" as resource.", RNS.LOG_DEBUG)
request_resource = RNS.Resource(packed_request, self, request_id = request_id, is_response = False)
return RequestReceipt(
self,
resource = request_resource,
response_callback = response_callback,
failed_callback = failed_callback
)
def rtt_packet(self, packet):
try:
# TODO: This is crude, we should use the delta
@@ -286,6 +344,12 @@ class Link:
"""
return min(self.no_inbound_for(), self.no_outbound_for())
def get_remote_identity(self):
"""
:returns: The identity of the remote peer, if it is known
"""
return self.__remote_identity
def had_outbound(self):
self.last_outbound = time.time()
@@ -397,6 +461,77 @@ class Link:
keepalive_packet.send()
self.had_outbound()
def handle_request(self, request_id, unpacked_request):
requested_at = unpacked_request[0]
path_hash = unpacked_request[1]
request_data = unpacked_request[2]
if path_hash in self.destination.request_handlers:
request_handler = self.destination.request_handlers[path_hash]
path = request_handler[0]
response_generator = request_handler[1]
allow = request_handler[2]
allowed_list = request_handler[3]
allowed = False
if not allow == RNS.Destination.ALLOW_NONE:
if allow == RNS.Destination.ALLOW_LIST:
if self.__remote_identity.hash in allowed_list:
allowed = True
elif allow == RNS.Destination.ALLOW_ALL:
allowed = True
if allowed:
RNS.log("Handling request "+RNS.prettyhexrep(request_id)+" for: "+str(path), RNS.LOG_DEBUG)
response = response_generator(path, request_data, request_id, self.__remote_identity, requested_at)
if response != None:
packed_response = umsgpack.packb([request_id, response])
if len(packed_response) <= Link.MDU:
RNS.Packet(self, packed_response, RNS.Packet.DATA, context = RNS.Packet.RESPONSE).send()
else:
response_resource = RNS.Resource(packed_response, self, request_id = request_id, is_response = True)
else:
identity_string = RNS.prettyhexrep(self.get_remote_identity()) if self.get_remote_identity() != None else "<Unknown>"
RNS.log("Request "+RNS.prettyhexrep(request_id)+" from "+identity_string+" not allowed for: "+str(path), RNS.LOG_DEBUG)
def handle_response(self, request_id, response_data):
remove = None
for pending_request in self.pending_requests:
if pending_request.request_id == request_id:
remove = pending_request
try:
pending_request.response_received(response_data)
except Exception as e:
RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR)
break
if remove != None:
self.pending_requests.remove(remove)
def request_resource_concluded(self, resource):
if resource.status == RNS.Resource.COMPLETE:
packed_request = resource.data.read()
unpacked_request = umsgpack.unpackb(packed_request)
request_id = RNS.Identity.truncated_hash(packed_request)
request_data = unpacked_request
self.handle_request(request_id, request_data)
else:
RNS.log("Incoming request resource failed with status: "+RNS.hexrep([resource.status]), RNS.LOG_DEBUG)
def response_resource_concluded(self, resource):
if resource.status == RNS.Resource.COMPLETE:
packed_response = resource.data.read()
unpacked_response = umsgpack.unpackb(packed_response)
request_id = unpacked_response[0]
response_data = unpacked_response[1]
self.handle_response(request_id, response_data)
else:
RNS.log("Incoming response resource failed with status: "+RNS.hexrep([resource.status]), RNS.LOG_DEBUG)
def receive(self, packet):
self.watchdog_lock = True
if not self.status == Link.CLOSED and not (self.initiator and packet.context == RNS.Packet.KEEPALIVE and packet.data == bytes([0xFF])):
@@ -424,6 +559,40 @@ class Link:
if self.destination.callbacks.proof_requested:
self.destination.callbacks.proof_requested(packet)
elif packet.context == RNS.Packet.LINKIDENTIFY:
plaintext = self.decrypt(packet.data)
if not self.initiator and len(plaintext) == RNS.Identity.KEYSIZE//8 + RNS.Identity.SIGLENGTH//8:
public_key = plaintext[:RNS.Identity.KEYSIZE//8]
signed_data = self.link_id+public_key
signature = plaintext[RNS.Identity.KEYSIZE//8:RNS.Identity.KEYSIZE//8+RNS.Identity.SIGLENGTH//8]
identity = RNS.Identity(create_keys=False)
identity.load_public_key(public_key)
if identity.validate(signature, signed_data):
self.__remote_identity = identity
if self.callbacks.remote_identified != None:
self.callbacks.remote_identified(self.__remote_identity)
elif packet.context == RNS.Packet.REQUEST:
try:
request_id = packet.getTruncatedHash()
packed_request = self.decrypt(packet.data)
unpacked_request = umsgpack.unpackb(packed_request)
self.handle_request(request_id, unpacked_request)
except Exception as e:
RNS.log("Error occurred while handling request. The contained exception was: "+str(e), RNS.LOG_ERROR)
elif packet.context == RNS.Packet.RESPONSE:
try:
packed_response = self.decrypt(packet.data)
unpacked_response = umsgpack.unpackb(packed_response)
request_id = unpacked_response[0]
response_data = unpacked_response[1]
self.handle_response(request_id, response_data)
except Exception as e:
RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR)
elif packet.context == RNS.Packet.LRRTT:
if not self.initiator:
self.rtt_packet(packet)
@@ -433,7 +602,12 @@ class Link:
elif packet.context == RNS.Packet.RESOURCE_ADV:
packet.plaintext = self.decrypt(packet.data)
if self.resource_strategy == Link.ACCEPT_NONE:
if RNS.ResourceAdvertisement.is_request(packet):
RNS.Resource.accept(packet, callback=self.request_resource_concluded)
elif RNS.ResourceAdvertisement.is_response(packet):
RNS.Resource.accept(packet, callback=self.response_resource_concluded)
elif self.resource_strategy == Link.ACCEPT_NONE:
pass
elif self.resource_strategy == Link.ACCEPT_APP:
if self.callbacks.resource != None:
@@ -574,6 +748,15 @@ class Link:
"""
self.callbacks.resource_concluded = callback
def set_remote_identified_callback(self, callback):
"""
Registers a function to be called when an initiating peer has
identified over this link.
:param callback: A function or method with the signature *callback(identity)* to be called.
"""
self.callbacks.remote_identified = callback
def resource_concluded(self, resource):
if resource in self.incoming_resources:
self.incoming_resources.remove(resource)
@@ -639,4 +822,77 @@ class Link:
return self.__encryption_disabled
def __str__(self):
return RNS.prettyhexrep(self.link_id)
return RNS.prettyhexrep(self.link_id)
class RequestReceipt():
FAILED = 0x00
SENT = 0x01
DELIVERED = 0x02
READY = 0x03
def __init__(self, link, packet_receipt = None, resource = None, response_callback = None, failed_callback = None):
self.packet_receipt = packet_receipt
self.resource = resource
if self.packet_receipt != None:
self.hash = packet_receipt.truncated_hash
self.packet_receipt.set_timeout_callback(self.request_timed_out)
elif self.resource != None:
self.hash = resource.request_id
resource.set_callback(self.request_resource_concluded)
self.link = link
self.request_id = self.hash
self.response = None
self.status = RequestReceipt.SENT
self.sent_at = time.time()
self.timeout = RNS.Packet.TIMEOUT
self.concluded_at = None
self.callbacks = RequestReceiptCallbacks()
self.callbacks.response = response_callback
self.callbacks.failed = failed_callback
self.link.pending_requests.append(self)
def request_resource_concluded(self, resource):
if resource.status == RNS.Resource.COMPLETE:
RNS.log("Request "+RNS.prettyhexrep(self.request_id)+" successfully sent as resource.", RNS.LOG_DEBUG)
else:
RNS.log("Sending request "+RNS.prettyhexrep(self.request_id)+" as resource failed with status: "+RNS.hexrep([resource.status]), RNS.LOG_DEBUG)
self.status = RequestReceipt.FAILED
self.concluded_at = time.time()
self.link.pending_requests.remove(self)
if self.callbacks.failed != None:
self.callbacks.failed(self)
def request_timed_out(self, packet_receipt):
self.status = RequestReceipt.FAILED
self.concluded_at = time.time()
self.link.pending_requests.remove(self)
if self.callbacks.failed != None:
self.callbacks.failed(self)
def response_received(self, response):
self.response = response
if self.packet_receipt != None:
self.packet_receipt.status = RNS.PacketReceipt.DELIVERED
self.packet_receipt.proved = True
self.packet_receipt.concluded_at = time.time()
if self.packet_receipt.callbacks.delivery != None:
self.packet_receipt.callbacks.delivery(self)
if self.callbacks.response != None:
self.callbacks.response(self)
class RequestReceiptCallbacks:
def __init__(self):
self.response = None
self.failed = None
+13 -12
View File
@@ -41,7 +41,7 @@ class Packet:
HEADER_4 = 0x03 # Reserved
header_types = [HEADER_1, HEADER_2, HEADER_3, HEADER_4]
# Data packet context types
# Packet context types
NONE = 0x00 # Generic data packet
RESOURCE = 0x01 # Packet is part of a resource
RESOURCE_ADV = 0x02 # Packet is a resource advertisement
@@ -56,7 +56,8 @@ class Packet:
PATH_RESPONSE = 0x0B # Packet is a response to a path request
COMMAND = 0x0C # Packet is a command
COMMAND_STATUS = 0x0D # Packet is a status of an executed command
KEEPALIVE = 0xFB # Packet is a keepalive packet
KEEPALIVE = 0xFA # Packet is a keepalive packet
LINKIDENTIFY = 0xFB # Packet is a link peer identification proof
LINKCLOSE = 0xFC # Packet is a link close message
LINKPROOF = 0xFD # Packet is a link packet proof
LRRTT = 0xFE # Packet is a link request round-trip time measurement
@@ -67,7 +68,6 @@ class Packet:
HEADER_MAXSIZE = RNS.Reticulum.HEADER_MAXSIZE
MDU = RNS.Reticulum.MDU
# TODO: Update this
# With an MTU of 500, the maximum of data we can
# send in a single encrypted packet is given by
# the below calculation; 383 bytes.
@@ -325,15 +325,16 @@ class PacketReceipt:
# Creates a new packet receipt from a sent packet
def __init__(self, packet):
self.hash = packet.get_hash()
self.sent = True
self.sent_at = time.time()
self.timeout = Packet.TIMEOUT
self.proved = False
self.status = PacketReceipt.SENT
self.destination = packet.destination
self.callbacks = PacketReceiptCallbacks()
self.concluded_at = None
self.hash = packet.get_hash()
self.truncated_hash = packet.getTruncatedHash()
self.sent = True
self.sent_at = time.time()
self.timeout = Packet.TIMEOUT
self.proved = False
self.status = PacketReceipt.SENT
self.destination = packet.destination
self.callbacks = PacketReceiptCallbacks()
self.concluded_at = None
def get_status(self):
"""
+70 -17
View File
@@ -21,6 +21,8 @@ class Resource:
:param progress_callback: A *callable* with the signature *callback(resource)*. Will be called whenever the resource transfer progress is updated.
:param segment_index: Internal use, ignore.
:param original_hash: Internal use, ignore.
:param is_request: Internal use, ignore.
:param is_response: Internal use, ignore.
"""
WINDOW_FLEXIBILITY = 4
WINDOW_MIN = 1
@@ -119,7 +121,8 @@ class Resource:
resource.link.register_incoming_resource(resource)
RNS.log("Accepting resource advertisement for "+RNS.prettyhexrep(resource.hash), RNS.LOG_DEBUG)
resource.link.callbacks.resource_started(resource)
if resource.link.callbacks.resource_started != None:
resource.link.callbacks.resource_started(resource)
resource.hashmap_update(0, resource.hashmap_raw)
@@ -133,7 +136,7 @@ class Resource:
# Create a resource for transmission to a remote destination
# The data passed can be either a bytes-array or a file opened
# in binary read mode.
def __init__(self, data, link, advertise=True, auto_compress=True, callback=None, progress_callback=None, segment_index = 1, original_hash = None):
def __init__(self, data, link, advertise=True, auto_compress=True, callback=None, progress_callback=None, segment_index = 1, original_hash = None, request_id = None, is_response = False):
data_size = None
resource_data = None
if hasattr(data, "read"):
@@ -188,6 +191,8 @@ class Resource:
self.__watchdog_job_id = 0
self.__progress_callback = progress_callback
self.rtt = None
self.request_id = request_id
self.is_response = is_response
self.receiver_min_consecutive_height = 0
@@ -250,6 +255,7 @@ class Resource:
self.random_hash = RNS.Identity.get_random_hash()[:Resource.RANDOM_HASH_SIZE]
self.hash = RNS.Identity.full_hash(data+self.random_hash)
self.truncated_hash = RNS.Identity.truncated_hash(data+self.random_hash)
self.expected_proof = RNS.Identity.full_hash(data+self.hash)
if original_hash == None:
@@ -739,6 +745,9 @@ class Resource:
self.link.resource_concluded(self)
self.callback(self)
def set_callback(self, callback):
self.callback = callback
def progress_callback(self, callback):
self.__progress_callback = callback
@@ -767,24 +776,63 @@ class Resource:
class ResourceAdvertisement:
HASHMAP_MAX_LEN = 73
HASHMAP_MAX_LEN = 70
COLLISION_GUARD_SIZE = 2*Resource.WINDOW_MAX+HASHMAP_MAX_LEN
def __init__(self, resource=None):
@staticmethod
def is_request(advertisement_packet):
adv = ResourceAdvertisement.unpack(advertisement_packet.plaintext)
if adv.q != None and adv.u:
return True
else:
return False
@staticmethod
def is_response(advertisement_packet):
adv = ResourceAdvertisement.unpack(advertisement_packet.plaintext)
if adv.q != None and adv.p:
return True
else:
return False
@staticmethod
def get_request_id(advertisement_packet):
adv = ResourceAdvertisement.unpack(advertisement_packet.plaintext)
return adv.q
def __init__(self, resource=None, request_id=None, is_response=False):
if resource != None:
self.t = resource.size # Transfer size
self.d = resource.total_size # Total uncompressed data size
self.n = len(resource.parts) # Number of parts
self.h = resource.hash # Resource hash
self.r = resource.random_hash # Resource random hash
self.o = resource.original_hash # First-segment hash
self.m = resource.hashmap # Resource hashmap
self.c = resource.compressed # Compression flag
self.e = resource.encrypted # Encryption flag
self.s = resource.split # Split flag
self.i = resource.segment_index # Segment index
self.l = resource.total_segments # Total segments
self.f = 0x00 | self.s << 2 | self.c << 1 | self.e # Flags
self.t = resource.size # Transfer size
self.d = resource.total_size # Total uncompressed data size
self.n = len(resource.parts) # Number of parts
self.h = resource.hash # Resource hash
self.r = resource.random_hash # Resource random hash
self.o = resource.original_hash # First-segment hash
self.m = resource.hashmap # Resource hashmap
self.c = resource.compressed # Compression flag
self.e = resource.encrypted # Encryption flag
self.s = resource.split # Split flag
self.i = resource.segment_index # Segment index
self.l = resource.total_segments # Total segments
self.q = resource.request_id # ID of associated request
self.u = False # Is request flag
self.p = False # Is response flag
if self.q != None:
if not resource.is_response:
self.u = True
self.p = False
else:
self.u = False
self.p = True
# Flags
self.f = 0x00 | self.p << 4 | self.u << 3 | self.s << 2 | self.c << 1 | self.e
def pack(self, segment=0):
hashmap_start = segment*ResourceAdvertisement.HASHMAP_MAX_LEN
@@ -803,12 +851,14 @@ class ResourceAdvertisement:
"o": self.o, # Original hash
"i": self.i, # Segment index
"l": self.l, # Total segments
"q": self.q, # Request ID
"f": self.f, # Resource flags
"m": hashmap
}
return umsgpack.packb(dictionary)
@staticmethod
def unpack(data):
dictionary = umsgpack.unpackb(data)
@@ -824,8 +874,11 @@ class ResourceAdvertisement:
adv.f = dictionary["f"]
adv.i = dictionary["i"]
adv.l = dictionary["l"]
adv.q = dictionary["q"]
adv.e = True if (adv.f & 0x01) == 0x01 else False
adv.c = True if ((adv.f >> 1) & 0x01) == 0x01 else False
adv.s = True if ((adv.f >> 2) & 0x01) == 0x01 else False
adv.u = True if ((adv.f >> 3) & 0x01) == 0x01 else False
adv.p = True if ((adv.f >> 4) & 0x01) == 0x01 else False
return adv
+73 -19
View File
@@ -217,13 +217,27 @@ class Reticulum:
try:
if ("interface_enabled" in c) and c.as_bool("interface_enabled") == True:
if c["type"] == "UDPInterface":
device = c["device"] if "device" in c else None
port = int(c["port"]) if "port" in c else None
listen_ip = c["listen_ip"] if "listen_ip" in c else None
listen_port = int(c["listen_port"]) if "listen_port" in c else None
forward_ip = c["forward_ip"] if "forward_ip" in c else None
forward_port = int(c["forward_port"]) if "forward_port" in c else None
if port != None:
if listen_port == None:
listen_port = port
if forward_port == None:
forward_port = port
interface = UDPInterface.UDPInterface(
RNS.Transport,
name,
c["listen_ip"],
int(c["listen_port"]),
c["forward_ip"],
int(c["forward_port"])
device,
listen_ip,
listen_port,
forward_ip,
forward_port
)
if "outgoing" in c and c.as_bool("outgoing") == True:
@@ -235,11 +249,20 @@ class Reticulum:
if c["type"] == "TCPServerInterface":
device = c["device"] if "device" in c else None
port = int(c["port"]) if "port" in c else None
listen_ip = c["listen_ip"] if "listen_ip" in c else None
listen_port = int(c["listen_port"]) if "listen_port" in c else None
if port != None:
listen_port = port
interface = TCPInterface.TCPServerInterface(
RNS.Transport,
name,
c["listen_ip"],
int(c["listen_port"])
device,
listen_ip,
listen_port
)
if "outgoing" in c and c.as_bool("outgoing") == True:
@@ -482,10 +505,10 @@ allow_unencrypted = False
# If you enable Transport, your system will route traffic
# for other peers, pass announces and serve path requests.
# Unless you really know what you're doing, this should be
# done only for systems that are suited to act as transport
# nodes, ie. if they are stationary and always-on. This
# directive is optional and can be removed for brevity.
# This should be done for systems that are suited to act
# as transport nodes, ie. if they are stationary and
# always-on. This directive is optional and can be removed
# for brevity.
enable_transport = False
@@ -533,20 +556,37 @@ loglevel = 4
[interfaces]
# This interface enables communication with other
# Reticulum nodes on your local ethernet networks.
# It's enabled by default, and provides basic
# connectivity to other peers in your local ethernet
# broadcast domain. You can modify it to suit your
# needs or turn it off completely.
# local Reticulum nodes over UDP. You can modify it
# to suit your needs or turn it off completely.
# As a minimum, you should probably specify the
# network device you want to communicate on, such
# as eth0 or wlan0.
[[Default UDP Interface]]
type = UDPInterface
interface_enabled = True
outgoing = True
listen_ip = 0.0.0.0
listen_port = 4242
forward_ip = 255.255.255.255
forward_port = 4242
device = eth0
port = 4242
# Assuming the eth0 device has the address
# 10.55.0.72/24, the above configuration would
# be equivalent to the following manual setup.
# Note that we are both listening and forwarding
# to the network segments broadcast address.
# listen_ip = 10.55.0.255
# listen_port = 4242
# forward_ip = 10.55.0.255
# forward_port = 4242
# You can of course also communicate only with
# a single IP address
# listen_ip = 10.55.0.15
# listen_port = 4242
# forward_ip = 10.55.0.16
# forward_port = 4242
# This example demonstrates a TCP server interface.
@@ -557,9 +597,23 @@ loglevel = 4
type = TCPServerInterface
interface_enabled = False
outgoing = True
# This configuration will listen on all IP
# interfaces on port 4242
listen_ip = 0.0.0.0
listen_port = 4242
# Alternatively you can bind to a specific IP
# listen_ip = 10.0.0.88
# listen_port = 4242
# Or a specific network device
# device = eth0
# port = 4242
# To connect to a TCP server interface, you would
# naturally use the TCP client interface. Here's
+14 -3
View File
@@ -49,6 +49,8 @@ class Transport:
receipts = [] # Receipts of all outgoing packets for proof processing
# TODO: "destination_table" should really be renamed to "path_table"
# Notes on memory usage: 1 megabyte of memory can store approximately
# 55.100 path table entries or approximately 22.300 link table entries.
announce_table = {} # A table for storing announces currently waiting to be retransmitted
destination_table = {} # A lookup table containing the next hop to a given destination
reverse_table = {} # A lookup table for storing packet hashes used to return proofs and replies
@@ -375,6 +377,7 @@ class Transport:
# just the relevant interface if the packet has an attached
# interface, or belongs to a link.
else:
stored_hash = False
for interface in Transport.interfaces:
if interface.OUT:
should_transmit = True
@@ -389,6 +392,10 @@ class Transport:
if should_transmit:
RNS.log("Transmitting "+str(len(packet.raw))+" bytes on: "+str(interface), RNS.LOG_EXTREME)
RNS.log("Hash is "+RNS.prettyhexrep(packet.packet_hash), RNS.LOG_EXTREME)
if not stored_hash:
Transport.packet_hashlist.append(packet.packet_hash)
interface.processOutgoing(packet.raw)
sent = True
@@ -396,7 +403,7 @@ class Transport:
packet.sent = True
packet.sent_at = time.time()
# Don't generate receipt if it has been explicitly disabled
# Don't generate receipt if it has been explicitly disabled
if (packet.create_receipt == True and
# Only generate receipts for DATA packets
packet.packet_type == RNS.Packet.DATA and
@@ -1136,13 +1143,17 @@ class Transport:
@staticmethod
def exit_handler():
RNS.log("Saving packet hashlist to storage...", RNS.LOG_VERBOSE)
try:
if not RNS.Reticulum.transport_enabled():
Transport.packet_hashlist = []
else:
RNS.log("Saving packet hashlist to storage...", RNS.LOG_VERBOSE)
packet_hashlist_path = RNS.Reticulum.storagepath+"/packet_hashlist"
file = open(packet_hashlist_path, "wb")
file.write(umsgpack.packb(Transport.packet_hashlist))
file.close()
RNS.log("Done packet hashlist to storage", RNS.LOG_VERBOSE)
except Exception as e:
RNS.log("Could not save packet hashlist to storage, the contained exception was: "+str(e), RNS.LOG_ERROR)
+7 -2
View File
@@ -5,6 +5,8 @@ import time
import random
import threading
from ._version import __version__
from .Reticulum import Reticulum
from .Identity import Identity
from .Link import Link
@@ -12,7 +14,7 @@ from .Transport import Transport
from .Destination import Destination
from .Packet import Packet
from .Packet import PacketReceipt
from .Resource import Resource
from .Resource import Resource, ResourceAdvertisement
modules = glob.glob(os.path.dirname(__file__)+"/*.py")
__all__ = [ os.path.basename(f)[:-3] for f in modules if not f.endswith('__init__.py')]
@@ -60,6 +62,9 @@ def loglevelname(level):
return "Unknown"
def version():
return __version__
def log(msg, level=3, _override_destination = False):
global _always_override_destination
@@ -68,7 +73,7 @@ def log(msg, level=3, _override_destination = False):
logstring = "["+time.strftime(logtimefmt)+"] ["+loglevelname(level)+"] "+msg
logging_lock.acquire()
if (logdest == LOG_STDOUT or _always_override_destination):
if (logdest == LOG_STDOUT or _always_override_destination or _override_destination):
print(logstring)
logging_lock.release()
+1
View File
@@ -0,0 +1 @@
__version__ = "0.2.2"
Binary file not shown.
+1 -1
View File
@@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 205a0b937612ce08d1a58b1cbb471256
config: 966ae7177c1d48c9ee15971994c623b5
tags: 645f666f9bcd5a90fca523b33c5a78b7
+23
View File
@@ -68,6 +68,29 @@ destination, and passing traffic back and forth over the link.
This example can also be found at `<https://github.com/markqvist/Reticulum/blob/master/Examples/Link.py>`_.
.. _example-identify:
Identification
==============
The *Identify* example explores identifying an intiator of a link, once
the link has been established.
.. literalinclude:: ../../Examples/Identify.py
This example can also be found at `<https://github.com/markqvist/Reticulum/blob/master/Examples/Identify.py>`_.
.. _example-request:
Requests & Responses
====================
The *Request* example explores sendig requests and receiving responses.
.. literalinclude:: ../../Examples/Request.py
This example can also be found at `<https://github.com/markqvist/Reticulum/blob/master/Examples/Request.py>`_.
.. _example-filetransfer:
Filetransfer
+4 -2
View File
@@ -2,7 +2,9 @@
What is Reticulum?
******************
Reticulum is a cryptography-based networking stack for wide-area networks built on readily available hardware, and can operate even with very high latency and extremely low bandwidth. Reticulum allows you to build very wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more.
Reticulum is a cryptography-based networking stack for wide-area networks built on readily available hardware, and can operate even with very high latency and extremely low bandwidth.
Reticulum allows you to build very wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more.
Reticulum is a complete networking stack, and does not use IP or higher layers, although it is easy to utilise IP (with TCP or UDP) as the underlying carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the Internet or private IP networks. Reticulum is built directly on cryptographic principles, allowing resilience and stable functionality in open and trustless networks.
@@ -43,7 +45,7 @@ What does Reticulum Offer?
* A variety of supported interface types
* An intuitive and easy-to-use API
* An intuitive and developer-friendly API
* Reliable and efficient transfer of arbritrary amounts of data
+1 -1
View File
@@ -1,6 +1,6 @@
var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '0.2.1 beta',
VERSION: '0.2.2 beta',
LANGUAGE: 'None',
COLLAPSE_INDEX: false,
BUILDER: 'html',
+619 -5
View File
@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Examples &#8212; Reticulum Network Stack 0.2.1 beta documentation</title>
<title>Examples &#8212; Reticulum Network Stack 0.2.2 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -27,7 +27,7 @@
<li class="right" >
<a href="reference.html" title="API Reference"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Examples</a></li>
</ul>
</div>
@@ -1044,6 +1044,614 @@ destination, and passing traffic back and forth over the link.</p>
</div>
<p>This example can also be found at <a class="reference external" href="https://github.com/markqvist/Reticulum/blob/master/Examples/Link.py">https://github.com/markqvist/Reticulum/blob/master/Examples/Link.py</a>.</p>
</div>
<div class="section" id="example-identify">
<span id="identification"></span><h2>Identification<a class="headerlink" href="#example-identify" title="Permalink to this headline"></a></h2>
<p>The <em>Identify</em> example explores identifying an intiator of a link, once
the link has been established.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1">##########################################################</span>
<span class="c1"># This RNS example demonstrates how to set up a link to #</span>
<span class="c1"># a destination, and identify the initiator to it&#39;s peer #</span>
<span class="c1">##########################################################</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">argparse</span>
<span class="kn">import</span> <span class="nn">RNS</span>
<span class="c1"># Let&#39;s define an app name. We&#39;ll use this for all</span>
<span class="c1"># destinations we create. Since this echo example</span>
<span class="c1"># is part of a range of example utilities, we&#39;ll put</span>
<span class="c1"># them all within the app namespace &quot;example_utilities&quot;</span>
<span class="n">APP_NAME</span> <span class="o">=</span> <span class="s2">&quot;example_utilities&quot;</span>
<span class="c1">##########################################################</span>
<span class="c1">#### Server Part #########################################</span>
<span class="c1">##########################################################</span>
<span class="c1"># A reference to the latest client link that connected</span>
<span class="n">latest_client_link</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># This initialisation is executed when the users chooses</span>
<span class="c1"># to run as a server</span>
<span class="k">def</span> <span class="nf">server</span><span class="p">(</span><span class="n">configpath</span><span class="p">):</span>
<span class="c1"># We must first initialise Reticulum</span>
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
<span class="c1"># Randomly create a new identity for our link example</span>
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="p">()</span>
<span class="c1"># We create a destination that clients can connect to. We</span>
<span class="c1"># want clients to create links to this destination, so we</span>
<span class="c1"># need to create a &quot;single&quot; destination type.</span>
<span class="n">server_destination</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="p">(</span>
<span class="n">server_identity</span><span class="p">,</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">IN</span><span class="p">,</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">SINGLE</span><span class="p">,</span>
<span class="n">APP_NAME</span><span class="p">,</span>
<span class="s2">&quot;identifyexample&quot;</span>
<span class="p">)</span>
<span class="c1"># We configure a function that will get called every time</span>
<span class="c1"># a new client creates a link to this destination.</span>
<span class="n">server_destination</span><span class="o">.</span><span class="n">set_link_established_callback</span><span class="p">(</span><span class="n">client_connected</span><span class="p">)</span>
<span class="c1"># Everything&#39;s ready!</span>
<span class="c1"># Let&#39;s Wait for client requests or user input</span>
<span class="n">server_loop</span><span class="p">(</span><span class="n">server_destination</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">server_loop</span><span class="p">(</span><span class="n">destination</span><span class="p">):</span>
<span class="c1"># Let the user know that everything is ready</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span>
<span class="s2">&quot;Link identification example &quot;</span><span class="o">+</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">destination</span><span class="o">.</span><span class="n">hash</span><span class="p">)</span><span class="o">+</span>
<span class="s2">&quot; running, waiting for a connection.&quot;</span>
<span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Hit enter to manually send an announce (Ctrl-C to quit)&quot;</span><span class="p">)</span>
<span class="c1"># We enter a loop that runs until the users exits.</span>
<span class="c1"># If the user hits enter, we will announce our server</span>
<span class="c1"># destination on the network, which will let clients</span>
<span class="c1"># know how to create messages directed towards it.</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="n">entered</span> <span class="o">=</span> <span class="nb">input</span><span class="p">()</span>
<span class="n">destination</span><span class="o">.</span><span class="n">announce</span><span class="p">()</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Sent announce from &quot;</span><span class="o">+</span><span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">destination</span><span class="o">.</span><span class="n">hash</span><span class="p">))</span>
<span class="c1"># When a client establishes a link to our server</span>
<span class="c1"># destination, this function will be called with</span>
<span class="c1"># a reference to the link.</span>
<span class="k">def</span> <span class="nf">client_connected</span><span class="p">(</span><span class="n">link</span><span class="p">):</span>
<span class="k">global</span> <span class="n">latest_client_link</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Client connected&quot;</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">set_link_closed_callback</span><span class="p">(</span><span class="n">client_disconnected</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">set_packet_callback</span><span class="p">(</span><span class="n">server_packet_received</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">set_remote_identified_callback</span><span class="p">(</span><span class="n">remote_identified</span><span class="p">)</span>
<span class="n">latest_client_link</span> <span class="o">=</span> <span class="n">link</span>
<span class="k">def</span> <span class="nf">client_disconnected</span><span class="p">(</span><span class="n">link</span><span class="p">):</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Client disconnected&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">remote_identified</span><span class="p">(</span><span class="n">identity</span><span class="p">):</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Remote identified as: &quot;</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">identity</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">server_packet_received</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">packet</span><span class="p">):</span>
<span class="k">global</span> <span class="n">latest_client_link</span>
<span class="c1"># Get the originating identity for display</span>
<span class="n">remote_peer</span> <span class="o">=</span> <span class="s2">&quot;unidentified peer&quot;</span>
<span class="k">if</span> <span class="n">packet</span><span class="o">.</span><span class="n">link</span><span class="o">.</span><span class="n">get_remote_identity</span><span class="p">()</span> <span class="o">!=</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">remote_peer</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">packet</span><span class="o">.</span><span class="n">link</span><span class="o">.</span><span class="n">get_remote_identity</span><span class="p">())</span>
<span class="c1"># When data is received over any active link,</span>
<span class="c1"># it will all be directed to the last client</span>
<span class="c1"># that connected.</span>
<span class="n">text</span> <span class="o">=</span> <span class="n">message</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;utf-8&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Received data from &quot;</span><span class="o">+</span><span class="n">remote_peer</span><span class="o">+</span><span class="s2">&quot;: &quot;</span><span class="o">+</span><span class="n">text</span><span class="p">)</span>
<span class="n">reply_text</span> <span class="o">=</span> <span class="s2">&quot;I received </span><span class="se">\&quot;</span><span class="s2">&quot;</span><span class="o">+</span><span class="n">text</span><span class="o">+</span><span class="s2">&quot;</span><span class="se">\&quot;</span><span class="s2"> over the link from &quot;</span><span class="o">+</span><span class="n">remote_peer</span>
<span class="n">reply_data</span> <span class="o">=</span> <span class="n">reply_text</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;utf-8&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Packet</span><span class="p">(</span><span class="n">latest_client_link</span><span class="p">,</span> <span class="n">reply_data</span><span class="p">)</span><span class="o">.</span><span class="n">send</span><span class="p">()</span>
<span class="c1">##########################################################</span>
<span class="c1">#### Client Part #########################################</span>
<span class="c1">##########################################################</span>
<span class="c1"># A reference to the server link</span>
<span class="n">server_link</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># A reference to the client identity</span>
<span class="n">client_identity</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># This initialisation is executed when the users chooses</span>
<span class="c1"># to run as a client</span>
<span class="k">def</span> <span class="nf">client</span><span class="p">(</span><span class="n">destination_hexhash</span><span class="p">,</span> <span class="n">configpath</span><span class="p">):</span>
<span class="k">global</span> <span class="n">client_identity</span>
<span class="c1"># We need a binary representation of the destination</span>
<span class="c1"># hash that was entered on the command line</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">destination_hexhash</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">20</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;Destination length is invalid, must be 20 hexadecimal characters (10 bytes)&quot;</span><span class="p">)</span>
<span class="n">destination_hash</span> <span class="o">=</span> <span class="nb">bytes</span><span class="o">.</span><span class="n">fromhex</span><span class="p">(</span><span class="n">destination_hexhash</span><span class="p">)</span>
<span class="k">except</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Invalid destination entered. Check your input!</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="c1"># We must first initialise Reticulum</span>
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
<span class="c1"># Create a new client identity</span>
<span class="n">client_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="p">()</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span>
<span class="s2">&quot;Client created new identity &quot;</span><span class="o">+</span>
<span class="nb">str</span><span class="p">(</span><span class="n">client_identity</span><span class="p">)</span>
<span class="p">)</span>
<span class="c1"># Check if we know a path to the destination</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Transport</span><span class="o">.</span><span class="n">has_path</span><span class="p">(</span><span class="n">destination_hash</span><span class="p">):</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Destination is not yet known. Requesting path and waiting for announce to arrive...&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Transport</span><span class="o">.</span><span class="n">request_path</span><span class="p">(</span><span class="n">destination_hash</span><span class="p">)</span>
<span class="k">while</span> <span class="ow">not</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Transport</span><span class="o">.</span><span class="n">has_path</span><span class="p">(</span><span class="n">destination_hash</span><span class="p">):</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
<span class="c1"># Recall the server identity</span>
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="o">.</span><span class="n">recall</span><span class="p">(</span><span class="n">destination_hash</span><span class="p">)</span>
<span class="c1"># Inform the user that we&#39;ll begin connecting</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Establishing link with server...&quot;</span><span class="p">)</span>
<span class="c1"># When the server identity is known, we set</span>
<span class="c1"># up a destination</span>
<span class="n">server_destination</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="p">(</span>
<span class="n">server_identity</span><span class="p">,</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">OUT</span><span class="p">,</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">SINGLE</span><span class="p">,</span>
<span class="n">APP_NAME</span><span class="p">,</span>
<span class="s2">&quot;identifyexample&quot;</span>
<span class="p">)</span>
<span class="c1"># And create a link</span>
<span class="n">link</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Link</span><span class="p">(</span><span class="n">server_destination</span><span class="p">)</span>
<span class="c1"># We set a callback that will get executed</span>
<span class="c1"># every time a packet is received over the</span>
<span class="c1"># link</span>
<span class="n">link</span><span class="o">.</span><span class="n">set_packet_callback</span><span class="p">(</span><span class="n">client_packet_received</span><span class="p">)</span>
<span class="c1"># We&#39;ll also set up functions to inform the</span>
<span class="c1"># user when the link is established or closed</span>
<span class="n">link</span><span class="o">.</span><span class="n">set_link_established_callback</span><span class="p">(</span><span class="n">link_established</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">set_link_closed_callback</span><span class="p">(</span><span class="n">link_closed</span><span class="p">)</span>
<span class="c1"># Everything is set up, so let&#39;s enter a loop</span>
<span class="c1"># for the user to interact with the example</span>
<span class="n">client_loop</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">client_loop</span><span class="p">():</span>
<span class="k">global</span> <span class="n">server_link</span>
<span class="c1"># Wait for the link to become active</span>
<span class="k">while</span> <span class="ow">not</span> <span class="n">server_link</span><span class="p">:</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
<span class="n">should_quit</span> <span class="o">=</span> <span class="kc">False</span>
<span class="k">while</span> <span class="ow">not</span> <span class="n">should_quit</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&gt; &quot;</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s2">&quot; &quot;</span><span class="p">)</span>
<span class="n">text</span> <span class="o">=</span> <span class="nb">input</span><span class="p">()</span>
<span class="c1"># Check if we should quit the example</span>
<span class="k">if</span> <span class="n">text</span> <span class="o">==</span> <span class="s2">&quot;quit&quot;</span> <span class="ow">or</span> <span class="n">text</span> <span class="o">==</span> <span class="s2">&quot;q&quot;</span> <span class="ow">or</span> <span class="n">text</span> <span class="o">==</span> <span class="s2">&quot;exit&quot;</span><span class="p">:</span>
<span class="n">should_quit</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">server_link</span><span class="o">.</span><span class="n">teardown</span><span class="p">()</span>
<span class="c1"># If not, send the entered text over the link</span>
<span class="k">if</span> <span class="n">text</span> <span class="o">!=</span> <span class="s2">&quot;&quot;</span><span class="p">:</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">text</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">&quot;utf-8&quot;</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Link</span><span class="o">.</span><span class="n">MDU</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Packet</span><span class="p">(</span><span class="n">server_link</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span><span class="o">.</span><span class="n">send</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span>
<span class="s2">&quot;Cannot send this packet, the data size of &quot;</span><span class="o">+</span>
<span class="nb">str</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">))</span><span class="o">+</span><span class="s2">&quot; bytes exceeds the link packet MDU of &quot;</span><span class="o">+</span>
<span class="nb">str</span><span class="p">(</span><span class="n">RNS</span><span class="o">.</span><span class="n">Link</span><span class="o">.</span><span class="n">MDU</span><span class="p">)</span><span class="o">+</span><span class="s2">&quot; bytes&quot;</span><span class="p">,</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">LOG_ERROR</span>
<span class="p">)</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Error while sending data over the link: &quot;</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">))</span>
<span class="n">should_quit</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">server_link</span><span class="o">.</span><span class="n">teardown</span><span class="p">()</span>
<span class="c1"># This function is called when a link</span>
<span class="c1"># has been established with the server</span>
<span class="k">def</span> <span class="nf">link_established</span><span class="p">(</span><span class="n">link</span><span class="p">):</span>
<span class="c1"># We store a reference to the link</span>
<span class="c1"># instance for later use</span>
<span class="k">global</span> <span class="n">server_link</span><span class="p">,</span> <span class="n">client_identity</span>
<span class="n">server_link</span> <span class="o">=</span> <span class="n">link</span>
<span class="c1"># Inform the user that the server is</span>
<span class="c1"># connected</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Link established with server, identifying to remote peer...&quot;</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">identify</span><span class="p">(</span><span class="n">client_identity</span><span class="p">)</span>
<span class="c1"># When a link is closed, we&#39;ll inform the</span>
<span class="c1"># user, and exit the program</span>
<span class="k">def</span> <span class="nf">link_closed</span><span class="p">(</span><span class="n">link</span><span class="p">):</span>
<span class="k">if</span> <span class="n">link</span><span class="o">.</span><span class="n">teardown_reason</span> <span class="o">==</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Link</span><span class="o">.</span><span class="n">TIMEOUT</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;The link timed out, exiting now&quot;</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">link</span><span class="o">.</span><span class="n">teardown_reason</span> <span class="o">==</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Link</span><span class="o">.</span><span class="n">DESTINATION_CLOSED</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;The link was closed by the server, exiting now&quot;</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Link closed, exiting now&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="o">.</span><span class="n">exit_handler</span><span class="p">()</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1.5</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">_exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1"># When a packet is received over the link, we</span>
<span class="c1"># simply print out the data.</span>
<span class="k">def</span> <span class="nf">client_packet_received</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">packet</span><span class="p">):</span>
<span class="n">text</span> <span class="o">=</span> <span class="n">message</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;utf-8&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Received data on the link: &quot;</span><span class="o">+</span><span class="n">text</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&gt; &quot;</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s2">&quot; &quot;</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span>
<span class="c1">##########################################################</span>
<span class="c1">#### Program Startup #####################################</span>
<span class="c1">##########################################################</span>
<span class="c1"># This part of the program runs at startup,</span>
<span class="c1"># and parses input of from the user, and then</span>
<span class="c1"># starts up the desired program mode.</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="o">.</span><span class="n">ArgumentParser</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&quot;Simple link example&quot;</span><span class="p">)</span>
<span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span>
<span class="s2">&quot;-s&quot;</span><span class="p">,</span>
<span class="s2">&quot;--server&quot;</span><span class="p">,</span>
<span class="n">action</span><span class="o">=</span><span class="s2">&quot;store_true&quot;</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s2">&quot;wait for incoming link requests from clients&quot;</span>
<span class="p">)</span>
<span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span>
<span class="s2">&quot;--config&quot;</span><span class="p">,</span>
<span class="n">action</span><span class="o">=</span><span class="s2">&quot;store&quot;</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s2">&quot;path to alternative Reticulum config directory&quot;</span><span class="p">,</span>
<span class="nb">type</span><span class="o">=</span><span class="nb">str</span>
<span class="p">)</span>
<span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span>
<span class="s2">&quot;destination&quot;</span><span class="p">,</span>
<span class="n">nargs</span><span class="o">=</span><span class="s2">&quot;?&quot;</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s2">&quot;hexadecimal hash of the server destination&quot;</span><span class="p">,</span>
<span class="nb">type</span><span class="o">=</span><span class="nb">str</span>
<span class="p">)</span>
<span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span>
<span class="k">if</span> <span class="n">args</span><span class="o">.</span><span class="n">config</span><span class="p">:</span>
<span class="n">configarg</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">config</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">configarg</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="n">args</span><span class="o">.</span><span class="n">server</span><span class="p">:</span>
<span class="n">server</span><span class="p">(</span><span class="n">configarg</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="p">(</span><span class="n">args</span><span class="o">.</span><span class="n">destination</span> <span class="o">==</span> <span class="kc">None</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">parser</span><span class="o">.</span><span class="n">print_help</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">client</span><span class="p">(</span><span class="n">args</span><span class="o">.</span><span class="n">destination</span><span class="p">,</span> <span class="n">configarg</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
</pre></div>
</div>
<p>This example can also be found at <a class="reference external" href="https://github.com/markqvist/Reticulum/blob/master/Examples/Identify.py">https://github.com/markqvist/Reticulum/blob/master/Examples/Identify.py</a>.</p>
</div>
<div class="section" id="requests-responses">
<span id="example-request"></span><h2>Requests &amp; Responses<a class="headerlink" href="#requests-responses" title="Permalink to this headline"></a></h2>
<p>The <em>Request</em> example explores sendig requests and receiving responses.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1">##########################################################</span>
<span class="c1"># This RNS example demonstrates how to set perform #</span>
<span class="c1"># requests and receive responses over a link. #</span>
<span class="c1">##########################################################</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">random</span>
<span class="kn">import</span> <span class="nn">argparse</span>
<span class="kn">import</span> <span class="nn">RNS</span>
<span class="c1"># Let&#39;s define an app name. We&#39;ll use this for all</span>
<span class="c1"># destinations we create. Since this echo example</span>
<span class="c1"># is part of a range of example utilities, we&#39;ll put</span>
<span class="c1"># them all within the app namespace &quot;example_utilities&quot;</span>
<span class="n">APP_NAME</span> <span class="o">=</span> <span class="s2">&quot;example_utilities&quot;</span>
<span class="c1">##########################################################</span>
<span class="c1">#### Server Part #########################################</span>
<span class="c1">##########################################################</span>
<span class="c1"># A reference to the latest client link that connected</span>
<span class="n">latest_client_link</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">def</span> <span class="nf">random_text_generator</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">request_id</span><span class="p">,</span> <span class="n">remote_identity</span><span class="p">,</span> <span class="n">requested_at</span><span class="p">):</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Generating response to request &quot;</span><span class="o">+</span><span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">request_id</span><span class="p">))</span>
<span class="n">texts</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&quot;They looked up&quot;</span><span class="p">,</span> <span class="s2">&quot;On each full moon&quot;</span><span class="p">,</span> <span class="s2">&quot;Becky was upset&quot;</span><span class="p">,</span> <span class="s2">&quot;Ill stay away from it&quot;</span><span class="p">,</span> <span class="s2">&quot;The pet shop stocks everything&quot;</span><span class="p">]</span>
<span class="k">return</span> <span class="n">texts</span><span class="p">[</span><span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">texts</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">)]</span>
<span class="c1"># This initialisation is executed when the users chooses</span>
<span class="c1"># to run as a server</span>
<span class="k">def</span> <span class="nf">server</span><span class="p">(</span><span class="n">configpath</span><span class="p">):</span>
<span class="c1"># We must first initialise Reticulum</span>
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
<span class="c1"># Randomly create a new identity for our link example</span>
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="p">()</span>
<span class="c1"># We create a destination that clients can connect to. We</span>
<span class="c1"># want clients to create links to this destination, so we</span>
<span class="c1"># need to create a &quot;single&quot; destination type.</span>
<span class="n">server_destination</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="p">(</span>
<span class="n">server_identity</span><span class="p">,</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">IN</span><span class="p">,</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">SINGLE</span><span class="p">,</span>
<span class="n">APP_NAME</span><span class="p">,</span>
<span class="s2">&quot;requestexample&quot;</span>
<span class="p">)</span>
<span class="c1"># We configure a function that will get called every time</span>
<span class="c1"># a new client creates a link to this destination.</span>
<span class="n">server_destination</span><span class="o">.</span><span class="n">set_link_established_callback</span><span class="p">(</span><span class="n">client_connected</span><span class="p">)</span>
<span class="c1"># We register a request handler for handling incoming</span>
<span class="c1"># requests over any established links.</span>
<span class="n">server_destination</span><span class="o">.</span><span class="n">register_request_handler</span><span class="p">(</span>
<span class="s2">&quot;/random/text&quot;</span><span class="p">,</span>
<span class="n">response_generator</span> <span class="o">=</span> <span class="n">random_text_generator</span><span class="p">,</span>
<span class="n">allow</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">ALLOW_ALL</span>
<span class="p">)</span>
<span class="c1"># Everything&#39;s ready!</span>
<span class="c1"># Let&#39;s Wait for client requests or user input</span>
<span class="n">server_loop</span><span class="p">(</span><span class="n">server_destination</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">server_loop</span><span class="p">(</span><span class="n">destination</span><span class="p">):</span>
<span class="c1"># Let the user know that everything is ready</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span>
<span class="s2">&quot;Request example &quot;</span><span class="o">+</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">destination</span><span class="o">.</span><span class="n">hash</span><span class="p">)</span><span class="o">+</span>
<span class="s2">&quot; running, waiting for a connection.&quot;</span>
<span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Hit enter to manually send an announce (Ctrl-C to quit)&quot;</span><span class="p">)</span>
<span class="c1"># We enter a loop that runs until the users exits.</span>
<span class="c1"># If the user hits enter, we will announce our server</span>
<span class="c1"># destination on the network, which will let clients</span>
<span class="c1"># know how to create messages directed towards it.</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="n">entered</span> <span class="o">=</span> <span class="nb">input</span><span class="p">()</span>
<span class="n">destination</span><span class="o">.</span><span class="n">announce</span><span class="p">()</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Sent announce from &quot;</span><span class="o">+</span><span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">destination</span><span class="o">.</span><span class="n">hash</span><span class="p">))</span>
<span class="c1"># When a client establishes a link to our server</span>
<span class="c1"># destination, this function will be called with</span>
<span class="c1"># a reference to the link.</span>
<span class="k">def</span> <span class="nf">client_connected</span><span class="p">(</span><span class="n">link</span><span class="p">):</span>
<span class="k">global</span> <span class="n">latest_client_link</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Client connected&quot;</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">set_link_closed_callback</span><span class="p">(</span><span class="n">client_disconnected</span><span class="p">)</span>
<span class="n">latest_client_link</span> <span class="o">=</span> <span class="n">link</span>
<span class="k">def</span> <span class="nf">client_disconnected</span><span class="p">(</span><span class="n">link</span><span class="p">):</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Client disconnected&quot;</span><span class="p">)</span>
<span class="c1">##########################################################</span>
<span class="c1">#### Client Part #########################################</span>
<span class="c1">##########################################################</span>
<span class="c1"># A reference to the server link</span>
<span class="n">server_link</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># This initialisation is executed when the users chooses</span>
<span class="c1"># to run as a client</span>
<span class="k">def</span> <span class="nf">client</span><span class="p">(</span><span class="n">destination_hexhash</span><span class="p">,</span> <span class="n">configpath</span><span class="p">):</span>
<span class="c1"># We need a binary representation of the destination</span>
<span class="c1"># hash that was entered on the command line</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">destination_hexhash</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">20</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;Destination length is invalid, must be 20 hexadecimal characters (10 bytes)&quot;</span><span class="p">)</span>
<span class="n">destination_hash</span> <span class="o">=</span> <span class="nb">bytes</span><span class="o">.</span><span class="n">fromhex</span><span class="p">(</span><span class="n">destination_hexhash</span><span class="p">)</span>
<span class="k">except</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Invalid destination entered. Check your input!</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
<span class="c1"># We must first initialise Reticulum</span>
<span class="n">reticulum</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="p">(</span><span class="n">configpath</span><span class="p">)</span>
<span class="c1"># Check if we know a path to the destination</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Transport</span><span class="o">.</span><span class="n">has_path</span><span class="p">(</span><span class="n">destination_hash</span><span class="p">):</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Destination is not yet known. Requesting path and waiting for announce to arrive...&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Transport</span><span class="o">.</span><span class="n">request_path</span><span class="p">(</span><span class="n">destination_hash</span><span class="p">)</span>
<span class="k">while</span> <span class="ow">not</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Transport</span><span class="o">.</span><span class="n">has_path</span><span class="p">(</span><span class="n">destination_hash</span><span class="p">):</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
<span class="c1"># Recall the server identity</span>
<span class="n">server_identity</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Identity</span><span class="o">.</span><span class="n">recall</span><span class="p">(</span><span class="n">destination_hash</span><span class="p">)</span>
<span class="c1"># Inform the user that we&#39;ll begin connecting</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Establishing link with server...&quot;</span><span class="p">)</span>
<span class="c1"># When the server identity is known, we set</span>
<span class="c1"># up a destination</span>
<span class="n">server_destination</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="p">(</span>
<span class="n">server_identity</span><span class="p">,</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">OUT</span><span class="p">,</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Destination</span><span class="o">.</span><span class="n">SINGLE</span><span class="p">,</span>
<span class="n">APP_NAME</span><span class="p">,</span>
<span class="s2">&quot;requestexample&quot;</span>
<span class="p">)</span>
<span class="c1"># And create a link</span>
<span class="n">link</span> <span class="o">=</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Link</span><span class="p">(</span><span class="n">server_destination</span><span class="p">)</span>
<span class="c1"># We&#39;ll set up functions to inform the</span>
<span class="c1"># user when the link is established or closed</span>
<span class="n">link</span><span class="o">.</span><span class="n">set_link_established_callback</span><span class="p">(</span><span class="n">link_established</span><span class="p">)</span>
<span class="n">link</span><span class="o">.</span><span class="n">set_link_closed_callback</span><span class="p">(</span><span class="n">link_closed</span><span class="p">)</span>
<span class="c1"># Everything is set up, so let&#39;s enter a loop</span>
<span class="c1"># for the user to interact with the example</span>
<span class="n">client_loop</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">client_loop</span><span class="p">():</span>
<span class="k">global</span> <span class="n">server_link</span>
<span class="c1"># Wait for the link to become active</span>
<span class="k">while</span> <span class="ow">not</span> <span class="n">server_link</span><span class="p">:</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
<span class="n">should_quit</span> <span class="o">=</span> <span class="kc">False</span>
<span class="k">while</span> <span class="ow">not</span> <span class="n">should_quit</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&gt; &quot;</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s2">&quot; &quot;</span><span class="p">)</span>
<span class="n">text</span> <span class="o">=</span> <span class="nb">input</span><span class="p">()</span>
<span class="c1"># Check if we should quit the example</span>
<span class="k">if</span> <span class="n">text</span> <span class="o">==</span> <span class="s2">&quot;quit&quot;</span> <span class="ow">or</span> <span class="n">text</span> <span class="o">==</span> <span class="s2">&quot;q&quot;</span> <span class="ow">or</span> <span class="n">text</span> <span class="o">==</span> <span class="s2">&quot;exit&quot;</span><span class="p">:</span>
<span class="n">should_quit</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">server_link</span><span class="o">.</span><span class="n">teardown</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">server_link</span><span class="o">.</span><span class="n">request</span><span class="p">(</span>
<span class="s2">&quot;/random/text&quot;</span><span class="p">,</span>
<span class="n">data</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span>
<span class="n">response_callback</span> <span class="o">=</span> <span class="n">got_response</span><span class="p">,</span>
<span class="n">failed_callback</span> <span class="o">=</span> <span class="n">request_failed</span>
<span class="p">)</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Error while sending request over the link: &quot;</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">))</span>
<span class="n">should_quit</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">server_link</span><span class="o">.</span><span class="n">teardown</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">got_response</span><span class="p">(</span><span class="n">request_receipt</span><span class="p">):</span>
<span class="n">request_id</span> <span class="o">=</span> <span class="n">request_receipt</span><span class="o">.</span><span class="n">request_id</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">request_receipt</span><span class="o">.</span><span class="n">response</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Got response for request &quot;</span><span class="o">+</span><span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">request_id</span><span class="p">)</span><span class="o">+</span><span class="s2">&quot;: &quot;</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">response</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">request_received</span><span class="p">(</span><span class="n">request_receipt</span><span class="p">):</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;The request &quot;</span><span class="o">+</span><span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">request_receipt</span><span class="o">.</span><span class="n">request_id</span><span class="p">)</span><span class="o">+</span><span class="s2">&quot; was received by the remote peer.&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">request_failed</span><span class="p">(</span><span class="n">request_receipt</span><span class="p">):</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;The request &quot;</span><span class="o">+</span><span class="n">RNS</span><span class="o">.</span><span class="n">prettyhexrep</span><span class="p">(</span><span class="n">request_receipt</span><span class="o">.</span><span class="n">request_id</span><span class="p">)</span><span class="o">+</span><span class="s2">&quot; failed.&quot;</span><span class="p">)</span>
<span class="c1"># This function is called when a link</span>
<span class="c1"># has been established with the server</span>
<span class="k">def</span> <span class="nf">link_established</span><span class="p">(</span><span class="n">link</span><span class="p">):</span>
<span class="c1"># We store a reference to the link</span>
<span class="c1"># instance for later use</span>
<span class="k">global</span> <span class="n">server_link</span>
<span class="n">server_link</span> <span class="o">=</span> <span class="n">link</span>
<span class="c1"># Inform the user that the server is</span>
<span class="c1"># connected</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Link established with server, hit enter to perform a request, or type in </span><span class="se">\&quot;</span><span class="s2">quit</span><span class="se">\&quot;</span><span class="s2"> to quit&quot;</span><span class="p">)</span>
<span class="c1"># When a link is closed, we&#39;ll inform the</span>
<span class="c1"># user, and exit the program</span>
<span class="k">def</span> <span class="nf">link_closed</span><span class="p">(</span><span class="n">link</span><span class="p">):</span>
<span class="k">if</span> <span class="n">link</span><span class="o">.</span><span class="n">teardown_reason</span> <span class="o">==</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Link</span><span class="o">.</span><span class="n">TIMEOUT</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;The link timed out, exiting now&quot;</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">link</span><span class="o">.</span><span class="n">teardown_reason</span> <span class="o">==</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Link</span><span class="o">.</span><span class="n">DESTINATION_CLOSED</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;The link was closed by the server, exiting now&quot;</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="s2">&quot;Link closed, exiting now&quot;</span><span class="p">)</span>
<span class="n">RNS</span><span class="o">.</span><span class="n">Reticulum</span><span class="o">.</span><span class="n">exit_handler</span><span class="p">()</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1.5</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">_exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1">##########################################################</span>
<span class="c1">#### Program Startup #####################################</span>
<span class="c1">##########################################################</span>
<span class="c1"># This part of the program runs at startup,</span>
<span class="c1"># and parses input of from the user, and then</span>
<span class="c1"># starts up the desired program mode.</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="o">.</span><span class="n">ArgumentParser</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&quot;Simple request/response example&quot;</span><span class="p">)</span>
<span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span>
<span class="s2">&quot;-s&quot;</span><span class="p">,</span>
<span class="s2">&quot;--server&quot;</span><span class="p">,</span>
<span class="n">action</span><span class="o">=</span><span class="s2">&quot;store_true&quot;</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s2">&quot;wait for incoming requests from clients&quot;</span>
<span class="p">)</span>
<span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span>
<span class="s2">&quot;--config&quot;</span><span class="p">,</span>
<span class="n">action</span><span class="o">=</span><span class="s2">&quot;store&quot;</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s2">&quot;path to alternative Reticulum config directory&quot;</span><span class="p">,</span>
<span class="nb">type</span><span class="o">=</span><span class="nb">str</span>
<span class="p">)</span>
<span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span>
<span class="s2">&quot;destination&quot;</span><span class="p">,</span>
<span class="n">nargs</span><span class="o">=</span><span class="s2">&quot;?&quot;</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s2">&quot;hexadecimal hash of the server destination&quot;</span><span class="p">,</span>
<span class="nb">type</span><span class="o">=</span><span class="nb">str</span>
<span class="p">)</span>
<span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span>
<span class="k">if</span> <span class="n">args</span><span class="o">.</span><span class="n">config</span><span class="p">:</span>
<span class="n">configarg</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">config</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">configarg</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="n">args</span><span class="o">.</span><span class="n">server</span><span class="p">:</span>
<span class="n">server</span><span class="p">(</span><span class="n">configarg</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="p">(</span><span class="n">args</span><span class="o">.</span><span class="n">destination</span> <span class="o">==</span> <span class="kc">None</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">parser</span><span class="o">.</span><span class="n">print_help</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">client</span><span class="p">(</span><span class="n">args</span><span class="o">.</span><span class="n">destination</span><span class="p">,</span> <span class="n">configarg</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="n">exit</span><span class="p">()</span>
</pre></div>
</div>
<p>This example can also be found at <a class="reference external" href="https://github.com/markqvist/Reticulum/blob/master/Examples/Request.py">https://github.com/markqvist/Reticulum/blob/master/Examples/Request.py</a>.</p>
</div>
<div class="section" id="filetransfer">
<span id="example-filetransfer"></span><h2>Filetransfer<a class="headerlink" href="#filetransfer" title="Permalink to this headline"></a></h2>
<p>The <em>Filetransfer</em> example implements a basic file-server program that
@@ -1186,7 +1794,12 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
<span class="k">def</span> <span class="nf">client_request</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">packet</span><span class="p">):</span>
<span class="k">global</span> <span class="n">serve_path</span>
<span class="n">filename</span> <span class="o">=</span> <span class="n">message</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;utf-8&quot;</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">filename</span> <span class="o">=</span> <span class="n">message</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&quot;utf-8&quot;</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">filename</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">if</span> <span class="n">filename</span> <span class="ow">in</span> <span class="n">list_files</span><span class="p">():</span>
<span class="k">try</span><span class="p">:</span>
<span class="c1"># If we have the requested file, we&#39;ll</span>
@@ -1548,7 +2161,6 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
<span class="n">saved_filename</span> <span class="o">=</span> <span class="n">current_filename</span>
<span class="k">if</span> <span class="n">resource</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="n">RNS</span><span class="o">.</span><span class="n">Resource</span><span class="o">.</span><span class="n">COMPLETE</span><span class="p">:</span>
<span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isfile</span><span class="p">(</span><span class="n">saved_filename</span><span class="p">):</span>
@@ -1667,6 +2279,8 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
<li><a class="reference internal" href="#broadcast">Broadcast</a></li>
<li><a class="reference internal" href="#echo">Echo</a></li>
<li><a class="reference internal" href="#link">Link</a></li>
<li><a class="reference internal" href="#example-identify">Identification</a></li>
<li><a class="reference internal" href="#requests-responses">Requests &amp; Responses</a></li>
<li><a class="reference internal" href="#filetransfer">Filetransfer</a></li>
</ul>
</li>
@@ -1705,7 +2319,7 @@ interface to efficiently pass files of any size over a Reticulum <a class="refer
<li class="right" >
<a href="reference.html" title="API Reference"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Examples</a></li>
</ul>
</div>
+17 -5
View File
@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Index &#8212; Reticulum Network Stack 0.2.1 beta documentation</title>
<title>Index &#8212; Reticulum Network Stack 0.2.2 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -23,7 +23,7 @@
<li class="right" style="margin-right: 10px">
<a href="#" title="General Index"
accesskey="I">index</a></li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Index</a></li>
</ul>
</div>
@@ -103,6 +103,8 @@
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Transport.deregister_announce_handler">deregister_announce_handler() (RNS.Transport static method)</a>
</li>
<li><a href="reference.html#RNS.Destination.deregister_request_handler">deregister_request_handler() (RNS.Destination method)</a>
</li>
<li><a href="reference.html#RNS.Destination">Destination (class in RNS)</a>
</li>
@@ -157,6 +159,8 @@
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Identity.get_random_hash">get_random_hash() (RNS.Identity static method)</a>
</li>
<li><a href="reference.html#RNS.Link.get_remote_identity">get_remote_identity() (RNS.Link method)</a>
</li>
<li><a href="reference.html#RNS.PacketReceipt.get_rtt">get_rtt() (RNS.PacketReceipt method)</a>
</li>
@@ -182,10 +186,12 @@
<h2 id="I">I</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Identity">Identity (class in RNS)</a>
<li><a href="reference.html#RNS.Link.identify">identify() (RNS.Link method)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Identity">Identity (class in RNS)</a>
</li>
<li><a href="reference.html#RNS.Link.inactive_for">inactive_for() (RNS.Link method)</a>
</li>
</ul></td>
@@ -257,9 +263,13 @@
<li><a href="reference.html#RNS.Identity.recall_app_data">recall_app_data() (RNS.Identity static method)</a>
</li>
<li><a href="reference.html#RNS.Transport.register_announce_handler">register_announce_handler() (RNS.Transport static method)</a>
</li>
<li><a href="reference.html#RNS.Destination.register_request_handler">register_request_handler() (RNS.Destination method)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Link.request">request() (RNS.Link method)</a>
</li>
<li><a href="reference.html#RNS.Transport.request_path">request_path() (RNS.Transport static method)</a>
</li>
<li><a href="reference.html#RNS.Packet.resend">resend() (RNS.Packet method)</a>
@@ -292,10 +302,12 @@
</li>
<li><a href="reference.html#RNS.Destination.set_proof_strategy">set_proof_strategy() (RNS.Destination method)</a>
</li>
<li><a href="reference.html#RNS.Link.set_resource_callback">set_resource_callback() (RNS.Link method)</a>
<li><a href="reference.html#RNS.Link.set_remote_identified_callback">set_remote_identified_callback() (RNS.Link method)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="reference.html#RNS.Link.set_resource_callback">set_resource_callback() (RNS.Link method)</a>
</li>
<li><a href="reference.html#RNS.Link.set_resource_concluded_callback">set_resource_concluded_callback() (RNS.Link method)</a>
</li>
<li><a href="reference.html#RNS.Link.set_resource_started_callback">set_resource_started_callback() (RNS.Link method)</a>
@@ -375,7 +387,7 @@
<li class="right" style="margin-right: 10px">
<a href="#" title="General Index"
>index</a></li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Index</a></li>
</ul>
</div>
+8 -8
View File
@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Getting Started Fast &#8212; Reticulum Network Stack 0.2.1 beta documentation</title>
<title>Getting Started Fast &#8212; Reticulum Network Stack 0.2.2 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -16,7 +16,7 @@
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="Examples" href="examples.html" />
<link rel="next" title="Understanding Reticulum" href="understanding.html" />
<link rel="prev" title="What is Reticulum?" href="whatis.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
@@ -26,12 +26,12 @@
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="examples.html" title="Examples"
<a href="understanding.html" title="Understanding Reticulum"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="whatis.html" title="What is Reticulum?"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Getting Started Fast</a></li>
</ul>
</div>
@@ -131,8 +131,8 @@ dont use pip, but try this recipe:</p>
<p class="topless"><a href="whatis.html"
title="previous chapter">What is Reticulum?</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="examples.html"
title="next chapter">Examples</a></p>
<p class="topless"><a href="understanding.html"
title="next chapter">Understanding Reticulum</a></p>
<div role="note" aria-label="source link">
<h3>This Page</h3>
<ul class="this-page-menu">
@@ -161,12 +161,12 @@ dont use pip, but try this recipe:</p>
<a href="genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="examples.html" title="Examples"
<a href="understanding.html" title="Understanding Reticulum"
>next</a> |</li>
<li class="right" >
<a href="whatis.html" title="What is Reticulum?"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Getting Started Fast</a></li>
</ul>
</div>
+5 -3
View File
@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Reticulum Network Stack Manual &#8212; Reticulum Network Stack 0.2.1 beta documentation</title>
<title>Reticulum Network Stack Manual &#8212; Reticulum Network Stack 0.2.2 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -27,7 +27,7 @@
<li class="right" >
<a href="whatis.html" title="What is Reticulum?"
accesskey="N">next</a> |</li>
<li class="nav-item nav-item-0"><a href="#">Reticulum Network Stack 0.2.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-0"><a href="#">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Reticulum Network Stack Manual</a></li>
</ul>
</div>
@@ -103,6 +103,8 @@ the development of Reticulum itself.</p>
<li class="toctree-l2"><a class="reference internal" href="examples.html#broadcast">Broadcast</a></li>
<li class="toctree-l2"><a class="reference internal" href="examples.html#echo">Echo</a></li>
<li class="toctree-l2"><a class="reference internal" href="examples.html#link">Link</a></li>
<li class="toctree-l2"><a class="reference internal" href="examples.html#example-identify">Identification</a></li>
<li class="toctree-l2"><a class="reference internal" href="examples.html#requests-responses">Requests &amp; Responses</a></li>
<li class="toctree-l2"><a class="reference internal" href="examples.html#filetransfer">Filetransfer</a></li>
</ul>
</li>
@@ -165,7 +167,7 @@ the development of Reticulum itself.</p>
<li class="right" >
<a href="whatis.html" title="What is Reticulum?"
>next</a> |</li>
<li class="nav-item nav-item-0"><a href="#">Reticulum Network Stack 0.2.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-0"><a href="#">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Reticulum Network Stack Manual</a></li>
</ul>
</div>
Binary file not shown.
+90 -4
View File
@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>API Reference &#8212; Reticulum Network Stack 0.2.1 beta documentation</title>
<title>API Reference &#8212; Reticulum Network Stack 0.2.2 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -31,7 +31,7 @@
<li class="right" >
<a href="understanding.html" title="Understanding Reticulum"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">API Reference</a></li>
</ul>
</div>
@@ -508,6 +508,39 @@ proofs should be returned for received packets.</p>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Destination.register_request_handler">
<span class="sig-name descname"><span class="pre">register_request_handler</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">path</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">response_generator</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">allow</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">0</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">allowed_list</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Destination.register_request_handler" title="Permalink to this definition"></a></dt>
<dd><p>Registers a request handler.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters</dt>
<dd class="field-odd"><ul class="simple">
<li><p><strong>path</strong> The path for the request handler to be registered.</p></li>
<li><p><strong>response_generator</strong> A function or method with the signature <em>response_generator(path, data, request_id, remote_identity, requested_at)</em> to be called. Whatever this funcion returns will be sent as a response to the requester. If the function returns <code class="docutils literal notranslate"><span class="pre">None</span></code>, no response will be sent.</p></li>
<li><p><strong>allow</strong> One of <code class="docutils literal notranslate"><span class="pre">RNS.Destination.ALLOW_NONE</span></code>, <code class="docutils literal notranslate"><span class="pre">RNS.Destination.ALLOW_ALL</span></code> or <code class="docutils literal notranslate"><span class="pre">RNS.Destination.ALLOW_LIST</span></code>. If <code class="docutils literal notranslate"><span class="pre">RNS.Destination.ALLOW_LIST</span></code> is set, the request handler will only respond to requests for identified peers in the supplied list.</p></li>
<li><p><strong>allowed_list</strong> A list of <em>bytes-like</em> <a class="reference internal" href="#api-identity"><span class="std std-ref">RNS.Identity</span></a> hashes.</p></li>
</ul>
</dd>
<dt class="field-even">Raises</dt>
<dd class="field-even"><p><code class="docutils literal notranslate"><span class="pre">ValueError</span></code> if any of the supplied arguments are invalid.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Destination.deregister_request_handler">
<span class="sig-name descname"><span class="pre">deregister_request_handler</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">path</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Destination.deregister_request_handler" title="Permalink to this definition"></a></dt>
<dd><p>Deregisters a request handler.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters</dt>
<dd class="field-odd"><p><strong>path</strong> The path for the request handler to be deregistered.</p>
</dd>
<dt class="field-even">Returns</dt>
<dd class="field-even"><p>True if the handler was deregistered, otherwise False.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Destination.create_keys">
<span class="sig-name descname"><span class="pre">create_keys</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Destination.create_keys" title="Permalink to this definition"></a></dt>
@@ -771,6 +804,35 @@ from a the <em>send()</em> method of a <a class="reference internal" href="#api-
<dd><p>Interval for sending keep-alive packets on established links in seconds.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.identify">
<span class="sig-name descname"><span class="pre">identify</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">identity</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.identify" title="Permalink to this definition"></a></dt>
<dd><p>Identifies the initiator of the link to the remote peer. This can only happen
once the link has been established, and is carried out over the encrypted link.
The identity is only revealed to the remote peer, and initiator anonymity is
thus preserved. This method can be used for authentication.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters</dt>
<dd class="field-odd"><p><strong>identity</strong> An RNS.Identity instance to identify as.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.request">
<span class="sig-name descname"><span class="pre">request</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">path</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">data</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">response_callback</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">failed_callback</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.request" title="Permalink to this definition"></a></dt>
<dd><p>Sends a request to the remote peer.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters</dt>
<dd class="field-odd"><ul class="simple">
<li><p><strong>path</strong> The request path.</p></li>
<li><p><strong>response_callback</strong> A function or method with the signature <em>response_callback(request_receipt)</em> to be called when a response is received. See the <a class="reference internal" href="examples.html#example-request"><span class="std std-ref">Request Example</span></a> for more info.</p></li>
<li><p><strong>failed_callback</strong> A function or method with the signature <em>failed_callback(request_receipt)</em> to be called when a request fails. See the <a class="reference internal" href="examples.html#example-request"><span class="std std-ref">Request Example</span></a> for more info.</p></li>
</ul>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.no_inbound_for">
<span class="sig-name descname"><span class="pre">no_inbound_for</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.no_inbound_for" title="Permalink to this definition"></a></dt>
@@ -801,6 +863,16 @@ from a the <em>send()</em> method of a <a class="reference internal" href="#api-
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.get_remote_identity">
<span class="sig-name descname"><span class="pre">get_remote_identity</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.get_remote_identity" title="Permalink to this definition"></a></dt>
<dd><dl class="field-list simple">
<dt class="field-odd">Returns</dt>
<dd class="field-odd"><p>The identity of the remote peer, if it is known</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.teardown">
<span class="sig-name descname"><span class="pre">teardown</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.teardown" title="Permalink to this definition"></a></dt>
@@ -858,6 +930,18 @@ transferring over this link.</p>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.set_remote_identified_callback">
<span class="sig-name descname"><span class="pre">set_remote_identified_callback</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">callback</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.set_remote_identified_callback" title="Permalink to this definition"></a></dt>
<dd><p>Registers a function to be called when an initiating peer has
identified over this link.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters</dt>
<dd class="field-odd"><p><strong>callback</strong> A function or method with the signature <em>callback(identity)</em> to be called.</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="RNS.Link.set_resource_strategy">
<span class="sig-name descname"><span class="pre">set_resource_strategy</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">resource_strategy</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Link.set_resource_strategy" title="Permalink to this definition"></a></dt>
@@ -891,7 +975,7 @@ client application and throw an error message to the user.</p>
<span id="api-resource"></span><h3>Resource<a class="headerlink" href="#resource" title="Permalink to this headline"></a></h3>
<dl class="py class">
<dt class="sig sig-object py" id="RNS.Resource">
<em class="property"><span class="pre">class</span> </em><span class="sig-prename descclassname"><span class="pre">RNS.</span></span><span class="sig-name descname"><span class="pre">Resource</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">data</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">link</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">advertise</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">True</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">auto_compress</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">True</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">callback</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">progress_callback</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">segment_index</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">1</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">original_hash</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Resource" title="Permalink to this definition"></a></dt>
<em class="property"><span class="pre">class</span> </em><span class="sig-prename descclassname"><span class="pre">RNS.</span></span><span class="sig-name descname"><span class="pre">Resource</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">data</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">link</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">advertise</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">True</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">auto_compress</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">True</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">callback</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">progress_callback</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">segment_index</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">1</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">original_hash</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">request_id</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">is_response</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#RNS.Resource" title="Permalink to this definition"></a></dt>
<dd><p>The Resource class allows transferring arbitrary amounts
of data over a link. It will automatically handle sequencing,
compression, coordination and checksumming.</p>
@@ -906,6 +990,8 @@ compression, coordination and checksumming.</p>
<li><p><strong>progress_callback</strong> A <em>callable</em> with the signature <em>callback(resource)</em>. Will be called whenever the resource transfer progress is updated.</p></li>
<li><p><strong>segment_index</strong> Internal use, ignore.</p></li>
<li><p><strong>original_hash</strong> Internal use, ignore.</p></li>
<li><p><strong>is_request</strong> Internal use, ignore.</p></li>
<li><p><strong>is_response</strong> Internal use, ignore.</p></li>
</ul>
</dd>
</dl>
@@ -1058,7 +1144,7 @@ will announce it.</p>
<li class="right" >
<a href="understanding.html" title="Understanding Reticulum"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">API Reference</a></li>
</ul>
</div>
+3 -3
View File
@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Search &#8212; Reticulum Network Stack 0.2.1 beta documentation</title>
<title>Search &#8212; Reticulum Network Stack 0.2.2 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -29,7 +29,7 @@
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Search</a></li>
</ul>
</div>
@@ -85,7 +85,7 @@
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Search</a></li>
</ul>
</div>
File diff suppressed because one or more lines are too long
+3 -3
View File
@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Understanding Reticulum &#8212; Reticulum Network Stack 0.2.1 beta documentation</title>
<title>Understanding Reticulum &#8212; Reticulum Network Stack 0.2.2 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -31,7 +31,7 @@
<li class="right" >
<a href="gettingstartedfast.html" title="Getting Started Fast"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Understanding Reticulum</a></li>
</ul>
</div>
@@ -853,7 +853,7 @@ proof 11
<li class="right" >
<a href="gettingstartedfast.html" title="Getting Started Fast"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Understanding Reticulum</a></li>
</ul>
</div>
+6 -5
View File
@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>What is Reticulum? &#8212; Reticulum Network Stack 0.2.1 beta documentation</title>
<title>What is Reticulum? &#8212; Reticulum Network Stack 0.2.2 beta documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/classic.css" />
@@ -31,7 +31,7 @@
<li class="right" >
<a href="index.html" title="Reticulum Network Stack Manual"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">What is Reticulum?</a></li>
</ul>
</div>
@@ -43,7 +43,8 @@
<div class="section" id="what-is-reticulum">
<h1>What is Reticulum?<a class="headerlink" href="#what-is-reticulum" title="Permalink to this headline"></a></h1>
<p>Reticulum is a cryptography-based networking stack for wide-area networks built on readily available hardware, and can operate even with very high latency and extremely low bandwidth. Reticulum allows you to build very wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more.</p>
<p>Reticulum is a cryptography-based networking stack for wide-area networks built on readily available hardware, and can operate even with very high latency and extremely low bandwidth.</p>
<p>Reticulum allows you to build very wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more.</p>
<p>Reticulum is a complete networking stack, and does not use IP or higher layers, although it is easy to utilise IP (with TCP or UDP) as the underlying carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the Internet or private IP networks. Reticulum is built directly on cryptographic principles, allowing resilience and stable functionality in open and trustless networks.</p>
<p>No kernel modules or drivers are required. Reticulum runs completely in userland, and can run on practically any system that runs Python 3.</p>
<div class="section" id="current-status">
@@ -71,7 +72,7 @@
</li>
<li><p>Unforgeable packet delivery confirmations</p></li>
<li><p>A variety of supported interface types</p></li>
<li><p>An intuitive and easy-to-use API</p></li>
<li><p>An intuitive and developer-friendly API</p></li>
<li><p>Reliable and efficient transfer of arbritrary amounts of data</p>
<ul>
<li><p>Reticulum can handle a few bytes of data or files of many gigabytes</p></li>
@@ -181,7 +182,7 @@ network, and vice versa.</p>
<li class="right" >
<a href="index.html" title="Reticulum Network Stack Manual"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.1 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-0"><a href="index.html">Reticulum Network Stack 0.2.2 beta documentation</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">What is Reticulum?</a></li>
</ul>
</div>
+1 -1
View File
@@ -22,7 +22,7 @@ copyright = '2021, Mark Qvist'
author = 'Mark Qvist'
# The full version, including alpha/beta/rc tags
release = '0.2.1 beta'
release = '0.2.2 beta'
# -- General configuration ---------------------------------------------------
+23
View File
@@ -68,6 +68,29 @@ destination, and passing traffic back and forth over the link.
This example can also be found at `<https://github.com/markqvist/Reticulum/blob/master/Examples/Link.py>`_.
.. _example-identify:
Identification
==============
The *Identify* example explores identifying an intiator of a link, once
the link has been established.
.. literalinclude:: ../../Examples/Identify.py
This example can also be found at `<https://github.com/markqvist/Reticulum/blob/master/Examples/Identify.py>`_.
.. _example-request:
Requests & Responses
====================
The *Request* example explores sendig requests and receiving responses.
.. literalinclude:: ../../Examples/Request.py
This example can also be found at `<https://github.com/markqvist/Reticulum/blob/master/Examples/Request.py>`_.
.. _example-filetransfer:
Filetransfer
+4 -2
View File
@@ -2,7 +2,9 @@
What is Reticulum?
******************
Reticulum is a cryptography-based networking stack for wide-area networks built on readily available hardware, and can operate even with very high latency and extremely low bandwidth. Reticulum allows you to build very wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more.
Reticulum is a cryptography-based networking stack for wide-area networks built on readily available hardware, and can operate even with very high latency and extremely low bandwidth.
Reticulum allows you to build very wide-area networks with off-the-shelf tools, and offers end-to-end encryption, autoconfiguring cryptographically backed multi-hop transport, efficient addressing, unforgeable packet acknowledgements and more.
Reticulum is a complete networking stack, and does not use IP or higher layers, although it is easy to utilise IP (with TCP or UDP) as the underlying carrier for Reticulum. It is therefore trivial to tunnel Reticulum over the Internet or private IP networks. Reticulum is built directly on cryptographic principles, allowing resilience and stable functionality in open and trustless networks.
@@ -43,7 +45,7 @@ What does Reticulum Offer?
* A variety of supported interface types
* An intuitive and easy-to-use API
* An intuitive and developer-friendly API
* Reliable and efficient transfer of arbritrary amounts of data
+4 -2
View File
@@ -1,11 +1,13 @@
import setuptools
exec(open("RNS/_version.py", "r").read())
with open("README.md", "r") as fh:
long_description = fh.read()
setuptools.setup(
name="rns",
version="0.2.1",
version=__version__,
author="Mark Qvist",
author_email="mark@unsigned.io",
description="Self-configuring, encrypted and resilient mesh networking stack for LoRa, packet radio, WiFi and everything in between",
@@ -18,6 +20,6 @@ setuptools.setup(
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
install_requires=['cryptography>=3.4.7', 'pyserial'],
install_requires=['cryptography>=3.4.7', 'pyserial', 'netifaces>=0.10.4'],
python_requires='>=3.5',
)