NekoBoxForAndroid/libcore/box.go
2023-09-24 14:11:36 +09:00

215 lines
4.6 KiB
Go

package libcore
import (
"context"
"errors"
"fmt"
"io"
"libcore/device"
"log"
"runtime"
"runtime/debug"
"strings"
"time"
"github.com/matsuridayo/sing-box-extra/boxbox"
_ "github.com/matsuridayo/sing-box-extra/distro/all"
"github.com/matsuridayo/libneko/protect_server"
"github.com/matsuridayo/libneko/speedtest"
"github.com/matsuridayo/sing-box-extra/boxapi"
"github.com/sagernet/sing-box/common/conntrack"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/outbound"
"github.com/sagernet/sing/service/pause"
)
var mainInstance *BoxInstance
func VersionBox() string {
version := []string{
"sing-box-extra: " + boxbox.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 {
*boxbox.Box
cancel context.CancelFunc
state int
v2api *boxapi.SbV2rayServer
selector *outbound.Selector
pauseManager pause.Manager
ForTest bool
}
func NewSingBoxInstance(config string) (b *BoxInstance, err error) {
defer device.DeferPanicToError("NewSingBoxInstance", func(err_ error) { err = err_ })
// parse options
var options option.Options
err = options.UnmarshalJSON([]byte(config))
if err != nil {
return nil, fmt.Errorf("decode config: %v", err)
}
// create box
ctx, cancel := context.WithCancel(context.Background())
sleepManager := pause.NewDefaultManager(ctx)
ctx = pause.ContextWithManager(ctx, sleepManager)
instance, err := boxbox.New(boxbox.Options{
Options: options,
Context: ctx,
PlatformInterface: boxPlatformInterfaceInstance,
})
if err != nil {
cancel()
return nil, fmt.Errorf("create service: %v", err)
}
b = &BoxInstance{
Box: instance,
cancel: cancel,
pauseManager: sleepManager,
}
// fuck your sing-box platformFormatter
pf := instance.GetLogPlatformFormatter()
pf.DisableColors = true
pf.DisableLineBreak = false
// selector
if proxy, ok := b.Router().Outbound("proxy"); ok {
if selector, ok := proxy.(*outbound.Selector); ok {
b.selector = selector
}
}
return b, nil
}
func (b *BoxInstance) Start() (err error) {
defer device.DeferPanicToError("box.Start", func(err_ error) { err = err_ })
if outdated != "" {
return errors.New(outdated)
}
if b.state == 0 {
b.state = 1
return b.Box.Start()
}
return errors.New("already started")
}
func (b *BoxInstance) Close() (err error) {
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
b.CloseWithTimeout(b.cancel, time.Second*2, log.Println)
return nil
}
func (b *BoxInstance) Sleep() {
b.pauseManager.DevicePause()
_ = b.Box.Router().ResetNetwork()
}
func (b *BoxInstance) Wake() {
b.pauseManager.DeviceWake()
}
func (b *BoxInstance) SetAsMain() {
mainInstance = b
goServeProtect(true)
}
func (b *BoxInstance) SetConnectionPoolEnabled(enable bool) {
// TODO api
}
func (b *BoxInstance) SetV2rayStats(outbounds string) {
b.v2api = boxapi.NewSbV2rayServer(option.V2RayStatsServiceOptions{
Enabled: true,
Outbounds: strings.Split(outbounds, "\n"),
})
b.Box.Router().SetV2RayServer(b.v2api)
}
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)
}
return speedtest.UrlTest(boxapi.CreateProxyHttpClient(i.Box), link, timeout)
}
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))
})
}
}