github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/app/proxyman/outbound/handler.go (about) 1 package outbound 2 3 import ( 4 "context" 5 6 core "github.com/v2fly/v2ray-core/v5" 7 "github.com/v2fly/v2ray-core/v5/app/proxyman" 8 "github.com/v2fly/v2ray-core/v5/common" 9 "github.com/v2fly/v2ray-core/v5/common/dice" 10 "github.com/v2fly/v2ray-core/v5/common/environment" 11 "github.com/v2fly/v2ray-core/v5/common/environment/envctx" 12 "github.com/v2fly/v2ray-core/v5/common/mux" 13 "github.com/v2fly/v2ray-core/v5/common/net" 14 "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" 15 "github.com/v2fly/v2ray-core/v5/common/serial" 16 "github.com/v2fly/v2ray-core/v5/common/session" 17 "github.com/v2fly/v2ray-core/v5/features/dns" 18 "github.com/v2fly/v2ray-core/v5/features/outbound" 19 "github.com/v2fly/v2ray-core/v5/features/policy" 20 "github.com/v2fly/v2ray-core/v5/features/stats" 21 "github.com/v2fly/v2ray-core/v5/proxy" 22 "github.com/v2fly/v2ray-core/v5/transport" 23 "github.com/v2fly/v2ray-core/v5/transport/internet" 24 "github.com/v2fly/v2ray-core/v5/transport/internet/security" 25 "github.com/v2fly/v2ray-core/v5/transport/pipe" 26 ) 27 28 func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter) { 29 var uplinkCounter stats.Counter 30 var downlinkCounter stats.Counter 31 32 policy := v.GetFeature(policy.ManagerType()).(policy.Manager) 33 if len(tag) > 0 && policy.ForSystem().Stats.OutboundUplink { 34 statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager) 35 name := "outbound>>>" + tag + ">>>traffic>>>uplink" 36 c, _ := stats.GetOrRegisterCounter(statsManager, name) 37 if c != nil { 38 uplinkCounter = c 39 } 40 } 41 if len(tag) > 0 && policy.ForSystem().Stats.OutboundDownlink { 42 statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager) 43 name := "outbound>>>" + tag + ">>>traffic>>>downlink" 44 c, _ := stats.GetOrRegisterCounter(statsManager, name) 45 if c != nil { 46 downlinkCounter = c 47 } 48 } 49 50 return uplinkCounter, downlinkCounter 51 } 52 53 // Handler is an implements of outbound.Handler. 54 type Handler struct { 55 ctx context.Context 56 tag string 57 senderSettings *proxyman.SenderConfig 58 streamSettings *internet.MemoryStreamConfig 59 proxy proxy.Outbound 60 outboundManager outbound.Manager 61 mux *mux.ClientManager 62 uplinkCounter stats.Counter 63 downlinkCounter stats.Counter 64 dns dns.Client 65 } 66 67 // NewHandler create a new Handler based on the given configuration. 68 func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbound.Handler, error) { 69 v := core.MustFromContext(ctx) 70 uplinkCounter, downlinkCounter := getStatCounter(v, config.Tag) 71 h := &Handler{ 72 ctx: ctx, 73 tag: config.Tag, 74 outboundManager: v.GetFeature(outbound.ManagerType()).(outbound.Manager), 75 uplinkCounter: uplinkCounter, 76 downlinkCounter: downlinkCounter, 77 } 78 79 if config.SenderSettings != nil { 80 senderSettings, err := serial.GetInstanceOf(config.SenderSettings) 81 if err != nil { 82 return nil, err 83 } 84 switch s := senderSettings.(type) { 85 case *proxyman.SenderConfig: 86 h.senderSettings = s 87 mss, err := internet.ToMemoryStreamConfig(s.StreamSettings) 88 if err != nil { 89 return nil, newError("failed to parse stream settings").Base(err).AtWarning() 90 } 91 h.streamSettings = mss 92 default: 93 return nil, newError("settings is not SenderConfig") 94 } 95 } 96 97 proxyConfig, err := serial.GetInstanceOf(config.ProxySettings) 98 if err != nil { 99 return nil, err 100 } 101 102 rawProxyHandler, err := common.CreateObject(ctx, proxyConfig) 103 if err != nil { 104 return nil, err 105 } 106 107 proxyHandler, ok := rawProxyHandler.(proxy.Outbound) 108 if !ok { 109 return nil, newError("not an outbound handler") 110 } 111 112 if h.senderSettings != nil && h.senderSettings.MultiplexSettings != nil { 113 config := h.senderSettings.MultiplexSettings 114 if config.Concurrency < 1 || config.Concurrency > 1024 { 115 return nil, newError("invalid mux concurrency: ", config.Concurrency).AtWarning() 116 } 117 h.mux = &mux.ClientManager{ 118 Enabled: h.senderSettings.MultiplexSettings.Enabled, 119 Picker: &mux.IncrementalWorkerPicker{ 120 Factory: mux.NewDialingWorkerFactory( 121 ctx, 122 proxyHandler, 123 h, 124 mux.ClientStrategy{ 125 MaxConcurrency: config.Concurrency, 126 MaxConnection: 128, 127 }, 128 ), 129 }, 130 } 131 } 132 133 if h.senderSettings != nil && h.senderSettings.DomainStrategy != proxyman.SenderConfig_AS_IS { 134 err := core.RequireFeatures(ctx, func(d dns.Client) error { 135 h.dns = d 136 return nil 137 }) 138 if err != nil { 139 return nil, err 140 } 141 } 142 143 h.proxy = proxyHandler 144 return h, nil 145 } 146 147 // Tag implements outbound.Handler. 148 func (h *Handler) Tag() string { 149 return h.tag 150 } 151 152 // Dispatch implements proxy.Outbound.Dispatch. 153 func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) { 154 if h.senderSettings != nil && h.senderSettings.DomainStrategy != proxyman.SenderConfig_AS_IS { 155 outbound := session.OutboundFromContext(ctx) 156 if outbound == nil { 157 outbound = new(session.Outbound) 158 ctx = session.ContextWithOutbound(ctx, outbound) 159 } 160 if outbound.Target.Address != nil && outbound.Target.Address.Family().IsDomain() { 161 if addr := h.resolveIP(ctx, outbound.Target.Address.Domain(), h.Address()); addr != nil { 162 outbound.Target.Address = addr 163 } 164 } 165 } 166 if h.mux != nil && (h.mux.Enabled || session.MuxPreferedFromContext(ctx)) { 167 if err := h.mux.Dispatch(ctx, link); err != nil { 168 err := newError("failed to process mux outbound traffic").Base(err) 169 session.SubmitOutboundErrorToOriginator(ctx, err) 170 err.WriteToLog(session.ExportIDToError(ctx)) 171 common.Interrupt(link.Writer) 172 } 173 } else { 174 if err := h.proxy.Process(ctx, link, h); err != nil { 175 // Ensure outbound ray is properly closed. 176 err := newError("failed to process outbound traffic").Base(err) 177 session.SubmitOutboundErrorToOriginator(ctx, err) 178 err.WriteToLog(session.ExportIDToError(ctx)) 179 common.Interrupt(link.Writer) 180 } else { 181 common.Must(common.Close(link.Writer)) 182 } 183 common.Interrupt(link.Reader) 184 } 185 } 186 187 // Address implements internet.Dialer. 188 func (h *Handler) Address() net.Address { 189 if h.senderSettings == nil || h.senderSettings.Via == nil { 190 return nil 191 } 192 return h.senderSettings.Via.AsAddress() 193 } 194 195 // Dial implements internet.Dialer. 196 func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Connection, error) { 197 if h.senderSettings != nil { 198 if h.senderSettings.ProxySettings.HasTag() && !h.senderSettings.ProxySettings.TransportLayerProxy { 199 tag := h.senderSettings.ProxySettings.Tag 200 handler := h.outboundManager.GetHandler(tag) 201 if handler != nil { 202 newError("proxying to ", tag, " for dest ", dest).AtDebug().WriteToLog(session.ExportIDToError(ctx)) 203 ctx = session.ContextWithOutbound(ctx, &session.Outbound{ 204 Target: dest, 205 }) 206 207 opts := pipe.OptionsFromContext(ctx) 208 uplinkReader, uplinkWriter := pipe.New(opts...) 209 downlinkReader, downlinkWriter := pipe.New(opts...) 210 211 go handler.Dispatch(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter}) 212 conn := net.NewConnection(net.ConnectionInputMulti(uplinkWriter), net.ConnectionOutputMulti(downlinkReader)) 213 214 securityEngine, err := security.CreateSecurityEngineFromSettings(ctx, h.streamSettings) 215 if err != nil { 216 return nil, newError("unable to create security engine").Base(err) 217 } 218 219 if securityEngine != nil { 220 conn, err = securityEngine.Client(conn, security.OptionWithDestination{Dest: dest}) 221 if err != nil { 222 return nil, newError("unable to create security protocol client from security engine").Base(err) 223 } 224 } 225 226 return h.getStatCouterConnection(conn), nil 227 } 228 229 newError("failed to get outbound handler with tag: ", tag).AtWarning().WriteToLog(session.ExportIDToError(ctx)) 230 } 231 232 if h.senderSettings.Via != nil { 233 outbound := session.OutboundFromContext(ctx) 234 if outbound == nil { 235 outbound = new(session.Outbound) 236 ctx = session.ContextWithOutbound(ctx, outbound) 237 } 238 outbound.Gateway = h.senderSettings.Via.AsAddress() 239 } 240 241 if h.senderSettings.DomainStrategy != proxyman.SenderConfig_AS_IS { 242 outbound := session.OutboundFromContext(ctx) 243 if outbound == nil { 244 outbound = new(session.Outbound) 245 ctx = session.ContextWithOutbound(ctx, outbound) 246 } 247 outbound.Resolver = func(ctx context.Context, domain string) net.Address { 248 return h.resolveIP(ctx, domain, h.Address()) 249 } 250 } 251 } 252 253 enablePacketAddrCapture := true 254 if h.senderSettings != nil && h.senderSettings.ProxySettings != nil && h.senderSettings.ProxySettings.HasTag() && h.senderSettings.ProxySettings.TransportLayerProxy { 255 tag := h.senderSettings.ProxySettings.Tag 256 newError("transport layer proxying to ", tag, " for dest ", dest).AtDebug().WriteToLog(session.ExportIDToError(ctx)) 257 ctx = session.SetTransportLayerProxyTagToContext(ctx, tag) 258 enablePacketAddrCapture = false 259 } 260 261 if isStream, err := packetaddr.GetDestinationSubsetOf(dest); err == nil && enablePacketAddrCapture { 262 packetConn, err := internet.ListenSystemPacket(ctx, &net.UDPAddr{IP: net.AnyIP.IP(), Port: 0}, h.streamSettings.SocketSettings) 263 if err != nil { 264 return nil, newError("unable to listen socket").Base(err) 265 } 266 conn := packetaddr.ToPacketAddrConnWrapper(packetConn, isStream) 267 return h.getStatCouterConnection(conn), nil 268 } 269 270 proxyEnvironment := envctx.EnvironmentFromContext(h.ctx).(environment.ProxyEnvironment) 271 transportEnvironment, err := proxyEnvironment.NarrowScopeToTransport("transport") 272 if err != nil { 273 return nil, newError("unable to narrow environment to transport").Base(err) 274 } 275 ctx = envctx.ContextWithEnvironment(ctx, transportEnvironment) 276 conn, err := internet.Dial(ctx, dest, h.streamSettings) 277 return h.getStatCouterConnection(conn), err 278 } 279 280 func (h *Handler) resolveIP(ctx context.Context, domain string, localAddr net.Address) net.Address { 281 strategy := h.senderSettings.DomainStrategy 282 ips, err := dns.LookupIPWithOption(h.dns, domain, dns.IPOption{ 283 IPv4Enable: strategy == proxyman.SenderConfig_USE_IP || strategy == proxyman.SenderConfig_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()), 284 IPv6Enable: strategy == proxyman.SenderConfig_USE_IP || strategy == proxyman.SenderConfig_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()), 285 FakeEnable: false, 286 }) 287 if err != nil { 288 newError("failed to get IP address for domain ", domain).Base(err).WriteToLog(session.ExportIDToError(ctx)) 289 } 290 if len(ips) == 0 { 291 return nil 292 } 293 return net.IPAddress(ips[dice.Roll(len(ips))]) 294 } 295 296 func (h *Handler) getStatCouterConnection(conn internet.Connection) internet.Connection { 297 if h.uplinkCounter != nil || h.downlinkCounter != nil { 298 return &internet.StatCouterConnection{ 299 Connection: conn, 300 ReadCounter: h.downlinkCounter, 301 WriteCounter: h.uplinkCounter, 302 } 303 } 304 return conn 305 } 306 307 // GetOutbound implements proxy.GetOutbound. 308 func (h *Handler) GetOutbound() proxy.Outbound { 309 return h.proxy 310 } 311 312 // Start implements common.Runnable. 313 func (h *Handler) Start() error { 314 return nil 315 } 316 317 // Close implements common.Closable. 318 func (h *Handler) Close() error { 319 common.Close(h.mux) 320 321 if closableProxy, ok := h.proxy.(common.Closable); ok { 322 if err := closableProxy.Close(); err != nil { 323 return newError("unable to close proxy").Base(err) 324 } 325 } 326 return nil 327 }