2022-08-04 22:01:20 +08:00
|
|
|
package settings
|
|
|
|
|
|
|
|
import (
|
2023-09-03 14:29:37 +08:00
|
|
|
"context"
|
|
|
|
"strconv"
|
2022-08-04 22:01:20 +08:00
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/sagernet/sing-box/adapter"
|
|
|
|
"github.com/sagernet/sing-tun"
|
2024-11-12 19:37:10 +08:00
|
|
|
"github.com/sagernet/sing/common/control"
|
2022-08-04 22:01:20 +08:00
|
|
|
E "github.com/sagernet/sing/common/exceptions"
|
2023-09-03 14:29:37 +08:00
|
|
|
M "github.com/sagernet/sing/common/metadata"
|
2023-03-13 11:31:36 +08:00
|
|
|
"github.com/sagernet/sing/common/shell"
|
2022-08-04 22:01:20 +08:00
|
|
|
"github.com/sagernet/sing/common/x/list"
|
2024-11-09 21:16:11 +08:00
|
|
|
"github.com/sagernet/sing/service"
|
2022-08-04 22:01:20 +08:00
|
|
|
)
|
|
|
|
|
2023-09-03 14:29:37 +08:00
|
|
|
type DarwinSystemProxy struct {
|
2022-08-04 22:01:20 +08:00
|
|
|
monitor tun.DefaultInterfaceMonitor
|
|
|
|
interfaceName string
|
|
|
|
element *list.Element[tun.DefaultInterfaceUpdateCallback]
|
2023-09-03 14:29:37 +08:00
|
|
|
serverAddr M.Socksaddr
|
|
|
|
supportSOCKS bool
|
|
|
|
isEnabled bool
|
2022-08-04 22:01:20 +08:00
|
|
|
}
|
|
|
|
|
2023-09-03 14:29:37 +08:00
|
|
|
func NewSystemProxy(ctx context.Context, serverAddr M.Socksaddr, supportSOCKS bool) (*DarwinSystemProxy, error) {
|
2024-11-10 12:11:21 +08:00
|
|
|
interfaceMonitor := service.FromContext[adapter.NetworkManager](ctx).InterfaceMonitor()
|
2023-09-03 14:29:37 +08:00
|
|
|
if interfaceMonitor == nil {
|
|
|
|
return nil, E.New("missing interface monitor")
|
2022-08-04 22:01:20 +08:00
|
|
|
}
|
2023-09-03 14:29:37 +08:00
|
|
|
proxy := &DarwinSystemProxy{
|
|
|
|
monitor: interfaceMonitor,
|
|
|
|
serverAddr: serverAddr,
|
|
|
|
supportSOCKS: supportSOCKS,
|
2022-08-04 22:01:20 +08:00
|
|
|
}
|
2024-11-12 19:37:10 +08:00
|
|
|
proxy.element = interfaceMonitor.RegisterCallback(proxy.routeUpdate)
|
2023-09-03 14:29:37 +08:00
|
|
|
return proxy, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *DarwinSystemProxy) IsEnabled() bool {
|
|
|
|
return p.isEnabled
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *DarwinSystemProxy) Enable() error {
|
|
|
|
return p.update0()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *DarwinSystemProxy) Disable() error {
|
2022-08-04 22:01:20 +08:00
|
|
|
interfaceDisplayName, err := getInterfaceDisplayName(p.interfaceName)
|
|
|
|
if err != nil {
|
2023-09-03 14:29:37 +08:00
|
|
|
return err
|
2022-08-04 22:01:20 +08:00
|
|
|
}
|
2023-09-03 14:29:37 +08:00
|
|
|
if p.supportSOCKS {
|
|
|
|
err = shell.Exec("networksetup", "-setsocksfirewallproxystate", interfaceDisplayName, "off").Attach().Run()
|
2022-08-04 22:01:20 +08:00
|
|
|
}
|
|
|
|
if err == nil {
|
2023-09-03 14:29:37 +08:00
|
|
|
err = shell.Exec("networksetup", "-setwebproxystate", interfaceDisplayName, "off").Attach().Run()
|
|
|
|
}
|
|
|
|
if err == nil {
|
|
|
|
err = shell.Exec("networksetup", "-setsecurewebproxystate", interfaceDisplayName, "off").Attach().Run()
|
2022-08-04 22:01:20 +08:00
|
|
|
}
|
|
|
|
if err == nil {
|
2023-09-03 14:29:37 +08:00
|
|
|
p.isEnabled = false
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-11-12 19:37:10 +08:00
|
|
|
func (p *DarwinSystemProxy) routeUpdate(defaultInterface *control.Interface, flags int) {
|
|
|
|
if !p.isEnabled || defaultInterface == nil {
|
2023-09-03 14:29:37 +08:00
|
|
|
return
|
2022-08-04 22:01:20 +08:00
|
|
|
}
|
2023-09-03 14:29:37 +08:00
|
|
|
_ = p.update0()
|
2022-08-04 22:01:20 +08:00
|
|
|
}
|
|
|
|
|
2023-09-03 14:29:37 +08:00
|
|
|
func (p *DarwinSystemProxy) update0() error {
|
2024-11-11 16:23:45 +08:00
|
|
|
newInterface := p.monitor.DefaultInterface()
|
|
|
|
if p.interfaceName == newInterface.Name {
|
2023-09-03 14:29:37 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if p.interfaceName != "" {
|
|
|
|
_ = p.Disable()
|
|
|
|
}
|
2024-11-11 16:23:45 +08:00
|
|
|
p.interfaceName = newInterface.Name
|
2022-08-04 22:01:20 +08:00
|
|
|
interfaceDisplayName, err := getInterfaceDisplayName(p.interfaceName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-09-03 14:29:37 +08:00
|
|
|
if p.supportSOCKS {
|
|
|
|
err = shell.Exec("networksetup", "-setsocksfirewallproxy", interfaceDisplayName, p.serverAddr.AddrString(), strconv.Itoa(int(p.serverAddr.Port))).Attach().Run()
|
2022-08-04 22:01:20 +08:00
|
|
|
}
|
2023-09-03 14:29:37 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2022-08-04 22:01:20 +08:00
|
|
|
}
|
2023-09-03 14:29:37 +08:00
|
|
|
err = shell.Exec("networksetup", "-setwebproxy", interfaceDisplayName, p.serverAddr.AddrString(), strconv.Itoa(int(p.serverAddr.Port))).Attach().Run()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2022-08-04 22:01:20 +08:00
|
|
|
}
|
2023-09-03 14:29:37 +08:00
|
|
|
err = shell.Exec("networksetup", "-setsecurewebproxy", interfaceDisplayName, p.serverAddr.AddrString(), strconv.Itoa(int(p.serverAddr.Port))).Attach().Run()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
p.isEnabled = true
|
|
|
|
return nil
|
2022-08-04 22:01:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func getInterfaceDisplayName(name string) (string, error) {
|
2023-03-13 11:31:36 +08:00
|
|
|
content, err := shell.Exec("networksetup", "-listallhardwareports").ReadOutput()
|
2022-08-04 22:01:20 +08:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
for _, deviceSpan := range strings.Split(string(content), "Ethernet Address") {
|
|
|
|
if strings.Contains(deviceSpan, "Device: "+name) {
|
|
|
|
substr := "Hardware Port: "
|
|
|
|
deviceSpan = deviceSpan[strings.Index(deviceSpan, substr)+len(substr):]
|
|
|
|
deviceSpan = deviceSpan[:strings.Index(deviceSpan, "\n")]
|
|
|
|
return deviceSpan, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "", E.New(name, " not found in networksetup -listallhardwareports")
|
|
|
|
}
|