github.com/eagleql/xray-core@v1.4.4/proxy/socks/client.go (about)

     1  package socks
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	"github.com/eagleql/xray-core/common"
     8  	"github.com/eagleql/xray-core/common/buf"
     9  	"github.com/eagleql/xray-core/common/net"
    10  	"github.com/eagleql/xray-core/common/protocol"
    11  	"github.com/eagleql/xray-core/common/retry"
    12  	"github.com/eagleql/xray-core/common/session"
    13  	"github.com/eagleql/xray-core/common/signal"
    14  	"github.com/eagleql/xray-core/common/task"
    15  	"github.com/eagleql/xray-core/core"
    16  	"github.com/eagleql/xray-core/features/policy"
    17  	"github.com/eagleql/xray-core/transport"
    18  	"github.com/eagleql/xray-core/transport/internet"
    19  )
    20  
    21  // Client is a Socks5 client.
    22  type Client struct {
    23  	serverPicker  protocol.ServerPicker
    24  	policyManager policy.Manager
    25  }
    26  
    27  // NewClient create a new Socks5 client based on the given config.
    28  func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) {
    29  	serverList := protocol.NewServerList()
    30  	for _, rec := range config.Server {
    31  		s, err := protocol.NewServerSpecFromPB(rec)
    32  		if err != nil {
    33  			return nil, newError("failed to get server spec").Base(err)
    34  		}
    35  		serverList.AddServer(s)
    36  	}
    37  	if serverList.Size() == 0 {
    38  		return nil, newError("0 target server")
    39  	}
    40  
    41  	v := core.MustFromContext(ctx)
    42  	return &Client{
    43  		serverPicker:  protocol.NewRoundRobinServerPicker(serverList),
    44  		policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
    45  	}, nil
    46  }
    47  
    48  // Process implements proxy.Outbound.Process.
    49  func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
    50  	outbound := session.OutboundFromContext(ctx)
    51  	if outbound == nil || !outbound.Target.IsValid() {
    52  		return newError("target not specified.")
    53  	}
    54  	// Destination of the inner request.
    55  	destination := outbound.Target
    56  
    57  	// Outbound server.
    58  	var server *protocol.ServerSpec
    59  	// Outbound server's destination.
    60  	var dest net.Destination
    61  	// Connection to the outbound server.
    62  	var conn internet.Connection
    63  
    64  	if err := retry.ExponentialBackoff(5, 100).On(func() error {
    65  		server = c.serverPicker.PickServer()
    66  		dest = server.Destination()
    67  		rawConn, err := dialer.Dial(ctx, dest)
    68  		if err != nil {
    69  			return err
    70  		}
    71  		conn = rawConn
    72  
    73  		return nil
    74  	}); err != nil {
    75  		return newError("failed to find an available destination").Base(err)
    76  	}
    77  
    78  	defer func() {
    79  		if err := conn.Close(); err != nil {
    80  			newError("failed to closed connection").Base(err).WriteToLog(session.ExportIDToError(ctx))
    81  		}
    82  	}()
    83  
    84  	p := c.policyManager.ForLevel(0)
    85  
    86  	request := &protocol.RequestHeader{
    87  		Version: socks5Version,
    88  		Command: protocol.RequestCommandTCP,
    89  		Address: destination.Address,
    90  		Port:    destination.Port,
    91  	}
    92  	if destination.Network == net.Network_UDP {
    93  		request.Command = protocol.RequestCommandUDP
    94  	}
    95  
    96  	user := server.PickUser()
    97  	if user != nil {
    98  		request.User = user
    99  		p = c.policyManager.ForLevel(user.Level)
   100  	}
   101  
   102  	if err := conn.SetDeadline(time.Now().Add(p.Timeouts.Handshake)); err != nil {
   103  		newError("failed to set deadline for handshake").Base(err).WriteToLog(session.ExportIDToError(ctx))
   104  	}
   105  	udpRequest, err := ClientHandshake(request, conn, conn)
   106  	if err != nil {
   107  		return newError("failed to establish connection to server").AtWarning().Base(err)
   108  	}
   109  	if udpRequest != nil {
   110  		if udpRequest.Address == net.AnyIP || udpRequest.Address == net.AnyIPv6 {
   111  			udpRequest.Address = dest.Address
   112  		}
   113  	}
   114  
   115  	if err := conn.SetDeadline(time.Time{}); err != nil {
   116  		newError("failed to clear deadline after handshake").Base(err).WriteToLog(session.ExportIDToError(ctx))
   117  	}
   118  
   119  	ctx, cancel := context.WithCancel(ctx)
   120  	timer := signal.CancelAfterInactivity(ctx, cancel, p.Timeouts.ConnectionIdle)
   121  
   122  	var requestFunc func() error
   123  	var responseFunc func() error
   124  	if request.Command == protocol.RequestCommandTCP {
   125  		requestFunc = func() error {
   126  			defer timer.SetTimeout(p.Timeouts.DownlinkOnly)
   127  			return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer))
   128  		}
   129  		responseFunc = func() error {
   130  			defer timer.SetTimeout(p.Timeouts.UplinkOnly)
   131  			return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer))
   132  		}
   133  	} else if request.Command == protocol.RequestCommandUDP {
   134  		udpConn, err := dialer.Dial(ctx, udpRequest.Destination())
   135  		if err != nil {
   136  			return newError("failed to create UDP connection").Base(err)
   137  		}
   138  		defer udpConn.Close()
   139  		requestFunc = func() error {
   140  			defer timer.SetTimeout(p.Timeouts.DownlinkOnly)
   141  			writer := &UDPWriter{Writer: udpConn, Request: request}
   142  			return buf.Copy(link.Reader, writer, buf.UpdateActivity(timer))
   143  		}
   144  		responseFunc = func() error {
   145  			defer timer.SetTimeout(p.Timeouts.UplinkOnly)
   146  			reader := &UDPReader{Reader: udpConn}
   147  			return buf.Copy(reader, link.Writer, buf.UpdateActivity(timer))
   148  		}
   149  	}
   150  
   151  	var responseDonePost = task.OnSuccess(responseFunc, task.Close(link.Writer))
   152  	if err := task.Run(ctx, requestFunc, responseDonePost); err != nil {
   153  		return newError("connection ends").Base(err)
   154  	}
   155  
   156  	return nil
   157  }
   158  
   159  func init() {
   160  	common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
   161  		return NewClient(ctx, config.(*ClientConfig))
   162  	}))
   163  }