Upptäcka ett aktivt VPN i en sandlådad Mac-app
Det finns inget publikt API för den här frågan, och tricket med gränssnittsnamnet är bara en heuristik. Sex kontroller för att avgöra vad din app egentligen bör förlita sig på.
Du behöver en skärm som beter sig annorlunda när tunneln är uppe, och det finns inget anrop som talar om det för dig. Det är utgivningsbegränsningen: på macOS rapporterar inget publikt ramverk om ett godtyckligt VPN är anslutet, och på iOS tillhör statusen appen som installerade konfigurationen. Kontrollerna nedan går från den gränsen till något du kan släppa, och vilken som helst av dem kan avsluta sökningen tidigt.
Kontroll 1: går frågan överhuvudtaget att besvara?
Sandlådan avgör frågan innan något API gör det. En tredjepartsprocess kan inte räkna upp en annan leverantörs tillägg och kan inte heller läsa det tilläggets konfiguration, så det finns inget stött objekt att fråga.
Varje kringgående möter samma mur. Gränssnittslistor, scutil --nc list och direkta läsningar av systemkonfigurationen kräver antingen behörigheter som sandlådan håller tillbaka eller bygger på beteende som Apple aldrig lovade. Om planen behöver något av detta är planen problemet. En funktion byggd på en odokumenterad detalj är en supportbiljett med leveransdatum.
Kontroll 2: hanterar din app tunneln själv?
Om den gör det har du ett stött svar för din egen konfiguration. Läs in inställningarna, hantera felet, läs sedan statusen och prenumerera på ändringar i stället för att polla:
vpnManager.loadFromPreferences { error in
// handle a load failure before reading status
}
NotificationCenter.default.addObserver(
forName: .NEVPNStatusDidChange,
object: nil,
queue: nil
) { note in
let status = (note.object as? NEVPNConnection)?.status
}Var precis om vad detta returnerar: tillståndet för den tunnel du installerade. En användare som kör en annan klient visas som frånkopplad, och det är ingen bugg i din kod. Bestäm vilken av de två frågorna din funktion egentligen ställer innan du bygger på svaret.
Kontroll 3: vad skulle en heuristik egentligen säga dig?
Det vitt kopierade tillvägagångssättet frågar systemets proxyordbok efter dess avgränsade poster och läser nycklarna, som råkar vara gränssnittsnamn:
let settings = CFNetworkCopySystemProxySettings()?.takeRetainedValue() as NSDictionary?
let scoped = settings?["__SCOPED__"] as? [String: Any]
let names = scoped?.keys ?? []En nyckel som börjar med tap, tun, ppp, ipsec eller utun antyder att ett tunnelavgränsat gränssnitt finns. Att matcha hela namnet misslyckas direkt, eftersom iOS 12 kom med utun1 och utun2 och vissa profiler visas som ipsec4, ppp0 eller utun0. Jämför i stället prefix.
Ändå identifierar ett namn ingen ägare. Systemfunktioner och andra leverantörers tillägg skapar också utun-gränssnitt, och ett kan finnas utan att bära något av din trafik. Läs det som en vink om att något tunnelformat finns i närheten, inget starkare. En falsk positiv är billig här om du bara grindar en funktion, och dyr om du grindar inloggningsskärmen.
Kontroll 4: kan du testa destinationen i stället?
Om appen bara behöver veta om en intern tjänst går att nå, testa den tjänsten. En kort probe med timeout svarar på frågan som funktionen faktiskt hänger på, och den fortsätter att fungera när användaren byter klient eller tunneln ändrar form.
Nåbarhet är en observation om din väg. Ett gränssnittsnamn är en slutsats om något intill din väg, och därför skapar heuristiken diskussioner i felrapporter medan proben skapar bevis. Om proben misslyckas medan användaren insisterar på att VPN:et är på, har du nu en konkret väg att felsöka: namnuppslagning, rutt eller tjänstebehörighet.
Kontroll 5: kan plattformen genomdriva det du behöver?
När kravet är en garanti för att inget läcker ut ur tunneln hör profilen hemma där, inte detektorn. Att slå på includeAllNetworks i en fulltunnelkonfiguration på iOS 14 eller senare ändrar plattformens beteende i stället för att be din app sluta sig till det. Flaggan är dokumenterad i NEVPNProtocol-referensen.
Det är en annan sorts svar: genomdrivande i stället för observation. Det gäller också bara profiler du kontrollerar, så det hjälper inte en konsumentapp som bevakar någon annans tunnel.
Kontroll 6: gör felet säkert och observerbart
Stäng bara av den förmåga som behöver tunneln och lämna resten av appen användbar. En detektor som döljer hela gränssnittet varje gång den är osäker skapar ett avbrott för alla vars tunnel använder ett namn du aldrig förutsåg. Fall tillbaka på det konservativa beteendet i stället: lokala funktioner fortsätter att fungera och bara det fjärranrop som verkligen behöver tunneln inaktiveras. Det valet är ett produktbeslut, och det förtjänar att skrivas ned bredvid heuristiken.
Logga vad du observerade: gränssnittsnamn, plattformsversion och resultatet av en probe. Det gör nästa rapport från en diskussion till en kontroll. Föredra sedan en stödd deklaration där du har en, eftersom en hanterad konfiguration kan bära avsikten explicit och ta bort det mesta av skälet att inspektera namn överhuvudtaget.
Om kontrollerna 1 till 5 lämnade dig utan ett stött svar, släpp proben och märk resultatet ärligt. En dokumenterad best-effort-signal är ett bättre produktbeslut än ett privat API som går sönder i nästa version.