Skip to main content

libunftp/server/
chancomms.rs

1//! Contains code pertaining to the communication between the data and control channels.
2#![cfg_attr(not(feature = "proxy_protocol"), allow(dead_code, unused_imports))]
3
4use super::session::SharedSession;
5use crate::{
6    auth::UserDetail,
7    server::controlchan::Reply,
8    server::session::TraceId,
9    storage::{self, StorageBackend},
10};
11use std::fmt;
12use thiserror::Error;
13use tokio::sync::mpsc::{Receiver, Sender};
14use tokio::sync::oneshot;
15
16// Commands that can be sent to the data channel / data loop.
17#[derive(PartialEq, Eq, Debug)]
18pub enum DataChanMsg {
19    ExternalCommand(DataChanCmd),
20    Abort,
21}
22
23#[derive(PartialEq, Eq, Debug)]
24pub enum DataChanCmd {
25    Retr {
26        /// The path to the file the client would like to retrieve.
27        path: String,
28    },
29    Stor {
30        /// The path to the file the client would like to store.
31        path: String,
32    },
33    Appe {
34        /// The path to the file the client would like to append to.
35        path: String,
36    },
37    List {
38        /// Arguments passed along with the list command.
39        options: Option<String>,
40        /// The path of the file/directory the clients wants to list
41        path: Option<String>,
42    },
43    Nlst {
44        /// The path of the file/directory the clients wants to list.
45        path: Option<String>,
46    },
47    Mlsd {
48        /// The path of the directory the clients wants to list.
49        path: Option<String>,
50    },
51}
52
53impl DataChanCmd {
54    /// Returns the path the command pertains to
55    pub fn path(&self) -> Option<String> {
56        match self {
57            DataChanCmd::Retr { path, .. } => Some(path.clone()),
58            DataChanCmd::Stor { path, .. } => Some(path.clone()),
59            DataChanCmd::Appe { path, .. } => Some(path.clone()),
60            DataChanCmd::List { path, .. } => path.clone(),
61            DataChanCmd::Mlsd { path } => path.clone(),
62            DataChanCmd::Nlst { path } => path.clone(),
63        }
64    }
65}
66
67/// Messages that can be sent to the control channel loop.
68#[derive(Debug)]
69#[allow(dead_code)]
70pub enum ControlChanMsg {
71    /// Permission Denied
72    PermissionDenied,
73    /// File not found
74    NotFound,
75    /// Data was successfully sent to the client during a GET
76    SentData {
77        /// The path as specified by the client
78        path: String,
79        /// The number of bytes transferred
80        bytes: u64,
81    },
82    /// We've written the data from the client to the StorageBackend
83    WrittenData {
84        /// The path as specified by the client
85        path: String,
86        /// The number of bytes transferred
87        bytes: u64,
88    },
89    /// Data connection was unexpectedly closed
90    ConnectionReset,
91    /// Data connection was closed on purpose or not on purpose. We don't know, but that is FTP
92    DataConnectionClosedAfterStor,
93    /// Failed to write data to disk
94    WriteFailed,
95    /// Listed the directory successfully
96    DirectorySuccessfullyListed,
97    /// Failed to list the directory contents
98    DirectoryListFailure,
99    /// Successfully cwd
100    CwdSuccess,
101    /// File successfully deleted
102    DelFileSuccess {
103        /// The path as specified by the client
104        path: String,
105    },
106    /// File successfully deleted
107    RmDirSuccess {
108        /// The path as specified by the client
109        path: String,
110    },
111    /// File successfully deleted
112    RenameSuccess {
113        /// The old path as specified by the client
114        old_path: String,
115        /// The new path as specified by the client
116        new_path: String,
117    },
118    /// Failed to delete file
119    DelFail,
120    /// Quit the client connection
121    ExitControlLoop,
122    /// Successfully created directory
123    MkDirSuccess { path: String },
124    /// Failed to crate directory
125    MkdirFail,
126    /// Authentication successful
127    AuthSuccess { username: String, trace_id: TraceId },
128    /// Authentication failed
129    AuthFailed,
130    /// Sent to switch the control channel to TLS/SSL mode.
131    SecureControlChannel,
132    /// Sent to switch the control channel from TLS/SSL mode back to plaintext.
133    PlaintextControlChannel,
134    /// Errors coming from the storage backend
135    StorageError(storage::Error),
136    /// Reply on the command channel
137    CommandChannelReply(Reply),
138}
139
140impl fmt::Display for ControlChanMsg {
141    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
142        fmt::Debug::fmt(self, f)
143    }
144}
145
146/// An error that occurred during port allocation
147#[derive(Error, Debug)]
148#[error("Could not allocate port")]
149pub struct PortAllocationError;
150
151// ProxyLoopMsg is sent to the proxy loop when proxy protocol mode is enabled. See the
152// Server::proxy_protocol_mode and Server::listen_proxy_protocol_mode methods.
153#[derive(Debug)]
154pub(crate) enum SwitchboardMessage<Storage, User>
155where
156    Storage: StorageBackend<User>,
157    User: UserDetail,
158{
159    /// Command to assign a data port to a session
160    AssignDataPortCommand(SharedSession<Storage, User>, oneshot::Sender<Result<Reply, PortAllocationError>>),
161    /// Command to clean up an active data channel (used when exiting the control loop)
162    CloseDataPortCommand(SharedSession<Storage, User>),
163}
164
165pub(crate) type SwitchboardSender<Storage, User> = Sender<SwitchboardMessage<Storage, User>>;
166pub(crate) type SwitchboardReceiver<Storage, User> = Receiver<SwitchboardMessage<Storage, User>>;