Potřebujete, aby se jedna obrazovka chovala jinak, když je tunel aktivní, a neexistuje volání, které by vám to řeklo. To je omezení při vydání: v macOS žádný veřejný framework nehlásí, zda je připojena libovolná VPN, a v iOS patří stav aplikaci, která konfiguraci nainstalovala. Následující kontroly vedou od tohoto limitu k něčemu, co můžete vydat, a kterákoli z nich může hledání ukončit dřív.

Kontrola 1: dá se na otázku vůbec odpovědět?

Sandbox řeší otázku dřív než jakékoli API. Proces třetí strany nemůže vyjmenovat rozšíření jiného dodavatele a nemůže přečíst ani konfiguraci tohoto rozšíření, takže neexistuje podporovaný objekt, na který se zeptat.

Každé obejití narazí na stejnou zeď. Výpisy rozhraní, scutil --nc list a přímá čtení systémové konfigurace buď vyžadují oprávnění, která sandbox zadržuje, nebo závisí na chování, které Apple nikdy neslíbil. Pokud plán potřebuje jedno z toho, problém je v plánu. Funkce postavená na nedokumentovaném detailu je tiket podpory s datem dodání.

Kontrola 2: spravuje vaše aplikace tunel sama?

Pokud ano, máte podporovanou odpověď pro vlastní konfiguraci. Načtěte předvolby, ošetřete chybu, potom přečtěte stav a přihlaste se k odběru změn místo dotazování:

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
}

Buďte přesní v tom, co to vrací: stav tunelu, který jste nainstalovali. Uživatel s jiným klientem se zobrazí jako nepřipojený a to není chyba ve vašem kódu. Rozhodněte, kterou z těchto dvou otázek vaše funkce skutečně klade, než na odpovědi postavíte cokoli dalšího.

Kontrola 3: co by vám heuristika vlastně řekla?

Široce kopírovaný přístup se ptá systémového slovníku proxy na jeho omezené položky a čte klíče, které jsou náhodou názvy rozhraní:

let settings = CFNetworkCopySystemProxySettings()?.takeRetainedValue() as NSDictionary?
let scoped = settings?["__SCOPED__"] as? [String: Any]
let names = scoped?.keys ?? []

Klíč začínající na tap, tun, ppp, ipsec nebo utun znamená, že je přítomno rozhraní s rozsahem tunelu. Porovnávání celého názvu selže okamžitě, protože iOS 12 přišel s utun1 a utun2 a některé profily se zobrazují jako ipsec4, ppp0 nebo utun0. Porovnávejte raději prefixy.

Přesto název neidentifikuje žádného vlastníka. Systémové funkce a rozšíření jiných dodavatelů také vytvářejí rozhraní utun a nějaké může existovat, aniž by neslo jakýkoli váš provoz. Čtěte to jako náznak, že poblíž je něco ve tvaru tunelu, nic silnějšího. Falešný pozitiv je tu levný, pokud omezujete jen jednu funkci, a drahý, pokud omezujete přihlašovací obrazovku.

Kontrola 4: můžete místo toho otestovat cíl?

Pokud aplikace potřebuje jen vědět, zda je dostupná interní služba, otestujte tuto službu. Krátká sonda s časovým limitem odpovídá na otázku, na které funkce skutečně závisí, a funguje dál, když uživatel změní klienta nebo tunel změní tvar.

Dostupnost je pozorování o vaší cestě. Název rozhraní je odvození o něčem, co s vaší cestou sousedí, a proto heuristika produkuje hádky v hlášeních chyb, zatímco sonda produkuje důkazy. Pokud sonda selže, zatímco uživatel trvá na tom, že VPN je zapnutá, máte nyní konkrétní cestu k ladění: překlad názvů, trasu nebo oprávnění služby.

Kontrola 5: může platforma vynutit to, co potřebujete?

Když je požadavkem záruka, že nic neunikne z tunelu, profil je místo, kam patří, ne detektor. Zapnutí includeAllNetworks v konfiguraci plného tunelu na iOS 14 nebo novějším mění chování platformy místo toho, aby vaše aplikace něco odvozovala. Příznak je zdokumentován v referenci NEVPNProtocol.

To je jiný druh odpovědi: vynucení místo pozorování. Platí také jen pro profily, které ovládáte, takže nepomůže spotřebitelské aplikaci, která sleduje cizí tunel.

Kontrola 6: udělejte selhání bezpečným a pozorovatelným

Vypněte jen tu schopnost, která tunel potřebuje, a zbytek aplikace nechte použitelný. Detektor, který při každé nejistotě skryje celé rozhraní, vyrábí výpadek pro každého, jehož tunel používá název, který jste nikdy nepředpokládali. Vraťte se místo toho ke konzervativnímu chování: místní funkce fungují dál a zakázáno je jen to vzdálené volání, které tunel skutečně potřebuje. Tato volba je produktové rozhodnutí a zaslouží si být zapsána vedle heuristiky.

Zaznamenejte, co jste pozorovali: názvy rozhraní, verzi platformy a výsledek jedné sondy. To promění příští hlášení z hádky v kontrolu. Pak dávejte přednost podporovanému prohlášení, kde ho máte, protože spravovaná konfigurace může nést záměr výslovně a odstranit většinu důvodu názvy vůbec zkoumat.

Pokud vás kontroly 1 až 5 nechaly bez podporované odpovědi, vyšlete sondu a výsledek pojmenujte poctivě. Zdokumentovaný signál nejlepšího úsilí je lepší produktové rozhodnutí než soukromé API, které se v příštím vydání rozbije.

Související články