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