Files
navi/mobile/naviscore/naviscore.go
T
M4andCursor 7ceb3a5148
ci / test (macos-latest) (push) Waiting to run
ci / test (ubuntu-latest) (push) Waiting to run
ci / test (windows-latest) (push) Waiting to run
Release 3.8.2.13: iOS Packet Tunnel client and Clash URL subscriptions.
Add SwiftUI + Network Extension (LibXray/hev), parse Clash Meta YAML from subscription URLs into vless/trojan/hy2 share links, keep the selected server on connect, and ship Navis-3.8.2.13.ipa with versioned changelog.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-01 22:46:32 +03:00

152 lines
3.3 KiB
Go

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