diff --git a/README.md b/README.md index 8ac0944..ed7c7dc 100644 --- a/README.md +++ b/README.md @@ -18,15 +18,13 @@ Filtering asynchronous SOCKS5 (TCP/UDP) proxy server based on [fast-socks5](http ``` bash RUST_LOG=trace cargo run -- --allow=http://localhost/allow.txt \ --allow=/path/to/allow.txt \ - --cache=/path/to/cache.txt \ no-auth ``` * set `socks5://127.0.0.1:1080` proxy in your application * use http://127.0.0.1:8010 for API: - * `/api/allow/{domain.com}` - add rule to the current session (and `--cache` if defined) - * `/api/block/{domain.com}` - delete rule from the current session (and `--cache` if defined) + * `/api/allow/{domain.com}` - add rule to the current session + * `/api/block/{domain.com}` - delete rule from the current session * `/api/rules` - return active rules (from server memory) - * `/api/cache/clean` - clean the `--cache` file (returns deleted rules or `null` if not enabled) ### Allow list example @@ -69,8 +67,7 @@ Group=psocks WorkingDirectory=/var/lib/psocks ExecStart=/usr/local/bin/psocks \ - -a=https://codeberg.org/postscriptum/psocks-list/raw/branch/main/allow/internet.txt \ - -c=/var/lib/psocks/cache.txt \ + -a=http://localhost/allow.txt \ no-auth Restart=always diff --git a/src/main.rs b/src/main.rs index 041784c..bb756f2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -63,27 +63,13 @@ async fn api_rules(rules: &State>) -> Result>, Statu Ok(Json(result)) } -#[rocket::get("/api/cache/clean")] -async fn api_cache_clean(rules: &State>) -> Result>>, Status> { - let result = rules.cache_clean().await; - info!("Clean cache file: {result:?}"); - Ok(Json(result.map_err(|e| { - error!("Could not handle cache clean request: `{e}`"); - Status::InternalServerError - })?)) -} - #[rocket::launch] async fn rocket() -> _ { env_logger::init(); let opt: &'static Opt = Box::leak(Box::new(Opt::from_args())); - let rules = Arc::new( - Rules::from_opt(&opt.allow_list, opt.cache.clone()) - .await - .unwrap(), - ); + let rules = Arc::new(Rules::from_opt(&opt.allow_list).await.unwrap()); let totals = Arc::new(Total::with_rules(rules.rules().await)); @@ -108,14 +94,7 @@ async fn rocket() -> _ { .manage(Instant::now()) .mount( "/", - rocket::routes![ - index, - api_totals, - api_allow, - api_block, - api_rules, - api_cache_clean - ], + rocket::routes![index, api_totals, api_allow, api_block, api_rules], ) } diff --git a/src/opt.rs b/src/opt.rs index 8f70781..5a26c13 100644 --- a/src/opt.rs +++ b/src/opt.rs @@ -1,4 +1,4 @@ -use std::{net::SocketAddr, num::ParseFloatError, path::PathBuf, time::Duration}; +use std::{net::SocketAddr, num::ParseFloatError, time::Duration}; use structopt::StructOpt; /// # How to use it: @@ -50,10 +50,6 @@ pub struct Opt { /// * remote URL #[structopt(short = "a", long)] pub allow_list: Vec, - - /// FS cache to persist JSON/API changes between server sessions - #[structopt(short = "c", long)] - pub cache: Option, } /// Choose the authentication type diff --git a/src/rules.rs b/src/rules.rs index 468844f..bc23499 100644 --- a/src/rules.rs +++ b/src/rules.rs @@ -1,23 +1,17 @@ -mod cache; mod item; use anyhow::Result; -use cache::Cache; use item::Item; use log::*; -use std::{collections::HashSet, path::PathBuf}; +use std::collections::HashSet; use tokio::{fs, sync::RwLock}; -pub struct Rules { - /// In-memory registry, based on `--allow-list` + `--cache` - index: RwLock>, - /// FS cache for JSON/API changes, based on `--cache` value - cache: Cache, -} +/// In-memory registry, based on `--allow-list` +pub struct Rules(RwLock>); impl Rules { /// Build new List object - pub async fn from_opt(list: &Vec, cache: Option) -> Result { + pub async fn from_opt(list: &Vec) -> Result { fn handle(this: &mut HashSet, line: &str) -> Option { if line.starts_with("/") || line.starts_with("#") || line.is_empty() { return None; @@ -51,59 +45,36 @@ impl Rules { } } - let cache = Cache::from_path(cache).await?; - - let cache_rules_total = cache.read().await?.map(|data| { - let mut t = 0; - for line in data.lines() { - if handle(&mut index, line).is_some_and(|status| !status) { - warn!("Cache file contains duplicated entry: `{line}`") - } - t += 1 - } - t - }); - let len = index.len(); - info!("Total rules parsed: {len} (riles: {rules_total} / cache: {cache_rules_total:?})",); + info!("Total rules parsed: {len} (added: {rules_total})",); - Ok(Self { - index: RwLock::new(index), - cache, - }) + Ok(Self(RwLock::new(index))) } /// Check if rule is exist in the (allow) index pub async fn any(&self, value: &str) -> bool { - self.index.read().await.iter().any(|item| match item { + self.0.read().await.iter().any(|item| match item { Item::Exact(v) => v == value, Item::Ending(v) => value.ends_with(v), }) } /// Get total rules from the current session pub async fn rules(&self) -> u64 { - self.index.read().await.len() as u64 + self.0.read().await.len() as u64 } /// Allow given `rule` /// * return `false` if the `rule` is exist pub async fn allow(&self, rule: &str) -> Result { - self.cache.allow(rule).await?; - Ok(self.index.write().await.insert(Item::from_line(rule))) + Ok(self.0.write().await.insert(Item::from_line(rule))) } /// Block given `rule` /// * return `false` if the `rule` is not exist pub async fn block(&self, rule: &str) -> Result { - self.cache.block(rule).await?; - Ok(self.index.write().await.remove(&Item::from_line(rule))) - } - /// Privately clean `--cache` file collected, return deleted rules - /// * we can implement `self.index` update at this step @TODO - pub async fn cache_clean(&self) -> Result>> { - self.cache.clean().await + Ok(self.0.write().await.remove(&Item::from_line(rule))) } /// Return active rules (from server memory) pub async fn list(&self) -> Vec { let mut list: Vec = self - .index + .0 .read() .await .iter() diff --git a/src/rules/cache.rs b/src/rules/cache.rs deleted file mode 100644 index b0dc884..0000000 --- a/src/rules/cache.rs +++ /dev/null @@ -1,84 +0,0 @@ -use anyhow::{Result, bail}; -use std::{collections::HashSet, path::PathBuf}; -use tokio::fs; - -pub struct Cache(Option); - -impl Cache { - pub async fn from_path(path: Option) -> Result { - Ok(Self(match path { - Some(p) => { - init_file(&p).await?; - Some(p) - } - None => None, - })) - } - pub async fn read(&self) -> Result> { - Ok(if let Some(ref p) = self.0 { - init_file(p).await?; - Some(fs::read_to_string(p).await?) - } else { - None - }) - } - pub async fn allow(&self, rule: &str) -> Result<()> { - if let Some(ref p) = self.0 { - init_file(p).await?; - let mut rules = HashSet::new(); - let lines = fs::read_to_string(p).await?; - for line in lines.lines() { - rules.insert(line); - } - rules.insert(rule); - fs::write(p, rules.into_iter().collect::>().join("\n")).await?; - } - Ok(()) - } - pub async fn block(&self, rule: &str) -> Result<()> { - if let Some(ref p) = self.0 { - init_file(p).await?; - let mut rules = HashSet::new(); - let lines = fs::read_to_string(p).await?; - for line in lines.lines() { - if line != rule { - rules.insert(line); - } - } - fs::write(p, rules.into_iter().collect::>().join("\n")).await?; - } - Ok(()) - } - /// Clean `--cache` file collected, return deleted rules - pub async fn clean(&self) -> Result>> { - match self.0 { - Some(ref p) => { - init_file(p).await?; - let mut rules = Vec::new(); - let lines = fs::read_to_string(p).await?; - for line in lines.lines() { - rules.push(line.into()); - } - fs::write(p, "").await?; - Ok(Some(rules)) - } - None => Ok(None), - } - } -} - -/// Make sure that cache file is always exist (e.g. user may remove it when the daemon is running) -async fn init_file(path: &PathBuf) -> Result<()> { - if path.exists() { - if path.is_file() { - return Ok(()); - } else { - bail!( - "Cache path `{}` exist but it is not a file!", - path.to_string_lossy() - ) - } - } - fs::write(path, "").await?; - Ok(()) -}