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