Skip to content

QFJ-285 Add proxy support (compatible with SSL) #91

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 13, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}
}
}
Loading