import Foundation import Observation @Observable @MainActor final class TTLManager { private(set) var currentTTL: Int = 64 private(set) var currentIPv6HopLimit: Int = 64 private(set) var isProtected: Bool = false private(set) var lastError: String? var onStatusChanged: (() -> Void)? let defaultTTL: Int = 64 let targetTTL: Int = 65 init() { refreshStatus() } /// Reads the current system IPv4 TTL value using sysctlbyname (no root required) func readCurrentTTL() -> Int { var ttl: Int32 = 0 var size = MemoryLayout.size let result = sysctlbyname("net.inet.ip.ttl", &ttl, &size, nil, 0) if result == 0 { return Int(ttl) } return defaultTTL } /// Reads the current system IPv6 Hop Limit value using sysctlbyname (no root required) func readCurrentIPv6HopLimit() -> Int { var hlim: Int32 = 0 var size = MemoryLayout.size let result = sysctlbyname("net.inet6.ip6.hlim", &hlim, &size, nil, 0) if result == 0 { return Int(hlim) } return defaultTTL } /// Refreshes current IPv4 TTL, IPv6 Hop Limit, and protection status func refreshStatus() { currentTTL = readCurrentTTL() currentIPv6HopLimit = readCurrentIPv6HopLimit() isProtected = (currentTTL == targetTTL) && (currentIPv6HopLimit == targetTTL) lastError = nil onStatusChanged?() } func didSetTTL(_ value: Int) { currentTTL = value currentIPv6HopLimit = value isProtected = value == targetTTL lastError = nil onStatusChanged?() } func setError(_ message: String) { lastError = message onStatusChanged?() } }