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 = OnceLock::new(); static REDIRECT_IP: OnceLock> = 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); } pub fn set_redirect_ip(ip: String) { let mut bytes = ip.into_bytes(); bytes.push(0); let _ = REDIRECT_IP.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")); // Milestone-0 transport watch (self-gates on OPENFUT_TRANSPORT_WATCH). crate::transport_watch::note_getaddrinfo(host); 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", ); } } let redirect = REDIRECT_IP .get() .map(|v| v.as_ptr()) .unwrap_or(c"127.0.0.1".as_ptr().cast()); let real = REAL.get().copied().unwrap_or(sys_getaddrinfo); return real(redirect, service_name, hints, result); } } } let real = REAL.get().copied().unwrap_or(sys_getaddrinfo); real(node_name, service_name, hints, result) }