github.com/EagleQL/Xray-core@v1.4.3/proxy/vmess/outbound/outbound.go (about) 1 package outbound 2 3 //go:generate go run github.com/xtls/xray-core/common/errors/errorgen 4 5 import ( 6 "context" 7 "time" 8 9 "github.com/xtls/xray-core/common" 10 "github.com/xtls/xray-core/common/buf" 11 "github.com/xtls/xray-core/common/net" 12 "github.com/xtls/xray-core/common/platform" 13 "github.com/xtls/xray-core/common/protocol" 14 "github.com/xtls/xray-core/common/retry" 15 "github.com/xtls/xray-core/common/session" 16 "github.com/xtls/xray-core/common/signal" 17 "github.com/xtls/xray-core/common/task" 18 "github.com/xtls/xray-core/common/xudp" 19 core "github.com/xtls/xray-core/core" 20 "github.com/xtls/xray-core/features/policy" 21 "github.com/xtls/xray-core/proxy/vmess" 22 "github.com/xtls/xray-core/proxy/vmess/encoding" 23 "github.com/xtls/xray-core/transport" 24 "github.com/xtls/xray-core/transport/internet" 25 ) 26 27 // Handler is an outbound connection handler for VMess protocol. 28 type Handler struct { 29 serverList *protocol.ServerList 30 serverPicker protocol.ServerPicker 31 policyManager policy.Manager 32 cone bool 33 } 34 35 // New creates a new VMess outbound handler. 36 func New(ctx context.Context, config *Config) (*Handler, error) { 37 serverList := protocol.NewServerList() 38 for _, rec := range config.Receiver { 39 s, err := protocol.NewServerSpecFromPB(rec) 40 if err != nil { 41 return nil, newError("failed to parse server spec").Base(err) 42 } 43 serverList.AddServer(s) 44 } 45 46 v := core.MustFromContext(ctx) 47 handler := &Handler{ 48 serverList: serverList, 49 serverPicker: protocol.NewRoundRobinServerPicker(serverList), 50 policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), 51 cone: ctx.Value("cone").(bool), 52 } 53 54 return handler, nil 55 } 56 57 // Process implements proxy.Outbound.Process(). 58 func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { 59 var rec *protocol.ServerSpec 60 var conn internet.Connection 61 62 err := retry.ExponentialBackoff(5, 200).On(func() error { 63 rec = h.serverPicker.PickServer() 64 rawConn, err := dialer.Dial(ctx, rec.Destination()) 65 if err != nil { 66 return err 67 } 68 conn = rawConn 69 70 return nil 71 }) 72 if err != nil { 73 return newError("failed to find an available destination").Base(err).AtWarning() 74 } 75 defer conn.Close() 76 77 outbound := session.OutboundFromContext(ctx) 78 if outbound == nil || !outbound.Target.IsValid() { 79 return newError("target not specified").AtError() 80 } 81 82 target := outbound.Target 83 newError("tunneling request to ", target, " via ", rec.Destination()).WriteToLog(session.ExportIDToError(ctx)) 84 85 command := protocol.RequestCommandTCP 86 if target.Network == net.Network_UDP { 87 command = protocol.RequestCommandUDP 88 } 89 if target.Address.Family().IsDomain() && target.Address.Domain() == "v1.mux.cool" { 90 command = protocol.RequestCommandMux 91 } 92 93 user := rec.PickUser() 94 request := &protocol.RequestHeader{ 95 Version: encoding.Version, 96 User: user, 97 Command: command, 98 Address: target.Address, 99 Port: target.Port, 100 Option: protocol.RequestOptionChunkStream, 101 } 102 103 account := request.User.Account.(*vmess.MemoryAccount) 104 request.Security = account.Security 105 106 if request.Security == protocol.SecurityType_AES128_GCM || request.Security == protocol.SecurityType_NONE || request.Security == protocol.SecurityType_CHACHA20_POLY1305 { 107 request.Option.Set(protocol.RequestOptionChunkMasking) 108 } 109 110 if shouldEnablePadding(request.Security) && request.Option.Has(protocol.RequestOptionChunkMasking) { 111 request.Option.Set(protocol.RequestOptionGlobalPadding) 112 } 113 114 if request.Security == protocol.SecurityType_ZERO { 115 request.Security = protocol.SecurityType_NONE 116 request.Option.Clear(protocol.RequestOptionChunkStream) 117 request.Option.Clear(protocol.RequestOptionChunkMasking) 118 } 119 120 input := link.Reader 121 output := link.Writer 122 123 isAEAD := false 124 if !aeadDisabled && len(account.AlterIDs) == 0 { 125 isAEAD = true 126 } 127 128 session := encoding.NewClientSession(ctx, isAEAD, protocol.DefaultIDHash) 129 sessionPolicy := h.policyManager.ForLevel(request.User.Level) 130 131 ctx, cancel := context.WithCancel(ctx) 132 timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) 133 134 if request.Command == protocol.RequestCommandUDP && h.cone && request.Port != 53 && request.Port != 443 { 135 request.Command = protocol.RequestCommandMux 136 request.Address = net.DomainAddress("v1.mux.cool") 137 request.Port = net.Port(666) 138 } 139 140 requestDone := func() error { 141 defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) 142 143 writer := buf.NewBufferedWriter(buf.NewWriter(conn)) 144 if err := session.EncodeRequestHeader(request, writer); err != nil { 145 return newError("failed to encode request").Base(err).AtWarning() 146 } 147 148 bodyWriter := session.EncodeRequestBody(request, writer) 149 bodyWriter2 := bodyWriter 150 if request.Command == protocol.RequestCommandMux && request.Port == 666 { 151 bodyWriter = xudp.NewPacketWriter(bodyWriter, target) 152 } 153 if err := buf.CopyOnceTimeout(input, bodyWriter, time.Millisecond*100); err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout { 154 return newError("failed to write first payload").Base(err) 155 } 156 157 if err := writer.SetBuffered(false); err != nil { 158 return err 159 } 160 161 if err := buf.Copy(input, bodyWriter, buf.UpdateActivity(timer)); err != nil { 162 return err 163 } 164 165 if request.Option.Has(protocol.RequestOptionChunkStream) { 166 if err := bodyWriter2.WriteMultiBuffer(buf.MultiBuffer{}); err != nil { 167 return err 168 } 169 } 170 171 return nil 172 } 173 174 responseDone := func() error { 175 defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) 176 177 reader := &buf.BufferedReader{Reader: buf.NewReader(conn)} 178 header, err := session.DecodeResponseHeader(reader) 179 if err != nil { 180 return newError("failed to read header").Base(err) 181 } 182 h.handleCommand(rec.Destination(), header.Command) 183 184 bodyReader := session.DecodeResponseBody(request, reader) 185 if request.Command == protocol.RequestCommandMux && request.Port == 666 { 186 bodyReader = xudp.NewPacketReader(&buf.BufferedReader{Reader: bodyReader}) 187 } 188 189 return buf.Copy(bodyReader, output, buf.UpdateActivity(timer)) 190 } 191 192 var responseDonePost = task.OnSuccess(responseDone, task.Close(output)) 193 if err := task.Run(ctx, requestDone, responseDonePost); err != nil { 194 return newError("connection ends").Base(err) 195 } 196 197 return nil 198 } 199 200 var ( 201 enablePadding = false 202 aeadDisabled = false 203 ) 204 205 func shouldEnablePadding(s protocol.SecurityType) bool { 206 return enablePadding || s == protocol.SecurityType_AES128_GCM || s == protocol.SecurityType_CHACHA20_POLY1305 || s == protocol.SecurityType_AUTO 207 } 208 209 func init() { 210 common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { 211 return New(ctx, config.(*Config)) 212 })) 213 214 const defaultFlagValue = "NOT_DEFINED_AT_ALL" 215 216 paddingValue := platform.NewEnvFlag("xray.vmess.padding").GetValue(func() string { return defaultFlagValue }) 217 if paddingValue != defaultFlagValue { 218 enablePadding = true 219 } 220 221 isAeadDisabled := platform.NewEnvFlag("xray.vmess.aead.disabled").GetValue(func() string { return defaultFlagValue }) 222 if isAeadDisabled == "true" { 223 aeadDisabled = true 224 } 225 }