github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/proxy/socks/client.go (about)

     1  package socks
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	core "github.com/v2fly/v2ray-core/v5"
     8  	"github.com/v2fly/v2ray-core/v5/common"
     9  	"github.com/v2fly/v2ray-core/v5/common/buf"
    10  	"github.com/v2fly/v2ray-core/v5/common/net"
    11  	"github.com/v2fly/v2ray-core/v5/common/net/packetaddr"
    12  	"github.com/v2fly/v2ray-core/v5/common/protocol"
    13  	"github.com/v2fly/v2ray-core/v5/common/retry"
    14  	"github.com/v2fly/v2ray-core/v5/common/session"
    15  	"github.com/v2fly/v2ray-core/v5/common/signal"
    16  	"github.com/v2fly/v2ray-core/v5/common/task"
    17  	"github.com/v2fly/v2ray-core/v5/features/dns"
    18  	"github.com/v2fly/v2ray-core/v5/features/policy"
    19  	"github.com/v2fly/v2ray-core/v5/transport"
    20  	"github.com/v2fly/v2ray-core/v5/transport/internet"
    21  	"github.com/v2fly/v2ray-core/v5/transport/internet/udp"
    22  )
    23  
    24  // Client is a Socks5 client.
    25  type Client struct {
    26  	serverPicker   protocol.ServerPicker
    27  	policyManager  policy.Manager
    28  	version        Version
    29  	dns            dns.Client
    30  	delayAuthWrite bool
    31  }
    32  
    33  // NewClient create a new Socks5 client based on the given config.
    34  func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) {
    35  	serverList := protocol.NewServerList()
    36  	for _, rec := range config.Server {
    37  		s, err := protocol.NewServerSpecFromPB(rec)
    38  		if err != nil {
    39  			return nil, newError("failed to get server spec").Base(err)
    40  		}
    41  		serverList.AddServer(s)
    42  	}
    43  	if serverList.Size() == 0 {
    44  		return nil, newError("0 target server")
    45  	}
    46  
    47  	v := core.MustFromContext(ctx)
    48  	c := &Client{
    49  		serverPicker:   protocol.NewRoundRobinServerPicker(serverList),
    50  		policyManager:  v.GetFeature(policy.ManagerType()).(policy.Manager),
    51  		version:        config.Version,
    52  		delayAuthWrite: config.DelayAuthWrite,
    53  	}
    54  	if config.Version == Version_SOCKS4 {
    55  		c.dns = v.GetFeature(dns.ClientType()).(dns.Client)
    56  	}
    57  
    58  	return c, nil
    59  }
    60  
    61  // Process implements proxy.Outbound.Process.
    62  func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
    63  	outbound := session.OutboundFromContext(ctx)
    64  	if outbound == nil || !outbound.Target.IsValid() {
    65  		return newError("target not specified.")
    66  	}
    67  	// Destination of the inner request.
    68  	destination := outbound.Target
    69  
    70  	// Outbound server.
    71  	var server *protocol.ServerSpec
    72  	// Outbound server's destination.
    73  	var dest net.Destination
    74  	// Connection to the outbound server.
    75  	var conn internet.Connection
    76  
    77  	if err := retry.ExponentialBackoff(5, 100).On(func() error {
    78  		server = c.serverPicker.PickServer()
    79  		dest = server.Destination()
    80  		rawConn, err := dialer.Dial(ctx, dest)
    81  		if err != nil {
    82  			return err
    83  		}
    84  		conn = rawConn
    85  
    86  		return nil
    87  	}); err != nil {
    88  		return newError("failed to find an available destination").Base(err)
    89  	}
    90  
    91  	defer func() {
    92  		if err := conn.Close(); err != nil {
    93  			newError("failed to closed connection").Base(err).WriteToLog(session.ExportIDToError(ctx))
    94  		}
    95  	}()
    96  
    97  	p := c.policyManager.ForLevel(0)
    98  
    99  	request := &protocol.RequestHeader{
   100  		Version: socks5Version,
   101  		Command: protocol.RequestCommandTCP,
   102  		Address: destination.Address,
   103  		Port:    destination.Port,
   104  	}
   105  
   106  	switch c.version {
   107  	case Version_SOCKS4:
   108  		if request.Address.Family().IsDomain() {
   109  			ips, err := dns.LookupIPWithOption(c.dns, request.Address.Domain(), dns.IPOption{IPv4Enable: true, IPv6Enable: false, FakeEnable: false})
   110  			if err != nil {
   111  				return err
   112  			} else if len(ips) == 0 {
   113  				return dns.ErrEmptyResponse
   114  			}
   115  			request.Address = net.IPAddress(ips[0])
   116  		}
   117  		fallthrough
   118  	case Version_SOCKS4A:
   119  		request.Version = socks4Version
   120  
   121  		if destination.Network == net.Network_UDP {
   122  			return newError("udp is not supported in socks4")
   123  		} else if destination.Address.Family().IsIPv6() {
   124  			return newError("ipv6 is not supported in socks4")
   125  		}
   126  	}
   127  
   128  	if destination.Network == net.Network_UDP {
   129  		request.Command = protocol.RequestCommandUDP
   130  	}
   131  
   132  	user := server.PickUser()
   133  	if user != nil {
   134  		request.User = user
   135  		p = c.policyManager.ForLevel(user.Level)
   136  	}
   137  
   138  	if err := conn.SetDeadline(time.Now().Add(p.Timeouts.Handshake)); err != nil {
   139  		newError("failed to set deadline for handshake").Base(err).WriteToLog(session.ExportIDToError(ctx))
   140  	}
   141  
   142  	var udpRequest *protocol.RequestHeader
   143  	var err error
   144  	if request.Version == socks4Version {
   145  		err = ClientHandshake4(request, conn, conn)
   146  		if err != nil {
   147  			return newError("failed to establish connection to server").AtWarning().Base(err)
   148  		}
   149  	} else {
   150  		udpRequest, err = ClientHandshake(request, conn, conn, c.delayAuthWrite)
   151  		if err != nil {
   152  			return newError("failed to establish connection to server").AtWarning().Base(err)
   153  		}
   154  	}
   155  
   156  	if udpRequest != nil {
   157  		if udpRequest.Address == net.AnyIP || udpRequest.Address == net.AnyIPv6 {
   158  			udpRequest.Address = dest.Address
   159  		}
   160  	}
   161  
   162  	if err := conn.SetDeadline(time.Time{}); err != nil {
   163  		newError("failed to clear deadline after handshake").Base(err).WriteToLog(session.ExportIDToError(ctx))
   164  	}
   165  
   166  	ctx, cancel := context.WithCancel(ctx)
   167  	timer := signal.CancelAfterInactivity(ctx, cancel, p.Timeouts.ConnectionIdle)
   168  
   169  	if packetConn, err := packetaddr.ToPacketAddrConn(link, destination); err == nil {
   170  		udpConn, err := dialer.Dial(ctx, udpRequest.Destination())
   171  		if err != nil {
   172  			return newError("failed to create UDP connection").Base(err)
   173  		}
   174  		defer udpConn.Close()
   175  
   176  		requestDone := func() error {
   177  			protocolWriter := NewUDPWriter(request, udpConn)
   178  			return udp.CopyPacketConn(protocolWriter, packetConn, udp.UpdateActivity(timer))
   179  		}
   180  		responseDone := func() error {
   181  			protocolReader := &UDPReader{
   182  				reader: udpConn,
   183  			}
   184  			return udp.CopyPacketConn(packetConn, protocolReader, udp.UpdateActivity(timer))
   185  		}
   186  		responseDoneAndCloseWriter := task.OnSuccess(responseDone, task.Close(link.Writer))
   187  		if err := task.Run(ctx, requestDone, responseDoneAndCloseWriter); err != nil {
   188  			return newError("connection ends").Base(err)
   189  		}
   190  		return nil
   191  	}
   192  
   193  	var requestFunc func() error
   194  	var responseFunc func() error
   195  	if request.Command == protocol.RequestCommandTCP {
   196  		requestFunc = func() error {
   197  			defer timer.SetTimeout(p.Timeouts.DownlinkOnly)
   198  			return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer))
   199  		}
   200  		responseFunc = func() error {
   201  			defer timer.SetTimeout(p.Timeouts.UplinkOnly)
   202  			return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer))
   203  		}
   204  	} else if request.Command == protocol.RequestCommandUDP {
   205  		udpConn, err := dialer.Dial(ctx, udpRequest.Destination())
   206  		if err != nil {
   207  			return newError("failed to create UDP connection").Base(err)
   208  		}
   209  		defer udpConn.Close()
   210  		requestFunc = func() error {
   211  			defer timer.SetTimeout(p.Timeouts.DownlinkOnly)
   212  			return buf.Copy(link.Reader, &buf.SequentialWriter{Writer: NewUDPWriter(request, udpConn)}, buf.UpdateActivity(timer))
   213  		}
   214  		responseFunc = func() error {
   215  			defer timer.SetTimeout(p.Timeouts.UplinkOnly)
   216  			reader := &UDPReader{reader: udpConn}
   217  			return buf.Copy(reader, link.Writer, buf.UpdateActivity(timer))
   218  		}
   219  	}
   220  
   221  	responseDonePost := task.OnSuccess(responseFunc, task.Close(link.Writer))
   222  	if err := task.Run(ctx, requestFunc, responseDonePost); err != nil {
   223  		return newError("connection ends").Base(err)
   224  	}
   225  
   226  	return nil
   227  }
   228  
   229  func init() {
   230  	common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
   231  		return NewClient(ctx, config.(*ClientConfig))
   232  	}))
   233  }