1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
//! Contains code pertaining to the setup options that can be given to the [`Server`](crate::Server)

use bitflags::bitflags;
use std::time::Duration;
use std::{
    fmt::Formatter,
    fmt::{self, Debug, Display},
    net::{IpAddr, Ipv4Addr},
    ops::Range,
};

// Once we're sure about the types of these I think its good to expose it to the API user so that
// he/she can see what our server defaults are.
pub(crate) const DEFAULT_GREETING: &str = "Welcome to the libunftp FTP server";
pub(crate) const DEFAULT_IDLE_SESSION_TIMEOUT_SECS: u64 = 600;
pub(crate) const DEFAULT_PASSIVE_HOST: PassiveHost = PassiveHost::FromConnection;
pub(crate) const DEFAULT_PASSIVE_PORTS: Range<u16> = 49152..65535;
pub(crate) const DEFAULT_FTPS_REQUIRE: FtpsRequired = FtpsRequired::None;
pub(crate) const DEFAULT_FTPS_TRUST_STORE: &str = "./trusted.pem";

/// The option to [Server.passive_host](crate::Server::passive_host). It allows the user to specify how the IP address
/// communicated in the _PASV_ response is determined.
#[derive(Debug, PartialEq, Clone)]
pub enum PassiveHost {
    /// Use the IP address of the control connection
    FromConnection,
    /// Advertise this specific IP address
    Ip(Ipv4Addr),
    /// Resolve this DNS name into an IPv4 address.
    Dns(String),
    // We also be nice to have:
    // - PerUser(Box<dyn (Fn(Box<dyn UserDetail>) -> Ipv4Addr) + Send + Sync>) or something like
    //   that to allow a per user decision
}

impl Eq for PassiveHost {}

impl Default for PassiveHost {
    fn default() -> Self {
        PassiveHost::FromConnection
    }
}

impl From<Ipv4Addr> for PassiveHost {
    fn from(ip: Ipv4Addr) -> Self {
        PassiveHost::Ip(ip)
    }
}

impl From<[u8; 4]> for PassiveHost {
    fn from(ip: [u8; 4]) -> Self {
        PassiveHost::Ip(ip.into())
    }
}

impl From<&str> for PassiveHost {
    fn from(dns_or_ip: &str) -> Self {
        match dns_or_ip.parse() {
            Ok(IpAddr::V4(ip)) => PassiveHost::Ip(ip),
            _ => PassiveHost::Dns(dns_or_ip.to_string()),
        }
    }
}

/// The option to [Server.ftps_required](crate::Server::ftps_required). It allows the user to specify whether clients are required
/// to upgrade a to secure TLS connection i.e. use FTPS.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum FtpsRequired {
    /// All users, including anonymous must use FTPS
    All,
    /// All non-anonymous users requires FTPS.
    Accounts,
    /// FTPS not enforced.
    None, // would be nice to have a per-user setting also.
}

impl Eq for FtpsRequired {}

impl From<bool> for FtpsRequired {
    fn from(on: bool) -> Self {
        match on {
            true => FtpsRequired::All,
            false => FtpsRequired::None,
        }
    }
}

impl Display for FtpsRequired {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(
            f,
            "{}",
            match self {
                FtpsRequired::All => "All users, including anonymous, requires FTPS",
                FtpsRequired::Accounts => "All non-anonymous users requires FTPS",
                FtpsRequired::None => "FTPS not enforced",
            }
        )
    }
}

bitflags! {
    /// Used to configure TLS options employed for FTPS
    pub struct TlsFlags: u32 {
        /// Enables TLS version 1.2
        const V1_2               = 0b00000001;
        /// Enables TLS version 1.3
        const V1_3               = 0b00000010;
        /// Enables TLS session resumption via means of sever side session IDs.
        const RESUMPTION_SESS_ID = 0b00001000;
        /// Enables TLS session resumption via means tickets ([rfc5077](https://tools.ietf.org/html/rfc5077))
        const RESUMPTION_TICKETS = 0b00010000;
        /// Enables the latest safe TLS versions i.e. 1.2 and 1.3
        const LATEST_VERSIONS = Self::V1_2.bits | Self::V1_3.bits;
    }
}

impl Default for TlsFlags {
    fn default() -> TlsFlags {
        // Switch TLS 1.3 off by default since we still see a PUT bug with lftp when switching
        // session resumption on along with TLS 1.3.
        TlsFlags::V1_2 | TlsFlags::RESUMPTION_SESS_ID | TlsFlags::RESUMPTION_TICKETS
    }
}

/// The option to [Server.ftps_client_auth](crate::Server::ftps_client_auth). Tells if and how mutual TLS (client certificate
/// authentication) should be handled.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum FtpsClientAuth {
    /// Mutual TLS is switched off and the server won't ask the client for a certificate in the TLS
    /// protocol. This is the default.
    Off,
    /// Mutual TLS is on and whilst the server will request a certificate it will still proceed
    /// without one. If a certificate is sent by the client it will be validated against the
    /// configured trust anchors (see [Server::ftps_trust_store](crate::Server::ftps_trust_store)).
    Request,
    /// Mutual TLS is on, the server will request a certificate and it won't proceed without a
    /// client certificate that validates against the configured trust anchors (see
    /// [Server::ftps_trust_store](crate::Server::ftps_trust_store)).
    Require,
}

impl Eq for FtpsClientAuth {}

impl Default for FtpsClientAuth {
    fn default() -> FtpsClientAuth {
        FtpsClientAuth::Off
    }
}

impl From<bool> for FtpsClientAuth {
    fn from(on: bool) -> Self {
        match on {
            true => FtpsClientAuth::Require,
            false => FtpsClientAuth::Off,
        }
    }
}

/// The options for [Server.sitemd5](crate::Server::sitemd5).
/// Allow MD5 either to be used by all, logged in users only or no one.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum SiteMd5 {
    /// Enabled for all users, including anonymous
    All,
    /// Enabled for all non-anonymous users.
    Accounts,
    /// Disabled
    None, // would be nice to have a per-user setting also.
}

impl Default for SiteMd5 {
    fn default() -> SiteMd5 {
        SiteMd5::Accounts
    }
}

/// Tells how graceful shutdown should happen. An instance of this struct should be returned from
/// the future passed to [Server.shutdown_indicator](crate::Server::shutdown_indicator).
pub struct Shutdown {
    pub(crate) grace_period: Duration,
    //pub(crate) handle_new_connections: bool,
}

impl Shutdown {
    /// Creates a Shutdown instance with default values
    pub fn new() -> Self {
        Shutdown::default()
    }

    /// Defines how much time to allow for components to shut down before shutdown is forceful.
    pub fn grace_period(mut self, d: impl Into<Duration>) -> Self {
        self.grace_period = d.into();
        self
    }

    // /// Control channel connections will still be accepted for a while as connections
    // /// are drained. Clients connecting during this phase will receive an FTP error code.
    // pub fn handle_new_connections(mut self) -> Self {
    //     self.handle_new_connections = true;
    //     self
    // }
    //
    // /// Control channel connections will not be allowed during the shutdown phase.
    // pub fn block_new_connections(mut self) -> Self {
    //     self.handle_new_connections = false;
    //     self
    // }
}

impl Default for Shutdown {
    fn default() -> Shutdown {
        Shutdown {
            grace_period: Duration::from_secs(10),
            //handle_new_connections: false,
        }
    }
}