diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..70a47c5 Binary files /dev/null and b/.DS_Store differ diff --git a/Cargo.lock b/Cargo.lock index b649c17..12ee0c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,6 +81,12 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[package]] +name = "bitflags" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" + [[package]] name = "block-buffer" version = "0.10.4" @@ -226,6 +232,7 @@ dependencies = [ "serde", "serde_json", "tokio", + "tower-http", ] [[package]] @@ -349,6 +356,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + [[package]] name = "httparse" version = "1.10.1" @@ -432,6 +445,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "mio" version = "1.2.2" @@ -820,6 +843,19 @@ dependencies = [ "tungstenite", ] +[[package]] +name = "tokio-util" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "tower" version = "0.5.3" @@ -836,6 +872,31 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower-http" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b11f75e912b0c2be01b63d8cf8057b8c3f97cf34abb3d431a3a4c8675498e233" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -890,6 +951,12 @@ version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-ident" version = "1.0.24" diff --git a/Cargo.toml b/Cargo.toml index 3072733..ec9225b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,4 @@ tokio = { version = "1.53.1", features = ["rt", "rt-multi-thread", "macros", "sy ed25519-dalek = { version = "2", features = ["rand_core"] } rand = "0.8" bs58 = "0.5.1" +tower-http = { version = "0.7.0", features = ["fs", "cors"] } diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..cd31e2b Binary files /dev/null and b/icon.png differ diff --git a/src/config.rs b/src/config.rs index 7bf6349..ea4141b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,7 +2,7 @@ use std::{collections::HashSet, path::PathBuf}; use serde::{Deserialize, Serialize}; -use crate::protocol::ServerMeta; +use crate::types::{Channel, ChannelKind, ServerMeta}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Config { @@ -18,13 +18,26 @@ impl Config { meta: ServerMeta { name: "New Server".to_string(), description: String::new(), + + channels: vec![Channel { + id: "text-channels".to_string(), + name: "Text Channels".to_string(), + + data: ChannelKind::Category { + channels: vec![Channel { + id: "general".to_string(), + name: "General".to_string(), + data: ChannelKind::Text, + }], + }, + }], }, port: 3415, - public_hostname: "localhost:3000".to_string(), + public_hostname: "localhost:3415".to_string(), - hostnames: HashSet::from_iter(["localhost:3000".to_string()]), + hostnames: HashSet::from_iter(["localhost:3415".to_string()]), } } diff --git a/src/main.rs b/src/main.rs index cd992dd..25696ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ pub mod config; pub mod protocol; pub mod server; pub mod signature; +pub mod types; use std::{ net::{IpAddr, Ipv4Addr, SocketAddr}, @@ -14,6 +15,10 @@ use axum::{ response::Response, routing::{any, get}, }; +use tower_http::{ + cors::{Any, CorsLayer}, + services::ServeFile, +}; use crate::server::Server; @@ -21,10 +26,17 @@ use crate::server::Server; async fn main() -> anyhow::Result<()> { let server = Server::new().await?; + let cors = CorsLayer::new() + .allow_origin(Any) + .allow_methods(Any) + .allow_headers(Any); + let app = Router::new() .route("/meta", get(meta)) .route("/", any(ws_handler)) - .with_state(server.clone()); + .route_service("/icon", ServeFile::new("./icon.png")) + .with_state(server.clone()) + .layer(cors); let listener = tokio::net::TcpListener::bind(SocketAddr::new( IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), diff --git a/src/protocol/initialize.rs b/src/protocol/initialize.rs index 2a50b8b..650cc48 100644 --- a/src/protocol/initialize.rs +++ b/src/protocol/initialize.rs @@ -37,9 +37,9 @@ impl UserConnections { )); }; - let server_timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(); + let server_timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis() as u64; - if server_timestamp - timestamp > 2 { + if server_timestamp.saturating_sub(timestamp) > 2000 { send_socket( &mut socket, &ClientMethod::Error { @@ -53,7 +53,8 @@ impl UserConnections { return Err(anyhow::anyhow!("Client tampstamp wasn't correct")); } - if !server.config.hostnames.contains(&hostname) { + if hostname != server.config.public_hostname || !server.config.hostnames.contains(&hostname) + { send_socket( &mut socket, &ClientMethod::Error { @@ -100,10 +101,9 @@ impl UserConnections { &mut socket, &ClientMethod::Initialized { public_key: crate::signature::to_string(&server.key.verifying_key()), - signature: server - .key - .sign(format!("{timestamp}@{hostname}@{public_key_string}").as_bytes()) - .to_string(), + signature: crate::signature::to_string_sig(&server.key.sign( + format!("{server_timestamp}@{hostname}@{public_key_string}").as_bytes(), + )), timestamp: server_timestamp, hostname, diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 0503573..a8ee7c5 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -4,17 +4,10 @@ use axum::extract::ws::{Message, Utf8Bytes, WebSocket}; use serde::{Deserialize, Serialize}; use tokio::sync::Mutex; +use crate::types::ClientMeta; + pub mod initialize; -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ClientMeta {} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ServerMeta { - pub name: String, - pub description: String, -} - #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "method")] pub enum ClientMethod { diff --git a/src/server.rs b/src/server.rs index f76a9e4..41365e0 100644 --- a/src/server.rs +++ b/src/server.rs @@ -12,7 +12,8 @@ use tokio::sync::Mutex; use crate::{ config::Config, - protocol::{ClientMeta, ClientMethod, read_loop, send_socket}, + protocol::{ClientMethod, read_loop, send_socket}, + types::ClientMeta, }; pub struct UserConnections { @@ -49,7 +50,7 @@ impl Server { let mut clients_meta = s.clients.lock().await; - let client_meta = + let clients = clients_meta .entry(public_key) .or_insert_with(|| UserConnections { @@ -59,18 +60,23 @@ impl Server { connections: HashMap::new(), }); - client_meta.connections.insert( - client_meta - .counter - .fetch_add(1, std::sync::atomic::Ordering::Relaxed), - client.clone(), - ); + let conid = clients + .counter + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); + + clients.connections.insert(conid, client.clone()); if let Err(e) = read_loop(&client).await { eprintln!("Failed to handle client: {e}"); } else { println!("Client connection closed") } + + clients.connections.remove(&conid); + + if clients.connections.len() == 0 { + clients_meta.remove(&public_key); + } } Err(e) => { diff --git a/src/signature.rs b/src/signature.rs index 851b9eb..3dd0753 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -24,6 +24,10 @@ pub fn to_string(key: &VerifyingKey) -> String { bs58::encode(key.to_bytes()).into_string() } +pub fn to_string_sig(signature: &Signature) -> String { + bs58::encode(signature.to_bytes()).into_string() +} + pub fn from_string(key: &str) -> anyhow::Result { Ok(VerifyingKey::from_bytes( &bs58::decode(key) @@ -38,6 +42,6 @@ pub fn from_string_sig(signature: &str) -> anyhow::Result { &bs58::decode(signature) .into_vec()? .try_into() - .map_err(|_| anyhow::anyhow!("Invalid public key"))?, + .map_err(|_| anyhow::anyhow!("Invalid signature"))?, )) } diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..98fde83 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,29 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ClientMeta {} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ServerMeta { + pub name: String, + pub description: String, + pub channels: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "kind")] +#[serde(rename_all = "camelCase")] +pub enum ChannelKind { + Text, + Category { channels: Vec }, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Channel { + pub id: String, + pub name: String, + + #[serde(flatten)] + pub data: ChannelKind, +}