From 3436450963ecebede51b2c4c16345992dacad43b Mon Sep 17 00:00:00 2001 From: Gitea Date: Sun, 24 Jul 2022 15:29:29 +0300 Subject: [PATCH] local ip addr; fixed parser with verbose flag; ddtu can send|receive text now ! --- src/args/parser.rs | 178 +++++++++++++++++++++++++--------------- src/main.rs | 15 +++- src/nodes/recv_node.rs | 4 +- src/nodes/send_node.rs | 80 ++++++++++++------ src/protocol/packets.rs | 45 ++++++++-- src/util/local_ip.rs | 53 ++++++++++++ src/util/mod.rs | 3 +- 7 files changed, 271 insertions(+), 107 deletions(-) create mode 100644 src/util/local_ip.rs diff --git a/src/args/parser.rs b/src/args/parser.rs index a14e16d..a2a0fd8 100644 --- a/src/args/parser.rs +++ b/src/args/parser.rs @@ -65,93 +65,135 @@ pub fn parse(raw_args: &Vec) -> Result { return Err(Error::new("no arguments")); } - match raw_args[1].to_lowercase().as_str() { - "help" => { - parsed_args.help = true; - } - - "version" => { - parsed_args.version = true; - } - - "verbose" => { - parsed_args.verbose_output = true; - } - - "send" => { - if arg_amount < 5 { - // not enough arguments ! - return Err(Error::new("not enough arguments for send mode")); + + let mut arg_index: usize = 1; + loop { + match raw_args[arg_index].to_lowercase().as_str() { + "help" => { + parsed_args.help = true; + break; } - - parsed_args.mode = RunMode::SEND; - - // DataType - match raw_args[2].to_lowercase().as_str() { - "text" => { - parsed_args.send_type = DataType::TEXT; - parsed_args.text_to_send = String::from(&raw_args[3]); + + "version" => { + parsed_args.version = true; + break; + } + + "verbose" => { + parsed_args.verbose_output = true; + arg_index += 1; + continue; + } + + "send" => { + if arg_amount < 5 && !parsed_args.verbose_output { + // not enough arguments ! + return Err(Error::new("not enough arguments for send mode")); + } else if arg_amount < 6 && parsed_args.verbose_output { + return Err(Error::new("not enough arguments for send mode")); } - - "file" => { - parsed_args.send_type = DataType::FILE; - parsed_args.send_path = std::path::Path::new(&raw_args[3]).to_path_buf(); + + parsed_args.mode = RunMode::SEND; + + match parsed_args.verbose_output { + true => { + arg_index = 3; + } + + false => { + arg_index = 2; + } } - "dir" => { - parsed_args.send_type = DataType::DIR; - parsed_args.send_path = std::path::Path::new(&raw_args[3]).to_path_buf(); + // DataType + match raw_args[arg_index].to_lowercase().as_str() { + "text" => { + parsed_args.send_type = DataType::TEXT; + parsed_args.text_to_send = String::from(&raw_args[arg_index + 1]); + } + + "file" => { + parsed_args.send_type = DataType::FILE; + parsed_args.send_path = std::path::Path::new(&raw_args[arg_index + 1]).to_path_buf(); + } + + "dir" => { + parsed_args.send_type = DataType::DIR; + parsed_args.send_path = std::path::Path::new(&raw_args[arg_index + 1]).to_path_buf(); + } + + _ => { + return Err(Error::new(format!("invalid DataType {}", raw_args[arg_index]).as_str())); + } } + + // addr + arg_index += 1; + parsed_args.address_str = raw_args[arg_index + 1].to_string(); - _ => { - return Err(Error::new(format!("invalid DataType {}", raw_args[2]).as_str())); - } + break; } - - // src - if parsed_args.send_type == DataType::FILE || parsed_args.send_type == DataType::DIR { - // check if exists - match path::Path::new(raw_args[3].as_str()).exists() { + + "recv" => { + parsed_args.mode = RunMode::RECEIVE; + + let arg_index: usize; + match parsed_args.verbose_output { true => { - parsed_args.send_path = path::Path::new(raw_args[3].as_str()).to_path_buf(); + arg_index = 3; } false => { - return Err(Error::new(format!("\"{}\" does not exist or unreachable", raw_args[3]).as_str())); + arg_index = 2; } } - } + + if arg_amount > 2 && !parsed_args.verbose_output { + parsed_args.save_directory = path::Path::new(raw_args[arg_index].as_str()).to_path_buf(); + } else if arg_amount > 3 && parsed_args.verbose_output { + parsed_args.save_directory = path::Path::new(raw_args[arg_index].as_str()).to_path_buf(); + } - // addr - parsed_args.address_str = raw_args[4].to_string(); - } + break; + } + + "sync" => { + parsed_args.mode = RunMode::SYNC; + + if arg_amount < 3 && !parsed_args.verbose_output { + return Err(Error::new("not enough arguments for syncing")); + } else if arg_amount < 4 && !parsed_args.verbose_output { + return Err(Error::new("not enough arguments for syncing")); + } + + let arg_index: usize; + match parsed_args.verbose_output { + true => { + arg_index = 3; + } + + false => { + arg_index = 2; + } + } + + let sync_path: &path::Path = path::Path::new(raw_args[arg_index].as_str()); + if !sync_path.exists() || !sync_path.is_dir() { + return Err(Error::new(format!("{} does not exist or is not a directory", sync_path.display()).as_str())); + } - "recv" => { - parsed_args.mode = RunMode::RECEIVE; - if arg_amount > 2 { - parsed_args.save_directory = path::Path::new(raw_args[2].as_str()).to_path_buf(); + break; } - } - - "sync" => { - parsed_args.mode = RunMode::SYNC; - if arg_amount < 3 { - return Err(Error::new("not enough arguments for syncing")); + + "daemon" => { + parsed_args.mode = RunMode::DAEMON; + break; } - - let sync_path: &path::Path = path::Path::new(raw_args[2].as_str()); - if !sync_path.exists() || !sync_path.is_dir() { - return Err(Error::new(format!("{} does not exist or is not a directory", sync_path.display()).as_str())); + + _ => { + return Err(Error::new(format!("invalid argument {}", raw_args[arg_index]).as_str())); } } - - "daemon" => { - parsed_args.mode = RunMode::DAEMON; - } - - _ => { - return Err(Error::new(format!("invalid argument {}", raw_args[1]).as_str())); - } } return Ok(parsed_args); diff --git a/src/main.rs b/src/main.rs index bd1fc61..8e8929b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,7 +25,6 @@ use args::parser::Args; use nodes::recv_node::RecvOptions; use nodes::send_node::SendOptions; - const VERSION: &str = "v0.1.0"; const HELP_MESSAGE: &str = "ddtu - digital data transferring tool v0.1.0 @@ -69,8 +68,6 @@ fn main() { return; } } - - println!("{:?}", args); // handle them if args.help { @@ -84,7 +81,17 @@ fn main() { } match args.mode { - RunMode::RECEIVE => { + RunMode::RECEIVE => { + match util::local_ip::get_addr() { + Ok(local_ip_addr) => { + println!("Listening on {}:{}", local_ip_addr.to_string(), DEFAULT_PORT); + } + + Err(_) => { + println!("Listening on port {}", DEFAULT_PORT); + } + } + match nodes::recv_node::start( RecvOptions::new(format!("0.0.0.0:{}", DEFAULT_PORT), args.save_directory), args.verbose_output) { Ok(_) => {} diff --git a/src/nodes/recv_node.rs b/src/nodes/recv_node.rs index 9378399..69fd270 100644 --- a/src/nodes/recv_node.rs +++ b/src/nodes/recv_node.rs @@ -78,10 +78,10 @@ pub fn start(options: RecvOptions, verbose_output: bool) -> Result<(), Error> { incoming_packet = packet; } - Err(_) => { + Err(error) => { // not a valid packet; it's most probably not another instance if verbose_output { - println!("{} did not provide valid handshake packet. Dropping connection...", address); + println!("{} did not provide valid handshake packet: {}. Dropping connection...", address, error.text); } packets::close_connection(&mut connection); continue; diff --git a/src/nodes/send_node.rs b/src/nodes/send_node.rs index cb03cc6..47322c2 100644 --- a/src/nodes/send_node.rs +++ b/src/nodes/send_node.rs @@ -47,7 +47,7 @@ impl SendOptions { pub fn start(options: SendOptions, verbose_output: bool) -> Result<(), Error> { let mut connection: net::TcpStream; - match net::TcpStream::connect(options.address) { + match net::TcpStream::connect(&options.address) { Ok(conn) => { connection = conn; } @@ -59,6 +59,10 @@ pub fn start(options: SendOptions, verbose_output: bool) -> Result<(), Error> { } } + if verbose_output { + println!("Connected to {}", &options.address); + } + // send handshake let handshake: packets::Handshake = packets::Handshake{ protocol_version: specs::PROTOCOL_VERSION_1, @@ -67,7 +71,11 @@ pub fn start(options: SendOptions, verbose_output: bool) -> Result<(), Error> { }; match send_packet(&mut connection, &handshake) { - Ok(_) => {} + Ok(_) => { + if verbose_output { + println!("Sent handshake"); + } + } Err(error) => { packets::close_connection(&mut connection); @@ -88,6 +96,10 @@ pub fn start(options: SendOptions, verbose_output: bool) -> Result<(), Error> { Error::new(format!("handshake hasn't been approved").as_str()) ); } + + if verbose_output { + println!("Handshake has been accepted"); + } } Err(error) => { @@ -136,37 +148,55 @@ pub fn start(options: SendOptions, verbose_output: bool) -> Result<(), Error> { } } }); - - - // handle incoming packets - loop { - let incoming_packet: Box; - match ch_recv.recv() { - Ok(p) => { - incoming_packet = p; - } - Err(error) => { + + match options.send_type { + DataType::TEXT => { + if let Err(error) = send_packet(&mut connection, &packets::Text::new(&options.text_to_send)) { packets::close_connection(&mut connection); - return Err(Error::new( - format!("error receiving a new packet from listener thread: {}", error).as_str() - )); + return Err(Error::new(format!("error sending text: {}", error.text).as_str())) } + packets::close_connection(&mut connection); } - match incoming_packet.get_type() { - PacketType::ConnectionShutdown => { - if verbose_output { - println!("Connection has been shut down"); - } - packets::close_connection(&mut connection); - - break; - } + DataType::FILE => { + + } + + DataType::DIR => { - _ => {} } } + + // // handle incoming packets + // loop { + // let incoming_packet: Box; + // match ch_recv.recv() { + // Ok(p) => { + // incoming_packet = p; + // } + + // Err(error) => { + // packets::close_connection(&mut connection); + // return Err(Error::new( + // format!("error receiving a new packet from listener thread: {}", error).as_str() + // )); + // } + // } + + // match incoming_packet.get_type() { + // PacketType::ConnectionShutdown => { + // if verbose_output { + // println!("Connection has been shut down"); + // } + // packets::close_connection(&mut connection); + + // break; + // } + + // _ => {} + // } + // } return Ok(()); } diff --git a/src/protocol/packets.rs b/src/protocol/packets.rs index 5196081..0eb1847 100644 --- a/src/protocol/packets.rs +++ b/src/protocol/packets.rs @@ -23,7 +23,7 @@ use crate::protocol::specs::*; use crate::fsys::file::ChunkedFile; -pub fn send_packet(conn: &mut net::TcpStream, packet: &dyn Packet) -> Result<(), Error> { +pub fn send_packet(conn: &mut W, packet: &dyn Packet) -> Result<(), Error> where W: Write { let mut payload: Vec = Vec::::new(); let packet_bytes: Vec = packet.as_bytes(); @@ -43,15 +43,29 @@ pub fn send_packet(conn: &mut net::TcpStream, packet: &dyn Packet) -> Result<(), } } -pub fn read_next_packet(conn: &mut net::TcpStream) -> Result, Error> { +pub fn read_next_packet(conn: &mut R) -> Result, Error> where R: Read { let mut u128_buf: [u8; 16] = [0; 16]; + loop { + match conn.read_exact(&mut u128_buf) { + Ok(()) => { + break; + } + + Err(error) => { + // this is guaranteed to happen. It looks like networking in Rust is naive. Stream literally + // is being read ONCE and that's it, even if it is empty, which results in an error. + if error.kind() == std::io::ErrorKind::UnexpectedEof { + // try once again, maybe the packet has been delivered this time + continue; + } - match conn.read_exact(&mut u128_buf) { - Ok(()) => {} - Err(error) => { - return Err(Error::new(format!("error while reading initial length bytes: {}", error).as_str())); + return Err( + Error::new(format!("error reading initial length bytes: {}", error).as_str()) + ); + } } } + let packet_len: u128 = u128::from_be_bytes(u128_buf); let mut packet_bytes: Vec = vec!(0; packet_len as usize); @@ -71,6 +85,23 @@ pub fn read_next_packet(conn: &mut net::TcpStream) -> Result { + let mut new_handshake_packet: Handshake = Handshake::empty(); + match new_handshake_packet.from_bytes(packet_bytes) { + Some(error) => { + return Err(Error::new(format!("could not construct a new handshake packet: {}", error.text).as_str())); + } + + None => { + return Ok(Box::new(new_handshake_packet)); + } + } + } + + HANDSHAKE_ACCEPT_PACKET_ID => { + return Ok(Box::new(HandshakeAccept{})); + } + CONNECTION_SHUTDOWN_PACKET_ID => { return Ok(Box::new(ConnectionShutdown{})); } @@ -118,7 +149,7 @@ pub fn read_next_packet(conn: &mut net::TcpStream) -> Result { - return Err(Error::new(format!("invalid packet type ID \"{}\"", packet_type_byte).as_str())); + return Err(Error::new(format!("invalid or unimplemented packet type ID \"{}\"", packet_type_byte).as_str())); } } } diff --git a/src/util/local_ip.rs b/src/util/local_ip.rs new file mode 100644 index 0000000..0dbd26d --- /dev/null +++ b/src/util/local_ip.rs @@ -0,0 +1,53 @@ +/* +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 crate::util::error::Error; + +pub fn get_addr() -> Result { + let socket: net::UdpSocket; + match net::UdpSocket::bind("0.0.0.0:0") { + Ok(s) => { + socket = s; + } + + Err(error) => { + return Err( + Error::new(format!("error binding UDP socket: {}", error).as_str()) + ); + } + } + + match socket.connect("8.8.8.8:80") { + Ok(()) => {} + Err(error) => { + return Err( + Error::new(format!("could not connect to 8.8.8.8: {}", error).as_str()) + ); + } + } + + match socket.local_addr() { + Ok(addr) => { + return Ok(addr.ip()); + } + + Err(error) => { + return Err( + Error::new(format!("could not get local IP address from socket: {}", error).as_str()) + ); + } + } +} \ No newline at end of file diff --git a/src/util/mod.rs b/src/util/mod.rs index e331771..b72bb7b 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,2 +1,3 @@ pub mod error; -pub mod buf_read; \ No newline at end of file +pub mod buf_read; +pub mod local_ip; \ No newline at end of file