added address blocking, expanded readme
This commit is contained in:
parent
2b0178773c
commit
0a9a247549
26
README.md
26
README.md
|
@ -7,6 +7,9 @@ Sunbeam is a simple and fast multi-endpoint TCP relay for
|
|||
for um
|
||||
we swear we had a perfectly reasonable use in mind, but we honestly can't remember it now.
|
||||
|
||||
### What it does
|
||||
Sunbeam is a little tiny server that passes data between TCP connections.
|
||||
|
||||
Run at the command line, Sunbeam takes any number of local ports (numbers between 1 and 65535)
|
||||
and remote addresses (strings like [ip address]:[port]). For each address argument passed,
|
||||
Sunbeam attempts to connect to that address, and for each port argument, it listens for incoming
|
||||
|
@ -17,9 +20,9 @@ promptly retransmitted to all other connections, save for two exceptions:
|
|||
- Data is not passed between servers that Sunbeam connects to.
|
||||
|
||||
For example, Sunbeam can be used as a relay for an audio stream by giving it the address of the
|
||||
stream server and a local port. Clients connecting to the local port will receive the stream data,
|
||||
but if one client sends data back to Sunbeam, it will not interfere with other clients' streams
|
||||
(it will, however, be relayed back to the source).
|
||||
stream server and a local port. Clients connecting to the local port will receive the stream
|
||||
data, but if one client sends data back to Sunbeam, it will not interfere with other clients'
|
||||
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
|
||||
|
@ -28,6 +31,23 @@ can also pass the `-l` flag to require Sunbeam to run in loopback mode.
|
|||
General usage looks like the following:
|
||||
`sunbeam [-l] [local port] [remote address]:[port]`
|
||||
|
||||
### How to block IP addresses
|
||||
It's 2019, and a server that can't filter incoming connections is a server that belongs to The
|
||||
Enemy. That's why Sunbeam, minimal as it is, includes this functionality nonetheless.
|
||||
You can create the file `.nosunbeam` in the directory where Sunbeam runs. Sunbeam will read it,
|
||||
parse each line as an IP address (v4 or v6 are both fine), and immediately drop any incoming
|
||||
connections from those addresses.
|
||||
|
||||
### Why you can't give it domain names
|
||||
Rust has a minimal stdlib. One consequence of this is the fact that there's no way to resolve a
|
||||
domain name using only stdlib, without writing and entire DNS client yourself. We admit we don't
|
||||
really understand how it all works, but it seems we only have two choices: live without domain
|
||||
name resolution, or pull in some enormous DNS crate with 37 unstable dependencies. We choose the
|
||||
former. As a result, you'll have to do your DNS resolution some other way, like by running `dig`
|
||||
at the command line to look up the IP address for the domain you want. Yes, it's clunky, but
|
||||
ergonomics are not really the biggest problem this program has either.
|
||||
|
||||
### Why it's called that
|
||||
This program is called "Sunbeam" because of a particular plot device that we thought was clever
|
||||
in the sci-fi novel *The Three Body Problem* by Cixin Liu. A character discovers that, due to a
|
||||
particular plasma-driven mechanism inside the sun, it's possible to use the sun as a gigantic
|
||||
|
|
48
sunbeam.rs
48
sunbeam.rs
|
@ -1,8 +1,9 @@
|
|||
use std::collections::VecDeque;
|
||||
use std::collections::{VecDeque,HashSet};
|
||||
use std::env::args;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::net::{TcpListener,TcpStream,SocketAddr};
|
||||
use std::net::{TcpListener,TcpStream,SocketAddr,IpAddr};
|
||||
use std::process::exit;
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
|
@ -12,6 +13,7 @@ static IDLE_THRESH:usize = 65536;
|
|||
static BUFFERSIZE_INITIAL:usize = 511;
|
||||
|
||||
static LOOPBACK_FLAG:&str = "-l";
|
||||
static BLOCKFILE_NAME:&str = ".nosunbeam";
|
||||
|
||||
fn main() {
|
||||
let argv = args().collect::<Vec<String>>();
|
||||
|
@ -43,6 +45,36 @@ fn main() {
|
|||
if loopbackmode {
|
||||
println!("-i- running in loopback mode (relaying data between all connections)");
|
||||
}
|
||||
|
||||
let mut blocklist:HashSet<IpAddr> = HashSet::new();
|
||||
match File::open(&BLOCKFILE_NAME) {
|
||||
Err(why) => match why.kind() {
|
||||
io::ErrorKind::NotFound => (),
|
||||
_ => {
|
||||
eprintln!("-!- failed to open {} to read IP address blocking rules: {}",BLOCKFILE_NAME,why);
|
||||
exit(1);
|
||||
},
|
||||
},
|
||||
Ok(mut file) => {
|
||||
let mut blockstring = String::new();
|
||||
match file.read_to_string(&mut blockstring) {
|
||||
Err(why) => {
|
||||
eprintln!("-!- failed to read {} for IP address blocking rules: {}",BLOCKFILE_NAME,why);
|
||||
exit(1);
|
||||
},
|
||||
Ok(_) => (),
|
||||
};
|
||||
for line in blockstring.lines() {
|
||||
match line.parse::<IpAddr>() {
|
||||
Err(_why) => {
|
||||
eprintln!("-!- could not parse '{}' in {} as an IP address",line,BLOCKFILE_NAME);
|
||||
exit(1);
|
||||
},
|
||||
Ok(address) => blocklist.insert(address),
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let mut connectors:VecDeque<(u16,TcpListener,VecDeque<(TcpStream,SocketAddr)>)> = VecDeque::new();
|
||||
for port in ports.iter() {
|
||||
|
@ -126,10 +158,14 @@ fn main() {
|
|||
},
|
||||
},
|
||||
Ok((stream,address)) => {
|
||||
println!("--> connection opened by {} on port {}",address,port);
|
||||
stream.set_nonblocking(true).expect("cannot set stream to nonblocking");
|
||||
stream.set_nodelay(true).expect("cannot set stream to nodelay");
|
||||
connections.push_back((stream,address));
|
||||
if blocklist.contains(&address.ip()) {
|
||||
println!("-x- refusing connection to {} (listed in {})",address,BLOCKFILE_NAME);
|
||||
} else {
|
||||
println!("--> connection opened by {} on port {}",address,port);
|
||||
stream.set_nonblocking(true).expect("cannot set stream to nonblocking");
|
||||
stream.set_nodelay(true).expect("cannot set stream to nodelay");
|
||||
connections.push_back((stream,address));
|
||||
}
|
||||
},
|
||||
};
|
||||
if let Some((mut stream,address)) = connections.pop_front() {
|
||||
|
|
Loading…
Reference in New Issue