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  }