Skip to content

Commit

Permalink
Moved creation of ProxySocketFactory in to DxlClient#doConnect so
Browse files Browse the repository at this point in the history
that even socket factories created via a sslSocketFactoryCallback can
also be used via proxy. Also added comments to
ProxyUsageVerificationTest
  • Loading branch information
krisleonard-mcafee committed Jul 3, 2019
1 parent cae1f42 commit cc9f2ce
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 47 deletions.
44 changes: 37 additions & 7 deletions src/main/java/com/opendxl/client/DxlClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.opendxl.client.message.Request;
import com.opendxl.client.message.Response;
import com.opendxl.client.util.UuidGenerator;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
Expand All @@ -24,6 +25,8 @@

import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.security.KeyStore;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -1253,9 +1256,7 @@ private synchronized void doInit() throws DxlException {

try {
if (ks != null && this.sslSocketFactoryCallback == null) {
this.socketFactory = SSLValidationSocketFactory.newInstance(ks, DxlClientConfig.KS_PASS,
config.isUseWebSockets(), config.getProxyAddress(), config.getProxyPort(),
config.getProxyUserName(), config.getProxyPassword());
this.socketFactory = SSLValidationSocketFactory.newInstance(ks, DxlClientConfig.KS_PASS);
}
//
// Each thread is a daemon thread.
Expand Down Expand Up @@ -1343,13 +1344,42 @@ private synchronized void doConnect(final Map<String, Broker> brokers)

connectOps.setHttpsHostnameVerificationEnabled(getConfig().isHttpsHostnameVerificationEnabled());

// Set socket factory if applicable
// Get socket factory
SSLSocketFactory connectOpsSocketFactory;
if (this.sslSocketFactoryCallback != null) {
connectOps.setSocketFactory(this.sslSocketFactoryCallback.createFactory(getConfig()));
connectOpsSocketFactory = this.sslSocketFactoryCallback.createFactory(getConfig());
} else {
connectOps.setSocketFactory(socketFactory);
connectOpsSocketFactory = socketFactory;
}

// Wrap the socket factory in the ProxySocketFactory if there is a proxy host address
if (StringUtils.isNotBlank(this.config.getProxyAddress())) {
// Create proxy socket factory
connectOpsSocketFactory =
new ProxySocketFactory(connectOpsSocketFactory,
this.config.getProxyAddress(), this.config.getProxyPort());

// Set the default Authenticator if there is a proxy username and password
if (StringUtils.isNotBlank(this.config.getProxyUserName())) {
// Basic authentication was disabled in Java 8
// (https://www.oracle.com/technetwork/java/javase/8u111-relnotes-3124969.html)
// This is a workaround to re-enable basic authentication when making a proxy connection.
// The other alternative is to set this as a system property when starting the OpenDXL Java Client:
// -Djdk.http.auth.tunneling.disabledSchemes= ""
System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(
getConfig().getProxyUserName(), getConfig().getProxyPassword());
}
});
}
}

// Set the socket factory on the connectOps
connectOps.setSocketFactory(connectOpsSocketFactory);

for (Map.Entry<String, Broker> entry : brokers.entrySet()) {
if (this.interrupt.get()) {
break;
Expand Down Expand Up @@ -1556,7 +1586,7 @@ protected interface SslSocketFactoryCallback {
* @throws Exception If an error occurs
*/
SSLSocketFactory createFactory(DxlClientConfig config) throws Exception;
};
}

/**
* Implements the {@link MqttCallback} interface (used to received callbacks from the MQTT client.
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/com/opendxl/client/ProxySocketFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ class ProxySocketFactory extends SSLSocketFactory {
* @return The proxy port
*/
private static Integer resolveProxyPort() {
return Integer.valueOf(System.getProperty("http.proxyPort", "3128"));
return Integer.valueOf(System.getProperty("https.proxyPort",
System.getProperty("http.proxyPort", "3128")));
}

/**
Expand All @@ -57,7 +58,8 @@ private static Integer resolveProxyPort() {
* @return The proxy host
*/
private static String resolveProxyHost() {
return System.getProperty("http.proxyHost", "localhost");
return System.getProperty("https.proxyHost",
System.getProperty("http.proxyHost", "localhost"));
}

/**
Expand Down
41 changes: 4 additions & 37 deletions src/main/java/com/opendxl/client/SSLValidationSocketFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,16 @@

package com.opendxl.client;

import org.apache.commons.lang3.StringUtils;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.security.KeyStore;
import java.security.SecureRandom;

/**
* Helper class for SSL connections
* <p>
* <P>
* Performs validation of presented server certificates and client authentication
* </P>
*/
Expand All @@ -40,15 +36,12 @@ private SSLValidationSocketFactory() {
* We always return a new instance to avoid caching which wouldn't accurately represent a separate client
* connecting to a broker.
*
* @param keyStore The keystore
* @param keyStore The keystore
* @param keyStorePassword The keystore
* @return A new instance of an {@link javax.net.ssl.SSLSocketFactory} that validates presented certificates.
* @throws Exception If an SSL exception occurs
*/
public static SSLSocketFactory newInstance(final KeyStore keyStore, final String keyStorePassword,
final boolean useWebSockets, final String proxyHost,
final int proxyPort, final String proxyUserName,
final char[] proxyPassword)
public static SSLSocketFactory newInstance(final KeyStore keyStore, final String keyStorePassword)
throws Exception {

final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
Expand All @@ -59,32 +52,6 @@ public static SSLSocketFactory newInstance(final KeyStore keyStore, final String

final SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), secureRandom);

// If there is no proxy return a non proxy socket factory
if (!useWebSockets || StringUtils.isBlank(proxyHost)) {
return sslContext.getSocketFactory();
}

// Create proxy socket factory
ProxySocketFactory proxySocketFactory =
new ProxySocketFactory(sslContext.getSocketFactory(), proxyHost, proxyPort);

// Set the default Authenticator if there is a proxy username and password
if (StringUtils.isNotBlank(proxyUserName)) {
// Basic authentication was disabled in Java 8
// (https://www.oracle.com/technetwork/java/javase/8u111-relnotes-3124969.html)
// This is a workaround to re-enable basic authentication when making a proxy connection.
// The other alternative is to set this as a system property when starting the OpenDXL Java Client:
// -Djdk.http.auth.tunneling.disabledSchemes= ""
System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(proxyUserName, proxyPassword);
}
});
}

return proxySocketFactory;
return sslContext.getSocketFactory();
}
}
13 changes: 12 additions & 1 deletion src/test/java/com/opendxl/client/ProxyUsageVerificationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,27 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

/**
* Test class for verifying WebSocket connection to a broker via a proxy
*/
public class ProxyUsageVerificationTest {

/**
* Test to verifying WebSocket connection to a broker via a proxy
*
* @throws DxlException If there is an issue getting the DXL Client config
*/
@Test
public void verifyProxyUsage() throws DxlException {

DxlClientImplFactory dxlClientImplFactory = DxlClientImplFactory.getDefaultInstance();
DxlClientConfig config = dxlClientImplFactory.getConfig();

int originalProxyPort = config.getProxyPort();

// Modify the proxy port to be something other than the proxy port
config.setProxyPort(originalProxyPort - 10);

// Attempt to connect with the invalid proxy information and expect a connection refused failure
try (DxlClient client = new DxlClient(config)) {
client.getConfig().setConnectRetries(1);
client.connect();
Expand All @@ -26,6 +36,7 @@ public void verifyProxyUsage() throws DxlException {
assertTrue(e.getMessage().contains("Unable to connect to server: Connection refused"));
}

// Attempt to connect with the valid proxy information and do not expect an exception
config.setProxyPort(originalProxyPort);
try (DxlClient client = new DxlClient(config)) {
client.getConfig().setConnectRetries(1);
Expand Down

0 comments on commit cc9f2ce

Please sign in to comment.