diff --git a/Cargo.toml b/Cargo.toml index 4560be7..6b9409b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,6 @@ prost = "0.8" bytes = "1.1.0" base64 = "0.13.0" tokio = { version = "1", features = ["full"] } -actix-web = { version = "3", features = ["openssl"] } futures = "0.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" @@ -29,7 +28,7 @@ num-traits = "0.2" num-derive = "0.3" pretty-hex = "0.2" sea-orm = { version = "0.7", features = [ "sqlx-all", "runtime-async-std-native-tls", "debug-print" ] } -hostname = "0.3" +#hostname = "0.3" #local-ip-address = "0.4" chrono = "0.4" rand = "0.8" diff --git a/README.md b/README.md index f566501..164e92a 100644 --- a/README.md +++ b/README.md @@ -29,15 +29,9 @@ Clone repository with `git clone --recurse-submodules `. This is requi Look at the instructions in the `proto` project on how to get the required file set. -## Retrieving an SSL certificate and traffic encryption keys +## Retrieving traffic encryption keys -To generate an SSL certificate, you'll need `openssl` tool installed. - -- On *nix, use `misc/ssl_stuff/get_cert.sh` script -- On Windows, TODO - -To get the traffic encryption key, there're many possible ways, but the easiest one would be to generate them using -[Ec2b tool](https://github.com/Jasuf/Ec2b). Move `Ec2bSeed.bin` into `keys/master.ec2b` and `Ec2bKey.bin` into `keys/master.key`. +Refer to `Sapozhok`'s README about traffic encryption keys. Note that `RustySamovar` doesn't need SSL keys, only RSA and regional ones. ## Compiling @@ -47,7 +41,7 @@ Just plain and simple `cargo build`. ## Preparation -To run the game, you'll need some of the game's files: +To run the server, you'll need some of the game's files: - [Lua scripts](https://github.com/14eyes/YSLua), grab them from `DecompiledLua/Lua` subdirectory and put into `data/lua/` subfolder of the server @@ -58,16 +52,6 @@ To run the game, you'll need some of the game's files: Alternatively you can dump everything by yourself using tools available at Bublik. -## Redirecting the game's traffic to the server - -The simplest method is by modifying the `hosts` file. Copy the contents from the provided file into your system-wide one. -Note that you'll need to comment those lines as soon as you'll want to play on the official servers or access official -resources (like web events or daily login rewards). - ## Starting the server -Just `cargo run` but with a caveat. Server listens on privileged ports (80, 443), so it needs permissions for that. - -- On Windows, UAC prompt should automatically pop up and ask you to elevate server's priviledges. If it's not happening, run the server's - executable as admin. -- On *nix, you'll need to grant the server the specific capability. You can do it by running `sudo setcap 'cap_net_bind_service=+ep' ./target/debug/RustySamovar`. **Please don't run the server as root!** +Just `cargo run` will do the trick. diff --git a/src/entitymanager/entities.rs b/src/entitymanager/entities.rs index 10ca306..bb71e29 100644 --- a/src/entitymanager/entities.rs +++ b/src/entitymanager/entities.rs @@ -1,4 +1,3 @@ -use actix_web::web::Json; use std::collections::HashMap; use std::sync::Arc; diff --git a/src/main.rs b/src/main.rs index 1e031d6..634bb16 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,6 @@ mod entitymanager; mod subsystems; use server::NetworkServer; -use server::DispatchServer; use dbmanager::DatabaseManager; use jsonmanager::JsonManager; use luamanager::LuaManager; @@ -32,12 +31,6 @@ fn main() { .with_test_writer() .init(); - thread::spawn(|| { - //let mut ds = DispatchServer::new("127.0.0.1", 9696); - let mut ds = DispatchServer::new(); - ds.run(); - }); - let mut ns = NetworkServer::new("0.0.0.0", 4242).unwrap(); ns.run().expect("Failed to serve!"); } diff --git a/src/server/auth_manager.rs b/src/server/auth_manager.rs index 568215b..84c093b 100644 --- a/src/server/auth_manager.rs +++ b/src/server/auth_manager.rs @@ -13,7 +13,6 @@ use crate::server::IpcMessage; use packet_processor_macro::*; #[macro_use] use packet_processor::*; -use crate::DispatchServer; #[packet_processor(GetPlayerTokenReq)] pub struct AuthManager { @@ -69,7 +68,7 @@ impl AuthManager { let key_id = req.unk4 as u8; - let rsa_key_collection = DispatchServer::load_rsa_keys("RSAConfig"); + let rsa_key_collection = mhycrypt::load_rsa_keys("RSAConfig", "keys"); let keys = match rsa_key_collection.get(&key_id) { Some(keys) => keys, None => panic!("Unknown key ID {}!", key_id), diff --git a/src/server/dispatch_server.rs b/src/server/dispatch_server.rs deleted file mode 100644 index 8a0d6b3..0000000 --- a/src/server/dispatch_server.rs +++ /dev/null @@ -1,810 +0,0 @@ -use std::io::prelude::*; -use std::net::TcpListener; -use std::net::TcpStream; -use std::collections::HashMap; -use std::fs; -use std::sync::Arc; -use std::fs::read_to_string; // use instead of std::fs::File -use std::path::Path; - -extern crate futures; -extern crate base64; -extern crate actix_web; -extern crate openssl; - -use serde::{de, Deserialize, Deserializer, Serialize}; -use serde::de::Error; -use futures::executor; - -use actix_web::{rt::System, web, get, App, HttpRequest, HttpResponse, HttpServer, Responder, middleware::Logger}; -use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslVerifyMode, SslOptions, SslMode}; -use openssl::rsa::{Rsa, Padding}; -use openssl::symm::Cipher; -use openssl::sha::Sha256; -use rand::{distributions::Alphanumeric, Rng}; - -use prost::Message; - -use mhycrypt; -use openssl::hash::MessageDigest; -use openssl::pkey::{PKey, Private, Public}; -use openssl::sign::Signer; -use pretty_env_logger::env_logger::fmt; -use serde::de::Unexpected; -//use openssl::rand; - -#[derive(Clone)] -pub struct DispatchServer {} - -// Keys stuff -#[derive(Deserialize,Debug)] -pub struct KeyInfo { - pub key_id: u8, - #[serde(deserialize_with = "deserialize_priv_key")] - pub encrypt_key: Rsa, - #[serde(deserialize_with = "deserialize_priv_key")] - pub signing_key: Rsa, -} - -fn deserialize_pub_key<'de, D>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, -{ - let public_key_pem: String = Deserialize::deserialize(deserializer)?; - - Rsa::public_key_from_pem(public_key_pem.as_bytes()).map_err(D::Error::custom) -} - -fn deserialize_priv_key<'de, D>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, -{ - let private_key_pem: String = Deserialize::deserialize(deserializer)?; - - Rsa::private_key_from_pem(private_key_pem.as_bytes()).map_err(D::Error::custom) -} - -#[derive(Deserialize,Debug)] -struct ClientInfo { - version: String, - lang: i32, - platform: i32, - binary: i32, - time: i32, - channel_id: i32, - sub_channel_id: i32, - account_type: Option, - key_id: Option, -} - -#[derive(Deserialize,Debug)] -struct TokenToVerify -{ - uid: String, - token: String, -} - -#[derive(Deserialize,Debug)] -struct ActionToCheck -{ - action_type: String, - api_name: String, - username: Option, -} - -#[derive(Deserialize,Debug)] -struct LoginData { - account: String, - is_crypto: bool, - password: String, -} -/* -#[derive(Deserialize,Debug)] -struct GranterData { - app_id: String, - channel_id: String, - device: String, - sign: String, - data: String, -}*/ - -#[derive(Deserialize,Debug)] -struct GranterData { - #[serde(deserialize_with = "deserialize_u32_or_string")] - app_id: u32, - #[serde(deserialize_with = "deserialize_u32_or_string")] - channel_id: u32, - device: String, - sign: String, - data: String, -} - -/* Deserialization hack */ -fn deserialize_u32_or_string<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, -{ - #[derive(Deserialize)] - #[serde(untagged)] - enum StrOrU32<'a> { - Str(&'a str), - U32(u32), - } - - Ok(match StrOrU32::deserialize(deserializer)? { - StrOrU32::Str(v) => v.parse().unwrap(), // Ignoring parsing errors - StrOrU32::U32(v) => v, - }) -} - -#[derive(Deserialize,Debug)] -struct MinorApiLogData { - data: String, -} - -#[derive(Deserialize,Debug)] -struct GeetestGetData { - gt: String, - challenge: String, - lang: String, - is_next: Option, - client_type: Option, - w: Option, - pt: Option, - callback: Option, -} - -#[derive(Deserialize,Debug)] -struct GeetestGetTypeData { - gt: String, - t: u64, - callback: Option, -} - -#[derive(Deserialize,Debug)] -struct GeetestAjaxData { - gt: String, - challenge: String, - client_type: Option, - w: Option, - callback: Option, - #[serde(rename = "$_BBF")] - BBF: Option, -} - -impl DispatchServer { - pub fn new() -> DispatchServer { - let ds = DispatchServer { }; - - return ds; - } - - pub fn run(self) { - let mut sys = System::new("http-server"); - let slef = Arc::new(self); - executor::block_on(slef.run_internal()); - System::current().stop(); - println!("Finished!"); - } - - async fn run_internal(self: &Arc) { - //let (http_port, https_port) = (2880, 2443); - println!("Hostname {}, local IP {}", DispatchServer::get_hostname(), DispatchServer::get_local_ip()); - - let (http_port, https_port) = (80, 443); - - let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls_server()).unwrap(); - //builder.set_verify(SslVerifyMode::NONE); - //builder.set_min_proto_version(None).unwrap(); - //builder.set_cipher_list("DEFAULT").unwrap(); - //builder.set_mode(SslMode::NO_AUTO_CHAIN | SslMode::SEND_FALLBACK_SCSV); - builder.set_private_key_file("keys/ssl.key", SslFiletype::PEM).unwrap(); - builder.set_certificate_chain_file("keys/ssl.cer").unwrap(); - - let http_server = HttpServer::new(move || { - App::new() - .wrap(Logger::default()) - .route("/", web::get().to(|| HttpResponse::Ok())) - .route("/query_security_file", web::get().to(DispatchServer::query_security_file)) - .route("/query_region_list", web::get().to(DispatchServer::query_region_list)) - .route("/query_cur_region", web::get().to(DispatchServer::query_cur_region)) - //.route("", web::post().to(DispatchServer::)) - .route("/hk4e_global/mdk/shield/api/verify", web::post().to(DispatchServer::shield_verify)) - //.route("/account/risky/api/check", web::post().to(DispatchServer::risky_api_check)) - .route("/account/risky/api/check", web::post().to(DispatchServer::risky_api_check_old)) - .route("/hk4e_global/mdk/shield/api/login", web::post().to(DispatchServer::shield_login)) - .route("/hk4e_global/combo/granter/login/v2/login", web::post().to(DispatchServer::granter_login)) - // Misc stuff, not really required - .route("/common/h5log/log/batch", web::post().to(DispatchServer::minor_api_log)) - - .route("/combo/box/api/config/sdk/combo", web::get().to(DispatchServer::combo_combo)) - .route("/hk4e_global/combo/granter/api/getConfig", web::get().to(DispatchServer::get_config)) - .route("/hk4e_global/mdk/shield/api/loadConfig", web::get().to(DispatchServer::load_config)) - //.route("/hk4e_global/combo/granter/api/getFont", web::get().to(DispatchServer::get_font)) - .route("/hk4e_global/mdk/agreement/api/getAgreementInfos", web::get().to(DispatchServer::get_agreement_infos)) - .route("/admin/mi18n/plat_oversea/m2020030410/m2020030410-version.json", web::get().to(DispatchServer::version_data)) - .route("/hk4e_global/combo/granter/api/compareProtocolVersion", web::post().to(DispatchServer::compare_protocol_version)) - // GEETEST - .route("/get.php", web::get().to(DispatchServer::geetest_get)) - .route("/gettype.php", web::get().to(DispatchServer::geetest_get_type)) - .route("/ajax.php", web::get().to(DispatchServer::geetest_ajax_get)) - .route("/ajax.php", web::post().to(DispatchServer::geetest_ajax_post)) - // Logging - .route("/log/sdk/upload", web::post().to(DispatchServer::log_skip)) - .route("/sdk/dataUpload", web::post().to(DispatchServer::log_skip)) - .route("/crash/dataUpload", web::post().to(DispatchServer::log_skip)) - - }) - .bind(format!("0.0.0.0:{}", http_port)).expect("Failed to bind HTTP port") - .bind_openssl(format!("0.0.0.0:{}", https_port), builder).expect("Failed to bind HTTPS port") - .run(); - - http_server.stop(true).await; - } - - async fn query_security_file() -> String { - return "".to_string(); - } - - async fn query_region_list(c: web::Query) -> String { - println!("RegionList, Client: {:?}", c); - - let keys = DispatchServer::load_keys("master"); - - let mut region_info = proto::RegionSimpleInfo::default(); - region_info.name = "ps_rusty".into(); - region_info.title = "Rusty Samovar".into(); - region_info.r#type = "DEV_PUBLIC".into(); - //region_info.dispatch_url = format!("http://{}:{}/query_cur_region", DispatchServer::get_hostname(), 80); - region_info.dispatch_url = format!("http://127.0.0.1:{}/query_cur_region", 80); - - let mut region_list = proto::QueryRegionListHttpRsp::default(); - region_list.region_list = vec![region_info]; - region_list.enable_login_pc = true; - - region_list.client_secret_key = keys.0.clone(); - - let json_config = "{\"sdkenv\":\"2\",\"checkdevice\":\"false\",\"loadPatch\":\"false\",\"showexception\":\"false\",\"regionConfig\":\"pm|fk|add\",\"downloadMode\":\"0\"}"; - - let mut custom_config = json_config.as_bytes().to_owned(); - - mhycrypt::mhy_xor(&mut custom_config, &keys.1); - - region_list.client_custom_config_encrypted = custom_config.to_vec(); - - let mut region_list_buf = Vec::new(); - - region_list.encode(&mut region_list_buf).unwrap(); - - return base64::encode(region_list_buf); - } - - async fn query_cur_region(c: web::Query) -> String { - println!("CurRegion, Client: {:?}", c); - - let keys = DispatchServer::load_keys("master"); - - let mut region_info = proto::RegionInfo::default(); - region_info.gateserver_ip = DispatchServer::get_local_ip(); - region_info.gateserver_port = 4242; - region_info.secret_key = keys.0.clone(); - - let mut region_config = proto::QueryCurrRegionHttpRsp::default(); - region_config.region_info = Some(region_info); - region_config.client_secret_key = keys.0.clone(); - - let json_config = format!("{{\"coverSwitch\": [\"8\"], \"perf_report_config_url\": \"http://{}:{}/config/verify\", \"perf_report_record_url\": \"http://{}:{}/dataUpload\" }}", - DispatchServer::get_hostname(), 80, DispatchServer::get_hostname(), 80); - - let mut custom_config = json_config.as_bytes().to_owned(); - - mhycrypt::mhy_xor(&mut custom_config, &keys.1); - - region_config.region_custom_config_encrypted = custom_config.to_vec(); - - let mut region_conf_buf = Vec::new(); - - region_config.encode(&mut region_conf_buf).unwrap(); - - if c.0.version.contains("2.7.5") || c.0.version.contains("2.8.") {// TODO: use proper version check! - let key_id = match c.0.key_id { - Some(key_id) => key_id, - None => panic!("Client version >= 2.7.50, but it haven't provided key_id!"), - }; - let rsa_key_collection = DispatchServer::load_rsa_keys("RSAConfig"); - let keys = match rsa_key_collection.get(&key_id) { - Some(keys) => keys, - None => panic!("Unknown key ID {}!", key_id), - }; - - const key_size: usize = 256; // TODO: hardcoded constant! - - let mut out_buf: Vec = Vec::new(); - let mut enc_buf: Vec = vec![0; key_size]; - - for chunk in region_conf_buf.chunks((key_size - 11) as usize) { // TODO: value hardcoded for the PKCS1 v1.5! - let len = keys.encrypt_key.public_encrypt(chunk, &mut enc_buf, Padding::PKCS1).unwrap(); - out_buf.append(&mut enc_buf[0..len].to_vec()); - enc_buf.resize(key_size, 0); - } - - let keypair = PKey::from_rsa(keys.signing_key.clone()).unwrap(); - let mut signer = Signer::new(MessageDigest::sha256(), &keypair).unwrap(); - let signature = signer.sign_oneshot_to_vec(®ion_conf_buf).unwrap(); - - return format!(" - {{ - \"content\": \"{}\", - \"sign\": \"{}\" - }} - ", base64::encode(out_buf), base64::encode(signature)); - } else { - return base64::encode(region_conf_buf); - } - } - - async fn risky_api_check_old(a: web::Json) -> String { - println!("Action: {:?}", a); - - let email = "ceo@hoyolab.com"; - let name = "Ceo"; - let token = Self::generate_fake_token(); - let uid = 0x1234; - - let payload = DispatchServer::build_account_data(email, name, &token, uid); - - return DispatchServer::make_answer(0, &payload); - } - - async fn risky_api_check(a: web::Json) -> String { - println!("Action: {:?}", a); - - let challenge = "5876e8bb6d90e0d6cf4dd26b109fe508"; - let gt = "16bddce04c7385dbb7282778c29bba3e"; - let id = "a0f5968aa4664b55ac914bffa1cd8058"; - - let payload = format!(" - {{ - \"action\": \"ACTION_GEETEST\", - \"geetest\": {{ - \"challenge\": \"{}\", - \"gt\": \"{}\", - \"new_captcha\": 1, - \"success\": 1 - }}, - \"id\": \"{}\" - }} - ", challenge, gt, id); - - return DispatchServer::make_answer(0, &payload); - } - - async fn shield_login(l: web::Json) -> String { - println!("Login: {:?}", l); - - let email = "ceo@hoyolab.com"; - let name = "Ceo"; - let token = Self::generate_fake_token(); - let uid = 0x1234; - - let payload = DispatchServer::build_account_data(email, name, &token, uid); - - return DispatchServer::make_answer(0, &payload); - } - - async fn granter_login(g: web::Json) -> String { - println!("Granter: {:?}", g); - - let payload = DispatchServer::verify_token_v2(); - - return DispatchServer::make_answer(0, &payload); - } - - async fn combo_combo() -> String { - - let payload = format!("{{ - \"vals\": {{ - \"disable_email_bind_skip\": \"false\", - \"email_bind_remind\": \"true\", - \"email_bind_remind_interval\": \"7\" - }} - }}"); - - return DispatchServer::make_answer(0,&payload); - } - - async fn get_config() -> String { - let payload = format!("{{ - \"announce_url\": \"https://localhost/hk4e/announcement/index.html\", - \"disable_ysdk_guard\": false, - \"enable_announce_pic_popup\": true, - \"log_level\": \"INFO\", - \"protocol\": true, - \"push_alias_type\": 2, - \"qr_enabled\": false - }}"); - - return DispatchServer::make_answer(0,&payload); - } - - async fn load_config() -> String { - let payload = format!("{{ - \"client\": \"PC\", - \"disable_mmt\": false, - \"disable_regist\": false, - \"enable_email_captcha\": false, - \"enable_ps_bind_account\": false, - \"game_key\": \"hk4e_global\", - \"guest\": false, - \"id\": 6, - \"identity\": \"I_IDENTITY\", - \"ignore_versions\": \"\", - \"name\": \"原神海外\", - \"scene\": \"S_NORMAL\", - \"server_guest\": false, - \"thirdparty\": [ - \"fb\", - \"tw\" - ], - \"thirdparty_ignore\": {{ - \"fb\": \"\", - \"tw\": \"\" - }} - }}"); - return DispatchServer::make_answer(0,&payload); - } - - async fn shield_verify(t: web::Json) -> String { - println!("Token: {:?}", t); - - let email = "ceo@hoyolab.com"; - let name = "Ceo"; - let token = t.token.clone(); - let uid = t.uid.parse().unwrap(); - - let payload = DispatchServer::build_account_data(email, name, &token, uid); - - return DispatchServer::make_answer(0, &payload); - } - - async fn minor_api_log(l: web::Json) -> String { - return "{\"retcode\":0,\"message\":\"success\",\"data\":null}".to_string(); - } - - /* - GEETEST - */ - async fn geetest_get(g: web::Query) -> String { - println!("GeetestGet: {:?}", g); - - let is_next = match g.is_next { - None => false, - Some(_) => true, - }; - - if (is_next) { - let callback = g.callback.as_ref().unwrap(); - - return format!(" - {}( {{ - \"gt\": \"{}\", - \"challenge\": \"{}\", - \"id\": \"a7b56e21f6771ab10e2bc4a3a511c4be0\", - \"bg\": \"pictures/gt/1dce8a0cd/bg/744f986a0.jpg\", - \"fullbg\": \"pictures/gt/1dce8a0cd/1dce8a0cd.jpg\", - \"link\": \"\", - \"ypos\": 85, - \"xpos\": 0, - \"height\": 160, - \"slice\": \"pictures/gt/1dce8a0cd/slice/744f986a0.png\", \ - \"api_server\": \"https://api-na.geetest.com/\", - \"static_servers\": [\"static.geetest.com/\", \"dn-staticdown.qbox.me/\"], - \"mobile\": true, - \"theme\": \"ant\", - \"theme_version\": \"1.2.6\", - \"template\": \"\", - \"logo\": false, - \"clean\": false, - \"type\": \"multilink\", - \"fullpage\": false, - \"feedback\": \"\", - \"show_delay\": 250, - \"hide_delay\": 800, - \"benchmark\": false, - \"version\": \"6.0.9\", - \"product\": \"embed\", - \"https\": true, - \"width\": \"100%\", - \"c\": [12, 58, 98, 36, 43, 95, 62, 15, 12], - \"s\": \"6b70592c\", - \"so\": 0, - \"i18n_labels\": {{ - \"cancel\": \"Cancel\", - \"close\": \"Close\", - \"error\": \"Error. Close and retry.\", - \"fail\": \"Incorrect position\", - \"feedback\": \"Info\", - \"forbidden\": \"Retry after 3 seconds\", - \"loading\": \"Loading\", - \"logo\": \"Geetest\", - \"read_reversed\": false, - \"refresh\": \"Refresh\", - \"slide\": \"Slide to unlock\", - \"success\": \"sec s. You're better than score% of users\", - \"tip\": \"\", - \"voice\": \"Voice test\" - }}, - \"gct_path\": \"/static/js/gct.d0a2919ae56f007ecb8e22fb47f80f33.js\" - }} )", callback, g.gt, g.challenge); - } else { - let data = " - ( { - \"status\": \"success\", - \"data\": { - \"theme\": \"wind\", - \"theme_version\": \"1.5.8\", - \"static_servers\": [\"static.geetest.com\", \"dn-staticdown.qbox.me\"], - \"api_server\": \"api-na.geetest.com\", - \"logo\": false, - \"feedback\": \"\", - \"c\": [12, 58, 98, 36, 43, 95, 62, 15, 12], - \"s\": \"3f6b3542\", - \"i18n_labels\": { - \"copyright\": \"Geetest\", - \"error\": \"Error\", - \"error_content\": \"Retry\", - \"error_title\": \"Timeout\", - \"fullpage\": \"Confirm\", - \"goto_cancel\": \"Cancel\", - \"goto_confirm\": \"OK\", - \"goto_homepage\": \"Go to Geetest homepage?\", - \"loading_content\": \"Confirm\", - \"next\": \"Loaging\", - \"next_ready\": \"Not fulfilled\", - \"read_reversed\": false, - \"ready\": \"Click to confirm\", - \"refresh_page\": \"Error. Refresh the page to continue.\", - \"reset\": \"Retry\", - \"success\": \"Success\", - \"success_title\": \"Success\" - } - } - }) - "; - - return match g.callback.as_ref() { - None => data.to_string(), - Some(callback) => format!( - "{}{}", - callback, data), - } - } - } - - async fn geetest_get_type(gt: web::Query) -> String { - println!("GeetestGetType: {:?}", gt); - - let data = "\ - ( { - \"status\": \"success\", - \"data\": { - \"type\": \"fullpage\", - \"static_servers\": [\"static.geetest.com/\", \"dn-staticdown.qbox.me/\"], - \"click\": \"/static/js/click.3.0.2.js\", - \"pencil\": \"/static/js/pencil.1.0.3.js\", - \"voice\": \"/static/js/voice.1.2.0.js\", - \"fullpage\": \"/static/js/fullpage.9.0.8.js\", - \"beeline\": \"/static/js/beeline.1.0.1.js\", - \"slide\": \"/static/js/slide.7.8.6.js\", - \"geetest\": \"/static/js/geetest.6.0.9.js\", - \"aspect_radio\": { - \"slide\": 103, \"click\": 128, \"voice\": 128, \"pencil\": 128, \"beeline\": 50 - } - } - }) - "; - - return match >.callback { - None => data.to_string(), - Some(callback) => format!( - "{}{}", - callback, data), - }; - } - - async fn geetest_ajax_get(ga: web::Query) -> String { - return Self::geetest_ajax(ga.into_inner()).await; - } - - async fn geetest_ajax_post(ga: web::Json) -> String { - return Self::geetest_ajax(ga.into_inner()).await; - } - - async fn geetest_ajax(ga: GeetestAjaxData) -> String { - println!("GeetestAjax: {:?}", ga); - - let is_next = match ga.BBF { - None => false, - Some(_) => true, - }; - - if (is_next) { - let callback = ga.callback.as_ref().unwrap(); - - return format!(" - {}( {{ - \"success\": 1, - \"message\": \"success\", - \"validate\": \"\", - \"score\": \"11\" - }} )", callback); - } else { - let data = " - { - \"status\": \"success\", - \"data\": { - \"result\": \"slide\" - } - } - "; - - return match ga.callback.as_ref() { - None => data.to_string(), - Some(callback) => format!( - "{}( - {} - )", - callback, data), - } - } - } - - async fn log_skip(body: web::Bytes) -> String { - println!("Logging: {}", std::str::from_utf8(&body).unwrap()); - - return "{}".to_string(); - } - - async fn get_agreement_infos() -> String { - let payload = format!("{{ - \"marketing_agreements\": [] - }}"); - - return DispatchServer::make_answer(0, &payload); - } - - async fn compare_protocol_version() -> String { - let payload = format!("{{ - \"modified\": true, - \"protocol\": {{ - \"app_id\": 4, - \"create_time\": \"0\", - \"id\": 0, - \"language\": \"ru\", - \"major\": 4, - \"minimum\": 0, - \"priv_proto\": \"\", - \"teenager_proto\": \"\", - \"user_proto\": \"\" - }} - }}"); - - return DispatchServer::make_answer(0, &payload); - } - - async fn version_data() -> String { - return "{\"version\": 54}".to_string(); - } - - fn get_hostname() -> String { - return hostname::get().unwrap().into_string().unwrap(); - //return "localhost"; - } - - fn get_local_ip() -> String { - //return local_ip_address::local_ip().unwrap().to_string(); - return "127.0.0.1".to_string(); - } - - pub fn load_rsa_keys(name: &str) -> HashMap { - // Key depo - let path = format!("./{}/{}.json", "keys", name); - let json_file_path = Path::new(&path); - let json_file_str = read_to_string(json_file_path).unwrap_or_else(|_| panic!("File {} not found", path)); - let data: Vec = serde_json::from_str(&json_file_str).expect(&format!("Error while reading json {}", name)); - - data.into_iter().map(|ki| (ki.key_id, ki)).collect() - } - - fn load_keys(name: &str) -> (Vec, Vec) { - // Key - let filename = format!("./{}/{}.key", "keys", name); - let mut f = fs::File::open(&filename).expect(&format!("File '{}' not found", filename)); - let metadata = fs::metadata(&filename).expect("unable to read metadata"); - let mut key = vec![0; metadata.len() as usize]; - f.read(&mut key).expect("buffer overflow"); - // Ec2b - let filename = format!("./{}/{}.ec2b", "keys", name); - let mut f = fs::File::open(&filename).expect(&format!("File '{}' not found", filename)); - let metadata = fs::metadata(&filename).expect("unable to read metadata"); - let mut ec2b = vec![0; metadata.len() as usize]; - f.read(&mut ec2b).expect("buffer overflow"); - return (ec2b, key); - } - - fn verify_token_v2() -> String { - let account_type = 1; - let combo_id = 0x4321; - let open_id = 0x1234; - - #[cfg(not(feature = "raw_packet_dump"))] - let combo_token = Self::generate_fake_token(); - #[cfg(feature = "raw_packet_dump")] - let combo_token = std::str::from_utf8(&[32u8; 4096*3]).unwrap(); - - return format!("{{ - \"account_type\": \"{}\", - \"combo_id\": \"{}\", - \"combo_token\": \"{}\", - \"data\": {{\"guest\": \"false\"}}, - \"heartbeat\": false, - \"open_id\": \"{}\" - }}", account_type, combo_id, combo_token, open_id); - } - - fn build_account_data(email: &str, name: &str, token: &str, uid: i32) -> String { - let payload = format!("{{ - \"account\": {{ - \"apple_name\": \"\", - \"area_code\": \"**\", - \"country\": \"US\", - \"device_grant_ticket\": \"\", - \"email\": \"{}\", - \"facebook_name\": \"\", - \"game_center_name\": \"\", - \"google_name\": \"\", - \"identity_card\": \"\", - \"is_email_verify\": \"0\", - \"mobile\": \"\", - \"name\": \"{}\", - \"reactivate_ticket\": \"\", - \"realname\": \"\", - \"safe_mobile\": \"\", - \"sony_name\": \"\", - \"tap_name\": \"\", - \"token\": \"{}\", - \"twitter_name\": \"\", - \"uid\": \"{}\" - }}, - \"device_grant_required\": \"false\", - \"realperson_required\": \"false\", - \"realname_operation\": \"None\", - \"realperson_required\": false, - \"safe_moblie_required\": \"false\" - }}", email, name, token, uid); - - return payload.into(); - } - - fn make_answer(code: i32, data: &str) -> String { - let message = match code { - 0 => "OK", - -1 => "not matched", - _ => "ERROR", - }; - - return format!("{{ - \"retcode\": \"{}\", - \"message\": \"{}\", - \"data\": {} - }}", code, message, data).to_string(); - } - - fn generate_fake_token() -> String { - return rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(32) - .map(char::from) - .collect(); - } -} diff --git a/src/server/mod.rs b/src/server/mod.rs index 94972ae..b82347a 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -5,7 +5,6 @@ mod auth_manager; mod login_manager; mod client_connection; mod ipc_message; -mod dispatch_server; pub use self::network_server::NetworkServer; pub use self::game_server::GameServer; @@ -14,4 +13,3 @@ pub use self::auth_manager::AuthManager; pub use self::login_manager::LoginManager; pub use self::client_connection::ClientConnection; pub use self::ipc_message::IpcMessage; -pub use self::dispatch_server::DispatchServer;