Compare commits

..

11 Commits

Author SHA1 Message Date
世界
82269a4937 documentation: Bump version 2024-06-19 20:43:55 +08:00
世界
1b74c3c245 Update quic-go to v0.44.0 2024-06-19 20:43:55 +08:00
世界
3d30ea02ae Improve base DNS transports 2024-06-19 20:43:55 +08:00
世界
1abe60755b auto-redirect: Add route address set support for nftables 2024-06-19 20:43:55 +08:00
世界
826ebd56a6 Add auto-redirect & Improve auto-route 2024-06-19 20:43:55 +08:00
世界
6635876db8 Add support for tailing comma in json decoder 2024-06-19 20:43:55 +08:00
世界
45c0c46479 platform: Add log update interval 2024-06-19 20:43:55 +08:00
世界
9e727a440b platform: Prepare connections list 2024-06-19 18:49:51 +08:00
世界
0ee9ed78bb Fix hysteria2 test 2024-06-19 18:49:51 +08:00
世界
49e8e39ec1 Drop support for go1.18 and go1.19 2024-06-19 18:49:51 +08:00
iosmanthus
8d8aa9d8e3 Introduce bittorrent related protocol sniffers
* Introduce bittorrent related protocol sniffers

including, sniffers of
1. BitTorrent Protocol (TCP)
2. uTorrent Transport Protocol (UDP)

Signed-off-by: iosmanthus <myosmanthustree@gmail.com>
Co-authored-by: 世界 <i@sekai.icu>
2024-06-17 13:06:46 +08:00
107 changed files with 2357 additions and 1505 deletions

View File

@@ -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: .

View File

@@ -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

View File

@@ -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
}

View File

@@ -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

View File

@@ -22,5 +22,4 @@ type V2RayServerTransportHandler interface {
type V2RayClientTransport interface {
DialContext(ctx context.Context) (net.Conn, error)
Close() error
}

6
box.go
View File

@@ -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()

View File

@@ -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()

View File

@@ -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)

View File

@@ -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")
}
}

View File

@@ -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
}

View File

@@ -6,7 +6,7 @@ import (
var commandRuleSet = &cobra.Command{
Use: "rule-set",
Short: "Manage rule-sets",
Short: "Manage rule sets",
}
func init() {

View File

@@ -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") {

View File

@@ -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
}

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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())

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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
}
}

View File

@@ -7,5 +7,4 @@ const (
ProtocolDNS = "dns"
ProtocolSTUN = "stun"
ProtocolBitTorrent = "bittorrent"
ProtocolDTLS = "dtls"
)

View File

@@ -11,7 +11,6 @@ const (
)
const (
RuleSetTypeInline = "inline"
RuleSetTypeLocal = "local"
RuleSetTypeRemote = "remote"
RuleSetVersion1 = 1

View File

@@ -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**:

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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` 时排除自定义路由。

View File

@@ -9,6 +9,6 @@
}
```
### Fields
### 字段
No fields.
No fields.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 | / |

View File

@@ -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 | / |

View File

@@ -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.

View File

@@ -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

View File

@@ -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.

View File

@@ -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 验证字段。如果配置,将禁用其他验证方法。
服务器与和客户端之间允许的最大时间差。
默认禁用检查。
### 重载
对于服务器配置,如果修改,证书和密钥将自动重新加载。

View File

@@ -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

View File

@@ -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
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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")
}

View File

@@ -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
}

View File

@@ -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")
}

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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{}{}:

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
View File

@@ -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
View File

@@ -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=

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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()

View File

@@ -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()
}

View File

@@ -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

View File

@@ -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

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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()
}

View File

@@ -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)

View File

@@ -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()
}

View File

@@ -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()
}

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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++ {

View File

@@ -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()

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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)
}
}

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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)
}

View File

@@ -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")
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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,

View File

@@ -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