Skip to content

Commit

Permalink
upd: pre-release
Browse files Browse the repository at this point in the history
  • Loading branch information
jerrita committed Apr 12, 2024
1 parent ce0d51f commit 5d8ea3d
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 102 deletions.
47 changes: 44 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,54 @@
# PeerBan

> WIP
零配置全自动全平台吸血客户端封禁器,现处于早期开发阶段,欢迎 PR。

全自动吸血客户端封禁器,开发中。
```shell
Usage: peerban [OPTIONS]

Options:
-b, --backend <BACKEND> [default: qb]
-e, --endpoint <ENDPOINT> [default: http://127.0.0.1:8080]
-a, --auth <AUTH> [default: admin:admin]
-s, --scan <SCAN> Scan interval in seconds. [default: 5]
-c, --clear Clear all bans before start.
-h, --help Print help
```

![SnapShot](./res/snapshot.png)

## Features

- 零配置,只需通过命令行传递连接参数
- 高性能,一般内存占用小于 3 MB
- 启发式封锁,除预置规则外,会自动封禁 `假进度上报/超量下载` 客户端
- 支持多种后端,目前支持 `qBittorrent`
- 永封,你也可以在启动时加入 `-c` 参数解封全部重新封

## Installation

```shell
cargo install --git https://github.com/jerrita/peerban
```

> Docker & Binary is WIP
## Backend Supports

- [x] qBittorrent

## Contributes

> 欢迎 PR,如果你有任何问题或建议,欢迎提出 Issue。
- 后端适配参考 `backend/qb.rs`,你只需要实现 `Backend` trait 中的四个函数即可。
- 新增内置规则参考 `rules/preload.rs`,新增一行即可。

## RoadMap

- [x] ProtoType
- [ ] Persistence
- [ ] Container
- [ ] WebUI
- [ ] Rule Hot-Update

## Similar Projects

Expand Down
Binary file added res/snapshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ pub trait Backend {
async fn describe(&mut self) -> Result<String>;
async fn get_uploading_torrents(&self) -> Result<Vec<Torrent>>;
async fn get_peers(&self, hash: &str) -> Result<Vec<Peer>>;
async fn ban_clear(&self) -> Result<()>;
async fn ban_peer(&self, peer: &Peer) -> Result<()>;
}
12 changes: 12 additions & 0 deletions src/backend/qb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,16 @@ impl Backend for QBitBackend {
.await?;
Ok(())
}

async fn ban_clear(&self) -> Result<()> {
let mut form = HashMap::new();
form.insert("json", serde_json::json!({"banned_IPs": ""}).to_string());
reqwest::Client::new()
.post(&format!("{}/app/setPreferences", self.endpoint))
.header("Cookie", self.cookie.clone().unwrap())
.form(&form)
.send()
.await?;
Ok(())
}
}
25 changes: 0 additions & 25 deletions src/conf.rs

This file was deleted.

50 changes: 14 additions & 36 deletions src/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,57 +4,48 @@ use anyhow::Result;
use log::{debug, info, warn};

use crate::backend::Backend;
use crate::conf::PeerBanConfig;
use crate::peer::BannedPeer;
use crate::rules::{Rule, RuleType};
use crate::rules::preload::PREDEFINED_RULES;
use crate::rules::Rule;

struct Statistic {
pub torrents: u64,
pub peers: u64,
pub banned: u64,
pub released: u64,
}

pub struct Daemon {
backend: Box<dyn Backend>,
conf: PeerBanConfig,
banned: Vec<BannedPeer>,
rules: Vec<Rule>,
scan_time: u64,
clear: bool,
}

impl Daemon {
pub fn new(backend: Box<dyn Backend>, conf: PeerBanConfig) -> Self {
let mut rules = PREDEFINED_RULES.clone();
if conf.block_progress_fallback {
rules.push(Rule {
class: RuleType::ProgressProbe,
value: conf.block_progress_fallback_threshold.into(),
});
}
if conf.block_excessive_clients {
rules.push(Rule {
class: RuleType::ExcessiveProbe,
value: conf.block_excessive_clients_threshold.into(),
});
}
pub fn new(backend: Box<dyn Backend>, scan: u64, clear: bool) -> Self {
let rules = PREDEFINED_RULES.clone();
Daemon {
backend,
conf,
banned: Vec::new(),
rules,
scan_time: scan,
clear,
}
}

pub async fn run(&mut self) -> Result<()> {
info!("Backend: {}", self.backend.describe().await?);
info!("[interval] scan: {}s, block: {}s", self.conf.scan_time, self.conf.block_time);
info!("[interval] scan: {}s", self.scan_time);
let mut stat = Statistic {
torrents: 0,
peers: 0,
banned: 0,
released: 0,
};
if self.clear {
self.backend.ban_clear().await?;
info!("[start] jail cleared.");
}
loop {
let mut flag = false;
let torrents = self.backend.get_uploading_torrents().await?;
Expand Down Expand Up @@ -87,24 +78,11 @@ impl Daemon {
}
}
}

// Remove expired banned peers
let now = Instant::now();
self.banned.retain(|banned| {
if now.duration_since(banned.time).as_secs() > self.conf.block_time {
flag = true;
stat.released += 1;
info!("Released {}({}) {:?}.", banned.peer.address, banned.peer.id, banned.rule);
false
} else {
true
}
});
}
if flag {
info!("[active] torrents: {}, peers: {}, banned: {}, released: {}", stat.torrents, stat.peers, stat.banned, stat.released);
info!("[active] torrents: {}, peers: {}, banned: {}", stat.torrents, stat.peers, stat.banned);
}
tokio::time::sleep(tokio::time::Duration::from_secs(self.conf.scan_time)).await;
tokio::time::sleep(tokio::time::Duration::from_secs(self.scan_time)).await;
}
}
}
8 changes: 5 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ mod backend;
mod torrent;
mod peer;
mod rules;
mod conf;
mod daemon;

#[derive(Parser, Debug)]
Expand All @@ -19,6 +18,10 @@ struct Args {
endpoint: String,
#[arg(short, long, default_value = "admin:admin")]
auth: String,
#[arg(short, long, default_value = "5", help = "Scan interval in seconds.")]
scan: u64,
#[arg(short, long, default_value = "false", help = "Clear all bans before start.")]
clear: bool,
}

#[tokio::main]
Expand All @@ -34,8 +37,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
}

let qb = QBitBackend::new(args.endpoint, args.auth);
let conf = conf::PeerBanConfig::default();
let mut daemon = Daemon::new(Box::new(qb), conf);
let mut daemon = Daemon::new(Box::new(qb), args.scan, args.clear);
loop {
match daemon.run().await {
Ok(_) => (),
Expand Down
10 changes: 7 additions & 3 deletions src/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::peer::Peer;

pub mod preload;

#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
pub enum RuleType {
IDPrefixMatch,
IDContains,
Expand Down Expand Up @@ -57,9 +57,13 @@ impl From<&str> for Rule {
if split.clone().count() != 2 {
panic!("Invalid rule string, use class@value format.");
}
let class = split.next().unwrap().into();
Rule {
class: split.next().unwrap().into(),
value: split.next().unwrap().into(),
class,
value: match class {
RuleType::ProgressProbe | RuleType::ExcessiveProbe => Value::Number(split.next().unwrap().parse().unwrap()),
_ => Value::String(split.next().unwrap().to_string()),
},
}
}
}
67 changes: 35 additions & 32 deletions src/rules/preload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,41 @@ use crate::rules::Rule;

lazy_static!(
pub static ref PREDEFINED_RULES: Vec<Rule> = vec![
"idStartsWith@-XL",
"idStartsWith@-SD",
"idStartsWith@-XF",
"idStartsWith@-QD",
"idStartsWith@-BN",
"idStartsWith@-DL",
"idStartsWith@-TS",
"idStartsWith@-FG",
"idStartsWith@-TT",
"idStartsWith@-NX",
"idStartsWith@-SP",
"idStartsWith@-GT0002",
"idStartsWith@-GT0003",
"idStartsWith@-DT",
"idStartsWith@-HP",
"idContains@cacao",
"idStartsWith@-XL",
"idStartsWith@-SD",
"idStartsWith@-XF",
"idStartsWith@-QD",
"idStartsWith@-BN",
"idStartsWith@-DL",
"idStartsWith@-TS",
"idStartsWith@-FG",
"idStartsWith@-TT",
"idStartsWith@-NX",
"idStartsWith@-SP",
"idStartsWith@-GT0002",
"idStartsWith@-GT0003",
"idStartsWith@-DT",
"idStartsWith@-HP",
"idContains@cacao",

"nameStartsWith@-XL",
"nameContains@Xunlei",
"nameStartsWith@TaiPei-Torrent",
"nameStartsWith@Xfplay",
"nameStartsWith@BitSpirit",
"nameContains@FlashGet",
"nameContains@TuDou",
"nameContains@TorrentStorm",
"nameContains@QQDownload",
"[email protected]/anacrolix/torrent",
"nameStartsWith@qBittorrent/3.3.15",
"nameStartsWith@dt/torrent",
"nameStartsWith@hp/torrent",
"nameStartsWith@DT",
"[email protected]",
"[email protected]/thank423/trafficConsume",
"nameStartsWith@-XL",
"nameContains@Xunlei",
"nameStartsWith@TaiPei-Torrent",
"nameStartsWith@Xfplay",
"nameStartsWith@BitSpirit",
"nameContains@FlashGet",
"nameContains@TuDou",
"nameContains@TorrentStorm",
"nameContains@QQDownload",
"[email protected]/anacrolix/torrent",
"nameStartsWith@qBittorrent/3.3.15",
"nameStartsWith@dt/torrent",
"nameStartsWith@hp/torrent",
"nameStartsWith@DT",
"[email protected]",
"[email protected]/thank423/trafficConsume",

"[email protected]", // 进度倒退检测
"[email protected]", // 超量下载检测
].iter().map(|&s| Rule::from(s)).collect();
);

0 comments on commit 5d8ea3d

Please sign in to comment.