Compare commits
41 Commits
v1.2-beta5
...
v1.2-beta6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,6 +6,7 @@
|
|||||||
/bin/
|
/bin/
|
||||||
/dist/
|
/dist/
|
||||||
/sing-box
|
/sing-box
|
||||||
|
/sing-box.exe
|
||||||
/build/
|
/build/
|
||||||
/*.jar
|
/*.jar
|
||||||
/*.aar
|
/*.aar
|
||||||
|
|||||||
38
box.go
38
box.go
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/experimental"
|
"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/inbound"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
@@ -36,7 +37,7 @@ type Box struct {
|
|||||||
done chan 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()
|
createdAt := time.Now()
|
||||||
logOptions := common.PtrValueOrDefault(options.Log)
|
logOptions := common.PtrValueOrDefault(options.Log)
|
||||||
|
|
||||||
@@ -61,7 +62,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
|||||||
} else {
|
} else {
|
||||||
switch logOptions.Output {
|
switch logOptions.Output {
|
||||||
case "":
|
case "":
|
||||||
if options.PlatformInterface != nil {
|
if platformInterface != nil {
|
||||||
logWriter = io.Discard
|
logWriter = io.Discard
|
||||||
} else {
|
} else {
|
||||||
logWriter = os.Stdout
|
logWriter = os.Stdout
|
||||||
@@ -86,10 +87,10 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
|||||||
TimestampFormat: "-0700 2006-01-02 15:04:05",
|
TimestampFormat: "-0700 2006-01-02 15:04:05",
|
||||||
}
|
}
|
||||||
if needClashAPI {
|
if needClashAPI {
|
||||||
observableLogFactory = log.NewObservableFactory(logFormatter, logWriter, options.PlatformInterface)
|
observableLogFactory = log.NewObservableFactory(logFormatter, logWriter, platformInterface)
|
||||||
logFactory = observableLogFactory
|
logFactory = observableLogFactory
|
||||||
} else {
|
} else {
|
||||||
logFactory = log.NewFactory(logFormatter, logWriter, options.PlatformInterface)
|
logFactory = log.NewFactory(logFormatter, logWriter, platformInterface)
|
||||||
}
|
}
|
||||||
if logOptions.Level != "" {
|
if logOptions.Level != "" {
|
||||||
logLevel, err := log.ParseLevel(logOptions.Level)
|
logLevel, err := log.ParseLevel(logOptions.Level)
|
||||||
@@ -109,7 +110,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
|||||||
common.PtrValueOrDefault(options.DNS),
|
common.PtrValueOrDefault(options.DNS),
|
||||||
common.PtrValueOrDefault(options.NTP),
|
common.PtrValueOrDefault(options.NTP),
|
||||||
options.Inbounds,
|
options.Inbounds,
|
||||||
options.PlatformInterface,
|
platformInterface,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "parse route options")
|
return nil, E.Cause(err, "parse route options")
|
||||||
@@ -129,7 +130,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
|||||||
router,
|
router,
|
||||||
logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
|
logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
|
||||||
inboundOptions,
|
inboundOptions,
|
||||||
options.PlatformInterface,
|
platformInterface,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "parse inbound[", i, "]")
|
return nil, E.Cause(err, "parse inbound[", i, "]")
|
||||||
@@ -212,6 +213,18 @@ func (s *Box) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
for i, out := range s.outbounds {
|
||||||
if starter, isStarter := out.(common.Starter); isStarter {
|
if starter, isStarter := out.(common.Starter); isStarter {
|
||||||
err := starter.Start()
|
err := starter.Start()
|
||||||
@@ -242,18 +255,7 @@ func (s *Box) start() error {
|
|||||||
return E.Cause(err, "initialize inbound/", in.Type(), "[", tag, "]")
|
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)")
|
s.logger.Info("sing-box started (", F.Seconds(time.Since(s.createdAt).Seconds()), "s)")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
_ "github.com/sagernet/gomobile/asset"
|
_ "github.com/sagernet/gomobile/event/key"
|
||||||
"github.com/sagernet/sing-box/cmd/internal/build_shared"
|
"github.com/sagernet/sing-box/cmd/internal/build_shared"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing/common/rw"
|
"github.com/sagernet/sing/common/rw"
|
||||||
@@ -84,11 +84,12 @@ func buildiOS() {
|
|||||||
"-libname=box",
|
"-libname=box",
|
||||||
}
|
}
|
||||||
if !debugEnabled {
|
if !debugEnabled {
|
||||||
args = append(args,
|
args = append(
|
||||||
"-trimpath", "-ldflags=-s -w -buildid=",
|
args, "-trimpath", "-ldflags=-s -w -buildid=",
|
||||||
|
"-tags", "with_gvisor,with_utls,with_clash_api,with_conntrack",
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
args = append(args, "-tags", "debug")
|
args = append(args, "-tags", "with_gvisor,with_utls,with_clash_api,with_conntrack,debug")
|
||||||
}
|
}
|
||||||
|
|
||||||
args = append(args, "./experimental/libbox")
|
args = append(args, "./experimental/libbox")
|
||||||
@@ -101,7 +102,7 @@ func buildiOS() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
copyPath := filepath.Join("..", "sfi")
|
copyPath := filepath.Join("..", "sing-box-for-ios")
|
||||||
if rw.FileExists(copyPath) {
|
if rw.FileExists(copyPath) {
|
||||||
targetDir := filepath.Join(copyPath, "Libbox.xcframework")
|
targetDir := filepath.Join(copyPath, "Libbox.xcframework")
|
||||||
targetDir, _ = filepath.Abs(targetDir)
|
targetDir, _ = filepath.Abs(targetDir)
|
||||||
|
|||||||
@@ -31,7 +31,10 @@ func check() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
_, err = box.New(ctx, options)
|
instance, err := box.New(ctx, options, nil)
|
||||||
|
if err == nil {
|
||||||
|
instance.Close()
|
||||||
|
}
|
||||||
cancel()
|
cancel()
|
||||||
return err
|
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
|
options.Log.DisableColor = true
|
||||||
}
|
}
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
instance, err := box.New(ctx, options)
|
instance, err := box.New(ctx, options, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cancel()
|
cancel()
|
||||||
return nil, nil, E.Cause(err, "create service")
|
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"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/common/dialer/conntrack"
|
||||||
"github.com/sagernet/sing-box/common/warning"
|
"github.com/sagernet/sing-box/common/warning"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
@@ -159,16 +160,30 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !address.IsIPv6() {
|
if !address.IsIPv6() {
|
||||||
return DialSlowContext(&d.dialer4, ctx, network, address)
|
return trackConn(DialSlowContext(&d.dialer4, ctx, network, address))
|
||||||
} else {
|
} 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) {
|
func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
if !destination.IsIPv6() {
|
if !destination.IsIPv6() {
|
||||||
return d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4)
|
return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4))
|
||||||
} else {
|
} 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)
|
bufReader := std_bufio.NewReader(conn)
|
||||||
header, err := proxyproto.Read(bufReader)
|
header, err := proxyproto.Read(bufReader)
|
||||||
if err != nil && !(l.AcceptNoHeader && err == proxyproto.ErrNoProxyProtocol) {
|
if err != nil && !(l.AcceptNoHeader && err == proxyproto.ErrNoProxyProtocol) {
|
||||||
return nil, err
|
return nil, &Error{err}
|
||||||
}
|
}
|
||||||
if bufReader.Buffered() > 0 {
|
if bufReader.Buffered() > 0 {
|
||||||
cache := buf.NewSize(bufReader.Buffered())
|
cache := buf.NewSize(bufReader.Buffered())
|
||||||
_, err = cache.ReadFullFrom(bufReader, cache.FreeLen())
|
_, err = cache.ReadFullFrom(bufReader, cache.FreeLen())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, &Error{err}
|
||||||
}
|
}
|
||||||
conn = bufio.NewCachedConn(conn, cache)
|
conn = bufio.NewCachedConn(conn, cache)
|
||||||
}
|
}
|
||||||
@@ -42,3 +42,21 @@ func (l *Listener) Accept() (net.Conn, error) {
|
|||||||
}
|
}
|
||||||
return conn, nil
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/badtls"
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
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) {
|
func NewDialerFromOptions(router adapter.Router, dialer N.Dialer, serverAddress string, options option.OutboundTLSOptions) (N.Dialer, error) {
|
||||||
@@ -43,22 +42,7 @@ func NewClient(router adapter.Router, serverAddress string, options option.Outbo
|
|||||||
func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) {
|
func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) {
|
||||||
ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
|
ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
tlsConn, err := config.Client(conn)
|
return aTLS.ClientHandshake(ctx, conn, config)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Dialer struct {
|
type Dialer struct {
|
||||||
|
|||||||
@@ -1,51 +1,25 @@
|
|||||||
package tls
|
package tls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
aTLS "github.com/sagernet/sing/common/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
Config = aTLS.Config
|
||||||
|
ConfigCompat = aTLS.ConfigCompat
|
||||||
|
ServerConfig = aTLS.ServerConfig
|
||||||
|
ServerConfigCompat = aTLS.ServerConfigCompat
|
||||||
|
WithSessionIDGenerator = aTLS.WithSessionIDGenerator
|
||||||
|
Conn = aTLS.Conn
|
||||||
|
|
||||||
STDConfig = tls.Config
|
STDConfig = tls.Config
|
||||||
STDConn = tls.Conn
|
STDConn = tls.Conn
|
||||||
ConnectionState = tls.ConnectionState
|
ConnectionState = tls.ConnectionState
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config interface {
|
|
||||||
ServerName() string
|
|
||||||
SetServerName(serverName string)
|
|
||||||
NextProtos() []string
|
|
||||||
SetNextProtos(nextProto []string)
|
|
||||||
Config() (*STDConfig, error)
|
|
||||||
Client(conn net.Conn) (Conn, error)
|
|
||||||
Clone() Config
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConfigWithSessionIDGenerator interface {
|
|
||||||
SetSessionIDGenerator(generator func(clientHello []byte, sessionID []byte) error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ServerConfig interface {
|
|
||||||
Config
|
|
||||||
adapter.Service
|
|
||||||
Server(conn net.Conn) (Conn, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ServerConfigCompat interface {
|
|
||||||
ServerConfig
|
|
||||||
ServerHandshake(ctx context.Context, conn net.Conn) (Conn, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Conn interface {
|
|
||||||
net.Conn
|
|
||||||
HandshakeContext(ctx context.Context) error
|
|
||||||
ConnectionState() ConnectionState
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseTLSVersion(version string) (uint16, error) {
|
func ParseTLSVersion(version string) (uint16, error) {
|
||||||
switch version {
|
switch version {
|
||||||
case "1.0":
|
case "1.0":
|
||||||
|
|||||||
@@ -4,19 +4,25 @@ package tls
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
mRand "math/rand"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
@@ -24,12 +30,14 @@ import (
|
|||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing/common/debug"
|
"github.com/sagernet/sing/common/debug"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
aTLS "github.com/sagernet/sing/common/tls"
|
||||||
utls "github.com/sagernet/utls"
|
utls "github.com/sagernet/utls"
|
||||||
|
|
||||||
"golang.org/x/crypto/hkdf"
|
"golang.org/x/crypto/hkdf"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Config = (*RealityClientConfig)(nil)
|
var _ ConfigCompat = (*RealityClientConfig)(nil)
|
||||||
|
|
||||||
type RealityClientConfig struct {
|
type RealityClientConfig struct {
|
||||||
uClient *UTLSClientConfig
|
uClient *UTLSClientConfig
|
||||||
@@ -85,6 +93,10 @@ func (e *RealityClientConfig) Config() (*STDConfig, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *RealityClientConfig) Client(conn net.Conn) (Conn, error) {
|
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{
|
verifier := &realityVerifier{
|
||||||
serverName: e.uClient.ServerName(),
|
serverName: e.uClient.ServerName(),
|
||||||
}
|
}
|
||||||
@@ -137,9 +149,43 @@ func (e *RealityClientConfig) Client(conn net.Conn) (Conn, error) {
|
|||||||
fmt.Printf("REALITY uConn.AuthKey: %v\n", authKey)
|
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
|
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) {
|
func (e *RealityClientConfig) SetSessionIDGenerator(generator func(clientHello []byte, sessionID []byte) error) {
|
||||||
e.uClient.config.SessionIDGenerator = generator
|
e.uClient.config.SessionIDGenerator = generator
|
||||||
}
|
}
|
||||||
@@ -180,8 +226,5 @@ func (c *realityVerifier) VerifyPeerCertificate(rawCerts [][]byte, verifiedChain
|
|||||||
if _, err := certs[0].Verify(opts); err != nil {
|
if _, err := certs[0].Verify(opts); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !c.verified {
|
|
||||||
return E.New("reality verification failed")
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/reality"
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
"github.com/sagernet/sing-box/common/dialer"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
@@ -19,8 +19,6 @@ import (
|
|||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
"github.com/nekohasekai/reality"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ ServerConfigCompat = (*RealityServerConfig)(nil)
|
var _ ServerConfigCompat = (*RealityServerConfig)(nil)
|
||||||
@@ -136,7 +134,7 @@ func (c *RealityServerConfig) Config() (*tls.Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *RealityServerConfig) Client(conn net.Conn) (Conn, error) {
|
func (c *RealityServerConfig) Client(conn net.Conn) (Conn, error) {
|
||||||
return nil, os.ErrInvalid
|
return ClientHandshake(context.Background(), conn, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RealityServerConfig) Start() error {
|
func (c *RealityServerConfig) Start() error {
|
||||||
@@ -148,7 +146,7 @@ func (c *RealityServerConfig) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *RealityServerConfig) Server(conn net.Conn) (Conn, error) {
|
func (c *RealityServerConfig) Server(conn net.Conn) (Conn, error) {
|
||||||
return nil, os.ErrInvalid
|
return ServerHandshake(context.Background(), conn, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *RealityServerConfig) ServerHandshake(ctx context.Context, conn net.Conn) (Conn, error) {
|
func (c *RealityServerConfig) ServerHandshake(ctx context.Context, conn net.Conn) (Conn, error) {
|
||||||
|
|||||||
@@ -2,14 +2,13 @@ package tls
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/badtls"
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
aTLS "github.com/sagernet/sing/common/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewServer(ctx context.Context, router adapter.Router, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
func NewServer(ctx context.Context, router adapter.Router, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
|
||||||
@@ -26,23 +25,5 @@ func NewServer(ctx context.Context, router adapter.Router, logger log.Logger, op
|
|||||||
func ServerHandshake(ctx context.Context, conn net.Conn, config ServerConfig) (Conn, error) {
|
func ServerHandshake(ctx context.Context, conn net.Conn, config ServerConfig) (Conn, error) {
|
||||||
ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
|
ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
if compatServer, isCompat := config.(ServerConfigCompat); isCompat {
|
return aTLS.ServerHandshake(ctx, conn, config)
|
||||||
return compatServer.ServerHandshake(ctx, conn)
|
|
||||||
}
|
|
||||||
tlsConn, err := config.Server(conn)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package tls
|
|||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
@@ -13,6 +14,8 @@ import (
|
|||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
utls "github.com/sagernet/utls"
|
utls "github.com/sagernet/utls"
|
||||||
|
|
||||||
|
"golang.org/x/net/http2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UTLSClientConfig struct {
|
type UTLSClientConfig struct {
|
||||||
@@ -33,6 +36,9 @@ func (e *UTLSClientConfig) NextProtos() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *UTLSClientConfig) SetNextProtos(nextProto []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
|
e.config.NextProtos = nextProto
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,6 +165,29 @@ func NewUTLSClient(router adapter.Router, serverAddress string, options option.O
|
|||||||
return &UTLSClientConfig{&tlsConfig, id}, nil
|
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) {
|
func uTLSClientHelloID(name string) (utls.ClientHelloID, error) {
|
||||||
switch name {
|
switch name {
|
||||||
case "chrome", "":
|
case "chrome", "":
|
||||||
@@ -178,7 +207,9 @@ func uTLSClientHelloID(name string) (utls.ClientHelloID, error) {
|
|||||||
case "android":
|
case "android":
|
||||||
return utls.HelloAndroid_11_OkHttp, nil
|
return utls.HelloAndroid_11_OkHttp, nil
|
||||||
case "random":
|
case "random":
|
||||||
return utls.HelloRandomized, nil
|
return randomFingerprint, nil
|
||||||
|
case "randomized":
|
||||||
|
return randomizedFingerprint, nil
|
||||||
default:
|
default:
|
||||||
return utls.ClientHelloID{}, E.New("unknown uTLS fingerprint: ", name)
|
return utls.ClientHelloID{}, E.New("unknown uTLS fingerprint: ", name)
|
||||||
}
|
}
|
||||||
|
|||||||
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,16 @@
|
|||||||
|
#### 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
|
#### 1.2-beta5
|
||||||
|
|
||||||
* Add [VLESS server](/configuration/inbound/vless) and [vision](/configuration/outbound/vless#flow) support
|
* Add [VLESS server](/configuration/inbound/vless) and [vision](/configuration/outbound/vless#flow) support
|
||||||
|
|||||||
@@ -77,11 +77,11 @@ Both if empty.
|
|||||||
|
|
||||||
==Required==
|
==Required==
|
||||||
|
|
||||||
| Method | Password Format |
|
| Method | Password Format |
|
||||||
|---------------|-------------------------------------|
|
|---------------|------------------------------------------------|
|
||||||
| none | / |
|
| none | / |
|
||||||
| 2022 methods | `openssl rand -base64 <Key Length>` |
|
| 2022 methods | `sing-box generate rand --base64 <Key Length>` |
|
||||||
| other methods | any string |
|
| other methods | any string |
|
||||||
|
|
||||||
### Listen Fields
|
### Listen Fields
|
||||||
|
|
||||||
|
|||||||
@@ -77,8 +77,8 @@ See [Listen Fields](/configuration/shared/listen) for details.
|
|||||||
|
|
||||||
==必填==
|
==必填==
|
||||||
|
|
||||||
| 方法 | 密码格式 |
|
| 方法 | 密码格式 |
|
||||||
|---------------|-------------------------------|
|
|---------------|------------------------------------------|
|
||||||
| none | / |
|
| none | / |
|
||||||
| 2022 methods | `openssl rand -base64 <密钥长度>` |
|
| 2022 methods | `sing-box generate rand --base64 <密钥长度>` |
|
||||||
| other methods | 任意字符串 |
|
| other methods | 任意字符串 |
|
||||||
@@ -46,8 +46,15 @@
|
|||||||
"exclude_package": [
|
"exclude_package": [
|
||||||
"com.android.captiveportallogin"
|
"com.android.captiveportallogin"
|
||||||
],
|
],
|
||||||
...
|
"platform": {
|
||||||
// Listen Fields
|
"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.
|
Exclude android packages in route.
|
||||||
|
|
||||||
|
#### platform
|
||||||
|
|
||||||
|
Platform-specific settings, provided by client applications.
|
||||||
|
|
||||||
|
#### platform.http_proxy
|
||||||
|
|
||||||
|
System HTTP proxy settings.
|
||||||
|
|
||||||
### Listen Fields
|
### Listen Fields
|
||||||
|
|
||||||
See [Listen Fields](/configuration/shared/listen) for details.
|
See [Listen Fields](/configuration/shared/listen) for details.
|
||||||
|
|||||||
@@ -46,8 +46,15 @@
|
|||||||
"exclude_package": [
|
"exclude_package": [
|
||||||
"com.android.captiveportallogin"
|
"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`。
|
UID 规则仅在 Linux 下被支持,并且需要 `auto_route`。
|
||||||
|
|
||||||
限制被路由的的用户。默认不限制。
|
限制被路由的用户。默认不限制。
|
||||||
|
|
||||||
#### include_uid_range
|
#### include_uid_range
|
||||||
|
|
||||||
限制被路由的的用户范围。
|
限制被路由的用户范围。
|
||||||
|
|
||||||
#### exclude_uid
|
#### exclude_uid
|
||||||
|
|
||||||
排除路由的的用户。
|
排除路由的用户。
|
||||||
|
|
||||||
#### exclude_uid_range
|
#### exclude_uid_range
|
||||||
|
|
||||||
排除路由的的用户范围。
|
排除路由的用户范围。
|
||||||
|
|
||||||
#### include_android_user
|
#### include_android_user
|
||||||
|
|
||||||
@@ -183,6 +190,14 @@ TCP/IP 栈。
|
|||||||
|
|
||||||
排除路由的 Android 应用包名。
|
排除路由的 Android 应用包名。
|
||||||
|
|
||||||
|
#### platform
|
||||||
|
|
||||||
|
平台特定的设置,由客户端应用提供。
|
||||||
|
|
||||||
|
#### platform.http_proxy
|
||||||
|
|
||||||
|
系统 HTTP 代理设置。
|
||||||
|
|
||||||
### 监听字段
|
### 监听字段
|
||||||
|
|
||||||
参阅 [监听字段](/zh/configuration/shared/listen/)。
|
参阅 [监听字段](/zh/configuration/shared/listen/)。
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
"users": [
|
"users": [
|
||||||
{
|
{
|
||||||
"name": "sekai",
|
"name": "sekai",
|
||||||
"uuid": "bf000d23-0752-40b4-affe-68f7707a9661"
|
"uuid": "bf000d23-0752-40b4-affe-68f7707a9661",
|
||||||
|
"flow": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tls": {},
|
"tls": {},
|
||||||
@@ -30,6 +31,20 @@ See [Listen Fields](/configuration/shared/listen) for details.
|
|||||||
|
|
||||||
VLESS users.
|
VLESS users.
|
||||||
|
|
||||||
|
#### users.uuid
|
||||||
|
|
||||||
|
==Required==
|
||||||
|
|
||||||
|
VLESS user id.
|
||||||
|
|
||||||
|
#### users.flow
|
||||||
|
|
||||||
|
VLESS Sub-protocol.
|
||||||
|
|
||||||
|
Available values:
|
||||||
|
|
||||||
|
* `xtls-rprx-vision`
|
||||||
|
|
||||||
#### tls
|
#### tls
|
||||||
|
|
||||||
TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
|
TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
"users": [
|
"users": [
|
||||||
{
|
{
|
||||||
"name": "sekai",
|
"name": "sekai",
|
||||||
"uuid": "bf000d23-0752-40b4-affe-68f7707a9661"
|
"uuid": "bf000d23-0752-40b4-affe-68f7707a9661",
|
||||||
|
"flow": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tls": {},
|
"tls": {},
|
||||||
@@ -30,6 +31,20 @@
|
|||||||
|
|
||||||
VLESS 用户。
|
VLESS 用户。
|
||||||
|
|
||||||
|
#### users.uuid
|
||||||
|
|
||||||
|
==必填==
|
||||||
|
|
||||||
|
VLESS 用户 ID。
|
||||||
|
|
||||||
|
#### users.flow
|
||||||
|
|
||||||
|
VLESS 子协议。
|
||||||
|
|
||||||
|
可用值:
|
||||||
|
|
||||||
|
* `xtls-rprx-vision`
|
||||||
|
|
||||||
#### tls
|
#### tls
|
||||||
|
|
||||||
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
|
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ The server port.
|
|||||||
|
|
||||||
==Required==
|
==Required==
|
||||||
|
|
||||||
The VLESS user id.
|
VLESS user id.
|
||||||
|
|
||||||
#### flow
|
#### flow
|
||||||
|
|
||||||
@@ -60,6 +60,8 @@ TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
|
|||||||
|
|
||||||
#### packet_encoding
|
#### packet_encoding
|
||||||
|
|
||||||
|
UDP packet encoding, xudp is used by default.
|
||||||
|
|
||||||
| Encoding | Description |
|
| Encoding | Description |
|
||||||
|------------|-----------------------|
|
|------------|-----------------------|
|
||||||
| (none) | Disabled |
|
| (none) | Disabled |
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
|||||||
|
|
||||||
#### packet_encoding
|
#### packet_encoding
|
||||||
|
|
||||||
|
UDP 包编码,默认使用 xudp。
|
||||||
|
|
||||||
| 编码 | 描述 |
|
| 编码 | 描述 |
|
||||||
|------------|---------------|
|
|------------|---------------|
|
||||||
| (空) | 禁用 |
|
| (空) | 禁用 |
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ Encryption methods:
|
|||||||
* `none`
|
* `none`
|
||||||
* `zero`
|
* `zero`
|
||||||
* `aes-128-gcm`
|
* `aes-128-gcm`
|
||||||
* `chancha20-poly1305`
|
* `chacha20-poly1305`
|
||||||
|
|
||||||
Legacy encryption methods:
|
Legacy encryption methods:
|
||||||
|
|
||||||
@@ -86,6 +86,8 @@ TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
|
|||||||
|
|
||||||
#### packet_encoding
|
#### packet_encoding
|
||||||
|
|
||||||
|
UDP packet encoding.
|
||||||
|
|
||||||
| Encoding | Description |
|
| Encoding | Description |
|
||||||
|------------|-----------------------|
|
|------------|-----------------------|
|
||||||
| (none) | Disabled |
|
| (none) | Disabled |
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ VMess 用户 ID。
|
|||||||
* `none`
|
* `none`
|
||||||
* `zero`
|
* `zero`
|
||||||
* `aes-128-gcm`
|
* `aes-128-gcm`
|
||||||
* `chancha20-poly1305`
|
* `chacha20-poly1305`
|
||||||
|
|
||||||
旧加密方法:
|
旧加密方法:
|
||||||
|
|
||||||
@@ -86,6 +86,8 @@ TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
|||||||
|
|
||||||
#### packet_encoding
|
#### packet_encoding
|
||||||
|
|
||||||
|
UDP 包编码。
|
||||||
|
|
||||||
| 编码 | 描述 |
|
| 编码 | 描述 |
|
||||||
|------------|---------------|
|
|------------|---------------|
|
||||||
| (空) | 禁用 |
|
| (空) | 禁用 |
|
||||||
|
|||||||
@@ -218,6 +218,7 @@ Available fingerprint values:
|
|||||||
* ios
|
* ios
|
||||||
* android
|
* android
|
||||||
* random
|
* random
|
||||||
|
* randomized
|
||||||
|
|
||||||
Chrome fingerprint will be used if empty.
|
Chrome fingerprint will be used if empty.
|
||||||
|
|
||||||
@@ -318,7 +319,7 @@ Handshake server address and [Dial options](/configuration/shared/dial).
|
|||||||
|
|
||||||
==Required==
|
==Required==
|
||||||
|
|
||||||
Private key, generated by `./xray x25519`.
|
Private key, generated by `sing-box generate reality-keypair`.
|
||||||
|
|
||||||
#### public_key
|
#### public_key
|
||||||
|
|
||||||
@@ -326,7 +327,7 @@ Private key, generated by `./xray x25519`.
|
|||||||
|
|
||||||
==Required==
|
==Required==
|
||||||
|
|
||||||
Public key, generated by `./xray x25519`.
|
Public key, generated by `sing-box generate reality-keypair`.
|
||||||
|
|
||||||
#### short_id
|
#### short_id
|
||||||
|
|
||||||
|
|||||||
@@ -218,6 +218,7 @@ uTLS 是 "crypto/tls" 的一个分支,它提供了 ClientHello 指纹识别阻
|
|||||||
* ios
|
* ios
|
||||||
* android
|
* android
|
||||||
* random
|
* random
|
||||||
|
* randomized
|
||||||
|
|
||||||
默认使用 chrome 指纹。
|
默认使用 chrome 指纹。
|
||||||
|
|
||||||
@@ -314,7 +315,7 @@ MAC 密钥。
|
|||||||
|
|
||||||
==必填==
|
==必填==
|
||||||
|
|
||||||
私钥,由 `./xray x25519` 生成。
|
私钥,由 `sing-box generate reality-keypair` 生成。
|
||||||
|
|
||||||
#### public_key
|
#### public_key
|
||||||
|
|
||||||
@@ -322,7 +323,7 @@ MAC 密钥。
|
|||||||
|
|
||||||
==必填==
|
==必填==
|
||||||
|
|
||||||
公钥,由 `./xray x25519` 生成。
|
公钥,由 `sing-box generate reality-keypair` 生成。
|
||||||
|
|
||||||
#### short_id
|
#### short_id
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
"server": "127.0.0.1",
|
"server": "127.0.0.1",
|
||||||
"server_port": 4443,
|
"server_port": 4443,
|
||||||
"version": 3,
|
"version": 3,
|
||||||
"password": "fuck me till the daylight",
|
"password": "8JCsPssfgS8tiRwiMlhARg==",
|
||||||
"tls": {
|
"tls": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"server_name": "google.com",
|
"server_name": "google.com",
|
||||||
|
|||||||
@@ -8,45 +8,6 @@ Welcome to the wiki page for the sing-box project.
|
|||||||
|
|
||||||
The universal proxy platform.
|
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
|
## 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)。
|
||||||
@@ -44,6 +44,7 @@ type Server struct {
|
|||||||
urlTestHistory *urltest.HistoryStorage
|
urlTestHistory *urltest.HistoryStorage
|
||||||
mode string
|
mode string
|
||||||
storeSelected bool
|
storeSelected bool
|
||||||
|
cacheFilePath string
|
||||||
cacheFile adapter.ClashCacheFile
|
cacheFile adapter.ClashCacheFile
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,11 +76,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
|||||||
} else {
|
} else {
|
||||||
cachePath = C.BasePath(cachePath)
|
cachePath = C.BasePath(cachePath)
|
||||||
}
|
}
|
||||||
cacheFile, err := cachefile.Open(cachePath)
|
server.cacheFilePath = cachePath
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "open cache file")
|
|
||||||
}
|
|
||||||
server.cacheFile = cacheFile
|
|
||||||
}
|
}
|
||||||
cors := cors.New(cors.Options{
|
cors := cors.New(cors.Options{
|
||||||
AllowedOrigins: []string{"*"},
|
AllowedOrigins: []string{"*"},
|
||||||
@@ -118,6 +115,13 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Start() error {
|
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)
|
listener, err := net.Listen("tcp", s.httpServer.Addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "external controller listen error")
|
return E.Cause(err, "external controller listen error")
|
||||||
|
|||||||
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
|
||||||
|
}
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
//go:build ios
|
|
||||||
|
|
||||||
package libbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
type LogClient struct {
|
|
||||||
sockPath string
|
|
||||||
handler LogClientHandler
|
|
||||||
conn net.Conn
|
|
||||||
}
|
|
||||||
|
|
||||||
type LogClientHandler interface {
|
|
||||||
Connected()
|
|
||||||
Disconnected()
|
|
||||||
WriteLog(message string)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLogClient(sharedDirectory string, handler LogClientHandler) *LogClient {
|
|
||||||
return &LogClient{
|
|
||||||
sockPath: filepath.Join(sharedDirectory, "log.sock"),
|
|
||||||
handler: handler,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *LogClient) Connect() error {
|
|
||||||
conn, err := net.DialUnix("unix", nil, &net.UnixAddr{
|
|
||||||
Name: c.sockPath,
|
|
||||||
Net: "unix",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.conn = conn
|
|
||||||
go c.loopConnection(&messageConn{conn})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *LogClient) Disconnect() error {
|
|
||||||
return common.Close(c.conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *LogClient) loopConnection(conn *messageConn) {
|
|
||||||
c.handler.Connected()
|
|
||||||
defer c.handler.Disconnected()
|
|
||||||
for {
|
|
||||||
message, err := conn.Read()
|
|
||||||
if err != nil {
|
|
||||||
c.handler.WriteLog("(log client error) " + err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.handler.WriteLog(string(message))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
//go:build ios
|
|
||||||
|
|
||||||
package libbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"io"
|
|
||||||
"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 LogServer struct {
|
|
||||||
sockPath string
|
|
||||||
listener net.Listener
|
|
||||||
|
|
||||||
access sync.Mutex
|
|
||||||
savedLines *list.List[string]
|
|
||||||
subscriber *observable.Subscriber[string]
|
|
||||||
observer *observable.Observer[string]
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLogServer(sharedDirectory string) *LogServer {
|
|
||||||
server := &LogServer{
|
|
||||||
sockPath: filepath.Join(sharedDirectory, "log.sock"),
|
|
||||||
savedLines: new(list.List[string]),
|
|
||||||
subscriber: observable.NewSubscriber[string](128),
|
|
||||||
}
|
|
||||||
server.observer = observable.NewObserver[string](server.subscriber, 64)
|
|
||||||
return server
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *LogServer) Start() error {
|
|
||||||
os.Remove(s.sockPath)
|
|
||||||
listener, err := net.ListenUnix("unix", &net.UnixAddr{
|
|
||||||
Name: s.sockPath,
|
|
||||||
Net: "unix",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
go s.loopConnection(listener)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *LogServer) Close() error {
|
|
||||||
return s.listener.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *LogServer) 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 (s *LogServer) loopConnection(listener net.Listener) {
|
|
||||||
for {
|
|
||||||
conn, err := listener.Accept()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
hErr := s.handleConnection(&messageConn{conn})
|
|
||||||
if hErr != nil && !E.IsClosed(err) {
|
|
||||||
log.Warn("log-server: process connection: ", hErr)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *LogServer) handleConnection(conn *messageConn) 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 = conn.Write([]byte(line))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case message := <-subscription:
|
|
||||||
err = conn.Write([]byte(message))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case <-done:
|
|
||||||
conn.Close()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type messageConn struct {
|
|
||||||
net.Conn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *messageConn) Read() ([]byte, error) {
|
|
||||||
var messageLength uint16
|
|
||||||
err := binary.Read(c.Conn, binary.BigEndian, &messageLength)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
data := make([]byte, messageLength)
|
|
||||||
_, err = io.ReadFull(c.Conn, data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *messageConn) Write(message []byte) error {
|
|
||||||
err := binary.Write(c.Conn, binary.BigEndian, uint16(len(message)))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = c.Conn.Write(message)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
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)
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
package libbox
|
package libbox
|
||||||
|
|
||||||
|
import "github.com/sagernet/sing-box/option"
|
||||||
|
|
||||||
type PlatformInterface interface {
|
type PlatformInterface interface {
|
||||||
AutoDetectInterfaceControl(fd int32) error
|
AutoDetectInterfaceControl(fd int32) error
|
||||||
OpenTun(options TunOptions) (TunInterface, error)
|
OpenTun(options TunOptions) (int32, error)
|
||||||
WriteLog(message string)
|
WriteLog(message string)
|
||||||
UseProcFS() bool
|
UseProcFS() bool
|
||||||
FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int32, destinationAddress string, destinationPort int32) (int32, error)
|
FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int32, destinationAddress string, destinationPort int32) (int32, error)
|
||||||
@@ -14,3 +16,51 @@ type TunInterface interface {
|
|||||||
FileDescriptor() int32
|
FileDescriptor() int32
|
||||||
Close() error
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/process"
|
"github.com/sagernet/sing-box/common/process"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Interface interface {
|
type Interface interface {
|
||||||
AutoDetectInterfaceControl() control.Func
|
AutoDetectInterfaceControl() control.Func
|
||||||
OpenTun(options tun.Options) (tun.Tun, error)
|
OpenTun(options tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
|
||||||
process.Searcher
|
process.Searcher
|
||||||
io.Writer
|
io.Writer
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,14 +3,13 @@ package libbox
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box"
|
"github.com/sagernet/sing-box"
|
||||||
"github.com/sagernet/sing-box/common/process"
|
"github.com/sagernet/sing-box/common/process"
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/internal/procfs"
|
"github.com/sagernet/sing-box/experimental/libbox/internal/procfs"
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
@@ -28,10 +27,8 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
platformInterface.WriteLog("Hello " + runtime.GOOS + "/" + runtime.GOARCH)
|
|
||||||
options.PlatformInterface = &platformInterfaceWrapper{platformInterface, platformInterface.UseProcFS()}
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
instance, err := box.New(ctx, options)
|
instance, err := box.New(ctx, options, &platformInterfaceWrapper{platformInterface, platformInterface.UseProcFS()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cancel()
|
cancel()
|
||||||
return nil, E.Cause(err, "create service")
|
return nil, E.Cause(err, "create service")
|
||||||
@@ -67,26 +64,19 @@ func (w *platformInterfaceWrapper) AutoDetectInterfaceControl() control.Func {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *platformInterfaceWrapper) OpenTun(options tun.Options) (tun.Tun, error) {
|
func (w *platformInterfaceWrapper) OpenTun(options tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) {
|
||||||
if len(options.IncludeUID) > 0 || len(options.ExcludeUID) > 0 {
|
if len(options.IncludeUID) > 0 || len(options.ExcludeUID) > 0 {
|
||||||
return nil, E.New("android: unsupported uid options")
|
return nil, E.New("android: unsupported uid options")
|
||||||
}
|
}
|
||||||
if len(options.IncludeAndroidUser) > 0 {
|
if len(options.IncludeAndroidUser) > 0 {
|
||||||
return nil, E.New("android: unsupported android_user option")
|
return nil, E.New("android: unsupported android_user option")
|
||||||
}
|
}
|
||||||
|
tunFd, err := w.iif.OpenTun(&tunOptions{options, platformOptions})
|
||||||
optionsWrapper := tunOptions(options)
|
|
||||||
tunInterface, err := w.iif.OpenTun(&optionsWrapper)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
tunFd := tunInterface.FileDescriptor()
|
options.FileDescriptor = int(tunFd)
|
||||||
return &nativeTun{
|
return tun.New(options)
|
||||||
tunFd: int(tunFd),
|
|
||||||
tunFile: os.NewFile(uintptr(tunFd), "tun"),
|
|
||||||
tunMTU: options.MTU,
|
|
||||||
closer: tunInterface,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *platformInterfaceWrapper) Write(p []byte) (n int, err error) {
|
func (w *platformInterfaceWrapper) Write(p []byte) (n int, err error) {
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package libbox
|
package libbox
|
||||||
|
|
||||||
import C "github.com/sagernet/sing-box/constant"
|
import (
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
|
||||||
|
"github.com/dustin/go-humanize"
|
||||||
|
)
|
||||||
|
|
||||||
func SetBasePath(path string) {
|
func SetBasePath(path string) {
|
||||||
C.SetBasePath(path)
|
C.SetBasePath(path)
|
||||||
@@ -9,3 +13,7 @@ func SetBasePath(path string) {
|
|||||||
func Version() string {
|
func Version() string {
|
||||||
return C.Version
|
return C.Version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FormatBytes(length int64) string {
|
||||||
|
return humanize.Bytes(uint64(length))
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
package libbox
|
package libbox
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TunOptions interface {
|
type TunOptions interface {
|
||||||
@@ -21,6 +22,9 @@ type TunOptions interface {
|
|||||||
GetInet6RouteAddress() RoutePrefixIterator
|
GetInet6RouteAddress() RoutePrefixIterator
|
||||||
GetIncludePackage() StringIterator
|
GetIncludePackage() StringIterator
|
||||||
GetExcludePackage() StringIterator
|
GetExcludePackage() StringIterator
|
||||||
|
IsHTTPProxyEnabled() bool
|
||||||
|
GetHTTPProxyServer() string
|
||||||
|
GetHTTPProxyServerPort() int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type RoutePrefix struct {
|
type RoutePrefix struct {
|
||||||
@@ -28,6 +32,16 @@ type RoutePrefix struct {
|
|||||||
Prefix int32
|
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 {
|
type RoutePrefixIterator interface {
|
||||||
Next() *RoutePrefix
|
Next() *RoutePrefix
|
||||||
HasNext() bool
|
HasNext() bool
|
||||||
@@ -44,7 +58,10 @@ func mapRoutePrefix(prefixes []netip.Prefix) RoutePrefixIterator {
|
|||||||
|
|
||||||
var _ TunOptions = (*tunOptions)(nil)
|
var _ TunOptions = (*tunOptions)(nil)
|
||||||
|
|
||||||
type tunOptions tun.Options
|
type tunOptions struct {
|
||||||
|
tun.Options
|
||||||
|
option.TunPlatformOptions
|
||||||
|
}
|
||||||
|
|
||||||
func (o *tunOptions) GetInet4Address() RoutePrefixIterator {
|
func (o *tunOptions) GetInet4Address() RoutePrefixIterator {
|
||||||
return mapRoutePrefix(o.Inet4Address)
|
return mapRoutePrefix(o.Inet4Address)
|
||||||
@@ -89,21 +106,17 @@ func (o *tunOptions) GetExcludePackage() StringIterator {
|
|||||||
return newIterator(o.ExcludePackage)
|
return newIterator(o.ExcludePackage)
|
||||||
}
|
}
|
||||||
|
|
||||||
type nativeTun struct {
|
func (o *tunOptions) IsHTTPProxyEnabled() bool {
|
||||||
tunFd int
|
if o.TunPlatformOptions.HTTPProxy == nil {
|
||||||
tunFile *os.File
|
return false
|
||||||
tunMTU uint32
|
}
|
||||||
closer io.Closer
|
return o.TunPlatformOptions.HTTPProxy.Enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *nativeTun) Read(p []byte) (n int, err error) {
|
func (o *tunOptions) GetHTTPProxyServer() string {
|
||||||
return t.tunFile.Read(p)
|
return o.TunPlatformOptions.HTTPProxy.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *nativeTun) Write(p []byte) (n int, err error) {
|
func (o *tunOptions) GetHTTPProxyServerPort() int32 {
|
||||||
return t.tunFile.Write(p)
|
return int32(o.TunPlatformOptions.HTTPProxy.ServerPort)
|
||||||
}
|
|
||||||
|
|
||||||
func (t *nativeTun) Close() error {
|
|
||||||
return t.closer.Close()
|
|
||||||
}
|
}
|
||||||
|
|||||||
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
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
//go:build with_gvisor && linux
|
|
||||||
|
|
||||||
package libbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/sagernet/sing-tun"
|
|
||||||
|
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
|
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ tun.GVisorTun = (*nativeTun)(nil)
|
|
||||||
|
|
||||||
func (t *nativeTun) NewEndpoint() (stack.LinkEndpoint, error) {
|
|
||||||
return fdbased.New(&fdbased.Options{
|
|
||||||
FDs: []int{t.tunFd},
|
|
||||||
MTU: t.tunMTU,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
41
go.mod
41
go.mod
@@ -14,42 +14,47 @@ require (
|
|||||||
github.com/go-chi/render v1.0.2
|
github.com/go-chi/render v1.0.2
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible
|
github.com/gofrs/uuid v4.4.0+incompatible
|
||||||
github.com/hashicorp/yamux v0.1.1
|
github.com/hashicorp/yamux v0.1.1
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20230220010740-598984875576
|
github.com/insomniacslk/dhcp v0.0.0-20230301142404-3e45eea5edd7
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||||
github.com/mholt/acmez v1.1.0
|
github.com/mholt/acmez v1.1.0
|
||||||
github.com/miekg/dns v1.1.50
|
github.com/miekg/dns v1.1.51
|
||||||
github.com/nekohasekai/reality v0.0.0-20230225080858-d70c703b04cd
|
|
||||||
github.com/oschwald/maxminddb-golang v1.10.0
|
github.com/oschwald/maxminddb-golang v1.10.0
|
||||||
github.com/pires/go-proxyproto v0.6.2
|
github.com/pires/go-proxyproto v0.6.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/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0
|
||||||
github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca
|
github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca
|
||||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
|
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
|
||||||
github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b
|
github.com/sagernet/reality v0.0.0-20230228045158-d3e085a8e5d1
|
||||||
|
github.com/sagernet/sing v0.1.8-0.20230305055148-e16845727f76
|
||||||
github.com/sagernet/sing-dns v0.1.4
|
github.com/sagernet/sing-dns v0.1.4
|
||||||
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9
|
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9
|
||||||
github.com/sagernet/sing-shadowtls v0.0.0-20230221123345-78e50cd7b587
|
github.com/sagernet/sing-shadowtls v0.1.0
|
||||||
github.com/sagernet/sing-tun v0.1.1
|
github.com/sagernet/sing-tun v0.1.2-0.20230226091124-0cdb0eed74d9
|
||||||
github.com/sagernet/sing-vmess v0.1.2
|
github.com/sagernet/sing-vmess v0.1.3-0.20230303082804-627cc46ae68b
|
||||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195
|
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/utls v0.0.0-20230225061716-536a007c8b01
|
||||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
|
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
|
||||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c
|
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c
|
||||||
github.com/spf13/cobra v1.6.1
|
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.etcd.io/bbolt v1.3.7
|
||||||
go.uber.org/atomic v1.10.0
|
go.uber.org/atomic v1.10.0
|
||||||
go.uber.org/zap v1.24.0
|
go.uber.org/zap v1.24.0
|
||||||
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f
|
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35
|
||||||
golang.org/x/crypto v0.6.0
|
golang.org/x/crypto v0.7.0
|
||||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb
|
golang.org/x/exp v0.0.0-20230304125523-9ff063c70017
|
||||||
golang.org/x/net v0.7.0
|
golang.org/x/net v0.8.0
|
||||||
golang.org/x/sys v0.5.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/grpc v1.53.0
|
||||||
google.golang.org/protobuf v1.28.1
|
google.golang.org/protobuf v1.28.1
|
||||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
|
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//replace github.com/sagernet/sing-tun => ../sing-tun
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/ajg/form v1.5.1 // indirect
|
github.com/ajg/form v1.5.1 // indirect
|
||||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||||
@@ -76,13 +81,13 @@ require (
|
|||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/u-root/uio v0.0.0-20230215032506-9aa6f7e2d72c // indirect
|
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||||
go.uber.org/multierr v1.6.0 // indirect
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
golang.org/x/mod v0.6.0 // indirect
|
golang.org/x/mod v0.8.0 // indirect
|
||||||
golang.org/x/text v0.7.0 // indirect
|
golang.org/x/text v0.8.0 // indirect
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // 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
|
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
|||||||
144
go.sum
144
go.sum
@@ -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/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 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
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 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||||
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
|
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/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 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
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.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
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 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
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 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
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/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 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
||||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20230220010740-598984875576 h1:ehee0+xI4xtJTRJhN+PVA2GzCLB6KRgHRoZMg2lHwJU=
|
github.com/insomniacslk/dhcp v0.0.0-20230301142404-3e45eea5edd7 h1:Fg8rHYs8luh8kCSAHDUIQCNMkn74Gvr1o5YPZdNRgY0=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20230220010740-598984875576/go.mod h1:yGKD3yZIGAjEZXiIjVQ0SQ09Y/RzETOoqEOi6nXqX0Y=
|
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.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 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
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 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
|
||||||
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
|
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=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
@@ -81,19 +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/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 h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
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 h1:IQ9CGHKOHokorxnffsqDvmmE30mDenO1lptYZ1AYkHY=
|
||||||
github.com/mholt/acmez v1.1.0/go.mod h1:zwo5+fbLLTowAX8o8ETfQzbDtwGEXnPhkmGdKIP+bgs=
|
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.51 h1:0+Xg7vObnhrz/4ZCZcZh7zPXlmU0aveS2HDBd0m0qSo=
|
||||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
github.com/miekg/dns v1.1.51/go.mod h1:2Z9d3CP1LQWihRZUf29mQ19yDThaI4DAYzte2CaQW5c=
|
||||||
github.com/nekohasekai/reality v0.0.0-20230225080858-d70c703b04cd h1:vd4qbG9ZTW10e1uqo8PDLshe5XL2yPhdINhGlJYaOoQ=
|
|
||||||
github.com/nekohasekai/reality v0.0.0-20230225080858-d70c703b04cd/go.mod h1:C+iqSNDBQ8qMhlNZ0JSUO9POEWq8qX87hukGfmO7/fA=
|
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
|
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
|
||||||
@@ -117,6 +95,10 @@ github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05
|
|||||||
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
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/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
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 h1:KyhtFFt1Jtp5vW2ohNvstvQffTOQ/s5vENuGXzdA+TM=
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
|
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
|
||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||||
@@ -127,32 +109,32 @@ github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6E
|
|||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY=
|
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY=
|
||||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32/go.mod h1:QMCkxXAC3CvBgDZVIJp43NWTuwGBScCzMLVLynjERL8=
|
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-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.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||||
github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b h1:Ji2AfGlc4j9AitobOx4k3BCj7eS5nSxL1cgaL81zvlo=
|
github.com/sagernet/sing v0.1.8-0.20230305055148-e16845727f76 h1:JCZ4tpEuT6U6oC87EaDBMsPg7+NxesIwESs4dzAGgJo=
|
||||||
github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
github.com/sagernet/sing v0.1.8-0.20230305055148-e16845727f76/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||||
github.com/sagernet/sing-dns v0.1.4 h1:7VxgeoSCiiazDSaXXQVcvrTBxFpOePPq/4XdgnUDN+0=
|
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-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 h1:qS39eA4C7x+zhEkySbASrtmb6ebdy5v0y2M6mgkmSO0=
|
||||||
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9/go.mod h1:f3mHTy5shnVM9l8UocMlJgC/1G/zdj5FuEuVXhDinGU=
|
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9/go.mod h1:f3mHTy5shnVM9l8UocMlJgC/1G/zdj5FuEuVXhDinGU=
|
||||||
github.com/sagernet/sing-shadowtls v0.0.0-20230221123345-78e50cd7b587 h1:OjIXlHT2bblZfp+ciupM4xY9+Ccpj9FsuHRtKRBv+Pg=
|
github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ=
|
||||||
github.com/sagernet/sing-shadowtls v0.0.0-20230221123345-78e50cd7b587/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc=
|
github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc=
|
||||||
github.com/sagernet/sing-tun v0.1.1 h1:2Hg3GAyJWzQ7Ua1j74dE+mI06vaqSBO9yD4tkTjggn4=
|
github.com/sagernet/sing-tun v0.1.2-0.20230226091124-0cdb0eed74d9 h1:tq1kc0HFj/jfhLfVC1NJI6lex2g6w2W+gVsFu6H2Kis=
|
||||||
github.com/sagernet/sing-tun v0.1.1/go.mod h1:WzW/SkT+Nh9uJn/FIYUE2YJHYuPwfbh8sATOzU9QDGw=
|
github.com/sagernet/sing-tun v0.1.2-0.20230226091124-0cdb0eed74d9/go.mod h1:KnRkwaDHbb06zgeNPu0LQ8A+vA9myMxKEgHN1brCPHg=
|
||||||
github.com/sagernet/sing-vmess v0.1.2 h1:RbOZNAId2LrCai8epMoQXlf0XTrou0bfcw08hNBg6lM=
|
github.com/sagernet/sing-vmess v0.1.3-0.20230303082804-627cc46ae68b h1:NZeF0ATeJwe4W3gTJNeIfTB6yBxai665q1HvDOaWmmU=
|
||||||
github.com/sagernet/sing-vmess v0.1.2/go.mod h1:9NSj8mZTx1JIY/HF9LaYRppUsVkysIN5tEFpNZujXxY=
|
github.com/sagernet/sing-vmess v0.1.3-0.20230303082804-627cc46ae68b/go.mod h1:9NSj8mZTx1JIY/HF9LaYRppUsVkysIN5tEFpNZujXxY=
|
||||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
|
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/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-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=
|
||||||
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/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 h1:m4MI13+NRKddIvbdSN0sFHK8w5ROTa60Zi9diZ7EE08=
|
||||||
github.com/sagernet/utls v0.0.0-20230225061716-536a007c8b01/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM=
|
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 h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
|
||||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
|
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 h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
|
||||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=
|
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 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
||||||
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
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=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
@@ -162,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/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.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.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.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.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.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/u-root/uio v0.0.0-20230215032506-9aa6f7e2d72c h1:PHoGTnweZP+KIg/8Zc6+iOesrIF5yHkpb4GBDxHm7yE=
|
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
|
||||||
github.com/u-root/uio v0.0.0-20230215032506-9aa6f7e2d72c/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
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 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
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.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 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
|
||||||
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
@@ -183,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/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 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
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-20230303233057-f1b76eb4bb35 h1:nJAwRlGWZZDOD+6wni9KVUNHMpHko/OnRwsrCYeAzPo=
|
||||||
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
|
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-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-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-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.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.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w=
|
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
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.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
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-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-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-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-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-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
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-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-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 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-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-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-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-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-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-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-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-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-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-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-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-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.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.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.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.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.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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/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.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
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 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
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-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.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.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
|
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
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-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-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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/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 h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w=
|
||||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
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=
|
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import (
|
|||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
|
"go.uber.org/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ adapter.Inbound = (*myInboundAdapter)(nil)
|
var _ adapter.Inbound = (*myInboundAdapter)(nil)
|
||||||
@@ -42,6 +44,8 @@ type myInboundAdapter struct {
|
|||||||
udpAddr M.Socksaddr
|
udpAddr M.Socksaddr
|
||||||
packetOutboundClosed chan struct{}
|
packetOutboundClosed chan struct{}
|
||||||
packetOutbound chan *myInboundPacket
|
packetOutbound chan *myInboundPacket
|
||||||
|
|
||||||
|
inShutdown atomic.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *myInboundAdapter) Type() string {
|
func (a *myInboundAdapter) Type() string {
|
||||||
@@ -97,6 +101,7 @@ func (a *myInboundAdapter) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *myInboundAdapter) Close() error {
|
func (a *myInboundAdapter) Close() error {
|
||||||
|
a.inShutdown.Store(true)
|
||||||
var err error
|
var err error
|
||||||
if a.clearSystemProxy != nil {
|
if a.clearSystemProxy != nil {
|
||||||
err = a.clearSystemProxy()
|
err = a.clearSystemProxy()
|
||||||
|
|||||||
@@ -39,10 +39,17 @@ func (a *myInboundAdapter) loopTCPIn() {
|
|||||||
for {
|
for {
|
||||||
conn, err := tcpListener.Accept()
|
conn, err := tcpListener.Accept()
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
a.logger.Error("accept: ", err)
|
a.tcpListener.Close()
|
||||||
|
a.logger.Error("serve error: ", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
go a.injectTCP(conn, adapter.InboundContext{})
|
go a.injectTCP(conn, adapter.InboundContext{})
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ type Tun struct {
|
|||||||
tunIf tun.Tun
|
tunIf tun.Tun
|
||||||
tunStack tun.Stack
|
tunStack tun.Stack
|
||||||
platformInterface platform.Interface
|
platformInterface platform.Interface
|
||||||
|
platformOptions option.TunPlatformOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions, platformInterface platform.Interface) (*Tun, error) {
|
func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions, platformInterface platform.Interface) (*Tun, error) {
|
||||||
@@ -96,6 +97,7 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
|||||||
udpTimeout: udpTimeout,
|
udpTimeout: udpTimeout,
|
||||||
stack: options.Stack,
|
stack: options.Stack,
|
||||||
platformInterface: platformInterface,
|
platformInterface: platformInterface,
|
||||||
|
platformOptions: common.PtrValueOrDefault(options.Platform),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,9 +150,9 @@ func (t *Tun) Start() error {
|
|||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if t.platformInterface != nil {
|
if t.platformInterface != nil {
|
||||||
tunInterface, err = t.platformInterface.OpenTun(t.tunOptions)
|
tunInterface, err = t.platformInterface.OpenTun(t.tunOptions, t.platformOptions)
|
||||||
} else {
|
} else {
|
||||||
tunInterface, err = tun.Open(t.tunOptions)
|
tunInterface, err = tun.New(t.tunOptions)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "configure tun interface")
|
return E.Cause(err, "configure tun interface")
|
||||||
|
|||||||
@@ -50,11 +50,13 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
|||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
users: options.Users,
|
users: options.Users,
|
||||||
}
|
}
|
||||||
service := vless.NewService[int](adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
|
service := vless.NewService[int](logger, adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||||
service.UpdateUsers(common.MapIndexed(inbound.users, func(index int, _ option.VLESSUser) int {
|
service.UpdateUsers(common.MapIndexed(inbound.users, func(index int, _ option.VLESSUser) int {
|
||||||
return index
|
return index
|
||||||
}), common.Map(inbound.users, func(it option.VLESSUser) string {
|
}), common.Map(inbound.users, func(it option.VLESSUser) string {
|
||||||
return it.UUID
|
return it.UUID
|
||||||
|
}), common.Map(inbound.users, func(it option.VLESSUser) string {
|
||||||
|
return it.Flow
|
||||||
}))
|
}))
|
||||||
inbound.service = service
|
inbound.service = service
|
||||||
var err error
|
var err error
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ func NewFactory(formatter Formatter, writer io.Writer, platformWriter io.Writer)
|
|||||||
formatter: formatter,
|
formatter: formatter,
|
||||||
platformFormatter: Formatter{
|
platformFormatter: Formatter{
|
||||||
BaseTime: formatter.BaseTime,
|
BaseTime: formatter.BaseTime,
|
||||||
DisableColors: C.IsIos,
|
DisableColors: C.IsDarwin || C.IsIos,
|
||||||
},
|
},
|
||||||
writer: writer,
|
writer: writer,
|
||||||
platformWriter: platformWriter,
|
platformWriter: platformWriter,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
"github.com/sagernet/sing/common/observable"
|
"github.com/sagernet/sing/common/observable"
|
||||||
@@ -27,7 +28,8 @@ func NewObservableFactory(formatter Formatter, writer io.Writer, platformWriter
|
|||||||
factory := &observableFactory{
|
factory := &observableFactory{
|
||||||
formatter: formatter,
|
formatter: formatter,
|
||||||
platformFormatter: Formatter{
|
platformFormatter: Formatter{
|
||||||
BaseTime: formatter.BaseTime,
|
BaseTime: formatter.BaseTime,
|
||||||
|
DisableColors: C.IsDarwin || C.IsIos,
|
||||||
},
|
},
|
||||||
writer: writer,
|
writer: writer,
|
||||||
platformWriter: platformWriter,
|
platformWriter: platformWriter,
|
||||||
@@ -91,7 +93,7 @@ func (l *observableLogger) Log(ctx context.Context, level Level, args []any) {
|
|||||||
}
|
}
|
||||||
l.subscriber.Emit(Entry{level, messageSimple})
|
l.subscriber.Emit(Entry{level, messageSimple})
|
||||||
if l.platformWriter != nil {
|
if l.platformWriter != nil {
|
||||||
l.platformWriter.Write([]byte(l.formatter.Format(ctx, level, l.tag, F.ToString(args...), nowTime)))
|
l.platformWriter.Write([]byte(l.platformFormatter.Format(ctx, level, l.tag, F.ToString(args...), nowTime)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
mkdocs.yml
13
mkdocs.yml
@@ -8,8 +8,8 @@ remote_branch: docs
|
|||||||
edit_uri: ""
|
edit_uri: ""
|
||||||
theme:
|
theme:
|
||||||
name: material
|
name: material
|
||||||
icon:
|
logo: assets/icon.svg
|
||||||
logo: material/tools
|
favicon: assets/icon.svg
|
||||||
palette:
|
palette:
|
||||||
- scheme: default
|
- scheme: default
|
||||||
primary: white
|
primary: white
|
||||||
@@ -35,6 +35,11 @@ nav:
|
|||||||
- Features: features.md
|
- Features: features.md
|
||||||
- Support: support.md
|
- Support: support.md
|
||||||
- Change Log: changelog.md
|
- Change Log: changelog.md
|
||||||
|
- Installation:
|
||||||
|
- From source: installation/from-source.md
|
||||||
|
- Clients:
|
||||||
|
- SFI:
|
||||||
|
- installation/clients/sfi/index.md
|
||||||
- Configuration:
|
- Configuration:
|
||||||
- configuration/index.md
|
- configuration/index.md
|
||||||
- Log:
|
- Log:
|
||||||
@@ -153,6 +158,10 @@ plugins:
|
|||||||
Support: 支持
|
Support: 支持
|
||||||
Change Log: 更新日志
|
Change Log: 更新日志
|
||||||
|
|
||||||
|
Installation: 安装
|
||||||
|
From source: 从源代码
|
||||||
|
Clients: 客户端
|
||||||
|
|
||||||
Configuration: 配置
|
Configuration: 配置
|
||||||
Log: 日志
|
Log: 日志
|
||||||
DNS Server: DNS 服务器
|
DNS Server: DNS 服务器
|
||||||
|
|||||||
@@ -5,19 +5,17 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/json"
|
"github.com/sagernet/sing-box/common/json"
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
)
|
)
|
||||||
|
|
||||||
type _Options struct {
|
type _Options struct {
|
||||||
Log *LogOptions `json:"log,omitempty"`
|
Log *LogOptions `json:"log,omitempty"`
|
||||||
DNS *DNSOptions `json:"dns,omitempty"`
|
DNS *DNSOptions `json:"dns,omitempty"`
|
||||||
NTP *NTPOptions `json:"ntp,omitempty"`
|
NTP *NTPOptions `json:"ntp,omitempty"`
|
||||||
Inbounds []Inbound `json:"inbounds,omitempty"`
|
Inbounds []Inbound `json:"inbounds,omitempty"`
|
||||||
Outbounds []Outbound `json:"outbounds,omitempty"`
|
Outbounds []Outbound `json:"outbounds,omitempty"`
|
||||||
Route *RouteOptions `json:"route,omitempty"`
|
Route *RouteOptions `json:"route,omitempty"`
|
||||||
Experimental *ExperimentalOptions `json:"experimental,omitempty"`
|
Experimental *ExperimentalOptions `json:"experimental,omitempty"`
|
||||||
PlatformInterface platform.Interface `json:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Options _Options
|
type Options _Options
|
||||||
|
|||||||
104
option/platform.go
Normal file
104
option/platform.go
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
package option
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sagernet/sing-box/common/json"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OnDemandOptions struct {
|
||||||
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
|
Rules []OnDemandRule `json:"rules,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OnDemandRule struct {
|
||||||
|
Action *OnDemandRuleAction `json:"action,omitempty"`
|
||||||
|
DNSSearchDomainMatch Listable[string] `json:"dns_search_domain_match,omitempty"`
|
||||||
|
DNSServerAddressMatch Listable[string] `json:"dns_server_address_match,omitempty"`
|
||||||
|
InterfaceTypeMatch *OnDemandRuleInterfaceType `json:"interface_type_match,omitempty"`
|
||||||
|
SSIDMatch Listable[string] `json:"ssid_match,omitempty"`
|
||||||
|
ProbeURL string `json:"probe_url,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OnDemandRuleAction int
|
||||||
|
|
||||||
|
func (r *OnDemandRuleAction) MarshalJSON() ([]byte, error) {
|
||||||
|
if r == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
value := *r
|
||||||
|
var actionName string
|
||||||
|
switch value {
|
||||||
|
case 1:
|
||||||
|
actionName = "connect"
|
||||||
|
case 2:
|
||||||
|
actionName = "disconnect"
|
||||||
|
case 3:
|
||||||
|
actionName = "evaluate_connection"
|
||||||
|
default:
|
||||||
|
return nil, E.New("unknown action: ", value)
|
||||||
|
}
|
||||||
|
return json.Marshal(actionName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *OnDemandRuleAction) UnmarshalJSON(bytes []byte) error {
|
||||||
|
var actionName string
|
||||||
|
if err := json.Unmarshal(bytes, &actionName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var actionValue int
|
||||||
|
switch actionName {
|
||||||
|
case "connect":
|
||||||
|
actionValue = 1
|
||||||
|
case "disconnect":
|
||||||
|
actionValue = 2
|
||||||
|
case "evaluate_connection":
|
||||||
|
actionValue = 3
|
||||||
|
case "ignore":
|
||||||
|
actionValue = 4
|
||||||
|
default:
|
||||||
|
return E.New("unknown action name: ", actionName)
|
||||||
|
}
|
||||||
|
*r = OnDemandRuleAction(actionValue)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type OnDemandRuleInterfaceType int
|
||||||
|
|
||||||
|
func (r *OnDemandRuleInterfaceType) MarshalJSON() ([]byte, error) {
|
||||||
|
if r == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
value := *r
|
||||||
|
var interfaceTypeName string
|
||||||
|
switch value {
|
||||||
|
case 1:
|
||||||
|
interfaceTypeName = "any"
|
||||||
|
case 2:
|
||||||
|
interfaceTypeName = "wifi"
|
||||||
|
case 3:
|
||||||
|
interfaceTypeName = "cellular"
|
||||||
|
default:
|
||||||
|
return nil, E.New("unknown interface type: ", value)
|
||||||
|
}
|
||||||
|
return json.Marshal(interfaceTypeName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *OnDemandRuleInterfaceType) UnmarshalJSON(bytes []byte) error {
|
||||||
|
var interfaceTypeName string
|
||||||
|
if err := json.Unmarshal(bytes, &interfaceTypeName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var interfaceTypeValue int
|
||||||
|
switch interfaceTypeName {
|
||||||
|
case "any":
|
||||||
|
interfaceTypeValue = 1
|
||||||
|
case "wifi":
|
||||||
|
interfaceTypeValue = 2
|
||||||
|
case "cellular":
|
||||||
|
interfaceTypeValue = 3
|
||||||
|
default:
|
||||||
|
return E.New("unknown interface type name: ", interfaceTypeName)
|
||||||
|
}
|
||||||
|
*r = OnDemandRuleInterfaceType(interfaceTypeValue)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -19,5 +19,6 @@ type TunInboundOptions struct {
|
|||||||
EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"`
|
EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"`
|
||||||
UDPTimeout int64 `json:"udp_timeout,omitempty"`
|
UDPTimeout int64 `json:"udp_timeout,omitempty"`
|
||||||
Stack string `json:"stack,omitempty"`
|
Stack string `json:"stack,omitempty"`
|
||||||
|
Platform *TunPlatformOptions `json:"platform,omitempty"`
|
||||||
InboundOptions
|
InboundOptions
|
||||||
}
|
}
|
||||||
|
|||||||
10
option/tun_platform.go
Normal file
10
option/tun_platform.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package option
|
||||||
|
|
||||||
|
type TunPlatformOptions struct {
|
||||||
|
HTTPProxy *HTTPProxyOptions `json:"http_proxy,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPProxyOptions struct {
|
||||||
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
|
ServerOptions
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ type VLESSInboundOptions struct {
|
|||||||
type VLESSUser struct {
|
type VLESSUser struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
UUID string `json:"uuid"`
|
UUID string `json:"uuid"`
|
||||||
|
Flow string `json:"flow,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type VLESSOutboundOptions struct {
|
type VLESSOutboundOptions struct {
|
||||||
@@ -20,5 +21,5 @@ type VLESSOutboundOptions struct {
|
|||||||
Network NetworkList `json:"network,omitempty"`
|
Network NetworkList `json:"network,omitempty"`
|
||||||
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
||||||
Transport *V2RayTransportOptions `json:"transport,omitempty"`
|
Transport *V2RayTransportOptions `json:"transport,omitempty"`
|
||||||
PacketEncoding string `json:"packet_encoding,omitempty"`
|
PacketEncoding *string `json:"packet_encoding,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,30 +39,6 @@ func (a *myOutboundAdapter) Network() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext) error {
|
func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
ctx = adapter.WithContext(ctx, &metadata)
|
|
||||||
var outConn net.Conn
|
|
||||||
var err error
|
|
||||||
if len(metadata.DestinationAddresses) > 0 {
|
|
||||||
outConn, err = N.DialSerial(ctx, this, N.NetworkTCP, metadata.Destination, metadata.DestinationAddresses)
|
|
||||||
} else {
|
|
||||||
outConn, err = this.DialContext(ctx, N.NetworkTCP, metadata.Destination)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return N.HandshakeFailure(conn, err)
|
|
||||||
}
|
|
||||||
if cachedReader, isCached := conn.(N.CachedReader); isCached {
|
|
||||||
payload := cachedReader.ReadCached()
|
|
||||||
if payload != nil && !payload.IsEmpty() {
|
|
||||||
_, err = outConn.Write(payload.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bufio.CopyConn(ctx, conn, outConn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewEarlyConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext) error {
|
|
||||||
ctx = adapter.WithContext(ctx, &metadata)
|
ctx = adapter.WithContext(ctx, &metadata)
|
||||||
var outConn net.Conn
|
var outConn net.Conn
|
||||||
var err error
|
var err error
|
||||||
@@ -111,28 +87,30 @@ func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) erro
|
|||||||
return bufio.CopyConn(ctx, conn, serverConn)
|
return bufio.CopyConn(ctx, conn, serverConn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_payload := buf.StackNew()
|
if earlyConn, isEarlyConn := common.Cast[N.EarlyConn](serverConn); isEarlyConn && earlyConn.NeedHandshake() {
|
||||||
payload := common.Dup(_payload)
|
_payload := buf.StackNew()
|
||||||
err := conn.SetReadDeadline(time.Now().Add(C.ReadPayloadTimeout))
|
payload := common.Dup(_payload)
|
||||||
if err != os.ErrInvalid {
|
err := conn.SetReadDeadline(time.Now().Add(C.ReadPayloadTimeout))
|
||||||
|
if err != os.ErrInvalid {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = payload.ReadOnceFrom(conn)
|
||||||
|
if err != nil && !E.IsTimeout(err) {
|
||||||
|
return E.Cause(err, "read payload")
|
||||||
|
}
|
||||||
|
err = conn.SetReadDeadline(time.Time{})
|
||||||
|
if err != nil {
|
||||||
|
payload.Release()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = serverConn.Write(payload.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return N.HandshakeFailure(conn, err)
|
||||||
}
|
|
||||||
_, err = payload.ReadOnceFrom(conn)
|
|
||||||
if err != nil && !E.IsTimeout(err) {
|
|
||||||
return E.Cause(err, "read payload")
|
|
||||||
}
|
|
||||||
err = conn.SetReadDeadline(time.Time{})
|
|
||||||
if err != nil {
|
|
||||||
payload.Release()
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
runtime.KeepAlive(_payload)
|
||||||
|
payload.Release()
|
||||||
}
|
}
|
||||||
_, err = serverConn.Write(payload.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
return N.HandshakeFailure(conn, err)
|
|
||||||
}
|
|
||||||
runtime.KeepAlive(_payload)
|
|
||||||
payload.Release()
|
|
||||||
return bufio.CopyConn(ctx, conn, serverConn)
|
return bufio.CopyConn(ctx, conn, serverConn)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ func (h *Shadowsocks) ListenPacket(ctx context.Context, destination M.Socksaddr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Shadowsocks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *Shadowsocks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
return NewEarlyConnection(ctx, h, conn, metadata)
|
return NewConnection(ctx, h, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Shadowsocks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
func (h *Shadowsocks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
|
|||||||
@@ -109,11 +109,13 @@ func (h *ShadowsocksR) DialContext(ctx context.Context, network string, destinat
|
|||||||
conn = h.cipher.StreamConn(h.obfs.StreamConn(conn))
|
conn = h.cipher.StreamConn(h.obfs.StreamConn(conn))
|
||||||
writeIv, err := conn.(*shadowstream.Conn).ObtainWriteIV()
|
writeIv, err := conn.(*shadowstream.Conn).ObtainWriteIV()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
conn = h.protocol.StreamConn(conn, writeIv)
|
conn = h.protocol.StreamConn(conn, writeIv)
|
||||||
err = M.SocksaddrSerializer.WriteAddrPort(conn, destination)
|
err = M.SocksaddrSerializer.WriteAddrPort(conn, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
return nil, E.Cause(err, "write request")
|
return nil, E.Cause(err, "write request")
|
||||||
}
|
}
|
||||||
return conn, nil
|
return conn, nil
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
|
|||||||
return common.Error(tls.ClientHandshake(ctx, conn, tlsConfig))
|
return common.Error(tls.ClientHandshake(ctx, conn, tlsConfig))
|
||||||
}
|
}
|
||||||
case 3:
|
case 3:
|
||||||
if idConfig, loaded := tlsConfig.(tls.ConfigWithSessionIDGenerator); loaded {
|
if idConfig, loaded := tlsConfig.(tls.WithSessionIDGenerator); loaded {
|
||||||
tlsHandshakeFunc = func(ctx context.Context, conn net.Conn, sessionIDGenerator shadowtls.TLSSessionIDGeneratorFunc) error {
|
tlsHandshakeFunc = func(ctx context.Context, conn net.Conn, sessionIDGenerator shadowtls.TLSSessionIDGeneratorFunc) error {
|
||||||
idConfig.SetSessionIDGenerator(sessionIDGenerator)
|
idConfig.SetSessionIDGenerator(sessionIDGenerator)
|
||||||
return common.Error(tls.ClientHandshake(ctx, conn, tlsConfig))
|
return common.Error(tls.ClientHandshake(ctx, conn, tlsConfig))
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ func (h *Trojan) ListenPacket(ctx context.Context, destination M.Socksaddr) (net
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Trojan) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *Trojan) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
return NewEarlyConnection(ctx, h, conn, metadata)
|
return NewConnection(ctx, h, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Trojan) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
func (h *Trojan) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
@@ -124,6 +124,7 @@ func (h *trojanDialer) DialContext(ctx context.Context, network string, destinat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
common.Close(conn)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
switch N.NetworkName(network) {
|
switch N.NetworkName(network) {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/sagernet/sing-dns"
|
"github.com/sagernet/sing-dns"
|
||||||
"github.com/sagernet/sing-vmess/packetaddr"
|
"github.com/sagernet/sing-vmess/packetaddr"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
|
"github.com/sagernet/sing/common/bufio"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
@@ -58,16 +59,20 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
|||||||
return nil, E.Cause(err, "create client transport: ", options.Transport.Type)
|
return nil, E.Cause(err, "create client transport: ", options.Transport.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch options.PacketEncoding {
|
if options.PacketEncoding == nil {
|
||||||
case "":
|
|
||||||
case "packetaddr":
|
|
||||||
outbound.packetAddr = true
|
|
||||||
case "xudp":
|
|
||||||
outbound.xudp = true
|
outbound.xudp = true
|
||||||
default:
|
} else {
|
||||||
return nil, E.New("unknown packet encoding: ", options.PacketEncoding)
|
switch *options.PacketEncoding {
|
||||||
|
case "":
|
||||||
|
case "packetaddr":
|
||||||
|
outbound.packetAddr = true
|
||||||
|
case "xudp":
|
||||||
|
outbound.xudp = true
|
||||||
|
default:
|
||||||
|
return nil, E.New("unknown packet encoding: ", options.PacketEncoding)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
outbound.client, err = vless.NewClient(options.UUID, options.Flow)
|
outbound.client, err = vless.NewClient(options.UUID, options.Flow, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -97,7 +102,17 @@ func (h *VLESS) DialContext(ctx context.Context, network string, destination M.S
|
|||||||
return h.client.DialEarlyConn(conn, destination)
|
return h.client.DialEarlyConn(conn, destination)
|
||||||
case N.NetworkUDP:
|
case N.NetworkUDP:
|
||||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||||
return h.client.DialEarlyPacketConn(conn, destination)
|
if h.xudp {
|
||||||
|
return h.client.DialEarlyXUDPPacketConn(conn, destination)
|
||||||
|
} else if h.packetAddr {
|
||||||
|
packetConn, err := h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &bufio.BindPacketConn{PacketConn: dialer.NewResolvePacketConn(ctx, h.router, dns.DomainStrategyAsIS, packetaddr.NewConn(packetConn, destination)), Addr: destination}, nil
|
||||||
|
} else {
|
||||||
|
return h.client.DialEarlyPacketConn(conn, destination)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return nil, E.Extend(N.ErrUnknownNetwork, network)
|
return nil, E.Extend(N.ErrUnknownNetwork, network)
|
||||||
}
|
}
|
||||||
@@ -119,6 +134,7 @@ func (h *VLESS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
common.Close(conn)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if h.xudp {
|
if h.xudp {
|
||||||
@@ -135,7 +151,7 @@ func (h *VLESS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
return NewEarlyConnection(ctx, h, conn, metadata)
|
return NewConnection(ctx, h, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *VLESS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
func (h *VLESS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ func (h *VMess) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *VMess) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *VMess) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
return NewEarlyConnection(ctx, h, conn, metadata)
|
return NewConnection(ctx, h, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *VMess) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
func (h *VMess) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
@@ -157,6 +157,7 @@ func (h *vmessDialer) DialContext(ctx context.Context, network string, destinati
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
common.Close(conn)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
switch N.NetworkName(network) {
|
switch N.NetworkName(network) {
|
||||||
|
|||||||
@@ -579,7 +579,7 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
|||||||
return vmess.HandleMuxConnection(ctx, conn, adapter.NewUpstreamHandler(metadata, r.RouteConnection, r.RoutePacketConnection, r))
|
return vmess.HandleMuxConnection(ctx, conn, adapter.NewUpstreamHandler(metadata, r.RouteConnection, r.RoutePacketConnection, r))
|
||||||
case uot.UOTMagicAddress:
|
case uot.UOTMagicAddress:
|
||||||
r.logger.InfoContext(ctx, "inbound UoT connection")
|
r.logger.InfoContext(ctx, "inbound UoT connection")
|
||||||
metadata.Destination = M.Socksaddr{}
|
metadata.Destination = M.Socksaddr{Addr: netip.IPv4Unspecified()}
|
||||||
return r.RoutePacketConnection(ctx, uot.NewClientConn(conn), metadata)
|
return r.RoutePacketConnection(ctx, uot.NewClientConn(conn), metadata)
|
||||||
}
|
}
|
||||||
if metadata.InboundOptions.SniffEnabled {
|
if metadata.InboundOptions.SniffEnabled {
|
||||||
@@ -898,10 +898,9 @@ func (r *Router) prepareGeoIPDatabase() error {
|
|||||||
geoPath = "geoip.db"
|
geoPath = "geoip.db"
|
||||||
if foundPath, loaded := C.FindPath(geoPath); loaded {
|
if foundPath, loaded := C.FindPath(geoPath); loaded {
|
||||||
geoPath = foundPath
|
geoPath = foundPath
|
||||||
} else {
|
|
||||||
geoPath = C.BasePath(geoPath)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
geoPath = C.BasePath(geoPath)
|
||||||
if !rw.FileExists(geoPath) {
|
if !rw.FileExists(geoPath) {
|
||||||
r.logger.Warn("geoip database not exists: ", geoPath)
|
r.logger.Warn("geoip database not exists: ", geoPath)
|
||||||
var err error
|
var err error
|
||||||
@@ -935,10 +934,9 @@ func (r *Router) prepareGeositeDatabase() error {
|
|||||||
geoPath = "geosite.db"
|
geoPath = "geosite.db"
|
||||||
if foundPath, loaded := C.FindPath(geoPath); loaded {
|
if foundPath, loaded := C.FindPath(geoPath); loaded {
|
||||||
geoPath = foundPath
|
geoPath = foundPath
|
||||||
} else {
|
|
||||||
geoPath = C.BasePath(geoPath)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
geoPath = C.BasePath(geoPath)
|
||||||
if !rw.FileExists(geoPath) {
|
if !rw.FileExists(geoPath) {
|
||||||
r.logger.Warn("geosite database not exists: ", geoPath)
|
r.logger.Warn("geosite database not exists: ", geoPath)
|
||||||
var err error
|
var err error
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ func startInstance(t *testing.T, options option.Options) *box.Box {
|
|||||||
var instance *box.Box
|
var instance *box.Box
|
||||||
var err error
|
var err error
|
||||||
for retry := 0; retry < 3; retry++ {
|
for retry := 0; retry < 3; retry++ {
|
||||||
instance, err = box.New(ctx, options)
|
instance, err = box.New(ctx, options, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
err = instance.Start()
|
err = instance.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
51
test/config/vless-tls-client.json
Normal file
51
test/config/vless-tls-client.json
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"log": {
|
||||||
|
"loglevel": "debug"
|
||||||
|
},
|
||||||
|
"inbounds": [
|
||||||
|
{
|
||||||
|
"listen": "0.0.0.0",
|
||||||
|
"port": "1080",
|
||||||
|
"protocol": "socks",
|
||||||
|
"settings": {
|
||||||
|
"auth": "noauth",
|
||||||
|
"udp": true,
|
||||||
|
"ip": "127.0.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outbounds": [
|
||||||
|
{
|
||||||
|
"protocol": "vless",
|
||||||
|
"settings": {
|
||||||
|
"vnext": [
|
||||||
|
{
|
||||||
|
"address": "host.docker.internal",
|
||||||
|
"port": 1234,
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"id": "",
|
||||||
|
"encryption": "none",
|
||||||
|
"flow": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"streamSettings": {
|
||||||
|
"network": "tcp",
|
||||||
|
"security": "tls",
|
||||||
|
"tlsSettings": {
|
||||||
|
"serverName": "example.org",
|
||||||
|
"certificates": [
|
||||||
|
{
|
||||||
|
"certificateFile": "/path/to/certificate.crt",
|
||||||
|
"keyFile": "/path/to/private.key"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fingerprint": "chrome"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
16
test/go.mod
16
test/go.mod
@@ -10,7 +10,7 @@ require (
|
|||||||
github.com/docker/docker v20.10.18+incompatible
|
github.com/docker/docker v20.10.18+incompatible
|
||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible
|
github.com/gofrs/uuid v4.4.0+incompatible
|
||||||
github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b
|
github.com/sagernet/sing v0.1.8-0.20230305055148-e16845727f76
|
||||||
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9
|
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9
|
||||||
github.com/spyzhov/ajson v0.7.1
|
github.com/spyzhov/ajson v0.7.1
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.1
|
||||||
@@ -18,8 +18,6 @@ require (
|
|||||||
golang.org/x/net v0.7.0
|
golang.org/x/net v0.7.0
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/xtls/reality => github.com/nekohasekai/reality v0.0.0-20230225043811-04070a6bdbea
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
berty.tech/go-libtor v1.0.385 // indirect
|
berty.tech/go-libtor v1.0.385 // indirect
|
||||||
github.com/Dreamacro/clash v1.13.0 // indirect
|
github.com/Dreamacro/clash v1.13.0 // indirect
|
||||||
@@ -53,7 +51,6 @@ require (
|
|||||||
github.com/miekg/dns v1.1.50 // indirect
|
github.com/miekg/dns v1.1.50 // indirect
|
||||||
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c // indirect
|
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c // indirect
|
||||||
github.com/morikuni/aec v1.0.0 // indirect
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
github.com/nekohasekai/reality v0.0.0-20230225080858-d70c703b04cd // indirect
|
|
||||||
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
|
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||||
@@ -66,16 +63,19 @@ require (
|
|||||||
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
|
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
|
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
|
||||||
|
github.com/sagernet/badhttp v0.0.0-20230228035330-e77eb9a689fd // indirect
|
||||||
|
github.com/sagernet/badhttp2 v0.0.0-20230228040529-408b0b8e774d // indirect
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 // indirect
|
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 // indirect
|
||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
||||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 // indirect
|
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 // indirect
|
||||||
|
github.com/sagernet/reality v0.0.0-20230228045158-d3e085a8e5d1 // indirect
|
||||||
github.com/sagernet/sing-dns v0.1.4 // indirect
|
github.com/sagernet/sing-dns v0.1.4 // indirect
|
||||||
github.com/sagernet/sing-shadowtls v0.0.0-20230221123345-78e50cd7b587 // indirect
|
github.com/sagernet/sing-shadowtls v0.1.0 // indirect
|
||||||
github.com/sagernet/sing-tun v0.1.1 // indirect
|
github.com/sagernet/sing-tun v0.1.2-0.20230226091124-0cdb0eed74d9 // indirect
|
||||||
github.com/sagernet/sing-vmess v0.1.2 // indirect
|
github.com/sagernet/sing-vmess v0.1.3-0.20230303082804-627cc46ae68b // indirect
|
||||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 // indirect
|
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 // indirect
|
||||||
github.com/sagernet/tfo-go v0.0.0-20230207095944-549363a7327d // indirect
|
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 // indirect
|
||||||
github.com/sagernet/utls v0.0.0-20230225061716-536a007c8b01 // indirect
|
github.com/sagernet/utls v0.0.0-20230225061716-536a007c8b01 // indirect
|
||||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect
|
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect
|
||||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c // indirect
|
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c // indirect
|
||||||
|
|||||||
28
test/go.sum
28
test/go.sum
@@ -104,8 +104,6 @@ github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c h1:RC8WMpjonrBfyAh6VN/PO
|
|||||||
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c/go.mod h1:9OcmHNQQUTbk4XCffrLgN1NEKc2mh5u++biHVrvHsSU=
|
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c/go.mod h1:9OcmHNQQUTbk4XCffrLgN1NEKc2mh5u++biHVrvHsSU=
|
||||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/nekohasekai/reality v0.0.0-20230225080858-d70c703b04cd h1:vd4qbG9ZTW10e1uqo8PDLshe5XL2yPhdINhGlJYaOoQ=
|
|
||||||
github.com/nekohasekai/reality v0.0.0-20230225080858-d70c703b04cd/go.mod h1:C+iqSNDBQ8qMhlNZ0JSUO9POEWq8qX87hukGfmO7/fA=
|
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
|
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
|
||||||
github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
|
github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
|
||||||
@@ -132,6 +130,10 @@ 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-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 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
||||||
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||||
|
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 h1:KyhtFFt1Jtp5vW2ohNvstvQffTOQ/s5vENuGXzdA+TM=
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
|
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
|
||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||||
@@ -140,24 +142,26 @@ github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6E
|
|||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY=
|
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY=
|
||||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32/go.mod h1:QMCkxXAC3CvBgDZVIJp43NWTuwGBScCzMLVLynjERL8=
|
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-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.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||||
github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b h1:Ji2AfGlc4j9AitobOx4k3BCj7eS5nSxL1cgaL81zvlo=
|
github.com/sagernet/sing v0.1.8-0.20230305055148-e16845727f76 h1:JCZ4tpEuT6U6oC87EaDBMsPg7+NxesIwESs4dzAGgJo=
|
||||||
github.com/sagernet/sing v0.1.8-0.20230221060643-3401d210384b/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
github.com/sagernet/sing v0.1.8-0.20230305055148-e16845727f76/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||||
github.com/sagernet/sing-dns v0.1.4 h1:7VxgeoSCiiazDSaXXQVcvrTBxFpOePPq/4XdgnUDN+0=
|
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-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 h1:qS39eA4C7x+zhEkySbASrtmb6ebdy5v0y2M6mgkmSO0=
|
||||||
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9/go.mod h1:f3mHTy5shnVM9l8UocMlJgC/1G/zdj5FuEuVXhDinGU=
|
github.com/sagernet/sing-shadowsocks v0.1.2-0.20230221080503-769c01d6bba9/go.mod h1:f3mHTy5shnVM9l8UocMlJgC/1G/zdj5FuEuVXhDinGU=
|
||||||
github.com/sagernet/sing-shadowtls v0.0.0-20230221123345-78e50cd7b587 h1:OjIXlHT2bblZfp+ciupM4xY9+Ccpj9FsuHRtKRBv+Pg=
|
github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ=
|
||||||
github.com/sagernet/sing-shadowtls v0.0.0-20230221123345-78e50cd7b587/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc=
|
github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc=
|
||||||
github.com/sagernet/sing-tun v0.1.1 h1:2Hg3GAyJWzQ7Ua1j74dE+mI06vaqSBO9yD4tkTjggn4=
|
github.com/sagernet/sing-tun v0.1.2-0.20230226091124-0cdb0eed74d9 h1:tq1kc0HFj/jfhLfVC1NJI6lex2g6w2W+gVsFu6H2Kis=
|
||||||
github.com/sagernet/sing-tun v0.1.1/go.mod h1:WzW/SkT+Nh9uJn/FIYUE2YJHYuPwfbh8sATOzU9QDGw=
|
github.com/sagernet/sing-tun v0.1.2-0.20230226091124-0cdb0eed74d9/go.mod h1:KnRkwaDHbb06zgeNPu0LQ8A+vA9myMxKEgHN1brCPHg=
|
||||||
github.com/sagernet/sing-vmess v0.1.2 h1:RbOZNAId2LrCai8epMoQXlf0XTrou0bfcw08hNBg6lM=
|
github.com/sagernet/sing-vmess v0.1.3-0.20230303082804-627cc46ae68b h1:NZeF0ATeJwe4W3gTJNeIfTB6yBxai665q1HvDOaWmmU=
|
||||||
github.com/sagernet/sing-vmess v0.1.2/go.mod h1:9NSj8mZTx1JIY/HF9LaYRppUsVkysIN5tEFpNZujXxY=
|
github.com/sagernet/sing-vmess v0.1.3-0.20230303082804-627cc46ae68b/go.mod h1:9NSj8mZTx1JIY/HF9LaYRppUsVkysIN5tEFpNZujXxY=
|
||||||
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
|
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/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-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=
|
||||||
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/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 h1:m4MI13+NRKddIvbdSN0sFHK8w5ROTa60Zi9diZ7EE08=
|
||||||
github.com/sagernet/utls v0.0.0-20230225061716-536a007c8b01/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM=
|
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 h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
|
||||||
|
|||||||
292
test/reality_test.go
Normal file
292
test/reality_test.go
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing-box/transport/vless"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVLESSVisionReality(t *testing.T) {
|
||||||
|
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||||
|
|
||||||
|
userUUID := newUUID()
|
||||||
|
startInstance(t, option.Options{
|
||||||
|
Inbounds: []option.Inbound{
|
||||||
|
{
|
||||||
|
Type: C.TypeMixed,
|
||||||
|
Tag: "mixed-in",
|
||||||
|
MixedOptions: option.HTTPMixedInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||||
|
ListenPort: clientPort,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.TypeVLESS,
|
||||||
|
VLESSOptions: option.VLESSInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
Users: []option.VLESSUser{
|
||||||
|
{
|
||||||
|
Name: "sekai",
|
||||||
|
UUID: userUUID.String(),
|
||||||
|
Flow: vless.FlowVision,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TLS: &option.InboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ServerName: "google.com",
|
||||||
|
Reality: &option.InboundRealityOptions{
|
||||||
|
Enabled: true,
|
||||||
|
Handshake: option.InboundRealityHandshakeOptions{
|
||||||
|
ServerOptions: option.ServerOptions{
|
||||||
|
Server: "google.com",
|
||||||
|
ServerPort: 443,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ShortID: []string{"0123456789abcdef"},
|
||||||
|
PrivateKey: "UuMBgl7MXTPx9inmQp2UC7Jcnwc6XYbwDNebonM-FCc",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.TypeTrojan,
|
||||||
|
Tag: "trojan",
|
||||||
|
TrojanOptions: option.TrojanInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||||
|
ListenPort: otherPort,
|
||||||
|
},
|
||||||
|
Users: []option.TrojanUser{
|
||||||
|
{
|
||||||
|
Name: "sekai",
|
||||||
|
Password: userUUID.String(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TLS: &option.InboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ServerName: "example.org",
|
||||||
|
CertificatePath: certPem,
|
||||||
|
KeyPath: keyPem,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbounds: []option.Outbound{
|
||||||
|
{
|
||||||
|
Type: C.TypeDirect,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.TypeTrojan,
|
||||||
|
Tag: "trojan-out",
|
||||||
|
TrojanOptions: option.TrojanOutboundOptions{
|
||||||
|
ServerOptions: option.ServerOptions{
|
||||||
|
Server: "127.0.0.1",
|
||||||
|
ServerPort: otherPort,
|
||||||
|
},
|
||||||
|
Password: userUUID.String(),
|
||||||
|
TLS: &option.OutboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ServerName: "example.org",
|
||||||
|
CertificatePath: certPem,
|
||||||
|
},
|
||||||
|
DialerOptions: option.DialerOptions{
|
||||||
|
Detour: "vless-out",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.TypeVLESS,
|
||||||
|
Tag: "vless-out",
|
||||||
|
VLESSOptions: option.VLESSOutboundOptions{
|
||||||
|
ServerOptions: option.ServerOptions{
|
||||||
|
Server: "127.0.0.1",
|
||||||
|
ServerPort: serverPort,
|
||||||
|
},
|
||||||
|
UUID: userUUID.String(),
|
||||||
|
Flow: vless.FlowVision,
|
||||||
|
TLS: &option.OutboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ServerName: "google.com",
|
||||||
|
Reality: &option.OutboundRealityOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ShortID: "0123456789abcdef",
|
||||||
|
PublicKey: "jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0",
|
||||||
|
},
|
||||||
|
UTLS: &option.OutboundUTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Route: &option.RouteOptions{
|
||||||
|
Rules: []option.Rule{
|
||||||
|
{
|
||||||
|
DefaultOptions: option.DefaultRule{
|
||||||
|
Inbound: []string{"mixed-in"},
|
||||||
|
Outbound: "trojan-out",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
testSuit(t, clientPort, testPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVLESSRealityTransport(t *testing.T) {
|
||||||
|
t.Run("grpc", func(t *testing.T) {
|
||||||
|
testVLESSRealityTransport(t, &option.V2RayTransportOptions{
|
||||||
|
Type: C.V2RayTransportTypeGRPC,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("websocket", func(t *testing.T) {
|
||||||
|
testVLESSRealityTransport(t, &option.V2RayTransportOptions{
|
||||||
|
Type: C.V2RayTransportTypeWebsocket,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("h2", func(t *testing.T) {
|
||||||
|
testVLESSRealityTransport(t, &option.V2RayTransportOptions{
|
||||||
|
Type: C.V2RayTransportTypeHTTP,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testVLESSRealityTransport(t *testing.T, transport *option.V2RayTransportOptions) {
|
||||||
|
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||||
|
|
||||||
|
userUUID := newUUID()
|
||||||
|
startInstance(t, option.Options{
|
||||||
|
Inbounds: []option.Inbound{
|
||||||
|
{
|
||||||
|
Type: C.TypeMixed,
|
||||||
|
Tag: "mixed-in",
|
||||||
|
MixedOptions: option.HTTPMixedInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||||
|
ListenPort: clientPort,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.TypeVLESS,
|
||||||
|
VLESSOptions: option.VLESSInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
Users: []option.VLESSUser{
|
||||||
|
{
|
||||||
|
Name: "sekai",
|
||||||
|
UUID: userUUID.String(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TLS: &option.InboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ServerName: "google.com",
|
||||||
|
Reality: &option.InboundRealityOptions{
|
||||||
|
Enabled: true,
|
||||||
|
Handshake: option.InboundRealityHandshakeOptions{
|
||||||
|
ServerOptions: option.ServerOptions{
|
||||||
|
Server: "google.com",
|
||||||
|
ServerPort: 443,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ShortID: []string{"0123456789abcdef"},
|
||||||
|
PrivateKey: "UuMBgl7MXTPx9inmQp2UC7Jcnwc6XYbwDNebonM-FCc",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Transport: transport,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.TypeTrojan,
|
||||||
|
Tag: "trojan",
|
||||||
|
TrojanOptions: option.TrojanInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||||
|
ListenPort: otherPort,
|
||||||
|
},
|
||||||
|
Users: []option.TrojanUser{
|
||||||
|
{
|
||||||
|
Name: "sekai",
|
||||||
|
Password: userUUID.String(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TLS: &option.InboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ServerName: "example.org",
|
||||||
|
CertificatePath: certPem,
|
||||||
|
KeyPath: keyPem,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbounds: []option.Outbound{
|
||||||
|
{
|
||||||
|
Type: C.TypeDirect,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.TypeTrojan,
|
||||||
|
Tag: "trojan-out",
|
||||||
|
TrojanOptions: option.TrojanOutboundOptions{
|
||||||
|
ServerOptions: option.ServerOptions{
|
||||||
|
Server: "127.0.0.1",
|
||||||
|
ServerPort: otherPort,
|
||||||
|
},
|
||||||
|
Password: userUUID.String(),
|
||||||
|
TLS: &option.OutboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ServerName: "example.org",
|
||||||
|
CertificatePath: certPem,
|
||||||
|
},
|
||||||
|
DialerOptions: option.DialerOptions{
|
||||||
|
Detour: "vless-out",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.TypeVLESS,
|
||||||
|
Tag: "vless-out",
|
||||||
|
VLESSOptions: option.VLESSOutboundOptions{
|
||||||
|
ServerOptions: option.ServerOptions{
|
||||||
|
Server: "127.0.0.1",
|
||||||
|
ServerPort: serverPort,
|
||||||
|
},
|
||||||
|
UUID: userUUID.String(),
|
||||||
|
TLS: &option.OutboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ServerName: "google.com",
|
||||||
|
Reality: &option.OutboundRealityOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ShortID: "0123456789abcdef",
|
||||||
|
PublicKey: "jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0",
|
||||||
|
},
|
||||||
|
UTLS: &option.OutboundUTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Transport: transport,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Route: &option.RouteOptions{
|
||||||
|
Rules: []option.Rule{
|
||||||
|
{
|
||||||
|
DefaultOptions: option.DefaultRule{
|
||||||
|
Inbound: []string{"mixed-in"},
|
||||||
|
Outbound: "trojan-out",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
testSuit(t, clientPort, testPort)
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ const (
|
|||||||
clientPort
|
clientPort
|
||||||
testPort
|
testPort
|
||||||
otherPort
|
otherPort
|
||||||
|
otherClientPort
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestShadowsocks(t *testing.T) {
|
func TestShadowsocks(t *testing.T) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/transport/vless"
|
"github.com/sagernet/sing-box/transport/vless"
|
||||||
|
|
||||||
|
"github.com/gofrs/uuid"
|
||||||
"github.com/spyzhov/ajson"
|
"github.com/spyzhov/ajson"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -65,17 +66,17 @@ func TestVLESS(t *testing.T) {
|
|||||||
|
|
||||||
func TestVLESSXRay(t *testing.T) {
|
func TestVLESSXRay(t *testing.T) {
|
||||||
t.Run("origin", func(t *testing.T) {
|
t.Run("origin", func(t *testing.T) {
|
||||||
testVLESSXray(t, "", "")
|
testVLESSXrayOutbound(t, "", "")
|
||||||
})
|
})
|
||||||
t.Run("xudp", func(t *testing.T) {
|
t.Run("xudp", func(t *testing.T) {
|
||||||
testVLESSXray(t, "xudp", "")
|
testVLESSXrayOutbound(t, "xudp", "")
|
||||||
})
|
})
|
||||||
t.Run("vision", func(t *testing.T) {
|
t.Run("vision", func(t *testing.T) {
|
||||||
testVLESSXray(t, "", vless.FlowVision)
|
testVLESSXrayOutbound(t, "xudp", vless.FlowVision)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testVLESSXray(t *testing.T, packetEncoding string, flow string) {
|
func testVLESSXrayOutbound(t *testing.T, packetEncoding string, flow string) {
|
||||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||||
|
|
||||||
content, err := os.ReadFile("config/vless-tls-server.json")
|
content, err := os.ReadFile("config/vless-tls-server.json")
|
||||||
@@ -167,7 +168,7 @@ func testVLESSXray(t *testing.T, packetEncoding string, flow string) {
|
|||||||
},
|
},
|
||||||
UUID: userID.String(),
|
UUID: userID.String(),
|
||||||
Flow: flow,
|
Flow: flow,
|
||||||
PacketEncoding: packetEncoding,
|
PacketEncoding: &packetEncoding,
|
||||||
TLS: &option.OutboundTLSOptions{
|
TLS: &option.OutboundTLSOptions{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
ServerName: "example.org",
|
ServerName: "example.org",
|
||||||
@@ -192,7 +193,7 @@ func testVLESSXray(t *testing.T, packetEncoding string, flow string) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
testTCP(t, clientPort, testPort)
|
testSuit(t, clientPort, testPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVLESSSelf(t *testing.T) {
|
func TestVLESSSelf(t *testing.T) {
|
||||||
@@ -234,6 +235,7 @@ func testVLESSSelf(t *testing.T, flow string) {
|
|||||||
{
|
{
|
||||||
Name: "sekai",
|
Name: "sekai",
|
||||||
UUID: userUUID.String(),
|
UUID: userUUID.String(),
|
||||||
|
Flow: flow,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
TLS: &option.InboundTLSOptions{
|
TLS: &option.InboundTLSOptions{
|
||||||
@@ -308,6 +310,7 @@ func testVLESSSelfTLS(t *testing.T, flow string) {
|
|||||||
{
|
{
|
||||||
Name: "sekai",
|
Name: "sekai",
|
||||||
UUID: userUUID.String(),
|
UUID: userUUID.String(),
|
||||||
|
Flow: flow,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
TLS: &option.InboundTLSOptions{
|
TLS: &option.InboundTLSOptions{
|
||||||
@@ -396,22 +399,17 @@ func testVLESSSelfTLS(t *testing.T, flow string) {
|
|||||||
testSuit(t, clientPort, testPort)
|
testSuit(t, clientPort, testPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVLESSVisionReality(t *testing.T) {
|
func TestVLESSXrayInbound(t *testing.T) {
|
||||||
|
testVLESSXrayInbound(t, vless.FlowVision)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testVLESSXrayInbound(t *testing.T, flow string) {
|
||||||
|
userId, err := uuid.DefaultGenerator.NewV4()
|
||||||
|
require.NoError(t, err)
|
||||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||||
|
|
||||||
userUUID := newUUID()
|
|
||||||
startInstance(t, option.Options{
|
startInstance(t, option.Options{
|
||||||
Inbounds: []option.Inbound{
|
Inbounds: []option.Inbound{
|
||||||
{
|
|
||||||
Type: C.TypeMixed,
|
|
||||||
Tag: "mixed-in",
|
|
||||||
MixedOptions: option.HTTPMixedInboundOptions{
|
|
||||||
ListenOptions: option.ListenOptions{
|
|
||||||
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
|
||||||
ListenPort: clientPort,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Type: C.TypeVLESS,
|
Type: C.TypeVLESS,
|
||||||
VLESSOptions: option.VLESSInboundOptions{
|
VLESSOptions: option.VLESSInboundOptions{
|
||||||
@@ -422,23 +420,15 @@ func TestVLESSVisionReality(t *testing.T) {
|
|||||||
Users: []option.VLESSUser{
|
Users: []option.VLESSUser{
|
||||||
{
|
{
|
||||||
Name: "sekai",
|
Name: "sekai",
|
||||||
UUID: userUUID.String(),
|
UUID: userId.String(),
|
||||||
|
Flow: flow,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
TLS: &option.InboundTLSOptions{
|
TLS: &option.InboundTLSOptions{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
ServerName: "google.com",
|
ServerName: "example.org",
|
||||||
Reality: &option.InboundRealityOptions{
|
CertificatePath: certPem,
|
||||||
Enabled: true,
|
KeyPath: keyPem,
|
||||||
Handshake: option.InboundRealityHandshakeOptions{
|
|
||||||
ServerOptions: option.ServerOptions{
|
|
||||||
Server: "google.com",
|
|
||||||
ServerPort: 443,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ShortID: []string{"0123456789abcdef"},
|
|
||||||
PrivateKey: "UuMBgl7MXTPx9inmQp2UC7Jcnwc6XYbwDNebonM-FCc",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -453,7 +443,7 @@ func TestVLESSVisionReality(t *testing.T) {
|
|||||||
Users: []option.TrojanUser{
|
Users: []option.TrojanUser{
|
||||||
{
|
{
|
||||||
Name: "sekai",
|
Name: "sekai",
|
||||||
Password: userUUID.String(),
|
Password: userId.String(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
TLS: &option.InboundTLSOptions{
|
TLS: &option.InboundTLSOptions{
|
||||||
@@ -465,10 +455,22 @@ func TestVLESSVisionReality(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Outbounds: []option.Outbound{
|
})
|
||||||
|
|
||||||
|
startInstance(t, option.Options{
|
||||||
|
Inbounds: []option.Inbound{
|
||||||
{
|
{
|
||||||
Type: C.TypeDirect,
|
Type: C.TypeMixed,
|
||||||
|
Tag: "mixed-in",
|
||||||
|
MixedOptions: option.HTTPMixedInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: option.ListenAddress(netip.IPv4Unspecified()),
|
||||||
|
ListenPort: otherClientPort,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
Outbounds: []option.Outbound{
|
||||||
{
|
{
|
||||||
Type: C.TypeTrojan,
|
Type: C.TypeTrojan,
|
||||||
Tag: "trojan-out",
|
Tag: "trojan-out",
|
||||||
@@ -477,7 +479,7 @@ func TestVLESSVisionReality(t *testing.T) {
|
|||||||
Server: "127.0.0.1",
|
Server: "127.0.0.1",
|
||||||
ServerPort: otherPort,
|
ServerPort: otherPort,
|
||||||
},
|
},
|
||||||
Password: userUUID.String(),
|
Password: userId.String(),
|
||||||
TLS: &option.OutboundTLSOptions{
|
TLS: &option.OutboundTLSOptions{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
ServerName: "example.org",
|
ServerName: "example.org",
|
||||||
@@ -489,40 +491,45 @@ func TestVLESSVisionReality(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: C.TypeVLESS,
|
Type: C.TypeSocks,
|
||||||
Tag: "vless-out",
|
Tag: "vless-out",
|
||||||
VLESSOptions: option.VLESSOutboundOptions{
|
SocksOptions: option.SocksOutboundOptions{
|
||||||
ServerOptions: option.ServerOptions{
|
ServerOptions: option.ServerOptions{
|
||||||
Server: "127.0.0.1",
|
Server: "127.0.0.1",
|
||||||
ServerPort: serverPort,
|
ServerPort: clientPort,
|
||||||
},
|
|
||||||
UUID: userUUID.String(),
|
|
||||||
Flow: vless.FlowVision,
|
|
||||||
TLS: &option.OutboundTLSOptions{
|
|
||||||
Enabled: true,
|
|
||||||
ServerName: "google.com",
|
|
||||||
Reality: &option.OutboundRealityOptions{
|
|
||||||
Enabled: true,
|
|
||||||
ShortID: "0123456789abcdef",
|
|
||||||
PublicKey: "jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0",
|
|
||||||
},
|
|
||||||
UTLS: &option.OutboundUTLSOptions{
|
|
||||||
Enabled: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Route: &option.RouteOptions{
|
|
||||||
Rules: []option.Rule{
|
|
||||||
{
|
|
||||||
DefaultOptions: option.DefaultRule{
|
|
||||||
Inbound: []string{"mixed-in"},
|
|
||||||
Outbound: "trojan-out",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
testSuit(t, clientPort, testPort)
|
|
||||||
|
content, err := os.ReadFile("config/vless-tls-client.json")
|
||||||
|
require.NoError(t, err)
|
||||||
|
config, err := ajson.Unmarshal(content)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
config.MustKey("inbounds").MustIndex(0).MustKey("port").SetNumeric(float64(clientPort))
|
||||||
|
outbound := config.MustKey("outbounds").MustIndex(0)
|
||||||
|
settings := outbound.MustKey("settings").MustKey("vnext").MustIndex(0)
|
||||||
|
settings.MustKey("port").SetNumeric(float64(serverPort))
|
||||||
|
user := settings.MustKey("users").MustIndex(0)
|
||||||
|
user.MustKey("id").SetString(userId.String())
|
||||||
|
user.MustKey("flow").SetString(flow)
|
||||||
|
content, err = ajson.Marshal(config)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
content, err = ajson.Marshal(config)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
startDockerContainer(t, DockerOptions{
|
||||||
|
Image: ImageXRayCore,
|
||||||
|
Ports: []uint16{clientPort},
|
||||||
|
EntryPoint: "xray",
|
||||||
|
Stdin: content,
|
||||||
|
Bind: map[string]string{
|
||||||
|
certPem: "/path/to/certificate.crt",
|
||||||
|
keyPem: "/path/to/private.key",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
testTCP(t, otherClientPort, testPort)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ const (
|
|||||||
|
|
||||||
var CRLF = []byte{'\r', '\n'}
|
var CRLF = []byte{'\r', '\n'}
|
||||||
|
|
||||||
|
var _ N.EarlyConn = (*ClientConn)(nil)
|
||||||
|
|
||||||
type ClientConn struct {
|
type ClientConn struct {
|
||||||
N.ExtendedConn
|
N.ExtendedConn
|
||||||
key [KeyLength]byte
|
key [KeyLength]byte
|
||||||
@@ -41,6 +43,10 @@ func NewClientConn(conn net.Conn, key [KeyLength]byte, destination M.Socksaddr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) NeedHandshake() bool {
|
||||||
|
return !c.headerWritten
|
||||||
|
}
|
||||||
|
|
||||||
func (c *ClientConn) Write(p []byte) (n int, err error) {
|
func (c *ClientConn) Write(p []byte) (n int, err error) {
|
||||||
if c.headerWritten {
|
if c.headerWritten {
|
||||||
return c.ExtendedConn.Write(p)
|
return c.ExtendedConn.Write(p)
|
||||||
@@ -101,6 +107,10 @@ func NewClientPacketConn(conn net.Conn, key [KeyLength]byte) *ClientPacketConn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ClientPacketConn) NeedHandshake() bool {
|
||||||
|
return !c.headerWritten
|
||||||
|
}
|
||||||
|
|
||||||
func (c *ClientPacketConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {
|
func (c *ClientPacketConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {
|
||||||
return ReadPacket(c.Conn, buffer)
|
return ReadPacket(c.Conn, buffer)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build !go1.20
|
||||||
|
|
||||||
package v2raygrpclite
|
package v2raygrpclite
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
118
transport/v2raygrpclite/server_badhttp.go
Normal file
118
transport/v2raygrpclite/server_badhttp.go
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
//go:build go1.20 && !go1.21
|
||||||
|
|
||||||
|
package v2raygrpclite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sagernet/badhttp"
|
||||||
|
"github.com/sagernet/badhttp2"
|
||||||
|
"github.com/sagernet/badhttp2/h2c"
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/common/tls"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing-box/transport/v2rayhttp"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
sHttp "github.com/sagernet/sing/protocol/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ adapter.V2RayServerTransport = (*Server)(nil)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
handler adapter.V2RayServerTransportHandler
|
||||||
|
errorHandler E.Handler
|
||||||
|
httpServer *http.Server
|
||||||
|
h2Server *http2.Server
|
||||||
|
h2cHandler http.Handler
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Network() []string {
|
||||||
|
return []string{N.NetworkTCP}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (*Server, error) {
|
||||||
|
server := &Server{
|
||||||
|
handler: handler,
|
||||||
|
path: fmt.Sprintf("/%s/Tun", url.QueryEscape(options.ServiceName)),
|
||||||
|
h2Server: new(http2.Server),
|
||||||
|
}
|
||||||
|
server.httpServer = &http.Server{
|
||||||
|
Handler: server,
|
||||||
|
}
|
||||||
|
server.h2cHandler = h2c.NewHandler(server, server.h2Server)
|
||||||
|
if tlsConfig != nil {
|
||||||
|
if len(tlsConfig.NextProtos()) == 0 {
|
||||||
|
tlsConfig.SetNextProtos([]string{http2.NextProtoTLS})
|
||||||
|
}
|
||||||
|
server.httpServer.TLSConfig = tlsConfig
|
||||||
|
}
|
||||||
|
return server, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
if request.Method == "PRI" && len(request.Header) == 0 && request.URL.Path == "*" && request.Proto == "HTTP/2.0" {
|
||||||
|
s.h2cHandler.ServeHTTP(writer, request)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if request.URL.Path != s.path {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad path: ", request.URL.Path))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if request.Method != http.MethodPost {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad method: ", request.Method))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ct := request.Header.Get("Content-Type"); !strings.HasPrefix(ct, "application/grpc") {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad content type: ", ct))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writer.Header().Set("Content-Type", "application/grpc")
|
||||||
|
writer.Header().Set("TE", "trailers")
|
||||||
|
writer.WriteHeader(http.StatusOK)
|
||||||
|
var metadata M.Metadata
|
||||||
|
metadata.Source = sHttp.SourceAddress(v2rayhttp.BadRequest(request))
|
||||||
|
conn := v2rayhttp.NewHTTP2Wrapper(newGunConn(request.Body, writer, writer.(http.Flusher)))
|
||||||
|
s.handler.NewConnection(request.Context(), conn, metadata)
|
||||||
|
conn.CloseWrapper()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) fallbackRequest(ctx context.Context, writer http.ResponseWriter, request *http.Request, statusCode int, err error) {
|
||||||
|
conn := v2rayhttp.NewHTTPConn(request.Body, writer)
|
||||||
|
fErr := s.handler.FallbackConnection(ctx, &conn, M.Metadata{})
|
||||||
|
if fErr == nil {
|
||||||
|
return
|
||||||
|
} else if fErr == os.ErrInvalid {
|
||||||
|
fErr = nil
|
||||||
|
}
|
||||||
|
writer.WriteHeader(statusCode)
|
||||||
|
s.handler.NewError(request.Context(), E.Cause(E.Errors(err, E.Cause(fErr, "fallback connection")), "process connection from ", request.RemoteAddr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Serve(listener net.Listener) error {
|
||||||
|
if s.httpServer.TLSConfig != nil {
|
||||||
|
err := http2.ConfigureServer(s.httpServer, s.h2Server)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.httpServer.ServeTLS(listener, "", "")
|
||||||
|
} else {
|
||||||
|
return s.httpServer.Serve(listener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ServePacket(listener net.PacketConn) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Close() error {
|
||||||
|
return common.Close(common.PtrOrNil(s.httpServer))
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build !go1.20
|
||||||
|
|
||||||
package v2rayhttp
|
package v2rayhttp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
178
transport/v2rayhttp/server_badhttp.go
Normal file
178
transport/v2rayhttp/server_badhttp.go
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
//go:build go1.20 && !go1.21
|
||||||
|
|
||||||
|
package v2rayhttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
std_bufio "bufio"
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
stdHTTP "net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/sagernet/badhttp"
|
||||||
|
"github.com/sagernet/badhttp2"
|
||||||
|
"github.com/sagernet/badhttp2/h2c"
|
||||||
|
"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/option"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
sHttp "github.com/sagernet/sing/protocol/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ adapter.V2RayServerTransport = (*Server)(nil)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
ctx context.Context
|
||||||
|
handler adapter.V2RayServerTransportHandler
|
||||||
|
httpServer *http.Server
|
||||||
|
h2Server *http2.Server
|
||||||
|
h2cHandler http.Handler
|
||||||
|
host []string
|
||||||
|
path string
|
||||||
|
method string
|
||||||
|
headers http.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Network() []string {
|
||||||
|
return []string{N.NetworkTCP}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(ctx context.Context, options option.V2RayHTTPOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (*Server, error) {
|
||||||
|
server := &Server{
|
||||||
|
ctx: ctx,
|
||||||
|
handler: handler,
|
||||||
|
h2Server: new(http2.Server),
|
||||||
|
host: options.Host,
|
||||||
|
path: options.Path,
|
||||||
|
method: options.Method,
|
||||||
|
headers: make(http.Header),
|
||||||
|
}
|
||||||
|
if server.method == "" {
|
||||||
|
server.method = "PUT"
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(server.path, "/") {
|
||||||
|
server.path = "/" + server.path
|
||||||
|
}
|
||||||
|
for key, value := range options.Headers {
|
||||||
|
server.headers.Set(key, value)
|
||||||
|
}
|
||||||
|
server.httpServer = &http.Server{
|
||||||
|
Handler: server,
|
||||||
|
ReadHeaderTimeout: C.TCPTimeout,
|
||||||
|
MaxHeaderBytes: http.DefaultMaxHeaderBytes,
|
||||||
|
TLSConfig: tlsConfig,
|
||||||
|
}
|
||||||
|
server.h2cHandler = h2c.NewHandler(server, server.h2Server)
|
||||||
|
return server, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
if request.Method == "PRI" && len(request.Header) == 0 && request.URL.Path == "*" && request.Proto == "HTTP/2.0" {
|
||||||
|
s.h2cHandler.ServeHTTP(writer, request)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
host := request.Host
|
||||||
|
if len(s.host) > 0 && !common.Contains(s.host, host) {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusBadRequest, E.New("bad host: ", host))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(request.URL.Path, s.path) {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad path: ", request.URL.Path))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if request.Method != s.method {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad method: ", request.Method))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.Header().Set("Cache-Control", "no-store")
|
||||||
|
|
||||||
|
for key, values := range s.headers {
|
||||||
|
for _, value := range values {
|
||||||
|
writer.Header().Set(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteHeader(http.StatusOK)
|
||||||
|
writer.(http.Flusher).Flush()
|
||||||
|
|
||||||
|
var metadata M.Metadata
|
||||||
|
metadata.Source = sHttp.SourceAddress(BadRequest(request))
|
||||||
|
if h, ok := writer.(http.Hijacker); ok {
|
||||||
|
conn, _, err := h.Hijack()
|
||||||
|
if err != nil {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusInternalServerError, E.Cause(err, "hijack conn"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.handler.NewConnection(request.Context(), conn, metadata)
|
||||||
|
} else {
|
||||||
|
conn := NewHTTP2Wrapper(&ServerHTTPConn{
|
||||||
|
NewHTTPConn(request.Body, writer),
|
||||||
|
writer.(http.Flusher),
|
||||||
|
})
|
||||||
|
s.handler.NewConnection(request.Context(), conn, metadata)
|
||||||
|
conn.CloseWrapper()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) fallbackRequest(ctx context.Context, writer http.ResponseWriter, request *http.Request, statusCode int, err error) {
|
||||||
|
conn := NewHTTPConn(request.Body, writer)
|
||||||
|
fErr := s.handler.FallbackConnection(ctx, &conn, M.Metadata{})
|
||||||
|
if fErr == nil {
|
||||||
|
return
|
||||||
|
} else if fErr == os.ErrInvalid {
|
||||||
|
fErr = nil
|
||||||
|
}
|
||||||
|
writer.WriteHeader(statusCode)
|
||||||
|
s.handler.NewError(request.Context(), E.Cause(E.Errors(err, E.Cause(fErr, "fallback connection")), "process connection from ", request.RemoteAddr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Serve(listener net.Listener) error {
|
||||||
|
if s.httpServer.TLSConfig != nil {
|
||||||
|
err := http2.ConfigureServer(s.httpServer, s.h2Server)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.httpServer.ServeTLS(listener, "", "")
|
||||||
|
} else {
|
||||||
|
return s.httpServer.Serve(listener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ServePacket(listener net.PacketConn) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Close() error {
|
||||||
|
return common.Close(common.PtrOrNil(s.httpServer))
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ stdHTTP.ResponseWriter = (*BadResponseWriter)(nil)
|
||||||
|
_ stdHTTP.Hijacker = (*BadResponseWriter)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
type BadResponseWriter struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *BadResponseWriter) Header() stdHTTP.Header {
|
||||||
|
return stdHTTP.Header(w.ResponseWriter.Header())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *BadResponseWriter) Hijack() (net.Conn, *std_bufio.ReadWriter, error) {
|
||||||
|
if hijacker, loaded := common.Cast[http.Hijacker](w.ResponseWriter); loaded {
|
||||||
|
return hijacker.Hijack()
|
||||||
|
}
|
||||||
|
return nil, nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func BadRequest(r *http.Request) *stdHTTP.Request {
|
||||||
|
return (*stdHTTP.Request)(unsafe.Pointer(r))
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build !go1.20
|
||||||
|
|
||||||
package v2raywebsocket
|
package v2raywebsocket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
141
transport/v2raywebsocket/server_badhttp.go
Normal file
141
transport/v2raywebsocket/server_badhttp.go
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
//go:build go1.20 && !go1.21
|
||||||
|
|
||||||
|
package v2raywebsocket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"net"
|
||||||
|
stdHTTP "net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sagernet/badhttp"
|
||||||
|
"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/option"
|
||||||
|
"github.com/sagernet/sing-box/transport/v2rayhttp"
|
||||||
|
"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"
|
||||||
|
sHttp "github.com/sagernet/sing/protocol/http"
|
||||||
|
"github.com/sagernet/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ adapter.V2RayServerTransport = (*Server)(nil)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
ctx context.Context
|
||||||
|
handler adapter.V2RayServerTransportHandler
|
||||||
|
httpServer *http.Server
|
||||||
|
path string
|
||||||
|
maxEarlyData uint32
|
||||||
|
earlyDataHeaderName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(ctx context.Context, options option.V2RayWebsocketOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (*Server, error) {
|
||||||
|
server := &Server{
|
||||||
|
ctx: ctx,
|
||||||
|
handler: handler,
|
||||||
|
path: options.Path,
|
||||||
|
maxEarlyData: options.MaxEarlyData,
|
||||||
|
earlyDataHeaderName: options.EarlyDataHeaderName,
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(server.path, "/") {
|
||||||
|
server.path = "/" + server.path
|
||||||
|
}
|
||||||
|
server.httpServer = &http.Server{
|
||||||
|
Handler: server,
|
||||||
|
ReadHeaderTimeout: C.TCPTimeout,
|
||||||
|
MaxHeaderBytes: http.DefaultMaxHeaderBytes,
|
||||||
|
TLSConfig: tlsConfig,
|
||||||
|
}
|
||||||
|
return server, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var upgrader = websocket.Upgrader{
|
||||||
|
HandshakeTimeout: C.TCPTimeout,
|
||||||
|
CheckOrigin: func(r *stdHTTP.Request) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
if s.maxEarlyData == 0 || s.earlyDataHeaderName != "" {
|
||||||
|
if request.URL.Path != s.path {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad path: ", request.URL.Path))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
earlyData []byte
|
||||||
|
err error
|
||||||
|
conn net.Conn
|
||||||
|
)
|
||||||
|
if s.earlyDataHeaderName == "" {
|
||||||
|
if strings.HasPrefix(request.URL.RequestURI(), s.path) {
|
||||||
|
earlyDataStr := request.URL.RequestURI()[len(s.path):]
|
||||||
|
earlyData, err = base64.RawURLEncoding.DecodeString(earlyDataStr)
|
||||||
|
} else {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusNotFound, E.New("bad path: ", request.URL.Path))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
earlyDataStr := request.Header.Get(s.earlyDataHeaderName)
|
||||||
|
if earlyDataStr != "" {
|
||||||
|
earlyData, err = base64.RawURLEncoding.DecodeString(earlyDataStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusBadRequest, E.Cause(err, "decode early data"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wsConn, err := upgrader.Upgrade(&v2rayhttp.BadResponseWriter{ResponseWriter: writer}, v2rayhttp.BadRequest(request), nil)
|
||||||
|
if err != nil {
|
||||||
|
s.fallbackRequest(request.Context(), writer, request, http.StatusBadRequest, E.Cause(err, "upgrade websocket connection"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var metadata M.Metadata
|
||||||
|
metadata.Source = sHttp.SourceAddress(v2rayhttp.BadRequest(request))
|
||||||
|
conn = NewServerConn(wsConn, metadata.Source.TCPAddr())
|
||||||
|
if len(earlyData) > 0 {
|
||||||
|
conn = bufio.NewCachedConn(conn, buf.As(earlyData))
|
||||||
|
}
|
||||||
|
s.handler.NewConnection(request.Context(), conn, metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) fallbackRequest(ctx context.Context, writer http.ResponseWriter, request *http.Request, statusCode int, err error) {
|
||||||
|
conn := v2rayhttp.NewHTTPConn(request.Body, writer)
|
||||||
|
fErr := s.handler.FallbackConnection(ctx, &conn, M.Metadata{})
|
||||||
|
if fErr == nil {
|
||||||
|
return
|
||||||
|
} else if fErr == os.ErrInvalid {
|
||||||
|
fErr = nil
|
||||||
|
}
|
||||||
|
writer.WriteHeader(statusCode)
|
||||||
|
s.handler.NewError(request.Context(), E.Cause(E.Errors(err, E.Cause(fErr, "fallback connection")), "process connection from ", request.RemoteAddr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Network() []string {
|
||||||
|
return []string{N.NetworkTCP}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Serve(listener net.Listener) error {
|
||||||
|
if s.httpServer.TLSConfig == nil {
|
||||||
|
return s.httpServer.Serve(listener)
|
||||||
|
} else {
|
||||||
|
return s.httpServer.ServeTLS(listener, "", "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ServePacket(listener net.PacketConn) error {
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Close() error {
|
||||||
|
return common.Close(common.PtrOrNil(s.httpServer))
|
||||||
|
}
|
||||||
@@ -9,17 +9,20 @@ import (
|
|||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
"github.com/gofrs/uuid"
|
"github.com/gofrs/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
key [16]byte
|
key [16]byte
|
||||||
flow string
|
flow string
|
||||||
|
logger logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(userId string, flow string) (*Client, error) {
|
func NewClient(userId string, flow string, logger logger.Logger) (*Client, error) {
|
||||||
user := uuid.FromStringOrNil(userId)
|
user := uuid.FromStringOrNil(userId)
|
||||||
if user == uuid.Nil {
|
if user == uuid.Nil {
|
||||||
user = uuid.NewV5(user, userId)
|
user = uuid.NewV5(user, userId)
|
||||||
@@ -29,12 +32,12 @@ func NewClient(userId string, flow string) (*Client, error) {
|
|||||||
default:
|
default:
|
||||||
return nil, E.New("unsupported flow: " + flow)
|
return nil, E.New("unsupported flow: " + flow)
|
||||||
}
|
}
|
||||||
return &Client{user, flow}, nil
|
return &Client{user, flow, logger}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) prepareConn(conn net.Conn) (net.Conn, error) {
|
func (c *Client) prepareConn(conn net.Conn) (net.Conn, error) {
|
||||||
if c.flow == FlowVision {
|
if c.flow == FlowVision {
|
||||||
vConn, err := NewVisionConn(conn, c.key)
|
vConn, err := NewVisionConn(conn, c.key, c.logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "initialize vision")
|
return nil, E.Cause(err, "initialize vision")
|
||||||
}
|
}
|
||||||
@@ -82,6 +85,8 @@ func (c *Client) DialEarlyXUDPPacketConn(conn net.Conn, destination M.Socksaddr)
|
|||||||
return vmess.NewXUDPConn(&Conn{Conn: conn, protocolConn: conn, key: c.key, command: vmess.CommandMux, destination: destination, flow: c.flow}, destination), nil
|
return vmess.NewXUDPConn(&Conn{Conn: conn, protocolConn: conn, key: c.key, command: vmess.CommandMux, destination: destination, flow: c.flow}, destination), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ N.EarlyConn = (*Conn)(nil)
|
||||||
|
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
protocolConn net.Conn
|
protocolConn net.Conn
|
||||||
@@ -93,6 +98,10 @@ type Conn struct {
|
|||||||
responseRead bool
|
responseRead bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Conn) NeedHandshake() bool {
|
||||||
|
return !c.requestWritten
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Conn) Read(b []byte) (n int, err error) {
|
func (c *Conn) Read(b []byte) (n int, err error) {
|
||||||
if !c.responseRead {
|
if !c.responseRead {
|
||||||
err = ReadResponse(c.Conn)
|
err = ReadResponse(c.Conn)
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ var (
|
|||||||
tlsClientHandShakeStart = []byte{0x16, 0x03}
|
tlsClientHandShakeStart = []byte{0x16, 0x03}
|
||||||
tlsServerHandShakeStart = []byte{0x16, 0x03, 0x03}
|
tlsServerHandShakeStart = []byte{0x16, 0x03, 0x03}
|
||||||
tlsApplicationDataStart = []byte{0x17, 0x03, 0x03}
|
tlsApplicationDataStart = []byte{0x17, 0x03, 0x03}
|
||||||
|
|
||||||
|
commandPaddingContinue byte = 0
|
||||||
|
commandPaddingEnd byte = 1
|
||||||
|
commandPaddingDirect byte = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
var tls13CipherSuiteDic = map[uint16]string{
|
var tls13CipherSuiteDic = map[uint16]string{
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ func WriteRequest(writer io.Writer, request Request, payload []byte) error {
|
|||||||
requestLen += 1 // protobuf length
|
requestLen += 1 // protobuf length
|
||||||
|
|
||||||
var addonsLen int
|
var addonsLen int
|
||||||
if request.Flow != "" {
|
if request.Command == vmess.CommandTCP && request.Flow != "" {
|
||||||
addonsLen += 1 // protobuf header
|
addonsLen += 1 // protobuf header
|
||||||
addonsLen += UvarintLen(uint64(len(request.Flow)))
|
addonsLen += UvarintLen(uint64(len(request.Flow)))
|
||||||
addonsLen += len(request.Flow)
|
addonsLen += len(request.Flow)
|
||||||
|
|||||||
@@ -11,15 +11,18 @@ import (
|
|||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
"github.com/gofrs/uuid"
|
"github.com/gofrs/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Service[T any] struct {
|
type Service[T comparable] struct {
|
||||||
userMap map[[16]byte]T
|
userMap map[[16]byte]T
|
||||||
handler Handler
|
userFlow map[T]string
|
||||||
|
logger logger.Logger
|
||||||
|
handler Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
@@ -28,22 +31,26 @@ type Handler interface {
|
|||||||
E.Handler
|
E.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService[T any](handler Handler) *Service[T] {
|
func NewService[T comparable](logger logger.Logger, handler Handler) *Service[T] {
|
||||||
return &Service[T]{
|
return &Service[T]{
|
||||||
|
logger: logger,
|
||||||
handler: handler,
|
handler: handler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service[T]) UpdateUsers(userList []T, userUUIDList []string) {
|
func (s *Service[T]) UpdateUsers(userList []T, userUUIDList []string, userFlowList []string) {
|
||||||
userMap := make(map[[16]byte]T)
|
userMap := make(map[[16]byte]T)
|
||||||
|
userFlowMap := make(map[T]string)
|
||||||
for i, userName := range userList {
|
for i, userName := range userList {
|
||||||
userID := uuid.FromStringOrNil(userUUIDList[i])
|
userID := uuid.FromStringOrNil(userUUIDList[i])
|
||||||
if userID == uuid.Nil {
|
if userID == uuid.Nil {
|
||||||
userID = uuid.NewV5(uuid.Nil, userUUIDList[i])
|
userID = uuid.NewV5(uuid.Nil, userUUIDList[i])
|
||||||
}
|
}
|
||||||
userMap[userID] = userName
|
userMap[userID] = userName
|
||||||
|
userFlowMap[userName] = userFlowList[i]
|
||||||
}
|
}
|
||||||
s.userMap = userMap
|
s.userMap = userMap
|
||||||
|
s.userFlow = userFlowMap
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ N.TCPConnectionHandler = (*Service[int])(nil)
|
var _ N.TCPConnectionHandler = (*Service[int])(nil)
|
||||||
@@ -60,28 +67,43 @@ func (s *Service[T]) NewConnection(ctx context.Context, conn net.Conn, metadata
|
|||||||
ctx = auth.ContextWithUser(ctx, user)
|
ctx = auth.ContextWithUser(ctx, user)
|
||||||
metadata.Destination = request.Destination
|
metadata.Destination = request.Destination
|
||||||
|
|
||||||
protocolConn := conn
|
userFlow := s.userFlow[user]
|
||||||
switch request.Flow {
|
|
||||||
case "":
|
var responseWriter io.Writer
|
||||||
case FlowVision:
|
if request.Command == vmess.CommandTCP {
|
||||||
protocolConn, err = NewVisionConn(conn, request.UUID)
|
if request.Flow != userFlow {
|
||||||
if err != nil {
|
return E.New("flow mismatch: expected ", flowName(userFlow), ", but got ", flowName(request.Flow))
|
||||||
return E.Cause(err, "initialize vision")
|
}
|
||||||
|
switch userFlow {
|
||||||
|
case "":
|
||||||
|
case FlowVision:
|
||||||
|
responseWriter = conn
|
||||||
|
conn, err = NewVisionConn(conn, request.UUID, s.logger)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "initialize vision")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch request.Command {
|
switch request.Command {
|
||||||
case vmess.CommandTCP:
|
case vmess.CommandTCP:
|
||||||
return s.handler.NewConnection(ctx, &serverConn{Conn: protocolConn, responseWriter: conn}, metadata)
|
return s.handler.NewConnection(ctx, &serverConn{Conn: conn, responseWriter: responseWriter}, metadata)
|
||||||
case vmess.CommandUDP:
|
case vmess.CommandUDP:
|
||||||
return s.handler.NewPacketConnection(ctx, &serverPacketConn{ExtendedConn: bufio.NewExtendedConn(conn), destination: request.Destination}, metadata)
|
return s.handler.NewPacketConnection(ctx, &serverPacketConn{ExtendedConn: bufio.NewExtendedConn(conn), destination: request.Destination}, metadata)
|
||||||
case vmess.CommandMux:
|
case vmess.CommandMux:
|
||||||
return vmess.HandleMuxConnection(ctx, &serverConn{Conn: conn}, s.handler)
|
return vmess.HandleMuxConnection(ctx, &serverConn{Conn: conn, responseWriter: responseWriter}, s.handler)
|
||||||
default:
|
default:
|
||||||
return E.New("unknown command: ", request.Command)
|
return E.New("unknown command: ", request.Command)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func flowName(value string) string {
|
||||||
|
if value == "" {
|
||||||
|
return "none"
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
type serverConn struct {
|
type serverConn struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
responseWriter io.Writer
|
responseWriter io.Writer
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -31,32 +32,36 @@ func init() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const xrayChunkSize = 8192
|
||||||
|
|
||||||
type VisionConn struct {
|
type VisionConn struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
|
reader *bufio.ChunkReader
|
||||||
writer N.VectorisedWriter
|
writer N.VectorisedWriter
|
||||||
input *bytes.Reader
|
input *bytes.Reader
|
||||||
rawInput *bytes.Buffer
|
rawInput *bytes.Buffer
|
||||||
netConn net.Conn
|
netConn net.Conn
|
||||||
|
logger logger.Logger
|
||||||
|
|
||||||
userUUID [16]byte
|
userUUID [16]byte
|
||||||
isTLS bool
|
isTLS bool
|
||||||
numberOfPacketToFilter int
|
numberOfPacketToFilter int
|
||||||
isTLS12orAbove bool
|
isTLS12orAbove bool
|
||||||
remainingServerHello int32
|
remainingServerHello int32
|
||||||
cipher uint16
|
cipher uint16
|
||||||
enableXTLS bool
|
enableXTLS bool
|
||||||
filterTlsApplicationData bool
|
isPadding bool
|
||||||
directWrite bool
|
directWrite bool
|
||||||
writeUUID bool
|
writeUUID bool
|
||||||
filterUUID bool
|
withinPaddingBuffers bool
|
||||||
remainingContent int
|
remainingContent int
|
||||||
remainingPadding int
|
remainingPadding int
|
||||||
currentCommand int
|
currentCommand int
|
||||||
directRead bool
|
directRead bool
|
||||||
remainingReader io.Reader
|
remainingReader io.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewVisionConn(conn net.Conn, userUUID [16]byte) (*VisionConn, error) {
|
func NewVisionConn(conn net.Conn, userUUID [16]byte, logger logger.Logger) (*VisionConn, error) {
|
||||||
var (
|
var (
|
||||||
loaded bool
|
loaded bool
|
||||||
reflectType reflect.Type
|
reflectType reflect.Type
|
||||||
@@ -75,19 +80,22 @@ func NewVisionConn(conn net.Conn, userUUID [16]byte) (*VisionConn, error) {
|
|||||||
input, _ := reflectType.FieldByName("input")
|
input, _ := reflectType.FieldByName("input")
|
||||||
rawInput, _ := reflectType.FieldByName("rawInput")
|
rawInput, _ := reflectType.FieldByName("rawInput")
|
||||||
return &VisionConn{
|
return &VisionConn{
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
writer: bufio.NewVectorisedWriter(conn),
|
reader: bufio.NewChunkReader(conn, xrayChunkSize),
|
||||||
input: (*bytes.Reader)(unsafe.Pointer(reflectPointer + input.Offset)),
|
writer: bufio.NewVectorisedWriter(conn),
|
||||||
rawInput: (*bytes.Buffer)(unsafe.Pointer(reflectPointer + rawInput.Offset)),
|
input: (*bytes.Reader)(unsafe.Pointer(reflectPointer + input.Offset)),
|
||||||
netConn: netConn,
|
rawInput: (*bytes.Buffer)(unsafe.Pointer(reflectPointer + rawInput.Offset)),
|
||||||
userUUID: userUUID,
|
netConn: netConn,
|
||||||
numberOfPacketToFilter: 8,
|
logger: logger,
|
||||||
remainingServerHello: -1,
|
|
||||||
filterTlsApplicationData: true,
|
userUUID: userUUID,
|
||||||
writeUUID: true,
|
numberOfPacketToFilter: 8,
|
||||||
filterUUID: true,
|
remainingServerHello: -1,
|
||||||
remainingContent: -1,
|
isPadding: true,
|
||||||
remainingPadding: -1,
|
writeUUID: true,
|
||||||
|
withinPaddingBuffers: true,
|
||||||
|
remainingContent: -1,
|
||||||
|
remainingPadding: -1,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,26 +104,38 @@ func (c *VisionConn) Read(p []byte) (n int, err error) {
|
|||||||
n, err = c.remainingReader.Read(p)
|
n, err = c.remainingReader.Read(p)
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
c.remainingReader = nil
|
c.remainingReader = nil
|
||||||
if n > 0 {
|
}
|
||||||
return
|
if n > 0 {
|
||||||
}
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.directRead {
|
if c.directRead {
|
||||||
return c.netConn.Read(p)
|
return c.netConn.Read(p)
|
||||||
}
|
}
|
||||||
n, err = c.Conn.Read(p)
|
var bufferBytes []byte
|
||||||
if err != nil {
|
if len(p) > xrayChunkSize {
|
||||||
return
|
n, err = c.Conn.Read(p)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bufferBytes = p[:n]
|
||||||
|
} else {
|
||||||
|
buffer, err := c.reader.ReadChunk()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer buffer.FullReset()
|
||||||
|
bufferBytes = buffer.Bytes()
|
||||||
}
|
}
|
||||||
buffer := p[:n]
|
if c.withinPaddingBuffers || c.numberOfPacketToFilter > 0 {
|
||||||
if c.filterUUID && (c.isTLS || c.numberOfPacketToFilter > 0) {
|
buffers := c.unPadding(bufferBytes)
|
||||||
buffers := c.unPadding(buffer)
|
|
||||||
if c.remainingContent == 0 && c.remainingPadding == 0 {
|
if c.remainingContent == 0 && c.remainingPadding == 0 {
|
||||||
if c.currentCommand == 1 {
|
if c.currentCommand == 1 {
|
||||||
c.filterUUID = false
|
c.withinPaddingBuffers = false
|
||||||
|
c.remainingContent = -1
|
||||||
|
c.remainingPadding = -1
|
||||||
} else if c.currentCommand == 2 {
|
} else if c.currentCommand == 2 {
|
||||||
c.filterUUID = false
|
c.withinPaddingBuffers = false
|
||||||
c.directRead = true
|
c.directRead = true
|
||||||
|
|
||||||
inputBuffer, err := io.ReadAll(c.input)
|
inputBuffer, err := io.ReadAll(c.input)
|
||||||
@@ -130,9 +150,17 @@ func (c *VisionConn) Read(p []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buffers = append(buffers, rawInputBuffer)
|
buffers = append(buffers, rawInputBuffer)
|
||||||
} else if c.currentCommand != 0 {
|
|
||||||
|
c.logger.Trace("XtlsRead readV")
|
||||||
|
} else if c.currentCommand == 0 {
|
||||||
|
c.withinPaddingBuffers = true
|
||||||
|
} else {
|
||||||
return 0, E.New("unknown command ", c.currentCommand)
|
return 0, E.New("unknown command ", c.currentCommand)
|
||||||
}
|
}
|
||||||
|
} else if c.remainingContent > 0 || c.remainingPadding > 0 {
|
||||||
|
c.withinPaddingBuffers = true
|
||||||
|
} else {
|
||||||
|
c.withinPaddingBuffers = false
|
||||||
}
|
}
|
||||||
if c.numberOfPacketToFilter > 0 {
|
if c.numberOfPacketToFilter > 0 {
|
||||||
c.filterTLS(buffers)
|
c.filterTLS(buffers)
|
||||||
@@ -141,7 +169,7 @@ func (c *VisionConn) Read(p []byte) (n int, err error) {
|
|||||||
return c.remainingReader.Read(p)
|
return c.remainingReader.Read(p)
|
||||||
} else {
|
} else {
|
||||||
if c.numberOfPacketToFilter > 0 {
|
if c.numberOfPacketToFilter > 0 {
|
||||||
c.filterTLS([][]byte{buffer})
|
c.filterTLS([][]byte{bufferBytes})
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -151,27 +179,27 @@ func (c *VisionConn) Write(p []byte) (n int, err error) {
|
|||||||
if c.numberOfPacketToFilter > 0 {
|
if c.numberOfPacketToFilter > 0 {
|
||||||
c.filterTLS([][]byte{p})
|
c.filterTLS([][]byte{p})
|
||||||
}
|
}
|
||||||
if c.isTLS && c.filterTlsApplicationData {
|
if c.isPadding {
|
||||||
inputLen := len(p)
|
inputLen := len(p)
|
||||||
buffers := reshapeBuffer(p)
|
buffers := reshapeBuffer(p)
|
||||||
var specIndex int
|
var specIndex int
|
||||||
for i, buffer := range buffers {
|
for i, buffer := range buffers {
|
||||||
if buffer.Len() > 6 && bytes.Equal(tlsApplicationDataStart, buffer.To(3)) {
|
if c.isTLS && buffer.Len() > 6 && bytes.Equal(tlsApplicationDataStart, buffer.To(3)) {
|
||||||
var command byte = 1
|
var command byte = commandPaddingEnd
|
||||||
if c.enableXTLS {
|
if c.enableXTLS {
|
||||||
c.directWrite = true
|
c.directWrite = true
|
||||||
specIndex = i
|
specIndex = i
|
||||||
command = 2
|
command = commandPaddingDirect
|
||||||
}
|
}
|
||||||
c.filterTlsApplicationData = false
|
c.isPadding = false
|
||||||
buffers[i] = c.padding(buffer, command)
|
buffers[i] = c.padding(buffer, command)
|
||||||
break
|
break
|
||||||
} else if !c.isTLS12orAbove && c.numberOfPacketToFilter == 0 {
|
} else if !c.isTLS12orAbove && c.numberOfPacketToFilter <= 1 {
|
||||||
c.filterTlsApplicationData = false
|
c.isPadding = false
|
||||||
buffers[i] = c.padding(buffer, 0x01)
|
buffers[i] = c.padding(buffer, commandPaddingEnd)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
buffers[i] = c.padding(buffer, 0x00)
|
buffers[i] = c.padding(buffer, commandPaddingContinue)
|
||||||
}
|
}
|
||||||
if c.directWrite {
|
if c.directWrite {
|
||||||
encryptedBuffer := buffers[:specIndex+1]
|
encryptedBuffer := buffers[:specIndex+1]
|
||||||
@@ -181,6 +209,7 @@ func (c *VisionConn) Write(p []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
buffers = buffers[specIndex+1:]
|
buffers = buffers[specIndex+1:]
|
||||||
c.writer = bufio.NewVectorisedWriter(c.netConn)
|
c.writer = bufio.NewVectorisedWriter(c.netConn)
|
||||||
|
c.logger.Trace("XtlsWrite writeV ", specIndex, " ", buf.LenMulti(encryptedBuffer), " ", len(buffers))
|
||||||
time.Sleep(5 * time.Millisecond) // wtf
|
time.Sleep(5 * time.Millisecond) // wtf
|
||||||
}
|
}
|
||||||
err = c.writer.WriteVectorised(buffers)
|
err = c.writer.WriteVectorised(buffers)
|
||||||
@@ -209,10 +238,13 @@ func (c *VisionConn) filterTLS(buffers [][]byte) {
|
|||||||
sessionIdLen := int32(buffer[43])
|
sessionIdLen := int32(buffer[43])
|
||||||
cipherSuite := buffer[43+sessionIdLen+1 : 43+sessionIdLen+3]
|
cipherSuite := buffer[43+sessionIdLen+1 : 43+sessionIdLen+3]
|
||||||
c.cipher = uint16(cipherSuite[0])<<8 | uint16(cipherSuite[1])
|
c.cipher = uint16(cipherSuite[0])<<8 | uint16(cipherSuite[1])
|
||||||
|
} else {
|
||||||
|
c.logger.Trace("XtlsFilterTls short server hello, tls 1.2 or older? ", len(buffer), " ", c.remainingServerHello)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if bytes.Equal(tlsClientHandShakeStart, buffer[:2]) && buffer[5] == 1 {
|
} else if bytes.Equal(tlsClientHandShakeStart, buffer[:2]) && buffer[5] == 1 {
|
||||||
c.isTLS = true
|
c.isTLS = true
|
||||||
|
c.logger.Trace("XtlsFilterTls found tls client hello! ", len(buffer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.remainingServerHello > 0 {
|
if c.remainingServerHello > 0 {
|
||||||
@@ -226,13 +258,18 @@ func (c *VisionConn) filterTLS(buffers [][]byte) {
|
|||||||
if ok && cipher != "TLS_AES_128_CCM_8_SHA256" {
|
if ok && cipher != "TLS_AES_128_CCM_8_SHA256" {
|
||||||
c.enableXTLS = true
|
c.enableXTLS = true
|
||||||
}
|
}
|
||||||
|
c.logger.Trace("XtlsFilterTls found tls 1.3! ", len(buffer), " ", c.cipher, " ", c.enableXTLS)
|
||||||
c.numberOfPacketToFilter = 0
|
c.numberOfPacketToFilter = 0
|
||||||
return
|
return
|
||||||
} else if c.remainingServerHello == 0 {
|
} else if c.remainingServerHello == 0 {
|
||||||
|
c.logger.Trace("XtlsFilterTls found tls 1.2! ", len(buffer))
|
||||||
c.numberOfPacketToFilter = 0
|
c.numberOfPacketToFilter = 0
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if c.numberOfPacketToFilter == 0 {
|
||||||
|
c.logger.Trace("XtlsFilterTls stop filtering ", len(buffer))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,9 +279,12 @@ func (c *VisionConn) padding(buffer *buf.Buffer, command byte) *buf.Buffer {
|
|||||||
if buffer != nil {
|
if buffer != nil {
|
||||||
contentLen = buffer.Len()
|
contentLen = buffer.Len()
|
||||||
}
|
}
|
||||||
if contentLen < 900 {
|
if contentLen < 900 && c.isTLS {
|
||||||
l, _ := rand.Int(rand.Reader, big.NewInt(500))
|
l, _ := rand.Int(rand.Reader, big.NewInt(500))
|
||||||
paddingLen = int(l.Int64()) + 900 - contentLen
|
paddingLen = int(l.Int64()) + 900 - contentLen
|
||||||
|
} else {
|
||||||
|
l, _ := rand.Int(rand.Reader, big.NewInt(256))
|
||||||
|
paddingLen = int(l.Int64())
|
||||||
}
|
}
|
||||||
newBuffer := buf.New()
|
newBuffer := buf.New()
|
||||||
if c.writeUUID {
|
if c.writeUUID {
|
||||||
@@ -257,6 +297,7 @@ func (c *VisionConn) padding(buffer *buf.Buffer, command byte) *buf.Buffer {
|
|||||||
buffer.Release()
|
buffer.Release()
|
||||||
}
|
}
|
||||||
newBuffer.Extend(paddingLen)
|
newBuffer.Extend(paddingLen)
|
||||||
|
c.logger.Trace("XtlsPadding ", contentLen, " ", paddingLen, " ", command)
|
||||||
return newBuffer
|
return newBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,6 +308,7 @@ func (c *VisionConn) unPadding(buffer []byte) [][]byte {
|
|||||||
bufferIndex = 16
|
bufferIndex = 16
|
||||||
c.remainingContent = 0
|
c.remainingContent = 0
|
||||||
c.remainingPadding = 0
|
c.remainingPadding = 0
|
||||||
|
c.currentCommand = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.remainingContent == -1 && c.remainingPadding == -1 {
|
if c.remainingContent == -1 && c.remainingPadding == -1 {
|
||||||
@@ -284,6 +326,7 @@ func (c *VisionConn) unPadding(buffer []byte) [][]byte {
|
|||||||
c.remainingContent = int(paddingInfo[1])<<8 | int(paddingInfo[2])
|
c.remainingContent = int(paddingInfo[1])<<8 | int(paddingInfo[2])
|
||||||
c.remainingPadding = int(paddingInfo[3])<<8 | int(paddingInfo[4])
|
c.remainingPadding = int(paddingInfo[3])<<8 | int(paddingInfo[4])
|
||||||
bufferIndex += 5
|
bufferIndex += 5
|
||||||
|
c.logger.Trace("Xtls Unpadding new block ", bufferIndex, " ", c.remainingContent, " padding ", c.remainingPadding, " ", c.currentCommand)
|
||||||
}
|
}
|
||||||
} else if c.remainingContent > 0 {
|
} else if c.remainingContent > 0 {
|
||||||
end := c.remainingContent
|
end := c.remainingContent
|
||||||
@@ -307,3 +350,7 @@ func (c *VisionConn) unPadding(buffer []byte) [][]byte {
|
|||||||
}
|
}
|
||||||
return buffers
|
return buffers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *VisionConn) Upstream() any {
|
||||||
|
return c.Conn
|
||||||
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user