From be6b904db9540fc55bf51ba645bc3804c7f0c12a Mon Sep 17 00:00:00 2001 From: "Czar.Echavez" Date: Thu, 5 Oct 2023 16:24:48 +0100 Subject: [PATCH] - Fixed FTP client server infinite recursion - ftp server and clients can be installed on the same node, this could cause a loop of requests - fixed tests broken by merged with dev --- .../system/services/ftp/ftp_client.py | 30 +++--------- .../system/services/ftp/ftp_server.py | 14 +++++- .../simulator/system/services/service.py | 46 +------------------ .../services/web_server/web_server_service.py | 5 +- .../_simulator/_system/_services/test_dns.py | 6 ++- .../_simulator/_system/_services/test_ftp.py | 3 +- 6 files changed, 30 insertions(+), 74 deletions(-) diff --git a/src/primaite/simulator/system/services/ftp/ftp_client.py b/src/primaite/simulator/system/services/ftp/ftp_client.py index 4575f985..b2a1e8bf 100644 --- a/src/primaite/simulator/system/services/ftp/ftp_client.py +++ b/src/primaite/simulator/system/services/ftp/ftp_client.py @@ -97,6 +97,9 @@ class FTPClient(FTPServiceABC): self._connect_to_server( dest_ip_address=dest_ip_address, dest_port=dest_port, session_id=session_id, is_reattempt=True ) + else: + self.sys_log.error(f"{self.name}: Unable to send FTPPacket") + return False def _disconnect_from_server( self, dest_ip_address: Optional[IPv4Address] = None, dest_port: Optional[Port] = Port.FTP @@ -247,30 +250,6 @@ class FTPClient(FTPServiceABC): self.sys_log.error(f"{self.name}: File {src_folder_name}/{src_file_name} does not exist in FTP server") return False - def send( - self, - payload: FTPPacket, - session_id: Optional[str] = None, - dest_ip_address: Optional[IPv4Address] = None, - dest_port: Optional[Port] = None, - **kwargs, - ) -> bool: - """ - Sends a payload to the SessionManager. - - :param payload: The payload to be sent. - :param dest_ip_address: The ip address of the payload destination. - :param dest_port: The port of the payload destination. - :param session_id: The Session ID the payload is to originate from. Optional. - - :return: True if successful, False otherwise. - """ - self.sys_log.info(f"{self.name}: Sending FTP {payload.ftp_command.name} {payload.ftp_command_args}") - - return super().send( - payload=payload, dest_ip_address=dest_ip_address, dest_port=dest_port, session_id=session_id, **kwargs - ) - def receive(self, payload: FTPPacket, session_id: Optional[str] = None, **kwargs) -> bool: """ Receives a payload from the SessionManager. @@ -285,5 +264,8 @@ class FTPClient(FTPServiceABC): self.sys_log.error(f"{payload} is not an FTP packet") return False + if payload.status_code is None: + return False + self._process_ftp_command(payload=payload, session_id=session_id) return True diff --git a/src/primaite/simulator/system/services/ftp/ftp_server.py b/src/primaite/simulator/system/services/ftp/ftp_server.py index 62358ff2..d93150e0 100644 --- a/src/primaite/simulator/system/services/ftp/ftp_server.py +++ b/src/primaite/simulator/system/services/ftp/ftp_server.py @@ -38,9 +38,11 @@ class FTPServer(FTPServiceABC): :param: session_id: session ID linked to the FTP Packet. Optional. :type: session_id: Optional[str] """ + # error code by default + payload.status_code = FTPStatusCode.ERROR + # if server service is down, return error if self.operating_state != ServiceOperatingState.RUNNING: - payload.status_code = FTPStatusCode.ERROR self.sys_log.error("FTP Server not running") return payload @@ -61,9 +63,13 @@ class FTPServer(FTPServiceABC): payload.status_code = FTPStatusCode.OK return payload + self.sys_log.error(f"Invalid Port {payload.ftp_command_args}") + return payload + if payload.ftp_command == FTPCommand.QUIT: self.connections.pop(session_id) payload.status_code = FTPStatusCode.OK + return payload return super()._process_ftp_command(payload=payload, session_id=session_id, **kwargs) @@ -73,5 +79,11 @@ class FTPServer(FTPServiceABC): self.sys_log.error(f"{payload} is not an FTP packet") return False + """ + Usually + """ + if payload.status_code is not None: + return False + self.send(self._process_ftp_command(payload=payload, session_id=session_id), session_id) return True diff --git a/src/primaite/simulator/system/services/service.py b/src/primaite/simulator/system/services/service.py index d79487a3..aa1d5031 100644 --- a/src/primaite/simulator/system/services/service.py +++ b/src/primaite/simulator/system/services/service.py @@ -1,10 +1,8 @@ from enum import Enum -from ipaddress import IPv4Address -from typing import Any, Dict, Optional +from typing import Dict, Optional from primaite import getLogger from primaite.simulator.core import Action, ActionManager -from primaite.simulator.network.transmission.transport_layer import Port from primaite.simulator.system.software import IOSoftware _LOGGER = getLogger(__name__) @@ -74,48 +72,6 @@ class Service(IOSoftware): """ pass - def send( - self, - payload: Any, - session_id: Optional[str] = None, - dest_ip_address: Optional[IPv4Address] = None, - dest_port: Optional[Port] = None, - **kwargs, - ) -> bool: - """ - Sends a payload to the SessionManager. - - :param payload: The payload to be sent. - :param dest_ip_address: The ip address of the payload destination. - :param dest_port: The port of the payload destination. - :param session_id: The Session ID the payload is to originate from. Optional. - - :return: True if successful, False otherwise. - """ - return super().send( - payload=payload, dest_ip_address=dest_ip_address, dest_port=dest_port, session_id=session_id, **kwargs - ) - - def receive( - self, - payload: Any, - session_id: Optional[str] = None, - **kwargs, - ) -> bool: - """ - Receives a payload from the SessionManager. - - The specifics of how the payload is processed and whether a response payload - is generated should be implemented in subclasses. - - :param: payload: The payload to send. - :param: session_id: The id of the session - - :return: True if successful, False otherwise. - """ - - pass - def stop(self) -> None: """Stop the service.""" if self.operating_state in [ServiceOperatingState.RUNNING, ServiceOperatingState.PAUSED]: diff --git a/src/primaite/simulator/system/services/web_server/web_server_service.py b/src/primaite/simulator/system/services/web_server/web_server_service.py index 68624930..59686388 100644 --- a/src/primaite/simulator/system/services/web_server/web_server_service.py +++ b/src/primaite/simulator/system/services/web_server/web_server_service.py @@ -78,12 +78,13 @@ class WebServer(Service): response = HTTPResponsePacket(status_code=HTTPStatusCode.BAD_REQUEST, payload=payload) try: parsed_url = urlparse(payload.request_url) + path = parsed_url.path.strip("/") - if parsed_url.path is None or len(parsed_url.path) < 1: + if len(path) < 1: # query succeeded response.status_code = HTTPStatusCode.OK - if parsed_url.path.startswith("/users"): + if path.startswith("users"): # get data from DatabaseServer db_client: DatabaseClient = self.software_manager.software["DatabaseClient"] # get all users diff --git a/tests/unit_tests/_primaite/_simulator/_system/_services/test_dns.py b/tests/unit_tests/_primaite/_simulator/_system/_services/test_dns.py index 31718387..dc6df5d4 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_services/test_dns.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_services/test_dns.py @@ -25,7 +25,11 @@ def dns_server() -> Node: @pytest.fixture(scope="function") def dns_client() -> Node: node = Computer( - hostname="dns_client", ip_address="192.168.1.11", subnet_mask="255.255.255.0", default_gateway="192.168.1.1" + hostname="dns_client", + ip_address="192.168.1.11", + subnet_mask="255.255.255.0", + default_gateway="192.168.1.1", + dns_server=IPv4Address("192.168.1.10"), ) return node diff --git a/tests/unit_tests/_primaite/_simulator/_system/_services/test_ftp.py b/tests/unit_tests/_primaite/_simulator/_system/_services/test_ftp.py index 3ccb0c99..d382b8dd 100644 --- a/tests/unit_tests/_primaite/_simulator/_system/_services/test_ftp.py +++ b/tests/unit_tests/_primaite/_simulator/_system/_services/test_ftp.py @@ -5,7 +5,7 @@ import pytest from primaite.simulator.network.hardware.base import Node from primaite.simulator.network.hardware.nodes.computer import Computer from primaite.simulator.network.hardware.nodes.server import Server -from primaite.simulator.network.protocols.ftp import FTPCommand, FTPPacket +from primaite.simulator.network.protocols.ftp import FTPCommand, FTPPacket, FTPStatusCode from primaite.simulator.network.transmission.network_layer import IPProtocol from primaite.simulator.network.transmission.transport_layer import Port from primaite.simulator.system.services.ftp.ftp_client import FTPClient @@ -78,6 +78,7 @@ def test_ftp_client_store_file(ftp_client): "file_size": 24, }, packet_payload_size=24, + status_code=FTPStatusCode.OK, ) ftp_client_service: FTPClient = ftp_client.software_manager.software["FTPClient"]