// Package naviscore is a gomobile-friendly wrapper that runs Xray or Hysteria2 // in-process (macOS Manager semantics: local SOCKS/HTTP listeners). // // Build: // // gomobile bind -target=ios,iossimulator -o ios/Vendor/NavisCore.xcframework ./mobile/naviscore package naviscore import ( "encoding/json" "errors" "fmt" "net" "os" "path/filepath" "sync" "time" libxrayxray "github.com/xtls/libxray/xray" ) var ( mu sync.Mutex mode string // "xray" | "hy2" | "" hy2Stop func() error workDir string ) // SetWorkDir sets a writable directory for runtime config files (extension container). func SetWorkDir(dir string) { mu.Lock() defer mu.Unlock() workDir = dir } // StartXrayJSON runs Xray from a full config JSON string (SOCKS/HTTP inbounds included). func StartXrayJSON(configJSON string) error { mu.Lock() defer mu.Unlock() if mode != "" { return errors.New("core already running") } if err := libxrayxray.RunXrayFromJSON(configJSON); err != nil { return err } mode = "xray" return nil } // StartXrayFile runs Xray from a config.json path. func StartXrayFile(configPath string) error { mu.Lock() defer mu.Unlock() if mode != "" { return errors.New("core already running") } if err := libxrayxray.RunXray(configPath); err != nil { return err } mode = "xray" return nil } // StartHy2YAML writes YAML to workDir and starts the Hysteria2 client library. // If the hysteria embed is unavailable in this build, returns a clear error. func StartHy2YAML(configYAML string) error { mu.Lock() defer mu.Unlock() if mode != "" { return errors.New("core already running") } dir := workDir if dir == "" { dir = os.TempDir() } if err := os.MkdirAll(dir, 0o700); err != nil { return err } path := filepath.Join(dir, "hy2.yaml") if err := os.WriteFile(path, []byte(configYAML), 0o600); err != nil { return err } stop, err := startHysteria2(path) if err != nil { return err } hy2Stop = stop mode = "hy2" return nil } // Stop stops whichever core is running. func Stop() error { mu.Lock() defer mu.Unlock() var err error switch mode { case "xray": err = libxrayxray.StopXray() case "hy2": if hy2Stop != nil { err = hy2Stop() hy2Stop = nil } } mode = "" return err } // IsRunning reports whether a core is active. func IsRunning() bool { mu.Lock() defer mu.Unlock() switch mode { case "xray": return libxrayxray.GetXrayState() case "hy2": return hy2Stop != nil default: return false } } // WaitPort waits until TCP 127.0.0.1:port accepts connections. func WaitPort(port int, timeoutMs int) error { if port <= 0 { return fmt.Errorf("bad port") } deadline := time.Now().Add(time.Duration(timeoutMs) * time.Millisecond) addr := net.JoinHostPort("127.0.0.1", fmt.Sprintf("%d", port)) for time.Now().Before(deadline) { c, err := net.DialTimeout("tcp", addr, 200*time.Millisecond) if err == nil { _ = c.Close() return nil } time.Sleep(100 * time.Millisecond) } return fmt.Errorf("локальный прокси :%d не поднялся", port) } // Version returns a short build label. func Version() string { return "naviscore/xray+hy2" } // PingJSON is a tiny helper so Swift can verify the framework linked. func PingJSON() string { b, _ := json.Marshal(map[string]any{"ok": true, "version": Version()}) return string(b) }