Потребан вам је један екран који се понаша другачије када је тунел активан, а нема позива који вам то каже. То је ограничење при испоруци: на macOS-у ниједан јавни оквир не извештава да ли је произвољан VPN повезан, а на iOS-у статус припада апликацији која је инсталирала конфигурацију. Провере испод воде од те границе до нечега што можете да испоручите, и свака од њих може рано да заврши претрагу.

Провера 1: може ли се на питање уопште одговорити?

Sandbox решава питање пре било ког API-ја. Процес треће стране не може да наброји екстензију другог добављача нити може да прочита конфигурацију те екстензије, па нема подржаног објекта који се може питати.

Сваки заобилазни пут удара у исти зид. Листе интерфејса, scutil --nc list и директна читања системске конфигурације или захтевају овлашћења која sandbox ускраћује или зависе од понашања које Apple никада није обећао. Ако плану треба нешто од тога, проблем је у плану. Функција изграђена на недокументованом детаљу је захтев за подршку са датумом испоруке.

Провера 2: да ли ваша апликација сама управља тунелом?

Ако управља, имате подржан одговор за сопствену конфигурацију. Учитајте поставке, обрадите грешку, затим прочитајте статус и претплатите се на промене уместо анкетирања:

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
}

Будите прецизни око тога шта ово враћа: стање тунела који сте инсталирали. Корисник који покреће други клијент биће приказан као неповезан, и то није грешка у вашем коду. Одлучите које од та два питања ваша функција заиста поставља пре него што градите на одговору.

Провера 3: шта би вам хеуристика заправо рекла?

Широко копиран приступ пита системски речник проксија за његове обухваћене уносе и чита кључеве, који су случајно имена интерфејса:

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

Кључ који почиње са tap, tun, ppp, ipsec или utun имплицира да је присутан интерфејс обухваћен тунелом. Подударање целог имена одмах пада, јер је iOS 12 испоручен са utun1 и utun2, а неки профили се појављују као ipsec4, ppp0 или utun0. Уместо тога упоредите префиксе.

Ипак, име не идентификује власника. Системске функције и екстензије других добављача такође стварају utun интерфејсе, а један може постојати док не носи ништа од вашег саобраћаја. Читајте то као наговештај да је у близини нешто у облику тунела, ништа више. Лажно позитиван резултат овде је јефтин ако ограничавате само једну функцију, а скуп ако ограничавате екран за пријаву.

Провера 4: можете ли уместо тога да тестирате одредиште?

Ако апликација само треба да зна да ли је интерна услуга доступна, тестирајте ту услугу. Кратка сонда са временским ограничењем одговара на питање од којег функција заиста зависи, и наставља да ради када корисник промени клијента или тунел промени облик.

Доступност је запажање о вашој путањи. Име интерфејса је закључак о нечему што граничи с вашом путањом, зато хеуристика ствара расправе у извештајима о грешкама, а сонда ствара доказе. Ако сонда не успе док корисник тврди да је VPN укључен, сада имате конкретну путању за отклањање грешака: разрешавање имена, руту или дозволу услуге.

Провера 5: може ли платформа да наметне оно што вам треба?

Када је захтев гаранција да ништа не излази из тунела, профил је право место за то, а не детектор. Укључивање includeAllNetworks у конфигурацији пуног тунела на iOS 14 или новијем мења понашање платформе уместо да тражи од ваше апликације да то закључи. Заставица је документована у референци NEVPNProtocol.

То је друга врста одговора: наметање уместо посматрања. Такође се примењује само на профиле које контролишете, па не помаже потрошачкој апликацији која гледа туђи тунел.

Провера 6: учините неуспех сигурним и видљивим

Искључите само способност којој треба тунел и оставите остатак апликације употребљивим. Детектор који сакрије цело сучеље кад год није сигуран ствара прекид рада за свакога чији тунел користи име које никада нисте предвидели. Уместо тога се вратите на конзервативно понашање: локалне функције настављају да раде и онемогућен је само удаљени позив којем тунел заиста треба. Тај избор је одлука о производу и заслужује да буде записан поред хеуристике.

Забележите шта сте приметили: имена интерфејса, верзију платформе и резултат једне сонде. То следећи извештај претвара из расправе у проверу. Затим дајте предност подржаној декларацији где је имате, јер управљана конфигурација може експлицитно да носи намеру и уклони већину разлога за проверу имена уопште.

Ако су вас провере од 1 до 5 оставиле без подржаног одговора, испоручите сонду и искрено означите резултат. Документован сигнал најбољег напора боља је одлука о производу од приватног API-ја који се квари у следећем издању.

Повезани чланци