feat: make hook redirect IP configurable from launcher

The DLL now reads openfut.cfg from its own directory on DLL_PROCESS_ATTACH
and uses the IP it contains as the redirect target instead of hardcoding
127.0.0.1. Falls back to 127.0.0.1 if the file is absent.

The launcher writes openfut.cfg alongside version.dll when deploying, and
the Setup tab exposes a "Redirect IP" field with an "Update" button that
rewrites openfut.cfg in-place without redeploying the DLL. Useful when
running the emulator on a different machine on the LAN.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
funman300
2026-06-25 21:08:44 -07:00
parent 9b5da57f12
commit dfa2c9afc7
6 changed files with 114 additions and 13 deletions
+32
View File
@@ -0,0 +1,32 @@
/// Reads openfut.cfg from the same directory as this DLL.
///
/// The file contains a single line: the IP the hook should redirect EA
/// hostnames to, e.g. "192.168.1.10" or "127.0.0.1".
/// Falls back to 127.0.0.1 if the file is missing or unreadable.
use windows_sys::Win32::System::LibraryLoader::GetModuleFileNameA;
pub fn read_redirect_ip(module: windows_sys::Win32::Foundation::HMODULE) -> String {
if let Some(cfg_path) = config_path(module) {
if let Ok(content) = std::fs::read_to_string(&cfg_path) {
let ip = content.trim().to_string();
if !ip.is_empty() {
return ip;
}
}
}
"127.0.0.1".to_string()
}
fn config_path(module: windows_sys::Win32::Foundation::HMODULE) -> Option<std::path::PathBuf> {
let mut buf = vec![0u8; 512];
let len = unsafe { GetModuleFileNameA(module, buf.as_mut_ptr(), buf.len() as u32) };
if len == 0 {
return None;
}
let path = std::ffi::CStr::from_bytes_until_nul(&buf[..len as usize + 1])
.ok()?
.to_str()
.ok()?;
let dll_path = std::path::Path::new(path);
Some(dll_path.parent()?.join("openfut.cfg"))
}
+13 -3
View File
@@ -1,4 +1,3 @@
/// Network hooks — redirect EA FUT hostnames to localhost.
use std::{ffi::CStr, sync::OnceLock};
use windows_sys::Win32::Networking::WinSock::{ADDRINFOA, getaddrinfo as sys_getaddrinfo};
@@ -17,11 +16,19 @@ type GetaddrinfoFn = unsafe extern "system" fn(
) -> i32;
static REAL: OnceLock<GetaddrinfoFn> = OnceLock::new();
// Stored as a NUL-terminated byte string so the hook can pass it to getaddrinfo.
static REDIRECT_IP: OnceLock<Vec<u8>> = OnceLock::new();
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); // NUL-terminate for passing to getaddrinfo
let _ = REDIRECT_IP.set(bytes);
}
pub unsafe extern "system" fn hooked_getaddrinfo(
node_name: *const u8,
service_name: *const u8,
@@ -32,9 +39,12 @@ pub unsafe extern "system" fn hooked_getaddrinfo(
if let Ok(host) = CStr::from_ptr(node_name as *const i8).to_str() {
for target in INTERCEPT {
if host.eq_ignore_ascii_case(target) {
let local = b"127.0.0.1\0";
let redirect = REDIRECT_IP
.get()
.map(|v| v.as_ptr())
.unwrap_or(b"127.0.0.1\0".as_ptr());
let real = REAL.get().copied().unwrap_or(sys_getaddrinfo);
return real(local.as_ptr(), service_name, hints, result);
return real(redirect, service_name, hints, result);
}
}
}
+8 -3
View File
@@ -1,3 +1,4 @@
mod config;
mod hooks;
mod iat;
@@ -9,17 +10,21 @@ use windows_sys::Win32::{
#[no_mangle]
pub unsafe extern "system" fn DllMain(
_module: HMODULE,
module: HMODULE,
reason: u32,
_reserved: *mut (),
) -> BOOL {
if reason == DLL_PROCESS_ATTACH {
install_hooks();
install_hooks(module);
}
TRUE
}
unsafe fn install_hooks() {
unsafe fn install_hooks(module: HMODULE) {
// Load redirect IP from openfut.cfg before patching
let ip = config::read_redirect_ip(module);
hooks::set_redirect_ip(ip);
let real_ptr = iat::resolve(b"ws2_32.dll\0", b"getaddrinfo\0");
if real_ptr.is_null() {
return;