Papers please.
This commit is contained in:
parent
70bdcef3e7
commit
23ce1f80a5
2 changed files with 25 additions and 49 deletions
45
src/main.rs
45
src/main.rs
|
@ -22,7 +22,7 @@ use quinn::{
|
||||||
rustls::pki_types::{CertificateDer, PrivateKeyDer},
|
rustls::pki_types::{CertificateDer, PrivateKeyDer},
|
||||||
ConnectionError, Endpoint, Incoming, ServerConfig, TransportConfig,
|
ConnectionError, Endpoint, Incoming, ServerConfig, TransportConfig,
|
||||||
};
|
};
|
||||||
use routing::{RoutingError, RoutingTable};
|
use routing::RoutingTable;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
|
@ -81,6 +81,7 @@ struct Connection {
|
||||||
client: Ipv6Addr,
|
client: Ipv6Addr,
|
||||||
intent: &'static str,
|
intent: &'static str,
|
||||||
successful: bool,
|
successful: bool,
|
||||||
|
target: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
@ -201,7 +202,7 @@ async fn try_handle_quic(connection: Incoming, routing_table: &RoutingTable) ->
|
||||||
async fn handle_quic(connection: Incoming, routing_table: &RoutingTable) {
|
async fn handle_quic(connection: Incoming, routing_table: &RoutingTable) {
|
||||||
if let Err(e) = try_handle_quic(connection, routing_table).await {
|
if let Err(e) = try_handle_quic(connection, routing_table).await {
|
||||||
error!("Error handling QUIClime connection: {:#}", e);
|
error!("Error handling QUIClime connection: {:#}", e);
|
||||||
};
|
}
|
||||||
info!("Finished handling QUIClime connection");
|
info!("Finished handling QUIClime connection");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,6 +262,7 @@ async fn try_handle_minecraft(
|
||||||
routing_table: &'static RoutingTable,
|
routing_table: &'static RoutingTable,
|
||||||
inserter: Arc<Mutex<Inserter<Connection>>>,
|
inserter: Arc<Mutex<Inserter<Connection>>>,
|
||||||
) -> eyre::Result<()> {
|
) -> eyre::Result<()> {
|
||||||
|
let established = OffsetDateTime::now_utc();
|
||||||
let peer = connection.peer_addr()?;
|
let peer = connection.peer_addr()?;
|
||||||
info!("Minecraft client connected from: {}", peer);
|
info!("Minecraft client connected from: {}", peer);
|
||||||
let handshake = netty::read_packet(&mut connection, 512).await;
|
let handshake = netty::read_packet(&mut connection, 512).await;
|
||||||
|
@ -274,37 +276,14 @@ async fn try_handle_minecraft(
|
||||||
let Some(address) = handshake.normalized_address() else {
|
let Some(address) = handshake.normalized_address() else {
|
||||||
return politely_disconnect(connection, handshake).await;
|
return politely_disconnect(connection, handshake).await;
|
||||||
};
|
};
|
||||||
let (mut send_host, mut recv_host) =
|
if routing_table.ratelimit(peer.ip()) {
|
||||||
match routing_table.route_limited(&address, peer.ip()).await {
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(RoutingError::InvalidDomain) => {
|
|
||||||
tokio::task::spawn(async move {
|
|
||||||
if let Err(e) = inserter.lock().await.write(&Connection {
|
|
||||||
established: OffsetDateTime::now_utc(),
|
|
||||||
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: false,
|
|
||||||
}) {
|
|
||||||
error!("Failed to send telemetry: {e:?}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return politely_disconnect(connection, handshake).await;
|
|
||||||
}
|
|
||||||
Err(RoutingError::RateLimited) => {
|
|
||||||
warn!("Connection from {} has been rate limited!", peer);
|
|
||||||
return impolitely_disconnect(connection, handshake).await;
|
return impolitely_disconnect(connection, handshake).await;
|
||||||
}
|
}
|
||||||
};
|
let routing_result = routing_table.route(&address).await;
|
||||||
|
let routing_ok = routing_result.is_some();
|
||||||
tokio::task::spawn(async move {
|
tokio::task::spawn(async move {
|
||||||
if let Err(e) = inserter.lock().await.write(&Connection {
|
if let Err(e) = inserter.lock().await.write(&Connection {
|
||||||
established: OffsetDateTime::now_utc(),
|
established,
|
||||||
region: routing_table.base_domain(),
|
region: routing_table.base_domain(),
|
||||||
client: match peer.ip() {
|
client: match peer.ip() {
|
||||||
std::net::IpAddr::V4(ipv4_addr) => ipv4_addr.to_ipv6_mapped(),
|
std::net::IpAddr::V4(ipv4_addr) => ipv4_addr.to_ipv6_mapped(),
|
||||||
|
@ -314,11 +293,15 @@ async fn try_handle_minecraft(
|
||||||
netty::HandshakeType::Status => "status",
|
netty::HandshakeType::Status => "status",
|
||||||
netty::HandshakeType::Login => "login",
|
netty::HandshakeType::Login => "login",
|
||||||
},
|
},
|
||||||
successful: true,
|
successful: routing_ok,
|
||||||
|
target: address
|
||||||
}) {
|
}) {
|
||||||
error!("Failed to send telemetry: {e:?}");
|
error!("Failed to send telemetry: {e:?}");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
let Some((mut send_host, mut recv_host)) = routing_result else {
|
||||||
|
return politely_disconnect(connection, handshake).await;
|
||||||
|
};
|
||||||
handshake.send(&mut send_host).await?;
|
handshake.send(&mut send_host).await?;
|
||||||
let (mut recv_client, mut send_client) = connection.split();
|
let (mut recv_client, mut send_client) = connection.split();
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
|
@ -431,7 +414,7 @@ async fn handle_minecraft(
|
||||||
) {
|
) {
|
||||||
if let Err(e) = try_handle_minecraft(connection, routing_table, inserter).await {
|
if let Err(e) = try_handle_minecraft(connection, routing_table, inserter).await {
|
||||||
error!("Error handling Minecraft connection: {:#}", e);
|
error!("Error handling Minecraft connection: {:#}", e);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn listen_minecraft(
|
async fn listen_minecraft(
|
||||||
|
|
|
@ -27,11 +27,6 @@ pub struct RoutingTable {
|
||||||
limiter: DefaultKeyedRateLimiter<IpAddr>,
|
limiter: DefaultKeyedRateLimiter<IpAddr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum RoutingError {
|
|
||||||
InvalidDomain,
|
|
||||||
RateLimited,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RoutingTable {
|
impl RoutingTable {
|
||||||
pub fn new(base_domain: String) -> Self {
|
pub fn new(base_domain: String) -> Self {
|
||||||
RoutingTable {
|
RoutingTable {
|
||||||
|
@ -53,24 +48,22 @@ impl RoutingTable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn route_limited(
|
pub fn ratelimit(&self, ip: IpAddr) -> bool {
|
||||||
&self,
|
|
||||||
domain: &str,
|
|
||||||
ip: IpAddr,
|
|
||||||
) -> Result<(SendStream, RecvStream), RoutingError> {
|
|
||||||
if self.limiter.check_key(&ip).is_err() {
|
if self.limiter.check_key(&ip).is_err() {
|
||||||
return Err(RoutingError::RateLimited);
|
return true;
|
||||||
}
|
}
|
||||||
self.limiter.retain_recent();
|
self.limiter.retain_recent();
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn route(&self, domain: &str) -> Option<(SendStream, RecvStream)> {
|
||||||
let (send, recv) = oneshot::channel();
|
let (send, recv) = oneshot::channel();
|
||||||
self.table
|
self.table
|
||||||
.read()
|
.read()
|
||||||
.get(domain)
|
.get(domain)?
|
||||||
.ok_or(RoutingError::InvalidDomain)?
|
|
||||||
.send(RouterRequest::RouteRequest(send))
|
.send(RouterRequest::RouteRequest(send))
|
||||||
.ok()
|
.ok()?;
|
||||||
.ok_or(RoutingError::InvalidDomain)?;
|
recv.await.ok()
|
||||||
recv.await.ok().ok_or(RoutingError::InvalidDomain)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_domain(&self) -> String {
|
fn random_domain(&self) -> String {
|
||||||
|
|
Loading…
Reference in a new issue