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  }