import Foundation struct IPAddressCheck { private var ipv4Request: URLRequest { let url = URL(string: "http://fritz.box:49000/igdupnp/control/WANIPConn1")! var request = URLRequest(url: url) request.setValue("text/xml; charset=utf-8", forHTTPHeaderField: "Content-Type") request.setValue("urn:schemas-upnp-org:service:WANIPConnection:1#GetExternalIPAddress", forHTTPHeaderField: "SOAPAction") let body = """ """ request.httpMethod = "POST" request.httpBody = body.data(using: .utf8)! return request } func determineIPv4() async throws -> String { let data: Data let response: URLResponse do { (data, response) = try await URLSession.shared.data(for: ipv4Request) } catch { throw DNSError.routerRequestFailedForIPv4(error) } guard let httpResponse = response as? HTTPURLResponse else { throw DNSError.invalidRouterResponseForIPv4 } guard httpResponse.statusCode == 200 else { throw DNSError.invalidRouterResponseCodeForIPv4(httpResponse.statusCode) } guard let responseBody = String(data: data, encoding: .utf8) else { throw DNSError.invalidRouterResponseBodyForIPv4 } let ipString = responseBody .components(separatedBy: "").last! .components(separatedBy: "").first! let parts = ipString.components(separatedBy: ".") guard parts.count == 4 else { throw DNSError.invalidRouterResponseIpForIPv4(ipString) } let numbers = parts.compactMap(Int.init) guard numbers.count == 4 else { throw DNSError.invalidRouterResponseIpForIPv4(ipString) } guard numbers.contains(where: { $0 != 0 }) else { throw DNSError.invalidRouterResponseIpForIPv4(ipString) } return ipString } private func execute(_ command: String) throws -> (code: Int, output: String) { let task = Process() let pipe = Pipe() task.standardOutput = pipe task.standardError = pipe task.arguments = ["-cl", command] task.executableURL = URL(fileURLWithPath: "/bin/bash") task.standardInput = nil try task.run() task.waitUntilExit() let code = task.terminationStatus let data = pipe.fileHandleForReading.readDataToEndOfFile() let output = String(data: data, encoding: .utf8)! return (Int(code), output) } func determineIPv6() throws -> String { #if os(Linux) let ipv6Command = "ip addr show" #elseif os(macOS) let ipv6Command = "ifconfig" #else fatalError("Unsupported OS") #endif let code: Int let output: String do { (code, output) = try execute(ipv6Command) } catch { throw DNSError.failedToPerformCommandForIPv6(error) } guard code == 0 else { throw DNSError.invalidCommandResultForIPv6(code, output) } let addresses: [String] = output .components(separatedBy: "inet6 ") .dropFirst() .compactMap { $0 .components(separatedBy: "/").first? .components(separatedBy: " ").first } .compactMap { let parts = $0.components(separatedBy: ":") guard parts.count == 8 else { return nil } return $0 } guard let ip = addresses.first else { throw DNSError.invalidIpAddressForIPv6(output) } return ip } }