github.com/inazumav/sing-box@v0.0.0-20230926072359-ab51429a14f1/common/settings/proxy_darwin.go (about)

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