Fix: stderr-Pipe gegen Instant-Exit-Datenverlust, TCP-Vorab-Check, Fehler-Banner; Icon (Dracula) im Bundle
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user