async_tftp/server/
builder.rs

1use async_executor::Executor;
2use async_io::Async;
3use async_lock::Mutex;
4use std::collections::HashSet;
5use std::net::{SocketAddr, UdpSocket};
6use std::path::Path;
7use std::sync::Arc;
8use std::time::Duration;
9
10use super::handlers::{DirHandler, DirHandlerMode};
11use super::{Handler, ServerConfig, TftpServer};
12use crate::error::{Error, Result};
13
14/// TFTP server builder.
15pub struct TftpServerBuilder<H: Handler> {
16    handle: H,
17    addr: SocketAddr,
18    socket: Option<Async<UdpSocket>>,
19    timeout: Duration,
20    block_size_limit: Option<u16>,
21    window_size_limit: Option<u16>,
22    max_send_retries: u32,
23    ignore_client_timeout: bool,
24    ignore_client_block_size: bool,
25    ignore_client_window_size: bool,
26}
27
28impl TftpServerBuilder<DirHandler> {
29    /// Create new buidler with [`DirHandler`] that serves only read requests.
30    ///
31    /// [`DirHandler`]: handlers/struct.DirHandler.html
32    pub fn with_dir_ro<P>(dir: P) -> Result<TftpServerBuilder<DirHandler>>
33    where
34        P: AsRef<Path>,
35    {
36        let handler = DirHandler::new(dir, DirHandlerMode::ReadOnly)?;
37        Ok(TftpServerBuilder::with_handler(handler))
38    }
39
40    /// Create new buidler with [`DirHandler`] that serves only write requests.
41    pub fn with_dir_wo<P>(dir: P) -> Result<TftpServerBuilder<DirHandler>>
42    where
43        P: AsRef<Path>,
44    {
45        let handler = DirHandler::new(dir, DirHandlerMode::WriteOnly)?;
46        Ok(TftpServerBuilder::with_handler(handler))
47    }
48
49    /// Create new buidler with [`DirHandler`] that serves read and write requests.
50    pub fn with_dir_rw<P>(dir: P) -> Result<TftpServerBuilder<DirHandler>>
51    where
52        P: AsRef<Path>,
53    {
54        let handler = DirHandler::new(dir, DirHandlerMode::ReadWrite)?;
55        Ok(TftpServerBuilder::with_handler(handler))
56    }
57}
58
59impl<H: Handler> TftpServerBuilder<H> {
60    /// Create new builder with custom [`Handler`].
61    pub fn with_handler(handler: H) -> Self {
62        TftpServerBuilder {
63            handle: handler,
64            addr: "0.0.0.0:69".parse().unwrap(),
65            socket: None,
66            timeout: Duration::from_secs(3),
67            block_size_limit: None,
68            window_size_limit: None,
69            max_send_retries: 100,
70            ignore_client_timeout: false,
71            ignore_client_block_size: false,
72            ignore_client_window_size: false,
73        }
74    }
75
76    /// Set listening address.
77    ///
78    /// This is ignored if underling socket is set.
79    ///
80    /// **Default:** `0.0.0.0:69`
81    pub fn bind(self, addr: SocketAddr) -> Self {
82        TftpServerBuilder {
83            addr,
84            ..self
85        }
86    }
87
88    /// Set underling UDP socket.
89    pub fn socket(self, socket: Async<UdpSocket>) -> Self {
90        TftpServerBuilder {
91            socket: Some(socket),
92            ..self
93        }
94    }
95
96    /// Set underling UDP socket.
97    pub fn std_socket(self, socket: UdpSocket) -> Result<Self> {
98        let socket = Async::new(socket)?;
99
100        Ok(TftpServerBuilder {
101            socket: Some(socket),
102            ..self
103        })
104    }
105
106    /// Set retry timeout.
107    ///
108    /// Client can override this (RFC2349). If you want to enforce it you must
109    /// combine it [`ignore_client_timeout`](Self::ignore_client_timeout).
110    ///
111    /// This crate allows you to set non-standard timeouts (i.e. timeouts that are less
112    /// than a second). However if you choose to do it make sure you test it well in your
113    /// environment since client's behavior is undefined.
114    ///
115    /// **Default:** 3 seconds
116    pub fn timeout(self, timeout: Duration) -> Self {
117        TftpServerBuilder {
118            timeout,
119            ..self
120        }
121    }
122
123    /// Set maximum block size.
124    ///
125    /// Client can request a specific block size (RFC2348). Use this option if you
126    /// want to set a limit.
127    ///
128    /// **Real life scenario:** U-Boot does not support IP fragmentation and requests
129    /// block size of 1468. This works fine if your MTU is 1500 bytes, however if
130    /// you are accessing client through a VPN, then transfer will never start. Use
131    /// this option to workaround the problem.
132    pub fn block_size_limit(self, size: u16) -> Self {
133        TftpServerBuilder {
134            block_size_limit: Some(size),
135            ..self
136        }
137    }
138
139    /// Set maximum window size.
140    ///
141    /// Client can request a specific window size (RFC7440). Use this option if you
142    /// want to set a limit. Value of 1 is equivalent to window size option being disabled.
143    ///
144    /// Default: No limit.
145    pub fn window_size_limit(self, window_size: u16) -> Self {
146        TftpServerBuilder {
147            window_size_limit: Some(window_size),
148            ..self
149        }
150    }
151
152    /// Set maximum send retries for a data block.
153    ///
154    /// On timeout server will try to send the data block again. When retries are
155    /// reached for the specific data block the server closes the connection with
156    /// the client.
157    ///
158    /// Default: 100 retries.
159    pub fn max_send_retries(self, retries: u32) -> Self {
160        TftpServerBuilder {
161            max_send_retries: retries,
162            ..self
163        }
164    }
165
166    /// Ignore client's `timeout` option.
167    ///
168    /// With this you enforce server's timeout by ignoring client's
169    /// `timeout` option of RFC2349.
170    pub fn ignore_client_timeout(self) -> Self {
171        TftpServerBuilder {
172            ignore_client_timeout: true,
173            ..self
174        }
175    }
176
177    /// Ignore client's block size option.
178    ///
179    /// With this you can ignore client's `blksize` option of RFC2348.
180    /// This will enforce 512 block size that is defined in RFC1350.
181    pub fn ignore_client_block_size(self) -> Self {
182        TftpServerBuilder {
183            ignore_client_block_size: true,
184            ..self
185        }
186    }
187
188    /// Ignore client's window size option.
189    ///
190    /// With this you can ignore client's `window_size` option of RFC7440.
191    /// This will enforce the window size of 1 which behavior is equal to RFC1350
192    /// as per RFC7440.
193    pub fn ignore_client_window_size(self) -> Self {
194        TftpServerBuilder {
195            ignore_client_window_size: true,
196            ..self
197        }
198    }
199
200    /// Build [`TftpServer`].
201    pub async fn build(mut self) -> Result<TftpServer<H>> {
202        let socket = match self.socket.take() {
203            Some(socket) => socket,
204            None => Async::<UdpSocket>::bind(self.addr).map_err(Error::Bind)?,
205        };
206
207        let config = ServerConfig {
208            timeout: self.timeout,
209            block_size_limit: self.block_size_limit,
210            window_size_limit: self.window_size_limit,
211            max_send_retries: self.max_send_retries,
212            ignore_client_timeout: self.ignore_client_timeout,
213            ignore_client_block_size: self.ignore_client_block_size,
214            ignore_client_window_size: self.ignore_client_window_size,
215        };
216
217        let local_ip = socket.as_ref().local_addr()?.ip();
218        Ok(TftpServer {
219            socket,
220            handler: Arc::new(Mutex::new(self.handle)),
221            reqs_in_progress: Arc::new(Mutex::new(HashSet::new())),
222            ex: Executor::new(),
223            config,
224            local_ip,
225        })
226    }
227}