- 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
This commit is contained in:
Czar.Echavez
2023-10-05 16:24:48 +01:00
parent 4699e87ea8
commit be6b904db9
6 changed files with 30 additions and 74 deletions

View File

@@ -97,6 +97,9 @@ class FTPClient(FTPServiceABC):
self._connect_to_server( self._connect_to_server(
dest_ip_address=dest_ip_address, dest_port=dest_port, session_id=session_id, is_reattempt=True 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( def _disconnect_from_server(
self, dest_ip_address: Optional[IPv4Address] = None, dest_port: Optional[Port] = Port.FTP 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") self.sys_log.error(f"{self.name}: File {src_folder_name}/{src_file_name} does not exist in FTP server")
return False 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: def receive(self, payload: FTPPacket, session_id: Optional[str] = None, **kwargs) -> bool:
""" """
Receives a payload from the SessionManager. Receives a payload from the SessionManager.
@@ -285,5 +264,8 @@ class FTPClient(FTPServiceABC):
self.sys_log.error(f"{payload} is not an FTP packet") self.sys_log.error(f"{payload} is not an FTP packet")
return False return False
if payload.status_code is None:
return False
self._process_ftp_command(payload=payload, session_id=session_id) self._process_ftp_command(payload=payload, session_id=session_id)
return True return True

View File

@@ -38,9 +38,11 @@ class FTPServer(FTPServiceABC):
:param: session_id: session ID linked to the FTP Packet. Optional. :param: session_id: session ID linked to the FTP Packet. Optional.
:type: session_id: Optional[str] :type: session_id: Optional[str]
""" """
# error code by default
payload.status_code = FTPStatusCode.ERROR
# if server service is down, return error # if server service is down, return error
if self.operating_state != ServiceOperatingState.RUNNING: if self.operating_state != ServiceOperatingState.RUNNING:
payload.status_code = FTPStatusCode.ERROR
self.sys_log.error("FTP Server not running") self.sys_log.error("FTP Server not running")
return payload return payload
@@ -61,9 +63,13 @@ class FTPServer(FTPServiceABC):
payload.status_code = FTPStatusCode.OK payload.status_code = FTPStatusCode.OK
return payload return payload
self.sys_log.error(f"Invalid Port {payload.ftp_command_args}")
return payload
if payload.ftp_command == FTPCommand.QUIT: if payload.ftp_command == FTPCommand.QUIT:
self.connections.pop(session_id) self.connections.pop(session_id)
payload.status_code = FTPStatusCode.OK payload.status_code = FTPStatusCode.OK
return payload
return super()._process_ftp_command(payload=payload, session_id=session_id, **kwargs) 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") self.sys_log.error(f"{payload} is not an FTP packet")
return False 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) self.send(self._process_ftp_command(payload=payload, session_id=session_id), session_id)
return True return True

View File

@@ -1,10 +1,8 @@
from enum import Enum from enum import Enum
from ipaddress import IPv4Address from typing import Dict, Optional
from typing import Any, Dict, Optional
from primaite import getLogger from primaite import getLogger
from primaite.simulator.core import Action, ActionManager from primaite.simulator.core import Action, ActionManager
from primaite.simulator.network.transmission.transport_layer import Port
from primaite.simulator.system.software import IOSoftware from primaite.simulator.system.software import IOSoftware
_LOGGER = getLogger(__name__) _LOGGER = getLogger(__name__)
@@ -74,48 +72,6 @@ class Service(IOSoftware):
""" """
pass 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: def stop(self) -> None:
"""Stop the service.""" """Stop the service."""
if self.operating_state in [ServiceOperatingState.RUNNING, ServiceOperatingState.PAUSED]: if self.operating_state in [ServiceOperatingState.RUNNING, ServiceOperatingState.PAUSED]:

View File

@@ -78,12 +78,13 @@ class WebServer(Service):
response = HTTPResponsePacket(status_code=HTTPStatusCode.BAD_REQUEST, payload=payload) response = HTTPResponsePacket(status_code=HTTPStatusCode.BAD_REQUEST, payload=payload)
try: try:
parsed_url = urlparse(payload.request_url) 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 # query succeeded
response.status_code = HTTPStatusCode.OK response.status_code = HTTPStatusCode.OK
if parsed_url.path.startswith("/users"): if path.startswith("users"):
# get data from DatabaseServer # get data from DatabaseServer
db_client: DatabaseClient = self.software_manager.software["DatabaseClient"] db_client: DatabaseClient = self.software_manager.software["DatabaseClient"]
# get all users # get all users

View File

@@ -25,7 +25,11 @@ def dns_server() -> Node:
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def dns_client() -> Node: def dns_client() -> Node:
node = Computer( 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 return node

View File

@@ -5,7 +5,7 @@ import pytest
from primaite.simulator.network.hardware.base import Node from primaite.simulator.network.hardware.base import Node
from primaite.simulator.network.hardware.nodes.computer import Computer from primaite.simulator.network.hardware.nodes.computer import Computer
from primaite.simulator.network.hardware.nodes.server import Server 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.network_layer import IPProtocol
from primaite.simulator.network.transmission.transport_layer import Port from primaite.simulator.network.transmission.transport_layer import Port
from primaite.simulator.system.services.ftp.ftp_client import FTPClient 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, "file_size": 24,
}, },
packet_payload_size=24, packet_payload_size=24,
status_code=FTPStatusCode.OK,
) )
ftp_client_service: FTPClient = ftp_client.software_manager.software["FTPClient"] ftp_client_service: FTPClient = ftp_client.software_manager.software["FTPClient"]