/*
* Copyright (C) 2013 Sasha Vasko <sasha at aftercode dot net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.wifiafterconnect;
import java.io.IOException;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.HttpURLConnection;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import com.wifiafterconnect.util.Logger;
import com.wifiafterconnect.util.Preferences;
import com.wifiafterconnect.util.Worker;
import android.content.Context;
public class URLRedirectChecker extends Worker{
/* storing IP address saves on a number of roundtrips for ip lookup */
public static class CachedURL {
private InetAddress address = null;
private String hostname = "";
private URL url = null;
public void setURL (URL newURL) {
url = newURL;
try {
if (address == null || !hostname.equals (newURL.getHost())) {
hostname = newURL.getHost();
address = lookupHost (hostname);
}
} catch (NullPointerException e) {
}
if (address != null) {
try {
url = new URL (url.getProtocol(), address.getHostAddress(), url.getPort(), url.getFile());
} catch (MalformedURLException e) {
}
}
}
public URL getURL () {
return url;
}
}
private static CachedURL urlToCheckHttp;
private static CachedURL urlToCheckHttps;
public enum AuthorizationType {
None, IfNeeded, Force;
}
public AuthorizationType defaultType = AuthorizationType.IfNeeded;
private void initURLs () {
if (getContext() == null) {
try {
urlToCheckHttp.setURL(new URL(Constants.URL_TO_CHECK_HTTP));
urlToCheckHttps.setURL(new URL(Constants.URL_TO_CHECK_HTTPS));
} catch (MalformedURLException e) {
exception (e);
}
}else {
urlToCheckHttp.setURL(prefs.getURLToCheckHttp());
urlToCheckHttps.setURL(prefs.getURLToCheckHttps());
}
}
static {
CookieHandler.setDefault (new CookieManager (null, CookiePolicy.ACCEPT_ALL));
urlToCheckHttp = new CachedURL();
urlToCheckHttps = new CachedURL();
// we cannot follow the redirects as we need the WISPr data.
// If no WISPr detected then redirects will be followed while authenticating
HttpURLConnection.setFollowRedirects(!Preferences.getWISPrEnabled());
try {
TrustManager[] trustAllCerts = { new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
// TODO Auto-generated method stub
}
@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
// TODO Auto-generated method stub
}
} };
SSLContext sc = SSLContext.getInstance("SSL");
HostnameVerifier hv = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(hv);
} catch (Exception localException) {
}
}
public URLRedirectChecker(Logger logger, Context context) {
super (logger, context);
initURLs ();
}
public URLRedirectChecker(String tag, Context context) {
super (new Logger (tag == null ? "URLRedirectChecker" : tag), context);
initURLs ();
}
public URLRedirectChecker(Worker creator) {
super (creator);
initURLs ();
}
public boolean attemptAuthorization (ParsedHttpInput parsedPage) {
WifiAuthenticator auth = new WifiAuthenticator (this, parsedPage.getURL());
return auth.attemptAuthentication (parsedPage, null);
}
public void setSaveLogFile (URL url) {
setLogFileName ((url == null ? "probing" : url.getHost()) + ".log");
}
/* ######################################################
* The Captive Portal check code from android. Unlike them,
* we actually need the portal page, so that we can post a response.
*
* Copyright (C) 2012 The Android Open Source Project
* Licensed under the Apache License, Version 2.0 (the "License");
*/
private static final int SOCKET_TIMEOUT_MS = 10000;
private static final String DEFAULT_SERVER = "clients3.google.com";
public boolean isCaptivePortal(InetAddress server) {
HttpURLConnection urlConnection = null;
//if (!mIsCaptivePortalCheckEnabled) return false;
String url_string = "http://" + server.getHostAddress() + "/generate_204";
//if (DBG) log("Checking " + url_string);
try {
URL url = new URL(url_string);
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();
}
}
}
public static InetAddress lookupHost(String hostname) {
InetAddress inetAddress[];
try {
inetAddress = InetAddress.getAllByName(hostname);
} catch (UnknownHostException e) {
return null;
}
for (InetAddress a : inetAddress) {
if (a instanceof Inet4Address) return a;
}
return null;
}
/* ======================================================
* End of the The Captive Portal check code from android
*/
public boolean isCaptivePortal(String server_hostname) {
InetAddress server = lookupHost (server_hostname == null ? DEFAULT_SERVER : server_hostname);
return isCaptivePortal (server);
}
public boolean checkHttpConnection (URL url, AuthorizationType doAuthorize) {
//if (proto < 0 || proto >= protocols.length) return false;
//String protocol = protocols[proto];
boolean success = false;
/* per WISPR 2.0 specs #7.7 must use HTTP for initial get
* This may not be optimal for other gateways as they redirect to HTTPS for authentication
*/
if (url == null)
url = urlToCheckHttps.getURL();
try {
//URL url = new URL(protocol + "://www.google.com");
ParsedHttpInput parsed = null;
// due to switching to wifi, name resolution can fail if the timing in just right,
// give it another chance
for ( int i = 0; i < 2 && parsed == null ; ++i ) {
if ((parsed = ParsedHttpInput.get (this, url, null)) == null) {
try { Thread.sleep(100); } catch (InterruptedException e) {} // don't care
}
}
if (parsed == null)
return false;
String field = null;
URL redirectURL = null;
if (doAuthorize == AuthorizationType.Force) {
success = attemptAuthorization (parsed);
}else if (!url.getHost().equals(parsed.getURL().getHost())) {
// we were redirected! Kick the user out to the browser to sign on?
debug("Redirected to [" + parsed.getURL() + "]");
if (doAuthorize != AuthorizationType.None) {
setSaveLogFile (parsed.getURL());
success = attemptAuthorization (parsed);
}
}else if (!(field = parsed.getHttpHeader(ParsedHttpInput.HTTP_HEADER_LOCATION)).isEmpty()){
redirectURL = new URL (field);
}else if (parsed.hasMetaRefresh()) {
redirectURL = parsed.getMetaRefresh().getURL();
}else
success = true;
if (redirectURL != null) {
if (!redirectURL.getHost().equals(parsed.getURL().getHost())) {
debug("Redirected to [" + redirectURL + "]. Assuming Internet unavailable - probably a captive portal.");
if (!redirectURL.getProtocol().equals(url.getProtocol())) {
debug("protocol has changed!");
}
if (doAuthorize != AuthorizationType.None) {
setSaveLogFile (redirectURL);
debug("WISPr = [" + parsed.getWISPr() + "]");
if (!Preferences.getWISPrEnabled() || parsed.getWISPr() == null)
success = checkHttpConnection (redirectURL, AuthorizationType.Force);
else
success = attemptAuthorization (parsed);
}
} else {
// something wicked happened otherwise
error("Unexpected redirect URL [" + redirectURL + "] - giving up.");
}
}
} catch (MalformedURLException e){
error("Redirected to a malformed url ");
exception (e);
}
return success;
}
public void setDefaultType (AuthorizationType type) {
defaultType = type;
}
public boolean checkHttpConnection () {
boolean success = checkHttpConnection (urlToCheckHttp.getURL(), defaultType);
debug("Internet connection is " + (success ? "Available" : "Blocked by Captive portal"));
return success;
}
public boolean checkHttpConnection (AuthorizationType authType) {
boolean success = checkHttpConnection (urlToCheckHttp.getURL(), authType);
debug("Internet connection is " + (success ? "Available" : "Blocked by Captive portal"));
return success;
}
}