Skip to content

Commit 819c58d

Browse files
authored
Merge pull request #376 from michaelwilner/master
Socks proxy dialer
2 parents e80754d + 858f2c9 commit 819c58d

File tree

6 files changed

+176
-29
lines changed

6 files changed

+176
-29
lines changed

Gopkg.lock

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/configuration.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ const (
2323
SocketMinimumTLSVersion string = "SocketMinimumTLSVersion"
2424
SocketTimeout string = "SocketTimeout"
2525
SocketUseSSL string = "SocketUseSSL"
26+
ProxyType string = "ProxyType"
27+
ProxyHost string = "ProxyHost"
28+
ProxyPort string = "ProxyPort"
29+
ProxyUser string = "ProxyUser"
30+
ProxyPassword string = "ProxyPassword"
2631
DefaultApplVerID string = "DefaultApplVerID"
2732
StartTime string = "StartTime"
2833
EndTime string = "EndTime"

config/doc.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,27 @@ SocketUseSSL
288288
289289
Use SSL for initiators even if client certificates are not present. If set to N or omitted, TLS will not be used if SocketPrivateKeyFile or SocketCertificateFile are not supplied.
290290
291+
ProxyType
292+
293+
Proxy type. Valid Values:
294+
socks
295+
296+
ProxyHost
297+
298+
Proxy server IP address in the format of x.x.x.x or a domain name
299+
300+
ProxyPort
301+
302+
Proxy server port
303+
304+
ProxyUser
305+
306+
Proxy user
307+
308+
ProxyPassword
309+
310+
Proxy password
311+
291312
PersistMessages
292313
293314
If set to N, no messages will be persisted. This will force QuickFIX/Go to always send GapFills instead of resending messages. Use this if you know you never want to resend a message. Useful for market data streams. Valid Values:

dialer.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package quickfix
2+
3+
import (
4+
"fmt"
5+
"github.com/quickfixgo/quickfix/config"
6+
"golang.org/x/net/proxy"
7+
"net"
8+
)
9+
10+
func loadDialerConfig(settings *SessionSettings) (dialer proxy.Dialer, err error) {
11+
stdDialer := &net.Dialer{}
12+
if settings.HasSetting(config.SocketTimeout) {
13+
if stdDialer.Timeout, err = settings.DurationSetting(config.SocketTimeout); err != nil {
14+
return
15+
}
16+
}
17+
dialer = stdDialer
18+
19+
if !settings.HasSetting(config.ProxyType) {
20+
return
21+
}
22+
23+
var proxyType string
24+
if proxyType, err = settings.Setting(config.ProxyType); err != nil {
25+
return
26+
}
27+
28+
switch proxyType {
29+
case "socks":
30+
var proxyHost string
31+
var proxyPort int
32+
if proxyHost, err = settings.Setting(config.ProxyHost); err != nil {
33+
return
34+
} else if proxyPort, err = settings.IntSetting(config.ProxyPort); err != nil {
35+
return
36+
}
37+
38+
proxyAuth := new(proxy.Auth)
39+
if settings.HasSetting(config.ProxyUser) {
40+
if proxyAuth.User, err = settings.Setting(config.ProxyUser); err != nil {
41+
return
42+
}
43+
}
44+
if settings.HasSetting(config.ProxyPassword) {
45+
if proxyAuth.Password, err = settings.Setting(config.ProxyPassword); err != nil {
46+
return
47+
}
48+
}
49+
50+
dialer, err = proxy.SOCKS5("tcp", fmt.Sprintf("%s:%d", proxyHost, proxyPort), proxyAuth, dialer)
51+
default:
52+
err = fmt.Errorf("unsupported proxy type %s", proxyType)
53+
}
54+
return
55+
}

dialer_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package quickfix
2+
3+
import (
4+
"github.com/quickfixgo/quickfix/config"
5+
"github.com/stretchr/testify/suite"
6+
"net"
7+
"testing"
8+
"time"
9+
)
10+
11+
type DialerTestSuite struct {
12+
suite.Suite
13+
settings *Settings
14+
}
15+
16+
func TestDialerTestSuite(t *testing.T) {
17+
suite.Run(t, new(DialerTestSuite))
18+
}
19+
20+
func (s *DialerTestSuite) SetupTest() {
21+
s.settings = NewSettings()
22+
}
23+
24+
func (s *DialerTestSuite) TestLoadDialerNoSettings() {
25+
dialer, err := loadDialerConfig(s.settings.GlobalSettings())
26+
s.Require().Nil(err)
27+
28+
stdDialer, ok := dialer.(*net.Dialer)
29+
s.Require().True(ok)
30+
s.Require().NotNil(stdDialer)
31+
s.Zero(stdDialer.Timeout)
32+
}
33+
34+
func (s *DialerTestSuite) TestLoadDialerWithTimeout() {
35+
s.settings.GlobalSettings().Set(config.SocketTimeout, "10s")
36+
dialer, err := loadDialerConfig(s.settings.GlobalSettings())
37+
s.Require().Nil(err)
38+
39+
stdDialer, ok := dialer.(*net.Dialer)
40+
s.Require().True(ok)
41+
s.Require().NotNil(stdDialer)
42+
s.EqualValues(10*time.Second, stdDialer.Timeout)
43+
}
44+
45+
func (s *DialerTestSuite) TestLoadDialerInvalidProxy() {
46+
s.settings.GlobalSettings().Set(config.ProxyType, "totallyinvalidproxytype")
47+
_, err := loadDialerConfig(s.settings.GlobalSettings())
48+
s.Require().NotNil(err)
49+
}
50+
51+
func (s *DialerTestSuite) TestLoadDialerSocksProxy() {
52+
s.settings.GlobalSettings().Set(config.ProxyType, "socks")
53+
s.settings.GlobalSettings().Set(config.ProxyHost, "localhost")
54+
s.settings.GlobalSettings().Set(config.ProxyPort, "31337")
55+
dialer, err := loadDialerConfig(s.settings.GlobalSettings())
56+
s.Require().Nil(err)
57+
s.Require().NotNil(dialer)
58+
59+
_, ok := dialer.(*net.Dialer)
60+
s.Require().False(ok)
61+
}
62+
63+
func (s *DialerTestSuite) TestLoadDialerSocksProxyInvalidHost() {
64+
s.settings.GlobalSettings().Set(config.ProxyType, "socks")
65+
s.settings.GlobalSettings().Set(config.ProxyPort, "31337")
66+
_, err := loadDialerConfig(s.settings.GlobalSettings())
67+
s.Require().NotNil(err)
68+
}
69+
70+
func (s *DialerTestSuite) TestLoadDialerSocksProxyInvalidPort() {
71+
s.settings.GlobalSettings().Set(config.ProxyType, "socks")
72+
s.settings.GlobalSettings().Set(config.ProxyHost, "localhost")
73+
_, err := loadDialerConfig(s.settings.GlobalSettings())
74+
s.Require().NotNil(err)
75+
}

initiator.go

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ package quickfix
33
import (
44
"bufio"
55
"crypto/tls"
6-
"net"
6+
"golang.org/x/net/proxy"
77
"sync"
88
"time"
9-
10-
"github.com/quickfixgo/quickfix/config"
119
)
1210

1311
//Initiator initiates connections and processes messages for all sessions.
@@ -35,15 +33,14 @@ func (i *Initiator) Start() (err error) {
3533
return
3634
}
3735

38-
dialTimeout := time.Duration(0)
39-
if settings.HasSetting(config.SocketTimeout) {
40-
if dialTimeout, err = settings.DurationSetting(config.SocketTimeout); err != nil {
41-
return
42-
}
36+
var dialer proxy.Dialer
37+
if dialer, err = loadDialerConfig(settings); err != nil {
38+
return
4339
}
40+
4441
i.wg.Add(1)
4542
go func(sessID SessionID) {
46-
i.handleConnection(i.sessions[sessID], tlsConfig, dialTimeout)
43+
i.handleConnection(i.sessions[sessID], tlsConfig, dialer)
4744
i.wg.Done()
4845
}(sessionID)
4946
}
@@ -121,7 +118,7 @@ func (i *Initiator) waitForReconnectInterval(reconnectInterval time.Duration) bo
121118
return true
122119
}
123120

124-
func (i *Initiator) handleConnection(session *session, tlsConfig *tls.Config, dialTimeout time.Duration) {
121+
func (i *Initiator) handleConnection(session *session, tlsConfig *tls.Config, dialer proxy.Dialer) {
125122
var wg sync.WaitGroup
126123
wg.Add(1)
127124
go func() {
@@ -148,27 +145,17 @@ func (i *Initiator) handleConnection(session *session, tlsConfig *tls.Config, di
148145
address := session.SocketConnectAddress[connectionAttempt%len(session.SocketConnectAddress)]
149146
session.log.OnEventf("Connecting to: %v", address)
150147

151-
var netConn net.Conn
152-
if tlsConfig != nil {
153-
tlsConn, err := tls.DialWithDialer(&net.Dialer{Timeout: dialTimeout}, "tcp", address, tlsConfig)
154-
if err != nil {
155-
session.log.OnEventf("Failed to connect: %v", err)
156-
goto reconnect
157-
}
158-
159-
err = tlsConn.Handshake()
160-
if err != nil {
161-
session.log.OnEventf("Failed handshake:%v", err)
148+
netConn, err := dialer.Dial("tcp", address)
149+
if err != nil {
150+
session.log.OnEventf("Failed to connect: %v", err)
151+
goto reconnect
152+
} else if tlsConfig != nil {
153+
tlsConn := tls.Client(netConn, tlsConfig)
154+
if err = tlsConn.Handshake(); err != nil {
155+
session.log.OnEventf("Failed handshake: %v", err)
162156
goto reconnect
163157
}
164158
netConn = tlsConn
165-
} else {
166-
var err error
167-
netConn, err = net.Dial("tcp", address)
168-
if err != nil {
169-
session.log.OnEventf("Failed to connect: %v", err)
170-
goto reconnect
171-
}
172159
}
173160

174161
msgIn = make(chan fixIn)

0 commit comments

Comments
 (0)