diff --git a/Cargo.toml b/Cargo.toml index 46e0b3e..8c39549 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "packeteer" description = "An attempt at a Rust library that can be used to assist in programmatically analysing, serving and handling received protocol packets." -version = "0.2.2" +version = "0.3.0" edition = "2021" authors = ["Celeste "] license = "LGPL-3.0-or-later" @@ -17,5 +17,6 @@ all-features = true [features] http1 = [] gemini = [] +ftp = [] [dependencies] diff --git a/README b/README index 0114807..f8df2ed 100644 --- a/README +++ b/README @@ -5,6 +5,8 @@ Packeteer is a library that can handle structurally organising protocol packets Supported protocols ----------------------- * HTTP/1.x +* Gemini +* FTP What Packeteer is not ------------------------- diff --git a/README.html b/README.html index 57eaef4..0e7ac0e 100644 --- a/README.html +++ b/README.html @@ -4,6 +4,8 @@

Supported protocols

What Packeteer is not

diff --git a/src/ftp.rs b/src/ftp.rs new file mode 100644 index 0000000..47240cf --- /dev/null +++ b/src/ftp.rs @@ -0,0 +1,106 @@ +//! A module for handling FTP operations. +//! It contains structures for FTP requests and responses as well as functions for generating, unpacking and constructing them. + +// unwrap_url not required as we aren't unwrapping Url structures. +use super::{Url, wrap_url}; +use std::str::FromStr; + +/// A structure for FTP requests. +/// Generated with packeteer::ftp::generate_request +#[derive(Debug)] +pub struct FTPRequest { + pub url: Url, + pub command: String, + pub arguments: Vec, +} + +/// A structure for FTP responses. +/// Generated with packeteer::ftp::generate_response +#[derive(Debug)] +pub struct FTPResponse { + pub code: i32, + pub message: String, +} + +/// A function for generating FTPRequest structures. +/// The command and arguments are split for convenience. +/// There is also a URL element. FTP doesn't have any place to put a URL however it is here to make thing easier for clients handling multiple servers simulatenously and/or proxies. +pub fn generate_request(url: &str, cmd: &str) -> FTPRequest { + let wurl = wrap_url(url); + let split: Vec<&str> = cmd.split(" ").collect(); + let mut args: Vec = vec![]; + for v in &split { + if v != &split[0] { + args.push(v.to_string()); + } + } + let req = FTPRequest { url: wurl, command: split[0].to_string(), arguments: args }; + return req +} + +/// A function for generating FTPResponse structures. +/// Messages associated to status codes are not provided by the module, unlike http1, and must be provided by the program using it. +/// Also unlike http1, ftp doesn't automatically check status code for integrity. +pub fn generate_response(code: i32, message: &str) -> FTPResponse { + let res = FTPResponse { code: code, message: message.to_string() }; + return res +} + +/// A function for converting FTPRequest structures into an FTP request string. +/// Unpacking functions are simply taking the data stored in the structure and concatenating it into a string so nothing changes apart from the conversion. +pub fn unpack_request(req: FTPRequest) -> String { + let mut string = format!("{}", req.command); + let empty: Vec = vec![]; + if req.arguments != empty { + for v in req.arguments { + string = format!("{} {}", string, v); + } + } + string = format!("{}\r\n", string); + return string.to_string() +} + +/// A function for converting FTPResponse structures into FTP response strings. +/// Unpacking functions are simply taking the data stored in the structure and concatenating it into a string so nothing changes apart from the conversion. +pub fn unpack_response(res: FTPResponse) -> String { + let string = format!("{} {}\r\n", res.code, res.message); + return string +} + +/// A fucntion for converting FTP request strings into FTPRequest. +/// The arguments are split, similarly to generate_request. +/// Since FTPRequest features a Url field, it will be set to an invalid address. +/// Construction functions aim to be as transparent as possible so apart from turning it into a structure, the request stays relatively unchanged. +pub fn construct_request(req: &str) -> FTPRequest { + let split: Vec<&str> = req.split("\r\n").collect(); + let split1: Vec<&str> = split[0].split(" ").collect(); + let string = split1[0]; + let mut args: Vec = vec![]; + for v in &split1 { + if v != &split1[0] { + args.push(v.to_string()); + } + } + let freq = FTPRequest { url: wrap_url("ftp://packeteer.invalid/dont-process-me"), command: string.to_string(), arguments: args }; + return freq +} + +/// A function for converting FTP response strings into FTPResponse. +/// Construction functions aim to be as transparent as possible so apart from turning it into a structure, the request stays relatively unchanged. +pub fn construct_response(res: &str) -> FTPResponse { + let split: Vec<&str> = res.split("\r\n").collect(); + let split1: Vec<&str> = split[0].split(" ").collect(); + let n: i32 = FromStr::from_str(split1[0]).unwrap(); + let mut message = "".to_string(); + for v in &split1 { + if v != &split1[0] { + if message == "".to_string() { + message = v.to_string(); + } else { + message = format!("{} {}", message, v); + } + } + } + let resp = FTPResponse { code: n, message: message.to_string() }; + return resp +} diff --git a/src/http1.rs b/src/http1.rs index f580ec8..ed8187c 100644 --- a/src/http1.rs +++ b/src/http1.rs @@ -34,6 +34,7 @@ pub fn generate_request(method: &str, host: &str, location: &str, body: &str) -> } /// A function for generating Http1Response structures. /// By default, packeteer generates HTTP/1.1 responses. +/// Invalid status codes will automatically be turned into code 500 (Internal Server Error) pub fn generate_response(code: i32, body: &str) -> Http1Response { let mut xcode = code; if code_to_string(xcode) == "500 Internal Server Error" { @@ -75,6 +76,7 @@ pub fn construct_request(request: &str) -> Http1Request { } /// A function for converting HTTP/1.x response strings into Http1Response. /// Construction functions aim to be as transparent as possible so apart from turning it into a structure, the response stays relatively unchanged. +/// Responses passed here with invalid status codes will keep their invalid status code. pub fn construct_response(response: &str) -> Http1Response { let split = response.split("\r\n"); let mut response = Http1Response { version: "".to_string(), status: "".to_string(), headers: vec![], body: "".to_string(), code: 0 }; diff --git a/src/lib.rs b/src/lib.rs index 2425f62..2e334b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,3 +82,6 @@ pub mod http1; #[cfg(feature = "gemini")] pub mod gemini; + +#[cfg(feature = "ftp")] +pub mod ftp;