mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-14 04:38:28 +10:00
Compare commits
11 Commits
v1.10.0-al
...
v1.10.0-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82269a4937 | ||
|
|
1b74c3c245 | ||
|
|
3d30ea02ae | ||
|
|
1abe60755b | ||
|
|
826ebd56a6 | ||
|
|
6635876db8 | ||
|
|
45c0c46479 | ||
|
|
9e727a440b | ||
|
|
0ee9ed78bb | ||
|
|
49e8e39ec1 | ||
|
|
8d8aa9d8e3 |
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
@@ -49,7 +49,7 @@ jobs:
|
||||
with:
|
||||
images: ghcr.io/sagernet/sing-box
|
||||
- name: Build and release Docker images
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
platforms: linux/386,linux/amd64,linux/arm64,linux/s390x
|
||||
context: .
|
||||
|
||||
2
Makefile
2
Makefile
@@ -199,7 +199,7 @@ publish_docs:
|
||||
|
||||
docs_install:
|
||||
python -m venv venv
|
||||
source ./venv/bin/activate && pip install --force-reinstall mkdocs-material=="9.*" mkdocs-static-i18n=="1.2.*"
|
||||
source ./venv/bin/active && pip install --force-reinstall mkdocs-material=="9.*" mkdocs-static-i18n=="1.2.*"
|
||||
|
||||
clean:
|
||||
rm -rf bin dist sing-box
|
||||
|
||||
@@ -4,13 +4,14 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/common/urltest"
|
||||
"github.com/sagernet/sing-dns"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
type ClashServer interface {
|
||||
@@ -55,15 +56,16 @@ func (s *SavedRuleSet) MarshalBinary() ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = varbin.Write(&buffer, binary.BigEndian, s.Content)
|
||||
err = rw.WriteUVariant(&buffer, uint64(len(s.Content)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buffer.Write(s.Content)
|
||||
err = binary.Write(&buffer, binary.BigEndian, s.LastUpdated.Unix())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = varbin.Write(&buffer, binary.BigEndian, s.LastEtag)
|
||||
err = rw.WriteVString(&buffer, s.LastEtag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -77,7 +79,12 @@ func (s *SavedRuleSet) UnmarshalBinary(data []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = varbin.Read(reader, binary.BigEndian, &s.Content)
|
||||
contentLen, err := rw.ReadUVariant(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Content = make([]byte, contentLen)
|
||||
_, err = io.ReadFull(reader, s.Content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -87,7 +94,7 @@ func (s *SavedRuleSet) UnmarshalBinary(data []byte) error {
|
||||
return err
|
||||
}
|
||||
s.LastUpdated = time.Unix(lastUpdated, 0)
|
||||
err = varbin.Read(reader, binary.BigEndian, &s.LastEtag)
|
||||
s.LastEtag, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -51,9 +51,7 @@ type InboundContext struct {
|
||||
|
||||
// rule cache
|
||||
|
||||
IPCIDRMatchSource bool
|
||||
IPCIDRAcceptEmpty bool
|
||||
|
||||
IPCIDRMatchSource bool
|
||||
SourceAddressMatch bool
|
||||
SourcePortMatch bool
|
||||
DestinationAddressMatch bool
|
||||
@@ -64,7 +62,6 @@ type InboundContext struct {
|
||||
|
||||
func (c *InboundContext) ResetRuleCache() {
|
||||
c.IPCIDRMatchSource = false
|
||||
c.IPCIDRAcceptEmpty = false
|
||||
c.SourceAddressMatch = false
|
||||
c.SourcePortMatch = false
|
||||
c.DestinationAddressMatch = false
|
||||
|
||||
@@ -22,5 +22,4 @@ type V2RayServerTransportHandler interface {
|
||||
|
||||
type V2RayClientTransport interface {
|
||||
DialContext(ctx context.Context) (net.Conn, error)
|
||||
Close() error
|
||||
}
|
||||
|
||||
6
box.go
6
box.go
@@ -204,7 +204,7 @@ func (s *Box) PreStart() error {
|
||||
defer func() {
|
||||
v := recover()
|
||||
if v != nil {
|
||||
println(err.Error())
|
||||
log.Error(E.Cause(err, "origin error"))
|
||||
debug.PrintStack()
|
||||
panic("panic on early close: " + fmt.Sprint(v))
|
||||
}
|
||||
@@ -223,9 +223,9 @@ func (s *Box) Start() error {
|
||||
defer func() {
|
||||
v := recover()
|
||||
if v != nil {
|
||||
println(err.Error())
|
||||
log.Error(E.Cause(err, "origin error"))
|
||||
debug.PrintStack()
|
||||
println("panic on early start: " + fmt.Sprint(v))
|
||||
panic("panic on early close: " + fmt.Sprint(v))
|
||||
}
|
||||
}()
|
||||
s.Close()
|
||||
|
||||
@@ -45,9 +45,7 @@ func (s *Box) startOutbounds() error {
|
||||
}
|
||||
started[outboundTag] = true
|
||||
canContinue = true
|
||||
if starter, isStarter := outboundToStart.(interface {
|
||||
Start() error
|
||||
}); isStarter {
|
||||
if starter, isStarter := outboundToStart.(common.Starter); isStarter {
|
||||
monitor.Start("initialize outbound/", outboundToStart.Type(), "[", outboundTag, "]")
|
||||
err := starter.Start()
|
||||
monitor.Finish()
|
||||
|
||||
@@ -93,7 +93,7 @@ func buildAndroid() {
|
||||
|
||||
const name = "libbox.aar"
|
||||
copyPath := filepath.Join("..", "sing-box-for-android", "app", "libs")
|
||||
if rw.IsDir(copyPath) {
|
||||
if rw.FileExists(copyPath) {
|
||||
copyPath, _ = filepath.Abs(copyPath)
|
||||
err = rw.CopyFile(name, filepath.Join(copyPath, name))
|
||||
if err != nil {
|
||||
@@ -134,7 +134,7 @@ func buildiOS() {
|
||||
}
|
||||
|
||||
copyPath := filepath.Join("..", "sing-box-for-apple")
|
||||
if rw.IsDir(copyPath) {
|
||||
if rw.FileExists(copyPath) {
|
||||
targetDir := filepath.Join(copyPath, "Libbox.xcframework")
|
||||
targetDir, _ = filepath.Abs(targetDir)
|
||||
os.RemoveAll(targetDir)
|
||||
|
||||
@@ -30,7 +30,7 @@ func FindSDK() {
|
||||
}
|
||||
for _, path := range searchPath {
|
||||
path = os.ExpandEnv(path)
|
||||
if rw.IsFile(filepath.Join(path, "licenses", "android-sdk-license")) {
|
||||
if rw.FileExists(filepath.Join(path, "licenses", "android-sdk-license")) {
|
||||
androidSDKPath = path
|
||||
break
|
||||
}
|
||||
@@ -60,7 +60,7 @@ func FindSDK() {
|
||||
func findNDK() bool {
|
||||
const fixedVersion = "26.2.11394342"
|
||||
const versionFile = "source.properties"
|
||||
if fixedPath := filepath.Join(androidSDKPath, "ndk", fixedVersion); rw.IsFile(filepath.Join(fixedPath, versionFile)) {
|
||||
if fixedPath := filepath.Join(androidSDKPath, "ndk", fixedVersion); rw.FileExists(filepath.Join(fixedPath, versionFile)) {
|
||||
androidNDKPath = fixedPath
|
||||
return true
|
||||
}
|
||||
@@ -86,7 +86,7 @@ func findNDK() bool {
|
||||
})
|
||||
for _, versionName := range versionNames {
|
||||
currentNDKPath := filepath.Join(androidSDKPath, "ndk", versionName)
|
||||
if rw.IsFile(filepath.Join(androidSDKPath, versionFile)) {
|
||||
if rw.FileExists(filepath.Join(androidSDKPath, versionFile)) {
|
||||
androidNDKPath = currentNDKPath
|
||||
log.Warn("reproducibility warning: using NDK version " + versionName + " instead of " + fixedVersion)
|
||||
return true
|
||||
@@ -100,11 +100,11 @@ var GoBinPath string
|
||||
func FindMobile() {
|
||||
goBin := filepath.Join(build.Default.GOPATH, "bin")
|
||||
if runtime.GOOS == "windows" {
|
||||
if !rw.IsFile(filepath.Join(goBin, "gobind.exe")) {
|
||||
if !rw.FileExists(filepath.Join(goBin, "gobind.exe")) {
|
||||
log.Fatal("missing gomobile installation")
|
||||
}
|
||||
} else {
|
||||
if !rw.IsFile(filepath.Join(goBin, "gobind")) {
|
||||
if !rw.FileExists(filepath.Join(goBin, "gobind")) {
|
||||
log.Fatal("missing gomobile installation")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,11 +54,7 @@ func merge(outputPath string) error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
err = rw.MkdirParent(outputPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile(outputPath, buffer.Bytes(), 0o644)
|
||||
err = rw.WriteFile(outputPath, buffer.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
var commandRuleSet = &cobra.Command{
|
||||
Use: "rule-set",
|
||||
Short: "Manage rule-sets",
|
||||
Short: "Manage rule sets",
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -55,10 +55,10 @@ func compileRuleSet(sourcePath string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ruleSet, err := plainRuleSet.Upgrade()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ruleSet := plainRuleSet.Upgrade()
|
||||
var outputPath string
|
||||
if flagRuleSetCompileOutput == flagRuleSetCompileDefaultOutput {
|
||||
if strings.HasSuffix(sourcePath, ".json") {
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/common/srs"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var flagRuleSetDecompileOutput string
|
||||
|
||||
const flagRuleSetDecompileDefaultOutput = "<file_name>.json"
|
||||
|
||||
var commandRuleSetDecompile = &cobra.Command{
|
||||
Use: "decompile [binary-path]",
|
||||
Short: "Decompile rule-set binary to json",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := decompileRuleSet(args[0])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
commandRuleSet.AddCommand(commandRuleSetDecompile)
|
||||
commandRuleSetDecompile.Flags().StringVarP(&flagRuleSetDecompileOutput, "output", "o", flagRuleSetDecompileDefaultOutput, "Output file")
|
||||
}
|
||||
|
||||
func decompileRuleSet(sourcePath string) error {
|
||||
var (
|
||||
reader io.Reader
|
||||
err error
|
||||
)
|
||||
if sourcePath == "stdin" {
|
||||
reader = os.Stdin
|
||||
} else {
|
||||
reader, err = os.Open(sourcePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
plainRuleSet, err := srs.Read(reader, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ruleSet := option.PlainRuleSetCompat{
|
||||
Version: C.RuleSetVersion1,
|
||||
Options: plainRuleSet,
|
||||
}
|
||||
var outputPath string
|
||||
if flagRuleSetDecompileOutput == flagRuleSetDecompileDefaultOutput {
|
||||
if strings.HasSuffix(sourcePath, ".srs") {
|
||||
outputPath = sourcePath[:len(sourcePath)-4] + ".json"
|
||||
} else {
|
||||
outputPath = sourcePath + ".json"
|
||||
}
|
||||
} else {
|
||||
outputPath = flagRuleSetDecompileOutput
|
||||
}
|
||||
outputFile, err := os.Create(outputPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encoder := json.NewEncoder(outputFile)
|
||||
encoder.SetIndent("", " ")
|
||||
err = encoder.Encode(ruleSet)
|
||||
if err != nil {
|
||||
outputFile.Close()
|
||||
os.Remove(outputPath)
|
||||
return err
|
||||
}
|
||||
outputFile.Close()
|
||||
return nil
|
||||
}
|
||||
@@ -12,9 +12,7 @@ import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/route"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -22,8 +20,8 @@ import (
|
||||
var flagRuleSetMatchFormat string
|
||||
|
||||
var commandRuleSetMatch = &cobra.Command{
|
||||
Use: "match <rule-set path> <IP address/domain>",
|
||||
Short: "Check if an IP address or a domain matches the rule-set",
|
||||
Use: "match <rule-set path> <domain>",
|
||||
Short: "Check if a domain matches the rule set",
|
||||
Args: cobra.ExactArgs(2),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := ruleSetMatch(args[0], args[1])
|
||||
@@ -63,24 +61,14 @@ func ruleSetMatch(sourcePath string, domain string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
plainRuleSet, err = compat.Upgrade()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
plainRuleSet = compat.Upgrade()
|
||||
case C.RuleSetFormatBinary:
|
||||
plainRuleSet, err = srs.Read(bytes.NewReader(content), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return E.New("unknown rule-set format: ", flagRuleSetMatchFormat)
|
||||
}
|
||||
ipAddress := M.ParseAddr(domain)
|
||||
var metadata adapter.InboundContext
|
||||
if ipAddress.IsValid() {
|
||||
metadata.Destination = M.SocksaddrFrom(ipAddress, 0)
|
||||
} else {
|
||||
metadata.Domain = domain
|
||||
return E.New("unknown rule set format: ", flagRuleSetMatchFormat)
|
||||
}
|
||||
for i, ruleOptions := range plainRuleSet.Rules {
|
||||
var currentRule adapter.HeadlessRule
|
||||
@@ -88,8 +76,10 @@ func ruleSetMatch(sourcePath string, domain string) error {
|
||||
if err != nil {
|
||||
return E.Cause(err, "parse rule_set.rules.[", i, "]")
|
||||
}
|
||||
if currentRule.Match(&metadata) {
|
||||
println(F.ToString("match rules.[", i, "]: ", currentRule))
|
||||
if currentRule.Match(&adapter.InboundContext{
|
||||
Domain: domain,
|
||||
}) {
|
||||
println("match rules.[", i, "]: "+currentRule.String())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -109,7 +109,7 @@ func readConfigAndMerge() (option.Options, error) {
|
||||
}
|
||||
var mergedMessage json.RawMessage
|
||||
for _, options := range optionsList {
|
||||
mergedMessage, err = badjson.MergeJSON(options.options.RawMessage, mergedMessage, false)
|
||||
mergedMessage, err = badjson.MergeJSON(options.options.RawMessage, mergedMessage)
|
||||
if err != nil {
|
||||
return option.Options{}, E.Cause(err, "merge config at ", options.path)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
package geosite
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
type Reader struct {
|
||||
@@ -38,36 +34,45 @@ func Open(path string) (*Reader, []string, error) {
|
||||
return reader, codes, nil
|
||||
}
|
||||
|
||||
type geositeMetadata struct {
|
||||
Code string
|
||||
Index uint64
|
||||
Length uint64
|
||||
}
|
||||
|
||||
func (r *Reader) readMetadata() error {
|
||||
reader := bufio.NewReader(r.reader)
|
||||
version, err := reader.ReadByte()
|
||||
version, err := rw.ReadByte(r.reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if version != 0 {
|
||||
return E.New("unknown version")
|
||||
}
|
||||
metadataEntries, err := varbin.ReadValue[[]geositeMetadata](reader, binary.BigEndian)
|
||||
entryLength, err := rw.ReadUVariant(r.reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
keys := make([]string, entryLength)
|
||||
domainIndex := make(map[string]int)
|
||||
domainLength := make(map[string]int)
|
||||
for _, entry := range metadataEntries {
|
||||
domainIndex[entry.Code] = int(entry.Index)
|
||||
domainLength[entry.Code] = int(entry.Length)
|
||||
for i := 0; i < int(entryLength); i++ {
|
||||
var (
|
||||
code string
|
||||
codeIndex uint64
|
||||
codeLength uint64
|
||||
)
|
||||
code, err = rw.ReadVString(r.reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
keys[i] = code
|
||||
codeIndex, err = rw.ReadUVariant(r.reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
codeLength, err = rw.ReadUVariant(r.reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
domainIndex[code] = int(codeIndex)
|
||||
domainLength[code] = int(codeLength)
|
||||
}
|
||||
r.domainIndex = domainIndex
|
||||
r.domainLength = domainLength
|
||||
if reader.Buffered() > 0 {
|
||||
return common.Error(r.reader.Seek(int64(-reader.Buffered()), io.SeekCurrent))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -80,28 +85,27 @@ func (r *Reader) Read(code string) ([]Item, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
counter := &readCounter{Reader: r.reader}
|
||||
domain, err := varbin.ReadValue[[]Item](bufio.NewReader(counter), binary.BigEndian)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
counter := &rw.ReadCounter{Reader: r.reader}
|
||||
domain := make([]Item, r.domainLength[code])
|
||||
for i := range domain {
|
||||
var (
|
||||
item Item
|
||||
err error
|
||||
)
|
||||
item.Type, err = rw.ReadByte(counter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item.Value, err = rw.ReadVString(counter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
domain[i] = item
|
||||
}
|
||||
_, err = r.reader.Seek(int64(-index)-counter.count, io.SeekCurrent)
|
||||
_, err = r.reader.Seek(int64(-index)-counter.Count(), io.SeekCurrent)
|
||||
return domain, err
|
||||
}
|
||||
|
||||
func (r *Reader) Upstream() any {
|
||||
return r.reader
|
||||
}
|
||||
|
||||
type readCounter struct {
|
||||
io.Reader
|
||||
count int64
|
||||
}
|
||||
|
||||
func (r *readCounter) Read(p []byte) (n int, err error) {
|
||||
n, err = r.Reader.Read(p)
|
||||
if n > 0 {
|
||||
atomic.AddInt64(&r.count, int64(n))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,14 +2,13 @@ package geosite
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
func Write(writer varbin.Writer, domains map[string][]Item) error {
|
||||
func Write(writer io.Writer, domains map[string][]Item) error {
|
||||
keys := make([]string, 0, len(domains))
|
||||
for code := range domains {
|
||||
keys = append(keys, code)
|
||||
@@ -20,26 +19,38 @@ func Write(writer varbin.Writer, domains map[string][]Item) error {
|
||||
index := make(map[string]int)
|
||||
for _, code := range keys {
|
||||
index[code] = content.Len()
|
||||
err := varbin.Write(content, binary.BigEndian, domains[code])
|
||||
for _, domain := range domains[code] {
|
||||
content.WriteByte(domain.Type)
|
||||
err := rw.WriteVString(content, domain.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err := rw.WriteByte(writer, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = rw.WriteUVariant(writer, uint64(len(keys)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, code := range keys {
|
||||
err = rw.WriteVString(writer, code)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := writer.WriteByte(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = varbin.Write(writer, binary.BigEndian, common.Map(keys, func(it string) *geositeMetadata {
|
||||
return &geositeMetadata{
|
||||
Code: it,
|
||||
Index: uint64(index[it]),
|
||||
Length: uint64(len(domains[it])),
|
||||
err = rw.WriteUVariant(writer, uint64(index[code]))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = rw.WriteUVariant(writer, uint64(len(domains[code])))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = writer.Write(content.Bytes())
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package sniff
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
)
|
||||
|
||||
func DTLSRecord(ctx context.Context, packet []byte) (*adapter.InboundContext, error) {
|
||||
const fixedHeaderSize = 13
|
||||
if len(packet) < fixedHeaderSize {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
contentType := packet[0]
|
||||
switch contentType {
|
||||
case 20, 21, 22, 23, 25:
|
||||
default:
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
versionMajor := packet[1]
|
||||
if versionMajor != 0xfe {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
versionMinor := packet[2]
|
||||
if versionMinor != 0xff && versionMinor != 0xfd {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
return &adapter.InboundContext{Protocol: C.ProtocolDTLS}, nil
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package sniff_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/sagernet/sing-box/common/sniff"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSniffDTLSClientHello(t *testing.T) {
|
||||
t.Parallel()
|
||||
packet, err := hex.DecodeString("16fefd0000000000000000007e010000720000000000000072fefd668a43523798e064bd806d0c87660de9c611a59bbdfc3892c4e072d94f2cafc40000000cc02bc02fc00ac014c02cc0300100003c000d0010000e0403050306030401050106010807ff01000100000a00080006001d00170018000b00020100000e000900060008000700010000170000")
|
||||
require.NoError(t, err)
|
||||
metadata, err := sniff.DTLSRecord(context.Background(), packet)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, metadata.Protocol, C.ProtocolDTLS)
|
||||
}
|
||||
|
||||
func TestSniffDTLSClientApplicationData(t *testing.T) {
|
||||
t.Parallel()
|
||||
packet, err := hex.DecodeString("17fefd000100000000000100440001000000000001a4f682b77ecadd10f3f3a2f78d90566212366ff8209fd77314f5a49352f9bb9bd12f4daba0b4736ae29e46b9714d3b424b3e6d0234736619b5aa0d3f")
|
||||
require.NoError(t, err)
|
||||
metadata, err := sniff.DTLSRecord(context.Background(), packet)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, metadata.Protocol, C.ProtocolDTLS)
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package srs
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"compress/zlib"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
@@ -12,7 +11,7 @@ import (
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/domain"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
|
||||
"go4.org/netipx"
|
||||
)
|
||||
@@ -39,14 +38,14 @@ const (
|
||||
ruleItemFinal uint8 = 0xFF
|
||||
)
|
||||
|
||||
func Read(reader io.Reader, recover bool) (ruleSet option.PlainRuleSet, err error) {
|
||||
func Read(reader io.Reader, recovery bool) (ruleSet option.PlainRuleSet, err error) {
|
||||
var magicBytes [3]byte
|
||||
_, err = io.ReadFull(reader, magicBytes[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if magicBytes != MagicBytes {
|
||||
err = E.New("invalid sing-box rule-set file")
|
||||
err = E.New("invalid sing-box rule set file")
|
||||
return
|
||||
}
|
||||
var version uint8
|
||||
@@ -61,14 +60,13 @@ func Read(reader io.Reader, recover bool) (ruleSet option.PlainRuleSet, err erro
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
bReader := bufio.NewReader(zReader)
|
||||
length, err := binary.ReadUvarint(bReader)
|
||||
length, err := rw.ReadUVariant(zReader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ruleSet.Rules = make([]option.HeadlessRule, length)
|
||||
for i := uint64(0); i < length; i++ {
|
||||
ruleSet.Rules[i], err = readRule(bReader, recover)
|
||||
ruleSet.Rules[i], err = readRule(zReader, recovery)
|
||||
if err != nil {
|
||||
err = E.Cause(err, "read rule[", i, "]")
|
||||
return
|
||||
@@ -90,25 +88,20 @@ func Write(writer io.Writer, ruleSet option.PlainRuleSet) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bWriter := bufio.NewWriter(zWriter)
|
||||
_, err = varbin.WriteUvarint(bWriter, uint64(len(ruleSet.Rules)))
|
||||
err = rw.WriteUVariant(zWriter, uint64(len(ruleSet.Rules)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, rule := range ruleSet.Rules {
|
||||
err = writeRule(bWriter, rule)
|
||||
err = writeRule(zWriter, rule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = bWriter.Flush()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return zWriter.Close()
|
||||
}
|
||||
|
||||
func readRule(reader varbin.Reader, recover bool) (rule option.HeadlessRule, err error) {
|
||||
func readRule(reader io.Reader, recovery bool) (rule option.HeadlessRule, err error) {
|
||||
var ruleType uint8
|
||||
err = binary.Read(reader, binary.BigEndian, &ruleType)
|
||||
if err != nil {
|
||||
@@ -117,17 +110,17 @@ func readRule(reader varbin.Reader, recover bool) (rule option.HeadlessRule, err
|
||||
switch ruleType {
|
||||
case 0:
|
||||
rule.Type = C.RuleTypeDefault
|
||||
rule.DefaultOptions, err = readDefaultRule(reader, recover)
|
||||
rule.DefaultOptions, err = readDefaultRule(reader, recovery)
|
||||
case 1:
|
||||
rule.Type = C.RuleTypeLogical
|
||||
rule.LogicalOptions, err = readLogicalRule(reader, recover)
|
||||
rule.LogicalOptions, err = readLogicalRule(reader, recovery)
|
||||
default:
|
||||
err = E.New("unknown rule type: ", ruleType)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func writeRule(writer varbin.Writer, rule option.HeadlessRule) error {
|
||||
func writeRule(writer io.Writer, rule option.HeadlessRule) error {
|
||||
switch rule.Type {
|
||||
case C.RuleTypeDefault:
|
||||
return writeDefaultRule(writer, rule.DefaultOptions)
|
||||
@@ -138,7 +131,7 @@ func writeRule(writer varbin.Writer, rule option.HeadlessRule) error {
|
||||
}
|
||||
}
|
||||
|
||||
func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHeadlessRule, err error) {
|
||||
func readDefaultRule(reader io.Reader, recovery bool) (rule option.DefaultHeadlessRule, err error) {
|
||||
var lastItemType uint8
|
||||
for {
|
||||
var itemType uint8
|
||||
@@ -165,9 +158,6 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
|
||||
return
|
||||
}
|
||||
rule.DomainMatcher = matcher
|
||||
if recover {
|
||||
rule.Domain, rule.DomainSuffix = matcher.Dump()
|
||||
}
|
||||
case ruleItemDomainKeyword:
|
||||
rule.DomainKeyword, err = readRuleItemString(reader)
|
||||
case ruleItemDomainRegex:
|
||||
@@ -177,7 +167,7 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if recover {
|
||||
if recovery {
|
||||
rule.SourceIPCIDR = common.Map(rule.SourceIPSet.Prefixes(), netip.Prefix.String)
|
||||
}
|
||||
case ruleItemIPCIDR:
|
||||
@@ -185,7 +175,7 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if recover {
|
||||
if recovery {
|
||||
rule.IPCIDR = common.Map(rule.IPSet.Prefixes(), netip.Prefix.String)
|
||||
}
|
||||
case ruleItemSourcePort:
|
||||
@@ -219,7 +209,7 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
|
||||
}
|
||||
}
|
||||
|
||||
func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule) error {
|
||||
func writeDefaultRule(writer io.Writer, rule option.DefaultHeadlessRule) error {
|
||||
err := binary.Write(writer, binary.BigEndian, uint8(0))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -337,31 +327,73 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func readRuleItemString(reader varbin.Reader) ([]string, error) {
|
||||
return varbin.ReadValue[[]string](reader, binary.BigEndian)
|
||||
func readRuleItemString(reader io.Reader) ([]string, error) {
|
||||
length, err := rw.ReadUVariant(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value := make([]string, length)
|
||||
for i := uint64(0); i < length; i++ {
|
||||
value[i], err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func writeRuleItemString(writer varbin.Writer, itemType uint8, value []string) error {
|
||||
err := writer.WriteByte(itemType)
|
||||
func writeRuleItemString(writer io.Writer, itemType uint8, value []string) error {
|
||||
err := binary.Write(writer, binary.BigEndian, itemType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return varbin.Write(writer, binary.BigEndian, value)
|
||||
}
|
||||
|
||||
func readRuleItemUint16(reader varbin.Reader) ([]uint16, error) {
|
||||
return varbin.ReadValue[[]uint16](reader, binary.BigEndian)
|
||||
}
|
||||
|
||||
func writeRuleItemUint16(writer varbin.Writer, itemType uint8, value []uint16) error {
|
||||
err := writer.WriteByte(itemType)
|
||||
err = rw.WriteUVariant(writer, uint64(len(value)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return varbin.Write(writer, binary.BigEndian, value)
|
||||
for _, item := range value {
|
||||
err = rw.WriteVString(writer, item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeRuleItemCIDR(writer varbin.Writer, itemType uint8, value []string) error {
|
||||
func readRuleItemUint16(reader io.Reader) ([]uint16, error) {
|
||||
length, err := rw.ReadUVariant(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value := make([]uint16, length)
|
||||
for i := uint64(0); i < length; i++ {
|
||||
err = binary.Read(reader, binary.BigEndian, &value[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func writeRuleItemUint16(writer io.Writer, itemType uint8, value []uint16) error {
|
||||
err := binary.Write(writer, binary.BigEndian, itemType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = rw.WriteUVariant(writer, uint64(len(value)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, item := range value {
|
||||
err = binary.Write(writer, binary.BigEndian, item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeRuleItemCIDR(writer io.Writer, itemType uint8, value []string) error {
|
||||
var builder netipx.IPSetBuilder
|
||||
for i, prefixString := range value {
|
||||
prefix, err := netip.ParsePrefix(prefixString)
|
||||
@@ -387,8 +419,9 @@ func writeRuleItemCIDR(writer varbin.Writer, itemType uint8, value []string) err
|
||||
return writeIPSet(writer, ipSet)
|
||||
}
|
||||
|
||||
func readLogicalRule(reader varbin.Reader, recovery bool) (logicalRule option.LogicalHeadlessRule, err error) {
|
||||
mode, err := reader.ReadByte()
|
||||
func readLogicalRule(reader io.Reader, recovery bool) (logicalRule option.LogicalHeadlessRule, err error) {
|
||||
var mode uint8
|
||||
err = binary.Read(reader, binary.BigEndian, &mode)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -401,7 +434,7 @@ func readLogicalRule(reader varbin.Reader, recovery bool) (logicalRule option.Lo
|
||||
err = E.New("unknown logical mode: ", mode)
|
||||
return
|
||||
}
|
||||
length, err := binary.ReadUvarint(reader)
|
||||
length, err := rw.ReadUVariant(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -420,7 +453,7 @@ func readLogicalRule(reader varbin.Reader, recovery bool) (logicalRule option.Lo
|
||||
return
|
||||
}
|
||||
|
||||
func writeLogicalRule(writer varbin.Writer, logicalRule option.LogicalHeadlessRule) error {
|
||||
func writeLogicalRule(writer io.Writer, logicalRule option.LogicalHeadlessRule) error {
|
||||
err := binary.Write(writer, binary.BigEndian, uint8(1))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -436,7 +469,7 @@ func writeLogicalRule(writer varbin.Writer, logicalRule option.LogicalHeadlessRu
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = varbin.WriteUvarint(writer, uint64(len(logicalRule.Rules)))
|
||||
err = rw.WriteUVariant(writer, uint64(len(logicalRule.Rules)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2,13 +2,11 @@ package srs
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net/netip"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
|
||||
"go4.org/netipx"
|
||||
)
|
||||
@@ -22,57 +20,94 @@ type myIPRange struct {
|
||||
to netip.Addr
|
||||
}
|
||||
|
||||
type myIPRangeData struct {
|
||||
From []byte
|
||||
To []byte
|
||||
}
|
||||
|
||||
func readIPSet(reader varbin.Reader) (*netipx.IPSet, error) {
|
||||
version, err := reader.ReadByte()
|
||||
func readIPSet(reader io.Reader) (*netipx.IPSet, error) {
|
||||
var version uint8
|
||||
err := binary.Read(reader, binary.BigEndian, &version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if version != 1 {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
// WTF why using uint64 here
|
||||
var length uint64
|
||||
err = binary.Read(reader, binary.BigEndian, &length)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ranges := make([]myIPRangeData, length)
|
||||
err = varbin.Read(reader, binary.BigEndian, &ranges)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mySet := &myIPSet{
|
||||
rr: make([]myIPRange, len(ranges)),
|
||||
rr: make([]myIPRange, length),
|
||||
}
|
||||
for i, rangeData := range ranges {
|
||||
mySet.rr[i].from = M.AddrFromIP(rangeData.From)
|
||||
mySet.rr[i].to = M.AddrFromIP(rangeData.To)
|
||||
for i := uint64(0); i < length; i++ {
|
||||
var (
|
||||
fromLen uint64
|
||||
toLen uint64
|
||||
fromAddr netip.Addr
|
||||
toAddr netip.Addr
|
||||
)
|
||||
fromLen, err = rw.ReadUVariant(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fromBytes := make([]byte, fromLen)
|
||||
_, err = io.ReadFull(reader, fromBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = fromAddr.UnmarshalBinary(fromBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
toLen, err = rw.ReadUVariant(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
toBytes := make([]byte, toLen)
|
||||
_, err = io.ReadFull(reader, toBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = toAddr.UnmarshalBinary(toBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mySet.rr[i] = myIPRange{fromAddr, toAddr}
|
||||
}
|
||||
return (*netipx.IPSet)(unsafe.Pointer(mySet)), nil
|
||||
}
|
||||
|
||||
func writeIPSet(writer varbin.Writer, set *netipx.IPSet) error {
|
||||
err := writer.WriteByte(1)
|
||||
func writeIPSet(writer io.Writer, set *netipx.IPSet) error {
|
||||
err := binary.Write(writer, binary.BigEndian, uint8(1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dataList := common.Map((*myIPSet)(unsafe.Pointer(set)).rr, func(rr myIPRange) myIPRangeData {
|
||||
return myIPRangeData{
|
||||
From: rr.from.AsSlice(),
|
||||
To: rr.to.AsSlice(),
|
||||
mySet := (*myIPSet)(unsafe.Pointer(set))
|
||||
err = binary.Write(writer, binary.BigEndian, uint64(len(mySet.rr)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, rr := range mySet.rr {
|
||||
var (
|
||||
fromBinary []byte
|
||||
toBinary []byte
|
||||
)
|
||||
fromBinary, err = rr.from.MarshalBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
})
|
||||
err = binary.Write(writer, binary.BigEndian, uint64(len(dataList)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, data := range dataList {
|
||||
err = varbin.Write(writer, binary.BigEndian, data)
|
||||
err = rw.WriteUVariant(writer, uint64(len(fromBinary)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = writer.Write(fromBinary)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
toBinary, err = rr.to.MarshalBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = rw.WriteUVariant(writer, uint64(len(toBinary)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = writer.Write(toBinary)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -11,11 +11,12 @@ import (
|
||||
"strings"
|
||||
|
||||
cftls "github.com/sagernet/cloudflare-tls"
|
||||
"github.com/sagernet/fswatch"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/ntp"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
type echServerConfig struct {
|
||||
@@ -25,8 +26,9 @@ type echServerConfig struct {
|
||||
key []byte
|
||||
certificatePath string
|
||||
keyPath string
|
||||
watcher *fsnotify.Watcher
|
||||
echKeyPath string
|
||||
watcher *fswatch.Watcher
|
||||
echWatcher *fsnotify.Watcher
|
||||
}
|
||||
|
||||
func (c *echServerConfig) ServerName() string {
|
||||
@@ -64,84 +66,146 @@ func (c *echServerConfig) Clone() Config {
|
||||
}
|
||||
|
||||
func (c *echServerConfig) Start() error {
|
||||
err := c.startWatcher()
|
||||
if err != nil {
|
||||
c.logger.Warn("create credentials watcher: ", err)
|
||||
if c.certificatePath != "" && c.keyPath != "" {
|
||||
err := c.startWatcher()
|
||||
if err != nil {
|
||||
c.logger.Warn("create fsnotify watcher: ", err)
|
||||
}
|
||||
}
|
||||
if c.echKeyPath != "" {
|
||||
err := c.startECHWatcher()
|
||||
if err != nil {
|
||||
c.logger.Warn("create fsnotify watcher: ", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *echServerConfig) startWatcher() error {
|
||||
var watchPath []string
|
||||
if c.certificatePath != "" {
|
||||
watchPath = append(watchPath, c.certificatePath)
|
||||
}
|
||||
if c.keyPath != "" {
|
||||
watchPath = append(watchPath, c.keyPath)
|
||||
}
|
||||
if c.echKeyPath != "" {
|
||||
watchPath = append(watchPath, c.echKeyPath)
|
||||
}
|
||||
if len(watchPath) == 0 {
|
||||
return nil
|
||||
}
|
||||
watcher, err := fswatch.NewWatcher(fswatch.Options{
|
||||
Path: watchPath,
|
||||
Callback: func(path string) {
|
||||
err := c.credentialsUpdated(path)
|
||||
if err != nil {
|
||||
c.logger.Error(E.Cause(err, "reload credentials from ", path))
|
||||
}
|
||||
},
|
||||
})
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.watcher = watcher
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *echServerConfig) credentialsUpdated(path string) error {
|
||||
if path == c.certificatePath || path == c.keyPath {
|
||||
if path == c.certificatePath {
|
||||
certificate, err := os.ReadFile(c.certificatePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.certificate = certificate
|
||||
} else {
|
||||
key, err := os.ReadFile(c.keyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.key = key
|
||||
}
|
||||
keyPair, err := cftls.X509KeyPair(c.certificate, c.key)
|
||||
if err != nil {
|
||||
return E.Cause(err, "parse key pair")
|
||||
}
|
||||
c.config.Certificates = []cftls.Certificate{keyPair}
|
||||
c.logger.Info("reloaded TLS certificate")
|
||||
} else {
|
||||
echKeyContent, err := os.ReadFile(c.echKeyPath)
|
||||
if c.certificatePath != "" {
|
||||
err = watcher.Add(c.certificatePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
block, rest := pem.Decode(echKeyContent)
|
||||
if block == nil || block.Type != "ECH KEYS" || len(rest) > 0 {
|
||||
return E.New("invalid ECH keys pem")
|
||||
}
|
||||
echKeys, err := cftls.EXP_UnmarshalECHKeys(block.Bytes)
|
||||
if err != nil {
|
||||
return E.Cause(err, "parse ECH keys")
|
||||
}
|
||||
echKeySet, err := cftls.EXP_NewECHKeySet(echKeys)
|
||||
if err != nil {
|
||||
return E.Cause(err, "create ECH key set")
|
||||
}
|
||||
c.config.ServerECHProvider = echKeySet
|
||||
c.logger.Info("reloaded ECH keys")
|
||||
}
|
||||
if c.keyPath != "" {
|
||||
err = watcher.Add(c.keyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
c.watcher = watcher
|
||||
go c.loopUpdate()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *echServerConfig) loopUpdate() {
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-c.watcher.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if event.Op&fsnotify.Write != fsnotify.Write {
|
||||
continue
|
||||
}
|
||||
err := c.reloadKeyPair()
|
||||
if err != nil {
|
||||
c.logger.Error(E.Cause(err, "reload TLS key pair"))
|
||||
}
|
||||
case err, ok := <-c.watcher.Errors:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
c.logger.Error(E.Cause(err, "fsnotify error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *echServerConfig) reloadKeyPair() error {
|
||||
if c.certificatePath != "" {
|
||||
certificate, err := os.ReadFile(c.certificatePath)
|
||||
if err != nil {
|
||||
return E.Cause(err, "reload certificate from ", c.certificatePath)
|
||||
}
|
||||
c.certificate = certificate
|
||||
}
|
||||
if c.keyPath != "" {
|
||||
key, err := os.ReadFile(c.keyPath)
|
||||
if err != nil {
|
||||
return E.Cause(err, "reload key from ", c.keyPath)
|
||||
}
|
||||
c.key = key
|
||||
}
|
||||
keyPair, err := cftls.X509KeyPair(c.certificate, c.key)
|
||||
if err != nil {
|
||||
return E.Cause(err, "reload key pair")
|
||||
}
|
||||
c.config.Certificates = []cftls.Certificate{keyPair}
|
||||
c.logger.Info("reloaded TLS certificate")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *echServerConfig) startECHWatcher() error {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = watcher.Add(c.echKeyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.echWatcher = watcher
|
||||
go c.loopECHUpdate()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *echServerConfig) loopECHUpdate() {
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-c.echWatcher.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if event.Op&fsnotify.Write != fsnotify.Write {
|
||||
continue
|
||||
}
|
||||
err := c.reloadECHKey()
|
||||
if err != nil {
|
||||
c.logger.Error(E.Cause(err, "reload ECH key"))
|
||||
}
|
||||
case err, ok := <-c.echWatcher.Errors:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
c.logger.Error(E.Cause(err, "fsnotify error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *echServerConfig) reloadECHKey() error {
|
||||
echKeyContent, err := os.ReadFile(c.echKeyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
block, rest := pem.Decode(echKeyContent)
|
||||
if block == nil || block.Type != "ECH KEYS" || len(rest) > 0 {
|
||||
return E.New("invalid ECH keys pem")
|
||||
}
|
||||
echKeys, err := cftls.EXP_UnmarshalECHKeys(block.Bytes)
|
||||
if err != nil {
|
||||
return E.Cause(err, "parse ECH keys")
|
||||
}
|
||||
echKeySet, err := cftls.EXP_NewECHKeySet(echKeys)
|
||||
if err != nil {
|
||||
return E.Cause(err, "create ECH key set")
|
||||
}
|
||||
c.config.ServerECHProvider = echKeySet
|
||||
c.logger.Info("reloaded ECH keys")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -149,7 +213,12 @@ func (c *echServerConfig) Close() error {
|
||||
var err error
|
||||
if c.watcher != nil {
|
||||
err = E.Append(err, c.watcher.Close(), func(err error) error {
|
||||
return E.Cause(err, "close credentials watcher")
|
||||
return E.Cause(err, "close certificate watcher")
|
||||
})
|
||||
}
|
||||
if c.echWatcher != nil {
|
||||
err = E.Append(err, c.echWatcher.Close(), func(err error) error {
|
||||
return E.Cause(err, "close ECH key watcher")
|
||||
})
|
||||
}
|
||||
return err
|
||||
|
||||
@@ -7,13 +7,14 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/fswatch"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/ntp"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
var errInsecureUnused = E.New("tls: insecure unused")
|
||||
@@ -26,7 +27,7 @@ type STDServerConfig struct {
|
||||
key []byte
|
||||
certificatePath string
|
||||
keyPath string
|
||||
watcher *fswatch.Watcher
|
||||
watcher *fsnotify.Watcher
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) ServerName() string {
|
||||
@@ -87,37 +88,59 @@ func (c *STDServerConfig) Start() error {
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) startWatcher() error {
|
||||
var watchPath []string
|
||||
if c.certificatePath != "" {
|
||||
watchPath = append(watchPath, c.certificatePath)
|
||||
}
|
||||
if c.keyPath != "" {
|
||||
watchPath = append(watchPath, c.keyPath)
|
||||
}
|
||||
watcher, err := fswatch.NewWatcher(fswatch.Options{
|
||||
Path: watchPath,
|
||||
Callback: func(path string) {
|
||||
err := c.certificateUpdated(path)
|
||||
if err != nil {
|
||||
c.logger.Error(err)
|
||||
}
|
||||
},
|
||||
})
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.certificatePath != "" {
|
||||
err = watcher.Add(c.certificatePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.keyPath != "" {
|
||||
err = watcher.Add(c.keyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
c.watcher = watcher
|
||||
go c.loopUpdate()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) certificateUpdated(path string) error {
|
||||
if path == c.certificatePath {
|
||||
func (c *STDServerConfig) loopUpdate() {
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-c.watcher.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if event.Op&fsnotify.Write != fsnotify.Write {
|
||||
continue
|
||||
}
|
||||
err := c.reloadKeyPair()
|
||||
if err != nil {
|
||||
c.logger.Error(E.Cause(err, "reload TLS key pair"))
|
||||
}
|
||||
case err, ok := <-c.watcher.Errors:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
c.logger.Error(E.Cause(err, "fsnotify error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *STDServerConfig) reloadKeyPair() error {
|
||||
if c.certificatePath != "" {
|
||||
certificate, err := os.ReadFile(c.certificatePath)
|
||||
if err != nil {
|
||||
return E.Cause(err, "reload certificate from ", c.certificatePath)
|
||||
}
|
||||
c.certificate = certificate
|
||||
} else if path == c.keyPath {
|
||||
}
|
||||
if c.keyPath != "" {
|
||||
key, err := os.ReadFile(c.keyPath)
|
||||
if err != nil {
|
||||
return E.Cause(err, "reload key from ", c.keyPath)
|
||||
|
||||
@@ -13,14 +13,14 @@ var resourcePaths []string
|
||||
|
||||
func FindPath(name string) (string, bool) {
|
||||
name = os.ExpandEnv(name)
|
||||
if rw.IsFile(name) {
|
||||
if rw.FileExists(name) {
|
||||
return name, true
|
||||
}
|
||||
for _, dir := range resourcePaths {
|
||||
if path := filepath.Join(dir, dirName, name); rw.IsFile(path) {
|
||||
if path := filepath.Join(dir, dirName, name); rw.FileExists(path) {
|
||||
return path, true
|
||||
}
|
||||
if path := filepath.Join(dir, name); rw.IsFile(path) {
|
||||
if path := filepath.Join(dir, name); rw.FileExists(path) {
|
||||
return path, true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,5 +7,4 @@ const (
|
||||
ProtocolDNS = "dns"
|
||||
ProtocolSTUN = "stun"
|
||||
ProtocolBitTorrent = "bittorrent"
|
||||
ProtocolDTLS = "dtls"
|
||||
)
|
||||
|
||||
@@ -11,7 +11,6 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
RuleSetTypeInline = "inline"
|
||||
RuleSetTypeLocal = "local"
|
||||
RuleSetTypeRemote = "remote"
|
||||
RuleSetVersion1 = 1
|
||||
|
||||
@@ -2,76 +2,10 @@
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
#### 1.10.0-alpha.21
|
||||
#### 1.10.0-alpha.15
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.10.0-alpha.20
|
||||
|
||||
* Add DTLS sniffer
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.10.0-alpha.19
|
||||
|
||||
* Add `rule-set decompile` command
|
||||
* Add IP address support for `rule-set match` command
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.10.0-alpha.18
|
||||
|
||||
* Add new `inline` rule-set type **1**
|
||||
* Add auto reload support for local rule-set
|
||||
* Update fsnotify usages **2**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
The new [rule-set] type inline (which also becomes the default type)
|
||||
allows you to write headless rules directly without creating a rule-set file.
|
||||
|
||||
[rule-set]: /configuration/rule-set/
|
||||
|
||||
**2**:
|
||||
|
||||
sing-box now uses fsnotify correctly and will not cancel watching
|
||||
if the target file is deleted or recreated via rename (e.g. `mv`).
|
||||
|
||||
This affects all path options that support reload, including
|
||||
`tls.certificate_path`, `tls.key_path`, `tls.ech.key_path` and `rule_set.path`.
|
||||
|
||||
#### 1.10.0-alpha.17
|
||||
|
||||
* Some chaotic changes **1**
|
||||
* `rule_set_ipcidr_match_source` rule items are renamed **2**
|
||||
* Add `rule_set_ip_cidr_accept_empty` DNS address filter rule item **3**
|
||||
* Update quic-go to v0.45.1
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
Something may be broken, please actively report problems with this version.
|
||||
|
||||
**2**:
|
||||
|
||||
`rule_set_ipcidr_match_source` route and DNS rule items are renamed to
|
||||
`rule_set_ip_cidr_match_source` and will be remove in sing-box 1.11.0.
|
||||
|
||||
**3**:
|
||||
|
||||
See [DNS Rule](/configuration/dns/rule/#rule_set_ip_cidr_accept_empty).
|
||||
|
||||
#### 1.10.0-alpha.16
|
||||
|
||||
* Add custom options for `auto-route` and `auto-redirect` **1**
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
See [iproute2_table_index](/configuration/inbound/tun/#iproute2_table_index),
|
||||
[iproute2_rule_index](/configuration/inbound/tun/#iproute2_rule_index),
|
||||
[auto_redirect_input_mark](/configuration/inbound/tun/#auto_redirect_input_mark) and
|
||||
[auto_redirect_output_mark](/configuration/inbound/tun/#auto_redirect_output_mark).
|
||||
|
||||
#### 1.10.0-alpha.13
|
||||
|
||||
* TUN address fields are merged **1**
|
||||
@@ -486,7 +420,7 @@ See [Address Filter Fields](/configuration/dns/rule#address-filter-fields).
|
||||
Important changes since 1.7:
|
||||
|
||||
* Migrate cache file from Clash API to independent options **1**
|
||||
* Introducing [rule-set](/configuration/rule-set/) **2**
|
||||
* Introducing [Rule Set](/configuration/rule-set/) **2**
|
||||
* Add `sing-box geoip`, `sing-box geosite` and `sing-box rule-set` commands **3**
|
||||
* Allow nested logical rules **4**
|
||||
* Independent `source_ip_is_private` and `ip_is_private` rules **5**
|
||||
@@ -506,7 +440,7 @@ See [Cache File](/configuration/experimental/cache-file/) and
|
||||
|
||||
**2**:
|
||||
|
||||
rule-set is independent collections of rules that can be compiled into binaries to improve performance.
|
||||
Rule set is independent collections of rules that can be compiled into binaries to improve performance.
|
||||
Compared to legacy GeoIP and Geosite resources,
|
||||
it can include more types of rules, load faster,
|
||||
use less memory, and update automatically.
|
||||
@@ -514,16 +448,16 @@ use less memory, and update automatically.
|
||||
See [Route#rule_set](/configuration/route/#rule_set),
|
||||
[Route Rule](/configuration/route/rule/),
|
||||
[DNS Rule](/configuration/dns/rule/),
|
||||
[rule-set](/configuration/rule-set/),
|
||||
[Rule Set](/configuration/rule-set/),
|
||||
[Source Format](/configuration/rule-set/source-format/) and
|
||||
[Headless Rule](/configuration/rule-set/headless-rule/).
|
||||
|
||||
For GEO resources migration, see [Migrate GeoIP to rule-sets](/migration/#migrate-geoip-to-rule-sets) and
|
||||
[Migrate Geosite to rule-sets](/migration/#migrate-geosite-to-rule-sets).
|
||||
For GEO resources migration, see [Migrate GeoIP to rule sets](/migration/#migrate-geoip-to-rule-sets) and
|
||||
[Migrate Geosite to rule sets](/migration/#migrate-geosite-to-rule-sets).
|
||||
|
||||
**3**:
|
||||
|
||||
New commands manage GeoIP, Geosite and rule-set resources, and help you migrate GEO resources to rule-sets.
|
||||
New commands manage GeoIP, Geosite and rule set resources, and help you migrate GEO resources to rule sets.
|
||||
|
||||
**4**:
|
||||
|
||||
@@ -720,7 +654,7 @@ This change is intended to break incorrect usage and essentially requires no act
|
||||
|
||||
**1**:
|
||||
|
||||
Now the rules in the `rule_set` rule item can be logically considered to be merged into the rule using rule-sets,
|
||||
Now the rules in the `rule_set` rule item can be logically considered to be merged into the rule using rule sets,
|
||||
rather than completely following the AND logic.
|
||||
|
||||
#### 1.8.0-alpha.5
|
||||
@@ -736,7 +670,7 @@ Since GeoIP was deprecated, we made this rule independent, see [Migration](/migr
|
||||
#### 1.8.0-alpha.1
|
||||
|
||||
* Migrate cache file from Clash API to independent options **1**
|
||||
* Introducing [rule-set](/configuration/rule-set/) **2**
|
||||
* Introducing [Rule Set](/configuration/rule-set/) **2**
|
||||
* Add `sing-box geoip`, `sing-box geosite` and `sing-box rule-set` commands **3**
|
||||
* Allow nested logical rules **4**
|
||||
|
||||
@@ -747,7 +681,7 @@ See [Cache File](/configuration/experimental/cache-file/) and
|
||||
|
||||
**2**:
|
||||
|
||||
rule-set is independent collections of rules that can be compiled into binaries to improve performance.
|
||||
Rule set is independent collections of rules that can be compiled into binaries to improve performance.
|
||||
Compared to legacy GeoIP and Geosite resources,
|
||||
it can include more types of rules, load faster,
|
||||
use less memory, and update automatically.
|
||||
@@ -755,16 +689,16 @@ use less memory, and update automatically.
|
||||
See [Route#rule_set](/configuration/route/#rule_set),
|
||||
[Route Rule](/configuration/route/rule/),
|
||||
[DNS Rule](/configuration/dns/rule/),
|
||||
[rule-set](/configuration/rule-set/),
|
||||
[Rule Set](/configuration/rule-set/),
|
||||
[Source Format](/configuration/rule-set/source-format/) and
|
||||
[Headless Rule](/configuration/rule-set/headless-rule/).
|
||||
|
||||
For GEO resources migration, see [Migrate GeoIP to rule-sets](/migration/#migrate-geoip-to-rule-sets) and
|
||||
[Migrate Geosite to rule-sets](/migration/#migrate-geosite-to-rule-sets).
|
||||
For GEO resources migration, see [Migrate GeoIP to rule sets](/migration/#migrate-geoip-to-rule-sets) and
|
||||
[Migrate Geosite to rule sets](/migration/#migrate-geosite-to-rule-sets).
|
||||
|
||||
**3**:
|
||||
|
||||
New commands manage GeoIP, Geosite and rule-set resources, and help you migrate GEO resources to rule-sets.
|
||||
New commands manage GeoIP, Geosite and rule set resources, and help you migrate GEO resources to rule sets.
|
||||
|
||||
**4**:
|
||||
|
||||
|
||||
@@ -2,12 +2,6 @@
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.10.0"
|
||||
|
||||
:material-delete-clock: [rule_set_ipcidr_match_source](#rule_set_ipcidr_match_source)
|
||||
:material-plus: [rule_set_ip_cidr_match_source](#rule_set_ip_cidr_match_source)
|
||||
:material-plus: [rule_set_ip_cidr_accept_empty](#rule_set_ip_cidr_accept_empty)
|
||||
|
||||
!!! quote "Changes in sing-box 1.9.0"
|
||||
|
||||
:material-plus: [geoip](#geoip)
|
||||
@@ -123,10 +117,7 @@ icon: material/new-box
|
||||
"geoip-cn",
|
||||
"geosite-cn"
|
||||
],
|
||||
// deprecated
|
||||
"rule_set_ipcidr_match_source": false,
|
||||
"rule_set_ip_cidr_match_source": false,
|
||||
"rule_set_ip_cidr_accept_empty": false,
|
||||
"invert": false,
|
||||
"outbound": [
|
||||
"direct"
|
||||
@@ -166,7 +157,7 @@ icon: material/new-box
|
||||
(`source_port` || `source_port_range`) &&
|
||||
`other fields`
|
||||
|
||||
Additionally, included rule-sets can be considered merged rather than as a single rule sub-item.
|
||||
Additionally, included rule sets can be considered merged rather than as a single rule sub-item.
|
||||
|
||||
#### inbound
|
||||
|
||||
@@ -312,23 +303,13 @@ Match WiFi BSSID.
|
||||
|
||||
!!! question "Since sing-box 1.8.0"
|
||||
|
||||
Match [rule-set](/configuration/route/#rule_set).
|
||||
Match [Rule Set](/configuration/route/#rule_set).
|
||||
|
||||
#### rule_set_ipcidr_match_source
|
||||
|
||||
!!! question "Since sing-box 1.9.0"
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.10.0"
|
||||
|
||||
`rule_set_ipcidr_match_source` is renamed to `rule_set_ip_cidr_match_source` and will be remove in sing-box 1.11.0.
|
||||
|
||||
Make `ip_cidr` rule items in rule-sets match the source IP.
|
||||
|
||||
#### rule_set_ip_cidr_match_source
|
||||
|
||||
!!! question "Since sing-box 1.10.0"
|
||||
|
||||
Make `ip_cidr` rule items in rule-sets match the source IP.
|
||||
Make `ipcidr` in rule sets match the source IP.
|
||||
|
||||
#### invert
|
||||
|
||||
@@ -366,11 +347,11 @@ Will overrides `dns.client_subnet` and `servers.[].client_subnet`.
|
||||
|
||||
### Address Filter Fields
|
||||
|
||||
Only takes effect for address requests (A/AAAA/HTTPS). When the query results do not match the address filtering rule items, the current rule will be skipped.
|
||||
Only takes effect for IP address requests. When the query results do not match the address filtering rule items, the current rule will be skipped.
|
||||
|
||||
!!! info ""
|
||||
|
||||
`ip_cidr` items in included rule-sets also takes effect as an address filtering field.
|
||||
`ip_cidr` items in included rule sets also takes effect as an address filtering field.
|
||||
|
||||
!!! note ""
|
||||
|
||||
@@ -394,12 +375,6 @@ Match IP CIDR with query response.
|
||||
|
||||
Match private IP with query response.
|
||||
|
||||
#### rule_set_ip_cidr_accept_empty
|
||||
|
||||
!!! question "Since sing-box 1.10.0"
|
||||
|
||||
Make `ip_cidr` rules in rule-sets accept empty query response.
|
||||
|
||||
### Logical Fields
|
||||
|
||||
#### type
|
||||
|
||||
@@ -2,12 +2,6 @@
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.10.0 中的更改"
|
||||
|
||||
:material-delete-clock: [rule_set_ipcidr_match_source](#rule_set_ipcidr_match_source)
|
||||
:material-plus: [rule_set_ip_cidr_match_source](#rule_set_ip_cidr_match_source)
|
||||
:material-plus: [rule_set_ip_cidr_accept_empty](#rule_set_ip_cidr_accept_empty)
|
||||
|
||||
!!! quote "sing-box 1.9.0 中的更改"
|
||||
|
||||
:material-plus: [geoip](#geoip)
|
||||
@@ -123,10 +117,7 @@ icon: material/new-box
|
||||
"geoip-cn",
|
||||
"geosite-cn"
|
||||
],
|
||||
// 已弃用
|
||||
"rule_set_ipcidr_match_source": false,
|
||||
"rule_set_ip_cidr_match_source": false,
|
||||
"rule_set_ip_cidr_accept_empty": false,
|
||||
"invert": false,
|
||||
"outbound": [
|
||||
"direct"
|
||||
@@ -316,17 +307,7 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||
|
||||
!!! question "自 sing-box 1.9.0 起"
|
||||
|
||||
!!! failure "已在 sing-box 1.10.0 废弃"
|
||||
|
||||
`rule_set_ipcidr_match_source` 已重命名为 `rule_set_ip_cidr_match_source` 且将在 sing-box 1.11.0 移除。
|
||||
|
||||
使规则集中的 `ip_cidr` 规则匹配源 IP。
|
||||
|
||||
#### rule_set_ip_cidr_match_source
|
||||
|
||||
!!! question "自 sing-box 1.10.0 起"
|
||||
|
||||
使规则集中的 `ip_cidr` 规则匹配源 IP。
|
||||
使规则集中的 `ipcidr` 规则匹配源 IP。
|
||||
|
||||
#### invert
|
||||
|
||||
@@ -364,7 +345,7 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||
|
||||
### 地址筛选字段
|
||||
|
||||
仅对地址请求 (A/AAAA/HTTPS) 生效。 当查询结果与地址筛选规则项不匹配时,将跳过当前规则。
|
||||
仅对IP地址请求生效。 当查询结果与地址筛选规则项不匹配时,将跳过当前规则。
|
||||
|
||||
!!! info ""
|
||||
|
||||
@@ -384,7 +365,7 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||
|
||||
!!! question "自 sing-box 1.9.0 起"
|
||||
|
||||
与查询响应匹配 IP CIDR。
|
||||
与查询相应匹配 IP CIDR。
|
||||
|
||||
#### ip_is_private
|
||||
|
||||
@@ -392,12 +373,6 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||
|
||||
与查询响应匹配非公开 IP。
|
||||
|
||||
#### rule_set_ip_cidr_accept_empty
|
||||
|
||||
!!! question "自 sing-box 1.10.0 起"
|
||||
|
||||
使规则集中的 `ip_cidr` 规则接受空查询响应。
|
||||
|
||||
### 逻辑字段
|
||||
|
||||
#### type
|
||||
|
||||
@@ -13,11 +13,7 @@ icon: material/new-box
|
||||
:material-plus: [route_exclude_address](#route_address)
|
||||
:material-delete-clock: [inet4_route_exclude_address](#inet4_route_exclude_address)
|
||||
:material-delete-clock: [inet6_route_exclude_address](#inet6_route_exclude_address)
|
||||
:material-plus: [iproute2_table_index](#iproute2_table_index)
|
||||
:material-plus: [iproute2_rule_index](#iproute2_table_index)
|
||||
:material-plus: [auto_redirect](#auto_redirect)
|
||||
:material-plus: [auto_redirect_input_mark](#auto_redirect_input_mark)
|
||||
:material-plus: [auto_redirect_output_mark](#auto_redirect_output_mark)
|
||||
:material-plus: [route_address_set](#route_address_set)
|
||||
:material-plus: [route_exclude_address_set](#route_address_set)
|
||||
|
||||
@@ -57,12 +53,8 @@ icon: material/new-box
|
||||
"mtu": 9000,
|
||||
"gso": false,
|
||||
"auto_route": true,
|
||||
"iproute2_table_index": 2022,
|
||||
"iproute2_rule_index": 9000,
|
||||
"auto_redirect": false,
|
||||
"auto_redirect_input_mark": "0x2023",
|
||||
"auto_redirect_output_mark": "0x2024",
|
||||
"strict_route": true,
|
||||
"auto_redirect": false,
|
||||
"route_address": [
|
||||
"0.0.0.0/1",
|
||||
"128.0.0.0/1",
|
||||
@@ -137,8 +129,8 @@ icon: material/new-box
|
||||
"match_domain": []
|
||||
}
|
||||
},
|
||||
...
|
||||
// Listen Fields
|
||||
|
||||
... // Listen Fields
|
||||
}
|
||||
```
|
||||
|
||||
@@ -188,7 +180,7 @@ The maximum transmission unit.
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported on Linux with `auto_route` enabled.
|
||||
Only supported on Linux.
|
||||
|
||||
Enable generic segmentation offload.
|
||||
|
||||
@@ -204,57 +196,6 @@ Set the default route to the Tun.
|
||||
|
||||
By default, VPN takes precedence over tun. To make tun go through VPN, enable `route.override_android_vpn`.
|
||||
|
||||
#### iproute2_table_index
|
||||
|
||||
!!! question "Since sing-box 1.10.0"
|
||||
|
||||
Linux iproute2 table index generated by `auto_route`.
|
||||
|
||||
`2022` is used by default.
|
||||
|
||||
#### iproute2_rule_index
|
||||
|
||||
!!! question "Since sing-box 1.10.0"
|
||||
|
||||
Linux iproute2 rule start index generated by `auto_route`.
|
||||
|
||||
`9000` is used by default.
|
||||
|
||||
#### auto_redirect
|
||||
|
||||
!!! question "Since sing-box 1.10.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported on Linux with `auto_route` enabled.
|
||||
|
||||
Automatically configure iptables/nftables to redirect connections.
|
||||
|
||||
*In Android*:
|
||||
|
||||
Only local connections are forwarded. To share your VPN connection over hotspot or repeater,
|
||||
use [VPNHotspot](https://github.com/Mygod/VPNHotspot).
|
||||
|
||||
*In Linux*:
|
||||
|
||||
`auto_route` with `auto_redirect` now works as expected on routers **without intervention**.
|
||||
|
||||
#### auto_redirect_input_mark
|
||||
|
||||
!!! question "Since sing-box 1.10.0"
|
||||
|
||||
Connection input mark used by `route_address_set` and `route_exclude_address_set`.
|
||||
|
||||
`0x2023` is used by default.
|
||||
|
||||
#### auto_redirect_output_mark
|
||||
|
||||
!!! question "Since sing-box 1.10.0"
|
||||
|
||||
Connection output mark used by `route_address_set` and `route_exclude_address_set`.
|
||||
|
||||
`0x2024` is used by default.
|
||||
|
||||
#### strict_route
|
||||
|
||||
Enforce strict routing rules when `auto_route` is enabled:
|
||||
@@ -274,6 +215,25 @@ It prevents IP address leaks and makes DNS hijacking work on Android.
|
||||
|
||||
It may prevent some applications (such as VirtualBox) from working properly in certain situations.
|
||||
|
||||
#### auto_redirect
|
||||
|
||||
!!! question "Since sing-box 1.10.0"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported on Linux with `auto_route` enabled.
|
||||
|
||||
Automatically configure iptables/nftables to redirect connections.
|
||||
|
||||
*In Android*:
|
||||
|
||||
Only local connections are forwarded. To share your VPN connection over hotspot or repeater,
|
||||
use [VPNHotspot](https://github.com/Mygod/VPNHotspot).
|
||||
|
||||
*In Linux*:
|
||||
|
||||
`auto_route` with `auto_redirect` now works as expected on routers **without intervention**.
|
||||
|
||||
#### route_address
|
||||
|
||||
!!! question "Since sing-box 1.10.0"
|
||||
@@ -284,8 +244,7 @@ Use custom routes instead of default when `auto_route` is enabled.
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.10.0"
|
||||
|
||||
`inet4_route_address` is deprecated and will be removed in sing-box 1.11.0, please use [route_address](#route_address)
|
||||
instead.
|
||||
`inet4_route_address` is deprecated and will be removed in sing-box 1.11.0, please use [route_address](#route_address) instead.
|
||||
|
||||
Use custom routes instead of default when `auto_route` is enabled.
|
||||
|
||||
@@ -293,8 +252,7 @@ Use custom routes instead of default when `auto_route` is enabled.
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.10.0"
|
||||
|
||||
`inet6_route_address` is deprecated and will be removed in sing-box 1.11.0, please use [route_address](#route_address)
|
||||
instead.
|
||||
`inet6_route_address` is deprecated and will be removed in sing-box 1.11.0, please use [route_address](#route_address) instead.
|
||||
|
||||
Use custom routes instead of default when `auto_route` is enabled.
|
||||
|
||||
@@ -308,8 +266,7 @@ Exclude custom routes when `auto_route` is enabled.
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.10.0"
|
||||
|
||||
`inet4_route_exclude_address` is deprecated and will be removed in sing-box 1.11.0, please
|
||||
use [route_exclude_address](#route_exclude_address) instead.
|
||||
`inet4_route_exclude_address` is deprecated and will be removed in sing-box 1.11.0, please use [route_exclude_address](#route_exclude_address) instead.
|
||||
|
||||
Exclude custom routes when `auto_route` is enabled.
|
||||
|
||||
@@ -317,8 +274,7 @@ Exclude custom routes when `auto_route` is enabled.
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.10.0"
|
||||
|
||||
`inet6_route_exclude_address` is deprecated and will be removed in sing-box 1.11.0, please
|
||||
use [route_exclude_address](#route_exclude_address) instead.
|
||||
`inet6_route_exclude_address` is deprecated and will be removed in sing-box 1.11.0, please use [route_exclude_address](#route_exclude_address) instead.
|
||||
|
||||
Exclude custom routes when `auto_route` is enabled.
|
||||
|
||||
|
||||
@@ -12,12 +12,8 @@ icon: material/new-box
|
||||
:material-delete-clock: [inet6_route_address](#inet6_route_address)
|
||||
:material-plus: [route_exclude_address](#route_address)
|
||||
:material-delete-clock: [inet4_route_exclude_address](#inet4_route_exclude_address)
|
||||
:material-delete-clock: [inet6_route_exclude_address](#inet6_route_exclude_address)
|
||||
:material-plus: [iproute2_table_index](#iproute2_table_index)
|
||||
:material-plus: [iproute2_rule_index](#iproute2_table_index)
|
||||
:material-delete-clock: [inet6_route_exclude_address](#inet6_route_exclude_address)
|
||||
:material-plus: [auto_redirect](#auto_redirect)
|
||||
:material-plus: [auto_redirect_input_mark](#auto_redirect_input_mark)
|
||||
:material-plus: [auto_redirect_output_mark](#auto_redirect_output_mark)
|
||||
:material-plus: [route_address_set](#route_address_set)
|
||||
:material-plus: [route_exclude_address_set](#route_address_set)
|
||||
|
||||
@@ -57,12 +53,8 @@ icon: material/new-box
|
||||
"mtu": 9000,
|
||||
"gso": false,
|
||||
"auto_route": true,
|
||||
"iproute2_table_index": 2022,
|
||||
"iproute2_rule_index": 9000,
|
||||
"auto_redirect": false,
|
||||
"auto_redirect_input_mark": "0x2023",
|
||||
"auto_redirect_output_mark": "0x2024",
|
||||
"strict_route": true,
|
||||
"auto_redirect": false,
|
||||
"route_address": [
|
||||
"0.0.0.0/1",
|
||||
"128.0.0.0/1",
|
||||
@@ -168,7 +160,7 @@ tun 接口的 IPv4 和 IPv6 前缀。
|
||||
|
||||
!!! failure "已在 sing-box 1.10.0 废弃"
|
||||
|
||||
`inet4_address` 已合并到 `address` 且将在 sing-box 1.11.0 移除。
|
||||
`inet4_address` 已合并到 `address` 且将在 sing-box 1.11.0 移除.
|
||||
|
||||
==必填==
|
||||
|
||||
@@ -178,7 +170,7 @@ tun 接口的 IPv4 前缀。
|
||||
|
||||
!!! failure "已在 sing-box 1.10.0 废弃"
|
||||
|
||||
`inet6_address` 已合并到 `address` 且将在 sing-box 1.11.0 移除。
|
||||
`inet6_address` 已合并到 `address` 且将在 sing-box 1.11.0 移除.
|
||||
|
||||
tun 接口的 IPv6 前缀。
|
||||
|
||||
@@ -208,56 +200,6 @@ tun 接口的 IPv6 前缀。
|
||||
|
||||
VPN 默认优先于 tun。要使 tun 经过 VPN,启用 `route.override_android_vpn`。
|
||||
|
||||
#### iproute2_table_index
|
||||
|
||||
!!! question "自 sing-box 1.10.0 起"
|
||||
|
||||
`auto_route` 生成的 iproute2 路由表索引。
|
||||
|
||||
默认使用 `2022`。
|
||||
|
||||
#### iproute2_rule_index
|
||||
|
||||
!!! question "自 sing-box 1.10.0 起"
|
||||
|
||||
`auto_route` 生成的 iproute2 规则起始索引。
|
||||
|
||||
默认使用 `9000`。
|
||||
|
||||
#### auto_redirect
|
||||
|
||||
!!! question "自 sing-box 1.10.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅支持 Linux,且需要 `auto_route` 已启用。
|
||||
|
||||
自动配置 iptables 以重定向 TCP 连接。
|
||||
|
||||
*在 Android 中*:
|
||||
|
||||
仅转发本地 IPv4 连接。 要通过热点或中继共享您的 VPN 连接,请使用 [VPNHotspot](https://github.com/Mygod/VPNHotspot)。
|
||||
|
||||
*在 Linux 中*:
|
||||
|
||||
带有 `auto_redirect `的 `auto_route` 现在可以在路由器上按预期工作,**无需干预**。
|
||||
|
||||
#### auto_redirect_input_mark
|
||||
|
||||
!!! question "自 sing-box 1.10.0 起"
|
||||
|
||||
`route_address_set` 和 `route_exclude_address_set` 使用的连接输入标记。
|
||||
|
||||
默认使用 `0x2023`。
|
||||
|
||||
#### auto_redirect_output_mark
|
||||
|
||||
!!! question "自 sing-box 1.10.0 起"
|
||||
|
||||
`route_address_set` 和 `route_exclude_address_set` 使用的连接输出标记。
|
||||
|
||||
默认使用 `0x2024`。
|
||||
|
||||
#### strict_route
|
||||
|
||||
启用 `auto_route` 时执行严格的路由规则。
|
||||
@@ -278,6 +220,24 @@ tun 接口的 IPv6 前缀。
|
||||
|
||||
它可能会使某些应用程序(如 VirtualBox)在某些情况下无法正常工作。
|
||||
|
||||
#### auto_redirect
|
||||
|
||||
!!! question "自 sing-box 1.10.0 起"
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅支持 Linux。
|
||||
|
||||
自动配置 iptables 以重定向 TCP 连接。
|
||||
|
||||
*在 Android 中*:
|
||||
|
||||
仅转发本地 IPv4 连接。 要通过热点或中继共享您的 VPN 连接,请使用 [VPNHotspot](https://github.com/Mygod/VPNHotspot)。
|
||||
|
||||
*在 Linux 中*:
|
||||
|
||||
带有 `auto_redirect `的 `auto_route` 现在可以在路由器上按预期工作,**无需干预**。
|
||||
|
||||
#### route_address
|
||||
|
||||
!!! question "自 sing-box 1.10.0 起"
|
||||
@@ -288,7 +248,7 @@ tun 接口的 IPv6 前缀。
|
||||
|
||||
!!! failure "已在 sing-box 1.10.0 废弃"
|
||||
|
||||
`inet4_route_address` 已合并到 `route_address` 且将在 sing-box 1.11.0 移除。
|
||||
`inet4_route_address` 已合并到 `route_address` 且将在 sing-box 1.11.0 移除.
|
||||
|
||||
启用 `auto_route` 时使用自定义路由而不是默认路由。
|
||||
|
||||
@@ -296,7 +256,7 @@ tun 接口的 IPv6 前缀。
|
||||
|
||||
!!! failure "已在 sing-box 1.10.0 废弃"
|
||||
|
||||
`inet6_route_address` 已合并到 `route_address` 且将在 sing-box 1.11.0 移除。
|
||||
`inet6_route_address` 已合并到 `route_address` 且将在 sing-box 1.11.0 移除.
|
||||
|
||||
启用 `auto_route` 时使用自定义路由而不是默认路由。
|
||||
|
||||
@@ -310,7 +270,7 @@ tun 接口的 IPv6 前缀。
|
||||
|
||||
!!! failure "已在 sing-box 1.10.0 废弃"
|
||||
|
||||
`inet4_route_exclude_address` 已合并到 `route_exclude_address` 且将在 sing-box 1.11.0 移除。
|
||||
`inet4_route_exclude_address` 已合并到 `route_exclude_address` 且将在 sing-box 1.11.0 移除.
|
||||
|
||||
启用 `auto_route` 时排除自定义路由。
|
||||
|
||||
@@ -318,7 +278,7 @@ tun 接口的 IPv6 前缀。
|
||||
|
||||
!!! failure "已在 sing-box 1.10.0 废弃"
|
||||
|
||||
`inet6_route_exclude_address` 已合并到 `route_exclude_address` 且将在 sing-box 1.11.0 移除。
|
||||
`inet6_route_exclude_address` 已合并到 `route_exclude_address` 且将在 sing-box 1.11.0 移除.
|
||||
|
||||
启用 `auto_route` 时排除自定义路由。
|
||||
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
}
|
||||
```
|
||||
|
||||
### Fields
|
||||
### 字段
|
||||
|
||||
No fields.
|
||||
No fields.
|
||||
@@ -39,7 +39,7 @@ List of [Route Rule](./rule/)
|
||||
|
||||
!!! question "Since sing-box 1.8.0"
|
||||
|
||||
List of [rule-set](/configuration/rule-set/)
|
||||
List of [Rule Set](/configuration/rule-set/)
|
||||
|
||||
#### final
|
||||
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.10.0"
|
||||
|
||||
:material-delete-clock: [rule_set_ipcidr_match_source](#rule_set_ipcidr_match_source)
|
||||
:material-plus: [rule_set_ip_cidr_match_source](#rule_set_ip_cidr_match_source)
|
||||
|
||||
!!! quote "Changes in sing-box 1.8.0"
|
||||
|
||||
:material-plus: [rule_set](#rule_set)
|
||||
@@ -114,9 +105,7 @@ icon: material/alert-decagram
|
||||
"geoip-cn",
|
||||
"geosite-cn"
|
||||
],
|
||||
// deprecated
|
||||
"rule_set_ipcidr_match_source": false,
|
||||
"rule_set_ip_cidr_match_source": false,
|
||||
"invert": false,
|
||||
"outbound": "direct"
|
||||
},
|
||||
@@ -148,7 +137,7 @@ icon: material/alert-decagram
|
||||
(`source_port` || `source_port_range`) &&
|
||||
`other fields`
|
||||
|
||||
Additionally, included rule-sets can be considered merged rather than as a single rule sub-item.
|
||||
Additionally, included rule sets can be considered merged rather than as a single rule sub-item.
|
||||
|
||||
#### inbound
|
||||
|
||||
@@ -308,23 +297,13 @@ Match WiFi BSSID.
|
||||
|
||||
!!! question "Since sing-box 1.8.0"
|
||||
|
||||
Match [rule-set](/configuration/route/#rule_set).
|
||||
Match [Rule Set](/configuration/route/#rule_set).
|
||||
|
||||
#### rule_set_ipcidr_match_source
|
||||
|
||||
!!! question "Since sing-box 1.8.0"
|
||||
|
||||
!!! failure "Deprecated in sing-box 1.10.0"
|
||||
|
||||
`rule_set_ipcidr_match_source` is renamed to `rule_set_ip_cidr_match_source` and will be remove in sing-box 1.11.0.
|
||||
|
||||
Make `ip_cidr` in rule-sets match the source IP.
|
||||
|
||||
#### rule_set_ip_cidr_match_source
|
||||
|
||||
!!! question "Since sing-box 1.10.0"
|
||||
|
||||
Make `ip_cidr` in rule-sets match the source IP.
|
||||
Make `ipcidr` in rule sets match the source IP.
|
||||
|
||||
#### invert
|
||||
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
---
|
||||
icon: material/alert-decagram
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.10.0 中的更改"
|
||||
|
||||
:material-delete-clock: [rule_set_ipcidr_match_source](#rule_set_ipcidr_match_source)
|
||||
:material-plus: [rule_set_ip_cidr_match_source](#rule_set_ip_cidr_match_source)
|
||||
|
||||
!!! quote "sing-box 1.8.0 中的更改"
|
||||
|
||||
:material-plus: [rule_set](#rule_set)
|
||||
@@ -112,9 +103,7 @@ icon: material/alert-decagram
|
||||
"geoip-cn",
|
||||
"geosite-cn"
|
||||
],
|
||||
// 已弃用
|
||||
"rule_set_ipcidr_match_source": false,
|
||||
"rule_set_ip_cidr_match_source": false,
|
||||
"invert": false,
|
||||
"outbound": "direct"
|
||||
},
|
||||
@@ -312,17 +301,7 @@ icon: material/alert-decagram
|
||||
|
||||
!!! question "自 sing-box 1.8.0 起"
|
||||
|
||||
!!! failure "已在 sing-box 1.10.0 废弃"
|
||||
|
||||
`rule_set_ipcidr_match_source` 已重命名为 `rule_set_ip_cidr_match_source` 且将在 sing-box 1.11.0 移除。
|
||||
|
||||
使规则集中的 `ip_cidr` 规则匹配源 IP。
|
||||
|
||||
#### rule_set_ip_cidr_match_source
|
||||
|
||||
!!! question "自 sing-box 1.10.0 起"
|
||||
|
||||
使规则集中的 `ip_cidr` 规则匹配源 IP。
|
||||
使规则集中的 `ipcidr` 规则匹配源 IP。
|
||||
|
||||
#### invert
|
||||
|
||||
|
||||
@@ -1,22 +1,12 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.10.0"
|
||||
|
||||
:material-plus: BitTorrent support
|
||||
:material-plus: DTLS support
|
||||
|
||||
If enabled in the inbound, the protocol and domain name (if present) of by the connection can be sniffed.
|
||||
|
||||
#### Supported Protocols
|
||||
|
||||
| Network | Protocol | Domain Name |
|
||||
|:-------:|:------------:|:-----------:|
|
||||
| TCP | `http` | Host |
|
||||
| TCP | `tls` | Server Name |
|
||||
| UDP | `quic` | Server Name |
|
||||
| UDP | `stun` | / |
|
||||
| TCP/UDP | `dns` | / |
|
||||
| TCP/UDP | `bittorrent` | / |
|
||||
| UDP | `dtls` | / |
|
||||
| Network | Protocol | Domain Name |
|
||||
|:-------:|:-----------:|:-----------:|
|
||||
| TCP | HTTP | Host |
|
||||
| TCP | TLS | Server Name |
|
||||
| UDP | QUIC | Server Name |
|
||||
| UDP | STUN | / |
|
||||
| TCP/UDP | DNS | / |
|
||||
| TCP/UDP | BitTorrent | / |
|
||||
@@ -1,22 +1,12 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "sing-box 1.10.0 中的更改"
|
||||
|
||||
:material-plus: BitTorrent 支持
|
||||
:material-plus: DTLS 支持
|
||||
|
||||
如果在入站中启用,则可以嗅探连接的协议和域名(如果存在)。
|
||||
|
||||
#### 支持的协议
|
||||
|
||||
| 网络 | 协议 | 域名 |
|
||||
|:-------:|:------------:|:-----------:|
|
||||
| TCP | `http` | Host |
|
||||
| TCP | `tls` | Server Name |
|
||||
| UDP | `quic` | Server Name |
|
||||
| UDP | `stun` | / |
|
||||
| TCP/UDP | `dns` | / |
|
||||
| TCP/UDP | `bittorrent` | / |
|
||||
| UDP | `dtls` | / |
|
||||
| 网络 | 协议 | 域名 |
|
||||
|:-------:|:-----------:|:-----------:|
|
||||
| TCP | HTTP | Host |
|
||||
| TCP | TLS | Server Name |
|
||||
| UDP | QUIC | Server Name |
|
||||
| UDP | STUN | / |
|
||||
| TCP/UDP | DNS | / |
|
||||
| TCP/UDP | BitTorrent | / |
|
||||
@@ -1,56 +1,48 @@
|
||||
---
|
||||
icon: material/new-box
|
||||
---
|
||||
|
||||
!!! quote "Changes in sing-box 1.10.0"
|
||||
|
||||
:material-plus: `type: inline`
|
||||
|
||||
# rule-set
|
||||
# Rule Set
|
||||
|
||||
!!! question "Since sing-box 1.8.0"
|
||||
|
||||
### Structure
|
||||
|
||||
=== "Inline"
|
||||
```json
|
||||
{
|
||||
"type": "",
|
||||
"tag": "",
|
||||
"format": "",
|
||||
|
||||
... // Typed Fields
|
||||
}
|
||||
```
|
||||
|
||||
!!! question "Since sing-box 1.10.0"
|
||||
#### Local Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "inline", // optional
|
||||
"tag": "",
|
||||
"rules": []
|
||||
}
|
||||
```
|
||||
```json
|
||||
{
|
||||
"type": "local",
|
||||
|
||||
...
|
||||
|
||||
"path": ""
|
||||
}
|
||||
```
|
||||
|
||||
=== "Local File"
|
||||
#### Remote Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "local",
|
||||
"tag": "",
|
||||
"format": "source", // or binary
|
||||
"path": ""
|
||||
}
|
||||
```
|
||||
!!! info ""
|
||||
|
||||
=== "Remote File"
|
||||
Remote rule-set will be cached if `experimental.cache_file.enabled`.
|
||||
|
||||
!!! info ""
|
||||
|
||||
Remote rule-set will be cached if `experimental.cache_file.enabled`.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "remote",
|
||||
"tag": "",
|
||||
"format": "source", // or binary
|
||||
"url": "",
|
||||
"download_detour": "", // optional
|
||||
"update_interval": "" // optional
|
||||
}
|
||||
```
|
||||
```json
|
||||
{
|
||||
"type": "remote",
|
||||
|
||||
...,
|
||||
|
||||
"url": "",
|
||||
"download_detour": "",
|
||||
"update_interval": ""
|
||||
}
|
||||
```
|
||||
|
||||
### Fields
|
||||
|
||||
@@ -58,31 +50,19 @@ icon: material/new-box
|
||||
|
||||
==Required==
|
||||
|
||||
Type of rule-set, `local` or `remote`.
|
||||
Type of Rule Set, `local` or `remote`.
|
||||
|
||||
#### tag
|
||||
|
||||
==Required==
|
||||
|
||||
Tag of rule-set.
|
||||
|
||||
### Inline Fields
|
||||
|
||||
!!! question "Since sing-box 1.10.0"
|
||||
|
||||
#### rules
|
||||
|
||||
==Required==
|
||||
|
||||
List of [Headless Rule](./headless-rule.md/).
|
||||
|
||||
### Local or Remote Fields
|
||||
Tag of Rule Set.
|
||||
|
||||
#### format
|
||||
|
||||
==Required==
|
||||
|
||||
Format of rule-set file, `source` or `binary`.
|
||||
Format of Rule Set, `source` or `binary`.
|
||||
|
||||
### Local Fields
|
||||
|
||||
@@ -90,11 +70,7 @@ Format of rule-set file, `source` or `binary`.
|
||||
|
||||
==Required==
|
||||
|
||||
!!! note ""
|
||||
|
||||
Will be automatically reloaded if file modified since sing-box 1.10.0.
|
||||
|
||||
File path of rule-set.
|
||||
File path of Rule Set.
|
||||
|
||||
### Remote Fields
|
||||
|
||||
@@ -102,7 +78,7 @@ File path of rule-set.
|
||||
|
||||
==Required==
|
||||
|
||||
Download URL of rule-set.
|
||||
Download URL of Rule Set.
|
||||
|
||||
#### download_detour
|
||||
|
||||
@@ -112,6 +88,6 @@ Default outbound will be used if empty.
|
||||
|
||||
#### update_interval
|
||||
|
||||
Update interval of rule-set.
|
||||
Update interval of Rule Set.
|
||||
|
||||
`1d` will be used if empty.
|
||||
|
||||
@@ -21,7 +21,7 @@ Use `sing-box rule-set compile [--output <file-name>.srs] <file-name>.json` to c
|
||||
|
||||
==Required==
|
||||
|
||||
Version of rule-set, must be `1`.
|
||||
Version of Rule Set, must be `1`.
|
||||
|
||||
#### rules
|
||||
|
||||
|
||||
@@ -178,10 +178,6 @@ The server certificate line array, in PEM format.
|
||||
|
||||
#### certificate_path
|
||||
|
||||
!!! note ""
|
||||
|
||||
Will be automatically reloaded if file modified.
|
||||
|
||||
The path to the server certificate, in PEM format.
|
||||
|
||||
#### key
|
||||
@@ -194,10 +190,6 @@ The server private key line array, in PEM format.
|
||||
|
||||
==Server only==
|
||||
|
||||
!!! note ""
|
||||
|
||||
Will be automatically reloaded if file modified.
|
||||
|
||||
The path to the server private key, in PEM format.
|
||||
|
||||
## Custom TLS support
|
||||
@@ -274,10 +266,6 @@ ECH key line array, in PEM format.
|
||||
|
||||
==Server only==
|
||||
|
||||
!!! note ""
|
||||
|
||||
Will be automatically reloaded if file modified.
|
||||
|
||||
The path to ECH key, in PEM format.
|
||||
|
||||
#### config
|
||||
@@ -409,4 +397,8 @@ A hexadecimal string with zero to eight digits.
|
||||
|
||||
The maximum time difference between the server and the client.
|
||||
|
||||
Check disabled if empty.
|
||||
Check disabled if empty.
|
||||
|
||||
### Reload
|
||||
|
||||
For server configuration, certificate, key and ECH key will be automatically reloaded if modified.
|
||||
@@ -176,20 +176,12 @@ TLS 版本值:
|
||||
|
||||
#### certificate_path
|
||||
|
||||
!!! note ""
|
||||
|
||||
文件更改时将自动重新加载。
|
||||
|
||||
服务器 PEM 证书路径。
|
||||
|
||||
#### key
|
||||
|
||||
==仅服务器==
|
||||
|
||||
!!! note ""
|
||||
|
||||
文件更改时将自动重新加载。
|
||||
|
||||
服务器 PEM 私钥行数组。
|
||||
|
||||
#### key_path
|
||||
@@ -266,10 +258,6 @@ ECH PEM 密钥行数组
|
||||
|
||||
==仅服务器==
|
||||
|
||||
!!! note ""
|
||||
|
||||
文件更改时将自动重新加载。
|
||||
|
||||
ECH PEM 密钥路径
|
||||
|
||||
#### config
|
||||
@@ -396,3 +384,7 @@ ACME DNS01 验证字段。如果配置,将禁用其他验证方法。
|
||||
服务器与和客户端之间允许的最大时间差。
|
||||
|
||||
默认禁用检查。
|
||||
|
||||
### 重载
|
||||
|
||||
对于服务器配置,如果修改,证书和密钥将自动重新加载。
|
||||
@@ -33,7 +33,7 @@ The maxmind GeoIP National Database, as an IP classification database,
|
||||
is not entirely suitable for traffic bypassing,
|
||||
and all existing implementations suffer from high memory usage and difficult management.
|
||||
|
||||
sing-box 1.8.0 introduces [rule-set](/configuration/rule-set/), which can completely replace GeoIP,
|
||||
sing-box 1.8.0 introduces [Rule Set](/configuration/rule-set/), which can completely replace GeoIP,
|
||||
check [Migration](/migration/#migrate-geoip-to-rule-sets).
|
||||
|
||||
#### Geosite
|
||||
@@ -43,7 +43,7 @@ Geosite is deprecated and may be removed in the future.
|
||||
Geosite, the `domain-list-community` project maintained by V2Ray as an early traffic bypassing solution,
|
||||
suffers from a number of problems, including lack of maintenance, inaccurate rules, and difficult management.
|
||||
|
||||
sing-box 1.8.0 introduces [rule-set](/configuration/rule-set/), which can completely replace Geosite,
|
||||
sing-box 1.8.0 introduces [Rule Set](/configuration/rule-set/), which can completely replace Geosite,
|
||||
check [Migration](/migration/#migrate-geosite-to-rule-sets).
|
||||
|
||||
## 1.6.0
|
||||
|
||||
@@ -128,7 +128,7 @@ which will disrupt the existing `process_path` use cases in Windows.
|
||||
}
|
||||
```
|
||||
|
||||
### :material-checkbox-intermediate: Migrate GeoIP to rule-sets
|
||||
### :material-checkbox-intermediate: Migrate GeoIP to rule sets
|
||||
|
||||
!!! info "References"
|
||||
|
||||
@@ -136,11 +136,11 @@ which will disrupt the existing `process_path` use cases in Windows.
|
||||
[Route](/configuration/route/) /
|
||||
[Route Rule](/configuration/route/rule/) /
|
||||
[DNS Rule](/configuration/dns/rule/) /
|
||||
[rule-set](/configuration/rule-set/)
|
||||
[Rule Set](/configuration/rule-set/)
|
||||
|
||||
!!! tip
|
||||
|
||||
`sing-box geoip` commands can help you convert custom GeoIP into rule-sets.
|
||||
`sing-box geoip` commands can help you convert custom GeoIP into rule sets.
|
||||
|
||||
=== ":material-card-remove: Deprecated"
|
||||
|
||||
@@ -207,13 +207,13 @@ which will disrupt the existing `process_path` use cases in Windows.
|
||||
},
|
||||
"experimental": {
|
||||
"cache_file": {
|
||||
"enabled": true // required to save rule-set cache
|
||||
"enabled": true // required to save Rule Set cache
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### :material-checkbox-intermediate: Migrate Geosite to rule-sets
|
||||
### :material-checkbox-intermediate: Migrate Geosite to rule sets
|
||||
|
||||
!!! info "References"
|
||||
|
||||
@@ -221,11 +221,11 @@ which will disrupt the existing `process_path` use cases in Windows.
|
||||
[Route](/configuration/route/) /
|
||||
[Route Rule](/configuration/route/rule/) /
|
||||
[DNS Rule](/configuration/dns/rule/) /
|
||||
[rule-set](/configuration/rule-set/)
|
||||
[Rule Set](/configuration/rule-set/)
|
||||
|
||||
!!! tip
|
||||
|
||||
`sing-box geosite` commands can help you convert custom Geosite into rule-sets.
|
||||
`sing-box geosite` commands can help you convert custom Geosite into rule sets.
|
||||
|
||||
=== ":material-card-remove: Deprecated"
|
||||
|
||||
@@ -268,7 +268,7 @@ which will disrupt the existing `process_path` use cases in Windows.
|
||||
},
|
||||
"experimental": {
|
||||
"cache_file": {
|
||||
"enabled": true // required to save rule-set cache
|
||||
"enabled": true // required to save Rule Set cache
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ sing-box 1.9.0 使 QueryFullProcessImageNameW 输出 Win32 路径(如 `C:\fold
|
||||
},
|
||||
"experimental": {
|
||||
"cache_file": {
|
||||
"enabled": true // required to save rule-set cache
|
||||
"enabled": true // required to save Rule Set cache
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,7 +267,7 @@ sing-box 1.9.0 使 QueryFullProcessImageNameW 输出 Win32 路径(如 `C:\fold
|
||||
},
|
||||
"experimental": {
|
||||
"cache_file": {
|
||||
"enabled": true // required to save rule-set cache
|
||||
"enabled": true // required to save Rule Set cache
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -145,18 +143,7 @@ func (s *Server) PreStart() error {
|
||||
func (s *Server) Start() error {
|
||||
if s.externalController {
|
||||
s.checkAndDownloadExternalUI()
|
||||
var (
|
||||
listener net.Listener
|
||||
err error
|
||||
)
|
||||
for i := 0; i < 3; i++ {
|
||||
listener, err = net.Listen("tcp", s.httpServer.Addr)
|
||||
if runtime.GOOS == "android" && errors.Is(err, syscall.EADDRINUSE) {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
listener, err := net.Listen("tcp", s.httpServer.Addr)
|
||||
if err != nil {
|
||||
return E.Cause(err, "external controller listen error")
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/experimental/clashapi"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
func (c *CommandClient) SetClashMode(newMode string) error {
|
||||
@@ -22,7 +22,7 @@ func (c *CommandClient) SetClashMode(newMode string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = varbin.Write(conn, binary.BigEndian, newMode)
|
||||
err = rw.WriteVString(conn, newMode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -30,7 +30,7 @@ func (c *CommandClient) SetClashMode(newMode string) error {
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleSetClashMode(conn net.Conn) error {
|
||||
newMode, err := varbin.ReadValue[string](conn, binary.BigEndian)
|
||||
newMode, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -50,7 +50,7 @@ func (c *CommandClient) handleModeConn(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
for {
|
||||
newMode, err := varbin.ReadValue[string](conn, binary.BigEndian)
|
||||
newMode, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
c.handler.Disconnected(err.Error())
|
||||
return
|
||||
@@ -80,7 +80,7 @@ func (s *CommandServer) handleModeConn(conn net.Conn) error {
|
||||
for {
|
||||
select {
|
||||
case <-s.modeUpdate:
|
||||
err = varbin.Write(conn, binary.BigEndian, clashServer.Mode())
|
||||
err = rw.WriteVString(conn, clashServer.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -101,12 +101,12 @@ func readClashModeList(reader io.Reader) (modeList []string, currentMode string,
|
||||
}
|
||||
modeList = make([]string, modeListLength)
|
||||
for i := 0; i < int(modeListLength); i++ {
|
||||
modeList[i], err = varbin.ReadValue[string](reader, binary.BigEndian)
|
||||
modeList[i], err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
currentMode, err = varbin.ReadValue[string](reader, binary.BigEndian)
|
||||
currentMode, err = rw.ReadVString(reader)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -118,12 +118,12 @@ func writeClashModeList(writer io.Writer, clashServer adapter.ClashServer) error
|
||||
}
|
||||
if len(modeList) > 0 {
|
||||
for _, mode := range modeList {
|
||||
err = varbin.Write(writer, binary.BigEndian, mode)
|
||||
err = rw.WriteVString(writer, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = varbin.Write(writer, binary.BigEndian, clashServer.Mode())
|
||||
err = rw.WriteVString(writer, clashServer.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/sagernet/sing-box/experimental/clashapi"
|
||||
"github.com/sagernet/sing/common/binary"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
)
|
||||
@@ -19,7 +18,7 @@ func (c *CommandClient) CloseConnection(connId string) error {
|
||||
}
|
||||
defer conn.Close()
|
||||
writer := bufio.NewWriter(conn)
|
||||
err = varbin.Write(writer, binary.BigEndian, connId)
|
||||
err = binary.WriteData(writer, binary.BigEndian, connId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -33,7 +32,7 @@ func (c *CommandClient) CloseConnection(connId string) error {
|
||||
func (s *CommandServer) handleCloseConnection(conn net.Conn) error {
|
||||
reader := bufio.NewReader(conn)
|
||||
var connId string
|
||||
err := varbin.Read(reader, binary.BigEndian, &connId)
|
||||
err := binary.ReadData(reader, binary.BigEndian, &connId)
|
||||
if err != nil {
|
||||
return E.Cause(err, "read connection id")
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/sagernet/sing/common/binary"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
)
|
||||
@@ -20,17 +19,13 @@ import (
|
||||
func (c *CommandClient) handleConnectionsConn(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
reader := bufio.NewReader(conn)
|
||||
var (
|
||||
rawConnections []Connection
|
||||
connections Connections
|
||||
)
|
||||
var connections Connections
|
||||
for {
|
||||
err := varbin.Read(reader, binary.BigEndian, &rawConnections)
|
||||
err := binary.ReadData(reader, binary.BigEndian, &connections.connections)
|
||||
if err != nil {
|
||||
c.handler.Disconnected(err.Error())
|
||||
return
|
||||
}
|
||||
connections.input = rawConnections
|
||||
c.handler.WriteConnections(&connections)
|
||||
}
|
||||
}
|
||||
@@ -74,7 +69,7 @@ func (s *CommandServer) handleConnectionsConn(conn net.Conn) error {
|
||||
for _, connection := range trafficManager.ClosedConnections() {
|
||||
outConnections = append(outConnections, newConnection(connections, connection, true))
|
||||
}
|
||||
err = varbin.Write(writer, binary.BigEndian, outConnections)
|
||||
err = binary.WriteData(writer, binary.BigEndian, outConnections)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -97,32 +92,33 @@ const (
|
||||
)
|
||||
|
||||
type Connections struct {
|
||||
input []Connection
|
||||
filtered []Connection
|
||||
connections []Connection
|
||||
filteredConnections []Connection
|
||||
outConnections *[]Connection
|
||||
}
|
||||
|
||||
func (c *Connections) FilterState(state int32) {
|
||||
c.filtered = c.filtered[:0]
|
||||
c.filteredConnections = c.filteredConnections[:0]
|
||||
switch state {
|
||||
case ConnectionStateAll:
|
||||
c.filtered = append(c.filtered, c.input...)
|
||||
c.filteredConnections = append(c.filteredConnections, c.connections...)
|
||||
case ConnectionStateActive:
|
||||
for _, connection := range c.input {
|
||||
for _, connection := range c.connections {
|
||||
if connection.ClosedAt == 0 {
|
||||
c.filtered = append(c.filtered, connection)
|
||||
c.filteredConnections = append(c.filteredConnections, connection)
|
||||
}
|
||||
}
|
||||
case ConnectionStateClosed:
|
||||
for _, connection := range c.input {
|
||||
for _, connection := range c.connections {
|
||||
if connection.ClosedAt != 0 {
|
||||
c.filtered = append(c.filtered, connection)
|
||||
c.filteredConnections = append(c.filteredConnections, connection)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Connections) SortByDate() {
|
||||
slices.SortStableFunc(c.filtered, func(x, y Connection) int {
|
||||
slices.SortStableFunc(c.filteredConnections, func(x, y Connection) int {
|
||||
if x.CreatedAt < y.CreatedAt {
|
||||
return 1
|
||||
} else if x.CreatedAt > y.CreatedAt {
|
||||
@@ -134,7 +130,7 @@ func (c *Connections) SortByDate() {
|
||||
}
|
||||
|
||||
func (c *Connections) SortByTraffic() {
|
||||
slices.SortStableFunc(c.filtered, func(x, y Connection) int {
|
||||
slices.SortStableFunc(c.filteredConnections, func(x, y Connection) int {
|
||||
xTraffic := x.Uplink + x.Downlink
|
||||
yTraffic := y.Uplink + y.Downlink
|
||||
if xTraffic < yTraffic {
|
||||
@@ -148,7 +144,7 @@ func (c *Connections) SortByTraffic() {
|
||||
}
|
||||
|
||||
func (c *Connections) SortByTrafficTotal() {
|
||||
slices.SortStableFunc(c.filtered, func(x, y Connection) int {
|
||||
slices.SortStableFunc(c.filteredConnections, func(x, y Connection) int {
|
||||
xTraffic := x.UplinkTotal + x.DownlinkTotal
|
||||
yTraffic := y.UplinkTotal + y.DownlinkTotal
|
||||
if xTraffic < yTraffic {
|
||||
@@ -162,7 +158,7 @@ func (c *Connections) SortByTrafficTotal() {
|
||||
}
|
||||
|
||||
func (c *Connections) Iterator() ConnectionIterator {
|
||||
return newPtrIterator(c.filtered)
|
||||
return newPtrIterator(c.filteredConnections)
|
||||
}
|
||||
|
||||
type Connection struct {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
@@ -11,7 +10,7 @@ import (
|
||||
"github.com/sagernet/sing-box/common/urltest"
|
||||
"github.com/sagernet/sing-box/outbound"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
@@ -37,24 +36,19 @@ func (s *CommandServer) handleGroupConn(conn net.Conn) error {
|
||||
ticker := time.NewTicker(time.Duration(interval))
|
||||
defer ticker.Stop()
|
||||
ctx := connKeepAlive(conn)
|
||||
writer := bufio.NewWriter(conn)
|
||||
for {
|
||||
service := s.service
|
||||
if service != nil {
|
||||
err = writeGroups(writer, service)
|
||||
err := writeGroups(conn, service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = binary.Write(writer, binary.BigEndian, uint16(0))
|
||||
err := binary.Write(conn, binary.BigEndian, uint16(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = writer.Flush()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
@@ -74,11 +68,11 @@ type OutboundGroup struct {
|
||||
Selectable bool
|
||||
Selected string
|
||||
IsExpand bool
|
||||
ItemList []*OutboundGroupItem
|
||||
items []*OutboundGroupItem
|
||||
}
|
||||
|
||||
func (g *OutboundGroup) GetItems() OutboundGroupItemIterator {
|
||||
return newIterator(g.ItemList)
|
||||
return newIterator(g.items)
|
||||
}
|
||||
|
||||
type OutboundGroupIterator interface {
|
||||
@@ -99,10 +93,73 @@ type OutboundGroupItemIterator interface {
|
||||
}
|
||||
|
||||
func readGroups(reader io.Reader) (OutboundGroupIterator, error) {
|
||||
groups, err := varbin.ReadValue[[]*OutboundGroup](reader, binary.BigEndian)
|
||||
var groupLength uint16
|
||||
err := binary.Read(reader, binary.BigEndian, &groupLength)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
groups := make([]*OutboundGroup, 0, groupLength)
|
||||
for i := 0; i < int(groupLength); i++ {
|
||||
var group OutboundGroup
|
||||
group.Tag, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
group.Type, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = binary.Read(reader, binary.BigEndian, &group.Selectable)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
group.Selected, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = binary.Read(reader, binary.BigEndian, &group.IsExpand)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var itemLength uint16
|
||||
err = binary.Read(reader, binary.BigEndian, &itemLength)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
group.items = make([]*OutboundGroupItem, itemLength)
|
||||
for j := 0; j < int(itemLength); j++ {
|
||||
var item OutboundGroupItem
|
||||
item.Tag, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
item.Type, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = binary.Read(reader, binary.BigEndian, &item.URLTestTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = binary.Read(reader, binary.BigEndian, &item.URLTestDelay)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
group.items[j] = &item
|
||||
}
|
||||
groups = append(groups, &group)
|
||||
}
|
||||
return newIterator(groups), nil
|
||||
}
|
||||
|
||||
@@ -142,14 +199,63 @@ func writeGroups(writer io.Writer, boxService *BoxService) error {
|
||||
item.URLTestTime = history.Time.Unix()
|
||||
item.URLTestDelay = int32(history.Delay)
|
||||
}
|
||||
group.ItemList = append(group.ItemList, &item)
|
||||
group.items = append(group.items, &item)
|
||||
}
|
||||
if len(group.ItemList) < 2 {
|
||||
if len(group.items) < 2 {
|
||||
continue
|
||||
}
|
||||
groups = append(groups, group)
|
||||
}
|
||||
return varbin.Write(writer, binary.BigEndian, groups)
|
||||
|
||||
err := binary.Write(writer, binary.BigEndian, uint16(len(groups)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, group := range groups {
|
||||
err = rw.WriteVString(writer, group.Tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = rw.WriteVString(writer, group.Type)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, group.Selectable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = rw.WriteVString(writer, group.Selected)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, group.IsExpand)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, uint16(len(group.items)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, item := range group.items {
|
||||
err = rw.WriteVString(writer, item.Tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = rw.WriteVString(writer, item.Type)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, item.URLTestTime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, item.URLTestDelay)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CommandClient) SetGroupExpand(groupTag string, isExpand bool) error {
|
||||
@@ -162,7 +268,7 @@ func (c *CommandClient) SetGroupExpand(groupTag string, isExpand bool) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = varbin.Write(conn, binary.BigEndian, groupTag)
|
||||
err = rw.WriteVString(conn, groupTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -174,7 +280,7 @@ func (c *CommandClient) SetGroupExpand(groupTag string, isExpand bool) error {
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleSetGroupExpand(conn net.Conn) error {
|
||||
groupTag, err := varbin.ReadValue[string](conn, binary.BigEndian)
|
||||
groupTag, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -9,19 +9,8 @@ import (
|
||||
|
||||
"github.com/sagernet/sing/common/binary"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
)
|
||||
|
||||
func (s *CommandServer) ResetLog() {
|
||||
s.access.Lock()
|
||||
defer s.access.Unlock()
|
||||
s.savedLines.Init()
|
||||
select {
|
||||
case s.logReset <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CommandServer) WriteMessage(message string) {
|
||||
s.subscriber.Emit(message)
|
||||
s.access.Lock()
|
||||
@@ -32,6 +21,26 @@ func (s *CommandServer) WriteMessage(message string) {
|
||||
s.access.Unlock()
|
||||
}
|
||||
|
||||
func writeLog(writer *bufio.Writer, messages []string) error {
|
||||
err := binary.Write(writer, binary.BigEndian, uint8(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = binary.WriteData(writer, binary.BigEndian, messages)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writer.Flush()
|
||||
}
|
||||
|
||||
func writeClearLog(writer *bufio.Writer) error {
|
||||
err := binary.Write(writer, binary.BigEndian, uint8(1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writer.Flush()
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleLogConn(conn net.Conn) error {
|
||||
var (
|
||||
interval int64
|
||||
@@ -58,24 +67,8 @@ func (s *CommandServer) handleLogConn(conn net.Conn) error {
|
||||
}
|
||||
defer s.observer.UnSubscribe(subscription)
|
||||
writer := bufio.NewWriter(conn)
|
||||
select {
|
||||
case <-s.logReset:
|
||||
err = writer.WriteByte(1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = writer.Flush()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
}
|
||||
if len(savedLines) > 0 {
|
||||
err = writer.WriteByte(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = varbin.Write(writer, binary.BigEndian, savedLines)
|
||||
err = writeLog(writer, savedLines)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -83,15 +76,11 @@ func (s *CommandServer) handleLogConn(conn net.Conn) error {
|
||||
ctx := connKeepAlive(conn)
|
||||
var logLines []string
|
||||
for {
|
||||
err = writer.Flush()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-s.logReset:
|
||||
err = writer.WriteByte(1)
|
||||
err = writeClearLog(writer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -110,11 +99,7 @@ func (s *CommandServer) handleLogConn(conn net.Conn) error {
|
||||
break loopLogs
|
||||
}
|
||||
}
|
||||
err = writer.WriteByte(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = varbin.Write(writer, binary.BigEndian, logLines)
|
||||
err = writeLog(writer, logLines)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -125,7 +110,8 @@ func (s *CommandServer) handleLogConn(conn net.Conn) error {
|
||||
func (c *CommandClient) handleLogConn(conn net.Conn) {
|
||||
reader := bufio.NewReader(conn)
|
||||
for {
|
||||
messageType, err := reader.ReadByte()
|
||||
var messageType uint8
|
||||
err := binary.Read(reader, binary.BigEndian, &messageType)
|
||||
if err != nil {
|
||||
c.handler.Disconnected(err.Error())
|
||||
return
|
||||
@@ -133,7 +119,7 @@ func (c *CommandClient) handleLogConn(conn net.Conn) {
|
||||
var messages []string
|
||||
switch messageType {
|
||||
case 0:
|
||||
err = varbin.Read(reader, binary.BigEndian, &messages)
|
||||
err = binary.ReadData(reader, binary.BigEndian, &messages)
|
||||
if err != nil {
|
||||
c.handler.Disconnected(err.Error())
|
||||
return
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
func (c *CommandClient) ServiceReload() error {
|
||||
@@ -24,7 +24,7 @@ func (c *CommandClient) ServiceReload() error {
|
||||
return err
|
||||
}
|
||||
if hasError {
|
||||
errorMessage, err := varbin.ReadValue[string](conn, binary.BigEndian)
|
||||
errorMessage, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func (s *CommandServer) handleServiceReload(conn net.Conn) error {
|
||||
return err
|
||||
}
|
||||
if rErr != nil {
|
||||
return varbin.Write(conn, binary.BigEndian, rErr.Error())
|
||||
return rw.WriteVString(conn, rErr.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -61,7 +61,7 @@ func (c *CommandClient) ServiceClose() error {
|
||||
return nil
|
||||
}
|
||||
if hasError {
|
||||
errorMessage, err := varbin.ReadValue[string](conn, binary.BigEndian)
|
||||
errorMessage, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -78,7 +78,7 @@ func (s *CommandServer) handleServiceClose(conn net.Conn) error {
|
||||
return err
|
||||
}
|
||||
if rErr != nil {
|
||||
return varbin.Write(conn, binary.BigEndian, rErr.Error())
|
||||
return rw.WriteVString(conn, rErr.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/outbound"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
func (c *CommandClient) SelectOutbound(groupTag string, outboundTag string) error {
|
||||
@@ -19,11 +19,11 @@ func (c *CommandClient) SelectOutbound(groupTag string, outboundTag string) erro
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = varbin.Write(conn, binary.BigEndian, groupTag)
|
||||
err = rw.WriteVString(conn, groupTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = varbin.Write(conn, binary.BigEndian, outboundTag)
|
||||
err = rw.WriteVString(conn, outboundTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -31,11 +31,11 @@ func (c *CommandClient) SelectOutbound(groupTag string, outboundTag string) erro
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleSelectOutbound(conn net.Conn) error {
|
||||
groupTag, err := varbin.ReadValue[string](conn, binary.BigEndian)
|
||||
groupTag, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
outboundTag, err := varbin.ReadValue[string](conn, binary.BigEndian)
|
||||
outboundTag, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -66,6 +66,14 @@ func (s *CommandServer) SetService(newService *BoxService) {
|
||||
s.notifyURLTestUpdate()
|
||||
}
|
||||
|
||||
func (s *CommandServer) ResetLog() {
|
||||
s.savedLines.Init()
|
||||
select {
|
||||
case s.logReset <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CommandServer) notifyURLTestUpdate() {
|
||||
select {
|
||||
case s.urlTestUpdate <- struct{}{}:
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"io"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
func readError(reader io.Reader) error {
|
||||
@@ -15,7 +15,7 @@ func readError(reader io.Reader) error {
|
||||
return err
|
||||
}
|
||||
if hasError {
|
||||
errorMessage, err := varbin.ReadValue[string](reader, binary.BigEndian)
|
||||
errorMessage, err := rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -30,7 +30,7 @@ func writeError(writer io.Writer, wErr error) error {
|
||||
return err
|
||||
}
|
||||
if wErr != nil {
|
||||
err = varbin.Write(writer, binary.BigEndian, wErr.Error())
|
||||
err = rw.WriteVString(writer, wErr.Error())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/batch"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
@@ -25,7 +25,7 @@ func (c *CommandClient) URLTest(groupTag string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = varbin.Write(conn, binary.BigEndian, groupTag)
|
||||
err = rw.WriteVString(conn, groupTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -33,7 +33,7 @@ func (c *CommandClient) URLTest(groupTag string) error {
|
||||
}
|
||||
|
||||
func (s *CommandServer) handleURLTest(conn net.Conn) error {
|
||||
groupTag, err := varbin.ReadValue[string](conn, binary.BigEndian)
|
||||
groupTag, err := rw.ReadVString(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
func EncodeChunkedMessage(data []byte) []byte {
|
||||
@@ -35,13 +35,13 @@ type ErrorMessage struct {
|
||||
func (e *ErrorMessage) Encode() []byte {
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteByte(MessageTypeError)
|
||||
varbin.Write(&buffer, binary.BigEndian, e.Message)
|
||||
rw.WriteVString(&buffer, e.Message)
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
func DecodeErrorMessage(data []byte) (*ErrorMessage, error) {
|
||||
reader := bytes.NewReader(data)
|
||||
messageType, err := reader.ReadByte()
|
||||
messageType, err := rw.ReadByte(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -49,7 +49,7 @@ func DecodeErrorMessage(data []byte) (*ErrorMessage, error) {
|
||||
return nil, E.New("invalid message")
|
||||
}
|
||||
var message ErrorMessage
|
||||
message.Message, err = varbin.ReadValue[string](reader, binary.BigEndian)
|
||||
message.Message, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -87,7 +87,7 @@ func (e *ProfileEncoder) Encode() []byte {
|
||||
binary.Write(&buffer, binary.BigEndian, uint16(len(e.profiles)))
|
||||
for _, preview := range e.profiles {
|
||||
binary.Write(&buffer, binary.BigEndian, preview.ProfileID)
|
||||
varbin.Write(&buffer, binary.BigEndian, preview.Name)
|
||||
rw.WriteVString(&buffer, preview.Name)
|
||||
binary.Write(&buffer, binary.BigEndian, preview.Type)
|
||||
}
|
||||
return buffer.Bytes()
|
||||
@@ -117,7 +117,7 @@ func (d *ProfileDecoder) Decode(data []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
profile.Name, err = varbin.ReadValue[string](reader, binary.BigEndian)
|
||||
profile.Name, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -147,7 +147,7 @@ func (r *ProfileContentRequest) Encode() []byte {
|
||||
|
||||
func DecodeProfileContentRequest(data []byte) (*ProfileContentRequest, error) {
|
||||
reader := bytes.NewReader(data)
|
||||
messageType, err := reader.ReadByte()
|
||||
messageType, err := rw.ReadByte(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -176,13 +176,12 @@ func (c *ProfileContent) Encode() []byte {
|
||||
buffer := new(bytes.Buffer)
|
||||
buffer.WriteByte(MessageTypeProfileContent)
|
||||
buffer.WriteByte(1)
|
||||
gWriter := gzip.NewWriter(buffer)
|
||||
writer := bufio.NewWriter(gWriter)
|
||||
varbin.Write(writer, binary.BigEndian, c.Name)
|
||||
writer := gzip.NewWriter(buffer)
|
||||
rw.WriteVString(writer, c.Name)
|
||||
binary.Write(writer, binary.BigEndian, c.Type)
|
||||
varbin.Write(writer, binary.BigEndian, c.Config)
|
||||
rw.WriteVString(writer, c.Config)
|
||||
if c.Type != ProfileTypeLocal {
|
||||
varbin.Write(writer, binary.BigEndian, c.RemotePath)
|
||||
rw.WriteVString(writer, c.RemotePath)
|
||||
}
|
||||
if c.Type == ProfileTypeRemote {
|
||||
binary.Write(writer, binary.BigEndian, c.AutoUpdate)
|
||||
@@ -190,31 +189,29 @@ func (c *ProfileContent) Encode() []byte {
|
||||
binary.Write(writer, binary.BigEndian, c.LastUpdated)
|
||||
}
|
||||
writer.Flush()
|
||||
gWriter.Flush()
|
||||
gWriter.Close()
|
||||
writer.Close()
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
func DecodeProfileContent(data []byte) (*ProfileContent, error) {
|
||||
reader := bytes.NewReader(data)
|
||||
messageType, err := reader.ReadByte()
|
||||
var reader io.Reader = bytes.NewReader(data)
|
||||
messageType, err := rw.ReadByte(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if messageType != MessageTypeProfileContent {
|
||||
return nil, E.New("invalid message")
|
||||
}
|
||||
version, err := reader.ReadByte()
|
||||
version, err := rw.ReadByte(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gReader, err := gzip.NewReader(reader)
|
||||
reader, err = gzip.NewReader(reader)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "unsupported profile")
|
||||
}
|
||||
bReader := varbin.StubReader(gReader)
|
||||
var content ProfileContent
|
||||
content.Name, err = varbin.ReadValue[string](bReader, binary.BigEndian)
|
||||
content.Name, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -222,12 +219,12 @@ func DecodeProfileContent(data []byte) (*ProfileContent, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
content.Config, err = varbin.ReadValue[string](bReader, binary.BigEndian)
|
||||
content.Config, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if content.Type != ProfileTypeLocal {
|
||||
content.RemotePath, err = varbin.ReadValue[string](bReader, binary.BigEndian)
|
||||
content.RemotePath, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package libbox
|
||||
import (
|
||||
"os"
|
||||
"os/user"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -22,11 +21,6 @@ var (
|
||||
sTVOS bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
debug.SetPanicOnFault(true)
|
||||
debug.SetTraceback("all")
|
||||
}
|
||||
|
||||
func Setup(basePath string, workingPath string, tempPath string, isTVOS bool) {
|
||||
sBasePath = basePath
|
||||
sWorkingPath = workingPath
|
||||
|
||||
21
go.mod
21
go.mod
@@ -7,6 +7,7 @@ require (
|
||||
github.com/caddyserver/certmagic v0.20.0
|
||||
github.com/cloudflare/circl v1.3.7
|
||||
github.com/cretz/bine v0.2.0
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/go-chi/chi/v5 v5.0.12
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-chi/render v1.0.3
|
||||
@@ -16,25 +17,24 @@ require (
|
||||
github.com/libdns/cloudflare v0.1.1
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
github.com/mholt/acmez v1.2.0
|
||||
github.com/miekg/dns v1.1.61
|
||||
github.com/miekg/dns v1.1.59
|
||||
github.com/ooni/go-libtor v1.1.8
|
||||
github.com/oschwald/maxminddb-golang v1.12.0
|
||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1
|
||||
github.com/sagernet/fswatch v0.1.1
|
||||
github.com/sagernet/gomobile v0.1.3
|
||||
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f
|
||||
github.com/sagernet/quic-go v0.45.1-beta.2
|
||||
github.com/sagernet/quic-go v0.45.0-beta.2
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||
github.com/sagernet/sing v0.5.0-alpha.12
|
||||
github.com/sagernet/sing-dns v0.3.0-beta.10
|
||||
github.com/sagernet/sing v0.5.0-alpha.11
|
||||
github.com/sagernet/sing-dns v0.3.0-beta.5
|
||||
github.com/sagernet/sing-mux v0.2.0
|
||||
github.com/sagernet/sing-quic v0.2.0-beta.12
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||
github.com/sagernet/sing-quic v0.2.0-beta.9
|
||||
github.com/sagernet/sing-shadowsocks v0.2.6
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||
github.com/sagernet/sing-shadowtls v0.1.4
|
||||
github.com/sagernet/sing-tun v0.4.0-beta.13.0.20240703164908-1f043289199d
|
||||
github.com/sagernet/sing-vmess v0.1.12
|
||||
github.com/sagernet/sing-tun v0.4.0-beta.11
|
||||
github.com/sagernet/sing-vmess v0.1.8
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
||||
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6
|
||||
github.com/sagernet/utls v1.5.4
|
||||
@@ -59,7 +59,6 @@ require (
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gaukas/godicttls v0.0.4 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
@@ -98,5 +97,5 @@ require (
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/blake3 v1.3.0 // indirect
|
||||
lukechampine.com/blake3 v1.2.1 // indirect
|
||||
)
|
||||
|
||||
38
go.sum
38
go.sum
@@ -76,8 +76,8 @@ github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U
|
||||
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
|
||||
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
|
||||
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
|
||||
github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs=
|
||||
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
|
||||
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
|
||||
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss=
|
||||
@@ -100,8 +100,6 @@ github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkk
|
||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 h1:YbmpqPQEMdlk9oFSKYWRqVuu9qzNiOayIonKmv1gCXY=
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1/go.mod h1:J2yAxTFPDjrDPhuAi9aWFz2L3ox9it4qAluBBbN0H5k=
|
||||
github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs=
|
||||
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o=
|
||||
github.com/sagernet/gomobile v0.1.3 h1:ohjIb1Ou2+1558PnZour3od69suSuvkdSVOlO1tC4B8=
|
||||
github.com/sagernet/gomobile v0.1.3/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
|
||||
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f h1:NkhuupzH5ch7b/Y/6ZHJWrnNLoiNnSJaow6DPb8VW2I=
|
||||
@@ -110,29 +108,29 @@ github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZN
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
|
||||
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
|
||||
github.com/sagernet/quic-go v0.45.1-beta.2 h1:zkEeCbhdFFkrxKcuIRBtXNKci/1t2J/39QSG/sPvlmc=
|
||||
github.com/sagernet/quic-go v0.45.1-beta.2/go.mod h1:+N3FqM9DAzOWfe64uxXuBejVJwX7DeW7BslzLO6N/xI=
|
||||
github.com/sagernet/quic-go v0.45.0-beta.2 h1:nWq9KJTR+cGU8UU4E20XNjdM6QgbLkBgpq+NCExg5RY=
|
||||
github.com/sagernet/quic-go v0.45.0-beta.2/go.mod h1:rs3XCo3SQ2sB96NtaKnEyq+ZkyaKWL51BvIW3veaiWw=
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||
github.com/sagernet/sing v0.5.0-alpha.12 h1:pjffG3SUpuF9PLDCqPO2fOAUozXItIBmnMVTKQ/QMhM=
|
||||
github.com/sagernet/sing v0.5.0-alpha.12/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-dns v0.3.0-beta.10 h1:Js61EjQXVpcu2VDegWEQTH1isCcVwJju8WEHYgG4tQ0=
|
||||
github.com/sagernet/sing-dns v0.3.0-beta.10/go.mod h1:nXE6EYMXahB5DV3AcXYbFfuorqF7tbQ86kxweSxRKM4=
|
||||
github.com/sagernet/sing v0.5.0-alpha.11 h1:4nR9hv3Thxb16tdMY8eQ3xNqyvqGV2gLCwiTht6TkD8=
|
||||
github.com/sagernet/sing v0.5.0-alpha.11/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-dns v0.3.0-beta.5 h1:lX+wfnBVaOlSd7+GBgb431Tt/gmYwJXSHvS1HutfnD4=
|
||||
github.com/sagernet/sing-dns v0.3.0-beta.5/go.mod h1:qeO/lOUK/c3Zczp5a1VO13fbmolaM8xGKCUXtaX0/NQ=
|
||||
github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo=
|
||||
github.com/sagernet/sing-mux v0.2.0/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ=
|
||||
github.com/sagernet/sing-quic v0.2.0-beta.12 h1:BhvA5mmrDFEyDUQB5eeu+9UhF+ieyuNJ5Rsb0dAG3QY=
|
||||
github.com/sagernet/sing-quic v0.2.0-beta.12/go.mod h1:YVpLfVi8BvYM7NMrjmnvcRm3E8iMETf1gFQmTQDN9jI=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
||||
github.com/sagernet/sing-quic v0.2.0-beta.9 h1:gfqUwVgKA6APwFOPhwge9VrPZ0XQtmuXF8hbbIVZML8=
|
||||
github.com/sagernet/sing-quic v0.2.0-beta.9/go.mod h1:hb6RwYy9Js0gZi80zlTBWABMOIvJDv46K5yak/pbZ4w=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
||||
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
||||
github.com/sagernet/sing-tun v0.4.0-beta.13.0.20240703164908-1f043289199d h1:2nBM9W9fOCM45hjlu1Fh9qyzBCgKEkq+SOuRCbCCs7c=
|
||||
github.com/sagernet/sing-tun v0.4.0-beta.13.0.20240703164908-1f043289199d/go.mod h1:81JwnnYw8X9W9XvmZetSTTiPgIE3SbAbnc+EHKwPJ5U=
|
||||
github.com/sagernet/sing-vmess v0.1.12 h1:2gFD8JJb+eTFMoa8FIVMnknEi+vCSfaiTXTfEYAYAPg=
|
||||
github.com/sagernet/sing-vmess v0.1.12/go.mod h1:luTSsfyBGAc9VhtCqwjR+dt1QgqBhuYBCONB/POhF8I=
|
||||
github.com/sagernet/sing-tun v0.4.0-beta.11 h1:BRzcAFLhfysu3gd46CNJqIkgWw3rYDPKgUBbpbDvBfE=
|
||||
github.com/sagernet/sing-tun v0.4.0-beta.11/go.mod h1:YgaSM4cm+YIn6erBZN/eF+sW7I27BfBt91EWGo53MME=
|
||||
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc=
|
||||
github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA=
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
|
||||
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 h1:z3SJQhVyU63FT26Wn/UByW6b7q8QKB0ZkPqsyqcz2PI=
|
||||
@@ -222,5 +220,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
|
||||
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
||||
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
|
||||
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
|
||||
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||
|
||||
@@ -12,7 +12,10 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
"github.com/sagernet/sing/protocol/http"
|
||||
"github.com/sagernet/sing/protocol/socks"
|
||||
"github.com/sagernet/sing/protocol/socks/socks4"
|
||||
@@ -48,17 +51,16 @@ func NewMixed(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
}
|
||||
|
||||
func (h *Mixed) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
reader := std_bufio.NewReader(conn)
|
||||
headerBytes, err := reader.Peek(1)
|
||||
headerType, err := rw.ReadByte(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch headerBytes[0] {
|
||||
switch headerType {
|
||||
case socks4.Version, socks5.Version:
|
||||
return socks.HandleConnection0(ctx, conn, reader, h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata))
|
||||
default:
|
||||
return http.HandleConnection(ctx, conn, reader, h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata))
|
||||
return socks.HandleConnection0(ctx, conn, headerType, h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata))
|
||||
}
|
||||
reader := std_bufio.NewReader(bufio.NewCachedReader(conn, buf.As([]byte{headerType})))
|
||||
return http.HandleConnection(ctx, conn, reader, h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata))
|
||||
}
|
||||
|
||||
func (h *Mixed) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
|
||||
@@ -141,11 +141,11 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
||||
if ruleIndex == 0 {
|
||||
ruleIndex = tun.DefaultIPRoute2RuleIndex
|
||||
}
|
||||
inputMark := uint32(options.AutoRedirectInputMark)
|
||||
inputMark := options.AutoRedirectInputMark
|
||||
if inputMark == 0 {
|
||||
inputMark = tun.DefaultAutoRedirectInputMark
|
||||
}
|
||||
outputMark := uint32(options.AutoRedirectOutputMark)
|
||||
outputMark := options.AutoRedirectOutputMark
|
||||
if outputMark == 0 {
|
||||
outputMark = tun.DefaultAutoRedirectOutputMark
|
||||
}
|
||||
@@ -311,7 +311,7 @@ func (t *Tun) Start() error {
|
||||
forwarderBindInterface = true
|
||||
includeAllNetworks = t.platformInterface.IncludeAllNetworks()
|
||||
}
|
||||
tunStack, err := tun.NewStack(t.stack, tun.StackOptions{
|
||||
t.tunStack, err = tun.NewStack(t.stack, tun.StackOptions{
|
||||
Context: t.ctx,
|
||||
Tun: tunInterface,
|
||||
TunOptions: t.tunOptions,
|
||||
@@ -327,9 +327,8 @@ func (t *Tun) Start() error {
|
||||
return err
|
||||
}
|
||||
monitor.Start("initiating tun stack")
|
||||
err = tunStack.Start()
|
||||
err = t.tunStack.Start()
|
||||
monitor.Finish()
|
||||
t.tunStack = tunStack
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -356,7 +355,7 @@ func (t *Tun) PostStart() error {
|
||||
}
|
||||
t.routeExcludeAddressSet = append(t.routeExcludeAddressSet, ipSets...)
|
||||
}
|
||||
monitor.Start("initialize auto-redirect")
|
||||
monitor.Start("initiating auto-redirect")
|
||||
err := t.autoRedirect.Start()
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/v2ray"
|
||||
"github.com/sagernet/sing-box/transport/vless"
|
||||
"github.com/sagernet/sing-vmess"
|
||||
"github.com/sagernet/sing-vmess/packetaddr"
|
||||
"github.com/sagernet/sing-vmess/vless"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -83,11 +83,12 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
}
|
||||
|
||||
func (h *VLESS) Start() error {
|
||||
if h.tlsConfig != nil {
|
||||
err := h.tlsConfig.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err := common.Start(
|
||||
h.service,
|
||||
h.tlsConfig,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if h.transport == nil {
|
||||
return h.myInboundAdapter.Start()
|
||||
|
||||
@@ -93,16 +93,13 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
}
|
||||
|
||||
func (h *VMess) Start() error {
|
||||
err := h.service.Start()
|
||||
err := common.Start(
|
||||
h.service,
|
||||
h.tlsConfig,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if h.tlsConfig != nil {
|
||||
err = h.tlsConfig.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if h.transport == nil {
|
||||
return h.myInboundAdapter.Start()
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ func (r Rule) IsValid() bool {
|
||||
}
|
||||
}
|
||||
|
||||
type _DefaultRule struct {
|
||||
type DefaultRule struct {
|
||||
Inbound Listable[string] `json:"inbound,omitempty"`
|
||||
IPVersion int `json:"ip_version,omitempty"`
|
||||
Network Listable[string] `json:"network,omitempty"`
|
||||
@@ -94,31 +94,12 @@ type _DefaultRule struct {
|
||||
WIFISSID Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
RuleSet Listable[string] `json:"rule_set,omitempty"`
|
||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Outbound string `json:"outbound,omitempty"`
|
||||
|
||||
// Deprecated: renamed to rule_set_ip_cidr_match_source
|
||||
Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"`
|
||||
}
|
||||
|
||||
type DefaultRule _DefaultRule
|
||||
|
||||
func (r *DefaultRule) UnmarshalJSON(bytes []byte) error {
|
||||
err := json.Unmarshal(bytes, (*_DefaultRule)(r))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//nolint:staticcheck
|
||||
//goland:noinspection GoDeprecation
|
||||
if r.Deprecated_RulesetIPCIDRMatchSource {
|
||||
r.Deprecated_RulesetIPCIDRMatchSource = false
|
||||
r.RuleSetIPCIDRMatchSource = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DefaultRule) IsValid() bool {
|
||||
func (r DefaultRule) IsValid() bool {
|
||||
var defaultValue DefaultRule
|
||||
defaultValue.Invert = r.Invert
|
||||
defaultValue.Outbound = r.Outbound
|
||||
|
||||
@@ -64,7 +64,7 @@ func (r DNSRule) IsValid() bool {
|
||||
}
|
||||
}
|
||||
|
||||
type _DefaultDNSRule struct {
|
||||
type DefaultDNSRule struct {
|
||||
Inbound Listable[string] `json:"inbound,omitempty"`
|
||||
IPVersion int `json:"ip_version,omitempty"`
|
||||
QueryType Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||
@@ -96,35 +96,15 @@ type _DefaultDNSRule struct {
|
||||
WIFISSID Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
RuleSet Listable[string] `json:"rule_set,omitempty"`
|
||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||
RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,omitempty"`
|
||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Server string `json:"server,omitempty"`
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
||||
ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
|
||||
|
||||
// Deprecated: renamed to rule_set_ip_cidr_match_source
|
||||
Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"`
|
||||
}
|
||||
|
||||
type DefaultDNSRule _DefaultDNSRule
|
||||
|
||||
func (r *DefaultDNSRule) UnmarshalJSON(bytes []byte) error {
|
||||
err := json.Unmarshal(bytes, (*_DefaultDNSRule)(r))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//nolint:staticcheck
|
||||
//goland:noinspection GoDeprecation
|
||||
if r.Deprecated_RulesetIPCIDRMatchSource {
|
||||
r.Deprecated_RulesetIPCIDRMatchSource = false
|
||||
r.RuleSetIPCIDRMatchSource = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DefaultDNSRule) IsValid() bool {
|
||||
func (r DefaultDNSRule) IsValid() bool {
|
||||
var defaultValue DefaultDNSRule
|
||||
defaultValue.Invert = r.Invert
|
||||
defaultValue.Server = r.Server
|
||||
|
||||
@@ -17,7 +17,6 @@ type _RuleSet struct {
|
||||
Type string `json:"type"`
|
||||
Tag string `json:"tag"`
|
||||
Format string `json:"format"`
|
||||
InlineOptions PlainRuleSet `json:"-"`
|
||||
LocalOptions LocalRuleSet `json:"-"`
|
||||
RemoteOptions RemoteRuleSet `json:"-"`
|
||||
}
|
||||
@@ -27,15 +26,12 @@ type RuleSet _RuleSet
|
||||
func (r RuleSet) MarshalJSON() ([]byte, error) {
|
||||
var v any
|
||||
switch r.Type {
|
||||
case "", C.RuleSetTypeInline:
|
||||
r.Type = ""
|
||||
v = r.InlineOptions
|
||||
case C.RuleSetTypeLocal:
|
||||
v = r.LocalOptions
|
||||
case C.RuleSetTypeRemote:
|
||||
v = r.RemoteOptions
|
||||
default:
|
||||
return nil, E.New("unknown rule-set type: " + r.Type)
|
||||
return nil, E.New("unknown rule set type: " + r.Type)
|
||||
}
|
||||
return MarshallObjects((_RuleSet)(r), v)
|
||||
}
|
||||
@@ -48,28 +44,23 @@ func (r *RuleSet) UnmarshalJSON(bytes []byte) error {
|
||||
if r.Tag == "" {
|
||||
return E.New("missing tag")
|
||||
}
|
||||
if r.Type != C.RuleSetTypeInline {
|
||||
switch r.Format {
|
||||
case "":
|
||||
return E.New("missing format")
|
||||
case C.RuleSetFormatSource, C.RuleSetFormatBinary:
|
||||
default:
|
||||
return E.New("unknown rule-set format: " + r.Format)
|
||||
}
|
||||
} else {
|
||||
r.Format = ""
|
||||
switch r.Format {
|
||||
case "":
|
||||
return E.New("missing format")
|
||||
case C.RuleSetFormatSource, C.RuleSetFormatBinary:
|
||||
default:
|
||||
return E.New("unknown rule set format: " + r.Format)
|
||||
}
|
||||
var v any
|
||||
switch r.Type {
|
||||
case "", C.RuleSetTypeInline:
|
||||
r.Type = C.RuleSetTypeInline
|
||||
v = &r.InlineOptions
|
||||
case C.RuleSetTypeLocal:
|
||||
v = &r.LocalOptions
|
||||
case C.RuleSetTypeRemote:
|
||||
v = &r.RemoteOptions
|
||||
case "":
|
||||
return E.New("missing type")
|
||||
default:
|
||||
return E.New("unknown rule-set type: " + r.Type)
|
||||
return E.New("unknown rule set type: " + r.Type)
|
||||
}
|
||||
err = UnmarshallExcluded(bytes, (*_RuleSet)(r), v)
|
||||
if err != nil {
|
||||
@@ -197,7 +188,7 @@ func (r PlainRuleSetCompat) MarshalJSON() ([]byte, error) {
|
||||
case C.RuleSetVersion1:
|
||||
v = r.Options
|
||||
default:
|
||||
return nil, E.New("unknown rule-set version: ", r.Version)
|
||||
return nil, E.New("unknown rule set version: ", r.Version)
|
||||
}
|
||||
return MarshallObjects((_PlainRuleSetCompat)(r), v)
|
||||
}
|
||||
@@ -212,9 +203,9 @@ func (r *PlainRuleSetCompat) UnmarshalJSON(bytes []byte) error {
|
||||
case C.RuleSetVersion1:
|
||||
v = &r.Options
|
||||
case 0:
|
||||
return E.New("missing rule-set version")
|
||||
return E.New("missing rule set version")
|
||||
default:
|
||||
return E.New("unknown rule-set version: ", r.Version)
|
||||
return E.New("unknown rule set version: ", r.Version)
|
||||
}
|
||||
err = UnmarshallExcluded(bytes, (*_PlainRuleSetCompat)(r), v)
|
||||
if err != nil {
|
||||
@@ -223,13 +214,15 @@ func (r *PlainRuleSetCompat) UnmarshalJSON(bytes []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r PlainRuleSetCompat) Upgrade() (PlainRuleSet, error) {
|
||||
func (r PlainRuleSetCompat) Upgrade() PlainRuleSet {
|
||||
var result PlainRuleSet
|
||||
switch r.Version {
|
||||
case C.RuleSetVersion1:
|
||||
result = r.Options
|
||||
default:
|
||||
return PlainRuleSet{}, E.New("unknown rule-set version: " + F.ToString(r.Version))
|
||||
panic("unknown rule set version: " + F.ToString(r.Version))
|
||||
}
|
||||
return r.Options, nil
|
||||
return result
|
||||
}
|
||||
|
||||
type PlainRuleSet struct {
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"strconv"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
)
|
||||
import "net/netip"
|
||||
|
||||
type TunInboundOptions struct {
|
||||
InterfaceName string `json:"interface_name,omitempty"`
|
||||
@@ -18,8 +11,8 @@ type TunInboundOptions struct {
|
||||
IPRoute2TableIndex int `json:"iproute2_table_index,omitempty"`
|
||||
IPRoute2RuleIndex int `json:"iproute2_rule_index,omitempty"`
|
||||
AutoRedirect bool `json:"auto_redirect,omitempty"`
|
||||
AutoRedirectInputMark FwMark `json:"auto_redirect_input_mark,omitempty"`
|
||||
AutoRedirectOutputMark FwMark `json:"auto_redirect_output_mark,omitempty"`
|
||||
AutoRedirectInputMark uint32 `json:"auto_redirect_input_mark,omitempty"`
|
||||
AutoRedirectOutputMark uint32 `json:"auto_redirect_output_mark,omitempty"`
|
||||
StrictRoute bool `json:"strict_route,omitempty"`
|
||||
RouteAddress Listable[netip.Prefix] `json:"route_address,omitempty"`
|
||||
RouteAddressSet Listable[string] `json:"route_address_set,omitempty"`
|
||||
@@ -53,26 +46,3 @@ type TunInboundOptions struct {
|
||||
// Deprecated: merged to RouteExcludeAddress
|
||||
Inet6RouteExcludeAddress Listable[netip.Prefix] `json:"inet6_route_exclude_address,omitempty"`
|
||||
}
|
||||
|
||||
type FwMark uint32
|
||||
|
||||
func (f FwMark) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(F.ToString("0x", strconv.FormatUint(uint64(f), 16)))
|
||||
}
|
||||
|
||||
func (f *FwMark) UnmarshalJSON(bytes []byte) error {
|
||||
var stringValue string
|
||||
err := json.Unmarshal(bytes, &stringValue)
|
||||
if err != nil {
|
||||
if rawErr := json.Unmarshal(bytes, (*uint32)(f)); rawErr == nil {
|
||||
return nil
|
||||
}
|
||||
return E.Cause(err, "invalid number or string mark")
|
||||
}
|
||||
intValue, err := strconv.ParseUint(stringValue, 0, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*f = FwMark(intValue)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -130,8 +130,8 @@ func (h *Hysteria) NewPacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||
return NewPacketConnection(ctx, h, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *Hysteria) InterfaceUpdated() {
|
||||
h.client.CloseWithError(E.New("network changed"))
|
||||
func (h *Hysteria) InterfaceUpdated() error {
|
||||
return h.client.CloseWithError(E.New("network changed"))
|
||||
}
|
||||
|
||||
func (h *Hysteria) Close() error {
|
||||
|
||||
@@ -116,8 +116,8 @@ func (h *Hysteria2) NewPacketConnection(ctx context.Context, conn N.PacketConn,
|
||||
return NewPacketConnection(ctx, h, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *Hysteria2) InterfaceUpdated() {
|
||||
h.client.CloseWithError(E.New("network changed"))
|
||||
func (h *Hysteria2) InterfaceUpdated() error {
|
||||
return h.client.CloseWithError(E.New("network changed"))
|
||||
}
|
||||
|
||||
func (h *Hysteria2) Close() error {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package outbound
|
||||
|
||||
import (
|
||||
std_bufio "bufio"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
@@ -10,10 +11,16 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
"github.com/sagernet/sing/protocol/http"
|
||||
"github.com/sagernet/sing/protocol/socks"
|
||||
"github.com/sagernet/sing/protocol/socks/socks4"
|
||||
"github.com/sagernet/sing/protocol/socks/socks5"
|
||||
)
|
||||
|
||||
type ProxyListener struct {
|
||||
@@ -95,7 +102,16 @@ func (l *ProxyListener) acceptLoop() {
|
||||
}
|
||||
|
||||
func (l *ProxyListener) accept(ctx context.Context, conn *net.TCPConn) error {
|
||||
return socks.HandleConnection(ctx, conn, l.authenticator, l, M.Metadata{})
|
||||
headerType, err := rw.ReadByte(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch headerType {
|
||||
case socks4.Version, socks5.Version:
|
||||
return socks.HandleConnection0(ctx, conn, headerType, l.authenticator, l, M.Metadata{})
|
||||
}
|
||||
reader := std_bufio.NewReader(bufio.NewCachedReader(conn, buf.As([]byte{headerType})))
|
||||
return http.HandleConnection(ctx, conn, reader, l.authenticator, l, M.Metadata{})
|
||||
}
|
||||
|
||||
func (l *ProxyListener) NewConnection(ctx context.Context, conn net.Conn, upstreamMetadata M.Metadata) error {
|
||||
|
||||
@@ -44,10 +44,10 @@ func NewTor(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
||||
startConf.ExtraArgs = options.ExtraArgs
|
||||
if options.DataDirectory != "" {
|
||||
dataDirAbs, _ := filepath.Abs(startConf.DataDir)
|
||||
if geoIPPath := filepath.Join(dataDirAbs, "geoip"); rw.IsFile(geoIPPath) && !common.Contains(options.ExtraArgs, "--GeoIPFile") {
|
||||
if geoIPPath := filepath.Join(dataDirAbs, "geoip"); rw.FileExists(geoIPPath) && !common.Contains(options.ExtraArgs, "--GeoIPFile") {
|
||||
options.ExtraArgs = append(options.ExtraArgs, "--GeoIPFile", geoIPPath)
|
||||
}
|
||||
if geoIP6Path := filepath.Join(dataDirAbs, "geoip6"); rw.IsFile(geoIP6Path) && !common.Contains(options.ExtraArgs, "--GeoIPv6File") {
|
||||
if geoIP6Path := filepath.Join(dataDirAbs, "geoip6"); rw.FileExists(geoIP6Path) && !common.Contains(options.ExtraArgs, "--GeoIPv6File") {
|
||||
options.ExtraArgs = append(options.ExtraArgs, "--GeoIPv6File", geoIP6Path)
|
||||
}
|
||||
}
|
||||
@@ -58,12 +58,8 @@ func NewTor(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
||||
}
|
||||
if startConf.DataDir != "" {
|
||||
torrcFile := filepath.Join(startConf.DataDir, "torrc")
|
||||
err := rw.MkdirParent(torrcFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !rw.IsFile(torrcFile) {
|
||||
err := os.WriteFile(torrcFile, []byte(""), 0o600)
|
||||
if !rw.FileExists(torrcFile) {
|
||||
err := rw.WriteFile(torrcFile, []byte(""))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -108,9 +108,6 @@ func (h *Trojan) NewPacketConnection(ctx context.Context, conn N.PacketConn, met
|
||||
}
|
||||
|
||||
func (h *Trojan) InterfaceUpdated() {
|
||||
if h.transport != nil {
|
||||
h.transport.Close()
|
||||
}
|
||||
if h.multiplexDialer != nil {
|
||||
h.multiplexDialer.Reset()
|
||||
}
|
||||
|
||||
@@ -385,9 +385,9 @@ func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint
|
||||
continue
|
||||
}
|
||||
b.Go(realTag, func() (any, error) {
|
||||
testCtx, cancel := context.WithTimeout(g.ctx, C.TCPTimeout)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), C.TCPTimeout)
|
||||
defer cancel()
|
||||
t, err := urltest.URLTest(testCtx, g.link, p)
|
||||
t, err := urltest.URLTest(ctx, g.link, p)
|
||||
if err != nil {
|
||||
g.logger.Debug("outbound ", tag, " unavailable: ", err)
|
||||
g.history.DeleteURLTestHistory(realTag)
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/v2ray"
|
||||
"github.com/sagernet/sing-box/transport/vless"
|
||||
"github.com/sagernet/sing-vmess/packetaddr"
|
||||
"github.com/sagernet/sing-vmess/vless"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -127,9 +127,6 @@ func (h *VLESS) NewPacketConnection(ctx context.Context, conn N.PacketConn, meta
|
||||
}
|
||||
|
||||
func (h *VLESS) InterfaceUpdated() {
|
||||
if h.transport != nil {
|
||||
h.transport.Close()
|
||||
}
|
||||
if h.multiplexDialer != nil {
|
||||
h.multiplexDialer.Reset()
|
||||
}
|
||||
|
||||
@@ -103,9 +103,6 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
}
|
||||
|
||||
func (h *VMess) InterfaceUpdated() {
|
||||
if h.transport != nil {
|
||||
h.transport.Close()
|
||||
}
|
||||
if h.multiplexDialer != nil {
|
||||
h.multiplexDialer.Reset()
|
||||
}
|
||||
|
||||
@@ -534,10 +534,7 @@ func (r *Router) Start() error {
|
||||
|
||||
if r.needPackageManager && r.platformInterface == nil {
|
||||
monitor.Start("initialize package manager")
|
||||
packageManager, err := tun.NewPackageManager(tun.PackageManagerOptions{
|
||||
Callback: r,
|
||||
Logger: r.logger,
|
||||
})
|
||||
packageManager, err := tun.NewPackageManager(r)
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
return E.Cause(err, "create package manager")
|
||||
@@ -986,7 +983,6 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||
sniff.STUNMessage,
|
||||
sniff.UTP,
|
||||
sniff.UDPTracker,
|
||||
sniff.DTLSRecord,
|
||||
)
|
||||
if sniffMetadata != nil {
|
||||
metadata.Protocol = sniffMetadata.Protocol
|
||||
|
||||
@@ -36,7 +36,7 @@ func (m *DNSReverseMapping) Query(address netip.Addr) (string, bool) {
|
||||
return domain, loaded
|
||||
}
|
||||
|
||||
func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, index int, isAddressQuery bool) (context.Context, dns.Transport, dns.DomainStrategy, adapter.DNSRule, int) {
|
||||
func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, index int) (context.Context, dns.Transport, dns.DomainStrategy, adapter.DNSRule, int) {
|
||||
metadata := adapter.ContextFrom(ctx)
|
||||
if metadata == nil {
|
||||
panic("no context")
|
||||
@@ -47,9 +47,6 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, index int, isAd
|
||||
dnsRules = dnsRules[index+1:]
|
||||
}
|
||||
for currentRuleIndex, rule := range dnsRules {
|
||||
if rule.WithAddressLimit() && !isAddressQuery {
|
||||
continue
|
||||
}
|
||||
metadata.ResetRuleCache()
|
||||
if rule.Match(metadata) {
|
||||
detour := rule.Outbound()
|
||||
@@ -104,8 +101,7 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
|
||||
response, cached = r.dnsClient.ExchangeCache(ctx, message)
|
||||
if !cached {
|
||||
var metadata *adapter.InboundContext
|
||||
ctx, metadata = adapter.ExtendContext(ctx)
|
||||
metadata.Destination = M.Socksaddr{}
|
||||
ctx, metadata = adapter.AppendContext(ctx)
|
||||
if len(message.Question) > 0 {
|
||||
metadata.QueryType = message.Question[0].Qtype
|
||||
switch metadata.QueryType {
|
||||
@@ -127,16 +123,12 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
|
||||
dnsCtx context.Context
|
||||
addressLimit bool
|
||||
)
|
||||
dnsCtx, transport, strategy, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex, isAddressQuery(message))
|
||||
dnsCtx = adapter.OverrideContext(dnsCtx)
|
||||
if rule != nil && rule.WithAddressLimit() {
|
||||
|
||||
dnsCtx, transport, strategy, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex)
|
||||
if rule != nil && rule.WithAddressLimit() && isAddressQuery(message) {
|
||||
addressLimit = true
|
||||
response, err = r.dnsClient.ExchangeWithResponseCheck(dnsCtx, transport, message, strategy, func(response *mDNS.Msg) bool {
|
||||
addresses, addrErr := dns.MessageToAddresses(response)
|
||||
if addrErr != nil {
|
||||
return false
|
||||
}
|
||||
metadata.DestinationAddresses = addresses
|
||||
metadata.DestinationAddresses, _ = dns.MessageToAddresses(response)
|
||||
return rule.MatchAddressLimit(metadata)
|
||||
})
|
||||
} else {
|
||||
@@ -192,8 +184,7 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS
|
||||
return responseAddrs, nil
|
||||
}
|
||||
r.dnsLogger.DebugContext(ctx, "lookup domain ", domain)
|
||||
ctx, metadata := adapter.ExtendContext(ctx)
|
||||
metadata.Destination = M.Socksaddr{}
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
metadata.Domain = domain
|
||||
var (
|
||||
transport dns.Transport
|
||||
@@ -207,8 +198,9 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS
|
||||
dnsCtx context.Context
|
||||
addressLimit bool
|
||||
)
|
||||
dnsCtx, transport, transportStrategy, rule, ruleIndex = r.matchDNS(ctx, false, ruleIndex, true)
|
||||
dnsCtx = adapter.OverrideContext(dnsCtx)
|
||||
metadata.ResetRuleCache()
|
||||
metadata.DestinationAddresses = nil
|
||||
dnsCtx, transport, transportStrategy, rule, ruleIndex = r.matchDNS(ctx, false, ruleIndex)
|
||||
if strategy == dns.DomainStrategyAsIS {
|
||||
strategy = transportStrategy
|
||||
}
|
||||
@@ -257,7 +249,7 @@ func (r *Router) ClearDNSCache() {
|
||||
|
||||
func isAddressQuery(message *mDNS.Msg) bool {
|
||||
for _, question := range message.Question {
|
||||
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA || question.Qtype == mDNS.TypeHTTPS {
|
||||
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ func (r *Router) prepareGeoIPDatabase() error {
|
||||
geoPath = foundPath
|
||||
}
|
||||
}
|
||||
if !rw.IsFile(geoPath) {
|
||||
if !rw.FileExists(geoPath) {
|
||||
geoPath = filemanager.BasePath(r.ctx, geoPath)
|
||||
}
|
||||
if stat, err := os.Stat(geoPath); err == nil {
|
||||
@@ -61,7 +61,7 @@ func (r *Router) prepareGeoIPDatabase() error {
|
||||
os.Remove(geoPath)
|
||||
}
|
||||
}
|
||||
if !rw.IsFile(geoPath) {
|
||||
if !rw.FileExists(geoPath) {
|
||||
r.logger.Warn("geoip database not exists: ", geoPath)
|
||||
var err error
|
||||
for attempts := 0; attempts < 3; attempts++ {
|
||||
@@ -96,7 +96,7 @@ func (r *Router) prepareGeositeDatabase() error {
|
||||
geoPath = foundPath
|
||||
}
|
||||
}
|
||||
if !rw.IsFile(geoPath) {
|
||||
if !rw.FileExists(geoPath) {
|
||||
geoPath = filemanager.BasePath(r.ctx, geoPath)
|
||||
}
|
||||
if stat, err := os.Stat(geoPath); err == nil {
|
||||
@@ -107,7 +107,7 @@ func (r *Router) prepareGeositeDatabase() error {
|
||||
os.Remove(geoPath)
|
||||
}
|
||||
}
|
||||
if !rw.IsFile(geoPath) {
|
||||
if !rw.FileExists(geoPath) {
|
||||
r.logger.Warn("geosite database not exists: ", geoPath)
|
||||
var err error
|
||||
for attempts := 0; attempts < 3; attempts++ {
|
||||
|
||||
@@ -29,13 +29,9 @@ func (r *abstractDefaultRule) Type() string {
|
||||
|
||||
func (r *abstractDefaultRule) Start() error {
|
||||
for _, item := range r.allItems {
|
||||
if starter, isStarter := item.(interface {
|
||||
Start() error
|
||||
}); isStarter {
|
||||
err := starter.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err := common.Start(item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -187,13 +183,8 @@ func (r *abstractLogicalRule) UpdateGeosite() error {
|
||||
}
|
||||
|
||||
func (r *abstractLogicalRule) Start() error {
|
||||
for _, rule := range common.FilterIsInstance(r.rules, func(it adapter.HeadlessRule) (interface {
|
||||
Start() error
|
||||
}, bool,
|
||||
) {
|
||||
rule, loaded := it.(interface {
|
||||
Start() error
|
||||
})
|
||||
for _, rule := range common.FilterIsInstance(r.rules, func(it adapter.HeadlessRule) (common.Starter, bool) {
|
||||
rule, loaded := it.(common.Starter)
|
||||
return rule, loaded
|
||||
}) {
|
||||
err := rule.Start()
|
||||
|
||||
@@ -205,7 +205,7 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.RuleSet) > 0 {
|
||||
item := NewRuleSetItem(router, options.RuleSet, options.RuleSetIPCIDRMatchSource, false)
|
||||
item := NewRuleSetItem(router, options.RuleSet, options.RuleSetIPCIDRMatchSource)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.RuleSet) > 0 {
|
||||
item := NewRuleSetItem(router, options.RuleSet, options.RuleSetIPCIDRMatchSource, options.RuleSetIPCIDRAcceptEmpty)
|
||||
item := NewRuleSetItem(router, options.RuleSet, options.RuleSetIPCIDRMatchSource)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
|
||||
@@ -75,19 +75,18 @@ func NewRawIPCIDRItem(isSource bool, ipSet *netipx.IPSet) *IPCIDRItem {
|
||||
func (r *IPCIDRItem) Match(metadata *adapter.InboundContext) bool {
|
||||
if r.isSource || metadata.IPCIDRMatchSource {
|
||||
return r.ipSet.Contains(metadata.Source.Addr)
|
||||
}
|
||||
if metadata.Destination.IsIP() {
|
||||
return r.ipSet.Contains(metadata.Destination.Addr)
|
||||
}
|
||||
if len(metadata.DestinationAddresses) > 0 {
|
||||
for _, address := range metadata.DestinationAddresses {
|
||||
if r.ipSet.Contains(address) {
|
||||
return true
|
||||
} else {
|
||||
if metadata.Destination.IsIP() {
|
||||
return r.ipSet.Contains(metadata.Destination.Addr)
|
||||
} else {
|
||||
for _, address := range metadata.DestinationAddresses {
|
||||
if r.ipSet.Contains(address) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return metadata.IPCIDRAcceptEmpty
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *IPCIDRItem) String() string {
|
||||
|
||||
@@ -39,7 +39,7 @@ func NewPortRangeItem(isSource bool, rangeList []string) (*PortRangeItem, error)
|
||||
}
|
||||
}
|
||||
if subIndex == len(portRange)-1 {
|
||||
end = 0xFFFF
|
||||
end = 0xFF
|
||||
} else {
|
||||
end, err = strconv.ParseUint(portRange[subIndex+1:], 10, 16)
|
||||
if err != nil {
|
||||
|
||||
@@ -15,16 +15,14 @@ type RuleSetItem struct {
|
||||
router adapter.Router
|
||||
tagList []string
|
||||
setList []adapter.RuleSet
|
||||
ipCidrMatchSource bool
|
||||
ipCidrAcceptEmpty bool
|
||||
ipcidrMatchSource bool
|
||||
}
|
||||
|
||||
func NewRuleSetItem(router adapter.Router, tagList []string, ipCIDRMatchSource bool, ipCidrAcceptEmpty bool) *RuleSetItem {
|
||||
func NewRuleSetItem(router adapter.Router, tagList []string, ipCIDRMatchSource bool) *RuleSetItem {
|
||||
return &RuleSetItem{
|
||||
router: router,
|
||||
tagList: tagList,
|
||||
ipCidrMatchSource: ipCIDRMatchSource,
|
||||
ipCidrAcceptEmpty: ipCidrAcceptEmpty,
|
||||
ipcidrMatchSource: ipCIDRMatchSource,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +39,7 @@ func (r *RuleSetItem) Start() error {
|
||||
}
|
||||
|
||||
func (r *RuleSetItem) Match(metadata *adapter.InboundContext) bool {
|
||||
metadata.IPCIDRMatchSource = r.ipCidrMatchSource
|
||||
metadata.IPCIDRAcceptEmpty = r.ipCidrAcceptEmpty
|
||||
metadata.IPCIDRMatchSource = r.ipcidrMatchSource
|
||||
for _, ruleSet := range r.setList {
|
||||
if ruleSet.Match(metadata) {
|
||||
return true
|
||||
@@ -52,7 +49,7 @@ func (r *RuleSetItem) Match(metadata *adapter.InboundContext) bool {
|
||||
}
|
||||
|
||||
func (r *RuleSetItem) ContainsDestinationIPCIDRRule() bool {
|
||||
if r.ipCidrMatchSource {
|
||||
if r.ipcidrMatchSource {
|
||||
return false
|
||||
}
|
||||
return common.Any(r.setList, func(ruleSet adapter.RuleSet) bool {
|
||||
|
||||
@@ -20,12 +20,12 @@ import (
|
||||
|
||||
func NewRuleSet(ctx context.Context, router adapter.Router, logger logger.ContextLogger, options option.RuleSet) (adapter.RuleSet, error) {
|
||||
switch options.Type {
|
||||
case C.RuleSetTypeInline, C.RuleSetTypeLocal, "":
|
||||
return NewLocalRuleSet(router, logger, options)
|
||||
case C.RuleSetTypeLocal:
|
||||
return NewLocalRuleSet(router, options)
|
||||
case C.RuleSetTypeRemote:
|
||||
return NewRemoteRuleSet(ctx, router, logger, options), nil
|
||||
default:
|
||||
return nil, E.New("unknown rule-set type: ", options.Type)
|
||||
return nil, E.New("unknown rule set type: ", options.Type)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,8 @@ package route
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/fswatch"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/srs"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
@@ -16,7 +14,6 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
|
||||
"go4.org/netipx"
|
||||
@@ -25,55 +22,50 @@ import (
|
||||
var _ adapter.RuleSet = (*LocalRuleSet)(nil)
|
||||
|
||||
type LocalRuleSet struct {
|
||||
router adapter.Router
|
||||
logger logger.Logger
|
||||
tag string
|
||||
rules []adapter.HeadlessRule
|
||||
metadata adapter.RuleSetMetadata
|
||||
fileFormat string
|
||||
watcher *fswatch.Watcher
|
||||
refs atomic.Int32
|
||||
tag string
|
||||
rules []adapter.HeadlessRule
|
||||
metadata adapter.RuleSetMetadata
|
||||
refs atomic.Int32
|
||||
}
|
||||
|
||||
func NewLocalRuleSet(router adapter.Router, logger logger.Logger, options option.RuleSet) (*LocalRuleSet, error) {
|
||||
ruleSet := &LocalRuleSet{
|
||||
router: router,
|
||||
logger: logger,
|
||||
tag: options.Tag,
|
||||
fileFormat: options.Format,
|
||||
}
|
||||
if options.Type == C.RuleSetTypeInline {
|
||||
if len(options.InlineOptions.Rules) == 0 {
|
||||
return nil, E.New("empty inline rule-set")
|
||||
}
|
||||
err := ruleSet.reloadRules(options.InlineOptions.Rules)
|
||||
func NewLocalRuleSet(router adapter.Router, options option.RuleSet) (*LocalRuleSet, error) {
|
||||
var plainRuleSet option.PlainRuleSet
|
||||
switch options.Format {
|
||||
case C.RuleSetFormatSource, "":
|
||||
content, err := os.ReadFile(options.LocalOptions.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
err := ruleSet.reloadFile(options.LocalOptions.Path)
|
||||
compat, err := json.UnmarshalExtended[option.PlainRuleSetCompat](content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if options.Type == C.RuleSetTypeLocal {
|
||||
var watcher *fswatch.Watcher
|
||||
filePath, _ := filepath.Abs(options.LocalOptions.Path)
|
||||
watcher, err := fswatch.NewWatcher(fswatch.Options{
|
||||
Path: []string{filePath},
|
||||
Callback: func(path string) {
|
||||
uErr := ruleSet.reloadFile(path)
|
||||
if uErr != nil {
|
||||
logger.Error(E.Cause(uErr, "reload rule-set ", options.Tag))
|
||||
}
|
||||
},
|
||||
})
|
||||
plainRuleSet = compat.Upgrade()
|
||||
case C.RuleSetFormatBinary:
|
||||
setFile, err := os.Open(options.LocalOptions.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ruleSet.watcher = watcher
|
||||
plainRuleSet, err = srs.Read(setFile, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, E.New("unknown rule set format: ", options.Format)
|
||||
}
|
||||
return ruleSet, nil
|
||||
rules := make([]adapter.HeadlessRule, len(plainRuleSet.Rules))
|
||||
var err error
|
||||
for i, ruleOptions := range plainRuleSet.Rules {
|
||||
rules[i], err = NewHeadlessRule(router, ruleOptions)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse rule_set.rules.[", i, "]")
|
||||
}
|
||||
}
|
||||
var metadata adapter.RuleSetMetadata
|
||||
metadata.ContainsProcessRule = hasHeadlessRule(plainRuleSet.Rules, isProcessHeadlessRule)
|
||||
metadata.ContainsWIFIRule = hasHeadlessRule(plainRuleSet.Rules, isWIFIHeadlessRule)
|
||||
metadata.ContainsIPCIDRRule = hasHeadlessRule(plainRuleSet.Rules, isIPCIDRHeadlessRule)
|
||||
return &LocalRuleSet{tag: options.Tag, rules: rules, metadata: metadata}, nil
|
||||
}
|
||||
|
||||
func (s *LocalRuleSet) Name() string {
|
||||
@@ -85,61 +77,6 @@ func (s *LocalRuleSet) String() string {
|
||||
}
|
||||
|
||||
func (s *LocalRuleSet) StartContext(ctx context.Context, startContext adapter.RuleSetStartContext) error {
|
||||
if s.watcher != nil {
|
||||
err := s.watcher.Start()
|
||||
if err != nil {
|
||||
s.logger.Error(E.Cause(err, "watch rule-set file"))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *LocalRuleSet) reloadFile(path string) error {
|
||||
var plainRuleSet option.PlainRuleSet
|
||||
switch s.fileFormat {
|
||||
case C.RuleSetFormatSource, "":
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
compat, err := json.UnmarshalExtended[option.PlainRuleSetCompat](content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
plainRuleSet, err = compat.Upgrade()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case C.RuleSetFormatBinary:
|
||||
setFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
plainRuleSet, err = srs.Read(setFile, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return E.New("unknown rule-set format: ", s.fileFormat)
|
||||
}
|
||||
return s.reloadRules(plainRuleSet.Rules)
|
||||
}
|
||||
|
||||
func (s *LocalRuleSet) reloadRules(headlessRules []option.HeadlessRule) error {
|
||||
rules := make([]adapter.HeadlessRule, len(headlessRules))
|
||||
var err error
|
||||
for i, ruleOptions := range headlessRules {
|
||||
rules[i], err = NewHeadlessRule(s.router, ruleOptions)
|
||||
if err != nil {
|
||||
return E.Cause(err, "parse rule_set.rules.[", i, "]")
|
||||
}
|
||||
}
|
||||
var metadata adapter.RuleSetMetadata
|
||||
metadata.ContainsProcessRule = hasHeadlessRule(headlessRules, isProcessHeadlessRule)
|
||||
metadata.ContainsWIFIRule = hasHeadlessRule(headlessRules, isWIFIHeadlessRule)
|
||||
metadata.ContainsIPCIDRRule = hasHeadlessRule(headlessRules, isIPCIDRHeadlessRule)
|
||||
s.rules = rules
|
||||
s.metadata = metadata
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -180,7 +117,7 @@ func (s *LocalRuleSet) UnregisterCallback(element *list.Element[adapter.RuleSetU
|
||||
|
||||
func (s *LocalRuleSet) Close() error {
|
||||
s.rules = nil
|
||||
return common.Close(common.PtrOrNil(s.watcher))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *LocalRuleSet) Match(metadata *adapter.InboundContext) bool {
|
||||
|
||||
@@ -168,17 +168,14 @@ func (s *RemoteRuleSet) loadBytes(content []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
plainRuleSet, err = compat.Upgrade()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
plainRuleSet = compat.Upgrade()
|
||||
case C.RuleSetFormatBinary:
|
||||
plainRuleSet, err = srs.Read(bytes.NewReader(content), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return E.New("unknown rule-set format: ", s.options.Format)
|
||||
return E.New("unknown rule set format: ", s.options.Format)
|
||||
}
|
||||
rules := make([]adapter.HeadlessRule, len(plainRuleSet.Rules))
|
||||
for i, ruleOptions := range plainRuleSet.Rules {
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
package trojan
|
||||
|
||||
import (
|
||||
std_bufio "bufio"
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"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"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
"github.com/sagernet/sing/common/task"
|
||||
"github.com/sagernet/smux"
|
||||
)
|
||||
@@ -35,36 +33,27 @@ func HandleMuxConnection(ctx context.Context, conn net.Conn, metadata M.Metadata
|
||||
return group.Run(ctx)
|
||||
}
|
||||
|
||||
func newMuxConnection(ctx context.Context, conn net.Conn, metadata M.Metadata, handler Handler) {
|
||||
err := newMuxConnection0(ctx, conn, metadata, handler)
|
||||
func newMuxConnection(ctx context.Context, stream net.Conn, metadata M.Metadata, handler Handler) {
|
||||
err := newMuxConnection0(ctx, stream, metadata, handler)
|
||||
if err != nil {
|
||||
handler.NewError(ctx, E.Cause(err, "process trojan-go multiplex connection"))
|
||||
}
|
||||
}
|
||||
|
||||
func newMuxConnection0(ctx context.Context, conn net.Conn, metadata M.Metadata, handler Handler) error {
|
||||
reader := std_bufio.NewReader(conn)
|
||||
command, err := reader.ReadByte()
|
||||
func newMuxConnection0(ctx context.Context, stream net.Conn, metadata M.Metadata, handler Handler) error {
|
||||
command, err := rw.ReadByte(stream)
|
||||
if err != nil {
|
||||
return E.Cause(err, "read command")
|
||||
}
|
||||
metadata.Destination, err = M.SocksaddrSerializer.ReadAddrPort(reader)
|
||||
metadata.Destination, err = M.SocksaddrSerializer.ReadAddrPort(stream)
|
||||
if err != nil {
|
||||
return E.Cause(err, "read destination")
|
||||
}
|
||||
if reader.Buffered() > 0 {
|
||||
buffer := buf.NewSize(reader.Buffered())
|
||||
_, err = buffer.ReadFullFrom(reader, buffer.Len())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conn = bufio.NewCachedConn(conn, buffer)
|
||||
}
|
||||
switch command {
|
||||
case CommandTCP:
|
||||
return handler.NewConnection(ctx, conn, metadata)
|
||||
return handler.NewConnection(ctx, stream, metadata)
|
||||
case CommandUDP:
|
||||
return handler.NewPacketConnection(ctx, &PacketConn{Conn: conn}, metadata)
|
||||
return handler.NewPacketConnection(ctx, &PacketConn{Conn: stream}, metadata)
|
||||
default:
|
||||
return E.New("unknown command ", command)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package trojan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
@@ -77,8 +76,7 @@ func (s *Service[K]) NewConnection(ctx context.Context, conn net.Conn, metadata
|
||||
return E.Cause(err, "skip crlf")
|
||||
}
|
||||
|
||||
var command byte
|
||||
err = binary.Read(conn, binary.BigEndian, &command)
|
||||
command, err := rw.ReadByte(conn)
|
||||
if err != nil {
|
||||
return E.Cause(err, "read command")
|
||||
}
|
||||
|
||||
@@ -72,6 +72,12 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
return common.Close(
|
||||
common.PtrOrNil(c.conn),
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Client) connect() (*grpc.ClientConn, error) {
|
||||
conn := c.conn
|
||||
if conn != nil && conn.GetState() != connectivity.Shutdown {
|
||||
@@ -107,13 +113,3 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
}
|
||||
return NewGRPCConn(stream, cancel), nil
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
c.connAccess.Lock()
|
||||
defer c.connAccess.Unlock()
|
||||
if c.conn != nil {
|
||||
c.conn.Close()
|
||||
c.conn = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/baderror"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
var _ net.Conn = (*GRPCConn)(nil)
|
||||
@@ -90,7 +90,7 @@ func (c *GRPCConn) Upstream() any {
|
||||
return c.GunService
|
||||
}
|
||||
|
||||
var _ N.WriteCloser = (*clientConnWrapper)(nil)
|
||||
var _ rw.WriteCloser = (*clientConnWrapper)(nil)
|
||||
|
||||
type clientConnWrapper struct {
|
||||
GunService_TunClient
|
||||
|
||||
@@ -100,7 +100,7 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
conn.setup(nil, err)
|
||||
} else if response.StatusCode != 200 {
|
||||
response.Body.Close()
|
||||
conn.setup(nil, E.New("v2ray-grpc: unexpected status: ", response.Status))
|
||||
conn.setup(nil, E.New("unexpected status: ", response.Status))
|
||||
} else {
|
||||
conn.setup(response.Body, nil)
|
||||
}
|
||||
@@ -109,6 +109,8 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
v2rayhttp.ResetTransport(c.transport)
|
||||
if c.transport != nil {
|
||||
v2rayhttp.CloseIdleConnections(c.transport)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/sagernet/sing/common/baderror"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
// kanged from: https://github.com/Qv2ray/gun-lite
|
||||
@@ -96,7 +96,7 @@ func (c *GunConn) read(b []byte) (n int, err error) {
|
||||
}
|
||||
|
||||
func (c *GunConn) Write(b []byte) (n int, err error) {
|
||||
varLen := varbin.UvarintLen(uint64(len(b)))
|
||||
varLen := rw.UVariantLen(uint64(len(b)))
|
||||
buffer := buf.NewSize(6 + varLen + len(b))
|
||||
header := buffer.Extend(6 + varLen)
|
||||
header[0] = 0x00
|
||||
@@ -117,13 +117,13 @@ func (c *GunConn) Write(b []byte) (n int, err error) {
|
||||
func (c *GunConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||
defer buffer.Release()
|
||||
dataLen := buffer.Len()
|
||||
varLen := varbin.UvarintLen(uint64(dataLen))
|
||||
varLen := rw.UVariantLen(uint64(dataLen))
|
||||
header := buffer.ExtendHeader(6 + varLen)
|
||||
header[0] = 0x00
|
||||
binary.BigEndian.PutUint32(header[1:5], uint32(1+varLen+dataLen))
|
||||
header[5] = 0x0A
|
||||
binary.PutUvarint(header[6:], uint64(dataLen))
|
||||
err := common.Error(c.writer.Write(buffer.Bytes()))
|
||||
err := rw.WriteBytes(c.writer, buffer.Bytes())
|
||||
if err != nil {
|
||||
return baderror.WrapH2(err)
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) {
|
||||
conn.Setup(nil, err)
|
||||
} else if response.StatusCode != 200 {
|
||||
response.Body.Close()
|
||||
conn.Setup(nil, E.New("v2ray-http: unexpected status: ", response.Status))
|
||||
conn.Setup(nil, E.New("unexpected status: ", response.Status))
|
||||
} else {
|
||||
conn.Setup(response.Body, nil)
|
||||
}
|
||||
@@ -155,6 +155,6 @@ func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) {
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
c.transport = ResetTransport(c.transport)
|
||||
CloseIdleConnections(c.transport)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ func (c *HTTPConn) Read(b []byte) (n int, err error) {
|
||||
return 0, E.Cause(err, "read response")
|
||||
}
|
||||
if response.StatusCode != 200 {
|
||||
return 0, E.New("v2ray-http: unexpected status: ", response.Status)
|
||||
return 0, E.New("unexpected status: ", response.Status)
|
||||
}
|
||||
if cacheLen := reader.Buffered(); cacheLen > 0 {
|
||||
c.responseCache = buf.NewSize(cacheLen)
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
package v2rayhttp
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
type clientConnPool struct {
|
||||
t *http2.Transport
|
||||
mu sync.Mutex
|
||||
conns map[string][]*http2.ClientConn // key is host:port
|
||||
}
|
||||
|
||||
type efaceWords struct {
|
||||
typ unsafe.Pointer
|
||||
data unsafe.Pointer
|
||||
}
|
||||
|
||||
func ResetTransport(rawTransport http.RoundTripper) http.RoundTripper {
|
||||
switch transport := rawTransport.(type) {
|
||||
case *http.Transport:
|
||||
transport.CloseIdleConnections()
|
||||
return transport.Clone()
|
||||
case *http2.Transport:
|
||||
connPool := transportConnPool(transport)
|
||||
p := (*clientConnPool)((*efaceWords)(unsafe.Pointer(&connPool)).data)
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
for _, vv := range p.conns {
|
||||
for _, cc := range vv {
|
||||
cc.Close()
|
||||
}
|
||||
}
|
||||
return transport
|
||||
default:
|
||||
panic(E.New("unknown transport type: ", reflect.TypeOf(transport)))
|
||||
}
|
||||
}
|
||||
|
||||
//go:linkname transportConnPool golang.org/x/net/http2.(*Transport).connPool
|
||||
func transportConnPool(t *http2.Transport) http2.ClientConnPool
|
||||
@@ -104,7 +104,7 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
if response.StatusCode != 101 ||
|
||||
!strings.EqualFold(response.Header.Get("Connection"), "upgrade") ||
|
||||
!strings.EqualFold(response.Header.Get("Upgrade"), "websocket") {
|
||||
return nil, E.New("v2ray-http-upgrade: unexpected status: ", response.Status)
|
||||
return nil, E.New("unexpected status: ", response.Status)
|
||||
}
|
||||
if bufReader.Buffered() > 0 {
|
||||
buffer := buf.NewSize(bufReader.Buffered())
|
||||
@@ -116,7 +116,3 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/quic-go"
|
||||
"github.com/sagernet/quic-go/http3"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
@@ -38,7 +37,7 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
||||
DisablePathMTUDiscovery: !C.IsLinux && !C.IsWindows,
|
||||
}
|
||||
if len(tlsConfig.NextProtos()) == 0 {
|
||||
tlsConfig.SetNextProtos([]string{http3.NextProtoH3})
|
||||
tlsConfig.SetNextProtos([]string{"h2", "http/1.1"})
|
||||
}
|
||||
return &Client{
|
||||
ctx: ctx,
|
||||
@@ -97,15 +96,5 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
c.connAccess.Lock()
|
||||
defer c.connAccess.Unlock()
|
||||
if c.conn != nil {
|
||||
c.conn.CloseWithError(0, "")
|
||||
}
|
||||
if c.rawConn != nil {
|
||||
c.rawConn.Close()
|
||||
}
|
||||
c.conn = nil
|
||||
c.rawConn = nil
|
||||
return nil
|
||||
return common.Close(c.conn, c.rawConn)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/quic-go"
|
||||
"github.com/sagernet/quic-go/http3"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
@@ -35,7 +34,7 @@ func NewServer(ctx context.Context, options option.V2RayQUICOptions, tlsConfig t
|
||||
DisablePathMTUDiscovery: !C.IsLinux && !C.IsWindows,
|
||||
}
|
||||
if len(tlsConfig.NextProtos()) == 0 {
|
||||
tlsConfig.SetNextProtos([]string{http3.NextProtoH3})
|
||||
tlsConfig.SetNextProtos([]string{"h2", "http/1.1"})
|
||||
}
|
||||
server := &Server{
|
||||
ctx: ctx,
|
||||
|
||||
@@ -127,7 +127,3 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
return &EarlyWebsocketConn{Client: c, ctx: ctx, create: make(chan struct{})}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user