mirror of
https://github.com/RustySamovar/RustySamovar.git
synced 2024-11-24 11:23:33 +00:00
Split project into gateserver (Kalitka) and gameserver (RustySamovar)
This commit is contained in:
parent
74f5e82d66
commit
f312e61323
46
Cargo.toml
46
Cargo.toml
@ -1,44 +1,2 @@
|
|||||||
[package]
|
[workspace]
|
||||||
name = "RustySamovar"
|
members = ["RustySamovar", "Kalitka"]
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[features]
|
|
||||||
raw_packet_dump = []
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
kcp = { path = "kcp" }
|
|
||||||
mhycrypt = { path = "mhycrypt" }
|
|
||||||
proto = { path = "proto" }
|
|
||||||
lua_serde = { path = "lua_serde" }
|
|
||||||
packet-processor-macro = { path = "packet-processor-macro" }
|
|
||||||
packet-processor = { path = "packet-processor" }
|
|
||||||
rs-ipc = { path = "rs-ipc" }
|
|
||||||
excel-hash-wrapper-macro = { path = "excel-hash-wrapper-macro" }
|
|
||||||
|
|
||||||
prost = "0.8"
|
|
||||||
bytes = "1.1.0"
|
|
||||||
base64 = "0.13.0"
|
|
||||||
tokio = { version = "1", features = ["full"] }
|
|
||||||
futures = "0.3"
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
|
||||||
serde_json = "1.0"
|
|
||||||
pretty_env_logger = "0.4"
|
|
||||||
num-traits = "0.2"
|
|
||||||
num-derive = "0.3"
|
|
||||||
pretty-hex = "0.2"
|
|
||||||
sea-orm = { version = "0.7", features = [ "sqlx-all", "runtime-async-std-native-tls", "debug-print" ] }
|
|
||||||
#hostname = "0.3"
|
|
||||||
#local-ip-address = "0.4"
|
|
||||||
chrono = "0.4"
|
|
||||||
rand = "0.8"
|
|
||||||
tracing = "0.1"
|
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
|
||||||
openssl = { version = "0.10", features = ["vendored"] }
|
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
|
||||||
openssl = "0.10"
|
|
||||||
|
46
Kalitka/Cargo.toml
Normal file
46
Kalitka/Cargo.toml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
[package]
|
||||||
|
name = "Kalitka"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[features]
|
||||||
|
raw_packet_dump = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
kcp = { path = "../kcp" }
|
||||||
|
mhycrypt = { path = "../mhycrypt" }
|
||||||
|
proto = { path = "../proto" }
|
||||||
|
#lua_serde = { path = "../lua_serde" }
|
||||||
|
packet-processor-macro = { path = "../packet-processor-macro" }
|
||||||
|
packet-processor = { path = "../packet-processor" }
|
||||||
|
rs-ipc = { path = "../rs-ipc" }
|
||||||
|
#excel-hash-wrapper-macro = { path = "../excel-hash-wrapper-macro" }
|
||||||
|
rs-nodeconf = { path = "../rs-nodeconf" }
|
||||||
|
rs-utils = { path = "../rs-utils" }
|
||||||
|
|
||||||
|
prost = "0.8"
|
||||||
|
bytes = "1.1.0"
|
||||||
|
base64 = "0.13.0"
|
||||||
|
#tokio = { version = "1", features = ["full"] }
|
||||||
|
#futures = "0.3"
|
||||||
|
#serde = { version = "1.0", features = ["derive"] }
|
||||||
|
#serde_json = "1.0"
|
||||||
|
pretty_env_logger = "0.4"
|
||||||
|
num-traits = "0.2"
|
||||||
|
num-derive = "0.3"
|
||||||
|
pretty-hex = "0.2"
|
||||||
|
#sea-orm = { version = "0.7", features = [ "sqlx-all", "runtime-async-std-native-tls", "debug-print" ] }
|
||||||
|
#hostname = "0.3"
|
||||||
|
#local-ip-address = "0.4"
|
||||||
|
chrono = "0.4"
|
||||||
|
rand = "0.8"
|
||||||
|
tracing = "0.1"
|
||||||
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
openssl = { version = "0.10", features = ["vendored"] }
|
||||||
|
|
||||||
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
openssl = "0.10"
|
27
Kalitka/src/main.rs
Normal file
27
Kalitka/src/main.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
extern crate pretty_env_logger;
|
||||||
|
|
||||||
|
extern crate tracing_subscriber;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate num_derive;
|
||||||
|
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
mod server;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
use server::NetworkServer;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
//pretty_env_logger::init();
|
||||||
|
|
||||||
|
//let mut rt_main = tokio::runtime::Runtime::new().unwrap();
|
||||||
|
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_max_level(tracing::Level::DEBUG)
|
||||||
|
.with_test_writer()
|
||||||
|
.init();
|
||||||
|
|
||||||
|
let mut ns = NetworkServer::new("0.0.0.0", 4242).unwrap();
|
||||||
|
ns.run().expect("Failed to serve!");
|
||||||
|
}
|
137
Kalitka/src/server/auth_manager.rs
Normal file
137
Kalitka/src/server/auth_manager.rs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
use openssl::hash::MessageDigest;
|
||||||
|
use openssl::pkey::PKey;
|
||||||
|
use openssl::rsa::Padding;
|
||||||
|
use openssl::sign::Signer;
|
||||||
|
use std::sync::mpsc;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
use prost::Message;
|
||||||
|
|
||||||
|
use rs_ipc::{IpcMessage, PushSocket};
|
||||||
|
|
||||||
|
use packet_processor_macro::*;
|
||||||
|
#[macro_use]
|
||||||
|
use packet_processor::*;
|
||||||
|
use rs_nodeconf::NodeConfig;
|
||||||
|
|
||||||
|
#[packet_processor(GetPlayerTokenReq)]
|
||||||
|
pub struct AuthManager {
|
||||||
|
conv_to_user: HashMap<u32, u32>,
|
||||||
|
user_to_conv: HashMap<u32, u32>,
|
||||||
|
//packets_to_send_tx: mpsc::Sender<IpcMessage>,
|
||||||
|
packets_to_send_tx: PushSocket,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuthManager {
|
||||||
|
pub const SPOOFED_PLAYER_UID: u32 = 1337;
|
||||||
|
|
||||||
|
pub fn new(node_config: &NodeConfig) -> AuthManager {
|
||||||
|
let mut am = AuthManager {
|
||||||
|
conv_to_user: HashMap::new(),
|
||||||
|
user_to_conv: HashMap::new(),
|
||||||
|
packet_callbacks: HashMap::new(),
|
||||||
|
packets_to_send_tx: node_config.connect_out_queue().unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
|
am.register();
|
||||||
|
|
||||||
|
return am;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_get_player_token(&mut self, conv: u32, metadata: &proto::PacketHead, req: &proto::GetPlayerTokenReq, rsp: &mut proto::GetPlayerTokenRsp) {
|
||||||
|
let seed: u64 = 0xBABECAFEF00D; // TODO: use real value!
|
||||||
|
let client_hardcoded_seed: u64 = 0x12345678;
|
||||||
|
let uid = self.get_uid_by_account_id(req.account_uid.parse().unwrap());
|
||||||
|
|
||||||
|
rsp.account_type = req.account_type;
|
||||||
|
rsp.account_uid = req.account_uid.clone();
|
||||||
|
rsp.token = req.account_token.clone();
|
||||||
|
rsp.secret_key_seed = seed; // TODO: temporary workaround!
|
||||||
|
rsp.uid = uid;
|
||||||
|
|
||||||
|
if req.key_id > 0 { // TODO: detect client version properly!
|
||||||
|
// Versions 2.7.5x+ use different algorithm for key initialization
|
||||||
|
|
||||||
|
// TODO: as of now (2022-05-16) this algorithm here is more of a PoC, because we can't really sign the data
|
||||||
|
// or decrypt the client seed we're getting from the client.
|
||||||
|
//
|
||||||
|
// Connecting to our server still requires patching the client to disable signature verification and hardcoding
|
||||||
|
// some known client seed value. This will allow the patched client to connect to official servers (beware of
|
||||||
|
// the ban for modding the client!)
|
||||||
|
//
|
||||||
|
// An alternative approach to hardcoding the client seed would be to employ RCE in WindSeedClientNotify to extract
|
||||||
|
// the seed from the client itself. That still would require patching the client though (to allow invalid signatures),
|
||||||
|
// so it's of a very little difference to us.
|
||||||
|
//
|
||||||
|
// Another alternative is to replace keys inside global-metadata.dat file, but that requires writing an encryption
|
||||||
|
// tool. While still possible, it's tiresome, and won't allow patched client to connect to official server without
|
||||||
|
// switching back and forth between two versions of global-metadata.dat file.
|
||||||
|
|
||||||
|
let key_id = req.key_id as u8;
|
||||||
|
|
||||||
|
let rsa_key_collection = mhycrypt::load_rsa_keys("RSAConfig", "keys");
|
||||||
|
let keys = match rsa_key_collection.get(&key_id) {
|
||||||
|
Some(keys) => keys,
|
||||||
|
None => panic!("Unknown key ID {}!", key_id),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Decrypt received client seed
|
||||||
|
|
||||||
|
let client_seed_encrypted = base64::decode(&req.client_rand_key).unwrap();
|
||||||
|
|
||||||
|
let mut dec_buf: Vec<u8> = vec![0; 256];
|
||||||
|
|
||||||
|
let client_seed = match keys.signing_key.private_decrypt(&client_seed_encrypted, &mut dec_buf, Padding::PKCS1) {
|
||||||
|
Ok(seed_size) => {
|
||||||
|
// Note: from_be_bytes here, because client seems to swap order of bytes for the seed
|
||||||
|
u64::from_be_bytes(dec_buf[0..seed_size].try_into().unwrap())
|
||||||
|
},
|
||||||
|
Err(e) => { // TODO: must panic here!
|
||||||
|
println!("Error decrypting client seed: {}", e);
|
||||||
|
client_hardcoded_seed // TODO: temporary workaround!
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Encrypt server seed which we'll use in negotiating with the client
|
||||||
|
|
||||||
|
let mut enc_buf: Vec<u8> = vec![0; 256];
|
||||||
|
|
||||||
|
// Note: to_be_bytes here, because client seems to swap order of bytes for the seed
|
||||||
|
let seed_bytes = (seed ^ client_seed).to_be_bytes();
|
||||||
|
|
||||||
|
let len = keys.encrypt_key.public_encrypt(&seed_bytes, &mut enc_buf, Padding::PKCS1).unwrap();
|
||||||
|
|
||||||
|
// Sign it
|
||||||
|
let keypair = PKey::from_rsa(keys.signing_key.clone()).unwrap();
|
||||||
|
let mut signer = Signer::new(MessageDigest::sha256(), &keypair).unwrap();
|
||||||
|
let signature = signer.sign_oneshot_to_vec(&seed_bytes).unwrap();
|
||||||
|
|
||||||
|
rsp.key_id = key_id as u32;
|
||||||
|
rsp.server_rand_key = base64::encode(&enc_buf);
|
||||||
|
rsp.sign = base64::encode(&signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.conv_to_user.insert(conv, uid);
|
||||||
|
self.user_to_conv.insert(uid, conv);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_uid_by_account_id(&self, account_uid: u32) -> u32 {
|
||||||
|
// TODO!
|
||||||
|
return AuthManager::SPOOFED_PLAYER_UID;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_conv(&self, conv: u32) -> Option<u32> {
|
||||||
|
match self.conv_to_user.get(&conv) {
|
||||||
|
Some(uid) => return Some(*uid),
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_uid(&self, uid: u32) -> Option<u32> {
|
||||||
|
match self.user_to_conv.get(&uid) {
|
||||||
|
Some(conv) => return Some(*conv),
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
121
Kalitka/src/server/client_connection.rs
Normal file
121
Kalitka/src/server/client_connection.rs
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
use std::io;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::fs;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use std::net::UdpSocket;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
use rs_utils::TimeManager;
|
||||||
|
|
||||||
|
extern crate kcp;
|
||||||
|
extern crate mhycrypt;
|
||||||
|
|
||||||
|
use kcp::Kcp;
|
||||||
|
|
||||||
|
pub struct ClientConnection {
|
||||||
|
conv: u32,
|
||||||
|
token: u32,
|
||||||
|
ikcp: Kcp<Source>,
|
||||||
|
established_time: SystemTime,
|
||||||
|
key: [u8; 0x1000],
|
||||||
|
pending_seed: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Source
|
||||||
|
{
|
||||||
|
address: Option<SocketAddr>,
|
||||||
|
socket: UdpSocket,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Source {
|
||||||
|
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
|
||||||
|
return self.socket.send_to(data, self.address.expect("Unknown destination address!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientConnection {
|
||||||
|
pub fn new(socket: UdpSocket, conv: u32, token: u32) -> ClientConnection {
|
||||||
|
let s = Source {
|
||||||
|
address: None,
|
||||||
|
socket: socket,
|
||||||
|
};
|
||||||
|
|
||||||
|
return ClientConnection {
|
||||||
|
conv: conv,
|
||||||
|
token: token,
|
||||||
|
ikcp: Kcp::new(conv, token, s),
|
||||||
|
established_time: SystemTime::now(),
|
||||||
|
key: ClientConnection::read_key("master").try_into().expect("Incorrect master key"),
|
||||||
|
pending_seed: None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_source(&mut self, new_source: SocketAddr) {
|
||||||
|
self.ikcp.output.0.address = Some(new_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_udp_packet(&mut self, data: &[u8]) -> Vec<Vec<u8>> {
|
||||||
|
match self.pending_seed {
|
||||||
|
None => {},
|
||||||
|
Some(seed) => {
|
||||||
|
mhycrypt::mhy_generate_key(&mut self.key, seed, false);
|
||||||
|
self.pending_seed = None;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut packets: Vec<Vec<u8>> = Vec::new();
|
||||||
|
self.ikcp.input(data).unwrap();
|
||||||
|
self.ikcp.update(self.elapsed_time_millis()).unwrap();
|
||||||
|
self.ikcp.flush().unwrap();
|
||||||
|
loop {
|
||||||
|
let mut buf = [0u8; 0x20000];
|
||||||
|
match self.ikcp.recv(&mut buf) {
|
||||||
|
Err(_) => break,
|
||||||
|
Ok(size) => {
|
||||||
|
#[cfg(feature = "raw_packet_dump")]
|
||||||
|
{
|
||||||
|
use pretty_hex::*;
|
||||||
|
let cfg = HexConfig {title: true, width: 16, group: 0, ascii: true, ..HexConfig::default() };
|
||||||
|
println!("{:?}", buf[..size].to_vec().hex_conf(cfg));
|
||||||
|
}
|
||||||
|
mhycrypt::mhy_xor(&mut buf[..size], &self.key);
|
||||||
|
let data = buf[..size].to_owned();
|
||||||
|
packets.push(data);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.ikcp.update(self.elapsed_time_millis()).unwrap();
|
||||||
|
return packets;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_key(&mut self, seed: u64) {
|
||||||
|
self.pending_seed = Some(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_key(key_name: &str) -> Vec<u8> {
|
||||||
|
let filename = format!("./{}/{}.key", "keys", key_name);
|
||||||
|
let mut f = fs::File::open(&filename).expect(&format!("File '{}' not found", filename));
|
||||||
|
let metadata = fs::metadata(&filename).expect("unable to read metadata");
|
||||||
|
let mut buffer = vec![0; metadata.len() as usize];
|
||||||
|
f.read(&mut buffer).expect("buffer overflow");
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn elapsed_time_millis(&self) -> u32 {
|
||||||
|
return TimeManager::duration_since(self.established_time).try_into().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_udp_packet(&mut self, data: &[u8]) {
|
||||||
|
let mut buf = data.to_owned();
|
||||||
|
mhycrypt::mhy_xor(&mut buf, &self.key);
|
||||||
|
self.ikcp.send(&buf).expect("Failed to send data!");
|
||||||
|
self.ikcp.flush().unwrap();
|
||||||
|
self.ikcp.update(self.elapsed_time_millis()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
9
Kalitka/src/server/mod.rs
Normal file
9
Kalitka/src/server/mod.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
mod network_server;
|
||||||
|
mod auth_manager;
|
||||||
|
//mod login_manager;
|
||||||
|
mod client_connection;
|
||||||
|
|
||||||
|
pub use self::network_server::NetworkServer;
|
||||||
|
pub use self::auth_manager::AuthManager;
|
||||||
|
//pub use self::login_manager::LoginManager;
|
||||||
|
pub use self::client_connection::ClientConnection;
|
236
Kalitka/src/server/network_server.rs
Normal file
236
Kalitka/src/server/network_server.rs
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
use std::fmt;
|
||||||
|
use std::net::UdpSocket;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::io::Cursor;
|
||||||
|
use std::thread;
|
||||||
|
use std::sync::mpsc;
|
||||||
|
use std::sync::{Arc, RwLock, Mutex};
|
||||||
|
|
||||||
|
use num_derive::FromPrimitive;
|
||||||
|
use num_traits::FromPrimitive;
|
||||||
|
use num_derive::ToPrimitive;
|
||||||
|
use num_traits::ToPrimitive;
|
||||||
|
|
||||||
|
use crate::utils::HandshakePacket;
|
||||||
|
use crate::utils::DataPacket;
|
||||||
|
use crate::server::ClientConnection;
|
||||||
|
use crate::server::AuthManager;
|
||||||
|
|
||||||
|
use rs_ipc::{IpcMessage, PullSocket, PushSocket};
|
||||||
|
|
||||||
|
use proto::PacketHead;
|
||||||
|
use proto::GetPlayerTokenRsp;
|
||||||
|
|
||||||
|
use prost::Message;
|
||||||
|
|
||||||
|
use rs_ipc::{SubSocket, PubSocket};
|
||||||
|
|
||||||
|
use packet_processor::{PacketProcessor, EasilyUnpackable};
|
||||||
|
use rs_nodeconf::NodeConfig;
|
||||||
|
|
||||||
|
extern crate kcp;
|
||||||
|
|
||||||
|
// -------------
|
||||||
|
|
||||||
|
pub struct NetworkServer {
|
||||||
|
socket: UdpSocket,
|
||||||
|
clients: Arc<Mutex<HashMap<u32,ClientConnection>>>,
|
||||||
|
node_config: NodeConfig,
|
||||||
|
packets_to_process_tx: PubSocket,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct NetworkServerError {
|
||||||
|
reason: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetworkServerError {
|
||||||
|
pub fn new(reason: &str) -> NetworkServerError {
|
||||||
|
return NetworkServerError {reason: reason.to_string()};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for NetworkServerError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "NetworkServerError: {}", self.reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetworkServer {
|
||||||
|
pub fn new(host: &str, port: i16) -> Result<NetworkServer, NetworkServerError> {
|
||||||
|
let node_config = NodeConfig::new();
|
||||||
|
|
||||||
|
let mut packets_to_process_tx = node_config.bind_in_queue().unwrap();
|
||||||
|
|
||||||
|
let gs = NetworkServer {
|
||||||
|
socket: match UdpSocket::bind(format!("{}:{}", host, port).to_string()) {
|
||||||
|
Ok(socket) => socket,
|
||||||
|
Err(e) => return Err(NetworkServerError::new(format!("Failed to bind socket: {}", e).as_str())),
|
||||||
|
},
|
||||||
|
clients: Arc::new(Mutex::new(HashMap::new())),
|
||||||
|
node_config: node_config,
|
||||||
|
packets_to_process_tx: packets_to_process_tx,
|
||||||
|
};
|
||||||
|
|
||||||
|
print!("Connection established\n");
|
||||||
|
|
||||||
|
return Ok(gs);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&mut self) -> Result<i16, NetworkServerError> {
|
||||||
|
print!("Starting server\n");
|
||||||
|
|
||||||
|
let mut packets_to_send_rx = self.node_config.bind_out_queue().unwrap();
|
||||||
|
|
||||||
|
let clients = self.clients.clone();
|
||||||
|
let mut auth_manager = Arc::new(Mutex::new(AuthManager::new(&self.node_config)));
|
||||||
|
let am = auth_manager.clone();
|
||||||
|
|
||||||
|
let packet_relaying_thread = thread::spawn(move || {
|
||||||
|
loop {
|
||||||
|
let IpcMessage(packet_id, user_id, metadata, data) = packets_to_send_rx.recv().unwrap();
|
||||||
|
|
||||||
|
let conv = match packet_id {
|
||||||
|
proto::PacketId::GetPlayerTokenRsp => user_id, // Mapping is not performed on those
|
||||||
|
_ => am.lock().unwrap().resolve_uid(user_id).unwrap_or_else(|| panic!("Unknown user ID {}!", user_id)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let data_packet = DataPacket::new(packet_id.clone() as u16, metadata, data.clone());
|
||||||
|
|
||||||
|
match clients.lock().unwrap().get_mut(&conv) {
|
||||||
|
Some(client) => {
|
||||||
|
let bytes = data_packet.to_bytes();
|
||||||
|
client.send_udp_packet(&bytes);
|
||||||
|
|
||||||
|
if packet_id == proto::PacketId::GetPlayerTokenRsp {
|
||||||
|
// TODO: a bit hacky!
|
||||||
|
let token_rsp: GetPlayerTokenRsp = EasilyUnpackable::from(&data);
|
||||||
|
client.update_key(token_rsp.secret_key_seed);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => panic!("Unknown client conv: {}", conv),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut buffer = [0u8; 65536];
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match self.socket.recv_from(&mut buffer) {
|
||||||
|
Ok( (bytes_number, source_address) ) => self.process_udp_packet(source_address, &buffer[..bytes_number], &mut auth_manager),
|
||||||
|
Err(e) => panic!("Failed to receive data: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//packet_relaying_thread.join().unwrap();
|
||||||
|
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_udp_packet(&mut self, source_address: SocketAddr, packet_bytes: &[u8], auth_manager: &mut Arc<Mutex<AuthManager>>) {
|
||||||
|
//print!("Received packet! Len = {}\n", packet_bytes.len());
|
||||||
|
|
||||||
|
let hs_packet = HandshakePacket::new(packet_bytes);
|
||||||
|
|
||||||
|
match hs_packet {
|
||||||
|
Ok(hs_packet) => {
|
||||||
|
//print!("Received handshake packet: {:#?}\n", hs_packet);
|
||||||
|
if hs_packet.is_connect() {
|
||||||
|
//print!("Sending reply to CONNECT\n");
|
||||||
|
// TODO: assign conv and token!
|
||||||
|
let conv = 0x96969696u32;
|
||||||
|
let token = 0x42424242u32;
|
||||||
|
|
||||||
|
let reply = HandshakePacket::new_conv(conv, token);
|
||||||
|
|
||||||
|
let mut client = ClientConnection::new(self.socket.try_clone().unwrap(), conv, token);
|
||||||
|
client.update_source(source_address);
|
||||||
|
|
||||||
|
self.clients.lock().unwrap().insert(conv, client);
|
||||||
|
|
||||||
|
self.socket.send_to(&reply.to_bytes(), source_address).expect("Failed to send data!");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
//print!("Error constructing handshake: {:#?}", e);
|
||||||
|
let conv = kcp::get_conv(packet_bytes);
|
||||||
|
|
||||||
|
let packets = match self.clients.lock().unwrap().get_mut(&conv) {
|
||||||
|
Some(client) => {
|
||||||
|
client.update_source(source_address);
|
||||||
|
|
||||||
|
client.process_udp_packet(packet_bytes)
|
||||||
|
}
|
||||||
|
None => panic!("Unknown client conv: {}", conv),
|
||||||
|
};
|
||||||
|
|
||||||
|
for packet in packets.iter() {
|
||||||
|
self.process_game_packet(conv, packet, auth_manager);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_game_packet(&mut self, conv: u32, packet: &[u8], auth_manager: &mut Arc<Mutex<AuthManager>>) {
|
||||||
|
let data = match DataPacket::new_from_bytes(packet) {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(e) => panic!("Malformed data packet: {:#?}!", e),
|
||||||
|
};
|
||||||
|
|
||||||
|
let head = match PacketHead::decode(&mut Cursor::new(&data.metadata)) {
|
||||||
|
Ok(head) => head,
|
||||||
|
Err(e) => panic!("Malformed packet header: {:#?}!", e),
|
||||||
|
};
|
||||||
|
|
||||||
|
let packet_id: proto::PacketId = match FromPrimitive::from_u16(data.packet_id) {
|
||||||
|
Some(packet_id) => packet_id,
|
||||||
|
None => {
|
||||||
|
println!("Skipping unknown packet ID {}", data.packet_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let user_id = match packet_id {
|
||||||
|
proto::PacketId::GetPlayerTokenReq => {
|
||||||
|
auth_manager.lock().unwrap().process(conv, packet_id, data.metadata, data.data);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
_ => match auth_manager.lock().unwrap().resolve_conv(conv) {
|
||||||
|
None => {
|
||||||
|
println!("Unknown user with conv {}! Skipping", conv);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
Some(user_id) => user_id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if packet_id == proto::PacketId::UnionCmdNotify {
|
||||||
|
let union: proto::UnionCmdNotify = EasilyUnpackable::from(&data.data);
|
||||||
|
for u_cmd in union.cmd_list.into_iter() {
|
||||||
|
self.send_packet_to_process(user_id, u_cmd.message_id as u16, &data.metadata, &u_cmd.body);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.send_packet_to_process(user_id, data.packet_id, &data.metadata, &data.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_packet_to_process(&mut self, user_id: u32, packet_id: u16, metadata: &[u8], data: &[u8])
|
||||||
|
{
|
||||||
|
/*let sender: &mut PubSocket = match &self.packets_to_process_tx {
|
||||||
|
Some(mut sender) => &mut sender,
|
||||||
|
None => panic!("Processing queue wasn't set up!"),
|
||||||
|
};*/
|
||||||
|
|
||||||
|
let packet_id: proto::PacketId = match FromPrimitive::from_u16(packet_id) {
|
||||||
|
Some(packet_id) => packet_id,
|
||||||
|
None => {
|
||||||
|
println!("Skipping unknown packet ID {}", packet_id);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Got packet {:?}", packet_id);
|
||||||
|
self.packets_to_process_tx.send( IpcMessage(packet_id, user_id, metadata.to_vec(), data.to_vec()) );
|
||||||
|
}
|
||||||
|
}
|
15
Kalitka/src/utils/mod.rs
Normal file
15
Kalitka/src/utils/mod.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
mod handshake_packet;
|
||||||
|
mod data_packet;
|
||||||
|
/*mod id_manager;
|
||||||
|
mod time_manager;
|
||||||
|
mod avatar_builder;*/
|
||||||
|
/*
|
||||||
|
#[macro_use]
|
||||||
|
mod remapper;
|
||||||
|
*/
|
||||||
|
pub use self::handshake_packet::HandshakePacket;
|
||||||
|
pub use self::data_packet::DataPacket;
|
||||||
|
/*pub use self::id_manager::IdManager;
|
||||||
|
pub use self::time_manager::TimeManager;
|
||||||
|
pub use self::remapper::Remapper;
|
||||||
|
pub use self::avatar_builder::AvatarBuilder;*/
|
44
RustySamovar/Cargo.toml
Normal file
44
RustySamovar/Cargo.toml
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
[package]
|
||||||
|
name = "RustySamovar"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[features]
|
||||||
|
raw_packet_dump = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
kcp = { path = "../kcp" }
|
||||||
|
mhycrypt = { path = "../mhycrypt" }
|
||||||
|
proto = { path = "../proto" }
|
||||||
|
lua_serde = { path = "../lua_serde" }
|
||||||
|
packet-processor-macro = { path = "../packet-processor-macro" }
|
||||||
|
packet-processor = { path = "../packet-processor" }
|
||||||
|
rs-ipc = { path = "../rs-ipc" }
|
||||||
|
excel-hash-wrapper-macro = { path = "../excel-hash-wrapper-macro" }
|
||||||
|
|
||||||
|
prost = "0.8"
|
||||||
|
bytes = "1.1.0"
|
||||||
|
base64 = "0.13.0"
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
futures = "0.3"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
|
pretty_env_logger = "0.4"
|
||||||
|
num-traits = "0.2"
|
||||||
|
num-derive = "0.3"
|
||||||
|
pretty-hex = "0.2"
|
||||||
|
sea-orm = { version = "0.7", features = [ "sqlx-all", "runtime-async-std-native-tls", "debug-print" ] }
|
||||||
|
#hostname = "0.3"
|
||||||
|
#local-ip-address = "0.4"
|
||||||
|
chrono = "0.4"
|
||||||
|
rand = "0.8"
|
||||||
|
tracing = "0.1"
|
||||||
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
openssl = { version = "0.10", features = ["vendored"] }
|
||||||
|
|
||||||
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
openssl = "0.10"
|
@ -22,7 +22,7 @@ use rs_ipc::{IpcMessage, PullSocket, PushSocket};
|
|||||||
|
|
||||||
use proto::PacketHead;
|
use proto::PacketHead;
|
||||||
use proto::GetPlayerTokenRsp;
|
use proto::GetPlayerTokenRsp;
|
||||||
use proto::get_player_token_rsp;
|
//use proto::get_player_token_rsp;
|
||||||
|
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
|
|
90
RustySamovar/src/utils/data_packet.rs
Normal file
90
RustySamovar/src/utils/data_packet.rs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
use std::fmt;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DataDecError {
|
||||||
|
reason: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(packed)]
|
||||||
|
pub struct DataPacketSmallest // Not actually used, just represents the general structure
|
||||||
|
{
|
||||||
|
start_magic: u16,
|
||||||
|
packet_id: u16,
|
||||||
|
metadata_size: u16,
|
||||||
|
data_size: u32,
|
||||||
|
//metadata: [0u8; metadata_size],
|
||||||
|
//data: [0u8; data_size],
|
||||||
|
end_magic: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DataPacket
|
||||||
|
{
|
||||||
|
pub packet_id: u16,
|
||||||
|
pub metadata: Vec<u8>,
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataPacket {
|
||||||
|
const DATA_MAGIC_START: u16 = 0x4567;
|
||||||
|
const DATA_MAGIC_END: u16 = 0x89AB;
|
||||||
|
|
||||||
|
pub fn new(packet_id: u16, metadata: Vec<u8>, data: Vec<u8>) -> DataPacket {
|
||||||
|
return DataPacket {
|
||||||
|
packet_id: packet_id,
|
||||||
|
data: data,
|
||||||
|
metadata: metadata,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_from_bytes(raw_data: &[u8]) -> Result<DataPacket, DataDecError> {
|
||||||
|
if raw_data.len() < std::mem::size_of::<DataPacketSmallest>() {
|
||||||
|
return Err(DataDecError {reason: "Size is too small!".to_string()});
|
||||||
|
}
|
||||||
|
|
||||||
|
// unwrap() here are valid as we're cutting exactly 4 bytes of data
|
||||||
|
let start_magic = u16::from_be_bytes(raw_data[0..2].try_into().unwrap());
|
||||||
|
let end_magic = u16::from_be_bytes(raw_data[raw_data.len()-2..].try_into().unwrap());
|
||||||
|
|
||||||
|
if (start_magic == DataPacket::DATA_MAGIC_START) && (end_magic == DataPacket::DATA_MAGIC_END) {
|
||||||
|
let packet_id = u16::from_be_bytes(raw_data[2..4].try_into().unwrap());
|
||||||
|
let metadata_size = u16::from_be_bytes(raw_data[4..6].try_into().unwrap()) as usize;
|
||||||
|
let data_size = u32::from_be_bytes(raw_data[6..10].try_into().unwrap()) as usize;
|
||||||
|
|
||||||
|
let expected_size = std::mem::size_of::<DataPacketSmallest>() + metadata_size + data_size;
|
||||||
|
|
||||||
|
if raw_data.len() != expected_size {
|
||||||
|
return Err(DataDecError {reason: format!("Wrong packet size: expected {}, got {} (header {}, payload {})!", expected_size, raw_data.len(), std::mem::size_of::<DataPacketSmallest>(), metadata_size + data_size)});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(DataPacket {
|
||||||
|
packet_id: packet_id,
|
||||||
|
metadata: raw_data[10..metadata_size+10].to_owned(),
|
||||||
|
data: raw_data[metadata_size+10..metadata_size+data_size+10].to_owned(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return Err(DataDecError {reason: format!("Unknown magic: 0x{:x} 0x{:x}", start_magic, end_magic),});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
let mut ret = Vec::with_capacity(std::mem::size_of::<DataPacketSmallest>() + self.data.len() + self.metadata.len());
|
||||||
|
|
||||||
|
ret.extend_from_slice(&DataPacket::DATA_MAGIC_START.to_be_bytes());
|
||||||
|
ret.extend_from_slice(&self.packet_id.to_be_bytes());
|
||||||
|
ret.extend_from_slice(&(self.metadata.len() as u16).to_be_bytes());
|
||||||
|
ret.extend_from_slice(&(self.data.len() as u32).to_be_bytes());
|
||||||
|
ret.extend_from_slice(&self.metadata);
|
||||||
|
ret.extend_from_slice(&self.data);
|
||||||
|
ret.extend_from_slice(&DataPacket::DATA_MAGIC_END.to_be_bytes());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for DataPacket {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "packet")
|
||||||
|
}
|
||||||
|
}
|
91
RustySamovar/src/utils/handshake_packet.rs
Normal file
91
RustySamovar/src/utils/handshake_packet.rs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
use std::fmt;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct HandshakeDecError {
|
||||||
|
reason: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct HandshakePacket
|
||||||
|
{
|
||||||
|
start_magic: u32,
|
||||||
|
param1: u32,
|
||||||
|
param2: u32,
|
||||||
|
data: u32,
|
||||||
|
end_magic: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HandshakePacket {
|
||||||
|
const HS_MAGIC_CONNECT_START: u32 = 0x000000FF;
|
||||||
|
const HS_MAGIC_CONNECT_END: u32 = 0xFFFFFFFF;
|
||||||
|
const HS_MAGIC_SEND_CONV_START: u32 = 0x00000145;
|
||||||
|
const HS_MAGIC_SEND_CONV_END: u32 = 0x14514545;
|
||||||
|
const HS_MAGIC_DISCONNECT_START: u32 = 0x00000194;
|
||||||
|
const HS_MAGIC_DISCONNECT_END: u32 = 0x19419494;
|
||||||
|
|
||||||
|
const HS_CONNECTION_DATA: u32 = 1234567890;
|
||||||
|
|
||||||
|
pub fn new(raw_data: &[u8]) -> Result<HandshakePacket, HandshakeDecError> {
|
||||||
|
if raw_data.len() != std::mem::size_of::<HandshakePacket>() {
|
||||||
|
return Err(HandshakeDecError {reason: "Size mismatch!".to_string()});
|
||||||
|
}
|
||||||
|
|
||||||
|
// unwrap() here are valid as we're cutting exactly 4 bytes of data
|
||||||
|
let start_magic = u32::from_be_bytes(raw_data[0..4].try_into().unwrap());
|
||||||
|
let param1 = u32::from_be_bytes(raw_data[4..8].try_into().unwrap());
|
||||||
|
let param2 = u32::from_be_bytes(raw_data[8..12].try_into().unwrap());
|
||||||
|
let data = u32::from_be_bytes(raw_data[12..16].try_into().unwrap());
|
||||||
|
let end_magic = u32::from_be_bytes(raw_data[16..20].try_into().unwrap());
|
||||||
|
|
||||||
|
if (start_magic == HandshakePacket::HS_MAGIC_CONNECT_START) && (end_magic == HandshakePacket::HS_MAGIC_CONNECT_END) ||
|
||||||
|
(start_magic == HandshakePacket::HS_MAGIC_SEND_CONV_START) && (end_magic == HandshakePacket::HS_MAGIC_SEND_CONV_END) ||
|
||||||
|
(start_magic == HandshakePacket::HS_MAGIC_DISCONNECT_START) && (end_magic == HandshakePacket::HS_MAGIC_DISCONNECT_END) {
|
||||||
|
|
||||||
|
return Ok(HandshakePacket {
|
||||||
|
start_magic: start_magic,
|
||||||
|
param1: param1,
|
||||||
|
param2: param2,
|
||||||
|
data: data,
|
||||||
|
end_magic: end_magic,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return Err(HandshakeDecError {reason: format!("Unknown magic: 0x{:x} 0x{:x}", start_magic, end_magic),});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_connect(&self) -> bool {
|
||||||
|
return (self.start_magic == HandshakePacket::HS_MAGIC_CONNECT_START) &&
|
||||||
|
(self.end_magic == HandshakePacket::HS_MAGIC_CONNECT_END) &&
|
||||||
|
(self.data == HandshakePacket::HS_CONNECTION_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_conv(conv: u32, token: u32) -> HandshakePacket {
|
||||||
|
HandshakePacket {
|
||||||
|
start_magic: HandshakePacket::HS_MAGIC_SEND_CONV_START,
|
||||||
|
param1: conv,
|
||||||
|
param2: token,
|
||||||
|
data: HandshakePacket::HS_CONNECTION_DATA,
|
||||||
|
end_magic: HandshakePacket::HS_MAGIC_SEND_CONV_END,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
let mut ret = Vec::with_capacity(std::mem::size_of::<HandshakePacket>());
|
||||||
|
|
||||||
|
ret.extend_from_slice(&self.start_magic.to_be_bytes());
|
||||||
|
ret.extend_from_slice(&self.param1.to_be_bytes());
|
||||||
|
ret.extend_from_slice(&self.param2.to_be_bytes());
|
||||||
|
ret.extend_from_slice(&self.data.to_be_bytes());
|
||||||
|
ret.extend_from_slice(&self.end_magic.to_be_bytes());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for HandshakePacket {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "packet")
|
||||||
|
}
|
||||||
|
}
|
9
rs-nodeconf/Cargo.toml
Normal file
9
rs-nodeconf/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "rs-nodeconf"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rs-ipc = { path = "../rs-ipc" }
|
35
rs-nodeconf/src/config.rs
Normal file
35
rs-nodeconf/src/config.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use rs_ipc::{PubSocket, PullSocket, PushSocket, Result, SubSocket};
|
||||||
|
|
||||||
|
pub struct NodeConfig {
|
||||||
|
pub in_queue_addr: String,
|
||||||
|
pub in_queue_port: u16,
|
||||||
|
pub out_queue_addr: String,
|
||||||
|
pub out_queue_port: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeConfig {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
NodeConfig {
|
||||||
|
in_queue_addr: "127.0.0.1".to_string(),
|
||||||
|
in_queue_port: 9012,
|
||||||
|
out_queue_addr: "127.0.0.1".to_string(),
|
||||||
|
out_queue_port: 9014,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bind_in_queue(&self) -> Result<PubSocket> {
|
||||||
|
PubSocket::bind_tcp(&self.in_queue_addr, self.in_queue_port)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bind_out_queue(&self) -> Result<PullSocket> {
|
||||||
|
PullSocket::bind_tcp(&self.out_queue_addr, self.out_queue_port)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn connect_in_queue(&self) -> Result<SubSocket> {
|
||||||
|
SubSocket::connect_tcp(&self.in_queue_addr, self.in_queue_port)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn connect_out_queue(&self) -> Result<PushSocket> {
|
||||||
|
PushSocket::connect_tcp(&self.out_queue_addr, self.out_queue_port)
|
||||||
|
}
|
||||||
|
}
|
3
rs-nodeconf/src/lib.rs
Normal file
3
rs-nodeconf/src/lib.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
mod config;
|
||||||
|
|
||||||
|
pub use crate::config::NodeConfig;
|
10
rs-utils/Cargo.toml
Normal file
10
rs-utils/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "rs-utils"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
chrono = "0.4"
|
3
rs-utils/src/lib.rs
Normal file
3
rs-utils/src/lib.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
mod time_manager;
|
||||||
|
|
||||||
|
pub use time_manager::TimeManager;
|
28
rs-utils/src/time_manager.rs
Normal file
28
rs-utils/src/time_manager.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
use std::time::SystemTime;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||||
|
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
|
pub struct TimeManager {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimeManager {
|
||||||
|
pub fn duration_since(time_point: SystemTime) -> u64 {
|
||||||
|
SystemTime::now().duration_since(time_point).unwrap().as_millis().try_into().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn timestamp() -> u64 {
|
||||||
|
return Self::duration_since(SystemTime::UNIX_EPOCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Option<NaiveDateTime>, D::Error> {
|
||||||
|
let time: String = Deserialize::deserialize(deserializer)?;
|
||||||
|
|
||||||
|
if time.is_empty() {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Ok(Some(NaiveDateTime::parse_from_str(&time, "%Y-%m-%d %H:%M:%S").map_err(D::Error::custom)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user