Skip to content

Commit

Permalink
Merge pull request #91 from soliad/master
Browse files Browse the repository at this point in the history
QFJ-285 Add proxy support (compatible with SSL)
  • Loading branch information
chrjohn committed Dec 13, 2016
2 parents 9fbce5a + ce9ca3c commit 47a0c78
Show file tree
Hide file tree
Showing 6 changed files with 392 additions and 19 deletions.
52 changes: 52 additions & 0 deletions quickfixj-core/src/main/doc/usermanual/usage/configuration.html
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,58 @@ <H3>QuickFIX Settings</H3>
<TD><a href="https://docs.oracle.com/javase/6/docs/technotes/guides/security/SunProviders.html">Java default cipher suites</a></TD>
</TR>

<TR ALIGN="center" VALIGN="middle">
<TD COLSPAN="4" class="subsection"><A NAME="Security">Socks Proxy Options (Initiator only)</A></TD>
</TR>
<TR ALIGN="left" VALIGN="middle">
<TD valign="top"> <I>ProxyType</I></TD>
<TD>Proxy type</TD>
<TD>http<BR>socks</TD>
<TD></TD>
</TR>
<TR ALIGN="left" VALIGN="middle">
<TD valign="top"> <I>ProxyVersion</I></TD>
<TD>Proxy HTTP or Socks version to use</TD>
<TD>For socks: 4, 4a or 5 <BR> For http: 1.0 or 1.1</TD>
<TD>For socks:<BR>For http: 1.0</TD>
</TR>
<TR ALIGN="left" VALIGN="middle">
<TD valign="top"> <I>ProxyHost</I></TD>
<TD>Proxy server hostname or IP</TD>
<TD>valid IP address in the format of x.x.x.x or a domain name</TD>
<TD></TD>
</TR>
<TR ALIGN="left" VALIGN="middle">
<TD valign="top"> <I>ProxyPort</I></TD>
<TD>Proxy server port</TD>
<TD>positive integer</TD>
<TD></TD>
</TR>
<TR ALIGN="left" VALIGN="middle">
<TD valign="top"> <I>ProxyUser</I></TD>
<TD>Proxy user</TD>
<TD></TD>
<TD></TD>
</TR>
<TR ALIGN="left" VALIGN="middle">
<TD valign="top"> <I>ProxyPassword</I></TD>
<TD>Proxy password</TD>
<TD></TD>
<TD></TD>
</TR>
<TR ALIGN="left" VALIGN="middle">
<TD valign="top"> <I>ProxyDomain</I></TD>
<TD>Proxy domain (For http proxy)</TD>
<TD></TD>
<TD></TD>
</TR>
<TR ALIGN="left" VALIGN="middle">
<TD valign="top"> <I>ProxyWorkstation</I></TD>
<TD>Proxy workstation (For http proxy)</TD>
<TD></TD>
<TD></TD>
</TR>

<TR ALIGN="center" VALIGN="middle">
<TD COLSPAN="4" class="subsection"><A NAME="Socket">Socket Options (Acceptor or Initiator)</A></TD>
</TR>
Expand Down
48 changes: 48 additions & 0 deletions quickfixj-core/src/main/java/quickfix/Initiator.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,52 @@ public interface Initiator extends Connector {
* @see quickfix.SessionFactory#SETTING_CONNECTION_TYPE
*/
String SETTING_SOCKET_LOCAL_PORT = "SocketLocalPort";

/**
* Initiator setting for proxy type. Only valid when session connection
* type is "initiator".
*/
String SETTING_PROXY_TYPE = "ProxyType";

/**
* Initiator setting for proxy version. Only valid when session connection
* type is "initiator". - http 1.0 / 1.1
*/
String SETTING_PROXY_VERSION = "ProxyVersion";

/**
* Initiator setting for proxy host. Only valid when session connection
* type is "initiator".
*/
String SETTING_PROXY_HOST = "ProxyHost";

/**
* Initiator setting for proxy port. Only valid when session connection
* type is "initiator".
*/
String SETTING_PROXY_PORT = "ProxyPort";

/**
* Initiator setting for proxy port. Only valid when session connection
* type is "initiator".
*/
String SETTING_PROXY_USER = "ProxyUser";

/**
* Initiator setting for proxy port. Only valid when session connection
* type is "initiator".
*/
String SETTING_PROXY_PASSWORD = "ProxyPassword";

/**
* Initiator setting for proxy domain. Only valid when session connection
* type is "initiator".
*/
String SETTING_PROXY_DOMAIN = "ProxyDomain";

/**
* Initiator setting for proxy workstation. Only valid when session connection
* type is "initiator".
*/
String SETTING_PROXY_WORKSTATION = "ProxyWorkstation";
}
119 changes: 118 additions & 1 deletion quickfixj-core/src/main/java/quickfix/mina/ProtocolFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,28 @@

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;


import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.transport.socket.SocketConnector;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.apache.mina.transport.vmpipe.VmPipeAcceptor;
import org.apache.mina.transport.vmpipe.VmPipeAddress;
import org.apache.mina.transport.vmpipe.VmPipeConnector;
import org.apache.mina.proxy.ProxyConnector;
import org.apache.mina.proxy.handlers.ProxyRequest;
import org.apache.mina.proxy.handlers.http.HttpAuthenticationMethods;
import org.apache.mina.proxy.handlers.http.HttpProxyConstants;
import org.apache.mina.proxy.handlers.http.HttpProxyRequest;
import org.apache.mina.proxy.handlers.socks.SocksProxyConstants;
import org.apache.mina.proxy.handlers.socks.SocksProxyRequest;
import org.apache.mina.proxy.session.ProxyIoSession;


import quickfix.ConfigError;
import quickfix.RuntimeError;
Expand Down Expand Up @@ -58,7 +72,7 @@ public static String getTypeString(int type) {

public static SocketAddress createSocketAddress(int transportType, String host,
int port) throws ConfigError {
if (transportType == SOCKET) {
if (transportType == SOCKET || transportType == PROXY) {
return host != null ? new InetSocketAddress(host, port) : new InetSocketAddress(port);
} else if (transportType == VM_PIPE) {
return new VmPipeAddress(port);
Expand Down Expand Up @@ -102,6 +116,109 @@ public static IoAcceptor createIoAcceptor(int transportType) {
}
}

public static ProxyConnector createIoProxyConnector(SocketConnector socketConnector,
InetSocketAddress address,
InetSocketAddress proxyAddress,
String proxyType,
String proxyVersion,
String proxyUser,
String proxyPassword,
String proxyDomain,
String proxyWorkstation ) throws ConfigError {

// Create proxy connector.
ProxyRequest req;

ProxyConnector connector = new ProxyConnector(socketConnector);
connector.setConnectTimeoutMillis(5000);

if (proxyType.equalsIgnoreCase("http")) {
req = createHttpProxyRequest(address, proxyVersion, proxyUser, proxyPassword, proxyDomain, proxyWorkstation);
} else if (proxyType.equalsIgnoreCase("socks")) {
req = createSocksProxyRequest(address, proxyVersion, proxyUser, proxyPassword);
} else {
throw new ConfigError("Proxy type must be http or socks");
}

ProxyIoSession proxyIoSession = new ProxyIoSession(proxyAddress, req);

List<HttpAuthenticationMethods> l = new ArrayList<>();
l.add(HttpAuthenticationMethods.NO_AUTH);
l.add(HttpAuthenticationMethods.DIGEST);
l.add(HttpAuthenticationMethods.BASIC);

proxyIoSession.setPreferedOrder(l);
connector.setProxyIoSession(proxyIoSession);

return connector;
}


private static ProxyRequest createHttpProxyRequest(InetSocketAddress address,
String proxyVersion,
String proxyUser,
String proxyPassword,
String proxyDomain,
String proxyWorkstation) {
String uri = "http://" + address.getAddress().getHostAddress() + ":" + address.getPort();
HashMap<String, String> props = new HashMap<>();
props.put(HttpProxyConstants.USER_PROPERTY, proxyUser);
props.put(HttpProxyConstants.PWD_PROPERTY, proxyPassword);
if (proxyDomain != null && proxyWorkstation != null) {
props.put(HttpProxyConstants.DOMAIN_PROPERTY, proxyDomain);
props.put(HttpProxyConstants.WORKSTATION_PROPERTY, proxyWorkstation);
}

HttpProxyRequest req = new HttpProxyRequest(uri);
req.setProperties(props);
if (proxyVersion != null && proxyVersion.equalsIgnoreCase("1.1")) {
req.setHttpVersion(HttpProxyConstants.HTTP_1_1);
} else {
req.setHttpVersion(HttpProxyConstants.HTTP_1_0);
}

return req;
}


private static ProxyRequest createSocksProxyRequest(InetSocketAddress address,
String proxyVersion,
String proxyUser,
String proxyPassword) throws ConfigError {
SocksProxyRequest req;
if (proxyVersion.equalsIgnoreCase("4")) {
req = new SocksProxyRequest(
SocksProxyConstants.SOCKS_VERSION_4,
SocksProxyConstants.ESTABLISH_TCPIP_STREAM,
address,
proxyUser);

} else if (proxyVersion.equalsIgnoreCase("4a")) {
req = new SocksProxyRequest(
SocksProxyConstants.ESTABLISH_TCPIP_STREAM,
address.getAddress().getHostAddress(),
address.getPort(),
proxyUser);

} else if (proxyVersion.equalsIgnoreCase("5")) {
req = new SocksProxyRequest(
SocksProxyConstants.SOCKS_VERSION_5,
SocksProxyConstants.ESTABLISH_TCPIP_STREAM,
address,
proxyUser);

} else {
throw new ConfigError("SOCKS ProxyType must be 4,4a or 5");
}

if (proxyPassword != null) {
req.setPassword(proxyPassword);
}

return req;
}


public static IoConnector createIoConnector(SocketAddress address) throws ConfigError {
if (address instanceof InetSocketAddress) {
return new NioSocketConnector();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,46 @@ protected void createSessionInitiators()
sslConfig = SSLSupport.getSslConfig(getSettings(), sessionID);
}

String proxyUser = null;
String proxyPassword = null;
String proxyHost = null;

String proxyType = null;
String proxyVersion = null;

String proxyWorkstation = null;
String proxyDomain = null;

int proxyPort = -1;

if (getSettings().isSetting(sessionID, Initiator.SETTING_PROXY_TYPE)) {
proxyType = settings.getString(sessionID, Initiator.SETTING_PROXY_TYPE);
if (getSettings().isSetting(sessionID, Initiator.SETTING_PROXY_VERSION)) {
proxyVersion = settings.getString(sessionID,
Initiator.SETTING_PROXY_VERSION);
}

if (getSettings().isSetting(sessionID, Initiator.SETTING_PROXY_USER)) {
proxyUser = settings.getString(sessionID, Initiator.SETTING_PROXY_USER);
proxyPassword = settings.getString(sessionID,
Initiator.SETTING_PROXY_PASSWORD);
}
if (getSettings().isSetting(sessionID, Initiator.SETTING_PROXY_WORKSTATION)
&& getSettings().isSetting(sessionID, Initiator.SETTING_PROXY_DOMAIN)) {
proxyWorkstation = settings.getString(sessionID,
Initiator.SETTING_PROXY_WORKSTATION);
proxyDomain = settings.getString(sessionID, Initiator.SETTING_PROXY_DOMAIN);
}

proxyHost = settings.getString(sessionID, Initiator.SETTING_PROXY_HOST);
proxyPort = (int) settings.getLong(sessionID, Initiator.SETTING_PROXY_PORT);
}

final IoSessionInitiator ioSessionInitiator = new IoSessionInitiator(session,
socketAddresses, localAddress, reconnectingIntervals, getScheduledExecutorService(),
networkingOptions, getEventHandlingStrategy(), getIoFilterChainBuilder(),
sslEnabled, sslConfig);
socketAddresses, localAddress, reconnectingIntervals,
getScheduledExecutorService(), networkingOptions,
getEventHandlingStrategy(), getIoFilterChainBuilder(), sslEnabled, sslConfig,
proxyType, proxyVersion, proxyHost, proxyPort, proxyUser, proxyPassword, proxyDomain, proxyWorkstation);

initiators.add(ioSessionInitiator);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*******************************************************************************
* Copyright (c) quickfixengine.org All rights reserved.
*
* This file is part of the QuickFIX FIX Engine
*
* This file may be distributed under the terms of the quickfixengine.org
* license as defined by quickfixengine.org and appearing in the file
* LICENSE included in the packaging of this file.
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
* THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE.
*
* See http://www.quickfixengine.org/LICENSE for licensing information.
*
* Contact ask@quickfixengine.org if any conditions of this licensing
* are not clear to you.
******************************************************************************/

package quickfix.mina.initiator;

import org.apache.mina.core.session.IoSession;
import org.apache.mina.proxy.AbstractProxyIoHandler;

import quickfix.mina.ssl.SSLFilter;

class InitiatorProxyIoHandler extends AbstractProxyIoHandler {
private final InitiatorIoHandler initiatorIoHandler;
private final SSLFilter sslFilter;

InitiatorProxyIoHandler(InitiatorIoHandler initiatorIoHandler, SSLFilter sslFilter) {
super();
this.initiatorIoHandler = initiatorIoHandler;
this.sslFilter = sslFilter;
}

@Override
public void sessionCreated(IoSession session) throws Exception {
this.initiatorIoHandler.sessionCreated(session);
}

@Override
public void sessionClosed(IoSession ioSession) throws Exception {
this.initiatorIoHandler.sessionClosed(ioSession);
}

@Override
public void messageReceived(IoSession session, Object message) throws Exception {
this.initiatorIoHandler.messageReceived(session, message);
}

@Override
public void messageSent(IoSession session, Object message) throws Exception {
this.initiatorIoHandler.messageSent(session, message);
}

@Override
public void exceptionCaught(IoSession ioSession, Throwable cause) throws Exception {
this.initiatorIoHandler.exceptionCaught(ioSession, cause);
}

@Override
public void proxySessionOpened(IoSession ioSession) throws Exception {
if (this.sslFilter != null) {
this.sslFilter.initiateHandshake(ioSession);
}
}
}

0 comments on commit 47a0c78

Please sign in to comment.