Cleanup
This commit is contained in:
parent
9060cbd74f
commit
2aff61927b
7 changed files with 132 additions and 53 deletions
6
.github/workflows/unit.yml
vendored
6
.github/workflows/unit.yml
vendored
|
|
@ -17,9 +17,9 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
- name: Build
|
||||
- name: Build client
|
||||
run: cargo build --features client --verbose
|
||||
- name: Run tests
|
||||
- name: Test
|
||||
run: cargo test --verbose
|
||||
- name: test client
|
||||
- name: Test client
|
||||
run: cargo test --features client --verbose
|
||||
|
|
|
|||
|
|
@ -43,13 +43,15 @@ tftpc --help
|
|||
```
|
||||
|
||||
To connect the client to a tftp server running on IP address `127.0.0.1`, read-only, on port `1234` and download a file named `example.file`
|
||||
|
||||
```bash
|
||||
tftpc example.file -i 0.0.0.0 -p 1234 -d
|
||||
```
|
||||
|
||||
To connect the client to a tftp server running on IP address `127.0.0.1`, read-only, on port `1234` and upload a file named `example.file`
|
||||
|
||||
```bash
|
||||
tftpc ./example.file -i 0.0.0.0 -p 1234 -u
|
||||
tftpc example.file -i 0.0.0.0 -p 1234 -u
|
||||
```
|
||||
|
||||
## License
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ impl Client {
|
|||
timeout: config.timeout,
|
||||
mode: config.mode,
|
||||
filename: config.filename.clone(),
|
||||
save_path: config.save_directory.clone(),
|
||||
save_path: config.receive_directory.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -73,14 +73,20 @@ impl Client {
|
|||
} else {
|
||||
UdpSocket::bind((Ipv6Addr::UNSPECIFIED, 0))?
|
||||
};
|
||||
let file = self.filename.clone();
|
||||
|
||||
let file_name = self
|
||||
.filename
|
||||
.file_name()
|
||||
.ok_or("Invalid filename")?
|
||||
.to_str()
|
||||
.ok_or("Filename is not valid UTF-8")?
|
||||
.to_owned();
|
||||
let size = File::open(self.filename.clone())?.metadata()?.len() as usize;
|
||||
|
||||
Socket::send_to(
|
||||
&socket,
|
||||
&Packet::Wrq {
|
||||
filename: file.into_os_string().into_string().unwrap(),
|
||||
filename: file_name,
|
||||
mode: "octet".into(),
|
||||
options: vec![
|
||||
TransferOption {
|
||||
|
|
@ -98,7 +104,7 @@ impl Client {
|
|||
TransferOption {
|
||||
option: OptionType::TransferSize,
|
||||
value: size,
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
&self.remote_address,
|
||||
|
|
@ -174,7 +180,7 @@ impl Client {
|
|||
TransferOption {
|
||||
option: OptionType::TransferSize,
|
||||
value: 0,
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
&self.remote_address,
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ use std::time::Duration;
|
|||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct ClientConfig {
|
||||
/// Local IP address of the TFTP Server. (default: 127.0.0.1)
|
||||
/// Local IP address of the TFTP Client. (default: 127.0.0.1)
|
||||
pub remote_ip_address: IpAddr,
|
||||
/// Local Port number of the TFTP Server. (default: 69)
|
||||
/// Local Port number of the TFTP Client. (default: 69)
|
||||
pub port: u16,
|
||||
/// Blocksize to use during transfer. (default: 512)
|
||||
pub blocksize: usize,
|
||||
|
|
@ -35,8 +35,8 @@ pub struct ClientConfig {
|
|||
pub timeout: Duration,
|
||||
/// Upload or Download a file. (default: Download)
|
||||
pub mode: Mode,
|
||||
/// Directory where to save downloaded files. (default: Current Working Directory)
|
||||
pub save_directory: PathBuf,
|
||||
/// Download directory of the TFTP Client. (default: current working directory)
|
||||
pub receive_directory: PathBuf,
|
||||
/// File to Upload or Download.
|
||||
pub filename: PathBuf,
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ impl Default for ClientConfig {
|
|||
windowsize: DEFAULT_WINDOWSIZE,
|
||||
timeout: DEFAULT_TIMEOUT,
|
||||
mode: Mode::Download,
|
||||
save_directory: Default::default(),
|
||||
receive_directory: Default::default(),
|
||||
filename: Default::default(),
|
||||
}
|
||||
}
|
||||
|
|
@ -100,14 +100,14 @@ impl ClientConfig {
|
|||
return Err("Missing timeout after flag".into());
|
||||
}
|
||||
}
|
||||
"-sd" | "--save-directory" => {
|
||||
"-rd" | "--receive-directory" => {
|
||||
if let Some(dir_str) = args.next() {
|
||||
if !Path::new(&dir_str).exists() {
|
||||
return Err(format!("{dir_str} does not exist").into());
|
||||
}
|
||||
config.save_directory = dir_str.into();
|
||||
config.receive_directory = dir_str.into();
|
||||
} else {
|
||||
return Err("Missing save directory after flag".into());
|
||||
return Err("Missing receive directory after flag".into());
|
||||
}
|
||||
}
|
||||
"-u" | "--upload" => {
|
||||
|
|
@ -120,15 +120,17 @@ impl ClientConfig {
|
|||
println!("TFTP Client\n");
|
||||
println!("Usage: tftpd client <File> [OPTIONS]\n");
|
||||
println!("Options:");
|
||||
println!(" -i, --ip-address <IP ADDRESS>\tIp address of the server (default: 127.0.0.1)");
|
||||
println!(" -p, --port <PORT>\t\tPort of the server (default: 69)");
|
||||
println!(" -b, --blocksize <number>\tSets the blocksize (default: 512)");
|
||||
println!(" -w, --windowsize <number>\tSets the windowsize (default: 1)");
|
||||
println!(" -t, --timeout <seconds>\tSets the timeout in seconds (default: 5)");
|
||||
println!(" -u, --upload\t\t\tSets the client to upload mode, Ignores all previous download flags");
|
||||
println!(" -d, --download\t\tSet the client to download mode, Invalidates all previous upload flags");
|
||||
println!(" -sd, --save-directory <DIRECTORY>\tSet the directory to save files when in Download Mode (default: the directory setting)");
|
||||
println!(" -h, --help\t\t\tPrint help information");
|
||||
println!(" -i, --ip-address <IP ADDRESS>\t\tIp address of the server (default: 127.0.0.1)");
|
||||
println!(" -p, --port <PORT>\t\t\tPort of the server (default: 69)");
|
||||
println!(" -b, --blocksize <number>\t\tSets the blocksize (default: 512)");
|
||||
println!(" -w, --windowsize <number>\t\tSets the windowsize (default: 1)");
|
||||
println!(
|
||||
" -t, --timeout <seconds>\t\tSets the timeout in seconds (default: 5)"
|
||||
);
|
||||
println!(" -u, --upload\t\t\t\tSets the client to upload mode, Ignores all previous download flags");
|
||||
println!(" -d, --download\t\t\tSet the client to download mode, Invalidates all previous upload flags");
|
||||
println!(" -rd, --receive-directory <DIRECTORY>\tSet the directory to receive files when in Download mode (default: current working directory)");
|
||||
println!(" -h, --help\t\t\t\tPrint help information");
|
||||
process::exit(0);
|
||||
}
|
||||
file_name => {
|
||||
|
|
@ -166,7 +168,7 @@ mod tests {
|
|||
"-w",
|
||||
"2",
|
||||
"-t",
|
||||
"4"
|
||||
"4",
|
||||
]
|
||||
.iter()
|
||||
.map(|s| s.to_string()),
|
||||
|
|
@ -175,7 +177,7 @@ mod tests {
|
|||
|
||||
assert_eq!(config.remote_ip_address, Ipv4Addr::new(0, 0, 0, 0));
|
||||
assert_eq!(config.port, 1234);
|
||||
assert_eq!(config.save_directory, PathBuf::from("/"));
|
||||
assert_eq!(config.receive_directory, PathBuf::from("/"));
|
||||
assert_eq!(config.filename, PathBuf::from("test.file"));
|
||||
assert_eq!(config.windowsize, 2);
|
||||
assert_eq!(config.blocksize, 1024);
|
||||
|
|
@ -183,21 +185,12 @@ mod tests {
|
|||
assert_eq!(config.timeout, Duration::from_secs(4));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn parses_partial_config() {
|
||||
let config = ClientConfig::new(
|
||||
[
|
||||
"client",
|
||||
"test.file",
|
||||
"-d",
|
||||
"-b",
|
||||
"2048",
|
||||
"-p",
|
||||
"2000",
|
||||
]
|
||||
.iter()
|
||||
.map(|s| s.to_string()),
|
||||
["client", "test.file", "-d", "-b", "2048", "-p", "2000"]
|
||||
.iter()
|
||||
.map(|s| s.to_string()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use tftpd::{Client, ClientConfig, Mode};
|
|||
|
||||
fn main() {
|
||||
client(env::args()).unwrap_or_else(|err| {
|
||||
eprintln!("{err}");
|
||||
eprintln!("{err}");
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -27,7 +27,7 @@ fn client<T: Iterator<Item = String>>(args: T) -> Result<(), Box<dyn Error>> {
|
|||
);
|
||||
} else {
|
||||
println!(
|
||||
"Starting TFTP Client, downloading {} to {}",
|
||||
"Starting TFTP Client, downloading {} from {}",
|
||||
config.filename.display(),
|
||||
SocketAddr::new(config.remote_ip_address, config.port),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -118,23 +118,20 @@ impl Config {
|
|||
}
|
||||
"-h" | "--help" => {
|
||||
println!("TFTP Server Daemon\n");
|
||||
#[cfg(feature = "client")]
|
||||
println!("Usage: tftpd server [OPTIONS]\n");
|
||||
#[cfg(not(feature = "client"))]
|
||||
println!("Usage: tftpd [OPTIONS]\n");
|
||||
println!("Options:");
|
||||
println!(" -i, --ip-address <IP ADDRESS>\tSet the ip address of the server (default: 127.0.0.1)");
|
||||
println!(" -i, --ip-address <IP ADDRESS>\t\tSet the ip address of the server (default: 127.0.0.1)");
|
||||
println!(
|
||||
" -p, --port <PORT>\t\tSet the listening port of the server (default: 69)"
|
||||
" -p, --port <PORT>\t\t\tSet the listening port of the server (default: 69)"
|
||||
);
|
||||
println!(" -d, --directory <DIRECTORY>\tSet the serving directory (default: current working directory)");
|
||||
println!(" -d, --directory <DIRECTORY>\t\tSet the serving directory (default: current working directory)");
|
||||
println!(" -rd, --receive-directory <DIRECTORY>\tSet the directory to receive files to (default: the directory setting)");
|
||||
println!(" -sd, --send-directory <DIRECTORY>\tSet the directory to send files from (default: the directory setting)");
|
||||
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!(" --duplicate-packets <NUM>\tDuplicate all packets sent from the server (default: 0)");
|
||||
println!(" --overwrite\t\t\tOverwrite existing files (default: false)");
|
||||
println!(" -h, --help\t\t\tPrint help information");
|
||||
println!(" -s, --single-port\t\t\tUse a single port for both sending and receiving (default: false)");
|
||||
println!(" -r, --read-only\t\t\tRefuse all write requests, making the server read-only (default: false)");
|
||||
println!(" --duplicate-packets <NUM>\t\tDuplicate all packets sent from the server (default: 0)");
|
||||
println!(" --overwrite\t\t\t\tOverwrite existing files (default: false)");
|
||||
println!(" -h, --help\t\t\t\tPrint help information");
|
||||
process::exit(0);
|
||||
}
|
||||
"--duplicate-packets" => {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
use std::fs::{create_dir_all, remove_dir_all};
|
||||
use std::process::{Child, Command, ExitStatus};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
const SERVER_DIR: &str = "target/integration/server";
|
||||
const CLIENT_DIR: &str = "target/integration/client";
|
||||
|
|
@ -41,6 +43,7 @@ fn test_send() {
|
|||
initialize(format!("{SERVER_DIR}/{file_name}").as_str());
|
||||
|
||||
let _server = CommandRunner::new("target/debug/tftpd", &["-p", port, "-d", SERVER_DIR]);
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
let mut client = CommandRunner::new(
|
||||
"atftp",
|
||||
&[
|
||||
|
|
@ -65,6 +68,7 @@ fn test_receive() {
|
|||
initialize(format!("{CLIENT_DIR}/{file_name}").as_str());
|
||||
|
||||
let _server = CommandRunner::new("target/debug/tftpd", &["-p", port, "-d", SERVER_DIR]);
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
let mut client = CommandRunner::new(
|
||||
"atftp",
|
||||
&[
|
||||
|
|
@ -89,6 +93,7 @@ fn test_send_dir() {
|
|||
initialize(format!("{SERVER_DIR}/{file_name}").as_str());
|
||||
|
||||
let _server = CommandRunner::new("target/debug/tftpd", &["-p", port, "-sd", SERVER_DIR]);
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
let mut client = CommandRunner::new(
|
||||
"atftp",
|
||||
&[
|
||||
|
|
@ -104,6 +109,8 @@ fn test_send_dir() {
|
|||
|
||||
let status = client.wait();
|
||||
assert!(status.success());
|
||||
|
||||
check_files(file_name);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -113,6 +120,7 @@ fn test_receive_dir() {
|
|||
initialize(format!("{CLIENT_DIR}/{file_name}").as_str());
|
||||
|
||||
let _server = CommandRunner::new("target/debug/tftpd", &["-p", port, "-rd", SERVER_DIR]);
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
let mut client = CommandRunner::new(
|
||||
"atftp",
|
||||
&[
|
||||
|
|
@ -128,6 +136,8 @@ fn test_receive_dir() {
|
|||
|
||||
let status = client.wait();
|
||||
assert!(status.success());
|
||||
|
||||
check_files(file_name);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -140,6 +150,7 @@ fn test_send_ipv6() {
|
|||
"target/debug/tftpd",
|
||||
&["-i", "::1", "-p", port, "-d", SERVER_DIR],
|
||||
);
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
let mut client = CommandRunner::new(
|
||||
"atftp",
|
||||
&[
|
||||
|
|
@ -155,6 +166,8 @@ fn test_send_ipv6() {
|
|||
|
||||
let status = client.wait();
|
||||
assert!(status.success());
|
||||
|
||||
check_files(file_name);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -167,6 +180,7 @@ fn test_receive_ipv6() {
|
|||
"target/debug/tftpd",
|
||||
&["-i", "::1", "-p", port, "-d", SERVER_DIR],
|
||||
);
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
let mut client = CommandRunner::new(
|
||||
"atftp",
|
||||
&[
|
||||
|
|
@ -182,6 +196,8 @@ fn test_receive_ipv6() {
|
|||
|
||||
let status = client.wait();
|
||||
assert!(status.success());
|
||||
|
||||
check_files(file_name);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -191,6 +207,7 @@ fn test_send_single_port_options() {
|
|||
initialize(format!("{SERVER_DIR}/{file_name}").as_str());
|
||||
|
||||
let _server = CommandRunner::new("target/debug/tftpd", &["-p", port, "-d", SERVER_DIR, "-s"]);
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
let mut client = CommandRunner::new(
|
||||
"atftp",
|
||||
&[
|
||||
|
|
@ -208,6 +225,60 @@ fn test_send_single_port_options() {
|
|||
|
||||
let status = client.wait();
|
||||
assert!(status.success());
|
||||
|
||||
check_files(file_name);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_client_send() {
|
||||
let file_name = "client_send";
|
||||
let port = "6980";
|
||||
initialize(format!("{CLIENT_DIR}/{file_name}").as_str());
|
||||
|
||||
let _server = CommandRunner::new("target/debug/tftpd", &["-p", port, "-d", SERVER_DIR]);
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
|
||||
let mut client = CommandRunner::new(
|
||||
"target/debug/tftpc",
|
||||
&[
|
||||
format!("{CLIENT_DIR}/{file_name}").as_str(),
|
||||
"-p",
|
||||
port,
|
||||
"-u",
|
||||
],
|
||||
);
|
||||
|
||||
let status = client.wait();
|
||||
assert!(status.success());
|
||||
|
||||
check_files(file_name);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_client_receive() {
|
||||
let file_name = "client_receive";
|
||||
let port = "6981";
|
||||
initialize(format!("{SERVER_DIR}/{file_name}").as_str());
|
||||
|
||||
let _server = CommandRunner::new("target/debug/tftpd", &["-p", port, "-d", SERVER_DIR]);
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
|
||||
let mut client = CommandRunner::new(
|
||||
"target/debug/tftpc",
|
||||
&[
|
||||
file_name,
|
||||
"-p",
|
||||
port,
|
||||
"-d",
|
||||
"-rd",
|
||||
format!("{CLIENT_DIR}").as_str(),
|
||||
],
|
||||
);
|
||||
|
||||
let status = client.wait();
|
||||
assert!(status.success());
|
||||
|
||||
check_files(file_name);
|
||||
}
|
||||
|
||||
fn initialize(file_name: &str) {
|
||||
|
|
@ -235,3 +306,13 @@ fn create_file(file_name: &str) {
|
|||
.wait()
|
||||
.expect("error waiting for test file creation");
|
||||
}
|
||||
|
||||
fn check_files(file_name: &str) {
|
||||
let server_file = format!("{SERVER_DIR}/{file_name}");
|
||||
let client_file = format!("{CLIENT_DIR}/{file_name}");
|
||||
|
||||
let server_content = std::fs::read(server_file).expect("error reading server file");
|
||||
let client_content = std::fs::read(client_file).expect("error reading client file");
|
||||
|
||||
assert_eq!(server_content, client_content);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue