add WIP rust server
This commit is contained in:
Generated
+186
@@ -0,0 +1,186 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itransfer_client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"byteorder",
|
||||
"serde",
|
||||
"sha2",
|
||||
"sha3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keccak"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940"
|
||||
dependencies = [
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.144"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.163"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.163"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha3"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"keccak",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "itransfer_client"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.3.3"
|
||||
serde = { version = "1.0.163", features = ["serde_derive"], default-features = false }
|
||||
byteorder = { default-features = false, version = "1.4.3" }
|
||||
sha3 = { default-features = false, version = "0.10.8" }
|
||||
sha2 = { default-features = false, version = "0.10.6" }
|
||||
|
||||
[profile.release]
|
||||
lto = true # Enable link-time optimization
|
||||
codegen-units = 1 # Reduce number of codegen units to increase optimizations
|
||||
panic = 'abort' # Abort on panic
|
||||
strip = true # Strip symbols from binary; remove pdb
|
||||
@@ -0,0 +1,68 @@
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use serde::ser::{Serialize, Serializer, SerializeTuple};
|
||||
use serde::de::{Deserialize, Deserializer, Visitor, SeqAccess, Error};
|
||||
|
||||
pub trait BigArray<'de>: Sized {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer;
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where D: Deserializer<'de>;
|
||||
}
|
||||
|
||||
macro_rules! big_array {
|
||||
($($len:expr,)+) => {
|
||||
$(
|
||||
impl<'de, T> BigArray<'de> for [T; $len]
|
||||
where T: Default + Copy + Serialize + Deserialize<'de>
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer
|
||||
{
|
||||
let mut seq = serializer.serialize_tuple(self.len())?;
|
||||
for elem in &self[..] {
|
||||
seq.serialize_element(elem)?;
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
|
||||
fn deserialize<D>(deserializer: D) -> Result<[T; $len], D::Error>
|
||||
where D: Deserializer<'de>
|
||||
{
|
||||
struct ArrayVisitor<T> {
|
||||
element: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'de, T> Visitor<'de> for ArrayVisitor<T>
|
||||
where T: Default + Copy + Deserialize<'de>
|
||||
{
|
||||
type Value = [T; $len];
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str(concat!("an array of length ", $len))
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<[T; $len], A::Error>
|
||||
where A: SeqAccess<'de>
|
||||
{
|
||||
let mut arr = [T::default(); $len];
|
||||
for i in 0..$len {
|
||||
arr[i] = seq.next_element()?
|
||||
.ok_or_else(|| Error::invalid_length(i, &self))?;
|
||||
}
|
||||
Ok(arr)
|
||||
}
|
||||
}
|
||||
|
||||
let visitor = ArrayVisitor { element: PhantomData };
|
||||
deserializer.deserialize_tuple($len, visitor)
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
big_array! {
|
||||
40, 48, 50, 56, 64, 72, 96, 100, 128, 160, 192, 200, 224, 256, 384, 488, 512,
|
||||
768, 1024, 2048, 4096, 8192, 16384, 32768, 65536,
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
use bincode::{self, Error, options, Options};
|
||||
use std::{net::{UdpSocket, SocketAddr}, io::Write};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use sha3::{Digest, Sha3_512};
|
||||
use sha2::Sha512;
|
||||
use std::io::{self, BufRead};
|
||||
|
||||
mod big_array;
|
||||
use big_array::BigArray;
|
||||
|
||||
const MAX_FRAME_PAYLOAD:u16=508;
|
||||
const MAX_FRAME_PAYLOAD_U:usize=MAX_FRAME_PAYLOAD as usize;
|
||||
const HEADER_SIZE:u16 = 20;
|
||||
const MAX_PAYLOAD:u16 = MAX_FRAME_PAYLOAD - HEADER_SIZE;
|
||||
const MAX_PAYLOAD_U:usize = MAX_PAYLOAD as usize;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct PacketInformation {
|
||||
packet_numbers: u32, //4 bytes
|
||||
last_packet_size: u16, //2 bytes
|
||||
response_filename_checksum: u64, //8 bytes
|
||||
} //14 bytes
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Packet {
|
||||
packet_number: u32, //4 bytes
|
||||
payload_hash: [u8; 16], //16 bytes
|
||||
#[serde(with = "BigArray")]
|
||||
payload: [u8; MAX_PAYLOAD_U], //488 bytes
|
||||
} //512 bytes
|
||||
|
||||
fn read_stdin(message: String) -> String {
|
||||
print!("{}", message);
|
||||
io::stdout().flush().unwrap();
|
||||
|
||||
let mut buffer = String::new();
|
||||
let stdin = io::stdin();
|
||||
let mut handle = stdin.lock();
|
||||
|
||||
handle.read_line(&mut buffer).unwrap();
|
||||
buffer.trim().to_string()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
let timeout = 1;
|
||||
let mut server_ip = read_stdin("Enter server ip: ".to_string());//e.g. 213.47.107.152:1337
|
||||
if server_ip == "" {
|
||||
server_ip = "127.0.0.1".to_string(); //testing ip
|
||||
}
|
||||
if server_ip.find(":").is_none() {
|
||||
server_ip.push_str(":1337"); //default port
|
||||
}
|
||||
|
||||
let input_filename = read_stdin("Enter filename: ".to_string());//e.g. "data.bin"
|
||||
let filename: &str;
|
||||
if input_filename == "" {
|
||||
filename = "data.bin"; //testing filename
|
||||
} else {
|
||||
filename = &input_filename;
|
||||
}
|
||||
|
||||
let local_addr: SocketAddr = "0.0.0.0:0".parse().expect("Failed to parse address");
|
||||
let socket = UdpSocket::bind(local_addr).expect("Failed to bind socket");
|
||||
|
||||
socket.set_read_timeout(Some(std::time::Duration::new(timeout, 0))).expect("set_read_timeout call failed");
|
||||
//socket.set_nonblocking(true).expect("set_nonblocking call failed");
|
||||
|
||||
let server_addr: SocketAddr = server_ip.parse().expect("Failed to parse address");
|
||||
let message = filename;
|
||||
let sum: u64 = filename.bytes().map(|b| b as u64).sum();
|
||||
socket.send_to(message.as_bytes(), server_addr).expect("Failed to send data");
|
||||
|
||||
// Receive data
|
||||
let mut buffer = [0u8; MAX_FRAME_PAYLOAD_U];
|
||||
let (received_bytes, remote_addr) = socket.recv_from(&mut buffer).expect("Failed to receive data");
|
||||
|
||||
let filled_buffer = &buffer[..received_bytes];
|
||||
|
||||
//print the filled buffer
|
||||
println!("Received data: {:?} {}", filled_buffer, filled_buffer.len());
|
||||
|
||||
if remote_addr != server_addr {
|
||||
panic!("Received data from unknown address");
|
||||
}
|
||||
|
||||
let options = options().with_big_endian().allow_trailing_bytes().with_fixint_encoding();
|
||||
|
||||
let packet_info_result: Result<PacketInformation, Error> = options.deserialize(filled_buffer);
|
||||
let packet_info: PacketInformation;
|
||||
match packet_info_result {
|
||||
Ok(packet) => {
|
||||
println!("Packets to receive: {}", packet.packet_numbers);
|
||||
println!("Last packet size: {}", packet.last_packet_size);
|
||||
println!("Response filename checksum: {}", packet.response_filename_checksum);
|
||||
//check checksum with sum
|
||||
if packet.response_filename_checksum != sum {
|
||||
panic!("Filename Checksums do not match, correct checksum: {}", sum);
|
||||
}
|
||||
|
||||
packet_info = packet;
|
||||
}
|
||||
Err(err) => {
|
||||
panic!("Failed to deserialize data: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
//create vector to store the packets
|
||||
let mut packets: Vec<Vec<u8>> = Vec::new();
|
||||
for _ in 0..packet_info.packet_numbers {
|
||||
packets.push(Vec::new());
|
||||
}
|
||||
|
||||
let mut received_packets = 0;
|
||||
|
||||
let mut server_hash = [0u8; 64];
|
||||
let mut server_hash_received = false;
|
||||
|
||||
//receive the packets
|
||||
while received_packets < packet_info.packet_numbers-1 || !server_hash_received {
|
||||
let mut buffer = [0u8; MAX_FRAME_PAYLOAD_U];
|
||||
let res = socket.recv_from(&mut buffer);
|
||||
if let Ok((received_bytes, remote_addr)) = res {
|
||||
let filled_buffer = &buffer;//[..received_bytes];
|
||||
|
||||
//print the filled buffer
|
||||
//println!("Received data: {:?} {}", filled_buffer, filled_buffer.len());
|
||||
|
||||
if remote_addr != server_addr {
|
||||
panic!("Received data from unknown address");
|
||||
}
|
||||
|
||||
if received_bytes == 65 {
|
||||
//println!("Received hash packet, ignoring for now");
|
||||
server_hash = filled_buffer[0..64].try_into().expect("Failed to convert hash");
|
||||
//println!("Received hash: {:?}", server_hash);
|
||||
server_hash_received = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if received_bytes != MAX_FRAME_PAYLOAD_U {
|
||||
println!("Received packet with invalid size {} not {} | ignoring", received_bytes, MAX_FRAME_PAYLOAD_U);
|
||||
continue;
|
||||
}
|
||||
|
||||
let packet_result: Result<Packet, Error> = options.deserialize(filled_buffer);
|
||||
let packet: Packet;
|
||||
match packet_result {
|
||||
Ok(p) => {
|
||||
//println!("Packet {}", p.packet_number);
|
||||
//check checksum with sum
|
||||
if p.packet_number != packet_info.packet_numbers - 1 {
|
||||
let mut hasher = Sha3_512::new();
|
||||
hasher.update(&p.payload);
|
||||
let hash = hasher.finalize();
|
||||
|
||||
if p.payload_hash != hash[..16] {
|
||||
continue;
|
||||
}
|
||||
|
||||
packet = p;
|
||||
} else {
|
||||
//cut packet down to size
|
||||
packets[p.packet_number as usize] = p.payload[..packet_info.last_packet_size as usize].to_vec();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
panic!("Failed to deserialize data: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
if packets[packet.packet_number as usize].len() != 0 {
|
||||
//println!("Packet already received, ignoring");
|
||||
continue;
|
||||
}
|
||||
|
||||
packets[packet.packet_number as usize] = packet.payload.to_vec();
|
||||
received_packets += 1;
|
||||
} else {
|
||||
//println!("Timeout, requesting again {}/{}\r", received_packets, packet_info.packet_numbers);
|
||||
//collect packets that were not received and send them in n messages
|
||||
//where n is the minimum amount of messages needed to request all packets
|
||||
let mut missing_packets: Vec<u32> = Vec::new();
|
||||
for i in 0..packet_info.packet_numbers {
|
||||
if packets[i as usize].len() == 0 {
|
||||
missing_packets.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
//split lost_packets into groups of size 508-filename.len() bytes
|
||||
let mut missing_packet_groups: Vec<String> = Vec::new();
|
||||
let mut current_group: String = filename.to_string();
|
||||
for i in 0..missing_packets.len() {
|
||||
if current_group.len() + missing_packets[i].to_string().len() + 1 > MAX_PAYLOAD_U {
|
||||
missing_packet_groups.push(current_group);
|
||||
current_group = filename.to_string();
|
||||
}
|
||||
current_group.push('/');
|
||||
current_group.push_str(&missing_packets[i].to_string());
|
||||
}
|
||||
|
||||
if current_group.len() > filename.len() {
|
||||
missing_packet_groups.push(current_group);
|
||||
}
|
||||
|
||||
for i in 0..missing_packet_groups.len() {
|
||||
let message = &missing_packet_groups[i];
|
||||
//println!("Requesting packets: {}", message);
|
||||
socket.send_to(message.as_bytes(), server_addr).expect("Failed to send data");
|
||||
}
|
||||
|
||||
if !server_hash_received {
|
||||
let message = filename.to_string()+":";
|
||||
socket.send_to(message.as_bytes(), server_addr).expect("Failed to send data");
|
||||
}
|
||||
}
|
||||
|
||||
print!("Packet {}/{}\r", received_packets, packet_info.packet_numbers);
|
||||
}
|
||||
|
||||
//check hash via sha512
|
||||
|
||||
let mut hasher = Sha512::new();
|
||||
for i in 0..packets.len() {
|
||||
hasher.update(&packets[i]);
|
||||
}
|
||||
let client_hash = hasher.finalize();
|
||||
|
||||
if client_hash[..64] != server_hash {
|
||||
panic!("Hashes do not match, correct hash: {:?}, client hash: {:?}", server_hash, client_hash);
|
||||
}
|
||||
|
||||
|
||||
println!("Received all packets, writing to file");
|
||||
|
||||
//create folder "received" if it does not exist
|
||||
std::fs::create_dir_all("received").expect("Failed to create directory");
|
||||
|
||||
//write packets to file
|
||||
let mut file = std::fs::File::create("received/".to_string()+filename).expect("Failed to create file");
|
||||
for i in 0..packets.len() {
|
||||
file.write_all(&packets[i]).expect("Failed to write to file");
|
||||
}
|
||||
|
||||
}
|
||||
Generated
+219
@@ -0,0 +1,219 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itransfer_client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"byteorder",
|
||||
"regex",
|
||||
"serde",
|
||||
"sha2",
|
||||
"sha3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keccak"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940"
|
||||
dependencies = [
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.144"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.163"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.163"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha3"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"keccak",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "itransfer_client"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.3.3"
|
||||
serde = { version = "1.0.163", features = ["serde_derive"], default-features = false }
|
||||
byteorder = { default-features = false, version = "1.4.3" }
|
||||
sha3 = { default-features = false, version = "0.10.8" }
|
||||
sha2 = { default-features = false, version = "0.10.6" }
|
||||
regex = { default-features = true, version = "1.8.3" }
|
||||
|
||||
[profile.release]
|
||||
lto = true # Enable link-time optimization
|
||||
codegen-units = 1 # Reduce number of codegen units to increase optimizations
|
||||
panic = 'abort' # Abort on panic
|
||||
strip = true # Strip symbols from binary; remove pdb
|
||||
@@ -0,0 +1,13 @@
|
||||
packet size 1 - max:
|
||||
file request
|
||||
format: `[filename]`
|
||||
|
||||
packet size 2 - max:
|
||||
hash request
|
||||
format: `[filename]:`
|
||||
regex name: `hash_request_regex`
|
||||
|
||||
packet size 3 - max:
|
||||
missing packet request
|
||||
format: `[filename]/[packet number]/[packet number]/...`
|
||||
regex name: `missing_packet_request_regex`
|
||||
@@ -0,0 +1,68 @@
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use serde::ser::{Serialize, Serializer, SerializeTuple};
|
||||
use serde::de::{Deserialize, Deserializer, Visitor, SeqAccess, Error};
|
||||
|
||||
pub trait BigArray<'de>: Sized {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer;
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where D: Deserializer<'de>;
|
||||
}
|
||||
|
||||
macro_rules! big_array {
|
||||
($($len:expr,)+) => {
|
||||
$(
|
||||
impl<'de, T> BigArray<'de> for [T; $len]
|
||||
where T: Default + Copy + Serialize + Deserialize<'de>
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer
|
||||
{
|
||||
let mut seq = serializer.serialize_tuple(self.len())?;
|
||||
for elem in &self[..] {
|
||||
seq.serialize_element(elem)?;
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
|
||||
fn deserialize<D>(deserializer: D) -> Result<[T; $len], D::Error>
|
||||
where D: Deserializer<'de>
|
||||
{
|
||||
struct ArrayVisitor<T> {
|
||||
element: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'de, T> Visitor<'de> for ArrayVisitor<T>
|
||||
where T: Default + Copy + Deserialize<'de>
|
||||
{
|
||||
type Value = [T; $len];
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str(concat!("an array of length ", $len))
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<[T; $len], A::Error>
|
||||
where A: SeqAccess<'de>
|
||||
{
|
||||
let mut arr = [T::default(); $len];
|
||||
for i in 0..$len {
|
||||
arr[i] = seq.next_element()?
|
||||
.ok_or_else(|| Error::invalid_length(i, &self))?;
|
||||
}
|
||||
Ok(arr)
|
||||
}
|
||||
}
|
||||
|
||||
let visitor = ArrayVisitor { element: PhantomData };
|
||||
deserializer.deserialize_tuple($len, visitor)
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
big_array! {
|
||||
40, 48, 50, 56, 64, 72, 96, 100, 128, 160, 192, 200, 224, 256, 384, 488, 512,
|
||||
768, 1024, 2048, 4096, 8192, 16384, 32768, 65536,
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
use bincode::{self, options, Options};
|
||||
use std::{net::{UdpSocket, SocketAddr}, io::Read};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use sha3::{Digest, Sha3_512};
|
||||
use sha2::Sha512;
|
||||
use regex::Regex;
|
||||
|
||||
mod big_array;
|
||||
use big_array::BigArray;
|
||||
|
||||
const MAX_FRAME_PAYLOAD:u16=508;
|
||||
const MAX_FRAME_PAYLOAD_U:usize=MAX_FRAME_PAYLOAD as usize;
|
||||
const HEADER_SIZE:u16 = 20;
|
||||
const MAX_PAYLOAD:u16 = MAX_FRAME_PAYLOAD - HEADER_SIZE;
|
||||
const MAX_PAYLOAD_U:usize = MAX_PAYLOAD as usize;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct PacketInformation {
|
||||
packet_numbers: u32, //4 bytes
|
||||
last_packet_size: u16, //2 bytes
|
||||
response_filename_checksum: u64, //8 bytes
|
||||
} //14 bytes
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Packet {
|
||||
packet_number: u32, //4 bytes
|
||||
payload_hash: [u8; 16], //16 bytes
|
||||
#[serde(with = "BigArray")]
|
||||
payload: [u8; MAX_PAYLOAD_U], //488 bytes
|
||||
} //512 bytes
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct StrPacket {
|
||||
#[serde(with = "BigArray")]
|
||||
payload: [u8; MAX_PAYLOAD_U]
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let port = "1337";
|
||||
let timeout = 100;
|
||||
let local_addr: SocketAddr = ("0.0.0.0:".to_string()+port).parse().expect("Failed to parse address");
|
||||
let socket = UdpSocket::bind(local_addr).expect("Failed to bind socket");
|
||||
|
||||
socket.set_read_timeout(Some(std::time::Duration::from_millis(timeout))).expect("set_read_timeout call failed");
|
||||
|
||||
println!("UDP Server up and running on port {}", local_addr.port());
|
||||
|
||||
let _options = options().with_big_endian().allow_trailing_bytes().with_fixint_encoding();
|
||||
|
||||
let hash_request_regex = Regex::new(r"[a-zA-Z0-9.-_ ]+:").unwrap();
|
||||
|
||||
let missing_packet_request_regex = Regex::new(r"([a-zA-Z0-9.-_ ]+)(/[0-9]+)+").unwrap();
|
||||
|
||||
loop {
|
||||
let mut buffer = [0u8; MAX_FRAME_PAYLOAD_U];
|
||||
let res = socket.recv_from(&mut buffer);
|
||||
if let Ok((_received_bytes, remote_addr)) = res {
|
||||
let filled_buffer = &buffer;//[..received_bytes];
|
||||
|
||||
let request_packet = bincode::deserialize::<StrPacket>(filled_buffer).unwrap();
|
||||
let request: String = request_packet.payload.iter().map(|&c| c as char).collect();
|
||||
println!("Received request: {}", request);
|
||||
let req = request.as_str();
|
||||
|
||||
if hash_request_regex.is_match(req) {
|
||||
println!("Received hash request");
|
||||
continue;
|
||||
}
|
||||
|
||||
if missing_packet_request_regex.is_match(req) {
|
||||
println!("Received missing packet request");
|
||||
continue;
|
||||
}
|
||||
|
||||
println!("Received file request");
|
||||
|
||||
let request = "files/".to_string() + req;
|
||||
|
||||
let file_result = std::fs::File::open(request);
|
||||
|
||||
if file_result.is_err() {
|
||||
println!("File not found");
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut file = file_result.unwrap();
|
||||
|
||||
let mut file_buffer = [0u8; MAX_PAYLOAD_U];
|
||||
|
||||
let mut packet_number = 0u32;
|
||||
|
||||
let file_length = file.metadata().unwrap().len();
|
||||
println!("file length: {}", file_length);
|
||||
|
||||
let packet_information = PacketInformation {
|
||||
packet_numbers: (file_length/MAX_PAYLOAD as u64) as u32,
|
||||
last_packet_size: (file_length%MAX_PAYLOAD as u64) as u16,
|
||||
response_filename_checksum: request_packet.payload.iter().fold(0u64, |acc, &x| acc.wrapping_add(x as u64)),
|
||||
};
|
||||
|
||||
socket.send_to(bincode::serialize(&packet_information).unwrap().as_slice(), remote_addr).expect("Failed to send packet information");
|
||||
|
||||
//read file chunk by chunk
|
||||
|
||||
while let Ok(bytes_read) = file.read(&mut file_buffer) {
|
||||
if bytes_read == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
let mut hasher = Sha3_512::new();
|
||||
hasher.update(&file_buffer[..bytes_read]);
|
||||
let result = hasher.finalize();
|
||||
let mut packet = Packet {
|
||||
packet_number,
|
||||
payload_hash: [0u8; 16],
|
||||
payload: file_buffer[..bytes_read].try_into().unwrap(),
|
||||
};
|
||||
packet.payload_hash.copy_from_slice(&result[..16]);
|
||||
packet.payload[..bytes_read].copy_from_slice(&file_buffer[..bytes_read]);
|
||||
let packet_bytes = bincode::serialize(&packet).unwrap();
|
||||
socket.send_to(&packet_bytes, remote_addr).expect("Failed to send packet");
|
||||
packet_number += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user