2026-02-21 01:55:41 +02:00
|
|
|
use std::{
|
2026-02-22 01:25:05 +02:00
|
|
|
fs,
|
2026-02-21 01:55:41 +02:00
|
|
|
io::Write,
|
|
|
|
|
net::{SocketAddr, TcpListener, TcpStream},
|
2026-02-22 01:25:05 +02:00
|
|
|
path::Path,
|
2026-02-21 01:55:41 +02:00
|
|
|
result,
|
|
|
|
|
str::FromStr,
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-22 13:09:41 +02:00
|
|
|
use clap::Parser;
|
2026-02-21 11:42:51 +02:00
|
|
|
|
2026-02-22 13:09:41 +02:00
|
|
|
use crate::{buffer::ByteBuffer, cli::CliArgs};
|
2026-02-21 11:42:51 +02:00
|
|
|
|
2026-02-22 13:09:41 +02:00
|
|
|
mod buffer;
|
|
|
|
|
mod cli;
|
2026-02-21 01:55:41 +02:00
|
|
|
|
|
|
|
|
type Result<T> = result::Result<T, ()>;
|
|
|
|
|
|
|
|
|
|
fn main() -> Result<()> {
|
2026-02-22 13:09:41 +02:00
|
|
|
env_logger::init();
|
|
|
|
|
|
|
|
|
|
let args = CliArgs::parse();
|
|
|
|
|
|
|
|
|
|
let ip = args.ip.unwrap_or_default();
|
|
|
|
|
let port = args.port.unwrap_or_default();
|
|
|
|
|
let export = args.export;
|
2026-02-22 13:49:24 +02:00
|
|
|
let allowed = args.allowed_devices;
|
2026-02-22 13:09:41 +02:00
|
|
|
|
|
|
|
|
let socket = SocketAddr::from_str(&format!("{ip}:{port}")).unwrap();
|
2026-02-21 01:55:41 +02:00
|
|
|
let listener = TcpListener::bind(socket).unwrap();
|
|
|
|
|
|
|
|
|
|
for stream in listener.incoming() {
|
|
|
|
|
match stream {
|
2026-02-22 13:49:24 +02:00
|
|
|
Ok(conn) => handle_connection(conn, export.clone(), allowed.clone())?,
|
2026-02-21 01:55:41 +02:00
|
|
|
Err(e) => eprintln!("Something went wrong while listening {e}"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-22 13:49:24 +02:00
|
|
|
fn handle_connection(
|
|
|
|
|
mut conn: TcpStream,
|
|
|
|
|
export: String,
|
|
|
|
|
allowed_devices: Option<Vec<String>>,
|
|
|
|
|
) -> Result<()> {
|
2026-02-22 01:25:05 +02:00
|
|
|
let mut paths = vec![];
|
|
|
|
|
let mut buffer = ByteBuffer::default();
|
|
|
|
|
|
2026-02-22 13:49:24 +02:00
|
|
|
if let Some(allowed_devices) = allowed_devices {
|
|
|
|
|
let remote_ip = conn
|
|
|
|
|
.peer_addr()
|
|
|
|
|
.expect("Could not get remote IP address")
|
|
|
|
|
.ip();
|
|
|
|
|
|
|
|
|
|
for allowed in allowed_devices {
|
|
|
|
|
let allowed_net = ipnet::IpNet::from_str(&allowed).unwrap();
|
|
|
|
|
|
|
|
|
|
let is_allowed = allowed_net.contains(&remote_ip);
|
|
|
|
|
if !is_allowed {
|
|
|
|
|
log::error!("{remote_ip} tried to connect but is not allowed");
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-22 13:09:41 +02:00
|
|
|
walk_dir(&export, &mut paths);
|
|
|
|
|
|
|
|
|
|
log::info!("Sending {} files", paths.len());
|
2026-02-22 01:25:05 +02:00
|
|
|
|
|
|
|
|
buffer.write_usize(paths.len());
|
|
|
|
|
|
|
|
|
|
for file in paths {
|
2026-02-22 13:09:41 +02:00
|
|
|
let mut path = file.replace(&export, "");
|
|
|
|
|
if path.starts_with("/") {
|
|
|
|
|
path.remove(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log::info!("Sending {path}");
|
|
|
|
|
|
2026-02-22 01:25:05 +02:00
|
|
|
buffer.write_string(&path);
|
|
|
|
|
|
2026-02-22 13:09:41 +02:00
|
|
|
match fs::read(file) {
|
2026-02-21 01:55:41 +02:00
|
|
|
Ok(data) => {
|
2026-02-22 01:25:05 +02:00
|
|
|
buffer.write_usize(data.len());
|
2026-02-21 11:42:51 +02:00
|
|
|
buffer.write_bytes(&data);
|
2026-02-21 01:55:41 +02:00
|
|
|
}
|
2026-02-22 01:25:05 +02:00
|
|
|
Err(_) => {
|
|
|
|
|
buffer.write_usize(0);
|
2026-02-22 13:09:41 +02:00
|
|
|
eprintln!("No file found");
|
2026-02-22 01:25:05 +02:00
|
|
|
}
|
2026-02-21 01:55:41 +02:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-22 01:25:05 +02:00
|
|
|
let _ = conn.write_all(&buffer);
|
|
|
|
|
let _ = conn.flush();
|
|
|
|
|
|
2026-02-21 01:55:41 +02:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-22 13:09:41 +02:00
|
|
|
fn walk_dir<P: AsRef<Path>>(path: P, file_paths: &mut Vec<String>) {
|
2026-02-22 01:25:05 +02:00
|
|
|
for entry in fs::read_dir(path).unwrap().flatten() {
|
|
|
|
|
if entry.path().is_dir() {
|
2026-02-22 13:09:41 +02:00
|
|
|
walk_dir(entry.path(), file_paths);
|
|
|
|
|
continue;
|
2026-02-22 01:25:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let file_path = entry.path().to_str().unwrap().to_string();
|
|
|
|
|
|
|
|
|
|
file_paths.push(file_path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-21 01:55:41 +02:00
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use std::io::Read;
|
|
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
2026-02-22 13:09:41 +02:00
|
|
|
const IP: &str = "0.0.0.0";
|
|
|
|
|
const PORT: &str = "9696";
|
|
|
|
|
|
2026-02-21 01:55:41 +02:00
|
|
|
#[test]
|
|
|
|
|
fn test_connection() {
|
2026-02-22 13:09:41 +02:00
|
|
|
let conn = TcpStream::connect(format!("{IP}:{PORT}"));
|
2026-02-21 01:55:41 +02:00
|
|
|
assert!(conn.is_ok());
|
|
|
|
|
|
|
|
|
|
let mut conn = conn.unwrap();
|
2026-02-21 11:42:51 +02:00
|
|
|
let mut buffer = ByteBuffer::default();
|
2026-02-21 01:55:41 +02:00
|
|
|
|
|
|
|
|
let bytes_read = conn.read_to_end(&mut buffer);
|
|
|
|
|
assert!(bytes_read.is_ok());
|
|
|
|
|
|
2026-02-21 11:42:51 +02:00
|
|
|
let file_name = buffer.read_string();
|
2026-02-21 01:55:41 +02:00
|
|
|
assert_eq!(file_name, "examples/test.md");
|
2026-02-21 11:42:51 +02:00
|
|
|
|
|
|
|
|
// let content = buffer.read_bytes(buffer.unread_len());
|
|
|
|
|
// println!("{}", String::from_utf8(content).unwrap());
|
2026-02-21 01:55:41 +02:00
|
|
|
}
|
|
|
|
|
}
|