github.com/metacubex/mihomo@v1.18.5/adapter/outbound/shadowsocksr.go (about)

     1  package outbound
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net"
     8  	"strconv"
     9  
    10  	N "github.com/metacubex/mihomo/common/net"
    11  	"github.com/metacubex/mihomo/component/dialer"
    12  	"github.com/metacubex/mihomo/component/proxydialer"
    13  	C "github.com/metacubex/mihomo/constant"
    14  	"github.com/metacubex/mihomo/transport/shadowsocks/core"
    15  	"github.com/metacubex/mihomo/transport/shadowsocks/shadowaead"
    16  	"github.com/metacubex/mihomo/transport/shadowsocks/shadowstream"
    17  	"github.com/metacubex/mihomo/transport/socks5"
    18  	"github.com/metacubex/mihomo/transport/ssr/obfs"
    19  	"github.com/metacubex/mihomo/transport/ssr/protocol"
    20  )
    21  
    22  type ShadowSocksR struct {
    23  	*Base
    24  	option   *ShadowSocksROption
    25  	cipher   core.Cipher
    26  	obfs     obfs.Obfs
    27  	protocol protocol.Protocol
    28  }
    29  
    30  type ShadowSocksROption struct {
    31  	BasicOption
    32  	Name          string `proxy:"name"`
    33  	Server        string `proxy:"server"`
    34  	Port          int    `proxy:"port"`
    35  	Password      string `proxy:"password"`
    36  	Cipher        string `proxy:"cipher"`
    37  	Obfs          string `proxy:"obfs"`
    38  	ObfsParam     string `proxy:"obfs-param,omitempty"`
    39  	Protocol      string `proxy:"protocol"`
    40  	ProtocolParam string `proxy:"protocol-param,omitempty"`
    41  	UDP           bool   `proxy:"udp,omitempty"`
    42  }
    43  
    44  // StreamConnContext implements C.ProxyAdapter
    45  func (ssr *ShadowSocksR) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {
    46  	c = ssr.obfs.StreamConn(c)
    47  	c = ssr.cipher.StreamConn(c)
    48  	var (
    49  		iv  []byte
    50  		err error
    51  	)
    52  	switch conn := c.(type) {
    53  	case *shadowstream.Conn:
    54  		iv, err = conn.ObtainWriteIV()
    55  		if err != nil {
    56  			return nil, err
    57  		}
    58  	case *shadowaead.Conn:
    59  		return nil, fmt.Errorf("invalid connection type")
    60  	}
    61  	c = ssr.protocol.StreamConn(c, iv)
    62  	_, err = c.Write(serializesSocksAddr(metadata))
    63  	return c, err
    64  }
    65  
    66  // DialContext implements C.ProxyAdapter
    67  func (ssr *ShadowSocksR) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
    68  	return ssr.DialContextWithDialer(ctx, dialer.NewDialer(ssr.Base.DialOptions(opts...)...), metadata)
    69  }
    70  
    71  // DialContextWithDialer implements C.ProxyAdapter
    72  func (ssr *ShadowSocksR) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
    73  	if len(ssr.option.DialerProxy) > 0 {
    74  		dialer, err = proxydialer.NewByName(ssr.option.DialerProxy, dialer)
    75  		if err != nil {
    76  			return nil, err
    77  		}
    78  	}
    79  	c, err := dialer.DialContext(ctx, "tcp", ssr.addr)
    80  	if err != nil {
    81  		return nil, fmt.Errorf("%s connect error: %w", ssr.addr, err)
    82  	}
    83  	N.TCPKeepAlive(c)
    84  
    85  	defer func(c net.Conn) {
    86  		safeConnClose(c, err)
    87  	}(c)
    88  
    89  	c, err = ssr.StreamConnContext(ctx, c, metadata)
    90  	return NewConn(c, ssr), err
    91  }
    92  
    93  // ListenPacketContext implements C.ProxyAdapter
    94  func (ssr *ShadowSocksR) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
    95  	return ssr.ListenPacketWithDialer(ctx, dialer.NewDialer(ssr.Base.DialOptions(opts...)...), metadata)
    96  }
    97  
    98  // ListenPacketWithDialer implements C.ProxyAdapter
    99  func (ssr *ShadowSocksR) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
   100  	if len(ssr.option.DialerProxy) > 0 {
   101  		dialer, err = proxydialer.NewByName(ssr.option.DialerProxy, dialer)
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  	}
   106  	addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ssr.addr, ssr.prefer)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  
   111  	pc, err := dialer.ListenPacket(ctx, "udp", "", addr.AddrPort())
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	epc := ssr.cipher.PacketConn(N.NewEnhancePacketConn(pc))
   117  	epc = ssr.protocol.PacketConn(epc)
   118  	return newPacketConn(&ssrPacketConn{EnhancePacketConn: epc, rAddr: addr}, ssr), nil
   119  }
   120  
   121  // SupportWithDialer implements C.ProxyAdapter
   122  func (ssr *ShadowSocksR) SupportWithDialer() C.NetWork {
   123  	return C.ALLNet
   124  }
   125  
   126  func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) {
   127  	// SSR protocol compatibility
   128  	// https://github.com/metacubex/mihomo/pull/2056
   129  	if option.Cipher == "none" {
   130  		option.Cipher = "dummy"
   131  	}
   132  
   133  	addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
   134  	cipher := option.Cipher
   135  	password := option.Password
   136  	coreCiph, err := core.PickCipher(cipher, nil, password)
   137  	if err != nil {
   138  		return nil, fmt.Errorf("ssr %s initialize error: %w", addr, err)
   139  	}
   140  	var (
   141  		ivSize int
   142  		key    []byte
   143  	)
   144  
   145  	if option.Cipher == "dummy" {
   146  		ivSize = 0
   147  		key = core.Kdf(option.Password, 16)
   148  	} else {
   149  		ciph, ok := coreCiph.(*core.StreamCipher)
   150  		if !ok {
   151  			return nil, fmt.Errorf("%s is not none or a supported stream cipher in ssr", cipher)
   152  		}
   153  		ivSize = ciph.IVSize()
   154  		key = ciph.Key
   155  	}
   156  
   157  	obfs, obfsOverhead, err := obfs.PickObfs(option.Obfs, &obfs.Base{
   158  		Host:   option.Server,
   159  		Port:   option.Port,
   160  		Key:    key,
   161  		IVSize: ivSize,
   162  		Param:  option.ObfsParam,
   163  	})
   164  	if err != nil {
   165  		return nil, fmt.Errorf("ssr %s initialize obfs error: %w", addr, err)
   166  	}
   167  
   168  	protocol, err := protocol.PickProtocol(option.Protocol, &protocol.Base{
   169  		Key:      key,
   170  		Overhead: obfsOverhead,
   171  		Param:    option.ProtocolParam,
   172  	})
   173  	if err != nil {
   174  		return nil, fmt.Errorf("ssr %s initialize protocol error: %w", addr, err)
   175  	}
   176  
   177  	return &ShadowSocksR{
   178  		Base: &Base{
   179  			name:   option.Name,
   180  			addr:   addr,
   181  			tp:     C.ShadowsocksR,
   182  			udp:    option.UDP,
   183  			tfo:    option.TFO,
   184  			mpTcp:  option.MPTCP,
   185  			iface:  option.Interface,
   186  			rmark:  option.RoutingMark,
   187  			prefer: C.NewDNSPrefer(option.IPVersion),
   188  		},
   189  		option:   &option,
   190  		cipher:   coreCiph,
   191  		obfs:     obfs,
   192  		protocol: protocol,
   193  	}, nil
   194  }
   195  
   196  type ssrPacketConn struct {
   197  	N.EnhancePacketConn
   198  	rAddr net.Addr
   199  }
   200  
   201  func (spc *ssrPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
   202  	packet, err := socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b)
   203  	if err != nil {
   204  		return
   205  	}
   206  	return spc.EnhancePacketConn.WriteTo(packet[3:], spc.rAddr)
   207  }
   208  
   209  func (spc *ssrPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
   210  	n, _, e := spc.EnhancePacketConn.ReadFrom(b)
   211  	if e != nil {
   212  		return 0, nil, e
   213  	}
   214  
   215  	addr := socks5.SplitAddr(b[:n])
   216  	if addr == nil {
   217  		return 0, nil, errors.New("parse addr error")
   218  	}
   219  
   220  	udpAddr := addr.UDPAddr()
   221  	if udpAddr == nil {
   222  		return 0, nil, errors.New("parse addr error")
   223  	}
   224  
   225  	copy(b, b[len(addr):])
   226  	return n - len(addr), udpAddr, e
   227  }
   228  
   229  func (spc *ssrPacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {
   230  	data, put, _, err = spc.EnhancePacketConn.WaitReadFrom()
   231  	if err != nil {
   232  		return nil, nil, nil, err
   233  	}
   234  
   235  	_addr := socks5.SplitAddr(data)
   236  	if _addr == nil {
   237  		if put != nil {
   238  			put()
   239  		}
   240  		return nil, nil, nil, errors.New("parse addr error")
   241  	}
   242  
   243  	addr = _addr.UDPAddr()
   244  	if addr == nil {
   245  		if put != nil {
   246  			put()
   247  		}
   248  		return nil, nil, nil, errors.New("parse addr error")
   249  	}
   250  
   251  	data = data[len(_addr):]
   252  	return
   253  }