66 lines
2.5 KiB
Swift
66 lines
2.5 KiB
Swift
import Foundation
|
|
import Network
|
|
|
|
/// Schneller TCP-Vorab-Check, BEVOR ssh gestartet wird.
|
|
/// Falscher Port / falscher Host / Firewall → sofort klare Meldung
|
|
/// statt „Sitzung beendet (Code 255)" ohne Erklärung.
|
|
enum TCPCheck {
|
|
private final class Flag {
|
|
var done = false
|
|
}
|
|
|
|
/// - completion: nil bei Erfolg, sonst deutsche Fehlermeldung.
|
|
static func check(host: String, port: Int, timeout: TimeInterval = 4.0, completion: @escaping (String?) -> Void) {
|
|
let h = host.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
guard !h.isEmpty else { completion("Kein Host angegeben — Profil bearbeiten."); return }
|
|
guard (1...65535).contains(port), let p = NWEndpoint.Port(rawValue: UInt16(port)) else {
|
|
completion("Ungültiger Port \(port)."); return
|
|
}
|
|
let conn = NWConnection(host: NWEndpoint.Host(h), port: p, using: .tcp)
|
|
let lock = NSLock()
|
|
let flag = Flag()
|
|
func finish(_ message: String?) {
|
|
lock.lock()
|
|
defer { lock.unlock() }
|
|
guard !flag.done else { return }
|
|
flag.done = true
|
|
conn.cancel()
|
|
completion(message)
|
|
}
|
|
conn.stateUpdateHandler = { state in
|
|
switch state {
|
|
case .ready:
|
|
finish(nil)
|
|
case .failed(let err):
|
|
finish(describe(err, host: h, port: port))
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
conn.start(queue: .global(qos: .userInitiated))
|
|
DispatchQueue.global().asyncAfter(deadline: .now() + timeout) {
|
|
finish("Keine Antwort in \(Int(timeout)) s — Hostname/IP, Netzwerk/VPN oder Firewall prüfen.")
|
|
}
|
|
}
|
|
|
|
static func describe(_ error: NWError, host: String, port: Int) -> String {
|
|
switch error {
|
|
case .posix(let code):
|
|
switch code.rawValue {
|
|
case 61:
|
|
return "Port \(port) auf \(host) verweigert die Verbindung — läuft SSH dort? Stimmt der Port?"
|
|
case 51, 64, 65:
|
|
return "Host \(host) nicht erreichbar — Netzwerk/VPN prüfen."
|
|
case 60:
|
|
return "Zeitüberschreitung — Host/Port/Firewall prüfen."
|
|
default:
|
|
return "Netzwerkfehler (\(code.rawValue)): \(error.localizedDescription)"
|
|
}
|
|
case .dns:
|
|
return "Hostname \(host) kann nicht aufgelöst werden — Tippfehler? DNS/VPN prüfen."
|
|
default:
|
|
return error.localizedDescription
|
|
}
|
|
}
|
|
}
|