From c609030b92c70c2b08fa58f9077b375b058dfd7b Mon Sep 17 00:00:00 2001 From: Hiddify Date: Tue, 19 Mar 2024 17:07:11 +0100 Subject: [PATCH] release: version 1.1.0 --- custom/grpc_interface.go | 20 +- docker/docker-compose.yml | 28 +-- docker/hiddify.json | 80 +++---- v2/standalone.go | 472 +++++++++++++++++++------------------- v2/system_proxy.go | 88 +++---- v2/tunnel_service.go | 238 +++++++++---------- 6 files changed, 463 insertions(+), 463 deletions(-) diff --git a/custom/grpc_interface.go b/custom/grpc_interface.go index af85ffb..e1fee81 100644 --- a/custom/grpc_interface.go +++ b/custom/grpc_interface.go @@ -1,10 +1,10 @@ -package main - -import "C" -import v2 "github.com/hiddify/libcore/v2" - -//export StartCoreGrpcServer -func StartCoreGrpcServer(listenAddress *C.char) (CErr *C.char) { - err := v2.StartCoreGrpcServer(C.GoString(listenAddress)) - return emptyOrErrorC(err) -} +package main + +import "C" +import v2 "github.com/hiddify/libcore/v2" + +//export StartCoreGrpcServer +func StartCoreGrpcServer(listenAddress *C.char) (CErr *C.char) { + err := v2.StartCoreGrpcServer(C.GoString(listenAddress)) + return emptyOrErrorC(err) +} diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 1d22406..8e956be 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,14 +1,14 @@ -version: '3.8' - -services: - hiddify: - image: ghcr.io/hiddify/hiddify-next-core/cli:latest - ports: - - "2334:2334" - - "6756:6756" - - "6450:6450" - environment: - CONFIG: "https://raw.githubusercontent.com/ircfspace/warpsub/main/export/warp#WARP%20(IRCF)" - volumes: - - ./data/:/hiddify/data/ - command: ["/opt/hiddify.sh"] +version: '3.8' + +services: + hiddify: + image: ghcr.io/hiddify/hiddify-next-core/cli:latest + ports: + - "2334:2334" + - "6756:6756" + - "6450:6450" + environment: + CONFIG: "https://raw.githubusercontent.com/ircfspace/warpsub/main/export/warp#WARP%20(IRCF)" + volumes: + - ./data/:/hiddify/data/ + command: ["/opt/hiddify.sh"] diff --git a/docker/hiddify.json b/docker/hiddify.json index 5b15072..48a7172 100644 --- a/docker/hiddify.json +++ b/docker/hiddify.json @@ -1,41 +1,41 @@ -{ - "service-mode": "proxy", - "log-level": "info", - "resolve-destination": true, - "ipv6-mode": "prefer_ipv4", - "remote-dns-address": "tcp://1.1.1.1", - "remote-dns-domain-strategy": "", - "direct-dns-address": "1.1.1.1", - "direct-dns-domain-strategy": "", - "mixed-port": 2334, - "local-dns-port": 6450, - "tun-implementation": "mixed", - "mtu": 9000, - "strict-route": false, - "connection-test-url": "https://www.gstatic.com/generate_204", - "url-test-interval": 600, - "enable-clash-api": true, - "clash-api-port": 6756, - "bypass-lan": false, - "allow-connection-from-lan": true, - "enable-fake-dns": false, - "enable-dns-routing": true, - "independent-dns-cache": true, - "enable-tls-fragment": false, - "tls-fragment-size": "20-70", - "tls-fragment-sleep": "10-30", - "enable-tls-mixed-sni-case": false, - "enable-tls-padding": false, - "tls-padding-size": "15-30", - "enable-mux": false, - "mux-padding": false, - "mux-max-streams": 4, - "mux-protocol": "h2mux", - "enable-warp": false, - "warp-detour-mode": "outbound", - "warp-license-key": "", - "warp-clean-ip": "auto", - "warp-port": 0, - "warp-noise": "5-10", - "warp-noise-delay": "20-200" +{ + "service-mode": "proxy", + "log-level": "info", + "resolve-destination": true, + "ipv6-mode": "prefer_ipv4", + "remote-dns-address": "tcp://1.1.1.1", + "remote-dns-domain-strategy": "", + "direct-dns-address": "1.1.1.1", + "direct-dns-domain-strategy": "", + "mixed-port": 2334, + "local-dns-port": 6450, + "tun-implementation": "mixed", + "mtu": 9000, + "strict-route": false, + "connection-test-url": "https://www.gstatic.com/generate_204", + "url-test-interval": 600, + "enable-clash-api": true, + "clash-api-port": 6756, + "bypass-lan": false, + "allow-connection-from-lan": true, + "enable-fake-dns": false, + "enable-dns-routing": true, + "independent-dns-cache": true, + "enable-tls-fragment": false, + "tls-fragment-size": "20-70", + "tls-fragment-sleep": "10-30", + "enable-tls-mixed-sni-case": false, + "enable-tls-padding": false, + "tls-padding-size": "15-30", + "enable-mux": false, + "mux-padding": false, + "mux-max-streams": 4, + "mux-protocol": "h2mux", + "enable-warp": false, + "warp-detour-mode": "outbound", + "warp-license-key": "", + "warp-clean-ip": "auto", + "warp-port": 0, + "warp-noise": "5-10", + "warp-noise-delay": "20-200" } \ No newline at end of file diff --git a/v2/standalone.go b/v2/standalone.go index 6cce729..dd4525e 100644 --- a/v2/standalone.go +++ b/v2/standalone.go @@ -1,236 +1,236 @@ -package v2 - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "os" - "os/signal" - "runtime" - "strconv" - "strings" - "syscall" - "time" - - "github.com/hiddify/libcore/config" - pb "github.com/hiddify/libcore/hiddifyrpc" - - "github.com/sagernet/sing-box/option" -) - -func RunStandalone(hiddifySettingPath string, configPath string) error { - fmt.Println("Running in standalone mode") - useFlutterBridge = false - current, err := readAndBuildConfig(hiddifySettingPath, configPath) - if err != nil { - fmt.Printf("Error in read and build config %v", err) - return err - } - - go StartService(&pb.StartRequest{ - ConfigContent: current.Config, - EnableOldCommandServer: false, - DelayStart: false, - EnableRawConfig: true, - }) - go updateConfigInterval(current, hiddifySettingPath, configPath) - - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) - - fmt.Printf("Waiting for CTRL+C to stop\n") - <-sigChan - fmt.Printf("CTRL+C recived-->stopping\n") - _, err = Stop() - - return err -} - -type ConfigResult struct { - Config string - RefreshInterval int -} - -func readAndBuildConfig(hiddifySettingPath string, configPath string) (ConfigResult, error) { - var result ConfigResult - - result, err := readConfigContent(configPath) - if err != nil { - return result, err - } - hiddifyconfig := config.DefaultConfigOptions() - if hiddifySettingPath != "" { - hiddifyconfig, err = readConfigOptionsAt(hiddifySettingPath) - if err != nil { - return result, err - } - } - configOptions = hiddifyconfig - result.Config, err = buildConfig(result.Config, *hiddifyconfig) - if err != nil { - return result, err - } - - return result, nil -} - -func readConfigContent(configPath string) (ConfigResult, error) { - var content string - var refreshInterval int - - if strings.HasPrefix(configPath, "http://") || strings.HasPrefix(configPath, "https://") { - client := &http.Client{} - - // Create a new request - req, err := http.NewRequest("GET", configPath, nil) - if err != nil { - fmt.Println("Error creating request:", err) - return ConfigResult{}, err - } - req.Header.Set("User-Agent", "HiddifyNext/17.5.0 ("+runtime.GOOS+") like ClashMeta v2ray sing-box") - resp, err := client.Do(req) - if err != nil { - fmt.Println("Error making GET request:", err) - return ConfigResult{}, err - } - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return ConfigResult{}, fmt.Errorf("failed to read config body: %w", err) - } - content = string(body) - refreshInterval, _ = extractRefreshInterval(resp.Header, content) - fmt.Printf("Refresh interval: %d\n", refreshInterval) - } else { - data, err := ioutil.ReadFile(configPath) - if err != nil { - return ConfigResult{}, fmt.Errorf("failed to read config file: %w", err) - } - content = string(data) - } - - return ConfigResult{ - Config: content, - RefreshInterval: refreshInterval, - }, nil -} - -func extractRefreshInterval(header http.Header, bodyStr string) (int, error) { - refreshIntervalStr := header.Get("profile-update-interval") - if refreshIntervalStr != "" { - refreshInterval, err := strconv.Atoi(refreshIntervalStr) - if err != nil { - return 0, fmt.Errorf("failed to parse refresh interval from header: %w", err) - } - return refreshInterval, nil - } - - lines := strings.Split(bodyStr, "\n") - for _, line := range lines { - line = strings.TrimSpace(line) - if strings.HasPrefix(line, "//profile-update-interval:") || strings.HasPrefix(line, "#profile-update-interval:") { - parts := strings.SplitN(line, ":", 2) - str := strings.TrimSpace(parts[1]) - refreshInterval, err := strconv.Atoi(str) - if err != nil { - return 0, fmt.Errorf("failed to parse refresh interval from body: %w", err) - } - return refreshInterval, nil - } - } - return 0, nil -} -func buildConfig(configContent string, options config.ConfigOptions) (string, error) { - parsedContent, err := config.ParseConfigContent(configContent, true) - if err != nil { - return "", fmt.Errorf("failed to parse config content: %w", err) - } - singconfigs, err := readConfigBytes([]byte(parsedContent)) - if err != nil { - return "", err - } - - finalconfig, err := config.BuildConfig(options, *singconfigs) - if err != nil { - return "", fmt.Errorf("failed to build config: %w", err) - } - - finalconfig.Log.Output = "" - finalconfig.Experimental.ClashAPI.ExternalUI = "webui" - if options.AllowConnectionFromLAN { - finalconfig.Experimental.ClashAPI.ExternalController = "0.0.0.0:6756" - } else { - finalconfig.Experimental.ClashAPI.ExternalController = "127.0.0.1:6756" - } - - fmt.Printf("Open http://localhost:6756/ui/?secret=%s in your browser\n", finalconfig.Experimental.ClashAPI.Secret) - - if err := Setup("./", "./", "./tmp", 0, false); err != nil { - return "", fmt.Errorf("failed to set up global configuration: %w", err) - } - - configStr, err := config.ToJson(*finalconfig) - if err != nil { - return "", fmt.Errorf("failed to convert config to JSON: %w", err) - } - - return configStr, nil -} - -func updateConfigInterval(current ConfigResult, hiddifySettingPath string, configPath string) { - if current.RefreshInterval <= 0 { - return - } - - for { - <-time.After(time.Duration(current.RefreshInterval) * time.Hour) - new, err := readAndBuildConfig(hiddifySettingPath, configPath) - if err != nil { - continue - } - if new.Config != current.Config { - go Stop() - go StartService(&pb.StartRequest{ - ConfigContent: new.Config, - DelayStart: false, - EnableOldCommandServer: false, - DisableMemoryLimit: false, - EnableRawConfig: true, - }) - } - current = new - } - -} - -func readConfigBytes(content []byte) (*option.Options, error) { - var options option.Options - err := options.UnmarshalJSON(content) - if err != nil { - return nil, err - } - return &options, nil -} - -func readConfigOptionsAt(path string) (*config.ConfigOptions, error) { - content, err := os.ReadFile(path) - if err != nil { - return nil, err - } - var options config.ConfigOptions - err = json.Unmarshal(content, &options) - - if err != nil { - return nil, err - } - if options.Warp.WireguardConfigStr != "" { - err := json.Unmarshal([]byte(options.Warp.WireguardConfigStr), &options.Warp.WireguardConfig) - if err != nil { - return nil, err - } - } - - return &options, nil -} +package v2 + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "os" + "os/signal" + "runtime" + "strconv" + "strings" + "syscall" + "time" + + "github.com/hiddify/libcore/config" + pb "github.com/hiddify/libcore/hiddifyrpc" + + "github.com/sagernet/sing-box/option" +) + +func RunStandalone(hiddifySettingPath string, configPath string) error { + fmt.Println("Running in standalone mode") + useFlutterBridge = false + current, err := readAndBuildConfig(hiddifySettingPath, configPath) + if err != nil { + fmt.Printf("Error in read and build config %v", err) + return err + } + + go StartService(&pb.StartRequest{ + ConfigContent: current.Config, + EnableOldCommandServer: false, + DelayStart: false, + EnableRawConfig: true, + }) + go updateConfigInterval(current, hiddifySettingPath, configPath) + + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) + + fmt.Printf("Waiting for CTRL+C to stop\n") + <-sigChan + fmt.Printf("CTRL+C recived-->stopping\n") + _, err = Stop() + + return err +} + +type ConfigResult struct { + Config string + RefreshInterval int +} + +func readAndBuildConfig(hiddifySettingPath string, configPath string) (ConfigResult, error) { + var result ConfigResult + + result, err := readConfigContent(configPath) + if err != nil { + return result, err + } + hiddifyconfig := config.DefaultConfigOptions() + if hiddifySettingPath != "" { + hiddifyconfig, err = readConfigOptionsAt(hiddifySettingPath) + if err != nil { + return result, err + } + } + configOptions = hiddifyconfig + result.Config, err = buildConfig(result.Config, *hiddifyconfig) + if err != nil { + return result, err + } + + return result, nil +} + +func readConfigContent(configPath string) (ConfigResult, error) { + var content string + var refreshInterval int + + if strings.HasPrefix(configPath, "http://") || strings.HasPrefix(configPath, "https://") { + client := &http.Client{} + + // Create a new request + req, err := http.NewRequest("GET", configPath, nil) + if err != nil { + fmt.Println("Error creating request:", err) + return ConfigResult{}, err + } + req.Header.Set("User-Agent", "HiddifyNext/17.5.0 ("+runtime.GOOS+") like ClashMeta v2ray sing-box") + resp, err := client.Do(req) + if err != nil { + fmt.Println("Error making GET request:", err) + return ConfigResult{}, err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return ConfigResult{}, fmt.Errorf("failed to read config body: %w", err) + } + content = string(body) + refreshInterval, _ = extractRefreshInterval(resp.Header, content) + fmt.Printf("Refresh interval: %d\n", refreshInterval) + } else { + data, err := ioutil.ReadFile(configPath) + if err != nil { + return ConfigResult{}, fmt.Errorf("failed to read config file: %w", err) + } + content = string(data) + } + + return ConfigResult{ + Config: content, + RefreshInterval: refreshInterval, + }, nil +} + +func extractRefreshInterval(header http.Header, bodyStr string) (int, error) { + refreshIntervalStr := header.Get("profile-update-interval") + if refreshIntervalStr != "" { + refreshInterval, err := strconv.Atoi(refreshIntervalStr) + if err != nil { + return 0, fmt.Errorf("failed to parse refresh interval from header: %w", err) + } + return refreshInterval, nil + } + + lines := strings.Split(bodyStr, "\n") + for _, line := range lines { + line = strings.TrimSpace(line) + if strings.HasPrefix(line, "//profile-update-interval:") || strings.HasPrefix(line, "#profile-update-interval:") { + parts := strings.SplitN(line, ":", 2) + str := strings.TrimSpace(parts[1]) + refreshInterval, err := strconv.Atoi(str) + if err != nil { + return 0, fmt.Errorf("failed to parse refresh interval from body: %w", err) + } + return refreshInterval, nil + } + } + return 0, nil +} +func buildConfig(configContent string, options config.ConfigOptions) (string, error) { + parsedContent, err := config.ParseConfigContent(configContent, true) + if err != nil { + return "", fmt.Errorf("failed to parse config content: %w", err) + } + singconfigs, err := readConfigBytes([]byte(parsedContent)) + if err != nil { + return "", err + } + + finalconfig, err := config.BuildConfig(options, *singconfigs) + if err != nil { + return "", fmt.Errorf("failed to build config: %w", err) + } + + finalconfig.Log.Output = "" + finalconfig.Experimental.ClashAPI.ExternalUI = "webui" + if options.AllowConnectionFromLAN { + finalconfig.Experimental.ClashAPI.ExternalController = "0.0.0.0:6756" + } else { + finalconfig.Experimental.ClashAPI.ExternalController = "127.0.0.1:6756" + } + + fmt.Printf("Open http://localhost:6756/ui/?secret=%s in your browser\n", finalconfig.Experimental.ClashAPI.Secret) + + if err := Setup("./", "./", "./tmp", 0, false); err != nil { + return "", fmt.Errorf("failed to set up global configuration: %w", err) + } + + configStr, err := config.ToJson(*finalconfig) + if err != nil { + return "", fmt.Errorf("failed to convert config to JSON: %w", err) + } + + return configStr, nil +} + +func updateConfigInterval(current ConfigResult, hiddifySettingPath string, configPath string) { + if current.RefreshInterval <= 0 { + return + } + + for { + <-time.After(time.Duration(current.RefreshInterval) * time.Hour) + new, err := readAndBuildConfig(hiddifySettingPath, configPath) + if err != nil { + continue + } + if new.Config != current.Config { + go Stop() + go StartService(&pb.StartRequest{ + ConfigContent: new.Config, + DelayStart: false, + EnableOldCommandServer: false, + DisableMemoryLimit: false, + EnableRawConfig: true, + }) + } + current = new + } + +} + +func readConfigBytes(content []byte) (*option.Options, error) { + var options option.Options + err := options.UnmarshalJSON(content) + if err != nil { + return nil, err + } + return &options, nil +} + +func readConfigOptionsAt(path string) (*config.ConfigOptions, error) { + content, err := os.ReadFile(path) + if err != nil { + return nil, err + } + var options config.ConfigOptions + err = json.Unmarshal(content, &options) + + if err != nil { + return nil, err + } + if options.Warp.WireguardConfigStr != "" { + err := json.Unmarshal([]byte(options.Warp.WireguardConfigStr), &options.Warp.WireguardConfig) + if err != nil { + return nil, err + } + } + + return &options, nil +} diff --git a/v2/system_proxy.go b/v2/system_proxy.go index af359ea..8c807b7 100644 --- a/v2/system_proxy.go +++ b/v2/system_proxy.go @@ -1,44 +1,44 @@ -package v2 - -import ( - "context" - - pb "github.com/hiddify/libcore/hiddifyrpc" - "github.com/sagernet/sing-box/experimental/libbox" -) - -func (s *CoreService) GetSystemProxyStatus(ctx context.Context, empty *pb.Empty) (*pb.SystemProxyStatus, error) { - return GetSystemProxyStatus(ctx, empty) -} -func GetSystemProxyStatus(ctx context.Context, empty *pb.Empty) (*pb.SystemProxyStatus, error) { - status, err := libbox.NewStandaloneCommandClient().GetSystemProxyStatus() - - if err != nil { - return nil, err - } - - return &pb.SystemProxyStatus{ - Available: status.Available, - Enabled: status.Enabled, - }, nil -} - -func (s *CoreService) SetSystemProxyEnabled(ctx context.Context, in *pb.SetSystemProxyEnabledRequest) (*pb.Response, error) { - return SetSystemProxyEnabled(ctx, in) -} -func SetSystemProxyEnabled(ctx context.Context, in *pb.SetSystemProxyEnabledRequest) (*pb.Response, error) { - err := libbox.NewStandaloneCommandClient().SetSystemProxyEnabled(in.IsEnabled) - - if err != nil { - return &pb.Response{ - ResponseCode: pb.ResponseCode_FAILED, - Message: err.Error(), - }, err - } - - return &pb.Response{ - ResponseCode: pb.ResponseCode_OK, - Message: "", - }, nil - -} +package v2 + +import ( + "context" + + pb "github.com/hiddify/libcore/hiddifyrpc" + "github.com/sagernet/sing-box/experimental/libbox" +) + +func (s *CoreService) GetSystemProxyStatus(ctx context.Context, empty *pb.Empty) (*pb.SystemProxyStatus, error) { + return GetSystemProxyStatus(ctx, empty) +} +func GetSystemProxyStatus(ctx context.Context, empty *pb.Empty) (*pb.SystemProxyStatus, error) { + status, err := libbox.NewStandaloneCommandClient().GetSystemProxyStatus() + + if err != nil { + return nil, err + } + + return &pb.SystemProxyStatus{ + Available: status.Available, + Enabled: status.Enabled, + }, nil +} + +func (s *CoreService) SetSystemProxyEnabled(ctx context.Context, in *pb.SetSystemProxyEnabledRequest) (*pb.Response, error) { + return SetSystemProxyEnabled(ctx, in) +} +func SetSystemProxyEnabled(ctx context.Context, in *pb.SetSystemProxyEnabledRequest) (*pb.Response, error) { + err := libbox.NewStandaloneCommandClient().SetSystemProxyEnabled(in.IsEnabled) + + if err != nil { + return &pb.Response{ + ResponseCode: pb.ResponseCode_FAILED, + Message: err.Error(), + }, err + } + + return &pb.Response{ + ResponseCode: pb.ResponseCode_OK, + Message: "", + }, nil + +} diff --git a/v2/tunnel_service.go b/v2/tunnel_service.go index e1eddd4..76525f3 100644 --- a/v2/tunnel_service.go +++ b/v2/tunnel_service.go @@ -1,119 +1,119 @@ -package v2 - -import ( - "context" - "fmt" - "log" - "os" - - pb "github.com/hiddify/libcore/hiddifyrpc" -) - -func (s *TunnelService) Start(ctx context.Context, in *pb.TunnelStartRequest) (*pb.TunnelResponse, error) { - if in.ServerPort == 0 { - in.ServerPort = 2334 - } - useFlutterBridge = false - res, err := Start(&pb.StartRequest{ - ConfigContent: makeTunnelConfig(in.Ipv6, in.ServerPort, in.StrictRoute, in.EndpointIndependentNat, in.Stack), - EnableOldCommandServer: false, - DisableMemoryLimit: false, - EnableRawConfig: true, - }) - fmt.Printf("Start Result: %+v\n", res) - if err != nil { - return &pb.TunnelResponse{ - Message: err.Error(), - }, err - } - return &pb.TunnelResponse{ - Message: "OK", - }, err -} - -func makeTunnelConfig(Ipv6 bool, ServerPort int32, StrictRoute bool, EndpointIndependentNat bool, Stack string) string { - var ipv6 string - if Ipv6 { - ipv6 = ` "inet6_address": "fdfe:dcba:9876::1/126",` - } else { - ipv6 = "" - } - base := `{ - "inbounds": [ - { - "type": "tun", - "tag": "tun-in", - "interface_name": "HiddifyTunnel", - "inet4_address": "172.19.0.1/30", - ` + ipv6 + ` - "mtu": 9000, - "auto_route": true, - "strict_route": ` + fmt.Sprintf("%t", StrictRoute) + `, - "endpoint_independent_nat": ` + fmt.Sprintf("%t", EndpointIndependentNat) + `, - "stack": "` + Stack + `" - } - ], - "outbounds": [ - { - "type": "socks", - "tag": "socks-out", - "server": "127.0.0.1", - "server_port": ` + fmt.Sprintf("%d", ServerPort) + `, - "version": "5" - }, - { - "type": "direct", - "tag": "direct-out" - } - ], - "route": { - "rules": [ - { - "process_name":"Hiddify.exe", - "outbound": "direct-out" - }, - { - "process_name":"Hiddify", - "outbound": "direct-out" - }, - { - "process_name":"HiddifyCli", - "outbound": "direct-out" - }, - { - "process_name":"HiddifyCli.exe", - "outbound": "direct-out" - } - ] - } - }` - - return base -} - -func (s *TunnelService) Stop(ctx context.Context, _ *pb.Empty) (*pb.TunnelResponse, error) { - res, err := Stop() - log.Printf("Stop Result: %+v\n", res) - if err != nil { - return &pb.TunnelResponse{ - Message: err.Error(), - }, err - } - - return &pb.TunnelResponse{ - Message: "OK", - }, err -} -func (s *TunnelService) Status(ctx context.Context, _ *pb.Empty) (*pb.TunnelResponse, error) { - - return &pb.TunnelResponse{ - Message: "Not Implemented", - }, nil -} -func (s *TunnelService) Exit(ctx context.Context, _ *pb.Empty) (*pb.TunnelResponse, error) { - Stop() - os.Exit(0) - return &pb.TunnelResponse{ - Message: "OK", - }, nil -} +package v2 + +import ( + "context" + "fmt" + "log" + "os" + + pb "github.com/hiddify/libcore/hiddifyrpc" +) + +func (s *TunnelService) Start(ctx context.Context, in *pb.TunnelStartRequest) (*pb.TunnelResponse, error) { + if in.ServerPort == 0 { + in.ServerPort = 2334 + } + useFlutterBridge = false + res, err := Start(&pb.StartRequest{ + ConfigContent: makeTunnelConfig(in.Ipv6, in.ServerPort, in.StrictRoute, in.EndpointIndependentNat, in.Stack), + EnableOldCommandServer: false, + DisableMemoryLimit: false, + EnableRawConfig: true, + }) + fmt.Printf("Start Result: %+v\n", res) + if err != nil { + return &pb.TunnelResponse{ + Message: err.Error(), + }, err + } + return &pb.TunnelResponse{ + Message: "OK", + }, err +} + +func makeTunnelConfig(Ipv6 bool, ServerPort int32, StrictRoute bool, EndpointIndependentNat bool, Stack string) string { + var ipv6 string + if Ipv6 { + ipv6 = ` "inet6_address": "fdfe:dcba:9876::1/126",` + } else { + ipv6 = "" + } + base := `{ + "inbounds": [ + { + "type": "tun", + "tag": "tun-in", + "interface_name": "HiddifyTunnel", + "inet4_address": "172.19.0.1/30", + ` + ipv6 + ` + "mtu": 9000, + "auto_route": true, + "strict_route": ` + fmt.Sprintf("%t", StrictRoute) + `, + "endpoint_independent_nat": ` + fmt.Sprintf("%t", EndpointIndependentNat) + `, + "stack": "` + Stack + `" + } + ], + "outbounds": [ + { + "type": "socks", + "tag": "socks-out", + "server": "127.0.0.1", + "server_port": ` + fmt.Sprintf("%d", ServerPort) + `, + "version": "5" + }, + { + "type": "direct", + "tag": "direct-out" + } + ], + "route": { + "rules": [ + { + "process_name":"Hiddify.exe", + "outbound": "direct-out" + }, + { + "process_name":"Hiddify", + "outbound": "direct-out" + }, + { + "process_name":"HiddifyCli", + "outbound": "direct-out" + }, + { + "process_name":"HiddifyCli.exe", + "outbound": "direct-out" + } + ] + } + }` + + return base +} + +func (s *TunnelService) Stop(ctx context.Context, _ *pb.Empty) (*pb.TunnelResponse, error) { + res, err := Stop() + log.Printf("Stop Result: %+v\n", res) + if err != nil { + return &pb.TunnelResponse{ + Message: err.Error(), + }, err + } + + return &pb.TunnelResponse{ + Message: "OK", + }, err +} +func (s *TunnelService) Status(ctx context.Context, _ *pb.Empty) (*pb.TunnelResponse, error) { + + return &pb.TunnelResponse{ + Message: "Not Implemented", + }, nil +} +func (s *TunnelService) Exit(ctx context.Context, _ *pb.Empty) (*pb.TunnelResponse, error) { + Stop() + os.Exit(0) + return &pb.TunnelResponse{ + Message: "OK", + }, nil +}