|
|
@ -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); |
|
|
|
} |
|
|
|
} |
|
|
|