Преглед изворни кода

Server side sanity checks

- Added a quit button
- Misc changes
- Server checks for valid messages via checksum
Rain пре 1 месец
родитељ
комит
ab4741b407
5 измењених фајлова са 58 додато и 11 уклоњено
  1. 15 2
      client/src/actions.rs
  2. 2 0
      client/src/main.rs
  3. 4 4
      client/src/message.rs
  4. 17 1
      client/src/ui.rs
  5. 20 4
      server/src/main.rs

+ 15 - 2
client/src/actions.rs

@@ -1,5 +1,5 @@
 use cursive::{views::TextArea, Cursive};
-use utils::{hash::Hashable as _, serialize::Serialize as _};
+use utils::{binary::checksum, hash::Hashable as _, serialize::Serialize as _};
 
 use crate::{get_appdata, message::Message, ui, Appdata};
 
@@ -18,7 +18,6 @@ pub fn on_message_click(siv: &mut Cursive, message_id: &str) {
         ui::copyable(
             siv,
             ui::Labels::Message.localize(appdata.persistent_data.language),
-            // TODO: Use labels
             format!(
                 "{}: {}\n{}: {}\n{}: {}\n{}: {}",
                 ui::Labels::Time.localize(language), message.time,
@@ -102,6 +101,7 @@ fn fix_url(url: &str) -> Result<String, NetworkError> {
     if !url.starts_with("http://") && !url.starts_with("https://") {
         url = format!("http://{}", url);
     }
+
     if !url.ends_with('/') {
         url.push('/');
     }
@@ -116,6 +116,7 @@ pub fn send_message(siv: &mut Cursive, message: Message) -> Result<(), NetworkEr
 
     let mut bytes = message.serialize_checked();
     utils::aes::encrypt_cbc(&mut bytes, &password);
+    bytes.push(checksum(&bytes));
 
     let str = utils::binary::bin2hex(bytes);
 
@@ -156,6 +157,18 @@ pub fn load_messages(siv: &mut Cursive) -> Result<(), NetworkError> {
             continue;
         };
 
+        if message_ser_bin.len() < 2 {
+            continue;
+        }
+
+        // Checksum before decryption
+        if checksum(&message_ser_bin[..message_ser_bin.len()-1]) != message_ser_bin[message_ser_bin.len()-1] {
+            continue;
+        }
+
+        // Remove checksum
+        message_ser_bin.pop();
+
         if utils::aes::decrypt_cbc(&mut message_ser_bin, &password).is_err() {
             continue;
         }

+ 2 - 0
client/src/main.rs

@@ -25,6 +25,8 @@ const DEFAULT_USERNAME_PREFIX: &str = "Myst";
 const DEFAULT_CHANNEL: &str = "Root";
 const DEFAULT_PASSWORD: &str = "null";
 
+const INVALID_MESSAGE_IDENT: &str = "ERR";
+
 const LOGO: &str = include_str!("../assets/logo.txt");
 
 const SAVE_FILE: &str = "savedata.bin";

+ 4 - 4
client/src/message.rs

@@ -4,7 +4,7 @@ use utils::{
     time::time_millis,
 };
 
-use crate::{get_appdata, MAX_MESSAGE_LENGTH, MAX_USERNAME_LENGTH};
+use crate::{INVALID_MESSAGE_IDENT, MAX_MESSAGE_LENGTH, MAX_USERNAME_LENGTH, get_appdata};
 
 #[derive(Debug, Clone)]
 pub struct Message {
@@ -117,17 +117,17 @@ impl From<Message> for MessageSanitized {
     fn from(value: Message) -> Self {
         let mut sender = value.sender.clone();
         if !is_valid_username(&sender) {
-            sender = format!("Invl@{}", &value.sender.hash().to_string()[..8]);
+            sender = format!("{}@{}", INVALID_MESSAGE_IDENT, &value.sender.hash().to_string()[..8]);
         }
 
         let mut channel = value.channel.clone();
         if !is_valid_username(&channel) {
-            channel = format!("Invl@{}", &value.channel.hash().to_string()[..8]);
+            channel = format!("{}@{}", INVALID_MESSAGE_IDENT, &value.channel.hash().to_string()[..8]);
         }
 
         let mut content = value.content.clone();
         if !is_valid_message(&value.content) {
-            content = format!("Invalid@{}", &value.content.hash().to_string());
+            content = format!("{}@{}", INVALID_MESSAGE_IDENT, &value.content.hash().to_string());
         }
 
         MessageSanitized {

+ 17 - 1
client/src/ui.rs

@@ -29,6 +29,7 @@ pub const CURRENT_USERNAME_PANEL_ID: &str = "current_username_view_id";
 pub const USERNAME_FIELD_ID: &str = "username_field_id";
 pub const USERNAME_BUTTON_ID: &str = "username_button_id";
 pub const REFRESH_BUTTON_ID: &str = "refresh_button_id";
+pub const QUIT_BUTTON_ID: &str = "quit_button_id";
 pub const BLOCKED_WORDS_BUTTON_ID: &str = "blocked_words_view_id";
 pub const SERVER_SETTINGS_ADDRESS_FIELD_ID: &str = "server_settings_address_field_id";
 pub const SERVER_SETTINGS_REFRESH_FIELD_ID: &str = "server_settings_refresh_field_id";
@@ -79,6 +80,7 @@ pub enum Labels {
     Time,
     Sender,
     Content,
+    QuitButton,
 }
 
 #[derive(Debug, Clone, Copy)]
@@ -89,7 +91,7 @@ pub enum Language {
 }
 
 impl Labels {
-    // TODO: Double check the translations
+    // TODO (low): Double check the translations
     pub fn localize<'a>(&self, language: Language) -> String {
         let buf: [String; 3];
 
@@ -210,6 +212,7 @@ impl Labels {
 
                 [buf[0].as_str(), buf[1].as_str(), buf[2].as_str()]
             }
+            Labels::QuitButton => ["Quit", "Verlaten", "終了する"],
             Labels::RefreshButton => ["Refresh", "Vernieuwen", "更新する"],
             Labels::InvalidUsernameExplination => {
                 buf = [
@@ -550,6 +553,11 @@ pub fn visual_update(siv: &mut Cursive) {
             .set_label(Labels::SetUsername.localize(language));
     });
 
+    siv.call_on_name(QUIT_BUTTON_ID, |view: &mut NamedView<Button>| {
+        view.get_mut()
+            .set_label(Labels::QuitButton.localize(language));
+    });
+
     siv.call_on_name(REFRESH_BUTTON_ID, |view: &mut NamedView<Button>| {
         view.get_mut()
             .set_label(Labels::RefreshButton.localize(language));
@@ -788,6 +796,12 @@ pub fn setup_ui(siv: &mut Cursive) -> LinearLayout {
     })
     .with_name(USERNAME_BUTTON_ID);
 
+    // Quit button
+    let quit_button = Button::new("", move |siv| {
+        siv.quit();
+    })
+    .with_name(QUIT_BUTTON_ID);
+
     // Refresh button
     let refresh_button = Button::new("", move |siv| {
         if let Err(e) = actions::load_messages(siv) {
@@ -1025,6 +1039,8 @@ pub fn setup_ui(siv: &mut Cursive) -> LinearLayout {
                 .child(server_settings_button)
                 .child(DummyView.full_width())
                 .child(refresh_button)
+                .child(DummyView.full_width())
+                .child(quit_button)
                 .full_width(),
         );
 

+ 20 - 4
server/src/main.rs

@@ -1,6 +1,7 @@
+use std::env::args;
+
 use utils::{
-    http::{self, Request, Response},
-    strings::StaticString,
+    binary::checksum, http::{self, Request, Response}, strings::StaticString
 };
 
 const MAX_MESSAGE_LENGTH: usize = 2048;
@@ -55,6 +56,19 @@ fn index_get_sync(state: &mut Appdata, _: Request) -> Response {
 fn index_post_sync(state: &mut Appdata, request: Request) -> Response {
     let body = request.body;
 
+    let Some(message_ser_bin) = utils::binary::hex2bin(&body) else {
+        return Response::bad_request();
+    };
+
+    if message_ser_bin.len() < 2 {
+        return Response::bad_request();
+    }
+
+    // Checksum before decryption
+    if checksum(&message_ser_bin[..message_ser_bin.len()-1]) != message_ser_bin[message_ser_bin.len()-1] {
+        return Response::bad_request();
+    }
+
     println!("Received ({}): {:#?}", body.len(), body);
 
     match state.insert_message(body.clone()) {
@@ -76,10 +90,12 @@ fn router(state: &mut Appdata, request: Request) -> Response {
 }
 
 fn main() -> http::Result<()> {
-    println!("Starting...");
+    let port = args().nth(1).unwrap_or("8080".to_string());
+
+    println!("Starting on port {}", port);
 
     let appdata = Appdata::new();
-    http::run("127.0.0.1:8080", appdata, router)?;
+    http::run(&format!("127.0.0.1:{}", port), appdata, router)?;
 
     Ok(())
 }