From c5d696e079b7fbfa54b3460bebdaf185a56574de Mon Sep 17 00:00:00 2001 From: bnewbold Date: Sun, 29 May 2016 20:31:44 -0400 Subject: add client mode for testing/dev; misc progress --- src/client.rs | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/common.rs | 13 +++++++++++-- src/main.rs | 5 ++++- src/server.rs | 40 ++++++++++++++++++++++----------------- 4 files changed, 97 insertions(+), 21 deletions(-) diff --git a/src/client.rs b/src/client.rs index 637a216..ccdf07d 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,4 +1,5 @@ +extern crate getopts; extern crate utp; use super::common; @@ -6,7 +7,9 @@ use super::common; use std::string::String; use std::env; use std::process; +use std::process::exit; use std::process::Command; +use getopts::Options; use utp::{UtpSocket}; pub fn run_client(host: &str, local_file: &str, remote_file: &str, remote_is_dir: bool, is_recv: bool) { @@ -36,7 +39,7 @@ pub fn run_client(host: &str, local_file: &str, remote_file: &str, remote_is_dir let reply = String::from_utf8_lossy(&ssh_output.stdout); println!("SSH reply: {}", reply); let words: Vec<&str> = reply.split_whitespace().collect(); - if words.len() != 5 || words[0] != "UDP" || words[1] != "CONNECT" { + if words.len() != 5 || words[0] != "UCP" || words[1] != "CONNECT" { panic!("Unexpected data via SSH pipe (TCP)"); } let remote_host = words[2]; @@ -57,3 +60,58 @@ pub fn run_client(host: &str, local_file: &str, remote_file: &str, remote_is_dir } stream.close().unwrap(); } + +fn usage_client(opts: Options) { + let brief = "usage:\tucp client ..."; // XXX: + println!(""); + println!("IMPORTANT: this is the client mode of ucp. Unless you are developing/debugging, you probably want the 'regular' one (from the 'client' from you command)"); + print!("{}", opts.usage(&brief)); +} + +pub fn main_client() { + + let args: Vec = env::args().collect(); + + let mut opts = Options::new(); + opts.optflag("h", "help", "print this help menu"); + //opts.optflag("v", "verbose", "more debugging messages"); + opts.optflag("d", "dir-mode", "read/write a dir instead of file (client side)"); + opts.optopt("f", "from", "file or dir to read from (client side)", "FILE"); + opts.optopt("t", "to", "file or dir to write to (client side)", "FILE"); + opts.reqopt("", "host", "remote hostname to connect to", "HOSTNAME"); + opts.reqopt("", "port", "remote port to connect to", "PORT"); + + assert!(args.len() >= 2 && args[1] == "client"); + let matches = match opts.parse(&args[2..]) { + Ok(m) => { m } + Err(f) => { println!("Error parsing args!"); usage_client(opts); exit(-1); } + }; + + if matches.opt_present("h") { + usage_client(opts); + return; + } + + //let verbose: bool = matches.opt_present("v"); + let dir_mode: bool = matches.opt_present("d"); + + match (matches.opt_present("f"), matches.opt_present("t")) { + (true, true) | (false, false) => { + println!("Must be either 'from' or 'to', but not both"); + exit(-1); + }, + _ => {}, + } + + let port = matches.opt_str("port").unwrap().parse::().unwrap(); + let mut socket = UtpSocket::connect((matches.opt_str("host").unwrap().as_str(), port)).unwrap(); + let mut stream = socket.into(); + println!("opened socket"); + if matches.opt_present("f") { + common::source_files(&mut stream, &matches.opt_str("f").unwrap(), dir_mode); + } + if matches.opt_present("t") { + common::sink_files(&mut stream, &matches.opt_str("t").unwrap(), dir_mode); + } + stream.close().unwrap(); +} diff --git a/src/common.rs b/src/common.rs index 421f3a5..afdfe06 100644 --- a/src/common.rs +++ b/src/common.rs @@ -13,6 +13,7 @@ use utp::{UtpStream}; pub fn source_files(stream: &mut UtpStream, file_path: &str, recursive: bool) { + println!("SOURCE FILE: {}", file_path); if recursive { unimplemented!(); } let mut f = File::open(file_path).unwrap(); let metadata = f.metadata().unwrap(); @@ -21,8 +22,9 @@ pub fn source_files(stream: &mut UtpStream, file_path: &str, recursive: bool) { let flen: usize = metadata.len() as usize; // Format as 4 digits octal, left-padding with zero - let line = format!("C{:0<4o} {} {}\n", fmode, flen, file_path); + let line = format!("C{:0>4o} {} {}\n", fmode, flen, file_path); stream.write_all(line.as_bytes()).unwrap(); + println!("{}", line); let mut byte_buf = [0; 1]; stream.read_exact(&mut byte_buf).unwrap(); @@ -43,6 +45,7 @@ pub fn source_files(stream: &mut UtpStream, file_path: &str, recursive: bool) { let mut wbuf = &mut buf[..rlen]; stream.write_all(&wbuf).unwrap(); sent += rlen; + //println!("sent: {}", sent); } // f.close(); XXX: stream.read_exact(&mut byte_buf).unwrap(); @@ -73,6 +76,7 @@ fn raw_read_line(stream: &mut UtpStream) -> io::Result { // implementations of Read and Write on immutable references to UtpStream (a la TcpStream, File, et // al) pub fn sink_files(stream: &mut UtpStream, file_path: &str, recursive: bool) { + println!("SINK FILE: {}", file_path); if recursive { unimplemented!(); } let mut f = File::create(file_path).unwrap(); @@ -81,13 +85,17 @@ pub fn sink_files(stream: &mut UtpStream, file_path: &str, recursive: bool) { stream.read_exact(&mut byte_buf).unwrap(); let msg_type = byte_buf[0]; match msg_type as char { - 'C' => {} // pass + 'C' => { + println!("Going to create!"); + }, 'D' => { unimplemented!(); }, 'E' => { unimplemented!(); }, 'T' => { unimplemented!(); }, _ => { panic!(format!("Unexpected message type: {}", msg_type)); }, }; let line = raw_read_line(stream).unwrap(); + println!("got msg: {}", line); + stream.write(&[0]).unwrap(); let line: Vec<&str> = line.split_whitespace().collect(); assert!(line.len() == 3); let fmode: u32 = u32::from_str_radix(line[0], 8).unwrap(); @@ -100,6 +108,7 @@ pub fn sink_files(stream: &mut UtpStream, file_path: &str, recursive: bool) { let mut received: usize = 0; while received < flen { let rlen = stream.read(&mut buf).unwrap(); + //println!("recieved: {}", rlen); assert!(rlen > 0); let mut wbuf = &mut buf[..rlen]; f.write_all(&wbuf).unwrap(); diff --git a/src/main.rs b/src/main.rs index f916958..dc328f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,10 +24,13 @@ fn main() { let args: Vec = env::args().collect(); - // First check for "hidden" server mode + // First check for "hidden" server and client modes if args.len() > 1 && args[1] == "server" { server::main_server(); return; + } else if args.len() > 1 && args[1] == "client" { + client::main_client(); + return; } let mut opts = Options::new(); diff --git a/src/server.rs b/src/server.rs index 4c590be..390093e 100644 --- a/src/server.rs +++ b/src/server.rs @@ -12,7 +12,7 @@ use std::process::exit; use getopts::Options; use utp::{UtpSocket, UtpListener}; -fn run_server(path: &str, is_recv: bool, recursive: bool) { +fn run_server(path: &str, is_recv: bool, recursive: bool, daemonize: bool) { // TODO: try to detect the address the SSH connection came in on via the SSH_CONNECTION env // variable. @@ -27,23 +27,27 @@ fn run_server(path: &str, is_recv: bool, recursive: bool) { let listen_addr = listener.local_addr().unwrap().ip(); // Send back details so client can connect - println!("UDP CONNECT {} {} {}", listen_addr, listen_port, ""); + println!("UCP CONNECT {} {} {}", listen_addr, listen_port, ""); // TODO: maybe wait for an ACK of some sort here before daemonizing? - // At this point we partially daemonize (fork and redirect terminal), so that SSH will drop us. - // But, don't clobber working dir. - let working_dir = match env::home_dir() { - Some(path) => path, - None => env::current_dir().unwrap(), - }; - // XXX: should maybe use log/syslog from here on? - let daemonizer = daemonize::Daemonize::new().working_directory(working_dir); - - match daemonizer.start() { - Ok(_) => println!("Success, daemonized"), - Err(e) => println!("{}", e), - } + if daemonize { + // At this point we partially daemonize (fork and redirect terminal), so that SSH will drop us. + // But, don't clobber working dir. + let working_dir = match env::home_dir() { + Some(path) => path, + None => env::current_dir().unwrap(), + }; + // XXX: should maybe use log/syslog from here on? + let daemonizer = daemonize::Daemonize::new().working_directory(working_dir); + + match daemonizer.start() { + Ok(_) => println!("Success, daemonized"), + Err(e) => println!("{}", e), + } + } else { + println!("Not daemonizing (DEBUG MODE)"); + } let (mut socket, _src) = listener.accept().unwrap(); println!("Got connection from {}", socket.peer_addr().unwrap()); @@ -72,6 +76,7 @@ pub fn main_server() { opts.optflag("h", "help", "print this help menu"); //opts.optflag("v", "verbose", "more debugging messages"); opts.optflag("d", "dir-mode", "read/write a dir instead of file (server side)"); + opts.optflag("", "no-daemonize", "don't daemonize (for debuggign)"); opts.optopt("f", "from", "file or dir to read from (server side)", "FILE"); opts.optopt("t", "to", "file or dir to write to (server side)", "FILE"); @@ -88,6 +93,7 @@ pub fn main_server() { //let verbose: bool = matches.opt_present("v"); let dir_mode: bool = matches.opt_present("d"); + let daemonize: bool = !matches.opt_present("no-daemonize"); match (matches.opt_present("f"), matches.opt_present("t")) { (true, true) | (false, false) => { @@ -98,9 +104,9 @@ pub fn main_server() { } if matches.opt_present("f") { - run_server(&matches.opt_str("f").unwrap(), false, dir_mode); + run_server(&matches.opt_str("f").unwrap(), false, dir_mode, daemonize); } if matches.opt_present("t") { - run_server(&matches.opt_str("t").unwrap(), true, dir_mode); + run_server(&matches.opt_str("t").unwrap(), true, dir_mode, daemonize); } } -- cgit v1.2.3