github.com/yaling888/clash@v1.53.0/adapter/outbound/socks5.go (about)

     1  package outbound
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"net"
    10  	"net/netip"
    11  	"strconv"
    12  
    13  	"github.com/yaling888/clash/component/dialer"
    14  	C "github.com/yaling888/clash/constant"
    15  	"github.com/yaling888/clash/transport/socks5"
    16  )
    17  
    18  var _ C.ProxyAdapter = (*Socks5)(nil)
    19  
    20  type Socks5 struct {
    21  	*Base
    22  	user           string
    23  	pass           string
    24  	tls            bool
    25  	skipCertVerify bool
    26  	tlsConfig      *tls.Config
    27  }
    28  
    29  type Socks5Option struct {
    30  	BasicOption
    31  	Name             string `proxy:"name"`
    32  	Server           string `proxy:"server"`
    33  	Port             int    `proxy:"port"`
    34  	UserName         string `proxy:"username,omitempty"`
    35  	Password         string `proxy:"password,omitempty"`
    36  	TLS              bool   `proxy:"tls,omitempty"`
    37  	UDP              bool   `proxy:"udp,omitempty"`
    38  	SkipCertVerify   bool   `proxy:"skip-cert-verify,omitempty"`
    39  	RemoteDnsResolve bool   `proxy:"remote-dns-resolve,omitempty"`
    40  }
    41  
    42  // StreamConn implements C.ProxyAdapter
    43  func (ss *Socks5) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
    44  	var err error
    45  	c, _, err = ss.streamConn(c, metadata)
    46  
    47  	return c, err
    48  }
    49  
    50  func (ss *Socks5) StreamSocks5PacketConn(c net.Conn, pc net.PacketConn, metadata *C.Metadata) (net.PacketConn, error) {
    51  	if c == nil {
    52  		return pc, fmt.Errorf("%s connect error: parameter net.Conn is nil", ss.addr)
    53  	}
    54  
    55  	if pc == nil {
    56  		return pc, fmt.Errorf("%s connect error: parameter net.PacketConn is nil", ss.addr)
    57  	}
    58  
    59  	cc, bindAddr, err := ss.streamConn(c, metadata)
    60  	if err != nil {
    61  		return pc, err
    62  	}
    63  
    64  	c = cc
    65  
    66  	go func() {
    67  		_, _ = io.Copy(io.Discard, c)
    68  		_ = c.Close()
    69  		// A UDP association terminates when the TCP connection that the UDP
    70  		// ASSOCIATE request arrived at terminates. RFC1928
    71  		_ = pc.Close()
    72  	}()
    73  
    74  	// Support unspecified UDP bind address.
    75  	bindUDPAddr := bindAddr.UDPAddr()
    76  	if bindUDPAddr == nil {
    77  		return pc, errors.New("invalid UDP bind address")
    78  	} else if bindUDPAddr.IP.IsUnspecified() {
    79  		serverAddr, err := resolveUDPAddr("udp", ss.Addr())
    80  		if err != nil {
    81  			return pc, err
    82  		}
    83  
    84  		bindUDPAddr.IP = serverAddr.IP
    85  	}
    86  
    87  	return &socksPacketConn{PacketConn: pc, rAddr: bindUDPAddr, tcpConn: c}, nil
    88  }
    89  
    90  func (ss *Socks5) streamConn(c net.Conn, metadata *C.Metadata) (_ net.Conn, bindAddr socks5.Addr, err error) {
    91  	if ss.tls {
    92  		cc := tls.Client(c, ss.tlsConfig)
    93  		ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
    94  		defer cancel()
    95  		err = cc.HandshakeContext(ctx)
    96  		c = cc
    97  		if err != nil {
    98  			return c, nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
    99  		}
   100  	}
   101  
   102  	var user *socks5.User
   103  	if ss.user != "" {
   104  		user = &socks5.User{
   105  			Username: ss.user,
   106  			Password: ss.pass,
   107  		}
   108  	}
   109  
   110  	if metadata.NetWork == C.UDP {
   111  		udpAssocateAddr := socks5.AddrFromStdAddrPort(netip.AddrPortFrom(netip.IPv4Unspecified(), 0))
   112  		bindAddr, err = socks5.ClientHandshake(c, udpAssocateAddr, socks5.CmdUDPAssociate, user)
   113  	} else {
   114  		bindAddr, err = socks5.ClientHandshake(c, serializesSocksAddr(metadata), socks5.CmdConnect, user)
   115  	}
   116  
   117  	return c, bindAddr, err
   118  }
   119  
   120  // DialContext implements C.ProxyAdapter
   121  func (ss *Socks5) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
   122  	c, err := dialer.DialContext(ctx, "tcp", ss.addr, ss.Base.DialOptions(opts...)...)
   123  	if err != nil {
   124  		return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
   125  	}
   126  	tcpKeepAlive(c)
   127  
   128  	defer func(cc net.Conn, e error) {
   129  		safeConnClose(cc, e)
   130  	}(c, err)
   131  
   132  	c, err = ss.StreamConn(c, metadata)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	return NewConn(c, ss), nil
   138  }
   139  
   140  // ListenPacketContext implements C.ProxyAdapter
   141  func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) {
   142  	c, err := dialer.DialContext(ctx, "tcp", ss.addr, ss.Base.DialOptions(opts...)...)
   143  	if err != nil {
   144  		return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
   145  	}
   146  
   147  	defer func(cc net.Conn, e error) {
   148  		safeConnClose(cc, e)
   149  	}(c, err)
   150  
   151  	pc, err := dialer.ListenPacket(ctx, "udp", "", ss.Base.DialOptions(opts...)...)
   152  	if err != nil {
   153  		return
   154  	}
   155  
   156  	tcpKeepAlive(c)
   157  
   158  	pc, err = ss.StreamSocks5PacketConn(c, pc, metadata)
   159  	if err != nil {
   160  		return
   161  	}
   162  
   163  	return NewPacketConn(pc, ss), nil
   164  }
   165  
   166  func NewSocks5(option Socks5Option) *Socks5 {
   167  	var tlsConfig *tls.Config
   168  	if option.TLS {
   169  		tlsConfig = &tls.Config{
   170  			InsecureSkipVerify: option.SkipCertVerify,
   171  			ServerName:         option.Server,
   172  		}
   173  	}
   174  
   175  	return &Socks5{
   176  		Base: &Base{
   177  			name:  option.Name,
   178  			addr:  net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
   179  			tp:    C.Socks5,
   180  			udp:   option.UDP,
   181  			iface: option.Interface,
   182  			rmark: option.RoutingMark,
   183  			dns:   option.RemoteDnsResolve,
   184  		},
   185  		user:           option.UserName,
   186  		pass:           option.Password,
   187  		tls:            option.TLS,
   188  		skipCertVerify: option.SkipCertVerify,
   189  		tlsConfig:      tlsConfig,
   190  	}
   191  }
   192  
   193  type socksPacketConn struct {
   194  	net.PacketConn
   195  	rAddr   net.Addr
   196  	tcpConn net.Conn
   197  }
   198  
   199  func (uc *socksPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
   200  	packet, err := socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b)
   201  	if err != nil {
   202  		return
   203  	}
   204  	return uc.PacketConn.WriteTo(packet, uc.rAddr)
   205  }
   206  
   207  func (uc *socksPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
   208  	n, _, e := uc.PacketConn.ReadFrom(b)
   209  	if e != nil {
   210  		return 0, nil, e
   211  	}
   212  	addr, payload, err := socks5.DecodeUDPPacket(b)
   213  	if err != nil {
   214  		return 0, nil, err
   215  	}
   216  
   217  	udpAddr := addr.UDPAddr()
   218  	if udpAddr == nil {
   219  		return 0, nil, errors.New("parse udp addr error")
   220  	}
   221  
   222  	// due to DecodeUDPPacket is mutable, record addr length
   223  	copy(b, payload)
   224  	return n - len(addr) - 3, udpAddr, nil
   225  }
   226  
   227  func (uc *socksPacketConn) Close() error {
   228  	_ = uc.tcpConn.Close()
   229  	return uc.PacketConn.Close()
   230  }