add ip ban check
authoralfadur <mail@none>
Thu, 19 Dec 2019 23:13:58 +0300
changeset 15517 abd5eb807166
parent 15516 b907b9071ec5
child 15518 e705d30e0f10
add ip ban check
rust/hedgewars-server/Cargo.toml
rust/hedgewars-server/src/core/server.rs
rust/hedgewars-server/src/handlers.rs
rust/hedgewars-server/src/server/database.rs
rust/hedgewars-server/src/server/network.rs
--- a/rust/hedgewars-server/Cargo.toml	Tue Dec 17 18:54:17 2019 +0300
+++ b/rust/hedgewars-server/Cargo.toml	Thu Dec 19 23:13:58 2019 +0300
@@ -12,6 +12,7 @@
 [dependencies]
 getopts = "0.2.18"
 rand = "0.6"
+chrono = "0.4"
 mio = "0.6"
 mio-extras = "2.0.5"
 slab = "0.4"
--- a/rust/hedgewars-server/src/core/server.rs	Tue Dec 17 18:54:17 2019 +0300
+++ b/rust/hedgewars-server/src/core/server.rs	Thu Dec 19 23:13:58 2019 +0300
@@ -7,12 +7,11 @@
 use crate::{protocol::messages::HwProtocolMessage::Greeting, utils};
 
 use bitflags::*;
+use chrono::{offset, DateTime};
 use log::*;
-use slab;
+use slab::Slab;
 use std::{borrow::BorrowMut, collections::HashSet, iter, mem::replace, num::NonZeroU16};
 
-type Slab<T> = slab::Slab<T>;
-
 #[derive(Debug)]
 pub enum CreateRoomError {
     InvalidName,
@@ -88,14 +87,58 @@
     pub is_contributor: bool,
 }
 
+struct Ipv4AddrRange {
+    min: [u8; 4],
+    max: [u8; 4],
+}
+
+impl Ipv4AddrRange {
+    fn contains(&self, addr: [u8; 4]) -> bool {
+        (0..4).all(|i| self.min[i] <= addr[i] && addr[i] <= self.max[i])
+    }
+}
+
+struct BanCollection {
+    ban_ips: Vec<Ipv4AddrRange>,
+    ban_timeouts: Vec<DateTime<offset::Utc>>,
+    ban_reasons: Vec<String>,
+}
+
+impl BanCollection {
+    fn new() -> Self {
+        Self {
+            ban_ips: vec![],
+            ban_timeouts: vec![],
+            ban_reasons: vec![],
+        }
+    }
+
+    fn find(&self, addr: [u8; 4]) -> Option<String> {
+        let time = offset::Utc::now();
+        self.ban_ips
+            .iter()
+            .enumerate()
+            .find(|(i, r)| r.contains(addr) && time < self.ban_timeouts[*i])
+            .map(|(i, _)| self.ban_reasons[i].clone())
+    }
+}
+
 pub struct HwAnteroom {
     pub clients: IndexSlab<HwAnteClient>,
+    bans: BanCollection,
 }
 
 impl HwAnteroom {
     pub fn new(clients_limit: usize) -> Self {
         let clients = IndexSlab::with_capacity(clients_limit);
-        HwAnteroom { clients }
+        HwAnteroom {
+            clients,
+            bans: BanCollection::new(),
+        }
+    }
+
+    pub fn find_ip_ban(&self, addr: [u8; 4]) -> Option<String> {
+        self.bans.find(addr)
     }
 
     pub fn add_client(&mut self, client_id: ClientId, salt: String, is_local_admin: bool) {
--- a/rust/hedgewars-server/src/handlers.rs	Tue Dec 17 18:54:17 2019 +0300
+++ b/rust/hedgewars-server/src/handlers.rs	Thu Dec 19 23:13:58 2019 +0300
@@ -354,16 +354,25 @@
     server: &mut HwServer,
     client_id: ClientId,
     response: &mut Response,
+    addr: [u8; 4],
     is_local: bool,
 ) {
-    let mut salt = [0u8; 18];
-    thread_rng().fill_bytes(&mut salt);
+    let ban_reason = Some(addr)
+        .filter(|_| !is_local)
+        .and_then(|a| server.anteroom.find_ip_ban(a));
+    if let Some(reason) = ban_reason {
+        response.add(HwServerMessage::Bye(reason).send_self());
+        response.remove_client(client_id);
+    } else {
+        let mut salt = [0u8; 18];
+        thread_rng().fill_bytes(&mut salt);
 
-    server
-        .anteroom
-        .add_client(client_id, encode(&salt), is_local);
+        server
+            .anteroom
+            .add_client(client_id, encode(&salt), is_local);
 
-    response.add(HwServerMessage::Connected(utils::SERVER_VERSION).send_self());
+        response.add(HwServerMessage::Connected(utils::SERVER_VERSION).send_self());
+    }
 }
 
 pub fn handle_client_loss(server: &mut HwServer, client_id: ClientId, response: &mut Response) {
--- a/rust/hedgewars-server/src/server/database.rs	Tue Dec 17 18:54:17 2019 +0300
+++ b/rust/hedgewars-server/src/server/database.rs	Thu Dec 19 23:13:58 2019 +0300
@@ -7,8 +7,7 @@
 const CHECK_ACCOUNT_EXISTS_QUERY: &str =
     r"SELECT 1 FROM users WHERE users.name = :username LIMIT 1";
 
-const GET_ACCOUNT_QUERY: &str =
-    r"SELECT CASE WHEN users.status = 1 THEN users.pass ELSE '' END,
+const GET_ACCOUNT_QUERY: &str = r"SELECT CASE WHEN users.status = 1 THEN users.pass ELSE '' END,
      (SELECT COUNT(users_roles.rid) FROM users_roles WHERE users.uid = users_roles.uid AND users_roles.rid = 3),
      (SELECT COUNT(users_roles.rid) FROM users_roles WHERE users.uid = users_roles.uid AND users_roles.rid = 13)
      FROM users WHERE users.name = :username";
--- a/rust/hedgewars-server/src/server/network.rs	Tue Dec 17 18:54:17 2019 +0300
+++ b/rust/hedgewars-server/src/server/network.rs	Thu Dec 19 23:13:58 2019 +0300
@@ -523,13 +523,18 @@
             response.add(Redirect(self.ssl.listener.local_addr().unwrap().port()).send_self())
         }
 
-        handlers::handle_client_accept(
-            &mut self.server,
-            client_id,
-            &mut response,
-            self.clients[client_id].peer_addr.ip().is_loopback(),
-        );
-        self.handle_response(response, poll);
+        if let IpAddr::V4(addr) = self.clients[client_id].peer_addr.ip() {
+            handlers::handle_client_accept(
+                &mut self.server,
+                client_id,
+                &mut response,
+                addr.octets(),
+                addr.is_loopback(),
+            );
+            self.handle_response(response, poll);
+        } else {
+            todo!("implement something")
+        }
     }
 
     pub fn accept_client(&mut self, poll: &Poll, server_token: mio::Token) -> io::Result<()> {