diff --git a/Makefile b/Makefile index 3ff0fd7..3199f88 100644 --- a/Makefile +++ b/Makefile @@ -34,14 +34,20 @@ ios: lib_install windows-amd64: env GOOS=windows GOARCH=amd64 CC=x86_64-w64-mingw32-gcc $(GOBUILD) -o $(BINDIR)/$(NAME).dll ./custom + rsrc -manifest admin_service\cmd\admin_service.manifest -ico ..\assets\images\tray_icon_connected.ico -o admin_service\cmd\admin_service.syso + env GOOS=windows GOARCH=amd64 CC=x86_64-w64-mingw32-gcc $(GOBUILD) -o $(BINDIR)/hiddify-service.exe ./admin_service/cmd linux-amd64: env GOOS=linux GOARCH=amd64 $(GOBUILD) -o $(BINDIR)/$(NAME).so ./custom + env GOOS=linux GOARCH=amd64 $(GOBUILD) -o $(BINDIR)/hiddify-service ./admin_service/cmd + chmod +x $(BINDIR)/hiddify-service macos-amd64: env GOOS=darwin GOARCH=amd64 CGO_CFLAGS="-mmacosx-version-min=10.11" CGO_LDFLAGS="-mmacosx-version-min=10.11" CGO_ENABLED=1 go build -trimpath -tags $(TAGS),$(IOS_TAGS) -buildmode=c-shared -o $(BINDIR)/$(NAME).dylib ./custom + env GOOS=darwin GOARCH=amd64 CGO_CFLAGS="-mmacosx-version-min=10.11" CGO_LDFLAGS="-mmacosx-version-min=10.11" CGO_ENABLED=1 go build -trimpath -tags $(TAGS),$(IOS_TAGS) -o $(BINDIR)/hiddify-service ./admin_service/cmd macos-arm64: env GOOS=darwin GOARCH=arm64 CGO_CFLAGS="-mmacosx-version-min=10.11" CGO_LDFLAGS="-mmacosx-version-min=10.11" CGO_ENABLED=1 go build -trimpath -tags $(TAGS),$(IOS_TAGS) -buildmode=c-shared -o $(BINDIR)/$(NAME).dylib ./custom + macos-universal: macos-amd64 macos-arm64 lipo -create $(BINDIR)/$(BASENAME)-macos-amd64.dylib $(BINDIR)/$(BASENAME)-macos-arm64.dylib -output $(BINDIR)/$(NAME).dylib @@ -69,3 +75,5 @@ release: # Create a new tag for release. git tag v$${TAG} && \ git push -u origin HEAD --tags && \ echo "Github Actions will detect the new tag and release the new version."' + + diff --git a/admin_service_cmd/admin_service.manifest b/admin_service/cmd/admin_service.manifest similarity index 100% rename from admin_service_cmd/admin_service.manifest rename to admin_service/cmd/admin_service.manifest diff --git a/admin_service_cmd/admin_service.syso b/admin_service/cmd/admin_service.syso similarity index 100% rename from admin_service_cmd/admin_service.syso rename to admin_service/cmd/admin_service.syso diff --git a/admin_service_cmd/main.go b/admin_service/cmd/main.go similarity index 53% rename from admin_service_cmd/main.go rename to admin_service/cmd/main.go index 45bae49..7df10fa 100644 --- a/admin_service_cmd/main.go +++ b/admin_service/cmd/main.go @@ -6,12 +6,16 @@ package main #include // Import the function from the DLL -extern void AdminServiceStart(char *arg); +char* AdminServiceStart(const char* arg); + */ import "C" import ( + "fmt" "os" + "strings" + "unsafe" ) func main() { @@ -29,5 +33,24 @@ func main() { // defer C.free(unsafe.Pointer(arg)) // Call AdminServiceStart with the C string - C.AdminServiceStart(arg) + + result := C.AdminServiceStart(arg) + goRes := C.GoString(result) + defer C.free(unsafe.Pointer(result)) + + parts := strings.SplitN(goRes, " ", 2) + + var parsedExitCode int + _, err := fmt.Sscanf(parts[0], "%d", &parsedExitCode) + parsedOutMessage := parts[1] + if err != nil { + fmt.Println("Error parsing the string:", err) + return + } + fmt.Printf("%d %s", parsedExitCode, parsedOutMessage) + + if parsedExitCode != 0 { + os.Exit(int(parsedExitCode)) + } + } diff --git a/admin_service/service.go b/admin_service/service.go index 341e908..f24712f 100644 --- a/admin_service/service.go +++ b/admin_service/service.go @@ -1,7 +1,10 @@ package admin_service import ( + "fmt" "log" + "os" + "path/filepath" "github.com/hiddify/libcore/global" "github.com/kardianos/service" @@ -18,7 +21,7 @@ func (m *hiddifyNext) Start(s service.Service) error { return nil } func (m *hiddifyNext) Stop(s service.Service) error { - err := global.StopService() + err := global.StopServiceC() if err != nil { return err } @@ -30,45 +33,115 @@ func (m *hiddifyNext) run() { StartWebServer(port, false) } -func StartService(goArg string) { +func getCurrentExecutableDirectory() string { + executablePath, err := os.Executable() + if err != nil { + return "" + } + + // Extract the directory (folder) containing the executable + executableDirectory := filepath.Dir(executablePath) + + return executableDirectory +} +func StartService(goArg string) (int, string) { svcConfig := &service.Config{ Name: "Hiddify Tunnel Service", DisplayName: "Hiddify Tunnel Service", Description: "This is a bridge for tunnel", + Option: map[string]interface{}{ + "RunAtLoad": true, + "WorkingDirectory": getCurrentExecutableDirectory(), + }, } prg := &hiddifyNext{} s, err := service.New(prg, svcConfig) if err != nil { - log.Fatal(err) + // log.Printf("Error: %v", err) + return 1, fmt.Sprintf("Error: %v", err) } - if len(goArg) > 0 { - if goArg == "uninstall" { - err = s.Stop() - if err != nil { - log.Fatal(err) - } - } - err = service.Control(s, goArg) - if err != nil { - log.Fatal(err) - } - if goArg == "install" { - err = s.Start() - if err != nil { - log.Fatal(err) - } - } - return + if len(goArg) > 0 { + return control(s, goArg) } logger, err = s.Logger(nil) if err != nil { - log.Fatal(err) + log.Printf("Error: %v", err) } err = s.Run() if err != nil { logger.Error(err) + return 3, fmt.Sprintf("Error: %v", err) } + return 0, "" +} + +func control(s service.Service, goArg string) (int, string) { + dolog := false + var err error + status, serr := s.Status() + if dolog { + fmt.Printf("Current Status: %+v %+v!\n", status, serr) + } + switch goArg { + case "uninstall": + if status == service.StatusRunning { + s.Stop() + } + if dolog { + fmt.Printf("Tunnel Service Uninstalled Successfully.\n") + } + err = s.Uninstall() + case "start": + if status == service.StatusRunning { + if dolog { + fmt.Printf("Tunnel Service Already Running.\n") + } + return 0, "Tunnel Service Already Running." + } else if status == service.StatusUnknown { + s.Install() + status, serr = s.Status() + if dolog { + fmt.Printf("Check status again: %+v %+v!", status, serr) + } + } + if status != service.StatusRunning { + err = s.Start() + } + case "install": + err = s.Install() + status, serr = s.Status() + if dolog { + fmt.Printf("Check Status Again: %+v %+v", status, serr) + } + if status != service.StatusRunning { + err = s.Start() + } + case "stop": + if status == service.StatusStopped { + if dolog { + fmt.Printf("Tunnel Service Already Stopped.\n") + } + return 0, "Tunnel Service Already Stopped." + } + err = s.Stop() + default: + err = service.Control(s, goArg) + } + if err == nil { + out := fmt.Sprintf("Tunnel Service %sed Successfully.", goArg) + if dolog { + fmt.Printf(out) + } + return 0, out + } else { + out := fmt.Sprintf("Error: %v", err) + if dolog { + log.Printf(out) + } + return 2, out + } + } diff --git a/admin_service/web.go b/admin_service/web.go index 835e9c6..dcce7e6 100644 --- a/admin_service/web.go +++ b/admin_service/web.go @@ -65,11 +65,11 @@ func startHandler(w http.ResponseWriter, r *http.Request) { parameters := global.Parameters{Ipv6: ipv6, ServerPort: serverPort, StrictRoute: strictRoute, EndpointIndependentNat: endpointIndependentNat, Stack: theStack} - err = global.WriteParameters(parameters.Ipv6, parameters.ServerPort, parameters.StrictRoute, parameters.EndpointIndependentNat, GetStringFromStack(parameters.Stack)) - if err != nil { - http.Error(w, fmt.Sprintf("Error: %v", err), http.StatusBadRequest) - return - } + // err = global.WriteParameters(parameters.Ipv6, parameters.ServerPort, parameters.StrictRoute, parameters.EndpointIndependentNat, GetStringFromStack(parameters.Stack)) + // if err != nil { + // http.Error(w, fmt.Sprintf("Error: %v", err), http.StatusBadRequest) + // return + // } err = global.SetupC("./", "./", "./tmp", false) if err != nil { @@ -84,11 +84,12 @@ func startHandler(w http.ResponseWriter, r *http.Request) { http.Error(w, fmt.Sprintf("Ok"), http.StatusOK) } func StopHandler(w http.ResponseWriter, r *http.Request) { - err := global.StopService() + err := global.StopServiceC() if err != nil { http.Error(w, fmt.Sprintf("Error: %v", err), http.StatusBadRequest) return } + http.Error(w, fmt.Sprintf("Ok"), http.StatusOK) } func GetStack(stack string) global.Stack { diff --git a/build_windows.bat b/build_windows.bat index 26c0a0f..b4d76ed 100644 --- a/build_windows.bat +++ b/build_windows.bat @@ -5,6 +5,5 @@ set CC=x86_64-w64-mingw32-gcc set CGO_ENABLED=1 go build -trimpath -tags with_gvisor,with_quic,with_wireguard,with_ech,with_utls,with_clash_api,with_grpc -ldflags="-w -s" -buildmode=c-shared -o bin/libcore.dll ./custom -rsrc -manifest admin_service_cmd\admin_service.manifest -ico ..\assets\images\tray_icon_connected.ico -o admin_service_cmd\admin_service.syso -go build -o bin/hiddify-service.exe ./admin_service_cmd -@REM copy .\admin_service_cmd\admin_service.manifest bin\hiddify-service.exe.manifest +rsrc -manifest admin_service\cmd\admin_service.manifest -ico ..\assets\images\tray_icon_connected.ico -o admin_service\cmd\admin_service.syso +go build -o bin/hiddify-service.exe ./admin_service/cmd/ diff --git a/cmd/cmd_admin_service.go b/cmd/cmd_admin_service.go index 7180974..f295332 100644 --- a/cmd/cmd_admin_service.go +++ b/cmd/cmd_admin_service.go @@ -1,6 +1,8 @@ package main import ( + "fmt" + "github.com/hiddify/libcore/admin_service" "github.com/spf13/cobra" @@ -11,9 +13,12 @@ var commandService = &cobra.Command{ Short: "Sign box service start/stop/install/uninstall", Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - if len(args) < 2 { - admin_service.StartService("") + arg := "" + if len(args) > 1 { + arg = args[1] } - admin_service.StartService(args[1]) + code, out := admin_service.StartService(arg) + fmt.Printf("exitCode:%d msg=%s", code, out) + }, } diff --git a/cmd/cmd_run.go b/cmd/cmd_run.go index 678824d..21f6833 100644 --- a/cmd/cmd_run.go +++ b/cmd/cmd_run.go @@ -2,7 +2,9 @@ package main import ( "fmt" - "time" + "os" + "os/signal" + "syscall" "github.com/hiddify/libcore/config" "github.com/hiddify/libcore/global" @@ -53,6 +55,11 @@ func runSingbox(configPath string) error { } go global.StartServiceC(false, configStr) fmt.Printf("Waiting for 30 seconds\n") - <-time.After(time.Second * 30) + // <-time.After(time.Second * 30) + + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) + <-sigChan + return err } diff --git a/config/config.go b/config/config.go index 438ee74..883055b 100644 --- a/config/config.go +++ b/config/config.go @@ -7,6 +7,7 @@ import ( "net" "net/netip" "net/url" + "runtime" "strings" C "github.com/sagernet/sing-box/constant" @@ -118,40 +119,42 @@ func BuildConfig(configOpt ConfigOptions, input option.Options) (*option.Options } if configOpt.EnableTun { - tunInbound := option.Inbound{ - Type: C.TypeTun, - Tag: "tun-in", - TunOptions: option.TunInboundOptions{ - Stack: configOpt.TUNStack, - MTU: configOpt.MTU, - AutoRoute: true, - StrictRoute: configOpt.StrictRoute, - EndpointIndependentNat: true, - InboundOptions: option.InboundOptions{ - SniffEnabled: true, - SniffOverrideDestination: true, - DomainStrategy: inboundDomainStrategy, + if runtime.GOOS != "windows" && runtime.GOOS != "darwin" && runtime.GOOS != "linux" { + tunInbound := option.Inbound{ + Type: C.TypeTun, + Tag: "tun-in", + TunOptions: option.TunInboundOptions{ + Stack: configOpt.TUNStack, + MTU: configOpt.MTU, + AutoRoute: true, + StrictRoute: configOpt.StrictRoute, + EndpointIndependentNat: true, + InboundOptions: option.InboundOptions{ + SniffEnabled: true, + SniffOverrideDestination: true, + DomainStrategy: inboundDomainStrategy, + }, }, - }, + } + switch configOpt.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 configOpt.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( diff --git a/config/outbound.go b/config/outbound.go index 0320bc8..6ad4d9e 100644 --- a/config/outbound.go +++ b/config/outbound.go @@ -70,7 +70,7 @@ func patchOutboundTLSTricks(base option.Outbound, configOpt ConfigOptions, obj o if configOpt.TLSTricks.EnablePadding { tlsTricks.PaddingMode = "random" tlsTricks.PaddingSize = configOpt.TLSTricks.PaddingSize - fmt.Printf("--------------------%+v----%+v", tlsTricks.PaddingSize, configOpt) + // fmt.Printf("--------------------%+v----%+v", tlsTricks.PaddingSize, configOpt) outtls["utls"] = map[string]interface{}{ "enabled": true, "fingerprint": "custom", @@ -82,7 +82,7 @@ func patchOutboundTLSTricks(base option.Outbound, configOpt ConfigOptions, obj o // // } else { // // tls["tls_tricks"] = nil // } - fmt.Printf("-------%+v------------- ", tlsTricks) + // fmt.Printf("-------%+v------------- ", tlsTricks) } return obj } @@ -190,6 +190,12 @@ func patchWarp(base *option.Outbound) error { base.Type = C.TypeWireGuard warpConfig.WireGuardOptions.Detour = detour + if detour != "" { + if warpConfig.WireGuardOptions.MTU > 1000 { + warpConfig.WireGuardOptions.MTU -= 100 + } + warpConfig.WireGuardOptions.FakePackets = "" + } base.WireGuardOptions = warpConfig.WireGuardOptions } diff --git a/config/warp.go b/config/warp.go index 4f61ac5..1f182e3 100644 --- a/config/warp.go +++ b/config/warp.go @@ -188,6 +188,7 @@ func generateWarp(license string, host string, port uint16, fakePackets string) fmt.Println("Error marshaling Singbox configuration:", err) return nil, err } + fmt.Println(string(singboxJSON)) return singboxConfig, nil } diff --git a/custom/command_admin_service.go b/custom/command_admin_service.go index 7f28ff3..7af2b62 100644 --- a/custom/command_admin_service.go +++ b/custom/command_admin_service.go @@ -6,10 +6,19 @@ package main import "C" -import "github.com/hiddify/libcore/admin_service" +import ( + "fmt" + + "github.com/hiddify/libcore/admin_service" +) //export AdminServiceStart -func AdminServiceStart(arg *C.char) { +func AdminServiceStart(arg *C.char) *C.char { goArg := C.GoString(arg) - admin_service.StartService(goArg) + exitCode, outMessage := admin_service.StartService(goArg) + + // Allocate memory for the message and copy the string content + + return C.CString(fmt.Sprintf("%d %s", exitCode, outMessage)) + } diff --git a/global/global.go b/global/global.go index f01dab5..907d35d 100644 --- a/global/global.go +++ b/global/global.go @@ -236,7 +236,9 @@ func urlTest(groupTag string) error { } func StartServiceC(delayStart bool, content string) error { - + if box != nil { + return errors.New("instance already started") + } options, err := parseConfig(content) // if err != nil { // return stopAndAlert(EmptyConfiguration, err) @@ -254,13 +256,14 @@ func StartServiceC(delayStart bool, content string) error { // err = startCommandServer(*logFactory) // if err != nil { - // return stopAndAlert(StartCommandServer, err) + // return err // } instance, err := NewService(options) - // if err != nil { - // return stopAndAlert(CreateService, err) - // } + if err != nil { + // return stopAndAlert(CreateService, err) + return err + } // if delayStart { // time.Sleep(250 * time.Millisecond) @@ -272,34 +275,34 @@ func StartServiceC(delayStart bool, content string) error { fmt.Printf("String Service Error: %v\n", err) return err } - // box = instance + box = instance // commandServer.SetService(box) - // propagateStatus(Started) + status = Started return nil } -func StopService() error { - if status != Started { - return nil - } +func StopServiceC() error { + // if status != Started { + // return errors.New("instance not started") + // } if box == nil { return errors.New("instance not found") } - propagateStatus(Stopping) - commandServer.SetService(nil) + // propagateStatus(Stopping) err := box.Close() + // commandServer.SetService(nil) if err != nil { return err } box = nil - err = commandServer.Close() - if err != nil { - return err - } + // err = commandServer.Close() + // if err != nil { + // return err + // } commandServer = nil - propagateStatus(Stopped) + status = Stopped return nil } @@ -347,7 +350,7 @@ func MakeConfig(Ipv6 bool, ServerPort int, StrictRoute bool, EndpointIndependent { "type": "tun", "tag": "tun-in", - "interface_name": "tun0", + "interface_name": "HiddifyTunnel", "inet4_address": "172.19.0.1/30", ` + ipv6 + ` "mtu": 9000,