snell.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. package outbound
  2. import (
  3. "context"
  4. "fmt"
  5. "net"
  6. "strconv"
  7. N "github.com/metacubex/mihomo/common/net"
  8. "github.com/metacubex/mihomo/common/structure"
  9. "github.com/metacubex/mihomo/component/dialer"
  10. "github.com/metacubex/mihomo/component/proxydialer"
  11. C "github.com/metacubex/mihomo/constant"
  12. obfs "github.com/metacubex/mihomo/transport/simple-obfs"
  13. "github.com/metacubex/mihomo/transport/snell"
  14. )
  15. type Snell struct {
  16. *Base
  17. option *SnellOption
  18. psk []byte
  19. pool *snell.Pool
  20. obfsOption *simpleObfsOption
  21. version int
  22. }
  23. type SnellOption struct {
  24. BasicOption
  25. Name string `proxy:"name"`
  26. Server string `proxy:"server"`
  27. Port int `proxy:"port"`
  28. Psk string `proxy:"psk"`
  29. UDP bool `proxy:"udp,omitempty"`
  30. Version int `proxy:"version,omitempty"`
  31. ObfsOpts map[string]any `proxy:"obfs-opts,omitempty"`
  32. }
  33. type streamOption struct {
  34. psk []byte
  35. version int
  36. addr string
  37. obfsOption *simpleObfsOption
  38. }
  39. func streamConn(c net.Conn, option streamOption) *snell.Snell {
  40. switch option.obfsOption.Mode {
  41. case "tls":
  42. c = obfs.NewTLSObfs(c, option.obfsOption.Host)
  43. case "http":
  44. _, port, _ := net.SplitHostPort(option.addr)
  45. c = obfs.NewHTTPObfs(c, option.obfsOption.Host, port)
  46. }
  47. return snell.StreamConn(c, option.psk, option.version)
  48. }
  49. // StreamConnContext implements C.ProxyAdapter
  50. func (s *Snell) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {
  51. c = streamConn(c, streamOption{s.psk, s.version, s.addr, s.obfsOption})
  52. if metadata.NetWork == C.UDP {
  53. err := snell.WriteUDPHeader(c, s.version)
  54. return c, err
  55. }
  56. err := snell.WriteHeader(c, metadata.String(), uint(metadata.DstPort), s.version)
  57. return c, err
  58. }
  59. // DialContext implements C.ProxyAdapter
  60. func (s *Snell) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
  61. if s.version == snell.Version2 && len(opts) == 0 {
  62. c, err := s.pool.Get()
  63. if err != nil {
  64. return nil, err
  65. }
  66. if err = snell.WriteHeader(c, metadata.String(), uint(metadata.DstPort), s.version); err != nil {
  67. c.Close()
  68. return nil, err
  69. }
  70. return NewConn(c, s), err
  71. }
  72. return s.DialContextWithDialer(ctx, dialer.NewDialer(s.Base.DialOptions(opts...)...), metadata)
  73. }
  74. // DialContextWithDialer implements C.ProxyAdapter
  75. func (s *Snell) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) {
  76. if len(s.option.DialerProxy) > 0 {
  77. dialer, err = proxydialer.NewByName(s.option.DialerProxy, dialer)
  78. if err != nil {
  79. return nil, err
  80. }
  81. }
  82. c, err := dialer.DialContext(ctx, "tcp", s.addr)
  83. if err != nil {
  84. return nil, fmt.Errorf("%s connect error: %w", s.addr, err)
  85. }
  86. N.TCPKeepAlive(c)
  87. defer func(c net.Conn) {
  88. safeConnClose(c, err)
  89. }(c)
  90. c, err = s.StreamConnContext(ctx, c, metadata)
  91. return NewConn(c, s), err
  92. }
  93. // ListenPacketContext implements C.ProxyAdapter
  94. func (s *Snell) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
  95. return s.ListenPacketWithDialer(ctx, dialer.NewDialer(s.Base.DialOptions(opts...)...), metadata)
  96. }
  97. // ListenPacketWithDialer implements C.ProxyAdapter
  98. func (s *Snell) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (C.PacketConn, error) {
  99. var err error
  100. if len(s.option.DialerProxy) > 0 {
  101. dialer, err = proxydialer.NewByName(s.option.DialerProxy, dialer)
  102. if err != nil {
  103. return nil, err
  104. }
  105. }
  106. c, err := dialer.DialContext(ctx, "tcp", s.addr)
  107. if err != nil {
  108. return nil, err
  109. }
  110. N.TCPKeepAlive(c)
  111. c = streamConn(c, streamOption{s.psk, s.version, s.addr, s.obfsOption})
  112. err = snell.WriteUDPHeader(c, s.version)
  113. if err != nil {
  114. return nil, err
  115. }
  116. pc := snell.PacketConn(c)
  117. return newPacketConn(pc, s), nil
  118. }
  119. // SupportWithDialer implements C.ProxyAdapter
  120. func (s *Snell) SupportWithDialer() C.NetWork {
  121. return C.ALLNet
  122. }
  123. // SupportUOT implements C.ProxyAdapter
  124. func (s *Snell) SupportUOT() bool {
  125. return true
  126. }
  127. func NewSnell(option SnellOption) (*Snell, error) {
  128. addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
  129. psk := []byte(option.Psk)
  130. decoder := structure.NewDecoder(structure.Option{TagName: "obfs", WeaklyTypedInput: true})
  131. obfsOption := &simpleObfsOption{Host: "bing.com"}
  132. if err := decoder.Decode(option.ObfsOpts, obfsOption); err != nil {
  133. return nil, fmt.Errorf("snell %s initialize obfs error: %w", addr, err)
  134. }
  135. switch obfsOption.Mode {
  136. case "tls", "http", "":
  137. break
  138. default:
  139. return nil, fmt.Errorf("snell %s obfs mode error: %s", addr, obfsOption.Mode)
  140. }
  141. // backward compatible
  142. if option.Version == 0 {
  143. option.Version = snell.DefaultSnellVersion
  144. }
  145. switch option.Version {
  146. case snell.Version1, snell.Version2:
  147. if option.UDP {
  148. return nil, fmt.Errorf("snell version %d not support UDP", option.Version)
  149. }
  150. case snell.Version3:
  151. default:
  152. return nil, fmt.Errorf("snell version error: %d", option.Version)
  153. }
  154. s := &Snell{
  155. Base: &Base{
  156. name: option.Name,
  157. addr: addr,
  158. tp: C.Snell,
  159. udp: option.UDP,
  160. tfo: option.TFO,
  161. mpTcp: option.MPTCP,
  162. iface: option.Interface,
  163. rmark: option.RoutingMark,
  164. prefer: C.NewDNSPrefer(option.IPVersion),
  165. },
  166. option: &option,
  167. psk: psk,
  168. obfsOption: obfsOption,
  169. version: option.Version,
  170. }
  171. if option.Version == snell.Version2 {
  172. s.pool = snell.NewPool(func(ctx context.Context) (*snell.Snell, error) {
  173. var err error
  174. var cDialer C.Dialer = dialer.NewDialer(s.Base.DialOptions()...)
  175. if len(s.option.DialerProxy) > 0 {
  176. cDialer, err = proxydialer.NewByName(s.option.DialerProxy, cDialer)
  177. if err != nil {
  178. return nil, err
  179. }
  180. }
  181. c, err := cDialer.DialContext(ctx, "tcp", addr)
  182. if err != nil {
  183. return nil, err
  184. }
  185. N.TCPKeepAlive(c)
  186. return streamConn(c, streamOption{psk, option.Version, addr, obfsOption}), nil
  187. })
  188. }
  189. return s, nil
  190. }