Saving domain roots in unknown file
This commit is contained in:
Generated
+24
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user