Files
openfut-launcher/openfut-hook/src/hooks.rs
T
funman300 4e44a3792f wip(hook): preserve local FIFA17 configurable-server redirect + hook refactors
Preserved uncommitted openfut-hook WIP from the feat/launcher-arming checkout so the
canonical launcher can move to the reconciled merge ca7ce26 without losing it. This is
WORK-IN-PROGRESS (not production-polished): a configurable OpenFUT-server redirect for the
injected hook — new src/server.rs (server::set / sin_addr / dest_port_nbo_from_source_nbo,
referenced by lib.rs + connect_hook + connectex_hook) plus supporting changes to config.rs,
connect_hook.rs, connectex_hook.rs, hooks.rs, iat.rs, lib.rs, origin_spy.rs, ssl_patch.rs,
tls_bypass.rs, Cargo.{toml,lock}.

NOTE: this overlaps (same files, different approach) the SBC-tracing lineage's openfut-hook
changes now merged in ca7ce26; reconciling the two hook variants is a separate, deliberate
task. This commit only PRESERVES the local WIP on a dedicated branch.
2026-08-13 15:10:12 +00:00

96 lines
3.8 KiB
Rust

use std::{
ffi::CStr,
sync::{
atomic::{AtomicBool, Ordering},
OnceLock,
},
};
use windows_sys::Win32::Networking::WinSock::{getaddrinfo as sys_getaddrinfo, ADDRINFOA};
type GetaddrinfoFn =
unsafe extern "system" fn(*const u8, *const u8, *const ADDRINFOA, *mut *mut ADDRINFOA) -> i32;
static REAL: OnceLock<GetaddrinfoFn> = OnceLock::new();
// NUL-terminated dotted-quad of the resolved OpenFUT server, built once at init
// from the SAME shared config the socket hooks use. getaddrinfo redirects EA
// hostnames here so DNS resolves to the configured server. If configuration was
// missing/invalid this stays empty and EA hostnames are NOT redirected (no
// loopback fallback).
static REDIRECT_HOST: OnceLock<Vec<u8>> = OnceLock::new();
// Flipped to true the first time we successfully apply the runtime cert patch.
// The patch is deferred to here (rather than DllMain) because EAWebKit.dll may
// not be loaded yet when the hook DLL is injected.
static CERT_PATCHED: AtomicBool = AtomicBool::new(false);
pub fn set_real(f: GetaddrinfoFn) {
let _ = REAL.set(f);
}
/// Install the resolved redirect IPv4 (dotted-quad) getaddrinfo will hand back
/// for EA hostnames. Called once at init from the shared resolved server.
pub fn set_redirect_ip(ip: std::net::Ipv4Addr) {
let mut bytes = ip.to_string().into_bytes();
bytes.push(0);
let _ = REDIRECT_HOST.set(bytes);
}
/// Returns true if `host` is an EA / EA-Sports domain that should be redirected
/// to the local OpenFUT bridge.
fn is_ea_host(host: &str) -> bool {
let h = host.to_ascii_lowercase();
h.ends_with(".ea.com")
|| h == "ea.com"
|| h.ends_with(".easports.com")
|| h == "easports.com"
|| h.ends_with(".ugc.footapi.com")
|| h.ends_with(".footapi.com")
}
pub unsafe extern "system" fn hooked_getaddrinfo(
node_name: *const u8,
service_name: *const u8,
hints: *const ADDRINFOA,
result: *mut *mut ADDRINFOA,
) -> i32 {
if !node_name.is_null() {
if let Ok(host) = CStr::from_ptr(node_name as *const i8).to_str() {
crate::write_log(&format!("openfut_hook: getaddrinfo({host})\n"));
if is_ea_host(host) {
// Apply the ProtoSSL cert-verify bypass the first time we see an EA
// hostname — EAWebKit.dll must be loaded by now because it's calling us.
if !CERT_PATCHED.load(Ordering::Relaxed) {
if crate::ssl_patch::patch_eawebkit_cert_verify() {
CERT_PATCHED.store(true, Ordering::Relaxed);
crate::write_log(
"openfut_hook: ProtoSSL cert-verify patched (lazy, from getaddrinfo)\n",
);
} else {
crate::write_log(
"openfut_hook: ProtoSSL cert-verify patch FAILED in getaddrinfo\n",
);
}
}
// Only redirect when a server was configured & resolved. If not,
// fall through to the real resolver — we never invent a loopback
// destination here.
match REDIRECT_HOST.get() {
Some(redirect) => {
let real = REAL.get().copied().unwrap_or(sys_getaddrinfo);
return real(redirect.as_ptr(), service_name, hints, result);
}
None => {
crate::write_log(
"openfut_hook: EA host seen but no OpenFUT server configured — NOT redirecting\n",
);
}
}
}
}
}
let real = REAL.get().copied().unwrap_or(sys_getaddrinfo);
real(node_name, service_name, hints, result)
}