From 823ccf57bee19c81b435829e32f311e2fc9edadb Mon Sep 17 00:00:00 2001 From: Jake Probst Date: Tue, 20 Aug 2019 17:59:00 -0700 Subject: [PATCH] split login between auth and character services --- src/login/character.rs | 123 ++++++++++++++++++++++++++++++ src/login/login.rs | 169 +++++++++++++++++++++++++++++++++++++++++ src/login/main.rs | 147 ++++++++++++++--------------------- 3 files changed, 350 insertions(+), 89 deletions(-) create mode 100644 src/login/character.rs create mode 100644 src/login/login.rs diff --git a/src/login/character.rs b/src/login/character.rs new file mode 100644 index 0000000..282d4bb --- /dev/null +++ b/src/login/character.rs @@ -0,0 +1,123 @@ +use std::net; + +use rand::{Rng, RngCore}; +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::{PacketParseError, PSOPacket}; +use libpso::crypto::{CipherError, PSOCipher, NullCipher}; +use libpso::crypto::bb::PSOBBCipher; + +use elseware::pktvec; +use elseware::common::pktvec::PktVec; +use elseware::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY}; +use elseware::common::network::{PacketNetworkError}; +use elseware::common::client::Client; +use elseware::common::serverstate::{ServerPacket, ServerState, OnConnect}; +use elseware::common::util::array_to_utf8; + +use crate::login::{SharedLoginState, get_login_status}; +use crate::models::UserAccount; + +pub const CHARACTER_PORT: u16 = 12001; +type ConnectionPool = r2d2::Pool>; + +#[derive(Debug)] +pub enum CharacterError { +} + +#[derive(Debug)] +pub enum CharacterPacket { + Login(Login), + RequestSettings(RequestSettings), +} + +impl ServerPacket for CharacterPacket { + fn from_bytes(data: &Vec) -> Result { + match data[2] { + 0x93 => Ok(CharacterPacket::Login(Login::from_bytes(data)?)), + 0xE0 => Ok(CharacterPacket::RequestSettings(RequestSettings::from_bytes(data)?)), + _ => Err(PacketParseError::WrongPacketForServerType) + } + } +} + +pub struct CharacterServerState { + shared_state: SharedLoginState, + user: Option, +} + +impl CharacterServerState { + fn new(shared_state: SharedLoginState) -> CharacterServerState { + CharacterServerState { + shared_state: shared_state, + user: None, + } + } + + fn validate_login(&mut self, pkt: &Login) -> Box { + match get_login_status(&self.shared_state.connection_pool, pkt) { + Ok(user) => { + let mut response = LoginResponse::by_status(AccountStatus::Ok, pkt.security_data); + response.guildcard = user.guildcard.map_or(0, |gc| gc) as u32; + response.team_id = user.team_id.map_or(0, |ti| ti) as u32; + self.user = Some(user); + pktvec![response] + }, + Err(err) => { + pktvec![LoginResponse::by_status(err, pkt.security_data)] + } + } + } + + fn get_settings(&mut self) -> Box { + pktvec![] + } +} + +impl ServerState for CharacterServerState { + type Packet = CharacterPacket; + type PacketError = CharacterError; + + fn on_connect(&mut self) -> Vec { + let mut rng = rand::thread_rng(); + + let mut server_key = [0u8; 48]; + let mut client_key = [0u8; 48]; + rng.fill(&mut server_key[..]); + rng.fill(&mut client_key[..]); + + vec![OnConnect::Packet(Box::new(LoginWelcome::new(server_key, client_key))), + OnConnect::Cipher((Box::new(PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, client_key)), + Box::new(PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, server_key)))) + ] + } + + fn handle(&mut self, pkt: &CharacterPacket) -> Box>> { + println!("[character: recv] {:?}", pkt); + match pkt { + CharacterPacket::Login(login) => { + self.validate_login(login) + }, + CharacterPacket::RequestSettings(_req) => { + self.get_settings() + } + + } + } +} + + + + + +pub fn new_client(socket: mio::tcp::TcpStream, shared_state: SharedLoginState) { + let state = CharacterServerState::new(shared_state); + let client = Client::new(socket, Box::new(state)); + client.io_loop(); +} diff --git a/src/login/login.rs b/src/login/login.rs new file mode 100644 index 0000000..28e99bc --- /dev/null +++ b/src/login/login.rs @@ -0,0 +1,169 @@ +use std::net; + +use rand::{Rng, RngCore}; +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::{PacketParseError, PSOPacket}; +use libpso::crypto::{CipherError, PSOCipher, NullCipher}; +use libpso::crypto::bb::PSOBBCipher; + +use elseware::pktvec; +use elseware::common::pktvec::PktVec; +use elseware::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY}; +use elseware::common::network::{PacketNetworkError}; +use elseware::common::client::Client; +use elseware::common::serverstate::{ServerPacket, ServerState, OnConnect}; +use elseware::common::util::array_to_utf8; + +use crate::models::UserAccount; + +pub const LOGIN_PORT: u16 = 12000; +type ConnectionPool = r2d2::Pool>; + +#[derive(Debug)] +pub enum LoginError { +} + + +#[derive(Debug)] +pub enum LoginPacket { + Login(Login), +} + +impl ServerPacket for LoginPacket { + fn from_bytes(data: &Vec) -> Result { + match data[2] { + 0x93 => Ok(LoginPacket::Login(Login::from_bytes(data)?)), + _ => Err(PacketParseError::WrongPacketForServerType) + } + } +} + +#[derive(Clone)] +pub struct SharedLoginState { + pub connection_pool: ConnectionPool, +} + +impl SharedLoginState { + pub fn new(pool: ConnectionPool) -> SharedLoginState { + SharedLoginState { + connection_pool: pool, + } + } +} + + +pub struct LoginServerState { + pub shared_state: SharedLoginState, +} + +pub fn get_login_status(connection_pool: &ConnectionPool, pkt: &Login) -> Result { + 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::(&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 { + true => Ok(user), + false => Err(AccountStatus::InvalidPassword), + } + }) + }) +} + +impl LoginServerState { + fn new(shared_state: SharedLoginState) -> LoginServerState { + LoginServerState { + shared_state: shared_state, + } + } + + fn validate_login(&mut self, pkt: &Login) -> Box { + match get_login_status(&self.shared_state.connection_pool, pkt) { + Ok(_user) => { + let response = LoginResponse::by_status(AccountStatus::Ok, pkt.security_data); + let ip = net::Ipv4Addr::new(127,0,0,1); + let ip = u32::from_ne_bytes(ip.octets()); + pktvec![response, RedirectClient::new(ip, crate::character::CHARACTER_PORT)] + }, + Err(err) => { + pktvec![LoginResponse::by_status(err, pkt.security_data)] + } + } + } +} + +impl ServerState for LoginServerState { + type Packet = LoginPacket; + type PacketError = LoginError; + + fn on_connect(&mut self) -> Vec { + let mut rng = rand::thread_rng(); + + let mut server_key = [0u8; 48]; + let mut client_key = [0u8; 48]; + rng.fill(&mut server_key[..]); + rng.fill(&mut client_key[..]); + + vec![OnConnect::Packet(Box::new(LoginWelcome::new(server_key, client_key))), + OnConnect::Cipher((Box::new(PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, client_key)), + Box::new(PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, server_key)))) + ] + } + + fn handle(&mut self, pkt: &LoginPacket) -> Box>> { + println!("[login: recv] {:?}", pkt); + match pkt { + LoginPacket::Login(login) => { + self.validate_login(login) + } + } + } +} + + + + +pub fn new_client(socket: mio::tcp::TcpStream, shared_state: SharedLoginState) { + let state = LoginServerState::new(shared_state); + let client = Client::new(socket, Box::new(state)); + client.io_loop(); +} diff --git a/src/login/main.rs b/src/login/main.rs index bc12c61..9dfea04 100644 --- a/src/login/main.rs +++ b/src/login/main.rs @@ -1,110 +1,79 @@ -use std::net::{TcpListener, SocketAddr, Ipv4Addr}; +mod login; +mod character; +mod models; + +use std::net::{SocketAddr, Ipv4Addr}; use std::net; use std::thread; +use std::env; +use mio::tcp::TcpListener; use mio::{Events, Poll, Token, Ready, PollOpt}; -use rand::{Rng, RngCore}; +use dotenv::dotenv; -use libpso::{PacketParseError, PSOPacket}; -use libpso::packet::login::*; -use libpso::crypto::{CipherError, PSOCipher, NullCipher}; -use libpso::crypto::bb::PSOBBCipher; +use diesel::r2d2; +use diesel::prelude::*; +use diesel::pg::PgConnection; -use elseware::common::network::{send_packet, recv_packet, PacketNetworkError}; -use elseware::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY}; +use models::{NewUser, UserAccount}; -const LOGIN_PORT: u16 = 12000; -const CHARACTER_PORT: u16 = 12001; +fn main() { + dotenv().ok(); + //let database_url = env::var("DATABASE_URL").unwrap(); + //let conn = PgConnection::establish(&database_url).unwrap(); -struct Client { - running: bool, - socket: mio::tcp::TcpStream, - cipher_in: Box, - cipher_out: Box, -} + //use elseware::schema::user_accounts::dsl::*; + //use elseware::schema::user_accounts::table; -impl Client { - fn new(socket: net::TcpStream) -> 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 {}), - } - } + /*let u = NewUser::new("hi".to_string(), "qwer".to_string()); + diesel::insert_into(user_accounts).values(&u).execute(&conn).unwrap(); + let u = NewUser::new("hi2".to_string(), "qwer".to_string()); + diesel::insert_into(user_accounts).values(&u).execute(&conn).unwrap();*/ - fn send(&mut self, pkt: &dyn PSOPacket) { - match send_packet(&mut self.socket, &mut *self.cipher_out, pkt) { - Ok(_) => { - println!("[login] send ({:?}): {:?}", self.socket, pkt); - }, - Err(err) => { - println!("[login] error sending packet to {:?}: {:?}", self.socket, err); - self.running = false; - } - } - } -} + println!("[login+character] starting server"); + + let database_url = env::var("DATABASE_URL").unwrap(); + let connection_manager = r2d2::ConnectionManager::::new(database_url); + let connection_pool = r2d2::Pool::builder() + .build(connection_manager).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 login_shared_state = login::SharedLoginState::new(connection_pool); -fn client_loop(mut client: Client) { - let poll = mio::Poll::new().unwrap(); - poll.register(&client.socket, Token(0), Ready::readable(), PollOpt::edge()).unwrap(); + let poll = Poll::new().unwrap(); + poll.register(&login_listener, Token(0), Ready::readable(), PollOpt::edge()).unwrap(); + poll.register(&character_listener, Token(1), 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); - println!("{:?}", pkt); - - match pkt { - Ok(pkt) => { - //handle_packet(&mut client, pkt).expect("could not handle packet"); - println!("[login] pkt: {:?}", pkt); - }, - Err(err) => { - println!("[login] error recv-ing packet with {:?}: {:?}", client.socket, err); - break; - } - } - } + for event in &events { + match event.token() { + Token(0) => { + login_listener.accept().map(|(socket, addr)| { + let shared_state_clone = login_shared_state.clone(); + thread::spawn(move || { + println!("[login] accepted connection: {}", addr); + login::new_client(socket, shared_state_clone); + }); + }); + }, + Token(1) => { + character_listener.accept().map(|(socket, addr)| { + let shared_state_clone = login_shared_state.clone(); + thread::spawn(move || { + println!("[character] accepted connection: {}", addr); + character::new_client(socket, shared_state_clone); + }); + }); + }, + _ => {} } } } } - -fn new_client(socket: net::TcpStream) { - let mut client = Client::new(socket); - let mut rng = rand::thread_rng(); - - let mut server_key = [0u8; 48]; - let mut client_key = [0u8; 48]; - rng.fill(&mut server_key[..]); - rng.fill(&mut client_key[..]); - let welcome = LoginWelcome::new(server_key, client_key); - client.send(&welcome); - client.cipher_in = Box::new(PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, client_key)); - client.cipher_out = Box::new(PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, server_key)); - client_loop(client); -} - -fn main() { - println!("[login] starting server"); - - let listener = TcpListener::bind(&SocketAddr::from((Ipv4Addr::new(0,0,0,0), LOGIN_PORT))).unwrap(); - - while let Ok((socket, addr)) = listener.accept() { - thread::spawn(move || { - println!("[login] accepted connection: {}", addr); - new_client(socket); - }); - } - - - println!("[login] exiting..."); -}