aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-09-21 15:31:18 +0200
committermetamuffin <metamuffin@disroot.org>2024-09-21 15:31:18 +0200
commit34190a70b1efa0972ef58b88d356f985c46b89ae (patch)
treed8cf5b98a4d0aed34bff048fb1e15a87f106f6db
parent41c95fc5b6b1c8bc4b944d889414a4197a23837b (diff)
downloadhurrycurry-34190a70b1efa0972ef58b88d356f985c46b89ae.tar
hurrycurry-34190a70b1efa0972ef58b88d356f985c46b89ae.tar.bz2
hurrycurry-34190a70b1efa0972ef58b88d356f985c46b89ae.tar.zst
server: mdns and upnp
-rw-r--r--Cargo.lock169
-rw-r--r--server/Cargo.toml5
-rw-r--r--server/src/lib.rs2
-rw-r--r--server/src/main.rs19
-rw-r--r--server/src/network/mdns.rs61
-rw-r--r--server/src/network/mod.rs20
-rw-r--r--server/src/network/register.rs (renamed from server/src/register.rs)0
-rw-r--r--server/src/network/upnp.rs72
8 files changed, 341 insertions, 7 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a3e41432..82342302 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -179,6 +179,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
+name = "attohttpc"
+version = "0.16.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdb8867f378f33f78a811a8eb9bf108ad99430d7aad43315dd9319c827ef6247"
+dependencies = [
+ "http 0.2.12",
+ "log",
+ "url",
+ "wildmatch",
+]
+
+[[package]]
name = "autocfg"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -373,6 +385,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
[[package]]
+name = "c_linked_list"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4964518bd3b4a8190e832886cdc0da9794f12e8e6c1613a9e90ff331c4c8724b"
+
+[[package]]
name = "c_vec"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -486,6 +504,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
[[package]]
+name = "concurrent-queue"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
name = "cookie"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -750,6 +777,8 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
dependencies = [
+ "futures-core",
+ "futures-sink",
"spin",
]
@@ -782,6 +811,7 @@ checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
dependencies = [
"futures-channel",
"futures-core",
+ "futures-executor",
"futures-io",
"futures-sink",
"futures-task",
@@ -805,6 +835,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
+name = "futures-executor"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
name = "futures-io"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -852,6 +893,12 @@ dependencies = [
]
[[package]]
+name = "gcc"
+version = "0.3.55"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
+
+[[package]]
name = "generator"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -875,6 +922,28 @@ dependencies = [
]
[[package]]
+name = "get_if_addrs"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abddb55a898d32925f3148bd281174a68eeb68bbfd9a5938a57b18f506ee4ef7"
+dependencies = [
+ "c_linked_list",
+ "get_if_addrs-sys",
+ "libc",
+ "winapi 0.2.8",
+]
+
+[[package]]
+name = "get_if_addrs-sys"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d04f9fb746cf36b191c00f3ede8bde9c8e64f9f4b05ae2694a9ccf5e3f5ab48"
+dependencies = [
+ "gcc",
+ "libc",
+]
+
+[[package]]
name = "getrandom"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1146,17 +1215,20 @@ dependencies = [
[[package]]
name = "hurrycurry-server"
-version = "0.2.0"
+version = "2.1.0"
dependencies = [
"anyhow",
"bincode",
"clap",
"env_logger",
"futures-util",
+ "get_if_addrs",
"hurrycurry-bot",
"hurrycurry-client-lib",
"hurrycurry-protocol",
+ "igd",
"log",
+ "mdns-sd",
"pollster",
"rand 0.9.0-alpha.2",
"reqwest",
@@ -1262,6 +1334,34 @@ dependencies = [
]
[[package]]
+name = "if-addrs"
+version = "0.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a78a89907582615b19f6f0da1af18abf6ff08be259395669b834b057a7ee92d8"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "igd"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "556b5a75cd4adb7c4ea21c64af1c48cefb2ce7d43dc4352c720a1fe47c21f355"
+dependencies = [
+ "attohttpc",
+ "bytes",
+ "futures",
+ "http 0.2.12",
+ "hyper 0.14.30",
+ "log",
+ "rand 0.8.5",
+ "tokio",
+ "url",
+ "xmltree",
+]
+
+[[package]]
name = "image"
version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1535,6 +1635,19 @@ dependencies = [
]
[[package]]
+name = "mdns-sd"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35d1967e64b4ca7bba8af2458d0b9dd50471d541959ca2120cb9cc965946ef61"
+dependencies = [
+ "flume",
+ "if-addrs",
+ "log",
+ "polling",
+ "socket2",
+]
+
+[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1628,7 +1741,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"overload",
- "winapi",
+ "winapi 0.3.9",
]
[[package]]
@@ -1856,6 +1969,22 @@ dependencies = [
]
[[package]]
+name = "polling"
+version = "2.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce"
+dependencies = [
+ "autocfg",
+ "bitflags 1.3.2",
+ "cfg-if",
+ "concurrent-queue",
+ "libc",
+ "log",
+ "pin-project-lite",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
name = "pollster"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3340,6 +3469,18 @@ dependencies = [
]
[[package]]
+name = "wildmatch"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f44b95f62d34113cf558c93511ac93027e03e9c29a60dd0fd70e6e025c7270a"
+
+[[package]]
+name = "winapi"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
+
+[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3402,6 +3543,15 @@ dependencies = [
[[package]]
name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
@@ -3555,6 +3705,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546"
[[package]]
+name = "xml-rs"
+version = "0.8.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26"
+
+[[package]]
+name = "xmltree"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb"
+dependencies = [
+ "xml-rs",
+]
+
+[[package]]
name = "yansi"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/server/Cargo.toml b/server/Cargo.toml
index 520e3d24..ef14fddd 100644
--- a/server/Cargo.toml
+++ b/server/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "hurrycurry-server"
-version = "0.2.0"
+version = "2.1.0"
edition = "2021"
default-run = "hurrycurry-server"
@@ -26,6 +26,9 @@ reqwest = { version = "0.12.7", default-features = false, features = [
pollster = "0.3.0"
bincode = "2.0.0-rc.3"
xdg = "2.5.2"
+igd = { version = "0.12.1", features = ["aio"] }
+get_if_addrs = "0.5.3"
+mdns-sd = "0.11.4"
hurrycurry-protocol = { path = "protocol" }
hurrycurry-client-lib = { path = "client-lib" }
diff --git a/server/src/lib.rs b/server/src/lib.rs
index ea514d5b..c0ed8edb 100644
--- a/server/src/lib.rs
+++ b/server/src/lib.rs
@@ -27,10 +27,10 @@ pub mod commands;
pub mod data;
pub mod entity;
pub mod interaction;
-pub mod register;
pub mod scoreboard;
pub mod server;
pub mod state;
+pub mod network;
use hurrycurry_protocol::{glam::Vec2, Message};
diff --git a/server/src/main.rs b/server/src/main.rs
index 4db43c64..d0538126 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -21,7 +21,7 @@ use futures_util::{SinkExt, StreamExt};
use hurrycurry_protocol::{PacketC, PacketS, BINCODE_CONFIG, VERSION};
use hurrycurry_server::{
data::DATA_DIR,
- register::Register,
+ network::{mdns::mdns_loop, register::Register, upnp::upnp_loop},
server::{GameServerExt, Server},
trm, ConnectionID,
};
@@ -59,9 +59,12 @@ pub(crate) struct Args {
/// Enables submissions to the public server registry
#[arg(long)]
register: bool,
- /// Enables mDNS discoverability
+ /// Enables the mDNS responder for local network discovery
#[arg(long)]
- discoverable: bool,
+ mdns: bool,
+ // Enables automatic gateway port forwarding using UPnP
+ #[arg(long)]
+ upnp: bool,
/// Server name
#[arg(long, short = 'N', default_value = "A Hurry Curry! Server")]
server_name: String,
@@ -130,6 +133,16 @@ async fn run(args: Args) -> anyhow::Result<()> {
);
tokio::task::spawn(r.register_loop());
}
+ if args.upnp {
+ tokio::task::spawn(upnp_loop(args.listen.port()));
+ }
+ if args.mdns {
+ tokio::task::spawn(mdns_loop(
+ args.server_name.clone(),
+ args.listen.port(),
+ state.clone(),
+ ));
+ }
{
let state = state.clone();
diff --git a/server/src/network/mdns.rs b/server/src/network/mdns.rs
new file mode 100644
index 00000000..870ae7ec
--- /dev/null
+++ b/server/src/network/mdns.rs
@@ -0,0 +1,61 @@
+use crate::server::Server;
+use anyhow::Result;
+use get_if_addrs::get_if_addrs;
+use hurrycurry_protocol::VERSION;
+use log::{info, warn};
+use mdns_sd::{ServiceDaemon, ServiceInfo};
+use rand::random;
+use std::{collections::HashMap, sync::Arc, time::Duration};
+use tokio::{sync::RwLock, time::interval};
+
+pub async fn mdns_loop(name: String, port: u16, state: Arc<RwLock<Server>>) {
+ let d = match ServiceDaemon::new() {
+ Ok(d) => d,
+ Err(e) => {
+ warn!("mDNS daemon failed to start: {e}");
+ return;
+ }
+ };
+ let mut interval = interval(Duration::from_secs(60));
+ let hostname = format!("hks-{}.local.", random::<u64>());
+ loop {
+ interval.tick().await;
+ if let Err(e) = update_service(&d, &state, &name, &hostname, port).await {
+ warn!("mDNS service update failed: {e}");
+ }
+ info!("updated mDNS service record");
+ }
+}
+
+async fn update_service(
+ d: &ServiceDaemon,
+ state: &Arc<RwLock<Server>>,
+ name: &str,
+ hostname: &str,
+ port: u16,
+) -> Result<()> {
+ let addrs = get_if_addrs()?
+ .into_iter()
+ .map(|e| e.addr.ip())
+ .filter(|a| !a.is_loopback())
+ .collect::<Vec<_>>();
+
+ let players = state.read().await.count_chefs();
+
+ d.register(ServiceInfo::new(
+ "_hurrycurry._tcp.local.",
+ name,
+ hostname,
+ &*addrs,
+ port,
+ HashMap::from_iter([
+ ("players".to_string(), players.to_string()),
+ ("version".to_string(), env!("CARGO_PKG_VERSION").to_string()),
+ (
+ "protocol".to_string(),
+ format!("{}.{}", VERSION.0, VERSION.1),
+ ),
+ ]),
+ )?)?;
+ Ok(())
+}
diff --git a/server/src/network/mod.rs b/server/src/network/mod.rs
new file mode 100644
index 00000000..b7ffc15e
--- /dev/null
+++ b/server/src/network/mod.rs
@@ -0,0 +1,20 @@
+/*
+ Hurry Curry! - a game about cooking
+ Copyright 2024 metamuffin
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, version 3 of the License only.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+*/
+pub mod register;
+pub mod upnp;
+pub mod mdns;
diff --git a/server/src/register.rs b/server/src/network/register.rs
index b26768a2..b26768a2 100644
--- a/server/src/register.rs
+++ b/server/src/network/register.rs
diff --git a/server/src/network/upnp.rs b/server/src/network/upnp.rs
new file mode 100644
index 00000000..a16e9684
--- /dev/null
+++ b/server/src/network/upnp.rs
@@ -0,0 +1,72 @@
+/*
+ Hurry Curry! - a game about cooking
+ Copyright 2024 metamuffin
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, version 3 of the License only.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+*/
+use anyhow::{bail, Result};
+use get_if_addrs::get_if_addrs;
+use igd::{
+ aio::{search_gateway, Gateway},
+ PortMappingProtocol, SearchOptions,
+};
+use log::{error, info};
+use std::{
+ net::{IpAddr, Ipv4Addr, SocketAddrV4},
+ time::Duration,
+};
+use tokio::time::interval;
+
+pub async fn upnp_loop(port: u16) {
+ // TODO multiple gateways
+ let (gateway, local_addr) = match upnp_setup().await {
+ Ok(g) => g,
+ Err(e) => {
+ error!("UPnP gateway not found: {e}");
+ return;
+ }
+ };
+ let mut interval = interval(Duration::from_secs(120));
+ loop {
+ interval.tick().await;
+ match gateway
+ .add_port(
+ PortMappingProtocol::TCP,
+ 27032,
+ SocketAddrV4::new(local_addr, port),
+ 800,
+ "Hurry Curry! Game Server",
+ )
+ .await
+ {
+ Ok(()) => info!("port mapped successfully"),
+ Err(e) => error!("upnp failed: {e}"),
+ }
+ }
+}
+
+async fn upnp_setup() -> Result<(Gateway, Ipv4Addr)> {
+ let gateway = search_gateway(SearchOptions::default()).await?;
+ info!("IGD address is {}", gateway.addr);
+ for i in get_if_addrs()? {
+ let a = i.addr.ip();
+ if !a.is_loopback() {
+ if let IpAddr::V4(a) = a {
+ info!("local v4 address is {a}");
+ return Ok((gateway, a));
+ }
+ }
+ }
+ bail!("no good local address found")
+}