From 13b38c5b6c1143c888a8128dd95706ad05b7b5a5 Mon Sep 17 00:00:00 2001 From: realaravinth Date: Sat, 5 Jun 2021 19:55:35 +0530 Subject: [PATCH] counter is now a wrapper over mcaptcha --- CHANGELOG.md | 7 +- Cargo.lock | 143 +++++++++++++++++++++-- Cargo.toml | 1 + src/lib.rs | 3 +- src/master/embedded/counter.rs | 87 +++++++------- src/master/mod.rs | 68 ++--------- src/master/redis_master/mod.rs | 206 +++++++++++++++++++++++++++++++++ src/mcaptcha.rs | 118 +++++++++++++++++++ 8 files changed, 508 insertions(+), 125 deletions(-) create mode 100644 src/master/redis_master/mod.rs create mode 100644 src/mcaptcha.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 9146960..2494048 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,11 +15,8 @@ - `master::Master` is moved to `master::embedded::master` in preparation for Redis based implementation. -- `crate::mcaptcha` is moved to `master::embedded::counter` in preparation - for Redis based implementation. - - `AddSite` message for `Master` now requires an instance of - `crate::master::MCaptcha`. In the case of + `crate::mcaptcha::MCaptcha`. In the case of `crate::master::embedded::master`, it automatically starts `Counter` actor. @@ -54,7 +51,7 @@ ## Changed -- actix upgraded to `0.11` +- `actix` upgraded to `0.11` ## 0.1.1 diff --git a/Cargo.lock b/Cargo.lock index 4d7a3ec..f89e154 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,7 +9,7 @@ dependencies = [ "actix-rt", "actix_derive", "bitflags", - "bytes", + "bytes 0.5.6", "crossbeam-channel", "derive_more", "futures-channel", @@ -19,8 +19,8 @@ dependencies = [ "parking_lot", "pin-project", "smallvec", - "tokio", - "tokio-util", + "tokio 0.2.25", + "tokio-util 0.3.1", "trust-dns-proto", "trust-dns-resolver", ] @@ -47,7 +47,7 @@ dependencies = [ "futures-channel", "futures-util", "smallvec", - "tokio", + "tokio 0.2.25", ] [[package]] @@ -150,6 +150,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" +[[package]] +name = "bytes" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" + [[package]] name = "cfg-if" version = "0.1.10" @@ -162,6 +168,19 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "combine" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4369b5e4c0cddf64ad8981c0111e7df4f7078f4d6ba98fb31f2e17c4c57b7e" +dependencies = [ + "bytes 1.0.1", + "futures-util", + "memchr", + "pin-project-lite 0.2.6", + "tokio 1.6.1", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -180,6 +199,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" +[[package]] +name = "crc16" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "338089f42c427b86394a5ee60ff321da23a5c89c9d89514c829687b26359fcff" + [[package]] name = "crossbeam-channel" version = "0.4.4" @@ -349,6 +374,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "dtoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" + [[package]] name = "enum-as-inner" version = "0.3.3" @@ -628,6 +659,7 @@ dependencies = [ "pow_sha256", "pretty_env_logger", "rand 0.8.3", + "redis", "serde", "serde_json", ] @@ -702,12 +734,25 @@ dependencies = [ "kernel32-sys", "libc", "log", - "miow", + "miow 0.2.2", "net2", "slab", "winapi 0.2.8", ] +[[package]] +name = "mio" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" +dependencies = [ + "libc", + "log", + "miow 0.3.7", + "ntapi", + "winapi 0.3.9", +] + [[package]] name = "mio-uds" version = "0.6.8" @@ -716,7 +761,7 @@ checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" dependencies = [ "iovec", "libc", - "mio", + "mio 0.6.23", ] [[package]] @@ -731,6 +776,15 @@ dependencies = [ "ws2_32-sys", ] +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "net2" version = "0.2.37" @@ -742,6 +796,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "num_cpus" version = "1.13.0" @@ -965,6 +1028,28 @@ dependencies = [ "rand_core 0.6.2", ] +[[package]] +name = "redis" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a32cb439c4e89c1e6415e5b3b23d9d8cc6dc1bf5a8cade19adbd5418de803be" +dependencies = [ + "async-trait", + "bytes 1.0.1", + "combine", + "crc16", + "dtoa", + "futures-util", + "itoa", + "percent-encoding", + "pin-project-lite 0.2.6", + "rand 0.8.3", + "sha1", + "tokio 1.6.1", + "tokio-util 0.6.7", + "url", +] + [[package]] name = "redox_syscall" version = "0.2.5" @@ -1044,6 +1129,12 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" + [[package]] name = "sha2" version = "0.9.3" @@ -1171,14 +1262,14 @@ version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092" dependencies = [ - "bytes", + "bytes 0.5.6", "fnv", "futures-core", "iovec", "lazy_static", "libc", "memchr", - "mio", + "mio 0.6.23", "mio-uds", "pin-project-lite 0.1.12", "signal-hook-registry", @@ -1186,19 +1277,47 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "tokio" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a38d31d7831c6ed7aad00aa4c12d9375fd225a6dd77da1d25b707346319a975" +dependencies = [ + "autocfg", + "bytes 1.0.1", + "libc", + "memchr", + "mio 0.7.11", + "pin-project-lite 0.2.6", +] + [[package]] name = "tokio-util" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" dependencies = [ - "bytes", + "bytes 0.5.6", "futures-core", "futures-io", "futures-sink", "log", "pin-project-lite 0.1.12", - "tokio", + "tokio 0.2.25", +] + +[[package]] +name = "tokio-util" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592" +dependencies = [ + "bytes 1.0.1", + "futures-core", + "futures-sink", + "log", + "pin-project-lite 0.2.6", + "tokio 1.6.1", ] [[package]] @@ -1217,7 +1336,7 @@ dependencies = [ "rand 0.7.3", "smallvec", "thiserror", - "tokio", + "tokio 0.2.25", "url", ] @@ -1236,7 +1355,7 @@ dependencies = [ "resolv-conf", "smallvec", "thiserror", - "tokio", + "tokio 0.2.25", "trust-dns-proto", ] diff --git a/Cargo.toml b/Cargo.toml index 63c3edc..fd04387 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ derive_more = "0.99" rand = "0.8" pow_sha256 = { version = "0.2.1", git = "https://github.com/mcaptcha/pow_sha256" } +redis = { version = "0.20.1", features = ["tokio-comp","aio", "cluster"] } [dev-dependencies] actix-rt = "1" diff --git a/src/lib.rs b/src/lib.rs index 25c525e..62ac148 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -189,6 +189,7 @@ pub mod master; /// message datatypes to interact with [MCaptcha] actor pub mod cache; +pub mod mcaptcha; pub mod pow; pub mod system; mod utils; @@ -197,4 +198,4 @@ pub use crate::cache::hashcache::HashCache; pub use defense::{Defense, DefenseBuilder, LevelBuilder}; pub use master::embedded::counter::Counter; -pub use master::MCaptchaBuilder; +pub use mcaptcha::{MCaptcha, MCaptchaBuilder}; diff --git a/src/master/embedded/counter.rs b/src/master/embedded/counter.rs index bef65fb..ecb0d07 100644 --- a/src/master/embedded/counter.rs +++ b/src/master/embedded/counter.rs @@ -21,7 +21,7 @@ //! ```rust //! use libmcaptcha::{ //! master::embedded::counter::{Counter, AddVisitor}, -//! master::MCaptchaBuilder, +//! MCaptchaBuilder, //! cache::HashCache, //! LevelBuilder, //! DefenseBuilder @@ -86,56 +86,49 @@ use std::time::Duration; use actix::dev::*; use serde::{Deserialize, Serialize}; -use crate::master::MCaptcha; -use crate::{defense::Defense, master::AddVisitorResult}; +use crate::master::AddVisitorResult; +use crate::mcaptcha::MCaptcha; /// This struct represents the mCaptcha state and is used /// to configure leaky-bucket lifetime and manage defense #[derive(Clone, Serialize, Deserialize, Debug)] -pub struct Counter { - visitor_threshold: u32, - defense: Defense, - duration: u64, -} +pub struct Counter(MCaptcha); + impl From for Counter { fn from(m: MCaptcha) -> Counter { - let m = Counter { - duration: m.duration, - defense: m.defense, - visitor_threshold: m.visitor_threshold, - }; - m + Counter(m) } } -impl Counter { - /// increments the visitor count by one - pub fn add_visitor(&mut self) { - self.visitor_threshold += 1; - if self.visitor_threshold > self.defense.visitor_threshold() { - self.defense.tighten_up(); - } else { - self.defense.loosen_up(); - } - } +// impl Counter { +// /// increments the visitor count by one +// pub fn add_visitor(&mut self) { +// self.visitor_threshold += 1; +// if self.visitor_threshold > self.defense.visitor_threshold() { +// self.defense.tighten_up(); +// } else { +// self.defense.loosen_up(); +// } +// } +// +// /// decrements the visitor count by one +// pub fn decrement_visitor(&mut self) { +// if self.visitor_threshold > 0 { +// self.visitor_threshold -= 1; +// } +// } +// +// /// get current difficulty factor +// pub fn get_difficulty(&self) -> u32 { +// self.defense.get_difficulty() +// } +// +// /// get [Counter]'s lifetime +// pub fn get_duration(&self) -> u64 { +// self.duration +// } +// } - /// decrements the visitor count by one - pub fn decrement_visitor(&mut self) { - if self.visitor_threshold > 0 { - self.visitor_threshold -= 1; - } - } - - /// get current difficulty factor - pub fn get_difficulty(&self) -> u32 { - self.defense.get_difficulty() - } - - /// get [Counter]'s lifetime - pub fn get_duration(&self) -> u64 { - self.duration - } -} impl Actor for Counter { type Context = Context; } @@ -148,7 +141,7 @@ struct DeleteVisitor; impl Handler for Counter { type Result = (); fn handle(&mut self, _msg: DeleteVisitor, _ctx: &mut Self::Context) -> Self::Result { - self.decrement_visitor(); + self.0.decrement_visitor(); } } @@ -161,8 +154,8 @@ pub struct AddVisitor; impl AddVisitorResult { fn new(m: &Counter) -> Self { AddVisitorResult { - duration: m.get_duration(), - difficulty_factor: m.get_difficulty(), + duration: m.0.get_duration(), + difficulty_factor: m.0.get_difficulty(), } } } @@ -174,7 +167,7 @@ impl Handler for Counter { let addr = ctx.address(); use actix::clock::delay_for; - let duration: Duration = Duration::new(self.duration.clone(), 0); + let duration: Duration = Duration::new(self.0.get_duration(), 0); let wait_for = async move { //sleep(duration).await; delay_for(duration).await; @@ -183,7 +176,7 @@ impl Handler for Counter { .into_actor(self); ctx.spawn(wait_for); - self.add_visitor(); + self.0.add_visitor(); MessageResult(AddVisitorResult::new(&self)) } } @@ -197,7 +190,7 @@ impl Handler for Counter { type Result = MessageResult; fn handle(&mut self, _: GetCurrentVisitorCount, _ctx: &mut Self::Context) -> Self::Result { - MessageResult(self.visitor_threshold) + MessageResult(self.0.get_visitors()) } } diff --git a/src/master/mod.rs b/src/master/mod.rs index bddc455..ec524f2 100644 --- a/src/master/mod.rs +++ b/src/master/mod.rs @@ -23,9 +23,14 @@ use derive_builder::Builder; use serde::{Deserialize, Serialize}; pub mod embedded; - -use crate::defense::Defense; -use crate::errors::*; +#[allow( + unused_variables, + unused_imports, + unused_variables, + dead_code, + unused_macros +)] +use crate::mcaptcha::*; /// Describes actor handler trait impls that are required by a cache implementation pub trait Master: actix::Actor + actix::Handler + actix::Handler {} @@ -53,60 +58,3 @@ pub struct AddSite { pub id: String, pub mcaptcha: MCaptcha, } - -/// Builder for [MCaptcha] -#[derive(Clone, Serialize, Deserialize, Debug)] -pub struct MCaptchaBuilder { - visitor_threshold: u32, - defense: Option, - duration: Option, -} - -impl Default for MCaptchaBuilder { - fn default() -> Self { - MCaptchaBuilder { - visitor_threshold: 0, - defense: None, - duration: None, - } - } -} - -impl MCaptchaBuilder { - /// set defense - pub fn defense(&mut self, d: Defense) -> &mut Self { - self.defense = Some(d); - self - } - - /// set duration - pub fn duration(&mut self, d: u64) -> &mut Self { - self.duration = Some(d); - self - } - - /// Builds new [MCaptcha] - pub fn build(self: &mut MCaptchaBuilder) -> CaptchaResult { - if self.duration.is_none() { - Err(CaptchaError::PleaseSetValue("duration".into())) - } else if self.defense.is_none() { - Err(CaptchaError::PleaseSetValue("defense".into())) - } else if self.duration <= Some(0) { - Err(CaptchaError::CaptchaDurationZero) - } else { - let m = MCaptcha { - duration: self.duration.unwrap(), - defense: self.defense.clone().unwrap(), - visitor_threshold: self.visitor_threshold, - }; - Ok(m) - } - } -} - -#[derive(Clone, Serialize, Deserialize, Debug)] -pub struct MCaptcha { - visitor_threshold: u32, - defense: Defense, - duration: u64, -} diff --git a/src/master/redis_master/mod.rs b/src/master/redis_master/mod.rs new file mode 100644 index 0000000..43e1119 --- /dev/null +++ b/src/master/redis_master/mod.rs @@ -0,0 +1,206 @@ +/* + * mCaptcha - A proof of work based DoS protection system + * Copyright © 2021 Aravinth Manivannan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +use actix::dev::*; +use redis::cluster::ClusterClient; +//use redis::cluster::ClusterConnection; +use redis::Client; +//use redis::Connection; +use redis::RedisResult; +use redis::{aio::Connection, cluster::ClusterConnection}; + +//use crate::errors::*; +use crate::master::{AddSite, AddVisitor, Master as MasterTrait}; + +#[derive(Clone)] +pub enum Redis { + Single(Client), + Cluster(ClusterClient), +} + +pub enum RedisConnection { + Single(Connection), + Cluster(ClusterConnection), +} + +const INCR: &str = "MCAPTCHA_CACHE.COUNT"; +const GET: &str = "MCAPTCHA_CACHE.GET"; + +#[derive(Clone)] +pub struct Master { + pub redis: Redis, +} + +macro_rules! exec { + ($cmd:expr, $con:expr) => { + match $con { + RedisConnection::Single(mut con) => $cmd.query_async(&mut con).await, + RedisConnection::Cluster(mut con) => $cmd.query(&mut con), + } + }; +} + +impl Master { + async fn add_visitor(&mut self, key: &str) { + let mut cmd = redis::cmd(INCR); + cmd.arg(&[key]); + let a: RedisResult = exec!(cmd, self.get_connection().await); + + unimplemented!("Have to check return types of INCR command") + } + + async fn get_visitors(&mut self, key: &str) { + let mut cmd = redis::cmd(GET); + cmd.arg(&[key]); + let a: RedisResult = exec!(cmd, self.get_connection().await); + + unimplemented!("Have to check return types of GET command") + } + + async fn get_connection(&mut self) -> RedisConnection { + match &self.redis { + Redis::Single(c) => { + let con = c.get_async_connection().await.unwrap(); + RedisConnection::Single(con) + } + Redis::Cluster(c) => { + let con = c.get_connection().unwrap(); + RedisConnection::Cluster(con) + } + } + } + + async fn module_is_loaded(&mut self) -> () { + let mut cmd = redis::cmd("COMMAND"); + cmd.arg(&["INFO", INCR]); + let a: RedisResult = exec!(cmd, self.get_connection().await); + + let mut cmd = redis::cmd("COMMAND"); + cmd.arg(&["INFO", GET]); + let a: RedisResult = exec!(cmd, self.get_connection().await); + + unimplemented!("Have to check return types of INFO command") + } +} + +impl MasterTrait for Master {} + +impl Actor for Master { + type Context = Context; +} + +impl Handler for Master { + type Result = MessageResult; + + fn handle(&mut self, m: AddVisitor, ctx: &mut Self::Context) -> Self::Result { + let fut = async { + self.add_visitor(&m.0).await; + }; + unimplemented!(); + } +} + +impl Handler for Master { + type Result = (); + + fn handle(&mut self, m: AddSite, _ctx: &mut Self::Context) -> Self::Result { + unimplemented!(); + } +} + +//#[derive(Clone)] +//pub struct Cluster(pub ClusterClient); +//#[derive(Clone)] +//pub struct Single(pub Client); +// +//pub trait Redis: 'static + Unpin + Clone { +// type Result; +// fn get_connection(&'static self) -> Self::Result; +//} +//impl Redis for Cluster { +// type Result = RedisResult; +// fn get_connection(&self) -> Self::Result { +// self.0.get_connection() +// } +//} +// +//impl Redis for Single { +// type Result = impl Future; +// fn get_connection(&'static self) -> Self::Result { +// self.0.get_async_connection() +// } +//} + +//#[derive(Clone)] +//pub struct Master { +// pub redis: usize, +//} + +//impl MasterTrait for Master {} +// +//impl Actor for Master { +// type Context = Context; +//} +// +//impl Handler for Master { +// type Result = MessageResult; +// +// fn handle(&mut self, m: AddVisitor, ctx: &mut Self::Context) -> Self::Result { +// let fut = async { +// let test = "1"; +// }; +// unimplemented!(); +// } +//} +// +//impl Handler for Master { +// type Result = (); +// +// fn handle(&mut self, m: AddSite, _ctx: &mut Self::Context) -> Self::Result { +// unimplemented!(); +// } +//} + +//pub struct Master { +// pub redis: T, +//} +// +//impl MasterTrait for Master {} +// +//impl Actor for Master { +// type Context = Context; +//} +// +//impl Handler for Master { +// type Result = MessageResult; +// +// fn handle(&mut self, m: AddVisitor, ctx: &mut Self::Context) -> Self::Result { +// let fut = async { +// self.redis.get_connection(); +// let test = "1"; +// }; +// unimplemented!(); +// } +//} +// +//impl Handler for Master { +// type Result = (); +// +// fn handle(&mut self, m: AddSite, _ctx: &mut Self::Context) -> Self::Result { +// unimplemented!(); +// } +//} diff --git a/src/mcaptcha.rs b/src/mcaptcha.rs new file mode 100644 index 0000000..d166393 --- /dev/null +++ b/src/mcaptcha.rs @@ -0,0 +1,118 @@ +/* + * mCaptcha - A proof of work based DoS protection system + * Copyright © 2021 Aravinth Manivannan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +use serde::{Deserialize, Serialize}; + +use crate::defense::Defense; +use crate::errors::*; + +/// Builder for [MCaptcha] +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct MCaptchaBuilder { + visitor_threshold: u32, + defense: Option, + duration: Option, +} + +impl Default for MCaptchaBuilder { + fn default() -> Self { + MCaptchaBuilder { + visitor_threshold: 0, + defense: None, + duration: None, + } + } +} + +impl MCaptchaBuilder { + /// set defense + pub fn defense(&mut self, d: Defense) -> &mut Self { + self.defense = Some(d); + self + } + + /// set duration + pub fn duration(&mut self, d: u64) -> &mut Self { + self.duration = Some(d); + self + } + + /// Builds new [MCaptcha] + pub fn build(self: &mut MCaptchaBuilder) -> CaptchaResult { + if self.duration.is_none() { + Err(CaptchaError::PleaseSetValue("duration".into())) + } else if self.defense.is_none() { + Err(CaptchaError::PleaseSetValue("defense".into())) + } else if self.duration <= Some(0) { + Err(CaptchaError::CaptchaDurationZero) + } else { + let m = MCaptcha { + duration: self.duration.unwrap(), + defense: self.defense.clone().unwrap(), + visitor_threshold: self.visitor_threshold, + }; + Ok(m) + } + } +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct MCaptcha { + visitor_threshold: u32, + defense: Defense, + duration: u64, +} + +impl MCaptcha { + /// increments the visitor count by one + #[inline] + pub fn add_visitor(&mut self) { + self.visitor_threshold += 1; + if self.visitor_threshold > self.defense.visitor_threshold() { + self.defense.tighten_up(); + } else { + self.defense.loosen_up(); + } + } + + /// decrements the visitor count by one + #[inline] + pub fn decrement_visitor(&mut self) { + if self.visitor_threshold > 0 { + self.visitor_threshold -= 1; + } + } + + /// get current difficulty factor + #[inline] + pub fn get_difficulty(&self) -> u32 { + self.defense.get_difficulty() + } + + /// get [Counter]'s lifetime + #[inline] + pub fn get_duration(&self) -> u64 { + self.duration + } + + /// get [Counter]'s current visitor_threshold + #[inline] + pub fn get_visitors(&self) -> u32 { + self.visitor_threshold + } +}