github.com/Uhtred009/v2ray-core-1@v4.31.2+incompatible/app/proxyman/outbound/handler.go (about)

     1  package outbound
     2  
     3  import (
     4  	"context"
     5  
     6  	"v2ray.com/core"
     7  	"v2ray.com/core/app/proxyman"
     8  	"v2ray.com/core/common"
     9  	"v2ray.com/core/common/mux"
    10  	"v2ray.com/core/common/net"
    11  	"v2ray.com/core/common/session"
    12  	"v2ray.com/core/features/outbound"
    13  	"v2ray.com/core/features/policy"
    14  	"v2ray.com/core/features/stats"
    15  	"v2ray.com/core/proxy"
    16  	"v2ray.com/core/transport"
    17  	"v2ray.com/core/transport/internet"
    18  	"v2ray.com/core/transport/internet/tls"
    19  	"v2ray.com/core/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.DialingWorkerFactory{
   112  					Proxy:  proxyHandler,
   113  					Dialer: h,
   114  					Strategy: mux.ClientStrategy{
   115  						MaxConcurrency: config.Concurrency,
   116  						MaxConnection:  128,
   117  					},
   118  				},
   119  			},
   120  		}
   121  	}
   122  
   123  	h.proxy = proxyHandler
   124  	return h, nil
   125  }
   126  
   127  // Tag implements outbound.Handler.
   128  func (h *Handler) Tag() string {
   129  	return h.tag
   130  }
   131  
   132  // Dispatch implements proxy.Outbound.Dispatch.
   133  func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
   134  	if h.mux != nil && (h.mux.Enabled || session.MuxPreferedFromContext(ctx)) {
   135  		if err := h.mux.Dispatch(ctx, link); err != nil {
   136  			newError("failed to process mux outbound traffic").Base(err).WriteToLog(session.ExportIDToError(ctx))
   137  			common.Interrupt(link.Writer)
   138  		}
   139  	} else {
   140  		if err := h.proxy.Process(ctx, link, h); err != nil {
   141  			// Ensure outbound ray is properly closed.
   142  			newError("failed to process outbound traffic").Base(err).WriteToLog(session.ExportIDToError(ctx))
   143  			common.Interrupt(link.Writer)
   144  		} else {
   145  			common.Must(common.Close(link.Writer))
   146  		}
   147  		common.Interrupt(link.Reader)
   148  	}
   149  }
   150  
   151  // Address implements internet.Dialer.
   152  func (h *Handler) Address() net.Address {
   153  	if h.senderSettings == nil || h.senderSettings.Via == nil {
   154  		return nil
   155  	}
   156  	return h.senderSettings.Via.AsAddress()
   157  }
   158  
   159  // Dial implements internet.Dialer.
   160  func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Connection, error) {
   161  	if h.senderSettings != nil {
   162  		if h.senderSettings.ProxySettings.HasTag() {
   163  			tag := h.senderSettings.ProxySettings.Tag
   164  			handler := h.outboundManager.GetHandler(tag)
   165  			if handler != nil {
   166  				newError("proxying to ", tag, " for dest ", dest).AtDebug().WriteToLog(session.ExportIDToError(ctx))
   167  				ctx = session.ContextWithOutbound(ctx, &session.Outbound{
   168  					Target: dest,
   169  				})
   170  
   171  				opts := pipe.OptionsFromContext(ctx)
   172  				uplinkReader, uplinkWriter := pipe.New(opts...)
   173  				downlinkReader, downlinkWriter := pipe.New(opts...)
   174  
   175  				go handler.Dispatch(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter})
   176  				conn := net.NewConnection(net.ConnectionInputMulti(uplinkWriter), net.ConnectionOutputMulti(downlinkReader))
   177  
   178  				if config := tls.ConfigFromStreamSettings(h.streamSettings); config != nil {
   179  					tlsConfig := config.GetTLSConfig(tls.WithDestination(dest))
   180  					conn = tls.Client(conn, tlsConfig)
   181  				}
   182  
   183  				return h.getStatCouterConnection(conn), nil
   184  			}
   185  
   186  			newError("failed to get outbound handler with tag: ", tag).AtWarning().WriteToLog(session.ExportIDToError(ctx))
   187  		}
   188  
   189  		if h.senderSettings.Via != nil {
   190  			outbound := session.OutboundFromContext(ctx)
   191  			if outbound == nil {
   192  				outbound = new(session.Outbound)
   193  				ctx = session.ContextWithOutbound(ctx, outbound)
   194  			}
   195  			outbound.Gateway = h.senderSettings.Via.AsAddress()
   196  		}
   197  	}
   198  
   199  	conn, err := internet.Dial(ctx, dest, h.streamSettings)
   200  	return h.getStatCouterConnection(conn), err
   201  }
   202  
   203  func (h *Handler) getStatCouterConnection(conn internet.Connection) internet.Connection {
   204  	if h.uplinkCounter != nil || h.downlinkCounter != nil {
   205  		return &internet.StatCouterConnection{
   206  			Connection:   conn,
   207  			ReadCounter:  h.downlinkCounter,
   208  			WriteCounter: h.uplinkCounter,
   209  		}
   210  	}
   211  	return conn
   212  }
   213  
   214  // GetOutbound implements proxy.GetOutbound.
   215  func (h *Handler) GetOutbound() proxy.Outbound {
   216  	return h.proxy
   217  }
   218  
   219  // Start implements common.Runnable.
   220  func (h *Handler) Start() error {
   221  	return nil
   222  }
   223  
   224  // Close implements common.Closable.
   225  func (h *Handler) Close() error {
   226  	common.Close(h.mux)
   227  	return nil
   228  }