Split project into gateserver (Kalitka) and gameserver (RustySamovar)

This commit is contained in:
Nobody 2023-03-18 23:08:32 +05:00
parent 74f5e82d66
commit f312e61323
92 changed files with 907 additions and 45 deletions

View File

@ -1,44 +1,2 @@
[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"
[workspace]
members = ["RustySamovar", "Kalitka"]

46
Kalitka/Cargo.toml Normal file
View 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
View 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!");
}

View 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,
};
}
}

View 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();
}
}

View 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;

View 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
View 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
View 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"

View File

@ -22,7 +22,7 @@ use rs_ipc::{IpcMessage, PullSocket, PushSocket};
use proto::PacketHead;
use proto::GetPlayerTokenRsp;
use proto::get_player_token_rsp;
//use proto::get_player_token_rsp;
use prost::Message;

View 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")
}
}

View 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
View 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
View 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
View File

@ -0,0 +1,3 @@
mod config;
pub use crate::config::NodeConfig;

10
rs-utils/Cargo.toml Normal file
View 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
View File

@ -0,0 +1,3 @@
mod time_manager;
pub use time_manager::TimeManager;

View 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)?))
}
}
}