First working attempt

This commit is contained in:
Nobody 2021-09-25 00:37:52 +05:00
parent 346afbee3a
commit 518b68cf25
12 changed files with 569 additions and 0 deletions

6
.gitignore vendored
View File

@ -8,3 +8,9 @@ Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# *.proto files
/protobuf/
# keys
/keys/

15
Cargo.toml Normal file
View File

@ -0,0 +1,15 @@
[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
[dependencies]
kcp = { path = "../kcp" }
mhycrypt = { path = "../mhycrypt" }
prost = "0.8"
bytes = "1.1.0"
[build-dependencies]
prost-build = { version = "0.8.0" }

5
build.rs Normal file
View File

@ -0,0 +1,5 @@
use std::io::Result;
fn main() -> Result<()> {
prost_build::compile_protos(&["protobuf/packet_header.proto"], &["protobuf/"])?;
Ok(())
}

13
src/main.rs Normal file
View File

@ -0,0 +1,13 @@
mod server;
mod utils;
pub mod proto {
include!(concat!(env!("OUT_DIR"), "/proto.rs"));
}
use server::NetworkServer;
fn main() {
let mut ns = NetworkServer::new("0.0.0.0", 9696).unwrap();
ns.run().expect("Failed to serve!");
}

View File

@ -0,0 +1,101 @@
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;
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],
}
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"),
};
}
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>> {
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) => {
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) {
mhycrypt::mhy_generate_key(&mut self.key, seed, false);
}
fn read_key(key_name: &str) -> Vec<u8> {
let filename = format!("{}/{}.key", "keys", key_name);
let mut f = fs::File::open(&filename).expect("no file found");
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 SystemTime::now().duration_since(self.established_time).unwrap().as_millis().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!");
}
}

65
src/server/game_server.rs Normal file
View File

@ -0,0 +1,65 @@
use std::sync::mpsc;
use std::thread;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use crate::server::IpcMessage;
pub struct GameWorld {
packets_to_send_tx: mpsc::Sender<IpcMessage>,
}
impl GameWorld {
pub fn new(packets_to_send_tx: mpsc::Sender<IpcMessage>) -> GameWorld {
let gm = GameWorld {
packets_to_send_tx: packets_to_send_tx,
};
return gm;
}
pub fn process_packet(&mut self, conv: u32, packet_id: u16, metadata: Vec<u8>, data: Vec<u8>) {
}
}
pub struct GameServer {
packets_to_process_rx: mpsc::Receiver<IpcMessage>,
packets_to_send_tx: mpsc::Sender<IpcMessage>,
worlds: HashMap<u32, GameWorld>,
}
impl GameServer {
pub fn new(packets_to_process_rx: mpsc::Receiver<IpcMessage>, packets_to_send_tx: mpsc::Sender<IpcMessage>) -> GameServer {
let gs = GameServer {
packets_to_process_rx: packets_to_process_rx,
packets_to_send_tx: packets_to_send_tx,
worlds: HashMap::new(),
};
return gs;
}
pub fn run(&mut self) {
let world_processor = thread::spawn(move || {
println!("Starting world processor");
// TODO: Load worlds!
loop {
}
});
loop {
let IpcMessage(conv, packet_id, metadata, data) = self.packets_to_process_rx.recv().unwrap();
// TODO: each conv will have a distinct world!
let world = match self.worlds.entry(conv) {
Occupied(world) => world.into_mut(),
Vacant(entry) => {
let mut world = GameWorld::new(self.packets_to_send_tx.clone());
entry.insert(world)
},
};
world.process_packet(conv, packet_id, metadata, data);
}
}
}

View File

@ -0,0 +1 @@
pub struct IpcMessage(pub u32, pub u16, pub Vec<u8>, pub Vec<u8>);

9
src/server/mod.rs Normal file
View File

@ -0,0 +1,9 @@
mod network_server;
mod game_server;
mod client_connection;
mod ipc_message;
pub use self::network_server::NetworkServer;
pub use self::game_server::GameServer;
pub use self::client_connection::ClientConnection;
pub use self::ipc_message::IpcMessage;

View File

@ -0,0 +1,168 @@
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 crate::utils::HandshakePacket;
use crate::utils::DataPacket;
use crate::server::ClientConnection;
use crate::server::IpcMessage;
use crate::proto::PacketHead;
use prost::Message;
extern crate kcp;
pub struct NetworkServer {
socket: UdpSocket,
clients: Arc<Mutex<HashMap<u32,ClientConnection>>>,
packets_to_process_tx: Option<mpsc::Sender<IpcMessage>>,
}
#[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 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())),
packets_to_process_tx: None,
};
print!("Connection established\n");
return Ok(gs);
}
pub fn run(&mut self) -> Result<i16, NetworkServerError> {
print!("Starting server\n");
// Channel for relaying packets from network thread to processing thread
let (packets_to_process_tx, packets_to_process_rx) = mpsc::channel();
// Channel for relaying packets from network thread to processing thread
let (packets_to_send_tx, packets_to_send_rx) = mpsc::channel();
self.packets_to_process_tx = Some(packets_to_process_tx);
let clients = self.clients.clone();
let packet_relaying_thread = thread::spawn(move || {
loop {
let IpcMessage(conv, packet_id, metadata, data) = packets_to_send_rx.recv().unwrap();
let data = DataPacket::new(packet_id, metadata, data);
match clients.lock().unwrap().get_mut(&conv) {
Some(client) => {
client.send_udp_packet(&data.to_bytes());
// TODO: here, if encryption key was changed, do so
},
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]),
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]) {
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);
}
},
};
}
fn process_game_packet(&mut self, conv: u32, packet: &[u8]) {
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),
};
print!("Got packet with header: {:#?} and ID {}\n", head, data.packet_id);
let sender = match &self.packets_to_process_tx {
Some(sender) => sender,
None => panic!("Processing queue wasn't set up!"),
};
sender.send( IpcMessage(conv, data.packet_id, data.metadata, data.data) ).unwrap();
}
}

90
src/utils/data_packet.rs Normal file
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")
}
}

5
src/utils/mod.rs Normal file
View File

@ -0,0 +1,5 @@
mod handshake_packet;
mod data_packet;
pub use self::handshake_packet::HandshakePacket;
pub use self::data_packet::DataPacket;