15
15
package service
16
16
17
17
import (
18
+ "context"
18
19
"errors"
19
20
"fmt"
20
21
"net"
@@ -26,6 +27,7 @@ import (
26
27
"github.com/Jigsaw-Code/outline-sdk/transport/shadowsocks"
27
28
"github.com/Jigsaw-Code/outline-ss-server/ipinfo"
28
29
onet "github.com/Jigsaw-Code/outline-ss-server/net"
30
+ "github.com/Jigsaw-Code/outline-ss-server/service/metrics"
29
31
logging "github.com/op/go-logging"
30
32
"github.com/shadowsocks/go-shadowsocks2/socks"
31
33
)
@@ -35,7 +37,7 @@ type UDPMetrics interface {
35
37
ipinfo.IPInfoMap
36
38
37
39
// UDP metrics
38
- AddUDPPacketFromClient (clientInfo ipinfo.IPInfo , accessKey , status string , clientProxyBytes , proxyTargetBytes int )
40
+ AddUDPPacketFromClient (clientInfo ipinfo.IPInfo , accessKey , status string , data metrics. ProxyMetrics )
39
41
AddUDPPacketFromTarget (clientInfo ipinfo.IPInfo , accessKey , status string , targetProxyBytes , proxyClientBytes int )
40
42
AddUDPNatEntry (clientAddr net.Addr , accessKey string )
41
43
RemoveUDPNatEntry (clientAddr net.Addr , accessKey string )
@@ -119,15 +121,15 @@ func (h *packetHandler) Handle(clientConn net.PacketConn) {
119
121
120
122
for {
121
123
status := "OK"
122
- keyID , clientInfo , clientProxyBytes , proxyTargetBytes , connErr := h .handleConnection (clientConn )
124
+ keyID , clientInfo , proxyMetrics , connErr := h .handleConnection (context . TODO (), clientConn )
123
125
if connErr != nil {
124
126
if errors .Is (connErr .Cause , net .ErrClosed ) {
125
127
break
126
128
}
127
129
logger .Debugf ("UDP Error: %v: %v" , connErr .Message , connErr .Cause )
128
130
status = connErr .Status
129
131
}
130
- h .m .AddUDPPacketFromClient (clientInfo , keyID , status , clientProxyBytes , proxyTargetBytes )
132
+ h .m .AddUDPPacketFromClient (clientInfo , keyID , status , proxyMetrics )
131
133
}
132
134
}
133
135
@@ -157,52 +159,59 @@ func (h *packetHandler) authenticate(clientConn net.PacketConn) (net.Addr, *Ciph
157
159
return clientAddr , cipherEntry , textData , clientProxyBytes , nil
158
160
}
159
161
160
- func (h * packetHandler ) handleConnection (clientConn net.PacketConn ) (string , ipinfo.IPInfo , int , int , * onet.ConnectionError ) {
162
+ func (h * packetHandler ) proxyConnection (ctx context.Context , clientAddr net.Addr , tgtAddr net.Addr , clientConn net.PacketConn , cipherEntry CipherEntry , payload []byte , proxyMetrics * metrics.ProxyMetrics ) (ipinfo.IPInfo , * onet.ConnectionError ) {
163
+ tgtConn := h .nm .Get (clientAddr .String ())
164
+ if tgtConn == nil {
165
+ clientInfo , locErr := ipinfo .GetIPInfoFromAddr (h .m , clientAddr )
166
+ if locErr != nil {
167
+ logger .Warningf ("Failed client info lookup: %v" , locErr )
168
+ }
169
+ debugUDPAddr (clientAddr , "Got info \" %#v\" " , clientInfo )
170
+
171
+ udpConn , err := net .ListenPacket ("udp" , "" )
172
+ if err != nil {
173
+ return ipinfo.IPInfo {}, nil
174
+ }
175
+ tgtConn = h .nm .Add (clientAddr , clientConn , cipherEntry .CryptoKey , udpConn , clientInfo , cipherEntry .ID )
176
+ }
177
+
178
+ proxyTargetBytes , err := tgtConn .WriteTo (payload , tgtAddr )
179
+ proxyMetrics .ProxyTarget += int64 (proxyTargetBytes )
180
+ if err != nil {
181
+ return tgtConn .clientInfo , onet .NewConnectionError ("ERR_WRITE" , "Failed to write to target" , err )
182
+ }
183
+ return tgtConn .clientInfo , nil
184
+ }
185
+
186
+ func (h * packetHandler ) handleConnection (ctx context.Context , clientConn net.PacketConn ) (string , ipinfo.IPInfo , metrics.ProxyMetrics , * onet.ConnectionError ) {
161
187
defer func () {
162
188
if r := recover (); r != nil {
163
189
logger .Errorf ("Panic in UDP loop: %v. Continuing to listen." , r )
164
190
debug .PrintStack ()
165
191
}
166
192
}()
167
193
194
+ var proxyMetrics metrics.ProxyMetrics
168
195
clientAddr , cipherEntry , textData , clientProxyBytes , authErr := h .authenticate (clientConn )
196
+ proxyMetrics .ClientProxy += int64 (clientProxyBytes )
169
197
if authErr != nil {
170
- return "" , ipinfo.IPInfo {}, clientProxyBytes , 0 , authErr
198
+ return "" , ipinfo.IPInfo {}, proxyMetrics , authErr
171
199
}
172
200
173
- targetConn := h .nm .Get (clientAddr .String ())
174
- if targetConn == nil {
175
- udpConn , err := net .ListenPacket ("udp" , "" )
176
- if err != nil {
177
- return "" , ipinfo.IPInfo {}, clientProxyBytes , 0 , onet .NewConnectionError ("ERR_CREATE_SOCKET" , "Failed to create UDP socket" , err )
178
- }
179
-
180
- clientInfo , locErr := ipinfo .GetIPInfoFromAddr (h .m , clientAddr )
181
- if locErr != nil {
182
- logger .Warningf ("Failed client info lookup: %v" , locErr )
183
- }
184
- debugUDPAddr (clientAddr , "Got info \" %#v\" " , clientInfo )
185
-
186
- targetConn = h .nm .Add (clientAddr , clientConn , cipherEntry .CryptoKey , udpConn , clientInfo , cipherEntry .ID )
187
- }
188
-
189
- payload , tgtUDPAddr , onetErr := h .validatePacket (textData )
201
+ payload , tgtAddr , onetErr := h .getProxyRequest (textData )
190
202
if onetErr != nil {
191
- return targetConn . keyID , targetConn . clientInfo , clientProxyBytes , 0 , onetErr
203
+ return cipherEntry . ID , ipinfo. IPInfo {}, proxyMetrics , onetErr
192
204
}
205
+ debugUDPAddr (clientAddr , "Proxy exit %s" , tgtAddr .String ())
193
206
194
- debugUDPAddr (targetConn .clientAddr , "Proxy exit %v" , targetConn .LocalAddr ())
195
- proxyTargetBytes , err := targetConn .WriteTo (payload , tgtUDPAddr ) // accept only UDPAddr despite the signature
196
- if err != nil {
197
- return targetConn .keyID , targetConn .clientInfo , clientProxyBytes , proxyTargetBytes , onet .NewConnectionError ("ERR_WRITE" , "Failed to write to target" , err )
198
- }
199
- return targetConn .keyID , targetConn .clientInfo , clientProxyBytes , proxyTargetBytes , nil
207
+ clientInfo , err := h .proxyConnection (ctx , clientAddr , tgtAddr , clientConn , * cipherEntry , payload , & proxyMetrics )
208
+ return cipherEntry .ID , clientInfo , proxyMetrics , err
200
209
}
201
210
202
211
// Given the decrypted contents of a UDP packet, return
203
212
// the payload and the destination address, or an error if
204
213
// this packet cannot or should not be forwarded.
205
- func (h * packetHandler ) validatePacket (textData []byte ) ([]byte , * net.UDPAddr , * onet.ConnectionError ) {
214
+ func (h * packetHandler ) getProxyRequest (textData []byte ) ([]byte , * net.UDPAddr , * onet.ConnectionError ) {
206
215
tgtAddr := socks .SplitAddr (textData )
207
216
if tgtAddr == nil {
208
217
return nil , nil , onet .NewConnectionError ("ERR_READ_ADDRESS" , "Failed to get target address" , nil )
@@ -227,9 +236,7 @@ func isDNS(addr net.Addr) bool {
227
236
228
237
type natconn struct {
229
238
net.PacketConn
230
- cryptoKey * shadowsocks.EncryptionKey
231
- keyID string
232
- clientAddr net.Addr
239
+ cryptoKey * shadowsocks.EncryptionKey
233
240
// We store the client information in the NAT map to avoid recomputing it
234
241
// for every downstream packet in a UDP-based connection.
235
242
clientInfo ipinfo.IPInfo
@@ -310,12 +317,9 @@ func (m *natmap) Get(key string) *natconn {
310
317
return m .keyConn [key ]
311
318
}
312
319
313
- func (m * natmap ) set (clientAddr net.Addr , pc net.PacketConn , cryptoKey * shadowsocks. EncryptionKey , keyID string , clientInfo ipinfo.IPInfo ) * natconn {
320
+ func (m * natmap ) set (clientAddr net.Addr , pc net.PacketConn , clientInfo ipinfo.IPInfo ) * natconn {
314
321
entry := & natconn {
315
322
PacketConn : pc ,
316
- cryptoKey : cryptoKey ,
317
- keyID : keyID ,
318
- clientAddr : clientAddr ,
319
323
clientInfo : clientInfo ,
320
324
defaultTimeout : m .timeout ,
321
325
}
@@ -340,12 +344,12 @@ func (m *natmap) del(key string) net.PacketConn {
340
344
}
341
345
342
346
func (m * natmap ) Add (clientAddr net.Addr , clientConn net.PacketConn , cryptoKey * shadowsocks.EncryptionKey , targetConn net.PacketConn , clientInfo ipinfo.IPInfo , keyID string ) * natconn {
343
- entry := m .set (clientAddr , targetConn , cryptoKey , keyID , clientInfo )
347
+ entry := m .set (clientAddr , targetConn , clientInfo )
344
348
345
349
m .metrics .AddUDPNatEntry (clientAddr , keyID )
346
350
m .running .Add (1 )
347
351
go func () {
348
- timedCopy (clientAddr , clientConn , entry , keyID , m .metrics )
352
+ timedCopy (clientAddr , clientConn , entry , cryptoKey , keyID , m .metrics )
349
353
m .metrics .RemoveUDPNatEntry (clientAddr , keyID )
350
354
if pc := m .del (clientAddr .String ()); pc != nil {
351
355
pc .Close ()
@@ -375,13 +379,13 @@ var maxAddrLen int = len(socks.ParseAddr("[2001:db8::1]:12345"))
375
379
376
380
// copy from target to client until read timeout
377
381
func timedCopy (clientAddr net.Addr , clientConn net.PacketConn , targetConn * natconn ,
378
- keyID string , sm UDPMetrics ) {
382
+ cryptoKey * shadowsocks. EncryptionKey , keyID string , sm UDPMetrics ) {
379
383
// pkt is used for in-place encryption of downstream UDP packets, with the layout
380
384
// [padding?][salt][address][body][tag][extra]
381
385
// Padding is only used if the address is IPv4.
382
386
pkt := make ([]byte , serverUDPBufferSize )
383
387
384
- saltSize := targetConn . cryptoKey .SaltSize ()
388
+ saltSize := cryptoKey .SaltSize ()
385
389
// Leave enough room at the beginning of the packet for a max-length header (i.e. IPv6).
386
390
bodyStart := saltSize + maxAddrLen
387
391
@@ -425,7 +429,7 @@ func timedCopy(clientAddr net.Addr, clientConn net.PacketConn, targetConn *natco
425
429
// [ packBuf ]
426
430
// [ buf ]
427
431
packBuf := pkt [saltStart :]
428
- buf , err := shadowsocks .Pack (packBuf , plaintextBuf , targetConn . cryptoKey ) // Encrypt in-place
432
+ buf , err := shadowsocks .Pack (packBuf , plaintextBuf , cryptoKey ) // Encrypt in-place
429
433
if err != nil {
430
434
return onet .NewConnectionError ("ERR_PACK" , "Failed to pack data to client" , err )
431
435
}
@@ -456,7 +460,7 @@ var _ UDPMetrics = (*NoOpUDPMetrics)(nil)
456
460
func (m * NoOpUDPMetrics ) GetIPInfo (net.IP ) (ipinfo.IPInfo , error ) {
457
461
return ipinfo.IPInfo {}, nil
458
462
}
459
- func (m * NoOpUDPMetrics ) AddUDPPacketFromClient (clientInfo ipinfo.IPInfo , accessKey , status string , clientProxyBytes , proxyTargetBytes int ) {
463
+ func (m * NoOpUDPMetrics ) AddUDPPacketFromClient (clientInfo ipinfo.IPInfo , accessKey , status string , data metrics. ProxyMetrics ) {
460
464
}
461
465
func (m * NoOpUDPMetrics ) AddUDPPacketFromTarget (clientInfo ipinfo.IPInfo , accessKey , status string , targetProxyBytes , proxyClientBytes int ) {
462
466
}
0 commit comments