github.com/EagleQL/Xray-core@v1.4.3/app/proxyman/outbound/handler.go (about)

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