Finished /upload endpoint

This commit is contained in:
Random936 2024-12-23 17:47:33 -08:00
parent 0b2a4f130e
commit 7570739ba4
5 changed files with 103 additions and 8 deletions

40
Cargo.lock generated
View File

@ -114,6 +114,7 @@ dependencies = [
"matchit",
"memchr",
"mime",
"multer",
"percent-encoding",
"pin-project-lite",
"rustversion",
@ -269,6 +270,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "encoding_rs"
version = "0.8.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
dependencies = [
"cfg-if",
]
[[package]]
name = "equivalent"
version = "1.0.1"
@ -572,6 +582,23 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "multer"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b"
dependencies = [
"bytes",
"encoding_rs",
"futures-util",
"http",
"httparse",
"memchr",
"mime",
"spin",
"version_check",
]
[[package]]
name = "object"
version = "0.36.4"
@ -830,6 +857,12 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "strsim"
version = "0.11.1"
@ -1032,6 +1065,7 @@ dependencies = [
"serde",
"serde_yaml",
"tokio",
"tokio-util",
"tower-http",
]
@ -1041,6 +1075,12 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"

View File

@ -6,11 +6,12 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
axum = "0.7.6"
axum = { version = "0.7.6", features = ["multipart"] }
clap = { version = "4.5.18", features = ["derive"] }
expanduser = "1.2.2"
if-addrs = "0.13.3"
serde = { version = "1.0.216", features = ["derive"] }
serde_yaml = "0.9.34"
tokio = { version = "1.40.0", features = ["full"] }
tokio-util = "0.7.13"
tower-http = { version = "0.6.2", features = ["fs", "trace"] }

View File

@ -1,5 +1,5 @@
use std::{io, fs};
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::collections::HashMap;
use expanduser::expanduser;
@ -12,8 +12,8 @@ pub fn load_config() -> Config {
#[derive(Deserialize)]
pub struct Config {
download_path: PathBuf,
upload_path: PathBuf,
download_path: String,
upload_path: String,
pub web_port: u16,
pub shell_port: u16,
shells: HashMap<String, String>
@ -32,11 +32,11 @@ impl Config {
}
pub fn get_download_path(&self) -> PathBuf {
self.download_path.clone()
expanduser(self.download_path.clone()).unwrap()
}
pub fn get_upload_path(&self) -> PathBuf {
self.upload_path.clone()
expanduser(self.upload_path.clone()).unwrap()
}
pub fn get_shell<S: Into<String>>(&self, key: S) -> Option<String> {

View File

@ -6,7 +6,7 @@ use tokio::net::TcpListener;
use tower_http::services::ServeDir;
use axum::{
middleware,
routing::get,
routing::{get, post},
Router
};
@ -15,6 +15,7 @@ mod print_dir;
mod config;
mod shells;
mod logging;
mod upload;
#[tokio::main]
async fn main() {
@ -30,7 +31,7 @@ async fn main() {
print_dir::print_interface(&args.interface, &port, &args.directory);
let app = Router::new()
//.route("/upload", post(upload_handler))
.route("/upload", post(upload::upload_handler))
.route("/shells/:shell", get(shells::shells_handler))
.nest_service("/download", ServeDir::new(conf.get_download_path()))
.nest_service("/", ServeDir::new(cwd))

View File

@ -0,0 +1,53 @@
use std::{
fs::File,
io::{BufWriter, Write},
path::{Path, Component}
};
use axum::{
http::StatusCode,
extract::Multipart,
response::IntoResponse
};
use crate::config;
fn path_is_valid(path: &str) -> bool {
let path = Path::new(path);
let mut components = path.components().peekable();
if let Some(first) = components.peek() {
if !matches!(first, Component::Normal(_)) {
return false;
}
}
components.count() == 1
}
pub async fn upload_handler(mut multipart: Multipart) -> impl IntoResponse {
let conf = config::load_config();
while let Some(field) = multipart.next_field().await.unwrap() {
let file_name = match field.name() {
Some(file) => file.to_string(),
None => continue
};
let data = field.bytes().await.unwrap();
let path = conf.get_upload_path().join(&file_name);
if !path_is_valid(&file_name) {
return (StatusCode::FORBIDDEN, format!("Directory Traversal: {}", path.display()));
}
let Ok(file) = File::create(&path) else {
return (StatusCode::FORBIDDEN, format!("Failed to create file: {}", path.display()));
};
let mut writer = BufWriter::new(file);
if let Err(e) = writer.write_all(&data) {
return (StatusCode::INTERNAL_SERVER_ERROR, format!("An error occurred when writing to {}: {}", path.display(), e))
}
}
(StatusCode::CREATED, "Successfully uploaded file(s).".to_string())
}