diff --git a/Cargo.lock b/Cargo.lock index 8c755c0..f5b2280 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,6 +43,12 @@ dependencies = [ "syn", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.4.0" @@ -74,9 +80,9 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "tokio", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", ] @@ -110,9 +116,15 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bitflags" version = "1.3.2" @@ -290,6 +302,15 @@ dependencies = [ "syn", ] +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "env_logger" version = "0.10.2" @@ -459,7 +480,7 @@ dependencies = [ "libc", "wasi 0.13.3+wasi-0.2.2", "wasm-bindgen", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -491,6 +512,25 @@ dependencies = [ "web-time", ] +[[package]] +name = "h2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.3.1", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -620,6 +660,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", + "h2", "http 1.3.1", "http-body 1.0.1", "httparse", @@ -630,6 +671,23 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a01595e11bdcec50946522c32dde3fc6914743000a68b93000965f2f02406d" +dependencies = [ + "http 1.3.1", + "hyper 1.6.0", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots 1.0.0", +] + [[package]] name = "hyper-util" version = "0.1.12" @@ -795,6 +853,22 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown 0.15.3", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + [[package]] name = "is-terminal" version = "0.4.15" @@ -995,7 +1069,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1095,6 +1169,7 @@ dependencies = [ "parking_lot", "quinn", "rand 0.9.0", + "reqwest", "rustls-pemfile", "serde", "serde_json", @@ -1279,6 +1354,51 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51743d3e274e2b18df81c4dc6caf8a5b8e15dbe799e0dca05c7617380094e884" +[[package]] +name = "reqwest" +version = "0.12.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "hyper 1.6.0", + "hyper-rustls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "tokio", + "tokio-rustls", + "tower 0.5.2", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots 0.26.11", + "windows-registry", +] + [[package]] name = "ring" version = "0.17.11" @@ -1599,6 +1719,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + [[package]] name = "synstructure" version = "0.13.1" @@ -1730,6 +1859,29 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-rustls" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "tower" version = "0.4.13" @@ -1746,6 +1898,21 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 1.0.2", + "tokio", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -1861,6 +2028,7 @@ checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] @@ -1878,6 +2046,19 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.100" @@ -1939,6 +2120,24 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.0", +] + +[[package]] +name = "webpki-roots" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "winapi" version = "0.3.9" @@ -1970,13 +2169,48 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.53.0", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1985,7 +2219,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1994,14 +2228,30 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -2010,48 +2260,96 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "wit-bindgen-rt" version = "0.33.0" diff --git a/Cargo.toml b/Cargo.toml index eb2b434..74d585e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ log = "0.4.19" parking_lot = "0.12.3" quinn = "0.11.6" rand = "0.9.0" +reqwest = { version = "0.12.15", default-features = false, features = ["charset", "http2", "json", "rustls-tls"] } rustls-pemfile = "2" serde = { version = "1.0.164", features = ["derive"] } serde_json = "1.0.97" diff --git a/flake.nix b/flake.nix index cf843a6..38b5c82 100644 --- a/flake.nix +++ b/flake.nix @@ -145,6 +145,12 @@ example = "mc_connections"; description = lib.mdDoc "Name of Clickhouse table."; }; + + blocklistUrl = mkOption { + type = types.str; + example = "http://blocklists/blocklist.json"; + description = lib.mdDoc "URL of IP blocklist."; + }; }; }; @@ -176,6 +182,7 @@ CLICKHOUSE_PASSWORD_PATH = cfg.clickhousePasswordPath; CLICKHOUSE_DB = cfg.clickhouseDatabase; CLICKHOUSE_TABLE = cfg.clickhouseTable; + BLOCKLIST_URL = cfg.blocklistUrl; }; }; diff --git a/src/disconnect_response_blocked.json b/src/disconnect_response_blocked.json new file mode 100644 index 0000000..5f8eb3c --- /dev/null +++ b/src/disconnect_response_blocked.json @@ -0,0 +1,3 @@ +{ + "text": "You have been blocked due to suspicious activity. If you believe this was made in error, please contact the developer." +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 887c139..fea8ed6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ #![allow(clippy::cast_possible_wrap)] use std::{ + collections::HashMap, convert::Infallible, net::{Ipv6Addr, SocketAddr}, sync::Arc, @@ -17,13 +18,14 @@ use clickhouse::{inserter::Inserter, Row}; use eyre::{eyre, Context}; use log::{error, info, warn}; use netty::{Handshake, ReadError}; +use parking_lot::RwLock; use quinn::{ crypto::rustls::QuicServerConfig, rustls::pki_types::{CertificateDer, PrivateKeyDer}, ConnectionError, Endpoint, Incoming, ServerConfig, TransportConfig, }; use routing::RoutingTable; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use time::OffsetDateTime; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, @@ -80,7 +82,7 @@ struct Connection { region: &'static str, client: Ipv6Addr, intent: &'static str, - successful: bool, + outcome: &'static str, target: String, } @@ -118,12 +120,14 @@ async fn main() -> eyre::Result<()> { .with_max_rows(750_000) .with_period(Some(Duration::from_secs(15))); let inserter = Arc::new(Mutex::new(inserter)); + let blocklist = Arc::new(RwLock::new(HashMap::new())); #[allow(unreachable_code)] tokio::try_join!( listen_quic(endpoint, routing_table), listen_control(endpoint, routing_table), - listen_minecraft(routing_table, inserter.clone()), - send_commits(inserter) + listen_minecraft(routing_table, inserter.clone(), blocklist.clone()), + send_commits(inserter), + refresh_bl(blocklist) )?; Ok(()) } @@ -261,6 +265,7 @@ async fn try_handle_minecraft( mut connection: TcpStream, routing_table: &'static RoutingTable, inserter: Arc>>, + blocklist: Arc>>, ) -> eyre::Result<()> { let established = OffsetDateTime::now_utc(); let peer = connection.peer_addr()?; @@ -276,29 +281,73 @@ async fn try_handle_minecraft( let Some(address) = handshake.normalized_address() else { return politely_disconnect(connection, handshake).await; }; + + let target = address.clone(); + let trace = |outcome| { + tokio::task::spawn(async move { + if let Err(e) = inserter.lock().await.write(&Connection { + established, + region: routing_table.base_domain(), + client: match peer.ip() { + std::net::IpAddr::V4(ipv4_addr) => ipv4_addr.to_ipv6_mapped(), + std::net::IpAddr::V6(ipv6_addr) => ipv6_addr, + }, + intent: match handshake.next_state { + netty::HandshakeType::Status => "status", + netty::HandshakeType::Login => "login", + }, + outcome, + target, + }) { + error!("Failed to send telemetry: {e:?}"); + } + }); + }; + if routing_table.ratelimit(peer.ip()) { - return impolitely_disconnect(connection, handshake).await; + trace("ratelimited"); + return disconnect( + connection, + handshake, + include_str!("./serverlistping_response_rate.json"), + include_str!("./disconnect_response_rate.json"), + ) + .await; + } + let bl_status = blocklist + .read() + .get(&match peer.ip() { + std::net::IpAddr::V4(ipv4_addr) => ipv4_addr.to_ipv6_mapped(), + std::net::IpAddr::V6(ipv6_addr) => ipv6_addr, + }) + .copied(); + if bl_status == Some(BlocklistStatus::ShadowBanned) { + trace("shadowbanned"); + return politely_disconnect(connection, handshake).await; + } else if bl_status == Some(BlocklistStatus::Blocked) { + trace("blocked"); + return disconnect( + connection, + handshake, + include_str!("./serverlistping_response_blocked.json"), + include_str!("./disconnect_response_blocked.json"), + ) + .await; + } else if bl_status == Some(BlocklistStatus::PingMasked) + && handshake.next_state == netty::HandshakeType::Status + { + trace("ping_masked"); + return disconnect( + connection, + handshake, + include_str!("./serverlistping_response_masked.json"), + include_str!("./disconnect_response_blocked.json"), + ) + .await; } let routing_result = routing_table.route(&address).await; let routing_ok = routing_result.is_some(); - tokio::task::spawn(async move { - if let Err(e) = inserter.lock().await.write(&Connection { - established, - region: routing_table.base_domain(), - client: match peer.ip() { - std::net::IpAddr::V4(ipv4_addr) => ipv4_addr.to_ipv6_mapped(), - std::net::IpAddr::V6(ipv6_addr) => ipv6_addr, - }, - intent: match handshake.next_state { - netty::HandshakeType::Status => "status", - netty::HandshakeType::Login => "login", - }, - successful: routing_ok, - target: address - }) { - error!("Failed to send telemetry: {e:?}"); - } - }); + trace(if routing_ok { "ok" } else { "bad_domain" }); let Some((mut send_host, mut recv_host)) = routing_result else { return politely_disconnect(connection, handshake).await; }; @@ -316,53 +365,21 @@ async fn try_handle_minecraft( Ok(()) } -async fn politely_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.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.json")) - .await?; - connection.write_varint(buf.len() as i32).await?; - connection.write_all(&buf).await?; - } - } - Ok(()) +async fn politely_disconnect(connection: TcpStream, handshake: Handshake) -> eyre::Result<()> { + disconnect( + connection, + handshake, + include_str!("./serverlistping_response.json"), + include_str!("./disconnect_response.json"), + ) + .await } -async fn impolitely_disconnect( +async fn disconnect( mut connection: TcpStream, handshake: Handshake, + slp_resp: &str, + dc_resp: &str, ) -> eyre::Result<()> { match handshake.next_state { netty::HandshakeType::Status => { @@ -377,8 +394,7 @@ async fn impolitely_disconnect( } let mut buf = vec![]; buf.write_varint(0).await?; - buf.write_string(include_str!("./serverlistping_response_rate.json")) - .await?; + buf.write_string(slp_resp).await?; connection.write_varint(buf.len() as i32).await?; connection.write_all(&buf).await?; let packet = netty::read_packet(&mut connection, 9).await?; @@ -398,8 +414,7 @@ async fn impolitely_disconnect( 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?; + buf.write_string(dc_resp).await?; connection.write_varint(buf.len() as i32).await?; connection.write_all(&buf).await?; } @@ -411,8 +426,9 @@ async fn handle_minecraft( connection: TcpStream, routing_table: &'static RoutingTable, inserter: Arc>>, + blocklist: Arc>>, ) { - if let Err(e) = try_handle_minecraft(connection, routing_table, inserter).await { + if let Err(e) = try_handle_minecraft(connection, routing_table, inserter, blocklist).await { error!("Error handling Minecraft connection: {:#}", e); } } @@ -420,6 +436,7 @@ async fn handle_minecraft( async fn listen_minecraft( routing_table: &'static RoutingTable, inserter: Arc>>, + blocklist: Arc>>, ) -> eyre::Result { let server = tokio::net::TcpListener::bind( std::env::var("QUICLIME_BIND_ADDR_MC") @@ -434,6 +451,7 @@ async fn listen_minecraft( connection, routing_table, inserter.clone(), + blocklist.clone(), )); } Err(e) => { @@ -451,3 +469,29 @@ async fn send_commits(inserter: Arc>>) -> eyre::Resul tokio::time::sleep(Duration::from_secs(5)).await; } } + +#[derive(Deserialize, Clone, Copy, PartialEq, Eq)] +enum BlocklistStatus { + ShadowBanned, + Blocked, + PingMasked, +} + +async fn refresh_bl( + blocklist: Arc>>, +) -> eyre::Result { + let client = reqwest::Client::new(); + let url = std::env::var("BLOCKLIST_URL").context("Reading BLOCKLIST_URL")?; + loop { + tokio::time::sleep(Duration::from_secs(10)).await; + let Ok(resp) = client.get(&url).send().await else { + error!("Failed to fetch blocklist!"); + continue; + }; + let Ok(json) = resp.json::>().await else { + error!("Failed to fetch blocklist!"); + continue; + }; + *blocklist.write() = json; + } +} diff --git a/src/netty.rs b/src/netty.rs index f15bf90..aa8ff9d 100644 --- a/src/netty.rs +++ b/src/netty.rs @@ -165,7 +165,7 @@ pub struct Handshake { pub next_state: HandshakeType, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(i32)] pub enum HandshakeType { Status = 1, diff --git a/src/serverlistping_response_blocked.json b/src/serverlistping_response_blocked.json new file mode 100644 index 0000000..d960428 --- /dev/null +++ b/src/serverlistping_response_blocked.json @@ -0,0 +1,13 @@ +{ + "version": { + "name": "e4mc", + "protocol": -1 + }, + "players": { + "max": 0, + "online": 0 + }, + "description": { + "text": "You have been blocked due to suspicious activity." + } +} diff --git a/src/serverlistping_response_masked.json b/src/serverlistping_response_masked.json new file mode 100644 index 0000000..46a6821 --- /dev/null +++ b/src/serverlistping_response_masked.json @@ -0,0 +1,13 @@ +{ + "version": { + "name": "e4mc", + "protocol": -1 + }, + "players": { + "max": 0, + "online": 0 + }, + "description": { + "text": "Information hidden due to suspicious activity." + } +}