SOURCE RECORD · S08

S08 · VPN TP

DNS TCP and TLS diagnostic implementations

公开摘录 · 截取原件于 2026-09-12 / Captured 2026-09-12

保留原行号。个人/账户信息如有删除,已用 REDACTED 标识。原文件 SHA-256 只用于识别截取时的本机原文件;不能证明编写或提交时间,也不等于脱敏下载文件的哈希。

Original line numbers are retained. Redactions are explicitly marked. The original full-file hash identifies a captured local file, not its creation/submission time or the redacted download. Public-file hashes are listed separately. This is selected source, not a whole-repository or binary absence finding.

Project key: vpn
Original relative path: ConfigStudio/NetworkChecks/NetworkCheckServices.swift
Captured: 2026-09-12T18:57:15+08:00
Original full-file SHA256: 18b76ffb1c396aacd39fe050153fbb168c5d545afba06890c8266b6a3ff7fa8a
Evidence level: local source excerpt
Build correspondence: not yet established with an Apple-received binary.
Line numbers refer to the captured original file. Gaps are explicitly marked. No source code was changed.

Original lines 1 to 86

0001  import Foundation
0002  import Network
0003  import Security
0004  
0005  struct DNSLookupResult { var ipv4: [String]; var ipv6: [String]; var duration: TimeInterval }
0006  struct TCPCheckResult { var reachable: Bool; var duration: TimeInterval; var error: String? }
0007  struct TLSCertificateResult {
0008      var subject: String; var issuer: String; var validFrom: Date?; var validUntil: Date?; var remainingDays: Int?
0009      var domainMatches: Bool; var trusted: Bool; var chainSummary: [String]
0010  }
0011  
0012  protocol DNSLookupService { func lookup(host: String) async throws -> DNSLookupResult }
0013  protocol TCPPortCheckService { func check(host: String, port: Int, timeout: TimeInterval) async throws -> TCPCheckResult }
0014  protocol TLSCertificateCheckService { func check(host: String, port: Int, timeout: TimeInterval) async throws -> TLSCertificateResult }
0015  
0016  struct LocalDNSLookupService: DNSLookupService {
0017      func lookup(host: String) async throws -> DNSLookupResult {
0018          try await withCheckedThrowingContinuation { continuation in
0019              DispatchQueue.global(qos: .userInitiated).async {
0020                  let start = Date(); var hints = addrinfo(ai_flags: AI_ADDRCONFIG, ai_family: AF_UNSPEC, ai_socktype: SOCK_STREAM, ai_protocol: 0, ai_addrlen: 0, ai_canonname: nil, ai_addr: nil, ai_next: nil)
0021                  var result: UnsafeMutablePointer<addrinfo>?
0022                  guard getaddrinfo(host, nil, &hints, &result) == 0, let first = result else { continuation.resume(throwing: AppError.dnsFailure); return }
0023                  defer { freeaddrinfo(result) }
0024                  var ipv4: [String] = [], ipv6: [String] = [], pointer: UnsafeMutablePointer<addrinfo>? = first
0025                  while let info = pointer?.pointee {
0026                      var buffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))
0027                      if getnameinfo(info.ai_addr, info.ai_addrlen, &buffer, socklen_t(buffer.count), nil, 0, NI_NUMERICHOST) == 0 {
0028                          let value = String(cString: buffer)
0029                          if info.ai_family == AF_INET { ipv4.append(value) } else if info.ai_family == AF_INET6 { ipv6.append(value) }
0030                      }
0031                      pointer = info.ai_next
0032                  }
0033                  continuation.resume(returning: DNSLookupResult(ipv4: Array(Set(ipv4)).sorted(), ipv6: Array(Set(ipv6)).sorted(), duration: Date().timeIntervalSince(start)))
0034              }
0035          }
0036      }
0037  }
0038  
0039  private final class CompletionGate<Value>: @unchecked Sendable {
0040      private let lock = NSLock(); private var completed = false
0041      func finish(_ block: () -> Void) { lock.lock(); defer { lock.unlock() }; guard !completed else { return }; completed = true; block() }
0042  }
0043  
0044  struct LocalTCPPortCheckService: TCPPortCheckService {
0045      func check(host: String, port: Int, timeout: TimeInterval = 5) async throws -> TCPCheckResult {
0046          guard let nwPort = NWEndpoint.Port(rawValue: UInt16(port)) else { throw AppError.invalidPort }
0047          return try await withCheckedThrowingContinuation { continuation in
0048              let start = Date(), connection = NWConnection(host: NWEndpoint.Host(host), port: nwPort, using: .tcp), gate = CompletionGate<TCPCheckResult>()
0049              connection.stateUpdateHandler = { state in
0050                  switch state {
0051                  case .ready: gate.finish { continuation.resume(returning: .init(reachable: true, duration: Date().timeIntervalSince(start), error: nil)); connection.cancel() }
0052                  case .failed(let error): gate.finish { continuation.resume(returning: .init(reachable: false, duration: Date().timeIntervalSince(start), error: SecureLogger.redact(error.localizedDescription))); connection.cancel() }
0053                  default: break
0054                  }
0055              }
0056              connection.start(queue: .global(qos: .userInitiated))
0057              DispatchQueue.global().asyncAfter(deadline: .now() + timeout) { gate.finish { connection.cancel(); continuation.resume(throwing: AppError.tcpTimeout) } }
0058          }
0059      }
0060  }
0061  
0062  struct LocalTLSCertificateCheckService: TLSCertificateCheckService {
0063      func check(host: String, port: Int = 443, timeout: TimeInterval = 8) async throws -> TLSCertificateResult {
0064          guard let nwPort = NWEndpoint.Port(rawValue: UInt16(port)) else { throw AppError.invalidPort }
0065          return try await withCheckedThrowingContinuation { continuation in
0066              let tls = NWProtocolTLS.Options(), gate = CompletionGate<TLSCertificateResult>()
0067              sec_protocol_options_set_tls_server_name(tls.securityProtocolOptions, host)
0068              sec_protocol_options_set_verify_block(tls.securityProtocolOptions, { _, trustObject, complete in
0069                  let trust = sec_trust_copy_ref(trustObject).takeRetainedValue()
0070                  SecTrustSetPolicies(trust, SecPolicyCreateSSL(true, host as CFString))
0071                  var error: CFError?; let trusted = SecTrustEvaluateWithError(trust, &error)
0072                  let result = certificateResult(trust: trust, trusted: trusted)
0073                  complete(trusted)
0074                  gate.finish { continuation.resume(returning: result) }
0075              }, .global(qos: .userInitiated))
0076              let parameters = NWParameters(tls: tls, tcp: NWProtocolTCP.Options())
0077              let connection = NWConnection(host: NWEndpoint.Host(host), port: nwPort, using: parameters)
0078              connection.stateUpdateHandler = { state in
0079                  if case .failed = state { gate.finish { continuation.resume(throwing: AppError.tlsFailure) } }
0080                  if case .ready = state { connection.cancel() }
0081              }
0082              connection.start(queue: .global(qos: .userInitiated))
0083              DispatchQueue.global().asyncAfter(deadline: .now() + timeout) { gate.finish { connection.cancel(); continuation.resume(throwing: AppError.tlsFailure) } }
0084          }
0085      }
0086  }