diff --git a/Cargo.lock b/Cargo.lock index 8e8a43c..0d180f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -163,6 +163,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" + [[package]] name = "cc" version = "1.2.10" @@ -303,6 +309,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + [[package]] name = "generic-array" version = "0.14.7" @@ -840,6 +852,7 @@ dependencies = [ "simplelog", "thiserror", "tokio", + "tokio-stream", "v_byte_macros", ] @@ -940,6 +953,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", + "bytes", "libc", "mio", "pin-project-lite", @@ -959,6 +973,17 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "typenum" version = "1.17.0" diff --git a/Cargo.toml b/Cargo.toml index af41ebd..cd7bd3c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,4 @@ rustls = "^0.23.21" hmac = "0.12.1" md-5 = "^0.10.6" tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread", "net", "sync"] } +tokio-stream = { version = "0.1.17", features = ["io-util"] } diff --git a/src/endianness.rs b/src/endianness.rs index a7b9aff..99430d7 100644 --- a/src/endianness.rs +++ b/src/endianness.rs @@ -3,6 +3,7 @@ use std::io::Read; use std::marker::PhantomData; use std::pin::Pin; use bytemuck::Pod; +use tokio::io::{AsyncRead, AsyncReadExt}; #[cfg(target_endian = "little")] pub const IS_LITTLE_ENDIAN: bool = true; @@ -130,6 +131,8 @@ impl ReadExtensions for T{ } + + pub trait SwapEndian: Clone + Copy{ fn swap_endian(self) -> Self; } diff --git a/src/main.rs b/src/main.rs index 8c95b7d..3ae92d8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,26 @@ use std::env::current_dir; use std::{env, fs}; + use std::fs::File; +use std::io::Cursor; use std::net::{Ipv4Addr, SocketAddrV4}; use chrono::Local; -use log::{info, trace}; +use log::{error, info, trace}; use once_cell::sync::Lazy; +use rc4::{KeyInit, Rc4, StreamCipher}; +use rc4::consts::U5; use simplelog::{ColorChoice, CombinedLogger, Config, LevelFilter, TerminalMode, TermLogger, WriteLogger}; -use crate::prudp::socket::{Socket, SocketImpl}; -use crate::prudp::packet::VirtualPort; +use crate::prudp::socket::{Socket, SocketData}; +use crate::prudp::packet::{PRUDPPacket, VirtualPort}; use crate::prudp::router::Router; +use crate::rmc::message::RMCMessage; +use crate::rmc::response::{RMCResponse, RMCResponseResult, send_response}; +use crate::rmc::response::ErrorCode::Core_InvalidIndex; mod endianness; mod prudp; +pub mod rmc; +mod protocols; static AUTH_SERVER_PORT: Lazy = Lazy::new(||{ env::var("AUTH_SERVER_PORT") @@ -44,23 +53,122 @@ async fn main() { start_servers().await; } +async fn auth_server_handle_rmc(packet: PRUDPPacket, rmc_message: RMCMessage){ + +} + async fn start_servers(){ info!("starting auth server on {}:{}", *OWN_IP, *AUTH_SERVER_PORT); - let auth_server_router = + let (auth_server_router, auth_router_join) = Router::new(SocketAddrV4::new(*OWN_IP, *AUTH_SERVER_PORT)).await .expect("unable to startauth server"); info!("setting up endpoints"); - let mut socket = + // dont assign it to the name _ as that will make it drop right here and now + let mut _socket = Socket::new( auth_server_router.clone(), VirtualPort::new(1,10), - "6f599f81" + "6f599f81", + Box::new(|_|{ + Box::pin( + async move { + let rc4: Rc4 = Rc4::new_from_slice( "CD&ML".as_bytes()).unwrap(); + let cypher = Box::new(rc4); + let server_cypher: Box = cypher; + + let rc4: Rc4 = Rc4::new_from_slice( "CD&ML".as_bytes()).unwrap(); + let cypher = Box::new(rc4); + let client_cypher: Box = cypher; + + (true, (server_cypher, client_cypher)) + } + ) + }), + Box::new(|p, socket, connection|{ + Box::pin( + async move { + println!("{:?}",p); + let Ok(rmc) = RMCMessage::new(&mut Cursor::new(&p.payload)) else { + error!("error reading rmc message"); + return; + }; + + println!("recieved rmc message: {{ protocol: {}, method: {}}}", rmc.protocol_id, rmc.method_id); + + if let Some(response) = protocols::auth::try_process_via_protocol(&rmc){ + send_response(&p, &socket, connection, response).await; + } + + send_response(&p, &socket, connection, RMCResponse{ + protocol_id: rmc.protocol_id as u8, + response_result: RMCResponseResult::Error { + call_id: rmc.call_id, + error_code: Core_InvalidIndex + } + }).await; + } + ) + }) ).await.expect("unable to create socket"); - let Some(connection) = socket.accept().await else { - return; - }; + auth_router_join.await.expect("auth server crashed") } + + +#[cfg(test)] +mod test{ + use std::io::Cursor; + use std::num::ParseIntError; + use std::str::from_utf8; + use hmac::digest::consts::U5; + use rc4::{KeyInit, Rc4, StreamCipher}; + use crate::prudp::packet::PRUDPPacket; + use crate::rmc; + + fn from_hex_stream(val: &str) -> Result, ParseIntError> { + let res: Result, _> = val.as_bytes() + .chunks_exact(2) + .map(|c| from_utf8(c).expect("unable to convert back to string")) + .map(|s| u8::from_str_radix(s, 16)) + .collect(); + + res + } + + #[tokio::test] + async fn simulate_packets(){ + let val = from_hex_stream("ead001037d00afa1e200a5000200d9e4a4050368c18c6de4e2fb1cc40f0c020100768744db99f92c5005a061fd2a1df280cd64d5c1a565952c6befa607cbaf34661312b16db0fa6fccfb81e28b5a3a9bed02b49152bbc99cc112b7e29b9e45ec3d4b89df0fe71390883d9a927c264d07ada0de9cd28499e3ccdf3fd079e4a9848d4d783778c42da2af06106a7326634dc5bec5c3438ef18e30109839ffcc").expect("uuuuh"); + + let mut packet = PRUDPPacket::new(&mut Cursor::new(&val)).expect("invalid packet"); + + let mut rc4: Rc4 = + Rc4::new_from_slice("CD&ML".as_bytes().into()).expect("invalid key"); + + rc4.apply_keystream(&mut packet.payload); + + println!("packet: {:?}", packet); + + let rmc_packet = rmc::message::RMCMessage::new(&mut Cursor::new(&packet.payload)).expect("unable to read message"); + + let mut a = Cursor::new(&rmc_packet.rest_of_data); + + let pid = rmc::structures::string::read(&mut a).expect("unable to read pid"); + } + + #[tokio::test] + async fn simulate_packets_response(){ + let val = from_hex_stream("ead001032501a1af6200a500010013ffcdbc3a2ebc44efc6e38ea32a72b40201002e8644db19fe2a5005a2637d2a16f3b1fe5633037c1ed61c5aefad8afebdf2ff8600e9350fba1298b570c70f6dd647eac2d3faf0ab74ef761e2ee43dc10e249e5f91aed6813dcc04b3c707d9442b6e353b9b0b654e98f860fe5379c41d3c2a1874b7dd37ebf499e03bd2fd3e9a9203c0959feb760c38f504dcd0c9e99b17fd410657da4efa3e01c8a68ab3042d6d489788d5580778d32249cdf1fba8bf68cf4019d116ea7c580622ea1e3635139d91b44635d5e95b6c35b33898fdc0117fa6fc7162840d07a49f1e7089aa0ea65409a8ddeb2334449ba73a0ff7de462cf4a706a696de0f0521b84ae5a3f8587f3585d202d3cc0fb0451519c1b830b5e3cdd6de52e9add7325cbbf08a7c2f8b875934942b226703a22b4bc8931932dab055049051e4144b02").expect("uuuuh"); + + let mut packet = PRUDPPacket::new(&mut Cursor::new(&val)).expect("invalid packet"); + + let mut rc4: Rc4 = + Rc4::new_from_slice("CD&ML".as_bytes().into()).expect("invalid key"); + + rc4.apply_keystream(&mut packet.payload); + + println!("packet: {:?}", packet); + } +} \ No newline at end of file diff --git a/src/protocols/auth/method_login_ex.rs b/src/protocols/auth/method_login_ex.rs new file mode 100644 index 0000000..25e17f2 --- /dev/null +++ b/src/protocols/auth/method_login_ex.rs @@ -0,0 +1,39 @@ +use std::io::Cursor; +use log::{error, info}; +use crate::rmc::message::RMCMessage; +use crate::rmc::response::{ErrorCode, RMCResponse, RMCResponseResult}; +use crate::rmc::structures::{string, any}; + +pub fn login_ex(name: &str) -> RMCResponseResult{ + // todo: figure out how the AuthenticationInfo struct works, parse it and validate login info + + //return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument); + unreachable!() +} + +pub fn login_ex_raw_params(rmcmessage: &RMCMessage) -> RMCResponseResult{ + let mut reader = Cursor::new(&rmcmessage.rest_of_data); + + let Ok(str) = string::read(&mut reader) else { + error!("error reading packet"); + return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument); + }; + + let Ok(any) = any::read(&mut reader) else { + error!("error reading packet"); + return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument); + }; + + match any.name.as_ref(){ + "AuthenticationInfo" => { + + } + v => { + error!("error reading packet: invalid structure type: {}", v); + return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument); + } + } + + //login_ex(&str) + rmcmessage.error_result_with_code(ErrorCode::Core_AccessDenied) +} \ No newline at end of file diff --git a/src/protocols/auth/mod.rs b/src/protocols/auth/mod.rs new file mode 100644 index 0000000..439445e --- /dev/null +++ b/src/protocols/auth/mod.rs @@ -0,0 +1,25 @@ +mod method_login_ex; + +use log::{error, info}; +use crate::protocols::auth::method_login_ex::{login_ex, login_ex_raw_params}; +use crate::rmc::message::RMCMessage; +use crate::rmc::response::{ErrorCode, RMCResponse, RMCResponseResult}; + +pub fn try_process_via_protocol(rmcmessage: &RMCMessage) -> Option{ + if rmcmessage.protocol_id != 10{ + return None; + } + + let response_result = match rmcmessage.method_id{ + 0x02 => login_ex_raw_params(rmcmessage), + _ => { + error!("invalid method id sent to ticket-granting protocol: {:?}", rmcmessage.method_id); + rmcmessage.error_result_with_code(ErrorCode::Core_Exception) + } + }; + + Some(RMCResponse{ + protocol_id: 10, + response_result + }) +} \ No newline at end of file diff --git a/src/protocols/mod.rs b/src/protocols/mod.rs new file mode 100644 index 0000000..5696e21 --- /dev/null +++ b/src/protocols/mod.rs @@ -0,0 +1 @@ +pub mod auth; \ No newline at end of file diff --git a/src/prudp/connection.rs b/src/prudp/connection.rs deleted file mode 100644 index 2ee80e4..0000000 --- a/src/prudp/connection.rs +++ /dev/null @@ -1,7 +0,0 @@ -use tokio::sync::mpsc::Receiver; - -//struct Connection(Arc, Receiver<>); - -struct ConnectionImpl{ - -} \ No newline at end of file diff --git a/src/prudp/mod.rs b/src/prudp/mod.rs index d47b899..cde32f3 100644 --- a/src/prudp/mod.rs +++ b/src/prudp/mod.rs @@ -2,5 +2,4 @@ pub mod packet; pub mod router; pub mod socket; mod auth_module; -mod sockaddr; -mod connection; \ No newline at end of file +mod sockaddr; \ No newline at end of file diff --git a/src/prudp/packet.rs b/src/prudp/packet.rs index 230484e..7682664 100644 --- a/src/prudp/packet.rs +++ b/src/prudp/packet.rs @@ -36,7 +36,7 @@ pub enum Error { pub type Result = std::result::Result; #[repr(transparent)] -#[derive(Copy, Clone, Pod, Zeroable, SwapEndian)] +#[derive(Copy, Clone, Pod, Zeroable, SwapEndian, Default)] pub struct TypesFlags(u16); impl TypesFlags { @@ -77,6 +77,10 @@ pub mod types { pub const SYN: u8 = 0x0; pub const CONNECT: u8 = 0x1; pub const DATA: u8 = 0x2; + pub const DISCONNECT: u8 = 0x3; + pub const PING: u8 = 0x4; + /// no idea what user is supposed to mean + pub const USER: u8 = 0x5; } impl Debug for TypesFlags { @@ -135,8 +139,8 @@ impl Debug for VirtualPort { #[repr(C)] #[derive(Debug, Copy, Clone, Pod, Zeroable, SwapEndian)] pub struct PRUDPHeader { - magic: [u8; 2], - version: u8, + pub magic: [u8; 2], + pub version: u8, pub packet_specific_size: u8, pub payload_size: u16, pub source_port: VirtualPort, @@ -146,6 +150,24 @@ pub struct PRUDPHeader { pub substream_id: u8, pub sequence_id: u16, } + +impl Default for PRUDPHeader{ + fn default() -> Self { + Self{ + magic: [0xEA, 0xD0], + version: 1, + session_id: 0, + source_port: VirtualPort(0), + sequence_id: 0, + payload_size: 0, + destination_port: VirtualPort(0), + types_and_flags: TypesFlags(0), + packet_specific_size: 0, + substream_id: 0 + } + } +} + #[repr(u16)] #[derive(EnumTryInto)] enum PacketSpecificData { @@ -414,7 +436,7 @@ impl PRUDPPacket { self.header.payload_size = self.payload.len() as u16; } - pub fn base_response_packet(&self) -> Self { + pub fn base_response_packet(&self) -> Self { Self { header: PRUDPHeader { magic: [0xEA, 0xD0], diff --git a/src/prudp/router.rs b/src/prudp/router.rs index 5437fc9..319b7a7 100644 --- a/src/prudp/router.rs +++ b/src/prudp/router.rs @@ -12,9 +12,10 @@ use tokio::task::JoinHandle; use once_cell::sync::Lazy; use log::{error, info, trace, warn}; use thiserror::Error; +use tokio::io::Join; use tokio::sync::RwLock; use crate::prudp::auth_module::AuthModule; -use crate::prudp::socket::{Socket, SocketImpl}; +use crate::prudp::socket::{Socket, SocketData}; use crate::prudp::packet::{PRUDPPacket, VirtualPort}; use crate::prudp::router::Error::VirtualPortTaken; use crate::prudp::sockaddr::PRUDPSockAddr; @@ -26,7 +27,7 @@ static SERVER_DATAGRAMS: Lazy = Lazy::new(||{ }); pub struct Router { - endpoints: RwLock<[Option>; 16]>, + endpoints: RwLock<[Option>; 16]>, running: AtomicBool, socket: Arc, //pub auth_module: Arc @@ -43,8 +44,8 @@ impl Router { fn process_prudp_packet(&self, packet: &PRUDPPacket){ } - async fn process_prudp_packets<'a>(&self, socket: &'a UdpSocket, addr: SocketAddrV4, udp_message: &[u8]){ - let mut stream = Cursor::new(udp_message); + async fn process_prudp_packets<'a>(self: Arc, socket: Arc, addr: SocketAddrV4, udp_message: Vec){ + let mut stream = Cursor::new(&udp_message); while stream.position() as usize != udp_message.len() { let packet = match PRUDPPacket::new(&mut stream){ @@ -93,13 +94,12 @@ impl Router { }; let current_msg = &msg_buffer[0..len]; - info!("attempting to process message"); - self.process_prudp_packets(&socket, addr, current_msg).await; + tokio::spawn(self.clone().process_prudp_packets(socket.clone(), addr, current_msg.to_vec())); } } - pub async fn new(addr: SocketAddrV4) -> io::Result>{ + pub async fn new(addr: SocketAddrV4) -> io::Result<(Arc, JoinHandle<()>)>{ trace!("starting router on {}", addr); let socket = Arc::new(UdpSocket::bind(addr).await?); @@ -114,14 +114,14 @@ impl Router { let arc = Arc::new(own_impl); - { + let task = { let socket = socket.clone(); let server= arc.clone(); tokio::spawn(async { server.server_thread_send_entry(socket).await; - }); - } + }) + }; { let socket = socket.clone(); @@ -135,7 +135,7 @@ impl Router { } - Ok(arc) + Ok((arc, task)) } pub fn get_udp_socket(&self) -> Arc{ @@ -149,7 +149,7 @@ impl Router { } // returns Some(()) i - pub(crate) async fn add_socket(&self, socket: Arc) -> Result<(), Error>{ + pub(crate) async fn add_socket(&self, socket: Arc) -> Result<(), Error>{ let mut endpoints = self.endpoints.write().await; let idx = socket.get_virual_port().get_port_number() as usize; diff --git a/src/prudp/socket.rs b/src/prudp/socket.rs index a474ebb..ec28e01 100644 --- a/src/prudp/socket.rs +++ b/src/prudp/socket.rs @@ -1,74 +1,101 @@ use std::array; use std::collections::{HashMap, VecDeque}; +use std::future::Future; use std::io::Write; use std::ops::Deref; +use std::pin::Pin; use tokio::net::UdpSocket; use std::sync::{Arc}; -use tokio::sync::{Mutex, RwLock}; +use tokio::sync::{Mutex, MutexGuard, RwLock}; use hmac::{Hmac, Mac}; use log::{error, info, trace, warn}; use rand::random; -use rc4::consts::U256; +use rc4::consts::{U256, U5}; +use rc4::{Rc4, Rc4Core, StreamCipher}; +use rc4::cipher::{KeySizeUser, StreamCipherCoreWrapper}; use rustls::internal::msgs::handshake::SessionId; +use tokio::io::AsyncWriteExt; use tokio::sync::mpsc::{channel, Receiver, Sender}; +use tokio::task::JoinHandle; +use tokio_stream::wrappers::ReceiverStream; use crate::prudp::packet::{flags, PacketOption, PRUDPPacket, types, VirtualPort}; use crate::prudp::packet::flags::{ACK, HAS_SIZE, MULTI_ACK, NEED_ACK, RELIABLE}; use crate::prudp::packet::PacketOption::{ConnectionSignature, MaximumSubstreamId, SupportedFunctions}; -use crate::prudp::packet::types::{CONNECT, DATA, SYN}; +use crate::prudp::packet::types::{CONNECT, DATA, PING, SYN}; use crate::prudp::router::{Error, Router}; use crate::prudp::sockaddr::PRUDPSockAddr; +use rc4::KeyInit; +// due to the way this is designed crashing the router thread causes deadlock, sorry ;-; +// (maybe i will fix that some day) + /// PRUDP Socket for accepting connections to then send and recieve data from those clients -pub struct Socket(Arc, Arc, Receiver); - -#[derive(Debug)] -pub struct SocketImpl { - virtual_port: VirtualPort, - socket: Arc, - access_key: &'static str, - connections: RwLock>>>, - connection_creation_sender: Sender, +pub struct Socket { + socket_data: Arc, + router: Arc, } +type OnConnectHandlerFn = Box Pin, Box))> + Send + Sync>> + Send + Sync>; +type OnDataHandlerFn = Box Fn(PRUDPPacket, Arc, &'a mut MutexGuard<'_, ConnectionData>) -> Pin + 'a + Send + Sync>> + Send + Sync>; -#[derive(Debug)] -pub struct Connection { - sock_addr: PRUDPSockAddr, - id: u64, - signature: [u8; 16], - server_signature: [u8; 16], - session_id: u8, - reliable_client_counter: u16, - reliable_server_counter: u16, - reliable_client_queue: VecDeque, +pub struct SocketData { + virtual_port: VirtualPort, + pub socket: Arc, + pub access_key: &'static str, + connections: RwLock>>>, + on_connect_handler: OnConnectHandlerFn, + on_data_handler: OnDataHandlerFn, +} + +pub struct ActiveConnectionData { + pub reliable_client_counter: u16, + pub reliable_server_counter: u16, + pub reliable_client_queue: VecDeque, + pub connection_data_channel: Sender>, + pub server_encryption: Box, + pub client_decryption: Box, + pub server_session_id: u8, +} + + +pub struct ConnectionData { + pub sock_addr: PRUDPSockAddr, + pub id: u64, + pub signature: [u8; 16], + pub server_signature: [u8; 16], + pub active_connection_data: Option, } impl Socket { - pub async fn new(router: Arc, port: VirtualPort, access_key: &'static str) -> Result { + pub async fn new( + router: Arc, + port: VirtualPort, + access_key: &'static str, + on_connection_handler: OnConnectHandlerFn, + on_data_handler: OnDataHandlerFn, + ) -> Result { trace!("creating socket on router at {} on virtual port {:?}", router.get_own_address(), port); - let (send, recv) = channel(20); - let socket = Arc::new( - SocketImpl::new(&router, send, port, access_key) + let socket_data = Arc::new( + SocketData::new_unbound(&router, port, access_key, on_connection_handler, on_data_handler) ); - router.add_socket(socket.clone()).await?; + router.add_socket(socket_data.clone()).await?; - Ok(Self(socket, router, recv)) - } - - pub async fn accept(&mut self) -> Option { - self.2.recv().await + Ok(Self { + socket_data, + router, + }) } } impl Drop for Socket { fn drop(&mut self) { { - let router = self.1.clone(); + let router = self.router.clone(); let virtual_port = self.virtual_port; trace!("socket dropped socket will be removed from router soon"); @@ -82,21 +109,27 @@ impl Drop for Socket { } impl Deref for Socket { - type Target = SocketImpl; + type Target = SocketData; fn deref(&self) -> &Self::Target { - &self.0 + &self.socket_data } } -impl SocketImpl { - fn new(router: &Router, connection_creation_sender: Sender, port: VirtualPort, access_key: &'static str) -> Self { - SocketImpl { +impl SocketData { + fn new_unbound(router: &Router, + port: VirtualPort, + access_key: &'static str, + on_connect_handler: OnConnectHandlerFn, + on_data_handler: OnDataHandlerFn, + ) -> Self { + SocketData { socket: router.get_udp_socket(), virtual_port: port, connections: Default::default(), access_key, - connection_creation_sender, + on_connect_handler, + on_data_handler, } } @@ -104,26 +137,22 @@ impl SocketImpl { self.virtual_port } - pub async fn process_packet(&self, connection: PRUDPSockAddr, packet: &PRUDPPacket) { - info!("recieved packet on endpoint"); - + pub async fn process_packet(self: &Arc, client_address: PRUDPSockAddr, packet: &PRUDPPacket) { let conn = self.connections.read().await; - if !conn.contains_key(&connection) { + if !conn.contains_key(&client_address) { drop(conn); let mut conn = self.connections.write().await; //only insert if we STILL dont have the connection preventing double insertion - if !conn.contains_key(&connection) { - conn.insert(connection, Arc::new(Mutex::new(Connection { - sock_addr: connection, + if !conn.contains_key(&client_address) { + conn.insert(client_address, Arc::new(Mutex::new(ConnectionData { + sock_addr: client_address, id: random(), signature: [0; 16], server_signature: [0; 16], - session_id: 0, - reliable_client_queue: VecDeque::new(), - reliable_client_counter: 0, - reliable_server_counter: 0, + + active_connection_data: None, }))); } drop(conn); @@ -133,7 +162,7 @@ impl SocketImpl { let connections = self.connections.read().await; - let Some(conn) = connections.get(&connection) else { + let Some(conn) = connections.get(&client_address) else { error!("connection is still not present after making sure connection is present, giving up."); return; }; @@ -143,7 +172,7 @@ impl SocketImpl { // dont keep holding the connections list unnescesarily drop(connections); - let mut conn = conn.lock().await; + let mut connection = conn.lock().await; if (packet.header.types_and_flags.get_flags() & ACK) != 0 { info!("acknowledgement recieved"); @@ -152,7 +181,7 @@ impl SocketImpl { if (packet.header.types_and_flags.get_flags() & MULTI_ACK) != 0 { info!("acknowledgement recieved"); - unimplemented!() + return; } @@ -166,9 +195,9 @@ impl SocketImpl { response_packet.header.types_and_flags.set_flag(ACK); response_packet.header.types_and_flags.set_flag(HAS_SIZE); - conn.signature = connection.calculate_connection_signature(); + connection.signature = client_address.calculate_connection_signature(); - response_packet.options.push(ConnectionSignature(conn.signature)); + response_packet.options.push(ConnectionSignature(connection.signature)); for options in &packet.options { match options { @@ -190,7 +219,7 @@ impl SocketImpl { response_packet.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); - self.socket.send_to(&vec, connection.regular_socket_addr).await.expect("failed to send data back"); + self.socket.send_to(&vec, client_address.regular_socket_addr).await.expect("failed to send data back"); } CONNECT => { info!("got connect"); @@ -202,18 +231,23 @@ impl SocketImpl { response_packet.header.types_and_flags.set_flag(HAS_SIZE); // todo: (or not) sliding windows and stuff - conn.session_id = packet.header.session_id; - response_packet.header.session_id = conn.session_id; + + response_packet.header.session_id = packet.header.session_id; response_packet.header.sequence_id = 1; response_packet.options.push(ConnectionSignature(Default::default())); + let mut init_seq_id = 0; + for option in &packet.options { match option { MaximumSubstreamId(max_substream) => response_packet.options.push(MaximumSubstreamId(*max_substream)), SupportedFunctions(funcs) => response_packet.options.push(SupportedFunctions(*funcs)), ConnectionSignature(sig) => { - conn.server_signature = *sig + connection.server_signature = *sig + } + PacketOption::InitialSequenceId(id) => { + init_seq_id = *id; } _ => { /* ? */ } } @@ -225,53 +259,125 @@ impl SocketImpl { // todo: implement something to do secure servers - if conn.server_signature == <[u8; 16] as Default>::default() { + if connection.server_signature == <[u8; 16] as Default>::default() { error!("didn't get connection signature from client") } response_packet.set_sizes(); - response_packet.calculate_and_assign_signature(self.access_key, None, Some(conn.server_signature)); + response_packet.calculate_and_assign_signature(self.access_key, None, Some(connection.server_signature)); let mut vec = Vec::new(); response_packet.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); - self.socket.send_to(&vec, connection.regular_socket_addr).await.expect("failed to send data back"); + self.socket.send_to(&vec, client_address.regular_socket_addr).await.expect("failed to send data back"); + + let (send, recv) = channel(100); + + let (accepted, (client_decryption, server_encryption)) + = (self.on_connect_handler)(packet.clone()).await; + + if !accepted { + // rejected + return; + } + + connection.active_connection_data = Some(ActiveConnectionData { + connection_data_channel: send, + client_decryption, + server_encryption, + reliable_client_queue: VecDeque::new(), + reliable_client_counter: 2, + reliable_server_counter: 1, + server_session_id: packet.header.session_id, + }); } DATA => { if (packet.header.types_and_flags.get_flags() & RELIABLE) != 0 { - match conn.reliable_client_queue.binary_search_by_key(&conn.reliable_client_counter, |p| p.header.sequence_id) { + let Some(active_connection) = connection.active_connection_data.as_mut() else { + error!("got data packet on non active connection!"); + return; + }; + + info!("ctr: {}, packet seq: {}", active_connection.reliable_client_counter, packet.header.sequence_id); + + match active_connection.reliable_client_queue.binary_search_by_key(&packet.header.sequence_id, |p| p.header.sequence_id) { Ok(_) => warn!("recieved packet twice"), - Err(position) => conn.reliable_client_queue.insert(position, packet.clone()), + Err(position) => active_connection.reliable_client_queue.insert(position, packet.clone()), } - if (packet.header.types_and_flags.get_flags() & NEED_ACK) != 0{ + + if (packet.header.types_and_flags.get_flags() & NEED_ACK) != 0 { let mut ack = packet.base_acknowledgement_packet(); + ack.header.session_id = active_connection.server_session_id; + ack.set_sizes(); - ack.calculate_and_assign_signature(self.access_key, None, Some(conn.server_signature)); + ack.calculate_and_assign_signature(self.access_key, None, Some(connection.server_signature)); let mut vec = Vec::new(); ack.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); - self.socket.send_to(&vec, connection.regular_socket_addr).await.expect("failed to send data back"); + self.socket.send_to(&vec, client_address.regular_socket_addr).await.expect("failed to send data back"); } - while let Some(packet) = - conn.reliable_client_queue + while let Some(mut packet) = { + connection.active_connection_data.as_mut().map(|a| + a.reliable_client_queue .front() - .is_some_and(|v| v.header.sequence_id == conn.reliable_client_counter) - .then(|| conn.reliable_client_queue.pop_front()) - .flatten(){ - conn.reliable_client_counter = conn.reliable_client_counter.overflowing_add(1).0; + .is_some_and(|v| v.header.sequence_id == a.reliable_client_counter) + .then(|| a.reliable_client_queue.pop_front())).flatten().flatten() + } { + if packet.options.iter().any(|v| match v{ + PacketOption::FragmentId(f) => (*f != 0), + _ => false, + }){ + error!("fragmented packets are unsupported right now") + } - // ignored + let active_connection = connection.active_connection_data.as_mut() + .expect("we litterally just recieved a packet which requires the connection to be active, failing this should be impossible"); + + active_connection.reliable_client_counter = active_connection.reliable_client_counter.overflowing_add(1).0; + + active_connection.client_decryption.apply_keystream(&mut packet.payload); + + // we cant divert this off to another thread we HAVE to process it now to keep order + + (self.on_data_handler)(packet, self.clone(), &mut connection).await; + // ignored for now } } else { error!("unreliable packets are unimplemented"); unimplemented!() } - info!("{:?}", packet); + //info!("{:?}", packet); } + PING => { + let ConnectionData { + active_connection_data, + server_signature, + .. + } = &mut *connection; + + if (packet.header.types_and_flags.get_flags() & NEED_ACK) != 0 { + let Some(active_connection) = active_connection_data.as_mut() else { + error!("got data packet on non active connection!"); + return; + }; + + let mut ack = packet.base_acknowledgement_packet(); + ack.header.session_id = active_connection.server_session_id; + + ack.set_sizes(); + ack.calculate_and_assign_signature(self.access_key, None, Some(*server_signature)); + + let mut vec = Vec::new(); + ack.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); + + self.socket.send_to(&vec, client_address.regular_socket_addr).await.expect("failed to send data back"); + } + } + _ => unimplemented!("unimplemented packet type: {}", packet.header.types_and_flags.get_types()) } } @@ -286,9 +392,9 @@ mod test { use tokio::sync::mpsc::channel; use crate::prudp::packet::{PRUDPPacket, VirtualPort}; use crate::prudp::sockaddr::PRUDPSockAddr; - use crate::prudp::socket::SocketImpl; + use crate::prudp::socket::SocketData; - #[tokio::test] + /*#[tokio::test] async fn test_connect() { let packet_1 = [234, 208, 1, 27, 0, 0, 175, 161, 192, 0, 0, 0, 0, 0, 36, 21, 233, 179, 203, 154, 57, 222, 219, 9, 21, 2, 29, 172, 56, 92, 0, 4, 4, 1, 0, 0, 1, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 0]; let packet_2 = [234, 208, 1, 31, 0, 0, 175, 161, 225, 0, 249, 0, 1, 0, 40, 168, 31, 138, 58, 193, 30, 134, 3, 232, 205, 245, 28, 155, 193, 198, 0, 4, 0, 0, 0, 0, 1, 16, 211, 240, 113, 188, 227, 114, 114, 30, 157, 179, 246, 55, 233, 240, 44, 197, 3, 2, 247, 244, 4, 1, 0]; @@ -299,13 +405,13 @@ mod test { let (send, recv) = channel(100); - let sock = SocketImpl { + let sock = Arc::new(SocketData { connections: Default::default(), access_key: "6f599f81", virtual_port: VirtualPort(0), socket: Arc::new(UdpSocket::bind(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 10000)).await.unwrap()), connection_creation_sender: send, - }; + }); println!("sent: {:?}", packet_1); sock.process_packet(PRUDPSockAddr { virtual_port: VirtualPort(0), @@ -316,5 +422,5 @@ mod test { virtual_port: VirtualPort(0), regular_socket_addr: SocketAddrV4::new(Ipv4Addr::LOCALHOST, 2469), }, &packet_2).await; - } + }*/ } \ No newline at end of file diff --git a/src/rmc/message.rs b/src/rmc/message.rs new file mode 100644 index 0000000..e7b6f8f --- /dev/null +++ b/src/rmc/message.rs @@ -0,0 +1,61 @@ +use std::io; +use std::io::{Read, Seek}; +use log::error; +use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; +use crate::rmc::response::{ErrorCode, RMCResponseResult}; + +#[derive(Debug)] +pub struct RMCMessage{ + pub protocol_id: u16, + pub call_id: u32, + pub method_id: u32, + + pub rest_of_data: Vec +} + +impl RMCMessage{ + pub fn new(stream: &mut (impl Seek + Read)) -> io::Result{ + let size: u32 = stream.read_struct(IS_BIG_ENDIAN)?; + + let mut header_size = 1 + 4 + 4; + + let protocol_id: u8 = stream.read_struct(IS_BIG_ENDIAN)?; + let protocol_id= protocol_id & (!0x80); + + let protocol_id: u16 = match protocol_id{ + 0x7F => { + header_size += 2; + stream.read_struct(IS_BIG_ENDIAN)? + }, + _ => protocol_id as u16 + }; + + let call_id = stream.read_struct(IS_BIG_ENDIAN)?; + let method_id = stream.read_struct(IS_BIG_ENDIAN)?; + + let mut rest_of_data = Vec::new(); + + stream.read_to_end(&mut rest_of_data)?; + + if header_size + rest_of_data.len() != size as usize { + error!("received incorrect rmc packet: expected size {} but found {}", size, header_size + rest_of_data.len()); + } + + + + //stream. + Ok(Self{ + protocol_id, + method_id, + call_id, + rest_of_data + }) + } + + pub fn error_result_with_code(&self, error_code: ErrorCode) -> RMCResponseResult{ + RMCResponseResult::Error { + call_id: self.call_id, + error_code + } + } +} \ No newline at end of file diff --git a/src/rmc/mod.rs b/src/rmc/mod.rs new file mode 100644 index 0000000..bf1eb0c --- /dev/null +++ b/src/rmc/mod.rs @@ -0,0 +1,6 @@ +pub mod message; +pub mod structures; +pub mod response; + + + diff --git a/src/rmc/response.rs b/src/rmc/response.rs new file mode 100644 index 0000000..8506e5d --- /dev/null +++ b/src/rmc/response.rs @@ -0,0 +1,432 @@ +use std::io; +use std::io::{Cursor, Write}; +use std::mem::transmute; +use tokio::sync::Mutex; +use bytemuck::bytes_of; +use hmac::digest::consts::U5; +use hmac::digest::KeyInit; +use rc4::{Rc4, StreamCipher}; +use crate::prudp::packet::{PRUDPHeader, PRUDPPacket, TypesFlags}; +use crate::prudp::packet::flags::{HAS_SIZE, NEED_ACK, RELIABLE}; +use crate::prudp::packet::PacketOption::FragmentId; +use crate::prudp::packet::types::DATA; +use crate::prudp::socket::{ConnectionData, SocketData}; + +pub enum RMCResponseResult { + Success{ + call_id: u32, + method_id: u32, + data: Vec, + }, + Error{ + error_code: ErrorCode, + call_id: u32, + } +} + +pub struct RMCResponse { + pub protocol_id: u8, + pub response_result: RMCResponseResult +} + +impl RMCResponse { + pub fn to_data(self) -> Vec{ + generate_response(self.protocol_id, self.response_result).expect("failed to generate response") + } +} + +pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Result>{ + let size = 1 + 1 + match &response{ + RMCResponseResult::Success { + data, + .. + } => 4 + 4 + data.len(), + RMCResponseResult::Error{..} => 4 + 4, + }; + + let mut data_out = Vec::with_capacity(size + 4); + + let u32_size: u32 = size as _; + + data_out.write_all(bytes_of(&u32_size))?; + data_out.push(protocol_id | 0x80); + + match response{ + RMCResponseResult::Success { + call_id, + method_id, + data + } => { + data_out.push(1); + data_out.write_all(bytes_of(&call_id))?; + data_out.write_all(bytes_of(&method_id))?; + data_out.write_all(&data)?; + }, + RMCResponseResult::Error { + call_id, + error_code + } => { + data_out.push(0); + let error_code_val: u32 = error_code.into(); + data_out.write_all(bytes_of(&error_code_val))?; + data_out.write_all(bytes_of(&call_id))?; + } + } + + assert_eq!(data_out.len(), size + 4); + + Ok(data_out) +} +pub async fn send_response(original_packet: &PRUDPPacket, socket: &SocketData, connection: &mut ConnectionData, rmcresponse: RMCResponse){ + + let ConnectionData{ + active_connection_data, + sock_addr, + server_signature, + .. + } = connection; + + let Some(active_connection) = active_connection_data else { + return; + }; + + + let mut packet = original_packet.base_response_packet(); + + + packet.header.types_and_flags.set_types(DATA); + packet.header.types_and_flags.set_flag(RELIABLE | HAS_SIZE | NEED_ACK); + + packet.header.sequence_id = active_connection.reliable_server_counter; + active_connection.reliable_server_counter += 1; + packet.header.session_id = active_connection.server_session_id; + packet.header.substream_id = 0; + + packet.options.push(FragmentId(0)); + + packet.payload = rmcresponse.to_data(); + + + active_connection.server_encryption.apply_keystream(&mut packet.payload); + + packet.set_sizes(); + packet.calculate_and_assign_signature(socket.access_key, None, Some(*server_signature)); + + let mut vec = Vec::new(); + + packet.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); + + socket.socket.send_to(&vec, sock_addr.regular_socket_addr).await.expect("failed to send data back"); + +} + +//taken from kinnays error list directly +#[allow(nonstandard_style)] +#[repr(u32)] +pub enum ErrorCode { + Core_Unknown = 0x00010001, + Core_NotImplemented = 0x00010002, + Core_InvalidPointer = 0x00010003, + Core_OperationAborted = 0x00010004, + Core_Exception = 0x00010005, + Core_AccessDenied = 0x00010006, + Core_InvalidHandle = 0x00010007, + Core_InvalidIndex = 0x00010008, + Core_OutOfMemory = 0x00010009, + Core_InvalidArgument = 0x0001000A, + Core_Timeout = 0x0001000B, + Core_InitializationFailure = 0x0001000C, + Core_CallInitiationFailure = 0x0001000D, + Core_RegistrationError = 0x0001000E, + Core_BufferOverflow = 0x0001000F, + Core_InvalidLockState = 0x00010010, + Core_InvalidSequence = 0x00010011, + Core_SystemError = 0x00010012, + Core_Cancelled = 0x00010013, + DDL_InvalidSignature = 0x00020001, + DDL_IncorrectVersion = 0x00020002, + RendezVous_ConnectionFailure = 0x00030001, + RendezVous_NotAuthenticated = 0x00030002, + RendezVous_InvalidUsername = 0x00030064, + RendezVous_InvalidPassword = 0x00030065, + RendezVous_UsernameAlreadyExists = 0x00030066, + RendezVous_AccountDisabled = 0x00030067, + RendezVous_AccountExpired = 0x00030068, + RendezVous_ConcurrentLoginDenied = 0x00030069, + RendezVous_EncryptionFailure = 0x0003006A, + RendezVous_InvalidPID = 0x0003006B, + RendezVous_MaxConnectionsReached = 0x0003006C, + RendezVous_InvalidGID = 0x0003006D, + RendezVous_InvalidControlScriptID = 0x0003006E, + RendezVous_InvalidOperationInLiveEnvironment = 0x0003006F, + RendezVous_DuplicateEntry = 0x00030070, + RendezVous_ControlScriptFailure = 0x00030071, + RendezVous_ClassNotFound = 0x00030072, + RendezVous_SessionVoid = 0x00030073, + RendezVous_DDLMismatch = 0x00030075, + RendezVous_InvalidConfiguration = 0x00030076, + RendezVous_SessionFull = 0x000300C8, + RendezVous_InvalidGatheringPassword = 0x000300C9, + RendezVous_WithoutParticipationPeriod = 0x000300CA, + RendezVous_PersistentGatheringCreationMax = 0x000300CB, + RendezVous_PersistentGatheringParticipationMax = 0x000300CC, + RendezVous_DeniedByParticipants = 0x000300CD, + RendezVous_ParticipantInBlackList = 0x000300CE, + RendezVous_GameServerMaintenance = 0x000300CF, + RendezVous_OperationPostpone = 0x000300D0, + RendezVous_OutOfRatingRange = 0x000300D1, + RendezVous_ConnectionDisconnected = 0x000300D2, + RendezVous_InvalidOperation = 0x000300D3, + RendezVous_NotParticipatedGathering = 0x000300D4, + RendezVous_MatchmakeSessionUserPasswordUnmatch = 0x000300D5, + RendezVous_MatchmakeSessionSystemPasswordUnmatch = 0x000300D6, + RendezVous_UserIsOffline = 0x000300D7, + RendezVous_AlreadyParticipatedGathering = 0x000300D8, + RendezVous_PermissionDenied = 0x000300D9, + RendezVous_NotFriend = 0x000300DA, + RendezVous_SessionClosed = 0x000300DB, + RendezVous_DatabaseTemporarilyUnavailable = 0x000300DC, + RendezVous_InvalidUniqueId = 0x000300DD, + RendezVous_MatchmakingWithdrawn = 0x000300DE, + RendezVous_LimitExceeded = 0x000300DF, + RendezVous_AccountTemporarilyDisabled = 0x000300E0, + RendezVous_PartiallyServiceClosed = 0x000300E1, + RendezVous_ConnectionDisconnectedForConcurrentLogin = 0x000300E2, + PythonCore_Exception = 0x00040001, + PythonCore_TypeError = 0x00040002, + PythonCore_IndexError = 0x00040003, + PythonCore_InvalidReference = 0x00040004, + PythonCore_CallFailure = 0x00040005, + PythonCore_MemoryError = 0x00040006, + PythonCore_KeyError = 0x00040007, + PythonCore_OperationError = 0x00040008, + PythonCore_ConversionError = 0x00040009, + PythonCore_ValidationError = 0x0004000A, + Transport_Unknown = 0x00050001, + Transport_ConnectionFailure = 0x00050002, + Transport_InvalidUrl = 0x00050003, + Transport_InvalidKey = 0x00050004, + Transport_InvalidURLType = 0x00050005, + Transport_DuplicateEndpoint = 0x00050006, + Transport_IOError = 0x00050007, + Transport_Timeout = 0x00050008, + Transport_ConnectionReset = 0x00050009, + Transport_IncorrectRemoteAuthentication = 0x0005000A, + Transport_ServerRequestError = 0x0005000B, + Transport_DecompressionFailure = 0x0005000C, + Transport_ReliableSendBufferFullFatal = 0x0005000D, + Transport_UPnPCannotInit = 0x0005000E, + Transport_UPnPCannotAddMapping = 0x0005000F, + Transport_NatPMPCannotInit = 0x00050010, + Transport_NatPMPCannotAddMapping = 0x00050011, + Transport_UnsupportedNAT = 0x00050013, + Transport_DnsError = 0x00050014, + Transport_ProxyError = 0x00050015, + Transport_DataRemaining = 0x00050016, + Transport_NoBuffer = 0x00050017, + Transport_NotFound = 0x00050018, + Transport_TemporaryServerError = 0x00050019, + Transport_PermanentServerError = 0x0005001A, + Transport_ServiceUnavailable = 0x0005001B, + Transport_ReliableSendBufferFull = 0x0005001C, + Transport_InvalidStation = 0x0005001D, + Transport_InvalidSubStreamID = 0x0005001E, + Transport_PacketBufferFull = 0x0005001F, + Transport_NatTraversalError = 0x00050020, + Transport_NatCheckError = 0x00050021, + DOCore_StationNotReached = 0x00060001, + DOCore_TargetStationDisconnect = 0x00060002, + DOCore_LocalStationLeaving = 0x00060003, + DOCore_ObjectNotFound = 0x00060004, + DOCore_InvalidRole = 0x00060005, + DOCore_CallTimeout = 0x00060006, + DOCore_RMCDispatchFailed = 0x00060007, + DOCore_MigrationInProgress = 0x00060008, + DOCore_NoAuthority = 0x00060009, + DOCore_NoTargetStationSpecified = 0x0006000A, + DOCore_JoinFailed = 0x0006000B, + DOCore_JoinDenied = 0x0006000C, + DOCore_ConnectivityTestFailed = 0x0006000D, + DOCore_Unknown = 0x0006000E, + DOCore_UnfreedReferences = 0x0006000F, + DOCore_JobTerminationFailed = 0x00060010, + DOCore_InvalidState = 0x00060011, + DOCore_FaultRecoveryFatal = 0x00060012, + DOCore_FaultRecoveryJobProcessFailed = 0x00060013, + DOCore_StationInconsitency = 0x00060014, + DOCore_AbnormalMasterState = 0x00060015, + DOCore_VersionMismatch = 0x00060016, + FPD_NotInitialized = 0x00650000, + FPD_AlreadyInitialized = 0x00650001, + FPD_NotConnected = 0x00650002, + FPD_Connected = 0x00650003, + FPD_InitializationFailure = 0x00650004, + FPD_OutOfMemory = 0x00650005, + FPD_RmcFailed = 0x00650006, + FPD_InvalidArgument = 0x00650007, + FPD_InvalidLocalAccountID = 0x00650008, + FPD_InvalidPrincipalID = 0x00650009, + FPD_InvalidLocalFriendCode = 0x0065000A, + FPD_LocalAccountNotExists = 0x0065000B, + FPD_LocalAccountNotLoaded = 0x0065000C, + FPD_LocalAccountAlreadyLoaded = 0x0065000D, + FPD_FriendAlreadyExists = 0x0065000E, + FPD_FriendNotExists = 0x0065000F, + FPD_FriendNumMax = 0x00650010, + FPD_NotFriend = 0x00650011, + FPD_FileIO = 0x00650012, + FPD_P2PInternetProhibited = 0x00650013, + FPD_Unknown = 0x00650014, + FPD_InvalidState = 0x00650015, + FPD_AddFriendProhibited = 0x00650017, + FPD_InvalidAccount = 0x00650019, + FPD_BlacklistedByMe = 0x0065001A, + FPD_FriendAlreadyAdded = 0x0065001C, + FPD_MyFriendListLimitExceed = 0x0065001D, + FPD_RequestLimitExceed = 0x0065001E, + FPD_InvalidMessageID = 0x0065001F, + FPD_MessageIsNotMine = 0x00650020, + FPD_MessageIsNotForMe = 0x00650021, + FPD_FriendRequestBlocked = 0x00650022, + FPD_NotInMyFriendList = 0x00650023, + FPD_FriendListedByMe = 0x00650024, + FPD_NotInMyBlacklist = 0x00650025, + FPD_IncompatibleAccount = 0x00650026, + FPD_BlockSettingChangeNotAllowed = 0x00650027, + FPD_SizeLimitExceeded = 0x00650028, + FPD_OperationNotAllowed = 0x00650029, + FPD_NotNetworkAccount = 0x0065002A, + FPD_NotificationNotFound = 0x0065002B, + FPD_PreferenceNotInitialized = 0x0065002C, + FPD_FriendRequestNotAllowed = 0x0065002D, + Ranking_NotInitialized = 0x00670001, + Ranking_InvalidArgument = 0x00670002, + Ranking_RegistrationError = 0x00670003, + Ranking_NotFound = 0x00670005, + Ranking_InvalidScore = 0x00670006, + Ranking_InvalidDataSize = 0x00670007, + Ranking_PermissionDenied = 0x00670009, + Ranking_Unknown = 0x0067000A, + Ranking_NotImplemented = 0x0067000B, + Authentication_NASAuthenticateError = 0x00680001, + Authentication_TokenParseError = 0x00680002, + Authentication_HttpConnectionError = 0x00680003, + Authentication_HttpDNSError = 0x00680004, + Authentication_HttpGetProxySetting = 0x00680005, + Authentication_TokenExpired = 0x00680006, + Authentication_ValidationFailed = 0x00680007, + Authentication_InvalidParam = 0x00680008, + Authentication_PrincipalIdUnmatched = 0x00680009, + Authentication_MoveCountUnmatch = 0x0068000A, + Authentication_UnderMaintenance = 0x0068000B, + Authentication_UnsupportedVersion = 0x0068000C, + Authentication_ServerVersionIsOld = 0x0068000D, + Authentication_Unknown = 0x0068000E, + Authentication_ClientVersionIsOld = 0x0068000F, + Authentication_AccountLibraryError = 0x00680010, + Authentication_ServiceNoLongerAvailable = 0x00680011, + Authentication_UnknownApplication = 0x00680012, + Authentication_ApplicationVersionIsOld = 0x00680013, + Authentication_OutOfService = 0x00680014, + Authentication_NetworkServiceLicenseRequired = 0x00680015, + Authentication_NetworkServiceLicenseSystemError = 0x00680016, + Authentication_NetworkServiceLicenseError3 = 0x00680017, + Authentication_NetworkServiceLicenseError4 = 0x00680018, + DataStore_Unknown = 0x00690001, + DataStore_InvalidArgument = 0x00690002, + DataStore_PermissionDenied = 0x00690003, + DataStore_NotFound = 0x00690004, + DataStore_AlreadyLocked = 0x00690005, + DataStore_UnderReviewing = 0x00690006, + DataStore_Expired = 0x00690007, + DataStore_InvalidCheckToken = 0x00690008, + DataStore_SystemFileError = 0x00690009, + DataStore_OverCapacity = 0x0069000A, + DataStore_OperationNotAllowed = 0x0069000B, + DataStore_InvalidPassword = 0x0069000C, + DataStore_ValueNotEqual = 0x0069000D, + ServiceItem_Unknown = 0x006C0001, + ServiceItem_InvalidArgument = 0x006C0002, + ServiceItem_EShopUnknownHttpError = 0x006C0003, + ServiceItem_EShopResponseParseError = 0x006C0004, + ServiceItem_NotOwned = 0x006C0005, + ServiceItem_InvalidLimitationType = 0x006C0006, + ServiceItem_ConsumptionRightShortage = 0x006C0007, + MatchmakeReferee_Unknown = 0x006F0001, + MatchmakeReferee_InvalidArgument = 0x006F0002, + MatchmakeReferee_AlreadyExists = 0x006F0003, + MatchmakeReferee_NotParticipatedGathering = 0x006F0004, + MatchmakeReferee_NotParticipatedRound = 0x006F0005, + MatchmakeReferee_StatsNotFound = 0x006F0006, + MatchmakeReferee_RoundNotFound = 0x006F0007, + MatchmakeReferee_RoundArbitrated = 0x006F0008, + MatchmakeReferee_RoundNotArbitrated = 0x006F0009, + Subscriber_Unknown = 0x00700001, + Subscriber_InvalidArgument = 0x00700002, + Subscriber_OverLimit = 0x00700003, + Subscriber_PermissionDenied = 0x00700004, + Ranking2_Unknown = 0x00710001, + Ranking2_InvalidArgument = 0x00710002, + Ranking2_InvalidScore = 0x00710003, + SmartDeviceVoiceChat_Unknown = 0x00720001, + SmartDeviceVoiceChat_InvalidArgument = 0x00720002, + SmartDeviceVoiceChat_InvalidResponse = 0x00720003, + SmartDeviceVoiceChat_InvalidAccessToken = 0x00720004, + SmartDeviceVoiceChat_Unauthorized = 0x00720005, + SmartDeviceVoiceChat_AccessError = 0x00720006, + SmartDeviceVoiceChat_UserNotFound = 0x00720007, + SmartDeviceVoiceChat_RoomNotFound = 0x00720008, + SmartDeviceVoiceChat_RoomNotActivated = 0x00720009, + SmartDeviceVoiceChat_ApplicationNotSupported = 0x0072000A, + SmartDeviceVoiceChat_InternalServerError = 0x0072000B, + SmartDeviceVoiceChat_ServiceUnavailable = 0x0072000C, + SmartDeviceVoiceChat_UnexpectedError = 0x0072000D, + SmartDeviceVoiceChat_UnderMaintenance = 0x0072000E, + SmartDeviceVoiceChat_ServiceNoLongerAvailable = 0x0072000F, + SmartDeviceVoiceChat_AccountTemporarilyDisabled = 0x00720010, + SmartDeviceVoiceChat_PermissionDenied = 0x00720011, + SmartDeviceVoiceChat_NetworkServiceLicenseRequired = 0x00720012, + SmartDeviceVoiceChat_AccountLibraryError = 0x00720013, + SmartDeviceVoiceChat_GameModeNotFound = 0x00720014, + Screening_Unknown = 0x00730001, + Screening_InvalidArgument = 0x00730002, + Screening_NotFound = 0x00730003, + Custom_Unknown = 0x00740001, + Ess_Unknown = 0x00750001, + Ess_GameSessionError = 0x00750002, + Ess_GameSessionMaintenance = 0x00750003 +} + +impl Into for ErrorCode { + fn into(self) -> u32 { + unsafe{ transmute(self) } + } +} + +#[cfg(test)] +mod test{ + use hmac::digest::consts::U5; + use hmac::digest::KeyInit; + use rc4::{Rc4, StreamCipher}; + + #[test] + fn test(){ + let mut data_orig = [0,1,2,3,4,5,6,7,8,9,69,4,20]; + let mut data = data_orig; + + let mut rc4: Rc4 = + Rc4::new_from_slice("FUCKE".as_bytes().into()).expect("invalid key"); + + rc4.apply_keystream(&mut data); + + assert_ne!(data_orig, data); + + let mut rc4: Rc4 = + Rc4::new_from_slice("FUCKE".as_bytes().into()).expect("invalid key"); + + rc4.apply_keystream(&mut data); + + assert_eq!(data_orig, data); + + } +} \ No newline at end of file diff --git a/src/rmc/structures/any.rs b/src/rmc/structures/any.rs new file mode 100644 index 0000000..b4fe71f --- /dev/null +++ b/src/rmc/structures/any.rs @@ -0,0 +1,28 @@ +use std::io::{Read, Seek}; +use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; +use super::{string, Result}; + +#[derive(Debug)] +pub struct Any{ + pub name: String, + pub data: Vec +} + +pub fn read(reader: &mut (impl Read + Seek)) -> Result{ + let name = string::read(reader)?; + + // also length ? + let len2: u32 = reader.read_struct(IS_BIG_ENDIAN)?; + let length: u32 = reader.read_struct(IS_BIG_ENDIAN)?; + + let mut data = vec![0; length as usize]; + + reader.read_exact(&mut data)?; + + Ok( + Any{ + name, + data + } + ) +} \ No newline at end of file diff --git a/src/rmc/structures/mod.rs b/src/rmc/structures/mod.rs new file mode 100644 index 0000000..8f1fab7 --- /dev/null +++ b/src/rmc/structures/mod.rs @@ -0,0 +1,19 @@ +use std::io; +use std::str::Utf8Error; +use std::string::FromUtf8Error; +use thiserror::Error; + +//ideas for the future: make a proc macro library which allows generation of struct reads + +#[derive(Error, Debug)] +pub enum Error{ + #[error("Io Error: {0}")] + Io(#[from] io::Error), + #[error("UTF8 conversion Error: {0}")] + Utf8(#[from] FromUtf8Error) +} + +type Result = std::result::Result; + +pub mod string; +pub mod any; \ No newline at end of file diff --git a/src/rmc/structures/string.rs b/src/rmc/structures/string.rs new file mode 100644 index 0000000..bf646ec --- /dev/null +++ b/src/rmc/structures/string.rs @@ -0,0 +1,18 @@ +use std::ffi::CString; +use std::io::{Read, Seek}; +use log::error; +use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; +use super::Result; + +pub fn read(reader: &mut (impl Read + Seek)) -> Result{ + let len: u16 = reader.read_struct(IS_BIG_ENDIAN)?; + let mut data = vec![0; len as usize - 1]; + reader.read_exact(&mut data)?; + + let null: u8 = reader.read_struct(IS_BIG_ENDIAN)?; + if null != 0{ + error!("unable to find null terminator... continuing anyways"); + } + + Ok(String::from_utf8(data)?) +} \ No newline at end of file