Browse Source

data access interface

pbs
Jake Probst 5 years ago
parent
commit
2fb97cc2b6
  1. 4
      src/common/client.rs
  2. 22
      src/login/character.rs
  3. 48
      src/login/dataaccess.rs
  4. 104
      src/login/login.rs
  5. 47
      src/login/main.rs
  6. 8
      src/main.rs
  7. 7
      src/patch/main.rs

4
src/common/client.rs

@ -61,6 +61,7 @@ impl<P: ServerPacket + std::fmt::Debug, E: std::fmt::Debug> Client<P, E> {
// TODO: this may need to pad to 8 bytes for bb cipher // TODO: this may need to pad to 8 bytes for bb cipher
fn send(&mut self, pkt: &dyn PSOPacket) { fn send(&mut self, pkt: &dyn PSOPacket) {
println!("[send] {:?}", pkt);
let buf = pkt.as_bytes(); let buf = pkt.as_bytes();
let mut cbuf = self.cipher_out.encrypt(&buf).unwrap(); let mut cbuf = self.cipher_out.encrypt(&buf).unwrap();
self.send_buffer.append(&mut cbuf); self.send_buffer.append(&mut cbuf);
@ -90,6 +91,7 @@ impl<P: ServerPacket + std::fmt::Debug, E: std::fmt::Debug> Client<P, E> {
match pkt { match pkt {
Ok(pkt) => { Ok(pkt) => {
println!("[recv] {:?}", pkt);
let response = self.state.handle(&pkt); let response = self.state.handle(&pkt);
for r in response { for r in response {
self.send(&*r); self.send(&*r);
@ -98,7 +100,7 @@ impl<P: ServerPacket + std::fmt::Debug, E: std::fmt::Debug> Client<P, E> {
Err(err) => { Err(err) => {
match err { match err {
PacketNetworkError::ClientDisconnected => self.running = false, PacketNetworkError::ClientDisconnected => self.running = false,
_ => println!("error recv-ing packet with {:?}: {:?}", self.socket, err),
_ => {} //println!("error recv-ing packet with {:?}: {:?}", self.socket, err),
} }
break; break;
} }

22
src/login/character.rs

@ -3,11 +3,6 @@ use std::net;
use rand::{Rng, RngCore}; use rand::{Rng, RngCore};
use bcrypt::{DEFAULT_COST, hash, verify}; use bcrypt::{DEFAULT_COST, hash, verify};
use diesel::r2d2;
use diesel::prelude::*;
use diesel::pg::PgConnection;
use elseware::schema::*;
use libpso::packet::login::*; use libpso::packet::login::*;
use libpso::{PacketParseError, PSOPacket}; use libpso::{PacketParseError, PSOPacket};
use libpso::crypto::{CipherError, PSOCipher, NullCipher}; use libpso::crypto::{CipherError, PSOCipher, NullCipher};
@ -21,11 +16,11 @@ use elseware::common::client::Client;
use elseware::common::serverstate::{ServerPacket, ServerState, OnConnect}; use elseware::common::serverstate::{ServerPacket, ServerState, OnConnect};
use elseware::common::util::array_to_utf8; use elseware::common::util::array_to_utf8;
use crate::dataaccess::DataAccess;
use crate::login::{SharedLoginState, get_login_status}; use crate::login::{SharedLoginState, get_login_status};
use crate::models::UserAccount; use crate::models::UserAccount;
pub const CHARACTER_PORT: u16 = 12001; pub const CHARACTER_PORT: u16 = 12001;
type ConnectionPool = r2d2::Pool<r2d2::ConnectionManager<PgConnection>>;
#[derive(Debug)] #[derive(Debug)]
pub enum CharacterError { pub enum CharacterError {
@ -47,13 +42,13 @@ impl ServerPacket for CharacterPacket {
} }
} }
pub struct CharacterServerState {
shared_state: SharedLoginState,
pub struct CharacterServerState<DA: DataAccess> {
shared_state: SharedLoginState<DA>,
user: Option<UserAccount>, user: Option<UserAccount>,
} }
impl CharacterServerState {
fn new(shared_state: SharedLoginState) -> CharacterServerState {
impl<DA: DataAccess> CharacterServerState<DA> {
fn new(shared_state: SharedLoginState<DA>) -> CharacterServerState<DA> {
CharacterServerState { CharacterServerState {
shared_state: shared_state, shared_state: shared_state,
user: None, user: None,
@ -61,7 +56,7 @@ impl CharacterServerState {
} }
fn validate_login(&mut self, pkt: &Login) -> Box<PktVec> { fn validate_login(&mut self, pkt: &Login) -> Box<PktVec> {
match get_login_status(&self.shared_state.connection_pool, pkt) {
match get_login_status(&self.shared_state.data_access, pkt) {
Ok(user) => { Ok(user) => {
let mut response = LoginResponse::by_status(AccountStatus::Ok, pkt.security_data); let mut response = LoginResponse::by_status(AccountStatus::Ok, pkt.security_data);
response.guildcard = user.guildcard.map_or(0, |gc| gc) as u32; response.guildcard = user.guildcard.map_or(0, |gc| gc) as u32;
@ -80,7 +75,7 @@ impl CharacterServerState {
} }
} }
impl ServerState for CharacterServerState {
impl<DA: DataAccess> ServerState for CharacterServerState<DA> {
type Packet = CharacterPacket; type Packet = CharacterPacket;
type PacketError = CharacterError; type PacketError = CharacterError;
@ -107,7 +102,6 @@ impl ServerState for CharacterServerState {
CharacterPacket::RequestSettings(_req) => { CharacterPacket::RequestSettings(_req) => {
self.get_settings() self.get_settings()
} }
} }
} }
} }
@ -116,7 +110,7 @@ impl ServerState for CharacterServerState {
pub fn new_client(socket: mio::tcp::TcpStream, shared_state: SharedLoginState) {
pub fn new_client<DA: DataAccess + 'static>(socket: mio::tcp::TcpStream, shared_state: SharedLoginState<DA>) {
let state = CharacterServerState::new(shared_state); let state = CharacterServerState::new(shared_state);
let client = Client::new(socket, Box::new(state)); let client = Client::new(socket, Box::new(state));
client.io_loop(); client.io_loop();

48
src/login/dataaccess.rs

@ -0,0 +1,48 @@
use diesel::r2d2;
use diesel::prelude::*;
use diesel::pg::PgConnection;
use crate::models::*;
type ConnectionPool = r2d2::Pool<r2d2::ConnectionManager<PgConnection>>;
pub trait DataAccess {
fn get_user_by_id(&self, id: u32) -> Option<UserAccount> {
unimplemented!();
}
fn get_user_by_name(&self, username: String) -> Option<UserAccount> {
unimplemented!();
}
}
#[derive(Clone)]
pub struct DBAccess {
connection_pool: ConnectionPool,
}
impl DBAccess {
pub fn new(pool: ConnectionPool) -> DBAccess {
DBAccess {
connection_pool: pool,
}
}
}
impl DataAccess for DBAccess {
fn get_user_by_name(&self, name: String) -> Option<UserAccount> {
use elseware::schema::user_accounts::dsl::{user_accounts, username};
self.connection_pool.get()
.map(|conn| {
user_accounts.filter(username.eq(name)).load::<UserAccount>(&conn)
.map(|mut user| user.pop()).unwrap_or(None)
})
.unwrap_or(None)
}
}
/*impl DataAccess for DatabaseAccess {
}*/

104
src/login/login.rs

@ -1,13 +1,10 @@
// TODO: rename this module to auth
use std::net; use std::net;
use rand::{Rng, RngCore}; use rand::{Rng, RngCore};
use bcrypt::{DEFAULT_COST, hash, verify}; use bcrypt::{DEFAULT_COST, hash, verify};
use diesel::r2d2;
use diesel::prelude::*;
use diesel::pg::PgConnection;
use elseware::schema::*;
use libpso::packet::login::*; use libpso::packet::login::*;
use libpso::{PacketParseError, PSOPacket}; use libpso::{PacketParseError, PSOPacket};
use libpso::crypto::{CipherError, PSOCipher, NullCipher}; use libpso::crypto::{CipherError, PSOCipher, NullCipher};
@ -16,15 +13,16 @@ use libpso::crypto::bb::PSOBBCipher;
use elseware::pktvec; use elseware::pktvec;
use elseware::common::pktvec::PktVec; use elseware::common::pktvec::PktVec;
use elseware::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY}; use elseware::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY};
use elseware::common::network::{PacketNetworkError};
//use elseware::common::network::{PacketNetworkError};
use elseware::common::client::Client; use elseware::common::client::Client;
use elseware::common::serverstate::{ServerPacket, ServerState, OnConnect}; use elseware::common::serverstate::{ServerPacket, ServerState, OnConnect};
use elseware::common::util::array_to_utf8; use elseware::common::util::array_to_utf8;
use crate::dataaccess::DataAccess;
use crate::models::UserAccount; use crate::models::UserAccount;
pub const LOGIN_PORT: u16 = 12000; pub const LOGIN_PORT: u16 = 12000;
type ConnectionPool = r2d2::Pool<r2d2::ConnectionManager<PgConnection>>;
//type ConnectionPool = r2d2::Pool<r2d2::ConnectionManager<PgConnection>>;
#[derive(Debug)] #[derive(Debug)]
pub enum LoginError { pub enum LoginError {
@ -46,78 +44,45 @@ impl ServerPacket for LoginPacket {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct SharedLoginState {
pub connection_pool: ConnectionPool,
pub struct SharedLoginState<DA: DataAccess> {
pub data_access: DA,
} }
impl SharedLoginState {
pub fn new(pool: ConnectionPool) -> SharedLoginState {
impl<DA: DataAccess> SharedLoginState<DA> {
pub fn new(data_access: DA) -> SharedLoginState<DA> {
SharedLoginState { SharedLoginState {
connection_pool: pool,
}
}
}
pub struct LoginServerState {
pub shared_state: SharedLoginState,
}
pub fn get_login_status(connection_pool: &ConnectionPool, pkt: &Login) -> Result<UserAccount, AccountStatus> {
use elseware::schema::user_accounts::dsl::{user_accounts, username};
connection_pool.get()
.map_err(|_err| AccountStatus::Error)
.and_then(|conn| {
array_to_utf8(pkt.username)
.map(|username_str| {
(conn, username_str)
})
.map_err(|err| {
println!("utf err: {:?}", err);
AccountStatus::Error
})
})
.and_then(|(conn, username_str)| {
user_accounts.filter(username.eq(username_str)).load::<UserAccount>(&conn)
.map_err(|err| {
println!("sql err: {:?}", err);
AccountStatus::Error
})
})
.and_then(|mut result| {
result.pop().ok_or(AccountStatus::InvalidUser)
})
.and_then(|user| {
array_to_utf8(pkt.password)
.map(|password| {
(user, password)
})
.map_err(|_err| AccountStatus::Error)
})
.and_then(|(user, password)| {
bcrypt::verify(password, user.password.as_str())
.map_err(|err| {
println!("bcrypt err: {:?}", err);
AccountStatus::Error
})
.and_then(|correct_password| {
match correct_password {
data_access: data_access,
}
}
}
pub struct LoginServerState<DA: DataAccess> {
pub shared_state: SharedLoginState<DA>,
}
pub fn get_login_status(data_access: &dyn DataAccess, pkt: &Login) -> Result<UserAccount, AccountStatus> {
let username = array_to_utf8(pkt.username).map_err(|_err| AccountStatus::Error)?;
let password = array_to_utf8(pkt.password).map_err(|_err| AccountStatus::Error)?;
let user = data_access.get_user_by_name(username).ok_or(AccountStatus::InvalidUser)?;
let verified = bcrypt::verify(password, user.password.as_str()).map_err(|_err| AccountStatus::Error)?;
match verified {
true => Ok(user), true => Ok(user),
false => Err(AccountStatus::InvalidPassword),
false => Err(AccountStatus::InvalidPassword)
} }
})
})
} }
impl LoginServerState {
fn new(shared_state: SharedLoginState) -> LoginServerState {
impl<DA: DataAccess> LoginServerState<DA> {
fn new(shared_state: SharedLoginState<DA>) -> LoginServerState<DA> {
LoginServerState { LoginServerState {
shared_state: shared_state, shared_state: shared_state,
} }
} }
fn validate_login(&mut self, pkt: &Login) -> Box<PktVec> { fn validate_login(&mut self, pkt: &Login) -> Box<PktVec> {
match get_login_status(&self.shared_state.connection_pool, pkt) {
match get_login_status(&self.shared_state.data_access, pkt) {
Ok(_user) => { Ok(_user) => {
let response = LoginResponse::by_status(AccountStatus::Ok, pkt.security_data); let response = LoginResponse::by_status(AccountStatus::Ok, pkt.security_data);
let ip = net::Ipv4Addr::new(127,0,0,1); let ip = net::Ipv4Addr::new(127,0,0,1);
@ -131,7 +96,7 @@ impl LoginServerState {
} }
} }
impl ServerState for LoginServerState {
impl<DA: DataAccess> ServerState for LoginServerState<DA> {
type Packet = LoginPacket; type Packet = LoginPacket;
type PacketError = LoginError; type PacketError = LoginError;
@ -150,7 +115,6 @@ impl ServerState for LoginServerState {
} }
fn handle(&mut self, pkt: &LoginPacket) -> Box<dyn Iterator<Item = Box<dyn PSOPacket>>> { fn handle(&mut self, pkt: &LoginPacket) -> Box<dyn Iterator<Item = Box<dyn PSOPacket>>> {
println!("[login: recv] {:?}", pkt);
match pkt { match pkt {
LoginPacket::Login(login) => { LoginPacket::Login(login) => {
self.validate_login(login) self.validate_login(login)
@ -160,9 +124,7 @@ impl ServerState for LoginServerState {
} }
pub fn new_client(socket: mio::tcp::TcpStream, shared_state: SharedLoginState) {
pub fn new_client<DA: DataAccess + 'static>(socket: mio::tcp::TcpStream, shared_state: SharedLoginState<DA>) {
let state = LoginServerState::new(shared_state); let state = LoginServerState::new(shared_state);
let client = Client::new(socket, Box::new(state)); let client = Client::new(socket, Box::new(state));
client.io_loop(); client.io_loop();

47
src/login/main.rs

@ -1,9 +1,12 @@
#![feature(const_generics)]
mod login; mod login;
mod character; mod character;
mod dataaccess;
mod models; mod models;
use std::net::{SocketAddr, Ipv4Addr}; use std::net::{SocketAddr, Ipv4Addr};
use std::net;
//use std::net;
use std::thread; use std::thread;
use std::env; use std::env;
@ -12,28 +15,50 @@ use mio::{Events, Poll, Token, Ready, PollOpt};
use dotenv::dotenv; use dotenv::dotenv;
use diesel::r2d2; use diesel::r2d2;
use diesel::prelude::*;
//use diesel::prelude::*;
use diesel::pg::PgConnection; use diesel::pg::PgConnection;
use models::{NewUser, UserAccount};
//use models::{NewUser, UserAccount, UserSettings, NewUserSettings};
use dataaccess::DBAccess;
fn main() { fn main() {
dotenv().ok(); dotenv().ok();
let database_url = env::var("DATABASE_URL").unwrap();
//let database_url = env::var("DATABASE_URL").unwrap();
//let conn = PgConnection::establish(&database_url).unwrap();
/*if let Some(arg) = env::args().nth(1) {
if arg == "dbstuff" {
let conn = PgConnection::establish(&database_url).unwrap();
//use elseware::schema::user_accounts::dsl::*;
//use elseware::schema::user_accounts::table;
use elseware::schema::user_accounts::dsl::*;
use elseware::schema::user_settings::dsl::*;
/*let u = NewUser::new("hi".to_string(), "qwer".to_string());
let u = NewUser::new("hi".to_string(), "qwer".to_string());
diesel::insert_into(user_accounts).values(&u).execute(&conn).unwrap(); diesel::insert_into(user_accounts).values(&u).execute(&conn).unwrap();
let u = NewUser::new("hi2".to_string(), "qwer".to_string()); let u = NewUser::new("hi2".to_string(), "qwer".to_string());
diesel::insert_into(user_accounts).values(&u).execute(&conn).unwrap();*/
let user: UserAccount = diesel::insert_into(user_accounts).values(&u).get_result(&conn).unwrap();
let mut s = models::EUserSettings(libpso::character::settings::UserSettings::default());
s.0.blocked_users[5] = 99;
s.0.blocked_users[6] = 123;
diesel::insert_into(user_settings).values(& NewUserSettings {
user_id: user.id,
settings: s,
}).execute(&conn).unwrap();
let us = user_settings.load::<UserSettings>(&conn).unwrap();
for u in us {
println!("{:?}", u.settings.0.blocked_users[4]);
println!("{:?}", u.settings.0.blocked_users[5]);
println!("{:?}", u.settings.0.blocked_users[6]);
}
}
}*/
//let database_url = env::var("DATABASE_URL").unwrap();
println!("[login+character] starting server"); println!("[login+character] starting server");
let database_url = env::var("DATABASE_URL").unwrap();
let connection_manager = r2d2::ConnectionManager::<PgConnection>::new(database_url); let connection_manager = r2d2::ConnectionManager::<PgConnection>::new(database_url);
let connection_pool = r2d2::Pool::builder() let connection_pool = r2d2::Pool::builder()
.build(connection_manager).unwrap(); .build(connection_manager).unwrap();
@ -41,7 +66,7 @@ fn main() {
let login_listener = TcpListener::bind(&SocketAddr::from((Ipv4Addr::new(0,0,0,0), login::LOGIN_PORT))).unwrap(); let login_listener = TcpListener::bind(&SocketAddr::from((Ipv4Addr::new(0,0,0,0), login::LOGIN_PORT))).unwrap();
let character_listener = TcpListener::bind(&SocketAddr::from((Ipv4Addr::new(0,0,0,0), character::CHARACTER_PORT))).unwrap(); let character_listener = TcpListener::bind(&SocketAddr::from((Ipv4Addr::new(0,0,0,0), character::CHARACTER_PORT))).unwrap();
let login_shared_state = login::SharedLoginState::new(connection_pool);
let login_shared_state = login::SharedLoginState::new(DBAccess::new(connection_pool));
let poll = Poll::new().unwrap(); let poll = Poll::new().unwrap();
poll.register(&login_listener, Token(0), Ready::readable(), PollOpt::edge()).unwrap(); poll.register(&login_listener, Token(0), Ready::readable(), PollOpt::edge()).unwrap();

8
src/main.rs

@ -1,8 +0,0 @@
#![feature(async_await)]
mod common;
//mod patch;
fn main() {
// something or other?
}

7
src/patch/main.rs

@ -9,6 +9,7 @@ use std::path::{Path, PathBuf, Components};
use std::convert::AsRef; use std::convert::AsRef;
use rand::{Rng, RngCore}; use rand::{Rng, RngCore};
use crc::{crc32, Hasher32}; use crc::{crc32, Hasher32};
use mio::tcp::TcpStream;
use libpso::{PacketParseError, PSOPacket}; use libpso::{PacketParseError, PSOPacket};
use libpso::packet::patch::*; use libpso::packet::patch::*;
use libpso::crypto::pc::PSOPCCipher; use libpso::crypto::pc::PSOPCCipher;
@ -348,7 +349,7 @@ impl Iterator for SendFileIterator {
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 state = PatchServerState::new(patch_file_tree, patch_file_lookup); let state = PatchServerState::new(patch_file_tree, patch_file_lookup);
let client = Client::new(socket, Box::new(state));
let client = Client::new(TcpStream::from_stream(socket).unwrap(), Box::new(state));
client.io_loop(); client.io_loop();
} }
@ -357,8 +358,6 @@ fn new_client(socket: net::TcpStream, patch_file_tree: PatchFileTree, patch_file
fn main() { fn main() {
println!("[patch] starting server"); println!("[patch] starting server");
let listener = TcpListener::bind(&SocketAddr::from((Ipv4Addr::new(0,0,0,0), PATCH_PORT))).unwrap();
let (patch_file_tree, patch_file_lookup) = generate_patch_tree("patchfiles/"); let (patch_file_tree, patch_file_lookup) = generate_patch_tree("patchfiles/");
println!("[patch] files to patch:"); println!("[patch] files to patch:");
let mut indent = 0; let mut indent = 0;
@ -379,6 +378,8 @@ fn main() {
} }
} }
let listener = TcpListener::bind(&SocketAddr::from((Ipv4Addr::new(0,0,0,0), PATCH_PORT))).unwrap();
println!("[patch] waiting for connections"); println!("[patch] waiting for connections");
while let Ok((socket, addr)) = listener.accept() { while let Ok((socket, addr)) = listener.accept() {
let local_patch_file_tree = patch_file_tree.clone(); let local_patch_file_tree = patch_file_tree.clone();

Loading…
Cancel
Save