github.com/v2fly/v2ray-core/v4@v4.45.2/proxy/socks/client.go (about)

     1  //go:build !confonly
     2  // +build !confonly
     3  
     4  package socks
     5  
     6  import (
     7  	"context"
     8  	"time"
     9  
    10  	core "github.com/v2fly/v2ray-core/v4"
    11  	"github.com/v2fly/v2ray-core/v4/common"
    12  	"github.com/v2fly/v2ray-core/v4/common/buf"
    13  	"github.com/v2fly/v2ray-core/v4/common/net"
    14  	"github.com/v2fly/v2ray-core/v4/common/protocol"
    15  	"github.com/v2fly/v2ray-core/v4/common/retry"
    16  	"github.com/v2fly/v2ray-core/v4/common/session"
    17  	"github.com/v2fly/v2ray-core/v4/common/signal"
    18  	"github.com/v2fly/v2ray-core/v4/common/task"
    19  	"github.com/v2fly/v2ray-core/v4/features/dns"
    20  	"github.com/v2fly/v2ray-core/v4/features/policy"
    21  	"github.com/v2fly/v2ray-core/v4/transport"
    22  	"github.com/v2fly/v2ray-core/v4/transport/internet"
    23  )
    24  
    25  // Client is a Socks5 client.
    26  type Client struct {
    27  	serverPicker  protocol.ServerPicker
    28  	policyManager policy.Manager
    29  	version       Version
    30  	dns           dns.Client
    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  	}
    53  	if config.Version == Version_SOCKS4 {
    54  		c.dns = v.GetFeature(dns.ClientType()).(dns.Client)
    55  	}
    56  
    57  	return c, nil
    58  }
    59  
    60  // Process implements proxy.Outbound.Process.
    61  func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
    62  	outbound := session.OutboundFromContext(ctx)
    63  	if outbound == nil || !outbound.Target.IsValid() {
    64  		return newError("target not specified.")
    65  	}
    66  	// Destination of the inner request.
    67  	destination := outbound.Target
    68  
    69  	// Outbound server.
    70  	var server *protocol.ServerSpec
    71  	// Outbound server's destination.
    72  	var dest net.Destination
    73  	// Connection to the outbound server.
    74  	var conn internet.Connection
    75  
    76  	if err := retry.ExponentialBackoff(5, 100).On(func() error {
    77  		server = c.serverPicker.PickServer()
    78  		dest = server.Destination()
    79  		rawConn, err := dialer.Dial(ctx, dest)
    80  		if err != nil {
    81  			return err
    82  		}
    83  		conn = rawConn
    84  
    85  		return nil
    86  	}); err != nil {
    87  		return newError("failed to find an available destination").Base(err)
    88  	}
    89  
    90  	defer func() {
    91  		if err := conn.Close(); err != nil {
    92  			newError("failed to closed connection").Base(err).WriteToLog(session.ExportIDToError(ctx))
    93  		}
    94  	}()
    95  
    96  	p := c.policyManager.ForLevel(0)
    97  
    98  	request := &protocol.RequestHeader{
    99  		Version: socks5Version,
   100  		Command: protocol.RequestCommandTCP,
   101  		Address: destination.Address,
   102  		Port:    destination.Port,
   103  	}
   104  
   105  	switch c.version {
   106  	case Version_SOCKS4:
   107  		if request.Address.Family().IsDomain() {
   108  			if d, ok := c.dns.(dns.ClientWithIPOption); ok {
   109  				d.SetFakeDNSOption(false) // Skip FakeDNS
   110  			} else {
   111  				newError("DNS client doesn't implement ClientWithIPOption")
   112  			}
   113  
   114  			lookupFunc := c.dns.LookupIP
   115  			if lookupIPv4, ok := c.dns.(dns.IPv4Lookup); ok {
   116  				lookupFunc = lookupIPv4.LookupIPv4
   117  			}
   118  			ips, err := lookupFunc(request.Address.Domain())
   119  			if err != nil {
   120  				return err
   121  			} else if len(ips) == 0 {
   122  				return dns.ErrEmptyResponse
   123  			}
   124  			request.Address = net.IPAddress(ips[0])
   125  		}
   126  		fallthrough
   127  	case Version_SOCKS4A:
   128  		request.Version = socks4Version
   129  
   130  		if destination.Network == net.Network_UDP {
   131  			return newError("udp is not supported in socks4")
   132  		} else if destination.Address.Family().IsIPv6() {
   133  			return newError("ipv6 is not supported in socks4")
   134  		}
   135  	}
   136  
   137  	if destination.Network == net.Network_UDP {
   138  		request.Command = protocol.RequestCommandUDP
   139  	}
   140  
   141  	user := server.PickUser()
   142  	if user != nil {
   143  		request.User = user
   144  		p = c.policyManager.ForLevel(user.Level)
   145  	}
   146  
   147  	if err := conn.SetDeadline(time.Now().Add(p.Timeouts.Handshake)); err != nil {
   148  		newError("failed to set deadline for handshake").Base(err).WriteToLog(session.ExportIDToError(ctx))
   149  	}
   150  	udpRequest, err := ClientHandshake(request, conn, conn)
   151  	if err != nil {
   152  		return newError("failed to establish connection to server").AtWarning().Base(err)
   153  	}
   154  	if udpRequest != nil {
   155  		if udpRequest.Address == net.AnyIP || udpRequest.Address == net.AnyIPv6 {
   156  			udpRequest.Address = dest.Address
   157  		}
   158  	}
   159  
   160  	if err := conn.SetDeadline(time.Time{}); err != nil {
   161  		newError("failed to clear deadline after handshake").Base(err).WriteToLog(session.ExportIDToError(ctx))
   162  	}
   163  
   164  	ctx, cancel := context.WithCancel(ctx)
   165  	timer := signal.CancelAfterInactivity(ctx, cancel, p.Timeouts.ConnectionIdle)
   166  
   167  	var requestFunc func() error
   168  	var responseFunc func() error
   169  	if request.Command == protocol.RequestCommandTCP {
   170  		requestFunc = func() error {
   171  			defer timer.SetTimeout(p.Timeouts.DownlinkOnly)
   172  			return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer))
   173  		}
   174  		responseFunc = func() error {
   175  			defer timer.SetTimeout(p.Timeouts.UplinkOnly)
   176  			return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer))
   177  		}
   178  	} else if request.Command == protocol.RequestCommandUDP {
   179  		udpConn, err := dialer.Dial(ctx, udpRequest.Destination())
   180  		if err != nil {
   181  			return newError("failed to create UDP connection").Base(err)
   182  		}
   183  		defer udpConn.Close()
   184  		requestFunc = func() error {
   185  			defer timer.SetTimeout(p.Timeouts.DownlinkOnly)
   186  			return buf.Copy(link.Reader, &buf.SequentialWriter{Writer: NewUDPWriter(request, udpConn)}, buf.UpdateActivity(timer))
   187  		}
   188  		responseFunc = func() error {
   189  			defer timer.SetTimeout(p.Timeouts.UplinkOnly)
   190  			reader := &UDPReader{reader: udpConn}
   191  			return buf.Copy(reader, link.Writer, buf.UpdateActivity(timer))
   192  		}
   193  	}
   194  
   195  	responseDonePost := task.OnSuccess(responseFunc, task.Close(link.Writer))
   196  	if err := task.Run(ctx, requestFunc, responseDonePost); err != nil {
   197  		return newError("connection ends").Base(err)
   198  	}
   199  
   200  	return nil
   201  }
   202  
   203  func init() {
   204  	common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
   205  		return NewClient(ctx, config.(*ClientConfig))
   206  	}))
   207  }