diff --git a/src/kerberos/mod.rs b/src/kerberos/mod.rs index cf5cec3..83fb89f 100644 --- a/src/kerberos/mod.rs +++ b/src/kerberos/mod.rs @@ -1,6 +1,6 @@ use std::io::{Read, Write}; use bytemuck::{bytes_of, Pod, Zeroable}; -use chrono::{Datelike, Timelike}; +use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc}; use hmac::Hmac; use md5::{Digest, Md5}; use rc4::{Rc4, Rc4Core, StreamCipher}; @@ -45,12 +45,47 @@ impl KerberosDateTime{ now.year() as u64, ) } + + #[inline] + pub fn get_seconds(&self) -> u8{ + (self.0 & 0b111111) as u8 + } + + #[inline] + pub fn get_minutes(&self) -> u8{ + ((self.0 >> 6) & 0b111111) as u8 + } + #[inline] + pub fn get_hours(&self) -> u8{ + ((self.0 >> 12) & 0b111111) as u8 + } + #[inline] + pub fn get_days(&self) -> u8{ + ((self.0 >> 17) & 0b111111) as u8 + } + + #[inline] + pub fn get_month(&self) -> u8{ + ((self.0 >> 22) & 0b111111) as u8 + } + + #[inline] + pub fn get_year(&self) -> u64{ + ((self.0 >> 26) & 0xFFFFFFFF) + } + + pub fn to_regular_time(&self) -> chrono::DateTime{ + NaiveDateTime::new( + NaiveDate::from_ymd_opt(self.get_year() as i32, self.get_month() as u32, self.get_days() as u32).unwrap(), + NaiveTime::from_hms_opt(self.get_hours() as u32, self.get_minutes() as u32, self.get_seconds() as u32).unwrap() + ).and_utc() + } } #[derive(Pod, Zeroable, Copy, Clone)] #[repr(C, packed)] pub struct TicketInternalData{ - issued_time: KerberosDateTime, + pub issued_time: KerberosDateTime, pub pid: u32, pub session_key: [u8; 32], } diff --git a/src/main.rs b/src/main.rs index 34645b8..aa0d0b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,10 +15,12 @@ use crate::nex::account::Account; use crate::protocols::auth; use crate::protocols::auth::AuthProtocolConfig; use crate::protocols::server::RMCProtocolServer; -use crate::prudp::socket::{EncryptionPair, Socket}; +use crate::prudp::socket::{ActiveSecureConnectionData, EncryptionPair, Socket}; use crate::prudp::packet::{PRUDPPacket, VirtualPort}; use crate::prudp::router::Router; +use crate::prudp::secure::{generate_secure_encryption_pairs, read_secure_connection_data}; use crate::rmc::message::RMCMessage; +use crate::rmc::structures::RmcSerialize; mod endianness; mod prudp; @@ -164,7 +166,9 @@ async fn start_secure_server() -> SecureServer{ info!("setting up endpoints"); - let rmcserver = RMCProtocolServer::new(Box::new([])); + let rmcserver = RMCProtocolServer::new(Box::new([ + Box::new(protocols::secure::bound_protocol()) + ])); let mut socket = Socket::new( @@ -174,9 +178,24 @@ async fn start_secure_server() -> SecureServer{ Box::new(|p, count|{ Box::pin( async move { + let (session_key, pid, check_value) = read_secure_connection_data(&p.payload, &SECURE_SERVER_ACCOUNT)?; + let check_value_response = check_value + 1; - Some((Vec::new(), Vec::new(), None)) + let data = bytemuck::bytes_of(&check_value_response); + + let mut response = Vec::new(); + + data.serialize(&mut response).ok()?; + + let encryption_pairs = generate_secure_encryption_pairs(session_key, count); + + Some((response, encryption_pairs, Some( + ActiveSecureConnectionData{ + pid, + session_key + } + ))) } ) }), diff --git a/src/protocols/auth/method_login.rs b/src/protocols/auth/method_login.rs index 31959b3..afd578d 100644 --- a/src/protocols/auth/method_login.rs +++ b/src/protocols/auth/method_login.rs @@ -2,6 +2,7 @@ use std::io::Cursor; use log::error; use crate::nex::account::Account; use crate::protocols::auth::AuthProtocolConfig; +use crate::prudp::socket::ConnectionData; use crate::rmc::message::RMCMessage; use crate::rmc::response::{ErrorCode, RMCResponseResult}; use crate::rmc::structures::RmcSerialize; @@ -12,7 +13,7 @@ pub async fn login(rmcmessage: &RMCMessage, _name: &str) -> RMCResponseResult{ rmcmessage.error_result_with_code(ErrorCode::Core_NotImplemented) } -pub async fn login_raw_params(rmcmessage: &RMCMessage, data: AuthProtocolConfig) -> RMCResponseResult{ +pub async fn login_raw_params(rmcmessage: &RMCMessage, _: &mut ConnectionData, data: AuthProtocolConfig) -> RMCResponseResult{ let mut reader = Cursor::new(&rmcmessage.rest_of_data); let Ok(str) = String::deserialize(&mut reader) else { diff --git a/src/protocols/auth/method_login_ex.rs b/src/protocols/auth/method_login_ex.rs index cc80dce..8dfbafa 100644 --- a/src/protocols/auth/method_login_ex.rs +++ b/src/protocols/auth/method_login_ex.rs @@ -7,11 +7,12 @@ use crate::kerberos::KerberosDateTime; use crate::nex::account::Account; use crate::protocols::auth::AuthProtocolConfig; use crate::protocols::auth::ticket_generation::generate_ticket; +use crate::prudp::socket::ConnectionData; +use crate::rmc; use crate::rmc::message::RMCMessage; use crate::rmc::response::{ErrorCode, RMCResponseResult}; use crate::rmc::structures::{RmcSerialize}; 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, proto_data: AuthProtocolConfig, pid: u32) -> RMCResponseResult{ @@ -32,7 +33,7 @@ pub async fn login_ex(rmcmessage: &RMCMessage, proto_data: AuthProtocolConfig, p let result = QResult::success(ErrorCode::Core_Unknown); - let connection_data = ConnectionData{ + let connection_data = rmc::structures::connection_data::ConnectionData{ station_url: proto_data.station_url, special_station_url: "", date_time: KerberosDateTime::now(), @@ -50,7 +51,7 @@ pub async fn login_ex(rmcmessage: &RMCMessage, proto_data: AuthProtocolConfig, p return rmcmessage.success_with_data(response); } -pub async fn login_ex_raw_params(rmcmessage: &RMCMessage, data: AuthProtocolConfig) -> RMCResponseResult{ +pub async fn login_ex_raw_params(rmcmessage: &RMCMessage, _: &mut ConnectionData, data: AuthProtocolConfig) -> RMCResponseResult{ let mut reader = Cursor::new(&rmcmessage.rest_of_data); let Ok(str) = String::deserialize(&mut reader) else { diff --git a/src/protocols/auth/method_request_ticket.rs b/src/protocols/auth/method_request_ticket.rs index 439eaa0..c8113cb 100644 --- a/src/protocols/auth/method_request_ticket.rs +++ b/src/protocols/auth/method_request_ticket.rs @@ -5,6 +5,7 @@ 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::prudp::socket::ConnectionData; use crate::rmc::message::RMCMessage; use crate::rmc::response::{ErrorCode, RMCResponseResult}; use crate::rmc::response::ErrorCode::Core_Unknown; @@ -38,7 +39,7 @@ pub async fn request_ticket(rmcmessage: &RMCMessage, data: AuthProtocolConfig, s rmcmessage.success_with_data(response) } -pub async fn request_ticket_raw_params(rmcmessage: &RMCMessage, data: AuthProtocolConfig) -> RMCResponseResult{ +pub async fn request_ticket_raw_params(rmcmessage: &RMCMessage, _: &mut ConnectionData, data: AuthProtocolConfig) -> RMCResponseResult{ let mut reader = Cursor::new(&rmcmessage.rest_of_data); let Ok(source_pid) = reader.read_struct(IS_BIG_ENDIAN) else { diff --git a/src/protocols/mod.rs b/src/protocols/mod.rs index 53b363d..68a8327 100644 --- a/src/protocols/mod.rs +++ b/src/protocols/mod.rs @@ -1,10 +1,14 @@ +use crate::prudp::socket::ConnectionData; + pub mod auth; pub mod server; +pub mod secure; + #[macro_export] macro_rules! define_protocol { ($id:literal ($($varname:ident : $ty:ty),*) => {$($func_id:literal => $func:path),*} ) => { #[allow(unused_parens)] - async fn protocol (rmcmessage: &RMCMessage, $($varname : $ty),*) -> Option{ + async fn protocol (rmcmessage: &crate::RMCMessage, connection: &mut crate::protocols::ConnectionData, $($varname : $ty),*) -> Option{ if rmcmessage.protocol_id != $id{ return None; } @@ -13,33 +17,34 @@ macro_rules! define_protocol { let response_result = match rmcmessage.method_id{ $( - $func_id => $func ( rmcmessage, self_data).await, + $func_id => $func ( rmcmessage, connection, self_data).await, )* _ => { - error!("invalid method id sent to protocol {}: {:?}", $id, rmcmessage.method_id); + log::error!("invalid method id sent to protocol {}: {:?}", $id, rmcmessage.method_id); return Some( - RMCResponse{ + crate::rmc::response::RMCResponse{ protocol_id: $id, - response_result: rmcmessage.error_result_with_code(ErrorCode::Core_NotImplemented) + response_result: rmcmessage.error_result_with_code(crate::rmc::response::ErrorCode::Core_NotImplemented) } ); } }; - Some(RMCResponse{ + Some(crate::rmc::response::RMCResponse{ protocol_id: $id, response_result }) } #[allow(unused_parens)] - pub fn bound_protocol($($varname : $ty,)*) -> Box Fn(&'message_lifetime RMCMessage) -> ::std::pin::Pin> + Send + 'message_lifetime>> + Send + Sync>{ + pub fn bound_protocol($($varname : $ty,)*) -> Box Fn(&'message_lifetime crate::RMCMessage, &'message_lifetime mut crate::protocols::ConnectionData) + -> ::std::pin::Pin> + Send + 'message_lifetime>> + Send + Sync>{ Box::new( - move |v| { + move |v, cd| { Box::pin(async move { $( let $varname = $varname.clone(); )* - protocol(v, $($varname,)*).await + protocol(v, cd, $($varname,)*).await }) } ) diff --git a/src/protocols/secure/method_register.rs b/src/protocols/secure/method_register.rs new file mode 100644 index 0000000..e50439d --- /dev/null +++ b/src/protocols/secure/method_register.rs @@ -0,0 +1,65 @@ +use std::io::{Cursor, Write}; +use bytemuck::bytes_of; +use log::{error, warn}; +use crate::protocols::auth::AuthProtocolConfig; +use crate::prudp::socket::ConnectionData; +use crate::prudp::station_url::{nat_types, StationUrl}; +use crate::prudp::station_url::Type::PRUDPS; +use crate::prudp::station_url::UrlOptions::{Address, NatFiltering, NatMapping, NatType, Port, PrincipalID, RVConnectionID}; +use crate::rmc::message::RMCMessage; +use crate::rmc::response::{ErrorCode, RMCResponseResult}; +use crate::rmc::structures::any::Any; +use crate::rmc::structures::qresult::QResult; +use crate::rmc::structures::RmcSerialize; + +type StringList = Vec; + +pub async fn register(rmcmessage: &RMCMessage, station_urls: Vec, conn_data: &mut ConnectionData) -> RMCResponseResult{ + + let Some(active_connection_data) = conn_data.active_connection_data.as_ref() else { + return rmcmessage.error_result_with_code(ErrorCode::RendezVous_NotAuthenticated) + }; + + let Some(active_secure_connection_data) = active_connection_data.active_secure_connection_data.as_ref() else { + return rmcmessage.error_result_with_code(ErrorCode::RendezVous_NotAuthenticated) + }; + + let public_station = StationUrl{ + url_type: PRUDPS, + options: vec![ + RVConnectionID(active_connection_data.connection_id), + Address(*conn_data.sock_addr.regular_socket_addr.ip()), + Port(conn_data.sock_addr.regular_socket_addr.port()), + NatFiltering(0), + NatMapping(0), + NatType(nat_types::BEHIND_NAT), + PrincipalID(active_secure_connection_data.pid), + ] + }; + + + + let result = QResult::success(ErrorCode::Core_Unknown); + + let mut response = Vec::new(); + + result.serialize(&mut response).expect("unable to serialize result"); + response.write_all(bytes_of(&active_connection_data.connection_id)).expect("unable to serialize connection id"); + public_station.to_string().serialize(&mut response).expect("unable to serialize station id"); + + rmcmessage.success_with_data(response) +} + +pub async fn register_raw_params(rmcmessage: &RMCMessage, conn_data: &mut ConnectionData, _: ()) -> RMCResponseResult{ + let mut reader = Cursor::new(&rmcmessage.rest_of_data); + + let Ok(station_urls) = StringList::deserialize(&mut reader) else { + return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument); + }; + + let Ok(station_urls): Result, _> = station_urls.iter().map(|c| StationUrl::try_from((&c) as &str)).collect() else { + return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument); + }; + + register(rmcmessage, station_urls, conn_data).await +} \ No newline at end of file diff --git a/src/protocols/secure/mod.rs b/src/protocols/secure/mod.rs new file mode 100644 index 0000000..b35fc44 --- /dev/null +++ b/src/protocols/secure/mod.rs @@ -0,0 +1,10 @@ +mod method_register; + +use crate::define_protocol; +use crate::protocols::secure::method_register::register_raw_params; + +define_protocol!{ + 11() => { + 0x01 => register_raw_params + } +} \ No newline at end of file diff --git a/src/protocols/server.rs b/src/protocols/server.rs index 13d69b0..624739a 100644 --- a/src/protocols/server.rs +++ b/src/protocols/server.rs @@ -9,7 +9,7 @@ use crate::rmc::message::RMCMessage; use crate::rmc::response::{RMCResponse, RMCResponseResult, send_response}; use crate::rmc::response::ErrorCode::Core_NotImplemented; -type ContainedProtocolList = Box<[Box Fn(&'a RMCMessage) -> Pin> + Send + 'a>> + Send + Sync>]>; +type ContainedProtocolList = Box<[Box Fn(&'a RMCMessage, &'a mut ConnectionData) -> Pin> + Send + 'a>> + Send + Sync>]>; pub struct RMCProtocolServer(ContainedProtocolList); @@ -27,12 +27,14 @@ impl RMCProtocolServer{ println!("recieved rmc message: {{ protocol: {}, method: {}}}", rmc.protocol_id, rmc.method_id); for proto in &self.0 { - if let Some(response) = proto(&rmc).await { + if let Some(response) = proto(&rmc, connection).await { send_response(&packet, &socket, connection, response).await; return; } } + error!("tried to send message to unimplemented protocol {} with method id {}", rmc.protocol_id, rmc.method_id); + send_response(&packet, &socket, connection, RMCResponse{ protocol_id: rmc.protocol_id as u8, response_result: RMCResponseResult::Error { diff --git a/src/prudp/mod.rs b/src/prudp/mod.rs index cde32f3..b86e522 100644 --- a/src/prudp/mod.rs +++ b/src/prudp/mod.rs @@ -2,4 +2,6 @@ pub mod packet; pub mod router; pub mod socket; mod auth_module; -mod sockaddr; \ No newline at end of file +mod sockaddr; +pub mod secure; +pub mod station_url; \ No newline at end of file diff --git a/src/prudp/packet.rs b/src/prudp/packet.rs index 4f9b28c..434d476 100644 --- a/src/prudp/packet.rs +++ b/src/prudp/packet.rs @@ -365,6 +365,8 @@ impl PRUDPPacket { .cloned() .collect(); + + Self{ header: PRUDPHeader{ types_and_flags: flags, diff --git a/src/prudp/secure.rs b/src/prudp/secure.rs new file mode 100644 index 0000000..01f2055 --- /dev/null +++ b/src/prudp/secure.rs @@ -0,0 +1,101 @@ +use std::io::Cursor; +use hmac::digest::consts::U32; +use log::error; +use rc4::cipher::StreamCipherCoreWrapper; +use rc4::{KeyInit, Rc4, Rc4Core, StreamCipher}; +use rc4::consts::U16; +use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; +use crate::kerberos::{derive_key, TicketInternalData}; +use crate::nex::account::Account; +use crate::prudp::packet::PRUDPHeader; +use crate::prudp::socket::EncryptionPair; +use crate::rmc::structures::RmcSerialize; + +pub fn read_secure_connection_data(data: &[u8], act: &Account) -> Option<([u8; 32], u32, u32)>{ + let mut cursor = Cursor::new(data); + + let mut ticket_data: Vec = Vec::deserialize(&mut cursor).ok()?; + let mut request_data: Vec = Vec::deserialize(&mut cursor).ok()?; + + let ticket_data_size = ticket_data.len(); + + let ticket_data = &mut ticket_data[0..ticket_data_size-0x10]; + + let server_key = derive_key(act.pid, act.kerbros_password); + + let mut rc4: StreamCipherCoreWrapper> = + Rc4::new_from_slice(&server_key).expect("unable to init rc4 keystream"); + + rc4.apply_keystream(ticket_data); + + let ticket_data: &TicketInternalData = match bytemuck::try_from_bytes(ticket_data){ + Ok(v) => v, + Err(e) => { + error!("unable to read internal ticket data: {}", e); + return None; + } + }; + + // todo: add ticket expiration + + let TicketInternalData{ + session_key, + pid: ticket_source_pid, + issued_time + } = *ticket_data; + + // todo: add checking if tickets are signed with a valid md5-hmac + let request_data_length = request_data.len(); + let request_data = &mut request_data[0.. request_data_length - 0x10]; + + let mut rc4: StreamCipherCoreWrapper> = + Rc4::new_from_slice(&session_key).expect("unable to init rc4 keystream"); + + rc4.apply_keystream(request_data); + + let mut reqest_data_cursor = Cursor::new(request_data); + + let pid: u32 = reqest_data_cursor.read_struct(IS_BIG_ENDIAN).ok()?; + + if pid != ticket_source_pid{ + let ticket_created_on = issued_time.to_regular_time(); + + error!("someone tried to spoof their pid, ticket was created on: {}", ticket_created_on.to_rfc2822()); + return None; + } + + let _cid: u32 = reqest_data_cursor.read_struct(IS_BIG_ENDIAN).ok()?; + let response_check: u32 = reqest_data_cursor.read_struct(IS_BIG_ENDIAN).ok()?; + + + + Some((session_key, pid, response_check)) +} + +type Rc4U32 = StreamCipherCoreWrapper>; + +pub fn generate_secure_encryption_pairs(mut session_key: [u8; 32], count: u8) -> Vec{ + let mut vec = Vec::with_capacity(count as usize); + + vec.push(EncryptionPair{ + send: Box::new(Rc4U32::new_from_slice(&session_key).expect("unable to create rc4")), + recv: Box::new(Rc4U32::new_from_slice(&session_key).expect("unable to create rc4")) + }); + + for _ in 1..=count{ + let modifier = session_key.len() + 1; + + let key_length = session_key.len(); + + for (position, val) in (&mut session_key[0..key_length/2]).iter_mut().enumerate(){ + *val = val.wrapping_add((modifier - position) as u8); + } + + vec.push(EncryptionPair{ + send: Box::new(Rc4U32::new_from_slice(&session_key).expect("unable to create rc4")), + recv: Box::new(Rc4U32::new_from_slice(&session_key).expect("unable to create rc4")) + }); + } + + vec +} \ No newline at end of file diff --git a/src/prudp/socket.rs b/src/prudp/socket.rs index 930d8f3..6f252e7 100644 --- a/src/prudp/socket.rs +++ b/src/prudp/socket.rs @@ -30,8 +30,8 @@ type OnConnectHandlerFn = Box Pin Fn(PRUDPPacket, Arc, &'a mut MutexGuard<'_, ConnectionData>) -> Pin + 'a + Send>> + Send + Sync>; pub struct ActiveSecureConnectionData { - pid: u32, - session_key: [u8; 32], + pub(crate) pid: u32, + pub(crate) session_key: [u8; 32], } pub struct SocketData { @@ -41,6 +41,7 @@ pub struct SocketData { connections: RwLock>>>, on_connect_handler: OnConnectHandlerFn, on_data_handler: OnDataHandlerFn, + } pub struct EncryptionPair{ @@ -54,6 +55,7 @@ pub struct ActiveConnectionData { pub reliable_client_queue: VecDeque, pub encryption_pairs: Vec, pub server_session_id: u8, + pub connection_id: u32, pub active_secure_connection_data: Option } @@ -227,7 +229,7 @@ impl SocketData { }; let Some(( - accepted, + response_data, encryption_pairs, active_secure_connection_data )) = (self.on_connect_handler)(packet.clone(), *max_substream).await else { @@ -242,11 +244,14 @@ impl SocketData { reliable_client_counter: 2, reliable_server_counter: 1, server_session_id: packet.header.session_id, - active_secure_connection_data + active_secure_connection_data, + connection_id: random() }); let mut response_packet = packet.base_response_packet(); + response_packet.payload = response_data; + response_packet.header.types_and_flags.set_types(CONNECT); response_packet.header.types_and_flags.set_flag(ACK); response_packet.header.types_and_flags.set_flag(HAS_SIZE); @@ -286,14 +291,7 @@ impl SocketData { response_packet.set_sizes(); - let potential_session_key = connection - .active_connection_data - .as_ref() - .unwrap().active_secure_connection_data - .as_ref() - .map(|s| s.session_key); - - response_packet.calculate_and_assign_signature(self.access_key, potential_session_key, Some(connection.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"); @@ -322,7 +320,14 @@ impl SocketData { ack.header.session_id = active_connection.server_session_id; ack.set_sizes(); - ack.calculate_and_assign_signature(self.access_key, None, Some(connection.server_signature)); + let potential_session_key = connection + .active_connection_data + .as_ref() + .unwrap().active_secure_connection_data + .as_ref() + .map(|s| s.session_key); + + ack.calculate_and_assign_signature(self.access_key, potential_session_key, Some(connection.server_signature)); let mut vec = Vec::new(); ack.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); @@ -383,7 +388,13 @@ impl SocketData { 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 potential_session_key = active_connection. + active_secure_connection_data + .as_ref() + .map(|s| s.session_key); + + ack.calculate_and_assign_signature(self.access_key, potential_session_key, Some(*server_signature)); let mut vec = Vec::new(); ack.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); @@ -392,28 +403,27 @@ impl SocketData { } } DISCONNECT => { - let ConnectionData{ - server_signature, - active_connection_data, - .. - } = &*connection; - let Some(active_connection) = active_connection_data.as_ref() else { + let Some(active_connection) = &connection.active_connection_data else { return; }; + + + info!("client disconnected"); + let mut ack = packet.base_acknowledgement_packet(); + ack.header.session_id = active_connection.server_session_id; + ack.set_sizes(); - let potential_session_key = active_connection_data - .as_ref() - .unwrap().active_secure_connection_data + let potential_session_key = active_connection.active_secure_connection_data .as_ref() .map(|s| s.session_key); - ack.calculate_and_assign_signature(self.access_key, potential_session_key, Some(*server_signature)); + ack.calculate_and_assign_signature(self.access_key, potential_session_key, Some(connection.server_signature)); let mut vec = Vec::new(); ack.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); @@ -450,7 +460,14 @@ impl ConnectionData{ packet.set_sizes(); - packet.calculate_and_assign_signature(socket.access_key, None, Some(self.server_signature)); + let potential_session_key = self.active_connection_data + .as_ref() + .unwrap().active_secure_connection_data + .as_ref() + .map(|s| s.session_key); + + + packet.calculate_and_assign_signature(socket.access_key, potential_session_key, Some(self.server_signature)); let mut vec = Vec::new(); diff --git a/src/prudp/station_url.rs b/src/prudp/station_url.rs new file mode 100644 index 0000000..f981fe8 --- /dev/null +++ b/src/prudp/station_url.rs @@ -0,0 +1,161 @@ +use std::net::Ipv4Addr; +use log::error; +use std::fmt::{Display, Formatter, Write}; +use crate::prudp::station_url::Type::{PRUDP, PRUDPS, UDP}; +use crate::prudp::station_url::UrlOptions::{Address, ConnectionID, NatFiltering, NatMapping, NatType, Platform, PMP, Port, PrincipalID, RVConnectionID, StreamID, StreamType, UPNP}; + +pub enum Type{ + UDP, + PRUDP, + PRUDPS +} + +pub mod nat_types{ + pub const BEHIND_NAT: u8 = 1; + pub const PUBLIC: u8 = 2; +} + +pub enum UrlOptions { + Address(Ipv4Addr), + Port(u16), + StreamType(u8), + StreamID(u8), + ConnectionID(u8), + PrincipalID(u32), + NatType(u8), + NatMapping(u8), + NatFiltering(u8), + UPNP(u8), + RVConnectionID(u32), + Platform(u8), + PMP(u8), + +} + +pub struct StationUrl{ + pub url_type: Type, + pub options: Vec +} + +impl StationUrl{ + fn read_options(options: &str) -> Option>{ + let mut options_out = Vec::new(); + + for option in options.split(';'){ + if option == "" { continue; } + let mut option_parts = option.split('='); + let option_name= option_parts.next()?.to_ascii_lowercase(); + let option_value = option_parts.next()?; + + match option_name.as_ref(){ + "address" => { + options_out.push(Address(option_value.parse().ok()?)) + }, + "port" => { + options_out.push(Port(option_value.parse().ok()?)) + } + "natf" => { + options_out.push(NatFiltering(option_value.parse().ok()?)) + } + "natm" => { + options_out.push(NatMapping(option_value.parse().ok()?)) + } + "sid" => { + options_out.push(StreamID(option_value.parse().ok()?)) + } + "upnp" => { + options_out.push(UPNP(option_value.parse().ok()?)) + } + "type" => { + options_out.push(NatType(option_value.parse().ok()?)) + } + "stream" => { + options_out.push(StreamType(option_value.parse().ok()?)) + } + "RVCID" => { + options_out.push(RVConnectionID(option_value.parse().ok()?)) + } + "pl" => { + options_out.push(Platform(option_value.parse().ok()?)) + } + "pmp" => { + options_out.push(PMP(option_value.parse().ok()?)) + } + _ => { + error!("unimplemented option type, skipping: {}", option_name); + } + } + + + + } + + Some(options_out) + } +} + +impl TryFrom<&str> for StationUrl{ + type Error = (); + + fn try_from(value: &str) -> Result { + let (url_type, options) = value.split_at(value.find(":/").ok_or(())?); + + let options = &options[2..]; + + let url_type = match url_type{ + "udp" => UDP, + "prudp" => PRUDP, + "prudps" => PRUDPS, + _ => return Err(()) + }; + + let options = Self::read_options(options).ok_or(())?; + + Ok( + Self{ + url_type, + options + } + ) + } +} + +impl<'a> Into for &'a StationUrl{ + fn into(self) -> String { + let mut url = match self.url_type{ + UDP => "udp:/", + PRUDP => "prudp:/", + PRUDPS => "prudps:/" + }.to_owned(); + + for option in &self.options{ + match option{ + Address(v) => write!(url, "address={}", v).expect("failed to write"), + Port(v) => write!(url, "port={}", v).expect("failed to write"), + StreamType(v) => write!(url, "stream={}", v).expect("failed to write"), + StreamID(v) => write!(url, "sid={}", v).expect("failed to write"), + ConnectionID(v) => write!(url, "CID={}", v).expect("failed to write"), + PrincipalID(v) => write!(url, "PID={}", v).expect("failed to write"), + NatType(v) => write!(url, "type={}", v).expect("failed to write"), + NatMapping(v) => write!(url, "natm={}", v).expect("failed to write"), + NatFiltering(v) => write!(url, "natf={}", v).expect("failed to write"), + UPNP(v) => write!(url, "upnp={}", v).expect("failed to write"), + RVConnectionID(v) => write!(url, "RVCID={}", v).expect("failed to write"), + Platform(v) => write!(url, "pl={}", v).expect("failed to write"), + PMP(v) => write!(url, "pmp={}", v).expect("failed to write"), + } + } + + url + } +} + +impl Display for StationUrl{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let str: String = self.into(); + + write!(f, "{}", str) + } + + +} \ No newline at end of file diff --git a/src/rmc/structures/list.rs b/src/rmc/structures/list.rs index d47f567..7b17f63 100644 --- a/src/rmc/structures/list.rs +++ b/src/rmc/structures/list.rs @@ -1,5 +1,6 @@ use std::io::{Read, Write}; use bytemuck::bytes_of; +use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; use crate::rmc::structures::RmcSerialize; @@ -15,7 +16,15 @@ impl RmcSerialize for Vec{ Ok(()) } - fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { - todo!() + fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result { + let len: u32 = reader.read_struct(IS_BIG_ENDIAN)?; + + let mut vec = Vec::with_capacity(len as usize); + + for _ in 0..len{ + vec.push(T::deserialize(reader)?); + } + + Ok(vec) } }