changes...
This commit is contained in:
parent
6af6e65666
commit
40ca10651f
18 changed files with 998 additions and 112 deletions
25
Cargo.lock
generated
25
Cargo.lock
generated
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"] }
|
||||
|
|
|
|||
|
|
@ -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<T: Read> ReadExtensions for T{
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
pub trait SwapEndian: Clone + Copy{
|
||||
fn swap_endian(self) -> Self;
|
||||
}
|
||||
|
|
|
|||
124
src/main.rs
124
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<u16> = 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"
|
||||
).await.expect("unable to create socket");
|
||||
"6f599f81",
|
||||
Box::new(|_|{
|
||||
Box::pin(
|
||||
async move {
|
||||
let rc4: Rc4<U5> = Rc4::new_from_slice( "CD&ML".as_bytes()).unwrap();
|
||||
let cypher = Box::new(rc4);
|
||||
let server_cypher: Box<dyn StreamCipher + Send + Sync> = cypher;
|
||||
|
||||
let Some(connection) = socket.accept().await else {
|
||||
let rc4: Rc4<U5> = Rc4::new_from_slice( "CD&ML".as_bytes()).unwrap();
|
||||
let cypher = Box::new(rc4);
|
||||
let client_cypher: Box<dyn StreamCipher + Send + Sync> = 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");
|
||||
|
||||
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<Vec<u8>, ParseIntError> {
|
||||
let res: Result<Vec<u8>, _> = 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<U5> =
|
||||
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<U5> =
|
||||
Rc4::new_from_slice("CD&ML".as_bytes().into()).expect("invalid key");
|
||||
|
||||
rc4.apply_keystream(&mut packet.payload);
|
||||
|
||||
println!("packet: {:?}", packet);
|
||||
}
|
||||
}
|
||||
39
src/protocols/auth/method_login_ex.rs
Normal file
39
src/protocols/auth/method_login_ex.rs
Normal file
|
|
@ -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)
|
||||
}
|
||||
25
src/protocols/auth/mod.rs
Normal file
25
src/protocols/auth/mod.rs
Normal file
|
|
@ -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<RMCResponse>{
|
||||
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
|
||||
})
|
||||
}
|
||||
1
src/protocols/mod.rs
Normal file
1
src/protocols/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod auth;
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
use tokio::sync::mpsc::Receiver;
|
||||
|
||||
//struct Connection(Arc<ConnectionImpl>, Receiver<>);
|
||||
|
||||
struct ConnectionImpl{
|
||||
|
||||
}
|
||||
|
|
@ -3,4 +3,3 @@ pub mod router;
|
|||
pub mod socket;
|
||||
mod auth_module;
|
||||
mod sockaddr;
|
||||
mod connection;
|
||||
|
|
@ -36,7 +36,7 @@ pub enum Error {
|
|||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[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 {
|
||||
|
|
|
|||
|
|
@ -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<u8> = Lazy::new(||{
|
|||
});
|
||||
|
||||
pub struct Router {
|
||||
endpoints: RwLock<[Option<Arc<SocketImpl>>; 16]>,
|
||||
endpoints: RwLock<[Option<Arc<SocketData>>; 16]>,
|
||||
running: AtomicBool,
|
||||
socket: Arc<UdpSocket>,
|
||||
//pub auth_module: Arc<dyn AuthModule>
|
||||
|
|
@ -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<Self>, socket: Arc<UdpSocket>, addr: SocketAddrV4, udp_message: Vec<u8>){
|
||||
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<Arc<Self>>{
|
||||
pub async fn new(addr: SocketAddrV4) -> io::Result<(Arc<Self>, 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<UdpSocket>{
|
||||
|
|
@ -149,7 +149,7 @@ impl Router {
|
|||
}
|
||||
|
||||
// returns Some(()) i
|
||||
pub(crate) async fn add_socket(&self, socket: Arc<SocketImpl>) -> Result<(), Error>{
|
||||
pub(crate) async fn add_socket(&self, socket: Arc<SocketData>) -> Result<(), Error>{
|
||||
let mut endpoints = self.endpoints.write().await;
|
||||
|
||||
let idx = socket.get_virual_port().get_port_number() as usize;
|
||||
|
|
|
|||
|
|
@ -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<SocketImpl>, Arc<Router>, Receiver<Connection>);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SocketImpl {
|
||||
virtual_port: VirtualPort,
|
||||
socket: Arc<UdpSocket>,
|
||||
access_key: &'static str,
|
||||
connections: RwLock<HashMap<PRUDPSockAddr, Arc<Mutex<Connection>>>>,
|
||||
connection_creation_sender: Sender<Connection>,
|
||||
pub struct Socket {
|
||||
socket_data: Arc<SocketData>,
|
||||
router: Arc<Router>,
|
||||
}
|
||||
|
||||
|
||||
type OnConnectHandlerFn = Box<dyn Fn(PRUDPPacket) -> Pin<Box<dyn Future<Output=(bool, (Box<dyn StreamCipher + Send + Sync>, Box<dyn StreamCipher + Send + Sync>))> + Send + Sync>> + Send + Sync>;
|
||||
type OnDataHandlerFn = Box<dyn for<'a> Fn(PRUDPPacket, Arc<SocketData>, &'a mut MutexGuard<'_, ConnectionData>) -> Pin<Box<dyn Future<Output=()> + '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<PRUDPPacket>,
|
||||
pub struct SocketData {
|
||||
virtual_port: VirtualPort,
|
||||
pub socket: Arc<UdpSocket>,
|
||||
pub access_key: &'static str,
|
||||
connections: RwLock<HashMap<PRUDPSockAddr, Arc<Mutex<ConnectionData>>>>,
|
||||
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<PRUDPPacket>,
|
||||
pub connection_data_channel: Sender<Vec<u8>>,
|
||||
pub server_encryption: Box<dyn StreamCipher + Send + Sync>,
|
||||
pub client_decryption: Box<dyn StreamCipher + Send + Sync>,
|
||||
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<ActiveConnectionData>,
|
||||
}
|
||||
|
||||
|
||||
impl Socket {
|
||||
pub async fn new(router: Arc<Router>, port: VirtualPort, access_key: &'static str) -> Result<Self, Error> {
|
||||
pub async fn new(
|
||||
router: Arc<Router>,
|
||||
port: VirtualPort,
|
||||
access_key: &'static str,
|
||||
on_connection_handler: OnConnectHandlerFn,
|
||||
on_data_handler: OnDataHandlerFn,
|
||||
) -> Result<Self, Error> {
|
||||
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<Connection> {
|
||||
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<Connection>, 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<Self>, 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;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
61
src/rmc/message.rs
Normal file
61
src/rmc/message.rs
Normal file
|
|
@ -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<u8>
|
||||
}
|
||||
|
||||
impl RMCMessage{
|
||||
pub fn new(stream: &mut (impl Seek + Read)) -> io::Result<Self>{
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
6
src/rmc/mod.rs
Normal file
6
src/rmc/mod.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
pub mod message;
|
||||
pub mod structures;
|
||||
pub mod response;
|
||||
|
||||
|
||||
|
||||
432
src/rmc/response.rs
Normal file
432
src/rmc/response.rs
Normal file
|
|
@ -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<u8>,
|
||||
},
|
||||
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<u8>{
|
||||
generate_response(self.protocol_id, self.response_result).expect("failed to generate response")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Result<Vec<u8>>{
|
||||
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<u32> 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<U5> =
|
||||
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<U5> =
|
||||
Rc4::new_from_slice("FUCKE".as_bytes().into()).expect("invalid key");
|
||||
|
||||
rc4.apply_keystream(&mut data);
|
||||
|
||||
assert_eq!(data_orig, data);
|
||||
|
||||
}
|
||||
}
|
||||
28
src/rmc/structures/any.rs
Normal file
28
src/rmc/structures/any.rs
Normal file
|
|
@ -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<u8>
|
||||
}
|
||||
|
||||
pub fn read(reader: &mut (impl Read + Seek)) -> Result<Any>{
|
||||
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
|
||||
}
|
||||
)
|
||||
}
|
||||
19
src/rmc/structures/mod.rs
Normal file
19
src/rmc/structures/mod.rs
Normal file
|
|
@ -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<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub mod string;
|
||||
pub mod any;
|
||||
18
src/rmc/structures/string.rs
Normal file
18
src/rmc/structures/string.rs
Normal file
|
|
@ -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<String>{
|
||||
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)?)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue