¿Cómo comprobar si hay acceso a Internet sin restricciones? (detección de portal cautivo)


Necesito detectar de manera confiable si un dispositivo tiene acceso completo a Internet, es decir, que el usuario no está confinado a un portal cautivo (también llamado jardín amurallado), es decir, una subred limitada que obliga a los usuarios a enviar sus credenciales en un formulario para obtener acceso completo.

Mi aplicación está automatizando el proceso de autenticación y, por lo tanto, es importante saber que el acceso completo a Internet no está disponible antes de iniciar la actividad de inicio de sesión.

La pregunta es no sobre cómo comprobar que la interfaz de red está activada y en estado conectado. Se trata de asegurarse de que el dispositivo tenga acceso a Internet sin restricciones en lugar de un segmento de intranet aislado.

Todos los enfoques que he intentado hasta ahora están fallando, porque conectarse a cualquier host conocido no lanzaría una excepción, sino que devolvería un código de respuesta válido HTTP 200 porque todas las solicitudes se enrutan a la página de inicio de sesión.

Aquí están todos los enfoques que probé, pero todos regresan true en lugar de false por las razones explicadas anteriormente:

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;

Entonces, ¿cómo me aseguro de conectarme a un host real en lugar de a la página de redirección de inicio de sesión? Obviamente, podría comprobar el cuerpo de respuesta real desde el host' ping ' que uso, pero no parece una solución adecuada.

Author: ccpizza, 2012-12-19

5 answers

Para referencia, aquí está el método' oficial ' de la base de código AOSP de Android 4.0.1: WifiWatchdogStateMachine.isWalledGardenConnection () . Estoy incluyendo el siguiente código en caso de que el enlace se rompa en el futuro.

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();
        }
    }
}

Este enfoque se basa en una URL específica, mWalledGardenUrl = "http://clients3.google.com/generate_204" siempre devolviendo un código de respuesta 204. Esto funcionará incluso si se ha interferido con DNS, ya que en ese caso se devolverá un código 200 en lugar del esperado 204. He visto algunos cautivos portals spoofing requests to this specific URL in order to prevent the Internet not accessible message on Android devices.

Google tiene una variación de este tema: buscar http://www.google.com/blank.html devolverá un código 200 con un cuerpo de respuesta de longitud cero. Así que si consigues un cuerpo no vacío esta sería otra forma de descubrir que estás detrás de un jardín amurallado.

Apple tiene sus propias URL para detectar portales cautivos: cuando la red está activa, los dispositivos IOS y macOS se conectarían a un URL como http://www.apple.com/library/test/success.html, http://attwifi.apple.com/library/test/success.html , o http://captive.apple.com/hotspot-detect.html que debe devolver un código de estado HTTP de 200 y un cuerpo que contenga Success.

NOTA : Este enfoque no funcionará en áreas con acceso restringido a Internet, como China, donde todo el país es un jardín amurallado, y donde la mayoría de los servicios de Google/Apple son bloqueado o filtrado. Algunos de estos podrían no estar bloqueados: http://www.google.cn/generate_204, http://g.cn/generate_204, http://gstatic.com/generate_204 o http://connectivitycheck.gstatic.com/generate_204 - sin embargo, todos estos pertenecen a Google, por lo que no se garantiza que funcionen.

 40
Author: ccpizza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-10 17:33:19

Otra posible solución podría ser conectarse a través de HTTPS e inspeccionar el certificado de destino. No estoy seguro de si los jardines amurallados realmente sirven la página de inicio de sesión a través de HTTPS o simplemente eliminan las conexiones. En cualquier caso, debería poder ver que su destino no es el que esperaba.

Por supuesto, también tiene la sobrecarga de TLS y comprobaciones de certificados. Tal es el precio de las conexiones autenticadas, desafortunadamente.

 5
Author: Delyan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-25 12:00:06

Creo que evitar la redirección de su conexión funcionará.

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;

Si eso no funciona, entonces creo que la única manera de hacerlo es comprobar el cuerpo de la respuesta.

 1
Author: Ralgha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-19 18:24:35

Esto se ha implementado en Android 4.2.2 + versión-Me parece su enfoque rápido e interesante:

CaptivePortalTracker.java detecta jardín amurallado de la siguiente manera - Intenta conectarte a www.google.com/generate_204 - Compruebe que la respuesta HTTP es 204

Si el cheque falla, estamos en un jardín amurallado.

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
Author: Xavier Stenuit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-28 20:44:33

Si ya está usando retrofit puede hacerlo por retrofit. haz un ping.página html y enviar una solicitud de head a ella utilizando retrofit y asegúrese de que su cliente http está configurado de la siguiente manera: (followRedirects(false) parte es la parte más importante)

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

Luego construye tu retrofit como a continuación:

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);
}

Su InternetCheckApi.la clase sería como:

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

Entonces puedes usarlo como a continuación:

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
     }
  }
);

Tenga en cuenta que su cliente http de comprobación de Internet debe estar separado de su http principal cliente.

 0
Author: Amir Ziarati,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-03 07:01:52