@@ -43,10 +43,7 @@ type TCPMetrics interface {
43
43
AddOpenTCPConnection (ip net.Addr )
44
44
AddAuthenticatedTCPConnection (ip net.Addr , accessKey string )
45
45
AddClosedTCPConnection (ip net.Addr , accessKey string , status string , data metrics.ProxyMetrics , duration time.Duration )
46
-
47
- // Shadowsocks TCP metrics
48
46
AddTCPProbe (status , drainResult string , port int , clientProxyBytes int64 )
49
- AddTCPCipherSearch (accessKeyFound bool , timeToCipher time.Duration )
50
47
}
51
48
52
49
func remoteIP (conn net.Conn ) net.IP {
@@ -119,26 +116,67 @@ func findEntry(firstBytes []byte, ciphers []*list.Element) (*CipherEntry, *list.
119
116
return nil , nil
120
117
}
121
118
119
+ type StreamAuthenticateFunc func (clientConn transport.StreamConn ) (string , transport.StreamConn , * onet.ConnectionError )
120
+
121
+ // ShadowsocksTCPMetrics is used to report Shadowsocks metrics on TCP connections.
122
+ type ShadowsocksTCPMetrics interface {
123
+ // Shadowsocks TCP metrics
124
+ AddTCPCipherSearch (accessKeyFound bool , timeToCipher time.Duration )
125
+ }
126
+
127
+ // NewShadowsocksStreamAuthenticator creates a stream authenticator that uses Shadowsocks.
128
+ // TODO(fortuna): Offer alternative transports.
129
+ func NewShadowsocksStreamAuthenticator (ciphers CipherList , replayCache * ReplayCache , metrics ShadowsocksTCPMetrics ) StreamAuthenticateFunc {
130
+ return func (clientConn transport.StreamConn ) (string , transport.StreamConn , * onet.ConnectionError ) {
131
+ // Find the cipher and acess key id.
132
+ cipherEntry , clientReader , clientSalt , timeToCipher , keyErr := findAccessKey (clientConn , remoteIP (clientConn ), ciphers )
133
+ metrics .AddTCPCipherSearch (keyErr == nil , timeToCipher )
134
+ if keyErr != nil {
135
+ const status = "ERR_CIPHER"
136
+ return "" , nil , onet .NewConnectionError (status , "Failed to find a valid cipher" , keyErr )
137
+ }
138
+ var id string
139
+ if cipherEntry != nil {
140
+ id = cipherEntry .ID
141
+ }
142
+
143
+ // Check if the connection is a replay.
144
+ isServerSalt := cipherEntry .SaltGenerator .IsServerSalt (clientSalt )
145
+ // Only check the cache if findAccessKey succeeded and the salt is unrecognized.
146
+ if isServerSalt || ! replayCache .Add (cipherEntry .ID , clientSalt ) {
147
+ var status string
148
+ if isServerSalt {
149
+ status = "ERR_REPLAY_SERVER"
150
+ } else {
151
+ status = "ERR_REPLAY_CLIENT"
152
+ }
153
+ return id , nil , onet .NewConnectionError (status , "Replay detected" , nil )
154
+ }
155
+
156
+ metrics .AddAuthenticatedTCPConnection (clientConn .RemoteAddr (), id )
157
+ ssr := shadowsocks .NewReader (clientReader , cipherEntry .CryptoKey )
158
+ ssw := shadowsocks .NewWriter (clientConn , cipherEntry .CryptoKey )
159
+ ssw .SetSaltGenerator (cipherEntry .SaltGenerator )
160
+ return id , transport .WrapConn (clientConn , ssr , ssw ), nil
161
+ }
162
+ }
163
+
122
164
type tcpHandler struct {
123
- port int
124
- ciphers CipherList
125
- m TCPMetrics
126
- readTimeout time.Duration
127
- // `replayCache` is a pointer to SSServer.replayCache, to share the cache among all ports.
128
- replayCache * ReplayCache
129
- dialer transport.StreamDialer
165
+ port int
166
+ m TCPMetrics
167
+ readTimeout time.Duration
168
+ authenticate StreamAuthenticateFunc
169
+ dialer transport.StreamDialer
130
170
}
131
171
132
172
// NewTCPService creates a TCPService
133
- // `replayCache` is a pointer to SSServer.replayCache, to share the cache among all ports.
134
- func NewTCPHandler (port int , ciphers CipherList , replayCache * ReplayCache , m TCPMetrics , timeout time.Duration ) TCPHandler {
173
+ func NewTCPHandler (port int , authenticate StreamAuthenticateFunc , m TCPMetrics , timeout time.Duration ) TCPHandler {
135
174
return & tcpHandler {
136
- port : port ,
137
- ciphers : ciphers ,
138
- m : m ,
139
- readTimeout : timeout ,
140
- replayCache : replayCache ,
141
- dialer : defaultDialer ,
175
+ port : port ,
176
+ m : m ,
177
+ readTimeout : timeout ,
178
+ authenticate : authenticate ,
179
+ dialer : defaultDialer ,
142
180
}
143
181
}
144
182
@@ -235,42 +273,6 @@ func (h *tcpHandler) Handle(ctx context.Context, clientConn transport.StreamConn
235
273
logger .Debugf ("Done with status %v, duration %v" , status , connDuration )
236
274
}
237
275
238
- func (h * tcpHandler ) authenticate (clientConn transport.StreamConn , proxyMetrics * metrics.ProxyMetrics ) (string , transport.StreamConn , * onet.ConnectionError ) {
239
- // TODO(fortuna): Offer alternative transports.
240
- // Find the cipher and acess key id.
241
- cipherEntry , clientReader , clientSalt , timeToCipher , keyErr := findAccessKey (clientConn , remoteIP (clientConn ), h .ciphers )
242
- h .m .AddTCPCipherSearch (keyErr == nil , timeToCipher )
243
- if keyErr != nil {
244
- logger .Debugf ("Failed to find a valid cipher after reading %v bytes: %v" , proxyMetrics .ClientProxy , keyErr )
245
- const status = "ERR_CIPHER"
246
- return "" , nil , onet .NewConnectionError (status , "Failed to find a valid cipher" , keyErr )
247
- }
248
- var id string
249
- if cipherEntry != nil {
250
- id = cipherEntry .ID
251
- }
252
-
253
- // Check if the connection is a replay.
254
- isServerSalt := cipherEntry .SaltGenerator .IsServerSalt (clientSalt )
255
- // Only check the cache if findAccessKey succeeded and the salt is unrecognized.
256
- if isServerSalt || ! h .replayCache .Add (cipherEntry .ID , clientSalt ) {
257
- var status string
258
- if isServerSalt {
259
- status = "ERR_REPLAY_SERVER"
260
- } else {
261
- status = "ERR_REPLAY_CLIENT"
262
- }
263
- logger .Debugf (status + ": %v sent %d bytes" , clientConn .RemoteAddr (), proxyMetrics .ClientProxy )
264
- return id , nil , onet .NewConnectionError (status , "Replay detected" , nil )
265
- }
266
-
267
- h .m .AddAuthenticatedTCPConnection (clientConn .RemoteAddr (), id )
268
- ssr := shadowsocks .NewReader (clientReader , cipherEntry .CryptoKey )
269
- ssw := shadowsocks .NewWriter (clientConn , cipherEntry .CryptoKey )
270
- ssw .SetSaltGenerator (cipherEntry .SaltGenerator )
271
- return id , transport .WrapConn (clientConn , ssr , ssw ), nil
272
- }
273
-
274
276
func getProxyRequest (clientConn transport.StreamConn ) (string , error ) {
275
277
// TODO(fortuna): Use Shadowsocks proxy, HTTP CONNECT or SOCKS5 based on first byte:
276
278
// case 1, 3 or 4: Shadowsocks (address type)
@@ -332,7 +334,7 @@ func (h *tcpHandler) handleConnection(ctx context.Context, listenerPort int, out
332
334
}
333
335
outerConn .SetReadDeadline (readDeadline )
334
336
335
- id , innerConn , authErr := h .authenticate (outerConn , proxyMetrics )
337
+ id , innerConn , authErr := h .authenticate (outerConn )
336
338
if authErr != nil {
337
339
// Drain to protect against probing attacks.
338
340
h .absorbProbe (listenerPort , outerConn , authErr .Status , proxyMetrics )
0 commit comments