fucking scanners

This commit is contained in:
Skye 2025-05-25 15:46:53 +09:00
parent 998b210463
commit bf24b4ecaa
Signed by: me
GPG key ID: 0104BC05F41B77B8
8 changed files with 8055 additions and 224 deletions

172
Cargo.lock generated
View file

@ -26,6 +26,12 @@ dependencies = [
"memchr",
]
[[package]]
name = "allocator-api2"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "async-trait"
version = "0.1.86"
@ -190,6 +196,26 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "dashmap"
version = "6.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
dependencies = [
"cfg-if",
"crossbeam-utils",
"hashbrown 0.14.5",
"lock_api",
"once_cell",
"parking_lot_core",
]
[[package]]
name = "displaydoc"
version = "0.2.5"
@ -214,6 +240,12 @@ dependencies = [
"termcolor",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "eyre"
version = "0.6.12"
@ -230,6 +262,12 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "form_urlencoded"
version = "1.2.1"
@ -254,12 +292,24 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-sink"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
[[package]]
name = "futures-task"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-timer"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
[[package]]
name = "futures-util"
version = "0.3.31"
@ -267,9 +317,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-core",
"futures-sink",
"futures-task",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
@ -292,8 +344,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi 0.13.3+wasi-0.2.2",
"wasm-bindgen",
"windows-targets",
]
@ -303,6 +357,46 @@ version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "governor"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cbe789d04bf14543f03c4b60cd494148aa79438c8440ae7d81a7778147745c3"
dependencies = [
"cfg-if",
"dashmap",
"futures-sink",
"futures-timer",
"futures-util",
"getrandom 0.3.1",
"hashbrown 0.15.3",
"nonzero_ext",
"parking_lot",
"portable-atomic",
"quanta",
"rand 0.9.0",
"smallvec",
"spinning_top",
"web-time",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "hashbrown"
version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash",
]
[[package]]
name = "hermit-abi"
version = "0.4.0"
@ -630,6 +724,12 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "nonzero_ext"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21"
[[package]]
name = "num-bigint"
version = "0.4.6"
@ -740,6 +840,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "portable-atomic"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
[[package]]
name = "ppv-lite86"
version = "0.2.20"
@ -758,6 +864,21 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "quanta"
version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e"
dependencies = [
"crossbeam-utils",
"libc",
"once_cell",
"raw-cpuid",
"wasi 0.11.0+wasi-snapshot-preview1",
"web-sys",
"winapi",
]
[[package]]
name = "quiclime"
version = "0.1.0"
@ -765,6 +886,7 @@ dependencies = [
"axum",
"env_logger",
"eyre",
"governor",
"idna",
"log",
"parking_lot",
@ -900,6 +1022,15 @@ dependencies = [
"zerocopy 0.8.20",
]
[[package]]
name = "raw-cpuid"
version = "11.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146"
dependencies = [
"bitflags 2.8.0",
]
[[package]]
name = "redox_syscall"
version = "0.5.9"
@ -1192,6 +1323,15 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "spinning_top"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300"
dependencies = [
"lock_api",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
@ -1502,6 +1642,16 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "web-sys"
version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "web-time"
version = "1.1.0"
@ -1521,6 +1671,22 @@ dependencies = [
"rustls-pki-types",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.9"
@ -1530,6 +1696,12 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.52.0"

View file

@ -10,6 +10,7 @@ license = "MIT OR Apache-2.0"
axum = "0.6.18"
env_logger = "0.10.0"
eyre = "0.6.12"
governor = "0.10.0"
idna = "1.0.3"
log = "0.4.19"
parking_lot = "0.12.3"

View file

@ -0,0 +1,3 @@
{
"text": "You are trying to connect too fast. Please wait a minute before retrying."
}

View file

@ -9,14 +9,14 @@ use axum::{
routing::{get, post},
};
use eyre::{eyre, Context};
use log::{error, info};
use log::{error, info, warn};
use netty::{Handshake, ReadError};
use quinn::{
crypto::rustls::QuicServerConfig,
rustls::pki_types::{CertificateDer, PrivateKeyDer},
ConnectionError, Endpoint, Incoming, ServerConfig, TransportConfig,
};
use routing::RoutingTable;
use routing::{RoutingError, RoutingTable};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::TcpStream,
@ -233,9 +233,17 @@ async fn try_handle_minecraft(
let Some(address) = handshake.normalized_address() else {
return politely_disconnect(connection, handshake).await;
};
let Some((mut send_host, mut recv_host)) = routing_table.route(&address).await else {
return politely_disconnect(connection, handshake).await;
};
let (mut send_host, mut recv_host) =
match routing_table.route_limited(&address, peer.ip()).await {
Ok(val) => val,
Err(RoutingError::InvalidDomain) => {
return politely_disconnect(connection, handshake).await;
}
Err(RoutingError::RateLimited) => {
warn!("Connection from {} has been rate limited!", peer);
return impolitely_disconnect(connection, handshake).await;
}
};
handshake.send(&mut send_host).await?;
let (mut recv_client, mut send_client) = connection.split();
tokio::select! {
@ -294,6 +302,53 @@ async fn politely_disconnect(mut connection: TcpStream, handshake: Handshake) ->
Ok(())
}
async fn impolitely_disconnect(
mut connection: TcpStream,
handshake: Handshake,
) -> eyre::Result<()> {
match handshake.next_state {
netty::HandshakeType::Status => {
let packet = netty::read_packet(&mut connection, 1).await?;
let mut packet = packet.as_slice();
let id = packet.read_varint()?;
if id != 0 {
return Err(eyre!(
"Packet isn't a Status Request(0x00), but {:#04x}",
id
));
}
let mut buf = vec![];
buf.write_varint(0).await?;
buf.write_string(include_str!("./serverlistping_response_rate.json"))
.await?;
connection.write_varint(buf.len() as i32).await?;
connection.write_all(&buf).await?;
let packet = netty::read_packet(&mut connection, 9).await?;
let mut packet = packet.as_slice();
let id = packet.read_varint()?;
if id != 1 {
return Err(eyre!("Packet isn't a Ping Request(0x01), but {:#04x}", id));
}
let payload = packet.read_long()?;
let mut buf = Vec::with_capacity(1 + 8);
buf.write_varint(1).await?;
buf.write_u64(payload).await?;
connection.write_varint(buf.len() as i32).await?;
connection.write_all(&buf).await?;
}
netty::HandshakeType::Login => {
let _ = netty::read_packet(&mut connection, 128).await?;
let mut buf = vec![];
buf.write_varint(0).await?;
buf.write_string(include_str!("./disconnect_response_rate.json"))
.await?;
connection.write_varint(buf.len() as i32).await?;
connection.write_all(&buf).await?;
}
}
Ok(())
}
async fn handle_minecraft(connection: TcpStream, routing_table: &'static RoutingTable) {
if let Err(e) = try_handle_minecraft(connection, routing_table).await {
error!("Error handling Minecraft connection: {:#}", e);

View file

@ -87,7 +87,10 @@ pub trait ReadExt: Read {
// }
}
pub async fn read_packet(mut reader: impl AsyncReadExt + Unpin, max_size: usize) -> Result<Vec<u8>, ReadError> {
pub async fn read_packet(
mut reader: impl AsyncReadExt + Unpin,
max_size: usize,
) -> Result<Vec<u8>, ReadError> {
let len = read_varint(&mut reader).await?;
if len < 0 || (len as usize) > max_size {
return Err(if len == 254 {
@ -101,7 +104,7 @@ pub async fn read_packet(mut reader: impl AsyncReadExt + Unpin, max_size: usize)
}
} else {
ReadError::PacketTooLarge
})
});
}
let mut buf = vec![0u8; len as usize];
if len == 254 {

View file

@ -1,3 +1,5 @@
use governor::DefaultKeyedRateLimiter;
use governor::Quota;
use log::info;
use log::warn;
use parking_lot::RwLock;
@ -5,6 +7,7 @@ use quinn::RecvStream;
use quinn::SendStream;
use rand::prelude::*;
use std::collections::HashMap;
use std::net::IpAddr;
use tokio::sync::mpsc;
use tokio::sync::oneshot;
@ -18,10 +21,15 @@ type RouterCallback = oneshot::Sender<(SendStream, RecvStream)>;
type RouteRequestReceiver = mpsc::UnboundedSender<RouterRequest>;
#[allow(clippy::module_name_repetitions)]
#[derive(Default)]
pub struct RoutingTable {
table: RwLock<HashMap<String, RouteRequestReceiver>>,
base_domain: String,
limiter: DefaultKeyedRateLimiter<IpAddr>,
}
pub enum RoutingError {
InvalidDomain,
RateLimited,
}
impl RoutingTable {
@ -29,6 +37,7 @@ impl RoutingTable {
RoutingTable {
table: RwLock::default(),
base_domain,
limiter: DefaultKeyedRateLimiter::dashmap(Quota::per_minute(30.try_into().unwrap())),
}
}
@ -44,14 +53,24 @@ impl RoutingTable {
}
}
pub async fn route(&self, domain: &str) -> Option<(SendStream, RecvStream)> {
pub async fn route_limited(
&self,
domain: &str,
ip: IpAddr,
) -> Result<(SendStream, RecvStream), RoutingError> {
if self.limiter.check_key(&ip).is_err() {
return Err(RoutingError::RateLimited);
}
self.limiter.retain_recent();
let (send, recv) = oneshot::channel();
self.table
.read()
.get(domain)?
.get(domain)
.ok_or(RoutingError::InvalidDomain)?
.send(RouterRequest::RouteRequest(send))
.ok()?;
recv.await.ok()
.ok()
.ok_or(RoutingError::InvalidDomain)?;
recv.await.ok().ok_or(RoutingError::InvalidDomain)
}
fn random_domain(&self) -> String {

View file

@ -0,0 +1,13 @@
{
"version": {
"name": "e4mc",
"protocol": -1
},
"players": {
"max": 0,
"online": 0
},
"description": {
"text": "You are trying to connect too fast. Please wait a minute before retrying."
}
}

File diff suppressed because it is too large Load diff