From e69bb356fbdfe75a046fb9d61d3b948756f61e84 Mon Sep 17 00:00:00 2001 From: Avril Date: Wed, 8 Sep 2021 15:46:52 +0100 Subject: [PATCH] Compile crashes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fortune for datse's current commit: Half blessing − 半吉 --- src/main.rs | 1 + src/server/web/ban.rs | 128 +++++++++++++++++++++++++++++++++++++++ src/server/web/mod.rs | 32 +++++++++- src/server/web/source.rs | 15 ++++- 4 files changed, 171 insertions(+), 5 deletions(-) create mode 100644 src/server/web/ban.rs diff --git a/src/main.rs b/src/main.rs index 0fb0a81..d791a72 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ #![cfg_attr(nightly, feature(never_type))] #![feature(entry_insert)] +#![feature(type_alias_impl_trait)] #![allow(dead_code)] #![allow(unused_imports)] diff --git a/src/server/web/ban.rs b/src/server/web/ban.rs new file mode 100644 index 0000000..3e2748e --- /dev/null +++ b/src/server/web/ban.rs @@ -0,0 +1,128 @@ +//! Banning +use std::sync::{ + Arc, + Weak, +}; +use std::{fmt, error}; +use std::collections::BTreeSet; +use std::cmp::{PartialOrd, Ordering}; +use tokio::sync::RwLock; +use std::net::IpAddr; +use std::iter::FromIterator; + +use super::*; + +use source::ClientInfo; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum BanKind +{ + IP(IpAddr), +} + +impl PartialEq for BanKind +{ + fn eq(&self, other: &ClientInfo) -> bool + { + match self { + Self::IP(addr) => addr == &other.ip_addr, + } + } +} + +impl PartialOrd for BanKind +{ + #[inline] fn partial_cmp(&self, other: &ClientInfo) -> Option { + match self { + Self::IP(addr) => addr.partial_cmp(&other.ip_addr), + } + } +} + +impl ClientInfo +{ + /// Ban this client IP + #[inline] pub fn ban_ip(&self) -> BanKind + { + BanKind::IP(self.ip_addr.clone()) + } +} + + +#[derive(Debug, Clone)] +pub struct Banlist(Arc>>); + +type OpaqueFuture<'a, T> = impl Future + 'a; + +impl Banlist +{ + /// Create a new, empty banlist. + /// + /// To create one from a list of bans, this type implements `FromIterator`. + pub fn new() -> Self + { + Self(Arc::new(RwLock::new(BTreeSet::new()))) + } + + /// Add a ban to the list and wait for it to complete. + pub async fn add_ban_inline(&self, ban: BanKind) -> bool + { + self.0.write().await.insert(ban) + } + + /// Add a ban to the list. + /// + /// If the list is being used, this operation is deferred until it is able to complete. A future is returned to allow you to wait until the operation completes (it is a background task.) + pub fn add_ban(&self, ban: BanKind) -> impl Future + { + let col = self.0.clone(); + tokio::spawn(async move { + col.write().await.insert(ban) + }).map(|x| x.unwrap()) + } + + /// Create a warp filter that disallows hosts on the list. + /// + /// # Lifetime + /// The filter holds a weak reference to this list. If the list is dropped, then the filter will panic. + pub fn filter(&self) -> impl Fn(ClientInfo) -> OpaqueFuture<'static, Result> + Clone + 'static + { + let refer = Arc::downgrade(&self.0); + move |client_info: ClientInfo| { + let refer = refer.upgrade().unwrap(); + async move { + let bans = refer.read().await; + //XXX: FUCK there has to be a better way to check this right? + for fuck in bans.iter() + { + if fuck == &client_info { + return Err(warp::reject::custom(ClientBannedError)); + } + } + Ok(client_info) + } + } + } +} + +impl FromIterator for Banlist +{ + fn from_iter>(iter: I) -> Self + { + Self(Arc::new(RwLock::new(iter.into_iter().collect()))) + } +} + +/// Error returned from filter when the client is banned. +#[derive(Debug)] +pub struct ClientBannedError; + +impl warp::reject::Reject for ClientBannedError{} +impl error::Error for ClientBannedError{} +impl fmt::Display for ClientBannedError +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "you are banned") + } +} diff --git a/src/server/web/mod.rs b/src/server/web/mod.rs index b4f7a22..44cc6dc 100644 --- a/src/server/web/mod.rs +++ b/src/server/web/mod.rs @@ -23,18 +23,44 @@ use cryptohelpers::{ use uuid::Uuid; +mod ban; mod source; mod forwarded_list; -pub async fn serve(cfg: Config) -> eyre::Result<()> +fn routing<'a>() -> impl warp::Filter + Clone + 'a { + let auth = warp::path("auth").and({ + //let req = warp::path("req") + warp::post() + }); + + let push = warp::path("push").and({ + warp::post() + }); + + let get = warp::path("get").and({ + warp::get() + }); + + auth.or(push).or(get) +} + +pub async fn serve(cfg: impl Into) -> eyre::Result<()> +{ + let cfg = cfg.into(); + let bans = ban::Banlist::new(); // Filter: Extract the client IP from the remote address of the connection of the X-Forwarded-For header if it is trusted in `cfg`. let client_ip = warp::addr::remote() .and(warp::header("X-Forwarded-For")) .map(source::extract(cfg.trust_x_forwarded_for)) - .and_then(|req: Result| async move { req.map_err(warp::reject::custom) }); + // Extract the IP + .and_then(|req: Result| async move { req.map_err(warp::reject::custom) }) + // Enforce banlist + .and_then(bans.filter()); + //.and_then(|req: source::ClientInfo| async move { Result::<_, std::convert::Infallible>::Ok(req) }); + + let r = warp::path("api").and(routing()); - Ok(()) } diff --git a/src/server/web/source.rs b/src/server/web/source.rs index 108132f..1b2d5a5 100644 --- a/src/server/web/source.rs +++ b/src/server/web/source.rs @@ -49,13 +49,24 @@ impl fmt::Display for NoIpError /// /// # Returns /// A filter to perform the extraction -#[inline] pub fn extract(trust_x: bool) -> impl Fn(Option, XForwardedFor) -> Result + Clone +#[inline] pub fn extract(trust_x: bool) -> impl Fn(Option, XForwardedFor) -> Result + Clone { move |opt, x| { if trust_x { x.into_first().ok_or(NoIpError) } else { opt.map(|x| x.ip()).ok_or(NoIpError) - } + }.map(|ip_addr| + // Create ClientInfo + ClientInfo { + ip_addr + }) } } + +/// Information about the client +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ClientInfo +{ + pub ip_addr: IpAddr +}