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 }