Ихэнх Android апп VPN илрүүлэхийн тулд ашигладаг шалгалт нь интерфейсийн нэр бөгөөд энэ нь хэзээ ч VPN-ийн шалгалт байгаагүй. Хөгжүүлэгчийн хүлээсэн мөртэй ямар нэг интерфейс байгааг мэдээлдэг бөгөөд энэ нь нэг төхөөрөмж дээр OpenVPN гэсэн үг, нөгөө дээр огт холбоогүй зүйл гэсэн үг. ping бол ижил төрлийн баталгаа: нэг очих газар хариу өгснийг нотолдог, багцыг туннель зөөснийг биш. Платформ үнэндээ юу ил гаргадаг вэ гэвэл сүлжээний багц болон тус бүрийн зарладаг транспорт юм, энэ нь өөр хариулттай өөр асуулт.

Домог: tun0 байвал VPN ажиллаж байна

Android VPN аппууд нь системд tun файлын дескриптор өгдөг VpnService-ийн хэрэгжүүлэлт юм. Интерфейсийн нэр нь үүнийг бүтээсэн хүнээс ирдэг: OpenVPN ихэвчлэн tun0 үүсгэдэг, PPTP болон L2TP ppp0 үүсгэдэг, WireGuard интерфейсээ туннелийн нэрээр нэрлэдэг, IPsec туннель уламжлалт нэр огт өгөхгүй байж болно. Тиймээс /proc/net/dev унших эсвэл NetworkInterface объектуудыг тоолох нь баримт биш, уламжлал хайж байна.

Алдааны хоёр чиглэл амьд байна. Төхөөрөмж дээр хэрэглэгчийн VPN-тэй ямар ч холбоогүй локал туннелийн tun0 байж болох бөгөөд бүрэн эрүүл WireGuard туннель зөвхөн tun0 мөрийг мэддэг шалгалтад үл үзэгдэх байж болно. Нэг функц дотор хуурамч эерэг ба хуурамч сөрөг гарвал түүнийг оношлогоо болгон ашиглах боломжгүй, учир нь «VPN илрээгүй» гэсэн дэмжлэгийн тасалбарт аль ч чиглэлд итгэж болохгүй.

Хандалт ч бас нарийссан. Шинэ Android хувилбарууд энгийн апп /proc-оос юу уншиж болохыг хязгаарладаг тул Android 8 дээр ажилласан нэрэнд суурилсан шалгалт код өөрчлөхгүйгээр дараагийн төхөөрөмж дээр хоосон жагсаалт буцааж болно. Шалгалт чимээгүйгээр нээлттэй тал руу бүтэлгүйтвэл апп туннель хэвийн байгаа техникт VPN байхгүй гэж дүгнэдэг.

Домог: амжилттай ping нь туннель трафик зөөж байгааг илэрхийлнэ

Дотоод хост руу ping тэр хост руу чиглэл байгаа үед л амжилттай болдог. Хуваасан туннелийн үед туннель ажиллаж, огт өөр зүйл хийж байхад хост Wi-Fi-аар хүртээмжтэй байж болно. Апп тус бүрийн VPN-ийн үед ping хийж буй апп туннелиас хасагдсан байж болох тул ICMP цуурай түүний гадна гарч буцаж ирдэг, энэ нь шалгалтын таамаглаж буйгийн эсрэг зүйлийг нотолж байна.

ICMP болон TCP мөн тусдаа шүүгддэг. Цуурайн хүсэлтийг зөвшөөрөх боловч үйлчилгээнд хэрэгтэй TCP портыг хаядаг туннель ping шалгалтыг давж, апп дээр бүтэлгүйтэх бөгөөд энэ нь төхөөрөмжийн шинж биш, харин нөгөө талын галт ханын дүрэм юм. Амжилттай ping-ийн цорын ганц шударга уншилт нь энэ очих газар энэ мөчид энэ протоколд хариу өгсөн гэсэн үг.

Платформ үнэндээ юу мэдээлдэг

API 21-ээс эхлэн ConnectivityManager.getAllNetworks() системийн мэддэг бүх сүлжээг буцаадаг бөгөөд getNetworkCapabilities(network) тухайн сүлжээ юу зарлаж байгааг мэдээлдэг. VPN сүлжээ TRANSPORT_VPN-ийг агуулдаг. Бүх сүлжээгээр гүйх нь нэг асуултад хариулна: энэ төхөөрөмж дээр одоо ямар нэг VPN ажиллаж байна уу?

Хоёр дахь, илүү нарийн асуултад өөр дуудлага хэрэгтэй. getActiveNetwork() нь аппын трафик одоо ашиглах сүлжээг буцаадаг бөгөөд түүн дээр ижил чадварыг шалгах нь хариулна: миний трафик VPN-ээр дамжих уу? Хоёр нь санал нийлэхгүй байж болох бөгөөд энэ зөрүү мэдээлэлтэй. Ажиллаж байгаа боловч одоогийн бус VPN нь апп тус бүрийн чиглүүлэлт эсвэл хуваасан туннелийн тохиргоо руу заадаг, алга болсон туннель руу биш.

Network active = cm.getActiveNetwork();
NetworkCapabilities caps = cm.getNetworkCapabilities(active);
boolean viaVpn = caps != null
    && caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN);

Хоёр дуудлагыг хоёуланг нь хамгаал. Жагсаалт ба асуулгын хооронд алга болсон сүлжээний хувьд чадвар null байж болох бөгөөд шилжүүлэх үед идэвхтэй сүлжээ null байж болно. NET_CAPABILITY_NOT_VPN нь эсрэг чиглэлд хэрэгтэй, сүлжээнд VPN транспорт туг байхгүй гэдгээс илүүтэйгээр энэ нь туннель биш гэдгийг батлахад.

NetworkInfo нь API-тай ижил домог байсан

NetworkInfo-той ConnectivityManager.TYPE_VPN нь энэ шалгалтын хоёр дахь үе бөгөөд яагаад хуучирсан нь ойлгомжтой. Орчин үеийн хувилбаруудад туннель холбогдсон байхад VPN-д буцаасан объект null байж болох тул null-ыг «VPN байхгүй» гэж үздэг код хариулт хамгийн чухал төхөөрөмжүүд дээр чимээгүйгээр хуурамч сөрөг мэдээлдэг.

Түүнтэй ихэвчлэн хамт ирдэг нэвтрүүлэг ч бас дээрдэхгүй. CONNECTIVITY_CHANGE нь VPN-ийн шилжилтийг алддаг гэсэн баримтжсан түүхтэй, үүнд сүлжээний өөрчлөлт бүрт биш, зөвхөн VPN холбогдох эсвэл салахад асдаг хувилбарууд ч багтана. Үүн дээр баригдсан сонсогч нь дараагийн бүрэн дахин холболт хүртэл платформын харцаас холддог төлөвийн хувьсагчийг үүсгэдэг.

Санал асуулгын оронд callback-уудад хариу үйлдэл үзүүл

TRANSPORT_VPN шаардаж, NET_CAPABILITY_NOT_VPN-ийг хасдаг NetworkRequest бүртгүүлээд, тэр хүсэлтээр registerNetworkCallback дуудна. Тэгвэл onAvailable болон onLost нь мэдэгдэл хэзээ өөрчлөгдөхийг хэлнэ, энэ нь танд үнэхээр чухал үйл явдал юм. Бүрэлдэхүүн хэсэг зогсоход callback-ийн бүртгэлийг цуцал, эс бөгөөс апп үхсэн дэлгэц рүү лавлагаа хадгалж, callback үргэлжлүүлэн ажиллана.

NetworkRequest req = new NetworkRequest.Builder()
    .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
    .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
    .build();
cm.registerNetworkCallback(req, callback);

Үүнд ACCESS_NETWORK_STATE хэрэгтэй бөгөөд энэ нь энгийн зөвшөөрөл: root байхгүй, хэрэглэгчийн зөвшөөрлийн цонх байхгүй, VPN зөвшөөрөл байхгүй. Таймертай санал асуулга нь хүмүүсийн ханддаг хувилбар бөгөөд батерейг шатааж, интервалаас богино шилжилтийг алддаг, энэ нь яг хэрэглэгчийн мэдээлдэг шилжилт юм.

Хариулт танд хэлж чадахгүй байгаа зүйл

Эдгээр API-ийн аль нь ч туннель эрүүл эсэх, аль сервер дээр төгсөх, тодорхой аппын трафик дотор нь байгаа эсэхийг мэдээлдэггүй. VPN сүлжээ ашиглах боломжтой чиглэлгүй байж болох бөгөөд апп тус бүрийн туннель асуулт тавьж буй аппыг хасч болно.

Тиймээс шалгалт юу харсныг бүртгэ, зөвхөн boolean утгыг биш. Сүлжээний танигч, зарласан транспортууд болон тэр сүлжээ идэвхтэй байсан эсэхийг бүртгэ. «VPN илрээгүй» гэсэн тайлан ашиглах боломжгүй, харин сүлжээ TRANSPORT_VPN зарласан боловч getActiveNetwork() Wi-Fi буцаасан гэсэн тайлан шууд чиглүүлэлт эсвэл хасах жагсаалт руу заадаг бөгөөд энэ нь хэн нэгэн ажиллаж болох засвар юм.

Холбоотой нийтлэлүүд