github.com/moqsien/xraycore@v1.8.5/app/proxyman/outbound/handler.go (about)

     1  package outbound
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"io"
     7  	"os"
     8  
     9  	"github.com/moqsien/xraycore/app/proxyman"
    10  	"github.com/moqsien/xraycore/common"
    11  	"github.com/moqsien/xraycore/common/buf"
    12  	"github.com/moqsien/xraycore/common/mux"
    13  	"github.com/moqsien/xraycore/common/net"
    14  	"github.com/moqsien/xraycore/common/net/cnc"
    15  	"github.com/moqsien/xraycore/common/session"
    16  	"github.com/moqsien/xraycore/core"
    17  	"github.com/moqsien/xraycore/features/outbound"
    18  	"github.com/moqsien/xraycore/features/policy"
    19  	"github.com/moqsien/xraycore/features/stats"
    20  	"github.com/moqsien/xraycore/proxy"
    21  	"github.com/moqsien/xraycore/transport"
    22  	"github.com/moqsien/xraycore/transport/internet"
    23  	"github.com/moqsien/xraycore/transport/internet/stat"
    24  	"github.com/moqsien/xraycore/transport/internet/tls"
    25  	"github.com/moqsien/xraycore/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  	tag             string
    56  	senderSettings  *proxyman.SenderConfig
    57  	streamSettings  *internet.MemoryStreamConfig
    58  	proxy           proxy.Outbound
    59  	outboundManager outbound.Manager
    60  	mux             *mux.ClientManager
    61  	xudp            *mux.ClientManager
    62  	udp443          string
    63  	uplinkCounter   stats.Counter
    64  	downlinkCounter stats.Counter
    65  }
    66  
    67  // NewHandler creates 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  		tag:             config.Tag,
    73  		outboundManager: v.GetFeature(outbound.ManagerType()).(outbound.Manager),
    74  		uplinkCounter:   uplinkCounter,
    75  		downlinkCounter: downlinkCounter,
    76  	}
    77  
    78  	if config.SenderSettings != nil {
    79  		senderSettings, err := config.SenderSettings.GetInstance()
    80  		if err != nil {
    81  			return nil, err
    82  		}
    83  		switch s := senderSettings.(type) {
    84  		case *proxyman.SenderConfig:
    85  			h.senderSettings = s
    86  			mss, err := internet.ToMemoryStreamConfig(s.StreamSettings)
    87  			if err != nil {
    88  				return nil, newError("failed to parse stream settings").Base(err).AtWarning()
    89  			}
    90  			h.streamSettings = mss
    91  		default:
    92  			return nil, newError("settings is not SenderConfig")
    93  		}
    94  	}
    95  
    96  	proxyConfig, err := config.ProxySettings.GetInstance()
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	rawProxyHandler, err := common.CreateObject(ctx, proxyConfig)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	proxyHandler, ok := rawProxyHandler.(proxy.Outbound)
   107  	if !ok {
   108  		return nil, newError("not an outbound handler")
   109  	}
   110  
   111  	if h.senderSettings != nil && h.senderSettings.MultiplexSettings != nil {
   112  		if config := h.senderSettings.MultiplexSettings; config.Enabled {
   113  			if config.Concurrency < 0 {
   114  				h.mux = &mux.ClientManager{Enabled: false}
   115  			}
   116  			if config.Concurrency == 0 {
   117  				config.Concurrency = 8 // same as before
   118  			}
   119  			if config.Concurrency > 0 {
   120  				h.mux = &mux.ClientManager{
   121  					Enabled: true,
   122  					Picker: &mux.IncrementalWorkerPicker{
   123  						Factory: &mux.DialingWorkerFactory{
   124  							Proxy:  proxyHandler,
   125  							Dialer: h,
   126  							Strategy: mux.ClientStrategy{
   127  								MaxConcurrency: uint32(config.Concurrency),
   128  								MaxConnection:  128,
   129  							},
   130  						},
   131  					},
   132  				}
   133  			}
   134  			if config.XudpConcurrency < 0 {
   135  				h.xudp = &mux.ClientManager{Enabled: false}
   136  			}
   137  			if config.XudpConcurrency == 0 {
   138  				h.xudp = nil // same as before
   139  			}
   140  			if config.XudpConcurrency > 0 {
   141  				h.xudp = &mux.ClientManager{
   142  					Enabled: true,
   143  					Picker: &mux.IncrementalWorkerPicker{
   144  						Factory: &mux.DialingWorkerFactory{
   145  							Proxy:  proxyHandler,
   146  							Dialer: h,
   147  							Strategy: mux.ClientStrategy{
   148  								MaxConcurrency: uint32(config.XudpConcurrency),
   149  								MaxConnection:  128,
   150  							},
   151  						},
   152  					},
   153  				}
   154  			}
   155  			h.udp443 = config.XudpProxyUDP443
   156  		}
   157  	}
   158  
   159  	h.proxy = proxyHandler
   160  	return h, nil
   161  }
   162  
   163  // Tag implements outbound.Handler.
   164  func (h *Handler) Tag() string {
   165  	return h.tag
   166  }
   167  
   168  // Dispatch implements proxy.Outbound.Dispatch.
   169  func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
   170  	outbound := session.OutboundFromContext(ctx)
   171  	if outbound.Target.Network == net.Network_UDP && outbound.OriginalTarget.Address != nil && outbound.OriginalTarget.Address != outbound.Target.Address {
   172  		link.Reader = &buf.EndpointOverrideReader{Reader: link.Reader, Dest: outbound.Target.Address, OriginalDest: outbound.OriginalTarget.Address}
   173  		link.Writer = &buf.EndpointOverrideWriter{Writer: link.Writer, Dest: outbound.Target.Address, OriginalDest: outbound.OriginalTarget.Address}
   174  	}
   175  	if h.mux != nil {
   176  		test := func(err error) {
   177  			if err != nil {
   178  				err := newError("failed to process mux outbound traffic").Base(err)
   179  				session.SubmitOutboundErrorToOriginator(ctx, err)
   180  				err.WriteToLog(session.ExportIDToError(ctx))
   181  				common.Interrupt(link.Writer)
   182  			}
   183  		}
   184  		if outbound.Target.Network == net.Network_UDP && outbound.Target.Port == 443 {
   185  			switch h.udp443 {
   186  			case "reject":
   187  				test(newError("XUDP rejected UDP/443 traffic").AtInfo())
   188  				return
   189  			case "skip":
   190  				goto out
   191  			}
   192  		}
   193  		if h.xudp != nil && outbound.Target.Network == net.Network_UDP {
   194  			if !h.xudp.Enabled {
   195  				goto out
   196  			}
   197  			test(h.xudp.Dispatch(ctx, link))
   198  			return
   199  		}
   200  		if h.mux.Enabled {
   201  			test(h.mux.Dispatch(ctx, link))
   202  			return
   203  		}
   204  	}
   205  out:
   206  	err := h.proxy.Process(ctx, link, h)
   207  	if err != nil {
   208  		if errors.Is(err, io.EOF) || errors.Is(err, io.ErrClosedPipe) || errors.Is(err, context.Canceled) {
   209  			err = nil
   210  		}
   211  	}
   212  	if err != nil {
   213  		// Ensure outbound ray is properly closed.
   214  		err := newError("failed to process outbound traffic").Base(err)
   215  		session.SubmitOutboundErrorToOriginator(ctx, err)
   216  		err.WriteToLog(session.ExportIDToError(ctx))
   217  		common.Interrupt(link.Writer)
   218  	} else {
   219  		common.Close(link.Writer)
   220  	}
   221  	common.Interrupt(link.Reader)
   222  }
   223  
   224  // Address implements internet.Dialer.
   225  func (h *Handler) Address() net.Address {
   226  	if h.senderSettings == nil || h.senderSettings.Via == nil {
   227  		return nil
   228  	}
   229  	return h.senderSettings.Via.AsAddress()
   230  }
   231  
   232  // Dial implements internet.Dialer.
   233  func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connection, error) {
   234  	if h.senderSettings != nil {
   235  		if h.senderSettings.ProxySettings.HasTag() {
   236  			tag := h.senderSettings.ProxySettings.Tag
   237  			handler := h.outboundManager.GetHandler(tag)
   238  			if handler != nil {
   239  				newError("proxying to ", tag, " for dest ", dest).AtDebug().WriteToLog(session.ExportIDToError(ctx))
   240  				ctx = session.ContextWithOutbound(ctx, &session.Outbound{
   241  					Target: dest,
   242  				})
   243  
   244  				opts := pipe.OptionsFromContext(ctx)
   245  				uplinkReader, uplinkWriter := pipe.New(opts...)
   246  				downlinkReader, downlinkWriter := pipe.New(opts...)
   247  
   248  				go handler.Dispatch(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter})
   249  				conn := cnc.NewConnection(cnc.ConnectionInputMulti(uplinkWriter), cnc.ConnectionOutputMulti(downlinkReader))
   250  
   251  				if config := tls.ConfigFromStreamSettings(h.streamSettings); config != nil {
   252  					tlsConfig := config.GetTLSConfig(tls.WithDestination(dest))
   253  					conn = tls.Client(conn, tlsConfig)
   254  				}
   255  
   256  				return h.getStatCouterConnection(conn), nil
   257  			}
   258  
   259  			newError("failed to get outbound handler with tag: ", tag).AtWarning().WriteToLog(session.ExportIDToError(ctx))
   260  		}
   261  
   262  		if h.senderSettings.Via != nil {
   263  			outbound := session.OutboundFromContext(ctx)
   264  			if outbound == nil {
   265  				outbound = new(session.Outbound)
   266  				ctx = session.ContextWithOutbound(ctx, outbound)
   267  			}
   268  			outbound.Gateway = h.senderSettings.Via.AsAddress()
   269  		}
   270  	}
   271  
   272  	if conn, err := h.getUoTConnection(ctx, dest); err != os.ErrInvalid {
   273  		return conn, err
   274  	}
   275  
   276  	conn, err := internet.Dial(ctx, dest, h.streamSettings)
   277  	return h.getStatCouterConnection(conn), err
   278  }
   279  
   280  func (h *Handler) getStatCouterConnection(conn stat.Connection) stat.Connection {
   281  	if h.uplinkCounter != nil || h.downlinkCounter != nil {
   282  		return &stat.CounterConnection{
   283  			Connection:   conn,
   284  			ReadCounter:  h.downlinkCounter,
   285  			WriteCounter: h.uplinkCounter,
   286  		}
   287  	}
   288  	return conn
   289  }
   290  
   291  // GetOutbound implements proxy.GetOutbound.
   292  func (h *Handler) GetOutbound() proxy.Outbound {
   293  	return h.proxy
   294  }
   295  
   296  // Start implements common.Runnable.
   297  func (h *Handler) Start() error {
   298  	return nil
   299  }
   300  
   301  // Close implements common.Closable.
   302  func (h *Handler) Close() error {
   303  	common.Close(h.mux)
   304  	return nil
   305  }