detour.go 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. package dialer
  2. import (
  3. "context"
  4. "net"
  5. "sync"
  6. "github.com/sagernet/sing-box/adapter"
  7. "github.com/sagernet/sing/common"
  8. E "github.com/sagernet/sing/common/exceptions"
  9. M "github.com/sagernet/sing/common/metadata"
  10. N "github.com/sagernet/sing/common/network"
  11. )
  12. type DirectDialer interface {
  13. IsEmpty() bool
  14. }
  15. type DetourDialer struct {
  16. outboundManager adapter.OutboundManager
  17. detour string
  18. legacyDNSDialer bool
  19. dialer N.Dialer
  20. initOnce sync.Once
  21. initErr error
  22. }
  23. func NewDetour(outboundManager adapter.OutboundManager, detour string, legacyDNSDialer bool) N.Dialer {
  24. return &DetourDialer{
  25. outboundManager: outboundManager,
  26. detour: detour,
  27. legacyDNSDialer: legacyDNSDialer,
  28. }
  29. }
  30. func InitializeDetour(dialer N.Dialer) error {
  31. detourDialer, isDetour := common.Cast[*DetourDialer](dialer)
  32. if !isDetour {
  33. return nil
  34. }
  35. return common.Error(detourDialer.Dialer())
  36. }
  37. func (d *DetourDialer) Dialer() (N.Dialer, error) {
  38. d.initOnce.Do(d.init)
  39. return d.dialer, d.initErr
  40. }
  41. func (d *DetourDialer) init() {
  42. dialer, loaded := d.outboundManager.Outbound(d.detour)
  43. if !loaded {
  44. d.initErr = E.New("outbound detour not found: ", d.detour)
  45. return
  46. }
  47. if !d.legacyDNSDialer {
  48. if directDialer, isDirect := dialer.(DirectDialer); isDirect {
  49. if directDialer.IsEmpty() {
  50. d.initErr = E.New("detour to an empty direct outbound makes no sense")
  51. return
  52. }
  53. }
  54. }
  55. d.dialer = dialer
  56. }
  57. func (d *DetourDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
  58. dialer, err := d.Dialer()
  59. if err != nil {
  60. return nil, err
  61. }
  62. return dialer.DialContext(ctx, network, destination)
  63. }
  64. func (d *DetourDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
  65. dialer, err := d.Dialer()
  66. if err != nil {
  67. return nil, err
  68. }
  69. return dialer.ListenPacket(ctx, destination)
  70. }
  71. func (d *DetourDialer) Upstream() any {
  72. detour, _ := d.Dialer()
  73. return detour
  74. }