tuic.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. package outbound
  2. import (
  3. "context"
  4. "crypto/tls"
  5. "errors"
  6. "fmt"
  7. "math"
  8. "net"
  9. "strconv"
  10. "time"
  11. "github.com/metacubex/mihomo/component/ca"
  12. "github.com/metacubex/mihomo/component/dialer"
  13. "github.com/metacubex/mihomo/component/proxydialer"
  14. "github.com/metacubex/mihomo/component/resolver"
  15. C "github.com/metacubex/mihomo/constant"
  16. "github.com/metacubex/mihomo/transport/tuic"
  17. "github.com/gofrs/uuid/v5"
  18. "github.com/metacubex/quic-go"
  19. M "github.com/sagernet/sing/common/metadata"
  20. "github.com/sagernet/sing/common/uot"
  21. )
  22. type Tuic struct {
  23. *Base
  24. option *TuicOption
  25. client *tuic.PoolClient
  26. }
  27. type TuicOption struct {
  28. BasicOption
  29. Name string `proxy:"name"`
  30. Server string `proxy:"server"`
  31. Port int `proxy:"port"`
  32. Token string `proxy:"token,omitempty"`
  33. UUID string `proxy:"uuid,omitempty"`
  34. Password string `proxy:"password,omitempty"`
  35. Ip string `proxy:"ip,omitempty"`
  36. HeartbeatInterval int `proxy:"heartbeat-interval,omitempty"`
  37. ALPN []string `proxy:"alpn,omitempty"`
  38. ReduceRtt bool `proxy:"reduce-rtt,omitempty"`
  39. RequestTimeout int `proxy:"request-timeout,omitempty"`
  40. UdpRelayMode string `proxy:"udp-relay-mode,omitempty"`
  41. CongestionController string `proxy:"congestion-controller,omitempty"`
  42. DisableSni bool `proxy:"disable-sni,omitempty"`
  43. MaxUdpRelayPacketSize int `proxy:"max-udp-relay-packet-size,omitempty"`
  44. FastOpen bool `proxy:"fast-open,omitempty"`
  45. MaxOpenStreams int `proxy:"max-open-streams,omitempty"`
  46. CWND int `proxy:"cwnd,omitempty"`
  47. SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
  48. Fingerprint string `proxy:"fingerprint,omitempty"`
  49. CustomCA string `proxy:"ca,omitempty"`
  50. CustomCAString string `proxy:"ca-str,omitempty"`
  51. ReceiveWindowConn int `proxy:"recv-window-conn,omitempty"`
  52. ReceiveWindow int `proxy:"recv-window,omitempty"`
  53. DisableMTUDiscovery bool `proxy:"disable-mtu-discovery,omitempty"`
  54. MaxDatagramFrameSize int `proxy:"max-datagram-frame-size,omitempty"`
  55. SNI string `proxy:"sni,omitempty"`
  56. UDPOverStream bool `proxy:"udp-over-stream,omitempty"`
  57. UDPOverStreamVersion int `proxy:"udp-over-stream-version,omitempty"`
  58. }
  59. // DialContext implements C.ProxyAdapter
  60. func (t *Tuic) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
  61. return t.DialContextWithDialer(ctx, dialer.NewDialer(t.Base.DialOptions(opts...)...), metadata)
  62. }
  63. // DialContextWithDialer implements C.ProxyAdapter
  64. func (t *Tuic) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (C.Conn, error) {
  65. conn, err := t.client.DialContextWithDialer(ctx, metadata, dialer, t.dialWithDialer)
  66. if err != nil {
  67. return nil, err
  68. }
  69. return NewConn(conn, t), err
  70. }
  71. // ListenPacketContext implements C.ProxyAdapter
  72. func (t *Tuic) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) {
  73. return t.ListenPacketWithDialer(ctx, dialer.NewDialer(t.Base.DialOptions(opts...)...), metadata)
  74. }
  75. // ListenPacketWithDialer implements C.ProxyAdapter
  76. func (t *Tuic) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
  77. if t.option.UDPOverStream {
  78. uotDestination := uot.RequestDestination(uint8(t.option.UDPOverStreamVersion))
  79. uotMetadata := *metadata
  80. uotMetadata.Host = uotDestination.Fqdn
  81. uotMetadata.DstPort = uotDestination.Port
  82. c, err := t.DialContextWithDialer(ctx, dialer, &uotMetadata)
  83. if err != nil {
  84. return nil, err
  85. }
  86. // tuic uos use stream-oriented udp with a special address, so we need a net.UDPAddr
  87. if !metadata.Resolved() {
  88. ip, err := resolver.ResolveIP(ctx, metadata.Host)
  89. if err != nil {
  90. return nil, errors.New("can't resolve ip")
  91. }
  92. metadata.DstIP = ip
  93. }
  94. destination := M.SocksaddrFromNet(metadata.UDPAddr())
  95. if t.option.UDPOverStreamVersion == uot.LegacyVersion {
  96. return newPacketConn(uot.NewConn(c, uot.Request{Destination: destination}), t), nil
  97. } else {
  98. return newPacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination}), t), nil
  99. }
  100. }
  101. pc, err := t.client.ListenPacketWithDialer(ctx, metadata, dialer, t.dialWithDialer)
  102. if err != nil {
  103. return nil, err
  104. }
  105. return newPacketConn(pc, t), nil
  106. }
  107. // SupportWithDialer implements C.ProxyAdapter
  108. func (t *Tuic) SupportWithDialer() C.NetWork {
  109. return C.ALLNet
  110. }
  111. func (t *Tuic) dialWithDialer(ctx context.Context, dialer C.Dialer) (transport *quic.Transport, addr net.Addr, err error) {
  112. if len(t.option.DialerProxy) > 0 {
  113. dialer, err = proxydialer.NewByName(t.option.DialerProxy, dialer)
  114. if err != nil {
  115. return nil, nil, err
  116. }
  117. }
  118. udpAddr, err := resolveUDPAddrWithPrefer(ctx, "udp", t.addr, t.prefer)
  119. if err != nil {
  120. return nil, nil, err
  121. }
  122. addr = udpAddr
  123. var pc net.PacketConn
  124. pc, err = dialer.ListenPacket(ctx, "udp", "", udpAddr.AddrPort())
  125. if err != nil {
  126. return nil, nil, err
  127. }
  128. transport = &quic.Transport{Conn: pc}
  129. transport.SetCreatedConn(true) // auto close conn
  130. transport.SetSingleUse(true) // auto close transport
  131. return
  132. }
  133. func NewTuic(option TuicOption) (*Tuic, error) {
  134. addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
  135. serverName := option.Server
  136. tlsConfig := &tls.Config{
  137. ServerName: serverName,
  138. InsecureSkipVerify: option.SkipCertVerify,
  139. MinVersion: tls.VersionTLS13,
  140. }
  141. if option.SNI != "" {
  142. tlsConfig.ServerName = option.SNI
  143. }
  144. var err error
  145. tlsConfig, err = ca.GetTLSConfig(tlsConfig, option.Fingerprint, option.CustomCA, option.CustomCAString)
  146. if err != nil {
  147. return nil, err
  148. }
  149. if option.ALPN != nil { // structure's Decode will ensure value not nil when input has value even it was set an empty array
  150. tlsConfig.NextProtos = option.ALPN
  151. } else {
  152. tlsConfig.NextProtos = []string{"h3"}
  153. }
  154. if option.RequestTimeout == 0 {
  155. option.RequestTimeout = 8000
  156. }
  157. if option.HeartbeatInterval <= 0 {
  158. option.HeartbeatInterval = 10000
  159. }
  160. udpRelayMode := tuic.QUIC
  161. if option.UdpRelayMode != "quic" {
  162. udpRelayMode = tuic.NATIVE
  163. }
  164. if option.MaxUdpRelayPacketSize == 0 {
  165. option.MaxUdpRelayPacketSize = 1252
  166. }
  167. if option.MaxOpenStreams == 0 {
  168. option.MaxOpenStreams = 100
  169. }
  170. if option.CWND == 0 {
  171. option.CWND = 32
  172. }
  173. packetOverHead := tuic.PacketOverHeadV4
  174. if len(option.Token) == 0 {
  175. packetOverHead = tuic.PacketOverHeadV5
  176. }
  177. if option.MaxDatagramFrameSize == 0 {
  178. option.MaxDatagramFrameSize = option.MaxUdpRelayPacketSize + packetOverHead
  179. }
  180. if option.MaxDatagramFrameSize > 1400 {
  181. option.MaxDatagramFrameSize = 1400
  182. }
  183. option.MaxUdpRelayPacketSize = option.MaxDatagramFrameSize - packetOverHead
  184. // ensure server's incoming stream can handle correctly, increase to 1.1x
  185. quicMaxOpenStreams := int64(option.MaxOpenStreams)
  186. quicMaxOpenStreams = quicMaxOpenStreams + int64(math.Ceil(float64(quicMaxOpenStreams)/10.0))
  187. quicConfig := &quic.Config{
  188. InitialStreamReceiveWindow: uint64(option.ReceiveWindowConn),
  189. MaxStreamReceiveWindow: uint64(option.ReceiveWindowConn),
  190. InitialConnectionReceiveWindow: uint64(option.ReceiveWindow),
  191. MaxConnectionReceiveWindow: uint64(option.ReceiveWindow),
  192. MaxIncomingStreams: quicMaxOpenStreams,
  193. MaxIncomingUniStreams: quicMaxOpenStreams,
  194. KeepAlivePeriod: time.Duration(option.HeartbeatInterval) * time.Millisecond,
  195. DisablePathMTUDiscovery: option.DisableMTUDiscovery,
  196. MaxDatagramFrameSize: int64(option.MaxDatagramFrameSize),
  197. EnableDatagrams: true,
  198. }
  199. if option.ReceiveWindowConn == 0 {
  200. quicConfig.InitialStreamReceiveWindow = tuic.DefaultStreamReceiveWindow / 10
  201. quicConfig.MaxStreamReceiveWindow = tuic.DefaultStreamReceiveWindow
  202. }
  203. if option.ReceiveWindow == 0 {
  204. quicConfig.InitialConnectionReceiveWindow = tuic.DefaultConnectionReceiveWindow / 10
  205. quicConfig.MaxConnectionReceiveWindow = tuic.DefaultConnectionReceiveWindow
  206. }
  207. if len(option.Ip) > 0 {
  208. addr = net.JoinHostPort(option.Ip, strconv.Itoa(option.Port))
  209. }
  210. if option.DisableSni {
  211. tlsConfig.ServerName = ""
  212. tlsConfig.InsecureSkipVerify = true // tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config
  213. }
  214. switch option.UDPOverStreamVersion {
  215. case uot.Version, uot.LegacyVersion:
  216. case 0:
  217. option.UDPOverStreamVersion = uot.LegacyVersion
  218. default:
  219. return nil, fmt.Errorf("tuic %s unknown udp over stream protocol version: %d", addr, option.UDPOverStreamVersion)
  220. }
  221. t := &Tuic{
  222. Base: &Base{
  223. name: option.Name,
  224. addr: addr,
  225. tp: C.Tuic,
  226. udp: true,
  227. tfo: option.FastOpen,
  228. iface: option.Interface,
  229. rmark: option.RoutingMark,
  230. prefer: C.NewDNSPrefer(option.IPVersion),
  231. },
  232. option: &option,
  233. }
  234. clientMaxOpenStreams := int64(option.MaxOpenStreams)
  235. // to avoid tuic's "too many open streams", decrease to 0.9x
  236. if clientMaxOpenStreams == 100 {
  237. clientMaxOpenStreams = clientMaxOpenStreams - int64(math.Ceil(float64(clientMaxOpenStreams)/10.0))
  238. }
  239. if clientMaxOpenStreams < 1 {
  240. clientMaxOpenStreams = 1
  241. }
  242. if len(option.Token) > 0 {
  243. tkn := tuic.GenTKN(option.Token)
  244. clientOption := &tuic.ClientOptionV4{
  245. TlsConfig: tlsConfig,
  246. QuicConfig: quicConfig,
  247. Token: tkn,
  248. UdpRelayMode: udpRelayMode,
  249. CongestionController: option.CongestionController,
  250. ReduceRtt: option.ReduceRtt,
  251. RequestTimeout: time.Duration(option.RequestTimeout) * time.Millisecond,
  252. MaxUdpRelayPacketSize: option.MaxUdpRelayPacketSize,
  253. FastOpen: option.FastOpen,
  254. MaxOpenStreams: clientMaxOpenStreams,
  255. CWND: option.CWND,
  256. }
  257. t.client = tuic.NewPoolClientV4(clientOption)
  258. } else {
  259. maxUdpRelayPacketSize := option.MaxUdpRelayPacketSize
  260. if maxUdpRelayPacketSize > tuic.MaxFragSizeV5 {
  261. maxUdpRelayPacketSize = tuic.MaxFragSizeV5
  262. }
  263. clientOption := &tuic.ClientOptionV5{
  264. TlsConfig: tlsConfig,
  265. QuicConfig: quicConfig,
  266. Uuid: uuid.FromStringOrNil(option.UUID),
  267. Password: option.Password,
  268. UdpRelayMode: udpRelayMode,
  269. CongestionController: option.CongestionController,
  270. ReduceRtt: option.ReduceRtt,
  271. MaxUdpRelayPacketSize: maxUdpRelayPacketSize,
  272. MaxOpenStreams: clientMaxOpenStreams,
  273. CWND: option.CWND,
  274. }
  275. t.client = tuic.NewPoolClientV5(clientOption)
  276. }
  277. return t, nil
  278. }