Saving domain roots in unknown file

This commit is contained in:
Dave
2020-09-04 20:27:19 +01:00
parent b7a8aefbde
commit 4d4ee26a9b
4 changed files with 146 additions and 12 deletions
Generated
+24
View File
@@ -805,6 +805,15 @@ dependencies = [
"termcolor",
]
[[package]]
name = "error-chain"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
dependencies = [
"version_check 0.9.1",
]
[[package]]
name = "extend"
version = "0.1.1"
@@ -2281,6 +2290,20 @@ dependencies = [
"tokio-test",
]
[[package]]
name = "publicsuffix"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b"
dependencies = [
"error-chain",
"idna 0.2.0",
"lazy_static",
"native-tls",
"regex",
"url 2.1.1",
]
[[package]]
name = "quick-error"
version = "1.2.3"
@@ -2769,6 +2792,7 @@ dependencies = [
"ordered-buffer",
"pretty_env_logger",
"proxy-helpers",
"publicsuffix",
"rand 0.7.3",
"socks5-requests",
"tokio 0.2.22",
@@ -14,7 +14,9 @@ log = "0.4"
pretty_env_logger = "0.3"
tokio = { version = "0.2", features = ["stream", "tcp", "rt-threaded", "macros"] }
tokio-tungstenite = "0.11.0"
publicsuffix = "1.3.1"
# internal
nymsphinx = { path = "../../common/nymsphinx" }
ordered-buffer = {path = "../../common/socks5/ordered-buffer"}
socks5-requests = { path = "../../common/socks5/requests" }
@@ -1,5 +1,6 @@
use fs::OpenOptions;
use io::BufReader;
use publicsuffix::{errors, List};
use std::fs;
use std::fs::File;
use std::io;
@@ -11,7 +12,10 @@ use std::path::PathBuf;
///
/// Requests to unknown hosts are automatically written to an `unknown_hosts`
/// list so that they can be copy/pasted into the `allowed_hosts` list if desired.
struct OutboundRequestFilter {
/// This may be handy for service provider node operators who want to be able to look in the
/// `unknown_hosts` file and allow new hosts (e.g. if a wallet has added a new outbound request
/// which needs to be allowed).
pub(crate) struct OutboundRequestFilter {
allowed_hosts: HostsStore,
unknown_hosts: HostsStore,
}
@@ -27,29 +31,50 @@ impl OutboundRequestFilter {
}
}
/// Returns `true` if a host is in the `allowed_hosts` list.
/// Returns `true` if a host's domain is in the `allowed_hosts` list.
///
/// If it's not in the list, return `false` and write it to the `unknown_hosts` storefile.
pub(crate) fn check(&mut self, host: &str) -> bool {
if self.allowed_hosts.contains(host) {
let trimmed = Self::trim_port(host);
let domain_root = Self::get_domain_root(&trimmed).unwrap();
if self.allowed_hosts.contains(&domain_root) {
true
} else {
self.unknown_hosts.maybe_add(host);
self.unknown_hosts.maybe_add(&domain_root);
false
}
}
fn trim_port(host: &str) -> String {
let mut tmp: Vec<&str> = host.split(":").collect();
if tmp.len() > 1 {
tmp.pop(); // get rid of last element (port)
let out = tmp.join(":");
out
} else {
host.to_string()
}
}
/// Attempts to get the root domain, shorn of port, subdomains, etc.
fn get_domain_root(host: &str) -> Result<String, errors::ErrorKind> {
let list = List::fetch()?;
let domain = list.parse_domain(host)?;
let root = domain.root().unwrap();
Ok(root.to_string())
}
}
/// A simple file-based store for information about allowed / unknown hosts.
#[derive(Debug)]
struct HostsStore {
pub(crate) struct HostsStore {
storefile: PathBuf,
hosts: Vec<String>,
}
impl HostsStore {
/// Constructs a new HostsStore
fn new(base_dir: PathBuf, filename: PathBuf) -> HostsStore {
pub(crate) fn new(base_dir: PathBuf, filename: PathBuf) -> HostsStore {
let storefile = HostsStore::setup_storefile(base_dir, filename);
let hosts = HostsStore::load_from_storefile(&storefile).expect(&format!(
"Could not load hosts from storefile at {:?}",
@@ -72,10 +97,7 @@ impl HostsStore {
}
fn append_to_file(&self, host: &str) {
fs::write(&self.storefile, host).expect(&format!(
"Could not write to storage file at {:?}",
self.storefile
));
HostsStore::append(&self.storefile, host);
}
fn contains(&self, host: &str) -> bool {
@@ -92,7 +114,7 @@ impl HostsStore {
}
fn maybe_add(&mut self, host: &str) {
if !self.contains(&host) {
if !self.contains(host) {
self.hosts.push(host.to_string());
self.append_to_file(host);
}
@@ -128,6 +150,64 @@ impl HostsStore {
mod tests {
use super::*;
#[cfg(test)]
mod trimming_port_information {
use super::*;
#[test]
fn happens_when_port_exists() {
let host = "nymtech.net:9999";
assert_eq!("nymtech.net", OutboundRequestFilter::trim_port(host));
}
#[test]
fn doesnt_happen_when_no_port_exists() {
let host = "nymtech.net";
assert_eq!("nymtech.net", OutboundRequestFilter::trim_port(host));
}
}
#[cfg(test)]
mod getting_the_root_domain {
use super::*;
#[test]
fn gets_a_com_tld_ok() {
let host = "domain.com";
assert_eq!(
"domain.com",
OutboundRequestFilter::get_domain_root(host).unwrap()
)
}
#[test]
fn trims_subdomains() {
let host = "foomp.domain.com";
assert_eq!(
"domain.com",
OutboundRequestFilter::get_domain_root(host).unwrap()
)
}
#[test]
fn works_for_non_com_roots() {
let host = "domain.co.uk";
assert_eq!(
"domain.co.uk",
OutboundRequestFilter::get_domain_root(host).unwrap()
)
}
#[test]
fn works_for_non_com_roots_with_subdomains() {
let host = "foomp.domain.co.uk";
assert_eq!(
"domain.co.uk",
OutboundRequestFilter::get_domain_root(host).unwrap()
)
}
}
#[cfg(test)]
mod requests_to_unknown_hosts {
use super::*;
@@ -180,6 +260,14 @@ mod tests {
assert_eq!(true, filter.check(host));
}
#[test]
fn are_allowed_for_subdomains() {
let host = "foomp.nymtech.net";
let mut filter = setup();
assert_eq!(true, filter.check(host));
}
#[test]
fn are_not_appended_to_file() {
let mut filter = setup();
+21 -1
View File
@@ -1,3 +1,4 @@
use crate::allowed_hosts::{HostsStore, OutboundRequestFilter};
use crate::connection::Connection;
use crate::websocket;
use futures::channel::mpsc;
@@ -8,6 +9,7 @@ use nymsphinx::addressing::clients::Recipient;
use ordered_buffer::OrderedMessageBuffer;
use proxy_helpers::connection_controller::{Controller, ControllerCommand};
use socks5_requests::{Request, Response};
use std::path::PathBuf;
use tokio::net::TcpStream;
use tokio_tungstenite::tungstenite::protocol::Message;
use tokio_tungstenite::WebSocketStream;
@@ -16,11 +18,25 @@ use websocket_requests::{requests::ClientRequest, responses::ServerResponse};
pub struct ServiceProvider {
listening_address: String,
outbound_request_filter: OutboundRequestFilter,
}
impl ServiceProvider {
pub fn new(listening_address: String) -> ServiceProvider {
ServiceProvider { listening_address }
let allowed_hosts = HostsStore::new(
HostsStore::default_base_dir(),
PathBuf::from("allowed.list"),
);
let unknown_hosts = HostsStore::new(
HostsStore::default_base_dir(),
PathBuf::from("unknown.list"),
);
let outbound_request_filter = OutboundRequestFilter::new(allowed_hosts, unknown_hosts);
ServiceProvider {
listening_address,
outbound_request_filter,
}
}
/// Listens for any messages from `mix_reader` that should be written back to the mix network
@@ -112,6 +128,10 @@ impl ServiceProvider {
message,
return_address,
} => {
if !self.outbound_request_filter.check(&remote_addr) {
continue;
}
let controller_sender_clone = controller_sender.clone();
let mut ordered_buffer = OrderedMessageBuffer::new();
ordered_buffer.write(message);