boxdd: Implement linux http proxy
This commit is contained in:
@@ -0,0 +1,394 @@
|
||||
//go:build linux && !android
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/settings"
|
||||
"github.com/sagernet/sing-box/daemon"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-tun"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/shell"
|
||||
)
|
||||
|
||||
type linuxPlatformInterface struct {
|
||||
daemon *Daemon
|
||||
access sync.Mutex
|
||||
ownerUser *user.User
|
||||
systemProxy *settings.LinuxSystemProxy
|
||||
systemProxyEnabled bool
|
||||
}
|
||||
|
||||
func newPlatformInterface(daemonInstance *Daemon) (daemonPlatform, error) {
|
||||
return &linuxPlatformInterface{
|
||||
daemon: daemonInstance,
|
||||
systemProxyEnabled: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) Initialize(networkManager adapter.NetworkManager) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) UsePlatformAutoDetectInterfaceControl() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) AutoDetectInterfaceControl(fd int) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) UsePlatformInterface() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) OpenInterface(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) ProcessPlatformOptions(options option.TunPlatformOptions) error {
|
||||
if options.HTTPProxy == nil || !options.HTTPProxy.Enabled {
|
||||
return nil
|
||||
}
|
||||
httpProxyOptions := options.HTTPProxy
|
||||
systemProxy, err := settings.NewLinuxSystemProxy(
|
||||
M.ParseSocksaddrHostPort(httpProxyOptions.Server, httpProxyOptions.ServerPort),
|
||||
false,
|
||||
p.executeAsOwner,
|
||||
)
|
||||
if err != nil {
|
||||
p.daemon.logger.Warn("initialize system proxy: ", err)
|
||||
return nil
|
||||
}
|
||||
p.access.Lock()
|
||||
if p.systemProxy != nil {
|
||||
p.access.Unlock()
|
||||
return E.New("only one enabled `tun.platform.http_proxy` is supported")
|
||||
}
|
||||
p.systemProxy = systemProxy
|
||||
err = p.applySystemProxyLocked()
|
||||
if err != nil {
|
||||
rollbackError := p.disableSystemProxyLocked()
|
||||
p.systemProxy = nil
|
||||
p.access.Unlock()
|
||||
return E.Errors(E.Cause(err, "set system proxy"), rollbackError)
|
||||
}
|
||||
p.access.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) UsePlatformDefaultInterfaceMonitor() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) UsePlatformNetworkInterfaces() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) NetworkInterfaces() ([]adapter.NetworkInterface, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) UnderNetworkExtension() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) NetworkExtensionIncludeAllNetworks() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) ClearDNSCache() {
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) RequestPermissionForWIFIState() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) ReadWIFIState() adapter.WIFIState {
|
||||
return adapter.WIFIState{}
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) UsePlatformConnectionOwnerFinder() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) FindConnectionOwner(request *adapter.FindConnectionOwnerRequest) (*adapter.ConnectionOwner, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) UsePlatformWIFIMonitor() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) UsePlatformNotification() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) SendNotification(notification *adapter.Notification) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) MyInterfaceAddress() []netip.Addr {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) UsePlatformNeighborResolver() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) StartNeighborMonitor(listener adapter.NeighborUpdateListener) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) CloseNeighborMonitor(listener adapter.NeighborUpdateListener) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) UsePlatformShell() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) CheckPlatformShell() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) OpenShellSession(user *adapter.PlatformUser, command string, environ []string, term string, rows int32, cols int32) (adapter.ShellSession, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) LookupUser(username string) (*adapter.PlatformUser, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) LookupSFTPServer() (string, error) {
|
||||
return "", os.ErrInvalid
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) ReadSystemSSHHostKey() ([]byte, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) TailscaleHostname() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) UsePlatformBridge() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) CreateBridge(options adapter.BridgeOptions) (adapter.BridgeSession, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) PrepareOwner(identity peerIdentity) error {
|
||||
p.access.Lock()
|
||||
defer p.access.Unlock()
|
||||
if listenAddress != "" {
|
||||
return p.applySystemProxyLocked()
|
||||
}
|
||||
if p.ownerUser != nil && p.ownerUser.Uid == identity.UserID {
|
||||
return p.applySystemProxyLocked()
|
||||
}
|
||||
ownerUser, err := user.LookupId(identity.UserID)
|
||||
if err != nil {
|
||||
return E.Cause(err, "lookup owner user")
|
||||
}
|
||||
return p.replaceOwnerLocked(ownerUser)
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) RestoreOwner(state ownerState) error {
|
||||
p.access.Lock()
|
||||
defer p.access.Unlock()
|
||||
if listenAddress != "" {
|
||||
return nil
|
||||
}
|
||||
ownerUser, err := user.LookupId(state.UserID)
|
||||
if err != nil {
|
||||
return E.Cause(err, "lookup owner user")
|
||||
}
|
||||
p.ownerUser = ownerUser
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) ReleaseOwner() error {
|
||||
p.access.Lock()
|
||||
defer p.access.Unlock()
|
||||
err := p.disableSystemProxyLocked()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.ownerUser = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) ResetPlatformOptions() error {
|
||||
p.access.Lock()
|
||||
defer p.access.Unlock()
|
||||
err := p.disableSystemProxyLocked()
|
||||
if err == nil {
|
||||
p.systemProxy = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) SetSystemProxyPreference(enabled bool) {
|
||||
p.access.Lock()
|
||||
p.systemProxyEnabled = enabled
|
||||
p.access.Unlock()
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) SystemProxyStatus() (*daemon.SystemProxyStatus, error) {
|
||||
p.access.Lock()
|
||||
defer p.access.Unlock()
|
||||
available := p.systemProxy != nil
|
||||
return &daemon.SystemProxyStatus{
|
||||
Available: available,
|
||||
Enabled: available && p.systemProxyEnabled,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) SetSystemProxyEnabled(enabled bool) error {
|
||||
p.access.Lock()
|
||||
defer p.access.Unlock()
|
||||
if p.systemProxy == nil {
|
||||
if !enabled {
|
||||
p.systemProxyEnabled = false
|
||||
return nil
|
||||
}
|
||||
return E.New("the system proxy is not available")
|
||||
}
|
||||
previousEnabled := p.systemProxyEnabled
|
||||
p.systemProxyEnabled = enabled
|
||||
err := p.applySystemProxyLocked()
|
||||
if err != nil {
|
||||
p.systemProxyEnabled = previousEnabled
|
||||
rollbackError := p.applySystemProxyLocked()
|
||||
return E.Errors(err, rollbackError)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) HandleSessionChange(eventType uint32, sessionID uint32, state ownerState) (uint32, bool, error) {
|
||||
return 0, false, nil
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) Close() error {
|
||||
p.access.Lock()
|
||||
defer p.access.Unlock()
|
||||
err := p.disableSystemProxyLocked()
|
||||
p.systemProxy = nil
|
||||
p.ownerUser = nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) applySystemProxyLocked() error {
|
||||
if p.systemProxy == nil {
|
||||
return nil
|
||||
}
|
||||
if p.systemProxyEnabled {
|
||||
if p.systemProxy.IsEnabled() || !p.ownerSessionActiveLocked() {
|
||||
return nil
|
||||
}
|
||||
return p.systemProxy.Enable()
|
||||
}
|
||||
return p.disableSystemProxyLocked()
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) disableSystemProxyLocked() error {
|
||||
if p.systemProxy == nil || !p.systemProxy.IsEnabled() || !p.ownerSessionActiveLocked() {
|
||||
return nil
|
||||
}
|
||||
return p.systemProxy.Disable()
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) ownerSessionActiveLocked() bool {
|
||||
if listenAddress != "" {
|
||||
return true
|
||||
}
|
||||
if p.ownerUser == nil {
|
||||
return false
|
||||
}
|
||||
_, err := os.Stat(ownerSessionBusPath(p.ownerUser))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) executeAsOwner(name string, args ...string) error {
|
||||
if listenAddress != "" {
|
||||
return shell.Exec(name, args...).Attach().Run()
|
||||
}
|
||||
ownerUser := p.ownerUser
|
||||
if ownerUser == nil {
|
||||
return E.New("missing owner session")
|
||||
}
|
||||
uid, err := strconv.ParseUint(ownerUser.Uid, 10, 32)
|
||||
if err != nil {
|
||||
return E.Cause(err, "parse owner user ID")
|
||||
}
|
||||
gid, err := strconv.ParseUint(ownerUser.Gid, 10, 32)
|
||||
if err != nil {
|
||||
return E.Cause(err, "parse owner group ID")
|
||||
}
|
||||
searchPath := os.Getenv("PATH")
|
||||
if searchPath == "" {
|
||||
searchPath = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
}
|
||||
command := exec.Command(name, args...)
|
||||
command.SysProcAttr = &syscall.SysProcAttr{
|
||||
Credential: &syscall.Credential{
|
||||
Uid: uint32(uid),
|
||||
Gid: uint32(gid),
|
||||
},
|
||||
}
|
||||
command.Env = []string{
|
||||
"HOME=" + ownerUser.HomeDir,
|
||||
"USER=" + ownerUser.Username,
|
||||
"LOGNAME=" + ownerUser.Username,
|
||||
"PATH=" + searchPath,
|
||||
"XDG_RUNTIME_DIR=" + ownerRuntimeDirectory(ownerUser),
|
||||
"DBUS_SESSION_BUS_ADDRESS=unix:path=" + ownerSessionBusPath(ownerUser),
|
||||
}
|
||||
output, err := command.CombinedOutput()
|
||||
if err != nil {
|
||||
outputText := strings.TrimSpace(string(output))
|
||||
if outputText != "" {
|
||||
return E.Cause(err, "execute (", name, ") ", strings.Join(args, " "), ": ", outputText)
|
||||
}
|
||||
return E.Cause(err, "execute (", name, ") ", strings.Join(args, " "))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *linuxPlatformInterface) replaceOwnerLocked(ownerUser *user.User) error {
|
||||
err := p.disableSystemProxyLocked()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.ownerUser = ownerUser
|
||||
return p.applySystemProxyLocked()
|
||||
}
|
||||
|
||||
func ownerRuntimeDirectory(ownerUser *user.User) string {
|
||||
return "/run/user/" + ownerUser.Uid
|
||||
}
|
||||
|
||||
func ownerSessionBusPath(ownerUser *user.User) string {
|
||||
return ownerRuntimeDirectory(ownerUser) + "/bus"
|
||||
}
|
||||
|
||||
var _ daemonPlatform = (*linuxPlatformInterface)(nil)
|
||||
Reference in New Issue
Block a user