WTF is this

This commit is contained in:
世界 2024-06-24 09:49:15 +08:00
parent bda93d516b
commit 923d3222b0
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
41 changed files with 421 additions and 507 deletions

View file

@ -4,14 +4,13 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/binary" "encoding/binary"
"io"
"net" "net"
"time" "time"
"github.com/sagernet/sing-box/common/urltest" "github.com/sagernet/sing-box/common/urltest"
"github.com/sagernet/sing-dns" "github.com/sagernet/sing-dns"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/varbin"
) )
type ClashServer interface { type ClashServer interface {
@ -56,16 +55,15 @@ func (s *SavedRuleSet) MarshalBinary() ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = rw.WriteUVariant(&buffer, uint64(len(s.Content))) err = varbin.Write(&buffer, binary.BigEndian, s.Content)
if err != nil { if err != nil {
return nil, err return nil, err
} }
buffer.Write(s.Content)
err = binary.Write(&buffer, binary.BigEndian, s.LastUpdated.Unix()) err = binary.Write(&buffer, binary.BigEndian, s.LastUpdated.Unix())
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = rw.WriteVString(&buffer, s.LastEtag) err = varbin.Write(&buffer, binary.BigEndian, s.LastEtag)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -79,12 +77,7 @@ func (s *SavedRuleSet) UnmarshalBinary(data []byte) error {
if err != nil { if err != nil {
return err return err
} }
contentLen, err := rw.ReadUVariant(reader) err = varbin.Read(reader, binary.BigEndian, &s.Content)
if err != nil {
return err
}
s.Content = make([]byte, contentLen)
_, err = io.ReadFull(reader, s.Content)
if err != nil { if err != nil {
return err return err
} }
@ -94,7 +87,7 @@ func (s *SavedRuleSet) UnmarshalBinary(data []byte) error {
return err return err
} }
s.LastUpdated = time.Unix(lastUpdated, 0) s.LastUpdated = time.Unix(lastUpdated, 0)
s.LastEtag, err = rw.ReadVString(reader) err = varbin.Read(reader, binary.BigEndian, &s.LastEtag)
if err != nil { if err != nil {
return err return err
} }

View file

@ -45,7 +45,7 @@ type Router interface {
DefaultInterface() string DefaultInterface() string
AutoDetectInterface() bool AutoDetectInterface() bool
AutoDetectInterfaceFunc() control.Func AutoDetectInterfaceFunc() control.Func
DefaultMark() int DefaultMark() uint32
NetworkMonitor() tun.NetworkUpdateMonitor NetworkMonitor() tun.NetworkUpdateMonitor
InterfaceMonitor() tun.DefaultInterfaceMonitor InterfaceMonitor() tun.DefaultInterfaceMonitor
PackageManager() tun.PackageManager PackageManager() tun.PackageManager

View file

@ -45,7 +45,9 @@ func (s *Box) startOutbounds() error {
} }
started[outboundTag] = true started[outboundTag] = true
canContinue = true canContinue = true
if starter, isStarter := outboundToStart.(common.Starter); isStarter { if starter, isStarter := outboundToStart.(interface {
Start() error
}); isStarter {
monitor.Start("initialize outbound/", outboundToStart.Type(), "[", outboundTag, "]") monitor.Start("initialize outbound/", outboundToStart.Type(), "[", outboundTag, "]")
err := starter.Start() err := starter.Start()
monitor.Finish() monitor.Finish()

View file

@ -93,7 +93,7 @@ func buildAndroid() {
const name = "libbox.aar" const name = "libbox.aar"
copyPath := filepath.Join("..", "sing-box-for-android", "app", "libs") copyPath := filepath.Join("..", "sing-box-for-android", "app", "libs")
if rw.FileExists(copyPath) { if rw.IsDir(copyPath) {
copyPath, _ = filepath.Abs(copyPath) copyPath, _ = filepath.Abs(copyPath)
err = rw.CopyFile(name, filepath.Join(copyPath, name)) err = rw.CopyFile(name, filepath.Join(copyPath, name))
if err != nil { if err != nil {
@ -134,7 +134,7 @@ func buildiOS() {
} }
copyPath := filepath.Join("..", "sing-box-for-apple") copyPath := filepath.Join("..", "sing-box-for-apple")
if rw.FileExists(copyPath) { if rw.IsDir(copyPath) {
targetDir := filepath.Join(copyPath, "Libbox.xcframework") targetDir := filepath.Join(copyPath, "Libbox.xcframework")
targetDir, _ = filepath.Abs(targetDir) targetDir, _ = filepath.Abs(targetDir)
os.RemoveAll(targetDir) os.RemoveAll(targetDir)

View file

@ -30,7 +30,7 @@ func FindSDK() {
} }
for _, path := range searchPath { for _, path := range searchPath {
path = os.ExpandEnv(path) path = os.ExpandEnv(path)
if rw.FileExists(filepath.Join(path, "licenses", "android-sdk-license")) { if rw.IsFile(filepath.Join(path, "licenses", "android-sdk-license")) {
androidSDKPath = path androidSDKPath = path
break break
} }
@ -60,7 +60,7 @@ func FindSDK() {
func findNDK() bool { func findNDK() bool {
const fixedVersion = "26.2.11394342" const fixedVersion = "26.2.11394342"
const versionFile = "source.properties" const versionFile = "source.properties"
if fixedPath := filepath.Join(androidSDKPath, "ndk", fixedVersion); rw.FileExists(filepath.Join(fixedPath, versionFile)) { if fixedPath := filepath.Join(androidSDKPath, "ndk", fixedVersion); rw.IsFile(filepath.Join(fixedPath, versionFile)) {
androidNDKPath = fixedPath androidNDKPath = fixedPath
return true return true
} }
@ -86,7 +86,7 @@ func findNDK() bool {
}) })
for _, versionName := range versionNames { for _, versionName := range versionNames {
currentNDKPath := filepath.Join(androidSDKPath, "ndk", versionName) currentNDKPath := filepath.Join(androidSDKPath, "ndk", versionName)
if rw.FileExists(filepath.Join(androidSDKPath, versionFile)) { if rw.IsFile(filepath.Join(androidSDKPath, versionFile)) {
androidNDKPath = currentNDKPath androidNDKPath = currentNDKPath
log.Warn("reproducibility warning: using NDK version " + versionName + " instead of " + fixedVersion) log.Warn("reproducibility warning: using NDK version " + versionName + " instead of " + fixedVersion)
return true return true
@ -100,11 +100,11 @@ var GoBinPath string
func FindMobile() { func FindMobile() {
goBin := filepath.Join(build.Default.GOPATH, "bin") goBin := filepath.Join(build.Default.GOPATH, "bin")
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
if !rw.FileExists(filepath.Join(goBin, "gobind.exe")) { if !rw.IsFile(filepath.Join(goBin, "gobind.exe")) {
log.Fatal("missing gomobile installation") log.Fatal("missing gomobile installation")
} }
} else { } else {
if !rw.FileExists(filepath.Join(goBin, "gobind")) { if !rw.IsFile(filepath.Join(goBin, "gobind")) {
log.Fatal("missing gomobile installation") log.Fatal("missing gomobile installation")
} }
} }

View file

@ -54,7 +54,11 @@ func merge(outputPath string) error {
return nil return nil
} }
} }
err = rw.WriteFile(outputPath, buffer.Bytes()) err = rw.MkdirParent(outputPath)
if err != nil {
return err
}
err = os.WriteFile(outputPath, buffer.Bytes(), 0o644)
if err != nil { if err != nil {
return err return err
} }

View file

@ -109,7 +109,7 @@ func readConfigAndMerge() (option.Options, error) {
} }
var mergedMessage json.RawMessage var mergedMessage json.RawMessage
for _, options := range optionsList { for _, options := range optionsList {
mergedMessage, err = badjson.MergeJSON(options.options.RawMessage, mergedMessage) mergedMessage, err = badjson.MergeJSON(options.options.RawMessage, mergedMessage, false)
if err != nil { if err != nil {
return option.Options{}, E.Cause(err, "merge config at ", options.path) return option.Options{}, E.Cause(err, "merge config at ", options.path)
} }

View file

@ -0,0 +1,34 @@
package geosite_test
import (
"bytes"
"testing"
"github.com/sagernet/sing-box/common/geosite"
"github.com/stretchr/testify/require"
)
func TestGeosite(t *testing.T) {
t.Parallel()
var buffer bytes.Buffer
err := geosite.Write(&buffer, map[string][]geosite.Item{
"test": {
{
Type: geosite.RuleTypeDomain,
Value: "example.org",
},
},
})
require.NoError(t, err)
reader, codes, err := geosite.NewReader(bytes.NewReader(buffer.Bytes()))
require.NoError(t, err)
require.Equal(t, []string{"test"}, codes)
items, err := reader.Read("test")
require.NoError(t, err)
require.Equal(t, []geosite.Item{{
Type: geosite.RuleTypeDomain,
Value: "example.org",
}}, items)
}

View file

@ -1,17 +1,24 @@
package geosite package geosite
import ( import (
"bufio"
"encoding/binary"
"io" "io"
"os" "os"
"sync"
"sync/atomic"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/varbin"
) )
type Reader struct { type Reader struct {
reader io.ReadSeeker access sync.Mutex
domainIndex map[string]int reader io.ReadSeeker
domainLength map[string]int bufferedReader *bufio.Reader
metadataIndex int64
domainIndex map[string]int
domainLength map[string]int
} }
func Open(path string) (*Reader, []string, error) { func Open(path string) (*Reader, []string, error) {
@ -19,14 +26,22 @@ func Open(path string) (*Reader, []string, error) {
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
reader := &Reader{ reader, codes, err := NewReader(content)
reader: content,
}
err = reader.readMetadata()
if err != nil { if err != nil {
content.Close() content.Close()
return nil, nil, err return nil, nil, err
} }
return reader, codes, nil
}
func NewReader(readSeeker io.ReadSeeker) (*Reader, []string, error) {
reader := &Reader{
reader: readSeeker,
}
err := reader.readMetadata()
if err != nil {
return nil, nil, err
}
codes := make([]string, 0, len(reader.domainIndex)) codes := make([]string, 0, len(reader.domainIndex))
for code := range reader.domainIndex { for code := range reader.domainIndex {
codes = append(codes, code) codes = append(codes, code)
@ -34,15 +49,23 @@ func Open(path string) (*Reader, []string, error) {
return reader, codes, nil return reader, codes, nil
} }
type geositeMetadata struct {
Code string
Index uint64
Length uint64
}
func (r *Reader) readMetadata() error { func (r *Reader) readMetadata() error {
version, err := rw.ReadByte(r.reader) counter := &readCounter{Reader: r.reader}
reader := bufio.NewReader(counter)
version, err := reader.ReadByte()
if err != nil { if err != nil {
return err return err
} }
if version != 0 { if version != 0 {
return E.New("unknown version") return E.New("unknown version")
} }
entryLength, err := rw.ReadUVariant(r.reader) entryLength, err := binary.ReadUvarint(reader)
if err != nil { if err != nil {
return err return err
} }
@ -55,16 +78,16 @@ func (r *Reader) readMetadata() error {
codeIndex uint64 codeIndex uint64
codeLength uint64 codeLength uint64
) )
code, err = rw.ReadVString(r.reader) code, err = varbin.ReadValue[string](reader, binary.BigEndian)
if err != nil { if err != nil {
return err return err
} }
keys[i] = code keys[i] = code
codeIndex, err = rw.ReadUVariant(r.reader) codeIndex, err = binary.ReadUvarint(reader)
if err != nil { if err != nil {
return err return err
} }
codeLength, err = rw.ReadUVariant(r.reader) codeLength, err = binary.ReadUvarint(reader)
if err != nil { if err != nil {
return err return err
} }
@ -73,6 +96,8 @@ func (r *Reader) readMetadata() error {
} }
r.domainIndex = domainIndex r.domainIndex = domainIndex
r.domainLength = domainLength r.domainLength = domainLength
r.metadataIndex = counter.count - int64(reader.Buffered())
r.bufferedReader = reader
return nil return nil
} }
@ -81,31 +106,32 @@ func (r *Reader) Read(code string) ([]Item, error) {
if !exists { if !exists {
return nil, E.New("code ", code, " not exists!") return nil, E.New("code ", code, " not exists!")
} }
_, err := r.reader.Seek(int64(index), io.SeekCurrent) _, err := r.reader.Seek(r.metadataIndex+int64(index), io.SeekStart)
if err != nil { if err != nil {
return nil, err return nil, err
} }
counter := &rw.ReadCounter{Reader: r.reader} r.bufferedReader.Reset(r.reader)
domain := make([]Item, r.domainLength[code]) itemList := make([]Item, r.domainLength[code])
for i := range domain { err = varbin.Read(r.bufferedReader, binary.BigEndian, &itemList)
var ( if err != nil {
item Item return nil, err
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) return itemList, nil
return domain, err
} }
func (r *Reader) Upstream() any { func (r *Reader) Upstream() any {
return r.reader 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,13 +2,13 @@ package geosite
import ( import (
"bytes" "bytes"
"io" "encoding/binary"
"sort" "sort"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/varbin"
) )
func Write(writer io.Writer, domains map[string][]Item) error { func Write(writer varbin.Writer, domains map[string][]Item) error {
keys := make([]string, 0, len(domains)) keys := make([]string, 0, len(domains))
for code := range domains { for code := range domains {
keys = append(keys, code) keys = append(keys, code)
@ -19,35 +19,34 @@ func Write(writer io.Writer, domains map[string][]Item) error {
index := make(map[string]int) index := make(map[string]int)
for _, code := range keys { for _, code := range keys {
index[code] = content.Len() index[code] = content.Len()
for _, domain := range domains[code] { for _, item := range domains[code] {
content.WriteByte(domain.Type) err := varbin.Write(content, binary.BigEndian, item)
err := rw.WriteVString(content, domain.Value)
if err != nil { if err != nil {
return err return err
} }
} }
} }
err := rw.WriteByte(writer, 0) err := writer.WriteByte(0)
if err != nil { if err != nil {
return err return err
} }
err = rw.WriteUVariant(writer, uint64(len(keys))) _, err = varbin.WriteUvarint(writer, uint64(len(keys)))
if err != nil { if err != nil {
return err return err
} }
for _, code := range keys { for _, code := range keys {
err = rw.WriteVString(writer, code) err = varbin.Write(writer, binary.BigEndian, code)
if err != nil { if err != nil {
return err return err
} }
err = rw.WriteUVariant(writer, uint64(index[code])) _, err = varbin.WriteUvarint(writer, uint64(index[code]))
if err != nil { if err != nil {
return err return err
} }
err = rw.WriteUVariant(writer, uint64(len(domains[code]))) _, err = varbin.WriteUvarint(writer, uint64(len(domains[code])))
if err != nil { if err != nil {
return err return err
} }

View file

@ -1,6 +1,7 @@
package srs package srs
import ( import (
"bufio"
"compress/zlib" "compress/zlib"
"encoding/binary" "encoding/binary"
"io" "io"
@ -11,7 +12,7 @@ import (
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/domain" "github.com/sagernet/sing/common/domain"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/varbin"
"go4.org/netipx" "go4.org/netipx"
) )
@ -38,7 +39,7 @@ const (
ruleItemFinal uint8 = 0xFF ruleItemFinal uint8 = 0xFF
) )
func Read(reader io.Reader, recovery bool) (ruleSet option.PlainRuleSet, err error) { func Read(reader io.Reader, recover bool) (ruleSet option.PlainRuleSet, err error) {
var magicBytes [3]byte var magicBytes [3]byte
_, err = io.ReadFull(reader, magicBytes[:]) _, err = io.ReadFull(reader, magicBytes[:])
if err != nil { if err != nil {
@ -60,13 +61,14 @@ func Read(reader io.Reader, recovery bool) (ruleSet option.PlainRuleSet, err err
if err != nil { if err != nil {
return return
} }
length, err := rw.ReadUVariant(zReader) bReader := bufio.NewReader(zReader)
length, err := binary.ReadUvarint(bReader)
if err != nil { if err != nil {
return return
} }
ruleSet.Rules = make([]option.HeadlessRule, length) ruleSet.Rules = make([]option.HeadlessRule, length)
for i := uint64(0); i < length; i++ { for i := uint64(0); i < length; i++ {
ruleSet.Rules[i], err = readRule(zReader, recovery) ruleSet.Rules[i], err = readRule(bReader, recover)
if err != nil { if err != nil {
err = E.Cause(err, "read rule[", i, "]") err = E.Cause(err, "read rule[", i, "]")
return return
@ -88,20 +90,25 @@ func Write(writer io.Writer, ruleSet option.PlainRuleSet) error {
if err != nil { if err != nil {
return err return err
} }
err = rw.WriteUVariant(zWriter, uint64(len(ruleSet.Rules))) bWriter := bufio.NewWriter(zWriter)
_, err = varbin.WriteUvarint(bWriter, uint64(len(ruleSet.Rules)))
if err != nil { if err != nil {
return err return err
} }
for _, rule := range ruleSet.Rules { for _, rule := range ruleSet.Rules {
err = writeRule(zWriter, rule) err = writeRule(bWriter, rule)
if err != nil { if err != nil {
return err return err
} }
} }
err = bWriter.Flush()
if err != nil {
return err
}
return zWriter.Close() return zWriter.Close()
} }
func readRule(reader io.Reader, recovery bool) (rule option.HeadlessRule, err error) { func readRule(reader varbin.Reader, recover bool) (rule option.HeadlessRule, err error) {
var ruleType uint8 var ruleType uint8
err = binary.Read(reader, binary.BigEndian, &ruleType) err = binary.Read(reader, binary.BigEndian, &ruleType)
if err != nil { if err != nil {
@ -110,17 +117,17 @@ func readRule(reader io.Reader, recovery bool) (rule option.HeadlessRule, err er
switch ruleType { switch ruleType {
case 0: case 0:
rule.Type = C.RuleTypeDefault rule.Type = C.RuleTypeDefault
rule.DefaultOptions, err = readDefaultRule(reader, recovery) rule.DefaultOptions, err = readDefaultRule(reader, recover)
case 1: case 1:
rule.Type = C.RuleTypeLogical rule.Type = C.RuleTypeLogical
rule.LogicalOptions, err = readLogicalRule(reader, recovery) rule.LogicalOptions, err = readLogicalRule(reader, recover)
default: default:
err = E.New("unknown rule type: ", ruleType) err = E.New("unknown rule type: ", ruleType)
} }
return return
} }
func writeRule(writer io.Writer, rule option.HeadlessRule) error { func writeRule(writer varbin.Writer, rule option.HeadlessRule) error {
switch rule.Type { switch rule.Type {
case C.RuleTypeDefault: case C.RuleTypeDefault:
return writeDefaultRule(writer, rule.DefaultOptions) return writeDefaultRule(writer, rule.DefaultOptions)
@ -131,7 +138,7 @@ func writeRule(writer io.Writer, rule option.HeadlessRule) error {
} }
} }
func readDefaultRule(reader io.Reader, recovery bool) (rule option.DefaultHeadlessRule, err error) { func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHeadlessRule, err error) {
var lastItemType uint8 var lastItemType uint8
for { for {
var itemType uint8 var itemType uint8
@ -158,6 +165,9 @@ func readDefaultRule(reader io.Reader, recovery bool) (rule option.DefaultHeadle
return return
} }
rule.DomainMatcher = matcher rule.DomainMatcher = matcher
if recover {
rule.Domain, rule.DomainSuffix = matcher.Dump()
}
case ruleItemDomainKeyword: case ruleItemDomainKeyword:
rule.DomainKeyword, err = readRuleItemString(reader) rule.DomainKeyword, err = readRuleItemString(reader)
case ruleItemDomainRegex: case ruleItemDomainRegex:
@ -167,7 +177,7 @@ func readDefaultRule(reader io.Reader, recovery bool) (rule option.DefaultHeadle
if err != nil { if err != nil {
return return
} }
if recovery { if recover {
rule.SourceIPCIDR = common.Map(rule.SourceIPSet.Prefixes(), netip.Prefix.String) rule.SourceIPCIDR = common.Map(rule.SourceIPSet.Prefixes(), netip.Prefix.String)
} }
case ruleItemIPCIDR: case ruleItemIPCIDR:
@ -175,7 +185,7 @@ func readDefaultRule(reader io.Reader, recovery bool) (rule option.DefaultHeadle
if err != nil { if err != nil {
return return
} }
if recovery { if recover {
rule.IPCIDR = common.Map(rule.IPSet.Prefixes(), netip.Prefix.String) rule.IPCIDR = common.Map(rule.IPSet.Prefixes(), netip.Prefix.String)
} }
case ruleItemSourcePort: case ruleItemSourcePort:
@ -209,7 +219,7 @@ func readDefaultRule(reader io.Reader, recovery bool) (rule option.DefaultHeadle
} }
} }
func writeDefaultRule(writer io.Writer, rule option.DefaultHeadlessRule) error { func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule) error {
err := binary.Write(writer, binary.BigEndian, uint8(0)) err := binary.Write(writer, binary.BigEndian, uint8(0))
if err != nil { if err != nil {
return err return err
@ -327,73 +337,31 @@ func writeDefaultRule(writer io.Writer, rule option.DefaultHeadlessRule) error {
return nil return nil
} }
func readRuleItemString(reader io.Reader) ([]string, error) { func readRuleItemString(reader varbin.Reader) ([]string, error) {
length, err := rw.ReadUVariant(reader) return varbin.ReadValue[[]string](reader, binary.BigEndian)
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 io.Writer, itemType uint8, value []string) error { func writeRuleItemString(writer varbin.Writer, itemType uint8, value []string) error {
err := binary.Write(writer, binary.BigEndian, itemType) err := writer.WriteByte(itemType)
if err != nil { if err != nil {
return err return err
} }
err = rw.WriteUVariant(writer, uint64(len(value))) 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)
if err != nil { if err != nil {
return err return err
} }
for _, item := range value { return varbin.Write(writer, binary.BigEndian, value)
err = rw.WriteVString(writer, item)
if err != nil {
return err
}
}
return nil
} }
func readRuleItemUint16(reader io.Reader) ([]uint16, error) { func writeRuleItemCIDR(writer varbin.Writer, itemType uint8, value []string) 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 var builder netipx.IPSetBuilder
for i, prefixString := range value { for i, prefixString := range value {
prefix, err := netip.ParsePrefix(prefixString) prefix, err := netip.ParsePrefix(prefixString)
@ -419,9 +387,8 @@ func writeRuleItemCIDR(writer io.Writer, itemType uint8, value []string) error {
return writeIPSet(writer, ipSet) return writeIPSet(writer, ipSet)
} }
func readLogicalRule(reader io.Reader, recovery bool) (logicalRule option.LogicalHeadlessRule, err error) { func readLogicalRule(reader varbin.Reader, recovery bool) (logicalRule option.LogicalHeadlessRule, err error) {
var mode uint8 mode, err := reader.ReadByte()
err = binary.Read(reader, binary.BigEndian, &mode)
if err != nil { if err != nil {
return return
} }
@ -434,7 +401,7 @@ func readLogicalRule(reader io.Reader, recovery bool) (logicalRule option.Logica
err = E.New("unknown logical mode: ", mode) err = E.New("unknown logical mode: ", mode)
return return
} }
length, err := rw.ReadUVariant(reader) length, err := binary.ReadUvarint(reader)
if err != nil { if err != nil {
return return
} }
@ -453,7 +420,7 @@ func readLogicalRule(reader io.Reader, recovery bool) (logicalRule option.Logica
return return
} }
func writeLogicalRule(writer io.Writer, logicalRule option.LogicalHeadlessRule) error { func writeLogicalRule(writer varbin.Writer, logicalRule option.LogicalHeadlessRule) error {
err := binary.Write(writer, binary.BigEndian, uint8(1)) err := binary.Write(writer, binary.BigEndian, uint8(1))
if err != nil { if err != nil {
return err return err
@ -469,7 +436,7 @@ func writeLogicalRule(writer io.Writer, logicalRule option.LogicalHeadlessRule)
if err != nil { if err != nil {
return err return err
} }
err = rw.WriteUVariant(writer, uint64(len(logicalRule.Rules))) _, err = varbin.WriteUvarint(writer, uint64(len(logicalRule.Rules)))
if err != nil { if err != nil {
return err return err
} }

View file

@ -2,11 +2,13 @@ package srs
import ( import (
"encoding/binary" "encoding/binary"
"io"
"net/netip" "net/netip"
"os"
"unsafe" "unsafe"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/varbin"
"go4.org/netipx" "go4.org/netipx"
) )
@ -20,94 +22,57 @@ type myIPRange struct {
to netip.Addr to netip.Addr
} }
func readIPSet(reader io.Reader) (*netipx.IPSet, error) { type myIPRangeData struct {
var version uint8 From []byte
err := binary.Read(reader, binary.BigEndian, &version) To []byte
}
func readIPSet(reader varbin.Reader) (*netipx.IPSet, error) {
version, err := reader.ReadByte()
if err != nil { if err != nil {
return nil, err return nil, err
} }
if version != 1 {
return nil, os.ErrInvalid
}
// WTF why using uint64 here
var length uint64 var length uint64
err = binary.Read(reader, binary.BigEndian, &length) err = binary.Read(reader, binary.BigEndian, &length)
if err != nil { if err != nil {
return nil, err return nil, err
} }
mySet := &myIPSet{ ranges := make([]myIPRangeData, length)
rr: make([]myIPRange, length), err = varbin.Read(reader, binary.BigEndian, &ranges)
if err != nil {
return nil, err
} }
for i := uint64(0); i < length; i++ { mySet := &myIPSet{
var ( rr: make([]myIPRange, len(ranges)),
fromLen uint64 }
toLen uint64 for i, rangeData := range ranges {
fromAddr netip.Addr mySet.rr[i].from = M.AddrFromIP(rangeData.From)
toAddr netip.Addr mySet.rr[i].to = M.AddrFromIP(rangeData.To)
)
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 return (*netipx.IPSet)(unsafe.Pointer(mySet)), nil
} }
func writeIPSet(writer io.Writer, set *netipx.IPSet) error { func writeIPSet(writer varbin.Writer, set *netipx.IPSet) error {
err := binary.Write(writer, binary.BigEndian, uint8(1)) err := writer.WriteByte(1)
if err != nil { if err != nil {
return err return err
} }
mySet := (*myIPSet)(unsafe.Pointer(set)) dataList := common.Map((*myIPSet)(unsafe.Pointer(set)).rr, func(rr myIPRange) myIPRangeData {
err = binary.Write(writer, binary.BigEndian, uint64(len(mySet.rr))) return myIPRangeData{
From: rr.from.AsSlice(),
To: rr.to.AsSlice(),
}
})
err = binary.Write(writer, binary.BigEndian, uint64(len(dataList)))
if err != nil { if err != nil {
return err return err
} }
for _, rr := range mySet.rr { for _, data := range dataList {
var ( err = varbin.Write(writer, binary.BigEndian, data)
fromBinary []byte
toBinary []byte
)
fromBinary, err = rr.from.MarshalBinary()
if err != nil {
return err
}
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 { if err != nil {
return err return err
} }

View file

@ -13,14 +13,14 @@ var resourcePaths []string
func FindPath(name string) (string, bool) { func FindPath(name string) (string, bool) {
name = os.ExpandEnv(name) name = os.ExpandEnv(name)
if rw.FileExists(name) { if rw.IsFile(name) {
return name, true return name, true
} }
for _, dir := range resourcePaths { for _, dir := range resourcePaths {
if path := filepath.Join(dir, dirName, name); rw.FileExists(path) { if path := filepath.Join(dir, dirName, name); rw.IsFile(path) {
return path, true return path, true
} }
if path := filepath.Join(dir, name); rw.FileExists(path) { if path := filepath.Join(dir, name); rw.IsFile(path) {
return path, true return path, true
} }
} }

View file

@ -9,7 +9,7 @@ import (
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/experimental/clashapi" "github.com/sagernet/sing-box/experimental/clashapi"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/varbin"
) )
func (c *CommandClient) SetClashMode(newMode string) error { func (c *CommandClient) SetClashMode(newMode string) error {
@ -22,7 +22,7 @@ func (c *CommandClient) SetClashMode(newMode string) error {
if err != nil { if err != nil {
return err return err
} }
err = rw.WriteVString(conn, newMode) err = varbin.Write(conn, binary.BigEndian, newMode)
if err != nil { if err != nil {
return err return err
} }
@ -30,7 +30,7 @@ func (c *CommandClient) SetClashMode(newMode string) error {
} }
func (s *CommandServer) handleSetClashMode(conn net.Conn) error { func (s *CommandServer) handleSetClashMode(conn net.Conn) error {
newMode, err := rw.ReadVString(conn) newMode, err := varbin.ReadValue[string](conn, binary.BigEndian)
if err != nil { if err != nil {
return err return err
} }
@ -50,7 +50,7 @@ func (c *CommandClient) handleModeConn(conn net.Conn) {
defer conn.Close() defer conn.Close()
for { for {
newMode, err := rw.ReadVString(conn) newMode, err := varbin.ReadValue[string](conn, binary.BigEndian)
if err != nil { if err != nil {
c.handler.Disconnected(err.Error()) c.handler.Disconnected(err.Error())
return return
@ -80,7 +80,7 @@ func (s *CommandServer) handleModeConn(conn net.Conn) error {
for { for {
select { select {
case <-s.modeUpdate: case <-s.modeUpdate:
err = rw.WriteVString(conn, clashServer.Mode()) err = varbin.Write(conn, binary.BigEndian, clashServer.Mode())
if err != nil { if err != nil {
return err return err
} }
@ -101,12 +101,12 @@ func readClashModeList(reader io.Reader) (modeList []string, currentMode string,
} }
modeList = make([]string, modeListLength) modeList = make([]string, modeListLength)
for i := 0; i < int(modeListLength); i++ { for i := 0; i < int(modeListLength); i++ {
modeList[i], err = rw.ReadVString(reader) modeList[i], err = varbin.ReadValue[string](reader, binary.BigEndian)
if err != nil { if err != nil {
return return
} }
} }
currentMode, err = rw.ReadVString(reader) currentMode, err = varbin.ReadValue[string](reader, binary.BigEndian)
return return
} }
@ -118,12 +118,12 @@ func writeClashModeList(writer io.Writer, clashServer adapter.ClashServer) error
} }
if len(modeList) > 0 { if len(modeList) > 0 {
for _, mode := range modeList { for _, mode := range modeList {
err = rw.WriteVString(writer, mode) err = varbin.Write(writer, binary.BigEndian, mode)
if err != nil { if err != nil {
return err return err
} }
} }
err = rw.WriteVString(writer, clashServer.Mode()) err = varbin.Write(writer, binary.BigEndian, clashServer.Mode())
if err != nil { if err != nil {
return err return err
} }

View file

@ -7,6 +7,7 @@ import (
"github.com/sagernet/sing-box/experimental/clashapi" "github.com/sagernet/sing-box/experimental/clashapi"
"github.com/sagernet/sing/common/binary" "github.com/sagernet/sing/common/binary"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/varbin"
"github.com/gofrs/uuid/v5" "github.com/gofrs/uuid/v5"
) )
@ -18,7 +19,7 @@ func (c *CommandClient) CloseConnection(connId string) error {
} }
defer conn.Close() defer conn.Close()
writer := bufio.NewWriter(conn) writer := bufio.NewWriter(conn)
err = binary.WriteData(writer, binary.BigEndian, connId) err = varbin.Write(writer, binary.BigEndian, connId)
if err != nil { if err != nil {
return err return err
} }
@ -32,7 +33,7 @@ func (c *CommandClient) CloseConnection(connId string) error {
func (s *CommandServer) handleCloseConnection(conn net.Conn) error { func (s *CommandServer) handleCloseConnection(conn net.Conn) error {
reader := bufio.NewReader(conn) reader := bufio.NewReader(conn)
var connId string var connId string
err := binary.ReadData(reader, binary.BigEndian, &connId) err := varbin.Read(reader, binary.BigEndian, &connId)
if err != nil { if err != nil {
return E.Cause(err, "read connection id") return E.Cause(err, "read connection id")
} }

View file

@ -12,6 +12,7 @@ import (
"github.com/sagernet/sing/common/binary" "github.com/sagernet/sing/common/binary"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/varbin"
"github.com/gofrs/uuid/v5" "github.com/gofrs/uuid/v5"
) )
@ -19,14 +20,18 @@ import (
func (c *CommandClient) handleConnectionsConn(conn net.Conn) { func (c *CommandClient) handleConnectionsConn(conn net.Conn) {
defer conn.Close() defer conn.Close()
reader := bufio.NewReader(conn) reader := bufio.NewReader(conn)
var connections Connections var (
rawConnections []Connection
connections Connections
)
for { for {
rawConnections = nil rawConnections = nil
err := binary.ReadData(reader, binary.BigEndian, &connections.connections) err := varbin.Read(reader, binary.BigEndian, &rawConnections)
if err != nil { if err != nil {
c.handler.Disconnected(err.Error()) c.handler.Disconnected(err.Error())
return return
} }
connections.input = rawConnections
c.handler.WriteConnections(&connections) c.handler.WriteConnections(&connections)
} }
} }
@ -70,7 +75,7 @@ func (s *CommandServer) handleConnectionsConn(conn net.Conn) error {
for _, connection := range trafficManager.ClosedConnections() { for _, connection := range trafficManager.ClosedConnections() {
outConnections = append(outConnections, newConnection(connections, connection, true)) outConnections = append(outConnections, newConnection(connections, connection, true))
} }
err = binary.WriteData(writer, binary.BigEndian, outConnections) err = varbin.Write(writer, binary.BigEndian, outConnections)
if err != nil { if err != nil {
return err return err
} }
@ -93,33 +98,32 @@ const (
) )
type Connections struct { type Connections struct {
connections []Connection input []Connection
filteredConnections []Connection filtered []Connection
outConnections *[]Connection
} }
func (c *Connections) FilterState(state int32) { func (c *Connections) FilterState(state int32) {
c.filteredConnections = c.filteredConnections[:0] c.filtered = c.filtered[:0]
switch state { switch state {
case ConnectionStateAll: case ConnectionStateAll:
c.filteredConnections = append(c.filteredConnections, c.connections...) c.filtered = append(c.filtered, c.input...)
case ConnectionStateActive: case ConnectionStateActive:
for _, connection := range c.connections { for _, connection := range c.input {
if connection.ClosedAt == 0 { if connection.ClosedAt == 0 {
c.filteredConnections = append(c.filteredConnections, connection) c.filtered = append(c.filtered, connection)
} }
} }
case ConnectionStateClosed: case ConnectionStateClosed:
for _, connection := range c.connections { for _, connection := range c.input {
if connection.ClosedAt != 0 { if connection.ClosedAt != 0 {
c.filteredConnections = append(c.filteredConnections, connection) c.filtered = append(c.filtered, connection)
} }
} }
} }
} }
func (c *Connections) SortByDate() { func (c *Connections) SortByDate() {
slices.SortStableFunc(c.filteredConnections, func(x, y Connection) int { slices.SortStableFunc(c.filtered, func(x, y Connection) int {
if x.CreatedAt < y.CreatedAt { if x.CreatedAt < y.CreatedAt {
return 1 return 1
} else if x.CreatedAt > y.CreatedAt { } else if x.CreatedAt > y.CreatedAt {
@ -131,7 +135,7 @@ func (c *Connections) SortByDate() {
} }
func (c *Connections) SortByTraffic() { func (c *Connections) SortByTraffic() {
slices.SortStableFunc(c.filteredConnections, func(x, y Connection) int { slices.SortStableFunc(c.filtered, func(x, y Connection) int {
xTraffic := x.Uplink + x.Downlink xTraffic := x.Uplink + x.Downlink
yTraffic := y.Uplink + y.Downlink yTraffic := y.Uplink + y.Downlink
if xTraffic < yTraffic { if xTraffic < yTraffic {
@ -145,7 +149,7 @@ func (c *Connections) SortByTraffic() {
} }
func (c *Connections) SortByTrafficTotal() { func (c *Connections) SortByTrafficTotal() {
slices.SortStableFunc(c.filteredConnections, func(x, y Connection) int { slices.SortStableFunc(c.filtered, func(x, y Connection) int {
xTraffic := x.UplinkTotal + x.DownlinkTotal xTraffic := x.UplinkTotal + x.DownlinkTotal
yTraffic := y.UplinkTotal + y.DownlinkTotal yTraffic := y.UplinkTotal + y.DownlinkTotal
if xTraffic < yTraffic { if xTraffic < yTraffic {
@ -159,7 +163,7 @@ func (c *Connections) SortByTrafficTotal() {
} }
func (c *Connections) Iterator() ConnectionIterator { func (c *Connections) Iterator() ConnectionIterator {
return newPtrIterator(c.filteredConnections) return newPtrIterator(c.filtered)
} }
type Connection struct { type Connection struct {

View file

@ -1,6 +1,7 @@
package libbox package libbox
import ( import (
"bufio"
"encoding/binary" "encoding/binary"
"io" "io"
"net" "net"
@ -10,7 +11,7 @@ import (
"github.com/sagernet/sing-box/common/urltest" "github.com/sagernet/sing-box/common/urltest"
"github.com/sagernet/sing-box/outbound" "github.com/sagernet/sing-box/outbound"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/varbin"
"github.com/sagernet/sing/service" "github.com/sagernet/sing/service"
) )
@ -36,19 +37,24 @@ func (s *CommandServer) handleGroupConn(conn net.Conn) error {
ticker := time.NewTicker(time.Duration(interval)) ticker := time.NewTicker(time.Duration(interval))
defer ticker.Stop() defer ticker.Stop()
ctx := connKeepAlive(conn) ctx := connKeepAlive(conn)
writer := bufio.NewWriter(conn)
for { for {
service := s.service service := s.service
if service != nil { if service != nil {
err := writeGroups(conn, service) err = writeGroups(writer, service)
if err != nil { if err != nil {
return err return err
} }
} else { } else {
err := binary.Write(conn, binary.BigEndian, uint16(0)) err = binary.Write(writer, binary.BigEndian, uint16(0))
if err != nil { if err != nil {
return err return err
} }
} }
err = writer.Flush()
if err != nil {
return err
}
select { select {
case <-ctx.Done(): case <-ctx.Done():
return ctx.Err() return ctx.Err()
@ -68,11 +74,11 @@ type OutboundGroup struct {
Selectable bool Selectable bool
Selected string Selected string
IsExpand bool IsExpand bool
items []*OutboundGroupItem ItemList []*OutboundGroupItem
} }
func (g *OutboundGroup) GetItems() OutboundGroupItemIterator { func (g *OutboundGroup) GetItems() OutboundGroupItemIterator {
return newIterator(g.items) return newIterator(g.ItemList)
} }
type OutboundGroupIterator interface { type OutboundGroupIterator interface {
@ -93,73 +99,10 @@ type OutboundGroupItemIterator interface {
} }
func readGroups(reader io.Reader) (OutboundGroupIterator, error) { func readGroups(reader io.Reader) (OutboundGroupIterator, error) {
var groupLength uint16 groups, err := varbin.ReadValue[[]*OutboundGroup](reader, binary.BigEndian)
err := binary.Read(reader, binary.BigEndian, &groupLength)
if err != nil { if err != nil {
return nil, err 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 return newIterator(groups), nil
} }
@ -199,63 +142,14 @@ func writeGroups(writer io.Writer, boxService *BoxService) error {
item.URLTestTime = history.Time.Unix() item.URLTestTime = history.Time.Unix()
item.URLTestDelay = int32(history.Delay) item.URLTestDelay = int32(history.Delay)
} }
group.items = append(group.items, &item) group.ItemList = append(group.ItemList, &item)
} }
if len(group.items) < 2 { if len(group.ItemList) < 2 {
continue continue
} }
groups = append(groups, group) 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 { func (c *CommandClient) SetGroupExpand(groupTag string, isExpand bool) error {
@ -268,7 +162,7 @@ func (c *CommandClient) SetGroupExpand(groupTag string, isExpand bool) error {
if err != nil { if err != nil {
return err return err
} }
err = rw.WriteVString(conn, groupTag) err = varbin.Write(conn, binary.BigEndian, groupTag)
if err != nil { if err != nil {
return err return err
} }
@ -280,7 +174,7 @@ func (c *CommandClient) SetGroupExpand(groupTag string, isExpand bool) error {
} }
func (s *CommandServer) handleSetGroupExpand(conn net.Conn) error { func (s *CommandServer) handleSetGroupExpand(conn net.Conn) error {
groupTag, err := rw.ReadVString(conn) groupTag, err := varbin.ReadValue[string](conn, binary.BigEndian)
if err != nil { if err != nil {
return err return err
} }

View file

@ -9,8 +9,19 @@ import (
"github.com/sagernet/sing/common/binary" "github.com/sagernet/sing/common/binary"
E "github.com/sagernet/sing/common/exceptions" 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) { func (s *CommandServer) WriteMessage(message string) {
s.subscriber.Emit(message) s.subscriber.Emit(message)
s.access.Lock() s.access.Lock()
@ -21,26 +32,6 @@ func (s *CommandServer) WriteMessage(message string) {
s.access.Unlock() 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 { func (s *CommandServer) handleLogConn(conn net.Conn) error {
var ( var (
interval int64 interval int64
@ -67,8 +58,24 @@ func (s *CommandServer) handleLogConn(conn net.Conn) error {
} }
defer s.observer.UnSubscribe(subscription) defer s.observer.UnSubscribe(subscription)
writer := bufio.NewWriter(conn) 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 { if len(savedLines) > 0 {
err = writeLog(writer, savedLines) err = writer.WriteByte(0)
if err != nil {
return err
}
err = varbin.Write(writer, binary.BigEndian, savedLines)
if err != nil { if err != nil {
return err return err
} }
@ -76,11 +83,15 @@ func (s *CommandServer) handleLogConn(conn net.Conn) error {
ctx := connKeepAlive(conn) ctx := connKeepAlive(conn)
var logLines []string var logLines []string
for { for {
err = writer.Flush()
if err != nil {
return err
}
select { select {
case <-ctx.Done(): case <-ctx.Done():
return ctx.Err() return ctx.Err()
case <-s.logReset: case <-s.logReset:
err = writeClearLog(writer) err = writer.WriteByte(1)
if err != nil { if err != nil {
return err return err
} }
@ -99,7 +110,11 @@ func (s *CommandServer) handleLogConn(conn net.Conn) error {
break loopLogs break loopLogs
} }
} }
err = writeLog(writer, logLines) err = writer.WriteByte(0)
if err != nil {
return err
}
err = varbin.Write(writer, binary.BigEndian, logLines)
if err != nil { if err != nil {
return err return err
} }
@ -110,8 +125,7 @@ func (s *CommandServer) handleLogConn(conn net.Conn) error {
func (c *CommandClient) handleLogConn(conn net.Conn) { func (c *CommandClient) handleLogConn(conn net.Conn) {
reader := bufio.NewReader(conn) reader := bufio.NewReader(conn)
for { for {
var messageType uint8 messageType, err := reader.ReadByte()
err := binary.Read(reader, binary.BigEndian, &messageType)
if err != nil { if err != nil {
c.handler.Disconnected(err.Error()) c.handler.Disconnected(err.Error())
return return
@ -119,7 +133,7 @@ func (c *CommandClient) handleLogConn(conn net.Conn) {
var messages []string var messages []string
switch messageType { switch messageType {
case 0: case 0:
err = binary.ReadData(reader, binary.BigEndian, &messages) err = varbin.Read(reader, binary.BigEndian, &messages)
if err != nil { if err != nil {
c.handler.Disconnected(err.Error()) c.handler.Disconnected(err.Error())
return return

View file

@ -5,7 +5,7 @@ import (
"net" "net"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/varbin"
) )
func (c *CommandClient) ServiceReload() error { func (c *CommandClient) ServiceReload() error {
@ -24,7 +24,7 @@ func (c *CommandClient) ServiceReload() error {
return err return err
} }
if hasError { if hasError {
errorMessage, err := rw.ReadVString(conn) errorMessage, err := varbin.ReadValue[string](conn, binary.BigEndian)
if err != nil { if err != nil {
return err return err
} }
@ -40,7 +40,7 @@ func (s *CommandServer) handleServiceReload(conn net.Conn) error {
return err return err
} }
if rErr != nil { if rErr != nil {
return rw.WriteVString(conn, rErr.Error()) return varbin.Write(conn, binary.BigEndian, rErr.Error())
} }
return nil return nil
} }
@ -61,7 +61,7 @@ func (c *CommandClient) ServiceClose() error {
return nil return nil
} }
if hasError { if hasError {
errorMessage, err := rw.ReadVString(conn) errorMessage, err := varbin.ReadValue[string](conn, binary.BigEndian)
if err != nil { if err != nil {
return nil return nil
} }
@ -78,7 +78,7 @@ func (s *CommandServer) handleServiceClose(conn net.Conn) error {
return err return err
} }
if rErr != nil { if rErr != nil {
return rw.WriteVString(conn, rErr.Error()) return varbin.Write(conn, binary.BigEndian, rErr.Error())
} }
return nil return nil
} }

View file

@ -6,7 +6,7 @@ import (
"github.com/sagernet/sing-box/outbound" "github.com/sagernet/sing-box/outbound"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/varbin"
) )
func (c *CommandClient) SelectOutbound(groupTag string, outboundTag string) error { 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 { if err != nil {
return err return err
} }
err = rw.WriteVString(conn, groupTag) err = varbin.Write(conn, binary.BigEndian, groupTag)
if err != nil { if err != nil {
return err return err
} }
err = rw.WriteVString(conn, outboundTag) err = varbin.Write(conn, binary.BigEndian, outboundTag)
if err != nil { if err != nil {
return err return err
} }
@ -31,11 +31,11 @@ func (c *CommandClient) SelectOutbound(groupTag string, outboundTag string) erro
} }
func (s *CommandServer) handleSelectOutbound(conn net.Conn) error { func (s *CommandServer) handleSelectOutbound(conn net.Conn) error {
groupTag, err := rw.ReadVString(conn) groupTag, err := varbin.ReadValue[string](conn, binary.BigEndian)
if err != nil { if err != nil {
return err return err
} }
outboundTag, err := rw.ReadVString(conn) outboundTag, err := varbin.ReadValue[string](conn, binary.BigEndian)
if err != nil { if err != nil {
return err return err
} }

View file

@ -66,14 +66,6 @@ func (s *CommandServer) SetService(newService *BoxService) {
s.notifyURLTestUpdate() s.notifyURLTestUpdate()
} }
func (s *CommandServer) ResetLog() {
s.savedLines.Init()
select {
case s.logReset <- struct{}{}:
default:
}
}
func (s *CommandServer) notifyURLTestUpdate() { func (s *CommandServer) notifyURLTestUpdate() {
select { select {
case s.urlTestUpdate <- struct{}{}: case s.urlTestUpdate <- struct{}{}:

View file

@ -5,7 +5,7 @@ import (
"io" "io"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/varbin"
) )
func readError(reader io.Reader) error { func readError(reader io.Reader) error {
@ -15,7 +15,7 @@ func readError(reader io.Reader) error {
return err return err
} }
if hasError { if hasError {
errorMessage, err := rw.ReadVString(reader) errorMessage, err := varbin.ReadValue[string](reader, binary.BigEndian)
if err != nil { if err != nil {
return err return err
} }
@ -30,7 +30,7 @@ func writeError(writer io.Writer, wErr error) error {
return err return err
} }
if wErr != nil { if wErr != nil {
err = rw.WriteVString(writer, wErr.Error()) err = varbin.Write(writer, binary.BigEndian, wErr.Error())
if err != nil { if err != nil {
return err return err
} }

View file

@ -11,7 +11,7 @@ import (
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/batch" "github.com/sagernet/sing/common/batch"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/varbin"
"github.com/sagernet/sing/service" "github.com/sagernet/sing/service"
) )
@ -25,7 +25,7 @@ func (c *CommandClient) URLTest(groupTag string) error {
if err != nil { if err != nil {
return err return err
} }
err = rw.WriteVString(conn, groupTag) err = varbin.Write(conn, binary.BigEndian, groupTag)
if err != nil { if err != nil {
return err return err
} }
@ -33,7 +33,7 @@ func (c *CommandClient) URLTest(groupTag string) error {
} }
func (s *CommandServer) handleURLTest(conn net.Conn) error { func (s *CommandServer) handleURLTest(conn net.Conn) error {
groupTag, err := rw.ReadVString(conn) groupTag, err := varbin.ReadValue[string](conn, binary.BigEndian)
if err != nil { if err != nil {
return err return err
} }

View file

@ -1,13 +1,13 @@
package libbox package libbox
import ( import (
"bufio"
"bytes" "bytes"
"compress/gzip" "compress/gzip"
"encoding/binary" "encoding/binary"
"io"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/varbin"
) )
func EncodeChunkedMessage(data []byte) []byte { func EncodeChunkedMessage(data []byte) []byte {
@ -35,13 +35,13 @@ type ErrorMessage struct {
func (e *ErrorMessage) Encode() []byte { func (e *ErrorMessage) Encode() []byte {
var buffer bytes.Buffer var buffer bytes.Buffer
buffer.WriteByte(MessageTypeError) buffer.WriteByte(MessageTypeError)
rw.WriteVString(&buffer, e.Message) varbin.Write(&buffer, binary.BigEndian, e.Message)
return buffer.Bytes() return buffer.Bytes()
} }
func DecodeErrorMessage(data []byte) (*ErrorMessage, error) { func DecodeErrorMessage(data []byte) (*ErrorMessage, error) {
reader := bytes.NewReader(data) reader := bytes.NewReader(data)
messageType, err := rw.ReadByte(reader) messageType, err := reader.ReadByte()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -49,7 +49,7 @@ func DecodeErrorMessage(data []byte) (*ErrorMessage, error) {
return nil, E.New("invalid message") return nil, E.New("invalid message")
} }
var message ErrorMessage var message ErrorMessage
message.Message, err = rw.ReadVString(reader) message.Message, err = varbin.ReadValue[string](reader, binary.BigEndian)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -87,7 +87,7 @@ func (e *ProfileEncoder) Encode() []byte {
binary.Write(&buffer, binary.BigEndian, uint16(len(e.profiles))) binary.Write(&buffer, binary.BigEndian, uint16(len(e.profiles)))
for _, preview := range e.profiles { for _, preview := range e.profiles {
binary.Write(&buffer, binary.BigEndian, preview.ProfileID) binary.Write(&buffer, binary.BigEndian, preview.ProfileID)
rw.WriteVString(&buffer, preview.Name) varbin.Write(&buffer, binary.BigEndian, preview.Name)
binary.Write(&buffer, binary.BigEndian, preview.Type) binary.Write(&buffer, binary.BigEndian, preview.Type)
} }
return buffer.Bytes() return buffer.Bytes()
@ -117,7 +117,7 @@ func (d *ProfileDecoder) Decode(data []byte) error {
if err != nil { if err != nil {
return err return err
} }
profile.Name, err = rw.ReadVString(reader) profile.Name, err = varbin.ReadValue[string](reader, binary.BigEndian)
if err != nil { if err != nil {
return err return err
} }
@ -147,7 +147,7 @@ func (r *ProfileContentRequest) Encode() []byte {
func DecodeProfileContentRequest(data []byte) (*ProfileContentRequest, error) { func DecodeProfileContentRequest(data []byte) (*ProfileContentRequest, error) {
reader := bytes.NewReader(data) reader := bytes.NewReader(data)
messageType, err := rw.ReadByte(reader) messageType, err := reader.ReadByte()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -176,12 +176,13 @@ func (c *ProfileContent) Encode() []byte {
buffer := new(bytes.Buffer) buffer := new(bytes.Buffer)
buffer.WriteByte(MessageTypeProfileContent) buffer.WriteByte(MessageTypeProfileContent)
buffer.WriteByte(1) buffer.WriteByte(1)
writer := gzip.NewWriter(buffer) gWriter := gzip.NewWriter(buffer)
rw.WriteVString(writer, c.Name) writer := bufio.NewWriter(gWriter)
varbin.Write(writer, binary.BigEndian, c.Name)
binary.Write(writer, binary.BigEndian, c.Type) binary.Write(writer, binary.BigEndian, c.Type)
rw.WriteVString(writer, c.Config) varbin.Write(writer, binary.BigEndian, c.Config)
if c.Type != ProfileTypeLocal { if c.Type != ProfileTypeLocal {
rw.WriteVString(writer, c.RemotePath) varbin.Write(writer, binary.BigEndian, c.RemotePath)
} }
if c.Type == ProfileTypeRemote { if c.Type == ProfileTypeRemote {
binary.Write(writer, binary.BigEndian, c.AutoUpdate) binary.Write(writer, binary.BigEndian, c.AutoUpdate)
@ -189,29 +190,31 @@ func (c *ProfileContent) Encode() []byte {
binary.Write(writer, binary.BigEndian, c.LastUpdated) binary.Write(writer, binary.BigEndian, c.LastUpdated)
} }
writer.Flush() writer.Flush()
writer.Close() gWriter.Flush()
gWriter.Close()
return buffer.Bytes() return buffer.Bytes()
} }
func DecodeProfileContent(data []byte) (*ProfileContent, error) { func DecodeProfileContent(data []byte) (*ProfileContent, error) {
var reader io.Reader = bytes.NewReader(data) reader := bytes.NewReader(data)
messageType, err := rw.ReadByte(reader) messageType, err := reader.ReadByte()
if err != nil { if err != nil {
return nil, err return nil, err
} }
if messageType != MessageTypeProfileContent { if messageType != MessageTypeProfileContent {
return nil, E.New("invalid message") return nil, E.New("invalid message")
} }
version, err := rw.ReadByte(reader) version, err := reader.ReadByte()
if err != nil { if err != nil {
return nil, err return nil, err
} }
reader, err = gzip.NewReader(reader) gReader, err := gzip.NewReader(reader)
if err != nil { if err != nil {
return nil, E.Cause(err, "unsupported profile") return nil, E.Cause(err, "unsupported profile")
} }
bReader := varbin.StubReader(gReader)
var content ProfileContent var content ProfileContent
content.Name, err = rw.ReadVString(reader) content.Name, err = varbin.ReadValue[string](bReader, binary.BigEndian)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -219,12 +222,12 @@ func DecodeProfileContent(data []byte) (*ProfileContent, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
content.Config, err = rw.ReadVString(reader) content.Config, err = varbin.ReadValue[string](bReader, binary.BigEndian)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if content.Type != ProfileTypeLocal { if content.Type != ProfileTypeLocal {
content.RemotePath, err = rw.ReadVString(reader) content.RemotePath, err = varbin.ReadValue[string](bReader, binary.BigEndian)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -3,6 +3,7 @@ package libbox
import ( import (
"os" "os"
"os/user" "os/user"
"runtime/debug"
"strconv" "strconv"
"time" "time"
@ -21,6 +22,11 @@ var (
sTVOS bool sTVOS bool
) )
func init() {
debug.SetPanicOnFault(true)
debug.SetTraceback("all")
}
func Setup(basePath string, workingPath string, tempPath string, isTVOS bool) { func Setup(basePath string, workingPath string, tempPath string, isTVOS bool) {
sBasePath = basePath sBasePath = basePath
sWorkingPath = workingPath sWorkingPath = workingPath

2
go.mod
View file

@ -27,7 +27,7 @@ require (
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f
github.com/sagernet/quic-go v0.47.0-beta.2 github.com/sagernet/quic-go v0.47.0-beta.2
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
github.com/sagernet/sing v0.4.3 github.com/sagernet/sing v0.5.0-beta.2
github.com/sagernet/sing-dns v0.2.3 github.com/sagernet/sing-dns v0.2.3
github.com/sagernet/sing-mux v0.2.0 github.com/sagernet/sing-mux v0.2.0
github.com/sagernet/sing-quic v0.2.2 github.com/sagernet/sing-quic v0.2.2

4
go.sum
View file

@ -108,8 +108,8 @@ github.com/sagernet/quic-go v0.47.0-beta.2/go.mod h1:bLVKvElSEMNv7pu7SZHscW02TYi
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= 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/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.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
github.com/sagernet/sing v0.4.3 h1:Ty/NAiNnVd6844k7ujlL5lkzydhcTH5Psc432jXA4Y8= github.com/sagernet/sing v0.5.0-beta.2 h1:V12EpwtsgYo5OLGjAiGoJobDJZeUsKv0b5y+yGAM6W0=
github.com/sagernet/sing v0.4.3/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls= github.com/sagernet/sing v0.5.0-beta.2/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-dns v0.2.3 h1:YzeBUn2tR38F7HtvGEQ0kLRLmZWMEgi/+7wqa4Twb1k= github.com/sagernet/sing-dns v0.2.3 h1:YzeBUn2tR38F7HtvGEQ0kLRLmZWMEgi/+7wqa4Twb1k=
github.com/sagernet/sing-dns v0.2.3/go.mod h1:BJpJv6XLnrUbSyIntOT6DG9FW0f4fETmPAHvNjOprLg= github.com/sagernet/sing-dns v0.2.3/go.mod h1:BJpJv6XLnrUbSyIntOT6DG9FW0f4fETmPAHvNjOprLg=
github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo= github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo=

View file

@ -12,10 +12,7 @@ import (
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common/auth" "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" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/rw"
"github.com/sagernet/sing/protocol/http" "github.com/sagernet/sing/protocol/http"
"github.com/sagernet/sing/protocol/socks" "github.com/sagernet/sing/protocol/socks"
"github.com/sagernet/sing/protocol/socks/socks4" "github.com/sagernet/sing/protocol/socks/socks4"
@ -51,16 +48,17 @@ 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 { func (h *Mixed) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
headerType, err := rw.ReadByte(conn) reader := std_bufio.NewReader(conn)
headerBytes, err := reader.Peek(1)
if err != nil { if err != nil {
return err return err
} }
switch headerType { switch headerBytes[0] {
case socks4.Version, socks5.Version: case socks4.Version, socks5.Version:
return socks.HandleConnection0(ctx, conn, headerType, h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata)) 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))
} }
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 { func (h *Mixed) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {

View file

@ -83,12 +83,11 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg
} }
func (h *VLESS) Start() error { func (h *VLESS) Start() error {
err := common.Start( if h.tlsConfig != nil {
h.service, err := h.tlsConfig.Start()
h.tlsConfig, if err != nil {
) return err
if err != nil { }
return err
} }
if h.transport == nil { if h.transport == nil {
return h.myInboundAdapter.Start() return h.myInboundAdapter.Start()

View file

@ -93,13 +93,16 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
} }
func (h *VMess) Start() error { func (h *VMess) Start() error {
err := common.Start( err := h.service.Start()
h.service,
h.tlsConfig,
)
if err != nil { if err != nil {
return err return err
} }
if h.tlsConfig != nil {
err = h.tlsConfig.Start()
if err != nil {
return err
}
}
if h.transport == nil { if h.transport == nil {
return h.myInboundAdapter.Start() return h.myInboundAdapter.Start()
} }

View file

@ -113,7 +113,7 @@ type DialerOptions struct {
Inet4BindAddress *ListenAddress `json:"inet4_bind_address,omitempty"` Inet4BindAddress *ListenAddress `json:"inet4_bind_address,omitempty"`
Inet6BindAddress *ListenAddress `json:"inet6_bind_address,omitempty"` Inet6BindAddress *ListenAddress `json:"inet6_bind_address,omitempty"`
ProtectPath string `json:"protect_path,omitempty"` ProtectPath string `json:"protect_path,omitempty"`
RoutingMark int `json:"routing_mark,omitempty"` RoutingMark uint32 `json:"routing_mark,omitempty"`
ReuseAddr bool `json:"reuse_addr,omitempty"` ReuseAddr bool `json:"reuse_addr,omitempty"`
ConnectTimeout Duration `json:"connect_timeout,omitempty"` ConnectTimeout Duration `json:"connect_timeout,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"` TCPFastOpen bool `json:"tcp_fast_open,omitempty"`

View file

@ -10,7 +10,7 @@ type RouteOptions struct {
AutoDetectInterface bool `json:"auto_detect_interface,omitempty"` AutoDetectInterface bool `json:"auto_detect_interface,omitempty"`
OverrideAndroidVPN bool `json:"override_android_vpn,omitempty"` OverrideAndroidVPN bool `json:"override_android_vpn,omitempty"`
DefaultInterface string `json:"default_interface,omitempty"` DefaultInterface string `json:"default_interface,omitempty"`
DefaultMark int `json:"default_mark,omitempty"` DefaultMark uint32 `json:"default_mark,omitempty"`
} }
type GeoIPOptions struct { type GeoIPOptions struct {

View file

@ -1,7 +1,6 @@
package outbound package outbound
import ( import (
std_bufio "bufio"
"context" "context"
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
@ -11,16 +10,10 @@ import (
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth" "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" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/rw"
"github.com/sagernet/sing/protocol/http"
"github.com/sagernet/sing/protocol/socks" "github.com/sagernet/sing/protocol/socks"
"github.com/sagernet/sing/protocol/socks/socks4"
"github.com/sagernet/sing/protocol/socks/socks5"
) )
type ProxyListener struct { type ProxyListener struct {
@ -102,16 +95,7 @@ func (l *ProxyListener) acceptLoop() {
} }
func (l *ProxyListener) accept(ctx context.Context, conn *net.TCPConn) error { func (l *ProxyListener) accept(ctx context.Context, conn *net.TCPConn) error {
headerType, err := rw.ReadByte(conn) return socks.HandleConnection(ctx, conn, l.authenticator, l, M.Metadata{})
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 { 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 startConf.ExtraArgs = options.ExtraArgs
if options.DataDirectory != "" { if options.DataDirectory != "" {
dataDirAbs, _ := filepath.Abs(startConf.DataDir) dataDirAbs, _ := filepath.Abs(startConf.DataDir)
if geoIPPath := filepath.Join(dataDirAbs, "geoip"); rw.FileExists(geoIPPath) && !common.Contains(options.ExtraArgs, "--GeoIPFile") { if geoIPPath := filepath.Join(dataDirAbs, "geoip"); rw.IsFile(geoIPPath) && !common.Contains(options.ExtraArgs, "--GeoIPFile") {
options.ExtraArgs = append(options.ExtraArgs, "--GeoIPFile", geoIPPath) options.ExtraArgs = append(options.ExtraArgs, "--GeoIPFile", geoIPPath)
} }
if geoIP6Path := filepath.Join(dataDirAbs, "geoip6"); rw.FileExists(geoIP6Path) && !common.Contains(options.ExtraArgs, "--GeoIPv6File") { if geoIP6Path := filepath.Join(dataDirAbs, "geoip6"); rw.IsFile(geoIP6Path) && !common.Contains(options.ExtraArgs, "--GeoIPv6File") {
options.ExtraArgs = append(options.ExtraArgs, "--GeoIPv6File", geoIP6Path) options.ExtraArgs = append(options.ExtraArgs, "--GeoIPv6File", geoIP6Path)
} }
} }
@ -58,8 +58,12 @@ func NewTor(ctx context.Context, router adapter.Router, logger log.ContextLogger
} }
if startConf.DataDir != "" { if startConf.DataDir != "" {
torrcFile := filepath.Join(startConf.DataDir, "torrc") torrcFile := filepath.Join(startConf.DataDir, "torrc")
if !rw.FileExists(torrcFile) { err := rw.MkdirParent(torrcFile)
err := rw.WriteFile(torrcFile, []byte("")) if err != nil {
return nil, err
}
if !rw.IsFile(torrcFile) {
err := os.WriteFile(torrcFile, []byte(""), 0o600)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -82,7 +82,7 @@ type Router struct {
interfaceFinder *control.DefaultInterfaceFinder interfaceFinder *control.DefaultInterfaceFinder
autoDetectInterface bool autoDetectInterface bool
defaultInterface string defaultInterface string
defaultMark int defaultMark uint32
networkMonitor tun.NetworkUpdateMonitor networkMonitor tun.NetworkUpdateMonitor
interfaceMonitor tun.DefaultInterfaceMonitor interfaceMonitor tun.DefaultInterfaceMonitor
packageManager tun.PackageManager packageManager tun.PackageManager
@ -1171,7 +1171,7 @@ func (r *Router) DefaultInterface() string {
return r.defaultInterface return r.defaultInterface
} }
func (r *Router) DefaultMark() int { func (r *Router) DefaultMark() uint32 {
return r.defaultMark return r.defaultMark
} }

View file

@ -50,7 +50,7 @@ func (r *Router) prepareGeoIPDatabase() error {
geoPath = foundPath geoPath = foundPath
} }
} }
if !rw.FileExists(geoPath) { if !rw.IsFile(geoPath) {
geoPath = filemanager.BasePath(r.ctx, geoPath) geoPath = filemanager.BasePath(r.ctx, geoPath)
} }
if stat, err := os.Stat(geoPath); err == nil { if stat, err := os.Stat(geoPath); err == nil {
@ -61,7 +61,7 @@ func (r *Router) prepareGeoIPDatabase() error {
os.Remove(geoPath) os.Remove(geoPath)
} }
} }
if !rw.FileExists(geoPath) { if !rw.IsFile(geoPath) {
r.logger.Warn("geoip database not exists: ", geoPath) r.logger.Warn("geoip database not exists: ", geoPath)
var err error var err error
for attempts := 0; attempts < 3; attempts++ { for attempts := 0; attempts < 3; attempts++ {
@ -96,7 +96,7 @@ func (r *Router) prepareGeositeDatabase() error {
geoPath = foundPath geoPath = foundPath
} }
} }
if !rw.FileExists(geoPath) { if !rw.IsFile(geoPath) {
geoPath = filemanager.BasePath(r.ctx, geoPath) geoPath = filemanager.BasePath(r.ctx, geoPath)
} }
if stat, err := os.Stat(geoPath); err == nil { if stat, err := os.Stat(geoPath); err == nil {
@ -107,7 +107,7 @@ func (r *Router) prepareGeositeDatabase() error {
os.Remove(geoPath) os.Remove(geoPath)
} }
} }
if !rw.FileExists(geoPath) { if !rw.IsFile(geoPath) {
r.logger.Warn("geosite database not exists: ", geoPath) r.logger.Warn("geosite database not exists: ", geoPath)
var err error var err error
for attempts := 0; attempts < 3; attempts++ { for attempts := 0; attempts < 3; attempts++ {

View file

@ -29,9 +29,13 @@ func (r *abstractDefaultRule) Type() string {
func (r *abstractDefaultRule) Start() error { func (r *abstractDefaultRule) Start() error {
for _, item := range r.allItems { for _, item := range r.allItems {
err := common.Start(item) if starter, isStarter := item.(interface {
if err != nil { Start() error
return err }); isStarter {
err := starter.Start()
if err != nil {
return err
}
} }
} }
return nil return nil
@ -183,8 +187,13 @@ func (r *abstractLogicalRule) UpdateGeosite() error {
} }
func (r *abstractLogicalRule) Start() error { func (r *abstractLogicalRule) Start() error {
for _, rule := range common.FilterIsInstance(r.rules, func(it adapter.HeadlessRule) (common.Starter, bool) { for _, rule := range common.FilterIsInstance(r.rules, func(it adapter.HeadlessRule) (interface {
rule, loaded := it.(common.Starter) Start() error
}, bool,
) {
rule, loaded := it.(interface {
Start() error
})
return rule, loaded return rule, loaded
}) { }) {
err := rule.Start() err := rule.Start()

View file

@ -1,12 +1,14 @@
package trojan package trojan
import ( import (
std_bufio "bufio"
"context" "context"
"net" "net"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/rw"
"github.com/sagernet/sing/common/task" "github.com/sagernet/sing/common/task"
"github.com/sagernet/smux" "github.com/sagernet/smux"
) )
@ -33,27 +35,36 @@ func HandleMuxConnection(ctx context.Context, conn net.Conn, metadata M.Metadata
return group.Run(ctx) return group.Run(ctx)
} }
func newMuxConnection(ctx context.Context, stream net.Conn, metadata M.Metadata, handler Handler) { func newMuxConnection(ctx context.Context, conn net.Conn, metadata M.Metadata, handler Handler) {
err := newMuxConnection0(ctx, stream, metadata, handler) err := newMuxConnection0(ctx, conn, metadata, handler)
if err != nil { if err != nil {
handler.NewError(ctx, E.Cause(err, "process trojan-go multiplex connection")) handler.NewError(ctx, E.Cause(err, "process trojan-go multiplex connection"))
} }
} }
func newMuxConnection0(ctx context.Context, stream net.Conn, metadata M.Metadata, handler Handler) error { func newMuxConnection0(ctx context.Context, conn net.Conn, metadata M.Metadata, handler Handler) error {
command, err := rw.ReadByte(stream) reader := std_bufio.NewReader(conn)
command, err := reader.ReadByte()
if err != nil { if err != nil {
return E.Cause(err, "read command") return E.Cause(err, "read command")
} }
metadata.Destination, err = M.SocksaddrSerializer.ReadAddrPort(stream) metadata.Destination, err = M.SocksaddrSerializer.ReadAddrPort(reader)
if err != nil { if err != nil {
return E.Cause(err, "read destination") 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 { switch command {
case CommandTCP: case CommandTCP:
return handler.NewConnection(ctx, stream, metadata) return handler.NewConnection(ctx, conn, metadata)
case CommandUDP: case CommandUDP:
return handler.NewPacketConnection(ctx, &PacketConn{Conn: stream}, metadata) return handler.NewPacketConnection(ctx, &PacketConn{Conn: conn}, metadata)
default: default:
return E.New("unknown command ", command) return E.New("unknown command ", command)
} }

View file

@ -2,6 +2,7 @@ package trojan
import ( import (
"context" "context"
"encoding/binary"
"net" "net"
"github.com/sagernet/sing/common/auth" "github.com/sagernet/sing/common/auth"
@ -76,7 +77,8 @@ func (s *Service[K]) NewConnection(ctx context.Context, conn net.Conn, metadata
return E.Cause(err, "skip crlf") return E.Cause(err, "skip crlf")
} }
command, err := rw.ReadByte(conn) var command byte
err = binary.Read(conn, binary.BigEndian, &command)
if err != nil { if err != nil {
return E.Cause(err, "read command") return E.Cause(err, "read command")
} }

View file

@ -8,7 +8,7 @@ import (
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/baderror" "github.com/sagernet/sing/common/baderror"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/rw" N "github.com/sagernet/sing/common/network"
) )
var _ net.Conn = (*GRPCConn)(nil) var _ net.Conn = (*GRPCConn)(nil)
@ -90,7 +90,7 @@ func (c *GRPCConn) Upstream() any {
return c.GunService return c.GunService
} }
var _ rw.WriteCloser = (*clientConnWrapper)(nil) var _ N.WriteCloser = (*clientConnWrapper)(nil)
type clientConnWrapper struct { type clientConnWrapper struct {
GunService_TunClient GunService_TunClient

View file

@ -13,7 +13,7 @@ import (
"github.com/sagernet/sing/common/baderror" "github.com/sagernet/sing/common/baderror"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/varbin"
) )
// kanged from: https://github.com/Qv2ray/gun-lite // 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) { func (c *GunConn) Write(b []byte) (n int, err error) {
varLen := rw.UVariantLen(uint64(len(b))) varLen := varbin.UvarintLen(uint64(len(b)))
buffer := buf.NewSize(6 + varLen + len(b)) buffer := buf.NewSize(6 + varLen + len(b))
header := buffer.Extend(6 + varLen) header := buffer.Extend(6 + varLen)
header[0] = 0x00 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 { func (c *GunConn) WriteBuffer(buffer *buf.Buffer) error {
defer buffer.Release() defer buffer.Release()
dataLen := buffer.Len() dataLen := buffer.Len()
varLen := rw.UVariantLen(uint64(dataLen)) varLen := varbin.UvarintLen(uint64(dataLen))
header := buffer.ExtendHeader(6 + varLen) header := buffer.ExtendHeader(6 + varLen)
header[0] = 0x00 header[0] = 0x00
binary.BigEndian.PutUint32(header[1:5], uint32(1+varLen+dataLen)) binary.BigEndian.PutUint32(header[1:5], uint32(1+varLen+dataLen))
header[5] = 0x0A header[5] = 0x0A
binary.PutUvarint(header[6:], uint64(dataLen)) binary.PutUvarint(header[6:], uint64(dataLen))
err := rw.WriteBytes(c.writer, buffer.Bytes()) err := common.Error(c.writer.Write(buffer.Bytes()))
if err != nil { if err != nil {
return baderror.WrapH2(err) return baderror.WrapH2(err)
} }