Вам патрэбен адзін экран, які паводзіць сябе інакш, калі тунель уключаны, але няма выкліку, які скажа вам пра гэта. Гэта абмежаванне выпуску: у macOS ніводны публічны фрэймворк не паведамляе, ці падключаны адвольны VPN, а ў iOS статус належыць праграме, якая ўсталявала канфігурацыю. Праверкі ніжэй вядуць ад гэтай мяжы да чагосьці, што можна выпусціць, і любая з іх можа скончыць пошук раней.

Праверка 1: ці можна наогул адказаць на пытанне?

Ізаляцыя вырашае пытанне раней за любое API. Працэс трэцяга боку не можа пералічыць пашырэнне іншага пастаўшчыка і не можа прачытаць канфігурацыю гэтага пашырэння, таму няма падтрымоўванага аб'екта, які можна запытаць.

Кожны абыходны шлях утыкаецца ў той жа мур. Спісы інтэрфейсаў, scutil --nc list і прамое чытанне сістэмнай канфігурацыі альбо патрабуюць правоў, якіх ізаляцыя не дае, альбо залежаць ад паводзін, якіх 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, якое ламаецца ў наступным выпуску.

Падобныя артыкулы