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