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