Browse Source

Handshake packet implementation

master
Gitea 2 years ago
parent
commit
857e2b4f21
  1. 45
      PROTOCOL
  2. 3
      TODO
  3. 43
      src/args/parser.rs
  4. 20
      src/main.rs
  5. 3
      src/nodes/mod.rs
  6. 9
      src/nodes/node.rs
  7. 106
      src/nodes/recv_node.rs
  8. 21
      src/nodes/send_node.rs
  9. 247
      src/protocol/packets.rs
  10. 140
      src/protocol/specs.rs
  11. 14
      src/util/buf_read.rs

45
PROTOCOL

@ -0,0 +1,45 @@
--- DDTU PROTOCOL v1 ---
- High level overview -
- Node Modes -
[Receive]
Is for receiving information.
[Send]
Is for sending data to the receiving end.
[Sync]
Is for syncing one directory among 2 ends. (Make contents the same on both sides)
[Daemon]
Is used to create a longstanding sync daemon which syncs the directory with all incoming
sync connections
All infomation|commands are sent via packets with specific structure
- Packet bytes representation -
First 16 bytes - the total packet length
The next 1 byte - number that represents this packet's type (ID) where:
1: HANDSHAKE PACKET
2: HANDSHAKE ACCEPT
3: CONNECTION SHUTDOWN PACKET
4: TEXT PACKET
5: FILEINFO PACKET
6: FILEDATA PACKET
Then the internal structure varies from one packet type to the other,
but the content-types are encoded as follows:
u8 - just 1 BE byte
u128 - 16 BE bytes
String - u128 representing the string length and then UTF-8 encoded character bytes
[u8] - u128 as the length of a byte array, then the array itself
bool - convert it to u8 where 0 == false, 1 == true
- Packets -
[Handshake]
This is the first packet that must be sent by connecting node to waiting node.
Its existence tells the receiving end that the

3
TODO

@ -1,4 +1,5 @@
1. Write base foundation for sending and receiving information 1. Write base foundation for sending and receiving information
2. Wire it all together 2. Wire it all together
3. Make it encrypted 3. Make it encrypted
4. Daemon 4. Allow receiving node to connect as well
5. Daemon

43
src/args/parser.rs

@ -14,18 +14,11 @@ GNU Affero General Public License for more details.
*/ */
use crate::util::error::Error; use crate::util::error::Error;
use crate::protocol::specs::RunMode;
use std::path; use std::path;
#[derive(PartialEq, Eq, Debug)] #[derive(PartialEq, Eq, Debug)]
pub enum RunMode { pub enum DataType {
SEND,
RECEIVE,
SYNC,
DAEMON,
}
#[derive(PartialEq, Eq, Debug)]
pub enum Type {
TEXT, TEXT,
FILE, FILE,
DIR, DIR,
@ -36,7 +29,7 @@ pub struct Args {
pub help: bool, pub help: bool,
pub version: bool, pub version: bool,
pub mode: RunMode, pub mode: RunMode,
pub send_type: Type, pub send_type: DataType,
pub address_str: String, pub address_str: String,
pub send_path: path::PathBuf, pub send_path: path::PathBuf,
pub save_directory: path::PathBuf, pub save_directory: path::PathBuf,
@ -50,7 +43,7 @@ impl Args {
help: false, help: false,
version: false, version: false,
mode: RunMode::DAEMON, mode: RunMode::DAEMON,
send_type: Type::TEXT, send_type: DataType::TEXT,
address_str: "".to_string(), address_str: "".to_string(),
send_path: path::PathBuf::new(), send_path: path::PathBuf::new(),
save_directory: path::PathBuf::new(), save_directory: path::PathBuf::new(),
@ -61,6 +54,7 @@ impl Args {
} }
pub fn parse(raw_args: &Vec<String>) -> Result<Args, Error> { pub fn parse(raw_args: &Vec<String>) -> Result<Args, Error> {
let mut parsed_args: Args = Args::default(); let mut parsed_args: Args = Args::default();
// parse arguments, ignoring the first argument // parse arguments, ignoring the first argument
@ -79,37 +73,37 @@ pub fn parse(raw_args: &Vec<String>) -> Result<Args, Error> {
} }
"send" => { "send" => {
if arg_amount < 4 { if arg_amount < 5 {
// not enough arguments ! // not enough arguments !
return Err(Error::new("not enough arguments for send mode")); return Err(Error::new("not enough arguments for send mode"));
} }
parsed_args.mode = RunMode::SEND; parsed_args.mode = RunMode::SEND;
// type // DataType
match raw_args[2].to_lowercase().as_str() { match raw_args[2].to_lowercase().as_str() {
"text" => { "text" => {
parsed_args.send_type = Type::TEXT; parsed_args.send_type = DataType::TEXT;
parsed_args.text_to_send = String::from(&raw_args[3]); parsed_args.text_to_send = String::from(&raw_args[3]);
} }
"file" => { "file" => {
parsed_args.send_type = Type::FILE; parsed_args.send_type = DataType::FILE;
parsed_args.send_path = std::path::Path::new(&raw_args[3]).to_path_buf(); parsed_args.send_path = std::path::Path::new(&raw_args[3]).to_path_buf();
} }
"dir" => { "dir" => {
parsed_args.send_type = Type::DIR; parsed_args.send_type = DataType::DIR;
parsed_args.send_path = std::path::Path::new(&raw_args[3]).to_path_buf(); parsed_args.send_path = std::path::Path::new(&raw_args[3]).to_path_buf();
} }
_ => { _ => {
return Err(Error::new(format!("invalid type {}", raw_args[2]).as_str())); return Err(Error::new(format!("invalid DataType {}", raw_args[2]).as_str()));
} }
} }
// src // src
if parsed_args.send_type == Type::FILE || parsed_args.send_type == Type::DIR { if parsed_args.send_type == DataType::FILE || parsed_args.send_type == DataType::DIR {
// check if exists // check if exists
match path::Path::new(raw_args[3].as_str()).exists() { match path::Path::new(raw_args[3].as_str()).exists() {
true => { true => {
@ -121,18 +115,15 @@ pub fn parse(raw_args: &Vec<String>) -> Result<Args, Error> {
} }
} }
} }
}
"recv" => { // addr
if arg_amount < 3 { parsed_args.address_str = raw_args[4].to_string();
// not enough arguments !
return Err(Error::new("not enough arguments for receiving mode"));
} }
"recv" => {
parsed_args.mode = RunMode::RECEIVE; parsed_args.mode = RunMode::RECEIVE;
parsed_args.address_str = raw_args[2].to_string(); if arg_amount > 2 {
if arg_amount > 3 { parsed_args.save_directory = path::Path::new(raw_args[2].as_str()).to_path_buf();
parsed_args.save_directory = path::Path::new(raw_args[3].as_str()).to_path_buf();
} }
} }

20
src/main.rs

@ -18,8 +18,10 @@ mod protocol;
mod args; mod args;
mod crypt; mod crypt;
mod fsys; mod fsys;
mod nodes;
use args::parser::{Args, RunMode}; use protocol::specs::RunMode;
use args::parser::Args;
use std::net; use std::net;
use crate::protocol::specs::{Packet, PacketType}; use crate::protocol::specs::{Packet, PacketType};
@ -34,10 +36,10 @@ ddtu [FLAG] [MODE] [MODE-specific arguments]
[version]: ddtu version -> print version [version]: ddtu version -> print version
[MODE] [MODE]
[send]: ddtu send [TYPE] [SRC] -> prepare to send [SRC] of [TYPE] and wait for someone to receive [send]: ddtu send [TYPE] [SRC] [ADDR] -> send [SRC] of [TYPE] to [ADDR]
[recv]: ddtu recv [SRC] [DEST](optional) -> receive anything from another ddtu instance running on [SRC], saving it at [DEST] [recv]: ddtu recv [DIR](optional) -> wait for connection and receive anything from another ddtu instance, saving it to [DIR]
[sync]: ddtu sync [DIR] [DEST] -> sync [DIRectory] contents with another ddtu instance on [DEST] [sync]: ddtu sync [DIR] [ADDR] -> sync [DIRectory] contents with another ddtu instance on [ADDR]
[daemon]: ddtu daemon -> start syncing daemon [daemon]: ddtu daemon -> start sync daemon
[TYPE] [TYPE]
[text]: ddtu send text \'text to send\' -> send text [text]: ddtu send text \'text to send\' -> send text
@ -93,11 +95,11 @@ fn main() {
println!("connected to {}", args.address_str); println!("connected to {}", args.address_str);
match protocol::specs::read_next_packet(&mut conn) { match protocol::packets::read_next_packet(&mut conn) {
Ok(packet) => { Ok(packet) => {
match packet.get_type() { match packet.get_type() {
PacketType::TextData => { PacketType::TextData => {
let mut text_packet = protocol::packets::TextPacket::empty(); let mut text_packet = protocol::packets::Text::empty();
text_packet.from_bytes(packet.as_bytes()); text_packet.from_bytes(packet.as_bytes());
println!("{}", text_packet.text); println!("{}", text_packet.text);
} }
@ -144,8 +146,8 @@ fn main() {
println!("{} has connected", addr); println!("{} has connected", addr);
// send text // send text
let textpacket = protocol::packets::TextPacket::new(&args.text_to_send); let Text = protocol::packets::Text::new(&args.text_to_send);
match protocol::specs::send_packet(&mut conn, &textpacket) { match protocol::packets::send_packet(&mut conn, &Text) {
Ok(()) => {} Ok(()) => {}
Err(error) => { Err(error) => {
println!("{}", error.text); println!("{}", error.text);

3
src/nodes/mod.rs

@ -1 +1,2 @@
pub mod node; pub mod recv_node;
pub mod send_node;

9
src/nodes/node.rs

@ -1,9 +0,0 @@
pub struct Node {
}
impl Node {
pub fn new() -> Node {
return Node{};
}
}

106
src/nodes/recv_node.rs

@ -0,0 +1,106 @@
/*
ddtu - digital data transferring utility
Copyright (C) 2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
*/
use crate::util::error::Error;
use crate::protocol::specs::PacketType;
use crate::protocol::specs::Packet;
use crate::protocol::packets;
use crate::protocol;
use std::path;
use std::net;
pub fn start(address_string: String, save_directory: path::PathBuf, verbose_output: bool) -> Option<Error> {
let listener: net::TcpListener;
match net::TcpListener::bind(address_string) {
Ok(l) => {
listener = l;
}
Err(error) => {
return Some(Error::new(format!("could not create TCP listener: {}", error).as_str()));
}
}
// wait for incoming connection from another ddtu instance,
// reject any connection that doesn't provide handshake information
let mut connection: net::TcpStream;
let mut address: net::SocketAddr;
let mut handshake: packets::Handshake = packets::Handshake::empty();
loop {
match listener.accept() {
Ok((c, a)) => {
connection = c;
address = a;
}
Err(error) => {
return Some(Error::new(format!("error accepting a new incoming connection: {}", error).as_str()));
}
}
if verbose_output {
println!("Connection from {}", address);
}
// read the first packet
let incoming_packet: Box<dyn Packet>;
match protocol::packets::read_next_packet(&mut connection) {
Ok(packet) => {
incoming_packet = packet;
}
Err(_) => {
// not a valid packet; it's most probably not another instance
if verbose_output {
println!("{} did not provide valid handshake packet. Dropping connection...", address);
}
connection.shutdown(net::Shutdown::Both);
continue;
}
}
match incoming_packet.get_type() {
PacketType::Handshake => {
match handshake.from_bytes(incoming_packet.as_bytes()) {
Some(error) => {
return Some(
Error::new(
format!("error constructing handshake from {}: {}", address, error.text).as_str()
));
}
None => {}
}
if verbose_output {
println!("Handshake from {}", address);
}
// break out of loop
break;
}
_ => {
if verbose_output {
println!("{} didn't send a handshake packet, but {:?} packet instead. Dropping connection...", address, incoming_packet.get_type());
}
connection.shutdown(net::Shutdown::Both);
continue;
}
}
}
// now read and process incoming packets; essentialy receiving something !
return None;
}

21
src/nodes/send_node.rs

@ -0,0 +1,21 @@
/*
ddtu - digital data transferring utility
Copyright (C) 2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
*/
use std::net;
use std::path;
pub fn start(address: String, source: path::PathBuf) {
}

247
src/protocol/packets.rs

@ -13,45 +13,150 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details. GNU Affero General Public License for more details.
*/ */
use std::net;
use std::io::Write;
use std::io::Read;
use crate::util::error::Error; use crate::util::error::Error;
use crate::util::buf_read::{read_u128_slice, read_utf8_string_slice}; use crate::util::buf_read::{read_u128_slice_be, read_utf8_string_slice};
use crate::protocol::specs::*; use crate::protocol::specs::*;
use crate::fsys::file::ChunkedFile; use crate::fsys::file::ChunkedFile;
pub struct TextPacket { pub fn send_packet(conn: &mut net::TcpStream, packet: &dyn Packet) -> Result<(), Error> {
let mut payload: Vec<u8> = Vec::<u8>::new();
let packet_bytes: Vec<u8> = packet.as_bytes();
let packet_len: usize = packet_bytes.len();
payload.extend_from_slice(&(packet_len as u128).to_be_bytes());
payload.extend_from_slice(&packet_bytes);
match conn.write_all(&payload) {
Ok(()) => {
return Ok(());
}
Err(error) => {
return Err(Error::new(format!("could not write packet to the connection: {}", error).as_str()))
}
}
}
pub fn read_next_packet(conn: &mut net::TcpStream) -> Result<Box<dyn Packet>, Error> {
let mut u128_buf: [u8; 16] = [0; 16];
match conn.read_exact(&mut u128_buf) {
Ok(()) => {}
Err(error) => {
return Err(Error::new(format!("error while reading initial length bytes: {}", error).as_str()));
}
}
let packet_len: u128 = u128::from_be_bytes(u128_buf);
let mut packet_bytes: Vec<u8> = vec!(0; packet_len as usize);
match conn.read_exact(&mut packet_bytes) {
Ok(()) => {}
Err(error) => {
return Err(Error::new(format!("error while reading packet contents: {}", error).as_str()));
}
}
if packet_bytes.len() < packet_len as usize {
return Err(
Error::new(
format!("read {} packet bytes instead of specified {}", packet_bytes.len(), packet_len).as_str())
);
}
let packet_type_byte: u8 = u8::from_be_bytes([packet_bytes[0]]);
match packet_type_byte {
CONNECTION_SHUTDOWN_PACKET_ID => {
return Ok(Box::new(ConnectionShutdown{}));
}
TEXT_PACKET_ID => {
let mut new_text_packet: Text = Text::empty();
let result = new_text_packet.from_bytes(packet_bytes);
match result {
Some(error) => {
return Err(Error::new(format!("could not construct new text packet: {}", error.text).as_str()));
}
None => {
return Ok(Box::new(new_text_packet));
}
}
}
FILEINFO_PACKET_ID => {
let mut new_fileinfo_packet: FileInfo = FileInfo::empty();
let result = new_fileinfo_packet.from_bytes(packet_bytes);
match result {
Some(error) => {
return Err(Error::new(format!("could not construct new fileinfo packet: {}", error.text).as_str()));
}
None => {
return Ok(Box::new(new_fileinfo_packet));
}
}
}
FILEDATA_PACKET_ID => {
let mut new_filedata_packet: FileData = FileData::empty();
let result = new_filedata_packet.from_bytes(packet_bytes);
match result {
Some(error) => {
return Err(Error::new(format!("could not construct new filedata packet: {}", error.text).as_str()));
}
None => {
return Ok(Box::new(new_filedata_packet));
}
}
}
_ => {
return Err(Error::new(format!("invalid packet type ID \"{}\"", packet_type_byte).as_str()));
}
}
}
pub struct Text {
pub text: String, pub text: String,
} }
impl TextPacket { impl Text {
pub fn empty() -> TextPacket { pub fn empty() -> Text {
return TextPacket{ return Text{
text: "".to_string(), text: "".to_string(),
}; };
} }
pub fn new(txt: &str) -> TextPacket { pub fn new(txt: &str) -> Text {
return TextPacket { return Text {
text: txt.to_string(), text: txt.to_string(),
} }
} }
} }
impl Packet for TextPacket { impl Packet for Text {
fn get_type(&self) -> PacketType { fn get_type(&self) -> PacketType {
return PacketType::TextData; return PacketType::TextData;
} }
fn as_bytes(&self) -> Vec<u8> { fn as_bytes(&self) -> Vec<u8> {
let mut packet_as_bytes: Vec<u8> = Vec::<u8>::new(); let mut packet_bytes: Vec<u8> = Vec::<u8>::new();
packet_as_bytes.extend_from_slice(&TEXT_PACKET_ID.to_be_bytes()); packet_bytes.extend_from_slice(&TEXT_PACKET_ID.to_be_bytes());
let text_length: u128 = self.text.len() as u128; let text_length: u128 = self.text.len() as u128;
packet_as_bytes.extend_from_slice(&text_length.to_be_bytes()); packet_bytes.extend_from_slice(&text_length.to_be_bytes());
packet_as_bytes.extend_from_slice(&self.text.as_bytes()); packet_bytes.extend_from_slice(&self.text.as_bytes());
return packet_as_bytes; return packet_bytes;
} }
fn from_bytes(&mut self, packet_bytes: Vec<u8>) -> Option<Error> { fn from_bytes(&mut self, packet_bytes: Vec<u8>) -> Option<Error> {
@ -70,7 +175,7 @@ impl Packet for TextPacket {
} }
// get text length // get text length
let text_length: u128 = read_u128_slice(&packet_bytes[1..17]); let text_length: u128 = read_u128_slice_be(&packet_bytes[1..17]);
if text_length as usize > packet_bytes[17..].len() { if text_length as usize > packet_bytes[17..].len() {
return Some( return Some(
@ -92,16 +197,16 @@ impl Packet for TextPacket {
} }
pub struct FileInfoPacket { pub struct FileInfo {
pub file_id: u128, pub file_id: u128,
pub filename: String, pub filename: String,
pub filesize: u128, pub filesize: u128,
pub relative_path: String, pub relative_path: String,
} }
impl FileInfoPacket { impl FileInfo {
pub fn empty() -> FileInfoPacket { pub fn empty() -> FileInfo {
return FileInfoPacket{ return FileInfo{
file_id: 0, file_id: 0,
filename: String::new(), filename: String::new(),
filesize: 0, filesize: 0,
@ -109,8 +214,8 @@ impl FileInfoPacket {
}; };
} }
pub fn new(file: &ChunkedFile, file_id: u128, rel_path: String) -> Result<FileInfoPacket, Error> { pub fn new(file: &ChunkedFile, file_id: u128, rel_path: String) -> Result<FileInfo, Error> {
let mut new_fileinfo_packet: FileInfoPacket = FileInfoPacket::empty(); let mut new_fileinfo_packet: FileInfo = FileInfo::empty();
// id // id
new_fileinfo_packet.file_id = file_id; new_fileinfo_packet.file_id = file_id;
@ -146,31 +251,33 @@ impl FileInfoPacket {
} }
} }
impl Packet for FileInfoPacket { impl Packet for FileInfo {
fn get_type(&self) -> PacketType { fn get_type(&self) -> PacketType {
return PacketType::FileInfo; return PacketType::FileInfo;
} }
fn as_bytes(&self) -> Vec<u8> { fn as_bytes(&self) -> Vec<u8> {
let mut packet_as_bytes: Vec<u8> = Vec::<u8>::new(); let mut packet_bytes: Vec<u8> = Vec::<u8>::new();
packet_bytes.extend_from_slice(&FILEINFO_PACKET_ID.to_be_bytes());
// file id // file id
packet_as_bytes.extend_from_slice(&self.file_id.to_be_bytes()); packet_bytes.extend_from_slice(&self.file_id.to_be_bytes());
// filename // filename
let filename_bytes_length: u128 = self.filename.len() as u128; let filename_bytes_length: u128 = self.filename.len() as u128;
packet_as_bytes.extend_from_slice(&filename_bytes_length.to_be_bytes()); packet_bytes.extend_from_slice(&filename_bytes_length.to_be_bytes());
packet_as_bytes.extend_from_slice(&self.filename.as_bytes()); packet_bytes.extend_from_slice(&self.filename.as_bytes());
// filesize // filesize
packet_as_bytes.extend_from_slice(&self.filesize.to_be_bytes()); packet_bytes.extend_from_slice(&self.filesize.to_be_bytes());
// relative path // relative path
let relative_path_bytes_length: u128 = self.relative_path.len() as u128; let relative_path_bytes_length: u128 = self.relative_path.len() as u128;
packet_as_bytes.extend_from_slice(&relative_path_bytes_length.to_be_bytes()); packet_bytes.extend_from_slice(&relative_path_bytes_length.to_be_bytes());
packet_as_bytes.extend_from_slice(&self.relative_path.as_bytes()); packet_bytes.extend_from_slice(&self.relative_path.as_bytes());
return packet_as_bytes; return packet_bytes;
} }
fn from_bytes(&mut self, packet_bytes: Vec<u8>) -> Option<Error> { fn from_bytes(&mut self, packet_bytes: Vec<u8>) -> Option<Error> {
@ -181,17 +288,17 @@ impl Packet for FileInfoPacket {
} }
// file id // file id
self.file_id = read_u128_slice(&packet_bytes[0..16]); self.file_id = read_u128_slice_be(&packet_bytes[0..16]);
// get filename // get filename
let filename_length: u128 = read_u128_slice(&packet_bytes[16..32]); let filename_length: u128 = read_u128_slice_be(&packet_bytes[16..32]);
self.filename = read_utf8_string_slice(&packet_bytes[32..(filename_length+32) as usize]); self.filename = read_utf8_string_slice(&packet_bytes[32..(filename_length+32) as usize]);
// filesize // filesize
self.filesize = read_u128_slice(&packet_bytes[(filename_length+32) as usize..(filename_length+32+16) as usize]); self.filesize = read_u128_slice_be(&packet_bytes[(filename_length+32) as usize..(filename_length+32+16) as usize]);
// relative path // relative path
let rel_path_length: u128 = read_u128_slice( let rel_path_length: u128 = read_u128_slice_be(
&packet_bytes[(filename_length+32+16) as usize..(filename_length+32+16+16)as usize] &packet_bytes[(filename_length+32+16) as usize..(filename_length+32+16+16)as usize]
); );
self.relative_path = read_utf8_string_slice( self.relative_path = read_utf8_string_slice(
@ -203,15 +310,15 @@ impl Packet for FileInfoPacket {
} }
pub struct FileDataPacket { pub struct FileData {
pub file_id: u128, pub file_id: u128,
pub chunk_no: u128, pub chunk_no: u128,
pub chunk: [u8; CHUNK_SIZE], pub chunk: [u8; CHUNK_SIZE],
} }
impl FileDataPacket { impl FileData {
pub fn empty() -> FileDataPacket { pub fn empty() -> FileData {
return FileDataPacket { return FileData {
file_id: 0, file_id: 0,
chunk_no: 0, chunk_no: 0,
chunk: [0; CHUNK_SIZE], chunk: [0; CHUNK_SIZE],
@ -219,7 +326,7 @@ impl FileDataPacket {
} }
} }
impl Packet for FileDataPacket { impl Packet for FileData {
fn get_type(&self) -> PacketType { fn get_type(&self) -> PacketType {
return PacketType::FileData; return PacketType::FileData;
} }
@ -227,6 +334,8 @@ impl Packet for FileDataPacket {
fn as_bytes(&self) -> Vec<u8> { fn as_bytes(&self) -> Vec<u8> {
let mut packet_bytes: Vec<u8> = Vec::<u8>::new(); let mut packet_bytes: Vec<u8> = Vec::<u8>::new();
packet_bytes.extend_from_slice(&FILEDATA_PACKET_ID.to_be_bytes());
// file id // file id
packet_bytes.extend_from_slice(&self.file_id.to_be_bytes()); packet_bytes.extend_from_slice(&self.file_id.to_be_bytes());
@ -248,24 +357,24 @@ impl Packet for FileDataPacket {
} }
// file id // file id
self.file_id = read_u128_slice(&packet_bytes[0..16]); self.file_id = read_u128_slice_be(&packet_bytes[0..16]);
// chunk number // chunk number
self.chunk_no = read_u128_slice(&packet_bytes[16..32]); self.chunk_no = read_u128_slice_be(&packet_bytes[16..32]);
// chunk bytes // chunk bytes
let chunk_length = read_u128_slice(&packet_bytes[32..48]); let chunk_length = read_u128_slice_be(&packet_bytes[32..48]);
for i in 48..(48+chunk_length) as usize { for i in 48..(48+chunk_length) as usize {
self.chunk[i-48] = packet_bytes[i]; self.chunk[i-48] = packet_bytes[i].to_be();
} }
return None; return None;
} }
} }
pub struct ConnectionShutdownPacket {} pub struct ConnectionShutdown {}
impl Packet for ConnectionShutdownPacket { impl Packet for ConnectionShutdown {
fn get_type(&self) -> PacketType { fn get_type(&self) -> PacketType {
return PacketType::ConnectionShutdown; return PacketType::ConnectionShutdown;
} }
@ -279,34 +388,64 @@ impl Packet for ConnectionShutdownPacket {
} }
} }
pub struct HandshakePaket { pub struct Handshake {
pub protocol_version: u8, pub protocol_version: u8,
pub use_encryption: bool,
pub encryption_type: u8, pub encryption_type: u8,
pub encryption_key: Vec<u8>, pub encryption_key: Vec<u8>,
} }
impl HandshakePaket { impl Handshake {
fn empty() -> HandshakePaket { pub fn empty() -> Handshake {
return HandshakePaket{ return Handshake{
protocol_version: PROTOCOL_LATEST_VERSION, protocol_version: PROTOCOL_LATEST_VERSION,
use_encryption: false, encryption_type: ENCRYPTION_NO_ENCRYPTION,
encryption_type: ECRYPTION_XOR,
encryption_key: vec!(0, 0, 0, 0, 0, 0, 0, 0), encryption_key: vec!(0, 0, 0, 0, 0, 0, 0, 0),
}; };
} }
} }
impl Packet for HandshakePaket { impl Packet for Handshake {
fn get_type(&self) -> PacketType { fn get_type(&self) -> PacketType {
return PacketType::Handshake; return PacketType::Handshake;
} }
fn as_bytes(&self) -> Vec<u8> { fn as_bytes(&self) -> Vec<u8> {
return vec!(HANDSHAKE_PACKET_ID.to_be_bytes()[0]); let mut packet_bytes: Vec<u8> = Vec::<u8>::new();
packet_bytes.extend_from_slice(&HANDSHAKE_PACKET_ID.to_be_bytes());
// protocol version
packet_bytes.extend_from_slice(&PROTOCOL_LATEST_VERSION.to_be_bytes());
// encryption type
packet_bytes.extend_from_slice(&self.encryption_type.to_be_bytes());
// encryption key
packet_bytes.extend_from_slice(&(self.encryption_key.len() as u128).to_be_bytes());
packet_bytes.extend_from_slice(&self.encryption_key);
return packet_bytes;
}
fn from_bytes(&mut self, packet_bytes: Vec<u8>) -> Option<Error> {
if packet_bytes.len() < 1+1+1+16+1 {
return Some(
Error::new(format!("{} bytes is too small for a handshake packet to be valid", packet_bytes.len()).as_str())
);
}
// protocol version
self.protocol_version = packet_bytes[0].to_be();
// encryption type
self.encryption_type = packet_bytes[1].to_be();
// key
let key_len: u128 = read_u128_slice_be(&packet_bytes[1..17]);
for i in 17..(17+key_len) as usize {
self.encryption_key[i-17] = packet_bytes[i].to_be();
} }
fn from_bytes(&mut self, _packet_bytes: Vec<u8>) -> Option<Error> {
return None; return None;
} }
} }

140
src/protocol/specs.rs

@ -13,36 +13,12 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details. GNU Affero General Public License for more details.
*/ */
/*
---PROTOCOL SPECs---
- Packet bytes representation -
First 16 bytes - the total packet length
The next 1 byte - number that represents this packet's type (ID) where:
1: HANDSHAKE PACKET
2: CONNECTION SHUTDOWN PACKET
3: TEXT PACKET
4: FILEINFO PACKET
5: FILEDATA PACKET
Then the internal structure varies from one packet type to the other,
but the content-types are encoded as follows:
u8 - just 1 BE byte
u128 - 16 BE bytes
String - u128 representing the string length and then UTF-8 encoded character bytes
[u8] - u128 as the length of a byte array, then the array itself
*/
use crate::util::error::Error; use crate::util::error::Error;
use crate::protocol::packets::*; use crate::protocol::packets::*;
use std::net;
use std::io::Write;
use std::io::Read;
// Protocol versioning // Protocol versioning
pub const PROTOCOL_LATEST_VERSION: u8 = 1; pub const PROTOCOL_VERSION_1: u8 = 1;
pub const PROTOCOL_LATEST_VERSION: u8 = PROTOCOL_VERSION_1;
// Packets' IDs // Packets' IDs
pub const HANDSHAKE_PACKET_ID: u8 = 1; pub const HANDSHAKE_PACKET_ID: u8 = 1;
@ -55,9 +31,18 @@ pub const FILEDATA_PACKET_ID: u8 = 5;
pub const CHUNK_SIZE: usize = 262_144; // 256 KB pub const CHUNK_SIZE: usize = 262_144; // 256 KB
// Encryption types // Encryption types
pub const ENCRYPTION_NO_ENCRYPTION: u8 = 0;
pub const ECRYPTION_XOR: u8 = 1; pub const ECRYPTION_XOR: u8 = 1;
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq, Debug)]
pub enum RunMode {
SEND,
RECEIVE,
SYNC,
DAEMON,
}
#[derive(PartialEq, Eq, Debug)]
pub enum PacketType { pub enum PacketType {
ConnectionShutdown, ConnectionShutdown,
TextData, TextData,
@ -71,104 +56,3 @@ pub trait Packet {
fn as_bytes(&self) -> Vec<u8>; fn as_bytes(&self) -> Vec<u8>;
fn from_bytes(&mut self, packet_bytes: Vec<u8>) -> Option<Error>; fn from_bytes(&mut self, packet_bytes: Vec<u8>) -> Option<Error>;
} }
pub fn send_packet(conn: &mut net::TcpStream, packet: &dyn Packet) -> Result<(), Error> {
let mut payload: Vec<u8> = Vec::<u8>::new();
let packet_bytes: Vec<u8> = packet.as_bytes();
let packet_len: usize = packet_bytes.len();
payload.extend_from_slice(&u128::to_be_bytes(packet_len as u128));
payload.extend_from_slice(&packet_bytes);
match conn.write_all(&payload) {
Ok(()) => {
return Ok(());
}
Err(error) => {
return Err(Error::new(format!("could not write packet to the connection: {}", error).as_str()))
}
}
}
pub fn read_next_packet(conn: &mut net::TcpStream) -> Result<Box<dyn Packet>, Error> {
let mut u128_buf: [u8; 16] = [0; 16];
match conn.read_exact(&mut u128_buf) {
Ok(()) => {}
Err(error) => {
return Err(Error::new(format!("error while reading initial length bytes: {}", error).as_str()));
}
}
let packet_len: u128 = u128::from_be_bytes(u128_buf);
let mut packet_bytes: Vec<u8> = vec!(0; packet_len as usize);
match conn.read_exact(&mut packet_bytes) {
Ok(()) => {}
Err(error) => {
return Err(Error::new(format!("error while reading packet contents: {}", error).as_str()));
}
}
if packet_bytes.len() < packet_len as usize {
return Err(
Error::new(
format!("read {} packet bytes instead of specified {}", packet_bytes.len(), packet_len).as_str())
);
}
let packet_type_byte: u8 = u8::from_be_bytes([packet_bytes[0]]);
match packet_type_byte {
CONNECTION_SHUTDOWN_PACKET_ID => {
return Ok(Box::new(ConnectionShutdownPacket{}));
}
TEXT_PACKET_ID => {
let mut new_text_packet: TextPacket = TextPacket::empty();
let result = new_text_packet.from_bytes(packet_bytes);
match result {
Some(error) => {
return Err(Error::new(format!("could not construct new text packet: {}", error.text).as_str()));
}
None => {
return Ok(Box::new(new_text_packet));
}
}
}
FILEINFO_PACKET_ID => {
let mut new_fileinfo_packet: FileInfoPacket = FileInfoPacket::empty();
let result = new_fileinfo_packet.from_bytes(packet_bytes);
match result {
Some(error) => {
return Err(Error::new(format!("could not construct new fileinfo packet: {}", error.text).as_str()));
}
None => {
return Ok(Box::new(new_fileinfo_packet));
}
}
}
FILEDATA_PACKET_ID => {
let mut new_filedata_packet: FileDataPacket = FileDataPacket::empty();
let result = new_filedata_packet.from_bytes(packet_bytes);
match result {
Some(error) => {
return Err(Error::new(format!("could not construct new filedata packet: {}", error.text).as_str()));
}
None => {
return Ok(Box::new(new_filedata_packet));
}
}
}
_ => {
return Err(Error::new(format!("invalid packet type ID \"{}\"", packet_type_byte).as_str()));
}
}
}

14
src/util/buf_read.rs

@ -13,15 +13,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details. GNU Affero General Public License for more details.
*/ */
pub fn read_u128(buf: Vec<u8>) -> u128 { pub fn read_u128_slice_be(buf_16: &[u8]) -> u128 {
let mut arr: [u8; 16] = [0; 16];
for i in 0..16 {
arr[i] = buf[i];
}
return u128::from_be_bytes(arr);
}
pub fn read_u128_slice(buf_16: &[u8]) -> u128 {
let mut arr: [u8; 16] = [0; 16]; let mut arr: [u8; 16] = [0; 16];
for i in 0..16 { for i in 0..16 {
arr[i] = buf_16[i]; arr[i] = buf_16[i];
@ -29,10 +21,6 @@ pub fn read_u128_slice(buf_16: &[u8]) -> u128 {
return u128::from_be_bytes(arr); return u128::from_be_bytes(arr);
} }
pub fn read_utf8_string(buf: Vec<u8>) -> String {
return String::from_utf8_lossy(&buf).into_owned();
}
pub fn read_utf8_string_slice(buf: &[u8]) -> String { pub fn read_utf8_string_slice(buf: &[u8]) -> String {
return String::from_utf8_lossy(&buf).into_owned(); return String::from_utf8_lossy(&buf).into_owned();
} }
Loading…
Cancel
Save