diff --git a/Makefile b/Makefile index f2f6fcd..10ac101 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ endif TAGS=with_gvisor,with_quic,with_wireguard,with_ech,with_utls,with_clash_api,with_grpc IOS_ADD_TAGS=with_dhcp,with_low_memory,with_conntrack GOBUILDLIB=CGO_ENABLED=1 go build -trimpath -tags $(TAGS) -ldflags="-w -s" -buildmode=c-shared -GOBUILDSRV=CGO_ENABLED=1 go build -trimpath +GOBUILDSRV=CGO_ENABLED=1 go build -trimpath -tags $(TAGS) lib_install: go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.1.1 @@ -42,13 +42,13 @@ webui: rm -rf bin/webui mv Yacd-meta-gh-pages bin/webui - +.PHONY: build windows-amd64: curl http://localhost:18020/exit || echo "exited" env GOOS=windows GOARCH=amd64 CC=x86_64-w64-mingw32-gcc $(GOBUILDLIB) -o $(BINDIR)/$(LIBNAME).dll ./custom - go install -mod=readonly github.com/akavel/rsrc@latest + go install -mod=readonly github.com/akavel/rsrc@latest ||echo "rsrc error in installation" cp $(BINDIR)/$(LIBNAME).dll ./$(LIBNAME).dll - $$(go env GOPATH)/bin/rsrc -ico ./assets/hiddify-cli.ico -o ./cli/bydll/cli.syso + $$(go env GOPATH)/bin/rsrc -ico ./assets/hiddify-cli.ico -o ./cli/bydll/cli.syso ||echo "rsrc error in syso" env GOOS=windows GOARCH=amd64 CC=x86_64-w64-mingw32-gcc CGO_LDFLAGS="$(LIBNAME).dll" $(GOBUILDSRV) -o $(BINDIR)/$(CLINAME).exe ./cli/bydll rm ./$(LIBNAME).dll make webui diff --git a/admin_service/service.go b/admin_service/service.go index 4fa5c37..e0b8488 100644 --- a/admin_service/service.go +++ b/admin_service/service.go @@ -17,21 +17,18 @@ type hiddifyNext struct{} var port int = 18020 func (m *hiddifyNext) Start(s service.Service) error { - go m.run() + go StartWebServer(port, false) return nil } func (m *hiddifyNext) Stop(s service.Service) error { err := global.StopServiceC() if err != nil { - return err + return nil } // Stop should not block. Return with a few seconds. // <-time.After(time.Second * 1) return nil } -func (m *hiddifyNext) run() { - StartWebServer(port, false) -} func getCurrentExecutableDirectory() string { executablePath, err := os.Executable() @@ -63,7 +60,7 @@ func StartService(goArg string) (int, string) { return 1, fmt.Sprintf("Error: %v", err) } - if len(goArg) > 0 { + if len(goArg) > 0 && goArg != "run" { return control(s, goArg) } @@ -102,6 +99,7 @@ func control(s service.Service, goArg string) (int, string) { } return 0, "Tunnel Service Already Running." } else if status == service.StatusUnknown { + s.Uninstall() s.Install() status, serr = s.Status() if dolog { @@ -112,6 +110,7 @@ func control(s service.Service, goArg string) (int, string) { err = s.Start() } case "install": + s.Uninstall() err = s.Install() status, serr = s.Status() if dolog { diff --git a/admin_service/web.go b/admin_service/web.go index e14317d..7f0cfec 100644 --- a/admin_service/web.go +++ b/admin_service/web.go @@ -21,6 +21,7 @@ const ( func StartWebServer(Port int, TLS bool) { http.HandleFunc("/start", startHandler) http.HandleFunc("/stop", StopHandler) + http.HandleFunc("/status", StatusHandler) http.HandleFunc("/exit", ExitHandler) server := &http.Server{ Addr: "127.0.0.1:" + fmt.Sprintf("%d", Port), @@ -85,6 +86,8 @@ func startHandler(w http.ResponseWriter, r *http.Request) { } http.Error(w, fmt.Sprintf("Ok"), http.StatusOK) } +func StatusHandler(w http.ResponseWriter, r *http.Request) { +} func StopHandler(w http.ResponseWriter, r *http.Request) { err := global.StopServiceC() if err != nil { diff --git a/config/admin_service_cmd_runner_windows.go b/config/admin_service_cmd_runner_windows.go index 82a5806..d7047e0 100644 --- a/config/admin_service_cmd_runner_windows.go +++ b/config/admin_service_cmd_runner_windows.go @@ -4,6 +4,7 @@ package config import ( "os" + "strings" "syscall" "golang.org/x/sys/windows" @@ -19,20 +20,11 @@ func ExecuteCmd(exe string, background bool, args ...string) (string, error) { verbPtr, _ := syscall.UTF16PtrFromString(verb) exePtr, _ := syscall.UTF16PtrFromString(exe) cwdPtr, _ := syscall.UTF16PtrFromString(cwd) + argPtr, _ := syscall.UTF16PtrFromString(strings.Join(args, " ")) - // Convert args to UTF16Ptr slice - var argsPtr []*uint16 - for _, arg := range args { - argPtr, err := syscall.UTF16PtrFromString(arg) - if err != nil { - return "", err - } - argsPtr = append(argsPtr, argPtr) - } + var showCmd int32 = 0 // SW_NORMAL - var showCmd int32 = 1 // SW_NORMAL - - err = windows.ShellExecute(0, verbPtr, exePtr, nil, cwdPtr, showCmd) + err = windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd) if err != nil { return "", err } diff --git a/config/admin_service_commander.go b/config/admin_service_commander.go index 4f4c68a..2d60353 100644 --- a/config/admin_service_commander.go +++ b/config/admin_service_commander.go @@ -3,13 +3,12 @@ package config import ( "fmt" "io" - "time" - "net/http" "net/url" "os" "path/filepath" "runtime" + "time" "github.com/sagernet/sing-box/option" dns "github.com/sagernet/sing-dns" @@ -21,15 +20,18 @@ const ( stopEndpoint = "/stop" ) +var tunnelServiceRunning = false + func isSupportedOS() bool { return runtime.GOOS == "windows" || runtime.GOOS == "linux" } func ActivateTunnelService(opt ConfigOptions) (bool, error) { + tunnelServiceRunning = true // if !isSupportedOS() { // return false, E.New("Unsupported OS: " + runtime.GOOS) // } - go startTunnelRequest(opt, true) + go startTunnelRequestWithFailover(opt, true) return true, nil } @@ -37,11 +39,25 @@ func DeactivateTunnelService() (bool, error) { // if !isSupportedOS() { // return true, nil // } + if tunnelServiceRunning { + stopTunnelRequest() + } + tunnelServiceRunning = false - go stopTunnelRequest() return true, nil } +func startTunnelRequestWithFailover(opt ConfigOptions, installService bool) { + res, err := startTunnelRequest(opt, installService) + fmt.Printf("Start Tunnel Result: %v\n", res) + if err != nil { + + fmt.Printf("Start Tunnel Failed! Stopping core... err=%v\n", err) + // StopAndAlert(pb.MessageType.MessageType_UNEXPECTED_ERROR, "Start Tunnel Failed! Stopping...") + + } +} + func startTunnelRequest(opt ConfigOptions, installService bool) (bool, error) { params := map[string]interface{}{ "Ipv6": opt.IPv6Mode == option.DomainStrategy(dns.DomainStrategyUseIPv4), @@ -83,9 +99,9 @@ func stopTunnelRequest() (bool, error) { 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) + // 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 false, fmt.Errorf("unexpected Status Code: %d %s. Response Body: %s error:%v", response.StatusCode, response.Status, body, err) } return true, nil diff --git a/config/config.go b/config/config.go index e4a0c67..26eab3f 100644 --- a/config/config.go +++ b/config/config.go @@ -394,13 +394,16 @@ func BuildConfig(opt ConfigOptions, input option.Options) (*option.Options, erro var outbounds []option.Outbound var tags []string OutboundMainProxyTag = OutboundSelectTag - if opt.Warp.EnableWarp && (opt.Warp.Mode == WarpOverProxy || opt.Warp.Mode == ProxyOverWarp) { + //inbound==warp over proxies + //outbound==proxies over warp + if opt.Warp.EnableWarp && (opt.Warp.Mode == "inbound" || opt.Warp.Mode == "outbound") { + out, err := generateWarpSingbox(opt.Warp.WireguardConfig.ToWireguardConfig(), opt.Warp.CleanIP, opt.Warp.CleanPort, opt.Warp.FakePackets, opt.Warp.FakePacketSize, opt.Warp.FakePacketDelay) if err != nil { return nil, fmt.Errorf("failed to generate warp config: %v", err) } out.Tag = "Hiddify Warp ✅" - if opt.Warp.Mode == WarpOverProxy { + if opt.Warp.Mode == "inbound" { out.WireGuardOptions.Detour = OutboundURLTestTag OutboundMainProxyTag = out.Tag } else { @@ -531,7 +534,7 @@ func BuildConfig(opt ConfigOptions, input option.Options) (*option.Options, erro } func patchHiddifyWarpFromConfig(out option.Outbound, opt ConfigOptions) option.Outbound { - if opt.Warp.EnableWarp && opt.Warp.Mode == ProxyOverWarp { + if opt.Warp.EnableWarp && opt.Warp.Mode == "outbound" { if out.DirectOptions.Detour == "" { out.DirectOptions.Detour = "Hiddify Warp ✅" } diff --git a/config/option.go b/config/option.go index f0cbf7b..eb9501f 100644 --- a/config/option.go +++ b/config/option.go @@ -39,7 +39,7 @@ type InboundOptions struct { LocalDnsPort uint16 `json:"local-dns-port"` MTU uint32 `json:"mtu"` StrictRoute bool `json:"strict-route"` - TUNStack string `json:"tun-stack"` + TUNStack string `json:"tun-implementation"` } type URLTestOptions struct { diff --git a/custom/cmd_interface.go b/custom/cmd_interface.go index 50a3b11..e9055a0 100644 --- a/custom/cmd_interface.go +++ b/custom/cmd_interface.go @@ -5,7 +5,6 @@ package main */ import "C" import ( - "fmt" "unsafe" "github.com/hiddify/libcore/cmd" @@ -15,7 +14,7 @@ import ( func parseCli(argc C.int, argv **C.char) *C.char { args := make([]string, argc) for i := 0; i < int(argc); i++ { - fmt.Println("parseCli", C.GoString(*argv)) + // fmt.Println("parseCli", C.GoString(*argv)) args[i] = C.GoString(*argv) argv = (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(argv)) + uintptr(unsafe.Sizeof(*argv)))) } @@ -23,5 +22,5 @@ func parseCli(argc C.int, argv **C.char) *C.char { if err != nil { return C.CString(err.Error()) } - return C.CString("Ok") + return C.CString("") } diff --git a/custom/custom.go b/custom/custom.go index 48c0532..89c7a92 100644 --- a/custom/custom.go +++ b/custom/custom.go @@ -200,7 +200,7 @@ func stop() (CErr *C.char) { stopAndAlert("Unexpected Error in Stop!", err) CErr = C.CString(err.Error()) }) - config.DeactivateTunnelService() + if v2.CoreState != pb.CoreState_STARTED { stopAndAlert("Already Stopped", nil) return C.CString("") @@ -209,6 +209,7 @@ func stop() (CErr *C.char) { return C.CString("instance not found") } propagateStatus(pb.CoreState_STOPPING) + config.DeactivateTunnelService() commandServer.SetService(nil) err := v2.Box.Close() diff --git a/custom/status.go b/custom/status.go index 4b2ec53..573cf55 100644 --- a/custom/status.go +++ b/custom/status.go @@ -54,7 +54,7 @@ func stopAndAlert(alert string, err error) (resultErr error) { msg, _ := json.Marshal(StatusMessage{Status: convert2OldState(v2.CoreState), Alert: &alert, Message: &message}) bridge.SendStringToPort(statusPropagationPort, string(msg)) - config.DeactivateTunnelService() + go config.DeactivateTunnelService() if commandServer != nil { commandServer.SetService(nil) } diff --git a/global/global.go b/global/global.go index 6e767a5..eca4b07 100644 --- a/global/global.go +++ b/global/global.go @@ -163,6 +163,7 @@ func stop() error { if box == nil { return errors.New("instance not found") } + config.DeactivateTunnelService() propagateStatus(Stopping) commandServer.SetService(nil) @@ -292,10 +293,10 @@ func StopServiceC() error { // if status != Started { // return errors.New("instance not started") // } + config.DeactivateTunnelService() if box == nil { return errors.New("instance not found") } - // propagateStatus(Stopping) err := box.Close() // commandServer.SetService(nil) diff --git a/global/standalone.go b/global/standalone.go index 670b3cf..22ad347 100644 --- a/global/standalone.go +++ b/global/standalone.go @@ -18,6 +18,27 @@ import ( "github.com/sagernet/sing-box/option" ) +func RunStandalone(hiddifySettingPath string, configPath string) error { + fmt.Println("Running in standalone mode") + current, err := readAndBuildConfig(hiddifySettingPath, configPath) + if err != nil { + fmt.Printf("Error in read and build config %v", err) + return err + } + + go StartServiceC(false, current.Config) + go updateConfigInterval(current, hiddifySettingPath, configPath) + fmt.Printf("Press CTRL+C to stop\n") + fmt.Printf("Open http://localhost:6756/?secret=hiddify in your browser\n") + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) + + <-sigChan + err = StopServiceC() + + return err +} + type ConfigResult struct { Config string RefreshInterval int @@ -155,22 +176,6 @@ func updateConfigInterval(current ConfigResult, hiddifySettingPath string, confi } } -func RunStandalone(hiddifySettingPath string, configPath string) error { - current, err := readAndBuildConfig(hiddifySettingPath, configPath) - if err != nil { - return err - } - go StartServiceC(false, current.Config) - go updateConfigInterval(current, hiddifySettingPath, configPath) - fmt.Printf("Press CTRL+C to stop\n") - fmt.Printf("Open http://localhost:6756/?secret=hiddify in your browser\n") - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) - - <-sigChan - err = StopServiceC() - return err -} func readConfigBytes(content []byte) (*option.Options, error) { var options option.Options diff --git a/v2/custom.go b/v2/custom.go index 03d7ec0..571c5f6 100644 --- a/v2/custom.go +++ b/v2/custom.go @@ -19,7 +19,7 @@ var configOptions *config.ConfigOptions var activeConfigPath *string var logFactory *log.Factory -func stopAndAlert(msgType pb.MessageType, message string) { +func StopAndAlert(msgType pb.MessageType, message string) { SetCoreStatus(pb.CoreState_STOPPED, msgType, message) config.DeactivateTunnelService() // if commandServer != nil { @@ -37,7 +37,7 @@ func stopAndAlert(msgType pb.MessageType, message string) { func (s *server) Start(ctx context.Context, in *pb.StartRequest) (*pb.CoreInfoResponse, error) { defer config.DeferPanicToError("start", func(err error) { Log(pb.LogLevel_FATAL, pb.LogType_CORE, err.Error()) - stopAndAlert(pb.MessageType_UNEXPECTED_ERROR, err.Error()) + StopAndAlert(pb.MessageType_UNEXPECTED_ERROR, err.Error()) }) if CoreState != pb.CoreState_STOPPED { @@ -112,7 +112,7 @@ func (s *server) StartService(ctx context.Context, in *pb.StartRequest) (*pb.Cor func (s *server) Parse(ctx context.Context, in *pb.ParseRequest) (*pb.ParseResponse, error) { defer config.DeferPanicToError("parse", func(err error) { Log(pb.LogLevel_FATAL, pb.LogType_CONFIG, err.Error()) - stopAndAlert(pb.MessageType_UNEXPECTED_ERROR, err.Error()) + StopAndAlert(pb.MessageType_UNEXPECTED_ERROR, err.Error()) }) config, err := config.ParseConfigContent(in.Content, true) @@ -137,7 +137,7 @@ func (s *server) Parse(ctx context.Context, in *pb.ParseRequest) (*pb.ParseRespo // func (s *server) GenerateConfig(ctx context.Context, in *pb.GenerateConfigRequest) (*pb.GenerateConfigResponse, error) { // defer config.DeferPanicToError("generateConfig", func(err error) { // Log(pb.LogLevel_FATAL, pb.LogType_CONFIG, err.Error()) -// stopAndAlert(pb.MessageType_UNEXPECTED_ERROR, err.Error()) +// StopAndAlert(pb.MessageType_UNEXPECTED_ERROR, err.Error()) // }) // config, err := generateConfigFromFile(C.GoString(path), *configOptions) @@ -151,7 +151,7 @@ func (s *server) Parse(ctx context.Context, in *pb.ParseRequest) (*pb.ParseRespo func (s *server) Stop(ctx context.Context, empty *pb.Empty) (*pb.CoreInfoResponse, error) { defer config.DeferPanicToError("stop", func(err error) { Log(pb.LogLevel_FATAL, pb.LogType_CORE, err.Error()) - stopAndAlert(pb.MessageType_UNEXPECTED_ERROR, err.Error()) + StopAndAlert(pb.MessageType_UNEXPECTED_ERROR, err.Error()) }) config.DeactivateTunnelService() if CoreState != pb.CoreState_STARTED { @@ -199,7 +199,7 @@ func (s *server) Stop(ctx context.Context, empty *pb.Empty) (*pb.CoreInfoRespons func (s *server) Restart(ctx context.Context, in *pb.StartRequest) (*pb.CoreInfoResponse, error) { defer config.DeferPanicToError("restart", func(err error) { Log(pb.LogLevel_FATAL, pb.LogType_CORE, err.Error()) - stopAndAlert(pb.MessageType_UNEXPECTED_ERROR, err.Error()) + StopAndAlert(pb.MessageType_UNEXPECTED_ERROR, err.Error()) }) log.Debug("[Service] Restarting")