Files
sing-box-extended-mirror/common/settings/proxy_darwin.go
T

177 lines
4.6 KiB
Go
Raw Normal View History

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"
"sync"
2022-08-04 22:01:20 +08:00
"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 {
ctx context.Context
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
access sync.Mutex
updateCancel context.CancelFunc
2023-09-03 14:29:37 +08:00
isEnabled bool
2022-08-04 22:01:20 +08:00
}
func NewSystemProxy(ctx context.Context, serverAddr M.Socksaddr, supportSOCKS bool, bypassDomain []string) (*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{
ctx: ctx,
2023-09-03 14:29:37 +08:00
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 {
p.access.Lock()
defer p.access.Unlock()
2023-09-03 14:29:37 +08:00
return p.isEnabled
}
func (p *DarwinSystemProxy) Enable() error {
p.access.Lock()
defer p.access.Unlock()
return p.updateLocked(p.ctx)
2023-09-03 14:29:37 +08:00
}
func (p *DarwinSystemProxy) Disable() error {
p.access.Lock()
defer p.access.Unlock()
return p.disableLocked()
}
func (p *DarwinSystemProxy) Close() error {
p.access.Lock()
updateCancel := p.updateCancel
p.updateCancel = nil
p.access.Unlock()
if updateCancel != nil {
updateCancel()
}
p.monitor.UnregisterCallback(p.element)
return nil
}
func (p *DarwinSystemProxy) disableLocked() 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 defaultInterface == nil {
2023-09-03 14:29:37 +08:00
return
2022-08-04 22:01:20 +08:00
}
updateContext, updateCancel := context.WithCancel(p.ctx)
p.access.Lock()
previousCancel := p.updateCancel
p.updateCancel = updateCancel
p.access.Unlock()
if previousCancel != nil {
previousCancel()
}
go func() {
defer updateCancel()
p.access.Lock()
defer p.access.Unlock()
if !p.isEnabled || updateContext.Err() != nil {
return
}
_ = p.updateLocked(updateContext)
}()
2022-08-04 22:01:20 +08:00
}
func (p *DarwinSystemProxy) updateLocked(ctx context.Context) error {
2024-11-11 16:23:45 +08:00
newInterface := p.monitor.DefaultInterface()
if newInterface == nil || p.interfaceName == newInterface.Name {
2023-09-03 14:29:37 +08:00
return nil
}
if p.interfaceName != "" {
_ = p.disableLocked()
2023-09-03 14:29:37 +08:00
}
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 = ctx.Err()
if err != nil {
return err
}
2023-09-03 14:29:37 +08:00
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
}
err = ctx.Err()
if err != nil {
return err
}
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
}
err = ctx.Err()
if err != nil {
return err
}
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
}
2026-05-13 22:37:05 +08:00
for deviceSpan := range strings.SplitSeq(string(content), "Ethernet Address") {
2022-08-04 22:01:20 +08:00
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")
}