Files
proxypin/ios/Runner/Handlers/MethodHandler.swift
2025-09-21 20:09:40 +08:00

176 lines
8.0 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// MethodHandler.swift
// Runner
//
// Created by wanghongen on 2025/5/30.
//
import Flutter
import Network
import SystemConfiguration.CaptiveNetwork
import Security
public class MethodHandler: NSObject, FlutterPlugin {
public static let name = "com.proxypin/method"
private var channel: FlutterMethodChannel?
private var currentPathMonitor: NWPathMonitor?
private var currentCompletionHandler: ((Bool) -> Void)?
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: Self.name, binaryMessenger: registrar.messenger())
let instance = MethodHandler()
registrar.addMethodCallDelegate(instance, channel: channel)
instance.channel = channel
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "requestLocalNetwork":
//
self.requestLocalNetworkAccess { isAvailable in
print("[MethodHandler] requestLocalNetwork result: \(isAvailable)")
result(isAvailable)
}
case "isCaInstalled":
guard let args = call.arguments as? [String: Any], let pem = args["pem"] as? String else {
print("[MethodHandler] isCaInstalled ARG_ERROR: missing pem")
result(FlutterError(code: "ARG_ERROR", message: "Missing pem", details: nil))
return
}
let ret = self.isCertificateInstalled(pem: pem)
result(ret)
case "evaluateChainTrusted":
guard let args = call.arguments as? [String: Any], let leafPem = args["leafPem"] as? String, let caPem = args["caPem"] as? String else {
print("[MethodHandler] evaluateChainTrusted ARG_ERROR: missing leafPem/caPem")
result(FlutterError(code: "ARG_ERROR", message: "Missing leafPem/caPem", details: nil))
return
}
let host = args["host"] as? String
let ret = self.isChainTrusted(leafPem: leafPem, caPem: caPem, host: host)
// print("[MethodHandler] evaluateChainTrusted => \(ret)")
result(ret)
default:
print("[MethodHandler] method not implemented: \(call.method)")
result(FlutterMethodNotImplemented)
}
}
// MARK: - iOS: Check certificate trust
private func isCertificateInstalled(pem: String) -> Bool {
guard let der = self.decodePemToDer(pem) as CFData?, let certificate = SecCertificateCreateWithData(nil, der) else {
print("[MethodHandler] isCertificateTrusted decode/create cert failed")
return false
}
let policy = SecPolicyCreateBasicX509()
var trust: SecTrust?
let status = SecTrustCreateWithCertificates(certificate, policy, &trust)
if status != errSecSuccess || trust == nil {
print("[MethodHandler] SecTrustCreateWithCertificates failed status=\(status)")
return false
}
if #available(iOS 12.0, *) {
var error: CFError?
let ok = SecTrustEvaluateWithError(trust!, &error)
if let e = error {
print("[MethodHandler] SecTrustEvaluateWithError ok=\(ok) error=\(e)")
}
return ok
} else {
var trustResult = SecTrustResultType.invalid
let evalStatus = SecTrustEvaluate(trust!, &trustResult)
let ok = (evalStatus == errSecSuccess) && (trustResult == .unspecified || trustResult == .proceed)
print("[MethodHandler] SecTrustEvaluate status=\(evalStatus) result=\(trustResult.rawValue) trusted=\(ok)")
return ok
}
}
// MARK: - iOS: Evaluate leaf+CA chain with SSL policy
private func isChainTrusted(leafPem: String, caPem: String, host: String?) -> Bool {
guard let leafDer = self.decodePemToDer(leafPem) as CFData?, let leaf = SecCertificateCreateWithData(nil, leafDer) else {
print("[MethodHandler] isChainTrusted leaf decode/create failed")
return false
}
guard let caDer = self.decodePemToDer(caPem) as CFData?, let ca = SecCertificateCreateWithData(nil, caDer) else {
print("[MethodHandler] isChainTrusted ca decode/create failed")
return false
}
let certs: [SecCertificate] = [leaf, ca]
let policy = SecPolicyCreateSSL(true, host as CFString?)
var trust: SecTrust?
let status = SecTrustCreateWithCertificates(certs as CFTypeRef, policy, &trust)
if status != errSecSuccess || trust == nil {
print("[MethodHandler] isChainTrusted SecTrustCreateWithCertificates failed status=\(status)")
return false
}
if #available(iOS 12.0, *) {
var error: CFError?
let ok = SecTrustEvaluateWithError(trust!, &error)
if let e = error { print("[MethodHandler] isChainTrusted evaluate ok=\(ok) error=\(e)") } else { print("[MethodHandler] isChainTrusted evaluate ok=\(ok)") }
return ok
} else {
var trustResult = SecTrustResultType.invalid
let evalStatus = SecTrustEvaluate(trust!, &trustResult)
let ok = (evalStatus == errSecSuccess) && (trustResult == .unspecified || trustResult == .proceed)
// print("[MethodHandler] isChainTrusted evaluate status=\(evalStatus) result=\(trustResult.rawValue) trusted=\(ok)")
return ok
}
}
private func decodePemToDer(_ pem: String) -> Data? {
// Strip header/footer and whitespace
let lines = pem.components(separatedBy: "\n").filter { line in
return !line.contains("-----BEGIN") && !line.contains("-----END") && !line.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
}
let base64Str = lines.joined()
let der = Data(base64Encoded: base64Str, options: .ignoreUnknownCharacters)
return der
}
/// Wi-Fi
/// - Parameter completion: Bool true false
func requestLocalNetworkAccess(completion: @escaping (Bool) -> Void) {
//
self.currentPathMonitor?.cancel()
self.currentPathMonitor = NWPathMonitor()
// completion 便 pathUpdateHandler
// completion
self.currentCompletionHandler = completion
self.currentPathMonitor?.pathUpdateHandler = { [weak self] path in
guard let self = self else { return }
// completionHandler
guard let completionHandler = self.currentCompletionHandler else {
//
//
self.currentPathMonitor?.cancel()
return
}
var isLocalNetworkAvailable = false
print("Network path status: \(path.status)")
if path.status == .satisfied {
if path.usesInterfaceType(.wifi) || path.usesInterfaceType(.wiredEthernet) {
isLocalNetworkAvailable = true
}
}
// ( .unsatisfied, .requiresConnection) ( cellular),
// isLocalNetworkAvailable false
// completion handler
completionHandler(isLocalNetworkAvailable)
//
self.currentPathMonitor?.cancel()
self.currentPathMonitor = nil
self.currentCompletionHandler = nil
}
//
self.currentPathMonitor?.start(queue: DispatchQueue.global())
}
}