/****************************************************************************
* Copyright (C) 2012 ecsec GmbH.
* All rights reserved.
* Contact: ecsec GmbH (info@ecsec.de)
*
* This file is part of the Open eCard App.
*
* GNU General Public License Usage
* This file may be used under the terms of the GNU General Public
* License version 3.0 as published by the Free Software Foundation
* and appearing in the file LICENSE.GPL included in the packaging of
* this file. Please review the following information to ensure the
* GNU General Public License version 3.0 requirements will be met:
* http://www.gnu.org/copyleft/gpl.html.
*
* Other Usage
* Alternatively, this file may be used in accordance with the terms
* and conditions contained in a signed written agreement between
* you and ecsec GmbH.
*
***************************************************************************/
package org.openecard.crypto.tls.proxy;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import org.openecard.common.OpenecardProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Helper class to set up sockets with a specified or the system proxy.
* The default is to use the system wide proxy settings, but it can also be overloaded with specific settings.
* In order to overload the proxy settings, set the following values in {@link OpenecardProperties}:
* <ul>
* <li>proxy.host</li>
* <li>proxy.port</li>
* </ul>
*
* @author Tobias Wich <tobias.wich@ecsec.de>
*/
public class ProxySettings {
private static final Logger logger = LoggerFactory.getLogger(ProxySettings.class);
private static ProxySettings defaultInstance;
private static Proxy systemProxy;
private final Proxy proxy;
static {
load();
}
/**
* Preload proxy settings according to the global options.
* The load must be performed when the settings change while running.
*/
public static synchronized void load() {
Proxy p = null;
// get config values
String scheme = OpenecardProperties.getProperty("proxy.scheme");
// the empty string is no defined value, thus it means scheme not defined
scheme = scheme != null ? scheme.toUpperCase() : "";
String validate = OpenecardProperties.getProperty("proxy.validate_tls");
String host = OpenecardProperties.getProperty("proxy.host");
String port = OpenecardProperties.getProperty("proxy.port");
String user = OpenecardProperties.getProperty("proxy.user");
String pass = OpenecardProperties.getProperty("proxy.pass");
if ("SOCKS".equals(scheme)) {
// try to load SOCKS proxy
try {
if (host != null && port != null) {
p = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(host, Integer.parseInt(port)));
}
} catch (NumberFormatException ex) {
}
} else if ("HTTP".equals(scheme) || "HTTPS".equals(scheme)) {
// try to load HTTP CONNECT proxy
try {
// the default is always validate
boolean valid = true;
if (validate != null) {
valid = Boolean.parseBoolean(validate);
}
if (host != null && port != null) {
p = new HttpConnectProxy(scheme, valid, host, Integer.parseInt(port), user, pass);
}
} catch (NumberFormatException ex) {
}
} else if (! scheme.isEmpty()) {
logger.warn("Unsupported proxy scheme {} used.", scheme);
}
systemProxy = p;
// instantiate default instance
defaultInstance = new ProxySettings();
}
/**
* Create instance with default settings read from the system.
*/
public ProxySettings() {
this.proxy = null;
}
/**
* Create instance for the given proxy configuration.
*
* @param proxy Proxy to use when creating sockets.
*/
private ProxySettings(Proxy proxy) {
this.proxy = proxy;
}
/**
* Gets default ProxySettings instance.
*
* @see #ProxySettings()
* @return Default ProxySettings instance.
*/
public static ProxySettings getDefault() {
return defaultInstance;
}
/**
* Gets proxy instance for the chosen proxy configuration.
* This may either be a proxy specified when creating the instance, the proxy set via the
* {@link OpenecardProperties}, or the proxy selected by Java's {@link ProxySelector}.<br/>
* In case the ProxySelector is used, the host and port are needed in order to select the correct proxy
* (see {@link ProxySelector#select(java.net.URI)}).
*
* @param hostname Hostname for the proxy determination.
* @param port Port for the proxy determination.
* @return Proxy object according to the configuration of the ProxySettings instance.
* @throws URISyntaxException If host and/or port are invalid.
*/
private Proxy getProxy(String hostname, int port) throws URISyntaxException {
Proxy p;
if (proxy == null) {
// try to use the one from the system settings
if (systemProxy != null) {
p = systemProxy;
} else {
ProxySelector selector = ProxySelector.getDefault();
p = selector.select(new URI("socket://" + hostname + ":" + port)).get(0);
}
} else {
p = proxy;
}
logger.debug("Selecting proxy: {}", p);
return p;
}
/**
* Gets connected socket using the proxy configured in this ProxySettings instance.
*
* @param hostname Host to connect the socket to.
* @param port Port to connect the socket to.
* @return Connected socket
* @throws IOException If socket could not be connected.
* @throws URISyntaxException If proxy could not be determined for the host-port combination.
*/
public Socket getSocket(String hostname, int port) throws IOException, URISyntaxException {
Proxy p = getProxy(hostname, port);
Socket sock;
// HTTP CONNECT proxy is not handled by the Socket class, so do it ourselves
if (p instanceof HttpConnectProxy) {
HttpConnectProxy hp = (HttpConnectProxy) p;
sock = hp.getSocket(hostname, port);
} else {
sock = new Socket(getProxy(hostname, port));
SocketAddress addr = new InetSocketAddress(hostname, port);
sock.setKeepAlive(true);
// this is pretty much, but not a problem, as this only shifts the responsibility to the server
sock.setSoTimeout(5 * 60 * 1000);
sock.connect(addr, 60 * 1000);
}
return sock;
}
}