mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-25 10:01:30 +00:00
Add libbox wrapper
This commit is contained in:
parent
86e55c5c1c
commit
222196b182
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -6,3 +6,6 @@
|
||||||
/bin/
|
/bin/
|
||||||
/dist/
|
/dist/
|
||||||
/sing-box
|
/sing-box
|
||||||
|
/build/
|
||||||
|
/*.jar
|
||||||
|
/*.aar
|
8
Makefile
8
Makefile
|
@ -71,6 +71,14 @@ test_stdio:
|
||||||
go mod tidy && \
|
go mod tidy && \
|
||||||
go test -v -tags "$(TAGS_TEST),force_stdio" .
|
go test -v -tags "$(TAGS_TEST),force_stdio" .
|
||||||
|
|
||||||
|
lib:
|
||||||
|
go run ./cmd/internal/build_libbox
|
||||||
|
|
||||||
|
lib_install:
|
||||||
|
go get -v -d
|
||||||
|
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.0.0-20221130124640-349ebaa752ca
|
||||||
|
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.0.0-20221130124640-349ebaa752ca
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf bin dist sing-box
|
rm -rf bin dist sing-box
|
||||||
rm -f $(shell go env GOPATH)/sing-box
|
rm -f $(shell go env GOPATH)/sing-box
|
||||||
|
|
|
@ -34,6 +34,7 @@ type Router interface {
|
||||||
InterfaceFinder() control.InterfaceFinder
|
InterfaceFinder() control.InterfaceFinder
|
||||||
DefaultInterface() string
|
DefaultInterface() string
|
||||||
AutoDetectInterface() bool
|
AutoDetectInterface() bool
|
||||||
|
AutoDetectInterfaceFunc() control.Func
|
||||||
DefaultMark() int
|
DefaultMark() int
|
||||||
NetworkMonitor() tun.NetworkUpdateMonitor
|
NetworkMonitor() tun.NetworkUpdateMonitor
|
||||||
InterfaceMonitor() tun.DefaultInterfaceMonitor
|
InterfaceMonitor() tun.DefaultInterfaceMonitor
|
||||||
|
|
67
box.go
67
box.go
|
@ -9,6 +9,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/experimental"
|
"github.com/sagernet/sing-box/experimental"
|
||||||
"github.com/sagernet/sing-box/inbound"
|
"github.com/sagernet/sing-box/inbound"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
|
@ -53,24 +54,31 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
||||||
var logFactory log.Factory
|
var logFactory log.Factory
|
||||||
var observableLogFactory log.ObservableFactory
|
var observableLogFactory log.ObservableFactory
|
||||||
var logFile *os.File
|
var logFile *os.File
|
||||||
|
var logWriter io.Writer
|
||||||
if logOptions.Disabled {
|
if logOptions.Disabled {
|
||||||
observableLogFactory = log.NewNOPFactory()
|
observableLogFactory = log.NewNOPFactory()
|
||||||
logFactory = observableLogFactory
|
logFactory = observableLogFactory
|
||||||
} else {
|
} else {
|
||||||
var logWriter io.Writer
|
|
||||||
switch logOptions.Output {
|
switch logOptions.Output {
|
||||||
case "", "stderr":
|
case "":
|
||||||
|
if options.PlatformInterface != nil {
|
||||||
|
logWriter = io.Discard
|
||||||
|
} else {
|
||||||
|
logWriter = os.Stdout
|
||||||
|
}
|
||||||
|
case "stderr":
|
||||||
logWriter = os.Stderr
|
logWriter = os.Stderr
|
||||||
case "stdout":
|
case "stdout":
|
||||||
logWriter = os.Stdout
|
logWriter = os.Stdout
|
||||||
default:
|
default:
|
||||||
var err error
|
var err error
|
||||||
logFile, err = os.OpenFile(logOptions.Output, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
|
logFile, err = os.OpenFile(C.BasePath(logOptions.Output), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
logWriter = logFile
|
logWriter = logFile
|
||||||
}
|
}
|
||||||
|
}
|
||||||
logFormatter := log.Formatter{
|
logFormatter := log.Formatter{
|
||||||
BaseTime: createdAt,
|
BaseTime: createdAt,
|
||||||
DisableColors: logOptions.DisableColor || logFile != nil,
|
DisableColors: logOptions.DisableColor || logFile != nil,
|
||||||
|
@ -79,10 +87,10 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
||||||
TimestampFormat: "-0700 2006-01-02 15:04:05",
|
TimestampFormat: "-0700 2006-01-02 15:04:05",
|
||||||
}
|
}
|
||||||
if needClashAPI {
|
if needClashAPI {
|
||||||
observableLogFactory = log.NewObservableFactory(logFormatter, logWriter)
|
observableLogFactory = log.NewObservableFactory(logFormatter, logWriter, options.PlatformInterface)
|
||||||
logFactory = observableLogFactory
|
logFactory = observableLogFactory
|
||||||
} else {
|
} else {
|
||||||
logFactory = log.NewFactory(logFormatter, logWriter)
|
logFactory = log.NewFactory(logFormatter, logWriter, options.PlatformInterface)
|
||||||
}
|
}
|
||||||
if logOptions.Level != "" {
|
if logOptions.Level != "" {
|
||||||
logLevel, err := log.ParseLevel(logOptions.Level)
|
logLevel, err := log.ParseLevel(logOptions.Level)
|
||||||
|
@ -93,7 +101,6 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
||||||
} else {
|
} else {
|
||||||
logFactory.SetLevel(log.LevelTrace)
|
logFactory.SetLevel(log.LevelTrace)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
router, err := route.NewRouter(
|
router, err := route.NewRouter(
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -101,6 +108,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
||||||
common.PtrValueOrDefault(options.Route),
|
common.PtrValueOrDefault(options.Route),
|
||||||
common.PtrValueOrDefault(options.DNS),
|
common.PtrValueOrDefault(options.DNS),
|
||||||
options.Inbounds,
|
options.Inbounds,
|
||||||
|
options.PlatformInterface,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "parse route options")
|
return nil, E.Cause(err, "parse route options")
|
||||||
|
@ -120,6 +128,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
||||||
router,
|
router,
|
||||||
logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
|
logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
|
||||||
inboundOptions,
|
inboundOptions,
|
||||||
|
options.PlatformInterface,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "parse inbound[", i, "]")
|
return nil, E.Cause(err, "parse inbound[", i, "]")
|
||||||
|
@ -255,19 +264,43 @@ func (s *Box) Close() error {
|
||||||
default:
|
default:
|
||||||
close(s.done)
|
close(s.done)
|
||||||
}
|
}
|
||||||
for _, in := range s.inbounds {
|
var errors error
|
||||||
in.Close()
|
for i, in := range s.inbounds {
|
||||||
|
errors = E.Append(errors, in.Close(), func(err error) error {
|
||||||
|
return E.Cause(err, "close inbound/", in.Type(), "[", i, "]")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
for _, out := range s.outbounds {
|
for i, out := range s.outbounds {
|
||||||
common.Close(out)
|
errors = E.Append(errors, common.Close(out), func(err error) error {
|
||||||
|
return E.Cause(err, "close inbound/", out.Type(), "[", i, "]")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return common.Close(
|
if err := common.Close(s.router); err != nil {
|
||||||
s.router,
|
errors = E.Append(errors, err, func(err error) error {
|
||||||
s.logFactory,
|
return E.Cause(err, "close router")
|
||||||
s.clashServer,
|
})
|
||||||
s.v2rayServer,
|
}
|
||||||
common.PtrOrNil(s.logFile),
|
if err := common.Close(s.logFactory); err != nil {
|
||||||
)
|
errors = E.Append(errors, err, func(err error) error {
|
||||||
|
return E.Cause(err, "close log factory")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err := common.Close(s.clashServer); err != nil {
|
||||||
|
errors = E.Append(errors, err, func(err error) error {
|
||||||
|
return E.Cause(err, "close clash api server")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err := common.Close(s.v2rayServer); err != nil {
|
||||||
|
errors = E.Append(errors, err, func(err error) error {
|
||||||
|
return E.Cause(err, "close v2ray api server")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if s.logFile != nil {
|
||||||
|
errors = E.Append(errors, s.logFile.Close(), func(err error) error {
|
||||||
|
return E.Cause(err, "close log file")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Box) Router() adapter.Router {
|
func (s *Box) Router() adapter.Router {
|
||||||
|
|
|
@ -4,11 +4,12 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/cmd/internal/build_shared"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
findSDK()
|
build_shared.FindSDK()
|
||||||
|
|
||||||
command := exec.Command(os.Args[1], os.Args[2:]...)
|
command := exec.Command(os.Args[1], os.Args[2:]...)
|
||||||
command.Stdout = os.Stdout
|
command.Stdout = os.Stdout
|
||||||
|
|
61
cmd/internal/build_libbox/main.go
Normal file
61
cmd/internal/build_libbox/main.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
_ "github.com/sagernet/gomobile/asset"
|
||||||
|
"github.com/sagernet/sing-box/cmd/internal/build_shared"
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
|
"github.com/sagernet/sing/common/rw"
|
||||||
|
)
|
||||||
|
|
||||||
|
var debugEnabled bool
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.BoolVar(&debugEnabled, "debug", false, "enable debug")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
build_shared.FindSDK()
|
||||||
|
build_shared.FindMobile()
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
"bind",
|
||||||
|
"-v",
|
||||||
|
"-androidapi", "21",
|
||||||
|
"-javapkg=io.nekohasekai",
|
||||||
|
"-libname=box",
|
||||||
|
}
|
||||||
|
if !debugEnabled {
|
||||||
|
args = append(args,
|
||||||
|
"-trimpath", "-ldflags=-s -w -buildid=",
|
||||||
|
"-tags", "with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api,debug",
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
args = append(args, "-tags", "with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api")
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, "./experimental/libbox")
|
||||||
|
|
||||||
|
command := exec.Command(build_shared.GoBinPath+"/gomobile", args...)
|
||||||
|
command.Stdout = os.Stdout
|
||||||
|
command.Stderr = os.Stderr
|
||||||
|
err := command.Run()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = "libbox.aar"
|
||||||
|
copyPath := filepath.Join("..", "sing-box-for-android", "app", "libs")
|
||||||
|
if rw.FileExists(copyPath) {
|
||||||
|
copyPath, _ = filepath.Abs(copyPath)
|
||||||
|
err = rw.CopyFile(name, filepath.Join(copyPath, name))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Info("copied to ", copyPath)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package build_shared
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"go/build"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -18,7 +19,7 @@ var (
|
||||||
androidNDKPath string
|
androidNDKPath string
|
||||||
)
|
)
|
||||||
|
|
||||||
func findSDK() {
|
func FindSDK() {
|
||||||
searchPath := []string{
|
searchPath := []string{
|
||||||
"$ANDROID_HOME",
|
"$ANDROID_HOME",
|
||||||
"$HOME/Android/Sdk",
|
"$HOME/Android/Sdk",
|
||||||
|
@ -79,3 +80,13 @@ func findNDK() bool {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var GoBinPath string
|
||||||
|
|
||||||
|
func FindMobile() {
|
||||||
|
goBin := filepath.Join(build.Default.GOPATH, "bin")
|
||||||
|
if !rw.FileExists(goBin + "/" + "gobind") {
|
||||||
|
log.Fatal("missing gomobile installation")
|
||||||
|
}
|
||||||
|
GoBinPath = goBin
|
||||||
|
}
|
|
@ -70,15 +70,7 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
||||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
listener.Control = control.Append(listener.Control, bindFunc)
|
||||||
} else if router.AutoDetectInterface() {
|
} else if router.AutoDetectInterface() {
|
||||||
const useInterfaceName = C.IsLinux
|
bindFunc := router.AutoDetectInterfaceFunc()
|
||||||
bindFunc := control.BindToInterfaceFunc(router.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int) {
|
|
||||||
remoteAddr := M.ParseSocksaddr(address).Addr
|
|
||||||
if C.IsLinux {
|
|
||||||
return router.InterfaceMonitor().DefaultInterfaceName(remoteAddr), -1
|
|
||||||
} else {
|
|
||||||
return "", router.InterfaceMonitor().DefaultInterfaceIndex(remoteAddr)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
listener.Control = control.Append(listener.Control, bindFunc)
|
||||||
} else if router.DefaultInterface() != "" {
|
} else if router.DefaultInterface() != "" {
|
||||||
|
|
|
@ -3,13 +3,28 @@ package constant
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/rw"
|
"github.com/sagernet/sing/common/rw"
|
||||||
)
|
)
|
||||||
|
|
||||||
const dirName = "sing-box"
|
const dirName = "sing-box"
|
||||||
|
|
||||||
var resourcePaths []string
|
var (
|
||||||
|
basePath string
|
||||||
|
resourcePaths []string
|
||||||
|
)
|
||||||
|
|
||||||
|
func BasePath(name string) string {
|
||||||
|
if basePath == "" || strings.HasPrefix(name, "/") {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
return filepath.Join(basePath, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetBasePath(path string) {
|
||||||
|
basePath = path
|
||||||
|
}
|
||||||
|
|
||||||
func FindPath(name string) (string, bool) {
|
func FindPath(name string) (string, bool) {
|
||||||
name = os.ExpandEnv(name)
|
name = os.ExpandEnv(name)
|
||||||
|
|
|
@ -42,7 +42,6 @@ type Server struct {
|
||||||
httpServer *http.Server
|
httpServer *http.Server
|
||||||
trafficManager *trafficontrol.Manager
|
trafficManager *trafficontrol.Manager
|
||||||
urlTestHistory *urltest.HistoryStorage
|
urlTestHistory *urltest.HistoryStorage
|
||||||
tcpListener net.Listener
|
|
||||||
mode string
|
mode string
|
||||||
storeSelected bool
|
storeSelected bool
|
||||||
cacheFile adapter.ClashCacheFile
|
cacheFile adapter.ClashCacheFile
|
||||||
|
@ -71,6 +70,11 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
||||||
if cachePath == "" {
|
if cachePath == "" {
|
||||||
cachePath = "cache.db"
|
cachePath = "cache.db"
|
||||||
}
|
}
|
||||||
|
if foundPath, loaded := C.FindPath(cachePath); loaded {
|
||||||
|
cachePath = foundPath
|
||||||
|
} else {
|
||||||
|
cachePath = C.BasePath(cachePath)
|
||||||
|
}
|
||||||
cacheFile, err := cachefile.Open(cachePath)
|
cacheFile, err := cachefile.Open(cachePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "open cache file")
|
return nil, E.Cause(err, "open cache file")
|
||||||
|
@ -103,7 +107,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
||||||
})
|
})
|
||||||
if options.ExternalUI != "" {
|
if options.ExternalUI != "" {
|
||||||
chiRouter.Group(func(r chi.Router) {
|
chiRouter.Group(func(r chi.Router) {
|
||||||
fs := http.StripPrefix("/ui", http.FileServer(http.Dir(os.ExpandEnv(options.ExternalUI))))
|
fs := http.StripPrefix("/ui", http.FileServer(http.Dir(C.BasePath(os.ExpandEnv(options.ExternalUI)))))
|
||||||
r.Get("/ui", http.RedirectHandler("/ui/", http.StatusTemporaryRedirect).ServeHTTP)
|
r.Get("/ui", http.RedirectHandler("/ui/", http.StatusTemporaryRedirect).ServeHTTP)
|
||||||
r.Get("/ui/*", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/ui/*", func(w http.ResponseWriter, r *http.Request) {
|
||||||
fs.ServeHTTP(w, r)
|
fs.ServeHTTP(w, r)
|
||||||
|
@ -119,7 +123,6 @@ func (s *Server) Start() error {
|
||||||
return E.Cause(err, "external controller listen error")
|
return E.Cause(err, "external controller listen error")
|
||||||
}
|
}
|
||||||
s.logger.Info("restful api listening at ", listener.Addr())
|
s.logger.Info("restful api listening at ", listener.Addr())
|
||||||
s.tcpListener = listener
|
|
||||||
go func() {
|
go func() {
|
||||||
err = s.httpServer.Serve(listener)
|
err = s.httpServer.Serve(listener)
|
||||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
@ -132,7 +135,6 @@ func (s *Server) Start() error {
|
||||||
func (s *Server) Close() error {
|
func (s *Server) Close() error {
|
||||||
return common.Close(
|
return common.Close(
|
||||||
common.PtrOrNil(s.httpServer),
|
common.PtrOrNil(s.httpServer),
|
||||||
s.tcpListener,
|
|
||||||
s.trafficManager,
|
s.trafficManager,
|
||||||
s.cacheFile,
|
s.cacheFile,
|
||||||
)
|
)
|
||||||
|
|
15
experimental/libbox/config.go
Normal file
15
experimental/libbox/config.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package libbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseConfig(configContent string) (option.Options, error) {
|
||||||
|
var options option.Options
|
||||||
|
err := options.UnmarshalJSON([]byte(configContent))
|
||||||
|
if err != nil {
|
||||||
|
return option.Options{}, E.Cause(err, "decode config")
|
||||||
|
}
|
||||||
|
return options, nil
|
||||||
|
}
|
148
experimental/libbox/internal/procfs/procfs.go
Normal file
148
experimental/libbox/internal/procfs/procfs.go
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
package procfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
netIndexOfLocal = -1
|
||||||
|
netIndexOfUid = -1
|
||||||
|
nativeEndian binary.ByteOrder
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var x uint32 = 0x01020304
|
||||||
|
if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
|
||||||
|
nativeEndian = binary.BigEndian
|
||||||
|
} else {
|
||||||
|
nativeEndian = binary.LittleEndian
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolveSocketByProcSearch(network string, source, _ netip.AddrPort) int32 {
|
||||||
|
if netIndexOfLocal < 0 || netIndexOfUid < 0 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
path := "/proc/net/"
|
||||||
|
|
||||||
|
if network == N.NetworkTCP {
|
||||||
|
path += "tcp"
|
||||||
|
} else {
|
||||||
|
path += "udp"
|
||||||
|
}
|
||||||
|
|
||||||
|
if source.Addr().Is6() {
|
||||||
|
path += "6"
|
||||||
|
}
|
||||||
|
|
||||||
|
sIP := source.Addr().AsSlice()
|
||||||
|
if len(sIP) == 0 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes [2]byte
|
||||||
|
binary.BigEndian.PutUint16(bytes[:], source.Port())
|
||||||
|
local := fmt.Sprintf("%s:%s", hex.EncodeToString(nativeEndianIP(sIP)), hex.EncodeToString(bytes[:]))
|
||||||
|
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
reader := bufio.NewReader(file)
|
||||||
|
|
||||||
|
for {
|
||||||
|
row, _, err := reader.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := strings.Fields(string(row))
|
||||||
|
|
||||||
|
if len(fields) <= netIndexOfLocal || len(fields) <= netIndexOfUid {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.EqualFold(local, fields[netIndexOfLocal]) {
|
||||||
|
uid, err := strconv.Atoi(fields[netIndexOfUid])
|
||||||
|
if err != nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return int32(uid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func nativeEndianIP(ip net.IP) []byte {
|
||||||
|
result := make([]byte, len(ip))
|
||||||
|
|
||||||
|
for i := 0; i < len(ip); i += 4 {
|
||||||
|
value := binary.BigEndian.Uint32(ip[i:])
|
||||||
|
|
||||||
|
nativeEndian.PutUint32(result[i:], value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
file, err := os.Open("/proc/net/tcp")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
reader := bufio.NewReader(file)
|
||||||
|
|
||||||
|
header, _, err := reader.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
columns := strings.Fields(string(header))
|
||||||
|
|
||||||
|
var txQueue, rxQueue, tr, tmWhen bool
|
||||||
|
|
||||||
|
for idx, col := range columns {
|
||||||
|
offset := 0
|
||||||
|
|
||||||
|
if txQueue && rxQueue {
|
||||||
|
offset--
|
||||||
|
}
|
||||||
|
|
||||||
|
if tr && tmWhen {
|
||||||
|
offset--
|
||||||
|
}
|
||||||
|
|
||||||
|
switch col {
|
||||||
|
case "tx_queue":
|
||||||
|
txQueue = true
|
||||||
|
case "rx_queue":
|
||||||
|
rxQueue = true
|
||||||
|
case "tr":
|
||||||
|
tr = true
|
||||||
|
case "tm->when":
|
||||||
|
tmWhen = true
|
||||||
|
case "local_address":
|
||||||
|
netIndexOfLocal = idx + offset
|
||||||
|
case "uid":
|
||||||
|
netIndexOfUid = idx + offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
experimental/libbox/iterator.go
Normal file
31
experimental/libbox/iterator.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package libbox
|
||||||
|
|
||||||
|
import "github.com/sagernet/sing/common"
|
||||||
|
|
||||||
|
type StringIterator interface {
|
||||||
|
Next() string
|
||||||
|
HasNext() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ StringIterator = (*iterator[string])(nil)
|
||||||
|
|
||||||
|
type iterator[T any] struct {
|
||||||
|
values []T
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIterator[T any](values []T) *iterator[T] {
|
||||||
|
return &iterator[T]{values}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *iterator[T]) Next() T {
|
||||||
|
if len(i.values) == 0 {
|
||||||
|
return common.DefaultValue[T]()
|
||||||
|
}
|
||||||
|
nextValue := i.values[0]
|
||||||
|
i.values = i.values[1:]
|
||||||
|
return nextValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *iterator[T]) HasNext() bool {
|
||||||
|
return len(i.values) > 0
|
||||||
|
}
|
16
experimental/libbox/platform.go
Normal file
16
experimental/libbox/platform.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package libbox
|
||||||
|
|
||||||
|
type PlatformInterface interface {
|
||||||
|
AutoDetectInterfaceControl(fd int32) error
|
||||||
|
OpenTun(options TunOptions) (TunInterface, error)
|
||||||
|
WriteLog(message string)
|
||||||
|
UseProcFS() bool
|
||||||
|
FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int32, destinationAddress string, destinationPort int32) (int32, error)
|
||||||
|
PackageNameByUid(uid int32) (string, error)
|
||||||
|
UIDByPackageName(packageName string) (int32, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TunInterface interface {
|
||||||
|
FileDescriptor() int32
|
||||||
|
Close() error
|
||||||
|
}
|
16
experimental/libbox/platform/interface.go
Normal file
16
experimental/libbox/platform/interface.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package platform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/common/process"
|
||||||
|
"github.com/sagernet/sing-tun"
|
||||||
|
"github.com/sagernet/sing/common/control"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Interface interface {
|
||||||
|
AutoDetectInterfaceControl() control.Func
|
||||||
|
OpenTun(options tun.Options) (tun.Tun, error)
|
||||||
|
process.Searcher
|
||||||
|
io.Writer
|
||||||
|
}
|
35
experimental/libbox/pprof.go
Normal file
35
experimental/libbox/pprof.go
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
//go:build debug
|
||||||
|
|
||||||
|
package libbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
_ "net/http/pprof"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PProfServer struct {
|
||||||
|
server *http.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPProfServer(port int) *PProfServer {
|
||||||
|
return &PProfServer{
|
||||||
|
&http.Server{
|
||||||
|
Addr: ":" + strconv.Itoa(port),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PProfServer) Start() error {
|
||||||
|
ln, err := net.Listen("tcp", s.server.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go s.server.Serve(ln)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PProfServer) Close() error {
|
||||||
|
return s.server.Close()
|
||||||
|
}
|
21
experimental/libbox/pprof_stub.go
Normal file
21
experimental/libbox/pprof_stub.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
//go:build !debug
|
||||||
|
|
||||||
|
package libbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PProfServer struct{}
|
||||||
|
|
||||||
|
func NewPProfServer(port int) *PProfServer {
|
||||||
|
return &PProfServer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PProfServer) Start() error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PProfServer) Close() error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
120
experimental/libbox/service.go
Normal file
120
experimental/libbox/service.go
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
package libbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/netip"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box"
|
||||||
|
"github.com/sagernet/sing-box/common/process"
|
||||||
|
"github.com/sagernet/sing-box/experimental/libbox/internal/procfs"
|
||||||
|
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||||
|
"github.com/sagernet/sing-tun"
|
||||||
|
"github.com/sagernet/sing/common/control"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BoxService struct {
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
instance *box.Box
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) {
|
||||||
|
options, err := parseConfig(configContent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
options.PlatformInterface = &platformInterfaceWrapper{platformInterface, platformInterface.UseProcFS()}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
instance, err := box.New(ctx, options)
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
return nil, E.Cause(err, "create service")
|
||||||
|
}
|
||||||
|
return &BoxService{
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
instance: instance,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BoxService) Start() error {
|
||||||
|
return s.instance.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BoxService) Close() error {
|
||||||
|
s.cancel()
|
||||||
|
return s.instance.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ platform.Interface = (*platformInterfaceWrapper)(nil)
|
||||||
|
|
||||||
|
type platformInterfaceWrapper struct {
|
||||||
|
iif PlatformInterface
|
||||||
|
useProcFS bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *platformInterfaceWrapper) AutoDetectInterfaceControl() control.Func {
|
||||||
|
return func(network, address string, conn syscall.RawConn) error {
|
||||||
|
return control.Raw(conn, func(fd uintptr) error {
|
||||||
|
return w.iif.AutoDetectInterfaceControl(int32(fd))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *platformInterfaceWrapper) OpenTun(options tun.Options) (tun.Tun, error) {
|
||||||
|
if len(options.IncludeUID) > 0 || len(options.ExcludeUID) > 0 {
|
||||||
|
return nil, E.New("android: unsupported uid options")
|
||||||
|
}
|
||||||
|
if len(options.IncludeAndroidUser) > 0 {
|
||||||
|
return nil, E.New("android: unsupported android_user option")
|
||||||
|
}
|
||||||
|
|
||||||
|
optionsWrapper := tunOptions(options)
|
||||||
|
tunInterface, err := w.iif.OpenTun(&optionsWrapper)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tunFd := tunInterface.FileDescriptor()
|
||||||
|
return &nativeTun{
|
||||||
|
tunFd: int(tunFd),
|
||||||
|
tunFile: os.NewFile(uintptr(tunFd), "tun"),
|
||||||
|
tunMTU: options.MTU,
|
||||||
|
closer: tunInterface,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *platformInterfaceWrapper) Write(p []byte) (n int, err error) {
|
||||||
|
w.iif.WriteLog(string(p))
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *platformInterfaceWrapper) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*process.Info, error) {
|
||||||
|
var uid int32
|
||||||
|
if w.useProcFS {
|
||||||
|
uid = procfs.ResolveSocketByProcSearch(network, source, destination)
|
||||||
|
if uid == -1 {
|
||||||
|
return nil, E.New("procfs: not found")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var ipProtocol int32
|
||||||
|
switch N.NetworkName(network) {
|
||||||
|
case N.NetworkTCP:
|
||||||
|
ipProtocol = syscall.IPPROTO_TCP
|
||||||
|
case N.NetworkUDP:
|
||||||
|
ipProtocol = syscall.IPPROTO_UDP
|
||||||
|
default:
|
||||||
|
return nil, E.New("unknown network: ", network)
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
uid, err = w.iif.FindConnectionOwner(ipProtocol, source.Addr().String(), int32(source.Port()), destination.Addr().String(), int32(destination.Port()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
packageName, _ := w.iif.PackageNameByUid(uid)
|
||||||
|
return &process.Info{UserId: uid, PackageName: packageName}, nil
|
||||||
|
}
|
7
experimental/libbox/setup.go
Normal file
7
experimental/libbox/setup.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package libbox
|
||||||
|
|
||||||
|
import C "github.com/sagernet/sing-box/constant"
|
||||||
|
|
||||||
|
func SetBasePath(path string) {
|
||||||
|
C.SetBasePath(path)
|
||||||
|
}
|
109
experimental/libbox/tun.go
Normal file
109
experimental/libbox/tun.go
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
package libbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/netip"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-tun"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TunOptions interface {
|
||||||
|
GetInet4Address() RoutePrefixIterator
|
||||||
|
GetInet6Address() RoutePrefixIterator
|
||||||
|
GetDNSServerAddress() (string, error)
|
||||||
|
GetMTU() int32
|
||||||
|
GetAutoRoute() bool
|
||||||
|
GetStrictRoute() bool
|
||||||
|
GetInet4RouteAddress() RoutePrefixIterator
|
||||||
|
GetInet6RouteAddress() RoutePrefixIterator
|
||||||
|
GetIncludePackage() StringIterator
|
||||||
|
GetExcludePackage() StringIterator
|
||||||
|
}
|
||||||
|
|
||||||
|
type RoutePrefix struct {
|
||||||
|
Address string
|
||||||
|
Prefix int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type RoutePrefixIterator interface {
|
||||||
|
Next() *RoutePrefix
|
||||||
|
HasNext() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapRoutePrefix(prefixes []netip.Prefix) RoutePrefixIterator {
|
||||||
|
return newIterator(common.Map(prefixes, func(prefix netip.Prefix) *RoutePrefix {
|
||||||
|
return &RoutePrefix{
|
||||||
|
Address: prefix.Addr().String(),
|
||||||
|
Prefix: int32(prefix.Bits()),
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TunOptions = (*tunOptions)(nil)
|
||||||
|
|
||||||
|
type tunOptions tun.Options
|
||||||
|
|
||||||
|
func (o *tunOptions) GetInet4Address() RoutePrefixIterator {
|
||||||
|
return mapRoutePrefix(o.Inet4Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *tunOptions) GetInet6Address() RoutePrefixIterator {
|
||||||
|
return mapRoutePrefix(o.Inet6Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *tunOptions) GetDNSServerAddress() (string, error) {
|
||||||
|
if len(o.Inet4Address) == 0 || o.Inet4Address[0].Bits() == 32 {
|
||||||
|
return "", E.New("need one more IPv4 address for DNS hijacking")
|
||||||
|
}
|
||||||
|
return o.Inet4Address[0].Addr().Next().String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *tunOptions) GetMTU() int32 {
|
||||||
|
return int32(o.MTU)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *tunOptions) GetAutoRoute() bool {
|
||||||
|
return o.AutoRoute
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *tunOptions) GetStrictRoute() bool {
|
||||||
|
return o.StrictRoute
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *tunOptions) GetInet4RouteAddress() RoutePrefixIterator {
|
||||||
|
return mapRoutePrefix(o.Inet4RouteAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *tunOptions) GetInet6RouteAddress() RoutePrefixIterator {
|
||||||
|
return mapRoutePrefix(o.Inet6RouteAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *tunOptions) GetIncludePackage() StringIterator {
|
||||||
|
return newIterator(o.IncludePackage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *tunOptions) GetExcludePackage() StringIterator {
|
||||||
|
return newIterator(o.ExcludePackage)
|
||||||
|
}
|
||||||
|
|
||||||
|
type nativeTun struct {
|
||||||
|
tunFd int
|
||||||
|
tunFile *os.File
|
||||||
|
tunMTU uint32
|
||||||
|
closer io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *nativeTun) Read(p []byte) (n int, err error) {
|
||||||
|
return t.tunFile.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *nativeTun) Write(p []byte) (n int, err error) {
|
||||||
|
return t.tunFile.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *nativeTun) Close() error {
|
||||||
|
return t.closer.Close()
|
||||||
|
}
|
19
experimental/libbox/tun_gvisor.go
Normal file
19
experimental/libbox/tun_gvisor.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
//go:build with_gvisor && linux
|
||||||
|
|
||||||
|
package libbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sagernet/sing-tun"
|
||||||
|
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ tun.GVisorTun = (*nativeTun)(nil)
|
||||||
|
|
||||||
|
func (t *nativeTun) NewEndpoint() (stack.LinkEndpoint, error) {
|
||||||
|
return fdbased.New(&fdbased.Options{
|
||||||
|
FDs: []int{t.tunFd},
|
||||||
|
MTU: t.tunMTU,
|
||||||
|
})
|
||||||
|
}
|
1
go.mod
1
go.mod
|
@ -22,6 +22,7 @@ require (
|
||||||
github.com/pires/go-proxyproto v0.6.2
|
github.com/pires/go-proxyproto v0.6.2
|
||||||
github.com/refraction-networking/utls v1.2.2
|
github.com/refraction-networking/utls v1.2.2
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0
|
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0
|
||||||
|
github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca
|
||||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
|
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
|
||||||
github.com/sagernet/sing v0.1.7-0.20230209132010-5f1ef3441c13
|
github.com/sagernet/sing v0.1.7-0.20230209132010-5f1ef3441c13
|
||||||
github.com/sagernet/sing-dns v0.1.2-0.20230209132355-3c2e2957b455
|
github.com/sagernet/sing-dns v0.1.2-0.20230209132355-3c2e2957b455
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -119,6 +119,8 @@ github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 h1:KyhtFFt
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
|
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
|
||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
||||||
|
github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca h1:w56+kf8BeqLqllrRJ1tdwKc3sCdWOn/DuNHpY9fAiqs=
|
||||||
|
github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU=
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY=
|
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY=
|
||||||
|
|
|
@ -5,18 +5,19 @@ import (
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
)
|
)
|
||||||
|
|
||||||
func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.Inbound) (adapter.Inbound, error) {
|
func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.Inbound, platformInterface platform.Interface) (adapter.Inbound, error) {
|
||||||
if options.Type == "" {
|
if options.Type == "" {
|
||||||
return nil, E.New("missing inbound type")
|
return nil, E.New("missing inbound type")
|
||||||
}
|
}
|
||||||
switch options.Type {
|
switch options.Type {
|
||||||
case C.TypeTun:
|
case C.TypeTun:
|
||||||
return NewTun(ctx, router, logger, options.Tag, options.TunOptions)
|
return NewTun(ctx, router, logger, options.Tag, options.TunOptions, platformInterface)
|
||||||
case C.TypeRedirect:
|
case C.TypeRedirect:
|
||||||
return NewRedirect(ctx, router, logger, options.Tag, options.RedirectOptions), nil
|
return NewRedirect(ctx, router, logger, options.Tag, options.RedirectOptions), nil
|
||||||
case C.TypeTProxy:
|
case C.TypeTProxy:
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/canceler"
|
"github.com/sagernet/sing-box/common/canceler"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
|
@ -34,9 +35,10 @@ type Tun struct {
|
||||||
stack string
|
stack string
|
||||||
tunIf tun.Tun
|
tunIf tun.Tun
|
||||||
tunStack tun.Stack
|
tunStack tun.Stack
|
||||||
|
platformInterface platform.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions) (*Tun, error) {
|
func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions, platformInterface platform.Interface) (*Tun, error) {
|
||||||
tunName := options.InterfaceName
|
tunName := options.InterfaceName
|
||||||
if tunName == "" {
|
if tunName == "" {
|
||||||
tunName = tun.CalculateInterfaceName("")
|
tunName = tun.CalculateInterfaceName("")
|
||||||
|
@ -93,6 +95,7 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
||||||
endpointIndependentNat: options.EndpointIndependentNat,
|
endpointIndependentNat: options.EndpointIndependentNat,
|
||||||
udpTimeout: udpTimeout,
|
udpTimeout: udpTimeout,
|
||||||
stack: options.Stack,
|
stack: options.Stack,
|
||||||
|
platformInterface: platformInterface,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,17 +140,25 @@ func (t *Tun) Tag() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tun) Start() error {
|
func (t *Tun) Start() error {
|
||||||
if C.IsAndroid {
|
if C.IsAndroid && t.platformInterface == nil {
|
||||||
t.tunOptions.BuildAndroidRules(t.router.PackageManager(), t)
|
t.tunOptions.BuildAndroidRules(t.router.PackageManager(), t)
|
||||||
}
|
}
|
||||||
tunIf, err := tun.Open(t.tunOptions)
|
var (
|
||||||
|
tunInterface tun.Tun
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if t.platformInterface != nil {
|
||||||
|
tunInterface, err = t.platformInterface.OpenTun(t.tunOptions)
|
||||||
|
} else {
|
||||||
|
tunInterface, err = tun.Open(t.tunOptions)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "configure tun interface")
|
return E.Cause(err, "configure tun interface")
|
||||||
}
|
}
|
||||||
t.tunIf = tunIf
|
t.tunIf = tunInterface
|
||||||
t.tunStack, err = tun.NewStack(t.stack, tun.StackOptions{
|
t.tunStack, err = tun.NewStack(t.stack, tun.StackOptions{
|
||||||
Context: t.ctx,
|
Context: t.ctx,
|
||||||
Tun: tunIf,
|
Tun: tunInterface,
|
||||||
MTU: t.tunOptions.MTU,
|
MTU: t.tunOptions.MTU,
|
||||||
Name: t.tunOptions.Name,
|
Name: t.tunOptions.Name,
|
||||||
Inet4Address: t.tunOptions.Inet4Address,
|
Inet4Address: t.tunOptions.Inet4Address,
|
||||||
|
|
|
@ -13,14 +13,20 @@ var _ Factory = (*simpleFactory)(nil)
|
||||||
|
|
||||||
type simpleFactory struct {
|
type simpleFactory struct {
|
||||||
formatter Formatter
|
formatter Formatter
|
||||||
|
platformFormatter Formatter
|
||||||
writer io.Writer
|
writer io.Writer
|
||||||
|
platformWriter io.Writer
|
||||||
level Level
|
level Level
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactory(formatter Formatter, writer io.Writer) Factory {
|
func NewFactory(formatter Formatter, writer io.Writer, platformWriter io.Writer) Factory {
|
||||||
return &simpleFactory{
|
return &simpleFactory{
|
||||||
formatter: formatter,
|
formatter: formatter,
|
||||||
|
platformFormatter: Formatter{
|
||||||
|
BaseTime: formatter.BaseTime,
|
||||||
|
},
|
||||||
writer: writer,
|
writer: writer,
|
||||||
|
platformWriter: platformWriter,
|
||||||
level: LevelTrace,
|
level: LevelTrace,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +59,8 @@ func (l *simpleLogger) Log(ctx context.Context, level Level, args []any) {
|
||||||
if level > l.level {
|
if level > l.level {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
message := l.formatter.Format(ctx, level, l.tag, F.ToString(args...), time.Now())
|
nowTime := time.Now()
|
||||||
|
message := l.formatter.Format(ctx, level, l.tag, F.ToString(args...), nowTime)
|
||||||
if level == LevelPanic {
|
if level == LevelPanic {
|
||||||
panic(message)
|
panic(message)
|
||||||
}
|
}
|
||||||
|
@ -61,6 +68,9 @@ func (l *simpleLogger) Log(ctx context.Context, level Level, args []any) {
|
||||||
if level == LevelFatal {
|
if level == LevelFatal {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
if l.platformWriter != nil {
|
||||||
|
l.platformWriter.Write([]byte(l.platformFormatter.Format(ctx, level, l.tag, F.ToString(args...), nowTime)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *simpleLogger) Trace(args ...any) {
|
func (l *simpleLogger) Trace(args ...any) {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
var std ContextLogger
|
var std ContextLogger
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
std = NewFactory(Formatter{BaseTime: time.Now()}, os.Stderr).Logger()
|
std = NewFactory(Formatter{BaseTime: time.Now()}, os.Stderr, nil).Logger()
|
||||||
}
|
}
|
||||||
|
|
||||||
func StdLogger() ContextLogger {
|
func StdLogger() ContextLogger {
|
||||||
|
|
|
@ -15,16 +15,22 @@ var _ Factory = (*observableFactory)(nil)
|
||||||
|
|
||||||
type observableFactory struct {
|
type observableFactory struct {
|
||||||
formatter Formatter
|
formatter Formatter
|
||||||
|
platformFormatter Formatter
|
||||||
writer io.Writer
|
writer io.Writer
|
||||||
|
platformWriter io.Writer
|
||||||
level Level
|
level Level
|
||||||
subscriber *observable.Subscriber[Entry]
|
subscriber *observable.Subscriber[Entry]
|
||||||
observer *observable.Observer[Entry]
|
observer *observable.Observer[Entry]
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewObservableFactory(formatter Formatter, writer io.Writer) ObservableFactory {
|
func NewObservableFactory(formatter Formatter, writer io.Writer, platformWriter io.Writer) ObservableFactory {
|
||||||
factory := &observableFactory{
|
factory := &observableFactory{
|
||||||
formatter: formatter,
|
formatter: formatter,
|
||||||
|
platformFormatter: Formatter{
|
||||||
|
BaseTime: formatter.BaseTime,
|
||||||
|
},
|
||||||
writer: writer,
|
writer: writer,
|
||||||
|
platformWriter: platformWriter,
|
||||||
level: LevelTrace,
|
level: LevelTrace,
|
||||||
subscriber: observable.NewSubscriber[Entry](128),
|
subscriber: observable.NewSubscriber[Entry](128),
|
||||||
}
|
}
|
||||||
|
@ -74,7 +80,8 @@ func (l *observableLogger) Log(ctx context.Context, level Level, args []any) {
|
||||||
if level > l.level {
|
if level > l.level {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
message, messageSimple := l.formatter.FormatWithSimple(ctx, level, l.tag, F.ToString(args...), time.Now())
|
nowTime := time.Now()
|
||||||
|
message, messageSimple := l.formatter.FormatWithSimple(ctx, level, l.tag, F.ToString(args...), nowTime)
|
||||||
if level == LevelPanic {
|
if level == LevelPanic {
|
||||||
panic(message)
|
panic(message)
|
||||||
}
|
}
|
||||||
|
@ -83,6 +90,9 @@ func (l *observableLogger) Log(ctx context.Context, level Level, args []any) {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
l.subscriber.Emit(Entry{level, messageSimple})
|
l.subscriber.Emit(Entry{level, messageSimple})
|
||||||
|
if l.platformWriter != nil {
|
||||||
|
l.platformWriter.Write([]byte(l.formatter.Format(ctx, level, l.tag, F.ToString(args...), nowTime)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *observableLogger) Trace(args ...any) {
|
func (l *observableLogger) Trace(args ...any) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/json"
|
"github.com/sagernet/sing-box/common/json"
|
||||||
|
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,6 +16,7 @@ type _Options struct {
|
||||||
Outbounds []Outbound `json:"outbounds,omitempty"`
|
Outbounds []Outbound `json:"outbounds,omitempty"`
|
||||||
Route *RouteOptions `json:"route,omitempty"`
|
Route *RouteOptions `json:"route,omitempty"`
|
||||||
Experimental *ExperimentalOptions `json:"experimental,omitempty"`
|
Experimental *ExperimentalOptions `json:"experimental,omitempty"`
|
||||||
|
PlatformInterface platform.Interface `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Options _Options
|
type Options _Options
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/sagernet/sing-box/common/sniff"
|
"github.com/sagernet/sing-box/common/sniff"
|
||||||
"github.com/sagernet/sing-box/common/warning"
|
"github.com/sagernet/sing-box/common/warning"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-dns"
|
"github.com/sagernet/sing-dns"
|
||||||
|
@ -97,9 +98,10 @@ type Router struct {
|
||||||
processSearcher process.Searcher
|
processSearcher process.Searcher
|
||||||
clashServer adapter.ClashServer
|
clashServer adapter.ClashServer
|
||||||
v2rayServer adapter.V2RayServer
|
v2rayServer adapter.V2RayServer
|
||||||
|
platformInterface platform.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRouter(ctx context.Context, logFactory log.Factory, options option.RouteOptions, dnsOptions option.DNSOptions, inbounds []option.Inbound) (*Router, error) {
|
func NewRouter(ctx context.Context, logFactory log.Factory, options option.RouteOptions, dnsOptions option.DNSOptions, inbounds []option.Inbound, platformInterface platform.Interface) (*Router, error) {
|
||||||
if options.DefaultInterface != "" {
|
if options.DefaultInterface != "" {
|
||||||
warnDefaultInterfaceOnUnsupportedPlatform.Check()
|
warnDefaultInterfaceOnUnsupportedPlatform.Check()
|
||||||
}
|
}
|
||||||
|
@ -127,6 +129,7 @@ func NewRouter(ctx context.Context, logFactory log.Factory, options option.Route
|
||||||
autoDetectInterface: options.AutoDetectInterface,
|
autoDetectInterface: options.AutoDetectInterface,
|
||||||
defaultInterface: options.DefaultInterface,
|
defaultInterface: options.DefaultInterface,
|
||||||
defaultMark: options.DefaultMark,
|
defaultMark: options.DefaultMark,
|
||||||
|
platformInterface: platformInterface,
|
||||||
}
|
}
|
||||||
router.dnsClient = dns.NewClient(dnsOptions.DNSClientOptions.DisableCache, dnsOptions.DNSClientOptions.DisableExpire, router.dnsLogger)
|
router.dnsClient = dns.NewClient(dnsOptions.DNSClientOptions.DisableCache, dnsOptions.DNSClientOptions.DisableExpire, router.dnsLogger)
|
||||||
for i, ruleOptions := range options.Rules {
|
for i, ruleOptions := range options.Rules {
|
||||||
|
@ -248,9 +251,9 @@ func NewRouter(ctx context.Context, logFactory log.Factory, options option.Route
|
||||||
router.transportMap = transportMap
|
router.transportMap = transportMap
|
||||||
router.transportDomainStrategy = transportDomainStrategy
|
router.transportDomainStrategy = transportDomainStrategy
|
||||||
|
|
||||||
needInterfaceMonitor := options.AutoDetectInterface || common.Any(inbounds, func(inbound option.Inbound) bool {
|
needInterfaceMonitor := platformInterface == nil && (options.AutoDetectInterface || common.Any(inbounds, func(inbound option.Inbound) bool {
|
||||||
return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy || inbound.TunOptions.AutoRoute
|
return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy || inbound.TunOptions.AutoRoute
|
||||||
})
|
}))
|
||||||
|
|
||||||
if needInterfaceMonitor {
|
if needInterfaceMonitor {
|
||||||
networkMonitor, err := tun.NewNetworkUpdateMonitor(router)
|
networkMonitor, err := tun.NewNetworkUpdateMonitor(router)
|
||||||
|
@ -272,7 +275,7 @@ func NewRouter(ctx context.Context, logFactory log.Factory, options option.Route
|
||||||
}
|
}
|
||||||
|
|
||||||
needFindProcess := hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess
|
needFindProcess := hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess
|
||||||
needPackageManager := C.IsAndroid && (needFindProcess || common.Any(inbounds, func(inbound option.Inbound) bool {
|
needPackageManager := C.IsAndroid && platformInterface == nil && (needFindProcess || common.Any(inbounds, func(inbound option.Inbound) bool {
|
||||||
return len(inbound.TunOptions.IncludePackage) > 0 || len(inbound.TunOptions.ExcludePackage) > 0
|
return len(inbound.TunOptions.IncludePackage) > 0 || len(inbound.TunOptions.ExcludePackage) > 0
|
||||||
}))
|
}))
|
||||||
if needPackageManager {
|
if needPackageManager {
|
||||||
|
@ -283,6 +286,9 @@ func NewRouter(ctx context.Context, logFactory log.Factory, options option.Route
|
||||||
router.packageManager = packageManager
|
router.packageManager = packageManager
|
||||||
}
|
}
|
||||||
if needFindProcess {
|
if needFindProcess {
|
||||||
|
if platformInterface != nil {
|
||||||
|
router.processSearcher = platformInterface
|
||||||
|
} else {
|
||||||
searcher, err := process.NewSearcher(process.Config{
|
searcher, err := process.NewSearcher(process.Config{
|
||||||
Logger: logFactory.NewLogger("router/process"),
|
Logger: logFactory.NewLogger("router/process"),
|
||||||
PackageManager: router.packageManager,
|
PackageManager: router.packageManager,
|
||||||
|
@ -295,6 +301,7 @@ func NewRouter(ctx context.Context, logFactory log.Factory, options option.Route
|
||||||
router.processSearcher = searcher
|
router.processSearcher = searcher
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return router, nil
|
return router, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -737,6 +744,21 @@ func (r *Router) AutoDetectInterface() bool {
|
||||||
return r.autoDetectInterface
|
return r.autoDetectInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Router) AutoDetectInterfaceFunc() control.Func {
|
||||||
|
if r.platformInterface != nil {
|
||||||
|
return r.platformInterface.AutoDetectInterfaceControl()
|
||||||
|
} else {
|
||||||
|
return control.BindToInterfaceFunc(r.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int) {
|
||||||
|
remoteAddr := M.ParseSocksaddr(address).Addr
|
||||||
|
if C.IsLinux {
|
||||||
|
return r.InterfaceMonitor().DefaultInterfaceName(remoteAddr), -1
|
||||||
|
} else {
|
||||||
|
return "", r.InterfaceMonitor().DefaultInterfaceIndex(remoteAddr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Router) DefaultInterface() string {
|
func (r *Router) DefaultInterface() string {
|
||||||
return r.defaultInterface
|
return r.defaultInterface
|
||||||
}
|
}
|
||||||
|
@ -849,6 +871,8 @@ func (r *Router) prepareGeoIPDatabase() error {
|
||||||
geoPath = "geoip.db"
|
geoPath = "geoip.db"
|
||||||
if foundPath, loaded := C.FindPath(geoPath); loaded {
|
if foundPath, loaded := C.FindPath(geoPath); loaded {
|
||||||
geoPath = foundPath
|
geoPath = foundPath
|
||||||
|
} else {
|
||||||
|
geoPath = C.BasePath(geoPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !rw.FileExists(geoPath) {
|
if !rw.FileExists(geoPath) {
|
||||||
|
@ -861,7 +885,7 @@ func (r *Router) prepareGeoIPDatabase() error {
|
||||||
}
|
}
|
||||||
r.logger.Error("download geoip database: ", err)
|
r.logger.Error("download geoip database: ", err)
|
||||||
os.Remove(geoPath)
|
os.Remove(geoPath)
|
||||||
time.Sleep(10 * time.Second)
|
// time.Sleep(10 * time.Second)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -884,6 +908,8 @@ func (r *Router) prepareGeositeDatabase() error {
|
||||||
geoPath = "geosite.db"
|
geoPath = "geosite.db"
|
||||||
if foundPath, loaded := C.FindPath(geoPath); loaded {
|
if foundPath, loaded := C.FindPath(geoPath); loaded {
|
||||||
geoPath = foundPath
|
geoPath = foundPath
|
||||||
|
} else {
|
||||||
|
geoPath = C.BasePath(geoPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !rw.FileExists(geoPath) {
|
if !rw.FileExists(geoPath) {
|
||||||
|
@ -896,7 +922,7 @@ func (r *Router) prepareGeositeDatabase() error {
|
||||||
}
|
}
|
||||||
r.logger.Error("download geosite database: ", err)
|
r.logger.Error("download geosite database: ", err)
|
||||||
os.Remove(geoPath)
|
os.Remove(geoPath)
|
||||||
time.Sleep(10 * time.Second)
|
// time.Sleep(10 * time.Second)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -950,6 +976,7 @@ func (r *Router) downloadGeoIPDatabase(savePath string) error {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
defer httpClient.CloseIdleConnections()
|
||||||
response, err := httpClient.Get(downloadURL)
|
response, err := httpClient.Get(downloadURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -997,6 +1024,7 @@ func (r *Router) downloadGeositeDatabase(savePath string) error {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
defer httpClient.CloseIdleConnections()
|
||||||
response, err := httpClient.Get(downloadURL)
|
response, err := httpClient.Get(downloadURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
Loading…
Reference in a new issue