proxy_darwin.go 3.4 KB

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