Merge branch 'commands' into 'master'

Commands

See merge request ynerant/squinnondation!8
This commit is contained in:
eichhornchen 2021-01-07 11:13:38 +01:00
commit 25e502ab45
3 changed files with 1182 additions and 65 deletions

View File

@ -36,9 +36,9 @@ class TLV:
""" """
return True return True
def handle(self, squirrel: Any, sender: Any) -> None: def handle(self, user: Any, sender: Any) -> None:
""" """
Indicates what to do when this TLV is received from a given hazel. Indicates what to do when this TLV is received from a given peer.
It is ensured that the data is valid. It is ensured that the data is valid.
""" """
@ -72,14 +72,14 @@ class Pad1TLV(TLV):
""" """
return self.type.to_bytes(1, sys.byteorder) return self.type.to_bytes(1, sys.byteorder)
def handle(self, squirrel: Any, sender: Any) -> None: def handle(self, user: Any, sender: Any) -> None:
if not sender.active or not sender.symmetric or not sender.id: if not sender.active or not sender.symmetric or not sender.id:
# It doesn't say hello, we don't listen to it # It doesn't say hello, we don't listen to it
squirrel.send_packet(sender, Packet.construct(WarningTLV.construct( user.send_packet(sender, Packet.construct(WarningTLV.construct(
"You are not my neighbour, I don't listen to your Pad1TLV. Please say me Hello before."))) "You are not my neighbour, I don't listen to your Pad1TLV. Please say me Hello before.")))
return return
squirrel.add_system_message("I received a Pad1TLV, how disapointing.") user.add_system_message("I received a Pad1TLV, how disapointing.")
def __len__(self) -> int: def __len__(self) -> int:
""" """
@ -122,14 +122,14 @@ class PadNTLV(TLV):
return self.type.to_bytes(1, sys.byteorder) + self.length.to_bytes(1, sys.byteorder) \ return self.type.to_bytes(1, sys.byteorder) + self.length.to_bytes(1, sys.byteorder) \
+ self.mbz[:self.length] + self.mbz[:self.length]
def handle(self, squirrel: Any, sender: Any) -> None: def handle(self, user: Any, sender: Any) -> None:
if not sender.active or not sender.symmetric or not sender.id: if not sender.active or not sender.symmetric or not sender.id:
# It doesn't say hello, we don't listen to it # It doesn't say hello, we don't listen to it
squirrel.send_packet(sender, Packet.construct(WarningTLV.construct( user.send_packet(sender, Packet.construct(WarningTLV.construct(
"You are not my neighbour, I don't listen to your PadNTLV. Please say me Hello before."))) "You are not my neighbour, I don't listen to your PadNTLV. Please say me Hello before.")))
return return
squirrel.add_system_message(f"I received {self.length} zeros.") user.add_system_message(f"I received {self.length} zeros.")
@staticmethod @staticmethod
def construct(length: int) -> "PadNTLV": def construct(length: int) -> "PadNTLV":
@ -166,45 +166,48 @@ class HelloTLV(TLV):
data += self.dest_id.to_bytes(8, sys.byteorder) data += self.dest_id.to_bytes(8, sys.byteorder)
return data return data
def handle(self, squirrel: Any, sender: Any) -> None: def handle(self, user: Any, sender: Any) -> None:
time_h = time.time() time_h = time.time()
if sender.id > 0 and sender.id != self.source_id: if sender.id > 0 and sender.id != self.source_id:
squirrel.send_packet(sender, Packet.construct(WarningTLV.construct( user.send_packet(sender, Packet.construct(WarningTLV.construct(
f"You were known as the ID {sender.id}, but you declared that you have the ID {self.source_id}."))) f"You were known as the ID {sender.id}, but you declared that you have the ID {self.source_id}.")))
squirrel.add_system_message(f"A client known as the id {sender.id} declared that it uses " user.add_system_message(f"A client known as the id {sender.id} declared that it uses "
f"the id {self.source_id}.") f"the id {self.source_id}.")
sender.id = self.source_id sender.id = self.source_id
if self.source_id == user.id:
sender.marked_as_banned = True
if not sender.active: if not sender.active:
sender.id = self.source_id # The sender we are given misses an id sender.id = self.source_id # The sender we are given misses an id
time_hl = time.time() time_hl = time.time()
else: else:
time_hl = sender.last_long_hello_time time_hl = sender.last_long_hello_time
if self.is_long and self.dest_id == squirrel.id: if self.is_long and self.dest_id == user.id:
time_hl = time.time() time_hl = time.time()
# Add entry to/actualize the active hazelnuts dictionnary # Add entry to/actualize the active peers dictionnary
sender.last_hello_time = time_h sender.last_hello_time = time_h
sender.last_long_hello_time = time_hl sender.last_long_hello_time = time_hl
sender.symmetric = True sender.symmetric = True
sender.active = True sender.active = True
squirrel.update_hazelnut_table(sender) user.update_peer_table(sender)
squirrel.nbNS += 1 user.nbNS += 1
squirrel.add_system_message(f"{self.source_id} sent me a Hello " + ("long" if self.is_long else "short")) user.add_system_message(f"{self.source_id} sent me a Hello " + ("long" if self.is_long else "short"))
if not self.is_long: if not self.is_long:
squirrel.send_packet(sender, Packet.construct(HelloTLV.construct(16, squirrel, sender))) user.send_packet(sender, Packet.construct(HelloTLV.construct(16, user, sender)))
@property @property
def is_long(self) -> bool: def is_long(self) -> bool:
return self.length == 16 return self.length == 16
@staticmethod @staticmethod
def construct(length: int, squirrel: Any, destination: Any = None) -> "HelloTLV": def construct(length: int, user: Any, destination: Any = None) -> "HelloTLV":
tlv = HelloTLV() tlv = HelloTLV()
tlv.type = 2 tlv.type = 2
tlv.source_id = squirrel.id if squirrel else 0 tlv.source_id = user.id if user else 0
if (destination is None) or destination.id == -1 or length == 8: if (destination is None) or destination.id == -1 or length == 8:
tlv.length = 8 tlv.length = 8
tlv.dest_id = None # if the destination id is not known, or tlv.dest_id = None # if the destination id is not known, or
@ -238,28 +241,28 @@ class NeighbourTLV(TLV):
self.ip_address.packed + \ self.ip_address.packed + \
self.port.to_bytes(2, sys.byteorder) self.port.to_bytes(2, sys.byteorder)
def handle(self, squirrel: Any, sender: Any) -> None: def handle(self, user: Any, sender: Any) -> None:
if not sender.active or not sender.symmetric or not sender.id: if not sender.active or not sender.symmetric or not sender.id:
# It doesn't say hello, we don't listen to it # It doesn't say hello, we don't listen to it
squirrel.send_packet(sender, Packet.construct(WarningTLV.construct( user.send_packet(sender, Packet.construct(WarningTLV.construct(
"You are not my neighbour, I don't listen to your NeighbourTLV. Please say me Hello before."))) "You are not my neighbour, I don't listen to your NeighbourTLV. Please say me Hello before.")))
return return
if (self.ip_address, self.port) in squirrel.addresses: if (self.ip_address, self.port) in user.addresses:
# This case should never happen (and in our protocol it is not possible), # This case should never happen (and in our protocol it is not possible),
# but we include this test as a security measure. # but we include this test as a security measure.
return return
if not (str(self.ip_address), self.port) in squirrel.hazelnuts: if not (str(self.ip_address), self.port) in user.neighbours:
hazelnut = squirrel.new_hazel(str(self.ip_address), self.port) peer = user.new_peer(str(self.ip_address), self.port)
hazelnut.potential = True peer.potential = True
squirrel.update_hazelnut_table(hazelnut) user.update_peer_table(peer)
# squirrel.add_system_message(f"New potential friend {self.ip_address}:{self.port}!") # user.add_system_message(f"New potential friend {self.ip_address}:{self.port}!")
@staticmethod @staticmethod
def construct(address: str, port: int) -> "NeighbourTLV": def construct(address: str, port: int) -> "NeighbourTLV":
tlv = NeighbourTLV() tlv = NeighbourTLV()
tlv.type = 3 tlv.type = 3
tlv.length = 18 # A priori... tlv.length = 18
tlv.ip_address = IPv6Address(address) tlv.ip_address = IPv6Address(address)
tlv.port = port tlv.port = port
return tlv return tlv
@ -291,53 +294,53 @@ class DataTLV(TLV):
socket.htonl(self.nonce).to_bytes(4, sys.byteorder) + \ socket.htonl(self.nonce).to_bytes(4, sys.byteorder) + \
self.data self.data
def handle(self, squirrel: Any, sender: Any) -> None: def handle(self, user: Any, sender: Any) -> None:
""" """
A message has been sent. We log it. A message has been sent. We log it.
""" """
if not sender.active or not sender.symmetric or not sender.id: if not sender.active or not sender.symmetric or not sender.id:
# It doesn't say hello, we don't listen to it # It doesn't say hello, we don't listen to it
squirrel.send_packet(sender, Packet.construct(WarningTLV.construct( user.send_packet(sender, Packet.construct(WarningTLV.construct(
"You are not my neighbour, I don't listen to your DataTLV. Please say me Hello before."))) "You are not my neighbour, I don't listen to your DataTLV. Please say me Hello before.")))
return return
msg = self.data.decode('UTF-8') msg = self.data.decode('UTF-8')
# Acknowledge the packet # Acknowledge the packet
squirrel.send_packet(sender, Packet.construct(AckTLV.construct(self.sender_id, self.nonce))) user.send_packet(sender, Packet.construct(AckTLV.construct(self.sender_id, self.nonce)))
if not squirrel.receive_message_from(self, msg, self.sender_id, self.nonce, sender): if not user.receive_message_from(self, msg, self.sender_id, self.nonce, sender):
# The message was already received, do not print it on screen # The message was already received, do not print it on screen
squirrel.add_system_message(f"I was inundated a message which I already knew {self.sender_id, self.nonce}") user.add_system_message(f"I was inundated a message which I already knew {self.sender_id, self.nonce}")
return return
nickname_match = re.match("(.*): (.*)", msg) nickname_match = re.match("(.*): (.*)", msg)
if nickname_match is None: if nickname_match is None:
squirrel.send_packet(sender, Packet.construct(WarningTLV.construct( user.send_packet(sender, Packet.construct(WarningTLV.construct(
"Unable to retrieve your username. Please use the syntax 'nickname: message'"))) "Unable to retrieve your username. Please use the syntax 'nickname: message'")))
else: else:
nickname = nickname_match.group(1) nickname = nickname_match.group(1)
author = squirrel.find_hazelnut_by_id(self.sender_id) author = user.find_peer_by_id(self.sender_id)
if author: if author:
if author.nickname is None: if author.nickname is None:
author.nickname = nickname author.nickname = nickname
elif author.nickname != nickname: elif author.nickname != nickname:
squirrel.send_packet(author, Packet.construct(WarningTLV.construct( user.send_packet(author, Packet.construct(WarningTLV.construct(
"It seems that you used two different nicknames. " "It seems that you used two different nicknames. "
f"Known nickname: {author.nickname}, found: {nickname}"))) f"Known nickname: {author.nickname}, found: {nickname}")))
author.nickname = nickname author.nickname = nickname
@staticmethod @staticmethod
def construct(message: str, squirrel: Any) -> "DataTLV": def construct(message: str, user: Any) -> "DataTLV":
tlv = DataTLV() tlv = DataTLV()
tlv.type = 4 tlv.type = 4
tlv.sender_id = squirrel.id if squirrel else 0 tlv.sender_id = user.id if user else 0
tlv.nonce = squirrel.incr_nonce if squirrel else 0 tlv.nonce = user.incr_nonce if user else 0
tlv.data = message.encode("UTF-8") tlv.data = message.encode("UTF-8")
tlv.length = 12 + len(tlv.data) tlv.length = 12 + len(tlv.data)
if squirrel: if user:
squirrel.incr_nonce += 1 user.incr_nonce += 1
return tlv return tlv
@ -360,18 +363,18 @@ class AckTLV(TLV):
self.sender_id.to_bytes(8, sys.byteorder) + \ self.sender_id.to_bytes(8, sys.byteorder) + \
socket.htonl(self.nonce).to_bytes(4, sys.byteorder) socket.htonl(self.nonce).to_bytes(4, sys.byteorder)
def handle(self, squirrel: Any, sender: Any) -> None: def handle(self, user: Any, sender: Any) -> None:
""" """
When an AckTLV is received, we know that we do not have to inundate that neighbour anymore. When an AckTLV is received, we know that we do not have to inundate that neighbour anymore.
""" """
if not sender.active or not sender.symmetric or not sender.id: if not sender.active or not sender.symmetric or not sender.id:
# It doesn't say hello, we don't listen to it # It doesn't say hello, we don't listen to it
squirrel.send_packet(sender, Packet.construct(WarningTLV.construct( user.send_packet(sender, Packet.construct(WarningTLV.construct(
"You are not my neighbour, I don't listen to your AckTLV. Please say me Hello before."))) "You are not my neighbour, I don't listen to your AckTLV. Please say me Hello before.")))
return return
squirrel.add_system_message(f"I received an AckTLV from {sender}") user.add_system_message(f"I received an AckTLV from {sender}")
squirrel.remove_from_inundation(sender, self.sender_id, self.nonce) user.remove_from_inundation(sender, self.sender_id, self.nonce)
@staticmethod @staticmethod
def construct(sender_id: int, nonce: int) -> "AckTLV": def construct(sender_id: int, nonce: int) -> "AckTLV":
@ -408,17 +411,17 @@ class GoAwayTLV(TLV):
self.code.value.to_bytes(1, sys.byteorder) + \ self.code.value.to_bytes(1, sys.byteorder) + \
self.message.encode("UTF-8")[:self.length - 1] self.message.encode("UTF-8")[:self.length - 1]
def handle(self, squirrel: Any, sender: Any) -> None: def handle(self, user: Any, sender: Any) -> None:
if not sender.active or not sender.symmetric or not sender.id: if not sender.active or not sender.symmetric or not sender.id:
# It doesn't say hello, we don't listen to it # It doesn't say hello, we don't listen to it
squirrel.send_packet(sender, Packet.construct(WarningTLV.construct( user.send_packet(sender, Packet.construct(WarningTLV.construct(
"You are not my neighbour, I don't listen to your GoAwayTLV. Please say me Hello before."))) "You are not my neighbour, I don't listen to your GoAwayTLV. Please say me Hello before.")))
return return
if sender.active: if sender.active:
sender.active = False sender.active = False
squirrel.update_hazelnut_table(sender) user.update_peer_table(sender)
squirrel.add_system_message("Some told me that he went away : " + self.message) user.add_system_message("Some told me that he went away : " + self.message)
@staticmethod @staticmethod
def construct(ga_type: GoAwayType, message: str) -> "GoAwayTLV": def construct(ga_type: GoAwayType, message: str) -> "GoAwayTLV":
@ -445,9 +448,9 @@ class WarningTLV(TLV):
self.length.to_bytes(1, sys.byteorder) + \ self.length.to_bytes(1, sys.byteorder) + \
self.message.encode("UTF-8")[:self.length] self.message.encode("UTF-8")[:self.length]
def handle(self, squirrel: Any, sender: Any) -> None: def handle(self, user: Any, sender: Any) -> None:
squirrel.add_message(f"warning: *A client warned you: {self.message}*" user.add_message(f"warning: *A client warned you: {self.message}*"
if not squirrel.squinnondation.no_markdown else if not user.squinnondation.no_markdown else
f"warning: A client warned you: {self.message}") f"warning: A client warned you: {self.message}")
@staticmethod @staticmethod

1114
squinnondation/peers.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@ import curses
from argparse import ArgumentParser from argparse import ArgumentParser
from typing import Any from typing import Any
from .hazel import Hazelnut, Squirrel from .peers import Peer, User
from .messages import Packet, HelloTLV from .messages import Packet, HelloTLV
from .term_manager import TermManager from .term_manager import TermManager
@ -59,28 +59,28 @@ class Squinnondation:
nickname = screen.getstr(225).decode("UTF-8") # Limit nickname length to be included in a DataTLV nickname = screen.getstr(225).decode("UTF-8") # Limit nickname length to be included in a DataTLV
curses.noecho() curses.noecho()
squirrel = Squirrel(instance, nickname) user = User(instance, nickname)
if not squirrel.squinnondation.no_emoji: if not user.squinnondation.no_emoji:
# Check that the emoji lib is installed # Check that the emoji lib is installed
try: try:
import emoji import emoji
_ = emoji _ = emoji
except ImportError: except ImportError:
squirrel.squinnondation.no_emoji = True user.squinnondation.no_emoji = True
squirrel.add_system_message("Warning: the emoji lib is not installed. The support will be disabled." user.add_system_message("Warning: the emoji lib is not installed. The support will be disabled."
"To use them, please consider to install the emoji package.") "To use them, please consider to install the emoji package.")
squirrel.refresh_history() user.refresh_history()
squirrel.refresh_input() user.refresh_input()
if not instance.no_emoji: if not instance.no_emoji:
squirrel.refresh_emoji_pad() user.refresh_emoji_pad()
if instance.args.client_address and instance.args.client_port: if instance.args.client_address and instance.args.client_port:
hazelnut = Hazelnut(address=instance.args.client_address, port=instance.args.client_port) peer = Peer(address=instance.args.client_address, port=instance.args.client_port)
htlv = HelloTLV().construct(8, squirrel) htlv = HelloTLV().construct(8, user)
pkt = Packet().construct(htlv) pkt = Packet().construct(htlv)
squirrel.send_packet(hazelnut, pkt) user.send_packet(peer, pkt)
squirrel.start_threads() user.start_threads()
squirrel.wait_for_key() user.wait_for_key()