mirror of
https://codeberg.org/YGGverse/psocks.git
synced 2026-03-31 16:35:28 +00:00
remove cache feature
This commit is contained in:
parent
c821fa7492
commit
bbf7e73f11
5 changed files with 17 additions and 158 deletions
|
|
@ -18,15 +18,13 @@ Filtering asynchronous SOCKS5 (TCP/UDP) proxy server based on [fast-socks5](http
|
||||||
``` bash
|
``` bash
|
||||||
RUST_LOG=trace cargo run -- --allow=http://localhost/allow.txt \
|
RUST_LOG=trace cargo run -- --allow=http://localhost/allow.txt \
|
||||||
--allow=/path/to/allow.txt \
|
--allow=/path/to/allow.txt \
|
||||||
--cache=/path/to/cache.txt \
|
|
||||||
no-auth
|
no-auth
|
||||||
```
|
```
|
||||||
* set `socks5://127.0.0.1:1080` proxy in your application
|
* set `socks5://127.0.0.1:1080` proxy in your application
|
||||||
* use http://127.0.0.1:8010 for API:
|
* use http://127.0.0.1:8010 for API:
|
||||||
* `/api/allow/{domain.com}` - add rule to 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 (and `--cache` if defined)
|
* `/api/block/{domain.com}` - delete rule from the current session
|
||||||
* `/api/rules` - return active rules (from server memory)
|
* `/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
|
### Allow list example
|
||||||
|
|
||||||
|
|
@ -69,8 +67,7 @@ Group=psocks
|
||||||
|
|
||||||
WorkingDirectory=/var/lib/psocks
|
WorkingDirectory=/var/lib/psocks
|
||||||
ExecStart=/usr/local/bin/psocks \
|
ExecStart=/usr/local/bin/psocks \
|
||||||
-a=https://codeberg.org/postscriptum/psocks-list/raw/branch/main/allow/internet.txt \
|
-a=http://localhost/allow.txt \
|
||||||
-c=/var/lib/psocks/cache.txt \
|
|
||||||
no-auth
|
no-auth
|
||||||
|
|
||||||
Restart=always
|
Restart=always
|
||||||
|
|
|
||||||
25
src/main.rs
25
src/main.rs
|
|
@ -63,27 +63,13 @@ async fn api_rules(rules: &State<Arc<Rules>>) -> Result<Json<Vec<String>>, Statu
|
||||||
Ok(Json(result))
|
Ok(Json(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rocket::get("/api/cache/clean")]
|
|
||||||
async fn api_cache_clean(rules: &State<Arc<Rules>>) -> Result<Json<Option<Vec<String>>>, 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]
|
#[rocket::launch]
|
||||||
async fn rocket() -> _ {
|
async fn rocket() -> _ {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
let opt: &'static Opt = Box::leak(Box::new(Opt::from_args()));
|
let opt: &'static Opt = Box::leak(Box::new(Opt::from_args()));
|
||||||
|
|
||||||
let rules = Arc::new(
|
let rules = Arc::new(Rules::from_opt(&opt.allow_list).await.unwrap());
|
||||||
Rules::from_opt(&opt.allow_list, opt.cache.clone())
|
|
||||||
.await
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let totals = Arc::new(Total::with_rules(rules.rules().await));
|
let totals = Arc::new(Total::with_rules(rules.rules().await));
|
||||||
|
|
||||||
|
|
@ -108,14 +94,7 @@ async fn rocket() -> _ {
|
||||||
.manage(Instant::now())
|
.manage(Instant::now())
|
||||||
.mount(
|
.mount(
|
||||||
"/",
|
"/",
|
||||||
rocket::routes![
|
rocket::routes![index, api_totals, api_allow, api_block, api_rules],
|
||||||
index,
|
|
||||||
api_totals,
|
|
||||||
api_allow,
|
|
||||||
api_block,
|
|
||||||
api_rules,
|
|
||||||
api_cache_clean
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
/// # How to use it:
|
/// # How to use it:
|
||||||
|
|
@ -50,10 +50,6 @@ pub struct Opt {
|
||||||
/// * remote URL
|
/// * remote URL
|
||||||
#[structopt(short = "a", long)]
|
#[structopt(short = "a", long)]
|
||||||
pub allow_list: Vec<String>,
|
pub allow_list: Vec<String>,
|
||||||
|
|
||||||
/// FS cache to persist JSON/API changes between server sessions
|
|
||||||
#[structopt(short = "c", long)]
|
|
||||||
pub cache: Option<PathBuf>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Choose the authentication type
|
/// Choose the authentication type
|
||||||
|
|
|
||||||
51
src/rules.rs
51
src/rules.rs
|
|
@ -1,23 +1,17 @@
|
||||||
mod cache;
|
|
||||||
mod item;
|
mod item;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use cache::Cache;
|
|
||||||
use item::Item;
|
use item::Item;
|
||||||
use log::*;
|
use log::*;
|
||||||
use std::{collections::HashSet, path::PathBuf};
|
use std::collections::HashSet;
|
||||||
use tokio::{fs, sync::RwLock};
|
use tokio::{fs, sync::RwLock};
|
||||||
|
|
||||||
pub struct Rules {
|
/// In-memory registry, based on `--allow-list`
|
||||||
/// In-memory registry, based on `--allow-list` + `--cache`
|
pub struct Rules(RwLock<HashSet<Item>>);
|
||||||
index: RwLock<HashSet<Item>>,
|
|
||||||
/// FS cache for JSON/API changes, based on `--cache` value
|
|
||||||
cache: Cache,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Rules {
|
impl Rules {
|
||||||
/// Build new List object
|
/// Build new List object
|
||||||
pub async fn from_opt(list: &Vec<String>, cache: Option<PathBuf>) -> Result<Self> {
|
pub async fn from_opt(list: &Vec<String>) -> Result<Self> {
|
||||||
fn handle(this: &mut HashSet<Item>, line: &str) -> Option<bool> {
|
fn handle(this: &mut HashSet<Item>, line: &str) -> Option<bool> {
|
||||||
if line.starts_with("/") || line.starts_with("#") || line.is_empty() {
|
if line.starts_with("/") || line.starts_with("#") || line.is_empty() {
|
||||||
return None;
|
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();
|
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 {
|
Ok(Self(RwLock::new(index)))
|
||||||
index: RwLock::new(index),
|
|
||||||
cache,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
/// Check if rule is exist in the (allow) index
|
/// Check if rule is exist in the (allow) index
|
||||||
pub async fn any(&self, value: &str) -> bool {
|
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::Exact(v) => v == value,
|
||||||
Item::Ending(v) => value.ends_with(v),
|
Item::Ending(v) => value.ends_with(v),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/// Get total rules from the current session
|
/// Get total rules from the current session
|
||||||
pub async fn rules(&self) -> u64 {
|
pub async fn rules(&self) -> u64 {
|
||||||
self.index.read().await.len() as u64
|
self.0.read().await.len() as u64
|
||||||
}
|
}
|
||||||
/// Allow given `rule`
|
/// Allow given `rule`
|
||||||
/// * return `false` if the `rule` is exist
|
/// * return `false` if the `rule` is exist
|
||||||
pub async fn allow(&self, rule: &str) -> Result<bool> {
|
pub async fn allow(&self, rule: &str) -> Result<bool> {
|
||||||
self.cache.allow(rule).await?;
|
Ok(self.0.write().await.insert(Item::from_line(rule)))
|
||||||
Ok(self.index.write().await.insert(Item::from_line(rule)))
|
|
||||||
}
|
}
|
||||||
/// Block given `rule`
|
/// Block given `rule`
|
||||||
/// * return `false` if the `rule` is not exist
|
/// * return `false` if the `rule` is not exist
|
||||||
pub async fn block(&self, rule: &str) -> Result<bool> {
|
pub async fn block(&self, rule: &str) -> Result<bool> {
|
||||||
self.cache.block(rule).await?;
|
Ok(self.0.write().await.remove(&Item::from_line(rule)))
|
||||||
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<Option<Vec<String>>> {
|
|
||||||
self.cache.clean().await
|
|
||||||
}
|
}
|
||||||
/// Return active rules (from server memory)
|
/// Return active rules (from server memory)
|
||||||
pub async fn list(&self) -> Vec<String> {
|
pub async fn list(&self) -> Vec<String> {
|
||||||
let mut list: Vec<String> = self
|
let mut list: Vec<String> = self
|
||||||
.index
|
.0
|
||||||
.read()
|
.read()
|
||||||
.await
|
.await
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
use anyhow::{Result, bail};
|
|
||||||
use std::{collections::HashSet, path::PathBuf};
|
|
||||||
use tokio::fs;
|
|
||||||
|
|
||||||
pub struct Cache(Option<PathBuf>);
|
|
||||||
|
|
||||||
impl Cache {
|
|
||||||
pub async fn from_path(path: Option<PathBuf>) -> Result<Self> {
|
|
||||||
Ok(Self(match path {
|
|
||||||
Some(p) => {
|
|
||||||
init_file(&p).await?;
|
|
||||||
Some(p)
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
pub async fn read(&self) -> Result<Option<String>> {
|
|
||||||
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::<Vec<_>>().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::<Vec<_>>().join("\n")).await?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
/// Clean `--cache` file collected, return deleted rules
|
|
||||||
pub async fn clean(&self) -> Result<Option<Vec<String>>> {
|
|
||||||
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(())
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue