1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-07-13 17:07:45 +00:00

Various fixes to make serving patches work

Only tested against sqexPatch.dll so far, but this helps retail in
general. More correct-looking headers are sent, logging is increased
(to help debugging) and other fixes for boot patch verification.
This commit is contained in:
Joshua Goins 2025-07-11 21:39:15 -04:00
parent eb2e7a9987
commit f7c73a14a6
2 changed files with 67 additions and 11 deletions

View file

@ -1,5 +1,13 @@
{ {
auto_https off auto_https off
servers {
protocols h1
}
log default {
output stdout
format json
include http.log.access
}
} }
admin.ffxiv.localhost:80 { admin.ffxiv.localhost:80 {
@ -23,11 +31,29 @@ frontier.ffxiv.localhost:80 {
} }
patch-bootver.ffxiv.localhost:80 { patch-bootver.ffxiv.localhost:80 {
log
reverse_proxy :6900 reverse_proxy :6900
# to match retail
header {
Server nginx
-Via
Connection keep-alive
ETag f0ad4edf149fce33b842f1104bedfd22
}
} }
patch-gamever.ffxiv.localhost:80 { patch-gamever.ffxiv.localhost:80 {
log
reverse_proxy :6900 reverse_proxy :6900
# to match retail
header {
Server nginx
-Via
Connection keep-alive
ETag f0ad4edf149fce33b842f1104bedfd22
}
} }
ffxiv-login.square.localhost:80 { ffxiv-login.square.localhost:80 {
@ -35,6 +61,7 @@ ffxiv-login.square.localhost:80 {
} }
patch-dl.ffxiv.localhost:80 { patch-dl.ffxiv.localhost:80 {
log
root * ./patches root * ./patches
file_server browse file_server
} }

View file

@ -2,7 +2,7 @@ use std::cmp::Ordering;
use std::fs::read_dir; use std::fs::read_dir;
use axum::extract::Path; use axum::extract::Path;
use axum::http::{HeaderMap, StatusCode}; use axum::http::{HeaderMap, StatusCode, Uri};
use axum::response::IntoResponse; use axum::response::IntoResponse;
use axum::routing::post; use axum::routing::post;
use axum::{Router, routing::get}; use axum::{Router, routing::get};
@ -157,9 +157,24 @@ async fn verify_boot(
let config = get_config(); let config = get_config();
if !config.supports_platform(&platform) { if !config.supports_platform(&platform) {
tracing::warn!("Invalid platform! {platform}");
return StatusCode::INTERNAL_SERVER_ERROR.into_response(); return StatusCode::INTERNAL_SERVER_ERROR.into_response();
} }
// TODO: these are all very useful and should be documented somewhere
let mut headers = HeaderMap::new();
headers.insert(
"Content-Location",
"ffxivpatch/2b5cbc63/vercheck.dat".parse().unwrap(),
);
headers.insert(
"X-Repository",
"ffxivneo/win32/release/boot".parse().unwrap(),
);
headers.insert("X-Patch-Module", "ZiPatch".parse().unwrap());
headers.insert("X-Protocol", "http".parse().unwrap());
headers.insert("X-Latest-Version", boot_version.parse().unwrap());
if config.enforce_validity_checks { if config.enforce_validity_checks {
tracing::info!("Verifying boot components for {platform} {channel} {boot_version}..."); tracing::info!("Verifying boot components for {platform} {channel} {boot_version}...");
@ -174,7 +189,6 @@ async fn verify_boot(
// If we are up to date, yay! // If we are up to date, yay!
if boot_version == SUPPORTED_BOOT_VERSION { if boot_version == SUPPORTED_BOOT_VERSION {
let headers = HeaderMap::new();
return (headers).into_response(); return (headers).into_response();
} }
@ -189,6 +203,7 @@ async fn verify_boot(
// check if we need any patching // check if we need any patching
let mut send_patches = Vec::new(); let mut send_patches = Vec::new();
let patches = list_patch_files(&format!("{}/boot", &config.patch.patches_location)); let patches = list_patch_files(&format!("{}/boot", &config.patch.patches_location));
let mut patch_length = 0;
for patch in patches { for patch in patches {
let patch_str: &str = &patch; let patch_str: &str = &patch;
if actual_boot_version.partial_cmp(patch_str).unwrap() == Ordering::Less { if actual_boot_version.partial_cmp(patch_str).unwrap() == Ordering::Less {
@ -203,33 +218,46 @@ async fn verify_boot(
url: format!("http://{}/boot/{}.patch", config.patch.patch_dl_url, patch) url: format!("http://{}/boot/{}.patch", config.patch.patch_dl_url, patch)
.to_string(), .to_string(),
version: patch_str.to_string(), version: patch_str.to_string(),
hash_block_size: 50000000, hash_block_size: 0,
length: metadata.len() as i64, length: metadata.len() as i64,
size_on_disk: metadata.len() as i64, // NOTE: wrong but it should be fine to lie size_on_disk: metadata.len() as i64, // NOTE: wrong but it should be fine to lie
hashes: vec![], hashes: vec![],
unknown_a: 0, unknown_a: 19,
unknown_b: 0, unknown_b: 18,
}); });
patch_length += metadata.len();
} }
} }
if !send_patches.is_empty() { if !send_patches.is_empty() {
headers.insert(
"Content-Type",
"multipart/mixed; boundary=477D80B1_38BC_41d4_8B48_5273ADB89CAC"
.parse()
.unwrap(),
);
let patch_list = PatchList { let patch_list = PatchList {
id: "477D80B1_38BC_41d4_8B48_5273ADB89CAC".to_string(), id: "477D80B1_38BC_41d4_8B48_5273ADB89CAC".to_string(),
requested_version: boot_version.to_string().clone(), requested_version: boot_version.to_string().clone(),
content_location: format!("ffxivpatch/boot/metainfo/{}.http", boot_version.0), // FIXME: i think this is actually supposed to be the target version content_location: format!("ffxivpatch/2b5cbc63/metainfo/{}.http", boot_version.0), // FIXME: i think this is actually supposed to be the target version
patch_length: 0, patch_length,
patches: send_patches, patches: send_patches,
}; };
let patch_list_str = patch_list.to_string(PatchListType::Boot); let patch_list_str = patch_list.to_string(PatchListType::Boot);
return patch_list_str.into_response(); dbg!(&patch_list_str);
return (headers, patch_list_str).into_response();
} }
} }
let headers = HeaderMap::new();
(headers).into_response() (headers).into_response()
} }
async fn fallback(uri: Uri) -> (StatusCode, String) {
tracing::warn!("{}", uri);
(StatusCode::NOT_FOUND, format!("No route for {uri}"))
}
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
tracing_subscriber::fmt::init(); tracing_subscriber::fmt::init();
@ -242,7 +270,8 @@ async fn main() {
.route( .route(
"/http/{platform}/{channel}/{boot_version}/", "/http/{platform}/{channel}/{boot_version}/",
get(verify_boot), get(verify_boot),
); )
.fallback(fallback);
let config = get_config(); let config = get_config();