diff options
Diffstat (limited to 'server/registry/src/register.rs')
-rw-r--r-- | server/registry/src/register.rs | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/server/registry/src/register.rs b/server/registry/src/register.rs new file mode 100644 index 00000000..173361ef --- /dev/null +++ b/server/registry/src/register.rs @@ -0,0 +1,83 @@ +use crate::Registry; +use log::{debug, info}; +use rocket::{http::hyper::Uri, post, serde::json::Json, State}; +use serde::Deserialize; +use std::{net::IpAddr, str::FromStr, sync::Arc, time::Instant}; +use tokio::{net::lookup_host, sync::RwLock}; + +#[derive(Debug, Deserialize)] +pub(super) struct Submission { + secret: u128, + name: String, + players: usize, + last_game: i64, + version: (usize, usize), + + uri: String, +} + +#[post("/v1/register", data = "<submission>")] +pub(super) async fn r_register<'a>( + client_addr: IpAddr, + registry: &State<Arc<RwLock<Registry>>>, + submission: Json<Submission>, +) -> Result<&'static str, &'static str> { + debug!("submission {submission:?}"); + let uri = Uri::from_str(&submission.uri).map_err(|_| "invalid uri")?; + + let scheme = uri.scheme().ok_or("no scheme")?.as_str(); + let secure = match scheme { + "ws" => false, + "wss" => true, + _ => return Err("invalid scheme"), + }; + let host = uri.host().ok_or("no host")?; + let port = uri.port_u16().unwrap_or(if secure { 443 } else { 27032 }); + + let uri_q = match IpAddr::from_str(host) { + Ok(mut addr) => { + if addr.is_unspecified() { + addr = client_addr; + } + if addr.is_loopback() { + return Err("loopback address"); + } + if addr.is_multicast() { + return Err("multicast address"); + } + if client_addr == addr { + format!("{scheme}://{addr}:{port}",) + } else { + return Err("source address does not match uri"); + } + } + Err(_) => { + if lookup_host(format!("{host}:0")) + .await + .map_err(|_| "dns lookup failed")? + .find(|a| a.ip() == client_addr) + .is_some() + { + format!("{scheme}://{host}:{port}") + } else { + return Err("host verification failed"); + } + } + }; + + let mut g = registry.write().await; + + if g.servers.len() > 1000 { + return Err("too many registered servers"); + } + + info!("submission approved for {uri_q:?}"); + let entry = g.servers.entry(submission.secret).or_default(); + entry.name = submission.name.clone(); + entry.players_online = submission.players; + entry.last_game = submission.last_game; + entry.version = submission.version; + entry.address.insert(uri_q, Instant::now()); + + Ok("ok") +} |