allenap_libtftp/
lib.rs

1#[macro_use]
2extern crate slog;
3
4use std::io;
5use std::net;
6use std::error::Error;
7
8pub mod options;
9pub mod packet;
10mod packetreader;
11mod packetwriter;
12pub mod rrq;
13
14use self::options::Options;
15use self::packet::{Filename, Packet, TransferMode};
16
17
18/// Starts a TFTP server at the given address.
19///
20/// Well-formed requests are passed to `handler`, and all logging is
21/// handled by `logger`.
22pub fn serve(
23    addr: net::SocketAddr, handler: &Handler, logger: &slog::Logger)
24    -> io::Result<()>
25{
26    let socket = net::UdpSocket::bind(addr)?;
27    info!(logger, "Listening"; "address" => format!("{}", addr));
28
29    // RFC-2347 says "The maximum size of a request packet is 512 octets."
30    let mut bufin = [0; 512];
31    let mut bufout = [0; 4 + 512];
32    loop {
33        match socket.recv_from(&mut bufin) {
34            Ok((size, src)) => {
35                match Packet::parse(&mut bufin[..size]) {
36                    Ok(packet) => {
37                        match handler.handle(addr, src, packet) {
38                            Some(packet) => {
39                                let size = packet.write(&mut bufout)?;
40                                socket.send_to(&bufout[..size], &src)?;
41                            },
42                            None => {},
43                        };
44                    },
45                    Err(error) => warn!(
46                        logger, "Ignoring malformed packet";
47                        "error" => error.description()),
48                }
49            },
50            Err(error) => return Err(error),
51        }
52    };
53}
54
55
56/// A TFTP handler to which requests are passed once they've been
57/// parsed. A handler can choose to ignore, reject (with an error), or
58/// serve each request that comes in.
59pub trait Handler {
60
61    /// Handle a new, well-formed, TFTP request.
62    ///
63    /// The default implementation calls
64    /// [`handle_rrq`](#method.handle_rrq) for a read request and
65    /// [`handle_wrq`](#method.handle_wrq) for a write request, and
66    /// [`handle_other`](#method.handle_other) for everything else.
67    ///
68    /// In case of an error, this can return a `Packet` representing the
69    /// error to be sent to the other side. For example:
70    ///
71    /// ```
72    /// # use allenap_libtftp::packet;
73    /// Some(packet::Packet::Error(
74    ///     packet::ErrorCode::AccessViolation,
75    ///     packet::ErrorMessage("read not supported".to_owned()),
76    /// ));
77    /// ```
78    ///
79    /// Use this when the error occurs prior the commencing the
80    /// transfer; once the transfer has begin, send errors via the
81    /// channel created for the transfer.
82    fn handle(
83        &self, local: net::SocketAddr, remote: net::SocketAddr, packet: Packet)
84        -> Option<Packet>
85    {
86        match packet {
87            Packet::Read(filename, txmode, options) =>
88                self.handle_rrq(local, remote, filename, txmode, options),
89            Packet::Write(filename, txmode, options) =>
90                self.handle_wrq(local, remote, filename, txmode, options),
91            packet =>
92                self.handle_other(local, remote, packet),
93        }
94    }
95
96    /// Handle a read request (`RRQ`).
97    ///
98    /// By default this is rejected as an access violation. Implementors
99    /// can define something more interesting.
100    fn handle_rrq(
101        &self, _local: net::SocketAddr, _remote: net::SocketAddr,
102        _filename: Filename, _txmode: TransferMode, _options: Options)
103        -> Option<Packet>
104    {
105        Some(Packet::Error(
106            packet::ErrorCode::AccessViolation,
107            packet::ErrorMessage("read not supported".to_owned()),
108        ))
109    }
110
111    /// Handle a write request (`WRQ`).
112    ///
113    /// By default this is rejected as an access violation. Implementors
114    /// can define something more interesting.
115    fn handle_wrq(
116        &self, _local: net::SocketAddr, _remote: net::SocketAddr,
117        _filename: Filename, _txmode: TransferMode, _options: Options)
118        -> Option<Packet>
119    {
120        Some(Packet::Error(
121            packet::ErrorCode::AccessViolation,
122            packet::ErrorMessage("write not supported".to_owned()),
123        ))
124    }
125
126    /// Handle all other requests.
127    ///
128    /// By default these are completely ignored. The TFTP specs do not
129    /// define request types other than `RRQ` and `WRQ` so this might be
130    /// a misdirected or corrupted packet. Implementors may want to log
131    /// this.
132    fn handle_other(
133        &self, _local: net::SocketAddr, _remote: net::SocketAddr,
134        _packet: Packet)
135        -> Option<Packet>
136    {
137        None  // Ignore.
138    }
139
140}
141
142
143/// Bind a new UDP socket at the given address.
144fn make_socket(peer: net::SocketAddr) -> io::Result<net::UdpSocket> {
145    match peer {
146        net::SocketAddr::V4(_) => net::UdpSocket::bind(("0.0.0.0", 0)),
147        net::SocketAddr::V6(_) => net::UdpSocket::bind(("::", 0)),
148    }
149}