From c1ad27e92e38fb050a8cd0150d593d0ecda3eda2 Mon Sep 17 00:00:00 2001 From: Zhou Rundong Date: Mon, 9 Jul 2018 00:25:07 -0700 Subject: [PATCH 01/19] Add netcat placeholder. --- Cargo.toml | 4 +++- src/networking/nc/mod.rs | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 src/networking/nc/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 0efe15c..1c82486 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,8 +30,10 @@ lsb = [ ] ping = ["libmesabox/ping"] +nc = [] networking = [ - "ping" + "ping", + "nc" ] cat = ["libmesabox/cat"] diff --git a/src/networking/nc/mod.rs b/src/networking/nc/mod.rs new file mode 100644 index 0000000..cc8174b --- /dev/null +++ b/src/networking/nc/mod.rs @@ -0,0 +1,21 @@ +use super::{UtilSetup, Result, ArgsIter, UtilWrite}; + +use clap::Arg; +use std::borrow::Cow; +use std::io::Write; +use std::os::unix::ffi::OsStrExt; + +pub(crate) const NAME: &str = "nc"; +pub(crate) const DESCRIPTION: &str = "netcat"; + + +pub fn execute(setup: &mut S, args: T) -> Result<()> +where + S: UtilSetup, + T: ArgsIter, +{ + println!("this is netcat"); + + Ok(()) +} + From 1cb5afca9705095161c6f14e82d8114021208ef8 Mon Sep 17 00:00:00 2001 From: RundongZhou Date: Tue, 10 Jul 2018 21:42:25 -0700 Subject: [PATCH 02/19] Add dependencies for nc. --- src/networking/nc/mod.rs | 1004 +++++++++++++++++++++++++++++++++++++- 1 file changed, 1002 insertions(+), 2 deletions(-) diff --git a/src/networking/nc/mod.rs b/src/networking/nc/mod.rs index cc8174b..cc4618e 100644 --- a/src/networking/nc/mod.rs +++ b/src/networking/nc/mod.rs @@ -1,13 +1,958 @@ +extern crate mio; +extern crate clap; +extern crate libc; +extern crate socket2; +// extern crate tempfile; + +use tempfile::NamedTempFile; +use clap::{Arg, App, ArgMatches}; +use mio::{Events, Event, Poll, Ready, PollOpt, Token}; +use libc::{AF_UNSPEC, AF_INET, AF_INET6, AF_UNIX}; +use std::io; +use std::net::{SocketAddr}; +use mio::unix::EventedFd; +use std::io::{Read,Write, ErrorKind}; +use mio::unix::UnixReady; +use std::thread::sleep; +use std::fmt::Debug; +use std::os::unix::io::AsRawFd; +use std::os::unix::io::RawFd; +use std::time::Duration; +use std::net::{ToSocketAddrs}; use super::{UtilSetup, Result, ArgsIter, UtilWrite}; -use clap::Arg; +// use clap::Arg; use std::borrow::Cow; -use std::io::Write; +// use std::io::Write; use std::os::unix::ffi::OsStrExt; +use socket2::{Socket, Domain}; + pub(crate) const NAME: &str = "nc"; pub(crate) const DESCRIPTION: &str = "netcat"; +const BUFSIZE: usize = 16384; + +#[derive(Debug)] +struct NcOptions { + dflag: bool, + iflag: bool, + interval: Option, + lflag: bool, + host: String, + portlist: Vec, + unixflag: bool, + uflag: bool, + family: i32, + kflag: bool, + s_addr: Option, + pflag: bool, + vflag: bool, + zflag: bool, + timeout: Option, + unix_dg_tmp_socket: String, +} + +fn build_ports(ports: &str) -> Vec{ + return vec!(ports.parse::().expect(&format!("invalid port[s] {}", ports))); +} + +fn usage(ret: bool, msg: &str) { + eprint!("{}", msg); + if ret { + std::process::exit(1); + } +} + +fn err_exit(msg: &str) { + eprint!("{}", msg); + std::process::exit(1); +} + +fn warn(msg: &str) { + eprint!("{}", msg); +} + +fn debug_info(msg: &str) { + println!("{}", msg); +} + +impl NcOptions { + pub fn parse(matches: ArgMatches, msg: &str) -> Option { + let mut portlist = vec!(); + let lflag = matches.is_present("l"); + let mut host = String::from("127.0.0.1"); + let uport:String; + let mut interval = None; + let mut timeout = None; + let s_addr = match matches.value_of("s") { + Some(addr) => Some(String::from(addr)), + None => None + }; + let uflag = matches.is_present("u"); + let pflag = matches.is_present("p"); + + let zflag = matches.is_present("z"); + let kflag = matches.is_present("k"); + + + /* Cruft to make sure options are clean, and used properly. */ + let positionals: Vec<&str> = if matches.is_present("positionals") { + matches.values_of("positionals").unwrap().collect() + } else { + vec!() + }; + + let family = if matches.is_present("U") { + AF_UNIX + } else if matches.is_present("6") { + AF_INET6 + } else if matches.is_present("4") { + AF_INET + } else { + AF_UNSPEC + }; + + if positionals.len() == 1 { + if family == AF_UNIX { + host = String::from(positionals[0]); + uport = String::new(); + } else { + if !lflag { + usage(true, msg); + return None; + } + uport = String::from(positionals[0]); + // host = String::from("localhost"); + } + } else if positionals.len() >= 2 { + host = String::from(positionals[0]); + uport = String::from(positionals[1]); + } else { + usage(true, msg); + return None; + } + + if lflag && s_addr.is_some() { + err_exit("cannot use -s and -l"); + } + if lflag && pflag { + err_exit("cannot use -p and -l"); + } + if lflag && zflag { + err_exit("cannot use -z and -l"); + } + if !lflag && kflag { + err_exit("must use -l with -k"); + } + + if !uport.is_empty() { + portlist = build_ports(&uport); + } + + if matches.is_present("i") { + let sec = matches.value_of("i").unwrap().parse::().unwrap(); + interval = Some(Duration::new(sec, 0)); + } + + if matches.is_present("w") { + let sec = matches.value_of("w").unwrap().parse::().unwrap(); + timeout = Some(Duration::new(sec, 0)); + } + + let mut unix_dg_tmp_socket = String::new(); + + /* Get name of temporary socket for unix datagram client */ + if family == AF_UNIX && uflag && !lflag { + unix_dg_tmp_socket = if s_addr.is_some() { + s_addr.clone().unwrap() + } else { + let nf = NamedTempFile::new().expect("failed to create temporary file"); + let path = String::from(nf.path().to_str().unwrap()); + // nf.persist(&path).expect("failed to create temporary file"); + path + }; + } + + let ret = NcOptions { + dflag: matches.is_present("d"), + iflag: matches.is_present("i"), + interval: interval, + lflag: matches.is_present("l"), + unixflag: matches.is_present("U"), + uflag: uflag, + host: host, + portlist: portlist, + family: family, + kflag: matches.is_present("k"), + // sflag: sflag, + s_addr: s_addr, + pflag: pflag, + vflag: matches.is_present("v"), + timeout: timeout, + unix_dg_tmp_socket: unix_dg_tmp_socket, + zflag: zflag, + }; + + return Some(ret); + } +} + +fn remove_item(v: &mut Vec, item: T) { + debug_info(&format!("remove_item {:?}", item)); + // let index = v.iter().position(|t| *t == item).unwrap(); + match v.iter().position(|t| *t == item) { + Some(i) => v.remove(i), + None => return + }; +} + +// struct NcCore<'a> { +// sock: &'a mut Socket, +// opts: &'a NcOptions, +// poll: Poll, +// net_interest: Ready, +// event_stdin: EventedFd<'a>, +// event_net: EventedFd<'a>, +// event_stdout: EventedFd<'a>, +// stdinbuf: [u8; BUFSIZE], +// netinbuf: [u8; BUFSIZE], +// stdinbuf_bac: [u8; BUFSIZE], +// netinbuf_bac: [u8; BUFSIZE], +// netinbufpos: usize, +// stdinbufpos: usize, +// open_ends: Vec, +// } + +// impl <'a> NcCore<'a> { +// const STDIN_POLL: i32 = 1; +// const STDOUT_POLL: i32 = 2; +// const NETIN_POLL: i32 = 4; +// const NETOUT_POLL: i32 = 8; + +// const TK_STDIN: Token = Token(0); +// const TK_STDOUT: Token = Token(1); +// const TK_NET: Token = Token(2); + +// fn new(sock: &'a mut Socket, opts: &'a NcOptions, net_fd: &'a RawFd) -> NcCore<'a> { +// let mut ret = NcCore { +// sock: sock, +// opts: opts, + +// poll: Poll::new().unwrap(), +// net_interest: Ready::readable(), +// event_stdin: EventedFd(&0), +// event_net: EventedFd(net_fd), +// event_stdout: EventedFd(&1), +// stdinbuf: [0; BUFSIZE], +// netinbuf: [0; BUFSIZE], +// stdinbuf_bac: [0; BUFSIZE], +// netinbuf_bac: [0; BUFSIZE], +// netinbufpos: 0, +// stdinbufpos: 0, +// open_ends: vec![NcCore::STDIN_POLL, NcCore::STDOUT_POLL, NcCore::NETIN_POLL, NcCore::NETOUT_POLL], +// }; + +// ret.poll.register(&ret.event_stdin, NcCore::TK_STDIN, Ready::readable(), +// PollOpt::empty()).unwrap(); +// ret.poll.register(&ret.event_net, NcCore::TK_NET, ret.net_interest, +// PollOpt::empty()).unwrap(); +// ret.poll.register(&ret.event_stdout, NcCore::TK_STDOUT, Ready::empty(), +// PollOpt::empty()).unwrap(); + +// if ret.opts.dflag { +// ret.remove_stdin(); +// } + +// ret +// } + +// /* +// * readwrite() +// * Loop that polls on the network file descriptor and stdin. +// */ +// fn readwrite(&mut self) { +// let mut events = Events::with_capacity(1024); + +// let mut last_ready_end = -1; + +// loop { +// /* both inputs are gone, buffers are empty, we are done */ +// if self.stdin_gone() && self.netin_gone() && +// self.stdinbuf_empty() && self.netinbuf_empty() { +// self.sock.shutdown(std::net::Shutdown::Both); +// return; +// } + +// /* both outputs are gone, we can't continue */ +// if self.stdout_gone() && self.netout_gone() { +// self.sock.shutdown(std::net::Shutdown::Both); +// return; +// } + +// /* listen and net in gone, queues empty, done */ +// if self.opts.lflag && self.netin_gone() && +// self.stdinbuf_empty() && self.netinbuf_empty() { +// self.sock.shutdown(std::net::Shutdown::Both); +// return; +// } + +// /* help says -i is for "wait between lines sent". We read and +// * write arbitrary amounts of data, and we don't want to start +// * scanning for newlines, so this is as good as it gets */ +// if self.opts.iflag { +// sleep(self.opts.interval.unwrap()); +// } + +// self.poll.poll(&mut events, None).expect("polling error"); + +// /* timeout happened */ +// if events.is_empty() { +// return; +// } + +// for event in &events { +// // if any error happend, stop watching for the corresponding fd +// if event.readiness().is_error() { +// self.handle_error_event(&event); +// } + +// NcCore::debug_print_ready_end(&event, &mut last_ready_end); + +// if event.readiness().contains(UnixReady::hup()) { +// self.handle_hup_event(&event); +// } + +// // if no net out, finish watching stdin +// if self.netout_gone() { +// self.remove_stdin(); +// } + +// // if no stdout, stop watching net in +// if self.stdout_gone() { +// if !self.netin_gone() { +// self.sock.shutdown(std::net::Shutdown::Read); +// } +// self.remove_stdin(); +// } + +// // if stdin readable and buf not full, try to read stdin +// // error or eof, remove and deregister stdin +// // if buf not emtpy, reregister writable for netout +// // if buf full, reregister with empty +// if event.token() == NcCore::TK_STDIN && event.readiness().is_readable() && !self.stdinbuf_full() { +// self.read_stdin(); + +// if !self.stdinbuf_empty() { +// self.enable_netout(); +// } else if self.stdinbuf_full() { +// self.disable_stdin(); +// } +// } + +// // if net writable and buf not empty, try to write to net +// // error, stop watching for netout +// // memmove if needed +// // if stdinbuf empty, reregister to remove netout writable +// // if buf not full, reregister to add stdin readable +// if event.token() == NcCore::TK_NET && event.readiness().is_writable() && !self.stdinbuf_empty() { +// self.write_netout(); +// if self.stdinbuf_empty() { +// self.disable_netout(); +// } else if !self.stdinbuf_full() { +// self.enable_stdin(); +// } +// } + +// // if net readable and buf not full, try to read net +// // error or eof, remove and deregister netin +// // if buf not emtpy, reregister writable for stdout +// // if buf full, reregister to remove netin +// if event.token() == NcCore::TK_NET && event.readiness().is_readable() && !self.netinbuf_full() { +// self.read_netin(); +// if !self.netinbuf_empty() { +// self.enable_stdout(); +// } else if self.netinbuf_full() { +// self.disable_netin(); +// } +// } + +// // if stdout writable and buf not empty, try to write to stdout +// // error, stop watching for stdout +// // memmove if needed +// // if netinbuf empty, reregister to remove stdout writable +// // if buf not full, reregister to add netin readable +// if event.token() == NcCore::TK_STDOUT && event.readiness().is_writable() && !self.netinbuf_empty() { +// self.write_stdout(); +// if self.netinbuf_empty() { +// self.disable_stdout(); +// } else if !self.netinbuf_full() { +// self.enable_netin(); +// } +// } + +// // if stdin gone and stdinbuf empty, remove netout +// if self.stdin_gone() && self.stdinbuf_empty() { +// if !self.netout_gone() { +// // TODO: check && opts.Nflag { +// self.sock.shutdown(std::net::Shutdown::Write); +// } +// self.remove_netout(); +// } + +// // if netin gone and netinbuf empty, remove stdout +// if self.netin_gone() && self.netinbuf_empty() { +// self.remove_stdout(); +// } +// } + +// } +// } + +// fn run(sock: &mut Socket, opts: &NcOptions) { +// let net_fd = sock.as_raw_fd(); +// let mut nc = NcCore::new(sock, &opts, &net_fd); +// nc.readwrite(); +// } + +// fn stdin_gone(&self) -> bool { +// !self.open_ends.contains(&NcCore::STDIN_POLL) +// } + +// fn stdout_gone(&self) -> bool { +// !self.open_ends.contains(&NcCore::STDOUT_POLL) +// } + +// fn netin_gone(&self) -> bool { +// !self.open_ends.contains(&NcCore::NETIN_POLL) +// } + +// fn netout_gone(&self) -> bool { +// !self.open_ends.contains(&NcCore::NETOUT_POLL) +// } + +// fn stdinbuf_empty(&self) -> bool { +// self.stdinbufpos == 0 +// } + +// fn stdinbuf_full(&self) -> bool { +// self.stdinbufpos >= BUFSIZE +// } + +// fn netinbuf_empty(&self) -> bool { +// self.netinbufpos == 0 +// } + +// fn netinbuf_full(&self) -> bool { +// self.netinbufpos >= BUFSIZE +// } + +// fn remove_stdin(&mut self) { +// remove_item(&mut self.open_ends, NcCore::STDIN_POLL); +// self.poll.deregister(&self.event_stdin); +// } + +// fn remove_stdout(&mut self) { +// debug_info("remove_stdout"); +// remove_item(&mut self.open_ends, NcCore::STDOUT_POLL); +// self.poll.deregister(&self.event_stdout); +// } + +// fn remove_netin(&mut self) { +// remove_item(&mut self.open_ends, NcCore::NETIN_POLL); +// self.net_interest.remove(Ready::readable()); +// self.reregister_net(); +// } + +// fn remove_netout(&mut self) { +// remove_item(&mut self.open_ends, NcCore::NETOUT_POLL); +// self.net_interest.remove(Ready::writable()); +// self.reregister_net(); +// } + +// fn reregister_net(&mut self) { +// self.poll.reregister(&self.event_net, NcCore::TK_NET, self.net_interest, +// PollOpt::empty()).unwrap(); +// } + +// fn enable_netin(&mut self) { +// self.net_interest |= Ready::readable(); +// self.reregister_net(); +// } + +// fn disable_netin(&mut self) { +// self.net_interest.remove(Ready::readable()); +// self.reregister_net(); +// } + +// fn enable_netout(&mut self) { +// self.net_interest |= Ready::writable(); +// self.reregister_net(); +// } + +// fn disable_netout(&mut self) { +// self.net_interest.remove(Ready::writable()); +// self.reregister_net(); +// } + +// fn enable_stdin(&mut self) { +// self.poll.reregister(&self.event_stdin, NcCore::TK_STDIN, Ready::readable(), +// PollOpt::empty()).unwrap(); +// } + +// fn disable_stdin(&mut self) { +// self.poll.reregister(&self.event_stdin, NcCore::TK_STDIN, Ready::empty(), +// PollOpt::empty()).unwrap(); +// } + +// fn enable_stdout(&mut self) { +// self.poll.reregister(&self.event_stdout, NcCore::TK_STDOUT, Ready::writable(), +// PollOpt::empty()).unwrap(); +// } + +// fn disable_stdout(&mut self) { +// self.poll.reregister(&self.event_stdout, NcCore::TK_STDOUT, Ready::empty(), +// PollOpt::empty()).unwrap(); +// } + +// fn remove_net(&mut self) { +// remove_item(&mut self.open_ends, NcCore::NETIN_POLL); +// remove_item(&mut self.open_ends, NcCore::NETOUT_POLL); +// self.poll.deregister(&self.event_net); +// } + +// fn read_stdin(&mut self) { +// let mut remove = false; +// match io::stdin().read(&mut self.stdinbuf[self.stdinbufpos..]) { +// Ok(len) => { +// remove = len == 0; +// self.stdinbufpos += len; +// }, +// Err(e) => { +// match e.kind() { +// ErrorKind::Interrupted => {}, +// _ => remove = true +// } +// }, +// } +// if remove { +// self.remove_netin(); +// } +// } + +// fn write_netout(&mut self) { +// let mut remove = false; +// match self.sock.write(&mut self.stdinbuf[0..self.stdinbufpos]) { +// Ok(len) => { +// debug_info(&format!("write ok len={}", len)); +// // remove = len == 0; +// if len > 0 { +// self.stdinbufpos -= len; +// if self.stdinbufpos > 0 { +// self.stdinbuf_bac.copy_from_slice(&self.stdinbuf[len..len+self.stdinbufpos]); +// self.stdinbuf.copy_from_slice(&self.stdinbuf_bac); +// } +// } +// }, +// Err(e) => { +// debug_info(&format!("write error {:?}", e)); +// match e.kind() { +// ErrorKind::Interrupted => {}, +// _ => remove = true +// } +// }, +// } +// if remove { +// self.remove_netout(); +// } +// } + +// fn read_netin(&mut self) { +// let mut remove = false; +// match self.sock.read(&mut self.netinbuf[self.netinbufpos..]) { +// Ok(len) => { +// remove = len == 0; +// self.netinbufpos += len; +// }, +// Err(e) => { +// match e.kind() { +// ErrorKind::Interrupted => {}, +// _ => remove = true +// } +// }, +// } +// if remove { +// self.remove_netin(); +// } +// } + +// fn write_stdout(&mut self) { +// let mut remove = false; +// match io::stdout().write(&mut self.netinbuf[0..self.netinbufpos]) { +// Ok(len) => { +// // remove = len == 0; +// if len > 0 { +// self.netinbufpos -= len; +// if self.netinbufpos > 0 { +// self.netinbuf_bac.copy_from_slice(&self.netinbuf[len..len+self.netinbufpos]); +// self.netinbuf.copy_from_slice(&self.netinbuf_bac); +// } +// } +// }, +// Err(e) => { +// match e.kind() { +// ErrorKind::Interrupted => {}, +// _ => remove = true +// } +// }, +// } +// if remove { +// self.remove_stdout(); +// } +// } + +// fn handle_error_event(&mut self, event: &Event) { +// match event.token() { +// NcCore::TK_STDIN => self.remove_stdin(), +// NcCore::TK_STDOUT => self.remove_stdout(), +// NcCore::TK_NET => self.remove_net(), +// _ => err_exit("unknown token") +// } +// } + +// fn handle_hup_event(&mut self, event: &Event) { +// if !self.stdin_gone() && event.token() == NcCore::TK_STDIN && +// !event.readiness().is_readable() { +// self.remove_stdin(); +// } + +// if !self.netin_gone() && event.token() == NcCore::TK_NET && +// !event.readiness().is_readable() { +// self.remove_netin(); +// } + +// if event.token() == NcCore::TK_NET { +// debug_info("STDOUT HUP"); +// // TODO: check Nflag +// self.sock.shutdown(std::net::Shutdown::Write); +// self.remove_netout(); +// } +// } + +// fn debug_print_ready_end(event :&Event, last_ready_end: &mut i32) { +// let new_ready_end = match event.token() { +// NcCore::TK_STDIN => NcCore::STDIN_POLL, +// NcCore::TK_STDOUT => NcCore::STDOUT_POLL, +// NcCore::TK_NET => { +// let mut val = 0; +// if event.readiness().is_readable() { +// val |= NcCore::NETIN_POLL; +// } +// if event.readiness().is_writable() { +// val |= NcCore::NETOUT_POLL; +// } +// val +// } +// _ => -1 +// }; +// if *last_ready_end != new_ready_end { +// debug_info(&format!("new_ready_end {:?}", new_ready_end)); +// } else { +// if *last_ready_end & (NcCore::STDIN_POLL | NcCore::NETIN_POLL) != 0 { +// debug_info(&format!("new_ready_end {:?}", new_ready_end)); +// } +// } +// *last_ready_end = new_ready_end; +// } +// } + +// fn local_listen(opts: &NcOptions) -> Option { +// debug_info("local_listen"); +// // let mut addrs_iter = (&opts.host as &str, opts.portlist[0]).to_socket_addrs();//let mut addrs_iter = "127.9.0.1".to_socket_addrs();//(&(opts.host) as &str, opts.portlist[0]).to_socket_addrs(); +// // if addrs_iter.is_err() { +// // err_exit("get_matches") +// // } + +// let mut addrs_iter = match (&opts.host as &str, opts.portlist[0]).to_socket_addrs() { +// Ok(expr) => expr, +// Err(error) => { +// panic!("to_socket_addrs: {:?}", error) +// } +// }; + +// for addr in addrs_iter{ +// let family = match addr { +// SocketAddr::V4(_) => socket2::Domain::ipv4(), +// SocketAddr::V6(_) => socket2::Domain::ipv6(), +// }; + +// let sock_type = if opts.uflag { +// socket2::Type::dgram() +// } else { +// socket2::Type::stream() +// }; + +// let sock = Socket::new(family, sock_type, None).unwrap(); + +// debug_info("local_listen binding"); +// match sock.bind(&socket2::SockAddr::from(addr)) { +// Ok(_) => { +// if !opts.uflag { +// sock.listen(128).unwrap(); +// debug_info(&format!("local_listen returning sock.as_raw_fd() = {}", sock.as_raw_fd())); +// } +// debug_info("local_listen bind finish"); +// return Some(sock); +// } +// Err(_) => { +// debug_info("local_listen err, continue"); +// continue +// } +// }; + + + +// } +// err_exit("local_listen failed"); +// None + +// // for addr in addrs_iter{ +// // if opts.uflag { +// // // UdpSocket +// // match UdpSocket::bind(addr) { +// // Ok(sock) => return sock.as_raw_fd(), +// // Err(_) => continue +// // } +// // } else { +// // debug_info("creating TcpListener"); +// // match TcpListener::bind(addr) { +// // Ok(listener) => return listener.as_raw_fd(), +// // Err(_) => continue +// // }; +// // } +// // } +// // err_exit("local_listen failed"); +// // -1 +// } + +// fn remote_connect(opts: &NcOptions, port: u16) -> Option{ +// let mut addrs_iter = match (&opts.host as &str, port).to_socket_addrs() { +// Ok(expr) => expr, +// Err(error) => { +// panic!("to_socket_addrs: {:?}", error) +// } +// }; + +// for addr in addrs_iter{ +// let sock_domain = match addr { +// SocketAddr::V4(_) => socket2::Domain::ipv4(), +// SocketAddr::V6(_) => socket2::Domain::ipv6(), +// }; + +// let sock_type = if opts.uflag { +// socket2::Type::dgram() +// } else { +// socket2::Type::stream() +// }; + +// let sock = Socket::new(sock_domain, sock_type, None).unwrap(); + +// if opts.s_addr.is_some() || opts.pflag { +// // TODO: implement + +// } + + +// // TODO: maybe sometimes no timeout +// match sock.connect_timeout(&socket2::SockAddr::from(addr), Duration::new(1, 0)) { +// Ok(_) => return Some(sock), +// Err(_) => { +// if opts.vflag { +// let connection_type = if opts.uflag { +// "udp" +// } else { +// "tcp" +// }; +// warn(&format!("connect to {} port {} ({}) failed", opts.host, port, connection_type)); +// } +// } +// } + + + + +// // if opts.uflag { +// // // UdpSocket +// // match UdpSocket::bind(addr) { +// // Ok(sock) => return sock.as_raw_fd(), +// // Err(_) => continue +// // } +// // } else { +// // match TcpListener::bind(addr) { +// // Ok(listener) => return listener.as_raw_fd(), +// // Err(_) => continue +// // }; +// // } +// } +// return None; +// // err_exit("local_listen failed"); +// // -1 +// } + +// /* +// * unix_bind() +// * Returns a unix socket bound to the given path +// */ +// fn unix_bind(path: &str, opts: &NcOptions) -> Socket{ +// let sock_type = if opts.uflag { +// socket2::Type::dgram() +// } else { +// socket2::Type::stream() +// }; + +// let sock = Socket::new(Domain::unix(), sock_type, None).expect("failed to create unix socket"); +// sock.bind(&socket2::SockAddr::unix(path).expect("invalid unix socket path")).expect("bind error"); + +// sock +// } + +// /* +// * unix_listen() +// * Create a unix domain socket, and listen on it. +// */ +// fn unix_listen(path: &str) -> Socket{ +// let sock = Socket::new(Domain::unix(), socket2::Type::stream(), None).expect("failed to create unix socket"); +// sock.bind(&socket2::SockAddr::unix(path).expect("invalid unix socket path")).expect("bind error"); +// sock.listen(5).expect("listen error"); +// sock +// } + +// fn server(opts: &NcOptions) -> i32{ +// let mut sock :Socket = Socket::new(socket2::Domain::ipv4(), socket2::Type::stream(), None).unwrap();; +// if opts.family == AF_UNIX { +// sock = if opts.uflag { +// unix_bind(&opts.host, &opts) +// } else { +// unix_listen(&opts.host) +// } +// } + +// loop { +// if opts.family != AF_UNIX { +// sock = local_listen(opts).expect("server: listen error"); +// } +// // /* +// // * For UDP and -k, don't connect the socket, let it +// // * receive datagrams from multiple socket pairs. +// // */ +// // if opts.uflag && opts.kflag { +// // readwrite(sock.as_raw_fd(), opts); +// // } +// // /* +// // * For UDP and not -k, we will use recvfrom() initially +// // * to wait for a caller, then use the regular functions +// // * to talk to the caller. +// // */ +// // else if opts.uflag && !opts.kflag { + +// if opts.uflag { +// let mut netinbuf: [u8; BUFSIZE] = [0; BUFSIZE]; +// let (_, sockaddr) = sock.peek_from(&mut netinbuf).unwrap(); +// sock.connect(&sockaddr).expect("connect error"); + +// if opts.vflag { +// eprintln!("Connection from {:?} received!", sockaddr); +// } + +// // readwrite(&mut sock, opts); +// NcCore::run(&mut sock, opts); +// } else { +// debug_info(&format!("sock = {:?}", sock)); +// let (mut sock_conn, sockaddr) = sock.accept().unwrap(); +// if opts.vflag { +// eprintln!("Connection from {:?} received!", sockaddr); +// } + +// // readwrite(&mut sock_conn, opts); +// NcCore::run(&mut sock_conn, opts); + +// // sock_conn.shutdown(std::net::Shutdown::Both); +// } + +// // if opts.family != AF_UNIX { + +// // } + +// if !opts.kflag { +// break; +// } +// } + + +// return 0; +// } + +// /* +// * unix_connect() +// * Returns a socket connected to a local unix socket. Returns -1 on failure. +// */ +// fn unix_connect(path: &str, opts: &NcOptions) -> Socket { +// let sock = if opts.uflag { +// unix_bind(&opts.unix_dg_tmp_socket, opts) +// } else { +// Socket::new(Domain::unix(), socket2::Type::stream(), None).expect("failed to create unix socket") +// }; + +// sock.connect(&socket2::SockAddr::unix(path).expect("invalid unix socket path")).expect("bind error"); + +// sock +// } + +// fn unix_client(opts: &NcOptions) -> i32 { +// debug_info("unix_client"); +// let mut ret = 0; + +// let mut sock = unix_connect(&opts.host, opts); + +// if !opts.zflag { +// // readwrite(&mut sock, &opts); +// NcCore::run(&mut sock, opts); +// } else { +// ret = 1; +// } + +// if opts.uflag { +// std::fs::remove_file(&opts.unix_dg_tmp_socket).expect("failed to remove the unix tmp socket file"); +// } + +// ret +// } + +// fn nonunix_client(opts: &NcOptions) -> i32 { +// let mut ret: i32 = 0; +// for port in &opts.portlist { +// let mut sock = match remote_connect(opts, port.clone()) { +// Some(expr) => expr, +// None => continue, +// }; + +// // sock.set_nonblocking(true); + +// ret = 0; + +// // if opts.vflag || opts.zflag { +// // // TODO: implement +// // } + +// // TODO: Fflag && !zflag +// // readwrite(&mut sock, opts); +// NcCore::run(&mut sock, opts); + +// } +// ret +// } + + pub fn execute(setup: &mut S, args: T) -> Result<()> where @@ -16,6 +961,61 @@ where { println!("this is netcat"); + let mut ret: i32 = 0; + let mut help_msg: Vec = Vec::new(); + let app = util_app!(NAME) + .arg(Arg::with_name("l") + .short("l")) + .arg(Arg::with_name("i") + .short("i") + .value_name("interval") + .takes_value(true)) + .arg(Arg::with_name("s") + .short("s") + .value_name("source_ip_address") + .takes_value(true)) + .arg(Arg::with_name("d") + .short("d")) + .arg(Arg::with_name("U") + .short("U")) + .arg(Arg::with_name("u") + .short("u")) + .arg(Arg::with_name("v") + .short("v")) + .arg(Arg::with_name("k") + .short("k")) + .arg(Arg::with_name("z") + .short("z")) + .arg(Arg::with_name("positionals") + .multiple(true)); + + app.write_help(&mut help_msg); + let help_msg = String::from_utf8(help_msg).unwrap(); + let matches = app.get_matches_from_safe(args)?; + + + // println!("matches = {:?}", matches); + let opts = NcOptions::parse(matches, &help_msg)?; + + // println!("opts = {:?}", opts); + // if opts.is_none() { + // // app.write_help(&mut io::stdout()); + // print!("{}", help_msg); + + // } + + // let opts = opts.unwrap(); + + // if opts.lflag { + // ret = server(&opts); + // } else { + // if opts.family == AF_UNIX { + // ret = unix_client(&opts); + // } else { + // ret = nonunix_client(&opts); + // } + // } + Ok(()) } From 398eace5d3ba5a94b73c8a98a0989d6b3a369a73 Mon Sep 17 00:00:00 2001 From: RundongZhou Date: Sat, 14 Jul 2018 16:32:52 -0700 Subject: [PATCH 03/19] Implement helper for generating error. --- src/networking/nc/mod.rs | 62 +++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/src/networking/nc/mod.rs b/src/networking/nc/mod.rs index cc4618e..dc517b9 100644 --- a/src/networking/nc/mod.rs +++ b/src/networking/nc/mod.rs @@ -4,11 +4,14 @@ extern crate libc; extern crate socket2; // extern crate tempfile; +use std; +use failure; use tempfile::NamedTempFile; use clap::{Arg, App, ArgMatches}; use mio::{Events, Event, Poll, Ready, PollOpt, Token}; use libc::{AF_UNSPEC, AF_INET, AF_INET6, AF_UNIX}; use std::io; +use std::ffi::OsString; use std::net::{SocketAddr}; use mio::unix::EventedFd; use std::io::{Read,Write, ErrorKind}; @@ -19,7 +22,8 @@ use std::os::unix::io::AsRawFd; use std::os::unix::io::RawFd; use std::time::Duration; use std::net::{ToSocketAddrs}; -use super::{UtilSetup, Result, ArgsIter, UtilWrite}; +use super::{UtilSetup, ArgsIter, UtilWrite}; +use super::{MesaError}; // use clap::Arg; use std::borrow::Cow; @@ -53,6 +57,12 @@ struct NcOptions { unix_dg_tmp_socket: String, } +fn mesaerr_result(err_msg: &str) -> Result { + Err(MesaError::from( + failure::err_msg(format!("{}", err_msg)).compat() + )) +} + fn build_ports(ports: &str) -> Vec{ return vec!(ports.parse::().expect(&format!("invalid port[s] {}", ports))); } @@ -64,10 +74,10 @@ fn usage(ret: bool, msg: &str) { } } -fn err_exit(msg: &str) { - eprint!("{}", msg); - std::process::exit(1); -} +// fn err_exit(msg: &str) { +// eprint!("{}", msg); +// std::process::exit(1); +// } fn warn(msg: &str) { eprint!("{}", msg); @@ -78,7 +88,7 @@ fn debug_info(msg: &str) { } impl NcOptions { - pub fn parse(matches: ArgMatches, msg: &str) -> Option { + pub fn parse(matches: ArgMatches, msg: &str) -> Result { let mut portlist = vec!(); let lflag = matches.is_present("l"); let mut host = String::from("127.0.0.1"); @@ -119,8 +129,9 @@ impl NcOptions { uport = String::new(); } else { if !lflag { - usage(true, msg); - return None; + return mesaerr_result(msg); + // usage(true, msg); + // return None; } uport = String::from(positionals[0]); // host = String::from("localhost"); @@ -129,21 +140,22 @@ impl NcOptions { host = String::from(positionals[0]); uport = String::from(positionals[1]); } else { - usage(true, msg); - return None; + return mesaerr_result(msg); + // usage(true, msg); + // return None; } if lflag && s_addr.is_some() { - err_exit("cannot use -s and -l"); + return mesaerr_result("cannot use -s and -l"); } if lflag && pflag { - err_exit("cannot use -p and -l"); + return mesaerr_result("cannot use -p and -l"); } if lflag && zflag { - err_exit("cannot use -z and -l"); + return mesaerr_result("cannot use -z and -l"); } if !lflag && kflag { - err_exit("must use -l with -k"); + return mesaerr_result("must use -l with -k"); } if !uport.is_empty() { @@ -194,7 +206,7 @@ impl NcOptions { zflag: zflag, }; - return Some(ret); + return Ok(ret); } } @@ -954,7 +966,7 @@ fn remove_item(v: &mut Vec, item: T) { -pub fn execute(setup: &mut S, args: T) -> Result<()> +pub fn execute(setup: &mut S, args: T) -> Result<(), MesaError> where S: UtilSetup, T: ArgsIter, @@ -1000,15 +1012,25 @@ where // println!("opts = {:?}", opts); // if opts.is_none() { // // app.write_help(&mut io::stdout()); - // print!("{}", help_msg); - + // // print!("{}", help_msg); + // // return Err(failure::err_msg(help_msg)); + // // return Err(OsString::from(help_msg)); + // // return Err(MesaError::from(failure::err_msg("a").compat())); + // } - // let opts = opts.unwrap(); + + // let opts = match opts { + // Ok(opts) => opts, + // Err() => { + // return mesaerr_result(&help_msg); + // } + // }; // if opts.lflag { // ret = server(&opts); - // } else { + // } + // else { // if opts.family == AF_UNIX { // ret = unix_client(&opts); // } else { From 2be74039580be697f2847fb2316e86e4b6c5fcf3 Mon Sep 17 00:00:00 2001 From: RundongZhou Date: Sun, 15 Jul 2018 15:11:03 -0700 Subject: [PATCH 04/19] Remove all warning. --- src/networking/nc/mod.rs | 1427 +++++++++++++++++++------------------- 1 file changed, 708 insertions(+), 719 deletions(-) diff --git a/src/networking/nc/mod.rs b/src/networking/nc/mod.rs index dc517b9..170937e 100644 --- a/src/networking/nc/mod.rs +++ b/src/networking/nc/mod.rs @@ -7,11 +7,10 @@ extern crate socket2; use std; use failure; use tempfile::NamedTempFile; -use clap::{Arg, App, ArgMatches}; +use clap::{Arg, ArgMatches}; use mio::{Events, Event, Poll, Ready, PollOpt, Token}; use libc::{AF_UNSPEC, AF_INET, AF_INET6, AF_UNIX}; use std::io; -use std::ffi::OsString; use std::net::{SocketAddr}; use mio::unix::EventedFd; use std::io::{Read,Write, ErrorKind}; @@ -22,15 +21,11 @@ use std::os::unix::io::AsRawFd; use std::os::unix::io::RawFd; use std::time::Duration; use std::net::{ToSocketAddrs}; -use super::{UtilSetup, ArgsIter, UtilWrite}; +use socket2::{Socket, Domain}; +use super::{UtilSetup, ArgsIter}; use super::{MesaError}; -// use clap::Arg; -use std::borrow::Cow; -// use std::io::Write; -use std::os::unix::ffi::OsStrExt; -use socket2::{Socket, Domain}; pub(crate) const NAME: &str = "nc"; pub(crate) const DESCRIPTION: &str = "netcat"; @@ -63,16 +58,17 @@ fn mesaerr_result(err_msg: &str) -> Result { )) } -fn build_ports(ports: &str) -> Vec{ - return vec!(ports.parse::().expect(&format!("invalid port[s] {}", ports))); +fn build_ports(ports: &str) -> Result, MesaError>{ + return Ok(vec!(ports.parse::()?)); + //.expect(&format!("invalid port[s] {}", ports))); } -fn usage(ret: bool, msg: &str) { - eprint!("{}", msg); - if ret { - std::process::exit(1); - } -} +// fn usage(ret: bool, msg: &str) { +// eprint!("{}", msg); +// if ret { +// std::process::exit(1); +// } +// } // fn err_exit(msg: &str) { // eprint!("{}", msg); @@ -159,7 +155,7 @@ impl NcOptions { } if !uport.is_empty() { - portlist = build_ports(&uport); + portlist = build_ports(&uport)?; } if matches.is_present("i") { @@ -179,7 +175,7 @@ impl NcOptions { unix_dg_tmp_socket = if s_addr.is_some() { s_addr.clone().unwrap() } else { - let nf = NamedTempFile::new().expect("failed to create temporary file"); + let nf = NamedTempFile::new()?; //.expect("failed to create temporary file"); let path = String::from(nf.path().to_str().unwrap()); // nf.persist(&path).expect("failed to create temporary file"); path @@ -219,761 +215,755 @@ fn remove_item(v: &mut Vec, item: T) { }; } -// struct NcCore<'a> { -// sock: &'a mut Socket, -// opts: &'a NcOptions, -// poll: Poll, -// net_interest: Ready, -// event_stdin: EventedFd<'a>, -// event_net: EventedFd<'a>, -// event_stdout: EventedFd<'a>, -// stdinbuf: [u8; BUFSIZE], -// netinbuf: [u8; BUFSIZE], -// stdinbuf_bac: [u8; BUFSIZE], -// netinbuf_bac: [u8; BUFSIZE], -// netinbufpos: usize, -// stdinbufpos: usize, -// open_ends: Vec, -// } +struct NcCore<'a> { + sock: &'a mut Socket, + opts: &'a NcOptions, + poll: Poll, + net_interest: Ready, + event_stdin: EventedFd<'a>, + event_net: EventedFd<'a>, + event_stdout: EventedFd<'a>, + stdinbuf: [u8; BUFSIZE], + netinbuf: [u8; BUFSIZE], + stdinbuf_bac: [u8; BUFSIZE], + netinbuf_bac: [u8; BUFSIZE], + netinbufpos: usize, + stdinbufpos: usize, + open_ends: Vec, +} -// impl <'a> NcCore<'a> { -// const STDIN_POLL: i32 = 1; -// const STDOUT_POLL: i32 = 2; -// const NETIN_POLL: i32 = 4; -// const NETOUT_POLL: i32 = 8; - -// const TK_STDIN: Token = Token(0); -// const TK_STDOUT: Token = Token(1); -// const TK_NET: Token = Token(2); - -// fn new(sock: &'a mut Socket, opts: &'a NcOptions, net_fd: &'a RawFd) -> NcCore<'a> { -// let mut ret = NcCore { -// sock: sock, -// opts: opts, - -// poll: Poll::new().unwrap(), -// net_interest: Ready::readable(), -// event_stdin: EventedFd(&0), -// event_net: EventedFd(net_fd), -// event_stdout: EventedFd(&1), -// stdinbuf: [0; BUFSIZE], -// netinbuf: [0; BUFSIZE], -// stdinbuf_bac: [0; BUFSIZE], -// netinbuf_bac: [0; BUFSIZE], -// netinbufpos: 0, -// stdinbufpos: 0, -// open_ends: vec![NcCore::STDIN_POLL, NcCore::STDOUT_POLL, NcCore::NETIN_POLL, NcCore::NETOUT_POLL], -// }; - -// ret.poll.register(&ret.event_stdin, NcCore::TK_STDIN, Ready::readable(), -// PollOpt::empty()).unwrap(); -// ret.poll.register(&ret.event_net, NcCore::TK_NET, ret.net_interest, -// PollOpt::empty()).unwrap(); -// ret.poll.register(&ret.event_stdout, NcCore::TK_STDOUT, Ready::empty(), -// PollOpt::empty()).unwrap(); - -// if ret.opts.dflag { -// ret.remove_stdin(); -// } - -// ret -// } +impl <'a> NcCore<'a> { + const STDIN_POLL: i32 = 1; + const STDOUT_POLL: i32 = 2; + const NETIN_POLL: i32 = 4; + const NETOUT_POLL: i32 = 8; + + const TK_STDIN: Token = Token(0); + const TK_STDOUT: Token = Token(1); + const TK_NET: Token = Token(2); + + fn new(sock: &'a mut Socket, opts: &'a NcOptions, net_fd: &'a RawFd) -> Result, MesaError> { + let mut ret = NcCore { + sock: sock, + opts: opts, + + poll: Poll::new()?, + net_interest: Ready::readable(), + event_stdin: EventedFd(&0), + event_net: EventedFd(net_fd), + event_stdout: EventedFd(&1), + stdinbuf: [0; BUFSIZE], + netinbuf: [0; BUFSIZE], + stdinbuf_bac: [0; BUFSIZE], + netinbuf_bac: [0; BUFSIZE], + netinbufpos: 0, + stdinbufpos: 0, + open_ends: vec![NcCore::STDIN_POLL, NcCore::STDOUT_POLL, NcCore::NETIN_POLL, NcCore::NETOUT_POLL], + }; -// /* -// * readwrite() -// * Loop that polls on the network file descriptor and stdin. -// */ -// fn readwrite(&mut self) { -// let mut events = Events::with_capacity(1024); - -// let mut last_ready_end = -1; - -// loop { -// /* both inputs are gone, buffers are empty, we are done */ -// if self.stdin_gone() && self.netin_gone() && -// self.stdinbuf_empty() && self.netinbuf_empty() { -// self.sock.shutdown(std::net::Shutdown::Both); -// return; -// } - -// /* both outputs are gone, we can't continue */ -// if self.stdout_gone() && self.netout_gone() { -// self.sock.shutdown(std::net::Shutdown::Both); -// return; -// } - -// /* listen and net in gone, queues empty, done */ -// if self.opts.lflag && self.netin_gone() && -// self.stdinbuf_empty() && self.netinbuf_empty() { -// self.sock.shutdown(std::net::Shutdown::Both); -// return; -// } - -// /* help says -i is for "wait between lines sent". We read and -// * write arbitrary amounts of data, and we don't want to start -// * scanning for newlines, so this is as good as it gets */ -// if self.opts.iflag { -// sleep(self.opts.interval.unwrap()); -// } - -// self.poll.poll(&mut events, None).expect("polling error"); - -// /* timeout happened */ -// if events.is_empty() { -// return; -// } - -// for event in &events { -// // if any error happend, stop watching for the corresponding fd -// if event.readiness().is_error() { -// self.handle_error_event(&event); -// } - -// NcCore::debug_print_ready_end(&event, &mut last_ready_end); - -// if event.readiness().contains(UnixReady::hup()) { -// self.handle_hup_event(&event); -// } - -// // if no net out, finish watching stdin -// if self.netout_gone() { -// self.remove_stdin(); -// } - -// // if no stdout, stop watching net in -// if self.stdout_gone() { -// if !self.netin_gone() { -// self.sock.shutdown(std::net::Shutdown::Read); -// } -// self.remove_stdin(); -// } - -// // if stdin readable and buf not full, try to read stdin -// // error or eof, remove and deregister stdin -// // if buf not emtpy, reregister writable for netout -// // if buf full, reregister with empty -// if event.token() == NcCore::TK_STDIN && event.readiness().is_readable() && !self.stdinbuf_full() { -// self.read_stdin(); - -// if !self.stdinbuf_empty() { -// self.enable_netout(); -// } else if self.stdinbuf_full() { -// self.disable_stdin(); -// } -// } - -// // if net writable and buf not empty, try to write to net -// // error, stop watching for netout -// // memmove if needed -// // if stdinbuf empty, reregister to remove netout writable -// // if buf not full, reregister to add stdin readable -// if event.token() == NcCore::TK_NET && event.readiness().is_writable() && !self.stdinbuf_empty() { -// self.write_netout(); -// if self.stdinbuf_empty() { -// self.disable_netout(); -// } else if !self.stdinbuf_full() { -// self.enable_stdin(); -// } -// } - -// // if net readable and buf not full, try to read net -// // error or eof, remove and deregister netin -// // if buf not emtpy, reregister writable for stdout -// // if buf full, reregister to remove netin -// if event.token() == NcCore::TK_NET && event.readiness().is_readable() && !self.netinbuf_full() { -// self.read_netin(); -// if !self.netinbuf_empty() { -// self.enable_stdout(); -// } else if self.netinbuf_full() { -// self.disable_netin(); -// } -// } - -// // if stdout writable and buf not empty, try to write to stdout -// // error, stop watching for stdout -// // memmove if needed -// // if netinbuf empty, reregister to remove stdout writable -// // if buf not full, reregister to add netin readable -// if event.token() == NcCore::TK_STDOUT && event.readiness().is_writable() && !self.netinbuf_empty() { -// self.write_stdout(); -// if self.netinbuf_empty() { -// self.disable_stdout(); -// } else if !self.netinbuf_full() { -// self.enable_netin(); -// } -// } - -// // if stdin gone and stdinbuf empty, remove netout -// if self.stdin_gone() && self.stdinbuf_empty() { -// if !self.netout_gone() { -// // TODO: check && opts.Nflag { -// self.sock.shutdown(std::net::Shutdown::Write); -// } -// self.remove_netout(); -// } - -// // if netin gone and netinbuf empty, remove stdout -// if self.netin_gone() && self.netinbuf_empty() { -// self.remove_stdout(); -// } -// } - -// } -// } + ret.poll.register(&ret.event_stdin, NcCore::TK_STDIN, Ready::readable(), + PollOpt::empty())?; + ret.poll.register(&ret.event_net, NcCore::TK_NET, ret.net_interest, + PollOpt::empty())?; + ret.poll.register(&ret.event_stdout, NcCore::TK_STDOUT, Ready::empty(), + PollOpt::empty())?; -// fn run(sock: &mut Socket, opts: &NcOptions) { -// let net_fd = sock.as_raw_fd(); -// let mut nc = NcCore::new(sock, &opts, &net_fd); -// nc.readwrite(); -// } + if ret.opts.dflag { + ret.remove_stdin()?; + } -// fn stdin_gone(&self) -> bool { -// !self.open_ends.contains(&NcCore::STDIN_POLL) -// } + Ok(ret) + } -// fn stdout_gone(&self) -> bool { -// !self.open_ends.contains(&NcCore::STDOUT_POLL) -// } + /* + * readwrite() + * Loop that polls on the network file descriptor and stdin. + */ + fn readwrite(&mut self) -> Result<(), MesaError> { + let mut events = Events::with_capacity(1024); + + let mut last_ready_end = -1; + + loop { + /* both inputs are gone, buffers are empty, we are done */ + if self.stdin_gone() && self.netin_gone() && + self.stdinbuf_empty() && self.netinbuf_empty() { + self.sock.shutdown(std::net::Shutdown::Both)?; + return Ok(()); + } -// fn netin_gone(&self) -> bool { -// !self.open_ends.contains(&NcCore::NETIN_POLL) -// } + /* both outputs are gone, we can't continue */ + if self.stdout_gone() && self.netout_gone() { + self.sock.shutdown(std::net::Shutdown::Both)?; + return Ok(()); + } -// fn netout_gone(&self) -> bool { -// !self.open_ends.contains(&NcCore::NETOUT_POLL) -// } + /* listen and net in gone, queues empty, done */ + if self.opts.lflag && self.netin_gone() && + self.stdinbuf_empty() && self.netinbuf_empty() { + self.sock.shutdown(std::net::Shutdown::Both)?; + return Ok(()); + } -// fn stdinbuf_empty(&self) -> bool { -// self.stdinbufpos == 0 -// } + /* help says -i is for "wait between lines sent". We read and + * write arbitrary amounts of data, and we don't want to start + * scanning for newlines, so this is as good as it gets */ + if self.opts.iflag { + sleep(self.opts.interval.unwrap()); + } -// fn stdinbuf_full(&self) -> bool { -// self.stdinbufpos >= BUFSIZE -// } + self.poll.poll(&mut events, None)?; + // .expect("polling error"); -// fn netinbuf_empty(&self) -> bool { -// self.netinbufpos == 0 -// } + /* timeout happened */ + if events.is_empty() { + return Ok(()); + } -// fn netinbuf_full(&self) -> bool { -// self.netinbufpos >= BUFSIZE -// } + for event in &events { + // if any error happend, stop watching for the corresponding fd + if event.readiness().is_error() { + self.handle_error_event(&event)?; + } -// fn remove_stdin(&mut self) { -// remove_item(&mut self.open_ends, NcCore::STDIN_POLL); -// self.poll.deregister(&self.event_stdin); -// } + NcCore::debug_print_ready_end(&event, &mut last_ready_end); -// fn remove_stdout(&mut self) { -// debug_info("remove_stdout"); -// remove_item(&mut self.open_ends, NcCore::STDOUT_POLL); -// self.poll.deregister(&self.event_stdout); -// } + if event.readiness().contains(UnixReady::hup()) { + self.handle_hup_event(&event)?; + } -// fn remove_netin(&mut self) { -// remove_item(&mut self.open_ends, NcCore::NETIN_POLL); -// self.net_interest.remove(Ready::readable()); -// self.reregister_net(); -// } + // if no net out, finish watching stdin + if self.netout_gone() { + self.remove_stdin()?; + } -// fn remove_netout(&mut self) { -// remove_item(&mut self.open_ends, NcCore::NETOUT_POLL); -// self.net_interest.remove(Ready::writable()); -// self.reregister_net(); -// } + // if no stdout, stop watching net in + if self.stdout_gone() { + if !self.netin_gone() { + self.sock.shutdown(std::net::Shutdown::Read)?; + } + self.remove_stdin()?; + } -// fn reregister_net(&mut self) { -// self.poll.reregister(&self.event_net, NcCore::TK_NET, self.net_interest, -// PollOpt::empty()).unwrap(); -// } + // if stdin readable and buf not full, try to read stdin + // error or eof, remove and deregister stdin + // if buf not emtpy, reregister writable for netout + // if buf full, reregister with empty + if event.token() == NcCore::TK_STDIN && event.readiness().is_readable() && !self.stdinbuf_full() { + self.read_stdin()?; + + if !self.stdinbuf_empty() { + self.enable_netout()?; + } else if self.stdinbuf_full() { + self.disable_stdin()?; + } + } + + // if net writable and buf not empty, try to write to net + // error, stop watching for netout + // memmove if needed + // if stdinbuf empty, reregister to remove netout writable + // if buf not full, reregister to add stdin readable + if event.token() == NcCore::TK_NET && event.readiness().is_writable() && !self.stdinbuf_empty() { + self.write_netout()?; + if self.stdinbuf_empty() { + self.disable_netout()?; + } else if !self.stdinbuf_full() { + self.enable_stdin()?; + } + } -// fn enable_netin(&mut self) { -// self.net_interest |= Ready::readable(); -// self.reregister_net(); -// } + // if net readable and buf not full, try to read net + // error or eof, remove and deregister netin + // if buf not emtpy, reregister writable for stdout + // if buf full, reregister to remove netin + if event.token() == NcCore::TK_NET && event.readiness().is_readable() && !self.netinbuf_full() { + self.read_netin()?; + if !self.netinbuf_empty() { + self.enable_stdout()?; + } else if self.netinbuf_full() { + self.disable_netin()?; + } + } -// fn disable_netin(&mut self) { -// self.net_interest.remove(Ready::readable()); -// self.reregister_net(); -// } + // if stdout writable and buf not empty, try to write to stdout + // error, stop watching for stdout + // memmove if needed + // if netinbuf empty, reregister to remove stdout writable + // if buf not full, reregister to add netin readable + if event.token() == NcCore::TK_STDOUT && event.readiness().is_writable() && !self.netinbuf_empty() { + self.write_stdout()?; + if self.netinbuf_empty() { + self.disable_stdout()?; + } else if !self.netinbuf_full() { + self.enable_netin()?; + } + } -// fn enable_netout(&mut self) { -// self.net_interest |= Ready::writable(); -// self.reregister_net(); -// } + // if stdin gone and stdinbuf empty, remove netout + if self.stdin_gone() && self.stdinbuf_empty() { + if !self.netout_gone() { + // TODO: check && opts.Nflag { + self.sock.shutdown(std::net::Shutdown::Write)?; + } + self.remove_netout()?; + } -// fn disable_netout(&mut self) { -// self.net_interest.remove(Ready::writable()); -// self.reregister_net(); -// } + // if netin gone and netinbuf empty, remove stdout + if self.netin_gone() && self.netinbuf_empty() { + self.remove_stdout()?; + } + } -// fn enable_stdin(&mut self) { -// self.poll.reregister(&self.event_stdin, NcCore::TK_STDIN, Ready::readable(), -// PollOpt::empty()).unwrap(); -// } + } + } -// fn disable_stdin(&mut self) { -// self.poll.reregister(&self.event_stdin, NcCore::TK_STDIN, Ready::empty(), -// PollOpt::empty()).unwrap(); -// } + fn run(sock: &mut Socket, opts: &NcOptions) -> Result<(), MesaError> { + let net_fd = sock.as_raw_fd(); + let mut nc = NcCore::new(sock, &opts, &net_fd)?; + nc.readwrite() + } -// fn enable_stdout(&mut self) { -// self.poll.reregister(&self.event_stdout, NcCore::TK_STDOUT, Ready::writable(), -// PollOpt::empty()).unwrap(); -// } + fn stdin_gone(&self) -> bool { + !self.open_ends.contains(&NcCore::STDIN_POLL) + } -// fn disable_stdout(&mut self) { -// self.poll.reregister(&self.event_stdout, NcCore::TK_STDOUT, Ready::empty(), -// PollOpt::empty()).unwrap(); -// } + fn stdout_gone(&self) -> bool { + !self.open_ends.contains(&NcCore::STDOUT_POLL) + } -// fn remove_net(&mut self) { -// remove_item(&mut self.open_ends, NcCore::NETIN_POLL); -// remove_item(&mut self.open_ends, NcCore::NETOUT_POLL); -// self.poll.deregister(&self.event_net); -// } + fn netin_gone(&self) -> bool { + !self.open_ends.contains(&NcCore::NETIN_POLL) + } -// fn read_stdin(&mut self) { -// let mut remove = false; -// match io::stdin().read(&mut self.stdinbuf[self.stdinbufpos..]) { -// Ok(len) => { -// remove = len == 0; -// self.stdinbufpos += len; -// }, -// Err(e) => { -// match e.kind() { -// ErrorKind::Interrupted => {}, -// _ => remove = true -// } -// }, -// } -// if remove { -// self.remove_netin(); -// } -// } + fn netout_gone(&self) -> bool { + !self.open_ends.contains(&NcCore::NETOUT_POLL) + } -// fn write_netout(&mut self) { -// let mut remove = false; -// match self.sock.write(&mut self.stdinbuf[0..self.stdinbufpos]) { -// Ok(len) => { -// debug_info(&format!("write ok len={}", len)); -// // remove = len == 0; -// if len > 0 { -// self.stdinbufpos -= len; -// if self.stdinbufpos > 0 { -// self.stdinbuf_bac.copy_from_slice(&self.stdinbuf[len..len+self.stdinbufpos]); -// self.stdinbuf.copy_from_slice(&self.stdinbuf_bac); -// } -// } -// }, -// Err(e) => { -// debug_info(&format!("write error {:?}", e)); -// match e.kind() { -// ErrorKind::Interrupted => {}, -// _ => remove = true -// } -// }, -// } -// if remove { -// self.remove_netout(); -// } -// } + fn stdinbuf_empty(&self) -> bool { + self.stdinbufpos == 0 + } -// fn read_netin(&mut self) { -// let mut remove = false; -// match self.sock.read(&mut self.netinbuf[self.netinbufpos..]) { -// Ok(len) => { -// remove = len == 0; -// self.netinbufpos += len; -// }, -// Err(e) => { -// match e.kind() { -// ErrorKind::Interrupted => {}, -// _ => remove = true -// } -// }, -// } -// if remove { -// self.remove_netin(); -// } -// } + fn stdinbuf_full(&self) -> bool { + self.stdinbufpos >= BUFSIZE + } -// fn write_stdout(&mut self) { -// let mut remove = false; -// match io::stdout().write(&mut self.netinbuf[0..self.netinbufpos]) { -// Ok(len) => { -// // remove = len == 0; -// if len > 0 { -// self.netinbufpos -= len; -// if self.netinbufpos > 0 { -// self.netinbuf_bac.copy_from_slice(&self.netinbuf[len..len+self.netinbufpos]); -// self.netinbuf.copy_from_slice(&self.netinbuf_bac); -// } -// } -// }, -// Err(e) => { -// match e.kind() { -// ErrorKind::Interrupted => {}, -// _ => remove = true -// } -// }, -// } -// if remove { -// self.remove_stdout(); -// } -// } + fn netinbuf_empty(&self) -> bool { + self.netinbufpos == 0 + } -// fn handle_error_event(&mut self, event: &Event) { -// match event.token() { -// NcCore::TK_STDIN => self.remove_stdin(), -// NcCore::TK_STDOUT => self.remove_stdout(), -// NcCore::TK_NET => self.remove_net(), -// _ => err_exit("unknown token") -// } -// } + fn netinbuf_full(&self) -> bool { + self.netinbufpos >= BUFSIZE + } -// fn handle_hup_event(&mut self, event: &Event) { -// if !self.stdin_gone() && event.token() == NcCore::TK_STDIN && -// !event.readiness().is_readable() { -// self.remove_stdin(); -// } - -// if !self.netin_gone() && event.token() == NcCore::TK_NET && -// !event.readiness().is_readable() { -// self.remove_netin(); -// } - -// if event.token() == NcCore::TK_NET { -// debug_info("STDOUT HUP"); -// // TODO: check Nflag -// self.sock.shutdown(std::net::Shutdown::Write); -// self.remove_netout(); -// } -// } + fn remove_stdin(&mut self) -> std::io::Result<()> { + remove_item(&mut self.open_ends, NcCore::STDIN_POLL); + self.poll.deregister(&self.event_stdin) + } -// fn debug_print_ready_end(event :&Event, last_ready_end: &mut i32) { -// let new_ready_end = match event.token() { -// NcCore::TK_STDIN => NcCore::STDIN_POLL, -// NcCore::TK_STDOUT => NcCore::STDOUT_POLL, -// NcCore::TK_NET => { -// let mut val = 0; -// if event.readiness().is_readable() { -// val |= NcCore::NETIN_POLL; -// } -// if event.readiness().is_writable() { -// val |= NcCore::NETOUT_POLL; -// } -// val -// } -// _ => -1 -// }; -// if *last_ready_end != new_ready_end { -// debug_info(&format!("new_ready_end {:?}", new_ready_end)); -// } else { -// if *last_ready_end & (NcCore::STDIN_POLL | NcCore::NETIN_POLL) != 0 { -// debug_info(&format!("new_ready_end {:?}", new_ready_end)); -// } -// } -// *last_ready_end = new_ready_end; -// } -// } + fn remove_stdout(&mut self) -> std::io::Result<()> { + debug_info("remove_stdout"); + remove_item(&mut self.open_ends, NcCore::STDOUT_POLL); + self.poll.deregister(&self.event_stdout) + } -// fn local_listen(opts: &NcOptions) -> Option { -// debug_info("local_listen"); -// // let mut addrs_iter = (&opts.host as &str, opts.portlist[0]).to_socket_addrs();//let mut addrs_iter = "127.9.0.1".to_socket_addrs();//(&(opts.host) as &str, opts.portlist[0]).to_socket_addrs(); -// // if addrs_iter.is_err() { -// // err_exit("get_matches") -// // } - -// let mut addrs_iter = match (&opts.host as &str, opts.portlist[0]).to_socket_addrs() { -// Ok(expr) => expr, -// Err(error) => { -// panic!("to_socket_addrs: {:?}", error) -// } -// }; - -// for addr in addrs_iter{ -// let family = match addr { -// SocketAddr::V4(_) => socket2::Domain::ipv4(), -// SocketAddr::V6(_) => socket2::Domain::ipv6(), -// }; - -// let sock_type = if opts.uflag { -// socket2::Type::dgram() -// } else { -// socket2::Type::stream() -// }; - -// let sock = Socket::new(family, sock_type, None).unwrap(); - -// debug_info("local_listen binding"); -// match sock.bind(&socket2::SockAddr::from(addr)) { -// Ok(_) => { -// if !opts.uflag { -// sock.listen(128).unwrap(); -// debug_info(&format!("local_listen returning sock.as_raw_fd() = {}", sock.as_raw_fd())); -// } -// debug_info("local_listen bind finish"); -// return Some(sock); -// } -// Err(_) => { -// debug_info("local_listen err, continue"); -// continue -// } -// }; + fn remove_netin(&mut self) -> std::io::Result<()> { + remove_item(&mut self.open_ends, NcCore::NETIN_POLL); + self.net_interest.remove(Ready::readable()); + self.reregister_net() + } + fn remove_netout(&mut self) -> std::io::Result<()> { + remove_item(&mut self.open_ends, NcCore::NETOUT_POLL); + self.net_interest.remove(Ready::writable()); + return self.reregister_net(); + } + fn reregister_net(&mut self) -> std::io::Result<()> { + self.poll.reregister(&self.event_net, NcCore::TK_NET, self.net_interest, + PollOpt::empty()) + } -// } -// err_exit("local_listen failed"); -// None - -// // for addr in addrs_iter{ -// // if opts.uflag { -// // // UdpSocket -// // match UdpSocket::bind(addr) { -// // Ok(sock) => return sock.as_raw_fd(), -// // Err(_) => continue -// // } -// // } else { -// // debug_info("creating TcpListener"); -// // match TcpListener::bind(addr) { -// // Ok(listener) => return listener.as_raw_fd(), -// // Err(_) => continue -// // }; -// // } -// // } -// // err_exit("local_listen failed"); -// // -1 -// } + fn enable_netin(&mut self) -> std::io::Result<()>{ + self.net_interest |= Ready::readable(); + self.reregister_net() + } -// fn remote_connect(opts: &NcOptions, port: u16) -> Option{ -// let mut addrs_iter = match (&opts.host as &str, port).to_socket_addrs() { -// Ok(expr) => expr, -// Err(error) => { -// panic!("to_socket_addrs: {:?}", error) -// } -// }; - -// for addr in addrs_iter{ -// let sock_domain = match addr { -// SocketAddr::V4(_) => socket2::Domain::ipv4(), -// SocketAddr::V6(_) => socket2::Domain::ipv6(), -// }; - -// let sock_type = if opts.uflag { -// socket2::Type::dgram() -// } else { -// socket2::Type::stream() -// }; - -// let sock = Socket::new(sock_domain, sock_type, None).unwrap(); - -// if opts.s_addr.is_some() || opts.pflag { -// // TODO: implement - -// } - - -// // TODO: maybe sometimes no timeout -// match sock.connect_timeout(&socket2::SockAddr::from(addr), Duration::new(1, 0)) { -// Ok(_) => return Some(sock), -// Err(_) => { -// if opts.vflag { -// let connection_type = if opts.uflag { -// "udp" -// } else { -// "tcp" -// }; -// warn(&format!("connect to {} port {} ({}) failed", opts.host, port, connection_type)); -// } -// } -// } - - - - -// // if opts.uflag { -// // // UdpSocket -// // match UdpSocket::bind(addr) { -// // Ok(sock) => return sock.as_raw_fd(), -// // Err(_) => continue -// // } -// // } else { -// // match TcpListener::bind(addr) { -// // Ok(listener) => return listener.as_raw_fd(), -// // Err(_) => continue -// // }; -// // } -// } -// return None; -// // err_exit("local_listen failed"); -// // -1 -// } + fn disable_netin(&mut self) -> std::io::Result<()>{ + self.net_interest.remove(Ready::readable()); + self.reregister_net() + } -// /* -// * unix_bind() -// * Returns a unix socket bound to the given path -// */ -// fn unix_bind(path: &str, opts: &NcOptions) -> Socket{ -// let sock_type = if opts.uflag { -// socket2::Type::dgram() -// } else { -// socket2::Type::stream() -// }; - -// let sock = Socket::new(Domain::unix(), sock_type, None).expect("failed to create unix socket"); -// sock.bind(&socket2::SockAddr::unix(path).expect("invalid unix socket path")).expect("bind error"); - -// sock -// } + fn enable_netout(&mut self) -> std::io::Result<()> { + self.net_interest |= Ready::writable(); + self.reregister_net() + } -// /* -// * unix_listen() -// * Create a unix domain socket, and listen on it. -// */ -// fn unix_listen(path: &str) -> Socket{ -// let sock = Socket::new(Domain::unix(), socket2::Type::stream(), None).expect("failed to create unix socket"); -// sock.bind(&socket2::SockAddr::unix(path).expect("invalid unix socket path")).expect("bind error"); -// sock.listen(5).expect("listen error"); -// sock -// } + fn disable_netout(&mut self) -> std::io::Result<()> { + self.net_interest.remove(Ready::writable()); + self.reregister_net() + } -// fn server(opts: &NcOptions) -> i32{ -// let mut sock :Socket = Socket::new(socket2::Domain::ipv4(), socket2::Type::stream(), None).unwrap();; -// if opts.family == AF_UNIX { -// sock = if opts.uflag { -// unix_bind(&opts.host, &opts) -// } else { -// unix_listen(&opts.host) -// } -// } + fn enable_stdin(&mut self) -> std::io::Result<()> { + self.poll.reregister(&self.event_stdin, NcCore::TK_STDIN, Ready::readable(), + PollOpt::empty()) + } -// loop { -// if opts.family != AF_UNIX { -// sock = local_listen(opts).expect("server: listen error"); -// } -// // /* -// // * For UDP and -k, don't connect the socket, let it -// // * receive datagrams from multiple socket pairs. -// // */ -// // if opts.uflag && opts.kflag { -// // readwrite(sock.as_raw_fd(), opts); -// // } -// // /* -// // * For UDP and not -k, we will use recvfrom() initially -// // * to wait for a caller, then use the regular functions -// // * to talk to the caller. -// // */ -// // else if opts.uflag && !opts.kflag { - -// if opts.uflag { -// let mut netinbuf: [u8; BUFSIZE] = [0; BUFSIZE]; -// let (_, sockaddr) = sock.peek_from(&mut netinbuf).unwrap(); -// sock.connect(&sockaddr).expect("connect error"); - -// if opts.vflag { -// eprintln!("Connection from {:?} received!", sockaddr); -// } - -// // readwrite(&mut sock, opts); -// NcCore::run(&mut sock, opts); -// } else { -// debug_info(&format!("sock = {:?}", sock)); -// let (mut sock_conn, sockaddr) = sock.accept().unwrap(); -// if opts.vflag { -// eprintln!("Connection from {:?} received!", sockaddr); -// } - -// // readwrite(&mut sock_conn, opts); -// NcCore::run(&mut sock_conn, opts); - -// // sock_conn.shutdown(std::net::Shutdown::Both); -// } - -// // if opts.family != AF_UNIX { - -// // } - -// if !opts.kflag { -// break; -// } -// } + fn disable_stdin(&mut self) -> std::io::Result<()> { + self.poll.reregister(&self.event_stdin, NcCore::TK_STDIN, Ready::empty(), + PollOpt::empty()) + } + fn enable_stdout(&mut self) -> std::io::Result<()> { + self.poll.reregister(&self.event_stdout, NcCore::TK_STDOUT, Ready::writable(), + PollOpt::empty()) + } -// return 0; -// } + fn disable_stdout(&mut self) -> std::io::Result<()> { + self.poll.reregister(&self.event_stdout, NcCore::TK_STDOUT, Ready::empty(), + PollOpt::empty()) + } -// /* -// * unix_connect() -// * Returns a socket connected to a local unix socket. Returns -1 on failure. -// */ -// fn unix_connect(path: &str, opts: &NcOptions) -> Socket { -// let sock = if opts.uflag { -// unix_bind(&opts.unix_dg_tmp_socket, opts) -// } else { -// Socket::new(Domain::unix(), socket2::Type::stream(), None).expect("failed to create unix socket") -// }; + fn remove_net(&mut self) -> std::io::Result<()> { + remove_item(&mut self.open_ends, NcCore::NETIN_POLL); + remove_item(&mut self.open_ends, NcCore::NETOUT_POLL); + self.poll.deregister(&self.event_net) + } -// sock.connect(&socket2::SockAddr::unix(path).expect("invalid unix socket path")).expect("bind error"); + fn read_stdin(&mut self) -> std::io::Result<()> { + let mut remove = false; + match io::stdin().read(&mut self.stdinbuf[self.stdinbufpos..]) { + Ok(len) => { + remove = len == 0; + self.stdinbufpos += len; + }, + Err(e) => { + match e.kind() { + ErrorKind::Interrupted => {}, + _ => remove = true + } + }, + } + if remove { + return self.remove_netin(); + } + Ok(()) + } -// sock -// } + fn write_netout(&mut self) -> std::io::Result<()> { + let mut remove = false; + match self.sock.write(&mut self.stdinbuf[0..self.stdinbufpos]) { + Ok(len) => { + debug_info(&format!("write ok len={}", len)); + // remove = len == 0; + if len > 0 { + self.stdinbufpos -= len; + if self.stdinbufpos > 0 { + self.stdinbuf_bac.copy_from_slice(&self.stdinbuf[len..len+self.stdinbufpos]); + self.stdinbuf.copy_from_slice(&self.stdinbuf_bac); + } + } + }, + Err(e) => { + debug_info(&format!("write error {:?}", e)); + match e.kind() { + ErrorKind::Interrupted => {}, + _ => remove = true + } + }, + } + if remove { + return self.remove_netout(); + } + Ok(()) + } -// fn unix_client(opts: &NcOptions) -> i32 { -// debug_info("unix_client"); -// let mut ret = 0; + fn read_netin(&mut self) -> std::io::Result<()> { + let mut remove = false; + match self.sock.read(&mut self.netinbuf[self.netinbufpos..]) { + Ok(len) => { + remove = len == 0; + self.netinbufpos += len; + }, + Err(e) => { + match e.kind() { + ErrorKind::Interrupted => {}, + _ => remove = true + } + }, + } + if remove { + return self.remove_netin(); + } + Ok(()) + } -// let mut sock = unix_connect(&opts.host, opts); + fn write_stdout(&mut self) -> std::io::Result<()> { + let mut remove = false; + match io::stdout().write(&mut self.netinbuf[0..self.netinbufpos]) { + Ok(len) => { + // remove = len == 0; + if len > 0 { + self.netinbufpos -= len; + if self.netinbufpos > 0 { + self.netinbuf_bac.copy_from_slice(&self.netinbuf[len..len+self.netinbufpos]); + self.netinbuf.copy_from_slice(&self.netinbuf_bac); + } + } + }, + Err(e) => { + match e.kind() { + ErrorKind::Interrupted => {}, + _ => remove = true + } + }, + } + if remove { + return self.remove_stdout(); + } + Ok(()) + } -// if !opts.zflag { -// // readwrite(&mut sock, &opts); -// NcCore::run(&mut sock, opts); -// } else { -// ret = 1; -// } + fn handle_error_event(&mut self, event: &Event) -> std::io::Result<()> { + match event.token() { + NcCore::TK_STDIN => self.remove_stdin(), + NcCore::TK_STDOUT => self.remove_stdout(), + NcCore::TK_NET => self.remove_net(), + _ => unreachable!() + } + } -// if opts.uflag { -// std::fs::remove_file(&opts.unix_dg_tmp_socket).expect("failed to remove the unix tmp socket file"); -// } + fn handle_hup_event(&mut self, event: &Event) -> std::io::Result<()> { + if !self.stdin_gone() && event.token() == NcCore::TK_STDIN && + !event.readiness().is_readable() { + self.remove_stdin()? + } -// ret -// } + if !self.netin_gone() && event.token() == NcCore::TK_NET && + !event.readiness().is_readable() { + self.remove_netin()? + } -// fn nonunix_client(opts: &NcOptions) -> i32 { -// let mut ret: i32 = 0; -// for port in &opts.portlist { -// let mut sock = match remote_connect(opts, port.clone()) { -// Some(expr) => expr, -// None => continue, -// }; + if event.token() == NcCore::TK_NET { + debug_info("STDOUT HUP"); + // TODO: check Nflag + self.sock.shutdown(std::net::Shutdown::Write)?; + self.remove_netout()? + } -// // sock.set_nonblocking(true); + Ok(()) + } -// ret = 0; + fn debug_print_ready_end(event :&Event, last_ready_end: &mut i32) { + let new_ready_end = match event.token() { + NcCore::TK_STDIN => NcCore::STDIN_POLL, + NcCore::TK_STDOUT => NcCore::STDOUT_POLL, + NcCore::TK_NET => { + let mut val = 0; + if event.readiness().is_readable() { + val |= NcCore::NETIN_POLL; + } + if event.readiness().is_writable() { + val |= NcCore::NETOUT_POLL; + } + val + } + _ => -1 + }; + if *last_ready_end != new_ready_end { + debug_info(&format!("new_ready_end {:?}", new_ready_end)); + } else { + if *last_ready_end & (NcCore::STDIN_POLL | NcCore::NETIN_POLL) != 0 { + debug_info(&format!("new_ready_end {:?}", new_ready_end)); + } + } + *last_ready_end = new_ready_end; + } +} -// // if opts.vflag || opts.zflag { -// // // TODO: implement -// // } +fn local_listen(opts: &NcOptions) -> Result { + debug_info("local_listen"); + // let mut addrs_iter = (&opts.host as &str, opts.portlist[0]).to_socket_addrs();//let mut addrs_iter = "127.9.0.1".to_socket_addrs();//(&(opts.host) as &str, opts.portlist[0]).to_socket_addrs(); + // if addrs_iter.is_err() { + // err_exit("get_matches") + // } -// // TODO: Fflag && !zflag -// // readwrite(&mut sock, opts); -// NcCore::run(&mut sock, opts); + // let mut addrs_iter = match { + // Ok(expr) => expr, + // Err(error) => { + // panic!("to_socket_addrs: {:?}", error) + // } + // }; -// } -// ret -// } + let addrs_iter = (&opts.host as &str, opts.portlist[0]).to_socket_addrs()?; + + for addr in addrs_iter { + let family = match addr { + SocketAddr::V4(_) => socket2::Domain::ipv4(), + SocketAddr::V6(_) => socket2::Domain::ipv6(), + }; + + let sock_type = if opts.uflag { + socket2::Type::dgram() + } else { + socket2::Type::stream() + }; + + let sock = Socket::new(family, sock_type, None)?; + + debug_info("local_listen binding"); + match sock.bind(&socket2::SockAddr::from(addr)) { + Ok(_) => { + if !opts.uflag { + sock.listen(128)?; + debug_info(&format!("local_listen returning sock.as_raw_fd() = {}", sock.as_raw_fd())); + } + debug_info("local_listen bind finish"); + return Ok(sock); + } + Err(_) => { + debug_info("local_listen err, continue"); + continue + } + }; + } + + return mesaerr_result("local_listen failed"); +} + +fn remote_connect(opts: &NcOptions, port: u16) -> Result{ + let addrs_iter = (&opts.host as &str, port).to_socket_addrs()?; + // let mut addrs_iter = match (&opts.host as &str, port).to_socket_addrs() { + // Ok(expr) => expr, + // Err(error) => { + // panic!("to_socket_addrs: {:?}", error) + // } + // }; + + for addr in addrs_iter{ + let sock_domain = match addr { + SocketAddr::V4(_) => socket2::Domain::ipv4(), + SocketAddr::V6(_) => socket2::Domain::ipv6(), + }; + + let sock_type = if opts.uflag { + socket2::Type::dgram() + } else { + socket2::Type::stream() + }; + + let sock = Socket::new(sock_domain, sock_type, None)?; + + if opts.s_addr.is_some() || opts.pflag { + // TODO: implement + + } + + + // TODO: maybe sometimes no timeout + match sock.connect_timeout(&socket2::SockAddr::from(addr), Duration::new(1, 0)) { + Ok(_) => return Ok(sock), + Err(_) => { + if opts.vflag { + let connection_type = if opts.uflag { + "udp" + } else { + "tcp" + }; + warn(&format!("connect to {} port {} ({}) failed", opts.host, port, connection_type)); + } + } + } + + + + + // if opts.uflag { + // // UdpSocket + // match UdpSocket::bind(addr) { + // Ok(sock) => return sock.as_raw_fd(), + // Err(_) => continue + // } + // } else { + // match TcpListener::bind(addr) { + // Ok(listener) => return listener.as_raw_fd(), + // Err(_) => continue + // }; + // } + } + mesaerr_result("local_listen failed") + // err_exit("local_listen failed"); +} + +/* + * unix_bind() + * Returns a unix socket bound to the given path + */ +fn unix_bind(path: &str, opts: &NcOptions) -> Result { + let sock_type = if opts.uflag { + socket2::Type::dgram() + } else { + socket2::Type::stream() + }; + + let sock = Socket::new(Domain::unix(), sock_type, None)?; + // .expect("failed to create unix socket"); + + sock.bind(&socket2::SockAddr::unix(path)?)?; + + //.expect("invalid unix socket path")).expect("bind error"); + + Ok(sock) +} + +/* + * unix_listen() + * Create a unix domain socket, and listen on it. + */ +fn unix_listen(path: &str) -> Result { + let sock = Socket::new(Domain::unix(), socket2::Type::stream(), None)?; + // .expect("failed to create unix socket"); + sock.bind(&socket2::SockAddr::unix(path)?)?; + //.expect("invalid unix socket path")).expect("bind error"); + sock.listen(5)?; + //. .expect("listen error"); + Ok(sock) +} +fn server(opts: &NcOptions) -> Result<(), MesaError> { + let mut sock :Socket = Socket::new(socket2::Domain::ipv4(), socket2::Type::stream(), None)?; + if opts.family == AF_UNIX { + sock = if opts.uflag { + unix_bind(&opts.host, &opts)? + } else { + unix_listen(&opts.host)? + } + } + + loop { + if opts.family != AF_UNIX { + sock = local_listen(opts)?; + } + // /* + // * For UDP and -k, don't connect the socket, let it + // * receive datagrams from multiple socket pairs. + // */ + // if opts.uflag && opts.kflag { + // readwrite(sock.as_raw_fd(), opts); + // } + // /* + // * For UDP and not -k, we will use recvfrom() initially + // * to wait for a caller, then use the regular functions + // * to talk to the caller. + // */ + // else if opts.uflag && !opts.kflag { + + if opts.uflag { + let mut netinbuf: [u8; BUFSIZE] = [0; BUFSIZE]; + let (_, sockaddr) = sock.peek_from(&mut netinbuf)?; + sock.connect(&sockaddr)?; + //.expect("connect error"); + + if opts.vflag { + eprintln!("Connection from {:?} received!", sockaddr); + } + + // readwrite(&mut sock, opts); + NcCore::run(&mut sock, opts)?; + } else { + debug_info(&format!("sock = {:?}", sock)); + let (mut sock_conn, sockaddr) = sock.accept()?; + if opts.vflag { + eprintln!("Connection from {:?} received!", sockaddr); + } + + // readwrite(&mut sock_conn, opts); + NcCore::run(&mut sock_conn, opts)?; + + // sock_conn.shutdown(std::net::Shutdown::Both); + } + + // if opts.family != AF_UNIX { + + // } + + if !opts.kflag { + break; + } + } + + + return Ok(()); +} + +/* + * unix_connect() + * Returns a socket connected to a local unix socket. Returns -1 on failure. + */ +fn unix_connect(path: &str, opts: &NcOptions) -> Result { + let sock = if opts.uflag { + unix_bind(&opts.unix_dg_tmp_socket, opts)? + } else { + Socket::new(Domain::unix(), socket2::Type::stream(), None)? + //.expect("failed to create unix socket") + }; + sock.connect(&socket2::SockAddr::unix(path)?)?; + //.expect("invalid unix socket path")).expect("bind error"); -pub fn execute(setup: &mut S, args: T) -> Result<(), MesaError> + Ok(sock) +} + +fn unix_client(opts: &NcOptions) -> Result<(), MesaError> { + debug_info("unix_client"); + + let mut sock = unix_connect(&opts.host, opts)?; + + if !opts.zflag { + // readwrite(&mut sock, &opts); + NcCore::run(&mut sock, opts)?; + } else { + return mesaerr_result("TODO: unknown error"); + } + + if opts.uflag { + std::fs::remove_file(&opts.unix_dg_tmp_socket)?; + // .expect("failed to remove the unix tmp socket file"); + } + + Ok(()) +} + +fn nonunix_client(opts: &NcOptions) -> Result<(), MesaError> { + for port in &opts.portlist { + let mut sock = match remote_connect(opts, port.clone()) { + Ok(expr) => expr, + Err(_) => continue, + }; + + // sock.set_nonblocking(true); + + // if opts.vflag || opts.zflag { + // // TODO: implement + // } + + // TODO: Fflag && !zflag + // readwrite(&mut sock, opts); + NcCore::run(&mut sock, opts)?; + + } + Ok(()) +} + + + +pub fn execute(_setup: &mut S, args: T) -> Result<(), MesaError> where S: UtilSetup, T: ArgsIter, { println!("this is netcat"); - let mut ret: i32 = 0; let mut help_msg: Vec = Vec::new(); let app = util_app!(NAME) .arg(Arg::with_name("l") @@ -1001,8 +991,8 @@ where .arg(Arg::with_name("positionals") .multiple(true)); - app.write_help(&mut help_msg); - let help_msg = String::from_utf8(help_msg).unwrap(); + app.write_help(&mut help_msg)?; + let help_msg = String::from_utf8(help_msg)?; let matches = app.get_matches_from_safe(args)?; @@ -1027,16 +1017,15 @@ where // } // }; - // if opts.lflag { - // ret = server(&opts); - // } - // else { - // if opts.family == AF_UNIX { - // ret = unix_client(&opts); - // } else { - // ret = nonunix_client(&opts); - // } - // } + if opts.lflag { + return server(&opts); + } else { + if opts.family == AF_UNIX { + unix_client(&opts)?; + } else { + nonunix_client(&opts)?; + } + } Ok(()) } From 8e25124202c2ab4751a230c04bb37e3e9247e676 Mon Sep 17 00:00:00 2001 From: RundongZhou Date: Sun, 15 Jul 2018 16:51:13 -0700 Subject: [PATCH 05/19] Clean code. --- src/networking/nc/mod.rs | 127 ++++++--------------------------------- 1 file changed, 20 insertions(+), 107 deletions(-) diff --git a/src/networking/nc/mod.rs b/src/networking/nc/mod.rs index 170937e..9480473 100644 --- a/src/networking/nc/mod.rs +++ b/src/networking/nc/mod.rs @@ -2,7 +2,6 @@ extern crate mio; extern crate clap; extern crate libc; extern crate socket2; -// extern crate tempfile; use std; use failure; @@ -26,11 +25,11 @@ use super::{UtilSetup, ArgsIter}; use super::{MesaError}; - pub(crate) const NAME: &str = "nc"; pub(crate) const DESCRIPTION: &str = "netcat"; const BUFSIZE: usize = 16384; +const PRINT_DEBUG_INFO: bool = false; #[derive(Debug)] struct NcOptions { @@ -59,28 +58,25 @@ fn mesaerr_result(err_msg: &str) -> Result { } fn build_ports(ports: &str) -> Result, MesaError>{ - return Ok(vec!(ports.parse::()?)); - //.expect(&format!("invalid port[s] {}", ports))); -} - -// fn usage(ret: bool, msg: &str) { -// eprint!("{}", msg); -// if ret { -// std::process::exit(1); -// } -// } + // TODO: suport XX-XX + let port_list = match ports.parse::() { + Ok(port) => port, + Err(_) => { + return mesaerr_result(&format!("invalid port[s] {}", ports)); + } + }; -// fn err_exit(msg: &str) { -// eprint!("{}", msg); -// std::process::exit(1); -// } + Ok(vec!(port_list)) +} fn warn(msg: &str) { eprint!("{}", msg); } fn debug_info(msg: &str) { - println!("{}", msg); + if PRINT_DEBUG_INFO { + eprint!("{}", msg); + } } impl NcOptions { @@ -101,7 +97,6 @@ impl NcOptions { let zflag = matches.is_present("z"); let kflag = matches.is_present("k"); - /* Cruft to make sure options are clean, and used properly. */ let positionals: Vec<&str> = if matches.is_present("positionals") { matches.values_of("positionals").unwrap().collect() @@ -126,19 +121,14 @@ impl NcOptions { } else { if !lflag { return mesaerr_result(msg); - // usage(true, msg); - // return None; } uport = String::from(positionals[0]); - // host = String::from("localhost"); } } else if positionals.len() >= 2 { host = String::from(positionals[0]); uport = String::from(positionals[1]); } else { return mesaerr_result(msg); - // usage(true, msg); - // return None; } if lflag && s_addr.is_some() { @@ -175,9 +165,8 @@ impl NcOptions { unix_dg_tmp_socket = if s_addr.is_some() { s_addr.clone().unwrap() } else { - let nf = NamedTempFile::new()?; //.expect("failed to create temporary file"); + let nf = NamedTempFile::new()?; let path = String::from(nf.path().to_str().unwrap()); - // nf.persist(&path).expect("failed to create temporary file"); path }; } @@ -208,7 +197,6 @@ impl NcOptions { fn remove_item(v: &mut Vec, item: T) { debug_info(&format!("remove_item {:?}", item)); - // let index = v.iter().position(|t| *t == item).unwrap(); match v.iter().position(|t| *t == item) { Some(i) => v.remove(i), None => return @@ -312,8 +300,9 @@ impl <'a> NcCore<'a> { sleep(self.opts.interval.unwrap()); } - self.poll.poll(&mut events, None)?; - // .expect("polling error"); + if let Err(_) = self.poll.poll(&mut events, None) { + return mesaerr_result("polling error"); + } /* timeout happened */ if events.is_empty() { @@ -683,17 +672,6 @@ impl <'a> NcCore<'a> { fn local_listen(opts: &NcOptions) -> Result { debug_info("local_listen"); - // let mut addrs_iter = (&opts.host as &str, opts.portlist[0]).to_socket_addrs();//let mut addrs_iter = "127.9.0.1".to_socket_addrs();//(&(opts.host) as &str, opts.portlist[0]).to_socket_addrs(); - // if addrs_iter.is_err() { - // err_exit("get_matches") - // } - - // let mut addrs_iter = match { - // Ok(expr) => expr, - // Err(error) => { - // panic!("to_socket_addrs: {:?}", error) - // } - // }; let addrs_iter = (&opts.host as &str, opts.portlist[0]).to_socket_addrs()?; @@ -733,12 +711,6 @@ fn local_listen(opts: &NcOptions) -> Result { fn remote_connect(opts: &NcOptions, port: u16) -> Result{ let addrs_iter = (&opts.host as &str, port).to_socket_addrs()?; - // let mut addrs_iter = match (&opts.host as &str, port).to_socket_addrs() { - // Ok(expr) => expr, - // Err(error) => { - // panic!("to_socket_addrs: {:?}", error) - // } - // }; for addr in addrs_iter{ let sock_domain = match addr { @@ -756,10 +728,8 @@ fn remote_connect(opts: &NcOptions, port: u16) -> Result{ if opts.s_addr.is_some() || opts.pflag { // TODO: implement - } - // TODO: maybe sometimes no timeout match sock.connect_timeout(&socket2::SockAddr::from(addr), Duration::new(1, 0)) { Ok(_) => return Ok(sock), @@ -774,25 +744,8 @@ fn remote_connect(opts: &NcOptions, port: u16) -> Result{ } } } - - - - - // if opts.uflag { - // // UdpSocket - // match UdpSocket::bind(addr) { - // Ok(sock) => return sock.as_raw_fd(), - // Err(_) => continue - // } - // } else { - // match TcpListener::bind(addr) { - // Ok(listener) => return listener.as_raw_fd(), - // Err(_) => continue - // }; - // } } mesaerr_result("local_listen failed") - // err_exit("local_listen failed"); } /* @@ -807,12 +760,7 @@ fn unix_bind(path: &str, opts: &NcOptions) -> Result { }; let sock = Socket::new(Domain::unix(), sock_type, None)?; - // .expect("failed to create unix socket"); - sock.bind(&socket2::SockAddr::unix(path)?)?; - - //.expect("invalid unix socket path")).expect("bind error"); - Ok(sock) } @@ -822,11 +770,8 @@ fn unix_bind(path: &str, opts: &NcOptions) -> Result { */ fn unix_listen(path: &str) -> Result { let sock = Socket::new(Domain::unix(), socket2::Type::stream(), None)?; - // .expect("failed to create unix socket"); sock.bind(&socket2::SockAddr::unix(path)?)?; - //.expect("invalid unix socket path")).expect("bind error"); sock.listen(5)?; - //. .expect("listen error"); Ok(sock) } @@ -844,6 +789,7 @@ fn server(opts: &NcOptions) -> Result<(), MesaError> { if opts.family != AF_UNIX { sock = local_listen(opts)?; } + // TODO: implement // /* // * For UDP and -k, don't connect the socket, let it // * receive datagrams from multiple socket pairs. @@ -862,13 +808,11 @@ fn server(opts: &NcOptions) -> Result<(), MesaError> { let mut netinbuf: [u8; BUFSIZE] = [0; BUFSIZE]; let (_, sockaddr) = sock.peek_from(&mut netinbuf)?; sock.connect(&sockaddr)?; - //.expect("connect error"); if opts.vflag { eprintln!("Connection from {:?} received!", sockaddr); } - // readwrite(&mut sock, opts); NcCore::run(&mut sock, opts)?; } else { debug_info(&format!("sock = {:?}", sock)); @@ -877,12 +821,11 @@ fn server(opts: &NcOptions) -> Result<(), MesaError> { eprintln!("Connection from {:?} received!", sockaddr); } - // readwrite(&mut sock_conn, opts); NcCore::run(&mut sock_conn, opts)?; - - // sock_conn.shutdown(std::net::Shutdown::Both); + // TODO: sock_conn.shutdown(std::net::Shutdown::Both); } + // TODO: implement // if opts.family != AF_UNIX { // } @@ -905,11 +848,9 @@ fn unix_connect(path: &str, opts: &NcOptions) -> Result { unix_bind(&opts.unix_dg_tmp_socket, opts)? } else { Socket::new(Domain::unix(), socket2::Type::stream(), None)? - //.expect("failed to create unix socket") }; sock.connect(&socket2::SockAddr::unix(path)?)?; - //.expect("invalid unix socket path")).expect("bind error"); Ok(sock) } @@ -928,7 +869,6 @@ fn unix_client(opts: &NcOptions) -> Result<(), MesaError> { if opts.uflag { std::fs::remove_file(&opts.unix_dg_tmp_socket)?; - // .expect("failed to remove the unix tmp socket file"); } Ok(()) @@ -941,29 +881,21 @@ fn nonunix_client(opts: &NcOptions) -> Result<(), MesaError> { Err(_) => continue, }; - // sock.set_nonblocking(true); - // if opts.vflag || opts.zflag { // // TODO: implement // } // TODO: Fflag && !zflag - // readwrite(&mut sock, opts); NcCore::run(&mut sock, opts)?; - } Ok(()) } - - pub fn execute(_setup: &mut S, args: T) -> Result<(), MesaError> where S: UtilSetup, T: ArgsIter, { - println!("this is netcat"); - let mut help_msg: Vec = Vec::new(); let app = util_app!(NAME) .arg(Arg::with_name("l") @@ -995,28 +927,9 @@ where let help_msg = String::from_utf8(help_msg)?; let matches = app.get_matches_from_safe(args)?; - // println!("matches = {:?}", matches); let opts = NcOptions::parse(matches, &help_msg)?; - // println!("opts = {:?}", opts); - // if opts.is_none() { - // // app.write_help(&mut io::stdout()); - // // print!("{}", help_msg); - // // return Err(failure::err_msg(help_msg)); - // // return Err(OsString::from(help_msg)); - // // return Err(MesaError::from(failure::err_msg("a").compat())); - - // } - - - // let opts = match opts { - // Ok(opts) => opts, - // Err() => { - // return mesaerr_result(&help_msg); - // } - // }; - if opts.lflag { return server(&opts); } else { From 24abf66afb66190a2321640ff5778bab41f070fa Mon Sep 17 00:00:00 2001 From: RundongZhou Date: Sun, 15 Jul 2018 19:37:56 -0700 Subject: [PATCH 06/19] Add description. --- src/networking/nc/mod.rs | 51 ++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/src/networking/nc/mod.rs b/src/networking/nc/mod.rs index 9480473..3a40908 100644 --- a/src/networking/nc/mod.rs +++ b/src/networking/nc/mod.rs @@ -26,7 +26,7 @@ use super::{MesaError}; pub(crate) const NAME: &str = "nc"; -pub(crate) const DESCRIPTION: &str = "netcat"; +pub(crate) const DESCRIPTION: &str = "arbitrary TCP and UDP connections and listens"; const BUFSIZE: usize = 16384; const PRINT_DEBUG_INFO: bool = false; @@ -182,6 +182,7 @@ impl NcOptions { portlist: portlist, family: family, kflag: matches.is_present("k"), + // TODO: implement // sflag: sflag, s_addr: s_addr, pflag: pflag, @@ -276,20 +277,20 @@ impl <'a> NcCore<'a> { /* both inputs are gone, buffers are empty, we are done */ if self.stdin_gone() && self.netin_gone() && self.stdinbuf_empty() && self.netinbuf_empty() { - self.sock.shutdown(std::net::Shutdown::Both)?; + // TODO: self.sock.shutdown(std::net::Shutdown::Both)?; return Ok(()); } /* both outputs are gone, we can't continue */ if self.stdout_gone() && self.netout_gone() { - self.sock.shutdown(std::net::Shutdown::Both)?; + // TODO: self.sock.shutdown(std::net::Shutdown::Both)?; return Ok(()); } /* listen and net in gone, queues empty, done */ if self.opts.lflag && self.netin_gone() && self.stdinbuf_empty() && self.netinbuf_empty() { - self.sock.shutdown(std::net::Shutdown::Both)?; + // TODO: self.sock.shutdown(std::net::Shutdown::Both)?; return Ok(()); } @@ -329,7 +330,7 @@ impl <'a> NcCore<'a> { // if no stdout, stop watching net in if self.stdout_gone() { if !self.netin_gone() { - self.sock.shutdown(std::net::Shutdown::Read)?; + // TODO: self.sock.shutdown(std::net::Shutdown::Read)?; } self.remove_stdin()?; } @@ -393,7 +394,7 @@ impl <'a> NcCore<'a> { if self.stdin_gone() && self.stdinbuf_empty() { if !self.netout_gone() { // TODO: check && opts.Nflag { - self.sock.shutdown(std::net::Shutdown::Write)?; + // TODO: self.sock.shutdown(std::net::Shutdown::Write)?; } self.remove_netout()?; } @@ -548,6 +549,7 @@ impl <'a> NcCore<'a> { if len > 0 { self.stdinbufpos -= len; if self.stdinbufpos > 0 { + // TODO: improve efficiency self.stdinbuf_bac.copy_from_slice(&self.stdinbuf[len..len+self.stdinbufpos]); self.stdinbuf.copy_from_slice(&self.stdinbuf_bac); } @@ -595,6 +597,7 @@ impl <'a> NcCore<'a> { if len > 0 { self.netinbufpos -= len; if self.netinbufpos > 0 { + // TODO: improve efficiency self.netinbuf_bac.copy_from_slice(&self.netinbuf[len..len+self.netinbufpos]); self.netinbuf.copy_from_slice(&self.netinbuf_bac); } @@ -636,7 +639,7 @@ impl <'a> NcCore<'a> { if event.token() == NcCore::TK_NET { debug_info("STDOUT HUP"); // TODO: check Nflag - self.sock.shutdown(std::net::Shutdown::Write)?; + // TODO: self.sock.shutdown(std::net::Shutdown::Write)?; self.remove_netout()? } @@ -728,6 +731,7 @@ fn remote_connect(opts: &NcOptions, port: u16) -> Result{ if opts.s_addr.is_some() || opts.pflag { // TODO: implement + unimplemented!(); } // TODO: maybe sometimes no timeout @@ -876,7 +880,7 @@ fn unix_client(opts: &NcOptions) -> Result<(), MesaError> { fn nonunix_client(opts: &NcOptions) -> Result<(), MesaError> { for port in &opts.portlist { - let mut sock = match remote_connect(opts, port.clone()) { + let mut sock = match remote_connect(opts, *port) { Ok(expr) => expr, Err(_) => continue, }; @@ -899,35 +903,46 @@ where let mut help_msg: Vec = Vec::new(); let app = util_app!(NAME) .arg(Arg::with_name("l") - .short("l")) + .short("l") + .help("Used to specify that nc should listen for an incoming connection rather than initiate a connection to a remote host. It is an error to use this option in conjunction with the -p, -s, or -z options. Additionally, any timeouts specified with the -w option are ignored.")) .arg(Arg::with_name("i") .short("i") .value_name("interval") + .help("Specifies a delay time interval between lines of text sent and received. Also causes a delay time between connections to multiple ports.") .takes_value(true)) .arg(Arg::with_name("s") .short("s") .value_name("source_ip_address") - .takes_value(true)) + .takes_value(true) + .help("Specifies the IP of the interface which is used to send the packets. It is an error to use this option in conjunction with the -l option.")) .arg(Arg::with_name("d") - .short("d")) + .short("d") + .help("Do not attempt to read from stdin.")) .arg(Arg::with_name("U") - .short("U")) + .short("U") + .help("Specifies to use Unix Domain Sockets.")) .arg(Arg::with_name("u") - .short("u")) + .short("u") + .help("Use UDP instead of the default option of TCP.")) .arg(Arg::with_name("v") - .short("v")) + .short("v") + .help("Have nc give more verbose output.")) .arg(Arg::with_name("k") - .short("k")) + .short("k") + .help("Forces nc to stay listening for another connection after its current connection is completed. It is an error to use this option without the -l option.")) .arg(Arg::with_name("z") - .short("z")) + .short("z") + .help("Specifies that nc should just scan for listening daemons, without sending any data to them. It is an error to use this option in conjunction with the -l option.")) .arg(Arg::with_name("positionals") - .multiple(true)); + .value_name("[hostname] [port[s]]") + .multiple(true) + .required(true)); app.write_help(&mut help_msg)?; let help_msg = String::from_utf8(help_msg)?; let matches = app.get_matches_from_safe(args)?; - // println!("matches = {:?}", matches); + debug_info(&format!("matches = {:?}", matches)); let opts = NcOptions::parse(matches, &help_msg)?; if opts.lflag { From fb1f56192ccb85b362bdafe4bca83b43a8789dd3 Mon Sep 17 00:00:00 2001 From: RundongZhou Date: Sun, 15 Jul 2018 19:38:45 -0700 Subject: [PATCH 07/19] Remove sflag. --- src/networking/nc/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/networking/nc/mod.rs b/src/networking/nc/mod.rs index 3a40908..d4b67ed 100644 --- a/src/networking/nc/mod.rs +++ b/src/networking/nc/mod.rs @@ -182,8 +182,6 @@ impl NcOptions { portlist: portlist, family: family, kflag: matches.is_present("k"), - // TODO: implement - // sflag: sflag, s_addr: s_addr, pflag: pflag, vflag: matches.is_present("v"), From 6994b0651f46ac16961cf12b3d0253fa95704c21 Mon Sep 17 00:00:00 2001 From: Zhou Rundong Date: Thu, 26 Jul 2018 00:55:29 -0700 Subject: [PATCH 08/19] Fix some minor points according to the comments. --- src/networking/nc/mod.rs | 73 +++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 39 deletions(-) diff --git a/src/networking/nc/mod.rs b/src/networking/nc/mod.rs index d4b67ed..5ede54e 100644 --- a/src/networking/nc/mod.rs +++ b/src/networking/nc/mod.rs @@ -1,6 +1,5 @@ extern crate mio; extern crate clap; -extern crate libc; extern crate socket2; use std; @@ -10,7 +9,7 @@ use clap::{Arg, ArgMatches}; use mio::{Events, Event, Poll, Ready, PollOpt, Token}; use libc::{AF_UNSPEC, AF_INET, AF_INET6, AF_UNIX}; use std::io; -use std::net::{SocketAddr}; +use std::net::SocketAddr; use mio::unix::EventedFd; use std::io::{Read,Write, ErrorKind}; use mio::unix::UnixReady; @@ -81,10 +80,10 @@ fn debug_info(msg: &str) { impl NcOptions { pub fn parse(matches: ArgMatches, msg: &str) -> Result { - let mut portlist = vec!(); + let mut portlist = vec![]; let lflag = matches.is_present("l"); let mut host = String::from("127.0.0.1"); - let uport:String; + let uport: String; let mut interval = None; let mut timeout = None; let s_addr = match matches.value_of("s") { @@ -98,11 +97,7 @@ impl NcOptions { let kflag = matches.is_present("k"); /* Cruft to make sure options are clean, and used properly. */ - let positionals: Vec<&str> = if matches.is_present("positionals") { - matches.values_of("positionals").unwrap().collect() - } else { - vec!() - }; + let positionals:Vec<&str> = matches.values_of("positionals").unwrap().collect(); let family = if matches.is_present("U") { AF_UNIX @@ -128,7 +123,7 @@ impl NcOptions { host = String::from(positionals[0]); uport = String::from(positionals[1]); } else { - return mesaerr_result(msg); + unreachable!() } if lflag && s_addr.is_some() { @@ -345,7 +340,7 @@ impl <'a> NcCore<'a> { } else if self.stdinbuf_full() { self.disable_stdin()?; } - } + } // if net writable and buf not empty, try to write to net // error, stop watching for netout @@ -444,81 +439,81 @@ impl <'a> NcCore<'a> { self.netinbufpos >= BUFSIZE } - fn remove_stdin(&mut self) -> std::io::Result<()> { + fn remove_stdin(&mut self) -> io::Result<()> { remove_item(&mut self.open_ends, NcCore::STDIN_POLL); self.poll.deregister(&self.event_stdin) } - fn remove_stdout(&mut self) -> std::io::Result<()> { + fn remove_stdout(&mut self) -> io::Result<()> { debug_info("remove_stdout"); remove_item(&mut self.open_ends, NcCore::STDOUT_POLL); self.poll.deregister(&self.event_stdout) } - fn remove_netin(&mut self) -> std::io::Result<()> { + fn remove_netin(&mut self) -> io::Result<()> { remove_item(&mut self.open_ends, NcCore::NETIN_POLL); self.net_interest.remove(Ready::readable()); self.reregister_net() } - fn remove_netout(&mut self) -> std::io::Result<()> { + fn remove_netout(&mut self) -> io::Result<()> { remove_item(&mut self.open_ends, NcCore::NETOUT_POLL); self.net_interest.remove(Ready::writable()); return self.reregister_net(); } - fn reregister_net(&mut self) -> std::io::Result<()> { + fn reregister_net(&mut self) -> io::Result<()> { self.poll.reregister(&self.event_net, NcCore::TK_NET, self.net_interest, PollOpt::empty()) } - fn enable_netin(&mut self) -> std::io::Result<()>{ + fn enable_netin(&mut self) -> io::Result<()>{ self.net_interest |= Ready::readable(); self.reregister_net() } - fn disable_netin(&mut self) -> std::io::Result<()>{ + fn disable_netin(&mut self) -> io::Result<()>{ self.net_interest.remove(Ready::readable()); - self.reregister_net() + self.reregister_net() } - fn enable_netout(&mut self) -> std::io::Result<()> { + fn enable_netout(&mut self) -> io::Result<()> { self.net_interest |= Ready::writable(); - self.reregister_net() + self.reregister_net() } - fn disable_netout(&mut self) -> std::io::Result<()> { + fn disable_netout(&mut self) -> io::Result<()> { self.net_interest.remove(Ready::writable()); - self.reregister_net() + self.reregister_net() } - fn enable_stdin(&mut self) -> std::io::Result<()> { + fn enable_stdin(&mut self) -> io::Result<()> { self.poll.reregister(&self.event_stdin, NcCore::TK_STDIN, Ready::readable(), PollOpt::empty()) } - fn disable_stdin(&mut self) -> std::io::Result<()> { + fn disable_stdin(&mut self) -> io::Result<()> { self.poll.reregister(&self.event_stdin, NcCore::TK_STDIN, Ready::empty(), PollOpt::empty()) } - fn enable_stdout(&mut self) -> std::io::Result<()> { + fn enable_stdout(&mut self) -> io::Result<()> { self.poll.reregister(&self.event_stdout, NcCore::TK_STDOUT, Ready::writable(), PollOpt::empty()) } - fn disable_stdout(&mut self) -> std::io::Result<()> { + fn disable_stdout(&mut self) -> io::Result<()> { self.poll.reregister(&self.event_stdout, NcCore::TK_STDOUT, Ready::empty(), PollOpt::empty()) } - fn remove_net(&mut self) -> std::io::Result<()> { + fn remove_net(&mut self) -> io::Result<()> { remove_item(&mut self.open_ends, NcCore::NETIN_POLL); remove_item(&mut self.open_ends, NcCore::NETOUT_POLL); self.poll.deregister(&self.event_net) } - fn read_stdin(&mut self) -> std::io::Result<()> { + fn read_stdin(&mut self) -> io::Result<()> { let mut remove = false; match io::stdin().read(&mut self.stdinbuf[self.stdinbufpos..]) { Ok(len) => { @@ -538,7 +533,7 @@ impl <'a> NcCore<'a> { Ok(()) } - fn write_netout(&mut self) -> std::io::Result<()> { + fn write_netout(&mut self) -> io::Result<()> { let mut remove = false; match self.sock.write(&mut self.stdinbuf[0..self.stdinbufpos]) { Ok(len) => { @@ -567,7 +562,7 @@ impl <'a> NcCore<'a> { Ok(()) } - fn read_netin(&mut self) -> std::io::Result<()> { + fn read_netin(&mut self) -> io::Result<()> { let mut remove = false; match self.sock.read(&mut self.netinbuf[self.netinbufpos..]) { Ok(len) => { @@ -587,7 +582,7 @@ impl <'a> NcCore<'a> { Ok(()) } - fn write_stdout(&mut self) -> std::io::Result<()> { + fn write_stdout(&mut self) -> io::Result<()> { let mut remove = false; match io::stdout().write(&mut self.netinbuf[0..self.netinbufpos]) { Ok(len) => { @@ -607,14 +602,14 @@ impl <'a> NcCore<'a> { _ => remove = true } }, - } + } if remove { return self.remove_stdout(); } - Ok(()) + Ok(()) } - fn handle_error_event(&mut self, event: &Event) -> std::io::Result<()> { + fn handle_error_event(&mut self, event: &Event) -> io::Result<()> { match event.token() { NcCore::TK_STDIN => self.remove_stdin(), NcCore::TK_STDOUT => self.remove_stdout(), @@ -623,7 +618,7 @@ impl <'a> NcCore<'a> { } } - fn handle_hup_event(&mut self, event: &Event) -> std::io::Result<()> { + fn handle_hup_event(&mut self, event: &Event) -> io::Result<()> { if !self.stdin_gone() && event.token() == NcCore::TK_STDIN && !event.readiness().is_readable() { self.remove_stdin()? @@ -667,7 +662,7 @@ impl <'a> NcCore<'a> { debug_info(&format!("new_ready_end {:?}", new_ready_end)); } } - *last_ready_end = new_ready_end; + *last_ready_end = new_ready_end; } } @@ -910,9 +905,9 @@ where .takes_value(true)) .arg(Arg::with_name("s") .short("s") - .value_name("source_ip_address") + .value_name("source_ip_address") .takes_value(true) - .help("Specifies the IP of the interface which is used to send the packets. It is an error to use this option in conjunction with the -l option.")) + .help("Specifies the IP of the interface which is used to send the packets. It is an error to use this option in conjunction with the -l option.")) .arg(Arg::with_name("d") .short("d") .help("Do not attempt to read from stdin.")) From ee5879cfd232d21a2926895b92a47a19a1e0ac1d Mon Sep 17 00:00:00 2001 From: RundongZhou Date: Sun, 5 Aug 2018 16:33:28 -0700 Subject: [PATCH 09/19] Minor fix for UtilSetup. --- src/networking/nc/mod.rs | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/networking/nc/mod.rs b/src/networking/nc/mod.rs index 5ede54e..80545e0 100644 --- a/src/networking/nc/mod.rs +++ b/src/networking/nc/mod.rs @@ -20,7 +20,7 @@ use std::os::unix::io::RawFd; use std::time::Duration; use std::net::{ToSocketAddrs}; use socket2::{Socket, Domain}; -use super::{UtilSetup, ArgsIter}; +use super::{UtilSetup, UtilRead, ArgsIter}; use super::{MesaError}; @@ -48,6 +48,8 @@ struct NcOptions { zflag: bool, timeout: Option, unix_dg_tmp_socket: String, + stdin_fd: i32, + stderr: } fn mesaerr_result(err_msg: &str) -> Result { @@ -68,6 +70,10 @@ fn build_ports(ports: &str) -> Result, MesaError>{ Ok(vec!(port_list)) } +fn warn(setup: &mut S, msg: &str) { + let _ = write!(setup.error(), "{}", msg); +} + fn warn(msg: &str) { eprint!("{}", msg); } @@ -96,7 +102,7 @@ impl NcOptions { let zflag = matches.is_present("z"); let kflag = matches.is_present("k"); - /* Cruft to make sure options are clean, and used properly. */ + // Cruft to make sure options are clean, and used properly. let positionals:Vec<&str> = matches.values_of("positionals").unwrap().collect(); let family = if matches.is_present("U") { @@ -155,7 +161,7 @@ impl NcOptions { let mut unix_dg_tmp_socket = String::new(); - /* Get name of temporary socket for unix datagram client */ + // Get name of temporary socket for unix datagram client if family == AF_UNIX && uflag && !lflag { unix_dg_tmp_socket = if s_addr.is_some() { s_addr.clone().unwrap() @@ -183,6 +189,7 @@ impl NcOptions { timeout: timeout, unix_dg_tmp_socket: unix_dg_tmp_socket, zflag: zflag, + stdin_fd: 0 }; return Ok(ret); @@ -231,7 +238,7 @@ impl <'a> NcCore<'a> { poll: Poll::new()?, net_interest: Ready::readable(), - event_stdin: EventedFd(&0), + event_stdin: EventedFd(&opts.stdin_fd), event_net: EventedFd(net_fd), event_stdout: EventedFd(&1), stdinbuf: [0; BUFSIZE], @@ -267,20 +274,20 @@ impl <'a> NcCore<'a> { let mut last_ready_end = -1; loop { - /* both inputs are gone, buffers are empty, we are done */ + // both inputs are gone, buffers are empty, we are done if self.stdin_gone() && self.netin_gone() && self.stdinbuf_empty() && self.netinbuf_empty() { // TODO: self.sock.shutdown(std::net::Shutdown::Both)?; return Ok(()); } - /* both outputs are gone, we can't continue */ + // both outputs are gone, we can't continue if self.stdout_gone() && self.netout_gone() { // TODO: self.sock.shutdown(std::net::Shutdown::Both)?; return Ok(()); } - /* listen and net in gone, queues empty, done */ + // listen and net in gone, queues empty, done if self.opts.lflag && self.netin_gone() && self.stdinbuf_empty() && self.netinbuf_empty() { // TODO: self.sock.shutdown(std::net::Shutdown::Both)?; @@ -298,7 +305,7 @@ impl <'a> NcCore<'a> { return mesaerr_result("polling error"); } - /* timeout happened */ + // timeout happened if events.is_empty() { return Ok(()); } @@ -888,7 +895,7 @@ fn nonunix_client(opts: &NcOptions) -> Result<(), MesaError> { Ok(()) } -pub fn execute(_setup: &mut S, args: T) -> Result<(), MesaError> +pub fn execute(setup: &mut S, args: T) -> Result<(), MesaError> where S: UtilSetup, T: ArgsIter, @@ -936,7 +943,18 @@ where let matches = app.get_matches_from_safe(args)?; debug_info(&format!("matches = {:?}", matches)); - let opts = NcOptions::parse(matches, &help_msg)?; + let mut opts = NcOptions::parse(matches, &help_msg)?; + + // adjust stdin_fd for UtilSetup + // invalid fd is treated as dflag + let stdin_fd = match setup.input().raw_fd() { + Some(fd) => fd, + _ => { + opts.dflag = true; + 0 + } + }; + opts.stdin_fd = stdin_fd; if opts.lflag { return server(&opts); From ea8965ab30bf608779babfcbc8fec602dc91e035 Mon Sep 17 00:00:00 2001 From: RundongZhou Date: Sun, 5 Aug 2018 17:14:35 -0700 Subject: [PATCH 10/19] Fix compilation error. --- Cargo.lock | 1 + Cargo.toml | 2 +- libmesabox/Cargo.toml | 7 +++++-- libmesabox/src/lib.rs | 2 ++ {src => libmesabox/src}/networking/nc/mod.rs | 16 +++++++--------- libmesabox/src/util_list.rs | 3 ++- 6 files changed, 18 insertions(+), 13 deletions(-) rename {src => libmesabox/src}/networking/nc/mod.rs (99%) diff --git a/Cargo.lock b/Cargo.lock index 055cc95..b50b4cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -481,6 +481,7 @@ dependencies = [ "rustyline 2.0.0-alpha (git+https://github.com/kkawakam/rustyline)", "socket2 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "tar 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "trust-dns-resolver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1 (git+https://github.com/uutils/coreutils)", "walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 1c82486..d5a8c3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ lsb = [ ] ping = ["libmesabox/ping"] -nc = [] +nc = ["libmesabox/nc"] networking = [ "ping", "nc" diff --git a/libmesabox/Cargo.toml b/libmesabox/Cargo.toml index eab9d34..03c3fa4 100644 --- a/libmesabox/Cargo.toml +++ b/libmesabox/Cargo.toml @@ -28,8 +28,10 @@ lsb = [ ] ping = ["chrono", "crossbeam", "libc", "pnet", "byteorder", "trust-dns-resolver", "mio", "socket2"] +nc = ["tempfile", "socket2"] networking = [ - "ping" + "ping", + "nc" ] cat = [] @@ -112,9 +114,10 @@ pnet = { version = "0.21.0", optional = true } byteorder = { version = "1.2.3", optional = true } trust-dns-resolver = { version = "0.9.0", optional = true } mio = { version = "0.6.14", optional = true } -socket2 = { version = "0.3.5", optional = true } +socket2 = { version = "0.3.5", optional = true, features = ["unix"] } walkdir = { version = "2.1.4", optional = true } fnv = { version = "1.0.6", optional = true } # use the git version until an update is finally published to crates.io rustyline = { git = "https://github.com/kkawakam/rustyline", optional = true } log = { version = "0.4.3", optional = true } +tempfile = { version = "3.0.3", optional = true } diff --git a/libmesabox/src/lib.rs b/libmesabox/src/lib.rs index 4ddef16..6eb131e 100644 --- a/libmesabox/src/lib.rs +++ b/libmesabox/src/lib.rs @@ -55,6 +55,8 @@ extern crate rustyline; #[cfg(feature = "log")] #[macro_use] extern crate log; +#[cfg(feature = "tempfile")] +extern crate tempfile; use clap::{App, SubCommand}; use std::env::{self, VarsOs}; diff --git a/src/networking/nc/mod.rs b/libmesabox/src/networking/nc/mod.rs similarity index 99% rename from src/networking/nc/mod.rs rename to libmesabox/src/networking/nc/mod.rs index 80545e0..629b6b3 100644 --- a/src/networking/nc/mod.rs +++ b/libmesabox/src/networking/nc/mod.rs @@ -20,8 +20,7 @@ use std::os::unix::io::RawFd; use std::time::Duration; use std::net::{ToSocketAddrs}; use socket2::{Socket, Domain}; -use super::{UtilSetup, UtilRead, ArgsIter}; -use super::{MesaError}; +use {UtilSetup, UtilRead, ArgsIter, MesaError}; pub(crate) const NAME: &str = "nc"; @@ -48,8 +47,7 @@ struct NcOptions { zflag: bool, timeout: Option, unix_dg_tmp_socket: String, - stdin_fd: i32, - stderr: + stdin_fd: i32 } fn mesaerr_result(err_msg: &str) -> Result { @@ -70,9 +68,9 @@ fn build_ports(ports: &str) -> Result, MesaError>{ Ok(vec!(port_list)) } -fn warn(setup: &mut S, msg: &str) { - let _ = write!(setup.error(), "{}", msg); -} +// fn warn(setup: &mut S, msg: &str) { +// let _ = write!(setup.error(), "{}", msg); +// } fn warn(msg: &str) { eprint!("{}", msg); @@ -947,8 +945,8 @@ where // adjust stdin_fd for UtilSetup // invalid fd is treated as dflag - let stdin_fd = match setup.input().raw_fd() { - Some(fd) => fd, + let stdin_fd = match setup.input().raw_object() { + Some(fd) => fd.raw_value(), _ => { opts.dflag = true; 0 diff --git a/libmesabox/src/util_list.rs b/libmesabox/src/util_list.rs index fab9b1f..f0c0ecf 100644 --- a/libmesabox/src/util_list.rs +++ b/libmesabox/src/util_list.rs @@ -12,7 +12,8 @@ generate_fns! { (tar, "tar_util") }, networking { - (ping, "ping") + (ping, "ping"), + (nc, "nc") }, posix { (cat, "cat"), From c7325fd0d521a5415609678dc1a9b32bce98f578 Mon Sep 17 00:00:00 2001 From: RundongZhou Date: Sun, 5 Aug 2018 19:45:38 -0700 Subject: [PATCH 11/19] Fix integration. Fix portrange. --- Cargo.lock | 1 + libmesabox/Cargo.toml | 3 +- libmesabox/src/lib.rs | 2 + libmesabox/src/networking/nc/mod.rs | 73 +++++++++++++++++++---------- tests/networking/nc.rs | 0 5 files changed, 54 insertions(+), 25 deletions(-) create mode 100644 tests/networking/nc.rs diff --git a/Cargo.lock b/Cargo.lock index b50b4cd..d75db05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -478,6 +478,7 @@ dependencies = [ "nix 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "platform-info 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "pnet 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustyline 2.0.0-alpha (git+https://github.com/kkawakam/rustyline)", "socket2 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "tar 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/libmesabox/Cargo.toml b/libmesabox/Cargo.toml index 03c3fa4..e3cd83f 100644 --- a/libmesabox/Cargo.toml +++ b/libmesabox/Cargo.toml @@ -28,7 +28,7 @@ lsb = [ ] ping = ["chrono", "crossbeam", "libc", "pnet", "byteorder", "trust-dns-resolver", "mio", "socket2"] -nc = ["tempfile", "socket2"] +nc = ["tempfile", "socket2", "regex"] networking = [ "ping", "nc" @@ -121,3 +121,4 @@ fnv = { version = "1.0.6", optional = true } rustyline = { git = "https://github.com/kkawakam/rustyline", optional = true } log = { version = "0.4.3", optional = true } tempfile = { version = "3.0.3", optional = true } +regex = { version = "1.0.2", optional = true } diff --git a/libmesabox/src/lib.rs b/libmesabox/src/lib.rs index 6eb131e..6e11410 100644 --- a/libmesabox/src/lib.rs +++ b/libmesabox/src/lib.rs @@ -57,6 +57,8 @@ extern crate rustyline; extern crate log; #[cfg(feature = "tempfile")] extern crate tempfile; +#[cfg(feature = "regex")] +extern crate regex; use clap::{App, SubCommand}; use std::env::{self, VarsOs}; diff --git a/libmesabox/src/networking/nc/mod.rs b/libmesabox/src/networking/nc/mod.rs index 629b6b3..45990fb 100644 --- a/libmesabox/src/networking/nc/mod.rs +++ b/libmesabox/src/networking/nc/mod.rs @@ -2,6 +2,7 @@ extern crate mio; extern crate clap; extern crate socket2; +use regex::Regex; use std; use failure; use tempfile::NamedTempFile; @@ -57,15 +58,35 @@ fn mesaerr_result(err_msg: &str) -> Result { } fn build_ports(ports: &str) -> Result, MesaError>{ - // TODO: suport XX-XX - let port_list = match ports.parse::() { - Ok(port) => port, - Err(_) => { - return mesaerr_result(&format!("invalid port[s] {}", ports)); + let re = Regex::new(r"^(?P\d{1,5})(-(?P\d{1,5}))?$").unwrap(); + let caps = re.captures(ports); + if caps.is_some() { + let caps = caps.unwrap(); + let start = caps.name("start").unwrap().as_str().parse::().unwrap(); + let mut end = 0; + match caps.name("end") { + Some(match_end) => end = match_end.as_str().parse::().unwrap(), + None => end = start + } + if start <= 65535 && end <= 65535 { + let ret = if start <= end { + ((start as u16)..=((end) as u16)).collect() + } else { + ((end as u16)..=((start) as u16)).collect() + }; + return Ok(ret); } - }; + } + return mesaerr_result(&format!("invalid port[s] {}", ports)); - Ok(vec!(port_list)) + // let port_list = match ports.parse::() { + // Ok(port) => port, + // Err(_) => { + // return mesaerr_result(&format!("invalid port[s] {}", ports)); + // } + // }; + + // Ok(vec!(port_list)) } // fn warn(setup: &mut S, msg: &str) { @@ -99,6 +120,7 @@ impl NcOptions { let zflag = matches.is_present("z"); let kflag = matches.is_present("k"); + let unixflag = matches.is_present("U"); // Cruft to make sure options are clean, and used properly. let positionals:Vec<&str> = matches.values_of("positionals").unwrap().collect(); @@ -139,6 +161,9 @@ impl NcOptions { if lflag && zflag { return mesaerr_result("cannot use -z and -l"); } + if unixflag && zflag { + return mesaerr_result("cannot use -z and -U"); + } if !lflag && kflag { return mesaerr_result("must use -l with -k"); } @@ -175,7 +200,7 @@ impl NcOptions { iflag: matches.is_present("i"), interval: interval, lflag: matches.is_present("l"), - unixflag: matches.is_present("U"), + unixflag: unixflag, uflag: uflag, host: host, portlist: portlist, @@ -275,20 +300,17 @@ impl <'a> NcCore<'a> { // both inputs are gone, buffers are empty, we are done if self.stdin_gone() && self.netin_gone() && self.stdinbuf_empty() && self.netinbuf_empty() { - // TODO: self.sock.shutdown(std::net::Shutdown::Both)?; return Ok(()); } // both outputs are gone, we can't continue if self.stdout_gone() && self.netout_gone() { - // TODO: self.sock.shutdown(std::net::Shutdown::Both)?; return Ok(()); } // listen and net in gone, queues empty, done if self.opts.lflag && self.netin_gone() && self.stdinbuf_empty() && self.netinbuf_empty() { - // TODO: self.sock.shutdown(std::net::Shutdown::Both)?; return Ok(()); } @@ -328,7 +350,6 @@ impl <'a> NcCore<'a> { // if no stdout, stop watching net in if self.stdout_gone() { if !self.netin_gone() { - // TODO: self.sock.shutdown(std::net::Shutdown::Read)?; } self.remove_stdin()?; } @@ -390,10 +411,6 @@ impl <'a> NcCore<'a> { // if stdin gone and stdinbuf empty, remove netout if self.stdin_gone() && self.stdinbuf_empty() { - if !self.netout_gone() { - // TODO: check && opts.Nflag { - // TODO: self.sock.shutdown(std::net::Shutdown::Write)?; - } self.remove_netout()?; } @@ -636,8 +653,6 @@ impl <'a> NcCore<'a> { if event.token() == NcCore::TK_NET { debug_info("STDOUT HUP"); - // TODO: check Nflag - // TODO: self.sock.shutdown(std::net::Shutdown::Write)?; self.remove_netout()? } @@ -824,7 +839,6 @@ fn server(opts: &NcOptions) -> Result<(), MesaError> { } NcCore::run(&mut sock_conn, opts)?; - // TODO: sock_conn.shutdown(std::net::Shutdown::Both); } // TODO: implement @@ -863,10 +877,9 @@ fn unix_client(opts: &NcOptions) -> Result<(), MesaError> { let mut sock = unix_connect(&opts.host, opts)?; if !opts.zflag { - // readwrite(&mut sock, &opts); NcCore::run(&mut sock, opts)?; } else { - return mesaerr_result("TODO: unknown error"); + return mesaerr_result("doesn't support -z for unix socket"); } if opts.uflag { @@ -876,6 +889,10 @@ fn unix_client(opts: &NcOptions) -> Result<(), MesaError> { Ok(()) } +fn udptest(sock: &mut Socket) -> i32 { + 0 +} + fn nonunix_client(opts: &NcOptions) -> Result<(), MesaError> { for port in &opts.portlist { let mut sock = match remote_connect(opts, *port) { @@ -883,9 +900,17 @@ fn nonunix_client(opts: &NcOptions) -> Result<(), MesaError> { Err(_) => continue, }; - // if opts.vflag || opts.zflag { - // // TODO: implement - // } + if opts.vflag || opts.zflag { + // For UDP, make sure we are connected. + if opts.uflag { + if udptest(&mut sock) != 0 { + continue; + } + } + + // Don't look up port if -n. + + } // TODO: Fflag && !zflag NcCore::run(&mut sock, opts)?; diff --git a/tests/networking/nc.rs b/tests/networking/nc.rs new file mode 100644 index 0000000..e69de29 From b2cd0930686d529b3b724ba437a8e2d72799c8b6 Mon Sep 17 00:00:00 2001 From: RundongZhou Date: Sun, 19 Aug 2018 15:00:49 -0700 Subject: [PATCH 12/19] Before modifying to generic ncoption. --- libmesabox/src/networking/nc/mod.rs | 155 ++++++++++++++++------------ 1 file changed, 87 insertions(+), 68 deletions(-) diff --git a/libmesabox/src/networking/nc/mod.rs b/libmesabox/src/networking/nc/mod.rs index 45990fb..cb11b87 100644 --- a/libmesabox/src/networking/nc/mod.rs +++ b/libmesabox/src/networking/nc/mod.rs @@ -4,22 +4,18 @@ extern crate socket2; use regex::Regex; use std; -use failure; use tempfile::NamedTempFile; use clap::{Arg, ArgMatches}; use mio::{Events, Event, Poll, Ready, PollOpt, Token}; +use mio::unix::{EventedFd, UnixReady}; use libc::{AF_UNSPEC, AF_INET, AF_INET6, AF_UNIX}; -use std::io; -use std::net::SocketAddr; -use mio::unix::EventedFd; -use std::io::{Read,Write, ErrorKind}; -use mio::unix::UnixReady; +use std::io::{self, Read,Write, ErrorKind}; use std::thread::sleep; use std::fmt::Debug; -use std::os::unix::io::AsRawFd; -use std::os::unix::io::RawFd; +use std::os::unix::io::{AsRawFd, RawFd}; use std::time::Duration; -use std::net::{ToSocketAddrs}; +use std::net::{ToSocketAddrs, SocketAddr}; +use std::path::{Path, PathBuf}; use socket2::{Socket, Domain}; use {UtilSetup, UtilRead, ArgsIter, MesaError}; @@ -30,8 +26,44 @@ pub(crate) const DESCRIPTION: &str = "arbitrary TCP and UDP connections and list const BUFSIZE: usize = 16384; const PRINT_DEBUG_INFO: bool = false; +#[derive(Fail, Debug)] +enum NetcatError { + #[fail(display = "{}: {}", path, err)] + Input { + #[cause] + err: io::Error, + path: String, + }, + + #[fail(display = "local_listen failed")] + LocalListenErr, + + #[fail(display = "invalid port[s] {}", _0)] + InvalidPortErr(String), + + #[fail(display = "poll error")] + PollErr, + + #[fail(display = "{}", _0)] + UsageErr(String), +} + + +// struct Cat<'a, I, O, E> +// where +// I: BufRead, +// O: Write, +// E: Write, +// { +// stdin: I, +// stdout: O, +// stderr: E, +// current_dir: Option<&'a Path>, +// interactive: bool, +// } + #[derive(Debug)] -struct NcOptions { +struct NcOptions{ dflag: bool, iflag: bool, interval: Option, @@ -47,14 +79,9 @@ struct NcOptions { vflag: bool, zflag: bool, timeout: Option, - unix_dg_tmp_socket: String, - stdin_fd: i32 -} - -fn mesaerr_result(err_msg: &str) -> Result { - Err(MesaError::from( - failure::err_msg(format!("{}", err_msg)).compat() - )) + unix_dg_tmp_socket: PathBuf, + stdin_fd: i32, + nflag: bool } fn build_ports(ports: &str) -> Result, MesaError>{ @@ -77,16 +104,7 @@ fn build_ports(ports: &str) -> Result, MesaError>{ return Ok(ret); } } - return mesaerr_result(&format!("invalid port[s] {}", ports)); - - // let port_list = match ports.parse::() { - // Ok(port) => port, - // Err(_) => { - // return mesaerr_result(&format!("invalid port[s] {}", ports)); - // } - // }; - - // Ok(vec!(port_list)) + return Err(NetcatError::InvalidPortErr(String::from(ports)))?; } // fn warn(setup: &mut S, msg: &str) { @@ -141,7 +159,7 @@ impl NcOptions { uport = String::new(); } else { if !lflag { - return mesaerr_result(msg); + return Err(NetcatError::UsageErr(String::from(msg)))?; } uport = String::from(positionals[0]); } @@ -152,22 +170,6 @@ impl NcOptions { unreachable!() } - if lflag && s_addr.is_some() { - return mesaerr_result("cannot use -s and -l"); - } - if lflag && pflag { - return mesaerr_result("cannot use -p and -l"); - } - if lflag && zflag { - return mesaerr_result("cannot use -z and -l"); - } - if unixflag && zflag { - return mesaerr_result("cannot use -z and -U"); - } - if !lflag && kflag { - return mesaerr_result("must use -l with -k"); - } - if !uport.is_empty() { portlist = build_ports(&uport)?; } @@ -182,16 +184,16 @@ impl NcOptions { timeout = Some(Duration::new(sec, 0)); } - let mut unix_dg_tmp_socket = String::new(); + let mut unix_dg_tmp_socket = PathBuf::new(); // Get name of temporary socket for unix datagram client if family == AF_UNIX && uflag && !lflag { - unix_dg_tmp_socket = if s_addr.is_some() { - s_addr.clone().unwrap() - } else { - let nf = NamedTempFile::new()?; - let path = String::from(nf.path().to_str().unwrap()); - path + unix_dg_tmp_socket = match s_addr { + Some(ref addr) => addr.into(), + None => { + let nf = NamedTempFile::new()?; + nf.path().into() + } }; } @@ -212,7 +214,8 @@ impl NcOptions { timeout: timeout, unix_dg_tmp_socket: unix_dg_tmp_socket, zflag: zflag, - stdin_fd: 0 + stdin_fd: 0, + nflag: matches.is_present("n") }; return Ok(ret); @@ -317,12 +320,12 @@ impl <'a> NcCore<'a> { /* help says -i is for "wait between lines sent". We read and * write arbitrary amounts of data, and we don't want to start * scanning for newlines, so this is as good as it gets */ - if self.opts.iflag { + if self.opts.iflag { sleep(self.opts.interval.unwrap()); } if let Err(_) = self.poll.poll(&mut events, None) { - return mesaerr_result("polling error"); + return Err(NetcatError::PollErr)?; } // timeout happened @@ -722,7 +725,7 @@ fn local_listen(opts: &NcOptions) -> Result { }; } - return mesaerr_result("local_listen failed"); + return Err(NetcatError::LocalListenErr)?; } fn remote_connect(opts: &NcOptions, port: u16) -> Result{ @@ -762,14 +765,14 @@ fn remote_connect(opts: &NcOptions, port: u16) -> Result{ } } } - mesaerr_result("local_listen failed") + return Err(NetcatError::LocalListenErr)?; } /* * unix_bind() * Returns a unix socket bound to the given path */ -fn unix_bind(path: &str, opts: &NcOptions) -> Result { +fn unix_bind>(path: P, opts: &NcOptions) -> Result { let sock_type = if opts.uflag { socket2::Type::dgram() } else { @@ -785,7 +788,7 @@ fn unix_bind(path: &str, opts: &NcOptions) -> Result { * unix_listen() * Create a unix domain socket, and listen on it. */ -fn unix_listen(path: &str) -> Result { +fn unix_listen>(path: P) -> Result { let sock = Socket::new(Domain::unix(), socket2::Type::stream(), None)?; sock.bind(&socket2::SockAddr::unix(path)?)?; sock.listen(5)?; @@ -876,11 +879,7 @@ fn unix_client(opts: &NcOptions) -> Result<(), MesaError> { let mut sock = unix_connect(&opts.host, opts)?; - if !opts.zflag { - NcCore::run(&mut sock, opts)?; - } else { - return mesaerr_result("doesn't support -z for unix socket"); - } + NcCore::run(&mut sock, opts)?; if opts.uflag { std::fs::remove_file(&opts.unix_dg_tmp_socket)?; @@ -908,7 +907,14 @@ fn nonunix_client(opts: &NcOptions) -> Result<(), MesaError> { } } - // Don't look up port if -n. + let mut service_name = ""; + // if nflag doesn't exits, look up the service name + if !opts.nflag { + // TODO: parse /etc/services + service_name = ""; + } + + } @@ -927,7 +933,11 @@ where let app = util_app!(NAME) .arg(Arg::with_name("l") .short("l") - .help("Used to specify that nc should listen for an incoming connection rather than initiate a connection to a remote host. It is an error to use this option in conjunction with the -p, -s, or -z options. Additionally, any timeouts specified with the -w option are ignored.")) + .help("Used to specify that nc should listen for an incoming connection rather than initiate a connection to a remote host. It is an error to use this option in conjunction with the -p, -s, or -z options. Additionally, any timeouts specified with the -w option are ignored.") + .conflicts_with("s") + .conflicts_with("p") + .conflicts_with("z") + .conflicts_with("k")) .arg(Arg::with_name("i") .short("i") .value_name("interval") @@ -943,7 +953,8 @@ where .help("Do not attempt to read from stdin.")) .arg(Arg::with_name("U") .short("U") - .help("Specifies to use Unix Domain Sockets.")) + .help("Specifies to use Unix Domain Sockets.") + .conflicts_with("z")) .arg(Arg::with_name("u") .short("u") .help("Use UDP instead of the default option of TCP.")) @@ -953,6 +964,9 @@ where .arg(Arg::with_name("k") .short("k") .help("Forces nc to stay listening for another connection after its current connection is completed. It is an error to use this option without the -l option.")) + .arg(Arg::with_name("n") + .short("n") + .help("Do not do any DNS or service lookups on any specified addresses, hostnames or ports.")) .arg(Arg::with_name("z") .short("z") .help("Specifies that nc should just scan for listening daemons, without sending any data to them. It is an error to use this option in conjunction with the -l option.")) @@ -970,7 +984,12 @@ where // adjust stdin_fd for UtilSetup // invalid fd is treated as dflag - let stdin_fd = match setup.input().raw_object() { + let (input, output, error) = setup.stdio(); + let stdin = input.lock()?; + let stdout = output.lock()?; + let stderr = error.lock()?; + + let stdin_fd = match stdin.raw_object() { Some(fd) => fd.raw_value(), _ => { opts.dflag = true; From 09a92d389d070f9db7fd1f02151b2f527b38836a Mon Sep 17 00:00:00 2001 From: RundongZhou Date: Sun, 19 Aug 2018 15:57:34 -0700 Subject: [PATCH 13/19] Fix timeout. --- libmesabox/src/networking/nc/mod.rs | 38 ++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/libmesabox/src/networking/nc/mod.rs b/libmesabox/src/networking/nc/mod.rs index cb11b87..8d42c72 100644 --- a/libmesabox/src/networking/nc/mod.rs +++ b/libmesabox/src/networking/nc/mod.rs @@ -63,7 +63,7 @@ enum NetcatError { // } #[derive(Debug)] -struct NcOptions{ +struct NcOptions { dflag: bool, iflag: bool, interval: Option, @@ -81,6 +81,7 @@ struct NcOptions{ timeout: Option, unix_dg_tmp_socket: PathBuf, stdin_fd: i32, + stderr_fd: i32, nflag: bool } @@ -215,11 +216,24 @@ impl NcOptions { unix_dg_tmp_socket: unix_dg_tmp_socket, zflag: zflag, stdin_fd: 0, + stderr_fd: 2, nflag: matches.is_present("n") }; return Ok(ret); } + + pub fn setup_stdio(&mut self, setup: T) { + self.stdin_fd = match setup.input().raw_object() { + Some(fd) => fd.raw_value(), + _ => { + self.dflag = true; + 0 + } + }; + // self. + // opts.stdin_fd = stdin_fd; + } } fn remove_item(v: &mut Vec, item: T) { @@ -750,8 +764,12 @@ fn remote_connect(opts: &NcOptions, port: u16) -> Result{ unimplemented!(); } - // TODO: maybe sometimes no timeout - match sock.connect_timeout(&socket2::SockAddr::from(addr), Duration::new(1, 0)) { + let connect_res = match opts.timeout { + Some(t) => sock.connect_timeout(&socket2::SockAddr::from(addr), t), + None => sock.connect(&socket2::SockAddr::from(addr)) + }; + + match connect_res { Ok(_) => return Ok(sock), Err(_) => { if opts.vflag { @@ -941,8 +959,13 @@ where .arg(Arg::with_name("i") .short("i") .value_name("interval") + .takes_value(true)) .help("Specifies a delay time interval between lines of text sent and received. Also causes a delay time between connections to multiple ports.") + .arg(Arg::with_name("w") + .short("w") + .value_name("timeout") .takes_value(true)) + .help("If a connection and stdin are idle for more than timeout seconds, then the connection is silently closed. The -w flag has no effect on the -l option, i.e. nc will listen forever for a connection, with or without the -w flag. The default is no timeout.") .arg(Arg::with_name("s") .short("s") .value_name("source_ip_address") @@ -982,14 +1005,7 @@ where debug_info(&format!("matches = {:?}", matches)); let mut opts = NcOptions::parse(matches, &help_msg)?; - // adjust stdin_fd for UtilSetup - // invalid fd is treated as dflag - let (input, output, error) = setup.stdio(); - let stdin = input.lock()?; - let stdout = output.lock()?; - let stderr = error.lock()?; - - let stdin_fd = match stdin.raw_object() { + let stdin_fd = match setup.input().raw_object() { Some(fd) => fd.raw_value(), _ => { opts.dflag = true; From 588dc7384864db5a80296bfa2c13160c04e776b8 Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Fri, 31 Aug 2018 15:04:17 -0700 Subject: [PATCH 14/19] Polish code and bug fix in clap construction --- libmesabox/src/networking/nc/mod.rs | 47 ++++++++--------------------- 1 file changed, 12 insertions(+), 35 deletions(-) diff --git a/libmesabox/src/networking/nc/mod.rs b/libmesabox/src/networking/nc/mod.rs index 8d42c72..fe96178 100644 --- a/libmesabox/src/networking/nc/mod.rs +++ b/libmesabox/src/networking/nc/mod.rs @@ -27,7 +27,7 @@ const BUFSIZE: usize = 16384; const PRINT_DEBUG_INFO: bool = false; #[derive(Fail, Debug)] -enum NetcatError { +enum NcError { #[fail(display = "{}: {}", path, err)] Input { #[cause] @@ -49,19 +49,6 @@ enum NetcatError { } -// struct Cat<'a, I, O, E> -// where -// I: BufRead, -// O: Write, -// E: Write, -// { -// stdin: I, -// stdout: O, -// stderr: E, -// current_dir: Option<&'a Path>, -// interactive: bool, -// } - #[derive(Debug)] struct NcOptions { dflag: bool, @@ -85,7 +72,9 @@ struct NcOptions { nflag: bool } +/// 0-65535, 0, 65535-0 fn build_ports(ports: &str) -> Result, MesaError>{ + let splits: Vec<&str> = ports.split("-").collect(); let re = Regex::new(r"^(?P\d{1,5})(-(?P\d{1,5}))?$").unwrap(); let caps = re.captures(ports); if caps.is_some() { @@ -105,7 +94,7 @@ fn build_ports(ports: &str) -> Result, MesaError>{ return Ok(ret); } } - return Err(NetcatError::InvalidPortErr(String::from(ports)))?; + return Err(NcError::InvalidPortErr(String::from(ports)))?; } // fn warn(setup: &mut S, msg: &str) { @@ -160,7 +149,7 @@ impl NcOptions { uport = String::new(); } else { if !lflag { - return Err(NetcatError::UsageErr(String::from(msg)))?; + return Err(NcError::UsageErr(String::from(msg)))?; } uport = String::from(positionals[0]); } @@ -222,18 +211,6 @@ impl NcOptions { return Ok(ret); } - - pub fn setup_stdio(&mut self, setup: T) { - self.stdin_fd = match setup.input().raw_object() { - Some(fd) => fd.raw_value(), - _ => { - self.dflag = true; - 0 - } - }; - // self. - // opts.stdin_fd = stdin_fd; - } } fn remove_item(v: &mut Vec, item: T) { @@ -339,7 +316,7 @@ impl <'a> NcCore<'a> { } if let Err(_) = self.poll.poll(&mut events, None) { - return Err(NetcatError::PollErr)?; + return Err(NcError::PollErr)?; } // timeout happened @@ -739,7 +716,7 @@ fn local_listen(opts: &NcOptions) -> Result { }; } - return Err(NetcatError::LocalListenErr)?; + return Err(NcError::LocalListenErr)?; } fn remote_connect(opts: &NcOptions, port: u16) -> Result{ @@ -783,7 +760,7 @@ fn remote_connect(opts: &NcOptions, port: u16) -> Result{ } } } - return Err(NetcatError::LocalListenErr)?; + return Err(NcError::LocalListenErr)?; } /* @@ -959,13 +936,13 @@ where .arg(Arg::with_name("i") .short("i") .value_name("interval") - .takes_value(true)) - .help("Specifies a delay time interval between lines of text sent and received. Also causes a delay time between connections to multiple ports.") + .takes_value(true) + .help("Specifies a delay time interval between lines of text sent and received. Also causes a delay time between connections to multiple ports.")) .arg(Arg::with_name("w") .short("w") .value_name("timeout") - .takes_value(true)) - .help("If a connection and stdin are idle for more than timeout seconds, then the connection is silently closed. The -w flag has no effect on the -l option, i.e. nc will listen forever for a connection, with or without the -w flag. The default is no timeout.") + .takes_value(true) + .help("If a connection and stdin are idle for more than timeout seconds, then the connection is silently closed. The -w flag has no effect on the -l option, i.e. nc will listen forever for a connection, with or without the -w flag. The default is no timeout.")) .arg(Arg::with_name("s") .short("s") .value_name("source_ip_address") From 70520a0b81646d97c38e6299da4f1c1faa2cc5c2 Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Fri, 31 Aug 2018 15:59:41 -0700 Subject: [PATCH 15/19] Refactor build_ports without regex --- libmesabox/src/networking/nc/mod.rs | 51 ++++++++++++++++------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/libmesabox/src/networking/nc/mod.rs b/libmesabox/src/networking/nc/mod.rs index fe96178..de9122c 100644 --- a/libmesabox/src/networking/nc/mod.rs +++ b/libmesabox/src/networking/nc/mod.rs @@ -2,7 +2,6 @@ extern crate mio; extern crate clap; extern crate socket2; -use regex::Regex; use std; use tempfile::NamedTempFile; use clap::{Arg, ArgMatches}; @@ -38,8 +37,8 @@ enum NcError { #[fail(display = "local_listen failed")] LocalListenErr, - #[fail(display = "invalid port[s] {}", _0)] - InvalidPortErr(String), + #[fail(display = "port range not invalid")] + InvalidPortErr, #[fail(display = "poll error")] PollErr, @@ -48,6 +47,12 @@ enum NcError { UsageErr(String), } +impl From for NcError { + fn from(err: std::num::ParseIntError) -> Self { + NcError::InvalidPortErr + } +} + #[derive(Debug)] struct NcOptions { @@ -72,29 +77,31 @@ struct NcOptions { nflag: bool } -/// 0-65535, 0, 65535-0 -fn build_ports(ports: &str) -> Result, MesaError>{ +fn build_ports(ports: &str) -> Result, NcError> { let splits: Vec<&str> = ports.split("-").collect(); - let re = Regex::new(r"^(?P\d{1,5})(-(?P\d{1,5}))?$").unwrap(); - let caps = re.captures(ports); - if caps.is_some() { - let caps = caps.unwrap(); - let start = caps.name("start").unwrap().as_str().parse::().unwrap(); - let mut end = 0; - match caps.name("end") { - Some(match_end) => end = match_end.as_str().parse::().unwrap(), - None => end = start - } - if start <= 65535 && end <= 65535 { - let ret = if start <= end { - ((start as u16)..=((end) as u16)).collect() + match splits.len() { + 1 => { + if splits[0] == "" { + return Err(NcError::InvalidPortErr); + } + let port: u16 = splits[0].parse()?; + Ok(vec![port]) + }, + 2 => { + if splits[0] == "" || splits[1] == "" { + return Err(NcError::InvalidPortErr); + } + let start: u16 = splits[0].parse()?; + let end: u16 = splits[1].parse()?; + let (start, end) = if start > end { + (end, start) } else { - ((end as u16)..=((start) as u16)).collect() + (start, end) }; - return Ok(ret); - } + Ok((start..=end).collect()) + }, + _ => Err(NcError::InvalidPortErr) } - return Err(NcError::InvalidPortErr(String::from(ports)))?; } // fn warn(setup: &mut S, msg: &str) { From 7f0e2cf526e7755c6008a465400b6462c803a02c Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Fri, 31 Aug 2018 16:08:55 -0700 Subject: [PATCH 16/19] Delete trailling white spaces and unused comments --- libmesabox/src/networking/nc/mod.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/libmesabox/src/networking/nc/mod.rs b/libmesabox/src/networking/nc/mod.rs index de9122c..6415989 100644 --- a/libmesabox/src/networking/nc/mod.rs +++ b/libmesabox/src/networking/nc/mod.rs @@ -104,10 +104,6 @@ fn build_ports(ports: &str) -> Result, NcError> { } } -// fn warn(setup: &mut S, msg: &str) { -// let _ = write!(setup.error(), "{}", msg); -// } - fn warn(msg: &str) { eprint!("{}", msg); } @@ -137,7 +133,7 @@ impl NcOptions { let kflag = matches.is_present("k"); let unixflag = matches.is_present("U"); - // Cruft to make sure options are clean, and used properly. + // Cruft to make sure options are clean, and used properly. let positionals:Vec<&str> = matches.values_of("positionals").unwrap().collect(); let family = if matches.is_present("U") { @@ -183,7 +179,7 @@ impl NcOptions { let mut unix_dg_tmp_socket = PathBuf::new(); - // Get name of temporary socket for unix datagram client + // Get name of temporary socket for unix datagram client if family == AF_UNIX && uflag && !lflag { unix_dg_tmp_socket = match s_addr { Some(ref addr) => addr.into(), @@ -298,18 +294,18 @@ impl <'a> NcCore<'a> { let mut last_ready_end = -1; loop { - // both inputs are gone, buffers are empty, we are done + // both inputs are gone, buffers are empty, we are done if self.stdin_gone() && self.netin_gone() && self.stdinbuf_empty() && self.netinbuf_empty() { return Ok(()); } - // both outputs are gone, we can't continue + // both outputs are gone, we can't continue if self.stdout_gone() && self.netout_gone() { return Ok(()); } - // listen and net in gone, queues empty, done + // listen and net in gone, queues empty, done if self.opts.lflag && self.netin_gone() && self.stdinbuf_empty() && self.netinbuf_empty() { return Ok(()); @@ -326,7 +322,7 @@ impl <'a> NcCore<'a> { return Err(NcError::PollErr)?; } - // timeout happened + // timeout happened if events.is_empty() { return Ok(()); } @@ -1010,4 +1006,3 @@ where Ok(()) } - From 09b508ca7152f30ea7091e79da119155da8e323c Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Fri, 31 Aug 2018 16:35:58 -0700 Subject: [PATCH 17/19] Add test for build_ports --- libmesabox/src/networking/nc/mod.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/libmesabox/src/networking/nc/mod.rs b/libmesabox/src/networking/nc/mod.rs index 6415989..8ef1134 100644 --- a/libmesabox/src/networking/nc/mod.rs +++ b/libmesabox/src/networking/nc/mod.rs @@ -25,15 +25,8 @@ pub(crate) const DESCRIPTION: &str = "arbitrary TCP and UDP connections and list const BUFSIZE: usize = 16384; const PRINT_DEBUG_INFO: bool = false; -#[derive(Fail, Debug)] +#[derive(Fail, Debug, PartialEq)] enum NcError { - #[fail(display = "{}: {}", path, err)] - Input { - #[cause] - err: io::Error, - path: String, - }, - #[fail(display = "local_listen failed")] LocalListenErr, @@ -1006,3 +999,16 @@ where Ok(()) } + +mod tests { + use super::*; + + #[test] + fn test_build_ports() { + assert_eq!(vec![1, 2, 3, 4, 5], build_ports("5-1").unwrap()); + assert_eq!(vec![1, 2, 3, 4, 5], build_ports("1-5").unwrap()); + assert_eq!(Err(NcError::InvalidPortErr), build_ports("1-")); + assert_eq!(Err(NcError::InvalidPortErr), build_ports("-")); + assert_eq!(Err(NcError::InvalidPortErr), build_ports("65536")); + } +} From dfe8b0ca5e00ab3c529ae8e59c33ff195d9d6650 Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Fri, 31 Aug 2018 17:16:50 -0700 Subject: [PATCH 18/19] Add create_app to make things clearer --- libmesabox/src/networking/nc/mod.rs | 162 ++++++++++++++-------------- 1 file changed, 81 insertions(+), 81 deletions(-) diff --git a/libmesabox/src/networking/nc/mod.rs b/libmesabox/src/networking/nc/mod.rs index 8ef1134..d157f96 100644 --- a/libmesabox/src/networking/nc/mod.rs +++ b/libmesabox/src/networking/nc/mod.rs @@ -4,7 +4,7 @@ extern crate socket2; use std; use tempfile::NamedTempFile; -use clap::{Arg, ArgMatches}; +use clap::{App, Arg, ArgMatches}; use mio::{Events, Event, Poll, Ready, PollOpt, Token}; use mio::unix::{EventedFd, UnixReady}; use libc::{AF_UNSPEC, AF_INET, AF_INET6, AF_UNIX}; @@ -46,7 +46,6 @@ impl From for NcError { } } - #[derive(Debug)] struct NcOptions { dflag: bool, @@ -70,33 +69,6 @@ struct NcOptions { nflag: bool } -fn build_ports(ports: &str) -> Result, NcError> { - let splits: Vec<&str> = ports.split("-").collect(); - match splits.len() { - 1 => { - if splits[0] == "" { - return Err(NcError::InvalidPortErr); - } - let port: u16 = splits[0].parse()?; - Ok(vec![port]) - }, - 2 => { - if splits[0] == "" || splits[1] == "" { - return Err(NcError::InvalidPortErr); - } - let start: u16 = splits[0].parse()?; - let end: u16 = splits[1].parse()?; - let (start, end) = if start > end { - (end, start) - } else { - (start, end) - }; - Ok((start..=end).collect()) - }, - _ => Err(NcError::InvalidPortErr) - } -} - fn warn(msg: &str) { eprint!("{}", msg); } @@ -905,8 +877,6 @@ fn nonunix_client(opts: &NcOptions) -> Result<(), MesaError> { service_name = ""; } - - } // TODO: Fflag && !zflag @@ -921,56 +891,7 @@ where T: ArgsIter, { let mut help_msg: Vec = Vec::new(); - let app = util_app!(NAME) - .arg(Arg::with_name("l") - .short("l") - .help("Used to specify that nc should listen for an incoming connection rather than initiate a connection to a remote host. It is an error to use this option in conjunction with the -p, -s, or -z options. Additionally, any timeouts specified with the -w option are ignored.") - .conflicts_with("s") - .conflicts_with("p") - .conflicts_with("z") - .conflicts_with("k")) - .arg(Arg::with_name("i") - .short("i") - .value_name("interval") - .takes_value(true) - .help("Specifies a delay time interval between lines of text sent and received. Also causes a delay time between connections to multiple ports.")) - .arg(Arg::with_name("w") - .short("w") - .value_name("timeout") - .takes_value(true) - .help("If a connection and stdin are idle for more than timeout seconds, then the connection is silently closed. The -w flag has no effect on the -l option, i.e. nc will listen forever for a connection, with or without the -w flag. The default is no timeout.")) - .arg(Arg::with_name("s") - .short("s") - .value_name("source_ip_address") - .takes_value(true) - .help("Specifies the IP of the interface which is used to send the packets. It is an error to use this option in conjunction with the -l option.")) - .arg(Arg::with_name("d") - .short("d") - .help("Do not attempt to read from stdin.")) - .arg(Arg::with_name("U") - .short("U") - .help("Specifies to use Unix Domain Sockets.") - .conflicts_with("z")) - .arg(Arg::with_name("u") - .short("u") - .help("Use UDP instead of the default option of TCP.")) - .arg(Arg::with_name("v") - .short("v") - .help("Have nc give more verbose output.")) - .arg(Arg::with_name("k") - .short("k") - .help("Forces nc to stay listening for another connection after its current connection is completed. It is an error to use this option without the -l option.")) - .arg(Arg::with_name("n") - .short("n") - .help("Do not do any DNS or service lookups on any specified addresses, hostnames or ports.")) - .arg(Arg::with_name("z") - .short("z") - .help("Specifies that nc should just scan for listening daemons, without sending any data to them. It is an error to use this option in conjunction with the -l option.")) - .arg(Arg::with_name("positionals") - .value_name("[hostname] [port[s]]") - .multiple(true) - .required(true)); - + let app = create_app(); app.write_help(&mut help_msg)?; let help_msg = String::from_utf8(help_msg)?; let matches = app.get_matches_from_safe(args)?; @@ -1000,6 +921,85 @@ where Ok(()) } +fn create_app() -> App<'static, 'static> { + util_app!(NAME) + .arg(Arg::with_name("l") + .short("l") + .help("Used to specify that nc should listen for an incoming connection rather than initiate a connection to a remote host. It is an error to use this option in conjunction with the -p, -s, or -z options. Additionally, any timeouts specified with the -w option are ignored.") + .conflicts_with("s") + .conflicts_with("p") + .conflicts_with("z") + .conflicts_with("k")) + .arg(Arg::with_name("i") + .short("i") + .value_name("interval") + .takes_value(true) + .help("Specifies a delay time interval between lines of text sent and received. Also causes a delay time between connections to multiple ports.")) + .arg(Arg::with_name("w") + .short("w") + .value_name("timeout") + .takes_value(true) + .help("If a connection and stdin are idle for more than timeout seconds, then the connection is silently closed. The -w flag has no effect on the -l option, i.e. nc will listen forever for a connection, with or without the -w flag. The default is no timeout.")) + .arg(Arg::with_name("s") + .short("s") + .value_name("source_ip_address") + .takes_value(true) + .help("Specifies the IP of the interface which is used to send the packets. It is an error to use this option in conjunction with the -l option.")) + .arg(Arg::with_name("d") + .short("d") + .help("Do not attempt to read from stdin.")) + .arg(Arg::with_name("U") + .short("U") + .help("Specifies to use Unix Domain Sockets.") + .conflicts_with("z")) + .arg(Arg::with_name("u") + .short("u") + .help("Use UDP instead of the default option of TCP.")) + .arg(Arg::with_name("v") + .short("v") + .help("Have nc give more verbose output.")) + .arg(Arg::with_name("k") + .short("k") + .help("Forces nc to stay listening for another connection after its current connection is completed. It is an error to use this option without the -l option.")) + .arg(Arg::with_name("n") + .short("n") + .help("Do not do any DNS or service lookups on any specified addresses, hostnames or ports.")) + .arg(Arg::with_name("z") + .short("z") + .help("Specifies that nc should just scan for listening daemons, without sending any data to them. It is an error to use this option in conjunction with the -l option.")) + .arg(Arg::with_name("positionals") + .value_name("[hostname] [port[s]]") + .multiple(true) + .required(true)) +} + +fn build_ports(ports: &str) -> Result, NcError> { + let splits: Vec<&str> = ports.split("-").collect(); + match splits.len() { + 1 => { + if splits[0] == "" { + return Err(NcError::InvalidPortErr); + } + let port: u16 = splits[0].parse()?; + Ok(vec![port]) + }, + 2 => { + if splits[0] == "" || splits[1] == "" { + return Err(NcError::InvalidPortErr); + } + let start: u16 = splits[0].parse()?; + let end: u16 = splits[1].parse()?; + let (start, end) = if start > end { + (end, start) + } else { + (start, end) + }; + Ok((start..=end).collect()) + }, + _ => Err(NcError::InvalidPortErr) + } +} + mod tests { use super::*; From d98eb5c2a61b925cfd3299a302c912e11fc5295e Mon Sep 17 00:00:00 2001 From: Mingshen Sun Date: Wed, 5 Sep 2018 10:43:11 -0700 Subject: [PATCH 19/19] networking/nc: wrap long help message --- libmesabox/src/networking/nc/mod.rs | 141 ++++++++++++++++++---------- 1 file changed, 91 insertions(+), 50 deletions(-) diff --git a/libmesabox/src/networking/nc/mod.rs b/libmesabox/src/networking/nc/mod.rs index d157f96..b7d0a41 100644 --- a/libmesabox/src/networking/nc/mod.rs +++ b/libmesabox/src/networking/nc/mod.rs @@ -80,7 +80,7 @@ fn debug_info(msg: &str) { } impl NcOptions { - pub fn parse(matches: ArgMatches, msg: &str) -> Result { + pub fn from_matches(matches: ArgMatches, msg: &str) -> Result { let mut portlist = vec![]; let lflag = matches.is_present("l"); let mut host = String::from("127.0.0.1"); @@ -897,7 +897,7 @@ where let matches = app.get_matches_from_safe(args)?; debug_info(&format!("matches = {:?}", matches)); - let mut opts = NcOptions::parse(matches, &help_msg)?; + let mut opts = NcOptions::from_matches(matches, &help_msg)?; let stdin_fd = match setup.input().raw_object() { Some(fd) => fd.raw_value(), @@ -923,54 +923,95 @@ where fn create_app() -> App<'static, 'static> { util_app!(NAME) - .arg(Arg::with_name("l") - .short("l") - .help("Used to specify that nc should listen for an incoming connection rather than initiate a connection to a remote host. It is an error to use this option in conjunction with the -p, -s, or -z options. Additionally, any timeouts specified with the -w option are ignored.") - .conflicts_with("s") - .conflicts_with("p") - .conflicts_with("z") - .conflicts_with("k")) - .arg(Arg::with_name("i") - .short("i") - .value_name("interval") - .takes_value(true) - .help("Specifies a delay time interval between lines of text sent and received. Also causes a delay time between connections to multiple ports.")) - .arg(Arg::with_name("w") - .short("w") - .value_name("timeout") - .takes_value(true) - .help("If a connection and stdin are idle for more than timeout seconds, then the connection is silently closed. The -w flag has no effect on the -l option, i.e. nc will listen forever for a connection, with or without the -w flag. The default is no timeout.")) - .arg(Arg::with_name("s") - .short("s") - .value_name("source_ip_address") - .takes_value(true) - .help("Specifies the IP of the interface which is used to send the packets. It is an error to use this option in conjunction with the -l option.")) - .arg(Arg::with_name("d") - .short("d") - .help("Do not attempt to read from stdin.")) - .arg(Arg::with_name("U") - .short("U") - .help("Specifies to use Unix Domain Sockets.") - .conflicts_with("z")) - .arg(Arg::with_name("u") - .short("u") - .help("Use UDP instead of the default option of TCP.")) - .arg(Arg::with_name("v") - .short("v") - .help("Have nc give more verbose output.")) - .arg(Arg::with_name("k") - .short("k") - .help("Forces nc to stay listening for another connection after its current connection is completed. It is an error to use this option without the -l option.")) - .arg(Arg::with_name("n") - .short("n") - .help("Do not do any DNS or service lookups on any specified addresses, hostnames or ports.")) - .arg(Arg::with_name("z") - .short("z") - .help("Specifies that nc should just scan for listening daemons, without sending any data to them. It is an error to use this option in conjunction with the -l option.")) - .arg(Arg::with_name("positionals") - .value_name("[hostname] [port[s]]") - .multiple(true) - .required(true)) + .arg( + Arg::with_name("l") + .short("l") + .help("Used to specify that nc should listen for an incoming \ + connection rather than initiate a connection to a remote host. \ + It is an error to use this option in conjunction with the -p, \ + -s, or -z options. Additionally, any timeouts specified with the \ + -w option are ignored.") + .conflicts_with("s") + .conflicts_with("p") + .conflicts_with("z") + .conflicts_with("k") + ) + .arg( + Arg::with_name("i") + .short("i") + .value_name("interval") + .takes_value(true) + .help("Specifies a delay time interval between lines of text \ + sent and received. Also causes a delay time between connections \ + to multiple ports.") + ) + .arg( + Arg::with_name("w") + .short("w") + .value_name("timeout") + .takes_value(true) + .help("If a connection and stdin are idle for more than timeout \ + seconds, then the connection is silently closed. The -w flag has \ + no effect on the -l option, i.e. nc will listen forever for a \ + connection, with or without the -w flag. The default is no \ + timeout.") + ) + .arg( + Arg::with_name("s") + .short("s") + .value_name("source_ip_address") + .takes_value(true) + .help("Specifies the IP of the interface which is used to send \ + the packets. It is an error to use this option in conjunction \ + with the -l option.") + ) + .arg( + Arg::with_name("d") + .short("d") + .help("Do not attempt to read from stdin.") + ) + .arg( + Arg::with_name("U") + .short("U") + .help("Specifies to use Unix Domain Sockets.") + .conflicts_with("z") + ) + .arg( + Arg::with_name("u") + .short("u") + .help("Use UDP instead of the default option of TCP.") + ) + .arg( + Arg::with_name("v") + .short("v") + .help("Have nc give more verbose output.") + ) + .arg( + Arg::with_name("k") + .short("k") + .help("Forces nc to stay listening for another connection after \ + its current connection is completed. It is an error to use this \ + option without the -l option.") + ) + .arg( + Arg::with_name("n") + .short("n") + .help("Do not do any DNS or service lookups on any specified \ + addresses, hostnames or ports.") + ) + .arg( + Arg::with_name("z") + .short("z") + .help("Specifies that nc should just scan for listening daemons, \ + without sending any data to them. It is an error to use this \ + option in conjunction with the -l option.") + ) + .arg( + Arg::with_name("positionals") + .value_name("[hostname] [port[s]]") + .multiple(true) + .required(true) + ) } fn build_ports(ports: &str) -> Result, NcError> {