@@ -48,7 +48,7 @@ func ensureBytes(reader io.Reader, buf []byte, bytesNeeded int) ([]byte, error)
48
48
return buf , err
49
49
}
50
50
51
- func findAccessKey (clientConn onet.DuplexConn , cipherList map [string ]shadowaead.Cipher ) (string , onet.DuplexConn , error ) {
51
+ func findAccessKey (clientConn onet.DuplexConn , cipherMap map [string ]shadowaead.Cipher ) (string , onet.DuplexConn , error ) {
52
52
// This must have enough space to hold the salt + 2 bytes chunk length + AEAD tag (Oeverhead) for any cipher
53
53
replayBytes := make ([]byte , 0 , 32 + 2 + 16 )
54
54
// Constant of zeroes to use as the start chunk count. This must be as big as the max NonceSize() across all ciphers.
@@ -57,11 +57,12 @@ func findAccessKey(clientConn onet.DuplexConn, cipherList map[string]shadowaead.
57
57
chunkLenBuf := [2 ]byte {}
58
58
var err error
59
59
60
- // Try each cipher until we find one that authenticates successfully.
61
- // This assumes that all ciphers are AEAD .
60
+ // Try each cipher until we find one that authenticates successfully. This assumes that all ciphers are AEAD.
61
+ // We shuffle the cipher map so that every connection has the same expected time .
62
62
// TODO: Reorder list to try previously successful ciphers first for the client IP.
63
63
// TODO: Ban and log client IPs with too many failures too quick to protect against DoS.
64
- for id , cipher := range cipherList {
64
+ for _ , entry := range shuffleCipherMap (cipherMap ) {
65
+ id , cipher := entry .id , entry .cipher
65
66
replayBytes , err = ensureBytes (clientConn , replayBytes , cipher .SaltSize ())
66
67
if err != nil {
67
68
if logger .IsEnabledFor (logging .DEBUG ) {
@@ -103,15 +104,47 @@ type tcpService struct {
103
104
isRunning bool
104
105
}
105
106
107
+ // NewTCPService creates a TCPService
106
108
func NewTCPService (listener * net.TCPListener , ciphers * map [string ]shadowaead.Cipher , m metrics.ShadowsocksMetrics ) TCPService {
107
109
return & tcpService {listener : listener , ciphers : ciphers , m : m }
108
110
}
109
111
112
+ // TCPService is a Shadowsocks TCP service that can be started and stopped.
110
113
type TCPService interface {
111
114
Start ()
112
115
Stop () error
113
116
}
114
117
118
+ // proxyConnection will route the clientConn according to the address read from the connection.
119
+ func proxyConnection (clientConn onet.DuplexConn , proxyMetrics * metrics.ProxyMetrics ) * onet.ConnectionError {
120
+ tgtAddr , err := socks .ReadAddr (clientConn )
121
+ if err != nil {
122
+ return onet .NewConnectionError ("ERR_READ_ADDRESS" , "Failed to get target address" , err )
123
+ }
124
+ tgtTCPAddr , err := net .ResolveTCPAddr ("tcp" , tgtAddr .String ())
125
+ if err != nil {
126
+ return onet .NewConnectionError ("ERR_RESOLVE_ADDRESS" , fmt .Sprintf ("Failed to resolve target address %v" , tgtAddr .String ()), err )
127
+ }
128
+ if ! tgtTCPAddr .IP .IsGlobalUnicast () {
129
+ return onet .NewConnectionError ("ERR_ADDRESS_INVALID" , fmt .Sprintf ("Target address is not global unicast: %v" , tgtAddr .String ()), err )
130
+ }
131
+
132
+ tgtTCPConn , err := net .DialTCP ("tcp" , nil , tgtTCPAddr )
133
+ if err != nil {
134
+ return onet .NewConnectionError ("ERR_CONNECT" , "Failed to connect to target" , err )
135
+ }
136
+ defer tgtTCPConn .Close ()
137
+ tgtTCPConn .SetKeepAlive (true )
138
+ tgtConn := metrics .MeasureConn (tgtTCPConn , & proxyMetrics .ProxyTarget , & proxyMetrics .TargetProxy )
139
+
140
+ logger .Debugf ("proxy %s <-> %s" , clientConn .RemoteAddr ().String (), tgtConn .RemoteAddr ().String ())
141
+ _ , _ , err = onet .Relay (clientConn , tgtConn )
142
+ if err != nil {
143
+ return onet .NewConnectionError ("ERR_RELAY" , "Failed to relay traffic" , err )
144
+ }
145
+ return nil
146
+ }
147
+
115
148
func (s * tcpService ) Start () {
116
149
s .isRunning = true
117
150
for s .isRunning {
@@ -156,36 +189,10 @@ func (s *tcpService) Start() {
156
189
157
190
keyID , clientConn , err := findAccessKey (clientConn , * s .ciphers )
158
191
if err != nil {
159
- return & onet.ConnectionError { "ERR_CIPHER" , "Failed to find a valid cipher" , err }
192
+ return onet .NewConnectionError ( "ERR_CIPHER" , "Failed to find a valid cipher" , err )
160
193
}
161
194
162
- tgtAddr , err := socks .ReadAddr (clientConn )
163
- if err != nil {
164
- return & onet.ConnectionError {"ERR_READ_ADDRESS" , "Failed to get target address" , err }
165
- }
166
- tgtTCPAddr , err := net .ResolveTCPAddr ("tcp" , tgtAddr .String ())
167
- if err != nil {
168
- return & onet.ConnectionError {"ERR_RESOLVE_ADDRESS" , fmt .Sprintf ("Failed to resolve target address %v" , tgtAddr .String ()), err }
169
- }
170
- if ! tgtTCPAddr .IP .IsGlobalUnicast () {
171
- return & onet.ConnectionError {"ERR_ADDRESS_INVALID" , fmt .Sprintf ("Target address is not global unicast: %v" , tgtAddr .String ()), err }
172
- }
173
-
174
- tgtTCPConn , err := net .DialTCP ("tcp" , nil , tgtTCPAddr )
175
- if err != nil {
176
- return & onet.ConnectionError {"ERR_CONNECT" , "Failed to connect to target" , err }
177
- }
178
- defer tgtTCPConn .Close ()
179
- tgtTCPConn .SetKeepAlive (true )
180
- tgtConn := metrics .MeasureConn (tgtTCPConn , & proxyMetrics .ProxyTarget , & proxyMetrics .TargetProxy )
181
-
182
- // TODO: Disable logging in production. This is sensitive.
183
- logger .Debugf ("proxy %s <-> %s" , clientConn .RemoteAddr ().String (), tgtConn .RemoteAddr ().String ())
184
- _ , _ , err = onet .Relay (clientConn , tgtConn )
185
- if err != nil {
186
- return & onet.ConnectionError {"ERR_RELAY" , "Failed to relay traffic" , err }
187
- }
188
- return nil
195
+ return proxyConnection (clientConn , & proxyMetrics )
189
196
}()
190
197
}
191
198
}
0 commit comments