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 @@ @@ -1,22 +1,18 @@
// SPDX-License-Identifier: GPL-3.0-only
// SPDX-FileCopyrightText: 2023 Denis Drakhnia <numas13@gmail.com>
use std::fs;
use std::io;
use std::net::IpAddr;
use std::path::Path;
use std::{
fs, io,
net::{IpAddr, Ipv4Addr},
path::Path,
};
use log::LevelFilter;
use serde::{de::Error as _, Deserialize, Deserializer};
use thiserror::Error;
use xash3d_protocol::admin;
use xash3d_protocol::filter::Version;
#[cfg(not(feature = "ipv6"))]
pub const DEFAULT_MASTER_SERVER_IP: IpAddr = IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED);
use xash3d_protocol::{admin, filter::Version};
#[cfg(feature = "ipv6")]
pub const DEFAULT_MASTER_SERVER_IP: IpAddr = IpAddr::V6(std::net::Ipv6Addr::UNSPECIFIED);
pub const DEFAULT_MASTER_SERVER_IP: IpAddr = IpAddr::V4(Ipv4Addr::UNSPECIFIED);
pub const DEFAULT_MASTER_SERVER_PORT: u16 = 27010;
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}; @@ -23,7 +23,7 @@ use signal_hook::{consts::signal::*, flag as signal_flag};
use crate::cli::Cli;
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> {
let mut cfg = match cli.config_path {
@ -68,7 +68,7 @@ fn run() -> Result<(), Error> { @@ -68,7 +68,7 @@ fn run() -> Result<(), Error> {
process::exit(1);
});
let mut master = MasterServer::new(cfg)?;
let mut master = Master::new(cfg)?;
let sig_flag = Arc::new(AtomicBool::new(false));
// XXX: Windows does not support SIGUSR1.
#[cfg(not(windows))]

167
master/src/master_server.rs

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

Loading…
Cancel
Save