mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-12 01:57:18 +10:00
Compare commits
63 Commits
v1.2-beta3
...
v1.2-beta7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87dd328700 | ||
|
|
0cec92dd0f | ||
|
|
e88afa9665 | ||
|
|
3883a81315 | ||
|
|
c919ad079a | ||
|
|
83593aee70 | ||
|
|
ac7cc09694 | ||
|
|
d032e3568b | ||
|
|
c24df037ac | ||
|
|
a2d43b3746 | ||
|
|
5b3b74bd0f | ||
|
|
d24d3b26dc | ||
|
|
5db3cd7781 | ||
|
|
c88af8b081 | ||
|
|
45852ca3e7 | ||
|
|
03ce555104 | ||
|
|
dd0a07624e | ||
|
|
b9b2b77814 | ||
|
|
2366835121 | ||
|
|
42e1dea7d2 | ||
|
|
13d7716b02 | ||
|
|
7ecb9fc738 | ||
|
|
19b15e0d10 | ||
|
|
0b15de461b | ||
|
|
27aba99e6c | ||
|
|
8151bcfd6b | ||
|
|
e8802357e1 | ||
|
|
6e22c004f6 | ||
|
|
20e1caa531 | ||
|
|
32ad3c3db3 | ||
|
|
1f5f8a7dde | ||
|
|
6da1460795 | ||
|
|
b14ae51f71 | ||
|
|
5af8d001ae | ||
|
|
0ca344df5f | ||
|
|
49f568abbd | ||
|
|
3b4e811907 | ||
|
|
d0e9443031 | ||
|
|
f7e9d9ab1f | ||
|
|
7834d6bca7 | ||
|
|
ed50257735 | ||
|
|
f15f525c5c | ||
|
|
e4bff0460d | ||
|
|
5ce3ddee9b | ||
|
|
22bf7a9509 | ||
|
|
842730707c | ||
|
|
a8f13bd956 | ||
|
|
cd5c2a7999 | ||
|
|
fbc94b9e3e | ||
|
|
e766f25d55 | ||
|
|
140ed9a4cb | ||
|
|
60094884cd | ||
|
|
0e8a4d141a | ||
|
|
17b78a6339 | ||
|
|
e99741159b | ||
|
|
6b9603227b | ||
|
|
23e8d282a3 | ||
|
|
611d6bbfc5 | ||
|
|
f26785c0ba | ||
|
|
5bcfb71737 | ||
|
|
4135c4974f | ||
|
|
222196b182 | ||
|
|
86e55c5c1c |
8
.gitignore
vendored
8
.gitignore
vendored
@@ -5,4 +5,10 @@
|
||||
/site/
|
||||
/bin/
|
||||
/dist/
|
||||
/sing-box
|
||||
/sing-box
|
||||
/sing-box.exe
|
||||
/build/
|
||||
/*.jar
|
||||
/*.aar
|
||||
/*.xcframework/
|
||||
.DS_Store
|
||||
|
||||
12
Makefile
12
Makefile
@@ -1,7 +1,7 @@
|
||||
NAME = sing-box
|
||||
COMMIT = $(shell git rev-parse --short HEAD)
|
||||
TAGS ?= with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api
|
||||
TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_shadowsocksr
|
||||
TAGS ?= with_gvisor,with_quic,with_wireguard,with_utls,with_reality_server,with_clash_api
|
||||
TAGS_TEST ?= with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_reality_server,with_shadowsocksr
|
||||
PARAMS = -v -trimpath -tags "$(TAGS)" -ldflags "-s -w -buildid="
|
||||
MAIN = ./cmd/sing-box
|
||||
|
||||
@@ -71,6 +71,14 @@ test_stdio:
|
||||
go mod tidy && \
|
||||
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:
|
||||
rm -rf bin dist sing-box
|
||||
rm -f $(shell go env GOPATH)/sing-box
|
||||
|
||||
@@ -34,12 +34,15 @@ type Router interface {
|
||||
InterfaceFinder() control.InterfaceFinder
|
||||
DefaultInterface() string
|
||||
AutoDetectInterface() bool
|
||||
AutoDetectInterfaceFunc() control.Func
|
||||
DefaultMark() int
|
||||
NetworkMonitor() tun.NetworkUpdateMonitor
|
||||
InterfaceMonitor() tun.DefaultInterfaceMonitor
|
||||
PackageManager() tun.PackageManager
|
||||
Rules() []Rule
|
||||
|
||||
TimeService
|
||||
|
||||
ClashServer() ClashServer
|
||||
SetClashServer(server ClashServer)
|
||||
|
||||
|
||||
8
adapter/time.go
Normal file
8
adapter/time.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package adapter
|
||||
|
||||
import "time"
|
||||
|
||||
type TimeService interface {
|
||||
Service
|
||||
TimeFunc() func() time.Time
|
||||
}
|
||||
94
box.go
94
box.go
@@ -9,7 +9,9 @@ import (
|
||||
"time"
|
||||
|
||||
"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/libbox/platform"
|
||||
"github.com/sagernet/sing-box/inbound"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
@@ -35,7 +37,7 @@ type Box struct {
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
func New(ctx context.Context, options option.Options) (*Box, error) {
|
||||
func New(ctx context.Context, options option.Options, platformInterface platform.Interface) (*Box, error) {
|
||||
createdAt := time.Now()
|
||||
logOptions := common.PtrValueOrDefault(options.Log)
|
||||
|
||||
@@ -53,19 +55,25 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
||||
var logFactory log.Factory
|
||||
var observableLogFactory log.ObservableFactory
|
||||
var logFile *os.File
|
||||
var logWriter io.Writer
|
||||
if logOptions.Disabled {
|
||||
observableLogFactory = log.NewNOPFactory()
|
||||
logFactory = observableLogFactory
|
||||
} else {
|
||||
var logWriter io.Writer
|
||||
switch logOptions.Output {
|
||||
case "", "stderr":
|
||||
case "":
|
||||
if platformInterface != nil {
|
||||
logWriter = io.Discard
|
||||
} else {
|
||||
logWriter = os.Stdout
|
||||
}
|
||||
case "stderr":
|
||||
logWriter = os.Stderr
|
||||
case "stdout":
|
||||
logWriter = os.Stdout
|
||||
default:
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
@@ -79,10 +87,10 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
||||
TimestampFormat: "-0700 2006-01-02 15:04:05",
|
||||
}
|
||||
if needClashAPI {
|
||||
observableLogFactory = log.NewObservableFactory(logFormatter, logWriter)
|
||||
observableLogFactory = log.NewObservableFactory(logFormatter, logWriter, platformInterface)
|
||||
logFactory = observableLogFactory
|
||||
} else {
|
||||
logFactory = log.NewFactory(logFormatter, logWriter)
|
||||
logFactory = log.NewFactory(logFormatter, logWriter, platformInterface)
|
||||
}
|
||||
if logOptions.Level != "" {
|
||||
logLevel, err := log.ParseLevel(logOptions.Level)
|
||||
@@ -100,7 +108,9 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
||||
logFactory,
|
||||
common.PtrValueOrDefault(options.Route),
|
||||
common.PtrValueOrDefault(options.DNS),
|
||||
common.PtrValueOrDefault(options.NTP),
|
||||
options.Inbounds,
|
||||
platformInterface,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse route options")
|
||||
@@ -120,6 +130,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
||||
router,
|
||||
logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
|
||||
inboundOptions,
|
||||
platformInterface,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse inbound[", i, "]")
|
||||
@@ -202,6 +213,18 @@ func (s *Box) Start() error {
|
||||
}
|
||||
|
||||
func (s *Box) start() error {
|
||||
if s.clashServer != nil {
|
||||
err := s.clashServer.Start()
|
||||
if err != nil {
|
||||
return E.Cause(err, "start clash api server")
|
||||
}
|
||||
}
|
||||
if s.v2rayServer != nil {
|
||||
err := s.v2rayServer.Start()
|
||||
if err != nil {
|
||||
return E.Cause(err, "start v2ray api server")
|
||||
}
|
||||
}
|
||||
for i, out := range s.outbounds {
|
||||
if starter, isStarter := out.(common.Starter); isStarter {
|
||||
err := starter.Start()
|
||||
@@ -232,18 +255,7 @@ func (s *Box) start() error {
|
||||
return E.Cause(err, "initialize inbound/", in.Type(), "[", tag, "]")
|
||||
}
|
||||
}
|
||||
if s.clashServer != nil {
|
||||
err = s.clashServer.Start()
|
||||
if err != nil {
|
||||
return E.Cause(err, "start clash api server")
|
||||
}
|
||||
}
|
||||
if s.v2rayServer != nil {
|
||||
err = s.v2rayServer.Start()
|
||||
if err != nil {
|
||||
return E.Cause(err, "start v2ray api server")
|
||||
}
|
||||
}
|
||||
|
||||
s.logger.Info("sing-box started (", F.Seconds(time.Since(s.createdAt).Seconds()), "s)")
|
||||
return nil
|
||||
}
|
||||
@@ -255,19 +267,43 @@ func (s *Box) Close() error {
|
||||
default:
|
||||
close(s.done)
|
||||
}
|
||||
for _, in := range s.inbounds {
|
||||
in.Close()
|
||||
var errors error
|
||||
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 {
|
||||
common.Close(out)
|
||||
for i, out := range s.outbounds {
|
||||
errors = E.Append(errors, common.Close(out), func(err error) error {
|
||||
return E.Cause(err, "close inbound/", out.Type(), "[", i, "]")
|
||||
})
|
||||
}
|
||||
return common.Close(
|
||||
s.router,
|
||||
s.logFactory,
|
||||
s.clashServer,
|
||||
s.v2rayServer,
|
||||
common.PtrOrNil(s.logFile),
|
||||
)
|
||||
if err := common.Close(s.router); err != nil {
|
||||
errors = E.Append(errors, err, func(err error) error {
|
||||
return E.Cause(err, "close router")
|
||||
})
|
||||
}
|
||||
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 {
|
||||
|
||||
@@ -3,17 +3,32 @@ package main
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/cmd/internal/build_shared"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
)
|
||||
|
||||
func main() {
|
||||
findSDK()
|
||||
build_shared.FindSDK()
|
||||
|
||||
currentTag, err := common.Exec("git", "describe", "--tags", "--abbrev=0").Read()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
currentTag = strings.TrimSpace(currentTag)
|
||||
|
||||
if "v"+C.Version != currentTag {
|
||||
log.Fatal("version mismatch, update constant.Version (", C.Version, ")", " to ", currentTag[1:])
|
||||
}
|
||||
|
||||
command := exec.Command(os.Args[1], os.Args[2:]...)
|
||||
command.Stdout = os.Stdout
|
||||
command.Stderr = os.Stderr
|
||||
err := command.Run()
|
||||
err = command.Run()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
113
cmd/internal/build_libbox/main.go
Normal file
113
cmd/internal/build_libbox/main.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
_ "github.com/sagernet/gomobile/event/key"
|
||||
"github.com/sagernet/sing-box/cmd/internal/build_shared"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
var (
|
||||
debugEnabled bool
|
||||
target string
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.BoolVar(&debugEnabled, "debug", false, "enable debug")
|
||||
flag.StringVar(&target, "target", "android", "target platform")
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
build_shared.FindMobile()
|
||||
|
||||
switch target {
|
||||
case "android":
|
||||
buildAndroid()
|
||||
case "ios":
|
||||
buildiOS()
|
||||
}
|
||||
}
|
||||
|
||||
func buildAndroid() {
|
||||
build_shared.FindSDK()
|
||||
|
||||
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",
|
||||
)
|
||||
} else {
|
||||
args = append(args, "-tags", "with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api,debug")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func buildiOS() {
|
||||
args := []string{
|
||||
"bind",
|
||||
"-v",
|
||||
"-target", "ios,iossimulator,macos",
|
||||
"-libname=box",
|
||||
}
|
||||
if !debugEnabled {
|
||||
args = append(
|
||||
args, "-trimpath", "-ldflags=-s -w -buildid=",
|
||||
"-tags", "with_gvisor,with_utls,with_clash_api,with_conntrack",
|
||||
)
|
||||
} else {
|
||||
args = append(args, "-tags", "with_gvisor,with_utls,with_clash_api,with_conntrack,debug")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
copyPath := filepath.Join("..", "sing-box-for-ios")
|
||||
if rw.FileExists(copyPath) {
|
||||
targetDir := filepath.Join(copyPath, "Libbox.xcframework")
|
||||
targetDir, _ = filepath.Abs(targetDir)
|
||||
os.RemoveAll(targetDir)
|
||||
os.Rename("Libbox.xcframework", targetDir)
|
||||
log.Info("copied to ", targetDir)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
package build_shared
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -18,7 +19,7 @@ var (
|
||||
androidNDKPath string
|
||||
)
|
||||
|
||||
func findSDK() {
|
||||
func FindSDK() {
|
||||
searchPath := []string{
|
||||
"$ANDROID_HOME",
|
||||
"$HOME/Android/Sdk",
|
||||
@@ -79,3 +80,13 @@ func findNDK() bool {
|
||||
}
|
||||
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
|
||||
}
|
||||
@@ -31,7 +31,10 @@ func check() error {
|
||||
return err
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
_, err = box.New(ctx, options)
|
||||
instance, err := box.New(ctx, options, nil)
|
||||
if err == nil {
|
||||
instance.Close()
|
||||
}
|
||||
cancel()
|
||||
return err
|
||||
}
|
||||
|
||||
139
cmd/sing-box/cmd_generate.go
Normal file
139
cmd/sing-box/cmd_generate.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
var commandGenerate = &cobra.Command{
|
||||
Use: "generate",
|
||||
Short: "Generate things",
|
||||
}
|
||||
|
||||
func init() {
|
||||
commandGenerate.AddCommand(commandGenerateUUID)
|
||||
commandGenerate.AddCommand(commandGenerateRandom)
|
||||
commandGenerate.AddCommand(commandGenerateWireGuardKeyPair)
|
||||
commandGenerate.AddCommand(commandGenerateRealityKeyPair)
|
||||
mainCommand.AddCommand(commandGenerate)
|
||||
}
|
||||
|
||||
var (
|
||||
outputBase64 bool
|
||||
outputHex bool
|
||||
)
|
||||
|
||||
var commandGenerateRandom = &cobra.Command{
|
||||
Use: "rand <length>",
|
||||
Short: "Generate random bytes",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := generateRandom(args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
commandGenerateRandom.Flags().BoolVar(&outputBase64, "base64", false, "Generate base64 string")
|
||||
commandGenerateRandom.Flags().BoolVar(&outputHex, "hex", false, "Generate hex string")
|
||||
}
|
||||
|
||||
func generateRandom(args []string) error {
|
||||
length, err := strconv.Atoi(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
randomBytes := make([]byte, length)
|
||||
_, err = rand.Read(randomBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if outputBase64 {
|
||||
_, err = os.Stdout.WriteString(base64.StdEncoding.EncodeToString(randomBytes) + "\n")
|
||||
} else if outputHex {
|
||||
_, err = os.Stdout.WriteString(hex.EncodeToString(randomBytes) + "\n")
|
||||
} else {
|
||||
_, err = os.Stdout.Write(randomBytes)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
var commandGenerateUUID = &cobra.Command{
|
||||
Use: "uuid",
|
||||
Short: "Generate UUID string",
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := generateUUID()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func generateUUID() error {
|
||||
newUUID, err := uuid.NewV4()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = os.Stdout.WriteString(newUUID.String() + "\n")
|
||||
return err
|
||||
}
|
||||
|
||||
var commandGenerateWireGuardKeyPair = &cobra.Command{
|
||||
Use: "wg-keypair",
|
||||
Short: "Generate WireGuard key pair",
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := generateWireGuardKey()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func generateWireGuardKey() error {
|
||||
privateKey, err := wgtypes.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.Stdout.WriteString("PrivateKey: " + privateKey.String() + "\n")
|
||||
os.Stdout.WriteString("PublicKey: " + privateKey.PublicKey().String() + "\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
var commandGenerateRealityKeyPair = &cobra.Command{
|
||||
Use: "reality-keypair",
|
||||
Short: "Generate reality key pair",
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := generateRealityKey()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func generateRealityKey() error {
|
||||
privateKey, err := wgtypes.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
publicKey := privateKey.PublicKey()
|
||||
os.Stdout.WriteString("PrivateKey: " + base64.RawURLEncoding.EncodeToString(privateKey[:]) + "\n")
|
||||
os.Stdout.WriteString("PublicKey: " + base64.RawURLEncoding.EncodeToString(publicKey[:]) + "\n")
|
||||
return nil
|
||||
}
|
||||
@@ -64,7 +64,7 @@ func create() (*box.Box, context.CancelFunc, error) {
|
||||
options.Log.DisableColor = true
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
instance, err := box.New(ctx, options)
|
||||
instance, err := box.New(ctx, options, nil)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, nil, E.Cause(err, "create service")
|
||||
|
||||
51
common/dialer/conntrack/conn.go
Normal file
51
common/dialer/conntrack/conn.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package conntrack
|
||||
|
||||
import (
|
||||
"net"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
)
|
||||
|
||||
type Conn struct {
|
||||
net.Conn
|
||||
element *list.Element[*ConnEntry]
|
||||
}
|
||||
|
||||
func NewConn(conn net.Conn) *Conn {
|
||||
entry := &ConnEntry{
|
||||
Conn: conn,
|
||||
Stack: debug.Stack(),
|
||||
}
|
||||
connAccess.Lock()
|
||||
element := openConnection.PushBack(entry)
|
||||
connAccess.Unlock()
|
||||
return &Conn{
|
||||
Conn: conn,
|
||||
element: element,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) Close() error {
|
||||
if c.element.Value != nil {
|
||||
connAccess.Lock()
|
||||
if c.element.Value != nil {
|
||||
openConnection.Remove(c.element)
|
||||
c.element.Value = nil
|
||||
}
|
||||
connAccess.Unlock()
|
||||
}
|
||||
return c.Conn.Close()
|
||||
}
|
||||
|
||||
func (c *Conn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
func (c *Conn) ReaderReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Conn) WriterReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
51
common/dialer/conntrack/packet_conn.go
Normal file
51
common/dialer/conntrack/packet_conn.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package conntrack
|
||||
|
||||
import (
|
||||
"net"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
)
|
||||
|
||||
type PacketConn struct {
|
||||
net.PacketConn
|
||||
element *list.Element[*ConnEntry]
|
||||
}
|
||||
|
||||
func NewPacketConn(conn net.PacketConn) *PacketConn {
|
||||
entry := &ConnEntry{
|
||||
Conn: conn,
|
||||
Stack: debug.Stack(),
|
||||
}
|
||||
connAccess.Lock()
|
||||
element := openConnection.PushBack(entry)
|
||||
connAccess.Unlock()
|
||||
return &PacketConn{
|
||||
PacketConn: conn,
|
||||
element: element,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *PacketConn) Close() error {
|
||||
if c.element.Value != nil {
|
||||
connAccess.Lock()
|
||||
if c.element.Value != nil {
|
||||
openConnection.Remove(c.element)
|
||||
c.element.Value = nil
|
||||
}
|
||||
connAccess.Unlock()
|
||||
}
|
||||
return c.PacketConn.Close()
|
||||
}
|
||||
|
||||
func (c *PacketConn) Upstream() any {
|
||||
return c.PacketConn
|
||||
}
|
||||
|
||||
func (c *PacketConn) ReaderReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *PacketConn) WriterReplaceable() bool {
|
||||
return true
|
||||
}
|
||||
43
common/dialer/conntrack/track.go
Normal file
43
common/dialer/conntrack/track.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package conntrack
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
)
|
||||
|
||||
var (
|
||||
connAccess sync.RWMutex
|
||||
openConnection list.List[*ConnEntry]
|
||||
)
|
||||
|
||||
type ConnEntry struct {
|
||||
Conn io.Closer
|
||||
Stack []byte
|
||||
}
|
||||
|
||||
func Count() int {
|
||||
return openConnection.Len()
|
||||
}
|
||||
|
||||
func List() []*ConnEntry {
|
||||
connAccess.RLock()
|
||||
defer connAccess.RUnlock()
|
||||
connList := make([]*ConnEntry, 0, openConnection.Len())
|
||||
for element := openConnection.Front(); element != nil; element = element.Next() {
|
||||
connList = append(connList, element.Value)
|
||||
}
|
||||
return connList
|
||||
}
|
||||
|
||||
func Close() {
|
||||
connAccess.Lock()
|
||||
defer connAccess.Unlock()
|
||||
for element := openConnection.Front(); element != nil; element = element.Next() {
|
||||
common.Close(element.Value.Conn)
|
||||
element.Value = nil
|
||||
}
|
||||
openConnection = list.List[*ConnEntry]{}
|
||||
}
|
||||
5
common/dialer/conntrack/track_disable.go
Normal file
5
common/dialer/conntrack/track_disable.go
Normal file
@@ -0,0 +1,5 @@
|
||||
//go:build !with_conntrack
|
||||
|
||||
package conntrack
|
||||
|
||||
const Enabled = false
|
||||
5
common/dialer/conntrack/track_enable.go
Normal file
5
common/dialer/conntrack/track_enable.go
Normal file
@@ -0,0 +1,5 @@
|
||||
//go:build with_conntrack
|
||||
|
||||
package conntrack
|
||||
|
||||
const Enabled = true
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer/conntrack"
|
||||
"github.com/sagernet/sing-box/common/warning"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
@@ -70,15 +71,7 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
} else if router.AutoDetectInterface() {
|
||||
const useInterfaceName = C.IsLinux
|
||||
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)
|
||||
}
|
||||
})
|
||||
bindFunc := router.AutoDetectInterfaceFunc()
|
||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
} else if router.DefaultInterface() != "" {
|
||||
@@ -167,16 +160,30 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
|
||||
}
|
||||
}
|
||||
if !address.IsIPv6() {
|
||||
return DialSlowContext(&d.dialer4, ctx, network, address)
|
||||
return trackConn(DialSlowContext(&d.dialer4, ctx, network, address))
|
||||
} else {
|
||||
return DialSlowContext(&d.dialer6, ctx, network, address)
|
||||
return trackConn(DialSlowContext(&d.dialer6, ctx, network, address))
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
if !destination.IsIPv6() {
|
||||
return d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4)
|
||||
return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4))
|
||||
} else {
|
||||
return d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr6)
|
||||
return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr6))
|
||||
}
|
||||
}
|
||||
|
||||
func trackConn(conn net.Conn, err error) (net.Conn, error) {
|
||||
if !conntrack.Enabled || err != nil {
|
||||
return conn, err
|
||||
}
|
||||
return conntrack.NewConn(conn), nil
|
||||
}
|
||||
|
||||
func trackPacketConn(conn net.PacketConn, err error) (net.PacketConn, error) {
|
||||
if !conntrack.Enabled || err != nil {
|
||||
return conn, err
|
||||
}
|
||||
return conntrack.NewPacketConn(conn), nil
|
||||
}
|
||||
|
||||
@@ -24,13 +24,13 @@ func (l *Listener) Accept() (net.Conn, error) {
|
||||
bufReader := std_bufio.NewReader(conn)
|
||||
header, err := proxyproto.Read(bufReader)
|
||||
if err != nil && !(l.AcceptNoHeader && err == proxyproto.ErrNoProxyProtocol) {
|
||||
return nil, err
|
||||
return nil, &Error{err}
|
||||
}
|
||||
if bufReader.Buffered() > 0 {
|
||||
cache := buf.NewSize(bufReader.Buffered())
|
||||
_, err = cache.ReadFullFrom(bufReader, cache.FreeLen())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, &Error{err}
|
||||
}
|
||||
conn = bufio.NewCachedConn(conn, cache)
|
||||
}
|
||||
@@ -42,3 +42,21 @@ func (l *Listener) Accept() (net.Conn, error) {
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
var _ net.Error = (*Error)(nil)
|
||||
|
||||
type Error struct {
|
||||
error
|
||||
}
|
||||
|
||||
func (e *Error) Unwrap() error {
|
||||
return e.error
|
||||
}
|
||||
|
||||
func (e *Error) Timeout() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *Error) Temporary() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -2,16 +2,15 @@ package tls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/badtls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
aTLS "github.com/sagernet/sing/common/tls"
|
||||
)
|
||||
|
||||
func NewDialerFromOptions(router adapter.Router, dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
|
||||
@@ -31,29 +30,19 @@ func NewClient(router adapter.Router, serverAddress string, options option.Outbo
|
||||
}
|
||||
if options.ECH != nil && options.ECH.Enabled {
|
||||
return NewECHClient(router, serverAddress, options)
|
||||
} else if options.Reality != nil && options.Reality.Enabled {
|
||||
return NewRealityClient(router, serverAddress, options)
|
||||
} else if options.UTLS != nil && options.UTLS.Enabled {
|
||||
return NewUTLSClient(router, serverAddress, options)
|
||||
} else {
|
||||
return NewSTDClient(serverAddress, options)
|
||||
return NewSTDClient(router, serverAddress, options)
|
||||
}
|
||||
}
|
||||
|
||||
func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) {
|
||||
tlsConn := config.Client(conn)
|
||||
ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
|
||||
defer cancel()
|
||||
err := tlsConn.HandshakeContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if stdConn, isSTD := tlsConn.(*tls.Conn); isSTD {
|
||||
var badConn badtls.TLSConn
|
||||
badConn, err = badtls.Create(stdConn)
|
||||
if err == nil {
|
||||
return badConn, nil
|
||||
}
|
||||
}
|
||||
return tlsConn, nil
|
||||
return aTLS.ClientHandshake(ctx, conn, config)
|
||||
}
|
||||
|
||||
type Dialer struct {
|
||||
|
||||
@@ -1,42 +1,25 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
aTLS "github.com/sagernet/sing/common/tls"
|
||||
)
|
||||
|
||||
type (
|
||||
Config = aTLS.Config
|
||||
ConfigCompat = aTLS.ConfigCompat
|
||||
ServerConfig = aTLS.ServerConfig
|
||||
ServerConfigCompat = aTLS.ServerConfigCompat
|
||||
WithSessionIDGenerator = aTLS.WithSessionIDGenerator
|
||||
Conn = aTLS.Conn
|
||||
|
||||
STDConfig = tls.Config
|
||||
STDConn = tls.Conn
|
||||
ConnectionState = tls.ConnectionState
|
||||
)
|
||||
|
||||
type Config interface {
|
||||
ServerName() string
|
||||
SetServerName(serverName string)
|
||||
NextProtos() []string
|
||||
SetNextProtos(nextProto []string)
|
||||
Config() (*STDConfig, error)
|
||||
Client(conn net.Conn) Conn
|
||||
Clone() Config
|
||||
}
|
||||
|
||||
type ServerConfig interface {
|
||||
Config
|
||||
adapter.Service
|
||||
Server(conn net.Conn) Conn
|
||||
}
|
||||
|
||||
type Conn interface {
|
||||
net.Conn
|
||||
HandshakeContext(ctx context.Context) error
|
||||
ConnectionState() ConnectionState
|
||||
}
|
||||
|
||||
func ParseTLSVersion(version string) (uint16, error) {
|
||||
switch version {
|
||||
case "1.0":
|
||||
|
||||
@@ -44,8 +44,8 @@ func (e *ECHClientConfig) Config() (*STDConfig, error) {
|
||||
return nil, E.New("unsupported usage for ECH")
|
||||
}
|
||||
|
||||
func (e *ECHClientConfig) Client(conn net.Conn) Conn {
|
||||
return &echConnWrapper{cftls.Client(conn, e.config)}
|
||||
func (e *ECHClientConfig) Client(conn net.Conn) (Conn, error) {
|
||||
return &echConnWrapper{cftls.Client(conn, e.config)}, nil
|
||||
}
|
||||
|
||||
func (e *ECHClientConfig) Clone() Config {
|
||||
@@ -76,6 +76,10 @@ func (c *echConnWrapper) ConnectionState() tls.ConnectionState {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *echConnWrapper) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
func NewECHClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
var serverName string
|
||||
if options.ServerName != "" {
|
||||
@@ -90,6 +94,7 @@ func NewECHClient(router adapter.Router, serverAddress string, options option.Ou
|
||||
}
|
||||
|
||||
var tlsConfig cftls.Config
|
||||
tlsConfig.Time = router.TimeFunc()
|
||||
if options.DisableSNI {
|
||||
tlsConfig.ServerName = "127.0.0.1"
|
||||
} else {
|
||||
|
||||
@@ -11,7 +11,10 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func GenerateKeyPair(serverName string) (*tls.Certificate, error) {
|
||||
func GenerateKeyPair(timeFunc func() time.Time, serverName string) (*tls.Certificate, error) {
|
||||
if timeFunc == nil {
|
||||
timeFunc = time.Now
|
||||
}
|
||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -22,8 +25,8 @@ func GenerateKeyPair(serverName string) (*tls.Certificate, error) {
|
||||
}
|
||||
template := &x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
NotBefore: time.Now().Add(time.Hour * -1),
|
||||
NotAfter: time.Now().Add(time.Hour),
|
||||
NotBefore: timeFunc().Add(time.Hour * -1),
|
||||
NotAfter: timeFunc().Add(time.Hour),
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
|
||||
230
common/tls/reality_client.go
Normal file
230
common/tls/reality_client.go
Normal file
@@ -0,0 +1,230 @@
|
||||
//go:build with_utls
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/ed25519"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
mRand "math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/debug"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
aTLS "github.com/sagernet/sing/common/tls"
|
||||
utls "github.com/sagernet/utls"
|
||||
|
||||
"golang.org/x/crypto/hkdf"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
var _ ConfigCompat = (*RealityClientConfig)(nil)
|
||||
|
||||
type RealityClientConfig struct {
|
||||
uClient *UTLSClientConfig
|
||||
publicKey []byte
|
||||
shortID []byte
|
||||
}
|
||||
|
||||
func NewRealityClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (*RealityClientConfig, error) {
|
||||
if options.UTLS == nil || !options.UTLS.Enabled {
|
||||
return nil, E.New("uTLS is required by reality client")
|
||||
}
|
||||
|
||||
uClient, err := NewUTLSClient(router, serverAddress, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
publicKey, err := base64.RawURLEncoding.DecodeString(options.Reality.PublicKey)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "decode public_key")
|
||||
}
|
||||
if len(publicKey) != 32 {
|
||||
return nil, E.New("invalid public_key")
|
||||
}
|
||||
shortID, err := hex.DecodeString(options.Reality.ShortID)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "decode short_id")
|
||||
}
|
||||
if len(shortID) != 8 {
|
||||
return nil, E.New("invalid short_id")
|
||||
}
|
||||
return &RealityClientConfig{uClient, publicKey, shortID}, nil
|
||||
}
|
||||
|
||||
func (e *RealityClientConfig) ServerName() string {
|
||||
return e.uClient.ServerName()
|
||||
}
|
||||
|
||||
func (e *RealityClientConfig) SetServerName(serverName string) {
|
||||
e.uClient.SetServerName(serverName)
|
||||
}
|
||||
|
||||
func (e *RealityClientConfig) NextProtos() []string {
|
||||
return e.uClient.NextProtos()
|
||||
}
|
||||
|
||||
func (e *RealityClientConfig) SetNextProtos(nextProto []string) {
|
||||
e.uClient.SetNextProtos(nextProto)
|
||||
}
|
||||
|
||||
func (e *RealityClientConfig) Config() (*STDConfig, error) {
|
||||
return nil, E.New("unsupported usage for reality")
|
||||
}
|
||||
|
||||
func (e *RealityClientConfig) Client(conn net.Conn) (Conn, error) {
|
||||
return ClientHandshake(context.Background(), conn, e)
|
||||
}
|
||||
|
||||
func (e *RealityClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
|
||||
verifier := &realityVerifier{
|
||||
serverName: e.uClient.ServerName(),
|
||||
}
|
||||
uConfig := e.uClient.config.Clone()
|
||||
uConfig.InsecureSkipVerify = true
|
||||
uConfig.SessionTicketsDisabled = true
|
||||
uConfig.VerifyPeerCertificate = verifier.VerifyPeerCertificate
|
||||
uConn := utls.UClient(conn, uConfig, e.uClient.id)
|
||||
verifier.UConn = uConn
|
||||
err := uConn.BuildHandshakeState()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hello := uConn.HandshakeState.Hello
|
||||
hello.SessionId = make([]byte, 32)
|
||||
copy(hello.Raw[39:], hello.SessionId)
|
||||
|
||||
var nowTime time.Time
|
||||
if uConfig.Time != nil {
|
||||
nowTime = uConfig.Time()
|
||||
} else {
|
||||
nowTime = time.Now()
|
||||
}
|
||||
binary.BigEndian.PutUint64(hello.SessionId, uint64(nowTime.Unix()))
|
||||
|
||||
hello.SessionId[0] = 1
|
||||
hello.SessionId[1] = 7
|
||||
hello.SessionId[2] = 5
|
||||
copy(hello.SessionId[8:], e.shortID)
|
||||
|
||||
if debug.Enabled {
|
||||
fmt.Printf("REALITY hello.sessionId[:16]: %v\n", hello.SessionId[:16])
|
||||
}
|
||||
|
||||
authKey := uConn.HandshakeState.State13.EcdheParams.SharedKey(e.publicKey)
|
||||
if authKey == nil {
|
||||
return nil, E.New("nil auth_key")
|
||||
}
|
||||
verifier.authKey = authKey
|
||||
_, err = hkdf.New(sha256.New, authKey, hello.Random[:20], []byte("REALITY")).Read(authKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aesBlock, _ := aes.NewCipher(authKey)
|
||||
aesGcmCipher, _ := cipher.NewGCM(aesBlock)
|
||||
aesGcmCipher.Seal(hello.SessionId[:0], hello.Random[20:], hello.SessionId[:16], hello.Raw)
|
||||
copy(hello.Raw[39:], hello.SessionId)
|
||||
if debug.Enabled {
|
||||
fmt.Printf("REALITY hello.sessionId: %v\n", hello.SessionId)
|
||||
fmt.Printf("REALITY uConn.AuthKey: %v\n", authKey)
|
||||
}
|
||||
|
||||
err = uConn.HandshakeContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if debug.Enabled {
|
||||
fmt.Printf("REALITY Conn.Verified: %v\n", verifier.verified)
|
||||
}
|
||||
|
||||
if !verifier.verified {
|
||||
go realityClientFallback(uConn, e.uClient.ServerName(), e.uClient.id)
|
||||
return nil, E.New("reality verification failed")
|
||||
}
|
||||
|
||||
return &utlsConnWrapper{uConn}, nil
|
||||
}
|
||||
|
||||
func realityClientFallback(uConn net.Conn, serverName string, fingerprint utls.ClientHelloID) {
|
||||
defer uConn.Close()
|
||||
client := &http.Client{
|
||||
Transport: &http2.Transport{
|
||||
DialTLSContext: func(ctx context.Context, network, addr string, config *tls.Config) (net.Conn, error) {
|
||||
return uConn, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
request, _ := http.NewRequest("GET", "https://"+serverName, nil)
|
||||
request.Header.Set("User-Agent", fingerprint.Client)
|
||||
request.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", mRand.Intn(32)+30)})
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, _ = io.Copy(io.Discard, response.Body)
|
||||
response.Body.Close()
|
||||
}
|
||||
|
||||
func (e *RealityClientConfig) SetSessionIDGenerator(generator func(clientHello []byte, sessionID []byte) error) {
|
||||
e.uClient.config.SessionIDGenerator = generator
|
||||
}
|
||||
|
||||
func (e *RealityClientConfig) Clone() Config {
|
||||
return &RealityClientConfig{
|
||||
e.uClient.Clone().(*UTLSClientConfig),
|
||||
e.publicKey,
|
||||
e.shortID,
|
||||
}
|
||||
}
|
||||
|
||||
type realityVerifier struct {
|
||||
*utls.UConn
|
||||
serverName string
|
||||
authKey []byte
|
||||
verified bool
|
||||
}
|
||||
|
||||
func (c *realityVerifier) VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||
p, _ := reflect.TypeOf(c.Conn).Elem().FieldByName("peerCertificates")
|
||||
certs := *(*([]*x509.Certificate))(unsafe.Pointer(uintptr(unsafe.Pointer(c.Conn)) + p.Offset))
|
||||
if pub, ok := certs[0].PublicKey.(ed25519.PublicKey); ok {
|
||||
h := hmac.New(sha512.New, c.authKey)
|
||||
h.Write(pub)
|
||||
if bytes.Equal(h.Sum(nil), certs[0].Signature) {
|
||||
c.verified = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
opts := x509.VerifyOptions{
|
||||
DNSName: c.serverName,
|
||||
Intermediates: x509.NewCertPool(),
|
||||
}
|
||||
for _, cert := range certs[1:] {
|
||||
opts.Intermediates.AddCert(cert)
|
||||
}
|
||||
if _, err := certs[0].Verify(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
192
common/tls/reality_server.go
Normal file
192
common/tls/reality_server.go
Normal file
@@ -0,0 +1,192 @@
|
||||
//go:build with_reality_server
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/reality"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/debug"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
var _ ServerConfigCompat = (*RealityServerConfig)(nil)
|
||||
|
||||
type RealityServerConfig struct {
|
||||
config *reality.Config
|
||||
}
|
||||
|
||||
func NewRealityServer(ctx context.Context, router adapter.Router, logger log.Logger, options option.InboundTLSOptions) (*RealityServerConfig, error) {
|
||||
var tlsConfig reality.Config
|
||||
|
||||
if options.ACME != nil && len(options.ACME.Domain) > 0 {
|
||||
return nil, E.New("acme is unavailable in reality")
|
||||
}
|
||||
tlsConfig.Time = router.TimeFunc()
|
||||
if options.ServerName != "" {
|
||||
tlsConfig.ServerName = options.ServerName
|
||||
}
|
||||
if len(options.ALPN) > 0 {
|
||||
tlsConfig.NextProtos = append(tlsConfig.NextProtos, options.ALPN...)
|
||||
}
|
||||
if options.MinVersion != "" {
|
||||
minVersion, err := ParseTLSVersion(options.MinVersion)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse min_version")
|
||||
}
|
||||
tlsConfig.MinVersion = minVersion
|
||||
}
|
||||
if options.MaxVersion != "" {
|
||||
maxVersion, err := ParseTLSVersion(options.MaxVersion)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse max_version")
|
||||
}
|
||||
tlsConfig.MaxVersion = maxVersion
|
||||
}
|
||||
if options.CipherSuites != nil {
|
||||
find:
|
||||
for _, cipherSuite := range options.CipherSuites {
|
||||
for _, tlsCipherSuite := range tls.CipherSuites() {
|
||||
if cipherSuite == tlsCipherSuite.Name {
|
||||
tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, tlsCipherSuite.ID)
|
||||
continue find
|
||||
}
|
||||
}
|
||||
return nil, E.New("unknown cipher_suite: ", cipherSuite)
|
||||
}
|
||||
}
|
||||
if options.Certificate != "" || options.CertificatePath != "" {
|
||||
return nil, E.New("certificate is unavailable in reality")
|
||||
}
|
||||
if options.Key != "" || options.KeyPath != "" {
|
||||
return nil, E.New("key is unavailable in reality")
|
||||
}
|
||||
|
||||
tlsConfig.SessionTicketsDisabled = true
|
||||
tlsConfig.Type = N.NetworkTCP
|
||||
tlsConfig.Dest = options.Reality.Handshake.ServerOptions.Build().String()
|
||||
|
||||
tlsConfig.ServerNames = map[string]bool{options.ServerName: true}
|
||||
privateKey, err := base64.RawURLEncoding.DecodeString(options.Reality.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "decode private key")
|
||||
}
|
||||
if len(privateKey) != 32 {
|
||||
return nil, E.New("invalid private key")
|
||||
}
|
||||
tlsConfig.PrivateKey = privateKey
|
||||
tlsConfig.MaxTimeDiff = time.Duration(options.Reality.MaxTimeDifference)
|
||||
|
||||
tlsConfig.ShortIds = make(map[[8]byte]bool)
|
||||
for i, shortID := range options.Reality.ShortID {
|
||||
var shortIDBytesArray [8]byte
|
||||
decodedLen, err := hex.Decode(shortIDBytesArray[:], []byte(shortID))
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "decode short_id[", i, "]: ", shortID)
|
||||
}
|
||||
if decodedLen != 8 {
|
||||
return nil, E.New("invalid short_id[", i, "]: ", shortID)
|
||||
}
|
||||
tlsConfig.ShortIds[shortIDBytesArray] = true
|
||||
}
|
||||
|
||||
handshakeDialer := dialer.New(router, options.Reality.Handshake.DialerOptions)
|
||||
tlsConfig.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return handshakeDialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
|
||||
}
|
||||
|
||||
if debug.Enabled {
|
||||
tlsConfig.Show = true
|
||||
}
|
||||
|
||||
return &RealityServerConfig{&tlsConfig}, nil
|
||||
}
|
||||
|
||||
func (c *RealityServerConfig) ServerName() string {
|
||||
return c.config.ServerName
|
||||
}
|
||||
|
||||
func (c *RealityServerConfig) SetServerName(serverName string) {
|
||||
c.config.ServerName = serverName
|
||||
}
|
||||
|
||||
func (c *RealityServerConfig) NextProtos() []string {
|
||||
return c.config.NextProtos
|
||||
}
|
||||
|
||||
func (c *RealityServerConfig) SetNextProtos(nextProto []string) {
|
||||
c.config.NextProtos = nextProto
|
||||
}
|
||||
|
||||
func (c *RealityServerConfig) Config() (*tls.Config, error) {
|
||||
return nil, E.New("unsupported usage for reality")
|
||||
}
|
||||
|
||||
func (c *RealityServerConfig) Client(conn net.Conn) (Conn, error) {
|
||||
return ClientHandshake(context.Background(), conn, c)
|
||||
}
|
||||
|
||||
func (c *RealityServerConfig) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *RealityServerConfig) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *RealityServerConfig) Server(conn net.Conn) (Conn, error) {
|
||||
return ServerHandshake(context.Background(), conn, c)
|
||||
}
|
||||
|
||||
func (c *RealityServerConfig) ServerHandshake(ctx context.Context, conn net.Conn) (Conn, error) {
|
||||
tlsConn, err := reality.Server(ctx, conn, c.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &realityConnWrapper{Conn: tlsConn}, nil
|
||||
}
|
||||
|
||||
func (c *RealityServerConfig) Clone() Config {
|
||||
return &RealityServerConfig{
|
||||
config: c.config.Clone(),
|
||||
}
|
||||
}
|
||||
|
||||
var _ Conn = (*realityConnWrapper)(nil)
|
||||
|
||||
type realityConnWrapper struct {
|
||||
*reality.Conn
|
||||
}
|
||||
|
||||
func (c *realityConnWrapper) ConnectionState() ConnectionState {
|
||||
state := c.Conn.ConnectionState()
|
||||
return tls.ConnectionState{
|
||||
Version: state.Version,
|
||||
HandshakeComplete: state.HandshakeComplete,
|
||||
DidResume: state.DidResume,
|
||||
CipherSuite: state.CipherSuite,
|
||||
NegotiatedProtocol: state.NegotiatedProtocol,
|
||||
NegotiatedProtocolIsMutual: state.NegotiatedProtocolIsMutual,
|
||||
ServerName: state.ServerName,
|
||||
PeerCertificates: state.PeerCertificates,
|
||||
VerifiedChains: state.VerifiedChains,
|
||||
SignedCertificateTimestamps: state.SignedCertificateTimestamps,
|
||||
OCSPResponse: state.OCSPResponse,
|
||||
TLSUnique: state.TLSUnique,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *realityConnWrapper) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
16
common/tls/reality_stub.go
Normal file
16
common/tls/reality_stub.go
Normal file
@@ -0,0 +1,16 @@
|
||||
//go:build !with_reality_server
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
func NewRealityServer(ctx context.Context, router adapter.Router, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||
return nil, E.New(`reality server is not included in this build, rebuild with -tags with_reality_server`)
|
||||
}
|
||||
@@ -2,36 +2,28 @@ package tls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/common/badtls"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
aTLS "github.com/sagernet/sing/common/tls"
|
||||
)
|
||||
|
||||
func NewServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||
func NewServer(ctx context.Context, router adapter.Router, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||
if !options.Enabled {
|
||||
return nil, nil
|
||||
}
|
||||
return NewSTDServer(ctx, logger, options)
|
||||
if options.Reality != nil && options.Reality.Enabled {
|
||||
return NewRealityServer(ctx, router, logger, options)
|
||||
} else {
|
||||
return NewSTDServer(ctx, router, logger, options)
|
||||
}
|
||||
}
|
||||
|
||||
func ServerHandshake(ctx context.Context, conn net.Conn, config ServerConfig) (Conn, error) {
|
||||
tlsConn := config.Server(conn)
|
||||
ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
|
||||
defer cancel()
|
||||
err := tlsConn.HandshakeContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if stdConn, isSTD := tlsConn.(*tls.Conn); isSTD {
|
||||
var badConn badtls.TLSConn
|
||||
badConn, err = badtls.Create(stdConn)
|
||||
if err == nil {
|
||||
return badConn, nil
|
||||
}
|
||||
}
|
||||
return tlsConn, nil
|
||||
return aTLS.ServerHandshake(ctx, conn, config)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net/netip"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
@@ -35,15 +36,15 @@ func (s *STDClientConfig) Config() (*STDConfig, error) {
|
||||
return s.config, nil
|
||||
}
|
||||
|
||||
func (s *STDClientConfig) Client(conn net.Conn) Conn {
|
||||
return tls.Client(conn, s.config)
|
||||
func (s *STDClientConfig) Client(conn net.Conn) (Conn, error) {
|
||||
return tls.Client(conn, s.config), nil
|
||||
}
|
||||
|
||||
func (s *STDClientConfig) Clone() Config {
|
||||
return &STDClientConfig{s.config.Clone()}
|
||||
}
|
||||
|
||||
func NewSTDClient(serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
func NewSTDClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
var serverName string
|
||||
if options.ServerName != "" {
|
||||
serverName = options.ServerName
|
||||
@@ -57,6 +58,7 @@ func NewSTDClient(serverAddress string, options option.OutboundTLSOptions) (Conf
|
||||
}
|
||||
|
||||
var tlsConfig tls.Config
|
||||
tlsConfig.Time = router.TimeFunc()
|
||||
if options.DisableSNI {
|
||||
tlsConfig.ServerName = "127.0.0.1"
|
||||
} else {
|
||||
|
||||
@@ -48,12 +48,12 @@ func (c *STDServerConfig) Config() (*STDConfig, error) {
|
||||
return c.config, nil
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) Client(conn net.Conn) Conn {
|
||||
return tls.Client(conn, c.config)
|
||||
func (c *STDServerConfig) Client(conn net.Conn) (Conn, error) {
|
||||
return tls.Client(conn, c.config), nil
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) Server(conn net.Conn) Conn {
|
||||
return tls.Server(conn, c.config)
|
||||
func (c *STDServerConfig) Server(conn net.Conn) (Conn, error) {
|
||||
return tls.Server(conn, c.config), nil
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) Clone() Config {
|
||||
@@ -156,7 +156,7 @@ func (c *STDServerConfig) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewSTDServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||
func NewSTDServer(ctx context.Context, router adapter.Router, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||
if !options.Enabled {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -175,6 +175,7 @@ func NewSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
|
||||
} else {
|
||||
tlsConfig = &tls.Config{}
|
||||
}
|
||||
tlsConfig.Time = router.TimeFunc()
|
||||
if options.ServerName != "" {
|
||||
tlsConfig.ServerName = options.ServerName
|
||||
}
|
||||
@@ -230,7 +231,7 @@ func NewSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
|
||||
}
|
||||
if certificate == nil && key == nil && options.Insecure {
|
||||
tlsConfig.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
return GenerateKeyPair(info.ServerName)
|
||||
return GenerateKeyPair(router.TimeFunc(), info.ServerName)
|
||||
}
|
||||
} else {
|
||||
if certificate == nil {
|
||||
|
||||
@@ -5,6 +5,7 @@ package tls
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
@@ -12,8 +13,9 @@ import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
utls "github.com/sagernet/utls"
|
||||
|
||||
utls "github.com/refraction-networking/utls"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
type UTLSClientConfig struct {
|
||||
@@ -34,6 +36,9 @@ func (e *UTLSClientConfig) NextProtos() []string {
|
||||
}
|
||||
|
||||
func (e *UTLSClientConfig) SetNextProtos(nextProto []string) {
|
||||
if len(nextProto) == 1 && nextProto[0] == http2.NextProtoTLS {
|
||||
nextProto = append(nextProto, "http/1.1")
|
||||
}
|
||||
e.config.NextProtos = nextProto
|
||||
}
|
||||
|
||||
@@ -41,8 +46,19 @@ func (e *UTLSClientConfig) Config() (*STDConfig, error) {
|
||||
return nil, E.New("unsupported usage for uTLS")
|
||||
}
|
||||
|
||||
func (e *UTLSClientConfig) Client(conn net.Conn) Conn {
|
||||
return &utlsConnWrapper{utls.UClient(conn, e.config.Clone(), e.id)}
|
||||
func (e *UTLSClientConfig) Client(conn net.Conn) (Conn, error) {
|
||||
return &utlsConnWrapper{utls.UClient(conn, e.config.Clone(), e.id)}, nil
|
||||
}
|
||||
|
||||
func (e *UTLSClientConfig) SetSessionIDGenerator(generator func(clientHello []byte, sessionID []byte) error) {
|
||||
e.config.SessionIDGenerator = generator
|
||||
}
|
||||
|
||||
func (e *UTLSClientConfig) Clone() Config {
|
||||
return &UTLSClientConfig{
|
||||
config: e.config.Clone(),
|
||||
id: e.id,
|
||||
}
|
||||
}
|
||||
|
||||
type utlsConnWrapper struct {
|
||||
@@ -67,14 +83,11 @@ func (c *utlsConnWrapper) ConnectionState() tls.ConnectionState {
|
||||
}
|
||||
}
|
||||
|
||||
func (e *UTLSClientConfig) Clone() Config {
|
||||
return &UTLSClientConfig{
|
||||
config: e.config.Clone(),
|
||||
id: e.id,
|
||||
}
|
||||
func (c *utlsConnWrapper) Upstream() any {
|
||||
return c.UConn
|
||||
}
|
||||
|
||||
func NewUTLSClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
func NewUTLSClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (*UTLSClientConfig, error) {
|
||||
var serverName string
|
||||
if options.ServerName != "" {
|
||||
serverName = options.ServerName
|
||||
@@ -88,6 +101,7 @@ func NewUTLSClient(router adapter.Router, serverAddress string, options option.O
|
||||
}
|
||||
|
||||
var tlsConfig utls.Config
|
||||
tlsConfig.Time = router.TimeFunc()
|
||||
if options.DisableSNI {
|
||||
tlsConfig.ServerName = "127.0.0.1"
|
||||
} else {
|
||||
@@ -144,28 +158,59 @@ func NewUTLSClient(router adapter.Router, serverAddress string, options option.O
|
||||
}
|
||||
tlsConfig.RootCAs = certPool
|
||||
}
|
||||
var id utls.ClientHelloID
|
||||
switch options.UTLS.Fingerprint {
|
||||
case "chrome", "":
|
||||
id = utls.HelloChrome_Auto
|
||||
case "firefox":
|
||||
id = utls.HelloFirefox_Auto
|
||||
case "edge":
|
||||
id = utls.HelloEdge_Auto
|
||||
case "safari":
|
||||
id = utls.HelloSafari_Auto
|
||||
case "360":
|
||||
id = utls.Hello360_Auto
|
||||
case "qq":
|
||||
id = utls.HelloQQ_Auto
|
||||
case "ios":
|
||||
id = utls.HelloIOS_Auto
|
||||
case "android":
|
||||
id = utls.HelloAndroid_11_OkHttp
|
||||
case "random":
|
||||
id = utls.HelloRandomized
|
||||
default:
|
||||
return nil, E.New("unknown uTLS fingerprint: ", options.UTLS.Fingerprint)
|
||||
id, err := uTLSClientHelloID(options.UTLS.Fingerprint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &UTLSClientConfig{&tlsConfig, id}, nil
|
||||
}
|
||||
|
||||
var (
|
||||
randomFingerprint utls.ClientHelloID
|
||||
randomizedFingerprint utls.ClientHelloID
|
||||
)
|
||||
|
||||
func init() {
|
||||
modernFingerprints := []utls.ClientHelloID{
|
||||
utls.HelloChrome_Auto,
|
||||
utls.HelloFirefox_Auto,
|
||||
utls.HelloEdge_Auto,
|
||||
utls.HelloSafari_Auto,
|
||||
utls.HelloIOS_Auto,
|
||||
}
|
||||
randomFingerprint = modernFingerprints[rand.Intn(len(modernFingerprints))]
|
||||
|
||||
weights := utls.DefaultWeights
|
||||
weights.TLSVersMax_Set_VersionTLS13 = 1
|
||||
weights.FirstKeyShare_Set_CurveP256 = 0
|
||||
randomizedFingerprint = utls.HelloRandomized
|
||||
randomizedFingerprint.Seed, _ = utls.NewPRNGSeed()
|
||||
randomizedFingerprint.Weights = &weights
|
||||
}
|
||||
|
||||
func uTLSClientHelloID(name string) (utls.ClientHelloID, error) {
|
||||
switch name {
|
||||
case "chrome", "":
|
||||
return utls.HelloChrome_Auto, nil
|
||||
case "firefox":
|
||||
return utls.HelloFirefox_Auto, nil
|
||||
case "edge":
|
||||
return utls.HelloEdge_Auto, nil
|
||||
case "safari":
|
||||
return utls.HelloSafari_Auto, nil
|
||||
case "360":
|
||||
return utls.Hello360_Auto, nil
|
||||
case "qq":
|
||||
return utls.HelloQQ_Auto, nil
|
||||
case "ios":
|
||||
return utls.HelloIOS_Auto, nil
|
||||
case "android":
|
||||
return utls.HelloAndroid_11_OkHttp, nil
|
||||
case "random":
|
||||
return randomFingerprint, nil
|
||||
case "randomized":
|
||||
return randomizedFingerprint, nil
|
||||
default:
|
||||
return utls.ClientHelloID{}, E.New("unknown uTLS fingerprint: ", name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,3 +11,7 @@ import (
|
||||
func NewUTLSClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
return nil, E.New(`uTLS is not included in this build, rebuild with -tags with_utls`)
|
||||
}
|
||||
|
||||
func NewRealityClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
|
||||
return nil, E.New(`uTLS, which is required by reality client is not included in this build, rebuild with -tags with_utls`)
|
||||
}
|
||||
|
||||
@@ -3,13 +3,28 @@ package constant
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
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) {
|
||||
name = os.ExpandEnv(name)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package constant
|
||||
|
||||
var Version = "1.2-beta3"
|
||||
var Version = "1.2-beta7"
|
||||
|
||||
37
docs/assets/icon.svg
Normal file
37
docs/assets/icon.svg
Normal file
@@ -0,0 +1,37 @@
|
||||
<svg width="1027" height="1109" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" overflow="hidden">
|
||||
<defs>
|
||||
<filter id="fx0" x="-10%" y="-10%" width="120%" height="120%" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse">
|
||||
<feComponentTransfer color-interpolation-filters="sRGB">
|
||||
<feFuncR type="discrete" tableValues="0 0" />
|
||||
<feFuncG type="discrete" tableValues="0 0" />
|
||||
<feFuncB type="discrete" tableValues="0 0" />
|
||||
<feFuncA type="linear" slope="0.4" intercept="0" />
|
||||
</feComponentTransfer>
|
||||
<feGaussianBlur stdDeviation="4.58333 4.58333" />
|
||||
</filter>
|
||||
<clipPath id="clip1">
|
||||
<rect x="692" y="855" width="1027" height="1109" />
|
||||
</clipPath>
|
||||
<clipPath id="clip2">
|
||||
<rect x="-2" y="-2" width="541" height="786" />
|
||||
</clipPath>
|
||||
<clipPath id="clip3">
|
||||
<rect x="0" y="0" width="535" height="782" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g clip-path="url(#clip1)" transform="translate(-692 -855)">
|
||||
<path d="M692 1191 692 1575.69C692 1640.41 731.499 1651.19 731.499 1651.19L1148.03 1931.62C1212.66 1974.77 1194.71 1881.29 1194.71 1881.29L1194.71 1528.96 692 1191Z" fill="#37474F" fill-rule="evenodd" />
|
||||
<g clip-path="url(#clip2)" filter="url(#fx0)" transform="translate(1184 1182)">
|
||||
<g clip-path="url(#clip3)">
|
||||
<path d="M520.482 15.4819 520.482 400.176C520.482 464.89 480.983 475.676 480.983 475.676 480.983 475.676 129.086 712.963 64.4523 756.106-0.181814 799.25 17.7721 705.773 17.7721 705.773L17.7721 353.437 520.482 15.4819Z" fill="#455A64" fill-rule="evenodd" />
|
||||
</g>
|
||||
</g>
|
||||
<path d="M1698 1191 1698 1575.69C1698 1640.41 1658.5 1651.19 1658.5 1651.19 1658.5 1651.19 1306.6 1888.48 1241.97 1931.62 1177.34 1974.77 1195.29 1881.29 1195.29 1881.29L1195.29 1528.96 1698 1191Z" fill="#455A64" fill-rule="evenodd" />
|
||||
<path d="M1241.71 868.473C1212.96 850.509 1169.85 850.509 1144.7 868.473L713.557 1163.07C684.814 1181.04 684.814 1213.37 713.557 1231.33L1144.7 1529.53C1173.44 1547.49 1216.56 1547.49 1241.71 1529.53L1676.44 1227.74C1705.19 1209.78 1705.19 1177.44 1676.44 1159.48L1241.71 868.473Z" fill="#546E7A" fill-rule="evenodd" />
|
||||
<path d="M1195 1949C1173.4 1949 1159 1935.19 1159 1917.92L1159 1531.08C1159 1513.82 1173.4 1500 1195 1500 1216.6 1500 1231 1513.82 1231 1531.08L1231 1914.46C1231 1935.19 1216.6 1949 1195 1949Z" fill="#546E7A" fill-rule="evenodd" />
|
||||
<path d="M1553.92 1435.92C1553.92 1471.89 1557.5 1486.27 1518.03 1511.45L1428.32 1568.99C1388.85 1594.17 1374.5 1572.59 1374.5 1540.22L1374.5 1446.71C1374.5 1439.52 1374.5 1435.92 1363.73 1428.73 1270.43 1363.99 911.591 1115.84 847 1069.09L1012.07 954C1058.72 982.772 1399.61 1209.35 1539.56 1306.45 1546.74 1310.05 1550.33 1317.24 1550.33 1320.84L1550.33 1435.92Z" fill="#99AAB5" fill-rule="evenodd" />
|
||||
<path d="M1543.41 1310.21C1399.82 1213.17 1058.79 986.752 1015.72 958L951.103 997.534 847 1069.41C911.615 1116.14 1270.59 1360.53 1363.92 1425.22 1371.1 1428.81 1371.1 1432.41 1371.1 1436L1547 1313.8C1547 1313.8 1547 1310.21 1543.41 1310.21Z" fill="#CCD6DD" fill-rule="evenodd" />
|
||||
<path d="M1554.9 1435.48 1554.9 1324.19C1554.9 1317.01 1551.3 1313.42 1544.11 1309.83 1400.28 1212.89 1058.67 986.721 1015.51 958L940 1008.26C1062.26 1090.83 1389.49 1306.24 1475.79 1367.27 1486.58 1374.45 1486.58 1381.63 1486.58 1385.22L1486.58 1536 1522.54 1510.87C1558.5 1485.74 1554.9 1467.79 1554.9 1435.48Z" fill="#CCD6DD" fill-rule="evenodd" />
|
||||
<path d="M1543.23 1309.95C1399.6 1212.98 1058.49 986.731 1015.4 958L940 1008.28C1062.08 1090.88 1388.83 1306.36 1475.01 1367.41 1475.01 1367.41 1478.6 1371 1478.6 1371L1554 1317.13C1546.82 1313.54 1546.82 1309.95 1543.23 1309.95Z" fill="#E1E8ED" fill-rule="evenodd" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
@@ -1,3 +1,44 @@
|
||||
#### 1.2-beta7
|
||||
|
||||
* Fix the compatibility issue between VLESS's vision sub-protocol and the Xray-core client
|
||||
* Improve the stability of the VMESS server
|
||||
|
||||
#### 1.2-beta6
|
||||
|
||||
* Introducing our [new iOS client application](/installation/clients/sfi)
|
||||
* Add [platform options](/configuration/inbound/tun#platform) for tun inbound
|
||||
* Add custom TLS server support for http based v2ray transports
|
||||
* Add generate commands
|
||||
* Enable XUDP by default in VLESS
|
||||
* Update reality server
|
||||
* Update vision protocol
|
||||
* Fixed [user flow in vless server](/configuration/inbound/vless#usersflow)
|
||||
* Bug fixes
|
||||
* Update dependencies
|
||||
|
||||
#### 1.2-beta5
|
||||
|
||||
* Add [VLESS server](/configuration/inbound/vless) and [vision](/configuration/outbound/vless#flow) support
|
||||
* Add [reality TLS](/configuration/shared/tls) support
|
||||
* Fix match private address
|
||||
|
||||
#### 1.1.6
|
||||
|
||||
* Improve vmess request
|
||||
* Fix ipv6 redirect on Linux
|
||||
* Fix match geoip private
|
||||
* Fix parse hysteria UDP message
|
||||
* Fix socks connect response
|
||||
* Disable vmess header protection if transport enabled
|
||||
* Update QUIC v2 version number and initial salt
|
||||
|
||||
#### 1.2-beta4
|
||||
|
||||
* Add [NTP service](/configuration/ntp)
|
||||
* Add Add multiple server names and multi-user support for shadowtls
|
||||
* Add strict mode support for shadowtls v3
|
||||
* Add uTLS support for shadowtls v3
|
||||
|
||||
#### 1.2-beta3
|
||||
|
||||
* Update QUIC v2 version number and initial salt
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
| `trojan` | [Trojan](./trojan) | TCP |
|
||||
| `naive` | [Naive](./naive) | X |
|
||||
| `hysteria` | [Hysteria](./hysteria) | X |
|
||||
| `shadowtls` | [ShadowTLS](./shadowtls) | TCP |
|
||||
| `vless` | [VLESS](./vless) | TCP |
|
||||
| `tun` | [Tun](./tun) | X |
|
||||
| `redirect` | [Redirect](./redirect) | X |
|
||||
| `tproxy` | [TProxy](./tproxy) | X |
|
||||
|
||||
@@ -77,11 +77,11 @@ Both if empty.
|
||||
|
||||
==Required==
|
||||
|
||||
| Method | Password Format |
|
||||
|---------------|-------------------------------------|
|
||||
| none | / |
|
||||
| 2022 methods | `openssl rand -base64 <Key Length>` |
|
||||
| other methods | any string |
|
||||
| Method | Password Format |
|
||||
|---------------|------------------------------------------------|
|
||||
| none | / |
|
||||
| 2022 methods | `sing-box generate rand --base64 <Key Length>` |
|
||||
| other methods | any string |
|
||||
|
||||
### Listen Fields
|
||||
|
||||
|
||||
@@ -77,8 +77,8 @@ See [Listen Fields](/configuration/shared/listen) for details.
|
||||
|
||||
==必填==
|
||||
|
||||
| 方法 | 密码格式 |
|
||||
|---------------|-------------------------------|
|
||||
| none | / |
|
||||
| 2022 methods | `openssl rand -base64 <密钥长度>` |
|
||||
| other methods | 任意字符串 |
|
||||
| 方法 | 密码格式 |
|
||||
|---------------|------------------------------------------|
|
||||
| none | / |
|
||||
| 2022 methods | `sing-box generate rand --base64 <密钥长度>` |
|
||||
| other methods | 任意字符串 |
|
||||
@@ -9,12 +9,27 @@
|
||||
|
||||
"version": 3,
|
||||
"password": "fuck me till the daylight",
|
||||
"users": [
|
||||
{
|
||||
"name": "sekai",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg=="
|
||||
}
|
||||
],
|
||||
"handshake": {
|
||||
"server": "google.com",
|
||||
"server_port": 443,
|
||||
|
||||
... // Dial Fields
|
||||
}
|
||||
},
|
||||
"handshake_for_server_name": {
|
||||
"example.com": {
|
||||
"server": "example.com",
|
||||
"server_port": 443,
|
||||
|
||||
... // Dial Fields
|
||||
}
|
||||
},
|
||||
"strict_mode": false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -36,12 +51,31 @@ ShadowTLS protocol version.
|
||||
|
||||
#### password
|
||||
|
||||
Set password.
|
||||
ShadowTLS password.
|
||||
|
||||
Only available in the ShadowTLS v2/v3 protocol.
|
||||
Only available in the ShadowTLS protocol 2.
|
||||
|
||||
|
||||
#### users
|
||||
|
||||
ShadowTLS users.
|
||||
|
||||
Only available in the ShadowTLS protocol 3.
|
||||
|
||||
#### handshake
|
||||
|
||||
==Required==
|
||||
|
||||
Handshake server address and [Dial options](/configuration/shared/dial).
|
||||
Handshake server address and [Dial options](/configuration/shared/dial).
|
||||
|
||||
#### handshake_for_server_name
|
||||
|
||||
Handshake server address and [Dial options](/configuration/shared/dial) for specific server name.
|
||||
|
||||
Only available in the ShadowTLS protocol 2/3.
|
||||
|
||||
#### strict_mode
|
||||
|
||||
ShadowTLS strict mode.
|
||||
|
||||
Only available in the ShadowTLS protocol 3.
|
||||
|
||||
@@ -9,12 +9,27 @@
|
||||
|
||||
"version": 3,
|
||||
"password": "fuck me till the daylight",
|
||||
"users": [
|
||||
{
|
||||
"name": "sekai",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg=="
|
||||
}
|
||||
],
|
||||
"handshake": {
|
||||
"server": "google.com",
|
||||
"server_port": 443,
|
||||
|
||||
... // 拨号字段
|
||||
}
|
||||
},
|
||||
"handshake_for_server_name": {
|
||||
"example.com": {
|
||||
"server": "example.com",
|
||||
"server_port": 443,
|
||||
|
||||
... // 拨号字段
|
||||
}
|
||||
},
|
||||
"strict_mode": false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -36,12 +51,32 @@ ShadowTLS 协议版本。
|
||||
|
||||
#### password
|
||||
|
||||
设置密码。
|
||||
ShadowTLS 密码。
|
||||
|
||||
仅在 ShadowTLS v2/v3 协议中可用。
|
||||
仅在 ShadowTLS 协议版本 2 中可用。
|
||||
|
||||
#### users
|
||||
|
||||
ShadowTLS 用户。
|
||||
|
||||
仅在 ShadowTLS 协议版本 3 中可用。
|
||||
|
||||
#### handshake
|
||||
|
||||
==必填==
|
||||
|
||||
握手服务器地址和 [拨号参数](/zh/configuration/shared/dial/)。
|
||||
|
||||
#### handshake_for_server_name
|
||||
|
||||
==必填==
|
||||
|
||||
对于特定服务器名称的握手服务器地址和 [拨号参数](/zh/configuration/shared/dial/)。
|
||||
|
||||
仅在 ShadowTLS 协议版本 2/3 中可用。
|
||||
|
||||
#### strict_mode
|
||||
|
||||
ShadowTLS 严格模式。
|
||||
|
||||
仅在 ShadowTLS 协议版本 3 中可用。
|
||||
|
||||
@@ -46,8 +46,15 @@
|
||||
"exclude_package": [
|
||||
"com.android.captiveportallogin"
|
||||
],
|
||||
...
|
||||
// Listen Fields
|
||||
"platform": {
|
||||
"http_proxy": {
|
||||
"enabled": false,
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 8080
|
||||
}
|
||||
},
|
||||
|
||||
... // Listen Fields
|
||||
}
|
||||
```
|
||||
|
||||
@@ -187,6 +194,14 @@ Limit android packages in route.
|
||||
|
||||
Exclude android packages in route.
|
||||
|
||||
#### platform
|
||||
|
||||
Platform-specific settings, provided by client applications.
|
||||
|
||||
#### platform.http_proxy
|
||||
|
||||
System HTTP proxy settings.
|
||||
|
||||
### Listen Fields
|
||||
|
||||
See [Listen Fields](/configuration/shared/listen) for details.
|
||||
|
||||
@@ -46,8 +46,15 @@
|
||||
"exclude_package": [
|
||||
"com.android.captiveportallogin"
|
||||
],
|
||||
...
|
||||
// 监听字段
|
||||
"platform": {
|
||||
"http_proxy": {
|
||||
"enabled": false,
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 8080
|
||||
}
|
||||
},
|
||||
|
||||
... // 监听字段
|
||||
}
|
||||
```
|
||||
|
||||
@@ -148,19 +155,19 @@ TCP/IP 栈。
|
||||
|
||||
UID 规则仅在 Linux 下被支持,并且需要 `auto_route`。
|
||||
|
||||
限制被路由的的用户。默认不限制。
|
||||
限制被路由的用户。默认不限制。
|
||||
|
||||
#### include_uid_range
|
||||
|
||||
限制被路由的的用户范围。
|
||||
限制被路由的用户范围。
|
||||
|
||||
#### exclude_uid
|
||||
|
||||
排除路由的的用户。
|
||||
排除路由的用户。
|
||||
|
||||
#### exclude_uid_range
|
||||
|
||||
排除路由的的用户范围。
|
||||
排除路由的用户范围。
|
||||
|
||||
#### include_android_user
|
||||
|
||||
@@ -183,6 +190,14 @@ TCP/IP 栈。
|
||||
|
||||
排除路由的 Android 应用包名。
|
||||
|
||||
#### platform
|
||||
|
||||
平台特定的设置,由客户端应用提供。
|
||||
|
||||
#### platform.http_proxy
|
||||
|
||||
系统 HTTP 代理设置。
|
||||
|
||||
### 监听字段
|
||||
|
||||
参阅 [监听字段](/zh/configuration/shared/listen/)。
|
||||
|
||||
54
docs/configuration/inbound/vless.md
Normal file
54
docs/configuration/inbound/vless.md
Normal file
@@ -0,0 +1,54 @@
|
||||
### Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "vless-in",
|
||||
|
||||
... // Listen Fields
|
||||
|
||||
"users": [
|
||||
{
|
||||
"name": "sekai",
|
||||
"uuid": "bf000d23-0752-40b4-affe-68f7707a9661",
|
||||
"flow": ""
|
||||
}
|
||||
],
|
||||
"tls": {},
|
||||
"transport": {}
|
||||
}
|
||||
```
|
||||
|
||||
### Listen Fields
|
||||
|
||||
See [Listen Fields](/configuration/shared/listen) for details.
|
||||
|
||||
### Fields
|
||||
|
||||
#### users
|
||||
|
||||
==Required==
|
||||
|
||||
VLESS users.
|
||||
|
||||
#### users.uuid
|
||||
|
||||
==Required==
|
||||
|
||||
VLESS user id.
|
||||
|
||||
#### users.flow
|
||||
|
||||
VLESS Sub-protocol.
|
||||
|
||||
Available values:
|
||||
|
||||
* `xtls-rprx-vision`
|
||||
|
||||
#### tls
|
||||
|
||||
TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
|
||||
|
||||
#### transport
|
||||
|
||||
V2Ray Transport configuration, see [V2Ray Transport](/configuration/shared/v2ray-transport).
|
||||
54
docs/configuration/inbound/vless.zh.md
Normal file
54
docs/configuration/inbound/vless.zh.md
Normal file
@@ -0,0 +1,54 @@
|
||||
### 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "vless-in",
|
||||
|
||||
... // 监听字段
|
||||
|
||||
"users": [
|
||||
{
|
||||
"name": "sekai",
|
||||
"uuid": "bf000d23-0752-40b4-affe-68f7707a9661",
|
||||
"flow": ""
|
||||
}
|
||||
],
|
||||
"tls": {},
|
||||
"transport": {}
|
||||
}
|
||||
```
|
||||
|
||||
### 监听字段
|
||||
|
||||
参阅 [监听字段](/zh/configuration/shared/listen/)。
|
||||
|
||||
### 字段
|
||||
|
||||
#### users
|
||||
|
||||
==必填==
|
||||
|
||||
VLESS 用户。
|
||||
|
||||
#### users.uuid
|
||||
|
||||
==必填==
|
||||
|
||||
VLESS 用户 ID。
|
||||
|
||||
#### users.flow
|
||||
|
||||
VLESS 子协议。
|
||||
|
||||
可用值:
|
||||
|
||||
* `xtls-rprx-vision`
|
||||
|
||||
#### tls
|
||||
|
||||
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
|
||||
|
||||
#### transport
|
||||
|
||||
V2Ray 传输配置,参阅 [V2Ray 传输层](/zh/configuration/shared/v2ray-transport)。
|
||||
@@ -8,6 +8,7 @@ sing-box uses JSON for configuration files.
|
||||
{
|
||||
"log": {},
|
||||
"dns": {},
|
||||
"ntp": {},
|
||||
"inbounds": [],
|
||||
"outbounds": [],
|
||||
"route": {},
|
||||
@@ -21,6 +22,7 @@ sing-box uses JSON for configuration files.
|
||||
|----------------|--------------------------------|
|
||||
| `log` | [Log](./log) |
|
||||
| `dns` | [DNS](./dns) |
|
||||
| `ntp` | [NTP](./ntp) |
|
||||
| `inbounds` | [Inbound](./inbound) |
|
||||
| `outbounds` | [Outbound](./outbound) |
|
||||
| `route` | [Route](./route) |
|
||||
|
||||
50
docs/configuration/ntp/index.md
Normal file
50
docs/configuration/ntp/index.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# NTP
|
||||
|
||||
Built-in NTP client service.
|
||||
|
||||
If enabled, it will provide time for protocols like TLS/Shadowsocks/VMess, which is useful for environments where time
|
||||
synchronization is not possible.
|
||||
|
||||
### Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"ntp": {
|
||||
"enabled": false,
|
||||
"server": "time.apple.com",
|
||||
"server_port": 123,
|
||||
"interval": "30m",
|
||||
|
||||
... // Dial Fields
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Fields
|
||||
|
||||
#### enabled
|
||||
|
||||
Enable NTP service.
|
||||
|
||||
#### server
|
||||
|
||||
==Required==
|
||||
|
||||
NTP server address.
|
||||
|
||||
#### server_port
|
||||
|
||||
NTP server port.
|
||||
|
||||
123 is used by default.
|
||||
|
||||
#### interval
|
||||
|
||||
Time synchronization interval.
|
||||
|
||||
30 minutes is used by default.
|
||||
|
||||
### Dial Fields
|
||||
|
||||
See [Dial Fields](/configuration/shared/dial) for details.
|
||||
49
docs/configuration/ntp/index.zh.md
Normal file
49
docs/configuration/ntp/index.zh.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# NTP
|
||||
|
||||
内建的 NTP 客户端服务。
|
||||
|
||||
如果启用,它将为像 TLS/Shadowsocks/VMess 这样的协议提供时间,这对于无法进行时间同步的环境很有用。
|
||||
|
||||
### 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"ntp": {
|
||||
"enabled": false,
|
||||
"server": "time.apple.com",
|
||||
"server_port": 123,
|
||||
"interval": "30m",
|
||||
|
||||
... // 拨号字段
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 字段
|
||||
|
||||
#### enabled
|
||||
|
||||
启用 NTP 服务。
|
||||
|
||||
#### server
|
||||
|
||||
==必填==
|
||||
|
||||
NTP 服务器地址。
|
||||
|
||||
#### server_port
|
||||
|
||||
NTP 服务器端口。
|
||||
|
||||
默认使用 123。
|
||||
|
||||
#### interval
|
||||
|
||||
时间同步间隔。
|
||||
|
||||
默认使用 30 分钟。
|
||||
|
||||
### 拨号字段
|
||||
|
||||
参阅 [拨号字段](/zh/configuration/shared/dial/)。
|
||||
@@ -28,6 +28,7 @@
|
||||
| `hysteria` | [Hysteria](./hysteria) |
|
||||
| `shadowsocksr` | [ShadowsocksR](./shadowsocksr) |
|
||||
| `vless` | [VLESS](./vless) |
|
||||
| `shadowtls` | [ShadowTLS](./shadowtls) |
|
||||
| `tor` | [Tor](./tor) |
|
||||
| `ssh` | [SSH](./ssh) |
|
||||
| `dns` | [DNS](./dns) |
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 1080,
|
||||
"uuid": "bf000d23-0752-40b4-affe-68f7707a9661",
|
||||
"flow": "xtls-rprx-vision",
|
||||
"network": "tcp",
|
||||
"tls": {},
|
||||
"packet_encoding": "",
|
||||
@@ -17,10 +18,6 @@
|
||||
}
|
||||
```
|
||||
|
||||
!!! warning ""
|
||||
|
||||
The VLESS protocol is architecturally coupled to v2ray and is unmaintained. This outbound is provided for compatibility purposes only.
|
||||
|
||||
### Fields
|
||||
|
||||
#### server
|
||||
@@ -39,7 +36,15 @@ The server port.
|
||||
|
||||
==Required==
|
||||
|
||||
The VLESS user id.
|
||||
VLESS user id.
|
||||
|
||||
#### flow
|
||||
|
||||
VLESS Sub-protocol.
|
||||
|
||||
Available values:
|
||||
|
||||
* `xtls-rprx-vision`
|
||||
|
||||
#### network
|
||||
|
||||
@@ -55,6 +60,8 @@ TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
|
||||
|
||||
#### packet_encoding
|
||||
|
||||
UDP packet encoding, xudp is used by default.
|
||||
|
||||
| Encoding | Description |
|
||||
|------------|-----------------------|
|
||||
| (none) | Disabled |
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 1080,
|
||||
"uuid": "bf000d23-0752-40b4-affe-68f7707a9661",
|
||||
"flow": "xtls-rprx-vision",
|
||||
"network": "tcp",
|
||||
"tls": {},
|
||||
"packet_encoding": "",
|
||||
@@ -17,10 +18,6 @@
|
||||
}
|
||||
```
|
||||
|
||||
!!! warning ""
|
||||
|
||||
VLESS 协议与 v2ray 架构耦合且无人维护。 提供此出站仅出于兼容性目的。
|
||||
|
||||
### 字段
|
||||
|
||||
#### server
|
||||
@@ -41,6 +38,14 @@
|
||||
|
||||
VLESS 用户 ID。
|
||||
|
||||
#### flow
|
||||
|
||||
VLESS 子协议。
|
||||
|
||||
可用值:
|
||||
|
||||
* `xtls-rprx-vision`
|
||||
|
||||
#### network
|
||||
|
||||
启用的网络协议。
|
||||
@@ -55,6 +60,8 @@ TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
||||
|
||||
#### packet_encoding
|
||||
|
||||
UDP 包编码,默认使用 xudp。
|
||||
|
||||
| 编码 | 描述 |
|
||||
|------------|---------------|
|
||||
| (空) | 禁用 |
|
||||
|
||||
@@ -50,7 +50,7 @@ Encryption methods:
|
||||
* `none`
|
||||
* `zero`
|
||||
* `aes-128-gcm`
|
||||
* `chancha20-poly1305`
|
||||
* `chacha20-poly1305`
|
||||
|
||||
Legacy encryption methods:
|
||||
|
||||
@@ -86,6 +86,8 @@ TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
|
||||
|
||||
#### packet_encoding
|
||||
|
||||
UDP packet encoding.
|
||||
|
||||
| Encoding | Description |
|
||||
|------------|-----------------------|
|
||||
| (none) | Disabled |
|
||||
|
||||
@@ -50,7 +50,7 @@ VMess 用户 ID。
|
||||
* `none`
|
||||
* `zero`
|
||||
* `aes-128-gcm`
|
||||
* `chancha20-poly1305`
|
||||
* `chacha20-poly1305`
|
||||
|
||||
旧加密方法:
|
||||
|
||||
@@ -86,6 +86,8 @@ TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
||||
|
||||
#### packet_encoding
|
||||
|
||||
UDP 包编码。
|
||||
|
||||
| 编码 | 描述 |
|
||||
|------------|---------------|
|
||||
| (空) | 禁用 |
|
||||
|
||||
@@ -26,6 +26,20 @@
|
||||
"key_id": "",
|
||||
"mac_key": ""
|
||||
}
|
||||
},
|
||||
"reality": {
|
||||
"enabled": false,
|
||||
"handshake": {
|
||||
"server": "google.com",
|
||||
"server_port": 443,
|
||||
|
||||
... // Dial Fields
|
||||
},
|
||||
"private_key": "UuMBgl7MXTPx9inmQp2UC7Jcnwc6XYbwDNebonM-FCc",
|
||||
"short_id": [
|
||||
"0123456789abcdef"
|
||||
],
|
||||
"max_time_difference": "1m"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -53,6 +67,11 @@
|
||||
"utls": {
|
||||
"enabled": false,
|
||||
"fingerprint": ""
|
||||
},
|
||||
"reality": {
|
||||
"enabled": false,
|
||||
"public_key": "jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0",
|
||||
"short_id": "0123456789abcdef"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -199,6 +218,7 @@ Available fingerprint values:
|
||||
* ios
|
||||
* android
|
||||
* random
|
||||
* randomized
|
||||
|
||||
Chrome fingerprint will be used if empty.
|
||||
|
||||
@@ -275,6 +295,54 @@ The key identifier.
|
||||
|
||||
The MAC key.
|
||||
|
||||
### Reality Fields
|
||||
|
||||
!!! warning ""
|
||||
|
||||
reality server is not included by default, see [Installation](/#installation).
|
||||
|
||||
!!! warning ""
|
||||
|
||||
uTLS, which is required by reality client is not included by default, see [Installation](/#installation).
|
||||
|
||||
#### handshake
|
||||
|
||||
==Server only==
|
||||
|
||||
==Required==
|
||||
|
||||
Handshake server address and [Dial options](/configuration/shared/dial).
|
||||
|
||||
#### private_key
|
||||
|
||||
==Server only==
|
||||
|
||||
==Required==
|
||||
|
||||
Private key, generated by `sing-box generate reality-keypair`.
|
||||
|
||||
#### public_key
|
||||
|
||||
==Client only==
|
||||
|
||||
==Required==
|
||||
|
||||
Public key, generated by `sing-box generate reality-keypair`.
|
||||
|
||||
#### short_id
|
||||
|
||||
==Required==
|
||||
|
||||
A 8-bit hex string.
|
||||
|
||||
#### max_time_difference
|
||||
|
||||
==Server only==
|
||||
|
||||
The maximum time difference between the server and the client.
|
||||
|
||||
Check disabled if empty.
|
||||
|
||||
### Reload
|
||||
|
||||
For server configuration, certificate and key will be automatically reloaded if modified.
|
||||
@@ -26,6 +26,20 @@
|
||||
"key_id": "",
|
||||
"mac_key": ""
|
||||
}
|
||||
},
|
||||
"reality": {
|
||||
"enabled": false,
|
||||
"handshake": {
|
||||
"server": "google.com",
|
||||
"server_port": 443,
|
||||
|
||||
... // 拨号字段
|
||||
},
|
||||
"private_key": "UuMBgl7MXTPx9inmQp2UC7Jcnwc6XYbwDNebonM-FCc",
|
||||
"short_id": [
|
||||
"0123456789abcdef"
|
||||
],
|
||||
"max_time_difference": "1m"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -53,6 +67,11 @@
|
||||
"utls": {
|
||||
"enabled": false,
|
||||
"fingerprint": ""
|
||||
},
|
||||
"reality": {
|
||||
"enabled": false,
|
||||
"public_key": "jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0",
|
||||
"short_id": "0123456789abcdef"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -199,6 +218,7 @@ uTLS 是 "crypto/tls" 的一个分支,它提供了 ClientHello 指纹识别阻
|
||||
* ios
|
||||
* android
|
||||
* random
|
||||
* randomized
|
||||
|
||||
默认使用 chrome 指纹。
|
||||
|
||||
@@ -271,6 +291,52 @@ EAB(外部帐户绑定)包含将 ACME 帐户绑定或映射到其他已知
|
||||
|
||||
MAC 密钥。
|
||||
|
||||
### Reality 字段
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含 reality 服务器,参阅 [安装](/zh/#_2)。
|
||||
|
||||
!!! warning ""
|
||||
|
||||
默认安装不包含被 reality 客户端需要的 uTLS, 参阅 [安装](/zh/#_2)。
|
||||
|
||||
#### handshake
|
||||
|
||||
==仅服务器==
|
||||
|
||||
==必填==
|
||||
|
||||
握手服务器地址和 [拨号参数](/zh/configuration/shared/dial/)。
|
||||
|
||||
#### private_key
|
||||
|
||||
==仅服务器==
|
||||
|
||||
==必填==
|
||||
|
||||
私钥,由 `sing-box generate reality-keypair` 生成。
|
||||
|
||||
#### public_key
|
||||
|
||||
==仅客户端==
|
||||
|
||||
==必填==
|
||||
|
||||
公钥,由 `sing-box generate reality-keypair` 生成。
|
||||
|
||||
#### short_id
|
||||
|
||||
==必填==
|
||||
|
||||
一个八位十六进制的字符串。
|
||||
|
||||
#### max_time_difference
|
||||
|
||||
服务器与和客户端之间允许的最大时间差。
|
||||
|
||||
默认禁用检查。
|
||||
|
||||
### 重载
|
||||
|
||||
对于服务器配置,如果修改,证书和密钥将自动重新加载。
|
||||
@@ -8,7 +8,12 @@
|
||||
"listen": "::",
|
||||
"listen_port": 4443,
|
||||
"version": 3,
|
||||
"password": "fuck me till the daylight",
|
||||
"users": [
|
||||
{
|
||||
"name": "sekai",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg=="
|
||||
}
|
||||
],
|
||||
"handshake": {
|
||||
"server": "google.com",
|
||||
"server_port": 443
|
||||
@@ -48,10 +53,14 @@
|
||||
"server": "127.0.0.1",
|
||||
"server_port": 4443,
|
||||
"version": 3,
|
||||
"password": "fuck me till the daylight",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
||||
"tls": {
|
||||
"enabled": true,
|
||||
"server_name": "google.com"
|
||||
"server_name": "google.com",
|
||||
"utls": {
|
||||
"enabled": true,
|
||||
"fingerprint": "chrome"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -8,45 +8,6 @@ Welcome to the wiki page for the sing-box project.
|
||||
|
||||
The universal proxy platform.
|
||||
|
||||
## Installation
|
||||
|
||||
sing-box requires Golang **1.18.5** or a higher version.
|
||||
|
||||
```bash
|
||||
go install -v github.com/sagernet/sing-box/cmd/sing-box@latest
|
||||
```
|
||||
|
||||
Install with options:
|
||||
|
||||
```bash
|
||||
go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@latest
|
||||
```
|
||||
|
||||
| Build Tag | Description |
|
||||
|------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `with_quic` | Build with QUIC support, see [QUIC and HTTP3 DNS transports](./configuration/dns/server), [Naive inbound](./configuration/inbound/naive), [Hysteria Inbound](./configuration/inbound/hysteria), [Hysteria Outbound](./configuration/outbound/hysteria) and [V2Ray Transport#QUIC](./configuration/shared/v2ray-transport#quic). |
|
||||
| `with_grpc` | Build with standard gRPC support, see [V2Ray Transport#gRPC](./configuration/shared/v2ray-transport#grpc). |
|
||||
| `with_dhcp` | Build with DHCP support, see [DHCP DNS transport](./configuration/dns/server). |
|
||||
| `with_wireguard` | Build with WireGuard support, see [WireGuard outbound](./configuration/outbound/wireguard). |
|
||||
| `with_shadowsocksr` | Build with ShadowsocksR support, see [ShadowsocksR outbound](./configuration/outbound/shadowsocksr). |
|
||||
| `with_ech` | Build with TLS ECH extension support for TLS outbound, see [TLS](./configuration/shared/tls#ech). |
|
||||
| `with_utls` | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](./configuration/shared/tls#utls). |
|
||||
| `with_acme` | Build with ACME TLS certificate issuer support, see [TLS](./configuration/shared/tls). |
|
||||
| `with_clash_api` | Build with Clash API support, see [Experimental](./configuration/experimental#clash-api-fields). |
|
||||
| `with_v2ray_api` | Build with V2Ray API support, see [Experimental](./configuration/experimental#v2ray-api-fields). |
|
||||
| `with_gvisor` | Build with gVisor support, see [Tun inbound](./configuration/inbound/tun#stack) and [WireGuard outbound](./configuration/outbound/wireguard#system_interface). |
|
||||
| `with_embedded_tor` (CGO required) | Build with embedded Tor support, see [Tor outbound](./configuration/outbound/tor). |
|
||||
| `with_lwip` (CGO required) | Build with LWIP Tun stack support, see [Tun inbound](./configuration/inbound/tun#stack). |
|
||||
|
||||
The binary is built under $GOPATH/bin
|
||||
|
||||
```bash
|
||||
sing-box version
|
||||
```
|
||||
|
||||
It is also recommended to use systemd to manage sing-box service,
|
||||
see [Linux server installation example](./examples/linux-server-installation).
|
||||
|
||||
## License
|
||||
|
||||
```
|
||||
|
||||
@@ -8,45 +8,6 @@ description: 欢迎来到该 sing-box 项目的文档页。
|
||||
|
||||
通用代理平台。
|
||||
|
||||
## 安装
|
||||
|
||||
sing-box 需要 Golang **1.18.5** 或更高版本。
|
||||
|
||||
```bash
|
||||
go install -v github.com/sagernet/sing-box/cmd/sing-box@latest
|
||||
```
|
||||
|
||||
自定义安装:
|
||||
|
||||
```bash
|
||||
go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@latest
|
||||
```
|
||||
|
||||
| 构建标志 | 描述 |
|
||||
|------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `with_quic` | 启用 QUIC 支持,参阅 [QUIC 和 HTTP3 DNS 传输层](./configuration/dns/server),[Naive 入站](./configuration/inbound/naive),[Hysteria 入站](./configuration/inbound/hysteria),[Hysteria 出站](./configuration/outbound/hysteria) 和 [V2Ray 传输层#QUIC](./configuration/shared/v2ray-transport#quic)。 |
|
||||
| `with_grpc` | 启用标准 gRPC 支持,参阅 [V2Ray 传输层#gRPC](./configuration/shared/v2ray-transport#grpc)。 |
|
||||
| `with_dhcp` | 启用 DHCP 支持,参阅 [DHCP DNS 传输层](./configuration/dns/server)。 |
|
||||
| `with_wireguard` | 启用 WireGuard 支持,参阅 [WireGuard 出站](./configuration/outbound/wireguard)。 |
|
||||
| `with_shadowsocksr` | 启用 ShadowsocksR 支持,参阅 [ShadowsocksR 出站](./configuration/outbound/shadowsocksr)。 |
|
||||
| `with_ech` | 启用 TLS ECH 扩展支持,参阅 [TLS](./configuration/shared/tls#ech)。 |
|
||||
| `with_utls` | 启用 uTLS 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
|
||||
| `with_acme` | 启用 ACME TLS 证书签发支持,参阅 [TLS](./configuration/shared/tls)。 |
|
||||
| `with_clash_api` | 启用 Clash API 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
|
||||
| `with_v2ray_api` | 启用 V2Rat API 支持,参阅 [实验性](./configuration/experimental#v2ray-api-fields)。 |
|
||||
| `with_gvisor` | 启用 gVisor 支持,参阅 [Tun 入站](./configuration/inbound/tun#stack) 和 [WireGuard 出站](./configuration/outbound/wireguard#system_interface)。 |
|
||||
| `with_embedded_tor` (需要 CGO) | 启用 嵌入式 Tor 支持,参阅 [Tor 出站](./configuration/outbound/tor)。 |
|
||||
| `with_lwip` (需要 CGO) | 启用 LWIP Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
|
||||
|
||||
二进制文件将被构建在 `$GOPATH/bin` 下。
|
||||
|
||||
```bash
|
||||
sing-box version
|
||||
```
|
||||
|
||||
同时推荐使用 systemd 来管理 sing-box 服务器实例。
|
||||
参阅 [Linux 服务器安装示例](./examples/linux-server-installation)。
|
||||
|
||||
## 授权
|
||||
|
||||
```
|
||||
|
||||
21
docs/installation/clients/sfi/index.md
Normal file
21
docs/installation/clients/sfi/index.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# SFI
|
||||
|
||||
Experimental official iOS client for sing-box.
|
||||
|
||||
#### Requirements
|
||||
|
||||
* iOS 15.0+
|
||||
* macOS 12.0+ with Apple Silicon
|
||||
|
||||
#### Download
|
||||
|
||||
* [TestFlight](https://testflight.apple.com/join/c6ylui2j)
|
||||
|
||||
#### Limit
|
||||
|
||||
* `system` tun stack not working
|
||||
|
||||
#### Privacy policy
|
||||
|
||||
* SFI did not collect or share personal data.
|
||||
* The data generated by the software is always on your device.
|
||||
21
docs/installation/clients/sfi/index.zh.md
Normal file
21
docs/installation/clients/sfi/index.zh.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# SFI
|
||||
|
||||
实验性的官方 iOS sing-box 客户端。
|
||||
|
||||
#### 要求
|
||||
|
||||
* iOS 15.0+
|
||||
* macOS 12.0+ with Apple Silicon
|
||||
|
||||
#### 下载
|
||||
|
||||
* [TestFlight](https://testflight.apple.com/join/c6ylui2j)
|
||||
|
||||
#### 限制
|
||||
|
||||
* `system` tun stack 不工作
|
||||
|
||||
#### 隐私政策
|
||||
|
||||
* SFI 不收集或共享个人数据。
|
||||
* 软件生成的数据始终在您的设备上。
|
||||
39
docs/installation/from-source.md
Normal file
39
docs/installation/from-source.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Install from source
|
||||
|
||||
sing-box requires Golang **1.18.5** or a higher version.
|
||||
|
||||
```bash
|
||||
go install -v github.com/sagernet/sing-box/cmd/sing-box@latest
|
||||
```
|
||||
|
||||
Install with options:
|
||||
|
||||
```bash
|
||||
go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@latest
|
||||
```
|
||||
|
||||
| Build Tag | Description |
|
||||
|------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `with_quic` | Build with QUIC support, see [QUIC and HTTP3 DNS transports](./configuration/dns/server), [Naive inbound](./configuration/inbound/naive), [Hysteria Inbound](./configuration/inbound/hysteria), [Hysteria Outbound](./configuration/outbound/hysteria) and [V2Ray Transport#QUIC](./configuration/shared/v2ray-transport#quic). |
|
||||
| `with_grpc` | Build with standard gRPC support, see [V2Ray Transport#gRPC](./configuration/shared/v2ray-transport#grpc). |
|
||||
| `with_dhcp` | Build with DHCP support, see [DHCP DNS transport](./configuration/dns/server). |
|
||||
| `with_wireguard` | Build with WireGuard support, see [WireGuard outbound](./configuration/outbound/wireguard). |
|
||||
| `with_shadowsocksr` | Build with ShadowsocksR support, see [ShadowsocksR outbound](./configuration/outbound/shadowsocksr). |
|
||||
| `with_ech` | Build with TLS ECH extension support for TLS outbound, see [TLS](./configuration/shared/tls#ech). |
|
||||
| `with_utls` | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](./configuration/shared/tls#utls). |
|
||||
| `with_reality_server` | Build with reality TLS server support, see [TLS](./configuration/shared/tls). |
|
||||
| `with_acme` | Build with ACME TLS certificate issuer support, see [TLS](./configuration/shared/tls). |
|
||||
| `with_clash_api` | Build with Clash API support, see [Experimental](./configuration/experimental#clash-api-fields). |
|
||||
| `with_v2ray_api` | Build with V2Ray API support, see [Experimental](./configuration/experimental#v2ray-api-fields). |
|
||||
| `with_gvisor` | Build with gVisor support, see [Tun inbound](./configuration/inbound/tun#stack) and [WireGuard outbound](./configuration/outbound/wireguard#system_interface). |
|
||||
| `with_embedded_tor` (CGO required) | Build with embedded Tor support, see [Tor outbound](./configuration/outbound/tor). |
|
||||
| `with_lwip` (CGO required) | Build with LWIP Tun stack support, see [Tun inbound](./configuration/inbound/tun#stack). |
|
||||
|
||||
The binary is built under $GOPATH/bin
|
||||
|
||||
```bash
|
||||
sing-box version
|
||||
```
|
||||
|
||||
It is also recommended to use systemd to manage sing-box service,
|
||||
see [Linux server installation example](./examples/linux-server-installation).
|
||||
39
docs/installation/from-source.zh.md
Normal file
39
docs/installation/from-source.zh.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# 从源代码安装
|
||||
|
||||
sing-box 需要 Golang **1.18.5** 或更高版本。
|
||||
|
||||
```bash
|
||||
go install -v github.com/sagernet/sing-box/cmd/sing-box@latest
|
||||
```
|
||||
|
||||
自定义安装:
|
||||
|
||||
```bash
|
||||
go install -v -tags with_clash_api github.com/sagernet/sing-box/cmd/sing-box@latest
|
||||
```
|
||||
|
||||
| 构建标志 | 描述 |
|
||||
|------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `with_quic` | 启用 QUIC 支持,参阅 [QUIC 和 HTTP3 DNS 传输层](./configuration/dns/server),[Naive 入站](./configuration/inbound/naive),[Hysteria 入站](./configuration/inbound/hysteria),[Hysteria 出站](./configuration/outbound/hysteria) 和 [V2Ray 传输层#QUIC](./configuration/shared/v2ray-transport#quic)。 |
|
||||
| `with_grpc` | 启用标准 gRPC 支持,参阅 [V2Ray 传输层#gRPC](./configuration/shared/v2ray-transport#grpc)。 |
|
||||
| `with_dhcp` | 启用 DHCP 支持,参阅 [DHCP DNS 传输层](./configuration/dns/server)。 |
|
||||
| `with_wireguard` | 启用 WireGuard 支持,参阅 [WireGuard 出站](./configuration/outbound/wireguard)。 |
|
||||
| `with_shadowsocksr` | 启用 ShadowsocksR 支持,参阅 [ShadowsocksR 出站](./configuration/outbound/shadowsocksr)。 |
|
||||
| `with_ech` | 启用 TLS ECH 扩展支持,参阅 [TLS](./configuration/shared/tls#ech)。 |
|
||||
| `with_utls` | 启用 [uTLS](https://github.com/refraction-networking/utls) 支持,参阅 [TLS](./configuration/shared/tls#utls)。 |
|
||||
| `with_reality_server` | 启用 reality TLS 服务器支持,参阅 [TLS](./configuration/shared/tls)。 |
|
||||
| `with_acme` | 启用 ACME TLS 证书签发支持,参阅 [TLS](./configuration/shared/tls)。 |
|
||||
| `with_clash_api` | 启用 Clash API 支持,参阅 [实验性](./configuration/experimental#clash-api-fields)。 |
|
||||
| `with_v2ray_api` | 启用 V2Ray API 支持,参阅 [实验性](./configuration/experimental#v2ray-api-fields)。 |
|
||||
| `with_gvisor` | 启用 gVisor 支持,参阅 [Tun 入站](./configuration/inbound/tun#stack) 和 [WireGuard 出站](./configuration/outbound/wireguard#system_interface)。 |
|
||||
| `with_embedded_tor` (需要 CGO) | 启用 嵌入式 Tor 支持,参阅 [Tor 出站](./configuration/outbound/tor)。 |
|
||||
| `with_lwip` (需要 CGO) | 启用 LWIP Tun 栈支持,参阅 [Tun 入站](./configuration/inbound/tun#stack)。 |
|
||||
|
||||
二进制文件将被构建在 `$GOPATH/bin` 下。
|
||||
|
||||
```bash
|
||||
sing-box version
|
||||
```
|
||||
|
||||
同时推荐使用 systemd 来管理 sing-box 服务器实例。
|
||||
参阅 [Linux 服务器安装示例](./examples/linux-server-installation)。
|
||||
@@ -42,9 +42,9 @@ type Server struct {
|
||||
httpServer *http.Server
|
||||
trafficManager *trafficontrol.Manager
|
||||
urlTestHistory *urltest.HistoryStorage
|
||||
tcpListener net.Listener
|
||||
mode string
|
||||
storeSelected bool
|
||||
cacheFilePath string
|
||||
cacheFile adapter.ClashCacheFile
|
||||
}
|
||||
|
||||
@@ -71,11 +71,12 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
||||
if cachePath == "" {
|
||||
cachePath = "cache.db"
|
||||
}
|
||||
cacheFile, err := cachefile.Open(cachePath)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "open cache file")
|
||||
if foundPath, loaded := C.FindPath(cachePath); loaded {
|
||||
cachePath = foundPath
|
||||
} else {
|
||||
cachePath = C.BasePath(cachePath)
|
||||
}
|
||||
server.cacheFile = cacheFile
|
||||
server.cacheFilePath = cachePath
|
||||
}
|
||||
cors := cors.New(cors.Options{
|
||||
AllowedOrigins: []string{"*"},
|
||||
@@ -103,7 +104,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
||||
})
|
||||
if options.ExternalUI != "" {
|
||||
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/*", func(w http.ResponseWriter, r *http.Request) {
|
||||
fs.ServeHTTP(w, r)
|
||||
@@ -114,12 +115,18 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
||||
}
|
||||
|
||||
func (s *Server) Start() error {
|
||||
if s.cacheFilePath != "" {
|
||||
cacheFile, err := cachefile.Open(s.cacheFilePath)
|
||||
if err != nil {
|
||||
return E.Cause(err, "open cache file")
|
||||
}
|
||||
s.cacheFile = cacheFile
|
||||
}
|
||||
listener, err := net.Listen("tcp", s.httpServer.Addr)
|
||||
if err != nil {
|
||||
return E.Cause(err, "external controller listen error")
|
||||
}
|
||||
s.logger.Info("restful api listening at ", listener.Addr())
|
||||
s.tcpListener = listener
|
||||
go func() {
|
||||
err = s.httpServer.Serve(listener)
|
||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
@@ -132,7 +139,6 @@ func (s *Server) Start() error {
|
||||
func (s *Server) Close() error {
|
||||
return common.Close(
|
||||
common.PtrOrNil(s.httpServer),
|
||||
s.tcpListener,
|
||||
s.trafficManager,
|
||||
s.cacheFile,
|
||||
)
|
||||
|
||||
11
experimental/libbox/command.go
Normal file
11
experimental/libbox/command.go
Normal file
@@ -0,0 +1,11 @@
|
||||
//go:build darwin
|
||||
|
||||
package libbox
|
||||
|
||||
const (
|
||||
CommandLog int32 = iota
|
||||
CommandStatus
|
||||
CommandServiceStop
|
||||
CommandServiceReload
|
||||
CommandCloseConnections
|
||||
)
|
||||
75
experimental/libbox/command_client.go
Normal file
75
experimental/libbox/command_client.go
Normal file
@@ -0,0 +1,75 @@
|
||||
//go:build darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type CommandClient struct {
|
||||
sharedDirectory string
|
||||
handler CommandClientHandler
|
||||
conn net.Conn
|
||||
options CommandClientOptions
|
||||
}
|
||||
|
||||
type CommandClientOptions struct {
|
||||
Command int32
|
||||
StatusInterval int64
|
||||
}
|
||||
|
||||
type CommandClientHandler interface {
|
||||
Connected()
|
||||
Disconnected(message string)
|
||||
WriteLog(message string)
|
||||
WriteStatus(message *StatusMessage)
|
||||
}
|
||||
|
||||
func NewCommandClient(sharedDirectory string, handler CommandClientHandler, options *CommandClientOptions) *CommandClient {
|
||||
return &CommandClient{
|
||||
sharedDirectory: sharedDirectory,
|
||||
handler: handler,
|
||||
options: common.PtrValueOrDefault(options),
|
||||
}
|
||||
}
|
||||
|
||||
func clientConnect(sharedDirectory string) (net.Conn, error) {
|
||||
return net.DialUnix("unix", nil, &net.UnixAddr{
|
||||
Name: filepath.Join(sharedDirectory, "command.sock"),
|
||||
Net: "unix",
|
||||
})
|
||||
}
|
||||
|
||||
func (c *CommandClient) Connect() error {
|
||||
conn, err := clientConnect(c.sharedDirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.conn = conn
|
||||
err = binary.Write(conn, binary.BigEndian, uint8(c.options.Command))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch c.options.Command {
|
||||
case CommandLog:
|
||||
c.handler.Connected()
|
||||
go c.handleLogConn(conn)
|
||||
case CommandStatus:
|
||||
err = binary.Write(conn, binary.BigEndian, c.options.StatusInterval)
|
||||
if err != nil {
|
||||
return E.Cause(err, "write interval")
|
||||
}
|
||||
c.handler.Connected()
|
||||
go c.handleStatusConn(conn)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CommandClient) Disconnect() error {
|
||||
return common.Close(c.conn)
|
||||
}
|
||||
30
experimental/libbox/command_conntrack.go
Normal file
30
experimental/libbox/command_conntrack.go
Normal file
@@ -0,0 +1,30 @@
|
||||
//go:build darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
runtimeDebug "runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/common/dialer/conntrack"
|
||||
)
|
||||
|
||||
func ClientCloseConnections(sharedDirectory string) error {
|
||||
conn, err := clientConnect(sharedDirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
return binary.Write(conn, binary.BigEndian, uint8(CommandCloseConnections))
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleCloseConnections(conn net.Conn) error {
|
||||
conntrack.Close()
|
||||
go func() {
|
||||
time.Sleep(time.Second)
|
||||
runtimeDebug.FreeOSMemory()
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
103
experimental/libbox/command_log.go
Normal file
103
experimental/libbox/command_log.go
Normal file
@@ -0,0 +1,103 @@
|
||||
//go:build darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
func (s *CommandServer) WriteMessage(message string) {
|
||||
s.subscriber.Emit(message)
|
||||
s.access.Lock()
|
||||
s.savedLines.PushBack(message)
|
||||
if s.savedLines.Len() > 100 {
|
||||
s.savedLines.Remove(s.savedLines.Front())
|
||||
}
|
||||
s.access.Unlock()
|
||||
}
|
||||
|
||||
func readLog(reader io.Reader) ([]byte, error) {
|
||||
var messageLength uint16
|
||||
err := binary.Read(reader, binary.BigEndian, &messageLength)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := make([]byte, messageLength)
|
||||
_, err = io.ReadFull(reader, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func writeLog(writer io.Writer, message []byte) error {
|
||||
err := binary.Write(writer, binary.BigEndian, uint16(len(message)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = writer.Write(message)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleLogConn(conn net.Conn) error {
|
||||
var savedLines []string
|
||||
s.access.Lock()
|
||||
savedLines = make([]string, 0, s.savedLines.Len())
|
||||
for element := s.savedLines.Front(); element != nil; element = element.Next() {
|
||||
savedLines = append(savedLines, element.Value)
|
||||
}
|
||||
s.access.Unlock()
|
||||
subscription, done, err := s.observer.Subscribe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.observer.UnSubscribe(subscription)
|
||||
for _, line := range savedLines {
|
||||
err = writeLog(conn, []byte(line))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
ctx := connKeepAlive(conn)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case message := <-subscription:
|
||||
err = writeLog(conn, []byte(message))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case <-done:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CommandClient) handleLogConn(conn net.Conn) {
|
||||
for {
|
||||
message, err := readLog(conn)
|
||||
if err != nil {
|
||||
c.handler.Disconnected(err.Error())
|
||||
return
|
||||
}
|
||||
c.handler.WriteLog(string(message))
|
||||
}
|
||||
}
|
||||
|
||||
func connKeepAlive(reader io.Reader) context.Context {
|
||||
ctx, cancel := context.WithCancelCause(context.Background())
|
||||
go func() {
|
||||
for {
|
||||
_, err := readLog(reader)
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return ctx
|
||||
}
|
||||
48
experimental/libbox/command_reload.go
Normal file
48
experimental/libbox/command_reload.go
Normal file
@@ -0,0 +1,48 @@
|
||||
//go:build darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
func ClientServiceReload(sharedDirectory string) error {
|
||||
conn, err := clientConnect(sharedDirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
err = binary.Write(conn, binary.BigEndian, uint8(CommandServiceReload))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var hasError bool
|
||||
err = binary.Read(conn, binary.BigEndian, &hasError)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hasError {
|
||||
errorMessage, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return E.New(errorMessage)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleServiceReload(conn net.Conn) error {
|
||||
rErr := s.handler.ServiceReload()
|
||||
err := binary.Write(conn, binary.BigEndian, rErr != nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rErr != nil {
|
||||
return rw.WriteVString(conn, rErr.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
99
experimental/libbox/command_server.go
Normal file
99
experimental/libbox/command_server.go
Normal file
@@ -0,0 +1,99 @@
|
||||
//go:build darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/observable"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
)
|
||||
|
||||
type CommandServer struct {
|
||||
sockPath string
|
||||
listener net.Listener
|
||||
handler CommandServerHandler
|
||||
|
||||
access sync.Mutex
|
||||
savedLines *list.List[string]
|
||||
subscriber *observable.Subscriber[string]
|
||||
observer *observable.Observer[string]
|
||||
}
|
||||
|
||||
type CommandServerHandler interface {
|
||||
ServiceStop() error
|
||||
ServiceReload() error
|
||||
}
|
||||
|
||||
func NewCommandServer(sharedDirectory string, handler CommandServerHandler) *CommandServer {
|
||||
server := &CommandServer{
|
||||
sockPath: filepath.Join(sharedDirectory, "command.sock"),
|
||||
handler: handler,
|
||||
savedLines: new(list.List[string]),
|
||||
subscriber: observable.NewSubscriber[string](128),
|
||||
}
|
||||
server.observer = observable.NewObserver[string](server.subscriber, 64)
|
||||
return server
|
||||
}
|
||||
|
||||
func (s *CommandServer) Start() error {
|
||||
os.Remove(s.sockPath)
|
||||
listener, err := net.ListenUnix("unix", &net.UnixAddr{
|
||||
Name: s.sockPath,
|
||||
Net: "unix",
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.listener = listener
|
||||
go s.loopConnection(listener)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CommandServer) Close() error {
|
||||
return s.listener.Close()
|
||||
}
|
||||
|
||||
func (s *CommandServer) loopConnection(listener net.Listener) {
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
hErr := s.handleConnection(conn)
|
||||
if hErr != nil && !E.IsClosed(err) {
|
||||
log.Warn("log-server: process connection: ", hErr)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleConnection(conn net.Conn) error {
|
||||
defer conn.Close()
|
||||
var command uint8
|
||||
err := binary.Read(conn, binary.BigEndian, &command)
|
||||
if err != nil {
|
||||
return E.Cause(err, "read command")
|
||||
}
|
||||
switch int32(command) {
|
||||
case CommandLog:
|
||||
return s.handleLogConn(conn)
|
||||
case CommandStatus:
|
||||
return s.handleStatusConn(conn)
|
||||
case CommandServiceStop:
|
||||
return s.handleServiceStop(conn)
|
||||
case CommandServiceReload:
|
||||
return s.handleServiceReload(conn)
|
||||
case CommandCloseConnections:
|
||||
return s.handleCloseConnections(conn)
|
||||
default:
|
||||
return E.New("unknown command: ", command)
|
||||
}
|
||||
}
|
||||
63
experimental/libbox/command_status.go
Normal file
63
experimental/libbox/command_status.go
Normal file
@@ -0,0 +1,63 @@
|
||||
//go:build darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/common/dialer/conntrack"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type StatusMessage struct {
|
||||
Memory int64
|
||||
Goroutines int32
|
||||
Connections int32
|
||||
}
|
||||
|
||||
func readStatus() StatusMessage {
|
||||
var memStats runtime.MemStats
|
||||
runtime.ReadMemStats(&memStats)
|
||||
var message StatusMessage
|
||||
message.Memory = int64(memStats.StackInuse + memStats.HeapInuse + memStats.HeapIdle - memStats.HeapReleased)
|
||||
message.Goroutines = int32(runtime.NumGoroutine())
|
||||
message.Connections = int32(conntrack.Count())
|
||||
return message
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleStatusConn(conn net.Conn) error {
|
||||
var interval int64
|
||||
err := binary.Read(conn, binary.BigEndian, &interval)
|
||||
if err != nil {
|
||||
return E.Cause(err, "read interval")
|
||||
}
|
||||
ticker := time.NewTicker(time.Duration(interval))
|
||||
defer ticker.Stop()
|
||||
ctx := connKeepAlive(conn)
|
||||
for {
|
||||
err = binary.Write(conn, binary.BigEndian, readStatus())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-ticker.C:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CommandClient) handleStatusConn(conn net.Conn) {
|
||||
for {
|
||||
var message StatusMessage
|
||||
err := binary.Read(conn, binary.BigEndian, &message)
|
||||
if err != nil {
|
||||
c.handler.Disconnected(err.Error())
|
||||
return
|
||||
}
|
||||
c.handler.WriteStatus(&message)
|
||||
}
|
||||
}
|
||||
50
experimental/libbox/command_stop.go
Normal file
50
experimental/libbox/command_stop.go
Normal file
@@ -0,0 +1,50 @@
|
||||
//go:build darwin
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"runtime/debug"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
func ClientServiceStop(sharedDirectory string) error {
|
||||
conn, err := clientConnect(sharedDirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
err = binary.Write(conn, binary.BigEndian, uint8(CommandServiceStop))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var hasError bool
|
||||
err = binary.Read(conn, binary.BigEndian, &hasError)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hasError {
|
||||
errorMessage, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return E.New(errorMessage)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleServiceStop(conn net.Conn) error {
|
||||
rErr := s.handler.ServiceStop()
|
||||
err := binary.Write(conn, binary.BigEndian, rErr != nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rErr != nil {
|
||||
return rw.WriteVString(conn, rErr.Error())
|
||||
}
|
||||
debug.FreeOSMemory()
|
||||
return nil
|
||||
}
|
||||
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
|
||||
}
|
||||
54
experimental/libbox/log.go
Normal file
54
experimental/libbox/log.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
type StandardOutput interface {
|
||||
WriteOutput(message string)
|
||||
WriteErrorOutput(message string)
|
||||
}
|
||||
|
||||
func SetOutput(output StandardOutput) {
|
||||
log.SetOutput(logWriter{output})
|
||||
pipeIn, pipeOut, err := os.Pipe()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
os.Stdout = os.NewFile(pipeOut.Fd(), "stdout")
|
||||
go lineLog(pipeIn, output.WriteOutput)
|
||||
|
||||
pipeIn, pipeOut, err = os.Pipe()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
os.Stderr = os.NewFile(pipeOut.Fd(), "srderr")
|
||||
go lineLog(pipeIn, output.WriteErrorOutput)
|
||||
}
|
||||
|
||||
type logWriter struct {
|
||||
output StandardOutput
|
||||
}
|
||||
|
||||
func (w logWriter) Write(p []byte) (n int, err error) {
|
||||
w.output.WriteOutput(string(p))
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func lineLog(f *os.File, output func(string)) {
|
||||
const logSize = 1024 // matches android/log.h.
|
||||
r := bufio.NewReaderSize(f, logSize)
|
||||
for {
|
||||
line, _, err := r.ReadLine()
|
||||
str := string(line)
|
||||
if err != nil {
|
||||
str += " " + err.Error()
|
||||
}
|
||||
output(str)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
8
experimental/libbox/memory.go
Normal file
8
experimental/libbox/memory.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package libbox
|
||||
|
||||
import "runtime/debug"
|
||||
|
||||
func SetMemoryLimit() {
|
||||
debug.SetGCPercent(10)
|
||||
debug.SetMemoryLimit(30 * 1024 * 1024)
|
||||
}
|
||||
66
experimental/libbox/platform.go
Normal file
66
experimental/libbox/platform.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package libbox
|
||||
|
||||
import "github.com/sagernet/sing-box/option"
|
||||
|
||||
type PlatformInterface interface {
|
||||
AutoDetectInterfaceControl(fd int32) error
|
||||
OpenTun(options TunOptions) (int32, 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
|
||||
}
|
||||
|
||||
type OnDemandRuleIterator interface {
|
||||
Next() OnDemandRule
|
||||
HasNext() bool
|
||||
}
|
||||
|
||||
type OnDemandRule interface {
|
||||
Target() int32
|
||||
DNSSearchDomainMatch() StringIterator
|
||||
DNSServerAddressMatch() StringIterator
|
||||
InterfaceTypeMatch() int32
|
||||
SSIDMatch() StringIterator
|
||||
ProbeURL() string
|
||||
}
|
||||
|
||||
type onDemandRule struct {
|
||||
option.OnDemandRule
|
||||
}
|
||||
|
||||
func (r *onDemandRule) Target() int32 {
|
||||
if r.OnDemandRule.Action == nil {
|
||||
return -1
|
||||
}
|
||||
return int32(*r.OnDemandRule.Action)
|
||||
}
|
||||
|
||||
func (r *onDemandRule) DNSSearchDomainMatch() StringIterator {
|
||||
return newIterator(r.OnDemandRule.DNSSearchDomainMatch)
|
||||
}
|
||||
|
||||
func (r *onDemandRule) DNSServerAddressMatch() StringIterator {
|
||||
return newIterator(r.OnDemandRule.DNSServerAddressMatch)
|
||||
}
|
||||
|
||||
func (r *onDemandRule) InterfaceTypeMatch() int32 {
|
||||
if r.OnDemandRule.InterfaceTypeMatch == nil {
|
||||
return -1
|
||||
}
|
||||
return int32(*r.OnDemandRule.InterfaceTypeMatch)
|
||||
}
|
||||
|
||||
func (r *onDemandRule) SSIDMatch() StringIterator {
|
||||
return newIterator(r.OnDemandRule.SSIDMatch)
|
||||
}
|
||||
|
||||
func (r *onDemandRule) ProbeURL() string {
|
||||
return r.OnDemandRule.ProbeURL
|
||||
}
|
||||
17
experimental/libbox/platform/interface.go
Normal file
17
experimental/libbox/platform/interface.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/sagernet/sing-box/common/process"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
AutoDetectInterfaceControl() control.Func
|
||||
OpenTun(options tun.Options, platformOptions option.TunPlatformOptions) (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
|
||||
}
|
||||
112
experimental/libbox/service.go
Normal file
112
experimental/libbox/service.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"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-box/option"
|
||||
"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
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
instance, err := box.New(ctx, options, &platformInterfaceWrapper{platformInterface, platformInterface.UseProcFS()})
|
||||
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, platformOptions option.TunPlatformOptions) (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")
|
||||
}
|
||||
tunFd, err := w.iif.OpenTun(&tunOptions{options, platformOptions})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options.FileDescriptor = int(tunFd)
|
||||
return tun.New(options)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
19
experimental/libbox/setup.go
Normal file
19
experimental/libbox/setup.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
)
|
||||
|
||||
func SetBasePath(path string) {
|
||||
C.SetBasePath(path)
|
||||
}
|
||||
|
||||
func Version() string {
|
||||
return C.Version
|
||||
}
|
||||
|
||||
func FormatBytes(length int64) string {
|
||||
return humanize.Bytes(uint64(length))
|
||||
}
|
||||
122
experimental/libbox/tun.go
Normal file
122
experimental/libbox/tun.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
)
|
||||
|
||||
type TunOptions interface {
|
||||
GetInet4Address() RoutePrefixIterator
|
||||
GetInet6Address() RoutePrefixIterator
|
||||
GetDNSServerAddress() (string, error)
|
||||
GetMTU() int32
|
||||
GetAutoRoute() bool
|
||||
GetStrictRoute() bool
|
||||
GetInet4RouteAddress() RoutePrefixIterator
|
||||
GetInet6RouteAddress() RoutePrefixIterator
|
||||
GetIncludePackage() StringIterator
|
||||
GetExcludePackage() StringIterator
|
||||
IsHTTPProxyEnabled() bool
|
||||
GetHTTPProxyServer() string
|
||||
GetHTTPProxyServerPort() int32
|
||||
}
|
||||
|
||||
type RoutePrefix struct {
|
||||
Address string
|
||||
Prefix int32
|
||||
}
|
||||
|
||||
func (p *RoutePrefix) Mask() string {
|
||||
var bits int
|
||||
if M.ParseSocksaddr(p.Address).Addr.Is6() {
|
||||
bits = 128
|
||||
} else {
|
||||
bits = 32
|
||||
}
|
||||
return net.IP(net.CIDRMask(int(p.Prefix), bits)).String()
|
||||
}
|
||||
|
||||
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 struct {
|
||||
tun.Options
|
||||
option.TunPlatformOptions
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (o *tunOptions) IsHTTPProxyEnabled() bool {
|
||||
if o.TunPlatformOptions.HTTPProxy == nil {
|
||||
return false
|
||||
}
|
||||
return o.TunPlatformOptions.HTTPProxy.Enabled
|
||||
}
|
||||
|
||||
func (o *tunOptions) GetHTTPProxyServer() string {
|
||||
return o.TunPlatformOptions.HTTPProxy.Server
|
||||
}
|
||||
|
||||
func (o *tunOptions) GetHTTPProxyServerPort() int32 {
|
||||
return int32(o.TunPlatformOptions.HTTPProxy.ServerPort)
|
||||
}
|
||||
34
experimental/libbox/tun_darwin.go
Normal file
34
experimental/libbox/tun_darwin.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// kanged from wireauard-apple
|
||||
|
||||
const utunControlName = "com.apple.net.utun_control"
|
||||
|
||||
func GetTunnelFileDescriptor() int32 {
|
||||
ctlInfo := &unix.CtlInfo{}
|
||||
copy(ctlInfo.Name[:], utunControlName)
|
||||
for fd := 0; fd < 1024; fd++ {
|
||||
addr, err := unix.Getpeername(fd)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
addrCTL, loaded := addr.(*unix.SockaddrCtl)
|
||||
if !loaded {
|
||||
continue
|
||||
}
|
||||
if ctlInfo.Id == 0 {
|
||||
err = unix.IoctlCtlInfo(fd, ctlInfo)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if addrCTL.ID == ctlInfo.Id {
|
||||
return int32(fd)
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
49
go.mod
49
go.mod
@@ -14,44 +14,50 @@ require (
|
||||
github.com/go-chi/render v1.0.2
|
||||
github.com/gofrs/uuid v4.4.0+incompatible
|
||||
github.com/hashicorp/yamux v0.1.1
|
||||
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230301142404-3e45eea5edd7
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
github.com/mholt/acmez v1.1.0
|
||||
github.com/miekg/dns v1.1.50
|
||||
github.com/miekg/dns v1.1.51
|
||||
github.com/oschwald/maxminddb-golang v1.10.0
|
||||
github.com/pires/go-proxyproto v0.6.2
|
||||
github.com/refraction-networking/utls v1.2.2
|
||||
github.com/sagernet/badhttp v0.0.0-20230228035330-e77eb9a689fd
|
||||
github.com/sagernet/badhttp2 v0.0.0-20230228040529-408b0b8e774d
|
||||
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/sing v0.1.7-0.20230209132010-5f1ef3441c13
|
||||
github.com/sagernet/sing-dns v0.1.2-0.20230209132355-3c2e2957b455
|
||||
github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7
|
||||
github.com/sagernet/sing-tun v0.1.1
|
||||
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb
|
||||
github.com/sagernet/reality v0.0.0-20230228045158-d3e085a8e5d1
|
||||
github.com/sagernet/sing v0.1.8-0.20230307054559-0560a4da412b
|
||||
github.com/sagernet/sing-dns v0.1.4
|
||||
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9
|
||||
github.com/sagernet/sing-shadowtls v0.1.0
|
||||
github.com/sagernet/sing-tun v0.1.2-0.20230226091124-0cdb0eed74d9
|
||||
github.com/sagernet/sing-vmess v0.1.3-0.20230307060529-d110e81a50bc
|
||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195
|
||||
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d
|
||||
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9
|
||||
github.com/sagernet/utls v0.0.0-20230225061716-536a007c8b01
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
|
||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/stretchr/testify v1.8.2
|
||||
go.etcd.io/bbolt v1.3.7
|
||||
go.uber.org/atomic v1.10.0
|
||||
go.uber.org/zap v1.24.0
|
||||
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f
|
||||
golang.org/x/crypto v0.6.0
|
||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb
|
||||
golang.org/x/net v0.7.0
|
||||
golang.org/x/sys v0.5.0
|
||||
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35
|
||||
golang.org/x/crypto v0.7.0
|
||||
golang.org/x/exp v0.0.0-20230304125523-9ff063c70017
|
||||
golang.org/x/net v0.8.0
|
||||
golang.org/x/sys v0.6.0
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230215201556-9c5414ab4bde
|
||||
google.golang.org/grpc v1.53.0
|
||||
google.golang.org/protobuf v1.28.1
|
||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
|
||||
)
|
||||
|
||||
//replace github.com/sagernet/sing => ../sing
|
||||
//replace github.com/sagernet/sing-tun => ../sing-tun
|
||||
|
||||
require (
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/cloudflare/circl v1.2.1-0.20221019164342-6ab4dfed8f3c // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
@@ -66,6 +72,7 @@ require (
|
||||
github.com/libdns/libdns v0.2.1 // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
||||
@@ -74,13 +81,13 @@ require (
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/text v0.8.0 // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||
golang.org/x/tools v0.2.0 // indirect
|
||||
golang.org/x/tools v0.6.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
||||
160
go.sum
160
go.sum
@@ -4,8 +4,8 @@ github.com/Dreamacro/clash v1.13.0 h1:gF0E0TluE1LCmuhhg0/bjqABYDmSnXkUjXjRhZxyrm
|
||||
github.com/Dreamacro/clash v1.13.0/go.mod h1:hf0RkWPsQ0e8oS8WVJBIRocY/1ILYzQQg9zeMwd8LsM=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE=
|
||||
github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE=
|
||||
@@ -23,7 +23,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
|
||||
@@ -43,32 +42,20 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 h1:Z72DOke2yOK0Ms4Z2LK1E1OrRJXOxSj5DllTz2FYTRg=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8/go.mod h1:m5WMe03WCvWcXjRnhvaAbAAXdCnu20J5P+mmH44ZzpE=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230301142404-3e45eea5edd7 h1:Fg8rHYs8luh8kCSAHDUIQCNMkn74Gvr1o5YPZdNRgY0=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230301142404-3e45eea5edd7/go.mod h1:I9wtoXVkcRwQJ+U9nhxzZytbnT1xjn2DzUjxQ8Qegpc=
|
||||
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
|
||||
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
@@ -81,17 +68,10 @@ github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
|
||||
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
|
||||
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
||||
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
|
||||
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
|
||||
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
|
||||
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
|
||||
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
|
||||
github.com/mholt/acmez v1.1.0 h1:IQ9CGHKOHokorxnffsqDvmmE30mDenO1lptYZ1AYkHY=
|
||||
github.com/mholt/acmez v1.1.0/go.mod h1:zwo5+fbLLTowAX8o8ETfQzbDtwGEXnPhkmGdKIP+bgs=
|
||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/miekg/dns v1.1.51 h1:0+Xg7vObnhrz/4ZCZcZh7zPXlmU0aveS2HDBd0m0qSo=
|
||||
github.com/miekg/dns v1.1.51/go.mod h1:2Z9d3CP1LQWihRZUf29mQ19yDThaI4DAYzte2CaQW5c=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
|
||||
@@ -99,6 +79,8 @@ github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7
|
||||
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
|
||||
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
|
||||
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
|
||||
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8=
|
||||
github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -112,39 +94,47 @@ github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4
|
||||
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||
github.com/refraction-networking/utls v1.2.2 h1:uBE6V173CwG8MQrSBpNZHAix1fxOvuLKYyjFAu3uqo0=
|
||||
github.com/refraction-networking/utls v1.2.2/go.mod h1:L1goe44KvhnTfctUffM2isnJpSjPlYShrhXDeZaoYKw=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagernet/badhttp v0.0.0-20230228035330-e77eb9a689fd h1:nv3WtVfPGX+i2Ip/TR+Yd3LO1xFSpKUgWmYsXxKJ6vM=
|
||||
github.com/sagernet/badhttp v0.0.0-20230228035330-e77eb9a689fd/go.mod h1:geEm+9ZyRMZ8THRH0XSexeStaMDtkFBf4J1nMK92mAY=
|
||||
github.com/sagernet/badhttp2 v0.0.0-20230228040529-408b0b8e774d h1:RmBTGU4SvqxX57SDvpQtrkiQDaCnr4J/DMYMrUBL7OQ=
|
||||
github.com/sagernet/badhttp2 v0.0.0-20230228040529-408b0b8e774d/go.mod h1:Ag8QdZjLwuy3V2pyOcqlKz4Cdh0wKEOFlYgR3wPUGkI=
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 h1:KyhtFFt1Jtp5vW2ohNvstvQffTOQ/s5vENuGXzdA+TM=
|
||||
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/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/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/go.mod h1:QMCkxXAC3CvBgDZVIJp43NWTuwGBScCzMLVLynjERL8=
|
||||
github.com/sagernet/reality v0.0.0-20230228045158-d3e085a8e5d1 h1:8mSzchN6DkM26JKLalPwj2KLMIsEjzlp/pYgznlKE2Q=
|
||||
github.com/sagernet/reality v0.0.0-20230228045158-d3e085a8e5d1/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||
github.com/sagernet/sing v0.1.7-0.20230209132010-5f1ef3441c13 h1:S/+YvJCEChwnckGhzqSrE/Q2m6aVWhkt1I4Pv2yCMVw=
|
||||
github.com/sagernet/sing v0.1.7-0.20230209132010-5f1ef3441c13/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||
github.com/sagernet/sing-dns v0.1.2-0.20230209132355-3c2e2957b455 h1:VA/j2Jg+JURgKw2C1Dw2tpjfOZwbLXRy8PJRbJS/HEU=
|
||||
github.com/sagernet/sing-dns v0.1.2-0.20230209132355-3c2e2957b455/go.mod h1:nonvn66ja+UNrQl3jzJy0EFRn15QUaCFAVXTXf6TgJ4=
|
||||
github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7 h1:Plup6oEiyLzY3HDqQ+QsUBzgBGdVmcsgf3t8h940z9U=
|
||||
github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7/go.mod h1:O5LtOs8Ivw686FqLpO0Zu+A0ROVE15VeqEK3yDRRAms=
|
||||
github.com/sagernet/sing-tun v0.1.1 h1:2Hg3GAyJWzQ7Ua1j74dE+mI06vaqSBO9yD4tkTjggn4=
|
||||
github.com/sagernet/sing-tun v0.1.1/go.mod h1:WzW/SkT+Nh9uJn/FIYUE2YJHYuPwfbh8sATOzU9QDGw=
|
||||
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb h1:oyd3w17fXNmWVYFUe17YVHJW5CLW9X2mxJFDP/IWrAM=
|
||||
github.com/sagernet/sing-vmess v0.1.1-0.20230212211128-cb4e47dd0acb/go.mod h1:9KkmnQzTL4Gvv8U2TRAH2BOITCGsGPpHtUPP5sxn5sY=
|
||||
github.com/sagernet/sing v0.1.8-0.20230307054559-0560a4da412b h1:wxqf3O+cLHm1ZWEQG1DRwApwLlTV/NLKGqF1kNCk3Ms=
|
||||
github.com/sagernet/sing v0.1.8-0.20230307054559-0560a4da412b/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||
github.com/sagernet/sing-dns v0.1.4 h1:7VxgeoSCiiazDSaXXQVcvrTBxFpOePPq/4XdgnUDN+0=
|
||||
github.com/sagernet/sing-dns v0.1.4/go.mod h1:1+6pCa48B1AI78lD+/i/dLgpw4MwfnsSpZo0Ds8wzzk=
|
||||
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9 h1:qS39eA4C7x+zhEkySbASrtmb6ebdy5v0y2M6mgkmSO0=
|
||||
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9/go.mod h1:f3mHTy5shnVM9l8UocMlJgC/1G/zdj5FuEuVXhDinGU=
|
||||
github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ=
|
||||
github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc=
|
||||
github.com/sagernet/sing-tun v0.1.2-0.20230226091124-0cdb0eed74d9 h1:tq1kc0HFj/jfhLfVC1NJI6lex2g6w2W+gVsFu6H2Kis=
|
||||
github.com/sagernet/sing-tun v0.1.2-0.20230226091124-0cdb0eed74d9/go.mod h1:KnRkwaDHbb06zgeNPu0LQ8A+vA9myMxKEgHN1brCPHg=
|
||||
github.com/sagernet/sing-vmess v0.1.3-0.20230307060529-d110e81a50bc h1:vqlYWupvVDRpvv2F+RtECJN+VbuKjLtmQculQvOecls=
|
||||
github.com/sagernet/sing-vmess v0.1.3-0.20230307060529-d110e81a50bc/go.mod h1:V14iffGwhZPU2S7wgIiPlLWXygSjAXazYzD1w0ejBl4=
|
||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
|
||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
|
||||
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d h1:trP/l6ZPWvQ/5Gv99Z7/t/v8iYy06akDMejxW1sznUk=
|
||||
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d/go.mod h1:jk6Ii8Y3En+j2KQDLgdgQGwb3M6y7EL567jFnGYhN9g=
|
||||
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=
|
||||
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9/go.mod h1:FUyTEc5ye5NjKnDTDMuiLF2M6T4BE6y6KZuax//UCEg=
|
||||
github.com/sagernet/utls v0.0.0-20230225061716-536a007c8b01 h1:m4MI13+NRKddIvbdSN0sFHK8w5ROTa60Zi9diZ7EE08=
|
||||
github.com/sagernet/utls v0.0.0-20230225061716-536a007c8b01/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM=
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
||||
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
@@ -154,17 +144,17 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f h1:dpx1PHxYqAnXzbryJrWP1NQLzEjwcVgFLhkknuFQ7ww=
|
||||
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f/go.mod h1:IogEAUBXDEwX7oR/BMmCctShYs80ql4hF0ySdzGxf7E=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
|
||||
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
@@ -175,85 +165,81 @@ go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f h1:ketMxHg+vWm3yccyYiq+uK8D3fRmna2Fcj+awpQp84s=
|
||||
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
|
||||
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35 h1:nJAwRlGWZZDOD+6wni9KVUNHMpHko/OnRwsrCYeAzPo=
|
||||
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35/go.mod h1:TQvodOM+hJTioNQJilmLXu08JNb8i+ccq418+KWu1/Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w=
|
||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/exp v0.0.0-20230304125523-9ff063c70017 h1:3Ea9SZLCB0aRIhSEjM+iaGIlzzeDJdpi579El/YIhEE=
|
||||
golang.org/x/exp v0.0.0-20230304125523-9ff063c70017/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230215201556-9c5414ab4bde h1:ybF7AMzIUikL9x4LgwEmzhXtzRpKNqngme1VGDWz+Nk=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230215201556-9c5414ab4bde/go.mod h1:mQqgjkW8GQQcJQsbBvK890TKqUK1DfKWkuBGbOkuMHQ=
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w=
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
|
||||
|
||||
@@ -5,18 +5,19 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
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/option"
|
||||
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 == "" {
|
||||
return nil, E.New("missing inbound type")
|
||||
}
|
||||
switch options.Type {
|
||||
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:
|
||||
return NewRedirect(ctx, router, logger, options.Tag, options.RedirectOptions), nil
|
||||
case C.TypeTProxy:
|
||||
@@ -41,6 +42,8 @@ func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, o
|
||||
return NewHysteria(ctx, router, logger, options.Tag, options.HysteriaOptions)
|
||||
case C.TypeShadowTLS:
|
||||
return NewShadowTLS(ctx, router, logger, options.Tag, options.ShadowTLSOptions)
|
||||
case C.TypeVLESS:
|
||||
return NewVLESS(ctx, router, logger, options.Tag, options.VLESSOptions)
|
||||
default:
|
||||
return nil, E.New("unknown inbound type: ", options.Type)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
var _ adapter.Inbound = (*myInboundAdapter)(nil)
|
||||
@@ -42,6 +44,8 @@ type myInboundAdapter struct {
|
||||
udpAddr M.Socksaddr
|
||||
packetOutboundClosed chan struct{}
|
||||
packetOutbound chan *myInboundPacket
|
||||
|
||||
inShutdown atomic.Bool
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) Type() string {
|
||||
@@ -97,6 +101,7 @@ func (a *myInboundAdapter) Start() error {
|
||||
}
|
||||
|
||||
func (a *myInboundAdapter) Close() error {
|
||||
a.inShutdown.Store(true)
|
||||
var err error
|
||||
if a.clearSystemProxy != nil {
|
||||
err = a.clearSystemProxy()
|
||||
|
||||
@@ -39,10 +39,17 @@ func (a *myInboundAdapter) loopTCPIn() {
|
||||
for {
|
||||
conn, err := tcpListener.Accept()
|
||||
if err != nil {
|
||||
if E.IsClosed(err) {
|
||||
//goland:noinspection GoDeprecation
|
||||
//nolint:staticcheck
|
||||
if netError, isNetError := err.(net.Error); isNetError && netError.Temporary() {
|
||||
a.logger.Error(err)
|
||||
continue
|
||||
}
|
||||
if a.inShutdown.Load() && E.IsClosed(err) {
|
||||
return
|
||||
}
|
||||
a.logger.Error("accept: ", err)
|
||||
a.tcpListener.Close()
|
||||
a.logger.Error("serve error: ", err)
|
||||
continue
|
||||
}
|
||||
go a.injectTCP(conn, adapter.InboundContext{})
|
||||
|
||||
@@ -44,7 +44,7 @@ func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogge
|
||||
authenticator: auth.NewAuthenticator(options.Users),
|
||||
}
|
||||
if options.TLS != nil {
|
||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
tlsConfig, err := tls.NewServer(ctx, router, logger, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
if len(options.TLS.ALPN) == 0 {
|
||||
options.TLS.ALPN = []string{hysteria.DefaultALPN}
|
||||
}
|
||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
tlsConfig, err := tls.NewServer(ctx, router, logger, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ func NewNaive(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
return nil, E.New("missing users")
|
||||
}
|
||||
if options.TLS != nil {
|
||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
tlsConfig, err := tls.NewServer(ctx, router, logger, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ func newShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
|
||||
case common.Contains(shadowaead.List, options.Method):
|
||||
inbound.service, err = shadowaead.NewService(options.Method, nil, options.Password, udpTimeout, inbound.upstreamContextHandler())
|
||||
case common.Contains(shadowaead_2022.List, options.Method):
|
||||
inbound.service, err = shadowaead_2022.NewServiceWithPassword(options.Method, options.Password, udpTimeout, inbound.upstreamContextHandler())
|
||||
inbound.service, err = shadowaead_2022.NewServiceWithPassword(options.Method, options.Password, udpTimeout, inbound.upstreamContextHandler(), router.TimeFunc())
|
||||
default:
|
||||
err = E.New("unsupported method: ", options.Method)
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
|
||||
options.Password,
|
||||
udpTimeout,
|
||||
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound),
|
||||
router.TimeFunc(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -1,38 +1,22 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/shadowtls"
|
||||
"github.com/sagernet/sing-shadowtls"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/task"
|
||||
)
|
||||
|
||||
type ShadowTLS struct {
|
||||
myInboundAdapter
|
||||
handshakeDialer N.Dialer
|
||||
handshakeAddr M.Socksaddr
|
||||
version int
|
||||
password string
|
||||
fallbackAfter int
|
||||
service *shadowtls.Service
|
||||
}
|
||||
|
||||
func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSInboundOptions) (*ShadowTLS, error) {
|
||||
@@ -46,231 +30,41 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
|
||||
tag: tag,
|
||||
listenOptions: options.ListenOptions,
|
||||
},
|
||||
handshakeDialer: dialer.New(router, options.Handshake.DialerOptions),
|
||||
handshakeAddr: options.Handshake.ServerOptions.Build(),
|
||||
password: options.Password,
|
||||
}
|
||||
inbound.version = options.Version
|
||||
switch options.Version {
|
||||
case 0:
|
||||
fallthrough
|
||||
case 1:
|
||||
case 2:
|
||||
if options.FallbackAfter == nil {
|
||||
inbound.fallbackAfter = 2
|
||||
} else {
|
||||
inbound.fallbackAfter = *options.FallbackAfter
|
||||
|
||||
var handshakeForServerName map[string]shadowtls.HandshakeConfig
|
||||
if options.Version > 1 {
|
||||
handshakeForServerName = make(map[string]shadowtls.HandshakeConfig)
|
||||
for serverName, serverOptions := range options.HandshakeForServerName {
|
||||
handshakeForServerName[serverName] = shadowtls.HandshakeConfig{
|
||||
Server: serverOptions.ServerOptions.Build(),
|
||||
Dialer: dialer.New(router, serverOptions.DialerOptions),
|
||||
}
|
||||
}
|
||||
case 3:
|
||||
default:
|
||||
return nil, E.New("unknown shadowtls protocol version: ", options.Version)
|
||||
}
|
||||
service, err := shadowtls.NewService(shadowtls.ServiceConfig{
|
||||
Version: options.Version,
|
||||
Password: options.Password,
|
||||
Users: common.Map(options.Users, func(it option.ShadowTLSUser) shadowtls.User {
|
||||
return (shadowtls.User)(it)
|
||||
}),
|
||||
Handshake: shadowtls.HandshakeConfig{
|
||||
Server: options.Handshake.ServerOptions.Build(),
|
||||
Dialer: dialer.New(router, options.Handshake.DialerOptions),
|
||||
},
|
||||
HandshakeForServerName: handshakeForServerName,
|
||||
StrictMode: options.StrictMode,
|
||||
Handler: inbound.upstreamContextHandler(),
|
||||
Logger: logger,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inbound.service = service
|
||||
inbound.connHandler = inbound
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
func (s *ShadowTLS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
handshakeConn, err := s.handshakeDialer.DialContext(ctx, N.NetworkTCP, s.handshakeAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch s.version {
|
||||
case 1:
|
||||
var handshake task.Group
|
||||
handshake.Append("client handshake", func(ctx context.Context) error {
|
||||
return s.copyUntilHandshakeFinished(handshakeConn, conn)
|
||||
})
|
||||
handshake.Append("server handshake", func(ctx context.Context) error {
|
||||
return s.copyUntilHandshakeFinished(conn, handshakeConn)
|
||||
})
|
||||
handshake.FastFail()
|
||||
handshake.Cleanup(func() {
|
||||
handshakeConn.Close()
|
||||
})
|
||||
err = handshake.Run(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.newConnection(ctx, conn, metadata)
|
||||
case 2:
|
||||
hashConn := shadowtls.NewHashWriteConn(conn, s.password)
|
||||
go bufio.Copy(hashConn, handshakeConn)
|
||||
var request *buf.Buffer
|
||||
request, err = s.copyUntilHandshakeFinishedV2(ctx, handshakeConn, conn, hashConn, s.fallbackAfter)
|
||||
if err == nil {
|
||||
handshakeConn.Close()
|
||||
return s.newConnection(ctx, bufio.NewCachedConn(shadowtls.NewConn(conn), request), metadata)
|
||||
} else if err == os.ErrPermission {
|
||||
s.logger.WarnContext(ctx, "fallback connection")
|
||||
hashConn.Fallback()
|
||||
return common.Error(bufio.Copy(handshakeConn, conn))
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
fallthrough
|
||||
case 3:
|
||||
var clientHelloFrame *buf.Buffer
|
||||
clientHelloFrame, err = shadowtls.ExtractFrame(conn)
|
||||
if err != nil {
|
||||
return E.Cause(err, "read client handshake")
|
||||
}
|
||||
_, err = handshakeConn.Write(clientHelloFrame.Bytes())
|
||||
if err != nil {
|
||||
clientHelloFrame.Release()
|
||||
return E.Cause(err, "write client handshake")
|
||||
}
|
||||
err = shadowtls.VerifyClientHello(clientHelloFrame.Bytes(), s.password)
|
||||
if err != nil {
|
||||
s.logger.WarnContext(ctx, E.Cause(err, "client hello verify failed"))
|
||||
return bufio.CopyConn(ctx, conn, handshakeConn)
|
||||
}
|
||||
s.logger.TraceContext(ctx, "client hello verify success")
|
||||
clientHelloFrame.Release()
|
||||
|
||||
var serverHelloFrame *buf.Buffer
|
||||
serverHelloFrame, err = shadowtls.ExtractFrame(handshakeConn)
|
||||
if err != nil {
|
||||
return E.Cause(err, "read server handshake")
|
||||
}
|
||||
|
||||
_, err = conn.Write(serverHelloFrame.Bytes())
|
||||
if err != nil {
|
||||
serverHelloFrame.Release()
|
||||
return E.Cause(err, "write server handshake")
|
||||
}
|
||||
|
||||
serverRandom := shadowtls.ExtractServerRandom(serverHelloFrame.Bytes())
|
||||
|
||||
if serverRandom == nil {
|
||||
s.logger.WarnContext(ctx, "server random extract failed, will copy bidirectional")
|
||||
return bufio.CopyConn(ctx, conn, handshakeConn)
|
||||
}
|
||||
|
||||
if !shadowtls.IsServerHelloSupportTLS13(serverHelloFrame.Bytes()) {
|
||||
s.logger.WarnContext(ctx, "TLS 1.3 is not supported, will copy bidirectional")
|
||||
return bufio.CopyConn(ctx, conn, handshakeConn)
|
||||
}
|
||||
|
||||
serverHelloFrame.Release()
|
||||
s.logger.TraceContext(ctx, "client authenticated. server random extracted: ", hex.EncodeToString(serverRandom))
|
||||
|
||||
hmacWrite := hmac.New(sha1.New, []byte(s.password))
|
||||
hmacWrite.Write(serverRandom)
|
||||
|
||||
hmacAdd := hmac.New(sha1.New, []byte(s.password))
|
||||
hmacAdd.Write(serverRandom)
|
||||
hmacAdd.Write([]byte("S"))
|
||||
|
||||
hmacVerify := hmac.New(sha1.New, []byte(s.password))
|
||||
hmacVerifyReset := func() {
|
||||
hmacVerify.Reset()
|
||||
hmacVerify.Write(serverRandom)
|
||||
hmacVerify.Write([]byte("C"))
|
||||
}
|
||||
|
||||
var clientFirstFrame *buf.Buffer
|
||||
var group task.Group
|
||||
var handshakeFinished bool
|
||||
group.Append("client handshake relay", func(ctx context.Context) error {
|
||||
clientFrame, cErr := shadowtls.CopyByFrameUntilHMACMatches(conn, handshakeConn, hmacVerify, hmacVerifyReset)
|
||||
if cErr == nil {
|
||||
clientFirstFrame = clientFrame
|
||||
handshakeFinished = true
|
||||
handshakeConn.Close()
|
||||
}
|
||||
return cErr
|
||||
})
|
||||
group.Append("server handshake relay", func(ctx context.Context) error {
|
||||
cErr := shadowtls.CopyByFrameWithModification(handshakeConn, conn, s.password, serverRandom, hmacWrite)
|
||||
if E.IsClosedOrCanceled(cErr) && handshakeFinished {
|
||||
return nil
|
||||
}
|
||||
return cErr
|
||||
})
|
||||
group.Cleanup(func() {
|
||||
handshakeConn.Close()
|
||||
})
|
||||
err = group.Run(ctx)
|
||||
if err != nil {
|
||||
return E.Cause(err, "handshake relay")
|
||||
}
|
||||
|
||||
s.logger.TraceContext(ctx, "handshake relay finished")
|
||||
return s.newConnection(ctx, bufio.NewCachedConn(shadowtls.NewVerifiedConn(conn, hmacAdd, hmacVerify, nil), clientFirstFrame), metadata)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ShadowTLS) copyUntilHandshakeFinished(dst io.Writer, src io.Reader) error {
|
||||
const handshake = 0x16
|
||||
const changeCipherSpec = 0x14
|
||||
var hasSeenChangeCipherSpec bool
|
||||
var tlsHdr [5]byte
|
||||
for {
|
||||
_, err := io.ReadFull(src, tlsHdr[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
length := binary.BigEndian.Uint16(tlsHdr[3:])
|
||||
_, err = io.Copy(dst, io.MultiReader(bytes.NewReader(tlsHdr[:]), io.LimitReader(src, int64(length))))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tlsHdr[0] != handshake {
|
||||
if tlsHdr[0] != changeCipherSpec {
|
||||
return E.New("unexpected tls frame type: ", tlsHdr[0])
|
||||
}
|
||||
if !hasSeenChangeCipherSpec {
|
||||
hasSeenChangeCipherSpec = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
if hasSeenChangeCipherSpec {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ShadowTLS) copyUntilHandshakeFinishedV2(ctx context.Context, dst net.Conn, src io.Reader, hash *shadowtls.HashWriteConn, fallbackAfter int) (*buf.Buffer, error) {
|
||||
const applicationData = 0x17
|
||||
var tlsHdr [5]byte
|
||||
var applicationDataCount int
|
||||
for {
|
||||
_, err := io.ReadFull(src, tlsHdr[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
length := binary.BigEndian.Uint16(tlsHdr[3:])
|
||||
if tlsHdr[0] == applicationData {
|
||||
data := buf.NewSize(int(length))
|
||||
_, err = data.ReadFullFrom(src, int(length))
|
||||
if err != nil {
|
||||
data.Release()
|
||||
return nil, err
|
||||
}
|
||||
if hash.HasContent() && length >= 8 {
|
||||
checksum := hash.Sum()
|
||||
if bytes.Equal(data.To(8), checksum) {
|
||||
s.logger.TraceContext(ctx, "match current hashcode")
|
||||
data.Advance(8)
|
||||
return data, nil
|
||||
} else if hash.LastSum() != nil && bytes.Equal(data.To(8), hash.LastSum()) {
|
||||
s.logger.TraceContext(ctx, "match last hashcode")
|
||||
data.Advance(8)
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
_, err = io.Copy(dst, io.MultiReader(bytes.NewReader(tlsHdr[:]), data))
|
||||
data.Release()
|
||||
applicationDataCount++
|
||||
} else {
|
||||
_, err = io.Copy(dst, io.MultiReader(bytes.NewReader(tlsHdr[:]), io.LimitReader(src, int64(length))))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if applicationDataCount > fallbackAfter {
|
||||
return nil, os.ErrPermission
|
||||
}
|
||||
}
|
||||
func (h *ShadowTLS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||
}
|
||||
|
||||
@@ -100,9 +100,10 @@ type tproxyPacketWriter struct {
|
||||
|
||||
func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
defer buffer.Release()
|
||||
if w.destination == destination && w.conn != nil {
|
||||
_, err := w.conn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr()))
|
||||
if err == nil {
|
||||
conn := w.conn
|
||||
if w.destination == destination && conn != nil {
|
||||
_, err := conn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr()))
|
||||
if err != nil {
|
||||
w.conn = nil
|
||||
}
|
||||
return err
|
||||
|
||||
@@ -49,7 +49,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
|
||||
users: options.Users,
|
||||
}
|
||||
if options.TLS != nil {
|
||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
tlsConfig, err := tls.NewServer(ctx, router, logger, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/canceler"
|
||||
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/option"
|
||||
"github.com/sagernet/sing-tun"
|
||||
@@ -34,9 +35,11 @@ type Tun struct {
|
||||
stack string
|
||||
tunIf tun.Tun
|
||||
tunStack tun.Stack
|
||||
platformInterface platform.Interface
|
||||
platformOptions option.TunPlatformOptions
|
||||
}
|
||||
|
||||
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
|
||||
if tunName == "" {
|
||||
tunName = tun.CalculateInterfaceName("")
|
||||
@@ -93,6 +96,8 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
||||
endpointIndependentNat: options.EndpointIndependentNat,
|
||||
udpTimeout: udpTimeout,
|
||||
stack: options.Stack,
|
||||
platformInterface: platformInterface,
|
||||
platformOptions: common.PtrValueOrDefault(options.Platform),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -137,17 +142,25 @@ func (t *Tun) Tag() string {
|
||||
}
|
||||
|
||||
func (t *Tun) Start() error {
|
||||
if C.IsAndroid {
|
||||
if C.IsAndroid && t.platformInterface == nil {
|
||||
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, t.platformOptions)
|
||||
} else {
|
||||
tunInterface, err = tun.New(t.tunOptions)
|
||||
}
|
||||
if err != nil {
|
||||
return E.Cause(err, "configure tun interface")
|
||||
}
|
||||
t.tunIf = tunIf
|
||||
t.tunIf = tunInterface
|
||||
t.tunStack, err = tun.NewStack(t.stack, tun.StackOptions{
|
||||
Context: t.ctx,
|
||||
Tun: tunIf,
|
||||
Tun: tunInterface,
|
||||
MTU: t.tunOptions.MTU,
|
||||
Name: t.tunOptions.Name,
|
||||
Inet4Address: t.tunOptions.Inet4Address,
|
||||
|
||||
195
inbound/vless.go
Normal file
195
inbound/vless.go
Normal file
@@ -0,0 +1,195 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/v2ray"
|
||||
"github.com/sagernet/sing-box/transport/vless"
|
||||
"github.com/sagernet/sing-vmess"
|
||||
"github.com/sagernet/sing-vmess/packetaddr"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
var (
|
||||
_ adapter.Inbound = (*VLESS)(nil)
|
||||
_ adapter.InjectableInbound = (*VLESS)(nil)
|
||||
)
|
||||
|
||||
type VLESS struct {
|
||||
myInboundAdapter
|
||||
ctx context.Context
|
||||
users []option.VLESSUser
|
||||
service *vless.Service[int]
|
||||
tlsConfig tls.ServerConfig
|
||||
transport adapter.V2RayServerTransport
|
||||
}
|
||||
|
||||
func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VLESSInboundOptions) (*VLESS, error) {
|
||||
inbound := &VLESS{
|
||||
myInboundAdapter: myInboundAdapter{
|
||||
protocol: C.TypeVLESS,
|
||||
network: []string{N.NetworkTCP},
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
listenOptions: options.ListenOptions,
|
||||
},
|
||||
ctx: ctx,
|
||||
users: options.Users,
|
||||
}
|
||||
service := vless.NewService[int](logger, adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||
service.UpdateUsers(common.MapIndexed(inbound.users, func(index int, _ option.VLESSUser) int {
|
||||
return index
|
||||
}), common.Map(inbound.users, func(it option.VLESSUser) string {
|
||||
return it.UUID
|
||||
}), common.Map(inbound.users, func(it option.VLESSUser) string {
|
||||
return it.Flow
|
||||
}))
|
||||
inbound.service = service
|
||||
var err error
|
||||
if options.TLS != nil {
|
||||
inbound.tlsConfig, err = tls.NewServer(ctx, router, logger, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if options.Transport != nil {
|
||||
inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*vlessTransportHandler)(inbound))
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create server transport: ", options.Transport.Type)
|
||||
}
|
||||
}
|
||||
inbound.connHandler = inbound
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
func (h *VLESS) Start() error {
|
||||
err := common.Start(
|
||||
h.service,
|
||||
h.tlsConfig,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if h.transport == nil {
|
||||
return h.myInboundAdapter.Start()
|
||||
}
|
||||
if common.Contains(h.transport.Network(), N.NetworkTCP) {
|
||||
tcpListener, err := h.myInboundAdapter.ListenTCP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
sErr := h.transport.Serve(tcpListener)
|
||||
if sErr != nil && !E.IsClosed(sErr) {
|
||||
h.logger.Error("transport serve error: ", sErr)
|
||||
}
|
||||
}()
|
||||
}
|
||||
if common.Contains(h.transport.Network(), N.NetworkUDP) {
|
||||
udpConn, err := h.myInboundAdapter.ListenUDP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
sErr := h.transport.ServePacket(udpConn)
|
||||
if sErr != nil && !E.IsClosed(sErr) {
|
||||
h.logger.Error("transport serve error: ", sErr)
|
||||
}
|
||||
}()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *VLESS) Close() error {
|
||||
return common.Close(
|
||||
h.service,
|
||||
&h.myInboundAdapter,
|
||||
h.tlsConfig,
|
||||
h.transport,
|
||||
)
|
||||
}
|
||||
|
||||
func (h *VLESS) newTransportConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
h.injectTCP(conn, metadata)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
var err error
|
||||
if h.tlsConfig != nil && h.transport == nil {
|
||||
conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
|
||||
}
|
||||
|
||||
func (h *VLESS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (h *VLESS) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
userIndex, loaded := auth.UserFromContext[int](ctx)
|
||||
if !loaded {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
user := h.users[userIndex].Name
|
||||
if user == "" {
|
||||
user = F.ToString(userIndex)
|
||||
} else {
|
||||
metadata.User = user
|
||||
}
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
|
||||
return h.router.RouteConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *VLESS) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
userIndex, loaded := auth.UserFromContext[int](ctx)
|
||||
if !loaded {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
user := h.users[userIndex].Name
|
||||
if user == "" {
|
||||
user = F.ToString(userIndex)
|
||||
} else {
|
||||
metadata.User = user
|
||||
}
|
||||
if metadata.Destination.Fqdn == packetaddr.SeqPacketMagicAddress {
|
||||
metadata.Destination = M.Socksaddr{}
|
||||
conn = packetaddr.NewConn(conn.(vmess.PacketConn), metadata.Destination)
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet addr connection")
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||
}
|
||||
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
var _ adapter.V2RayServerTransportHandler = (*vlessTransportHandler)(nil)
|
||||
|
||||
type vlessTransportHandler VLESS
|
||||
|
||||
func (t *vlessTransportHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
return (*VLESS)(t).newTransportConnection(ctx, conn, adapter.InboundContext{
|
||||
Source: metadata.Source,
|
||||
Destination: metadata.Destination,
|
||||
})
|
||||
}
|
||||
|
||||
func (t *vlessTransportHandler) FallbackConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
@@ -50,6 +50,9 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
users: options.Users,
|
||||
}
|
||||
var serviceOptions []vmess.ServiceOption
|
||||
if timeFunc := router.TimeFunc(); timeFunc != nil {
|
||||
serviceOptions = append(serviceOptions, vmess.ServiceWithTimeFunc(timeFunc))
|
||||
}
|
||||
if options.Transport != nil && options.Transport.Type != "" {
|
||||
serviceOptions = append(serviceOptions, vmess.ServiceWithDisableHeaderProtection())
|
||||
}
|
||||
@@ -66,7 +69,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
return nil, err
|
||||
}
|
||||
if options.TLS != nil {
|
||||
inbound.tlsConfig, err = tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||
inbound.tlsConfig, err = tls.NewServer(ctx, router, logger, common.PtrValueOrDefault(options.TLS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -6,22 +6,30 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
|
||||
var _ Factory = (*simpleFactory)(nil)
|
||||
|
||||
type simpleFactory struct {
|
||||
formatter Formatter
|
||||
writer io.Writer
|
||||
level Level
|
||||
formatter Formatter
|
||||
platformFormatter Formatter
|
||||
writer io.Writer
|
||||
platformWriter io.Writer
|
||||
level Level
|
||||
}
|
||||
|
||||
func NewFactory(formatter Formatter, writer io.Writer) Factory {
|
||||
func NewFactory(formatter Formatter, writer io.Writer, platformWriter io.Writer) Factory {
|
||||
return &simpleFactory{
|
||||
formatter: formatter,
|
||||
writer: writer,
|
||||
level: LevelTrace,
|
||||
platformFormatter: Formatter{
|
||||
BaseTime: formatter.BaseTime,
|
||||
DisableColors: C.IsDarwin || C.IsIos,
|
||||
},
|
||||
writer: writer,
|
||||
platformWriter: platformWriter,
|
||||
level: LevelTrace,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +61,8 @@ func (l *simpleLogger) Log(ctx context.Context, level Level, args []any) {
|
||||
if level > l.level {
|
||||
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 {
|
||||
panic(message)
|
||||
}
|
||||
@@ -61,6 +70,9 @@ func (l *simpleLogger) Log(ctx context.Context, level Level, args []any) {
|
||||
if level == LevelFatal {
|
||||
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) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
var std ContextLogger
|
||||
|
||||
func init() {
|
||||
std = NewFactory(Formatter{BaseTime: time.Now()}, os.Stderr).Logger()
|
||||
std = NewFactory(Formatter{BaseTime: time.Now()}, os.Stderr, nil).Logger()
|
||||
}
|
||||
|
||||
func StdLogger() ContextLogger {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user