new: move running admin service to core

This commit is contained in:
Hiddify
2024-02-05 18:47:26 +01:00
parent 1a31b04e29
commit d96279386a
12 changed files with 234 additions and 40 deletions

2
.gitignore vendored
View File

@@ -5,3 +5,5 @@
**/*.log
.DS_Store
**/*.syso

Binary file not shown.

4
cmd.bat Normal file
View 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 %*

View 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
}

View 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
}

View 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)
}

View File

@@ -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(

View File

@@ -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

View File

@@ -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("")

View File

@@ -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
View File

@@ -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
View File

@@ -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=