123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- package outbound
- import (
- "context"
- "crypto/tls"
- "errors"
- "fmt"
- "io"
- "net"
- "net/netip"
- "strconv"
- N "github.com/metacubex/mihomo/common/net"
- "github.com/metacubex/mihomo/component/ca"
- "github.com/metacubex/mihomo/component/dialer"
- "github.com/metacubex/mihomo/component/proxydialer"
- C "github.com/metacubex/mihomo/constant"
- "github.com/metacubex/mihomo/transport/socks5"
- )
- type Socks5 struct {
- *Base
- option *Socks5Option
- user string
- pass string
- tls bool
- skipCertVerify bool
- tlsConfig *tls.Config
- }
- type Socks5Option struct {
- BasicOption
- Name string `proxy:"name"`
- Server string `proxy:"server"`
- Port int `proxy:"port"`
- UserName string `proxy:"username,omitempty"`
- Password string `proxy:"password,omitempty"`
- TLS bool `proxy:"tls,omitempty"`
- UDP bool `proxy:"udp,omitempty"`
- SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
- Fingerprint string `proxy:"fingerprint,omitempty"`
- }
- // StreamConnContext implements C.ProxyAdapter
- func (ss *Socks5) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {
- if ss.tls {
- cc := tls.Client(c, ss.tlsConfig)
- err := cc.HandshakeContext(ctx)
- c = cc
- if err != nil {
- return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
- }
- }
- var user *socks5.User
- if ss.user != "" {
- user = &socks5.User{
- Username: ss.user,
- Password: ss.pass,
- }
- }
- if _, err := socks5.ClientHandshake(c, serializesSocksAddr(metadata), socks5.CmdConnect, user); err != nil {
- return nil, err
- }
- return c, nil
- }
- // DialContext implements C.ProxyAdapter
- func (ss *Socks5) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
- return ss.DialContextWithDialer(ctx, dialer.NewDialer(ss.Base.DialOptions(opts...)...), metadata)
- }
- // DialContextWithDialer implements C.ProxyAdapter
- func (ss *Socks5) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
- if len(ss.option.DialerProxy) > 0 {
- dialer, err = proxydialer.NewByName(ss.option.DialerProxy, dialer)
- if err != nil {
- return nil, err
- }
- }
- c, err := dialer.DialContext(ctx, "tcp", ss.addr)
- if err != nil {
- return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
- }
- N.TCPKeepAlive(c)
- defer func(c net.Conn) {
- safeConnClose(c, err)
- }(c)
- c, err = ss.StreamConnContext(ctx, c, metadata)
- if err != nil {
- return nil, err
- }
- return NewConn(c, ss), nil
- }
- // SupportWithDialer implements C.ProxyAdapter
- func (ss *Socks5) SupportWithDialer() C.NetWork {
- return C.TCP
- }
- // ListenPacketContext implements C.ProxyAdapter
- func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) {
- var cDialer C.Dialer = dialer.NewDialer(ss.Base.DialOptions(opts...)...)
- if len(ss.option.DialerProxy) > 0 {
- cDialer, err = proxydialer.NewByName(ss.option.DialerProxy, cDialer)
- if err != nil {
- return nil, err
- }
- }
- c, err := cDialer.DialContext(ctx, "tcp", ss.addr)
- if err != nil {
- err = fmt.Errorf("%s connect error: %w", ss.addr, err)
- return
- }
- if ss.tls {
- cc := tls.Client(c, ss.tlsConfig)
- ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
- defer cancel()
- err = cc.HandshakeContext(ctx)
- c = cc
- }
- defer func(c net.Conn) {
- safeConnClose(c, err)
- }(c)
- N.TCPKeepAlive(c)
- var user *socks5.User
- if ss.user != "" {
- user = &socks5.User{
- Username: ss.user,
- Password: ss.pass,
- }
- }
- udpAssocateAddr := socks5.AddrFromStdAddrPort(netip.AddrPortFrom(netip.IPv4Unspecified(), 0))
- bindAddr, err := socks5.ClientHandshake(c, udpAssocateAddr, socks5.CmdUDPAssociate, user)
- if err != nil {
- err = fmt.Errorf("client hanshake error: %w", err)
- return
- }
- // Support unspecified UDP bind address.
- bindUDPAddr := bindAddr.UDPAddr()
- if bindUDPAddr == nil {
- err = errors.New("invalid UDP bind address")
- return
- } else if bindUDPAddr.IP.IsUnspecified() {
- serverAddr, err := resolveUDPAddr(ctx, "udp", ss.Addr())
- if err != nil {
- return nil, err
- }
- bindUDPAddr.IP = serverAddr.IP
- }
- pc, err := cDialer.ListenPacket(ctx, "udp", "", bindUDPAddr.AddrPort())
- if err != nil {
- return
- }
- go func() {
- io.Copy(io.Discard, c)
- c.Close()
- // A UDP association terminates when the TCP connection that the UDP
- // ASSOCIATE request arrived on terminates. RFC1928
- pc.Close()
- }()
- return newPacketConn(&socksPacketConn{PacketConn: pc, rAddr: bindUDPAddr, tcpConn: c}, ss), nil
- }
- func NewSocks5(option Socks5Option) (*Socks5, error) {
- var tlsConfig *tls.Config
- if option.TLS {
- tlsConfig = &tls.Config{
- InsecureSkipVerify: option.SkipCertVerify,
- ServerName: option.Server,
- }
- var err error
- tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint)
- if err != nil {
- return nil, err
- }
- }
- return &Socks5{
- Base: &Base{
- name: option.Name,
- addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),
- tp: C.Socks5,
- udp: option.UDP,
- tfo: option.TFO,
- mpTcp: option.MPTCP,
- iface: option.Interface,
- rmark: option.RoutingMark,
- prefer: C.NewDNSPrefer(option.IPVersion),
- },
- option: &option,
- user: option.UserName,
- pass: option.Password,
- tls: option.TLS,
- skipCertVerify: option.SkipCertVerify,
- tlsConfig: tlsConfig,
- }, nil
- }
- type socksPacketConn struct {
- net.PacketConn
- rAddr net.Addr
- tcpConn net.Conn
- }
- func (uc *socksPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
- packet, err := socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b)
- if err != nil {
- return
- }
- return uc.PacketConn.WriteTo(packet, uc.rAddr)
- }
- func (uc *socksPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
- n, _, e := uc.PacketConn.ReadFrom(b)
- if e != nil {
- return 0, nil, e
- }
- addr, payload, err := socks5.DecodeUDPPacket(b)
- if err != nil {
- return 0, nil, err
- }
- udpAddr := addr.UDPAddr()
- if udpAddr == nil {
- return 0, nil, errors.New("parse udp addr error")
- }
- // due to DecodeUDPPacket is mutable, record addr length
- copy(b, payload)
- return n - len(addr) - 3, udpAddr, nil
- }
- func (uc *socksPacketConn) Close() error {
- uc.tcpConn.Close()
- return uc.PacketConn.Close()
- }
|