github.com/sagernet/sing-box@v1.9.0-rc.20/common/settings/proxy_linux.go (about)

     1  //go:build linux && !android
     2  
     3  package settings
     4  
     5  import (
     6  	"context"
     7  	"os"
     8  	"os/exec"
     9  	"strings"
    10  
    11  	"github.com/sagernet/sing/common"
    12  	E "github.com/sagernet/sing/common/exceptions"
    13  	F "github.com/sagernet/sing/common/format"
    14  	M "github.com/sagernet/sing/common/metadata"
    15  	"github.com/sagernet/sing/common/shell"
    16  )
    17  
    18  type LinuxSystemProxy struct {
    19  	hasGSettings     bool
    20  	hasKWriteConfig5 bool
    21  	sudoUser         string
    22  	serverAddr       M.Socksaddr
    23  	supportSOCKS     bool
    24  	isEnabled        bool
    25  }
    26  
    27  func NewSystemProxy(ctx context.Context, serverAddr M.Socksaddr, supportSOCKS bool) (*LinuxSystemProxy, error) {
    28  	hasGSettings := common.Error(exec.LookPath("gsettings")) == nil
    29  	hasKWriteConfig5 := common.Error(exec.LookPath("kwriteconfig5")) == nil
    30  	var sudoUser string
    31  	if os.Getuid() == 0 {
    32  		sudoUser = os.Getenv("SUDO_USER")
    33  	}
    34  	if !hasGSettings && !hasKWriteConfig5 {
    35  		return nil, E.New("unsupported desktop environment")
    36  	}
    37  	return &LinuxSystemProxy{
    38  		hasGSettings:     hasGSettings,
    39  		hasKWriteConfig5: hasKWriteConfig5,
    40  		sudoUser:         sudoUser,
    41  		serverAddr:       serverAddr,
    42  		supportSOCKS:     supportSOCKS,
    43  	}, nil
    44  }
    45  
    46  func (p *LinuxSystemProxy) IsEnabled() bool {
    47  	return p.isEnabled
    48  }
    49  
    50  func (p *LinuxSystemProxy) Enable() error {
    51  	if p.hasGSettings {
    52  		err := p.runAsUser("gsettings", "set", "org.gnome.system.proxy.http", "enabled", "true")
    53  		if err != nil {
    54  			return err
    55  		}
    56  		if p.supportSOCKS {
    57  			err = p.setGnomeProxy("ftp", "http", "https", "socks")
    58  		} else {
    59  			err = p.setGnomeProxy("http", "https")
    60  		}
    61  		if err != nil {
    62  			return err
    63  		}
    64  		err = p.runAsUser("gsettings", "set", "org.gnome.system.proxy", "use-same-proxy", F.ToString(p.supportSOCKS))
    65  		if err != nil {
    66  			return err
    67  		}
    68  		err = p.runAsUser("gsettings", "set", "org.gnome.system.proxy", "mode", "manual")
    69  		if err != nil {
    70  			return err
    71  		}
    72  	}
    73  	if p.hasKWriteConfig5 {
    74  		err := p.runAsUser("kwriteconfig5", "--file", "kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "1")
    75  		if err != nil {
    76  			return err
    77  		}
    78  		if p.supportSOCKS {
    79  			err = p.setKDEProxy("ftp", "http", "https", "socks")
    80  		} else {
    81  			err = p.setKDEProxy("http", "https")
    82  		}
    83  		if err != nil {
    84  			return err
    85  		}
    86  		err = p.runAsUser("kwriteconfig5", "--file", "kioslaverc", "--group", "Proxy Settings", "--key", "Authmode", "0")
    87  		if err != nil {
    88  			return err
    89  		}
    90  		err = p.runAsUser("dbus-send", "--type=signal", "/KIO/Scheduler", "org.kde.KIO.Scheduler.reparseSlaveConfiguration", "string:''")
    91  		if err != nil {
    92  			return err
    93  		}
    94  	}
    95  	p.isEnabled = true
    96  	return nil
    97  }
    98  
    99  func (p *LinuxSystemProxy) Disable() error {
   100  	if p.hasGSettings {
   101  		err := p.runAsUser("gsettings", "set", "org.gnome.system.proxy", "mode", "none")
   102  		if err != nil {
   103  			return err
   104  		}
   105  	}
   106  	if p.hasKWriteConfig5 {
   107  		err := p.runAsUser("kwriteconfig5", "--file", "kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "0")
   108  		if err != nil {
   109  			return err
   110  		}
   111  		err = p.runAsUser("dbus-send", "--type=signal", "/KIO/Scheduler", "org.kde.KIO.Scheduler.reparseSlaveConfiguration", "string:''")
   112  		if err != nil {
   113  			return err
   114  		}
   115  	}
   116  	p.isEnabled = false
   117  	return nil
   118  }
   119  
   120  func (p *LinuxSystemProxy) runAsUser(name string, args ...string) error {
   121  	if os.Getuid() != 0 {
   122  		return shell.Exec(name, args...).Attach().Run()
   123  	} else if p.sudoUser != "" {
   124  		return shell.Exec("su", "-", p.sudoUser, "-c", F.ToString(name, " ", strings.Join(args, " "))).Attach().Run()
   125  	} else {
   126  		return E.New("set system proxy: unable to set as root")
   127  	}
   128  }
   129  
   130  func (p *LinuxSystemProxy) setGnomeProxy(proxyTypes ...string) error {
   131  	for _, proxyType := range proxyTypes {
   132  		err := p.runAsUser("gsettings", "set", "org.gnome.system.proxy."+proxyType, "host", p.serverAddr.AddrString())
   133  		if err != nil {
   134  			return err
   135  		}
   136  		err = p.runAsUser("gsettings", "set", "org.gnome.system.proxy."+proxyType, "port", F.ToString(p.serverAddr.Port))
   137  		if err != nil {
   138  			return err
   139  		}
   140  	}
   141  	return nil
   142  }
   143  
   144  func (p *LinuxSystemProxy) setKDEProxy(proxyTypes ...string) error {
   145  	for _, proxyType := range proxyTypes {
   146  		var proxyUrl string
   147  		if proxyType == "socks" {
   148  			proxyUrl = "socks://" + p.serverAddr.String()
   149  		} else {
   150  			proxyUrl = "http://" + p.serverAddr.String()
   151  		}
   152  		err := p.runAsUser(
   153  			"kwriteconfig5",
   154  			"--file",
   155  			"kioslaverc",
   156  			"--group",
   157  			"Proxy Settings",
   158  			"--key", proxyType+"Proxy",
   159  			proxyUrl,
   160  		)
   161  		if err != nil {
   162  			return err
   163  		}
   164  	}
   165  	return nil
   166  }