github.com/sagernet/sing-box@v1.2.7/common/settings/proxy_darwin.go (about)

     1  package settings
     2  
     3  import (
     4  	"net/netip"
     5  	"strings"
     6  
     7  	"github.com/sagernet/sing-box/adapter"
     8  	"github.com/sagernet/sing-tun"
     9  	E "github.com/sagernet/sing/common/exceptions"
    10  	F "github.com/sagernet/sing/common/format"
    11  	"github.com/sagernet/sing/common/shell"
    12  	"github.com/sagernet/sing/common/x/list"
    13  )
    14  
    15  type systemProxy struct {
    16  	monitor       tun.DefaultInterfaceMonitor
    17  	interfaceName string
    18  	element       *list.Element[tun.DefaultInterfaceUpdateCallback]
    19  	port          uint16
    20  	isMixed       bool
    21  }
    22  
    23  func (p *systemProxy) update(event int) error {
    24  	newInterfaceName := p.monitor.DefaultInterfaceName(netip.IPv4Unspecified())
    25  	if p.interfaceName == newInterfaceName {
    26  		return nil
    27  	}
    28  	if p.interfaceName != "" {
    29  		_ = p.unset()
    30  	}
    31  	p.interfaceName = newInterfaceName
    32  	interfaceDisplayName, err := getInterfaceDisplayName(p.interfaceName)
    33  	if err != nil {
    34  		return err
    35  	}
    36  	if p.isMixed {
    37  		err = shell.Exec("networksetup", "-setsocksfirewallproxy", interfaceDisplayName, "127.0.0.1", F.ToString(p.port)).Attach().Run()
    38  	}
    39  	if err == nil {
    40  		err = shell.Exec("networksetup", "-setwebproxy", interfaceDisplayName, "127.0.0.1", F.ToString(p.port)).Attach().Run()
    41  	}
    42  	if err == nil {
    43  		err = shell.Exec("networksetup", "-setsecurewebproxy", interfaceDisplayName, "127.0.0.1", F.ToString(p.port)).Attach().Run()
    44  	}
    45  	return err
    46  }
    47  
    48  func (p *systemProxy) unset() error {
    49  	interfaceDisplayName, err := getInterfaceDisplayName(p.interfaceName)
    50  	if err != nil {
    51  		return err
    52  	}
    53  	if p.isMixed {
    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  	return err
    63  }
    64  
    65  func getInterfaceDisplayName(name string) (string, error) {
    66  	content, err := shell.Exec("networksetup", "-listallhardwareports").ReadOutput()
    67  	if err != nil {
    68  		return "", err
    69  	}
    70  	for _, deviceSpan := range strings.Split(string(content), "Ethernet Address") {
    71  		if strings.Contains(deviceSpan, "Device: "+name) {
    72  			substr := "Hardware Port: "
    73  			deviceSpan = deviceSpan[strings.Index(deviceSpan, substr)+len(substr):]
    74  			deviceSpan = deviceSpan[:strings.Index(deviceSpan, "\n")]
    75  			return deviceSpan, nil
    76  		}
    77  	}
    78  	return "", E.New(name, " not found in networksetup -listallhardwareports")
    79  }
    80  
    81  func SetSystemProxy(router adapter.Router, port uint16, isMixed bool) (func() error, error) {
    82  	interfaceMonitor := router.InterfaceMonitor()
    83  	if interfaceMonitor == nil {
    84  		return nil, E.New("missing interface monitor")
    85  	}
    86  	proxy := &systemProxy{
    87  		monitor: interfaceMonitor,
    88  		port:    port,
    89  		isMixed: isMixed,
    90  	}
    91  	err := proxy.update(tun.EventInterfaceUpdate)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	proxy.element = interfaceMonitor.RegisterCallback(proxy.update)
    96  	return func() error {
    97  		interfaceMonitor.UnregisterCallback(proxy.element)
    98  		return proxy.unset()
    99  	}, nil
   100  }