it-swarm.com.de

Wie kann ich nach uneingeschränktem Internetzugang suchen? (Captive Portal-Erkennung)

Ich muss zuverlässig feststellen können, ob ein Gerät über einen vollständigen Internetzugang verfügt, dh dass der Benutzer nicht auf ein Captive-Portal (auch als walled garden ) bezeichnet ist, dh auf ein begrenztes Subnetz, das Benutzer dazu zwingt, ihre Anmeldeinformationen einzureichen auf einem Formular, um vollen Zugriff zu erhalten. 

Meine App automatisiert den Authentifizierungsprozess. Daher ist es wichtig zu wissen, dass kein vollständiger Internetzugang verfügbar ist, bevor die Anmeldeaktivitäten gestartet werden. 

Die Frage ist nicht , wie Sie prüfen können, ob die Netzwerkschnittstelle aktiv ist und sich in einem verbundenen Zustand befindet. Es geht darum sicherzustellen, dass das Gerät über einen uneingeschränkten Internetzugang verfügt, im Gegensatz zu einem Sandkasten-Intranetsegment.

Alle Ansätze, die ich bisher ausprobiert habe, schlagen fehl, da die Verbindung zu einem bekannten Host keine Ausnahme auslöst, sondern einen gültigen HTTP 200-Antwortcode zurückgibt, da alle Anforderungen an die Anmeldeseite weitergeleitet werden.

Hier sind alle Ansätze, die ich ausprobiert habe, aber aus den oben erläuterten Gründen geben sie alle true anstelle von false zurück:

1:

InetAddress.getByName(Host).isReachable(TIMEOUT_IN_MILLISECONDS);
isConnected = true; <exception not thrown>

2:

Socket socket = new Socket();
SocketAddress sockaddr = new InetSocketAddress(InetAddress.getByName(Host), 80);
socket.connect(sockaddr, pingTimeout);
isConnected = socket.isConnected();

3:

URL url = new URL(hostUrl));
URLConnection urlConn = url.openConnection();
HttpURLConnection httpConn = (HttpURLConnection) urlConn;
httpConn.setAllowUserInteraction(false);
httpConn.setRequestMethod("GET");
httpConn.connect();
responseCode = httpConn.getResponseCode();
isConnected = responseCode == HttpURLConnection.HTTP_OK;

Wie kann ich also sicherstellen, dass ich eine Verbindung zu einem tatsächlichen Host hergestellt habe, anstatt auf der Seite für die Login-Umleitung? Natürlich könnte ich den eigentlichen Antwort-Body vom "Ping" -Host überprüfen, den ich benutze, aber er sieht nicht nach einer geeigneten Lösung aus.

35
ccpizza

Als Referenz dient hier die "offizielle" Methode aus der AOSP-Codebasis von Android 4.0.1: WifiWatchdogStateMachine.isWalledGardenConnection () . Ich füge den folgenden Code ein, nur für den Fall, dass der Link in der Zukunft bricht.

private static final String mWalledGardenUrl = "http://clients3.google.com/generate_204";
private static final int WALLED_GARDEN_SOCKET_TIMEOUT_MS = 10000;

private boolean isWalledGardenConnection() {
    HttpURLConnection urlConnection = null;
    try {
        URL url = new URL(mWalledGardenUrl); // "http://clients3.google.com/generate_204"
        urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setInstanceFollowRedirects(false);
        urlConnection.setConnectTimeout(WALLED_GARDEN_SOCKET_TIMEOUT_MS);
        urlConnection.setReadTimeout(WALLED_GARDEN_SOCKET_TIMEOUT_MS);
        urlConnection.setUseCaches(false);
        urlConnection.getInputStream();
        // We got a valid response, but not from the real google
        return urlConnection.getResponseCode() != 204;
    } catch (IOException e) {
        if (DBG) {
            log("Walled garden check - probably not a portal: exception "
                    + e);
        }
        return false;
    } finally {
        if (urlConnection != null) {
            urlConnection.disconnect();
        }
    }
}

Dieser Ansatz basiert auf einer bestimmten URL. mWalledGardenUrl = "http://clients3.google.com/generate_204" gibt immer einen 204-Antwortcode zurück. Dies funktioniert auch, wenn DNS gestört wurde, da in diesem Fall ein 200-Code anstelle des erwarteten 204 zurückgegeben wird. Ich habe einige Captive-Portale gesehen, die Anfragen an diese bestimmte URL spoofing haben, um die Meldung Internet nicht erreichbar auf Android-Geräten zu verhindern.

Google hat eine Variation dieses Themas: Wenn Sie http://www.google.com/blank.html abrufen, wird ein 200-Code mit einem Antworttext der Länge Null zurückgegeben. Wenn Sie also einen nicht leeren Körper haben, wäre dies eine andere Möglichkeit, um herauszufinden, dass Sie sich hinter einem ummauerten Garten befinden.

Apple hat eigene URLs für die Erkennung von Captive-Portalen: Wenn das Netzwerk aktiv ist IOS und MacOS-Geräte eine Verbindung mit einer URL herstellen, wie http://www.Apple.com/library/test/success.html , http://attwifi.Apple.com/library/test/success.html oder http://captive.Apple.com/hotspot-detect.html , die einen HTTP-Statuscode von zurückgeben müssen 200 und ein Körper, der Success enthält. 

NOTE: Dieser Ansatz funktioniert nicht in Gegenden mit eingeschränktem Internetzugang wie China, wo das ganze Land ein ummauerter Garten ist und die meisten Google/Apple-Dienste blockiert oder gefiltert sind. Einige davon sind möglicherweise nicht blockiert: http://www.google.cn/generate_204, http://g.cn/generate_204, http://gstatic.com/generate_204 oder http://connectivitycheck.gstatic.com/generate_204 - diese gehören jedoch alle zu google, so dass sie nicht garantiert funktionieren.

42
ccpizza

Eine andere mögliche Lösung könnte sein, eine Verbindung über HTTPS herzustellen und das Zielzertifikat zu überprüfen. Sie sind sich nicht sicher, ob ummauerte Gärten die Anmeldeseite tatsächlich über HTTPS bereitstellen oder die Verbindungen einfach trennen. In beiden Fällen sollten Sie in der Lage sein zu sehen, dass Ihr Ziel nicht dem von Ihnen erwarteten entspricht.

Natürlich haben Sie auch den Overhead der TLS- und Zertifikatprüfungen. Dies ist leider der Preis für authentifizierte Verbindungen.

5
Delyan

Ich glaube, das Verhindern der Weiterleitung für Ihre Verbindung wird funktionieren.

URL url = new URL(hostUrl));
HttpURLConnection httpConn = (HttpURLConnection)url.openConnection();

/* This line prevents redirects */
httpConn.setInstanceFollowRedirects( false );

httpConn.setAllowUserInteraction( false );
httpConn.setRequestMethod( "GET" );
httpConn.connect();
responseCode = httpConn.getResponseCode();
isConnected = responseCode == HttpURLConnection.HTTP_OK;

Wenn dies nicht funktioniert, denke ich, dass der einzige Weg, dies zu tun, darin besteht, den Antwortkörper zu überprüfen.

1
Ralgha

wenn Sie retrofit bereits verwenden, können Sie dies mit retrofit tun. Erstellen Sie einfach eine ping.html-Seite, und senden Sie eine Kopfanforderung mit Retrofit an sie. Stellen Sie sicher, dass Ihr http-Client wie folgt konfiguriert ist: (followRedirects(false)-Teil ist der wichtigste Teil).

private OkHttpClient getCheckInternetOkHttpClient() {
    return new OkHttpClient.Builder()
            .readTimeout(2L, TimeUnit.SECONDS)
            .connectTimeout(2L, TimeUnit.SECONDS)
            .followRedirects(false)
            .build();
}

dann bauen Sie Ihr Retrofit wie folgt auf:

private InternetCheckApi getCheckInternetRetrofitApi() {
    return (new Retrofit.Builder())
            .baseUrl("[base url of your ping.html page]")             
            .addConverterFactory(GsonConverterFactory.create(new Gson()))
            .client(getCheckInternetOkHttpClient())
            .build().create(InternetCheckApi.class);
}

ihre InternetCheckApi.class würde wie folgt aussehen:

public interface InternetCheckApi {
    @Headers({"Content-Typel: application/json"})
    @HEAD("ping.html")
    Call<Void> checkInternetConnectivity();
}

dann kannst du es wie folgt verwenden:

getCheckInternetOkHttpClient().checkInternetConnectivity().enqueue(new Callback<Void>() {
     public void onResponse(Call<Void> call, Response<Void> response) {
       if(response.code() == 200) {
        //internet is available
       } else {
         //internet is not available
       }
     }

     public void onFailure(Call<Void> call, Throwable t) {
        //internet is not available
     }
  }
);

beachten Sie, dass Ihr HTTP-Client für den Internet-Check von Ihrem Haupt-HTTP-Client getrennt sein muss.

0
Amir Ziarati

Dies wurde auf der Android-Version 4.2.2+ implementiert - ich finde ihren Ansatz schnell und interessant:

CaptivePortalTracker.Java erkennt den ummauerten Garten wie folgt: __.- Versuchen Sie, eine Verbindung zu www.google.com/generate_204- herzustellen. Überprüfen Sie, ob die HTTP-Antwort 204 lautet

Wenn die Überprüfung fehlschlägt, befinden wir uns in einem ummauerten Garten.

private boolean isCaptivePortal(InetAddress server) {
    HttpURLConnection urlConnection = null;
    if (!mIsCaptivePortalCheckEnabled) return false;

    mUrl = "http://" + server.getHostAddress() + "/generate_204";
    if (DBG) log("Checking " + mUrl);
    try {
        URL url = new URL(mUrl);
        urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setInstanceFollowRedirects(false);
        urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
        urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
        urlConnection.setUseCaches(false);
        urlConnection.getInputStream();
        // we got a valid response, but not from the real google
        return urlConnection.getResponseCode() != 204;
    } catch (IOException e) {
        if (DBG) log("Probably not a portal: exception " + e);
        return false;
    } finally {
        if (urlConnection != null) {
            urlConnection.disconnect();
        }
    }
}
0
Xavier Stenuit