Browse Source

local ip addr; fixed parser with verbose flag; ddtu can send|receive text now !

master
Gitea 2 years ago
parent
commit
3436450963
  1. 158
      src/args/parser.rs
  2. 13
      src/main.rs
  3. 4
      src/nodes/recv_node.rs
  4. 76
      src/nodes/send_node.rs
  5. 45
      src/protocol/packets.rs
  6. 53
      src/util/local_ip.rs
  7. 1
      src/util/mod.rs

158
src/args/parser.rs

@ -65,93 +65,135 @@ pub fn parse(raw_args: &Vec<String>) -> Result<Args, Error> {
return Err(Error::new("no arguments"));
}
match raw_args[1].to_lowercase().as_str() {
"help" => {
parsed_args.help = true;
}
"version" => {
parsed_args.version = true;
}
let mut arg_index: usize = 1;
loop {
match raw_args[arg_index].to_lowercase().as_str() {
"help" => {
parsed_args.help = true;
break;
}
"verbose" => {
parsed_args.verbose_output = true;
}
"version" => {
parsed_args.version = true;
break;
}
"send" => {
if arg_amount < 5 {
// not enough arguments !
return Err(Error::new("not enough arguments for send mode"));
"verbose" => {
parsed_args.verbose_output = true;
arg_index += 1;
continue;
}
parsed_args.mode = RunMode::SEND;
"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"));
}
parsed_args.mode = RunMode::SEND;
match parsed_args.verbose_output {
true => {
arg_index = 3;
}
// 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]);
false => {
arg_index = 2;
}
}
"file" => {
parsed_args.send_type = DataType::FILE;
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()));
}
}
"dir" => {
parsed_args.send_type = DataType::DIR;
parsed_args.send_path = std::path::Path::new(&raw_args[3]).to_path_buf();
// addr
arg_index += 1;
parsed_args.address_str = raw_args[arg_index + 1].to_string();
break;
}
"recv" => {
parsed_args.mode = RunMode::RECEIVE;
let arg_index: usize;
match parsed_args.verbose_output {
true => {
arg_index = 3;
}
false => {
arg_index = 2;
}
}
_ => {
return Err(Error::new(format!("invalid DataType {}", raw_args[2]).as_str()));
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();
}
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() {
"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 => {
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;
}
}
}
// addr
parsed_args.address_str = raw_args[4].to_string();
}
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);

13
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
@ -70,8 +69,6 @@ fn main() {
}
}
println!("{:?}", args);
// handle them
if args.help {
println!("{}", HELP_MESSAGE);
@ -85,6 +82,16 @@ fn main() {
match args.mode {
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(_) => {}

4
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;

76
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) => {
@ -138,35 +150,53 @@ pub fn start(options: SendOptions, verbose_output: bool) -> Result<(), Error> {
});
// handle incoming packets
loop {
let incoming_packet: Box<dyn Packet>;
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);
DataType::FILE => {
break;
}
}
DataType::DIR => {
_ => {}
}
}
// // handle incoming packets
// loop {
// let incoming_packet: Box<dyn Packet>;
// 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(());
}

45
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<W>(conn: &mut W, packet: &dyn Packet) -> Result<(), Error> where W: Write {
let mut payload: Vec<u8> = Vec::<u8>::new();
let packet_bytes: Vec<u8> = 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<Box<dyn Packet + Send>, Error> {
pub fn read_next_packet<R>(conn: &mut R) -> Result<Box<dyn Packet + Send>, Error> where R: Read {
let mut u128_buf: [u8; 16] = [0; 16];
loop {
match conn.read_exact(&mut u128_buf) {
Ok(()) => {
break;
}
match conn.read_exact(&mut u128_buf) {
Ok(()) => {}
Err(error) => {
return Err(Error::new(format!("error while reading initial length bytes: {}", error).as_str()));
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;
}
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<u8> = vec!(0; packet_len as usize);
@ -71,6 +85,23 @@ pub fn read_next_packet(conn: &mut net::TcpStream) -> Result<Box<dyn Packet + Se
let packet_type_byte: u8 = u8::from_be_bytes([packet_bytes[0]]);
match packet_type_byte {
HANDSHAKE_PACKET_ID => {
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<Box<dyn Packet + Se
}
_ => {
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()));
}
}
}

53
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<net::IpAddr, Error> {
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())
);
}
}
}

1
src/util/mod.rs

@ -1,2 +1,3 @@
pub mod error;
pub mod buf_read;
pub mod local_ip;
Loading…
Cancel
Save