Browse Source

refactor, use one binary for both ip versions

ipv6
Denis Drakhnia 3 months ago
parent
commit
ce759fed3d
  1. 18
      master/src/config.rs
  2. 4
      master/src/main.rs
  3. 167
      master/src/master_server.rs

18
master/src/config.rs

@ -1,22 +1,18 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
// SPDX-FileCopyrightText: 2023 Denis Drakhnia <numas13@gmail.com> // SPDX-FileCopyrightText: 2023 Denis Drakhnia <numas13@gmail.com>
use std::fs; use std::{
use std::io; fs, io,
use std::net::IpAddr; net::{IpAddr, Ipv4Addr},
use std::path::Path; path::Path,
};
use log::LevelFilter; use log::LevelFilter;
use serde::{de::Error as _, Deserialize, Deserializer}; use serde::{de::Error as _, Deserialize, Deserializer};
use thiserror::Error; use thiserror::Error;
use xash3d_protocol::admin; use xash3d_protocol::{admin, filter::Version};
use xash3d_protocol::filter::Version;
#[cfg(not(feature = "ipv6"))] pub const DEFAULT_MASTER_SERVER_IP: IpAddr = IpAddr::V4(Ipv4Addr::UNSPECIFIED);
pub const DEFAULT_MASTER_SERVER_IP: IpAddr = IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED);
#[cfg(feature = "ipv6")]
pub const DEFAULT_MASTER_SERVER_IP: IpAddr = IpAddr::V6(std::net::Ipv6Addr::UNSPECIFIED);
pub const DEFAULT_MASTER_SERVER_PORT: u16 = 27010; pub const DEFAULT_MASTER_SERVER_PORT: u16 = 27010;
pub const DEFAULT_CHALLENGE_TIMEOUT: u32 = 10; pub const DEFAULT_CHALLENGE_TIMEOUT: u32 = 10;

4
master/src/main.rs

@ -23,7 +23,7 @@ use signal_hook::{consts::signal::*, flag as signal_flag};
use crate::cli::Cli; use crate::cli::Cli;
use crate::config::Config; use crate::config::Config;
use crate::master_server::{Error, MasterServer}; use crate::master_server::{Error, Master};
fn load_config(cli: &Cli) -> Result<Config, config::Error> { fn load_config(cli: &Cli) -> Result<Config, config::Error> {
let mut cfg = match cli.config_path { let mut cfg = match cli.config_path {
@ -68,7 +68,7 @@ fn run() -> Result<(), Error> {
process::exit(1); process::exit(1);
}); });
let mut master = MasterServer::new(cfg)?; let mut master = Master::new(cfg)?;
let sig_flag = Arc::new(AtomicBool::new(false)); let sig_flag = Arc::new(AtomicBool::new(false));
// XXX: Windows does not support SIGUSR1. // XXX: Windows does not support SIGUSR1.
#[cfg(not(windows))] #[cfg(not(windows))]

167
master/src/master_server.rs

@ -2,9 +2,12 @@
// SPDX-FileCopyrightText: 2023 Denis Drakhnia <numas13@gmail.com> // SPDX-FileCopyrightText: 2023 Denis Drakhnia <numas13@gmail.com>
use std::{ use std::{
cmp::Eq,
collections::hash_map, collections::hash_map,
fmt::Display,
hash::Hash,
io, io,
net::{SocketAddr, ToSocketAddrs, UdpSocket}, net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs, UdpSocket},
ops::Deref, ops::Deref,
sync::atomic::{AtomicBool, Ordering}, sync::atomic::{AtomicBool, Ordering},
time::{Duration, Instant}, time::{Duration, Instant},
@ -31,60 +34,54 @@ use crate::{
stats::Stats, stats::Stats,
}; };
#[cfg(not(feature = "ipv6"))] pub trait AddrExt: Sized + Eq + Hash + Display + Copy + ToSocketAddrs + ServerAddress {
mod protocol { type Ip;
use std::net::SocketAddr;
pub type Addr = std::net::SocketAddrV4; fn extract(addr: SocketAddr) -> Result<Self, SocketAddr>;
pub type Ip = std::net::Ipv4Addr; fn ip(&self) -> &Self::Ip;
fn wrap(self) -> SocketAddr;
}
impl AddrExt for SocketAddrV4 {
type Ip = Ipv4Addr;
pub fn extract_addr(addr: SocketAddr) -> Option<Addr> { fn extract(addr: SocketAddr) -> Result<Self, SocketAddr> {
if let SocketAddr::V4(a) = addr { if let SocketAddr::V4(addr) = addr {
Some(a) Ok(addr)
} else { } else {
None Err(addr)
} }
} }
#[inline(always)] fn ip(&self) -> &Self::Ip {
pub fn wrap_addr(addr: Addr) -> SocketAddr { SocketAddrV4::ip(self)
SocketAddr::V4(addr)
} }
#[inline(always)] fn wrap(self) -> SocketAddr {
pub fn check_addr(addr: &SocketAddr) -> bool { SocketAddr::V4(self)
addr.is_ipv4()
} }
} }
#[cfg(feature = "ipv6")] impl AddrExt for SocketAddrV6 {
mod protocol { type Ip = Ipv6Addr;
use std::net::SocketAddr;
pub type Addr = std::net::SocketAddrV6; fn extract(addr: SocketAddr) -> Result<Self, SocketAddr> {
pub type Ip = std::net::Ipv6Addr; if let SocketAddr::V6(addr) = addr {
Ok(addr)
pub fn extract_addr(addr: SocketAddr) -> Option<Addr> {
if let SocketAddr::V6(a) = addr {
Some(a)
} else { } else {
None Err(addr)
} }
} }
#[inline(always)] fn ip(&self) -> &Self::Ip {
pub fn wrap_addr(addr: Addr) -> SocketAddr { SocketAddrV6::ip(self)
SocketAddr::V6(addr)
} }
#[inline(always)] fn wrap(self) -> SocketAddr {
pub fn check_addr(addr: &SocketAddr) -> bool { SocketAddr::V6(self)
addr.is_ipv6()
} }
} }
use self::protocol::*;
/// The maximum size of UDP packets. /// The maximum size of UDP packets.
const MAX_PACKET_SIZE: usize = 512; const MAX_PACKET_SIZE: usize = 512;
@ -104,8 +101,6 @@ const ADMIN_LIMIT_CLEANUP_MAX: usize = 100;
pub enum Error { pub enum Error {
#[error("Failed to bind server socket: {0}")] #[error("Failed to bind server socket: {0}")]
BindSocket(io::Error), BindSocket(io::Error),
#[error("IP version is not supported")]
Unsupported,
#[error(transparent)] #[error(transparent)]
Protocol(#[from] ProtocolError), Protocol(#[from] ProtocolError),
#[error(transparent)] #[error(transparent)]
@ -136,8 +131,8 @@ impl<T> Entry<T> {
} }
impl Entry<ServerInfo> { impl Entry<ServerInfo> {
fn matches(&self, addr: Addr, region: Region, filter: &Filter) -> bool { fn matches<Addr: AddrExt>(&self, addr: Addr, region: Region, filter: &Filter) -> bool {
self.region == region && filter.matches(wrap_addr(addr), &self.value) self.region == region && filter.matches(addr.wrap(), &self.value)
} }
} }
@ -170,7 +165,7 @@ impl Counter {
} }
} }
pub struct MasterServer { pub struct MasterServer<Addr: AddrExt> {
sock: UdpSocket, sock: UdpSocket,
challenges: HashMap<Addr, Entry<u32>>, challenges: HashMap<Addr, Entry<u32>>,
challenges_counter: Counter, challenges_counter: Counter,
@ -187,15 +182,15 @@ pub struct MasterServer {
update_map: Box<str>, update_map: Box<str>,
update_addr: SocketAddr, update_addr: SocketAddr,
admin_challenges: HashMap<Ip, Entry<(u32, u32)>>, admin_challenges: HashMap<Addr::Ip, Entry<(u32, u32)>>,
admin_challenges_counter: Counter, admin_challenges_counter: Counter,
admin_list: Box<[config::AdminConfig]>, admin_list: Box<[config::AdminConfig]>,
// rate limit if hash is invalid // rate limit if hash is invalid
admin_limit: HashMap<Ip, Entry<()>>, admin_limit: HashMap<Addr::Ip, Entry<()>>,
admin_limit_counter: Counter, admin_limit_counter: Counter,
hash: config::HashConfig, hash: config::HashConfig,
blocklist: HashSet<Ip>, blocklist: HashSet<Addr::Ip>,
stats: Stats, stats: Stats,
@ -233,18 +228,52 @@ fn resolve_update_addr(cfg: &Config, local_addr: SocketAddr) -> SocketAddr {
local_addr local_addr
} }
impl MasterServer { pub enum Master {
V4(MasterServer<SocketAddrV4>),
V6(MasterServer<SocketAddrV6>),
}
impl Master {
pub fn new(cfg: Config) -> Result<Self, Error> { pub fn new(cfg: Config) -> Result<Self, Error> {
let addr = SocketAddr::new(cfg.server.ip, cfg.server.port); match SocketAddr::new(cfg.server.ip, cfg.server.port) {
if !check_addr(&addr) { SocketAddr::V4(addr) => MasterServer::new(cfg, addr).map(Self::V4),
return Err(Error::Unsupported); SocketAddr::V6(addr) => MasterServer::new(cfg, addr).map(Self::V6),
} }
}
pub fn update_config(&mut self, cfg: Config) -> Result<(), Error> {
let cfg = match self {
Self::V4(inner) => inner.update_config(cfg)?,
Self::V6(inner) => inner.update_config(cfg)?,
};
if let Some(cfg) = cfg {
info!("Server IP version changed, full restart");
*self = Self::new(cfg)?;
}
Ok(())
}
pub fn run(&mut self, sig_flag: &AtomicBool) -> Result<(), Error> {
match self {
Self::V4(inner) => inner.run(sig_flag),
Self::V6(inner) => inner.run(sig_flag),
}
}
}
impl<Addr> MasterServer<Addr>
where
Addr: AddrExt,
Addr::Ip: Eq + Hash + Display + Copy + std::str::FromStr,
{
pub fn new(cfg: Config, addr: Addr) -> Result<Self, Error> {
info!("Listen address: {}", addr); info!("Listen address: {}", addr);
let sock = UdpSocket::bind(addr).map_err(Error::BindSocket)?; let sock = UdpSocket::bind(addr).map_err(Error::BindSocket)?;
// make socket interruptable by singals // make socket interruptable by singals
sock.set_read_timeout(Some(Duration::from_secs(u32::MAX as u64)))?; sock.set_read_timeout(Some(Duration::from_secs(u32::MAX as u64)))?;
let update_addr = resolve_update_addr(&cfg, addr); let update_addr = resolve_update_addr(&cfg, addr.wrap());
Ok(Self { Ok(Self {
sock, sock,
@ -274,10 +303,16 @@ impl MasterServer {
}) })
} }
pub fn update_config(&mut self, cfg: Config) -> Result<(), Error> { fn local_addr(&self) -> io::Result<SocketAddr> {
let local_addr = self.sock.local_addr()?; self.sock.local_addr()
}
pub fn update_config(&mut self, cfg: Config) -> Result<Option<Config>, Error> {
let local_addr = self.local_addr()?;
let addr = SocketAddr::new(cfg.server.ip, cfg.server.port); let addr = SocketAddr::new(cfg.server.ip, cfg.server.port);
if local_addr != addr { if local_addr.is_ipv4() != addr.is_ipv4() {
return Ok(Some(cfg));
} else if local_addr != addr {
info!("Listen address: {}", addr); info!("Listen address: {}", addr);
self.sock = UdpSocket::bind(addr).map_err(Error::BindSocket)?; self.sock = UdpSocket::bind(addr).map_err(Error::BindSocket)?;
// make socket interruptable by singals // make socket interruptable by singals
@ -295,7 +330,7 @@ impl MasterServer {
self.hash = cfg.hash; self.hash = cfg.hash;
self.stats.update_config(cfg.stat); self.stats.update_config(cfg.stat);
Ok(()) Ok(None)
} }
pub fn run(&mut self, sig_flag: &AtomicBool) -> Result<(), Error> { pub fn run(&mut self, sig_flag: &AtomicBool) -> Result<(), Error> {
@ -310,14 +345,9 @@ impl MasterServer {
}, },
}; };
let from = match extract_addr(from) { let from = match Addr::extract(from) {
Some(from) => from, Ok(from) => from,
None => { Err(_) => continue,
if from.is_ipv6() {
debug!("{}: IPv6 is not implemented", from);
}
continue;
}
}; };
let src = &buf[..n]; let src = &buf[..n];
@ -596,7 +626,7 @@ impl MasterServer {
} }
} }
fn count_servers(&self, ip: &Ip) -> u16 { fn count_servers(&self, ip: &Addr::Ip) -> u16 {
self.servers.keys().filter(|i| i.ip() == ip).count() as u16 self.servers.keys().filter(|i| i.ip() == ip).count() as u16
} }
@ -645,7 +675,7 @@ impl MasterServer {
fn send_client_to_nat_servers(&self, to: Addr, servers: &[Addr]) -> Result<(), Error> { fn send_client_to_nat_servers(&self, to: Addr, servers: &[Addr]) -> Result<(), Error> {
let mut buf = [0; 64]; let mut buf = [0; 64];
let n = master::ClientAnnounce::new(wrap_addr(to)).encode(&mut buf)?; let n = master::ClientAnnounce::new(to.wrap()).encode(&mut buf)?;
let buf = &buf[..n]; let buf = &buf[..n];
for i in servers { for i in servers {
self.sock.send_to(buf, i)?; self.sock.send_to(buf, i)?;
@ -654,15 +684,20 @@ impl MasterServer {
} }
#[inline] #[inline]
fn is_blocked(&self, ip: &Ip) -> bool { fn is_blocked(&self, ip: &Addr::Ip) -> bool {
self.blocklist.contains(ip) self.blocklist.contains(ip)
} }
fn admin_command(&mut self, cmd: &str) { fn admin_command(&mut self, cmd: &str) {
let args: Vec<_> = cmd.split(' ').collect(); let args: Vec<_> = cmd.split(' ').collect();
fn helper<F: FnMut(&str, Ip)>(args: &[&str], mut op: F) { fn helper<Addr, F>(args: &[&str], mut op: F)
let iter = args.iter().map(|i| (i, i.parse::<Ip>())); where
Addr: AddrExt,
Addr::Ip: std::str::FromStr,
F: FnMut(&str, Addr::Ip),
{
let iter = args.iter().map(|i| (i, i.parse::<Addr::Ip>()));
for (i, ip) in iter { for (i, ip) in iter {
match ip { match ip {
Ok(ip) => op(i, ip), Ok(ip) => op(i, ip),
@ -673,14 +708,14 @@ impl MasterServer {
match args[0] { match args[0] {
"ban" => { "ban" => {
helper(&args[1..], |_, ip| { helper::<Addr, _>(&args[1..], |_, ip| {
if self.blocklist.insert(ip) { if self.blocklist.insert(ip) {
info!("ban ip: {}", ip); info!("ban ip: {}", ip);
} }
}); });
} }
"unban" => { "unban" => {
helper(&args[1..], |_, ip| { helper::<Addr, _>(&args[1..], |_, ip| {
if self.blocklist.remove(&ip) { if self.blocklist.remove(&ip) {
info!("unban ip: {}", ip); info!("unban ip: {}", ip);
} }

Loading…
Cancel
Save