Tünel açılıyor, anahtar simgesi görünüyor ve tarayıcıdaki her istek yine de zaman aşımına uğruyor. Bir VpnService uygulamasında bu birleşim genellikle okuma döngüsünün yaşadığı ve çıkış yarısının bozuk olduğu anlamına gelir, sunucunun el sıkışmayı reddettiği değil. Android arayüzü zaten oluşturdu, uygulama trafiğini içine taşıdı ve sürecinize bir dosya tanımlayıcısı verdi. O tanımlayıcıdan sonraki her şey sizin kodunuzdur ve tek bir eksik çağrı tüm akışı yutmaya yeter.

Sistemin verdiği ve vermediği

establish() bir tun arayüzü için ParcelFileDescriptor döndürür. Sistem giden IP paketlerini içine yazar ve sürecinizin bunları okuyup nereye gideceğine karar vermesini ve sıradan bir socket üzerinden göndermesini bekler. Altında çekirdek yönlendirmesi yoktur, bu yüzden kimse okumadığında paketler atılır ve arayüz sayaçları asla hareket etmez.

Tahmin etmek yerine saymaya başlayın. Her başarılı okumada bir sayacı artırın ve ilk paketlerin hedefini ve protokolünü günlüğe yazın. Trafik üretilirken sayaç sıfırda kalıyorsa hata yönlendirme kodunuzun üstündedir; bu, aramayı builder yapılandırmasına ve okuma moduna daraltır. Sayaç hareket ediyor ama tarayıcı yine başarısız oluyorsa hata gönderme yolunda veya dönüş yolundadır.

Bloklayan okuma döngünün hareket edip etmeyeceğini belirler

setBlocking(true) tanımlayıcıyı bloklayan moda alır ve API 21'den beri vardır. Eski sürümlerde çözüm, ham tanımlayıcıda fcntl ile O_NONBLOCK'u temizleyen küçük bir JNI yardımcısıdır. Mod önemlidir çünkü bekleyen paket olmadan bir okumanın ne yapabileceğine o karar verir.

Bloklamayan modda o okuma hemen negatif bir değerle döner ve sonucu yok sayan bir döngü hiç trafik taşımadan bir çekirdeği döndürür. Bloklayan modda okuma bekler, bir yönlendirme iş parçacığının istediği budur, ama iş parçacığı artık kesilemez. Tanımlayıcıyı kapatmak okumayı başarısız kılmanın desteklenen yoludur, böylece döngü çıkabilir. Süreci öldürmek veya iş parçacığını kesmek bir sonraki başlangıcı arayüze artık uymayan bir tanımlayıcıyla bırakabilir.

Her çağrıda arayüzün MTU boyutundaki bir tampona bir paket okuyun ve sıfırı veya negatif dönüşü boş paket değil, hata olarak değerlendirin.

protect() ilk gönderimden önce, sonra değil

Kapsüllenmiş paket taşıyan her socket önce korunmalıdır. Korumasız bir socket az önce kurduğunuz aynı yönlendirme kurallarına uyar, bu yüzden paketleri tünele geri döner ve kendi okuma döngünüz onları yeniden alır. Tünel sonra kendi çıktısını sonsuza kadar yönlendirir ve dışarıdan açılan ama asla yanıt vermeyen bir bağlantı gibi görünür.

Sıra protect, sonra connect veya bind, sonra send'dir. connect'ten sonra protect çağırmak önceki rota kararını yerinde bırakabilir, bu yüzden çağrı true döndürse bile socket yanlış yolu kullanmaya devam eder. Döndürülen boolean'ı kontrol edin ve false olduğunda çalışamayacak bir gönderime girmek yerine yüksek sesle başarısız olun.

protect() işlevine hiç dokunmamış bir socket'te sendto'dan gelen izin hatası bu hatanın olağan imzasıdır.

UDP örneği HTTPS taşımaz

Çoğu eğitimin kopyaladığı yönlendirme örneği yalnızca UDP ile ilgilenir. TCP el sıkışma, sıra numaraları, onaylar ve yeniden iletim gerektirir ve bir tun tanımlayıcısı ile socket arasında yük kopyalayan bir döngü bunların hiçbirini sağlamaz. Bu yüzden UDP DNS sorgusu geçebilirken her HTTPS isteği takılır. Ya kullanıcı alanında bir TCP yığını çalıştırın ya da bağlantıları cihazda TCP'yi sonlandırıp akışı ileten yerel bir proxy'ye verin.

Herhangi bir şeyi yeniden yazmadan önce ayrımı test edin: bir UDP sorgusu gönderin ve aynı ana bilgisayara bir TCP bağlantısı açın. UDP yolu çalışıyor ve TCP yolu ilk SYN'de takılıyorsa sorun yönlendirme kodundadır, sunucuda değil. Bu tek karşılaştırma, yararlı hiçbir şey içermeyen sunucu günlüklerini okumaya harcanan çok zamanı kurtarır.

Rota listesi ve uygulama başına filtreler gördüklerinizi sınırlar

addAddress, addRoute ve addDns sistemin tünele ne göndereceğini tanımlar. Rota listesinin dışındaki bir hedef tanımlayıcınıza asla ulaşmaz, bu yüzden hiçbir yönlendirme mantığı bunu düzeltmez. Yaptığınız builder çağrılarını günlüğe yazın ve uygulamanın gerçekten ulaşmaya çalıştığı adreslerle karşılaştırın.

Uygulama başına seçimin kendi tabanı vardır: addAllowedApplication ve addDisallowedApplication API 21 gerektirir ve eski sürümlerde bu yöntemler yoktur. Pakete göre filtreleyen bir tasarım orada hiç çalışamaz.

Yeniden kurma eski tanımlayıcılar bırakır

establish() yeniden çağrıldığında yeni bir tanımlayıcı döner ve önceki arayüzü kapatır. Eski tanımlayıcı artık geçersizdir, bu yüzden hâlâ üzerinde bloklanan bir iş parçacığı hata alır ve eski yol için oluşturulan socket'lerin değiştirilmesi gerekebilir. onRevoke, kullanıcının veya sistemin aldığı tünel için geri çağırmadır. Yaşam döngüsünü, yeniden kurmadan önce okuyucuyu durduran tek bir sahipte tutmak, bağlı-ama-ölü durumların çoğunu önler.

Her hatayı ait olduğu adıma göre okuyun. recvfrom'dan gelen bağlantı sıfırlaması, kodunuz hâlâ okurken uzak ucun kapandığı anlamına gelir. Kötü adres hatası başlık ayrıştırmasına veya offset hatasına işaret eder, ağa değil.

Çalışma sırası: bloklayan mod açık, okuma başına bir paket, başlık ayrıştırıldı, socket korundu, yük gönderildi, devirde temiz kapanış ve hedefi kapsayan rota listesi.

İlgili makaleler