Added docstrings, refactored method names.

This commit is contained in:
Mark Qvist 2021-05-16 23:14:49 +02:00
parent 3f1e2bc682
commit dfb5af5dd1
4 changed files with 168 additions and 26 deletions

View File

@ -161,7 +161,7 @@ class Identity:
announced_identity.load_public_key(public_key)
if announced_identity.pub != None and announced_identity.validate(signature, signed_data):
RNS.Identity.remember(packet.getHash(), destination_hash, public_key, app_data)
RNS.Identity.remember(packet.get_hash(), destination_hash, public_key, app_data)
RNS.log("Stored valid announce from "+RNS.prettyhexrep(destination_hash), RNS.LOG_DEBUG)
del announced_identity
return True

View File

@ -19,11 +19,23 @@ class LinkCallbacks:
self.link_established = None
self.link_closed = None
self.packet = None
self.resource = None
self.resource_started = None
self.resource_concluded = None
class Link:
"""
This class.
:param destination: A :ref:`RNS.Destination<api-destination>` instance which to establish a link to.
:param owner: Internal use by :ref:`RNS.Transport<api-Transport>`, ignore this argument.
:param peer_pub_bytes: Internal use by :ref:`RNS.Transport<api-Transport>`, ignore this argument.
"""
CURVE = ec.SECP256R1()
"""
The curve used for Elliptic Curve DH key exchanges
"""
ECPUBSIZE = 91
BLOCKSIZE = 16
AES_HMAC_OVERHEAD = 58
@ -33,9 +45,15 @@ class Link:
# but calculated from something like
# first-hop RTT latency and distance
DEFAULT_TIMEOUT = 15.0
"""
Default timeout for link establishment in seconds.
"""
TIMEOUT_FACTOR = 3
STALE_GRACE = 2
KEEPALIVE = 180
"""
Interval for sending keep-alive packets on established links in seconds.
"""
PENDING = 0x00
HANDSHAKE = 0x01
@ -243,18 +261,31 @@ class Link:
return None
def no_inbound_for(self):
"""
:returns: The time in seconds since last inbound packet on the link.
"""
return time.time() - self.last_inbound
def no_outbound_for(self):
"""
:returns: The time in seconds since last outbound packet on the link.
"""
return time.time() - self.last_outbound
def inactive_for(self):
"""
:returns: The time in seconds since activity on the link.
"""
return min(self.no_inbound_for(), self.no_outbound_for())
def had_outbound(self):
self.last_outbound = time.time()
def teardown(self):
"""
Closes the link and purges encryption keys. New keys will
be used if a new link to the same destination is established.
"""
if self.status != Link.PENDING and self.status != Link.CLOSED:
teardown_packet = RNS.Packet(self, self.link_id, context=RNS.Packet.LINKCLOSE)
teardown_packet.send()
@ -398,9 +429,8 @@ class Link:
pass
elif self.resource_strategy == Link.ACCEPT_APP:
if self.callbacks.resource != None:
thread = threading.Thread(target=self.callbacks.resource, args=(packet))
thread.setDaemon(True)
thread.start()
if self.callbacks.resource(resource):
RNS.Resource.accept(packet, self.callbacks.resource_concluded)
elif self.resource_strategy == Link.ACCEPT_ALL:
RNS.Resource.accept(packet, self.callbacks.resource_concluded)
@ -496,14 +526,41 @@ class Link:
self.callbacks.link_closed = callback
def packet_callback(self, callback):
"""
Registers a function to be called when a packet has been
received over this link.
:param callback: A function or method with the signature *callback(message, packet)* to be called.
"""
self.callbacks.packet = callback
# Called when an incoming resource transfer is started
def resource_callback(self, callback):
"""
Registers a function to be called when a resource has been
advertised over this link. If the function returns *True*
the resource will be accepted. If it returns *False* it will
be ignored.
:param callback: A function or method with the signature *callback(resource)* to be called.
"""
self.callbacks.resource = callback
def resource_started_callback(self, callback):
"""
Registers a function to be called when a resource has begun
transferring over this link.
:param callback: A function or method with the signature *callback(resource)* to be called.
"""
self.callbacks.resource_started = callback
# Called when a resource transfer is concluded
def resource_concluded_callback(self, callback):
"""
Registers a function to be called when a resource has concluded
transferring over this link.
:param callback: A function or method with the signature *callback(resource)* to be called.
"""
self.callbacks.resource_concluded = callback
def resource_concluded(self, resource):
@ -513,6 +570,12 @@ class Link:
self.outgoing_resources.remove(resource)
def set_resource_strategy(self, resource_strategy):
"""
Sets the resource strategy for the link.
:param resource_strategy: One of ``RNS.Link.ACCEPT_NONE``, ``RNS.Link.ACCEPT_ALL`` or ``RNS.Link.ACCEPT_APP``. If ``RNS.Link.ACCEPT_APP`` is set, the `resource_callback` will be called to determine whether the resource should be accepted or not.
:raises: *TypeError* if the resource strategy is unsupported.
"""
if not resource_strategy in Link.resource_strategies:
raise TypeError("Unsupported resource strategy")
else:
@ -542,7 +605,17 @@ class Link:
else:
return True
def disableEncryption(self):
def disable_encryption(self):
"""
HAZARDOUS. This will downgrade the link to encryptionless. All
information over the link will be sent in plaintext. Never use
this in production applications. Should only be used for debugging
purposes, and will disappear in a future version.
If encryptionless links are not explicitly allowed in the users
configuration file, Reticulum will terminate itself along with the
client application and throw an error message to the user.
"""
if (RNS.Reticulum.should_allow_unencrypted()):
RNS.log("The link "+str(self)+" was downgraded to an encryptionless link", RNS.LOG_NOTICE)
self.__encryption_disabled = True

View File

@ -5,6 +5,20 @@ import time
import RNS
class Packet:
"""
The Packet class is used to create packet instances that can be
sent over a Reticulum network.
:param destination: A :ref:`RNS.Destination<api-destination>` instance to which the packet will be sent.
:param data: The data payload to be included in the packet as *bytes*.
:param create_receipt: Specifies whether a :ref:`RNS.PacketReceipt<api-packetreceipt>` should be created when instantiating the packet.
:param type: Internal use by :ref:`RNS.Transport<api-transport>`. Defaults to ``RNS.Packet.DATA``, and should not be specified.
:param context: Internal use by :ref:`RNS.Transport<api-transport>`. Ignore.
:param transport_type: Internal use by :ref:`RNS.Transport<api-transport>`. Ignore.
:param transport_id: Internal use by :ref:`RNS.Transport<api-transport>`. Ignore.
:param attached_interface: Internal use by :ref:`RNS.Transport<api-transport>`. Ignore.
"""
# Packet types
DATA = 0x00 # Data packets
ANNOUNCE = 0x01 # Announces
@ -155,7 +169,7 @@ class Packet:
raise IOError("Packet size of "+str(len(self.raw))+" exceeds MTU of "+str(self.MTU)+" bytes")
self.packed = True
self.updateHash()
self.update_hash()
def unpack(self):
self.flags = self.raw[0]
@ -178,12 +192,14 @@ class Packet:
self.data = self.raw[13:]
self.packed = False
self.updateHash()
self.update_hash()
# Sends the packet. Returns a receipt if one is generated,
# or None if no receipt is available. Returns False if the
# packet could not be sent.
def send(self):
"""
Sends the packet.
:returns: A :ref:`RNS.PacketReceipt<api-packetreceipt>` instance if *create_receipt* was set to *True* when the packet was instantiated, if not returns *None*. If the packet could not be sent *False* is returned.
"""
if not self.sent:
if self.destination.type == RNS.Destination.LINK:
if self.destination.status == RNS.Link.CLOSED:
@ -208,6 +224,11 @@ class Packet:
raise IOError("Packet was already sent")
def resend(self):
"""
Re-sends the packet.
:returns: A :ref:`RNS.PacketReceipt<api-packetreceipt>` instance if *create_receipt* was set to *True* when the packet was instantiated, if not returns *None*. If the packet could not be sent *False* is returned.
"""
if self.sent:
if RNS.Transport.outbound(self):
return self.receipt
@ -239,16 +260,16 @@ class Packet:
def validate_proof(self, proof):
return self.receipt.validate_proof(proof)
def updateHash(self):
self.packet_hash = self.getHash()
def update_hash(self):
self.packet_hash = self.get_hash()
def getHash(self):
return RNS.Identity.full_hash(self.getHashablePart())
def get_hash(self):
return RNS.Identity.full_hash(self.get_hashable_part())
def getTruncatedHash(self):
return RNS.Identity.truncated_hash(self.getHashablePart())
return RNS.Identity.truncated_hash(self.get_hashable_part())
def getHashablePart(self):
def get_hashable_part(self):
hashable_part = bytes([self.raw[0] & 0b00001111])
if self.header_type == Packet.HEADER_2:
hashable_part += self.raw[12:]
@ -259,7 +280,7 @@ class Packet:
class ProofDestination:
def __init__(self, packet):
self.hash = packet.getHash()[:10];
self.hash = packet.get_hash()[:10];
self.type = RNS.Destination.SINGLE
def encrypt(self, plaintext):
@ -267,6 +288,12 @@ class ProofDestination:
class PacketReceipt:
"""
The PacketReceipt class is used to receive notifications about
:ref:`RNS.Packet<api-packet>` instances sent over the network. Instances
of this class should never be created manually, but always returned
from a the *send()* method of a :ref:`RNS.Packet<api-packet>` instance.
"""
# Receipt status constants
FAILED = 0x00
SENT = 0x01
@ -279,7 +306,7 @@ class PacketReceipt:
# Creates a new packet receipt from a sent packet
def __init__(self, packet):
self.hash = packet.getHash()
self.hash = packet.get_hash()
self.sent = True
self.sent_at = time.time()
self.timeout = Packet.TIMEOUT
@ -289,6 +316,12 @@ class PacketReceipt:
self.callbacks = PacketReceiptCallbacks()
self.concluded_at = None
def get_status(self):
"""
:returns: The status of the associated :ref:`RNS.Packet<api-packet>` instance. Can be one of ``RNS.PacketReceipt.SENT``, ``RNS.PacketReceipt.DELIVERED``, ``RNS.PacketReceipt.FAILED`` or ``RNS.PacketReceipt.CULLED``.
"""
return self.status
# Validate a proof packet
def validate_proof_packet(self, proof_packet):
if hasattr(proof_packet, "link") and proof_packet.link:
@ -374,6 +407,9 @@ class PacketReceipt:
return False
def rtt(self):
"""
:returns: The round-trip-time in seconds
"""
return self.concluded_at - self.sent_at
def is_timed_out(self):
@ -395,18 +431,30 @@ class PacketReceipt:
#self.callbacks.timeout(self)
# Set the timeout in seconds
def set_timeout(self, timeout):
"""
Sets a timeout in seconds
:param timeout: The timeout in seconds.
"""
self.timeout = float(timeout)
# Set a function that gets called when
# a successfull delivery has been proved
def delivery_callback(self, callback):
"""
Sets a function that gets called if a successfull delivery has been proven.
:param callback: A *callable* with the signature *callback(packet_receipt)*
"""
self.callbacks.delivery = callback
# Set a function that gets called if the
# delivery times out
def timeout_callback(self, callback):
"""
Sets a function that gets called if the delivery times out.
:param callback: A *callable* with the signature *callback(packet_receipt)*
"""
self.callbacks.timeout = callback
class PacketReceiptCallbacks:

View File

@ -318,7 +318,7 @@ class Transport:
Transport.jobs_locked = True
# TODO: This updateHash call might be redundant
packet.updateHash()
packet.update_hash()
sent = False
# Check if we have a known path for the destination in the path table
@ -907,12 +907,22 @@ class Transport:
@staticmethod
def register_announce_handler(handler):
"""
Registers an announce handler.
:param handler: Must be an object with an *aspect_filter* attribute and a *received_announce(destination_hash, announced_identity, app_data)* callable. See the :ref:`Announce Example<example-announce>` for more info.
"""
if hasattr(handler, "received_announce") and callable(handler.received_announce):
if hasattr(handler, "aspect_filter"):
Transport.announce_handlers.append(handler)
@staticmethod
def deregister_announce_handler(handler):
"""
Deregisters an announce handler.
:param handler: The announce handler to be deregistered.
"""
while handler in Transport.announce_handlers:
Transport.announce_handlers.remove(handler)
@ -940,7 +950,7 @@ class Transport:
def cache(packet, force_cache=False):
if RNS.Transport.should_cache(packet) or force_cache:
try:
packet_hash = RNS.hexrep(packet.getHash(), delimit=False)
packet_hash = RNS.hexrep(packet.get_hash(), delimit=False)
interface_reference = None
if packet.receiving_interface != None:
interface_reference = str(packet.receiving_interface)
@ -1009,6 +1019,10 @@ class Transport:
@staticmethod
def has_path(destination_hash):
"""
:param destination_hash: A destination hash as *bytes*.
:returns: *True* if a path to the destination is known, otherwise *False*.
"""
if destination_hash in Transport.destination_table:
return True
else:
@ -1016,6 +1030,13 @@ class Transport:
@staticmethod
def request_path(destination_hash):
"""
Requests a path to the destination from the network. If
another reachable peer on the network knows a path, it
will announce it.
:param destination_hash: A destination hash as *bytes*.
"""
path_request_data = destination_hash + RNS.Identity.get_random_hash()
path_request_dst = RNS.Destination(None, RNS.Destination.OUT, RNS.Destination.PLAIN, Transport.APP_NAME, "path", "request")
packet = RNS.Packet(path_request_dst, path_request_data, packet_type = RNS.Packet.DATA, transport_type = RNS.Transport.BROADCAST, header_type = RNS.Packet.HEADER_1)
@ -1147,7 +1168,7 @@ class Transport:
hops = de[2]
expires = de[3]
random_blobs = de[4]
packet_hash = de[6].getHash()
packet_hash = de[6].get_hash()
serialised_entry = [
destination_hash,