diff --git a/README.md b/README.md index cd8d5c9..c0b718d 100644 --- a/README.md +++ b/README.md @@ -26,10 +26,10 @@ cargo install tftpd tftpd --help ``` -To run the server on the IP address `0.0.0.0`, port `1234` in the `/home/user/tftp` directory: +To run the server on the IP address `0.0.0.0`, read-only, on port `1234` in the `/home/user/tftp` directory: ```bash -tftpd -i 0.0.0.0 -p 1234 -d "/home/user/tftp" +tftpd -i 0.0.0.0 -p 1234 -d "/home/user/tftp" --read-only ``` ## License diff --git a/src/config.rs b/src/config.rs index b4ac71a..72b0145 100644 --- a/src/config.rs +++ b/src/config.rs @@ -27,6 +27,8 @@ pub struct Config { pub directory: PathBuf, /// Use a single port for both sending and receiving. (default: false) pub single_port: bool, + /// Refuse all write requests, making the server read-only. (default: false) + pub read_only: bool, } impl Config { @@ -38,6 +40,7 @@ impl Config { port: 69, directory: env::current_dir().unwrap_or_else(|_| env::temp_dir()), single_port: false, + read_only: false, }; args.next(); @@ -71,6 +74,9 @@ impl Config { "-s" | "--single-port" => { config.single_port = true; } + "-r" | "--read-only" => { + config.read_only = true; + } "-h" | "--help" => { println!("TFTP Server Daemon\n"); println!("Usage: tftpd [OPTIONS]\n"); @@ -81,6 +87,7 @@ impl Config { ); println!(" -d, --directory \tSet the listening port of the server (default: Current Working Directory)"); println!(" -s, --single-port\t\tUse a single port for both sending and receiving (default: false)"); + println!(" -r, --read-only\t\tRefuse all write requests, making the server read-only (default: false)"); println!(" -h, --help\t\t\tPrint help information"); process::exit(0); } @@ -101,7 +108,7 @@ mod tests { #[test] fn parses_full_config() { let config = Config::new( - vec!["/", "-i", "0.0.0.0", "-p", "1234", "-d", "/", "-s"] + vec!["/", "-i", "0.0.0.0", "-p", "1234", "-d", "/", "-s", "-r"] .iter() .map(|s| s.to_string()), ) @@ -111,6 +118,7 @@ mod tests { assert_eq!(config.port, 1234); assert_eq!(config.directory, PathBuf::from_str("/").unwrap()); assert!(config.single_port); + assert!(config.read_only); } #[test] diff --git a/src/server.rs b/src/server.rs index 3e5ec7f..84a1adb 100644 --- a/src/server.rs +++ b/src/server.rs @@ -31,6 +31,7 @@ pub struct Server { socket: UdpSocket, directory: PathBuf, single_port: bool, + read_only: bool, largest_block_size: usize, clients: HashMap>, } @@ -44,6 +45,7 @@ impl Server { socket, directory: config.directory.clone(), single_port: config.single_port, + read_only: config.read_only, largest_block_size: DEFAULT_BLOCK_SIZE, clients: HashMap::new(), }; @@ -77,6 +79,22 @@ impl Server { mut options, .. } => { + if self.read_only { + if Socket::send_to( + &self.socket, + &Packet::Error { + code: ErrorCode::AccessViolation, + msg: "server is read-only".to_string(), + }, + &from, + ) + .is_err() + { + eprintln!("Could not send error packet"); + }; + eprintln!("Received invalid request"); + continue; + } println!("Receiving {filename} from {from}"); if let Err(err) = self.handle_wrq(filename.clone(), &mut options, &from) { eprintln!("Error while receiving file: {err}")