VpnService protect() ці выключэнне праграмы: што выпраўляе маршрутызацыю
Абараніце сокет, які нясе тунель, або выключыце ўсю праграму: тры ўмовы, якія вызначаюць патрэбны абсяг для вашага Android VPN.
Ёсць два спосабы трымаць уласныя пакеты тунеля па-за тунелем: абараніць транспартны сокет або вывесці ўсю праграму з VPN. Яны вырашаюць розныя задачы, і выбар шырэйшага, таму што яго прасцей падключыць, — гэта шлях, якім праграмы губляюць доступ да лакальнай сеткі і раздвоены DNS, не заўважаючы гэтага. Пачніце з таго, што павінна застацца па-за тунелем, потым выберыце найменшы абсяг, які гэта дасягае.
Што protect() прыбірае з тунеля
VpnService.protect()— гэта выклік для асобнага сокета. Вы перадаяце яму сокет або дэскрыптар, які нясе тунель, і Android пазначае гэты сокет, каб яго трафік ішоў праз ніжэйшую сетку, а не праз інтэрфейс VPN. Усё астатняе, што адпраўляе праграма, па-ранейшаму ідзе звычайным маршрутам VPN.
Менавіта ў гэтым і сэнс вузкага абсягу. Абаронены сокет не змяняе тое, як ваш HTTP-кліент развязае імёны, які інтэрфейс выкарыстоўваюць вашыя выклікі аналітыкі, і што адбываецца з другім злучэннем, адкрытым пазней.
Тры ўмовы, якія вызначаюць абсяг
Першая ўмова — ці павінен выйсці толькі транспарт тунеля. Калі астатні трафік праграмы ўсё яшчэ павінен ісці праз VPN, абарона сокета — правільны абсяг. Другая ўмова — ці кантралюеце вы стварэнне сокета. Бібліятэка, якая адкрывае ўласны сокет глыбока ўнутры, не дае вам месца, каб выклікаць protect(), калі яна не прапануе хук ці не прымае гатовы сокет. Трэцяя ўмова — ці павінна праграма наўпрост дасягаць лакальнай сеткі або captive portal, а гэта іншае патрабаванне, чым падтрымліваць тунель жывым.
Выключэнне праграмы праз addDisallowedApplication()— грубы інструмент. Яно прыбірае з VPN кожнае злучэнне, якое робіць праграма, уключаючы тыя, якія вы хацелі бачыць, а на некаторых прыладах яно яшчэ і змяняе паводзіны лакальнага выяўлення.
Трэці варыянт — прывязаць транспарт да выбранай сеткі з дапамогай bindProcessToNetwork() або дэскрыптара сеткі на ўзроўні сокета. Гэта карысна, калі прылада адначасова мае Wi-Fi і мабільную сетку і вы хочаце, каб тунель ішоў праз пэўны канал уверх.
Выклікайце яго падчас стварэння транспарту
Выклічце protect() у тым жа блокі, што стварае транспарт, да connect() або send(). Калі выклік вяртае false, зачыніце сокет і праваліце наладку; працяг пакідае вас з транспартам, пакеты якога могуць зноў трапіць у ваш уласны інтэрфейс.
Форма дастаткова малая, каб трымаць яе ў адным месцы:
DatagramSocket transport = new DatagramSocket();
if (!protect(transport)) {
transport.close();
throw new IOException("transport not protected");
}
transport.connect(serverAddress);Тут кусаюцца тры дэталі. Па-першае,boolean — адзіны сігнал, які вы атрымліваеце, таму запісвайце збой з дастатковым кантэкстам, каб адрозніць адкліканую згоду ад адмоўленага выкліку. Па-другое,protect() пакрывае сокет, а не нумар дэскрыптара: калі вы зачыніце сокет і пазней паўторна выкарыстаеце той жа цэлы лік для іншага сокета, новы сокет не абаронены. Па-трэцяе, калі native-бібліятэка адкрывае транспарт, абараніце дэскрыптар да таго, як native-код падключыцца, інакш маршрут можа быць ужо выбраны.Даведнік па VpnService— гэта месца, дзе можна пацвердзіць, якія перагрузкі рэальна дае ваш мэтавы ўзровень API.
Правярайце заблакаваны шлях, а не толькі шчаслівы
Правярайце пры ўзнятым тунелі, бо гэта адзіны стан, у якім два шляхі могуць адрознівацца. Адпраўце адзін запыт праз абаронены транспарт, а другі — з шляху кода, які ніколі не выклікаў protect(), потым параўнайце, што бачыць далёкі бок: абароненае злучэнне павінна прыйсці з ніжэйшай сеткай як крыніцай, а незабароненае паказвае ўласны адрас тунеля і рухае яго байтавыя лічыльнікі. На прыладзе ss або netstat паказвае тое ж раздзяленне яшчэ да таго, як што-небудзь пакіне радыё.
Правярайце адкліканне асобна, бо змена згоды зносіць інтэрфейс, не закранаючы ваш код сокета. Адклічце згоду на VPN або спыніце службу, перабудуйце транспарт і пацвердзіце, што protect() па-ранейшаму вяртае true і што транспарт перападключаецца, як толькі згоду зноў даюць.
Калі абаронены запыт усё ж ідзе праз тунель, выклік абароны, верагодна, адбываецца пасля таго, як сокет ужо маршрутызаваны. Перанясіце яго раней і праверце зноў.
Трымайце рашэнне запісаным побач з наладкай транспарту: якія сокеты абаронены, які трафік праграмы выключаны і што праграма робіць, калі абарона не ўдаецца. Менавіта гэты радок не дазваляе пазнейшаму рэфактарынгу пашырыць выключэнне на ўсю праграму таму, што адзін тэст не праходзіў. Ён таксама дае наступнаму чалавеку спосаб праверыць намер, не разбіраючы маніфест у адваротным парадку.