Aplicație Android care rezolvă nume în afara VPN: listă de verificare pentru legare
Android poate rezolva nume prin rețeaua implicită în timp ce VPN-ul este activ. Leagă procesul de rețeaua VPN și verifică ce resolver a folosit de fapt aplicația.
O aplicație Android nu reușește să rezolve un nume intern în timp ce browserul clientului VPN ajunge la el, iar aceeași compilare funcționează pe alt telefon. URL-ul nu este variabila. Variabila este rețeaua pe care procesul aplicației are voie să o folosească pentru rezolvare, iar Android nu garantează că VPN-ul activ este rețeaua folosită de socketurile tale. Fă aceste verificări în ordine și oprește-te când instantaneul resolverului nu se potrivește cu presupunerea ta.
Pasul 1: dovedește în ce rețea se află aplicația
Citește vizualizarea proprie a aplicației, nu bara de stare. Din ConnectivityManager obține rețeaua activă, LinkProperties ei, lista de servere DNS și setarea de DNS privat. Dacă lista conține resolverul VPN-ului, aplicația are o cale către el; dacă conține resolverul Wi-Fi, tunelul nu este rețeaua implicită pentru acel proces.
O pictogramă VPN spune doar că există un tunel. Nu spune că procesul aplicației a fost legat de el, iar Android nu rută întotdeauna rezolvarea numelor prin rețeaua care pare activă. Aplicațiile VPN locale care redirecționează către alt dispozitiv, rețelele Wi-Fi fără internet și compilările producătorilor pot lăsa aplicația să întrebe resolverul implicit în locul serverelor tunelului.
Pasul 2: cere transportul VPN și leagă procesul
Când aplicația trebuie să folosească resolverul tunelului, cere o rețea cu transport VPN și leagă procesul în callback, ca să nu ghicească codul când apare rețeaua:
val request = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_VPN)
.build()
connectivityManager.requestNetwork(request, object : NetworkCallback() {
override fun onAvailable(network: Network) {
val bound = connectivityManager.bindProcessToNetwork(network)
Log.i(TAG, "bound to VPN network: " + bound)
}
})
Apelul schimbă rețeaua implicită a întregului proces, așa că fiecare socket creat după aceea o folosește. Socketurile care există deja păstrează rețeaua pe care au fost create, de aceea un pool de conexiuni deschis înainte de legare poate continua să rezolve pe vechea cale. Valoarea returnată este boolean, iar un rezultat fals înseamnă că legarea nu s-a produs; încearcă din nou cu o scurtă întârziere și înregistrează ambele încercări.
Pasul 3: limitează legarea la ce are nevoie de ea
Legarea întregului proces este grosieră. Telemetria, verificările de actualizare și SDK-urile terțe trec și ele pe tunel, iar dacă tunelul nu are internet încep să eșueze. Pentru o singură cerere folosește fabrica de socketuri a rețelei și lasă implicitul dispozitivului pentru restul. Alege deliberat un model: legare îngustă per conexiune sau legare de proces cu dezlipire explicită atunci când VPN-ul dispare.
Două funcții ale resolverului pot învinge legarea fără nicio eroare. DNS-ul privat în modul strict trimite interogările către un server DoT numit în locul resolverului rețelei, iar o aplicație care are propriul client DNS-over-HTTPS ocolește complet resolverul sistemului. Verifică ambele înainte de a concluziona că legarea a eșuat. O interogare care nu ajunge niciodată la serverul DNS al VPN-ului nu va apărea în niciun jurnal din partea tunelului.
Pasul 4: nu te baza pe o constrângere CONNECTED
NetworkType.CONNECTED din WorkManager înseamnă că o rețea este conectată, iar transportul VPN o satisface chiar și când legătura Wi-Fi sau celulară de dedesubt a dispărut. Sarcina rulează, fiecare cerere eșuează, iar politica de reîncercare o transformă într-o furtună care arată ca un bug de programare. Tunelul este o rețea; constrângerea nu a fost niciodată o verificare de internet.
Verifică capacitatea de care are nevoie munca, cum ar fi accesul validat la internet, și păstrează o politică de reîncercare limitată cu backoff. Dacă sarcina are nevoie de un nume privat, rezolvă acel nume în rețeaua selectată înainte de a face munca și eșuează rapid cu o eroare clară în loc să repeți o cerere care nu poate reuși.
Pasul 5: înregistrează dovezile și retestează gama
Înregistrează tipurile de transport ale rețelei selectate, serverele DNS din LinkProperties, dacă legarea a returnat true și eșecul exact al primei cereri. Apoi rulează un nume privat și un nume public sub aceeași legare. Acea pereche arată dacă aplicația a folosit resolverul tunelului sau a căzut în tăcere pe implicitul dispozitivului, iar aceasta este diferența pe care o resimte utilizatorul.
Repetă pe cea mai veche versiune de Android pe care o suporți și pe o versiune actuală. Comportamentul resolverului care face calea implicită să pară corectă este exact locul unde dispozitivele și versiunile diferă, iar o compilare care trece doar pe cel mai nou dispozitiv de test nu a fost verificată pentru gama pe care o livrezi. adb shell dumpsys connectivity este o modalitate rapidă de a confirma ce rețele și capacități raportează dispozitivul în timp ce reproduci defecțiunea.