curtain/Sources/CurtainShared/NetstatParse.swift
Aric Camarata 8c19e960d2 Detection root-cause fix + audit batch: netstat path, UDP activator, settings coherence, refactor, docs
Detection: netstat lives at /usr/sbin/netstat, not /usr/bin — the hardcoded wrong
path silently killed the ESTABLISHED-TCP activator (root cause of the failed live
test). Fixed and live-verified. Added peered-UDP activator (5900-5902) for
High-Performance sessions, per-signal transition logging, unconditional error
logging for dead probe helpers, and probe v2 with full CGSession dictionary
diffing. 7 new parser tests (32 total).

Fixes from a full audit + adversarial review: idle source setting honored
(default now Remote session activity), cover scope reduced to a coherent
two-mode model with legacy migration (per-display toggle was inverted in
onlyMarked and dead in all), curtain test no longer schedules a teardown over a
live session, specific-display password box placement gets a real picker,
refuse-to-arm enforced, activation notification posts a real banner, menu
password gate bypassed when the event tap is dead, shared single-decoder aerial
player with stale-task guard and async playability check, password buffer zeroed
on successful unlock and Esc, XPC interruption/invalidation handlers, modern
Accessibility settings URL, launchPath modernized, codesign failures now abort
release.sh, monotonic CFBundleVersion, install.sh temp cleanup, dead
armDisarmHotkey setting removed.

Refactor: Curtain.swift and PreferencesWindow.swift split into focused files
(largest now 479 lines). Wiki, README, and contributing docs updated to match.
Build clean at 0 warnings, 32/32 tests pass.
2026-06-09 20:36:30 -04:00

64 lines
3.2 KiB
Swift

import Foundation
/// Purpose: decide, from `netstat -an` output text, whether there is a genuine
/// inbound Screen Sharing connection: an ESTABLISHED TCP session on local
/// port 5900 (classic VNC) or a peered UDP socket on 5900-5902 (the
/// Sequoia/26 High-Performance transport). A LISTEN socket on 5900 is
/// always present whenever Screen Sharing is enabled, and wildcard UDP
/// listeners can linger, so neither alone proves anything; only a row
/// with a real foreign peer (not `*.*`) counts.
public enum NetstatParse {
/// Returns true when the netstat output contains an ESTABLISHED inbound TCP
/// session on local port 5900 with a real peer.
public static func hasEstablishedVNC(_ netstatOutput: String) -> Bool {
for raw in netstatOutput.split(separator: "\n") {
let line = String(raw)
let fields = line.split(separator: " ", omittingEmptySubsequences: true).map(String.init)
// netstat -an TCP columns: proto recv-q send-q local-address foreign-address state
guard fields.count >= 6 else { continue }
guard fields[0].lowercased().hasPrefix("tcp") else { continue }
let local = fields[3]
let foreign = fields[4]
let state = fields[5]
// Local side must be our 5900 listener accepting the inbound connection.
guard local.hasSuffix(".5900") else { continue }
// Must be an established connection with a real peer, not a LISTEN socket.
guard state == "ESTABLISHED" else { continue }
guard isRealPeer(foreign) else { continue }
return true
}
return false
}
/// Returns true when the netstat output contains a UDP socket on local port
/// 5900-5902 that is connected to a real foreign peer. High-Performance Screen
/// Sharing (macOS 14+, Apple silicon) streams over UDP, so there is no
/// ESTABLISHED TCP row to find but an active session shows as a peered UDP
/// socket. A wildcard foreign address (`*.*`) is just a listener and never
/// counts, so this cannot fire at rest.
public static func hasPeeredUDPVNC(_ netstatOutput: String) -> Bool {
for raw in netstatOutput.split(separator: "\n") {
let line = String(raw)
let fields = line.split(separator: " ", omittingEmptySubsequences: true).map(String.init)
// netstat -an UDP columns: proto recv-q send-q local-address foreign-address
guard fields.count >= 5 else { continue }
guard fields[0].lowercased().hasPrefix("udp") else { continue }
let local = fields[3]
let foreign = fields[4]
guard local.hasSuffix(".5900") || local.hasSuffix(".5901") || local.hasSuffix(".5902") else { continue }
guard isRealPeer(foreign) else { continue }
return true
}
return false
}
/// A foreign address is a real peer only when it names an actual remote
/// host+port, not the `*.*` / `host.*` wildcard forms a listener shows.
private static func isRealPeer(_ foreign: String) -> Bool {
foreign != "*.*" && !foreign.hasSuffix(".*")
}
}