feat(auth): finish protocol 10 method 2 and 3

This commit is contained in:
DJMrTV 2025-02-02 20:25:22 +01:00
commit d18ba43aed
24 changed files with 490 additions and 43 deletions

7
Cargo.lock generated
View file

@ -549,6 +549,12 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]] [[package]]
name = "hmac" name = "hmac"
version = "0.12.1" version = "0.12.1"
@ -1304,6 +1310,7 @@ dependencies = [
"bytemuck", "bytemuck",
"chrono", "chrono",
"dotenv", "dotenv",
"hex",
"hmac", "hmac",
"log", "log",
"md-5", "md-5",

View file

@ -22,6 +22,7 @@ tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread", "net", "s
tokio-stream = { version = "0.1.17", features = ["io-util"] } tokio-stream = { version = "0.1.17", features = ["io-util"] }
tonic = "0.12.3" tonic = "0.12.3"
prost = "0.13.4" prost = "0.13.4"
hex = "0.4.3"
[build-dependencies] [build-dependencies]
tonic-build = "0.12.3" tonic-build = "0.12.3"

View file

@ -1,14 +1,13 @@
use std::{env, result}; use std::{env, result};
use std::net::{Ipv4Addr, SocketAddrV4}; use std::array::TryFromSliceError;
use std::net::{Ipv4Addr};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use thiserror::Error; use thiserror::Error;
use tonic::codegen::http::uri::InvalidUri;
use tonic::metadata::{Ascii, MetadataValue}; use tonic::metadata::{Ascii, MetadataValue};
use tonic::{Request, Status, transport}; use tonic::{Request, transport};
use tonic::codegen::InterceptedService; use tonic::codegen::InterceptedService;
use tonic::service::Interceptor;
use tonic::transport::Channel; use tonic::transport::Channel;
use crate::grpc::{InterceptorFunc, protobufs}; use crate::grpc::InterceptorFunc;
use crate::grpc::protobufs::account::account_client::AccountClient; use crate::grpc::protobufs::account::account_client::AccountClient;
use crate::grpc::protobufs::account::GetNexPasswordRequest; use crate::grpc::protobufs::account::GetNexPasswordRequest;
@ -42,7 +41,9 @@ pub enum Error{
#[error(transparent)] #[error(transparent)]
Transport(#[from] transport::Error), Transport(#[from] transport::Error),
#[error(transparent)] #[error(transparent)]
Status(#[from] tonic::Status) Status(#[from] tonic::Status),
#[error("invalid password size: {0}")]
PasswordConversion(#[from] TryFromSliceError)
} }
pub type Result<T> = result::Result<T, Error>; pub type Result<T> = result::Result<T, Error>;
@ -64,14 +65,14 @@ impl Client{
Ok(Self(client)) Ok(Self(client))
} }
pub async fn get_nex_password(&mut self , pid: u32) -> Result<Box<str>>{ pub async fn get_nex_password(&mut self , pid: u32) -> Result<[u8; 16]>{
let req = Request::new(GetNexPasswordRequest{ let req = Request::new(GetNexPasswordRequest{
pid pid
}); });
let response = self.0.get_nex_password(req).await?.into_inner(); let response = self.0.get_nex_password(req).await?.into_inner();
Ok(response.password.into_boxed_str()) Ok(response.password.as_bytes().try_into()?)
} }
} }

View file

@ -1,6 +1,3 @@
use std::env;
use std::net::Ipv4Addr;
use once_cell::sync::Lazy;
use tonic::{Request, Status}; use tonic::{Request, Status};
type InterceptorFunc = Box<(dyn Fn(Request<()>) -> Result<Request<()>, Status> + Send)>; type InterceptorFunc = Box<(dyn Fn(Request<()>) -> Result<Request<()>, Status> + Send)>;

111
src/kerberos/mod.rs Normal file
View file

@ -0,0 +1,111 @@
use std::io::{Read, Write};
use bytemuck::{bytes_of, Pod, Zeroable};
use chrono::{Datelike, Timelike};
use hmac::Hmac;
use md5::{Digest, Md5};
use rc4::{Rc4, Rc4Core, StreamCipher};
use rc4::cipher::StreamCipherCoreWrapper;
use rc4::consts::U16;
use hmac::Mac;
use rc4::KeyInit;
use crate::rmc::structures::RmcSerialize;
type Md5Hmac = Hmac<md5::Md5>;
pub fn derive_key(pid: u32, password: [u8; 16]) -> [u8; 16]{
let iteration_count = 65000 + pid%1024;
let mut key = password;
for _ in 0..iteration_count {
let mut md5 = Md5::new();
md5.update(key);
key = md5.finalize().try_into().unwrap();
}
key
}
#[derive(Pod, Zeroable, Copy, Clone)]
#[repr(transparent)]
pub struct KerberosDateTime(u64);
impl KerberosDateTime{
pub fn new(second: u64, minute: u64, hour: u64, day: u64, month: u64, year:u64 ) -> Self {
Self(second | (minute << 6) | (hour << 12) | (day << 17) | (month << 22) | (year << 26))
}
pub fn now() -> Self{
let now = chrono::Utc::now();
Self::new(
now.second() as u64,
now.minute() as u64,
now.hour() as u64,
now.day() as u64,
now.month() as u64,
now.year() as u64,
)
}
}
#[derive(Pod, Zeroable, Copy, Clone)]
#[repr(C, packed)]
pub struct TicketInternalData{
issued_time: KerberosDateTime,
pub pid: u32,
pub session_key: [u8; 32],
}
impl TicketInternalData{
pub(crate) fn new(pid: u32) -> Self{
Self{
issued_time: KerberosDateTime::now(),
pid,
session_key: rand::random()
}
}
pub(crate) fn encrypt(&self, key: [u8; 16]) -> Box<[u8]>{
let mut data = bytes_of(self).to_vec();
let mut rc4: StreamCipherCoreWrapper<Rc4Core<U16>> = Rc4::new_from_slice(&key).unwrap();
rc4.apply_keystream(&mut data);
let mut hmac = <Md5Hmac as KeyInit>::new_from_slice(&key).unwrap();
hmac.write_all(&data[..]).expect("failed to write data to hmac");
let hmac_result = &hmac.finalize().into_bytes()[..];
data.write_all(&hmac_result).expect("failed to write data to vec");
data.into_boxed_slice()
}
}
#[derive(Pod, Zeroable, Copy, Clone)]
#[repr(C, packed)]
pub struct Ticket{
pub session_key: [u8; 32],
pub pid: u32,
}
impl Ticket{
pub(crate) fn encrypt(&self, key: [u8; 16], internal_data: &[u8]) -> Box<[u8]>{
let mut data = bytes_of(self).to_vec();
internal_data.serialize(&mut data).expect("unable to write to vec");
let mut rc4: StreamCipherCoreWrapper<Rc4Core<U16>> = Rc4::new_from_slice(&key).unwrap();
rc4.apply_keystream(&mut data);
let mut hmac = <Md5Hmac as KeyInit>::new_from_slice(&key).unwrap();
hmac.write_all(&data[..]).expect("failed to write data to hmac");
let hmac_result = &hmac.finalize().into_bytes()[..];
data.write_all(&hmac_result).expect("failed to write data to vec");
data.into_boxed_slice()
}
}

View file

@ -11,6 +11,7 @@ use rc4::consts::U5;
use simplelog::{ColorChoice, CombinedLogger, Config, LevelFilter, TerminalMode, TermLogger, WriteLogger}; use simplelog::{ColorChoice, CombinedLogger, Config, LevelFilter, TerminalMode, TermLogger, WriteLogger};
use crate::nex::account::Account; use crate::nex::account::Account;
use crate::protocols::auth; use crate::protocols::auth;
use crate::protocols::auth::AuthProtocolConfig;
use crate::protocols::server::RMCProtocolServer; use crate::protocols::server::RMCProtocolServer;
use crate::prudp::socket::Socket; use crate::prudp::socket::Socket;
use crate::prudp::packet::{PRUDPPacket, VirtualPort}; use crate::prudp::packet::{PRUDPPacket, VirtualPort};
@ -24,9 +25,10 @@ mod protocols;
mod nex; mod nex;
mod grpc; mod grpc;
mod kerberos;
static KERBEROS_SERVER_PASSWORD: Lazy<String> = Lazy::new(||{ static KERBEROS_SERVER_PASSWORD: Lazy<String> = Lazy::new(||{
env::var("AUTH_SERVER_PORT") env::var("AUTH_SERVER_PASSWORD")
.ok() .ok()
.unwrap_or("password".to_owned()) .unwrap_or("password".to_owned())
}); });
@ -80,8 +82,15 @@ async fn start_servers(){
info!("setting up endpoints"); info!("setting up endpoints");
// dont assign it to the name _ as that will make it drop right here and now // dont assign it to the name _ as that will make it drop right here and now
let auth_protocol_config = AuthProtocolConfig{
secure_server_account: &SECURE_SERVER_ACCOUNT,
build_name: "branch:origin/project/wup-agmj build:3_8_15_2004_0",
station_url: "prudps:/PID=2;sid=1;stream=10;type=2;address=31.220.75.208;port=10001;CID=1"
};
let rmcserver = RMCProtocolServer::new(Box::new([ let rmcserver = RMCProtocolServer::new(Box::new([
Box::new(auth::bound_protocol(&SECURE_SERVER_ACCOUNT)) Box::new(auth::bound_protocol(auth_protocol_config))
])); ]));
let mut _socket = let mut _socket =

View file

@ -1,18 +1,38 @@
pub struct Account{ pub struct Account{
pid: u32, pub pid: u32,
username: Box<str>, pub username: Box<str>,
kerbros_password: Box<str>, pub kerbros_password: [u8; 16],
} }
impl Account{ impl Account{
pub fn new(pid: u32, username: &str, passwd: &str) -> Self{ pub fn new(pid: u32, username: &str, passwd: &str) -> Self{
let passwd_data = passwd.as_bytes();
let mut passwd = [0u8; 16];
for (idx, byte) in passwd_data.iter().enumerate(){
passwd[idx] = *byte;
}
Self{ Self{
kerbros_password: passwd.into(), kerbros_password: passwd,
username: username.into(), username: username.into(),
pid pid
} }
} }
pub fn new_raw_password(pid: u32, username: &str, passwd: [u8; 16]) -> Self{
Self{
kerbros_password: passwd,
username: username.into(),
pid
}
}
pub fn get_login_data(&self) -> (u32, [u8; 16]){
(self.pid, self.kerbros_password)
}
} }

View file

@ -1,17 +1,18 @@
use std::io::Cursor; use std::io::Cursor;
use log::error; use log::error;
use crate::nex::account::Account; use crate::nex::account::Account;
use crate::protocols::auth::method_login_ex::login_ex; use crate::protocols::auth::AuthProtocolConfig;
use crate::rmc::message::RMCMessage; use crate::rmc::message::RMCMessage;
use crate::rmc::response::{ErrorCode, RMCResponseResult}; use crate::rmc::response::{ErrorCode, RMCResponseResult};
use crate::rmc::structures::any::Any;
use crate::rmc::structures::RmcSerialize; use crate::rmc::structures::RmcSerialize;
pub async fn login(rmcmessage: &RMCMessage, name: &str) -> RMCResponseResult{ pub async fn login(rmcmessage: &RMCMessage, _name: &str) -> RMCResponseResult{
rmcmessage.error_result_with_code(ErrorCode::Core_NotImplemented) rmcmessage.error_result_with_code(ErrorCode::Core_NotImplemented)
} }
pub async fn login_raw_params(rmcmessage: &RMCMessage, data: (&Account)) -> RMCResponseResult{ pub async fn login_raw_params(rmcmessage: &RMCMessage, data: AuthProtocolConfig) -> RMCResponseResult{
let mut reader = Cursor::new(&rmcmessage.rest_of_data); let mut reader = Cursor::new(&rmcmessage.rest_of_data);
let Ok(str) = String::deserialize(&mut reader) else { let Ok(str) = String::deserialize(&mut reader) else {

View file

@ -1,13 +1,20 @@
use std::io::Cursor; use std::io::{Cursor, Write};
use log::{error, info}; use bytemuck::bytes_of;
use hex::encode;
use log::{error};
use crate::grpc::account; use crate::grpc::account;
use crate::kerberos::KerberosDateTime;
use crate::nex::account::Account; use crate::nex::account::Account;
use crate::protocols::auth::AuthProtocolConfig;
use crate::protocols::auth::ticket_generation::generate_ticket;
use crate::rmc::message::RMCMessage; use crate::rmc::message::RMCMessage;
use crate::rmc::response::{ErrorCode, RMCResponseResult}; use crate::rmc::response::{ErrorCode, RMCResponseResult};
use crate::rmc::structures::{RmcSerialize}; use crate::rmc::structures::{RmcSerialize};
use crate::rmc::structures::any::Any; use crate::rmc::structures::any::Any;
use crate::rmc::structures::connection_data::ConnectionData;
use crate::rmc::structures::qresult::QResult;
pub async fn login_ex(rmcmessage: &RMCMessage, secure_server_account: &Account , pid: u32) -> RMCResponseResult{ pub async fn login_ex(rmcmessage: &RMCMessage, proto_data: AuthProtocolConfig, pid: u32) -> RMCResponseResult{
// todo: figure out how the AuthenticationInfo struct works, parse it and validate login info // todo: figure out how the AuthenticationInfo struct works, parse it and validate login info
let Ok(mut client) = account::Client::new().await else { let Ok(mut client) = account::Client::new().await else {
@ -18,15 +25,35 @@ pub async fn login_ex(rmcmessage: &RMCMessage, secure_server_account: &Account ,
return rmcmessage.error_result_with_code(ErrorCode::Core_Exception); return rmcmessage.error_result_with_code(ErrorCode::Core_Exception);
}; };
let source_login_data = (pid, passwd);
let destination_login_data = proto_data.secure_server_account.get_login_data();
return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument); let ticket = generate_ticket(source_login_data, destination_login_data);
let result = QResult::success(ErrorCode::Core_Unknown);
let connection_data = ConnectionData{
station_url: proto_data.station_url,
special_station_url: "",
date_time: KerberosDateTime::now(),
special_protocols: Vec::new()
};
let mut response: Vec<u8> = Vec::new();
result.serialize(&mut response).expect("failed serializing result");
response.write_all(bytes_of(&source_login_data.0)).expect("failed writing pid");
ticket.serialize(&mut response).expect("failed serializing ticket");
connection_data.serialize(&mut response).expect("failed writing connection data");
proto_data.build_name.serialize(&mut response).expect("failed writing build name");
return rmcmessage.success_with_data(response);
} }
pub async fn login_ex_raw_params(rmcmessage: &RMCMessage, (secure_server_account): (&Account)) -> RMCResponseResult{ pub async fn login_ex_raw_params(rmcmessage: &RMCMessage, data: AuthProtocolConfig) -> RMCResponseResult{
let mut reader = Cursor::new(&rmcmessage.rest_of_data); let mut reader = Cursor::new(&rmcmessage.rest_of_data);
let Ok(_str) = String::deserialize(&mut reader) else { let Ok(str) = String::deserialize(&mut reader) else {
error!("error reading packet"); error!("error reading packet");
return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument); return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument);
}; };
@ -50,5 +77,5 @@ pub async fn login_ex_raw_params(rmcmessage: &RMCMessage, (secure_server_account
return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument); return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument);
}; };
login_ex(rmcmessage, secure_server_account, pid).await login_ex(rmcmessage, data, pid).await
} }

View file

@ -0,0 +1,53 @@
use std::io::Cursor;
use log::error;
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
use crate::grpc::account;
use crate::protocols::auth::{AuthProtocolConfig, get_login_data_by_pid};
use crate::protocols::auth::method_login_ex::login_ex;
use crate::protocols::auth::ticket_generation::generate_ticket;
use crate::rmc::message::RMCMessage;
use crate::rmc::response::{ErrorCode, RMCResponseResult};
use crate::rmc::response::ErrorCode::Core_Unknown;
use crate::rmc::structures::any::Any;
use crate::rmc::structures::qresult::QResult;
use crate::rmc::structures::RmcSerialize;
pub async fn request_ticket(rmcmessage: &RMCMessage, data: AuthProtocolConfig, source_pid: u32, destination_pid: u32) -> RMCResponseResult{
let Some(source_login_data) = get_login_data_by_pid(source_pid).await else {
return rmcmessage.error_result_with_code(ErrorCode::Core_Exception);
};
let desgination_login_data = if destination_pid == data.secure_server_account.pid{
data.secure_server_account.get_login_data()
} else {
let Some(login) = get_login_data_by_pid(destination_pid).await else {
return rmcmessage.error_result_with_code(ErrorCode::Core_Exception);
};
login
};
let result = QResult::success(Core_Unknown);
let ticket = generate_ticket(source_login_data, desgination_login_data);
let mut response: Vec<u8> = Vec::new();
result.serialize(&mut response).expect("failed serializing result");
ticket.serialize(&mut response).expect("failed serializing ticket");
rmcmessage.success_with_data(response)
}
pub async fn request_ticket_raw_params(rmcmessage: &RMCMessage, data: AuthProtocolConfig) -> RMCResponseResult{
let mut reader = Cursor::new(&rmcmessage.rest_of_data);
let Ok(source_pid) = reader.read_struct(IS_BIG_ENDIAN) else {
return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument);
};
let Ok(destination_pid) = reader.read_struct(IS_BIG_ENDIAN) else {
return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument);
};
request_ticket(rmcmessage, data, source_pid, destination_pid).await
}

View file

@ -1,18 +1,42 @@
mod method_login_ex; mod method_login_ex;
mod method_login; mod method_login;
mod ticket_generation;
mod method_request_ticket;
use std::sync::Arc;
use log::{error}; use log::{error};
use crate::define_protocol; use crate::define_protocol;
use crate::grpc::account;
use crate::nex::account::Account; use crate::nex::account::Account;
use crate::protocols::auth::method_login::login_raw_params; use crate::protocols::auth::method_login::login_raw_params;
use crate::protocols::auth::method_login_ex::{login_ex, login_ex_raw_params}; use crate::protocols::auth::method_login_ex::login_ex_raw_params;
use crate::protocols::auth::method_request_ticket::request_ticket_raw_params;
use crate::rmc::message::RMCMessage; use crate::rmc::message::RMCMessage;
use crate::rmc::response::{ErrorCode, RMCResponse}; use crate::rmc::response::{ErrorCode, RMCResponse};
#[derive(Copy, Clone)]
pub struct AuthProtocolConfig {
pub secure_server_account: &'static Account,
pub build_name: &'static str,
pub station_url: &'static str
}
define_protocol!{ define_protocol!{
10(secure_server_account: &'static Account) => { 10(proto_data: AuthProtocolConfig) => {
0x01 => login_raw_params, 0x01 => login_raw_params,
0x02 => login_ex_raw_params 0x02 => login_ex_raw_params,
0x03 => request_ticket_raw_params
} }
} }
async fn get_login_data_by_pid(pid: u32) -> Option<(u32, [u8; 16])> {
let Ok(mut client) = account::Client::new().await else {
return None
};
let Ok(passwd) = client.get_nex_password(pid).await else{
return None
};
Some((pid, passwd))
}

View file

@ -0,0 +1,19 @@
use crate::kerberos;
use crate::kerberos::{derive_key, Ticket};
pub fn generate_ticket(source_act_login_data: (u32, [u8;16]), dest_act_login_data: (u32, [u8;16])) -> Box<[u8]>{
let source_key = derive_key(source_act_login_data.0, source_act_login_data.1);
let dest_key = derive_key(dest_act_login_data.0, dest_act_login_data.1);
let internal_data = kerberos::TicketInternalData::new(source_act_login_data.0);
let encrypted_inner = internal_data.encrypt(dest_key);
let encrypted_session_ticket = Ticket{
pid: dest_act_login_data.0,
session_key: internal_data.session_key,
}.encrypt(source_key, &encrypted_inner);
encrypted_session_ticket
}

View file

@ -3,6 +3,7 @@ pub mod server;
#[macro_export] #[macro_export]
macro_rules! define_protocol { macro_rules! define_protocol {
($id:literal ($($varname:ident : $ty:ty),*) => {$($func_id:literal => $func:path),*} ) => { ($id:literal ($($varname:ident : $ty:ty),*) => {$($func_id:literal => $func:path),*} ) => {
#[allow(unused_parens)]
async fn protocol (rmcmessage: &RMCMessage, $($varname : $ty),*) -> Option<RMCResponse>{ async fn protocol (rmcmessage: &RMCMessage, $($varname : $ty),*) -> Option<RMCResponse>{
if rmcmessage.protocol_id != $id{ if rmcmessage.protocol_id != $id{
return None; return None;
@ -30,7 +31,7 @@ macro_rules! define_protocol {
response_result response_result
}) })
} }
#[allow(unused_parens)]
pub fn bound_protocol($($varname : $ty,)*) -> Box<dyn for<'message_lifetime> Fn(&'message_lifetime RMCMessage) -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = Option<RMCResponse>> + Send + 'message_lifetime>> + Send + Sync>{ pub fn bound_protocol($($varname : $ty,)*) -> Box<dyn for<'message_lifetime> Fn(&'message_lifetime RMCMessage) -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = Option<RMCResponse>> + Send + 'message_lifetime>> + Send + Sync>{
Box::new( Box::new(
move |v| { move |v| {

View file

@ -12,7 +12,7 @@ use hmac::{Hmac, Mac};
use log::{error, trace, warn}; use log::{error, trace, warn};
use md5::{Md5, Digest}; use md5::{Md5, Digest};
use thiserror::Error; use thiserror::Error;
use v_byte_macros::{EnumTryInto, SwapEndian}; use v_byte_macros::{SwapEndian};
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
use crate::prudp::packet::flags::ACK; use crate::prudp::packet::flags::ACK;
use crate::prudp::packet::PacketOption::{ConnectionSignature, FragmentId, InitialSequenceId, MaximumSubstreamId, SupportedFunctions}; use crate::prudp::packet::PacketOption::{ConnectionSignature, FragmentId, InitialSequenceId, MaximumSubstreamId, SupportedFunctions};
@ -173,13 +173,6 @@ impl Default for PRUDPHeader{
} }
#[derive(EnumTryInto)]
#[repr(u16)]
enum PacketSpecificData {
E = 0x10
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum PacketOption{ pub enum PacketOption{
SupportedFunctions(u32), SupportedFunctions(u32),

View file

@ -42,7 +42,6 @@ pub struct ActiveConnectionData {
pub reliable_client_counter: u16, pub reliable_client_counter: u16,
pub reliable_server_counter: u16, pub reliable_server_counter: u16,
pub reliable_client_queue: VecDeque<PRUDPPacket>, pub reliable_client_queue: VecDeque<PRUDPPacket>,
pub connection_data_channel: Sender<Vec<u8>>,
server_encryption: Box<dyn StreamCipher + Send>, server_encryption: Box<dyn StreamCipher + Send>,
client_decryption: Box<dyn StreamCipher + Send>, client_decryption: Box<dyn StreamCipher + Send>,
pub server_session_id: u8, pub server_session_id: u8,
@ -363,7 +362,7 @@ impl SocketData {
self.socket.send_to(&vec, client_address.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");
} }
} }
3 => {}
_ => unimplemented!("unimplemented packet type: {}", packet.header.types_and_flags.get_types()) _ => unimplemented!("unimplemented packet type: {}", packet.header.types_and_flags.get_types())
} }
} }

View file

@ -58,4 +58,12 @@ impl RMCMessage{
error_code error_code
} }
} }
pub fn success_with_data(&self, data: Vec<u8>) -> RMCResponseResult{
RMCResponseResult::Success {
call_id: self.call_id,
method_id: self.method_id,
data
}
}
} }

View file

@ -8,6 +8,8 @@ use crate::prudp::packet::PacketOption::FragmentId;
use crate::prudp::packet::types::DATA; use crate::prudp::packet::types::DATA;
use crate::prudp::socket::{ConnectionData, SocketData}; use crate::prudp::socket::{ConnectionData, SocketData};
pub enum RMCResponseResult { pub enum RMCResponseResult {
Success{ Success{
call_id: u32, call_id: u32,
@ -389,6 +391,7 @@ mod test{
use hmac::digest::consts::U5; use hmac::digest::consts::U5;
use hmac::digest::KeyInit; use hmac::digest::KeyInit;
use rc4::{Rc4, StreamCipher}; use rc4::{Rc4, StreamCipher};
use crate::rmc::response::ErrorCode;
#[test] #[test]
fn test(){ fn test(){
@ -410,4 +413,10 @@ mod test{
assert_eq!(data_orig, data); assert_eq!(data_orig, data);
} }
#[test]
fn test_enum_equivilance(){
let val: u32 = ErrorCode::Core_Unknown.into();
assert_eq!(val, 0x00010001)
}
} }

View file

@ -0,0 +1,44 @@
use std::io::{Read, Write};
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
use crate::rmc::structures::RmcSerialize;
impl<'a> RmcSerialize for &'a [u8]{
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
let u32_size = self.len() as u32;
writer.write(bytemuck::bytes_of(&u32_size))?;
writer.write(self)?;
Ok(())
}
/// DO NOT USE (also maybe split off the serialize and deserialize functions at some point)
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
panic!("cannot deserialize to a u8 slice reference (use this ONLY for writing)")
}
}
impl<'a> RmcSerialize for Vec<u8>{
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
(&self[..]).serialize(writer)
}
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
let len: u32 = reader.read_struct(IS_BIG_ENDIAN)?;
let mut data = vec![0; len as usize];
reader.read_exact(&mut data)?;
Ok(data)
}
}
impl<'a> RmcSerialize for Box<[u8]>{
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
(&self[..]).serialize(writer)
}
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
Vec::deserialize(reader).map(|v| v.into_boxed_slice())
}
}

View file

@ -0,0 +1,27 @@
use std::io::{Read, Write};
use bytemuck::bytes_of;
use crate::kerberos::KerberosDateTime;
use crate::rmc::structures::{rmc_struct, RmcSerialize};
pub struct ConnectionData<'a>{
pub station_url: &'a str,
pub special_protocols: Vec<u8>,
pub special_station_url: &'a str,
pub date_time: KerberosDateTime
}
impl<'a> RmcSerialize for ConnectionData<'a>{
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
rmc_struct::write_struct(writer, 1, |mut v|{
self.station_url.serialize(v).expect("unable to write station url");
self.special_protocols.serialize(v).expect("unable to write special protocols");
self.special_station_url.serialize(v).expect("unable to write special station url");
v.write_all(bytes_of(&self.date_time)).expect("unable to write date time");
})
}
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
todo!()
}
}

View file

@ -0,0 +1,21 @@
use std::io::{Read, Write};
use bytemuck::bytes_of;
use crate::rmc::structures::RmcSerialize;
impl<T: RmcSerialize> RmcSerialize for Vec<T>{
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
let u32_len = self.len();
writer.write_all(bytes_of(&u32_len))?;
for e in self{
e.serialize(writer)?;
}
Ok(())
}
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
todo!()
}
}

View file

@ -17,6 +17,11 @@ type Result<T> = std::result::Result<T, Error>;
pub mod string; pub mod string;
pub mod any; pub mod any;
pub mod qresult;
pub mod buffer;
pub mod connection_data;
pub mod rmc_struct;
pub mod list;
pub trait RmcSerialize: Sized{ pub trait RmcSerialize: Sized{
fn serialize(&self, writer: &mut dyn Write) -> Result<()>; fn serialize(&self, writer: &mut dyn Write) -> Result<()>;

View file

@ -0,0 +1,37 @@
use std::io::{Read, Write};
use bytemuck::{bytes_of, Pod, Zeroable};
use v_byte_macros::SwapEndian;
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
use crate::rmc::response::ErrorCode;
use crate::rmc::structures::{RmcSerialize, Result};
const ERROR_MASK: u32 = 1 << 31;
#[derive(Pod, Zeroable, Copy, Clone, SwapEndian)]
#[repr(transparent)]
pub struct QResult(u32);
impl QResult{
pub fn success(error_code: ErrorCode) -> Self{
let val: u32 = error_code.into();
Self(val & (!ERROR_MASK))
}
pub fn error(error_code: ErrorCode) -> Self{
let val: u32 = error_code.into();
Self(val | ERROR_MASK)
}
}
impl RmcSerialize for QResult{
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
writer.write(bytes_of(self))?;
Ok(())
}
fn deserialize(mut reader: &mut dyn Read) -> Result<Self> {
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
}
}

View file

@ -0,0 +1,24 @@
use std::io::Write;
use bytemuck::bytes_of;
use crate::rmc::structures::Result;
#[repr(C, packed)]
struct StructureHeader{
version: u8,
length: u32
}
pub fn write_struct(mut writer: &mut dyn Write, version: u8, pred: impl Fn(&mut Vec<u8>)) -> Result<()> {
writer.write_all(&[version])?;
let mut scratch_space: Vec<u8> = Vec::new();
(pred)(&mut scratch_space);
let u32_size= scratch_space.len() as u32;
writer.write_all(bytes_of(&u32_size))?;
writer.write_all(&scratch_space)?;
Ok(())
}

View file

@ -17,6 +17,15 @@ impl RmcSerialize for String{
Ok(String::from_utf8(data)?) Ok(String::from_utf8(data)?)
} }
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
(&self[..]).serialize(writer)
}
}
impl RmcSerialize for &str{
fn deserialize(mut reader: &mut dyn Read) -> Result<Self> {
panic!("cannot serialize to &str")
}
fn serialize(&self, writer: &mut dyn Write) -> Result<()> { fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
let u16_len: u16 = self.len() as u16; let u16_len: u16 = self.len() as u16;
writer.write(bytes_of(&u16_len))?; writer.write(bytes_of(&u16_len))?;