From 07b1e0eff1b09ecd424e14692f866f53a8183f01 Mon Sep 17 00:00:00 2001 From: Ellie D Date: Mon, 10 Jun 2019 16:21:34 -0500 Subject: [PATCH] added mirror mode --- README.md | 7 +++++-- sunbeam.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 59 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 8ee516a..6fcffd5 100644 --- a/README.md +++ b/README.md @@ -29,10 +29,13 @@ streams (it will, however, be relayed back to the source). This behavior can be overridden with "loopback mode" in which Sunbeam always relays data to all clients and servers except the one which originated the data. Sunbeam automatically goes into loopback mode if no ports are specified, or if only one port and no addresses are specified. You -can also pass the `-l` flag to require Sunbeam to run in loopback mode. +can also pass the `-l` or `--loopback` flag to require Sunbeam to run in loopback mode. + +If you want Sunbeam to send data back to its source in addition to relaying it, you can also +activate "mirror mode" by passing the `-m` or `--mirror` flag at the command line. General usage looks like the following: -`sunbeam [-l] [local port] [remote address]:[port]` +`sunbeam [flags] [local port]... [remote address]:[port]...` Sunbeam is capable of using both TCP and UDP for sending and receiving data, and can pass data between TCP and UDP streams. By default, Sunbeam attempts to use both protocols for every port diff --git a/sunbeam.rs b/sunbeam.rs index 1afdd1c..e44202d 100644 --- a/sunbeam.rs +++ b/sunbeam.rs @@ -22,7 +22,6 @@ static IDLE_MAX:u64 = 65535; // maximum number of idle cycles to count up befo static RECONNECT_WAIT:u64 = 1000; // time in ms to wait between reconnect attempts -static LOOPBACK_FLAG:&str = "-l"; // flag passed to sunbeam to activate loopback mode static BLOCKFILE_NAME:&str = ".nosunbeam"; // file to check for as the client blacklist static ALLOWFILE_NAME:&str = ".yesunbeam"; // file to check for as the client whitelist, for Whitelist-mode connectors @@ -127,7 +126,11 @@ fn load_rules(rulefilepath:&str) -> HashSet { fn main() { let argv = args().collect::>(); if argv.len() < 2 { - eprintln!("-!- usage: sunbeam [protocol][local port][privacy]... [protocol][remote resource address]:[remote port]..."); + eprintln!("-!- usage: sunbeam [flags] [protocol][local port][privacy]... [protocol][remote resource address]:[remote port]..."); + eprintln!(); + eprintln!(" flags:"); + eprintln!(" -l --loopback - also relay data to clients connecting on the same port"); + eprintln!(" -m --mirror - also relay data back to its source"); eprintln!(); eprintln!(" prefix ports or addresses with a letter to indicate transport protocol:"); eprintln!(" t - tcp"); @@ -141,12 +144,21 @@ fn main() { exit(1); } let mut loopbackmode:bool = false; + let mut mirrormode:bool = false; let mut addresses:Vec<(PortProtocol,SocketAddr)> = Vec::new(); let mut ports:Vec<(PortProtocol,PortPrivacy,u16)> = Vec::new(); + let mut flags:Vec = Vec::new(); + let mut switches:Vec<&str> = Vec::new(); for arg in argv[1..].iter() { - if arg == LOOPBACK_FLAG { - loopbackmode = true; + if arg.starts_with("--") { + switches.push(arg.trim_matches('-')); + continue; + } else if arg.starts_with("-") { + for c in arg.trim_matches('-').chars() { + flags.push(c); + } + continue; } let strippedarg = arg.trim_end_matches(&PORT_SUFFIXES[..]).trim_start_matches(&PORT_PREFIXES[..]); match strippedarg.parse::() { @@ -180,9 +192,32 @@ fn main() { } }; } + for flag in flags.iter() { + match flag { + 'l' => loopbackmode = true, + 'm' => mirrormode = true, + c => { + eprintln!("-!- unrecognized flag character `{}`",c); + exit(1); + }, + }; + } + for switch in switches.iter() { + match switch { + &"loopback" => loopbackmode = true, + &"mirror" => mirrormode = true, + s => { + eprintln!("-!- unrecognized switch `{}`",s); + exit(1); + }, + }; + } loopbackmode |= (addresses.len() == 0 && ports.len() == 1) || (addresses.len() > 0 && ports.len() == 0); if loopbackmode { - println!("-i- running in loopback mode (relaying data between all connections)"); + println!("-i- running in loopback mode (relaying data between all clients)"); + } + if mirrormode { + println!("-i- running in mirror mode (also relaying data back to source client)"); } let blocklist = load_rules(BLOCKFILE_NAME); @@ -358,6 +393,9 @@ fn main() { broken_tcpsources.push_back(address.clone()); broken = true; } else { + if mirrormode && n > 0 { + let _ = guest.stream.write_all(&tcp_buffer[..n]); + } tcp_nrecv = n; sourceaddress = address.clone(); }, @@ -374,9 +412,12 @@ fn main() { Err(why) => { eprintln!("-!- failed to receive UDP packets: {}",why); }, - Ok((nrecv,srcaddr)) => { + Ok((n,srcaddr)) => { if &srcaddr == address { - udp_nrecv = nrecv; + if mirrormode && n > 0 { + let _ = guest.socket.send_to(&udp_buffer[..n],&srcaddr); + } + udp_nrecv = n; sourceaddress = srcaddr.clone(); } }, @@ -419,6 +460,9 @@ fn main() { Ok(n) => if n == 0 { println!("<-- disconnect {}: {} (closed by remote endpoint)",host.port,client.address); } else { + if mirrormode && n > 0 { + let _ = client.stream.write_all(&tcp_buffer[..n]); + } tcp_nrecv = n; host.clients.push_back(client); }, @@ -439,6 +483,9 @@ fn main() { } else if host.privacy == PortPrivacy::Whitelist && !srcaddr.ip().is_loopback() && !allowlist.contains(&srcaddr.ip()) { println!("-x- refuse {}/udp: {} (not listed in {})",host.port,srcaddr,ALLOWFILE_NAME); } else { + if mirrormode && n > 0 { + let _ = host.socket.send_to(&udp_buffer[..n],&srcaddr); + } udp_nrecv = n; sourceaddress = srcaddr; let newclient = UdpClient {