github.com/v2fly/v2ray-core/v4@v4.45.2/app/proxyman/outbound/handler.go (about)

     1  package outbound
     2  
     3  import (
     4  	"context"
     5  
     6  	core "github.com/v2fly/v2ray-core/v4"
     7  	"github.com/v2fly/v2ray-core/v4/app/proxyman"
     8  	"github.com/v2fly/v2ray-core/v4/common"
     9  	"github.com/v2fly/v2ray-core/v4/common/mux"
    10  	"github.com/v2fly/v2ray-core/v4/common/net"
    11  	"github.com/v2fly/v2ray-core/v4/common/session"
    12  	"github.com/v2fly/v2ray-core/v4/features/outbound"
    13  	"github.com/v2fly/v2ray-core/v4/features/policy"
    14  	"github.com/v2fly/v2ray-core/v4/features/stats"
    15  	"github.com/v2fly/v2ray-core/v4/proxy"
    16  	"github.com/v2fly/v2ray-core/v4/transport"
    17  	"github.com/v2fly/v2ray-core/v4/transport/internet"
    18  	"github.com/v2fly/v2ray-core/v4/transport/internet/tls"
    19  	"github.com/v2fly/v2ray-core/v4/transport/pipe"
    20  )
    21  
    22  func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter) {
    23  	var uplinkCounter stats.Counter
    24  	var downlinkCounter stats.Counter
    25  
    26  	policy := v.GetFeature(policy.ManagerType()).(policy.Manager)
    27  	if len(tag) > 0 && policy.ForSystem().Stats.OutboundUplink {
    28  		statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
    29  		name := "outbound>>>" + tag + ">>>traffic>>>uplink"
    30  		c, _ := stats.GetOrRegisterCounter(statsManager, name)
    31  		if c != nil {
    32  			uplinkCounter = c
    33  		}
    34  	}
    35  	if len(tag) > 0 && policy.ForSystem().Stats.OutboundDownlink {
    36  		statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
    37  		name := "outbound>>>" + tag + ">>>traffic>>>downlink"
    38  		c, _ := stats.GetOrRegisterCounter(statsManager, name)
    39  		if c != nil {
    40  			downlinkCounter = c
    41  		}
    42  	}
    43  
    44  	return uplinkCounter, downlinkCounter
    45  }
    46  
    47  // Handler is an implements of outbound.Handler.
    48  type Handler struct {
    49  	tag             string
    50  	senderSettings  *proxyman.SenderConfig
    51  	streamSettings  *internet.MemoryStreamConfig
    52  	proxy           proxy.Outbound
    53  	outboundManager outbound.Manager
    54  	mux             *mux.ClientManager
    55  	uplinkCounter   stats.Counter
    56  	downlinkCounter stats.Counter
    57  }
    58  
    59  // NewHandler create a new Handler based on the given configuration.
    60  func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbound.Handler, error) {
    61  	v := core.MustFromContext(ctx)
    62  	uplinkCounter, downlinkCounter := getStatCounter(v, config.Tag)
    63  	h := &Handler{
    64  		tag:             config.Tag,
    65  		outboundManager: v.GetFeature(outbound.ManagerType()).(outbound.Manager),
    66  		uplinkCounter:   uplinkCounter,
    67  		downlinkCounter: downlinkCounter,
    68  	}
    69  
    70  	if config.SenderSettings != nil {
    71  		senderSettings, err := config.SenderSettings.GetInstance()
    72  		if err != nil {
    73  			return nil, err
    74  		}
    75  		switch s := senderSettings.(type) {
    76  		case *proxyman.SenderConfig:
    77  			h.senderSettings = s
    78  			mss, err := internet.ToMemoryStreamConfig(s.StreamSettings)
    79  			if err != nil {
    80  				return nil, newError("failed to parse stream settings").Base(err).AtWarning()
    81  			}
    82  			h.streamSettings = mss
    83  		default:
    84  			return nil, newError("settings is not SenderConfig")
    85  		}
    86  	}
    87  
    88  	proxyConfig, err := config.ProxySettings.GetInstance()
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	rawProxyHandler, err := common.CreateObject(ctx, proxyConfig)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	proxyHandler, ok := rawProxyHandler.(proxy.Outbound)
    99  	if !ok {
   100  		return nil, newError("not an outbound handler")
   101  	}
   102  
   103  	if h.senderSettings != nil && h.senderSettings.MultiplexSettings != nil {
   104  		config := h.senderSettings.MultiplexSettings
   105  		if config.Concurrency < 1 || config.Concurrency > 1024 {
   106  			return nil, newError("invalid mux concurrency: ", config.Concurrency).AtWarning()
   107  		}
   108  		h.mux = &mux.ClientManager{
   109  			Enabled: h.senderSettings.MultiplexSettings.Enabled,
   110  			Picker: &mux.IncrementalWorkerPicker{
   111  				Factory: mux.NewDialingWorkerFactory(
   112  					ctx,
   113  					proxyHandler,
   114  					h,
   115  					mux.ClientStrategy{
   116  						MaxConcurrency: config.Concurrency,
   117  						MaxConnection:  128,
   118  					},
   119  				),
   120  			},
   121  		}
   122  	}
   123  
   124  	h.proxy = proxyHandler
   125  	return h, nil
   126  }
   127  
   128  // Tag implements outbound.Handler.
   129  func (h *Handler) Tag() string {
   130  	return h.tag
   131  }
   132  
   133  // Dispatch implements proxy.Outbound.Dispatch.
   134  func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
   135  	if h.mux != nil && (h.mux.Enabled || session.MuxPreferedFromContext(ctx)) {
   136  		if err := h.mux.Dispatch(ctx, link); err != nil {
   137  			err := newError("failed to process mux outbound traffic").Base(err)
   138  			session.SubmitOutboundErrorToOriginator(ctx, err)
   139  			err.WriteToLog(session.ExportIDToError(ctx))
   140  			common.Interrupt(link.Writer)
   141  		}
   142  	} else {
   143  		if err := h.proxy.Process(ctx, link, h); err != nil {
   144  			// Ensure outbound ray is properly closed.
   145  			err := newError("failed to process outbound traffic").Base(err)
   146  			session.SubmitOutboundErrorToOriginator(ctx, err)
   147  			err.WriteToLog(session.ExportIDToError(ctx))
   148  			common.Interrupt(link.Writer)
   149  		} else {
   150  			common.Must(common.Close(link.Writer))
   151  		}
   152  		common.Interrupt(link.Reader)
   153  	}
   154  }
   155  
   156  // Address implements internet.Dialer.
   157  func (h *Handler) Address() net.Address {
   158  	if h.senderSettings == nil || h.senderSettings.Via == nil {
   159  		return nil
   160  	}
   161  	return h.senderSettings.Via.AsAddress()
   162  }
   163  
   164  // Dial implements internet.Dialer.
   165  func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Connection, error) {
   166  	if h.senderSettings != nil {
   167  		if h.senderSettings.ProxySettings.HasTag() && !h.senderSettings.ProxySettings.TransportLayerProxy {
   168  			tag := h.senderSettings.ProxySettings.Tag
   169  			handler := h.outboundManager.GetHandler(tag)
   170  			if handler != nil {
   171  				newError("proxying to ", tag, " for dest ", dest).AtDebug().WriteToLog(session.ExportIDToError(ctx))
   172  				ctx = session.ContextWithOutbound(ctx, &session.Outbound{
   173  					Target: dest,
   174  				})
   175  
   176  				opts := pipe.OptionsFromContext(ctx)
   177  				uplinkReader, uplinkWriter := pipe.New(opts...)
   178  				downlinkReader, downlinkWriter := pipe.New(opts...)
   179  
   180  				go handler.Dispatch(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter})
   181  				conn := net.NewConnection(net.ConnectionInputMulti(uplinkWriter), net.ConnectionOutputMulti(downlinkReader))
   182  
   183  				if config := tls.ConfigFromStreamSettings(h.streamSettings); config != nil {
   184  					tlsConfig := config.GetTLSConfig(tls.WithDestination(dest))
   185  					conn = tls.Client(conn, tlsConfig)
   186  				}
   187  
   188  				return h.getStatCouterConnection(conn), nil
   189  			}
   190  
   191  			newError("failed to get outbound handler with tag: ", tag).AtWarning().WriteToLog(session.ExportIDToError(ctx))
   192  		}
   193  
   194  		if h.senderSettings.Via != nil {
   195  			outbound := session.OutboundFromContext(ctx)
   196  			if outbound == nil {
   197  				outbound = new(session.Outbound)
   198  				ctx = session.ContextWithOutbound(ctx, outbound)
   199  			}
   200  			outbound.Gateway = h.senderSettings.Via.AsAddress()
   201  		}
   202  	}
   203  
   204  	if h.senderSettings != nil && h.senderSettings.ProxySettings != nil && h.senderSettings.ProxySettings.HasTag() && h.senderSettings.ProxySettings.TransportLayerProxy {
   205  		tag := h.senderSettings.ProxySettings.Tag
   206  		newError("transport layer proxying to ", tag, " for dest ", dest).AtDebug().WriteToLog(session.ExportIDToError(ctx))
   207  		ctx = session.SetTransportLayerProxyTagToContext(ctx, tag)
   208  	}
   209  
   210  	conn, err := internet.Dial(ctx, dest, h.streamSettings)
   211  	return h.getStatCouterConnection(conn), err
   212  }
   213  
   214  func (h *Handler) getStatCouterConnection(conn internet.Connection) internet.Connection {
   215  	if h.uplinkCounter != nil || h.downlinkCounter != nil {
   216  		return &internet.StatCouterConnection{
   217  			Connection:   conn,
   218  			ReadCounter:  h.downlinkCounter,
   219  			WriteCounter: h.uplinkCounter,
   220  		}
   221  	}
   222  	return conn
   223  }
   224  
   225  // GetOutbound implements proxy.GetOutbound.
   226  func (h *Handler) GetOutbound() proxy.Outbound {
   227  	return h.proxy
   228  }
   229  
   230  // Start implements common.Runnable.
   231  func (h *Handler) Start() error {
   232  	return nil
   233  }
   234  
   235  // Close implements common.Closable.
   236  func (h *Handler) Close() error {
   237  	common.Close(h.mux)
   238  	return nil
   239  }