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
#[macro_use]
extern crate slog;

use std::io;
use std::net;
use std::error::Error;

pub mod options;
pub mod packet;
mod packetreader;
mod packetwriter;
pub mod rrq;

use self::options::Options;
use self::packet::{Filename, Packet, TransferMode};


/// Starts a TFTP server at the given address.
///
/// Well-formed requests are passed to `handler`, and all logging is
/// handled by `logger`.
pub fn serve(
    addr: net::SocketAddr, handler: &Handler, logger: &slog::Logger)
    -> io::Result<()>
{
    let socket = net::UdpSocket::bind(addr)?;
    info!(logger, "Listening"; "address" => format!("{}", addr));

    // RFC-2347 says "The maximum size of a request packet is 512 octets."
    let mut bufin = [0; 512];
    let mut bufout = [0; 4 + 512];
    loop {
        match socket.recv_from(&mut bufin) {
            Ok((size, src)) => {
                match Packet::parse(&mut bufin[..size]) {
                    Ok(packet) => {
                        match handler.handle(addr, src, packet) {
                            Some(packet) => {
                                let size = packet.write(&mut bufout)?;
                                socket.send_to(&bufout[..size], &src)?;
                            },
                            None => {},
                        };
                    },
                    Err(error) => warn!(
                        logger, "Ignoring malformed packet";
                        "error" => error.description()),
                }
            },
            Err(error) => return Err(error),
        }
    };
}


/// A TFTP handler to which requests are passed once they've been
/// parsed. A handler can choose to ignore, reject (with an error), or
/// serve each request that comes in.
pub trait Handler {

    /// Handle a new, well-formed, TFTP request.
    ///
    /// The default implementation calls
    /// [`handle_rrq`](#method.handle_rrq) for a read request and
    /// [`handle_wrq`](#method.handle_wrq) for a write request, and
    /// [`handle_other`](#method.handle_other) for everything else.
    ///
    /// In case of an error, this can return a `Packet` representing the
    /// error to be sent to the other side. For example:
    ///
    /// ```
    /// # use allenap_libtftp::packet;
    /// Some(packet::Packet::Error(
    ///     packet::ErrorCode::AccessViolation,
    ///     packet::ErrorMessage("read not supported".to_owned()),
    /// ));
    /// ```
    ///
    /// Use this when the error occurs prior the commencing the
    /// transfer; once the transfer has begin, send errors via the
    /// channel created for the transfer.
    fn handle(
        &self, local: net::SocketAddr, remote: net::SocketAddr, packet: Packet)
        -> Option<Packet>
    {
        match packet {
            Packet::Read(filename, txmode, options) =>
                self.handle_rrq(local, remote, filename, txmode, options),
            Packet::Write(filename, txmode, options) =>
                self.handle_wrq(local, remote, filename, txmode, options),
            packet =>
                self.handle_other(local, remote, packet),
        }
    }

    /// Handle a read request (`RRQ`).
    ///
    /// By default this is rejected as an access violation. Implementors
    /// can define something more interesting.
    fn handle_rrq(
        &self, _local: net::SocketAddr, _remote: net::SocketAddr,
        _filename: Filename, _txmode: TransferMode, _options: Options)
        -> Option<Packet>
    {
        Some(Packet::Error(
            packet::ErrorCode::AccessViolation,
            packet::ErrorMessage("read not supported".to_owned()),
        ))
    }

    /// Handle a write request (`WRQ`).
    ///
    /// By default this is rejected as an access violation. Implementors
    /// can define something more interesting.
    fn handle_wrq(
        &self, _local: net::SocketAddr, _remote: net::SocketAddr,
        _filename: Filename, _txmode: TransferMode, _options: Options)
        -> Option<Packet>
    {
        Some(Packet::Error(
            packet::ErrorCode::AccessViolation,
            packet::ErrorMessage("write not supported".to_owned()),
        ))
    }

    /// Handle all other requests.
    ///
    /// By default these are completely ignored. The TFTP specs do not
    /// define request types other than `RRQ` and `WRQ` so this might be
    /// a misdirected or corrupted packet. Implementors may want to log
    /// this.
    fn handle_other(
        &self, _local: net::SocketAddr, _remote: net::SocketAddr,
        _packet: Packet)
        -> Option<Packet>
    {
        None  // Ignore.
    }

}


/// Bind a new UDP socket at the given address.
fn make_socket(peer: net::SocketAddr) -> io::Result<net::UdpSocket> {
    match peer {
        net::SocketAddr::V4(_) => net::UdpSocket::bind(("0.0.0.0", 0)),
        net::SocketAddr::V6(_) => net::UdpSocket::bind(("::", 0)),
    }
}