github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/app/proxyman/inbound/worker.go (about)

     1  package inbound
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  	"sync/atomic"
     7  	"time"
     8  
     9  	"github.com/v2fly/v2ray-core/v5/app/proxyman"
    10  	"github.com/v2fly/v2ray-core/v5/common"
    11  	"github.com/v2fly/v2ray-core/v5/common/buf"
    12  	"github.com/v2fly/v2ray-core/v5/common/environment"
    13  	"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
    14  	"github.com/v2fly/v2ray-core/v5/common/net"
    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/common/signal/done"
    18  	"github.com/v2fly/v2ray-core/v5/common/task"
    19  	"github.com/v2fly/v2ray-core/v5/features/routing"
    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/internet"
    23  	"github.com/v2fly/v2ray-core/v5/transport/internet/tcp"
    24  	"github.com/v2fly/v2ray-core/v5/transport/internet/udp"
    25  	"github.com/v2fly/v2ray-core/v5/transport/pipe"
    26  )
    27  
    28  type worker interface {
    29  	Start() error
    30  	Close() error
    31  	Port() net.Port
    32  	Proxy() proxy.Inbound
    33  }
    34  
    35  type tcpWorker struct {
    36  	address         net.Address
    37  	port            net.Port
    38  	proxy           proxy.Inbound
    39  	stream          *internet.MemoryStreamConfig
    40  	recvOrigDest    bool
    41  	tag             string
    42  	dispatcher      routing.Dispatcher
    43  	sniffingConfig  *proxyman.SniffingConfig
    44  	uplinkCounter   stats.Counter
    45  	downlinkCounter stats.Counter
    46  
    47  	hub internet.Listener
    48  
    49  	ctx context.Context
    50  }
    51  
    52  func getTProxyType(s *internet.MemoryStreamConfig) internet.SocketConfig_TProxyMode {
    53  	if s == nil || s.SocketSettings == nil {
    54  		return internet.SocketConfig_Off
    55  	}
    56  	return s.SocketSettings.Tproxy
    57  }
    58  
    59  func (w *tcpWorker) callback(conn internet.Connection) {
    60  	ctx, cancel := context.WithCancel(w.ctx)
    61  	sid := session.NewID()
    62  	ctx = session.ContextWithID(ctx, sid)
    63  
    64  	if w.recvOrigDest {
    65  		var dest net.Destination
    66  		switch getTProxyType(w.stream) {
    67  		case internet.SocketConfig_Redirect:
    68  			d, err := tcp.GetOriginalDestination(conn)
    69  			if err != nil {
    70  				newError("failed to get original destination").Base(err).WriteToLog(session.ExportIDToError(ctx))
    71  			} else {
    72  				dest = d
    73  			}
    74  		case internet.SocketConfig_TProxy:
    75  			dest = net.DestinationFromAddr(conn.LocalAddr())
    76  		}
    77  		if dest.IsValid() {
    78  			ctx = session.ContextWithOutbound(ctx, &session.Outbound{
    79  				Target: dest,
    80  			})
    81  		}
    82  	}
    83  	ctx = session.ContextWithInbound(ctx, &session.Inbound{
    84  		Source:  net.DestinationFromAddr(conn.RemoteAddr()),
    85  		Gateway: net.TCPDestination(w.address, w.port),
    86  		Tag:     w.tag,
    87  	})
    88  	content := new(session.Content)
    89  	if w.sniffingConfig != nil {
    90  		content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
    91  		content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
    92  		content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly
    93  	}
    94  	ctx = session.ContextWithContent(ctx, content)
    95  	if w.uplinkCounter != nil || w.downlinkCounter != nil {
    96  		conn = &internet.StatCouterConnection{
    97  			Connection:   conn,
    98  			ReadCounter:  w.uplinkCounter,
    99  			WriteCounter: w.downlinkCounter,
   100  		}
   101  	}
   102  	if err := w.proxy.Process(ctx, net.Network_TCP, conn, w.dispatcher); err != nil {
   103  		newError("connection ends").Base(err).WriteToLog(session.ExportIDToError(ctx))
   104  	}
   105  	cancel()
   106  	if err := conn.Close(); err != nil {
   107  		newError("failed to close connection").Base(err).WriteToLog(session.ExportIDToError(ctx))
   108  	}
   109  }
   110  
   111  func (w *tcpWorker) Proxy() proxy.Inbound {
   112  	return w.proxy
   113  }
   114  
   115  func (w *tcpWorker) Start() error {
   116  	ctx := context.Background()
   117  	proxyEnvironment := envctx.EnvironmentFromContext(w.ctx).(environment.ProxyEnvironment)
   118  	transportEnvironment, err := proxyEnvironment.NarrowScopeToTransport("transport")
   119  	if err != nil {
   120  		return newError("unable to narrow environment to transport").Base(err)
   121  	}
   122  	ctx = envctx.ContextWithEnvironment(ctx, transportEnvironment)
   123  	hub, err := internet.ListenTCP(ctx, w.address, w.port, w.stream, func(conn internet.Connection) {
   124  		go w.callback(conn)
   125  	})
   126  	if err != nil {
   127  		return newError("failed to listen TCP on ", w.port).AtWarning().Base(err)
   128  	}
   129  	w.hub = hub
   130  	return nil
   131  }
   132  
   133  func (w *tcpWorker) Close() error {
   134  	var errors []interface{}
   135  	if w.hub != nil {
   136  		if err := common.Close(w.hub); err != nil {
   137  			errors = append(errors, err)
   138  		}
   139  		if err := common.Close(w.proxy); err != nil {
   140  			errors = append(errors, err)
   141  		}
   142  	}
   143  	if len(errors) > 0 {
   144  		return newError("failed to close all resources").Base(newError(serial.Concat(errors...)))
   145  	}
   146  
   147  	return nil
   148  }
   149  
   150  func (w *tcpWorker) Port() net.Port {
   151  	return w.port
   152  }
   153  
   154  type udpConn struct {
   155  	lastActivityTime int64 // in seconds
   156  	reader           buf.Reader
   157  	writer           buf.Writer
   158  	output           func([]byte) (int, error)
   159  	remote           net.Addr
   160  	local            net.Addr
   161  	done             *done.Instance
   162  	uplink           stats.Counter
   163  	downlink         stats.Counter
   164  	inactive         bool
   165  }
   166  
   167  func (c *udpConn) setInactive() {
   168  	c.inactive = true
   169  }
   170  
   171  func (c *udpConn) updateActivity() {
   172  	atomic.StoreInt64(&c.lastActivityTime, time.Now().Unix())
   173  }
   174  
   175  // ReadMultiBuffer implements buf.Reader
   176  func (c *udpConn) ReadMultiBuffer() (buf.MultiBuffer, error) {
   177  	mb, err := c.reader.ReadMultiBuffer()
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  	c.updateActivity()
   182  
   183  	if c.uplink != nil {
   184  		c.uplink.Add(int64(mb.Len()))
   185  	}
   186  
   187  	return mb, nil
   188  }
   189  
   190  func (c *udpConn) Read(buf []byte) (int, error) {
   191  	panic("not implemented")
   192  }
   193  
   194  // Write implements io.Writer.
   195  func (c *udpConn) Write(buf []byte) (int, error) {
   196  	n, err := c.output(buf)
   197  	if c.downlink != nil {
   198  		c.downlink.Add(int64(n))
   199  	}
   200  	if err == nil {
   201  		c.updateActivity()
   202  	}
   203  	return n, err
   204  }
   205  
   206  func (c *udpConn) Close() error {
   207  	common.Must(c.done.Close())
   208  	common.Must(common.Close(c.writer))
   209  	return nil
   210  }
   211  
   212  func (c *udpConn) RemoteAddr() net.Addr {
   213  	return c.remote
   214  }
   215  
   216  func (c *udpConn) LocalAddr() net.Addr {
   217  	return c.local
   218  }
   219  
   220  func (*udpConn) SetDeadline(time.Time) error {
   221  	return nil
   222  }
   223  
   224  func (*udpConn) SetReadDeadline(time.Time) error {
   225  	return nil
   226  }
   227  
   228  func (*udpConn) SetWriteDeadline(time.Time) error {
   229  	return nil
   230  }
   231  
   232  type connID struct {
   233  	src  net.Destination
   234  	dest net.Destination
   235  }
   236  
   237  type udpWorker struct {
   238  	sync.RWMutex
   239  
   240  	proxy           proxy.Inbound
   241  	hub             *udp.Hub
   242  	address         net.Address
   243  	port            net.Port
   244  	tag             string
   245  	stream          *internet.MemoryStreamConfig
   246  	dispatcher      routing.Dispatcher
   247  	sniffingConfig  *proxyman.SniffingConfig
   248  	uplinkCounter   stats.Counter
   249  	downlinkCounter stats.Counter
   250  
   251  	checker    *task.Periodic
   252  	activeConn map[connID]*udpConn
   253  
   254  	ctx context.Context
   255  }
   256  
   257  func (w *udpWorker) getConnection(id connID) (*udpConn, bool) {
   258  	w.Lock()
   259  	defer w.Unlock()
   260  
   261  	if conn, found := w.activeConn[id]; found && !conn.done.Done() {
   262  		return conn, true
   263  	}
   264  
   265  	pReader, pWriter := pipe.New(pipe.DiscardOverflow(), pipe.WithSizeLimit(16*1024))
   266  	conn := &udpConn{
   267  		reader: pReader,
   268  		writer: pWriter,
   269  		output: func(b []byte) (int, error) {
   270  			return w.hub.WriteTo(b, id.src)
   271  		},
   272  		remote: &net.UDPAddr{
   273  			IP:   id.src.Address.IP(),
   274  			Port: int(id.src.Port),
   275  		},
   276  		local: &net.UDPAddr{
   277  			IP:   w.address.IP(),
   278  			Port: int(w.port),
   279  		},
   280  		done:     done.New(),
   281  		uplink:   w.uplinkCounter,
   282  		downlink: w.downlinkCounter,
   283  	}
   284  	w.activeConn[id] = conn
   285  
   286  	conn.updateActivity()
   287  	return conn, false
   288  }
   289  
   290  func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest net.Destination) {
   291  	id := connID{
   292  		src: source,
   293  	}
   294  	if originalDest.IsValid() {
   295  		id.dest = originalDest
   296  	}
   297  	conn, existing := w.getConnection(id)
   298  
   299  	// payload will be discarded in pipe is full.
   300  	conn.writer.WriteMultiBuffer(buf.MultiBuffer{b})
   301  
   302  	if !existing {
   303  		common.Must(w.checker.Start())
   304  
   305  		go func() {
   306  			ctx := w.ctx
   307  			sid := session.NewID()
   308  			ctx = session.ContextWithID(ctx, sid)
   309  
   310  			if originalDest.IsValid() {
   311  				ctx = session.ContextWithOutbound(ctx, &session.Outbound{
   312  					Target: originalDest,
   313  				})
   314  			}
   315  			ctx = session.ContextWithInbound(ctx, &session.Inbound{
   316  				Source:  source,
   317  				Gateway: net.UDPDestination(w.address, w.port),
   318  				Tag:     w.tag,
   319  			})
   320  			content := new(session.Content)
   321  			if w.sniffingConfig != nil {
   322  				content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
   323  				content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
   324  				content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly
   325  			}
   326  			ctx = session.ContextWithContent(ctx, content)
   327  			if err := w.proxy.Process(ctx, net.Network_UDP, conn, w.dispatcher); err != nil {
   328  				newError("connection ends").Base(err).WriteToLog(session.ExportIDToError(ctx))
   329  			}
   330  			conn.Close()
   331  			// conn not removed by checker TODO may be lock worker here is better
   332  			if !conn.inactive {
   333  				conn.setInactive()
   334  				w.removeConn(id)
   335  			}
   336  		}()
   337  	}
   338  }
   339  
   340  func (w *udpWorker) removeConn(id connID) {
   341  	w.Lock()
   342  	delete(w.activeConn, id)
   343  	w.Unlock()
   344  }
   345  
   346  func (w *udpWorker) handlePackets() {
   347  	receive := w.hub.Receive()
   348  	for payload := range receive {
   349  		w.callback(payload.Payload, payload.Source, payload.Target)
   350  	}
   351  }
   352  
   353  func (w *udpWorker) clean() error {
   354  	nowSec := time.Now().Unix()
   355  	w.Lock()
   356  	defer w.Unlock()
   357  
   358  	if len(w.activeConn) == 0 {
   359  		return newError("no more connections. stopping...")
   360  	}
   361  
   362  	for addr, conn := range w.activeConn {
   363  		if nowSec-atomic.LoadInt64(&conn.lastActivityTime) > 8 { // TODO Timeout too small
   364  			if !conn.inactive {
   365  				conn.setInactive()
   366  				delete(w.activeConn, addr)
   367  			}
   368  			conn.Close()
   369  		}
   370  	}
   371  
   372  	if len(w.activeConn) == 0 {
   373  		w.activeConn = make(map[connID]*udpConn, 16)
   374  	}
   375  
   376  	return nil
   377  }
   378  
   379  func (w *udpWorker) Start() error {
   380  	w.activeConn = make(map[connID]*udpConn, 16)
   381  	ctx := context.Background()
   382  	proxyEnvironment := envctx.EnvironmentFromContext(w.ctx).(environment.ProxyEnvironment)
   383  	transportEnvironment, err := proxyEnvironment.NarrowScopeToTransport("transport")
   384  	if err != nil {
   385  		return newError("unable to narrow environment to transport").Base(err)
   386  	}
   387  	ctx = envctx.ContextWithEnvironment(ctx, transportEnvironment)
   388  	h, err := udp.ListenUDP(ctx, w.address, w.port, w.stream, udp.HubCapacity(256))
   389  	if err != nil {
   390  		return err
   391  	}
   392  
   393  	w.checker = &task.Periodic{
   394  		Interval: time.Second * 16,
   395  		Execute:  w.clean,
   396  	}
   397  
   398  	w.hub = h
   399  	go w.handlePackets()
   400  	return nil
   401  }
   402  
   403  func (w *udpWorker) Close() error {
   404  	w.Lock()
   405  	defer w.Unlock()
   406  
   407  	var errors []interface{}
   408  
   409  	if w.hub != nil {
   410  		if err := w.hub.Close(); err != nil {
   411  			errors = append(errors, err)
   412  		}
   413  	}
   414  
   415  	if w.checker != nil {
   416  		if err := w.checker.Close(); err != nil {
   417  			errors = append(errors, err)
   418  		}
   419  	}
   420  
   421  	if err := common.Close(w.proxy); err != nil {
   422  		errors = append(errors, err)
   423  	}
   424  
   425  	if len(errors) > 0 {
   426  		return newError("failed to close all resources").Base(newError(serial.Concat(errors...)))
   427  	}
   428  	return nil
   429  }
   430  
   431  func (w *udpWorker) Port() net.Port {
   432  	return w.port
   433  }
   434  
   435  func (w *udpWorker) Proxy() proxy.Inbound {
   436  	return w.proxy
   437  }
   438  
   439  type dsWorker struct {
   440  	address         net.Address
   441  	proxy           proxy.Inbound
   442  	stream          *internet.MemoryStreamConfig
   443  	tag             string
   444  	dispatcher      routing.Dispatcher
   445  	sniffingConfig  *proxyman.SniffingConfig
   446  	uplinkCounter   stats.Counter
   447  	downlinkCounter stats.Counter
   448  
   449  	hub internet.Listener
   450  
   451  	ctx context.Context
   452  }
   453  
   454  func (w *dsWorker) callback(conn internet.Connection) {
   455  	ctx, cancel := context.WithCancel(w.ctx)
   456  	sid := session.NewID()
   457  	ctx = session.ContextWithID(ctx, sid)
   458  
   459  	ctx = session.ContextWithInbound(ctx, &session.Inbound{
   460  		Source:  net.DestinationFromAddr(conn.RemoteAddr()),
   461  		Gateway: net.UnixDestination(w.address),
   462  		Tag:     w.tag,
   463  	})
   464  	content := new(session.Content)
   465  	if w.sniffingConfig != nil {
   466  		content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
   467  		content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
   468  		content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly
   469  	}
   470  	ctx = session.ContextWithContent(ctx, content)
   471  	if w.uplinkCounter != nil || w.downlinkCounter != nil {
   472  		conn = &internet.StatCouterConnection{
   473  			Connection:   conn,
   474  			ReadCounter:  w.uplinkCounter,
   475  			WriteCounter: w.downlinkCounter,
   476  		}
   477  	}
   478  	if err := w.proxy.Process(ctx, net.Network_UNIX, conn, w.dispatcher); err != nil {
   479  		newError("connection ends").Base(err).WriteToLog(session.ExportIDToError(ctx))
   480  	}
   481  	cancel()
   482  	if err := conn.Close(); err != nil {
   483  		newError("failed to close connection").Base(err).WriteToLog(session.ExportIDToError(ctx))
   484  	}
   485  }
   486  
   487  func (w *dsWorker) Proxy() proxy.Inbound {
   488  	return w.proxy
   489  }
   490  
   491  func (w *dsWorker) Port() net.Port {
   492  	return net.Port(0)
   493  }
   494  
   495  func (w *dsWorker) Start() error {
   496  	ctx := context.Background()
   497  	proxyEnvironment := envctx.EnvironmentFromContext(w.ctx).(environment.ProxyEnvironment)
   498  	transportEnvironment, err := proxyEnvironment.NarrowScopeToTransport("transport")
   499  	if err != nil {
   500  		return newError("unable to narrow environment to transport").Base(err)
   501  	}
   502  	ctx = envctx.ContextWithEnvironment(ctx, transportEnvironment)
   503  	hub, err := internet.ListenUnix(ctx, w.address, w.stream, func(conn internet.Connection) {
   504  		go w.callback(conn)
   505  	})
   506  	if err != nil {
   507  		return newError("failed to listen Unix Domain Socket on ", w.address).AtWarning().Base(err)
   508  	}
   509  	w.hub = hub
   510  	return nil
   511  }
   512  
   513  func (w *dsWorker) Close() error {
   514  	var errors []interface{}
   515  	if w.hub != nil {
   516  		if err := common.Close(w.hub); err != nil {
   517  			errors = append(errors, err)
   518  		}
   519  		if err := common.Close(w.proxy); err != nil {
   520  			errors = append(errors, err)
   521  		}
   522  	}
   523  	if len(errors) > 0 {
   524  		return newError("failed to close all resources").Base(newError(serial.Concat(errors...)))
   525  	}
   526  
   527  	return nil
   528  }