proxy_darwin.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. package settings
  2. import (
  3. "context"
  4. "strconv"
  5. "strings"
  6. "github.com/sagernet/sing-box/adapter"
  7. "github.com/sagernet/sing-tun"
  8. "github.com/sagernet/sing/common/control"
  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.NetworkManager](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.routeUpdate)
  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) routeUpdate(defaultInterface *control.Interface, flags int) {
  62. if !p.isEnabled || defaultInterface == nil {
  63. return
  64. }
  65. _ = p.update0()
  66. }
  67. func (p *DarwinSystemProxy) update0() error {
  68. newInterface := p.monitor.DefaultInterface()
  69. if p.interfaceName == newInterface.Name {
  70. return nil
  71. }
  72. if p.interfaceName != "" {
  73. _ = p.Disable()
  74. }
  75. p.interfaceName = newInterface.Name
  76. interfaceDisplayName, err := getInterfaceDisplayName(p.interfaceName)
  77. if err != nil {
  78. return err
  79. }
  80. if p.supportSOCKS {
  81. err = shell.Exec("networksetup", "-setsocksfirewallproxy", interfaceDisplayName, p.serverAddr.AddrString(), strconv.Itoa(int(p.serverAddr.Port))).Attach().Run()
  82. }
  83. if err != nil {
  84. return err
  85. }
  86. err = shell.Exec("networksetup", "-setwebproxy", interfaceDisplayName, p.serverAddr.AddrString(), strconv.Itoa(int(p.serverAddr.Port))).Attach().Run()
  87. if err != nil {
  88. return err
  89. }
  90. err = shell.Exec("networksetup", "-setsecurewebproxy", interfaceDisplayName, p.serverAddr.AddrString(), strconv.Itoa(int(p.serverAddr.Port))).Attach().Run()
  91. if err != nil {
  92. return err
  93. }
  94. p.isEnabled = true
  95. return nil
  96. }
  97. func getInterfaceDisplayName(name string) (string, error) {
  98. content, err := shell.Exec("networksetup", "-listallhardwareports").ReadOutput()
  99. if err != nil {
  100. return "", err
  101. }
  102. for _, deviceSpan := range strings.Split(string(content), "Ethernet Address") {
  103. if strings.Contains(deviceSpan, "Device: "+name) {
  104. substr := "Hardware Port: "
  105. deviceSpan = deviceSpan[strings.Index(deviceSpan, substr)+len(substr):]
  106. deviceSpan = deviceSpan[:strings.Index(deviceSpan, "\n")]
  107. return deviceSpan, nil
  108. }
  109. }
  110. return "", E.New(name, " not found in networksetup -listallhardwareports")
  111. }