From 12408d60a06ba7a3862ae9f04d3bfc28fbb43998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 15 Apr 2022 19:30:31 +0800 Subject: [PATCH] Refine geosite --- README.md | 27 +- cli/gen-geosite/main.go | 60 ----- cli/geosite/main.go | 209 +++++++++++++++ cli/get-geoip/main.go | 27 -- cli/ss-local/main.go | 9 +- common/buf/pool.go | 18 ++ common/cond.go | 6 + common/exceptions/error.go | 3 + common/geosite/matcher.go | 62 +---- common/geosite/matcher_test.go | 21 -- common/geosite/metadata.go | 238 ++++++++++++++++++ common/geosite/reader.go | 84 +++++++ common/lowmem/debug.go | 7 + common/lowmem/free.go | 13 + common/network/dialer.go | 12 + common/network/http.go | 9 + common/rw/copy.go | 5 +- common/rw/count.go | 27 ++ common/rw/duplex.go | 4 +- common/rw/varinat.go | 23 +- debug.go | 12 + go.mod | 3 - go.sum | 57 ----- protocol/shadowsocks/shadowaead/aead.go | 16 +- protocol/shadowsocks/shadowaead/protocol.go | 46 +++- .../shadowsocks/shadowaead_2022/protocol.go | 2 +- transport/tcp/handler.go | 2 + 27 files changed, 739 insertions(+), 263 deletions(-) delete mode 100644 cli/gen-geosite/main.go create mode 100644 cli/geosite/main.go delete mode 100644 cli/get-geoip/main.go delete mode 100644 common/geosite/matcher_test.go create mode 100644 common/geosite/metadata.go create mode 100644 common/geosite/reader.go create mode 100644 common/lowmem/debug.go create mode 100644 common/lowmem/free.go create mode 100644 common/network/dialer.go create mode 100644 common/rw/count.go create mode 100644 debug.go diff --git a/README.md b/README.md index a6be57f..c6e5b0c 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,28 @@ Do you hear the people sing? -```shell -# geo resources -go install -v -trimpath -ldflags "-s -w -buildid=" ./cli/get-geoip -go install -v -trimpath -ldflags "-s -w -buildid=" ./cli/gen-geosite +### geosite -# ss-local +```shell +go install -v -trimpath -ldflags "-s -w -buildid=" ./cli/geosite +``` + +create from v2ray + +`geosite add v2ray` + +create cn only dat + +`geosite add v2ray cn` + +### geoip + +```shell +wget 'https://github.com/Dreamacro/maxmind-geoip/releases/latest/download/Country.mmdb' +``` + +### ss-local + +```shell go install -v -trimpath -ldflags "-s -w -buildid=" ./cli/ss-local ``` \ No newline at end of file diff --git a/cli/gen-geosite/main.go b/cli/gen-geosite/main.go deleted file mode 100644 index b35d236..0000000 --- a/cli/gen-geosite/main.go +++ /dev/null @@ -1,60 +0,0 @@ -package main - -import ( - "encoding/binary" - "io" - "net/http" - "os" - - "github.com/klauspost/compress/zstd" - "github.com/sagernet/sing/common/rw" - "github.com/sirupsen/logrus" - "github.com/ulikunitz/xz" - "github.com/v2fly/v2ray-core/v5/app/router/routercommon" - "google.golang.org/protobuf/proto" -) - -func main() { - response, err := http.Get("https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat.xz") - if err != nil { - logrus.Fatal(err) - } - defer response.Body.Close() - reader, err := xz.NewReader(response.Body) - if err != nil { - logrus.Fatal(err) - } - data, err := io.ReadAll(reader) - if err != nil { - logrus.Fatal(err) - } - geosite := routercommon.GeoSiteList{} - if err = proto.Unmarshal(data, &geosite); err != nil { - logrus.Fatal(err) - } - output, err := os.Create("geosite.dat") - if err != nil { - logrus.Fatal(err) - } - binary.Write(output, binary.BigEndian, byte(0)) // version - writer, _ := zstd.NewWriter(output) - rw.WriteUVariant(writer, uint64(len(geosite.Entry))) - for _, site := range geosite.Entry { - rw.WriteVString(writer, site.CountryCode) - domains := make([]string, 0, len(site.Domain)) - for _, domain := range site.Domain { - if domain.Type == routercommon.Domain_Full { - domains = append(domains, domain.Value) - } else { - domains = append(domains, domain.Value) - domains = append(domains, "."+domain.Value) - } - } - rw.WriteUVariant(writer, uint64(len(domains))) - for _, domain := range domains { - rw.WriteVString(writer, domain) - } - } - writer.Close() - output.Close() -} diff --git a/cli/geosite/main.go b/cli/geosite/main.go new file mode 100644 index 0000000..6c7c1f5 --- /dev/null +++ b/cli/geosite/main.go @@ -0,0 +1,209 @@ +package main + +import ( + "bytes" + "io/ioutil" + "os" + "strings" + + "github.com/sagernet/sing/common" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/geosite" + N "github.com/sagernet/sing/common/network" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/ulikunitz/xz" + "github.com/v2fly/v2ray-core/v5/app/router/routercommon" + "google.golang.org/protobuf/proto" +) + +var path string + +func main() { + command := &cobra.Command{ + Use: "geosite ...", + } + command.PersistentFlags().StringVarP(&path, "file", "f", "geosite.dat", "set resource path") + command.AddCommand(&cobra.Command{ + Use: "list", + Short: "List codes", + PreRun: load, + Run: edit, + }) + command.AddCommand(&cobra.Command{ + Use: "keep", + Short: "Keep selected codes", + PreRun: load, + Run: keep, + Args: cobra.MinimumNArgs(1), + }) + command.AddCommand(&cobra.Command{ + Use: "add [code]...", + Short: "Add codes form external file or url", + PreRun: load0, + Run: add, + Args: cobra.MinimumNArgs(1), + }) + if err := command.Execute(); err != nil { + logrus.Fatal(err) + } +} + +var site map[string][]string + +func load(cmd *cobra.Command, args []string) { + geoFile, err := os.Open(path) + if err != nil { + logrus.Fatal(E.Cause(err, "open geo resources")) + } + defer geoFile.Close() + site, err = geosite.Read(geoFile) + if err != nil { + logrus.Fatal(E.Cause(err, "read geo resources")) + } +} + +func load0(cmd *cobra.Command, args []string) { + geoFile, err := os.Open(path) + if err == nil { + defer geoFile.Close() + site, err = geosite.Read(geoFile) + if err != nil { + logrus.Fatal(E.Cause(err, "read geo resources")) + } + } + site = make(map[string][]string) +} + +func edit(cmd *cobra.Command, args []string) { + for code := range site { + println(strings.ToLower(code)) + } +} + +func keep(cmd *cobra.Command, args []string) { + kept := make(map[string][]string) + for _, code := range args { + code = strings.ToUpper(code) + if domains, exists := site[code]; exists { + kept[code] = domains + } else { + logrus.Fatal("code ", strings.ToLower(code), " do not exists!") + } + } + geoFile, err := os.Create(path) + if err != nil { + logrus.Fatal(err) + } + defer geoFile.Close() + err = geosite.Write(geoFile, kept) + if err != nil { + logrus.Fatal(err) + } +} + +func add(cmd *cobra.Command, args []string) { + resource := args[0] +find: + switch resource { + case "v2ray": + for _, dir := range []string{ + "/usr/share/v2ray", + "/usr/local/share/v2ray", + "/opt/share/v2ray", + } { + file := dir + "/geosite.dat" + if common.FileExists(file) { + resource = file + break find + } + } + + resource = "https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat.xz" + case "loyalsoldier": + resource = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat" + } + + var data []byte + var err error + if strings.HasPrefix(resource, "http://") || strings.HasPrefix(resource, "https://") { + logrus.Info("download ", resource) + data, err = N.Get(resource) + if err != nil { + logrus.Fatal(err) + } + } else { + logrus.Info("open ", resource) + file, err := os.Open(resource) + if err != nil { + logrus.Fatal(err) + } + data, err = ioutil.ReadAll(file) + file.Close() + if err != nil { + logrus.Fatal("read ", resource, ": ", err) + } + } + { + if strings.HasSuffix(resource, ".xz") { + decoder, err := xz.NewReader(bytes.NewReader(data)) + if err == nil { + data, _ = ioutil.ReadAll(decoder) + } + } + } + loaded := make(map[string][]string) + { + geositeList := routercommon.GeoSiteList{} + err = proto.Unmarshal(data, &geositeList) + if err == nil { + for _, geoSite := range geositeList.Entry { + domains := make([]string, 0, len(geoSite.Domain)) + for _, domain := range geoSite.Domain { + if domain.Type == routercommon.Domain_Full { + domains = append(domains, domain.Value) + } else { + domains = append(domains, domain.Value) + domains = append(domains, "."+domain.Value) + } + } + loaded[strings.ToLower(geoSite.CountryCode)] = common.Uniq(domains) + } + goto finish + } + } + { + loaded, _ = geosite.Read(bytes.NewReader(data)) + } +finish: + if len(loaded) == 0 { + logrus.Fatal("unknown resource format") + } + if len(args) > 1 { + for _, code := range args[1:] { + code = strings.ToLower(code) + if domains, exists := loaded[code]; exists { + site[code] = domains + } else { + logrus.Fatal("code ", code, " do not exists!") + } + } + } else { + for code, domains := range loaded { + site[code] = domains + } + } + for code, domains := range site { + site[code] = common.Uniq(domains) + } + geoFile, err := os.Create(path) + if err != nil { + logrus.Fatal(err) + } + defer geoFile.Close() + err = geosite.Write(geoFile, site) + if err != nil { + logrus.Fatal(err) + } + logrus.Info("saved ", path) +} diff --git a/cli/get-geoip/main.go b/cli/get-geoip/main.go deleted file mode 100644 index 7897d41..0000000 --- a/cli/get-geoip/main.go +++ /dev/null @@ -1,27 +0,0 @@ -package main - -import ( - "io" - "net/http" - "os" - - "github.com/sirupsen/logrus" -) - -func main() { - response, err := http.Get("https://github.com/Dreamacro/maxmind-geoip/releases/latest/download/Country.mmdb") - if err != nil { - logrus.Fatal(err) - } - defer response.Body.Close() - output, err := os.Create("Country.mmdb") - if err != nil { - logrus.Fatal(err) - } - defer output.Close() - _, err = io.Copy(output, response.Body) - if err != nil { - os.RemoveAll("Country.mmdb") - logrus.Fatal(err) - } -} diff --git a/cli/ss-local/main.go b/cli/ss-local/main.go index abc664f..8131349 100644 --- a/cli/ss-local/main.go +++ b/cli/ss-local/main.go @@ -258,13 +258,18 @@ func NewLocalClient(f *flags) (*LocalClient, error) { return nil, E.Cause(err, "geosite.dat not found") } - geositeMatcher, err := geosite.LoadGeositeMatcher(geodata, f.Bypass) + site, err := geosite.ReadArray(geodata, f.Bypass) + if err != nil { + return nil, err + } + + geositeMatcher, err := geosite.NewMatcher(site) if err != nil { return nil, err } client.Matcher = geositeMatcher - debug.FreeOSMemory() } + debug.FreeOSMemory() return client, nil } diff --git a/common/buf/pool.go b/common/buf/pool.go index 27bdfda..37b9d7b 100644 --- a/common/buf/pool.go +++ b/common/buf/pool.go @@ -22,3 +22,21 @@ func GetBytes() []byte { func PutBytes(buffer []byte) { pool.Put(buffer) } + +func Make(size int) []byte { + var buffer []byte + if size <= 64 { + buffer = make([]byte, 64) + } else if size <= 1024 { + buffer = make([]byte, 1024) + } else if size <= 4096 { + buffer = make([]byte, 4096) + } else if size <= 16384 { + buffer = make([]byte, 16384) + } else if size <= 65535 { + buffer = make([]byte, 65535) + } else { + buffer = make([]byte, size) + } + return buffer[:size] +} diff --git a/common/cond.go b/common/cond.go index f78afb6..6cf44cf 100644 --- a/common/cond.go +++ b/common/cond.go @@ -5,6 +5,7 @@ import ( "io" "log" "strings" + "unsafe" ) func Any[T any](array []T, block func(it T) bool) bool { @@ -53,6 +54,11 @@ func Find[T any](arr []T, block func(it T) bool) T { return defaultValue } +func Dup[T any](obj T) T { + p := uintptr(unsafe.Pointer(&obj)) + return *(*T)(unsafe.Pointer(p)) +} + func Uniq[T comparable](arr []T) []T { result := make([]T, 0, len(arr)) seen := make(map[T]struct{}, len(arr)) diff --git a/common/exceptions/error.go b/common/exceptions/error.go index 510ba90..57e72cc 100644 --- a/common/exceptions/error.go +++ b/common/exceptions/error.go @@ -42,6 +42,9 @@ func New(message ...any) error { } func Cause(cause error, message string) Exception { + if cause == nil { + return nil + } return exception{message, cause} } diff --git a/common/geosite/matcher.go b/common/geosite/matcher.go index 8fb5cc4..368ecb5 100644 --- a/common/geosite/matcher.go +++ b/common/geosite/matcher.go @@ -1,14 +1,6 @@ package geosite import ( - "bufio" - "encoding/binary" - "io" - "strings" - - "github.com/klauspost/compress/zstd" - E "github.com/sagernet/sing/common/exceptions" - "github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/trieset" ) @@ -20,58 +12,10 @@ func (m *Matcher) Match(domain string) bool { return m.ds.Has(domain) } -func LoadGeositeMatcher(reader io.Reader, code string) (*Matcher, error) { - version, err := rw.ReadByte(reader) +func NewMatcher(domains []string) (*Matcher, error) { + ds, err := trieset.New(domains) if err != nil { return nil, err } - if version != 0 { - return nil, E.New("bad geosite data") - } - decoder, err := zstd.NewReader(reader, zstd.WithDecoderLowmem(true), zstd.WithDecoderConcurrency(1)) - if err != nil { - return nil, err - } - defer decoder.Close() - bufferedReader := bufio.NewReader(decoder) - geositeLength, err := binary.ReadUvarint(bufferedReader) - if err != nil { - return nil, err - } - for geositeLength > 0 { - geositeLength-- - countryCode, err := rw.ReadVString(bufferedReader) - if err != nil { - return nil, err - } - domainLength, err := binary.ReadUvarint(bufferedReader) - if err != nil { - return nil, err - } - if strings.EqualFold(code, countryCode) { - domains := make([]string, 0, domainLength) - for domainLength > 0 { - domainLength-- - domain, err := rw.ReadVString(bufferedReader) - if err != nil { - return nil, err - } - domains = append(domains, domain) - } - ds, err := trieset.New(domains) - if err != nil { - return nil, err - } - return &Matcher{ds}, nil - } else { - for domainLength > 0 { - domainLength-- - _, err = rw.ReadVString(bufferedReader) - if err != nil { - return nil, err - } - } - } - } - return nil, E.New(code, " not found in geosite") + return &Matcher{ds}, nil } diff --git a/common/geosite/matcher_test.go b/common/geosite/matcher_test.go deleted file mode 100644 index 6d09a6d..0000000 --- a/common/geosite/matcher_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package geosite - -import ( - "os" - "testing" -) - -func TestGeosite(t *testing.T) { - geosite, err := os.Open("../../geosite.dat") - if err != nil { - t.Skip("no geosite found") - return - } - matcher, err := LoadGeositeMatcher(geosite, "cn") - if err != nil { - t.Fatal(err) - } - if !matcher.Match("baidu.cn") { - t.Fatal("match failed") - } -} diff --git a/common/geosite/metadata.go b/common/geosite/metadata.go new file mode 100644 index 0000000..3f6ce43 --- /dev/null +++ b/common/geosite/metadata.go @@ -0,0 +1,238 @@ +package geosite + +import ( + "bytes" + "io" + "sort" + + "github.com/sagernet/sing/common" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/rw" +) + +func Write(writer io.Writer, geosite map[string][]string) error { + keys := make([]string, 0, len(geosite)) + for code := range geosite { + keys = append(keys, code) + } + sort.Strings(keys) + + content := &bytes.Buffer{} + index := make(map[string]int) + for _, code := range keys { + index[code] = content.Len() + for _, domain := range geosite[code] { + if err := rw.WriteVString(content, domain); 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 = rw.WriteUVariant(writer, uint64(index[code])) + if err != nil { + return err + } + err = rw.WriteUVariant(writer, uint64(len(geosite[code]))) + if err != nil { + return err + } + } + + _, err = writer.Write(content.Bytes()) + if err != nil { + return err + } + + return nil +} + +func Read(reader io.Reader, codes ...string) (map[string][]string, error) { + version, err := rw.ReadByte(reader) + if err != nil { + return nil, err + } + if version != 0 { + return nil, E.New("bad version") + } + length, err := rw.ReadUVariant(reader) + if err != nil { + return nil, err + } + keys := make([]string, length) + domainIndex := make(map[string]int) + domainLength := make(map[string]int) + for i := 0; i < int(length); i++ { + code, err := rw.ReadVString(reader) + if err != nil { + return nil, err + } + keys[i] = code + codeIndex, err := rw.ReadUVariant(reader) + if err != nil { + return nil, err + } + codeLength, err := rw.ReadUVariant(reader) + if err != nil { + return nil, err + } + domainIndex[code] = int(codeIndex) + domainLength[code] = int(codeLength) + } + site := make(map[string][]string) + for _, code := range keys { + if len(codes) == 0 || common.Contains(codes, code) { + domains := make([]string, domainLength[code]) + for i := range domains { + domains[i], err = rw.ReadVString(reader) + if err != nil { + return nil, err + } + } + site[code] = domains + } else { + dLength := domainLength[code] + for i := 0; i < dLength; i++ { + _, err = rw.ReadVString(reader) + if err != nil { + return nil, err + } + } + } + } + return site, nil +} + +func ReadSeek(reader io.ReadSeeker, codes ...string) (map[string][]string, error) { + version, err := rw.ReadByte(reader) + if err != nil { + return nil, err + } + if version != 0 { + return nil, E.New("bad version") + } + length, err := rw.ReadUVariant(reader) + if err != nil { + return nil, err + } + keys := make([]string, length) + domainIndex := make(map[string]int) + domainLength := make(map[string]int) + for i := 0; i < int(length); i++ { + code, err := rw.ReadVString(reader) + if err != nil { + return nil, err + } + keys[i] = code + codeIndex, err := rw.ReadUVariant(reader) + if err != nil { + return nil, err + } + codeLength, err := rw.ReadUVariant(reader) + if err != nil { + return nil, err + } + domainIndex[code] = int(codeIndex) + domainLength[code] = int(codeLength) + } + if len(codes) == 0 { + codes = keys + } + site := make(map[string][]string) + counter := &rw.ReadCounter{Reader: reader} + for _, code := range codes { + domains := make([]string, domainLength[code]) + if _, exists := domainIndex[code]; !exists { + return nil, E.New("code ", code, " not exists!") + } + _, err = reader.Seek(int64(domainIndex[code])-counter.Count(), io.SeekCurrent) + if err != nil { + return nil, err + } + for i := range domains { + domains[i], err = rw.ReadVString(reader) + if err != nil { + return nil, err + } + } + site[code] = domains + } + + return site, nil +} + +func ReadArray(reader io.ReadSeeker, codes ...string) ([]string, error) { + version, err := rw.ReadByte(reader) + if err != nil { + return nil, err + } + if version != 0 { + return nil, E.New("bad version") + } + length, err := rw.ReadUVariant(reader) + if err != nil { + return nil, err + } + keys := make([]string, length) + domainIndex := make(map[string]int) + domainLength := make(map[string]int) + for i := 0; i < int(length); i++ { + code, err := rw.ReadVString(reader) + if err != nil { + return nil, err + } + keys[i] = code + codeIndex, err := rw.ReadUVariant(reader) + if err != nil { + return nil, err + } + codeLength, err := rw.ReadUVariant(reader) + if err != nil { + return nil, err + } + domainIndex[code] = int(codeIndex) + domainLength[code] = int(codeLength) + } + if len(codes) == 0 { + codes = keys + } + var domainL int + for _, code := range keys { + if _, exists := domainIndex[code]; !exists { + return nil, E.New("code ", code, " not exists!") + } + domainL += domainLength[code] + } + domains := make([]string, 0, domainL) + counter := &rw.ReadCounter{Reader: reader} + for _, code := range codes { + _, err := reader.Seek(int64(domainIndex[code])-counter.Count(), io.SeekCurrent) + if err != nil { + return nil, err + } + codeL := domainLength[code] + for i := 0; i < codeL; i++ { + domain, err := rw.ReadVString(reader) + if err != nil { + return nil, err + } + domains = append(domains, domain) + } + } + + return domains, nil +} diff --git a/common/geosite/reader.go b/common/geosite/reader.go new file mode 100644 index 0000000..5f86e8e --- /dev/null +++ b/common/geosite/reader.go @@ -0,0 +1,84 @@ +package geosite + +import ( + "io" + "strings" + + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/rw" +) + +type Reader struct { + reader io.ReadSeeker + counter *rw.ReadCounter + domainIndex map[string]int + domainLength map[string]int + cache map[string][]string +} + +func NewReader(reader io.ReadSeeker) (*Reader, error) { + r := &Reader{ + reader: reader, + counter: &rw.ReadCounter{Reader: reader}, + domainIndex: map[string]int{}, + domainLength: map[string]int{}, + cache: map[string][]string{}, + } + + version, err := rw.ReadByte(reader) + if err != nil { + return nil, err + } + if version != 0 { + return nil, E.New("bad version") + } + length, err := rw.ReadUVariant(reader) + if err != nil { + return nil, err + } + + for i := 0; i < int(length); i++ { + code, err := rw.ReadVString(reader) + if err != nil { + return nil, err + } + domainIndex, err := rw.ReadUVariant(reader) + if err != nil { + return nil, err + } + domainLength, err := rw.ReadUVariant(reader) + if err != nil { + return nil, err + } + r.domainIndex[code] = int(domainIndex) + r.domainLength[code] = int(domainLength) + } + + return r, nil +} + +func (r *Reader) Load(code string) ([]string, error) { + code = strings.ToLower(code) + if cache, ok := r.cache[code]; ok { + return cache, nil + } + index, exists := r.domainIndex[code] + if !exists { + return nil, E.New("code ", code, " not exists!") + } + _, err := r.reader.Seek(int64(index)-r.counter.Count(), io.SeekCurrent) + if err != nil { + return nil, err + } + r.counter.Reset() + dLength := r.domainLength[code] + domains := make([]string, dLength) + for i := 0; i < dLength; i++ { + domains[i], err = rw.ReadVString(r.counter) + if err != nil { + return nil, err + } + } + r.cache[code] = domains + return domains, nil +} diff --git a/common/lowmem/debug.go b/common/lowmem/debug.go new file mode 100644 index 0000000..3425d4f --- /dev/null +++ b/common/lowmem/debug.go @@ -0,0 +1,7 @@ +//go:build debug + +package lowmem + +func init() { + Enabled = true +} diff --git a/common/lowmem/free.go b/common/lowmem/free.go new file mode 100644 index 0000000..c7683dd --- /dev/null +++ b/common/lowmem/free.go @@ -0,0 +1,13 @@ +package lowmem + +import ( + "runtime" +) + +var Enabled = false + +func Free() { + if Enabled { + runtime.GC() + } +} diff --git a/common/network/dialer.go b/common/network/dialer.go new file mode 100644 index 0000000..123835b --- /dev/null +++ b/common/network/dialer.go @@ -0,0 +1,12 @@ +package network + +import ( + "context" + "net" +) + +type ContextDialer interface { + DialContext(ctx context.Context, network, address string) (net.Conn, error) +} + +var SystemDialer ContextDialer = &net.Dialer{} diff --git a/common/network/http.go b/common/network/http.go index 83101d5..fa908fe 100644 --- a/common/network/http.go +++ b/common/network/http.go @@ -18,6 +18,15 @@ func HTTPClientWithNetwork(network string) *http.Client { } } +func Get(url string) ([]byte, error) { + response, err := http.Get(url) + if err != nil { + return nil, err + } + defer response.Body.Close() + return ioutil.ReadAll(response.Body) +} + func GetContext(ctx context.Context, client *http.Client, url string) ([]byte, error) { request, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { diff --git a/common/rw/copy.go b/common/rw/copy.go index 8d101cc..ed47675 100644 --- a/common/rw/copy.go +++ b/common/rw/copy.go @@ -41,7 +41,7 @@ func ReadFromVar(writerVar *io.Writer, reader io.Reader) (int64, error) { } func CopyConn(ctx context.Context, conn net.Conn, dest net.Conn) error { - return task.Run(context.Background(), func() error { + err := task.Run(ctx, func() error { defer CloseRead(conn) defer CloseWrite(dest) return common.Error(io.Copy(dest, conn)) @@ -50,6 +50,9 @@ func CopyConn(ctx context.Context, conn net.Conn, dest net.Conn) error { defer CloseWrite(conn) return common.Error(io.Copy(conn, dest)) }) + conn.Close() + dest.Close() + return err } func CopyPacketConn(ctx context.Context, conn net.PacketConn, outPacketConn net.PacketConn) error { diff --git a/common/rw/count.go b/common/rw/count.go new file mode 100644 index 0000000..1dab79f --- /dev/null +++ b/common/rw/count.go @@ -0,0 +1,27 @@ +package rw + +import ( + "io" + "sync/atomic" +) + +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 +} + +func (r *ReadCounter) Count() int64 { + return r.count +} + +func (r *ReadCounter) Reset() { + atomic.StoreInt64(&r.count, 0) +} diff --git a/common/rw/duplex.go b/common/rw/duplex.go index fa5e9ae..5c809b5 100644 --- a/common/rw/duplex.go +++ b/common/rw/duplex.go @@ -14,12 +14,12 @@ func CloseRead(conn io.Closer) error { if closer, ok := conn.(ReadCloser); ok { return closer.CloseRead() } - return conn.Close() + return nil } func CloseWrite(conn io.Closer) error { if closer, ok := conn.(WriteCloser); ok { return closer.CloseWrite() } - return conn.Close() + return nil } diff --git a/common/rw/varinat.go b/common/rw/varinat.go index d4823e6..f6bbb6a 100644 --- a/common/rw/varinat.go +++ b/common/rw/varinat.go @@ -7,14 +7,23 @@ import ( "github.com/sagernet/sing/common" ) -type InputStream interface { +type stubByteReader struct { io.Reader - io.ByteReader } -type OutputStream interface { - io.Writer - io.ByteWriter +func (r stubByteReader) ReadByte() (byte, error) { + return ReadByte(r) +} + +func ToByteReader(reader io.Reader) io.ByteReader { + if byteReader, ok := reader.(io.ByteReader); ok { + return byteReader + } + return &stubByteReader{reader} +} + +func ReadUVariant(reader io.Reader) (uint64, error) { + return binary.ReadUvarint(ToByteReader(reader)) } func WriteUVariant(writer io.Writer, value uint64) error { @@ -30,8 +39,8 @@ func WriteVString(writer io.Writer, value string) error { return WriteString(writer, value) } -func ReadVString(reader InputStream) (string, error) { - length, err := binary.ReadUvarint(reader) +func ReadVString(reader io.Reader) (string, error) { + length, err := binary.ReadUvarint(ToByteReader(reader)) if err != nil { return "", err } diff --git a/debug.go b/debug.go new file mode 100644 index 0000000..6ca0244 --- /dev/null +++ b/debug.go @@ -0,0 +1,12 @@ +//go:build debug + +package sing + +import ( + "net/http" + _ "net/http/pprof" +) + +func init() { + go http.ListenAndServe(":8964", nil) +} diff --git a/go.mod b/go.mod index 64c99d7..ec988b8 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,6 @@ module github.com/sagernet/sing go 1.18 require ( - github.com/insomniacslk/dhcp v0.0.0-20220405050111-12fbdcb11b41 - github.com/klauspost/compress v1.15.1 github.com/openacid/low v0.1.21 github.com/oschwald/geoip2-golang v1.7.0 github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c @@ -34,7 +32,6 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/go.sum b/go.sum index 656177f..da66399 100644 --- a/go.sum +++ b/go.sum @@ -6,44 +6,20 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165 h1:BS21ZUJ/B5X2UVUbczfmdWH7GapPWAhxcMsDnjJTU1E= github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= -github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/insomniacslk/dhcp v0.0.0-20220405050111-12fbdcb11b41 h1:Yg3n3AI7GoHnWt7dyjsLPU+TEuZfPAg0OdiA3MJUV6I= -github.com/insomniacslk/dhcp v0.0.0-20220405050111-12fbdcb11b41/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= -github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= -github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= -github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= -github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/klauspost/compress v1.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583BL1A= -github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= -github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= -github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= -github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= -github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= -github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/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/openacid/errors v0.8.1/go.mod h1:GUQEJJOJE3W9skHm8E8Y4phdl2LLEN8iD7c5gcGgdx0= @@ -65,13 +41,10 @@ github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c h1:pqy40B3M github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -79,49 +52,19 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= -github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI= github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU= github.com/v2fly/v2ray-core/v5 v5.0.3 h1:2rnJ9vZbBQ7V4upWsoUVYGoqZl4grrx8SxOReKx+jjc= github.com/v2fly/v2ray-core/v5 v5.0.3/go.mod h1:zhDdsUJcNE8LcLRA3l7fEQ6QLuveD4/OLbQM2CceSHM= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220408190544-5352b0902921 h1:iU7T1X1J6yxDr0rda54sWGkHgOp5XJrqm79gcNlC2VM= golang.org/x/crypto v0.0.0-20220408190544-5352b0902921/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3 h1:EN5+DfgmRMvRUrMGERW2gQl3Vc+Z7ZMnI/xdEpPSf0c= golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f h1:8w7RhxzTVgUzw/AH/9mUV5q0vMgy40SQRursCcfmkCw= golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.zx2c4.com/wireguard v0.0.0-20220407013110-ef5c587f782d h1:q4JksJ2n0fmbXC0Aj0eOs6E0AcPqnKglxWXWFqGD6x0= diff --git a/protocol/shadowsocks/shadowaead/aead.go b/protocol/shadowsocks/shadowaead/aead.go index 9775ff9..ca26fcc 100644 --- a/protocol/shadowsocks/shadowaead/aead.go +++ b/protocol/shadowsocks/shadowaead/aead.go @@ -131,7 +131,7 @@ func (r *Reader) Read(b []byte) (n int, err error) { } } -type AEADWriter struct { +type Writer struct { upstream io.Writer cipher cipher.AEAD data []byte @@ -139,8 +139,8 @@ type AEADWriter struct { maxPacketSize int } -func NewWriter(upstream io.Writer, cipher cipher.AEAD, maxPacketSize int) *AEADWriter { - return &AEADWriter{ +func NewWriter(upstream io.Writer, cipher cipher.AEAD, maxPacketSize int) *Writer { + return &Writer{ upstream: upstream, cipher: cipher, data: make([]byte, maxPacketSize+PacketLengthBufferSize+cipher.Overhead()*2), @@ -149,19 +149,19 @@ func NewWriter(upstream io.Writer, cipher cipher.AEAD, maxPacketSize int) *AEADW } } -func (w *AEADWriter) Upstream() io.Writer { +func (w *Writer) Upstream() io.Writer { return w.upstream } -func (w *AEADWriter) Replaceable() bool { +func (w *Writer) Replaceable() bool { return false } -func (w *AEADWriter) SetWriter(writer io.Writer) { +func (w *Writer) SetWriter(writer io.Writer) { w.upstream = writer } -func (w *AEADWriter) ReadFrom(r io.Reader) (n int64, err error) { +func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) { for { offset := w.cipher.Overhead() + PacketLengthBufferSize readN, readErr := r.Read(w.data[offset : offset+w.maxPacketSize]) @@ -185,7 +185,7 @@ func (w *AEADWriter) ReadFrom(r io.Reader) (n int64, err error) { } } -func (w *AEADWriter) Write(p []byte) (n int, err error) { +func (w *Writer) Write(p []byte) (n int, err error) { if len(p) == 0 { return } diff --git a/protocol/shadowsocks/shadowaead/protocol.go b/protocol/shadowsocks/shadowaead/protocol.go index 54bfc89..6212b33 100644 --- a/protocol/shadowsocks/shadowaead/protocol.go +++ b/protocol/shadowsocks/shadowaead/protocol.go @@ -110,6 +110,32 @@ func (m *Method) KeyLength() int { return m.keySaltLength } +func (m *Method) ReadRequest(upstream io.Reader) (io.Reader, error) { + saltBuffer := buf.Make(m.keySaltLength) + salt := common.Dup(saltBuffer) + _, err := io.ReadFull(upstream, salt) + if err != nil { + return nil, E.Cause(err, "read salt") + } + if m.replayFilter != nil { + if !m.replayFilter.Check(salt) { + return nil, E.New("salt not unique") + } + } + return NewReader(upstream, m.constructor(Kdf(m.key, salt, m.keySaltLength)), MaxPacketSize), nil +} + +func (m *Method) WriteResponse(upstream io.Writer) (io.Writer, error) { + saltBuffer := buf.Make(m.keySaltLength) + salt := common.Dup(saltBuffer) + common.Must1(io.ReadFull(m.secureRNG, salt)) + _, err := upstream.Write(salt) + if err != nil { + return nil, err + } + return NewWriter(upstream, m.constructor(Kdf(m.key, salt, m.keySaltLength)), MaxPacketSize), nil +} + func (m *Method) DialConn(conn net.Conn, destination *M.AddrPort) (net.Conn, error) { shadowsocksConn := &clientConn{ Conn: conn, @@ -131,19 +157,19 @@ func (m *Method) DialPacketConn(conn net.Conn) socks.PacketConn { return &aeadPacketConn{conn, m} } -func (m *Method) EncodePacket(key []byte, buffer *buf.Buffer) error { - cipher := m.constructor(Kdf(key, buffer.To(m.keySaltLength), m.keySaltLength)) - cipher.Seal(buffer.From(m.keySaltLength)[:0], rw.ZeroBytes[:cipher.NonceSize()], buffer.From(m.keySaltLength), nil) - buffer.Extend(cipher.Overhead()) +func (m *Method) EncodePacket(buffer *buf.Buffer) error { + c := m.constructor(Kdf(m.key, buffer.To(m.keySaltLength), m.keySaltLength)) + c.Seal(buffer.From(m.keySaltLength)[:0], rw.ZeroBytes[:c.NonceSize()], buffer.From(m.keySaltLength), nil) + buffer.Extend(c.Overhead()) return nil } -func (m *Method) DecodePacket(key []byte, buffer *buf.Buffer) error { +func (m *Method) DecodePacket(buffer *buf.Buffer) error { if buffer.Len() < m.keySaltLength { return E.New("bad packet") } - aead := m.constructor(Kdf(key, buffer.To(m.keySaltLength), m.keySaltLength)) - packet, err := aead.Open(buffer.Index(m.keySaltLength), rw.ZeroBytes[:aead.NonceSize()], buffer.From(m.keySaltLength), nil) + c := m.constructor(Kdf(m.key, buffer.To(m.keySaltLength), m.keySaltLength)) + packet, err := c.Open(buffer.Index(m.keySaltLength), rw.ZeroBytes[:c.NonceSize()], buffer.From(m.keySaltLength), nil) if err != nil { return err } @@ -222,7 +248,7 @@ func (c *clientConn) readResponse() error { } if c.method.replayFilter != nil { if !c.method.replayFilter.Check(salt) { - return E.New("salt is not unique") + return E.New("salt not unique") } } c.reader = NewReader( @@ -288,7 +314,7 @@ func (c *aeadPacketConn) WritePacket(buffer *buf.Buffer, destination *M.AddrPort return err } buffer = buffer.WriteBufferAtFirst(header) - err = c.method.EncodePacket(c.method.key, buffer) + err = c.method.EncodePacket(buffer) if err != nil { return err } @@ -301,7 +327,7 @@ func (c *aeadPacketConn) ReadPacket(buffer *buf.Buffer) (*M.AddrPort, error) { return nil, err } buffer.Truncate(n) - err = c.method.DecodePacket(c.method.key, buffer) + err = c.method.DecodePacket(buffer) if err != nil { return nil, err } diff --git a/protocol/shadowsocks/shadowaead_2022/protocol.go b/protocol/shadowsocks/shadowaead_2022/protocol.go index 50ce702..fad55a0 100644 --- a/protocol/shadowsocks/shadowaead_2022/protocol.go +++ b/protocol/shadowsocks/shadowaead_2022/protocol.go @@ -256,7 +256,7 @@ func (c *clientConn) readResponse() error { } if !c.method.replayFilter.Check(salt) { - return E.New("salt is not unique") + return E.New("salt not unique") } reader := shadowaead.NewReader( diff --git a/transport/tcp/handler.go b/transport/tcp/handler.go index dad9be9..f7c7430 100644 --- a/transport/tcp/handler.go +++ b/transport/tcp/handler.go @@ -6,6 +6,7 @@ import ( "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/lowmem" M "github.com/sagernet/sing/common/metadata" "github.com/sagernet/sing/common/redir" ) @@ -105,6 +106,7 @@ func (l *Listener) loop() { if hErr != nil { l.handler.HandleError(&Error{Conn: tcpConn, Cause: hErr}) } + lowmem.Free() }() } }