tuic.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. //go:build with_quic
  2. package outbound
  3. import (
  4. "context"
  5. "net"
  6. "os"
  7. "time"
  8. "github.com/sagernet/sing-box/adapter"
  9. "github.com/sagernet/sing-box/common/dialer"
  10. "github.com/sagernet/sing-box/common/tls"
  11. C "github.com/sagernet/sing-box/constant"
  12. "github.com/sagernet/sing-box/log"
  13. "github.com/sagernet/sing-box/option"
  14. "github.com/sagernet/sing-quic/tuic"
  15. "github.com/sagernet/sing/common"
  16. "github.com/sagernet/sing/common/bufio"
  17. E "github.com/sagernet/sing/common/exceptions"
  18. M "github.com/sagernet/sing/common/metadata"
  19. N "github.com/sagernet/sing/common/network"
  20. "github.com/sagernet/sing/common/uot"
  21. "github.com/gofrs/uuid/v5"
  22. )
  23. var (
  24. _ adapter.Outbound = (*TUIC)(nil)
  25. _ adapter.InterfaceUpdateListener = (*TUIC)(nil)
  26. )
  27. type TUIC struct {
  28. myOutboundAdapter
  29. client *tuic.Client
  30. udpStream bool
  31. }
  32. func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TUICOutboundOptions) (*TUIC, error) {
  33. options.UDPFragmentDefault = true
  34. if options.TLS == nil || !options.TLS.Enabled {
  35. return nil, C.ErrTLSRequired
  36. }
  37. tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS))
  38. if err != nil {
  39. return nil, err
  40. }
  41. userUUID, err := uuid.FromString(options.UUID)
  42. if err != nil {
  43. return nil, E.Cause(err, "invalid uuid")
  44. }
  45. var tuicUDPStream bool
  46. if options.UDPOverStream && options.UDPRelayMode != "" {
  47. return nil, E.New("udp_over_stream is conflict with udp_relay_mode")
  48. }
  49. switch options.UDPRelayMode {
  50. case "native":
  51. case "quic":
  52. tuicUDPStream = true
  53. }
  54. outboundDialer, err := dialer.New(router, options.DialerOptions)
  55. if err != nil {
  56. return nil, err
  57. }
  58. client, err := tuic.NewClient(tuic.ClientOptions{
  59. Context: ctx,
  60. Dialer: outboundDialer,
  61. ServerAddress: options.ServerOptions.Build(),
  62. TLSConfig: tlsConfig,
  63. UUID: userUUID,
  64. Password: options.Password,
  65. CongestionControl: options.CongestionControl,
  66. UDPStream: tuicUDPStream,
  67. ZeroRTTHandshake: options.ZeroRTTHandshake,
  68. Heartbeat: time.Duration(options.Heartbeat),
  69. })
  70. if err != nil {
  71. return nil, err
  72. }
  73. return &TUIC{
  74. myOutboundAdapter: myOutboundAdapter{
  75. protocol: C.TypeTUIC,
  76. network: options.Network.Build(),
  77. router: router,
  78. logger: logger,
  79. tag: tag,
  80. dependencies: withDialerDependency(options.DialerOptions),
  81. },
  82. client: client,
  83. udpStream: options.UDPOverStream,
  84. }, nil
  85. }
  86. func (h *TUIC) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
  87. switch N.NetworkName(network) {
  88. case N.NetworkTCP:
  89. h.logger.InfoContext(ctx, "outbound connection to ", destination)
  90. return h.client.DialConn(ctx, destination)
  91. case N.NetworkUDP:
  92. if h.udpStream {
  93. h.logger.InfoContext(ctx, "outbound stream packet connection to ", destination)
  94. streamConn, err := h.client.DialConn(ctx, uot.RequestDestination(uot.Version))
  95. if err != nil {
  96. return nil, err
  97. }
  98. return uot.NewLazyConn(streamConn, uot.Request{
  99. IsConnect: true,
  100. Destination: destination,
  101. }), nil
  102. } else {
  103. conn, err := h.ListenPacket(ctx, destination)
  104. if err != nil {
  105. return nil, err
  106. }
  107. return bufio.NewBindPacketConn(conn, destination), nil
  108. }
  109. default:
  110. return nil, E.New("unsupported network: ", network)
  111. }
  112. }
  113. func (h *TUIC) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
  114. if h.udpStream {
  115. h.logger.InfoContext(ctx, "outbound stream packet connection to ", destination)
  116. streamConn, err := h.client.DialConn(ctx, uot.RequestDestination(uot.Version))
  117. if err != nil {
  118. return nil, err
  119. }
  120. return uot.NewLazyConn(streamConn, uot.Request{
  121. IsConnect: false,
  122. Destination: destination,
  123. }), nil
  124. } else {
  125. h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
  126. return h.client.ListenPacket(ctx)
  127. }
  128. }
  129. func (h *TUIC) InterfaceUpdated() {
  130. _ = h.client.CloseWithError(E.New("network changed"))
  131. }
  132. func (h *TUIC) Close() error {
  133. return h.client.CloseWithError(os.ErrClosed)
  134. }