hysteria2.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. package outbound
  2. import (
  3. "context"
  4. "crypto/tls"
  5. "errors"
  6. "fmt"
  7. "net"
  8. "runtime"
  9. "strconv"
  10. "time"
  11. CN "github.com/metacubex/mihomo/common/net"
  12. "github.com/metacubex/mihomo/common/utils"
  13. "github.com/metacubex/mihomo/component/ca"
  14. "github.com/metacubex/mihomo/component/dialer"
  15. "github.com/metacubex/mihomo/component/proxydialer"
  16. C "github.com/metacubex/mihomo/constant"
  17. "github.com/metacubex/mihomo/log"
  18. tuicCommon "github.com/metacubex/mihomo/transport/tuic/common"
  19. "github.com/metacubex/sing-quic/hysteria2"
  20. "github.com/metacubex/randv2"
  21. M "github.com/sagernet/sing/common/metadata"
  22. )
  23. func init() {
  24. hysteria2.SetCongestionController = tuicCommon.SetCongestionController
  25. }
  26. const minHopInterval = 5
  27. const defaultHopInterval = 30
  28. type Hysteria2 struct {
  29. *Base
  30. option *Hysteria2Option
  31. client *hysteria2.Client
  32. dialer proxydialer.SingDialer
  33. }
  34. type Hysteria2Option struct {
  35. BasicOption
  36. Name string `proxy:"name"`
  37. Server string `proxy:"server"`
  38. Port int `proxy:"port,omitempty"`
  39. Ports string `proxy:"ports,omitempty"`
  40. HopInterval int `proxy:"hop-interval,omitempty"`
  41. Up string `proxy:"up,omitempty"`
  42. Down string `proxy:"down,omitempty"`
  43. Password string `proxy:"password,omitempty"`
  44. Obfs string `proxy:"obfs,omitempty"`
  45. ObfsPassword string `proxy:"obfs-password,omitempty"`
  46. SNI string `proxy:"sni,omitempty"`
  47. SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
  48. Fingerprint string `proxy:"fingerprint,omitempty"`
  49. ALPN []string `proxy:"alpn,omitempty"`
  50. CustomCA string `proxy:"ca,omitempty"`
  51. CustomCAString string `proxy:"ca-str,omitempty"`
  52. CWND int `proxy:"cwnd,omitempty"`
  53. UdpMTU int `proxy:"udp-mtu,omitempty"`
  54. }
  55. func (h *Hysteria2) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
  56. options := h.Base.DialOptions(opts...)
  57. h.dialer.SetDialer(dialer.NewDialer(options...))
  58. c, err := h.client.DialConn(ctx, M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort))
  59. if err != nil {
  60. return nil, err
  61. }
  62. return NewConn(CN.NewRefConn(c, h), h), nil
  63. }
  64. func (h *Hysteria2) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) {
  65. options := h.Base.DialOptions(opts...)
  66. h.dialer.SetDialer(dialer.NewDialer(options...))
  67. pc, err := h.client.ListenPacket(ctx)
  68. if err != nil {
  69. return nil, err
  70. }
  71. if pc == nil {
  72. return nil, errors.New("packetConn is nil")
  73. }
  74. return newPacketConn(CN.NewRefPacketConn(CN.NewThreadSafePacketConn(pc), h), h), nil
  75. }
  76. func closeHysteria2(h *Hysteria2) {
  77. if h.client != nil {
  78. _ = h.client.CloseWithError(errors.New("proxy removed"))
  79. }
  80. }
  81. func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) {
  82. addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
  83. var salamanderPassword string
  84. if len(option.Obfs) > 0 {
  85. if option.ObfsPassword == "" {
  86. return nil, errors.New("missing obfs password")
  87. }
  88. switch option.Obfs {
  89. case hysteria2.ObfsTypeSalamander:
  90. salamanderPassword = option.ObfsPassword
  91. default:
  92. return nil, fmt.Errorf("unknown obfs type: %s", option.Obfs)
  93. }
  94. }
  95. serverName := option.Server
  96. if option.SNI != "" {
  97. serverName = option.SNI
  98. }
  99. tlsConfig := &tls.Config{
  100. ServerName: serverName,
  101. InsecureSkipVerify: option.SkipCertVerify,
  102. MinVersion: tls.VersionTLS13,
  103. }
  104. var err error
  105. tlsConfig, err = ca.GetTLSConfig(tlsConfig, option.Fingerprint, option.CustomCA, option.CustomCAString)
  106. if err != nil {
  107. return nil, err
  108. }
  109. if len(option.ALPN) > 0 {
  110. tlsConfig.NextProtos = option.ALPN
  111. }
  112. if option.UdpMTU == 0 {
  113. // "1200" from quic-go's MaxDatagramSize
  114. // "-3" from quic-go's DatagramFrame.MaxDataLen
  115. option.UdpMTU = 1200 - 3
  116. }
  117. singDialer := proxydialer.NewByNameSingDialer(option.DialerProxy, dialer.NewDialer())
  118. clientOptions := hysteria2.ClientOptions{
  119. Context: context.TODO(),
  120. Dialer: singDialer,
  121. Logger: log.SingLogger,
  122. SendBPS: StringToBps(option.Up),
  123. ReceiveBPS: StringToBps(option.Down),
  124. SalamanderPassword: salamanderPassword,
  125. Password: option.Password,
  126. TLSConfig: tlsConfig,
  127. UDPDisabled: false,
  128. CWND: option.CWND,
  129. UdpMTU: option.UdpMTU,
  130. ServerAddress: func(ctx context.Context) (*net.UDPAddr, error) {
  131. return resolveUDPAddrWithPrefer(ctx, "udp", addr, C.NewDNSPrefer(option.IPVersion))
  132. },
  133. }
  134. var ranges utils.IntRanges[uint16]
  135. var serverAddress []string
  136. if option.Ports != "" {
  137. ranges, err = utils.NewUnsignedRanges[uint16](option.Ports)
  138. if err != nil {
  139. return nil, err
  140. }
  141. ranges.Range(func(port uint16) bool {
  142. serverAddress = append(serverAddress, net.JoinHostPort(option.Server, strconv.Itoa(int(port))))
  143. return true
  144. })
  145. if len(serverAddress) > 0 {
  146. clientOptions.ServerAddress = func(ctx context.Context) (*net.UDPAddr, error) {
  147. return resolveUDPAddrWithPrefer(ctx, "udp", serverAddress[randv2.IntN(len(serverAddress))], C.NewDNSPrefer(option.IPVersion))
  148. }
  149. if option.HopInterval == 0 {
  150. option.HopInterval = defaultHopInterval
  151. } else if option.HopInterval < minHopInterval {
  152. option.HopInterval = minHopInterval
  153. }
  154. clientOptions.HopInterval = time.Duration(option.HopInterval) * time.Second
  155. }
  156. }
  157. if option.Port == 0 && len(serverAddress) == 0 {
  158. return nil, errors.New("invalid port")
  159. }
  160. client, err := hysteria2.NewClient(clientOptions)
  161. if err != nil {
  162. return nil, err
  163. }
  164. outbound := &Hysteria2{
  165. Base: &Base{
  166. name: option.Name,
  167. addr: addr,
  168. tp: C.Hysteria2,
  169. udp: true,
  170. iface: option.Interface,
  171. rmark: option.RoutingMark,
  172. prefer: C.NewDNSPrefer(option.IPVersion),
  173. },
  174. option: &option,
  175. client: client,
  176. dialer: singDialer,
  177. }
  178. runtime.SetFinalizer(outbound, closeHysteria2)
  179. return outbound, nil
  180. }