Skip to content

Commit 47a0c78

Browse files
authored
Merge pull request #91 from soliad/master
QFJ-285 Add proxy support (compatible with SSL)
2 parents 9fbce5a + ce9ca3c commit 47a0c78

File tree

6 files changed

+392
-19
lines changed

6 files changed

+392
-19
lines changed

quickfixj-core/src/main/doc/usermanual/usage/configuration.html

+52
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,58 @@ <H3>QuickFIX Settings</H3>
626626
<TD><a href="https://docs.oracle.com/javase/6/docs/technotes/guides/security/SunProviders.html">Java default cipher suites</a></TD>
627627
</TR>
628628

629+
<TR ALIGN="center" VALIGN="middle">
630+
<TD COLSPAN="4" class="subsection"><A NAME="Security">Socks Proxy Options (Initiator only)</A></TD>
631+
</TR>
632+
<TR ALIGN="left" VALIGN="middle">
633+
<TD valign="top"> <I>ProxyType</I></TD>
634+
<TD>Proxy type</TD>
635+
<TD>http<BR>socks</TD>
636+
<TD></TD>
637+
</TR>
638+
<TR ALIGN="left" VALIGN="middle">
639+
<TD valign="top"> <I>ProxyVersion</I></TD>
640+
<TD>Proxy HTTP or Socks version to use</TD>
641+
<TD>For socks: 4, 4a or 5 <BR> For http: 1.0 or 1.1</TD>
642+
<TD>For socks:<BR>For http: 1.0</TD>
643+
</TR>
644+
<TR ALIGN="left" VALIGN="middle">
645+
<TD valign="top"> <I>ProxyHost</I></TD>
646+
<TD>Proxy server hostname or IP</TD>
647+
<TD>valid IP address in the format of x.x.x.x or a domain name</TD>
648+
<TD></TD>
649+
</TR>
650+
<TR ALIGN="left" VALIGN="middle">
651+
<TD valign="top"> <I>ProxyPort</I></TD>
652+
<TD>Proxy server port</TD>
653+
<TD>positive integer</TD>
654+
<TD></TD>
655+
</TR>
656+
<TR ALIGN="left" VALIGN="middle">
657+
<TD valign="top"> <I>ProxyUser</I></TD>
658+
<TD>Proxy user</TD>
659+
<TD></TD>
660+
<TD></TD>
661+
</TR>
662+
<TR ALIGN="left" VALIGN="middle">
663+
<TD valign="top"> <I>ProxyPassword</I></TD>
664+
<TD>Proxy password</TD>
665+
<TD></TD>
666+
<TD></TD>
667+
</TR>
668+
<TR ALIGN="left" VALIGN="middle">
669+
<TD valign="top"> <I>ProxyDomain</I></TD>
670+
<TD>Proxy domain (For http proxy)</TD>
671+
<TD></TD>
672+
<TD></TD>
673+
</TR>
674+
<TR ALIGN="left" VALIGN="middle">
675+
<TD valign="top"> <I>ProxyWorkstation</I></TD>
676+
<TD>Proxy workstation (For http proxy)</TD>
677+
<TD></TD>
678+
<TD></TD>
679+
</TR>
680+
629681
<TR ALIGN="center" VALIGN="middle">
630682
<TD COLSPAN="4" class="subsection"><A NAME="Socket">Socket Options (Acceptor or Initiator)</A></TD>
631683
</TR>

quickfixj-core/src/main/java/quickfix/Initiator.java

+48
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,52 @@ public interface Initiator extends Connector {
6868
* @see quickfix.SessionFactory#SETTING_CONNECTION_TYPE
6969
*/
7070
String SETTING_SOCKET_LOCAL_PORT = "SocketLocalPort";
71+
72+
/**
73+
* Initiator setting for proxy type. Only valid when session connection
74+
* type is "initiator".
75+
*/
76+
String SETTING_PROXY_TYPE = "ProxyType";
77+
78+
/**
79+
* Initiator setting for proxy version. Only valid when session connection
80+
* type is "initiator". - http 1.0 / 1.1
81+
*/
82+
String SETTING_PROXY_VERSION = "ProxyVersion";
83+
84+
/**
85+
* Initiator setting for proxy host. Only valid when session connection
86+
* type is "initiator".
87+
*/
88+
String SETTING_PROXY_HOST = "ProxyHost";
89+
90+
/**
91+
* Initiator setting for proxy port. Only valid when session connection
92+
* type is "initiator".
93+
*/
94+
String SETTING_PROXY_PORT = "ProxyPort";
95+
96+
/**
97+
* Initiator setting for proxy port. Only valid when session connection
98+
* type is "initiator".
99+
*/
100+
String SETTING_PROXY_USER = "ProxyUser";
101+
102+
/**
103+
* Initiator setting for proxy port. Only valid when session connection
104+
* type is "initiator".
105+
*/
106+
String SETTING_PROXY_PASSWORD = "ProxyPassword";
107+
108+
/**
109+
* Initiator setting for proxy domain. Only valid when session connection
110+
* type is "initiator".
111+
*/
112+
String SETTING_PROXY_DOMAIN = "ProxyDomain";
113+
114+
/**
115+
* Initiator setting for proxy workstation. Only valid when session connection
116+
* type is "initiator".
117+
*/
118+
String SETTING_PROXY_WORKSTATION = "ProxyWorkstation";
71119
}

quickfixj-core/src/main/java/quickfix/mina/ProtocolFactory.java

+118-1
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,28 @@
2121

2222
import java.net.InetSocketAddress;
2323
import java.net.SocketAddress;
24+
import java.util.ArrayList;
25+
import java.util.List;
26+
import java.util.HashMap;
27+
2428

2529
import org.apache.mina.core.service.IoAcceptor;
2630
import org.apache.mina.core.service.IoConnector;
31+
import org.apache.mina.transport.socket.SocketConnector;
2732
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
2833
import org.apache.mina.transport.socket.nio.NioSocketConnector;
2934
import org.apache.mina.transport.vmpipe.VmPipeAcceptor;
3035
import org.apache.mina.transport.vmpipe.VmPipeAddress;
3136
import org.apache.mina.transport.vmpipe.VmPipeConnector;
37+
import org.apache.mina.proxy.ProxyConnector;
38+
import org.apache.mina.proxy.handlers.ProxyRequest;
39+
import org.apache.mina.proxy.handlers.http.HttpAuthenticationMethods;
40+
import org.apache.mina.proxy.handlers.http.HttpProxyConstants;
41+
import org.apache.mina.proxy.handlers.http.HttpProxyRequest;
42+
import org.apache.mina.proxy.handlers.socks.SocksProxyConstants;
43+
import org.apache.mina.proxy.handlers.socks.SocksProxyRequest;
44+
import org.apache.mina.proxy.session.ProxyIoSession;
45+
3246

3347
import quickfix.ConfigError;
3448
import quickfix.RuntimeError;
@@ -58,7 +72,7 @@ public static String getTypeString(int type) {
5872

5973
public static SocketAddress createSocketAddress(int transportType, String host,
6074
int port) throws ConfigError {
61-
if (transportType == SOCKET) {
75+
if (transportType == SOCKET || transportType == PROXY) {
6276
return host != null ? new InetSocketAddress(host, port) : new InetSocketAddress(port);
6377
} else if (transportType == VM_PIPE) {
6478
return new VmPipeAddress(port);
@@ -102,6 +116,109 @@ public static IoAcceptor createIoAcceptor(int transportType) {
102116
}
103117
}
104118

119+
public static ProxyConnector createIoProxyConnector(SocketConnector socketConnector,
120+
InetSocketAddress address,
121+
InetSocketAddress proxyAddress,
122+
String proxyType,
123+
String proxyVersion,
124+
String proxyUser,
125+
String proxyPassword,
126+
String proxyDomain,
127+
String proxyWorkstation ) throws ConfigError {
128+
129+
// Create proxy connector.
130+
ProxyRequest req;
131+
132+
ProxyConnector connector = new ProxyConnector(socketConnector);
133+
connector.setConnectTimeoutMillis(5000);
134+
135+
if (proxyType.equalsIgnoreCase("http")) {
136+
req = createHttpProxyRequest(address, proxyVersion, proxyUser, proxyPassword, proxyDomain, proxyWorkstation);
137+
} else if (proxyType.equalsIgnoreCase("socks")) {
138+
req = createSocksProxyRequest(address, proxyVersion, proxyUser, proxyPassword);
139+
} else {
140+
throw new ConfigError("Proxy type must be http or socks");
141+
}
142+
143+
ProxyIoSession proxyIoSession = new ProxyIoSession(proxyAddress, req);
144+
145+
List<HttpAuthenticationMethods> l = new ArrayList<>();
146+
l.add(HttpAuthenticationMethods.NO_AUTH);
147+
l.add(HttpAuthenticationMethods.DIGEST);
148+
l.add(HttpAuthenticationMethods.BASIC);
149+
150+
proxyIoSession.setPreferedOrder(l);
151+
connector.setProxyIoSession(proxyIoSession);
152+
153+
return connector;
154+
}
155+
156+
157+
private static ProxyRequest createHttpProxyRequest(InetSocketAddress address,
158+
String proxyVersion,
159+
String proxyUser,
160+
String proxyPassword,
161+
String proxyDomain,
162+
String proxyWorkstation) {
163+
String uri = "http://" + address.getAddress().getHostAddress() + ":" + address.getPort();
164+
HashMap<String, String> props = new HashMap<>();
165+
props.put(HttpProxyConstants.USER_PROPERTY, proxyUser);
166+
props.put(HttpProxyConstants.PWD_PROPERTY, proxyPassword);
167+
if (proxyDomain != null && proxyWorkstation != null) {
168+
props.put(HttpProxyConstants.DOMAIN_PROPERTY, proxyDomain);
169+
props.put(HttpProxyConstants.WORKSTATION_PROPERTY, proxyWorkstation);
170+
}
171+
172+
HttpProxyRequest req = new HttpProxyRequest(uri);
173+
req.setProperties(props);
174+
if (proxyVersion != null && proxyVersion.equalsIgnoreCase("1.1")) {
175+
req.setHttpVersion(HttpProxyConstants.HTTP_1_1);
176+
} else {
177+
req.setHttpVersion(HttpProxyConstants.HTTP_1_0);
178+
}
179+
180+
return req;
181+
}
182+
183+
184+
private static ProxyRequest createSocksProxyRequest(InetSocketAddress address,
185+
String proxyVersion,
186+
String proxyUser,
187+
String proxyPassword) throws ConfigError {
188+
SocksProxyRequest req;
189+
if (proxyVersion.equalsIgnoreCase("4")) {
190+
req = new SocksProxyRequest(
191+
SocksProxyConstants.SOCKS_VERSION_4,
192+
SocksProxyConstants.ESTABLISH_TCPIP_STREAM,
193+
address,
194+
proxyUser);
195+
196+
} else if (proxyVersion.equalsIgnoreCase("4a")) {
197+
req = new SocksProxyRequest(
198+
SocksProxyConstants.ESTABLISH_TCPIP_STREAM,
199+
address.getAddress().getHostAddress(),
200+
address.getPort(),
201+
proxyUser);
202+
203+
} else if (proxyVersion.equalsIgnoreCase("5")) {
204+
req = new SocksProxyRequest(
205+
SocksProxyConstants.SOCKS_VERSION_5,
206+
SocksProxyConstants.ESTABLISH_TCPIP_STREAM,
207+
address,
208+
proxyUser);
209+
210+
} else {
211+
throw new ConfigError("SOCKS ProxyType must be 4,4a or 5");
212+
}
213+
214+
if (proxyPassword != null) {
215+
req.setPassword(proxyPassword);
216+
}
217+
218+
return req;
219+
}
220+
221+
105222
public static IoConnector createIoConnector(SocketAddress address) throws ConfigError {
106223
if (address instanceof InetSocketAddress) {
107224
return new NioSocketConnector();

quickfixj-core/src/main/java/quickfix/mina/initiator/AbstractSocketInitiator.java

+39-3
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,46 @@ protected void createSessionInitiators()
104104
sslConfig = SSLSupport.getSslConfig(getSettings(), sessionID);
105105
}
106106

107+
String proxyUser = null;
108+
String proxyPassword = null;
109+
String proxyHost = null;
110+
111+
String proxyType = null;
112+
String proxyVersion = null;
113+
114+
String proxyWorkstation = null;
115+
String proxyDomain = null;
116+
117+
int proxyPort = -1;
118+
119+
if (getSettings().isSetting(sessionID, Initiator.SETTING_PROXY_TYPE)) {
120+
proxyType = settings.getString(sessionID, Initiator.SETTING_PROXY_TYPE);
121+
if (getSettings().isSetting(sessionID, Initiator.SETTING_PROXY_VERSION)) {
122+
proxyVersion = settings.getString(sessionID,
123+
Initiator.SETTING_PROXY_VERSION);
124+
}
125+
126+
if (getSettings().isSetting(sessionID, Initiator.SETTING_PROXY_USER)) {
127+
proxyUser = settings.getString(sessionID, Initiator.SETTING_PROXY_USER);
128+
proxyPassword = settings.getString(sessionID,
129+
Initiator.SETTING_PROXY_PASSWORD);
130+
}
131+
if (getSettings().isSetting(sessionID, Initiator.SETTING_PROXY_WORKSTATION)
132+
&& getSettings().isSetting(sessionID, Initiator.SETTING_PROXY_DOMAIN)) {
133+
proxyWorkstation = settings.getString(sessionID,
134+
Initiator.SETTING_PROXY_WORKSTATION);
135+
proxyDomain = settings.getString(sessionID, Initiator.SETTING_PROXY_DOMAIN);
136+
}
137+
138+
proxyHost = settings.getString(sessionID, Initiator.SETTING_PROXY_HOST);
139+
proxyPort = (int) settings.getLong(sessionID, Initiator.SETTING_PROXY_PORT);
140+
}
141+
107142
final IoSessionInitiator ioSessionInitiator = new IoSessionInitiator(session,
108-
socketAddresses, localAddress, reconnectingIntervals, getScheduledExecutorService(),
109-
networkingOptions, getEventHandlingStrategy(), getIoFilterChainBuilder(),
110-
sslEnabled, sslConfig);
143+
socketAddresses, localAddress, reconnectingIntervals,
144+
getScheduledExecutorService(), networkingOptions,
145+
getEventHandlingStrategy(), getIoFilterChainBuilder(), sslEnabled, sslConfig,
146+
proxyType, proxyVersion, proxyHost, proxyPort, proxyUser, proxyPassword, proxyDomain, proxyWorkstation);
111147

112148
initiators.add(ioSessionInitiator);
113149
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*******************************************************************************
2+
* Copyright (c) quickfixengine.org All rights reserved.
3+
*
4+
* This file is part of the QuickFIX FIX Engine
5+
*
6+
* This file may be distributed under the terms of the quickfixengine.org
7+
* license as defined by quickfixengine.org and appearing in the file
8+
* LICENSE included in the packaging of this file.
9+
*
10+
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
11+
* THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
12+
* PARTICULAR PURPOSE.
13+
*
14+
* See http://www.quickfixengine.org/LICENSE for licensing information.
15+
*
16+
* Contact [email protected] if any conditions of this licensing
17+
* are not clear to you.
18+
******************************************************************************/
19+
20+
package quickfix.mina.initiator;
21+
22+
import org.apache.mina.core.session.IoSession;
23+
import org.apache.mina.proxy.AbstractProxyIoHandler;
24+
25+
import quickfix.mina.ssl.SSLFilter;
26+
27+
class InitiatorProxyIoHandler extends AbstractProxyIoHandler {
28+
private final InitiatorIoHandler initiatorIoHandler;
29+
private final SSLFilter sslFilter;
30+
31+
InitiatorProxyIoHandler(InitiatorIoHandler initiatorIoHandler, SSLFilter sslFilter) {
32+
super();
33+
this.initiatorIoHandler = initiatorIoHandler;
34+
this.sslFilter = sslFilter;
35+
}
36+
37+
@Override
38+
public void sessionCreated(IoSession session) throws Exception {
39+
this.initiatorIoHandler.sessionCreated(session);
40+
}
41+
42+
@Override
43+
public void sessionClosed(IoSession ioSession) throws Exception {
44+
this.initiatorIoHandler.sessionClosed(ioSession);
45+
}
46+
47+
@Override
48+
public void messageReceived(IoSession session, Object message) throws Exception {
49+
this.initiatorIoHandler.messageReceived(session, message);
50+
}
51+
52+
@Override
53+
public void messageSent(IoSession session, Object message) throws Exception {
54+
this.initiatorIoHandler.messageSent(session, message);
55+
}
56+
57+
@Override
58+
public void exceptionCaught(IoSession ioSession, Throwable cause) throws Exception {
59+
this.initiatorIoHandler.exceptionCaught(ioSession, cause);
60+
}
61+
62+
@Override
63+
public void proxySessionOpened(IoSession ioSession) throws Exception {
64+
if (this.sslFilter != null) {
65+
this.sslFilter.initiateHandshake(ioSession);
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)