package org.openslx.dozmod.util;
import java.io.IOException;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.Socket;
import java.text.MessageFormat;
import java.util.concurrent.atomic.AtomicReference;
import javax.net.ssl.SSLContext;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.impl.routing.SystemDefaultRoutePlanner;
import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.URIScheme;
import org.apache.hc.core5.http.config.RegistryBuilder;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.http.ssl.TLS;
import org.apache.hc.core5.util.Timeout;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.openslx.bwlp.thrift.iface.MasterServer;
import org.openslx.dozmod.App;
import org.openslx.dozmod.authentication.ShibbolethEcp;
import org.openslx.thrifthelper.ThriftManager;
import org.openslx.util.Util;
import com.btr.proxy.search.ProxySearch;
import com.btr.proxy.search.wpad.WpadProxySearchStrategy;
import com.btr.proxy.util.Logger.LogBackEnd;
import com.btr.proxy.util.Logger.LogLevel;
/**
* Configures the proxy
*
* @author Jonathan Bauer
*/
public class ProxyConfigurator {
/**
* Logger for this class
*/
private final static Logger LOGGER = LogManager.getLogger(ProxyConfigurator.class);
private static AtomicReference<CloseableHttpClient> apacheClient = new AtomicReference<>();
private static final TLS[] SUPPORTED_TLS_VERSIONS;
private static final Timeout TIMEOUT_CONNECT = Timeout.ofSeconds(8);
private static final Timeout TIMEOUT_SOCKET = Timeout.ofSeconds(8);
private static final Timeout TIMEOUT_REQUEST = Timeout.ofSeconds(3);
static {
boolean ok = false;
try {
SSLContext.getInstance("TLSv1.3");
ok = true;
} catch (Exception e) {
}
if (ok) {
SUPPORTED_TLS_VERSIONS = new TLS[] { TLS.V_1_3, TLS.V_1_2, TLS.V_1_1 };
} else {
SUPPORTED_TLS_VERSIONS = new TLS[] { TLS.V_1_2, TLS.V_1_1 };
}
}
/**
* Initialization method.
*/
public static void init() {
MasterServer.Client masterClient = ThriftManager.getNewMasterClient(null, App.getMasterServerAddress(),
App.THRIFT_PORT, 4000);
if (masterClient != null) {
try {
masterClient.ping();
try {
masterClient.getInputProtocol().getTransport().close();
masterClient.getOutputProtocol().getTransport().close();
} catch (Throwable e) {
}
} catch (Exception e) {
masterClient = null;
}
}
// To be discussed: Let's bail out if the master server is reachable
if (masterClient != null && testHttpsMaster()) {
LOGGER.info("Not setting up proxy because master server seems reachable.");
return;
}
// Build special HTTP Client Builder for apache and try again
apacheClient.set(createSlxBuilder().build());
if (masterClient != null && testHttpsMaster()) {
LOGGER.info("Not setting up proxy since master server is reachable with custom client builder");
return;
}
// first setup the logger of proxy-vole
com.btr.proxy.util.Logger.setBackend(new LogBackEnd() {
public void log(Class<?> clazz, LogLevel loglevel, String msg, Object... params) {
Level priority;
switch (loglevel) {
case ERROR:
priority = Level.ERROR;
break;
case WARNING:
priority = Level.WARN;
break;
case INFO:
priority = Level.INFO;
break;
default:
priority = Level.DEBUG;
}
LogManager.getLogger(clazz).log(priority, MessageFormat.format(msg, params));
}
public boolean isLogginEnabled(LogLevel logLevel) {
return true;
}
});
LOGGER.info("Master server not directly reachable; trying to determine proxy");
// try to find local proxy settings
ProxySearch proxySearch = ProxySearch.getDefaultProxySearch();
ProxySelector myProxySelector = proxySearch.getProxySelector();
if (myProxySelector == null) {
// didn't work, try WPAD detection
LOGGER.error("No suitable proxy settings found, trying WPAD...");
WpadProxySearchStrategy ss = new WpadProxySearchStrategy();
myProxySelector = ss.getProxySelector();
}
// final check to see if WPAD actually worked
if (myProxySelector != null) {
ProxySelector.setDefault(myProxySelector);
LOGGER.debug("Proxy initialised.");
Util.sleep(10);
} else {
LOGGER.error("Could not find a suitable proxy!");
}
}
/**
* Get the HttpClient to be used with apache
* httpclient.
*
* @return client
*/
public static CloseableHttpClient getClient() {
CloseableHttpClient inst = apacheClient.get();
if (inst != null)
return inst;
inst = createShortTimeoutBuilder().build();
apacheClient.compareAndSet(null, inst);
return inst;
}
private static HttpClientBuilder createShortTimeoutBuilder() {
return HttpClientBuilder.create()
.setConnectionManager(PoolingHttpClientConnectionManagerBuilder.create()
.setSSLSocketFactory(SSLConnectionSocketFactoryBuilder.create()
.setTlsVersions(ProxyConfigurator.SUPPORTED_TLS_VERSIONS)
.build())
.setDefaultTlsConfig(TlsConfig.custom()
.setSupportedProtocols(ProxyConfigurator.SUPPORTED_TLS_VERSIONS)
.build())
.setDefaultConnectionConfig(ConnectionConfig.custom()
.setConnectTimeout(ProxyConfigurator.TIMEOUT_CONNECT)
.setSocketTimeout(ProxyConfigurator.TIMEOUT_SOCKET)
.build())
.setMaxConnPerRoute(4)
.build());
}
private static HttpClientBuilder createSlxBuilder() {
final RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.<ConnectionSocketFactory>create()
.register(URIScheme.HTTP.id, SlxSocketFactory.getSocketFactory())
.register(URIScheme.HTTPS.id, SSLConnectionSocketFactoryBuilder.create()
.setTlsVersions(ProxyConfigurator.SUPPORTED_TLS_VERSIONS)
.build());
final PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registryBuilder.build());
connectionManager.setDefaultTlsConfig(TlsConfig.custom()
.setSupportedProtocols(ProxyConfigurator.SUPPORTED_TLS_VERSIONS)
.build());
connectionManager.setDefaultConnectionConfig(ConnectionConfig.custom()
.setConnectTimeout(ProxyConfigurator.TIMEOUT_CONNECT)
.setSocketTimeout(ProxyConfigurator.TIMEOUT_SOCKET)
.build());
connectionManager.setDefaultMaxPerRoute(4);
return HttpClientBuilder.create()
.setRoutePlanner(new SlxRoutePlanner(null))
.setConnectionManager(connectionManager);
}
private static boolean testHttpsMaster() {
final RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(ProxyConfigurator.TIMEOUT_REQUEST)
.build();
HttpGet httpGet = new HttpGet(ShibbolethEcp.BWLP_SP.toString());
httpGet.setConfig(requestConfig);
httpGet.setHeader("Accept", "text/html, application/vnd.paos+xml");
httpGet.setHeader("PAOS",
"ver=\"urn:liberty:paos:2003-08\";\"urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp\"");
try {
HttpResponse response = getClient().execute(httpGet);
LOGGER.debug("Master server replies with " + response.getCode());
return response.getCode() == 200;
} catch (Exception e) {
LOGGER.debug("Cannot reach master server via HTTPS", e);
return false;
}
}
private static class SlxSocketFactory extends PlainConnectionSocketFactory {
public static final SlxSocketFactory INSTANCE = new SlxSocketFactory();
public static SlxSocketFactory getSocketFactory() {
return INSTANCE;
}
@Override
public Socket createSocket(HttpContext context) throws IOException {
Object obj = context.getAttribute("openslx.l7proxy");
if (obj instanceof HttpRoute) {
HttpRoute route = (HttpRoute) obj;
if (route != null && route.getProxyHost() != null) {
return new Socket(Proxy.NO_PROXY);
}
}
return new Socket();
}
}
private static class SlxRoutePlanner extends SystemDefaultRoutePlanner {
public SlxRoutePlanner(ProxySelector proxySelector) {
super(proxySelector);
}
@Override
public HttpHost determineProxy(final HttpHost target, final HttpContext context)
throws HttpException {
HttpHost host = super.determineProxy(target, context);
context.setAttribute("openslx.l7proxy", host);
return host;
}
}
}