proxy_darwin.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. package settings
  2. import (
  3. "context"
  4. "net/netip"
  5. "strconv"
  6. "strings"
  7. "github.com/sagernet/sing-box/adapter"
  8. "github.com/sagernet/sing-tun"
  9. E "github.com/sagernet/sing/common/exceptions"
  10. M "github.com/sagernet/sing/common/metadata"
  11. "github.com/sagernet/sing/common/shell"
  12. "github.com/sagernet/sing/common/x/list"
  13. "github.com/sagernet/sing/service"
  14. )
  15. type DarwinSystemProxy struct {
  16. monitor tun.DefaultInterfaceMonitor
  17. interfaceName string
  18. element *list.Element[tun.DefaultInterfaceUpdateCallback]
  19. serverAddr M.Socksaddr
  20. supportSOCKS bool
  21. isEnabled bool
  22. }
  23. func NewSystemProxy(ctx context.Context, serverAddr M.Socksaddr, supportSOCKS bool) (*DarwinSystemProxy, error) {
  24. interfaceMonitor := service.FromContext[adapter.Router](ctx).InterfaceMonitor()
  25. if interfaceMonitor == nil {
  26. return nil, E.New("missing interface monitor")
  27. }
  28. proxy := &DarwinSystemProxy{
  29. monitor: interfaceMonitor,
  30. serverAddr: serverAddr,
  31. supportSOCKS: supportSOCKS,
  32. }
  33. proxy.element = interfaceMonitor.RegisterCallback(proxy.update)
  34. return proxy, nil
  35. }
  36. func (p *DarwinSystemProxy) IsEnabled() bool {
  37. return p.isEnabled
  38. }
  39. func (p *DarwinSystemProxy) Enable() error {
  40. return p.update0()
  41. }
  42. func (p *DarwinSystemProxy) Disable() error {
  43. interfaceDisplayName, err := getInterfaceDisplayName(p.interfaceName)
  44. if err != nil {
  45. return err
  46. }
  47. if p.supportSOCKS {
  48. err = shell.Exec("networksetup", "-setsocksfirewallproxystate", interfaceDisplayName, "off").Attach().Run()
  49. }
  50. if err == nil {
  51. err = shell.Exec("networksetup", "-setwebproxystate", interfaceDisplayName, "off").Attach().Run()
  52. }
  53. if err == nil {
  54. err = shell.Exec("networksetup", "-setsecurewebproxystate", interfaceDisplayName, "off").Attach().Run()
  55. }
  56. if err == nil {
  57. p.isEnabled = false
  58. }
  59. return err
  60. }
  61. func (p *DarwinSystemProxy) update(event int) {
  62. if event&tun.EventInterfaceUpdate == 0 {
  63. return
  64. }
  65. if !p.isEnabled {
  66. return
  67. }
  68. _ = p.update0()
  69. }
  70. func (p *DarwinSystemProxy) update0() error {
  71. newInterfaceName := p.monitor.DefaultInterfaceName(netip.IPv4Unspecified())
  72. if p.interfaceName == newInterfaceName {
  73. return nil
  74. }
  75. if p.interfaceName != "" {
  76. _ = p.Disable()
  77. }
  78. p.interfaceName = newInterfaceName
  79. interfaceDisplayName, err := getInterfaceDisplayName(p.interfaceName)
  80. if err != nil {
  81. return err
  82. }
  83. if p.supportSOCKS {
  84. err = shell.Exec("networksetup", "-setsocksfirewallproxy", interfaceDisplayName, p.serverAddr.AddrString(), strconv.Itoa(int(p.serverAddr.Port))).Attach().Run()
  85. }
  86. if err != nil {
  87. return err
  88. }
  89. err = shell.Exec("networksetup", "-setwebproxy", interfaceDisplayName, p.serverAddr.AddrString(), strconv.Itoa(int(p.serverAddr.Port))).Attach().Run()
  90. if err != nil {
  91. return err
  92. }
  93. err = shell.Exec("networksetup", "-setsecurewebproxy", interfaceDisplayName, p.serverAddr.AddrString(), strconv.Itoa(int(p.serverAddr.Port))).Attach().Run()
  94. if err != nil {
  95. return err
  96. }
  97. p.isEnabled = true
  98. return nil
  99. }
  100. func getInterfaceDisplayName(name string) (string, error) {
  101. content, err := shell.Exec("networksetup", "-listallhardwareports").ReadOutput()
  102. if err != nil {
  103. return "", err
  104. }
  105. for _, deviceSpan := range strings.Split(string(content), "Ethernet Address") {
  106. if strings.Contains(deviceSpan, "Device: "+name) {
  107. substr := "Hardware Port: "
  108. deviceSpan = deviceSpan[strings.Index(deviceSpan, substr)+len(substr):]
  109. deviceSpan = deviceSpan[:strings.Index(deviceSpan, "\n")]
  110. return deviceSpan, nil
  111. }
  112. }
  113. return "", E.New(name, " not found in networksetup -listallhardwareports")
  114. }