Skip to content

Commit e82cf98

Browse files
authored
Merge pull request #85 from Jigsaw-Code/fortuna-clear
Drain on address or relay errors, tweak timeout
2 parents 25f6db2 + 477cfb5 commit e82cf98

File tree

3 files changed

+336
-79
lines changed

3 files changed

+336
-79
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
github.com/prometheus/client_golang v1.7.1
88
github.com/prometheus/common v0.10.0 // indirect
99
github.com/shadowsocks/go-shadowsocks2 v0.1.0
10+
github.com/stretchr/testify v1.4.0
1011
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0
1112
gopkg.in/yaml.v2 v2.3.0
1213
)

service/tcp.go

Lines changed: 62 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import (
2929
"github.com/Jigsaw-Code/outline-ss-server/service/metrics"
3030
ss "github.com/Jigsaw-Code/outline-ss-server/shadowsocks"
3131
logging "github.com/op/go-logging"
32-
3332
"github.com/shadowsocks/go-shadowsocks2/socks"
3433
)
3534

@@ -149,34 +148,21 @@ func (s *tcpService) SetTargetIPValidator(targetIPValidator onet.TargetIPValidat
149148
s.targetIPValidator = targetIPValidator
150149
}
151150

152-
// proxyConnection will route the clientConn according to the address read from the connection.
153-
func proxyConnection(clientConn onet.DuplexConn, proxyMetrics *metrics.ProxyMetrics, checkAllowedIP onet.TargetIPValidator) *onet.ConnectionError {
154-
tgtAddr, err := socks.ReadAddr(clientConn)
155-
if err != nil {
156-
return onet.NewConnectionError("ERR_READ_ADDRESS", "Failed to get target address", err)
157-
}
151+
func dialTarget(tgtAddr socks.Addr, proxyMetrics *metrics.ProxyMetrics, targetIPValidator onet.TargetIPValidator) (onet.DuplexConn, *onet.ConnectionError) {
158152
tgtTCPAddr, err := net.ResolveTCPAddr("tcp", tgtAddr.String())
159153
if err != nil {
160-
return onet.NewConnectionError("ERR_RESOLVE_ADDRESS", fmt.Sprintf("Failed to resolve target address %v", tgtAddr.String()), err)
154+
return nil, onet.NewConnectionError("ERR_RESOLVE_ADDRESS", fmt.Sprintf("Failed to resolve target address %v", tgtAddr.String()), err)
161155
}
162-
if err := checkAllowedIP(tgtTCPAddr.IP); err != nil {
163-
return err
156+
if err := targetIPValidator(tgtTCPAddr.IP); err != nil {
157+
return nil, err
164158
}
165159

166160
tgtTCPConn, err := net.DialTCP("tcp", nil, tgtTCPAddr)
167161
if err != nil {
168-
return onet.NewConnectionError("ERR_CONNECT", "Failed to connect to target", err)
162+
return nil, onet.NewConnectionError("ERR_CONNECT", "Failed to connect to target", err)
169163
}
170-
defer tgtTCPConn.Close()
171164
tgtTCPConn.SetKeepAlive(true)
172-
tgtConn := metrics.MeasureConn(tgtTCPConn, &proxyMetrics.ProxyTarget, &proxyMetrics.TargetProxy)
173-
174-
logger.Debugf("proxy %s <-> %s", clientConn.RemoteAddr().String(), tgtConn.RemoteAddr().String())
175-
_, _, err = onet.Relay(clientConn, tgtConn)
176-
if err != nil {
177-
return onet.NewConnectionError("ERR_RELAY", "Failed to relay traffic", err)
178-
}
179-
return nil
165+
return metrics.MeasureConn(tgtTCPConn, &proxyMetrics.ProxyTarget, &proxyMetrics.TargetProxy), nil
180166
}
181167

182168
func (s *tcpService) Serve(listener *net.TCPListener) error {
@@ -196,7 +182,7 @@ func (s *tcpService) Serve(listener *net.TCPListener) error {
196182

197183
defer s.running.Done()
198184
for {
199-
clientConn, err := listener.AcceptTCP()
185+
clientTCPConn, err := listener.AcceptTCP()
200186
if err != nil {
201187
s.mu.RLock()
202188
stopped := s.stopped
@@ -216,26 +202,26 @@ func (s *tcpService) Serve(listener *net.TCPListener) error {
216202
logger.Errorf("Panic in TCP handler: %v", r)
217203
}
218204
}()
219-
s.handleConnection(listener.Addr().(*net.TCPAddr).Port, clientConn)
205+
s.handleConnection(listener.Addr().(*net.TCPAddr).Port, clientTCPConn)
220206
}()
221207
}
222208
}
223209

224-
func (s *tcpService) handleConnection(listenerPort int, clientConn onet.DuplexConn) {
225-
clientLocation, err := s.m.GetLocation(clientConn.RemoteAddr())
210+
func (s *tcpService) handleConnection(listenerPort int, clientTCPConn *net.TCPConn) {
211+
clientLocation, err := s.m.GetLocation(clientTCPConn.RemoteAddr())
226212
if err != nil {
227213
logger.Warningf("Failed location lookup: %v", err)
228214
}
229-
logger.Debugf("Got location \"%v\" for IP %v", clientLocation, clientConn.RemoteAddr().String())
215+
logger.Debugf("Got location \"%v\" for IP %v", clientLocation, clientTCPConn.RemoteAddr().String())
230216
s.m.AddOpenTCPConnection(clientLocation)
231217

232218
connStart := time.Now()
233-
clientConn.(*net.TCPConn).SetKeepAlive(true)
234-
// Set a deadline for connection authentication
235-
clientConn.SetReadDeadline(connStart.Add(s.readTimeout))
219+
clientTCPConn.SetKeepAlive(true)
220+
// Set a deadline to receive the address to the target.
221+
clientTCPConn.SetReadDeadline(connStart.Add(s.readTimeout))
236222
var proxyMetrics metrics.ProxyMetrics
237-
clientConn = metrics.MeasureConn(clientConn, &proxyMetrics.ProxyClient, &proxyMetrics.ClientProxy)
238-
cipherEntry, clientReader, clientSalt, timeToCipher, keyErr := findAccessKey(clientConn, remoteIP(clientConn), s.ciphers)
223+
clientConn := metrics.MeasureConn(clientTCPConn, &proxyMetrics.ProxyClient, &proxyMetrics.ClientProxy)
224+
cipherEntry, clientReader, clientSalt, timeToCipher, keyErr := findAccessKey(clientConn, remoteIP(clientTCPConn), s.ciphers)
239225

240226
connError := func() *onet.ConnectionError {
241227
if keyErr != nil {
@@ -255,17 +241,58 @@ func (s *tcpService) handleConnection(listenerPort int, clientConn onet.DuplexCo
255241
status = "ERR_REPLAY_CLIENT"
256242
}
257243
s.absorbProbe(listenerPort, clientConn, clientLocation, status, &proxyMetrics)
258-
logger.Debugf(status+": %v in %s sent %d bytes", clientConn.RemoteAddr(), clientLocation, proxyMetrics.ClientProxy)
244+
logger.Debugf(status+": %v in %s sent %d bytes", clientTCPConn.RemoteAddr(), clientLocation, proxyMetrics.ClientProxy)
259245
return onet.NewConnectionError(status, "Replay detected", nil)
260246
}
261-
// Clear the authentication deadline
262-
clientConn.SetReadDeadline(time.Time{})
263247

264248
ssr := ss.NewShadowsocksReader(clientReader, cipherEntry.Cipher)
249+
tgtAddr, err := socks.ReadAddr(ssr)
250+
// Clear the deadline for the target address
251+
clientTCPConn.SetReadDeadline(time.Time{})
252+
if err != nil {
253+
// Drain to prevent a close on cipher error.
254+
io.Copy(ioutil.Discard, clientConn)
255+
return onet.NewConnectionError("ERR_READ_ADDRESS", "Failed to get target address", err)
256+
}
257+
258+
tgtConn, dialErr := dialTarget(tgtAddr, &proxyMetrics, s.targetIPValidator)
259+
if dialErr != nil {
260+
// We don't drain so dial errors and invalid addresses are communicated quickly.
261+
return dialErr
262+
}
263+
defer tgtConn.Close()
264+
265+
logger.Debugf("proxy %s <-> %s", clientTCPConn.RemoteAddr().String(), tgtConn.RemoteAddr().String())
265266
ssw := ss.NewShadowsocksWriter(clientConn, cipherEntry.Cipher)
266267
ssw.SetSaltGenerator(cipherEntry.SaltGenerator)
267-
clientConn = onet.WrapConn(clientConn, ssr, ssw)
268-
return proxyConnection(clientConn, &proxyMetrics, s.targetIPValidator)
268+
269+
fromClientErrCh := make(chan error)
270+
go func() {
271+
_, fromClientErr := ssr.WriteTo(tgtConn)
272+
if fromClientErr != nil {
273+
// Drain to prevent a close in the case of a cipher error.
274+
io.Copy(ioutil.Discard, clientConn)
275+
}
276+
clientConn.CloseRead()
277+
// Send FIN to target.
278+
// We must do this after the drain is completed, otherwise the target will close its
279+
// connection with the proxy, which will, in turn, close the connection with the client.
280+
tgtConn.CloseWrite()
281+
fromClientErrCh <- fromClientErr
282+
}()
283+
_, fromTargetErr := ssw.ReadFrom(tgtConn)
284+
// Send FIN to client.
285+
clientConn.CloseWrite()
286+
tgtConn.CloseRead()
287+
288+
fromClientErr := <-fromClientErrCh
289+
if fromClientErr != nil {
290+
return onet.NewConnectionError("ERR_RELAY_CLIENT", "Failed to relay traffic from client", fromClientErr)
291+
}
292+
if fromTargetErr != nil {
293+
return onet.NewConnectionError("ERR_RELAY_TARGET", "Failed to relay traffic from target", fromTargetErr)
294+
}
295+
return nil
269296
}()
270297

271298
connDuration := time.Now().Sub(connStart)

0 commit comments

Comments
 (0)