144 lines
3.0 KiB
Go
144 lines
3.0 KiB
Go
//go:build darwin
|
|
|
|
package sysproxy
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"net"
|
|
"os/exec"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
type darwinController struct {
|
|
mu sync.Mutex
|
|
enabled bool
|
|
host string
|
|
httpPort string
|
|
socksPort string
|
|
services []string
|
|
}
|
|
|
|
func newPlatform() Controller { return &darwinController{} }
|
|
|
|
func (c *darwinController) Enable(httpHostPort string) error {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
host, port, err := net.SplitHostPort(httpHostPort)
|
|
if err != nil {
|
|
return fmt.Errorf("sysproxy: bad address %q: %w", httpHostPort, err)
|
|
}
|
|
if host == "" || host == "0.0.0.0" {
|
|
host = "127.0.0.1"
|
|
}
|
|
socks := "1080"
|
|
if port == "1081" {
|
|
socks = "1080"
|
|
}
|
|
|
|
services, err := listNetworkServices()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(services) == 0 {
|
|
return fmt.Errorf("sysproxy: no network services")
|
|
}
|
|
|
|
for _, svc := range services {
|
|
if err := runNetworkSetup("-setwebproxy", svc, host, port); err != nil {
|
|
return err
|
|
}
|
|
if err := runNetworkSetup("-setsecurewebproxy", svc, host, port); err != nil {
|
|
return err
|
|
}
|
|
if err := runNetworkSetup("-setsocksfirewallproxy", svc, host, socks); err != nil {
|
|
return err
|
|
}
|
|
if err := runNetworkSetup("-setwebproxystate", svc, "on"); err != nil {
|
|
return err
|
|
}
|
|
if err := runNetworkSetup("-setsecurewebproxystate", svc, "on"); err != nil {
|
|
return err
|
|
}
|
|
if err := runNetworkSetup("-setsocksfirewallproxystate", svc, "on"); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
c.enabled = true
|
|
c.host = host
|
|
c.httpPort = port
|
|
c.socksPort = socks
|
|
c.services = services
|
|
return nil
|
|
}
|
|
|
|
func (c *darwinController) Disable() error {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
if !c.enabled {
|
|
return nil
|
|
}
|
|
services := c.services
|
|
if len(services) == 0 {
|
|
var err error
|
|
services, err = listNetworkServices()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
var first error
|
|
for _, svc := range services {
|
|
for _, args := range [][]string{
|
|
{"-setwebproxystate", svc, "off"},
|
|
{"-setsecurewebproxystate", svc, "off"},
|
|
{"-setsocksfirewallproxystate", svc, "off"},
|
|
} {
|
|
if err := runNetworkSetup(args...); err != nil && first == nil {
|
|
first = err
|
|
}
|
|
}
|
|
}
|
|
c.enabled = false
|
|
c.services = nil
|
|
return first
|
|
}
|
|
|
|
func (c *darwinController) Enabled() bool {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
return c.enabled
|
|
}
|
|
|
|
func listNetworkServices() ([]string, error) {
|
|
out, err := exec.Command("networksetup", "-listallnetworkservices").Output()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("networksetup: %w", err)
|
|
}
|
|
var services []string
|
|
sc := bufio.NewScanner(bytes.NewReader(out))
|
|
for sc.Scan() {
|
|
line := strings.TrimSpace(sc.Text())
|
|
if line == "" || strings.HasPrefix(line, "An asterisk") {
|
|
continue
|
|
}
|
|
if strings.HasPrefix(line, "*") {
|
|
continue // disabled service
|
|
}
|
|
services = append(services, line)
|
|
}
|
|
return services, sc.Err()
|
|
}
|
|
|
|
func runNetworkSetup(args ...string) error {
|
|
cmd := exec.Command("networksetup", args...)
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return fmt.Errorf("networksetup %v: %w (%s)", args, err, strings.TrimSpace(string(out)))
|
|
}
|
|
return nil
|
|
}
|