@@ -7,13 +7,24 @@ package proxy
7
7
8
8
import (
9
9
"context"
10
+ gotls "crypto/tls"
11
+ "io"
12
+ "runtime"
10
13
14
+ "github.com/pires/go-proxyproto"
15
+ "github.com/xtls/xray-core/common/buf"
16
+ "github.com/xtls/xray-core/common/errors"
11
17
"github.com/xtls/xray-core/common/net"
12
18
"github.com/xtls/xray-core/common/protocol"
19
+ "github.com/xtls/xray-core/common/session"
20
+ "github.com/xtls/xray-core/common/signal"
13
21
"github.com/xtls/xray-core/features/routing"
22
+ "github.com/xtls/xray-core/features/stats"
14
23
"github.com/xtls/xray-core/transport"
15
24
"github.com/xtls/xray-core/transport/internet"
25
+ "github.com/xtls/xray-core/transport/internet/reality"
16
26
"github.com/xtls/xray-core/transport/internet/stat"
27
+ "github.com/xtls/xray-core/transport/internet/tls"
17
28
)
18
29
19
30
// An Inbound processes inbound connections.
@@ -47,3 +58,78 @@ type GetInbound interface {
47
58
type GetOutbound interface {
48
59
GetOutbound () Outbound
49
60
}
61
+
62
+ // UnwrapRawConn support unwrap stats, tls, utls, reality and proxyproto conn and get raw tcp conn from it
63
+ func UnwrapRawConn (conn net.Conn ) (net.Conn , stats.Counter , stats.Counter ) {
64
+ var readCounter , writerCounter stats.Counter
65
+ if conn != nil {
66
+ statConn , ok := conn .(* stat.CounterConnection )
67
+ if ok {
68
+ conn = statConn .Connection
69
+ readCounter = statConn .ReadCounter
70
+ writerCounter = statConn .WriteCounter
71
+ }
72
+ if xc , ok := conn .(* gotls.Conn ); ok {
73
+ conn = xc .NetConn ()
74
+ } else if utlsConn , ok := conn .(* tls.UConn ); ok {
75
+ conn = utlsConn .NetConn ()
76
+ } else if realityConn , ok := conn .(* reality.Conn ); ok {
77
+ conn = realityConn .NetConn ()
78
+ } else if realityUConn , ok := conn .(* reality.UConn ); ok {
79
+ conn = realityUConn .NetConn ()
80
+ }
81
+ if pc , ok := conn .(* proxyproto.Conn ); ok {
82
+ conn = pc .Raw ()
83
+ // 8192 > 4096, there is no need to process pc's bufReader
84
+ }
85
+ }
86
+ return conn , readCounter , writerCounter
87
+ }
88
+
89
+ // CopyRawConnIfExist use the most efficient copy method.
90
+ // - If caller don't want to turn on splice, do not pass in both reader conn and writer conn
91
+ // - writer are from *transport.Link
92
+ func CopyRawConnIfExist (ctx context.Context , readerConn net.Conn , writerConn net.Conn , writer buf.Writer , timer signal.ActivityUpdater ) error {
93
+ readerConn , readCounter , _ := UnwrapRawConn (readerConn )
94
+ writerConn , _ , writeCounter := UnwrapRawConn (writerConn )
95
+ reader := buf .NewReader (readerConn )
96
+ if inbound := session .InboundFromContext (ctx ); inbound != nil {
97
+ if tc , ok := writerConn .(* net.TCPConn ); ok && readerConn != nil && writerConn != nil && (runtime .GOOS == "linux" || runtime .GOOS == "android" ) {
98
+ for inbound .CanSpliceCopy != 3 {
99
+ if inbound .CanSpliceCopy == 1 {
100
+ newError ("CopyRawConn splice" ).WriteToLog (session .ExportIDToError (ctx ))
101
+ runtime .Gosched () // necessary
102
+ w , err := tc .ReadFrom (readerConn )
103
+ if readCounter != nil {
104
+ readCounter .Add (w )
105
+ }
106
+ if writeCounter != nil {
107
+ writeCounter .Add (w )
108
+ }
109
+ if err != nil && errors .Cause (err ) != io .EOF {
110
+ return err
111
+ }
112
+ return nil
113
+ }
114
+ buffer , err := reader .ReadMultiBuffer ()
115
+ if ! buffer .IsEmpty () {
116
+ if readCounter != nil {
117
+ readCounter .Add (int64 (buffer .Len ()))
118
+ }
119
+ timer .Update ()
120
+ if werr := writer .WriteMultiBuffer (buffer ); werr != nil {
121
+ return werr
122
+ }
123
+ }
124
+ if err != nil {
125
+ return err
126
+ }
127
+ }
128
+ }
129
+ }
130
+ newError ("CopyRawConn readv" ).WriteToLog (session .ExportIDToError (ctx ))
131
+ if err := buf .Copy (reader , writer , buf .UpdateActivity (timer ), buf .AddToStatCounter (readCounter )); err != nil {
132
+ return newError ("failed to process response" ).Base (err )
133
+ }
134
+ return nil
135
+ }
0 commit comments