Generated
+67
@@ -81,6 +81,12 @@ version = "1.8.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
|
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.10.4"
|
version = "0.10.4"
|
||||||
@@ -226,6 +232,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tower-http",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -349,6 +356,12 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"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]]
|
[[package]]
|
||||||
name = "httparse"
|
name = "httparse"
|
||||||
version = "1.10.1"
|
version = "1.10.1"
|
||||||
@@ -432,6 +445,16 @@ version = "0.3.17"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
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]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "1.2.2"
|
version = "1.2.2"
|
||||||
@@ -820,6 +843,19 @@ dependencies = [
|
|||||||
"tungstenite",
|
"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]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
@@ -836,6 +872,31 @@ dependencies = [
|
|||||||
"tracing",
|
"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]]
|
[[package]]
|
||||||
name = "tower-layer"
|
name = "tower-layer"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
@@ -890,6 +951,12 @@ version = "1.20.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
|
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicase"
|
||||||
|
version = "2.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.24"
|
version = "1.0.24"
|
||||||
|
|||||||
@@ -12,3 +12,4 @@ tokio = { version = "1.53.1", features = ["rt", "rt-multi-thread", "macros", "sy
|
|||||||
ed25519-dalek = { version = "2", features = ["rand_core"] }
|
ed25519-dalek = { version = "2", features = ["rand_core"] }
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
bs58 = "0.5.1"
|
bs58 = "0.5.1"
|
||||||
|
tower-http = { version = "0.7.0", features = ["fs", "cors"] }
|
||||||
|
|||||||
+16
-3
@@ -2,7 +2,7 @@ use std::{collections::HashSet, path::PathBuf};
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::protocol::ServerMeta;
|
use crate::types::{Channel, ChannelKind, ServerMeta};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
@@ -18,13 +18,26 @@ impl Config {
|
|||||||
meta: ServerMeta {
|
meta: ServerMeta {
|
||||||
name: "New Server".to_string(),
|
name: "New Server".to_string(),
|
||||||
description: String::new(),
|
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,
|
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()]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-1
@@ -2,6 +2,7 @@ pub mod config;
|
|||||||
pub mod protocol;
|
pub mod protocol;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
pub mod signature;
|
pub mod signature;
|
||||||
|
pub mod types;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
net::{IpAddr, Ipv4Addr, SocketAddr},
|
net::{IpAddr, Ipv4Addr, SocketAddr},
|
||||||
@@ -14,6 +15,10 @@ use axum::{
|
|||||||
response::Response,
|
response::Response,
|
||||||
routing::{any, get},
|
routing::{any, get},
|
||||||
};
|
};
|
||||||
|
use tower_http::{
|
||||||
|
cors::{Any, CorsLayer},
|
||||||
|
services::ServeFile,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::server::Server;
|
use crate::server::Server;
|
||||||
|
|
||||||
@@ -21,10 +26,17 @@ use crate::server::Server;
|
|||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let server = Server::new().await?;
|
let server = Server::new().await?;
|
||||||
|
|
||||||
|
let cors = CorsLayer::new()
|
||||||
|
.allow_origin(Any)
|
||||||
|
.allow_methods(Any)
|
||||||
|
.allow_headers(Any);
|
||||||
|
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/meta", get(meta))
|
.route("/meta", get(meta))
|
||||||
.route("/", any(ws_handler))
|
.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(
|
let listener = tokio::net::TcpListener::bind(SocketAddr::new(
|
||||||
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
|
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)),
|
||||||
|
|||||||
@@ -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(
|
send_socket(
|
||||||
&mut socket,
|
&mut socket,
|
||||||
&ClientMethod::Error {
|
&ClientMethod::Error {
|
||||||
@@ -53,7 +53,8 @@ impl UserConnections {
|
|||||||
return Err(anyhow::anyhow!("Client tampstamp wasn't correct"));
|
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(
|
send_socket(
|
||||||
&mut socket,
|
&mut socket,
|
||||||
&ClientMethod::Error {
|
&ClientMethod::Error {
|
||||||
@@ -100,10 +101,9 @@ impl UserConnections {
|
|||||||
&mut socket,
|
&mut socket,
|
||||||
&ClientMethod::Initialized {
|
&ClientMethod::Initialized {
|
||||||
public_key: crate::signature::to_string(&server.key.verifying_key()),
|
public_key: crate::signature::to_string(&server.key.verifying_key()),
|
||||||
signature: server
|
signature: crate::signature::to_string_sig(&server.key.sign(
|
||||||
.key
|
format!("{server_timestamp}@{hostname}@{public_key_string}").as_bytes(),
|
||||||
.sign(format!("{timestamp}@{hostname}@{public_key_string}").as_bytes())
|
)),
|
||||||
.to_string(),
|
|
||||||
|
|
||||||
timestamp: server_timestamp,
|
timestamp: server_timestamp,
|
||||||
hostname,
|
hostname,
|
||||||
|
|||||||
+2
-9
@@ -4,17 +4,10 @@ use axum::extract::ws::{Message, Utf8Bytes, WebSocket};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::types::ClientMeta;
|
||||||
|
|
||||||
pub mod initialize;
|
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)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[serde(tag = "method")]
|
#[serde(tag = "method")]
|
||||||
pub enum ClientMethod {
|
pub enum ClientMethod {
|
||||||
|
|||||||
+14
-8
@@ -12,7 +12,8 @@ use tokio::sync::Mutex;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
protocol::{ClientMeta, ClientMethod, read_loop, send_socket},
|
protocol::{ClientMethod, read_loop, send_socket},
|
||||||
|
types::ClientMeta,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct UserConnections {
|
pub struct UserConnections {
|
||||||
@@ -49,7 +50,7 @@ impl Server {
|
|||||||
|
|
||||||
let mut clients_meta = s.clients.lock().await;
|
let mut clients_meta = s.clients.lock().await;
|
||||||
|
|
||||||
let client_meta =
|
let clients =
|
||||||
clients_meta
|
clients_meta
|
||||||
.entry(public_key)
|
.entry(public_key)
|
||||||
.or_insert_with(|| UserConnections {
|
.or_insert_with(|| UserConnections {
|
||||||
@@ -59,18 +60,23 @@ impl Server {
|
|||||||
connections: HashMap::new(),
|
connections: HashMap::new(),
|
||||||
});
|
});
|
||||||
|
|
||||||
client_meta.connections.insert(
|
let conid = clients
|
||||||
client_meta
|
.counter
|
||||||
.counter
|
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||||
.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
|
|
||||||
client.clone(),
|
clients.connections.insert(conid, client.clone());
|
||||||
);
|
|
||||||
|
|
||||||
if let Err(e) = read_loop(&client).await {
|
if let Err(e) = read_loop(&client).await {
|
||||||
eprintln!("Failed to handle client: {e}");
|
eprintln!("Failed to handle client: {e}");
|
||||||
} else {
|
} else {
|
||||||
println!("Client connection closed")
|
println!("Client connection closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clients.connections.remove(&conid);
|
||||||
|
|
||||||
|
if clients.connections.len() == 0 {
|
||||||
|
clients_meta.remove(&public_key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|||||||
+5
-1
@@ -24,6 +24,10 @@ pub fn to_string(key: &VerifyingKey) -> String {
|
|||||||
bs58::encode(key.to_bytes()).into_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<VerifyingKey> {
|
pub fn from_string(key: &str) -> anyhow::Result<VerifyingKey> {
|
||||||
Ok(VerifyingKey::from_bytes(
|
Ok(VerifyingKey::from_bytes(
|
||||||
&bs58::decode(key)
|
&bs58::decode(key)
|
||||||
@@ -38,6 +42,6 @@ pub fn from_string_sig(signature: &str) -> anyhow::Result<Signature> {
|
|||||||
&bs58::decode(signature)
|
&bs58::decode(signature)
|
||||||
.into_vec()?
|
.into_vec()?
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| anyhow::anyhow!("Invalid public key"))?,
|
.map_err(|_| anyhow::anyhow!("Invalid signature"))?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<Channel>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "kind")]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub enum ChannelKind {
|
||||||
|
Text,
|
||||||
|
Category { channels: Vec<Channel> },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Channel {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub data: ChannelKind,
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user