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