Browse Source

major refactor of client/server structures

pbs
Jake Probst 6 years ago
parent
commit
cec6da0d85
  1. 91
      src/common/client.rs
  2. 3
      src/common/mod.rs
  3. 3
      src/common/network.rs
  4. 23
      src/common/serverstate.rs
  5. 325
      src/patch/main.rs

91
src/common/client.rs

@ -0,0 +1,91 @@
use libpso::crypto::{PSOCipher, NullCipher};
use libpso::{PSOPacket, PacketParseError};
use crate::common::serverstate::{ServerState, ServerPacket, OnConnect};
use crate::common::network::{send_packet, recv_packet};
use std::net;
use mio::tcp::TcpStream;
use mio::{Poll, Events, Token, Ready, PollOpt};
pub struct Client<P, E> {
running: bool,
socket: mio::tcp::TcpStream,
cipher_in: Box<dyn PSOCipher>,
cipher_out: Box<dyn PSOCipher>,
state: Box<dyn ServerState<Packet = P, PacketError = E>>,
}
impl<P: ServerPacket + std::fmt::Debug, E: std::fmt::Debug> Client<P, E> {
pub fn new(socket: net::TcpStream, state: Box<dyn ServerState<Packet = P, PacketError = E>>) -> Client<P, E> {
let mut client = Client {
running: true,
socket: mio::tcp::TcpStream::from_stream(socket).expect("could not convert socket to nonblocking"),
cipher_in: Box::new(NullCipher {}),
cipher_out: Box::new(NullCipher {}),
state: state,
};
for task in client.state.on_connect() {
match task {
OnConnect::Packet(pkt) => client.send(&*pkt),
OnConnect::Cipher((cipher_in, cipher_out)) => {
client.cipher_in = cipher_in;
client.cipher_out = cipher_out;
},
}
}
client
}
fn send(&mut self, pkt: &dyn PSOPacket) {
match send_packet(&mut self.socket, &mut *self.cipher_out, pkt) {
Ok(_) => {
println!("[patch] send ({:?}): {:?}", self.socket, pkt);
},
Err(err) => {
println!("[patch] error sending packet to {:?}: {:?}", self.socket, err);
self.running = false;
}
}
}
pub fn io_loop(mut self) {
let poll = mio::Poll::new().unwrap();
poll.register(&self.socket, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
let mut events = Events::with_capacity(1024);
loop {
poll.poll(&mut events, None).unwrap();
for event in &events{
println!("event! {:?}", event);
if event.token() == Token(0) {
loop {
let pkt = recv_packet(&mut self.socket, &mut *self.cipher_in)
.and_then(|pkt| {
P::from_bytes(&pkt)
.map_err(|err| err.into())
});
match pkt {
Ok(pkt) => {
let response = self.state.handle(&pkt);
for r in response {
self.send(&*r);
}
},
Err(err) => {
println!("error recv-ing packet with {:?}: {:?}", self.socket, err);
break;
}
}
}
}
}
}
}
}

3
src/common/mod.rs

@ -1,4 +1,5 @@
pub mod cipherkeys; pub mod cipherkeys;
pub mod network; pub mod network;
pub mod serverstate;
pub mod client;

3
src/common/network.rs

@ -36,7 +36,6 @@ pub fn recv_packet<T: Read>(socket: &mut T, cipher: &mut dyn PSOCipher) -> Resul
let mut offset = 0; let mut offset = 0;
while offset < cipher.header_size() { while offset < cipher.header_size() {
let diff = socket.read(&mut size_buf[offset..])?; let diff = socket.read(&mut size_buf[offset..])?;
println!("! {} {:?}", diff, size_buf);
if diff == 0 { if diff == 0 {
return Err(PacketNetworkError::ClientDisconnected); return Err(PacketNetworkError::ClientDisconnected);
} }
@ -76,6 +75,8 @@ pub fn recv_packet<T: Read>(socket: &mut T, cipher: &mut dyn PSOCipher) -> Resul
Ok(full_buf) Ok(full_buf)
} }
// TODO: this fails horribly when the send buffer is full
// if it didnt send it all make a mio poll
pub fn send_packet<T: Write>(socket: &mut T, cipher: &mut dyn PSOCipher, pkt: &dyn PSOPacket) -> Result<(), PacketNetworkError> { pub fn send_packet<T: Write>(socket: &mut T, cipher: &mut dyn PSOCipher, pkt: &dyn PSOPacket) -> Result<(), PacketNetworkError> {
let buf = pkt.as_bytes(); let buf = pkt.as_bytes();
//println!("[send]: {:X?}", buf); //println!("[send]: {:X?}", buf);

23
src/common/serverstate.rs

@ -0,0 +1,23 @@
use libpso::{PSOPacket, PacketParseError};
use libpso::crypto::PSOCipher;
pub enum OnConnect {
Packet(Box<dyn PSOPacket>),
Cipher((Box<dyn PSOCipher>, Box<dyn PSOCipher>)),
}
pub trait ServerPacket: Sized {
fn from_bytes(data: &Vec<u8>) -> Result<Self, PacketParseError>;
}
pub trait ServerState {
type Packet: ServerPacket;
type PacketError;
//fn handle(&mut self, pkt: &Self::Packet) -> Result<Vec<Box<dyn PSOPacket>>, Self::PacketError>;
fn on_connect(&mut self) -> Vec<OnConnect>;
//fn handle(&mut self, pkt: &Self::Packet) -> Result<Box<dyn Iterator<Item = Box<dyn PSOPacket>>>, Self::PacketError>;
fn handle(&mut self, pkt: &Self::Packet) -> Box<dyn Iterator<Item = Box<dyn PSOPacket>>>;
}

325
src/patch/main.rs

@ -12,9 +12,10 @@ use rand::{Rng, RngCore};
use crc::{crc32, Hasher32}; use crc::{crc32, Hasher32};
use libpso::{PacketParseError, PSOPacket}; use libpso::{PacketParseError, PSOPacket};
use libpso::packet::patch::*; use libpso::packet::patch::*;
use libpso::crypto::{CipherError, PSOCipher, NullCipher};
use libpso::crypto::pc::PSOPCCipher; use libpso::crypto::pc::PSOPCCipher;
use elseware::common::network::{send_packet, recv_packet, PacketNetworkError}; use elseware::common::network::{send_packet, recv_packet, PacketNetworkError};
use elseware::common::client::Client;
use elseware::common::serverstate::{ServerPacket, ServerState, OnConnect};
const PATCH_PORT: u16 = 11000; const PATCH_PORT: u16 = 11000;
@ -43,25 +44,6 @@ impl From<std::io::Error> for PatchError {
} }
} }
#[derive(Debug)]
pub enum PatchPacket {
PatchWelcomeReply(PatchWelcomeReply),
LoginReply(LoginReply),
FileInfoReply(FileInfoReply),
FileInfoListEnd(FileInfoListEnd),
}
impl PatchPacket {
pub fn from_bytes(data: &Vec<u8>) -> Result<PatchPacket, PacketParseError> {
match data[2] {
0x02 => Ok(PatchPacket::PatchWelcomeReply(PatchWelcomeReply::from_bytes(data)?)),
0x04 => Ok(PatchPacket::LoginReply(LoginReply::from_bytes(data)?)),
0x0F => Ok(PatchPacket::FileInfoReply(FileInfoReply::from_bytes(data)?)),
0x10 => Ok(PatchPacket::FileInfoListEnd(FileInfoListEnd::from_bytes(data)?)),
_ => Err(PacketParseError::WrongPacketForServerType)
}
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct PatchFile { struct PatchFile {
@ -70,7 +52,6 @@ struct PatchFile {
size: u32, size: u32,
} }
enum PatchTreeIterItem { enum PatchTreeIterItem {
Directory(PathBuf), Directory(PathBuf),
File(PathBuf, u32), File(PathBuf, u32),
@ -109,50 +90,87 @@ impl PatchFileTree {
} }
#[derive(Debug)]
pub enum PatchPacket {
PatchWelcomeReply(PatchWelcomeReply),
LoginReply(LoginReply),
FileInfoReply(FileInfoReply),
FileInfoListEnd(FileInfoListEnd),
}
impl ServerPacket for PatchPacket {
fn from_bytes(data: &Vec<u8>) -> Result<PatchPacket, PacketParseError> {
match data[2] {
0x02 => Ok(PatchPacket::PatchWelcomeReply(PatchWelcomeReply::from_bytes(data)?)),
0x04 => Ok(PatchPacket::LoginReply(LoginReply::from_bytes(data)?)),
0x0F => Ok(PatchPacket::FileInfoReply(FileInfoReply::from_bytes(data)?)),
0x10 => Ok(PatchPacket::FileInfoListEnd(FileInfoListEnd::from_bytes(data)?)),
_ => Err(PacketParseError::WrongPacketForServerType)
}
}
}
struct Client {
running: bool,
key_in: u32,
key_out: u32,
socket: mio::tcp::TcpStream,
cipher_in: Box<dyn PSOCipher>,
cipher_out: Box<dyn PSOCipher>,
struct PatchServerState {
patch_file_tree: PatchFileTree, patch_file_tree: PatchFileTree,
patch_file_lookup: HashMap<u32, PatchFile>, patch_file_lookup: HashMap<u32, PatchFile>,
patch_file_info: Vec<FileInfoReply>, patch_file_info: Vec<FileInfoReply>,
} }
impl Client {
fn new(socket: net::TcpStream, patch_file_tree: PatchFileTree, patch_file_lookup: HashMap<u32, PatchFile>) -> Client {
impl PatchServerState {
fn new(patch_file_tree: PatchFileTree, patch_file_lookup: HashMap<u32, PatchFile>) -> PatchServerState {
PatchServerState {
patch_file_tree: patch_file_tree,
patch_file_lookup: patch_file_lookup,
patch_file_info: Vec::new(),
}
}
}
impl ServerState for PatchServerState {
type Packet = PatchPacket;
type PacketError = PatchError;
fn on_connect(&mut self) -> Vec<OnConnect> {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let key_in: u32 = rng.gen(); let key_in: u32 = rng.gen();
let key_out: u32 = rng.gen(); let key_out: u32 = rng.gen();
Client {
running: true,
key_in: key_in,
key_out: key_out,
socket: mio::tcp::TcpStream::from_stream(socket).expect("could not convert socket to nonblocking"),
cipher_in: Box::new(NullCipher {}),
cipher_out: Box::new(NullCipher {}),
patch_file_tree: patch_file_tree,
patch_file_lookup: patch_file_lookup,
patch_file_info: Vec::new()
}
vec![OnConnect::Packet(Box::new(PatchWelcome::new(key_out, key_in))),
OnConnect::Cipher((Box::new(PSOPCCipher::new(key_in)), Box::new(PSOPCCipher::new(key_out))))
]
} }
fn send(&mut self, pkt: &dyn PSOPacket) {
match send_packet(&mut self.socket, &mut *self.cipher_out, pkt) {
Ok(_) => {
println!("[patch] send ({:?}): {:?}", self.socket, pkt);
fn handle(&mut self, pkt: &PatchPacket) -> Box<dyn Iterator<Item = Box<dyn PSOPacket>>> {
match pkt {
PatchPacket::PatchWelcomeReply(_pkt) => {
let p: Vec<Box<dyn PSOPacket>> = vec![Box::new(RequestLogin {})];
Box::new(p.into_iter())
},
PatchPacket::LoginReply(_pkt) => {
let mut p: Vec<Box<dyn PSOPacket>> = vec![Box::new(Message::new("hello player".to_string()))];
p.append(&mut get_file_list_packets(&self.patch_file_tree));
p.push(Box::new(PatchEndList {}));
Box::new(p.into_iter())
},
PatchPacket::FileInfoReply(pkt) => {
self.patch_file_info.push(pkt.clone());
Box::new(None.into_iter())
}, },
Err(err) => {
println!("[patch] error sending packet to {:?}: {:?}", self.socket, err);
self.running = false;
PatchPacket::FileInfoListEnd(_pkt) => {
let need_update = self.patch_file_info.iter()
.filter(|file_info| does_file_need_updating(file_info, &self.patch_file_lookup))
.collect::<Vec<_>>();
let total_size = need_update.iter().fold(0, |a, file_info| a + file_info.size);
let total_files = need_update.len() as u32;
let p: Vec<Box<dyn PSOPacket>> = vec![Box::new(FilesToPatchMetadata::new(total_size, total_files)),
Box::new(PatchStartList {}),
];
Box::new(p.into_iter().chain(SendFileIterator::new(&self)))
} }
} }
} }
} }
fn load_patch_dir(basedir: &str, patchbase: &str, file_ids: &mut HashMap<u32, PatchFile>) -> PatchFileTree { fn load_patch_dir(basedir: &str, patchbase: &str, file_ids: &mut HashMap<u32, PatchFile>) -> PatchFileTree {
@ -192,7 +210,6 @@ fn generate_patch_tree(basedir: &str) -> (PatchFileTree, HashMap<u32, PatchFile>
} }
// TODO: this should be a function that takes client and sends packets instead of returning a vec of packets
fn get_file_list_packets(patch_file_tree: &PatchFileTree) -> Vec<Box<dyn PSOPacket>> { fn get_file_list_packets(patch_file_tree: &PatchFileTree) -> Vec<Box<dyn PSOPacket>> {
let mut pkts: Vec<Box<dyn PSOPacket>> = Vec::new(); let mut pkts: Vec<Box<dyn PSOPacket>> = Vec::new();
@ -213,20 +230,6 @@ fn get_file_list_packets(patch_file_tree: &PatchFileTree) -> Vec<Box<dyn PSOPack
pkts pkts
} }
fn send_file_list(client: &mut Client) -> Result<(), PatchError> {
client.send(&PatchStartList {});
let pkts = get_file_list_packets(&client.patch_file_tree);
for pkt in pkts {
client.send(&*pkt);
}
client.send(&PatchEndList {});
Ok(())
}
fn get_checksum_and_size(path: &PathBuf) -> Result<(u32, u32), PatchError> { fn get_checksum_and_size(path: &PathBuf) -> Result<(u32, u32), PatchError> {
let file = fs::File::open(path)?; let file = fs::File::open(path)?;
let size = file.metadata().unwrap().len(); let size = file.metadata().unwrap().len();
@ -248,124 +251,92 @@ fn does_file_need_updating(file_info: &FileInfoReply, patch_file_lookup: &HashMa
patch_file.checksum != file_info.checksum || patch_file.size != file_info.size patch_file.checksum != file_info.checksum || patch_file.size != file_info.size
} }
fn send_file(client: &mut Client, actual_path: &PathBuf, patch_path: &PathBuf, id: u32) -> Result<(), PatchError>{
let file = fs::File::open(actual_path)?;
let size = file.metadata().unwrap().len();
client.send(&StartFileSend::new(patch_path.to_str().unwrap(), size as u32, id));
let mut buf = [0u8; PATCH_FILE_CHUNK_SIZE as usize];
let mut reader = io::BufReader::new(file);
let mut chunk_num = 0;
while let Ok(len) = reader.read(&mut buf) {
if len == 0 {
break;
}
let mut crc = crc32::Digest::new(crc32::IEEE);
crc.write(&buf[0..len]);
let pkt = FileSend {
chunk_num: chunk_num,
checksum: crc.sum32(),
chunk_size: len as u32,
buffer: buf
};
client.send(&pkt);
chunk_num += 1;
}
client.send(&EndFileSend::new());
Ok(())
struct SendFileIterator {
done: bool,
file_iter: Box<dyn Iterator<Item = PatchTreeIterItem>>,
file_ids_to_update: HashSet<u32>,
patch_file_lookup: HashMap<u32, PatchFile>,
current_file: Option<io::BufReader<fs::File>>,
chunk_num: u32,
} }
fn send_file_data(client: &mut Client) -> Result<(), PatchError> {
let need_update = client.patch_file_info.iter()
.filter(|file_info| does_file_need_updating(file_info, &client.patch_file_lookup))
.collect::<Vec<_>>();
let total_size = need_update.iter().fold(0, |a, file_info| a + file_info.size);
let total_files = need_update.len() as u32;
let file_ids_to_update = need_update.iter().map(|k| k.id).collect::<HashSet<_>>();
client.send(&FilesToPatchMetadata::new(total_size, total_files));
client.send(&PatchStartList {});
for file in client.patch_file_tree.flatten() {
match file {
PatchTreeIterItem::Directory(path) => {
client.send(&ChangeDirectory::new(path.to_str().unwrap()));
},
PatchTreeIterItem::File(path, id) => {
if file_ids_to_update.contains(&id) {
let patch_file = client.patch_file_lookup.get(&id).unwrap().clone();
send_file(client, &patch_file.path, &path, id)?;
}
},
PatchTreeIterItem::UpDirectory => {
client.send(&UpOneDirectory {});
}
impl SendFileIterator {
fn new(state: &PatchServerState) -> SendFileIterator {
let need_update = state.patch_file_info.iter()
.filter(|file_info| does_file_need_updating(file_info, &state.patch_file_lookup))
.collect::<Vec<_>>();
let file_ids_to_update = need_update.iter().map(|k| k.id).collect::<HashSet<_>>();
SendFileIterator {
done: false,
file_ids_to_update: file_ids_to_update,
patch_file_lookup: state.patch_file_lookup.clone(),
file_iter: Box::new(state.patch_file_tree.flatten().into_iter()),
current_file: None,
chunk_num: 0,
} }
} }
client.send(&FinalizePatching {});
Ok(())
} }
impl Iterator for SendFileIterator {
type Item = Box<dyn PSOPacket>;
fn handle_packet(client: &mut Client, pkt: PatchPacket) -> Result<(), PatchError> {
println!("[patch] recv({:?}): {:?}", client.socket, pkt);
match pkt {
PatchPacket::PatchWelcomeReply(_pkt) => {
client.send(&RequestLogin {});
},
PatchPacket::LoginReply(_pkt) => {
client.send(&Message::new("hello player".to_string()));
send_file_list(client)?;
},
PatchPacket::FileInfoReply(pkt) => {
client.patch_file_info.push(pkt);
},
PatchPacket::FileInfoListEnd(_pkt) => {
send_file_data(client)?;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
return None;
} }
}
Ok(())
}
fn client_loop(mut client: Client) {
let poll = mio::Poll::new().unwrap();
poll.register(&client.socket, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
let mut events = Events::with_capacity(1024);
loop {
poll.poll(&mut events, None).unwrap();
for event in &events{
println!("event! {:?}", event);
if event.token() == Token(0) {
loop {
let pkt = recv_packet(&mut client.socket, &mut *client.cipher_in)
.and_then(|pkt| {
PatchPacket::from_bytes(&pkt)
.map_err(|err| err.into())
});
match pkt {
Ok(pkt) => {
handle_packet(&mut client, pkt).expect("could not handle packet");
},
Err(err) => {
println!("[patch] error recv-ing packet with {:?}: {:?}", client.socket, err);
break;
match self.current_file {
Some(ref mut file) => {
let mut buf = [0u8; PATCH_FILE_CHUNK_SIZE as usize];
let len = file.read(&mut buf).unwrap();
if len == 0 {
self.current_file = None;
self.chunk_num = 0;
Some(Box::new(EndFileSend::new()))
}
else {
let mut crc = crc32::Digest::new(crc32::IEEE);
crc.write(&buf[0..len]);
let pkt = FileSend {
chunk_num: self.chunk_num,
checksum: crc.sum32(),
chunk_size: len as u32,
buffer: buf,
};
self.chunk_num += 1;
Some(Box::new(pkt))
}
},
None => {
match self.file_iter.next() {
Some(next_file) => {
match next_file {
PatchTreeIterItem::Directory(path) => {
Some(Box::new(ChangeDirectory::new(path.to_str().unwrap())))
},
PatchTreeIterItem::File(path, id) => {
if self.file_ids_to_update.contains(&id) {
let patch_file = self.patch_file_lookup.get(&id).unwrap();
let file = fs::File::open(&patch_file.path).unwrap();
let size = file.metadata().unwrap().len();
self.current_file = Some(io::BufReader::new(file));
Some(Box::new(StartFileSend::new(path.to_str().unwrap(), size as u32, id)))
}
else {
self.next()
}
},
PatchTreeIterItem::UpDirectory => {
Some(Box::new(UpOneDirectory {}))
},
} }
},
None => {
self.current_file = None;
self.done = true;
Some(Box::new(FinalizePatching {}))
} }
} }
} }
@ -373,16 +344,16 @@ fn client_loop(mut client: Client) {
} }
} }
fn new_client(socket: net::TcpStream, patch_file_tree: PatchFileTree, patch_file_lookup: HashMap<u32, PatchFile>) { fn new_client(socket: net::TcpStream, patch_file_tree: PatchFileTree, patch_file_lookup: HashMap<u32, PatchFile>) {
let mut client = Client::new(socket, patch_file_tree, patch_file_lookup);
let welcome_pkt = PatchWelcome::new(client.key_out, client.key_in);
client.send(&welcome_pkt);
client.cipher_in = Box::new(PSOPCCipher::new(client.key_in));
client.cipher_out = Box::new(PSOPCCipher::new(client.key_out));
client_loop(client);
let state = PatchServerState::new(patch_file_tree, patch_file_lookup);
let client = Client::new(socket, Box::new(state));
client.io_loop();
} }
fn main() { fn main() {
println!("[patch] starting server"); println!("[patch] starting server");

Loading…
Cancel
Save