mirror of
https://github.com/MatsuriDayo/NekoBoxForAndroid.git
synced 2025-12-18 22:20:06 +08:00
226 lines
4.9 KiB
Go
226 lines
4.9 KiB
Go
package libcore
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"libcore/device"
|
|
"log"
|
|
"runtime"
|
|
"runtime/debug"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/matsuridayo/libneko/protect_server"
|
|
"github.com/matsuridayo/libneko/speedtest"
|
|
"github.com/sagernet/sing-box/boxapi"
|
|
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
|
"github.com/sagernet/sing-box/protocol/group"
|
|
|
|
box "github.com/sagernet/sing-box"
|
|
"github.com/sagernet/sing-box/common/conntrack"
|
|
"github.com/sagernet/sing-box/common/dialer"
|
|
"github.com/sagernet/sing-box/constant"
|
|
"github.com/sagernet/sing-box/option"
|
|
"github.com/sagernet/sing/service"
|
|
"github.com/sagernet/sing/service/pause"
|
|
)
|
|
|
|
func init() {
|
|
dialer.DoNotSelectInterface = true
|
|
}
|
|
|
|
var mainInstance *BoxInstance
|
|
|
|
func VersionBox() string {
|
|
version := []string{
|
|
"sing-box: " + constant.Version,
|
|
runtime.Version() + "@" + runtime.GOOS + "/" + runtime.GOARCH,
|
|
}
|
|
|
|
var tags string
|
|
debugInfo, loaded := debug.ReadBuildInfo()
|
|
if loaded {
|
|
for _, setting := range debugInfo.Settings {
|
|
switch setting.Key {
|
|
case "-tags":
|
|
tags = setting.Value
|
|
}
|
|
}
|
|
}
|
|
|
|
if tags != "" {
|
|
version = append(version, tags)
|
|
}
|
|
|
|
return strings.Join(version, "\n")
|
|
}
|
|
|
|
func ResetAllConnections(system bool) {
|
|
if system {
|
|
conntrack.Close()
|
|
log.Println("[Debug] Reset system connections done")
|
|
}
|
|
}
|
|
|
|
type BoxInstance struct {
|
|
access sync.Mutex
|
|
|
|
*box.Box
|
|
cancel context.CancelFunc
|
|
state int
|
|
|
|
v2api *boxapi.SbV2rayServer
|
|
selector *group.Selector
|
|
pauseManager pause.Manager
|
|
}
|
|
|
|
func NewSingBoxInstance(config string) (b *BoxInstance, err error) {
|
|
defer device.DeferPanicToError("NewSingBoxInstance", func(err_ error) { err = err_ })
|
|
|
|
// create box context
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
ctx = box.Context(ctx, nekoboxAndroidInboundRegistry(), nekoboxAndroidOutboundRegistry(), nekoboxAndroidEndpointRegistry())
|
|
ctx = service.ContextWithDefaultRegistry(ctx)
|
|
service.MustRegister[platform.Interface](ctx, boxPlatformInterfaceInstance)
|
|
|
|
// parse options
|
|
var options option.Options
|
|
err = options.UnmarshalJSONContext(ctx, []byte(config))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("decode config: %v", err)
|
|
}
|
|
|
|
// create box
|
|
instance, err := box.New(box.Options{
|
|
Options: options,
|
|
Context: ctx,
|
|
PlatformLogWriter: boxPlatformLogWriter,
|
|
})
|
|
if err != nil {
|
|
cancel()
|
|
return nil, fmt.Errorf("create service: %v", err)
|
|
}
|
|
|
|
b = &BoxInstance{
|
|
Box: instance,
|
|
cancel: cancel,
|
|
pauseManager: service.FromContext[pause.Manager](ctx),
|
|
}
|
|
|
|
// selector
|
|
if proxy, ok := b.Outbound().Outbound("proxy"); ok {
|
|
if selector, ok := proxy.(*group.Selector); ok {
|
|
b.selector = selector
|
|
}
|
|
}
|
|
|
|
return b, nil
|
|
}
|
|
|
|
func (b *BoxInstance) Start() (err error) {
|
|
b.access.Lock()
|
|
defer b.access.Unlock()
|
|
|
|
defer device.DeferPanicToError("box.Start", func(err_ error) { err = err_ })
|
|
|
|
if b.state == 0 {
|
|
b.state = 1
|
|
return b.Box.Start()
|
|
}
|
|
return errors.New("already started")
|
|
}
|
|
|
|
func (b *BoxInstance) Close() (err error) {
|
|
b.access.Lock()
|
|
defer b.access.Unlock()
|
|
|
|
defer device.DeferPanicToError("box.Close", func(err_ error) { err = err_ })
|
|
|
|
// no double close
|
|
if b.state == 2 {
|
|
return nil
|
|
}
|
|
b.state = 2
|
|
|
|
// clear main instance
|
|
if mainInstance == b {
|
|
mainInstance = nil
|
|
goServeProtect(false)
|
|
}
|
|
|
|
// close box
|
|
if b.cancel != nil {
|
|
b.cancel()
|
|
}
|
|
if b.Box != nil {
|
|
b.Box.Close()
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (b *BoxInstance) Sleep() {
|
|
if b.pauseManager != nil {
|
|
b.pauseManager.DevicePause()
|
|
}
|
|
// _ = b.Box.Router().ResetNetwork()
|
|
}
|
|
|
|
func (b *BoxInstance) Wake() {
|
|
if b.pauseManager != nil {
|
|
b.pauseManager.DeviceWake()
|
|
}
|
|
}
|
|
|
|
func (b *BoxInstance) SetAsMain() {
|
|
mainInstance = b
|
|
goServeProtect(true)
|
|
}
|
|
|
|
func (b *BoxInstance) SetV2rayStats(outbounds string) {
|
|
b.v2api = boxapi.NewSbV2rayServer(option.V2RayStatsServiceOptions{
|
|
Enabled: true,
|
|
Outbounds: strings.Split(outbounds, "\n"),
|
|
})
|
|
b.Box.Router().SetTracker(b.v2api.StatsService())
|
|
}
|
|
|
|
func (b *BoxInstance) QueryStats(tag, direct string) int64 {
|
|
if b.v2api == nil {
|
|
return 0
|
|
}
|
|
return b.v2api.QueryStats(fmt.Sprintf("outbound>>>%s>>>traffic>>>%s", tag, direct))
|
|
}
|
|
|
|
func (b *BoxInstance) SelectOutbound(tag string) bool {
|
|
if b.selector != nil {
|
|
return b.selector.SelectOutbound(tag)
|
|
}
|
|
return false
|
|
}
|
|
|
|
func UrlTest(i *BoxInstance, link string, timeout int32) (latency int32, err error) {
|
|
defer device.DeferPanicToError("box.UrlTest", func(err_ error) { err = err_ })
|
|
if i == nil {
|
|
// test current
|
|
return speedtest.UrlTest(boxapi.CreateProxyHttpClient(mainInstance.Box), link, timeout, speedtest.UrlTestStandard_RTT)
|
|
}
|
|
return speedtest.UrlTest(boxapi.CreateProxyHttpClient(i.Box), link, timeout, speedtest.UrlTestStandard_RTT)
|
|
}
|
|
|
|
var protectCloser io.Closer
|
|
|
|
func goServeProtect(start bool) {
|
|
if protectCloser != nil {
|
|
protectCloser.Close()
|
|
protectCloser = nil
|
|
}
|
|
if start {
|
|
protectCloser = protect_server.ServeProtect("protect_path", false, 0, func(fd int) {
|
|
intfBox.AutoDetectInterfaceControl(int32(fd))
|
|
})
|
|
}
|
|
}
|