new: move running admin service to core
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,3 +5,5 @@
|
||||
|
||||
**/*.log
|
||||
.DS_Store
|
||||
|
||||
**/*.syso
|
||||
Binary file not shown.
4
cmd.bat
Normal file
4
cmd.bat
Normal file
@@ -0,0 +1,4 @@
|
||||
@echo off
|
||||
set TAGS=with_gvisor,with_quic,with_wireguard,with_ech,with_utls,with_clash_api,with_grpc
|
||||
@REM set TAGS=with_dhcp,with_low_memory,with_conntrack
|
||||
go run --tags %TAGS% ./cmd %*
|
||||
38
config/admin_service_cmd_runner.go
Normal file
38
config/admin_service_cmd_runner.go
Normal file
@@ -0,0 +1,38 @@
|
||||
//go:build !windows
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func ExecuteCmd(executablePath, args string) (string, error) {
|
||||
err := execCmdImp([]string{"gksu", executablePath, args})
|
||||
if err == nil {
|
||||
return "Ok", nil
|
||||
}
|
||||
err := execCmdImp([]string{"pkexec", executablePath, args})
|
||||
if err == nil {
|
||||
return "Ok", nil
|
||||
}
|
||||
err := execCmdImp([]string{"/bin/sh", "-c", "sudo " + executablePath + " " + args})
|
||||
if err == nil {
|
||||
return "Ok", nil
|
||||
}
|
||||
return "", err
|
||||
|
||||
}
|
||||
|
||||
func execCmdImp(cmd []string) error {
|
||||
cmd := exec.Command(cmd[0], cmd[1:])
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
fmt.Printf("Running command: %v", cmd.String())
|
||||
if err := cmd.Run(); err != nil {
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
28
config/admin_service_cmd_runner_windows.go
Normal file
28
config/admin_service_cmd_runner_windows.go
Normal file
@@ -0,0 +1,28 @@
|
||||
//go:build windows
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func ExecuteCmd(exe string, args string) (string, error) {
|
||||
verb := "runas"
|
||||
cwd, _ := os.Getwd()
|
||||
|
||||
verbPtr, _ := syscall.UTF16PtrFromString(verb)
|
||||
exePtr, _ := syscall.UTF16PtrFromString(exe)
|
||||
cwdPtr, _ := syscall.UTF16PtrFromString(cwd)
|
||||
argPtr, _ := syscall.UTF16PtrFromString(args)
|
||||
|
||||
var showCmd int32 = 1 //SW_NORMAL
|
||||
|
||||
err := windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
116
config/admin_service_commander.go
Normal file
116
config/admin_service_commander.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/sagernet/sing-box/option"
|
||||
dns "github.com/sagernet/sing-dns"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
const (
|
||||
serviceURL = "http://localhost:18020"
|
||||
startEndpoint = "/start"
|
||||
stopEndpoint = "/stop"
|
||||
)
|
||||
|
||||
func isSupportedOS() bool {
|
||||
return runtime.GOOS == "windows" || runtime.GOOS == "linux"
|
||||
}
|
||||
func ActivateTunnelService(opt ConfigOptions) (bool, error) {
|
||||
if !isSupportedOS() {
|
||||
return false, E.New("Unsupported OS: " + runtime.GOOS)
|
||||
}
|
||||
|
||||
go startTunnelRequest(opt, true)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func DeactivateTunnelService() (bool, error) {
|
||||
if !isSupportedOS() {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
go stopTunnelRequest()
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func startTunnelRequest(opt ConfigOptions, installService bool) (bool, error) {
|
||||
params := map[string]interface{}{
|
||||
"Ipv6": opt.IPv6Mode == option.DomainStrategy(dns.DomainStrategyUseIPv4),
|
||||
"ServerPort": opt.InboundOptions.MixedPort,
|
||||
"StrictRoute": opt.InboundOptions.StrictRoute,
|
||||
"EndpointIndependentNat": true,
|
||||
"Stack": opt.InboundOptions.TUNStack,
|
||||
}
|
||||
|
||||
values := url.Values{}
|
||||
for key, value := range params {
|
||||
values.Add(key, fmt.Sprint(value))
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s%s?%s", serviceURL, startEndpoint, values.Encode())
|
||||
fmt.Printf("URL: %s\n", url)
|
||||
response, err := http.Get(url)
|
||||
if err != nil {
|
||||
if installService {
|
||||
return runTunnelService(opt)
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
body, err := io.ReadAll(response.Body)
|
||||
fmt.Printf("Response Code: %d %s. Response Body: %s Error:%v\n", response.StatusCode, response.Status, body, err)
|
||||
if err != nil || response.StatusCode != http.StatusOK {
|
||||
return false, fmt.Errorf("Unexpected Status Code: %d %s. Response Body: %s error:%v", response.StatusCode, response.Status, body, err)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func stopTunnelRequest() (bool, error) {
|
||||
response, err := http.Get(serviceURL + stopEndpoint)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("HTTP Request Error: %v", err)
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(response.Body)
|
||||
fmt.Printf("Response Code: %d %s. Response Body: %s Error:%v\n", response.StatusCode, response.Status, body, err)
|
||||
if err != nil || response.StatusCode != http.StatusOK {
|
||||
return false, fmt.Errorf("Unexpected Status Code: %d %s. Response Body: %s error:%v", response.StatusCode, response.Status, body, err)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func runTunnelService(opt ConfigOptions) (bool, error) {
|
||||
executablePath := getTunnelServicePath()
|
||||
|
||||
out, err := ExecuteCmd(executablePath, "install")
|
||||
fmt.Println("Shell command executed:", out, err)
|
||||
return startTunnelRequest(opt, false)
|
||||
}
|
||||
|
||||
func getTunnelServicePath() string {
|
||||
var fullPath string
|
||||
binFolder := filepath.Dir(os.Args[0])
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
fullPath = "HiddifyService.exe"
|
||||
case "darwin":
|
||||
fallthrough
|
||||
default:
|
||||
fullPath = "HiddifyService"
|
||||
}
|
||||
|
||||
return filepath.Join(binFolder, fullPath)
|
||||
}
|
||||
@@ -135,40 +135,42 @@ func BuildConfig(opt ConfigOptions, input option.Options) (*option.Options, erro
|
||||
}
|
||||
|
||||
if opt.EnableTun {
|
||||
tunInbound := option.Inbound{
|
||||
Type: C.TypeTun,
|
||||
Tag: InboundTUNTag,
|
||||
TunOptions: option.TunInboundOptions{
|
||||
Stack: opt.TUNStack,
|
||||
MTU: opt.MTU,
|
||||
AutoRoute: true,
|
||||
StrictRoute: opt.StrictRoute,
|
||||
EndpointIndependentNat: true,
|
||||
InboundOptions: option.InboundOptions{
|
||||
SniffEnabled: true,
|
||||
SniffOverrideDestination: true,
|
||||
DomainStrategy: inboundDomainStrategy,
|
||||
if ok, _ := ActivateTunnelService(opt); !ok {
|
||||
tunInbound := option.Inbound{
|
||||
Type: C.TypeTun,
|
||||
Tag: InboundTUNTag,
|
||||
TunOptions: option.TunInboundOptions{
|
||||
Stack: opt.TUNStack,
|
||||
MTU: opt.MTU,
|
||||
AutoRoute: true,
|
||||
StrictRoute: opt.StrictRoute,
|
||||
EndpointIndependentNat: true,
|
||||
InboundOptions: option.InboundOptions{
|
||||
SniffEnabled: true,
|
||||
SniffOverrideDestination: true,
|
||||
DomainStrategy: inboundDomainStrategy,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
switch opt.IPv6Mode {
|
||||
case option.DomainStrategy(dns.DomainStrategyUseIPv4):
|
||||
tunInbound.TunOptions.Inet4Address = []netip.Prefix{
|
||||
netip.MustParsePrefix("172.19.0.1/28"),
|
||||
}
|
||||
case option.DomainStrategy(dns.DomainStrategyUseIPv6):
|
||||
tunInbound.TunOptions.Inet6Address = []netip.Prefix{
|
||||
netip.MustParsePrefix("fdfe:dcba:9876::1/126"),
|
||||
}
|
||||
default:
|
||||
tunInbound.TunOptions.Inet4Address = []netip.Prefix{
|
||||
netip.MustParsePrefix("172.19.0.1/28"),
|
||||
}
|
||||
tunInbound.TunOptions.Inet6Address = []netip.Prefix{
|
||||
netip.MustParsePrefix("fdfe:dcba:9876::1/126"),
|
||||
}
|
||||
}
|
||||
options.Inbounds = append(options.Inbounds, tunInbound)
|
||||
}
|
||||
switch opt.IPv6Mode {
|
||||
case option.DomainStrategy(dns.DomainStrategyUseIPv4):
|
||||
tunInbound.TunOptions.Inet4Address = []netip.Prefix{
|
||||
netip.MustParsePrefix("172.19.0.1/28"),
|
||||
}
|
||||
case option.DomainStrategy(dns.DomainStrategyUseIPv6):
|
||||
tunInbound.TunOptions.Inet6Address = []netip.Prefix{
|
||||
netip.MustParsePrefix("fdfe:dcba:9876::1/126"),
|
||||
}
|
||||
default:
|
||||
tunInbound.TunOptions.Inet4Address = []netip.Prefix{
|
||||
netip.MustParsePrefix("172.19.0.1/28"),
|
||||
}
|
||||
tunInbound.TunOptions.Inet6Address = []netip.Prefix{
|
||||
netip.MustParsePrefix("fdfe:dcba:9876::1/126"),
|
||||
}
|
||||
}
|
||||
options.Inbounds = append(options.Inbounds, tunInbound)
|
||||
}
|
||||
|
||||
options.Inbounds = append(
|
||||
|
||||
@@ -16,7 +16,7 @@ func SaveCurrentConfig(path string, options option.Options) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p, err := filepath.Abs(filepath.Join(path, "current-config.json"))
|
||||
p, err := filepath.Abs(path)
|
||||
fmt.Printf("Saving config to %v %+v\n", p, err)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -156,7 +156,7 @@ func startService(delayStart bool) error {
|
||||
return fmt.Errorf("error building config: %w", err)
|
||||
}
|
||||
|
||||
config.SaveCurrentConfig(sWorkingPath, *patchedOptions)
|
||||
config.SaveCurrentConfig(filepath.Join(sWorkingPath, "current-config.json"), *patchedOptions)
|
||||
|
||||
err = startCommandServer(*logFactory)
|
||||
if err != nil {
|
||||
@@ -188,6 +188,7 @@ func stop() (CErr *C.char) {
|
||||
defer config.DeferPanicToError("stop", func(err error) {
|
||||
CErr = C.CString(err.Error())
|
||||
})
|
||||
config.DeactivateTunnelService()
|
||||
|
||||
if status != Started {
|
||||
return C.CString("")
|
||||
|
||||
@@ -122,7 +122,7 @@ func startService(delayStart bool) error {
|
||||
return fmt.Errorf("error building config: %w", err)
|
||||
}
|
||||
|
||||
config.SaveCurrentConfig(sWorkingPath, *patchedOptions)
|
||||
config.SaveCurrentConfig(filepath.Join(sWorkingPath, "tunnel-current-config.json"), *patchedOptions)
|
||||
|
||||
err = startCommandServer(*logFactory)
|
||||
if err != nil {
|
||||
@@ -248,11 +248,11 @@ func StartServiceC(delayStart bool, content string) error {
|
||||
|
||||
// options = *patchedOptions
|
||||
|
||||
err = config.SaveCurrentConfig(sWorkingPath, options)
|
||||
if err != nil {
|
||||
fmt.Printf("Error in saving config: %v\n", err)
|
||||
return err
|
||||
}
|
||||
// config.SaveCurrentConfig(filepath.Join(sWorkingPath, "custom-current-config.json"), options)
|
||||
// if err != nil {
|
||||
// fmt.Printf("Error in saving config: %v\n", err)
|
||||
// return err
|
||||
// }
|
||||
|
||||
// err = startCommandServer(*logFactory)
|
||||
// if err != nil {
|
||||
|
||||
1
go.mod
1
go.mod
@@ -18,6 +18,7 @@ require (
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/akavel/rsrc v0.10.2 // indirect
|
||||
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||
github.com/caddyserver/certmagic v0.20.0 // indirect
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
|
||||
2
go.sum
2
go.sum
@@ -2,6 +2,8 @@ berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw=
|
||||
berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/akavel/rsrc v0.10.2 h1:Zxm8V5eI1hW4gGaYsJQUhxpjkENuG91ki8B4zCrvEsw=
|
||||
github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
||||
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
||||
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc=
|
||||
|
||||
Reference in New Issue
Block a user