singmux.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. package outbound
  2. import (
  3. "context"
  4. "errors"
  5. "runtime"
  6. CN "github.com/metacubex/mihomo/common/net"
  7. "github.com/metacubex/mihomo/component/dialer"
  8. "github.com/metacubex/mihomo/component/proxydialer"
  9. "github.com/metacubex/mihomo/component/resolver"
  10. C "github.com/metacubex/mihomo/constant"
  11. "github.com/metacubex/mihomo/log"
  12. mux "github.com/sagernet/sing-mux"
  13. E "github.com/sagernet/sing/common/exceptions"
  14. M "github.com/sagernet/sing/common/metadata"
  15. )
  16. type SingMux struct {
  17. C.ProxyAdapter
  18. base ProxyBase
  19. client *mux.Client
  20. dialer proxydialer.SingDialer
  21. onlyTcp bool
  22. }
  23. type SingMuxOption struct {
  24. Enabled bool `proxy:"enabled,omitempty"`
  25. Protocol string `proxy:"protocol,omitempty"`
  26. MaxConnections int `proxy:"max-connections,omitempty"`
  27. MinStreams int `proxy:"min-streams,omitempty"`
  28. MaxStreams int `proxy:"max-streams,omitempty"`
  29. Padding bool `proxy:"padding,omitempty"`
  30. Statistic bool `proxy:"statistic,omitempty"`
  31. OnlyTcp bool `proxy:"only-tcp,omitempty"`
  32. BrutalOpts BrutalOption `proxy:"brutal-opts,omitempty"`
  33. }
  34. type BrutalOption struct {
  35. Enabled bool `proxy:"enabled,omitempty"`
  36. Up string `proxy:"up,omitempty"`
  37. Down string `proxy:"down,omitempty"`
  38. }
  39. type ProxyBase interface {
  40. DialOptions(opts ...dialer.Option) []dialer.Option
  41. }
  42. func (s *SingMux) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
  43. options := s.base.DialOptions(opts...)
  44. s.dialer.SetDialer(dialer.NewDialer(options...))
  45. c, err := s.client.DialContext(ctx, "tcp", M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort))
  46. if err != nil {
  47. return nil, err
  48. }
  49. return NewConn(CN.NewRefConn(c, s), s.ProxyAdapter), err
  50. }
  51. func (s *SingMux) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) {
  52. if s.onlyTcp {
  53. return s.ProxyAdapter.ListenPacketContext(ctx, metadata, opts...)
  54. }
  55. options := s.base.DialOptions(opts...)
  56. s.dialer.SetDialer(dialer.NewDialer(options...))
  57. // sing-mux use stream-oriented udp with a special address, so we need a net.UDPAddr
  58. if !metadata.Resolved() {
  59. ip, err := resolver.ResolveIP(ctx, metadata.Host)
  60. if err != nil {
  61. return nil, errors.New("can't resolve ip")
  62. }
  63. metadata.DstIP = ip
  64. }
  65. pc, err := s.client.ListenPacket(ctx, M.SocksaddrFromNet(metadata.UDPAddr()))
  66. if err != nil {
  67. return nil, err
  68. }
  69. if pc == nil {
  70. return nil, E.New("packetConn is nil")
  71. }
  72. return newPacketConn(CN.NewRefPacketConn(CN.NewThreadSafePacketConn(pc), s), s.ProxyAdapter), nil
  73. }
  74. func (s *SingMux) SupportUDP() bool {
  75. if s.onlyTcp {
  76. return s.ProxyAdapter.SupportUDP()
  77. }
  78. return true
  79. }
  80. func (s *SingMux) SupportUOT() bool {
  81. if s.onlyTcp {
  82. return s.ProxyAdapter.SupportUOT()
  83. }
  84. return true
  85. }
  86. func closeSingMux(s *SingMux) {
  87. _ = s.client.Close()
  88. }
  89. func NewSingMux(option SingMuxOption, proxy C.ProxyAdapter, base ProxyBase) (C.ProxyAdapter, error) {
  90. // TODO
  91. // "TCP Brutal is only supported on Linux-based systems"
  92. singDialer := proxydialer.NewSingDialer(proxy, dialer.NewDialer(), option.Statistic)
  93. client, err := mux.NewClient(mux.Options{
  94. Dialer: singDialer,
  95. Logger: log.SingLogger,
  96. Protocol: option.Protocol,
  97. MaxConnections: option.MaxConnections,
  98. MinStreams: option.MinStreams,
  99. MaxStreams: option.MaxStreams,
  100. Padding: option.Padding,
  101. Brutal: mux.BrutalOptions{
  102. Enabled: option.BrutalOpts.Enabled,
  103. SendBPS: StringToBps(option.BrutalOpts.Up),
  104. ReceiveBPS: StringToBps(option.BrutalOpts.Down),
  105. },
  106. })
  107. if err != nil {
  108. return nil, err
  109. }
  110. outbound := &SingMux{
  111. ProxyAdapter: proxy,
  112. base: base,
  113. client: client,
  114. dialer: singDialer,
  115. onlyTcp: option.OnlyTcp,
  116. }
  117. runtime.SetFinalizer(outbound, closeSingMux)
  118. return outbound, nil
  119. }