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>>;