From f7c73a14a6055d82c54e6f8afc802d182f0861b6 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Fri, 11 Jul 2025 21:39:15 -0400 Subject: [PATCH] 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. --- resources/Caddyfile | 29 +++++++++++++++++++++++- src/bin/kawari-patch.rs | 49 ++++++++++++++++++++++++++++++++--------- 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/resources/Caddyfile b/resources/Caddyfile index c0d2f70..bac2895 100644 --- a/resources/Caddyfile +++ b/resources/Caddyfile @@ -1,5 +1,13 @@ { auto_https off + servers { + protocols h1 + } + log default { + output stdout + format json + include http.log.access + } } admin.ffxiv.localhost:80 { @@ -23,11 +31,29 @@ frontier.ffxiv.localhost:80 { } patch-bootver.ffxiv.localhost:80 { + log reverse_proxy :6900 + + # to match retail + header { + Server nginx + -Via + Connection keep-alive + ETag f0ad4edf149fce33b842f1104bedfd22 + } } patch-gamever.ffxiv.localhost:80 { + log reverse_proxy :6900 + + # to match retail + header { + Server nginx + -Via + Connection keep-alive + ETag f0ad4edf149fce33b842f1104bedfd22 + } } ffxiv-login.square.localhost:80 { @@ -35,6 +61,7 @@ ffxiv-login.square.localhost:80 { } patch-dl.ffxiv.localhost:80 { + log root * ./patches - file_server browse + file_server } diff --git a/src/bin/kawari-patch.rs b/src/bin/kawari-patch.rs index 77b42d7..49158bc 100644 --- a/src/bin/kawari-patch.rs +++ b/src/bin/kawari-patch.rs @@ -2,7 +2,7 @@ use std::cmp::Ordering; use std::fs::read_dir; use axum::extract::Path; -use axum::http::{HeaderMap, StatusCode}; +use axum::http::{HeaderMap, StatusCode, Uri}; use axum::response::IntoResponse; use axum::routing::post; use axum::{Router, routing::get}; @@ -157,9 +157,24 @@ async fn verify_boot( let config = get_config(); if !config.supports_platform(&platform) { + tracing::warn!("Invalid platform! {platform}"); 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 { 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 boot_version == SUPPORTED_BOOT_VERSION { - let headers = HeaderMap::new(); return (headers).into_response(); } @@ -189,6 +203,7 @@ async fn verify_boot( // check if we need any patching let mut send_patches = Vec::new(); let patches = list_patch_files(&format!("{}/boot", &config.patch.patches_location)); + let mut patch_length = 0; for patch in patches { let patch_str: &str = &patch; 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) .to_string(), version: patch_str.to_string(), - hash_block_size: 50000000, + hash_block_size: 0, length: metadata.len() as i64, size_on_disk: metadata.len() as i64, // NOTE: wrong but it should be fine to lie hashes: vec![], - unknown_a: 0, - unknown_b: 0, + unknown_a: 19, + unknown_b: 18, }); + patch_length += metadata.len(); } } if !send_patches.is_empty() { + headers.insert( + "Content-Type", + "multipart/mixed; boundary=477D80B1_38BC_41d4_8B48_5273ADB89CAC" + .parse() + .unwrap(), + ); + let patch_list = PatchList { id: "477D80B1_38BC_41d4_8B48_5273ADB89CAC".to_string(), 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 - patch_length: 0, + content_location: format!("ffxivpatch/2b5cbc63/metainfo/{}.http", boot_version.0), // FIXME: i think this is actually supposed to be the target version + patch_length, patches: send_patches, }; 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() } +async fn fallback(uri: Uri) -> (StatusCode, String) { + tracing::warn!("{}", uri); + (StatusCode::NOT_FOUND, format!("No route for {uri}")) +} + #[tokio::main] async fn main() { tracing_subscriber::fmt::init(); @@ -242,7 +270,8 @@ async fn main() { .route( "/http/{platform}/{channel}/{boot_version}/", get(verify_boot), - ); + ) + .fallback(fallback); let config = get_config();