├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── UPDATE.md ├── WORKSPACE ├── api ├── client │ ├── api.go │ └── api_test.go └── server │ ├── server.go │ ├── server.http │ └── server_test.go ├── build.cmd ├── build.sh ├── cmd ├── shadowsocksr-server │ ├── BUILD │ ├── command │ │ ├── config.go │ │ └── help.go │ ├── main.go │ └── targets.bzl ├── signal_test │ └── main.go └── test │ └── main.go ├── common ├── cache │ ├── cache.go │ ├── cache_test.go │ └── lru_cache.go ├── ciphers │ ├── aead │ │ ├── aead.go │ │ ├── aes_gcm.go │ │ ├── chacha20_ietf_poly1305.go │ │ ├── cipher_conn.go │ │ └── cipher_packet.go │ ├── block │ │ ├── aes_cbc.go │ │ └── block.go │ ├── cipher.go │ ├── cipher_test.go │ ├── common.go │ └── stream │ │ ├── aes_cfb.go │ │ ├── aes_ctr.go │ │ ├── bf_cfb.go │ │ ├── cast5_cfb.go │ │ ├── chacha20.go │ │ ├── cipher_conn.go │ │ ├── cipher_packet.go │ │ ├── des_cfb.go │ │ ├── none.go │ │ ├── rc4.go │ │ ├── rc4_md5.go │ │ ├── salsa20.go │ │ └── stream.go ├── common.go ├── eventbus │ ├── eventbus.go │ └── eventbus_test.go ├── log │ └── logging.go ├── network │ ├── ciphers │ │ ├── ciphers.go │ │ └── ciphers_test.go │ ├── conn.go │ ├── decorate.go │ ├── listener.go │ ├── listener_test.go │ └── request.go ├── obfs │ ├── auth_aes128.go │ ├── auth_aes128_test.go │ ├── auth_chain.go │ ├── auth_chain_test.go │ ├── common.go │ ├── http_simple.go │ ├── http_simple_test.go │ ├── obfs.go │ ├── obfs_test.go │ ├── obfs_tls.go │ ├── obfs_tls_test.go │ ├── plain.go │ └── server_info.go └── pool │ ├── pool.go │ └── pool_test.go ├── core ├── global_data.go ├── interface.go └── version.go ├── error_formatting.html ├── go.mod ├── go.sum ├── model ├── model.go └── model_test.go ├── proxy ├── client │ └── shadowsocksr.go └── server │ ├── shadowsocksr.go │ └── shadowsocksr_test.go ├── publish ├── oss-cn-hongkong.aliyuncs.com ├── ossutil64 └── publish.sh ├── release ├── BUILD ├── bazel │ ├── BUILD │ ├── build.bzl │ ├── gpg.bzl │ ├── matrix.bzl │ └── zip.bzl ├── config.json ├── deplody.sh ├── mapping.bzl ├── systemd │ └── vnet.service ├── systemv │ └── vnet └── version.json ├── remote ├── service ├── limit.go ├── rule.go ├── rule_test.go ├── service.go ├── shadowsocksr.go └── shadowsocksr_test.go ├── test_all.cmd ├── testing └── servers │ └── http │ └── http.go └── utils ├── addrx ├── addrx.go └── addrx_test.go ├── arrayx ├── arrayx.go └── arrayx_test.go ├── auto_increment.go ├── binaryx └── binaryx.go ├── bytesx ├── bytesx.go └── bytesx_test.go ├── datasize └── datasize.go ├── goroutine └── goroutine.go ├── iox ├── io_test.go └── iox.go ├── ip.go ├── langx ├── langx.go └── langx_test.go ├── lock.go ├── monitor └── monitor.go ├── netx └── netx.go ├── osx └── osx.go ├── randomx ├── randomx.go └── randomx_test.go ├── rsax ├── rsax.go └── rsax_test.go ├── scheme.go ├── scheme_test.go ├── shadowsocksx └── shadowsocksx.go ├── snowflake.go ├── socksproxy ├── socks.go └── socks_test.go ├── stringx ├── stringx.go └── stringx_test.go ├── time.go └── time_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | /server 3 | /server.exe 4 | /vnet 5 | /vnet.exe 6 | /config.json 7 | *.test.exe 8 | *.pprof 9 | .idea/** 10 | main 11 | main.exe 12 | /test 13 | .vscode/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | go: 4 | - 1.10.x 5 | install: go get -u -t -v ./... 6 | before_deploy: make 7 | deploy: 8 | provider: releases 9 | prerelease: true 10 | skip_cleanup: true 11 | file_glob: true 12 | file: bin/* 13 | # draft: true 14 | overwrite: true 15 | on: 16 | repo: ProxyPanel/VNet-SSR 17 | branch: master 18 | tags: true 19 | api_key: 20 | secure: cdCjHQaPWHJoKIBZ0qXN47kn/LpEIkgLshVTi+xckQl/gDpJ0U/rSh8BMSl4Do3fYRuIPnmlQygDYc4nhYmMLEJA2tGwLmmVw7AYJI8o8+xXLKDcL2J2JVHER3h4m0fFWDkZlaxPGX3UgBFKabWhxwwuTqQdjLK7SaU6r1xmKuaU1z2Ufq/m5nV86DYbsPccMjBAN2UKl3kA0hdwVPDuBUr33ZqoXa+z/9PiVeJQM8WHF9SNqy/5YHp4VTfnZti41hH+7dyqkpu5uM1L/kVzfVLLJZ7f2bBQeOrIh/l4zfnT63rq5wJO9pgj/ETegPGw8xjI7M3dO0cBELRHN50sZgBTc0/Rl7/St5oHe20u2t6m/DKvyPj0T5ghvnE2PNcQwvo/jWQ8Qq9b94VK32T57+r+TS85A3iQ5fZUrz46tqhi7W959I8cwsQMz2yzwGl/qjkczfIvFRAjW/kmnbBRzdpItL1SVdFuhNHg1ysvDsAa4EHqw+nK+THdKZOrHuNUgSWHaCxDdn9mE3pRb7xg+M+brpmlEX0OF8/5oJ6ix5D7NGk3gEK1fm4tbc8fPnk8Nz95rhhy6bxaYnvCmKJcDU+KXy1M5SHmtEG72BH4YrUNHLqJpyFOCm6gwFhxsXcT9JCEZIhPQCKW8ODnYNVcyA62rrjOVs9UZWoYxam4SBk= 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export PATH := $(GOPATH)/bin:$(PATH) 2 | LDFLAGS := -s -w 3 | # The -w and -s flags reduce binary sizes by excluding unnecessary symbols and debug info 4 | 5 | all: 6 | env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o bin/vnet_darwin_amd64 ./cmd/server/server.go 7 | env CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build -ldflags "$(LDFLAGS)" -o bin/vnet_freebsd_386 ./cmd/server/server.go 8 | env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o bin/vnet_freebsd_amd64 ./cmd/server/server.go 9 | env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "$(LDFLAGS)" -o bin/vnet_linux_386 ./cmd/server/server.go 10 | env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o bin/vnet_linux_amd64 ./cmd/server/server.go 11 | env CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -ldflags "$(LDFLAGS)" -o bin/vnet_linux_arm ./cmd/server/server.go 12 | env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o bin/vnet_linux_arm64 ./cmd/server/server.go 13 | env CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags "$(LDFLAGS)" -o bin/vnet_windows_386.exe ./cmd/server/server.go 14 | env CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o bin/vnet_windows_amd64.exe ./cmd/server/server.go 15 | env CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build -ldflags "$(LDFLAGS)" -o bin/vnet_linux_mips64 ./cmd/server/server.go 16 | env CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -ldflags "$(LDFLAGS)" -o bin/vnet_linux_mips64le ./cmd/server/server.go 17 | env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o bin/vnet_linux_mips ./cmd/server/server.go 18 | env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o bin/vnet_linux_mipsle ./cmd/server/server.go 19 | 20 | clean: 21 | rm $(BINDIR)/* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VNet-SSR 2 | 3 | ## 功能介绍 4 | Vnet是一个网络工具,在某些网络条件受到限速的情况根据算法提高网络服务. 5 | 6 | ## 编译方式 7 | 预安装: [Go语言](https://golang.org/), [Bazel](https://docs.bazel.build/) 8 | ```sh 9 | rm -rf vent 10 | git clone https://github.com/ProxyPanel/VNet-SSR.git vent && cd vent 11 | GO111MODULE=off go get -v ./... 12 | bazel build --action_env=PATH=$PATH --action_env=SPWD=$PWD --action_env=GOPATH=$(go env GOPATH) --action_env=GOCACHE=$(go env GOCACHE) --spawn_strategy local //release:vnet_linux_amd64_package 13 | ``` 14 | 15 | ## 支持加密方式 16 | ``` 17 | aes-256-cfb 18 | bf-cfb 19 | chacha20 20 | chacha20-ietf 21 | aes-128-cfb 22 | aes-192-cfb 23 | aes-128-ctr 24 | aes-192-ctr 25 | aes-256-ctr 26 | cast5-cfb 27 | des-cfb 28 | rc4-md5 29 | salsa20 30 | aes-256-gcm 31 | aes-192-gcm 32 | aes-128-gcm 33 | chacha20-ietf-poly1305 34 | ``` 35 | 36 | ## 注意事项 37 | config.json配置文件中的所有时间单位都为毫秒 38 | 升级后续删除原有config.json重新生成 39 | -------------------------------------------------------------------------------- /UPDATE.md: -------------------------------------------------------------------------------- 1 | ## 2020-12-13 03:38:56 2 | version 2.1.0 3 | Open-source! 4 | 5 | ## 2019-11-12 18:09:00 6 | version 2.0.4 7 | fix: AEAD package Integrity 8 | 9 | ## 2019-10-29 00:09:15 10 | version 2.0.3 11 | fix: bug of Limit the number of client logins 12 | update: obfs log 13 | fix: traffic limit of node 14 | 15 | ## 2019-10-21 23:55:16 16 | version: 2.0.2 17 | update: Limit the number of client logins 18 | 19 | ## 2019-10-17 00:09:21 20 | version: 2.0.1 21 | update: user limit prefer -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | workspace(name = "vnet") 2 | -------------------------------------------------------------------------------- /api/client/api.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "crypto/tls" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/ProxyPanel/VNet-SSR/core" 8 | "net/http" 9 | "strconv" 10 | "time" 11 | 12 | "github.com/ProxyPanel/VNet-SSR/model" 13 | "github.com/ProxyPanel/VNet-SSR/utils/langx" 14 | "github.com/ProxyPanel/VNet-SSR/utils/stringx" 15 | "github.com/pkg/errors" 16 | "github.com/sirupsen/logrus" 17 | "github.com/tidwall/gjson" 18 | "gopkg.in/resty.v1" 19 | ) 20 | 21 | var restyc *resty.Client 22 | 23 | func init() { 24 | restyc = resty.New(). 25 | SetTransport(&http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}). 26 | SetTimeout(5 * time.Second). 27 | SetRedirectPolicy(resty.FlexibleRedirectPolicy(2)) 28 | } 29 | 30 | var Host = core.GetApp().Host() + "/api/ssr/v1" 31 | 32 | // implement for vnet api get request 33 | func get(url string) (result string, err error) { 34 | logrus.WithFields(logrus.Fields{"url": url}).Debug("get") 35 | 36 | header := map[string]string{ 37 | "key": core.GetApp().Key(), 38 | "timestamp": strconv.FormatInt(time.Now().Unix(), 10), 39 | } 40 | r, err := restyc.R().SetHeaders(header).Get(url) 41 | if err != nil { 42 | return "", errors.Wrap(err, "get request error") 43 | } 44 | if r.StatusCode() != http.StatusOK { 45 | return "", errors.New(fmt.Sprintf("get request status: %d body: %s", r.StatusCode(), string(r.Body()))) 46 | } 47 | body := r.Body() 48 | responseJson := stringx.BUnicodeToUtf8(body) 49 | 50 | return responseJson, nil 51 | } 52 | 53 | func post(url, param string) (result string, err error) { 54 | logrus.WithFields(logrus.Fields{ 55 | "param": param, 56 | "url": url, 57 | }).Debug("post") 58 | header := map[string]string{ 59 | "key": core.GetApp().Key(), 60 | "timestamp": strconv.FormatInt(time.Now().Unix(), 10), 61 | "Content-Type": "application/json", 62 | } 63 | r, err := restyc.R().SetHeaders(header).SetBody(param).Post(url) 64 | if err != nil { 65 | return "", errors.Wrap(err, "get request error") 66 | } 67 | if r.StatusCode() != http.StatusOK { 68 | return "", errors.New(fmt.Sprintf("get request status: %d body: %s", r.StatusCode(), string(r.Body()))) 69 | } 70 | responseJson := stringx.BUnicodeToUtf8(r.Body()) 71 | return responseJson, nil 72 | } 73 | 74 | /*------------------------------ code below is webapi implement ------------------------------*/ 75 | 76 | // GetNodeInfo Get Node Info 77 | func GetNodeInfo() (*model.NodeInfo, error) { 78 | response, err := get(fmt.Sprintf("%s/node/%s", Host, strconv.Itoa(core.GetApp().NodeId()))) 79 | if err != nil { 80 | return nil, err 81 | } 82 | 83 | if gjson.Get(response, "status").String() != "success" { 84 | return nil, errors.New(gjson.Get(response, "message").String()) 85 | } 86 | value := gjson.Get(response, "data").String() 87 | if value == "" { 88 | return nil, errors.New("get data not found: " + response) 89 | } 90 | result := &model.NodeInfo{} 91 | err = json.Unmarshal([]byte(value), &result) 92 | if err != nil { 93 | return nil, err 94 | } 95 | return result, nil 96 | } 97 | 98 | // GetUserList Get User List 99 | func GetUserList() ([]*model.UserInfo, error) { 100 | response, err := get(fmt.Sprintf("%s/userList/%s", Host, strconv.Itoa(core.GetApp().NodeId()))) 101 | if err != nil { 102 | return nil, err 103 | } 104 | if gjson.Get(response, "status").String() != "success" { 105 | return nil, errors.New(stringx.UnicodeToUtf8(gjson.Get(response, "message").String())) 106 | } 107 | value := gjson.Get(response, "data").String() 108 | if value == "" { 109 | return nil, errors.New("get data not found: " + response) 110 | } 111 | var result []*model.UserInfo 112 | err = json.Unmarshal([]byte(value), &result) 113 | if err != nil { 114 | return nil, err 115 | } 116 | return result, nil 117 | } 118 | 119 | func PostAllUserTraffic(allUserTraffic []*model.UserTraffic) error { 120 | value, err := post(fmt.Sprintf("%s/userTraffic/%s", Host, strconv.Itoa(core.GetApp().NodeId())), 121 | string(langx.Must(func() (interface{}, error) { 122 | return json.Marshal(allUserTraffic) 123 | }).([]byte))) 124 | 125 | if err != nil { 126 | return err 127 | } 128 | if gjson.Get(value, "status").String() != "success" { 129 | return errors.New(gjson.Get(value, "message").String()) 130 | } 131 | return nil 132 | } 133 | 134 | func PostNodeOnline(nodeOnline []*model.NodeOnline) error { 135 | value, err := post(fmt.Sprintf("%s/nodeOnline/%s", Host, strconv.Itoa(core.GetApp().NodeId())), 136 | string(langx.Must(func() (interface{}, error) { 137 | return json.Marshal(nodeOnline) 138 | }).([]byte))) 139 | 140 | if err != nil { 141 | return err 142 | } 143 | 144 | if gjson.Get(value, "status").String() != "success" { 145 | return errors.New(stringx.UnicodeToUtf8(gjson.Get(value, "message").String())) 146 | } 147 | return nil 148 | } 149 | 150 | func PostNodeStatus(status model.NodeStatus) error { 151 | value, err := post(fmt.Sprintf("%s/nodeStatus/%s", Host, strconv.Itoa(core.GetApp().NodeId())), 152 | string(langx.Must(func() (interface{}, error) { 153 | return json.Marshal(status) 154 | }).([]byte))) 155 | 156 | if err != nil { 157 | return err 158 | } 159 | if gjson.Get(value, "status").String() != "success" { 160 | return errors.New(stringx.UnicodeToUtf8(gjson.Get(value, "message").String())) 161 | } 162 | return nil 163 | } 164 | 165 | // PostTrigger when user trigger audit rules then report 166 | func PostTrigger(trigger model.Trigger) error { 167 | value, err := post(fmt.Sprintf("%s/trigger/%s", Host, strconv.Itoa(core.GetApp().NodeId())), 168 | string(langx.Must(func() (interface{}, error) { 169 | return json.Marshal(trigger) 170 | }).([]byte))) 171 | 172 | if err != nil { 173 | return err 174 | } 175 | if gjson.Get(value, "status").String() != "success" { 176 | return errors.New(stringx.UnicodeToUtf8(gjson.Get(value, "message").String())) 177 | } 178 | return nil 179 | } 180 | 181 | // GetNodeRule Get Node Rule 182 | func GetNodeRule() (*model.Rule, error) { 183 | response, err := get(fmt.Sprintf("%s/nodeRule/%s", Host, strconv.Itoa(core.GetApp().NodeId()))) 184 | if err != nil { 185 | return nil, err 186 | } 187 | if gjson.Get(response, "status").String() != "success" { 188 | return nil, errors.New(stringx.UnicodeToUtf8(gjson.Get(response, "message").String())) 189 | } 190 | value := gjson.Get(response, "data").String() 191 | if value == "" { 192 | return nil, errors.New("get data not found: " + response) 193 | } 194 | result := new(model.Rule) 195 | err = json.Unmarshal([]byte(value), result) 196 | if err != nil { 197 | return nil, err 198 | } 199 | return result, nil 200 | } 201 | -------------------------------------------------------------------------------- /api/client/api_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/ProxyPanel/VNet-SSR/model" 8 | "github.com/sirupsen/logrus" 9 | ) 10 | 11 | func ExampleGetNodeInfo() { 12 | Host = "http://localhost" 13 | logrus.SetLevel(logrus.DebugLevel) 14 | result, _ := GetNodeInfo() 15 | fmt.Printf("value: %+v \n", result) 16 | //Output: 17 | } 18 | 19 | func ExampleGetUserList() { 20 | Host = "http://dash.kitami.ml" 21 | logrus.SetLevel(logrus.DebugLevel) 22 | result, _ := GetUserList() 23 | fmt.Printf("value: %+v\n", result) 24 | //Output: 25 | } 26 | 27 | func ExamplePostAllUserTraffic() { 28 | Host = "http://localhost" 29 | logrus.SetLevel(logrus.DebugLevel) 30 | PostAllUserTraffic([]*model.UserTraffic{ 31 | { 32 | 1, 200, 200, 0, 0, 33 | }, 34 | }) 35 | //Output: 36 | } 37 | 38 | func ExamplePostNodeOnline() { 39 | Host = "http://localhost" 40 | logrus.SetLevel(logrus.DebugLevel) 41 | PostNodeOnline([]*model.NodeOnline{ 42 | { 43 | 1, 44 | "192.168.1.1", 45 | }, 46 | }) 47 | //Output: 48 | } 49 | 50 | func ExamplePostNodeStatus() { 51 | Host = "http://localhost" 52 | logrus.SetLevel(logrus.DebugLevel) 53 | PostNodeStatus(model.NodeStatus{ 54 | CPU: "10%", 55 | MEM: "10%", 56 | DISK: "10", 57 | NET: "up: 50kb,down: 50kb", 58 | UPTIME: 2000, 59 | }) 60 | 61 | //Output: 62 | } 63 | 64 | func TestGetNodeRule(t *testing.T) { 65 | Host = "http://ss.local3.com" 66 | model, err := GetNodeRule() 67 | if err != nil { 68 | t.Fatal(fmt.Sprintf("%+v", err)) 69 | return 70 | } 71 | fmt.Printf("%+v\n", model) 72 | } 73 | -------------------------------------------------------------------------------- /api/server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | "github.com/ProxyPanel/VNet-SSR/common/log" 8 | "github.com/ProxyPanel/VNet-SSR/common/obfs" 9 | "github.com/ProxyPanel/VNet-SSR/core" 10 | "github.com/ProxyPanel/VNet-SSR/model" 11 | "github.com/ProxyPanel/VNet-SSR/service" 12 | "github.com/ProxyPanel/VNet-SSR/utils/goroutine" 13 | "github.com/ProxyPanel/VNet-SSR/utils/langx" 14 | "github.com/gin-gonic/gin" 15 | "github.com/pkg/errors" 16 | "io/ioutil" 17 | "net/http" 18 | "strconv" 19 | "strings" 20 | "sync" 21 | ) 22 | 23 | const ( 24 | START = iota 25 | CLOSE = iota 26 | ) 27 | 28 | var ( 29 | secret string 30 | httpServer *http.Server 31 | httpServerMutex sync.Locker = new(sync.Mutex) 32 | httpServerChan chan int = make(chan int, 2) 33 | ) 34 | 35 | func init() { 36 | go goroutine.Protect(func() { 37 | for { 38 | sig := <-httpServerChan 39 | switch sig { 40 | case START: 41 | StartServer(core.GetApp().NodeInfo().PushPort, core.GetApp().NodeInfo().Secret) 42 | case CLOSE: 43 | StopServer() 44 | } 45 | 46 | } 47 | }) 48 | } 49 | 50 | func SetSecret(s string) { 51 | secret = s 52 | } 53 | 54 | func secretCheck() gin.HandlerFunc { 55 | return func(c *gin.Context) { 56 | s := c.GetHeader("secret") 57 | if s == secret { 58 | c.Next() 59 | } else { 60 | c.Abort() 61 | fail(c, errors.New("secret check error")) 62 | } 63 | } 64 | } 65 | 66 | func detailLog() gin.HandlerFunc { 67 | return func(c *gin.Context) { 68 | body, _ := ioutil.ReadAll(c.Request.Body) 69 | c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) 70 | log.Info("%s,%s,%s", c.Request.Method, c.Request.RequestURI, body) 71 | c.Next() 72 | } 73 | } 74 | 75 | func StartServer(port int, s string) { 76 | httpServerMutex.Lock() 77 | defer httpServerMutex.Unlock() 78 | if httpServer != nil { 79 | log.Error("http server is not close") 80 | return 81 | } 82 | addr := fmt.Sprintf(":%v", port) 83 | SetSecret(s) 84 | log.Info("start server on %s secret %s", addr, s) 85 | r := InitRouter() 86 | httpServer = &http.Server{ 87 | Addr: addr, 88 | Handler: r, 89 | } 90 | 91 | go goroutine.Protect(func() { 92 | if err := httpServer.ListenAndServe(); err != nil { 93 | if strings.Contains(err.Error(), " Server closed") { 94 | return 95 | } 96 | panic(err) 97 | } 98 | }) 99 | } 100 | 101 | func StopServer() { 102 | httpServerMutex.Lock() 103 | defer httpServerMutex.Unlock() 104 | if err := httpServer.Shutdown(context.Background()); err != nil { 105 | log.Err(err) 106 | } 107 | httpServer = nil 108 | return 109 | } 110 | 111 | func InitRouter() *gin.Engine { 112 | r := gin.Default() 113 | r.Use(detailLog()) 114 | r.Use(secretCheck()) 115 | r1 := r.Group("/api") 116 | { 117 | r1.POST("/user/add", UserAdd) 118 | r1.POST("/user/del/:uid", UserDel) 119 | r1.POST("/user/edit", UserEdit) 120 | r1.GET("/user/list", UserList) 121 | } 122 | r2 := r.Group("/api/v2") 123 | { 124 | r2.POST("/user/del/list", UsersDel) 125 | r2.POST("/user/add/list", UsersAdd) 126 | r2.POST("/node/reload", NodeReload) 127 | } 128 | return r 129 | } 130 | 131 | 132 | func UsersAdd(c *gin.Context) { 133 | var users []*model.UserInfo 134 | if err := c.BindJSON(&users); err != nil { 135 | fail(c, err) 136 | return 137 | } 138 | 139 | if err := service.GetSSRManager().AddUsers(users); err != nil { 140 | fail(c, err) 141 | return 142 | } 143 | 144 | success(c) 145 | } 146 | 147 | func UsersDel(c *gin.Context) { 148 | var uids []int 149 | if err := c.ShouldBind(&uids); err != nil { 150 | fail(c, err) 151 | return 152 | } 153 | 154 | if err := service.GetSSRManager().DelUsers(uids); err != nil { 155 | fail(c, err) 156 | return 157 | } 158 | 159 | success(c) 160 | } 161 | 162 | func UserAdd(c *gin.Context) { 163 | var user model.UserInfo 164 | if err := c.ShouldBind(&user); err != nil { 165 | fail(c, err) 166 | return 167 | } 168 | 169 | if err := service.GetSSRManager().AddUser(&user); err != nil { 170 | fail(c, err) 171 | return 172 | } 173 | success(c) 174 | } 175 | 176 | func UserDel(c *gin.Context) { 177 | if err := service.GetSSRManager().DelUser(langx.FirstResult(strconv.Atoi, c.Param("uid")).(int)); err != nil { 178 | fail(c, err) 179 | return 180 | } 181 | success(c) 182 | } 183 | 184 | func UserEdit(c *gin.Context) { 185 | var user model.UserInfo 186 | if err := c.ShouldBind(&user); err != nil { 187 | fail(c, err) 188 | return 189 | } 190 | if err := service.GetSSRManager().EditUser(&user); err != nil { 191 | fail(c, err) 192 | return 193 | } 194 | success(c) 195 | 196 | } 197 | 198 | func UserList(c *gin.Context) { 199 | c.JSON(http.StatusOK, service.GetSSRManager().GetUserList()) 200 | } 201 | 202 | func NodeReload(c *gin.Context) { 203 | var nodeInfo model.NodeInfo 204 | if err := c.ShouldBind(&nodeInfo); err != nil { 205 | fail(c, err) 206 | return 207 | } 208 | core.GetApp().SetNodeInfo(&nodeInfo) 209 | core.GetApp().SetObfsProtocolService(obfs.NewObfsAuthChainData(nodeInfo.Protocol)) 210 | if nodeInfo.ClientLimit != 0 { 211 | log.Info("set client limit with %v", nodeInfo.ClientLimit) 212 | core.GetApp().GetObfsProtocolService().SetMaxClient(nodeInfo.ClientLimit) 213 | } else { 214 | log.Info("ignore client limit, because client_limit is zero, use default limit is 64") 215 | } 216 | if err := service.Reload(); err != nil { 217 | fail(c, err) 218 | return 219 | } 220 | success(c) 221 | httpServerChan <- CLOSE 222 | httpServerChan <- START 223 | } 224 | 225 | func fail(c *gin.Context, err error) { 226 | c.JSON(http.StatusOK, gin.H{"success": "false", "content": err.Error()}) 227 | } 228 | 229 | func success(c *gin.Context) { 230 | c.JSON(http.StatusOK, gin.H{"success": "true", "content": "sucess"}) 231 | } 232 | 233 | func successWithData(c *gin.Context, data interface{}) { 234 | c.JSON(http.StatusOK, gin.H{"success": "true", "content": "sucess", "data": data}) 235 | } 236 | -------------------------------------------------------------------------------- /api/server/server.http: -------------------------------------------------------------------------------- 1 | # For a quick start check out our HTTP Requests collection (Tools|HTTP Client|Open HTTP Requests Collection). 2 | # 3 | # Following HTTP Request Live Templates are available: 4 | # * 'gtrp' and 'gtr' create a GET request with or without query parameters; 5 | # * 'ptr' and 'ptrp' create a POST request with a simple or parameter-like body; 6 | # * 'mptr' and 'fptr' create a POST request to submit a form with a text or file field (multipart/form-data); 7 | ### 删除用户 8 | POST http://localhost:8081/api/user/del/4 9 | 10 | ### 添加用户 11 | 12 | POST http://localhost:8081/api/user/add 13 | Content-Type: application/json 14 | 15 | { 16 | "uid":4, 17 | "port":10003, 18 | "passwd":"@123", 19 | "limit":0 20 | } 21 | 22 | ### 编辑用户 23 | POST http://localhost:8081/api/user/edit 24 | Content-Type: application/json 25 | 26 | { 27 | "uid":1, 28 | "port":10002, 29 | "passwd":"@123", 30 | "speed_limit_per_user":500 31 | } 32 | 33 | ### 用户列表 34 | GET http://localhost:8081/api/user/list?id=1 35 | secret: 6dkiwc7c 36 | 37 | ### 增加多个用户 38 | POST http://localhost:8081/api/v2/user/add/list 39 | Content-Type: application/json 40 | secret: 6dkiwc7c 41 | 42 | [ 43 | { 44 | "uid":4, 45 | "port":10003, 46 | "passwd":"@123", 47 | "limit":0 48 | }, 49 | { 50 | "uid":5, 51 | "port":10004, 52 | "passwd":"@123", 53 | "limit":0 54 | } 55 | ] 56 | 57 | 58 | ### 删除多个用户 59 | POST http://localhost:8081/api/v2/user/del/list 60 | Content-Type: application/json 61 | secret: 6dkiwc7c 62 | 63 | [1,4] 64 | 65 | 66 | ### 重载配置 67 | POST http://localhost:8081/api/v2/node/reload 68 | Content-Type: application/json 69 | secret: 6dkiwc7c 70 | 71 | { 72 | "id":1, 73 | "port":"8085", 74 | "passwd":"killer", 75 | "method":"aes-128-cfb", 76 | "protocol":"auth_chain_a", 77 | "obfs":"http_simple", 78 | "protocol_param":"", 79 | "obfs_param":"", 80 | "push_port": 8081, 81 | "single":1, 82 | "secret":"6dkiwc7c" 83 | } 84 | 85 | ### 86 | -------------------------------------------------------------------------------- /api/server/server_test.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "os" 6 | "os/signal" 7 | "syscall" 8 | ) 9 | 10 | func ExampleStartServer() { 11 | gin.SetMode(gin.ReleaseMode) 12 | StartServer(300,"abc") 13 | c := make(chan os.Signal, 1) 14 | signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) 15 | <-c 16 | //Output: 17 | } 18 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | 2 | @echo off 3 | choice /C wl /M "windows,linux" 4 | if errorlevel 2 goto linux 5 | if errorlevel 1 goto windows 6 | :windows 7 | echo build windows amd64 8 | set CGO_ENABLED=0 9 | set GOOS=windows 10 | set GOARCH=amd64 11 | go build -ldflags "-s -w" -o vnet.exe .\cmd\shadowsocksr-server\main.go 12 | goto end 13 | 14 | :linux 15 | echo build linux amd64 16 | set CGO_ENABLED=0 17 | set GOOS=linux 18 | set GOARCH=amd64 19 | go build -ldflags "-s -w" -o vnet .\cmd\shadowsocksr-server\main.go 20 | goto end 21 | 22 | :end 23 | echo done~ -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | OUTPUT=vnet 4 | INPUT=cmd/shadowsocksr-server/main.go 5 | APP_VERSION= 6 | 7 | linux() { 8 | env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o bin/linux/$OUTPUT"_amd64" $INPUT 9 | upx --brute ./bin/linux/vnet_amd64 -o ./bin/linux/vnet 10 | } 11 | 12 | osx() { 13 | env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w" -o bin/osx/$OUTPUT"_amd64" $INPUT 14 | upx --brute ./bin/osx/vnet_amd64 -o ./bin/osx/vnet 15 | } 16 | 17 | windows() { 18 | env CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o bin/window/$OUTPUT"_amd64.exe" $INPUT 19 | upx --brute ./bin/windows/vnet_amd64 -o ./bin/windows/vnet 20 | } 21 | 22 | update_version() { 23 | sed -ie "s/APP_VERSION = \"[^\"]*\"/APP_VERSION = \"$APP_VERSION\"/" ./core/version.go 24 | } 25 | 26 | clear() { 27 | rm -rf ./bin/* 28 | } 29 | 30 | clear 31 | mkdir -p ./bin/out 32 | read -r -p "select target os: [linux/windows/osx]" option 33 | 34 | case $option in 35 | linux) 36 | linux 37 | ;; 38 | windows) 39 | windows 40 | ;; 41 | osx) 42 | osx 43 | ;; 44 | *) 45 | echo "invaild option" 46 | exit 1 47 | ;; 48 | esac 49 | 50 | -------------------------------------------------------------------------------- /cmd/shadowsocksr-server/BUILD: -------------------------------------------------------------------------------- 1 | load("//release/bazel:build.bzl", "foreign_go_binary") 2 | load("//release/bazel:gpg.bzl", "gpg_sign") 3 | load("//release/bazel:matrix.bzl", "SUPPORTED_MATRIX") 4 | load("//cmd/shadowsocksr-server:targets.bzl", "gen_targets") 5 | 6 | package(default_visibility=["//visibility:public"]) 7 | 8 | gen_targets(SUPPORTED_MATRIX) 9 | 10 | -------------------------------------------------------------------------------- /cmd/shadowsocksr-server/command/config.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import "reflect" 4 | 5 | const ( 6 | API_HOST = "api_host" 7 | HOST = "host" 8 | NODE_ID = "node_id" 9 | KEY = "key" 10 | ) 11 | 12 | type FlagSetting struct { 13 | Name string 14 | Default interface{} 15 | Usage string 16 | Example string 17 | Type reflect.Kind 18 | Required bool 19 | } 20 | 21 | var flagConfigs = []FlagSetting{ 22 | FlagSetting{ 23 | Type: reflect.String, 24 | Name: API_HOST, 25 | Usage: "api host example: http://localhost", 26 | Required: true, 27 | }, 28 | FlagSetting{ 29 | Type: reflect.String, 30 | Name: HOST, 31 | Usage: "host example: 0.0.0.0", 32 | Required: true, 33 | Default: "0.0.0.0", 34 | }, 35 | FlagSetting{ 36 | Type: reflect.Int, 37 | Name: NODE_ID, 38 | Usage: "node_id", 39 | Required: true, 40 | }, 41 | FlagSetting{ 42 | Type: reflect.String, 43 | Name: KEY, 44 | Usage: "key", 45 | Required: true, 46 | }, 47 | } 48 | -------------------------------------------------------------------------------- /cmd/shadowsocksr-server/command/help.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | // Copyright © 2019 NAME HERE 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | import ( 18 | "fmt" 19 | "os" 20 | "path/filepath" 21 | "reflect" 22 | 23 | "github.com/ProxyPanel/VNet-SSR/common/log" 24 | "github.com/ProxyPanel/VNet-SSR/core" 25 | "github.com/spf13/cobra" 26 | "github.com/spf13/viper" 27 | ) 28 | 29 | var rootCmd = &cobra.Command{ 30 | Use: filepath.Base(os.Args[0]), 31 | Short: fmt.Sprintf("vnet version %s\r\n", core.APP_VERSION), 32 | Long: fmt.Sprintf("vnet webapi version with ProxyPanel, current version: %s", core.APP_VERSION), 33 | } 34 | 35 | func init() { 36 | cobra.OnInitialize(initConfig) 37 | rootCmd.Flags().String("config", "config.json", "config file default: config.json") 38 | _ = viper.BindPFlag("config", rootCmd.Flags().Lookup("config")) 39 | 40 | // add version menu 41 | rootCmd.SetVersionTemplate(core.APP_VERSION) 42 | 43 | for _, item := range flagConfigs { 44 | if item.Default != nil { 45 | switch item.Type { 46 | case reflect.String: 47 | rootCmd.Flags().String(item.Name, item.Default.(string), item.Usage) 48 | case reflect.Int: 49 | rootCmd.Flags().Int(item.Name, item.Default.(int), item.Usage) 50 | case reflect.Bool: 51 | rootCmd.Flags().Bool(item.Name, item.Default.(bool), item.Usage) 52 | } 53 | } else { 54 | switch item.Type { 55 | case reflect.String: 56 | rootCmd.Flags().String(item.Name, "", item.Usage) 57 | case reflect.Int: 58 | rootCmd.Flags().Int(item.Name, 0, item.Usage) 59 | case reflect.Bool: 60 | rootCmd.Flags().Bool(item.Name, false, item.Usage) 61 | } 62 | } 63 | _ = viper.BindPFlag(item.Name, rootCmd.Flags().Lookup(item.Name)) 64 | } 65 | } 66 | 67 | // initConfig reads in config file and ENV variables if set. 68 | func initConfig() { 69 | viper.SetConfigFile(viper.GetString("config")) 70 | // If a config file is found, read it in. 71 | if err := viper.ReadInConfig(); err == nil { 72 | abspath, err := filepath.Abs(viper.ConfigFileUsed()) 73 | if err != nil { 74 | panic(err) 75 | } 76 | fmt.Println("using config file:", abspath) 77 | } 78 | } 79 | 80 | func Execute(fn func()) { 81 | rootCmd.Run = runWrap(fn) 82 | if err := rootCmd.Execute(); err != nil { 83 | fmt.Println(err) 84 | os.Exit(1) 85 | } 86 | } 87 | 88 | func checkRequired() bool { 89 | for _, item := range flagConfigs { 90 | if item.Required { 91 | switch item.Type { 92 | case reflect.String: 93 | if viper.GetString(item.Name) == "" { 94 | log.Warn("miss param:" + item.Name) 95 | return false 96 | } 97 | case reflect.Int: 98 | if viper.GetInt(item.Name) == 0 { 99 | log.Warn("miss param:" + item.Name) 100 | return false 101 | } 102 | } 103 | } 104 | } 105 | return true 106 | } 107 | 108 | func runWrap(fn func()) func(cmd *cobra.Command, args []string) { 109 | return func(cmd *cobra.Command, args []string) { 110 | if !checkRequired() { 111 | _ = cmd.Help() 112 | return 113 | } 114 | fn() 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /cmd/shadowsocksr-server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/ProxyPanel/VNet-SSR/api/client" 6 | "github.com/ProxyPanel/VNet-SSR/api/server" 7 | "github.com/ProxyPanel/VNet-SSR/cmd/shadowsocksr-server/command" 8 | "github.com/ProxyPanel/VNet-SSR/common/log" 9 | "github.com/ProxyPanel/VNet-SSR/common/obfs" 10 | "github.com/ProxyPanel/VNet-SSR/core" 11 | "github.com/ProxyPanel/VNet-SSR/service" 12 | "github.com/ProxyPanel/VNet-SSR/utils/addrx" 13 | "github.com/ProxyPanel/VNet-SSR/utils/osx" 14 | "github.com/sirupsen/logrus" 15 | "github.com/spf13/viper" 16 | ) 17 | 18 | func main() { 19 | logrus.SetLevel(logrus.InfoLevel) 20 | ip, err := addrx.GetPublicIp() 21 | if err != nil { 22 | panic(err) 23 | } 24 | command.Execute(func() { 25 | if err := core.GetApp().Init(); err != nil { 26 | panic(err) 27 | } 28 | core.GetApp().SetApiHost(viper.GetString(command.API_HOST)) 29 | core.GetApp().SetNodeId(viper.GetInt(command.NODE_ID)) 30 | core.GetApp().SetKey(viper.GetString(command.KEY)) 31 | core.GetApp().SetHost(viper.GetString(command.HOST)) 32 | core.GetApp().SetPublicIP(ip) 33 | if core.GetApp().GetPublicIP() == "" { 34 | panic("get public ip error,please try align") 35 | } 36 | log.Info("get public ip %s", core.GetApp().GetPublicIP()) 37 | 38 | nodeInfo, err := client.GetNodeInfo() 39 | if err != nil { 40 | logrus.Fatal(err) 41 | } 42 | core.GetApp().SetNodeInfo(nodeInfo) 43 | logrus.WithFields(logrus.Fields{ 44 | "nodeInfo": fmt.Sprintf("%+v", nodeInfo), 45 | }).Info("get node info success") 46 | 47 | core.GetApp().SetObfsProtocolService(obfs.NewObfsAuthChainData(nodeInfo.Protocol)) 48 | if nodeInfo.ClientLimit != 0 { 49 | log.Info("set client limit with %v", nodeInfo.ClientLimit) 50 | core.GetApp().GetObfsProtocolService().SetMaxClient(nodeInfo.ClientLimit) 51 | } else { 52 | log.Info("ignore client limit, because client_limit is zero, use default limit is 64") 53 | } 54 | 55 | if err := service.Start(); err != nil { 56 | panic(err) 57 | return 58 | } 59 | 60 | server.StartServer(nodeInfo.PushPort, nodeInfo.Secret) 61 | osx.WaitSignal() 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /cmd/shadowsocksr-server/targets.bzl: -------------------------------------------------------------------------------- 1 | load("//release/bazel:build.bzl", "foreign_go_binary") 2 | load("//release/bazel:gpg.bzl", "gpg_sign") 3 | 4 | def gen_targets(matrix): 5 | pkg = "github.com/ProxyPanel/VNet-SSR/cmd/shadowsocksr-server" 6 | output = "vnet" 7 | 8 | for (os, arch) in matrix: 9 | bin_name = "vnet_" + os + "_" + arch 10 | foreign_go_binary( 11 | name = bin_name, 12 | pkg = pkg, 13 | output = output, 14 | os = os, 15 | arch = arch, 16 | ) 17 | 18 | gpg_sign( 19 | name = bin_name + "_sig", 20 | base = ":" + bin_name, 21 | ) 22 | 23 | if os in ["windows"]: 24 | bin_name = "vnet_" + os + "_" + arch + "_nowindow" 25 | foreign_go_binary( 26 | name = bin_name, 27 | pkg = pkg, 28 | output = "w" + output, 29 | os = os, 30 | arch = arch, 31 | ld = "-H windowsgui", 32 | ) 33 | 34 | gpg_sign( 35 | name = bin_name + "_sig", 36 | base = ":" + bin_name, 37 | ) 38 | 39 | if arch in ["mips", "mipsle"]: 40 | bin_name = "vnet_" + os + "_" + arch + "_softfloat" 41 | foreign_go_binary( 42 | name = bin_name, 43 | pkg = pkg, 44 | output = output+"_softfloat", 45 | os = os, 46 | arch = arch, 47 | mips = "softfloat", 48 | ) 49 | 50 | gpg_sign( 51 | name = bin_name + "_sig", 52 | base = ":" + bin_name, 53 | ) 54 | 55 | if arch in ["arm"]: 56 | bin_name = "vnet_" + os + "_" + arch + "_armv7" 57 | foreign_go_binary( 58 | name = bin_name, 59 | pkg = pkg, 60 | output = output+"_armv7", 61 | os = os, 62 | arch = arch, 63 | arm = "7", 64 | ) 65 | 66 | gpg_sign( 67 | name = bin_name + "_sig", 68 | base = ":" + bin_name, 69 | ) 70 | 71 | bin_name = "vnet_" + os + "_" + arch + "_armv6" 72 | foreign_go_binary( 73 | name = bin_name, 74 | pkg = pkg, 75 | output = output+"_armv6", 76 | os = os, 77 | arch = arch, 78 | arm = "6", 79 | ) 80 | 81 | gpg_sign( 82 | name = bin_name + "_sig", 83 | base = ":" + bin_name, 84 | ) 85 | -------------------------------------------------------------------------------- /cmd/signal_test/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/ProxyPanel/VNet-SSR/utils/osx" 4 | 5 | func main(){ 6 | println("start...") 7 | println(osx.WaitSignal().String()) 8 | } 9 | -------------------------------------------------------------------------------- /cmd/test/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | server2 "github.com/ProxyPanel/VNet-SSR/proxy/server" 5 | "github.com/ProxyPanel/VNet-SSR/service" 6 | "github.com/ProxyPanel/VNet-SSR/utils/osx" 7 | "github.com/sirupsen/logrus" 8 | _ "net/http/pprof" 9 | "os" 10 | "os/signal" 11 | "syscall" 12 | ) 13 | 14 | func main() { 15 | //go func() { 16 | // log.Println(http.ListenAndServe("0.0.0.0:445", nil)) 17 | //}() 18 | Test2() 19 | } 20 | 21 | func Test2() { 22 | logrus.SetLevel(logrus.InfoLevel) 23 | TestShadowsocksr() 24 | // Wait for interrupt signal to gracefully shutdown the server with 25 | // a timeout of 5 seconds. 26 | quit := make(chan os.Signal) 27 | // kill (no param) default send syscall.SIGTERM 28 | // kill -2 is syscall.SIGINT 29 | // kill -9 is syscall.SIGKILL but can't be catch, so don't need add it 30 | signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) 31 | <-quit 32 | } 33 | 34 | func TestShadowsocksr() { 35 | //service.GetRuleService().Load(&model.Rule{ 36 | // Model: service.RULE_MODE_REJECT, 37 | // Rules: []model.RuleItem{ 38 | // { 39 | // Id: 1, 40 | // Type: service.RULE_TYPE_REG, 41 | // Pattern: "(.*\\.||)(dafahao|minghui|dongtaiwang|epochtimes|ntdtv|falundafa|wujieliulan|zhengjian)\\.(org|com|net)", 42 | // }, 43 | // }, 44 | //}) 45 | //service.GetLimitInstance().Set(3718, 1024*400) 46 | 47 | //logrus.SetLevel(logrus.DebugLevel) 48 | server := server2.ShadowsocksRProxy{ 49 | Port: 12500, 50 | Host: "0.0.0.0", 51 | Method: "aes-128-gcm", 52 | Password: "killer", 53 | Protocol: "origin", 54 | Obfs: "plain", 55 | ObfsParam: "", 56 | Single: 0, 57 | ShadowsocksRArgs: &server2.ShadowsocksRArgs{}, 58 | HostFirewall: service.GetRuleService(), 59 | ILimiter: service.GetLimitInstance(), 60 | } 61 | //server.AddUser(1200, "killer") 62 | if err := server.Start();err != nil{ 63 | panic(err) 64 | return 65 | } 66 | //// Wait for interrupt signal to gracefully shutdown the server with 67 | //// a timeout of 5 seconds. 68 | //quit := make(chan os.Signal) 69 | //// kill (no param) default send syscall.SIGTERM 70 | //// kill -2 is syscall.SIGINT 71 | //// kill -9 is syscall.SIGKILL but can't be catch, so don't need add it 72 | //signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGKILL) 73 | //<-quit 74 | osx.WaitSignal() 75 | } 76 | -------------------------------------------------------------------------------- /common/cache/cache.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "runtime" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | // Cache store element with a expired time 10 | type Cache struct { 11 | *cache 12 | } 13 | 14 | type cache struct { 15 | mapping sync.Map 16 | janitor *janitor 17 | } 18 | 19 | type element struct { 20 | Expired time.Time 21 | Payload interface{} 22 | } 23 | 24 | // Put element in Cache with its ttl 25 | func (c *cache) Put(key interface{}, payload interface{}, ttl time.Duration) { 26 | c.mapping.Store(key, &element{ 27 | Payload: payload, 28 | Expired: time.Now().Add(ttl), 29 | }) 30 | } 31 | 32 | // Get element in Cache, and drop when it expired 33 | func (c *cache) Get(key interface{}) interface{} { 34 | item, exist := c.mapping.Load(key) 35 | if !exist { 36 | return nil 37 | } 38 | elm := item.(*element) 39 | // expired 40 | if time.Since(elm.Expired) > 0 { 41 | c.mapping.Delete(key) 42 | return nil 43 | } 44 | return elm.Payload 45 | } 46 | 47 | func (c *cache) Range(callback func(key, value interface{})) { 48 | c.mapping.Range(func(k, v interface{}) bool { 49 | elm := v.(*element) 50 | if time.Since(elm.Expired) > 0 { 51 | c.mapping.Delete(k) 52 | } else { 53 | callback(k, elm.Payload) 54 | } 55 | return true 56 | }) 57 | } 58 | 59 | func (c *cache) cleanup() { 60 | c.mapping.Range(func(k, v interface{}) bool { 61 | // key := k.(string) 62 | elm := v.(*element) 63 | if time.Since(elm.Expired) > 0 { 64 | c.mapping.Delete(k) 65 | } 66 | return true 67 | }) 68 | } 69 | 70 | func (c *cache) Size() int { 71 | var result int 72 | c.Range(func(k, v interface{}) { 73 | result++ 74 | }) 75 | return result 76 | } 77 | 78 | type janitor struct { 79 | interval time.Duration 80 | stop chan struct{} 81 | } 82 | 83 | func (j *janitor) process(c *cache) { 84 | ticker := time.NewTicker(j.interval) 85 | for { 86 | select { 87 | case <-ticker.C: 88 | c.cleanup() 89 | case <-j.stop: 90 | ticker.Stop() 91 | return 92 | } 93 | } 94 | } 95 | 96 | func stopJanitor(c *Cache) { 97 | c.janitor.stop <- struct{}{} 98 | } 99 | 100 | // New return *Cache 101 | func New(interval time.Duration) *Cache { 102 | j := &janitor{ 103 | interval: interval, 104 | stop: make(chan struct{}), 105 | } 106 | c := &cache{janitor: j} 107 | go j.process(c) 108 | C := &Cache{c} 109 | // this is very interesting,it worth be deep learning 110 | runtime.SetFinalizer(C, stopJanitor) 111 | return C 112 | } 113 | -------------------------------------------------------------------------------- /common/cache/cache_test.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "runtime" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestCache_Basic(t *testing.T) { 10 | interval := 200 * time.Millisecond 11 | ttl := 20 * time.Millisecond 12 | c := New(interval) 13 | c.Put("int", 1, ttl) 14 | c.Put("string", "a", ttl) 15 | 16 | i := c.Get("int") 17 | if i.(int) != 1 { 18 | t.Error("should recv 1") 19 | } 20 | 21 | s := c.Get("string") 22 | if s.(string) != "a" { 23 | t.Error("should recv 'a'") 24 | } 25 | } 26 | 27 | func TestCache_TTL(t *testing.T) { 28 | interval := 200 * time.Millisecond 29 | ttl := 20 * time.Millisecond 30 | c := New(interval) 31 | c.Put("int", 1, ttl) 32 | 33 | i := c.Get("int") 34 | if i.(int) != 1 { 35 | t.Error("should recv 1") 36 | } 37 | 38 | time.Sleep(ttl * 2) 39 | i = c.Get("int") 40 | if i != nil { 41 | t.Error("should recv nil") 42 | } 43 | } 44 | 45 | func TestCache_AutoCleanup(t *testing.T) { 46 | interval := 10 * time.Millisecond 47 | ttl := 15 * time.Millisecond 48 | c := New(interval) 49 | c.Put("int", 1, ttl) 50 | 51 | time.Sleep(ttl * 2) 52 | i := c.Get("int") 53 | if i != nil { 54 | t.Error("should recv nil") 55 | } 56 | } 57 | 58 | func TestCache_AutoGC(t *testing.T) { 59 | sign := make(chan struct{}) 60 | go func() { 61 | interval := 10 * time.Millisecond 62 | ttl := 15 * time.Millisecond 63 | c := New(interval) 64 | c.Put("int", 1, ttl) 65 | sign <- struct{}{} 66 | }() 67 | 68 | <-sign 69 | runtime.GC() 70 | } 71 | -------------------------------------------------------------------------------- /common/cache/lru_cache.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "runtime" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type LRU struct { 10 | *lrucache 11 | } 12 | 13 | type lrucache struct { 14 | mapping sync.Map 15 | lruJanitor *lruJanitor 16 | TTL time.Duration 17 | } 18 | 19 | type lruelement struct { 20 | Expired time.Time 21 | Payload interface{} 22 | TTL time.Duration 23 | } 24 | 25 | func (l *lrucache) Put(key interface{}, payload interface{}) { 26 | l.mapping.Store(key, &lruelement{ 27 | Payload: payload, 28 | Expired: time.Now().Add(l.TTL), 29 | TTL: l.TTL, 30 | }) 31 | } 32 | 33 | func (l *lrucache) Get(key interface{}) interface{} { 34 | item, exist := l.mapping.Load(key) 35 | if !exist { 36 | return nil 37 | } 38 | elm := item.(*lruelement) 39 | // expired 40 | if time.Since(elm.Expired) > 0 { 41 | l.mapping.Delete(key) 42 | return nil 43 | } 44 | // lru strategy 45 | elm.Expired = time.Now().Add(elm.TTL) 46 | l.mapping.Store(key, elm) 47 | return elm.Payload 48 | } 49 | 50 | func (l *lrucache) Delete(key interface{}) { 51 | l.mapping.Delete(key) 52 | } 53 | 54 | func (l *lrucache) IsExist(key interface{}) bool { 55 | _, exist := l.mapping.Load(key) 56 | return exist 57 | } 58 | 59 | func (l *lrucache) First() interface{} { 60 | var result interface{} = nil 61 | l.mapping.Range(func(key, value interface{}) bool { 62 | result = key 63 | return false 64 | }) 65 | return result 66 | } 67 | 68 | func (l *lrucache) Len() int { 69 | var result int = 0 70 | l.mapping.Range(func(key, valye interface{}) bool { 71 | result++ 72 | return true 73 | }) 74 | return result 75 | } 76 | 77 | func (l *lrucache) Clean() { 78 | l.mapping.Range(func(k, v interface{}) bool { 79 | // key := k.(string) 80 | elm := v.(*lruelement) 81 | if time.Since(elm.Expired) > 0 { 82 | l.mapping.Delete(k) 83 | } 84 | return true 85 | }) 86 | } 87 | 88 | type lruJanitor struct { 89 | interval time.Duration 90 | stop chan struct{} 91 | } 92 | 93 | func (j *lruJanitor) process(c *lrucache) { 94 | ticker := time.NewTicker(j.interval) 95 | for { 96 | select { 97 | case <-ticker.C: 98 | c.Clean() 99 | case <-j.stop: 100 | ticker.Stop() 101 | return 102 | } 103 | } 104 | } 105 | 106 | func stopLruJanitor(c *LRU) { 107 | c.lruJanitor.stop <- struct{}{} 108 | } 109 | 110 | // New return *Cache 111 | func NewLruCache(interval time.Duration) *LRU { 112 | j := &lruJanitor{ 113 | interval: interval, 114 | stop: make(chan struct{}), 115 | } 116 | c := &lrucache{ 117 | lruJanitor: j, 118 | TTL: interval, 119 | } 120 | go j.process(c) 121 | lru := &LRU{c} 122 | // this is very interesting,it worth be deep learning 123 | runtime.SetFinalizer(lru, stopLruJanitor) 124 | return lru 125 | } 126 | -------------------------------------------------------------------------------- /common/ciphers/aead/aead.go: -------------------------------------------------------------------------------- 1 | package aead 2 | 3 | import "crypto/cipher" 4 | 5 | type IAEADCipher interface { 6 | KeySize() int 7 | SaltSize() int 8 | NonceSize() int 9 | NewAEAD(key []byte, salt []byte, decryptOrEncrypt int) (cipher.AEAD, error) 10 | } 11 | 12 | var aeadCiphers = make(map[string]IAEADCipher) 13 | 14 | func registerAEADCiphers(method string, c IAEADCipher) { 15 | aeadCiphers[method] = c 16 | } 17 | 18 | func GetAEADCiphers() map[string]IAEADCipher { 19 | return aeadCiphers 20 | } 21 | 22 | func GetAEADCipher(method string) IAEADCipher { 23 | return aeadCiphers[method] 24 | } 25 | -------------------------------------------------------------------------------- /common/ciphers/aead/aes_gcm.go: -------------------------------------------------------------------------------- 1 | package aead 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "github.com/ProxyPanel/VNet-SSR/utils/shadowsocksx" 7 | ) 8 | 9 | func init() { 10 | registerAEADCiphers("aes-256-gcm", &aesGcm{32, 32, 12, 16}) 11 | registerAEADCiphers("aes-192-gcm", &aesGcm{24, 24, 12, 16}) 12 | registerAEADCiphers("aes-128-gcm", &aesGcm{16, 16, 12, 16}) 13 | } 14 | 15 | type aesGcm struct { 16 | keySize int 17 | saltSize int 18 | nonceSize int 19 | tagSize int 20 | } 21 | 22 | func (a *aesGcm) KeySize() int { 23 | return a.keySize 24 | } 25 | 26 | func (a *aesGcm) SaltSize() int { 27 | return a.saltSize 28 | } 29 | 30 | func (a *aesGcm) NonceSize() int { 31 | return a.nonceSize 32 | } 33 | 34 | func (a *aesGcm) NewAEAD(key []byte, salt []byte, _ int) (cipher.AEAD, error) { 35 | subkey := make([]byte, a.KeySize()) 36 | _ = shadowsocksx.HKDF_SHA1(key, salt, []byte("ss-subkey"), subkey) 37 | blk, err := aes.NewCipher(subkey) 38 | if err != nil { 39 | return nil, err 40 | } 41 | return cipher.NewGCM(blk) 42 | } 43 | -------------------------------------------------------------------------------- /common/ciphers/aead/chacha20_ietf_poly1305.go: -------------------------------------------------------------------------------- 1 | package aead 2 | 3 | import ( 4 | "crypto/cipher" 5 | "github.com/ProxyPanel/VNet-SSR/utils/shadowsocksx" 6 | 7 | "golang.org/x/crypto/chacha20poly1305" 8 | ) 9 | 10 | func init() { 11 | registerAEADCiphers("chacha20-ietf-poly1305", &chacha20IetfPoly1305{32, 32, 12, 16}) 12 | } 13 | 14 | type chacha20IetfPoly1305 struct { 15 | keySize int 16 | saltSize int 17 | nonceSize int 18 | tagSize int 19 | } 20 | 21 | func (c *chacha20IetfPoly1305) KeySize() int { 22 | return c.keySize 23 | } 24 | 25 | func (c *chacha20IetfPoly1305) SaltSize() int { 26 | return c.saltSize 27 | } 28 | 29 | func (c *chacha20IetfPoly1305) NonceSize() int { 30 | return c.nonceSize 31 | } 32 | 33 | func (c *chacha20IetfPoly1305) NewAEAD(key []byte, salt []byte, _ int) (cipher.AEAD, error) { 34 | subkey := make([]byte, c.KeySize()) 35 | shadowsocksx.HKDF_SHA1(key, salt, []byte("ss-subkey"), subkey) 36 | return chacha20poly1305.New(subkey) 37 | } 38 | -------------------------------------------------------------------------------- /common/ciphers/aead/cipher_conn.go: -------------------------------------------------------------------------------- 1 | package aead 2 | 3 | import ( 4 | "crypto/cipher" 5 | "io" 6 | "net" 7 | 8 | "bytes" 9 | "crypto/md5" 10 | "crypto/rand" 11 | 12 | "github.com/ProxyPanel/VNet-SSR/common/log" 13 | ) 14 | 15 | func GetAEADConnCipher(method string) func(string, net.Conn) (net.Conn, error) { 16 | c, ok := aeadCiphers[method] 17 | if !ok { 18 | return nil 19 | } 20 | return func(password string, conn net.Conn) (net.Conn, error) { 21 | salt := make([]byte, c.SaltSize()) 22 | if _, err := io.ReadFull(rand.Reader, salt); err != nil { 23 | return nil, err 24 | } 25 | sc := &aeadConn{ 26 | Conn: conn, 27 | IAEADCipher: c, 28 | key: evpBytesToKey(password, c.KeySize()), 29 | wNonce: make([]byte, c.NonceSize()), 30 | rNonce: make([]byte, c.NonceSize()), 31 | readBuffer: new(bytes.Buffer), 32 | } 33 | var err error 34 | sc.Encrypter, err = sc.NewAEAD(sc.key, salt, 0) 35 | _, err = conn.Write(salt) 36 | return sc, err 37 | } 38 | } 39 | 40 | const DataMaxSize = 0x3FFF 41 | 42 | type aeadConn struct { 43 | net.Conn 44 | IAEADCipher 45 | key []byte 46 | rNonce []byte 47 | wNonce []byte 48 | readBuffer *bytes.Buffer 49 | Encrypter cipher.AEAD 50 | Decrypter cipher.AEAD 51 | } 52 | 53 | func (a *aeadConn) GetKey() []byte { 54 | return a.key 55 | } 56 | 57 | func (a *aeadConn) Read(b []byte) (n int, err error) { 58 | if a.readBuffer.Len() > 0 { 59 | n, err = a.readBuffer.Read(b) 60 | return 61 | } 62 | if a.Decrypter == nil { 63 | salt := make([]byte, a.SaltSize()) 64 | if _, err = io.ReadFull(a.Conn, salt); err != nil { 65 | return 66 | } 67 | a.Decrypter, err = a.NewAEAD(a.key, salt, 1) 68 | if err != nil { 69 | log.Error("[AEAD Conn] init decrypter failed: %v", err) 70 | return 0, err 71 | } 72 | } 73 | var overHead = a.Decrypter.Overhead() 74 | buf := make([]byte, 2+overHead+DataMaxSize+overHead) 75 | dataBuf := buf[:2+a.Decrypter.Overhead()] 76 | _, err = io.ReadFull(a.Conn, dataBuf) 77 | if err != nil { 78 | return 79 | } 80 | 81 | _, err = a.Decrypter.Open(dataBuf[:0], a.rNonce, dataBuf, nil) 82 | increment(a.rNonce) 83 | if err != nil { 84 | return 0, err 85 | } 86 | 87 | size := (int(dataBuf[0])<<8 + int(dataBuf[1])) & DataMaxSize 88 | 89 | dataBuf = buf[:size+a.Decrypter.Overhead()] 90 | _, err = io.ReadFull(a.Conn, dataBuf) 91 | if err != nil { 92 | return 0, err 93 | } 94 | if len(b) >= size { 95 | n = size 96 | _, err = a.Decrypter.Open(b[:0], a.rNonce, dataBuf, nil) 97 | } else { 98 | _, err = a.Decrypter.Open(dataBuf[:0], a.rNonce, dataBuf, nil) 99 | if err == nil { 100 | n = copy(b, dataBuf[:len(b)]) 101 | a.readBuffer.Write(dataBuf[n:size]) 102 | } 103 | } 104 | increment(a.rNonce) 105 | return 106 | } 107 | 108 | func (a *aeadConn) Write(b []byte) (n int, err error) { 109 | r := bytes.NewBuffer(b) 110 | var rn int 111 | var overHead = a.Encrypter.Overhead() 112 | for { 113 | buf := make([]byte, 2+overHead+DataMaxSize+overHead) 114 | dataBuf := buf[2+overHead : 2+overHead+DataMaxSize] 115 | rn, err = r.Read(dataBuf) 116 | if err != nil { 117 | if err == io.EOF { 118 | err = nil 119 | } 120 | break 121 | } 122 | if rn > 0 { 123 | n += rn 124 | buf = buf[:2+overHead+rn+overHead] 125 | dataBuf = dataBuf[:rn] 126 | buf[0], buf[1] = byte(rn>>8), byte(rn&0xffff) 127 | a.Encrypter.Seal(buf[:0], a.wNonce, buf[:2], nil) 128 | increment(a.wNonce) 129 | 130 | a.Encrypter.Seal(dataBuf[:0], a.wNonce, dataBuf, nil) 131 | increment(a.wNonce) 132 | 133 | _, ew := a.Conn.Write(buf) 134 | if ew != nil { 135 | err = ew 136 | break 137 | } 138 | } else { 139 | break 140 | } 141 | } 142 | return n, err 143 | } 144 | 145 | func evpBytesToKey(password string, keyLen int) (key []byte) { 146 | const md5Len = 16 147 | 148 | cnt := (keyLen-1)/md5Len + 1 149 | m := make([]byte, cnt*md5Len) 150 | copy(m, MD5([]byte(password))) 151 | d := make([]byte, md5Len+len(password)) 152 | start := 0 153 | for i := 1; i < cnt; i++ { 154 | start += md5Len 155 | copy(d, m[start-md5Len:start]) 156 | copy(d[md5Len:], password) 157 | copy(m[start:], MD5(d)) 158 | } 159 | return m[:keyLen] 160 | } 161 | 162 | func MD5(data []byte) []byte { 163 | hash := md5.New() 164 | hash.Write(data) 165 | return hash.Sum(nil) 166 | } 167 | 168 | func increment(b []byte) { 169 | for i := range b { 170 | b[i]++ 171 | if b[i] != 0 { 172 | return 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /common/ciphers/aead/cipher_packet.go: -------------------------------------------------------------------------------- 1 | package aead 2 | 3 | import ( 4 | "crypto/rand" 5 | "io" 6 | "net" 7 | "sync" 8 | 9 | "github.com/pkg/errors" 10 | 11 | "github.com/ProxyPanel/VNet-SSR/common/log" 12 | "github.com/ProxyPanel/VNet-SSR/common/pool" 13 | ) 14 | 15 | const MAX_PACKET_SIZE = 65507 16 | 17 | var _zerononce [128]byte 18 | var ErrShortPacket = errors.New("short packet") 19 | 20 | type aeadPacket struct { 21 | net.PacketConn 22 | IAEADCipher 23 | sync.Mutex 24 | key []byte 25 | buf []byte 26 | } 27 | 28 | func GetAEADPacketCiphers(method string) func(string, net.PacketConn) (net.PacketConn, error) { 29 | c, ok := aeadCiphers[method] 30 | if !ok { 31 | return nil 32 | } 33 | return func(password string, packCon net.PacketConn) (net.PacketConn, error) { 34 | salt := make([]byte, c.SaltSize()) 35 | if _, err := io.ReadFull(rand.Reader, salt); err != nil { 36 | return nil, err 37 | } 38 | ap := &aeadPacket{ 39 | PacketConn: packCon, 40 | IAEADCipher: c, 41 | key: evpBytesToKey(password, c.KeySize()), 42 | buf: pool.GetBufBySize(MAX_PACKET_SIZE), 43 | } 44 | return ap, nil 45 | } 46 | } 47 | func (c *aeadPacket) GetKey() []byte { 48 | return c.key 49 | } 50 | func (c *aeadPacket) WriteTo(data []byte, addr net.Addr) (int, error) { 51 | c.Lock() 52 | defer c.Unlock() 53 | saltSize := c.SaltSize() 54 | salt := c.buf[:saltSize] 55 | if _, err := io.ReadFull(rand.Reader, salt); err != nil { 56 | return 0, err 57 | } 58 | 59 | aead, err := c.NewAEAD(c.key, salt, 0) 60 | 61 | if err != nil { 62 | return 0, err 63 | } 64 | if MAX_PACKET_SIZE < c.SaltSize()+len(data)+aead.Overhead() { 65 | return 0, errors.WithStack(io.ErrShortBuffer) 66 | } 67 | b := aead.Seal(c.buf[saltSize:saltSize], _zerononce[:aead.NonceSize()], data, nil) 68 | _, err = c.PacketConn.WriteTo(c.buf[:saltSize+len(b)], addr) 69 | return len(b), err 70 | } 71 | 72 | func (c *aeadPacket) ReadFrom(b []byte) (int, net.Addr, error) { 73 | n, addr, err := c.PacketConn.ReadFrom(b) 74 | if err != nil { 75 | return n, addr, err 76 | } 77 | saltSize := c.SaltSize() 78 | if len(b) < saltSize { 79 | return 0, nil, ErrShortPacket 80 | } 81 | salt := b[:saltSize] 82 | aead, err := c.NewAEAD(c.key, salt, 1) 83 | if err != nil { 84 | return 0, nil, err 85 | } 86 | 87 | if len(b) < saltSize+aead.Overhead() { 88 | return 0, nil, ErrShortPacket 89 | } 90 | 91 | if saltSize+len(c.buf)+aead.Overhead() < len(b) { 92 | return 0, nil, io.ErrShortBuffer 93 | } 94 | result, err := aead.Open(c.buf[:0], _zerononce[:aead.NonceSize()], b[saltSize:n], nil) 95 | if err != nil { 96 | log.Err(err) 97 | return n, addr, err 98 | } 99 | copy(b, result) 100 | return len(result), addr, err 101 | } 102 | -------------------------------------------------------------------------------- /common/ciphers/block/aes_cbc.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | ) 7 | 8 | func init() { 9 | registerBlockCiphers("aes-128-cbc", &aes_cbc{16, 16}) 10 | } 11 | 12 | type aes_cbc struct { 13 | keyLen int 14 | ivLen int 15 | } 16 | 17 | func (a *aes_cbc) KeyLen() int { 18 | return a.keyLen 19 | } 20 | func (a *aes_cbc) IVLen() int { 21 | return a.ivLen 22 | } 23 | func (a *aes_cbc) NewBlock(key, iv []byte, decryptOrEncrypt int) (cipher.BlockMode, error) { 24 | block, err := aes.NewCipher(key) 25 | if err != nil { 26 | return nil, err 27 | } 28 | if decryptOrEncrypt == 0 { 29 | return cipher.NewCBCEncrypter(block, iv), nil 30 | } else { 31 | return cipher.NewCBCDecrypter(block, iv), nil 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /common/ciphers/block/block.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | import "crypto/cipher" 4 | 5 | type IBlockCipher interface { 6 | KeyLen() int 7 | IVLen() int 8 | NewBlock(key []byte, iv []byte, decryptOrEncrypt int) (cipher.BlockMode, error) 9 | } 10 | 11 | var blockCiphers = make(map[string]IBlockCipher) 12 | 13 | func registerBlockCiphers(method string, c IBlockCipher) { 14 | blockCiphers[method] = c 15 | } 16 | 17 | func GetBlockCiphers() map[string]IBlockCipher { 18 | return blockCiphers 19 | } 20 | 21 | func GetBlockCipher(method string) IBlockCipher { 22 | return blockCiphers[method] 23 | } 24 | -------------------------------------------------------------------------------- /common/ciphers/common.go: -------------------------------------------------------------------------------- 1 | package ciphers 2 | 3 | import ( 4 | "crypto/md5" 5 | ) 6 | 7 | func evpBytesToKey(password string, keyLen int) (key []byte) { 8 | const md5Len = 16 9 | 10 | cnt := (keyLen-1)/md5Len + 1 11 | m := make([]byte, cnt*md5Len) 12 | copy(m, MD5([]byte(password))) 13 | d := make([]byte, md5Len+len(password)) 14 | start := 0 15 | for i := 1; i < cnt; i++ { 16 | start += md5Len 17 | copy(d, m[start-md5Len:start]) 18 | copy(d[md5Len:], password) 19 | copy(m[start:], MD5(d)) 20 | } 21 | return m[:keyLen] 22 | } 23 | 24 | func MD5(data []byte) []byte { 25 | hash := md5.New() 26 | hash.Write(data) 27 | return hash.Sum(nil) 28 | } 29 | 30 | func increment(b []byte) { 31 | for i := range b { 32 | b[i]++ 33 | if b[i] != 0 { 34 | return 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /common/ciphers/stream/aes_cfb.go: -------------------------------------------------------------------------------- 1 | package stream 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | ) 7 | 8 | func init() { 9 | registerStreamCiphers("aes-128-cfb", &aes_cfb{16, 16}) 10 | registerStreamCiphers("aes-192-cfb", &aes_cfb{24, 16}) 11 | registerStreamCiphers("aes-256-cfb", &aes_cfb{32, 16}) 12 | } 13 | 14 | type aes_cfb struct { 15 | keyLen int 16 | ivLen int 17 | } 18 | 19 | func (a *aes_cfb) KeyLen() int { 20 | return a.keyLen 21 | } 22 | func (a *aes_cfb) IVLen() int { 23 | return a.ivLen 24 | } 25 | func (a *aes_cfb) NewStream(key, iv []byte, decryptOrEncrypt int) (cipher.Stream, error) { 26 | block, err := aes.NewCipher(key) 27 | if err != nil { 28 | return nil, err 29 | } 30 | if decryptOrEncrypt == 0 { 31 | return cipher.NewCFBEncrypter(block, iv), nil 32 | } else { 33 | return cipher.NewCFBDecrypter(block, iv), nil 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /common/ciphers/stream/aes_ctr.go: -------------------------------------------------------------------------------- 1 | package stream 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | ) 7 | 8 | func init() { 9 | registerStreamCiphers("aes-128-ctr", &aes_ctr{16, 16}) 10 | registerStreamCiphers("aes-192-ctr", &aes_ctr{24, 16}) 11 | registerStreamCiphers("aes-256-ctr", &aes_ctr{32, 16}) 12 | } 13 | 14 | type aes_ctr struct { 15 | keyLen int 16 | ivLen int 17 | } 18 | 19 | func (a *aes_ctr) KeyLen() int { 20 | return a.keyLen 21 | } 22 | func (a *aes_ctr) IVLen() int { 23 | return a.ivLen 24 | } 25 | func (a *aes_ctr) NewStream(key, iv []byte, _ int) (cipher.Stream, error) { 26 | block, err := aes.NewCipher(key) 27 | if err != nil { 28 | return nil, err 29 | } 30 | return cipher.NewCTR(block, iv), nil 31 | } 32 | -------------------------------------------------------------------------------- /common/ciphers/stream/bf_cfb.go: -------------------------------------------------------------------------------- 1 | package stream 2 | 3 | import ( 4 | "crypto/cipher" 5 | 6 | "golang.org/x/crypto/blowfish" 7 | ) 8 | 9 | func init() { 10 | registerStreamCiphers("bf-cfb", &bf_cfb{16, 8}) 11 | } 12 | 13 | type bf_cfb struct { 14 | keyLen int 15 | ivLen int 16 | } 17 | 18 | func (a *bf_cfb) KeyLen() int { 19 | return a.keyLen 20 | } 21 | func (a *bf_cfb) IVLen() int { 22 | return a.ivLen 23 | } 24 | func (a *bf_cfb) NewStream(key, iv []byte, decryptOrEncrypt int) (cipher.Stream, error) { 25 | block, err := blowfish.NewCipher(key) 26 | if err != nil { 27 | return nil, err 28 | } 29 | if decryptOrEncrypt == 0 { 30 | return cipher.NewCFBEncrypter(block, iv), nil 31 | } else { 32 | return cipher.NewCFBDecrypter(block, iv), nil 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /common/ciphers/stream/cast5_cfb.go: -------------------------------------------------------------------------------- 1 | package stream 2 | 3 | import ( 4 | "crypto/cipher" 5 | 6 | "golang.org/x/crypto/cast5" 7 | ) 8 | 9 | func init() { 10 | registerStreamCiphers("cast5-cfb", &cast5_cfb{16, 8}) 11 | } 12 | 13 | type cast5_cfb struct { 14 | keyLen int 15 | ivLen int 16 | } 17 | 18 | func (a *cast5_cfb) KeyLen() int { 19 | return a.keyLen 20 | } 21 | func (a *cast5_cfb) IVLen() int { 22 | return a.ivLen 23 | } 24 | func (a *cast5_cfb) NewStream(key, iv []byte, decryptOrEncrypt int) (cipher.Stream, error) { 25 | block, err := cast5.NewCipher(key) 26 | if err != nil { 27 | return nil, err 28 | } 29 | if decryptOrEncrypt == 0 { 30 | return cipher.NewCFBEncrypter(block, iv), nil 31 | } else { 32 | return cipher.NewCFBDecrypter(block, iv), nil 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /common/ciphers/stream/chacha20.go: -------------------------------------------------------------------------------- 1 | package stream 2 | 3 | import ( 4 | "crypto/cipher" 5 | 6 | chacha20 "gitlab.com/yawning/chacha20.git" 7 | ) 8 | 9 | func init() { 10 | registerStreamCiphers("chacha20", &_chacha20{32, 8}) 11 | registerStreamCiphers("chacha20-ietf", &_chacha20{32, 12}) 12 | } 13 | 14 | type _chacha20 struct { 15 | keyLen int 16 | ivLen int 17 | } 18 | 19 | func (a *_chacha20) KeyLen() int { 20 | return a.keyLen 21 | } 22 | func (a *_chacha20) IVLen() int { 23 | return a.ivLen 24 | } 25 | func (a *_chacha20) NewStream(key, iv []byte, _ int) (cipher.Stream, error) { 26 | return chacha20.New(key, iv) 27 | } 28 | -------------------------------------------------------------------------------- /common/ciphers/stream/cipher_conn.go: -------------------------------------------------------------------------------- 1 | package stream 2 | 3 | import ( 4 | "crypto/cipher" 5 | "crypto/md5" 6 | "crypto/rand" 7 | "io" 8 | "net" 9 | 10 | "github.com/ProxyPanel/VNet-SSR/common/log" 11 | "github.com/ProxyPanel/VNet-SSR/common/pool" 12 | ) 13 | 14 | func GetStreamConnCiphers(method string) func(string, net.Conn) (net.Conn, error) { 15 | c, ok := streamCiphers[method] 16 | if !ok { 17 | return nil 18 | } 19 | return func(password string, conn net.Conn) (net.Conn, error) { 20 | iv := make([]byte, c.IVLen()) 21 | if _, err := io.ReadFull(rand.Reader, iv); err != nil { 22 | return nil, err 23 | } 24 | sc := &streamConn{ 25 | Conn: conn, 26 | IStreamCipher: c, 27 | key: evpBytesToKey(password, c.KeyLen()), 28 | } 29 | var err error 30 | sc.Encrypter, err = sc.NewStream(sc.key, iv, 0) 31 | _, err = conn.Write(iv) 32 | return sc, err 33 | } 34 | } 35 | 36 | type streamConn struct { 37 | net.Conn 38 | IStreamCipher 39 | key []byte 40 | Encrypter cipher.Stream 41 | Decrypter cipher.Stream 42 | } 43 | 44 | func (s *streamConn) GetKey() []byte { 45 | return s.key 46 | } 47 | 48 | func (s *streamConn) Read(b []byte) (n int, err error) { 49 | if s.Decrypter == nil { 50 | iv := make([]byte, s.IVLen()) 51 | if _, err = s.Conn.Read(iv); err != nil { 52 | return 53 | } 54 | s.Decrypter, err = s.NewStream(s.key, iv, 1) 55 | if err != nil { 56 | log.Error("[Stream Conn] init decrypter failed: %v", err) 57 | return 0, err 58 | } 59 | } 60 | buf := pool.GetBuf() 61 | if len(buf) < len(b) { 62 | pool.PutBuf(buf) 63 | buf = make([]byte, len(b)) 64 | } else { 65 | defer pool.PutBuf(buf) 66 | } 67 | 68 | buf = buf[:len(b)] 69 | n, err = s.Conn.Read(buf) 70 | if err != nil { 71 | return 72 | } 73 | s.Decrypter.XORKeyStream(b[:n], buf[:n]) 74 | return 75 | } 76 | 77 | func (s *streamConn) Write(b []byte) (n int, err error) { 78 | buf := pool.GetBuf() 79 | if len(buf) < len(b) { 80 | pool.PutBuf(buf) 81 | buf = make([]byte, len(b)) 82 | } else { 83 | buf = buf[:len(b)] 84 | defer pool.PutBuf(buf) 85 | } 86 | s.Encrypter.XORKeyStream(buf, b) 87 | return s.Conn.Write(buf) 88 | } 89 | 90 | func evpBytesToKey(password string, keyLen int) (key []byte) { 91 | const md5Len = 16 92 | 93 | cnt := (keyLen-1)/md5Len + 1 94 | m := make([]byte, cnt*md5Len) 95 | copy(m, MD5([]byte(password))) 96 | d := make([]byte, md5Len+len(password)) 97 | start := 0 98 | for i := 1; i < cnt; i++ { 99 | start += md5Len 100 | copy(d, m[start-md5Len:start]) 101 | copy(d[md5Len:], password) 102 | copy(m[start:], MD5(d)) 103 | } 104 | return m[:keyLen] 105 | } 106 | 107 | func MD5(data []byte) []byte { 108 | hash := md5.New() 109 | hash.Write(data) 110 | return hash.Sum(nil) 111 | } 112 | -------------------------------------------------------------------------------- /common/ciphers/stream/cipher_packet.go: -------------------------------------------------------------------------------- 1 | package stream 2 | 3 | import ( 4 | "crypto/rand" 5 | "fmt" 6 | "io" 7 | "net" 8 | "sync" 9 | 10 | "github.com/ProxyPanel/VNet-SSR/common/pool" 11 | "github.com/pkg/errors" 12 | ) 13 | 14 | var ErrShortPacket = errors.New("short packet") 15 | 16 | const MAX_PACKET_SIZE = 64 * 1204 17 | 18 | type streamPacket struct { 19 | net.PacketConn 20 | IStreamCipher 21 | sync.Mutex 22 | key []byte 23 | buf []byte 24 | } 25 | 26 | func GetStreamPacketCiphers(method string) func(string, net.PacketConn) (net.PacketConn, error) { 27 | c, ok := streamCiphers[method] 28 | if !ok { 29 | return nil 30 | } 31 | return func(password string, packet net.PacketConn) (net.PacketConn, error) { 32 | iv := make([]byte, c.IVLen()) 33 | if _, err := io.ReadFull(rand.Reader, iv); err != nil { 34 | return nil, err 35 | } 36 | sc := &streamPacket{ 37 | PacketConn: packet, 38 | IStreamCipher: c, 39 | key: evpBytesToKey(password, c.KeyLen()), 40 | buf: pool.GetBuf(), 41 | } 42 | return sc, nil 43 | } 44 | } 45 | 46 | func (c *streamPacket) GetKey() []byte { 47 | return c.key 48 | } 49 | 50 | func (c *streamPacket) WriteTo(b []byte, addr net.Addr) (n int, err error) { 51 | c.Lock() 52 | defer c.Unlock() 53 | ivLen := c.IVLen() 54 | dataLen := len(b) 55 | if MAX_PACKET_SIZE < ivLen+dataLen { 56 | return 0, io.ErrShortBuffer 57 | } 58 | iv := c.buf[:ivLen] 59 | if _, err := io.ReadFull(rand.Reader, iv); err != nil { 60 | return 0, err 61 | } 62 | encryper, err := c.NewStream(c.key, iv, 0) 63 | encryper.XORKeyStream(c.buf[ivLen:], b) 64 | _, err = c.PacketConn.WriteTo(c.buf[:ivLen+dataLen], addr) 65 | if err != nil { 66 | return 0, err 67 | } 68 | return dataLen, nil 69 | } 70 | 71 | func (c *streamPacket) ReadFrom(b []byte) (n int, addr net.Addr, err error) { 72 | defer func() { 73 | if e := recover(); e != nil { 74 | err = errors.WithStack(errors.New(fmt.Sprintf("%v", e))) 75 | } 76 | }() 77 | n, addr, err = c.PacketConn.ReadFrom(b) 78 | 79 | if err != nil { 80 | return n, addr, err 81 | } 82 | ivLen := c.IVLen() 83 | 84 | if len(b) > cap(c.buf) { 85 | return 0, nil, errors.WithStack(io.ErrShortBuffer) 86 | } 87 | 88 | if n < ivLen || len(b) < ivLen { 89 | return n, addr, ErrShortPacket 90 | } 91 | 92 | decryptr, err := c.NewStream(c.key, b[:ivLen], 0) 93 | if err != nil { 94 | return n, addr, err 95 | } 96 | 97 | decryptr.XORKeyStream(b[ivLen:], b[ivLen:ivLen+n]) 98 | copy(b, b[ivLen:]) 99 | return n - ivLen, addr, err 100 | } 101 | 102 | func (c *streamPacket) Close() error { 103 | pool.PutBuf(c.buf) 104 | return c.PacketConn.Close() 105 | } 106 | -------------------------------------------------------------------------------- /common/ciphers/stream/des_cfb.go: -------------------------------------------------------------------------------- 1 | package stream 2 | 3 | import ( 4 | "crypto/cipher" 5 | "crypto/des" 6 | ) 7 | 8 | func init() { 9 | registerStreamCiphers("des-cfb", &des_cfb{8, 8}) 10 | } 11 | 12 | type des_cfb struct { 13 | keyLen int 14 | ivLen int 15 | } 16 | 17 | func (a *des_cfb) KeyLen() int { 18 | return a.keyLen 19 | } 20 | func (a *des_cfb) IVLen() int { 21 | return a.ivLen 22 | } 23 | func (a *des_cfb) NewStream(key, iv []byte, decryptOrEncrypt int) (cipher.Stream, error) { 24 | block, err := des.NewCipher(key) 25 | if err != nil { 26 | return nil, err 27 | } 28 | if decryptOrEncrypt == 0 { 29 | return cipher.NewCFBEncrypter(block, iv), nil 30 | } else { 31 | return cipher.NewCFBDecrypter(block, iv), nil 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /common/ciphers/stream/none.go: -------------------------------------------------------------------------------- 1 | package stream 2 | 3 | import ( 4 | "crypto/cipher" 5 | ) 6 | 7 | type noneStream struct{ 8 | } 9 | 10 | func newNoneStream() (cipher.Stream,error){ 11 | return &noneStream{},nil 12 | } 13 | 14 | func (noneStream *noneStream) XORKeyStream(dst, src []byte){ 15 | copy(dst,src) 16 | } 17 | 18 | 19 | func init() { 20 | registerStreamCiphers("none", &none{16, 0}) 21 | } 22 | 23 | 24 | type none struct { 25 | keyLen int 26 | ivLen int 27 | } 28 | 29 | func (a *none) KeyLen() int { 30 | return a.keyLen 31 | } 32 | func (a *none) IVLen() int { 33 | return a.ivLen 34 | } 35 | 36 | func (a *none) NewStream(key, iv []byte, _ int) (cipher.Stream, error) { 37 | stream, err := newNoneStream() 38 | if err != nil { 39 | return nil, err 40 | } 41 | return stream, nil 42 | } 43 | -------------------------------------------------------------------------------- /common/ciphers/stream/rc4.go: -------------------------------------------------------------------------------- 1 | package stream 2 | 3 | import ( 4 | "crypto/cipher" 5 | "crypto/rc4" 6 | ) 7 | 8 | func init() { 9 | registerStreamCiphers("rc4", &rc4_cryptor{16, 0}) 10 | 11 | } 12 | 13 | type rc4_cryptor struct { 14 | keyLen int 15 | ivLen int 16 | } 17 | 18 | func (a *rc4_cryptor) KeyLen() int { 19 | return a.keyLen 20 | } 21 | func (a *rc4_cryptor) IVLen() int { 22 | return a.ivLen 23 | } 24 | 25 | func (a *rc4_cryptor) NewStream(key, iv []byte, _ int) (cipher.Stream, error) { 26 | block, err := rc4.NewCipher(key) 27 | if err != nil { 28 | return nil, err 29 | } 30 | return block, nil 31 | } 32 | -------------------------------------------------------------------------------- /common/ciphers/stream/rc4_md5.go: -------------------------------------------------------------------------------- 1 | package stream 2 | 3 | import ( 4 | "crypto/cipher" 5 | "crypto/md5" 6 | "crypto/rc4" 7 | ) 8 | 9 | func init() { 10 | registerStreamCiphers("rc4-md5", &rc4_md5{16, 16}) 11 | } 12 | 13 | type rc4_md5 struct { 14 | keyLen int 15 | ivLen int 16 | } 17 | 18 | func (a *rc4_md5) KeyLen() int { 19 | return a.keyLen 20 | } 21 | func (a *rc4_md5) IVLen() int { 22 | return a.ivLen 23 | } 24 | func (a *rc4_md5) NewStream(key, iv []byte, _ int) (cipher.Stream, error) { 25 | h := md5.New() 26 | h.Write(key) 27 | h.Write(iv) 28 | rc4key := h.Sum(nil) 29 | 30 | return rc4.NewCipher(rc4key) 31 | } 32 | -------------------------------------------------------------------------------- /common/ciphers/stream/salsa20.go: -------------------------------------------------------------------------------- 1 | package stream 2 | 3 | import ( 4 | "crypto/cipher" 5 | "encoding/binary" 6 | 7 | "github.com/ProxyPanel/VNet-SSR/common/pool" 8 | "golang.org/x/crypto/salsa20/salsa" 9 | ) 10 | 11 | func init() { 12 | registerStreamCiphers("salsa20", &salsa20{32, 8}) 13 | } 14 | 15 | type salsa20 struct { 16 | keyLen int 17 | ivLen int 18 | } 19 | 20 | func (a *salsa20) KeyLen() int { 21 | return a.keyLen 22 | } 23 | func (a *salsa20) IVLen() int { 24 | return a.ivLen 25 | } 26 | func (a *salsa20) NewStream(key, iv []byte, _ int) (cipher.Stream, error) { 27 | var c salsaStreamCipher 28 | copy(c.nonce[:], iv[:8]) 29 | copy(c.key[:], key[:32]) 30 | return &c, nil 31 | } 32 | 33 | type salsaStreamCipher struct { 34 | nonce [8]byte 35 | key [32]byte 36 | counter int 37 | } 38 | 39 | func (c *salsaStreamCipher) XORKeyStream(dst, src []byte) { 40 | var buf []byte 41 | padLen := c.counter % 64 42 | dataSize := len(src) + padLen 43 | if cap(dst) >= dataSize { 44 | buf = dst[:dataSize] 45 | } else if pool.BufferSize >= dataSize { 46 | buf = pool.GetBuf() 47 | defer pool.PutBuf(buf) 48 | buf = buf[:dataSize] 49 | } else { 50 | buf = make([]byte, dataSize) 51 | } 52 | 53 | var subNonce [16]byte 54 | copy(subNonce[:], c.nonce[:]) 55 | binary.LittleEndian.PutUint64(subNonce[len(c.nonce):], uint64(c.counter/64)) 56 | 57 | // It's difficult to avoid data copy here. src or dst maybe slice from 58 | // Conn.Read/Write, which can't have padding. 59 | copy(buf[padLen:], src[:]) 60 | salsa.XORKeyStream(buf, buf, &subNonce, &c.key) 61 | copy(dst, buf[padLen:]) 62 | 63 | c.counter += len(src) 64 | } 65 | -------------------------------------------------------------------------------- /common/ciphers/stream/stream.go: -------------------------------------------------------------------------------- 1 | package stream 2 | 3 | import "crypto/cipher" 4 | 5 | type IStreamCipher interface { 6 | KeyLen() int 7 | IVLen() int 8 | // decryptOrEncrypt 0: encrypt 1: decrypt 9 | NewStream(key []byte, iv []byte, decryptOrEncrypt int) (cipher.Stream, error) 10 | } 11 | 12 | var streamCiphers = make(map[string]IStreamCipher) 13 | 14 | func registerStreamCiphers(method string, c IStreamCipher) { 15 | streamCiphers[method] = c 16 | } 17 | 18 | func GetStreamCiphers() map[string]IStreamCipher { 19 | return streamCiphers 20 | } 21 | 22 | func GetStreamCipher(method string) IStreamCipher { 23 | return streamCiphers[method] 24 | } 25 | -------------------------------------------------------------------------------- /common/common.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | type TrafficReport interface{ 4 | Upload(uid int,n int64) 5 | Download(uid int,n int64) 6 | } 7 | 8 | 9 | type OnlineReport interface{ 10 | Online(uid int,ip string) 11 | } -------------------------------------------------------------------------------- /common/eventbus/eventbus.go: -------------------------------------------------------------------------------- 1 | package eventbus 2 | 3 | import ( 4 | evbus "github.com/asaskevich/EventBus" 5 | ) 6 | 7 | var eventBus evbus.Bus 8 | 9 | func init() { 10 | eventBus = evbus.New() 11 | } 12 | func GetEventBus() evbus.Bus { 13 | return eventBus 14 | } 15 | -------------------------------------------------------------------------------- /common/eventbus/eventbus_test.go: -------------------------------------------------------------------------------- 1 | package eventbus 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/asaskevich/EventBus" 7 | ) 8 | 9 | func Benchmark_eventbus(t *testing.B) { 10 | evbus := EventBus.New() 11 | evbus.SubscribeAsync("abc", func(arg string) { 12 | // t.Log(arg) 13 | }, false) 14 | 15 | go func() { 16 | for i := 0; i < t.N; i++ { 17 | evbus.Publish("abc", "aa") 18 | } 19 | }() 20 | go func() { 21 | for i := 0; i < t.N; i++ { 22 | evbus.Publish("abc", "aa") 23 | } 24 | }() 25 | for i := 0; i < t.N; i++ { 26 | evbus.Publish("abc", "aa") 27 | } 28 | 29 | } 30 | 31 | func Benchmark_Channel(t *testing.B) { 32 | ch := make(chan string, 128) 33 | go func() { 34 | for { 35 | <-ch 36 | } 37 | }() 38 | go func() { 39 | for i := 0; i < t.N; i++ { 40 | ch <- "aa" 41 | } 42 | }() 43 | go func() { 44 | for i := 0; i < t.N; i++ { 45 | ch <- "aa" 46 | } 47 | }() 48 | for i := 0; i < t.N; i++ { 49 | ch <- "aa" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /common/log/logging.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import "github.com/sirupsen/logrus" 4 | 5 | // default use root logging 6 | func Debug(message string, params ...interface{}) { 7 | if params != nil { 8 | logrus.Debugf(message, params...) 9 | } else { 10 | logrus.Debug(message) 11 | } 12 | } 13 | 14 | // default use root logging 15 | func Info(message string, params ...interface{}) { 16 | if params != nil { 17 | logrus.Infof(message, params...) 18 | } else { 19 | logrus.Info(message) 20 | } 21 | } 22 | 23 | // default use root logging 24 | func Warn(message string, params ...interface{}) { 25 | if params != nil { 26 | logrus.Warnf(message, params...) 27 | } else { 28 | logrus.Warn(message) 29 | } 30 | } 31 | 32 | // default use root logging 33 | func Error(message string, params ...interface{}) { 34 | if params != nil { 35 | logrus.Errorf(message, params...) 36 | } else { 37 | logrus.Error(message) 38 | } 39 | } 40 | 41 | // default use root logging 42 | func Err(err error) { 43 | logrus.Errorf("%v", err) 44 | } 45 | -------------------------------------------------------------------------------- /common/network/ciphers/ciphers.go: -------------------------------------------------------------------------------- 1 | package ciphers 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | "github.com/ProxyPanel/VNet-SSR/common/ciphers/aead" 8 | "github.com/ProxyPanel/VNet-SSR/common/ciphers/stream" 9 | ) 10 | 11 | //加密装饰 12 | func CipherDecorate(password, method string, conn net.Conn) (net.Conn, error) { 13 | d := stream.GetStreamConnCiphers(method) 14 | if d != nil { 15 | return d(password, conn) 16 | } 17 | d = aead.GetAEADConnCipher(method) 18 | if d != nil { 19 | return d(password, conn) 20 | } 21 | return nil, fmt.Errorf("[SS Cipher] not support : %s", method) 22 | } 23 | 24 | func CipherPacketDecorate(password, method string, conn net.PacketConn) (net.PacketConn, error) { 25 | d := stream.GetStreamPacketCiphers(method) 26 | if d != nil { 27 | return d(password, conn) 28 | } 29 | d = aead.GetAEADPacketCiphers(method) 30 | if d != nil { 31 | return d(password, conn) 32 | } 33 | return nil, fmt.Errorf("[SS Cipher] not support : %s", method) 34 | } 35 | 36 | func GetSupportCiphers() []string { 37 | stream := stream.GetStreamCiphers() 38 | list := make([]string, 0, 20) 39 | for k, _ := range stream { 40 | list = append(list, k) 41 | } 42 | aeas := aead.GetAEADCiphers() 43 | for k, _ := range aeas { 44 | list = append(list, k) 45 | } 46 | return list 47 | } 48 | -------------------------------------------------------------------------------- /common/network/ciphers/ciphers_test.go: -------------------------------------------------------------------------------- 1 | package ciphers 2 | 3 | import ( 4 | "crypto/rand" 5 | "io" 6 | "net" 7 | "os" 8 | "runtime/pprof" 9 | "testing" 10 | "time" 11 | 12 | "github.com/ProxyPanel/VNet-SSR/common/log" 13 | "github.com/ProxyPanel/VNet-SSR/utils/datasize" 14 | ) 15 | 16 | 17 | func init() { 18 | 19 | } 20 | 21 | //TODO goroutine pool 22 | func Test_Packet(t *testing.T) { 23 | log.Info("aa") 24 | listener, err := net.ListenPacket("udp", "0.0.0.0:8080") 25 | if err != nil { 26 | log.Err(err) 27 | } 28 | dlistener, err := CipherPacketDecorate("killer", "aes-128-gcm", listener) 29 | if err != nil { 30 | log.Err(err) 31 | } 32 | buf := make([]byte, 64*1024) 33 | go func() { 34 | for { 35 | _, _, err := dlistener.ReadFrom(buf) 36 | if err != nil { 37 | log.Err(err) 38 | continue 39 | } 40 | // log.Info("len: %d,addrx %v,data: %s\n", n, addrx, string(buf[:n])) 41 | } 42 | }() 43 | log.Info("开始发送数据:") 44 | raddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:8080") 45 | if err != nil { 46 | log.Err(err) 47 | return 48 | } 49 | conn, err := net.ListenPacket("udp", "0.0.0.0:8081") 50 | if err != nil { 51 | log.Err(err) 52 | return 53 | } 54 | dconn, err := CipherPacketDecorate("killer", "aes-128-gcm", conn) 55 | if err != nil { 56 | log.Err(err) 57 | return 58 | } 59 | tmp := make([]byte, 4*1024) 60 | if _, err := io.ReadFull(rand.Reader, tmp); err != nil { 61 | t.Error(err) 62 | } 63 | f, _ := os.Create("a.pprof") 64 | pprof.StartCPUProfile(f) 65 | defer pprof.StopCPUProfile() 66 | start := time.Now() 67 | var count uint64 = 0 68 | for time.Now().Second()-start.Second() < 5 { 69 | count += 4096 70 | go dconn.WriteTo(tmp, raddr) 71 | } 72 | size, _ := datasize.HumanSize(count / uint64(5)) 73 | log.Info("%s per second", size) 74 | 75 | time.Sleep(1 * time.Second) 76 | } 77 | -------------------------------------------------------------------------------- /common/network/conn.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "github.com/rs/xid" 5 | "net" 6 | "time" 7 | ) 8 | 9 | func DialTcp(addr string) (req *Request, err error) { 10 | conn, err := net.DialTimeout("tcp", addr,5 * time.Second) 11 | if err != nil { 12 | return nil, err 13 | } 14 | 15 | return &Request{ 16 | ISStream: true, 17 | Conn: conn, 18 | RequestID: xid.New().String(), 19 | RequestTime: time.Now(), 20 | }, nil 21 | } 22 | 23 | func DialUdp(addr string) (req *Request, err error) { 24 | udpAddr, err := net.ResolveUDPAddr("udp", addr) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | conn, err := net.DialUDP("udp", nil, udpAddr) 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | return &Request{ 35 | ISStream: false, 36 | PacketConn: conn, 37 | RequestID: xid.New().String(), 38 | RequestTime: time.Now(), 39 | }, nil 40 | 41 | } 42 | -------------------------------------------------------------------------------- /common/network/listener.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "github.com/ProxyPanel/VNet-SSR/common/log" 7 | "github.com/ProxyPanel/VNet-SSR/utils/addrx" 8 | "github.com/sirupsen/logrus" 9 | "net" 10 | "runtime/debug" 11 | "strings" 12 | "time" 13 | ) 14 | 15 | func NewListener(addr string, timeout time.Duration) *Listener { 16 | listener := new(Listener) 17 | listener.Timeout = timeout 18 | listener.Addr = addr 19 | return listener 20 | } 21 | 22 | type Listener struct { 23 | Addr string 24 | Timeout time.Duration 25 | TCP *net.TCPListener 26 | UDP net.PacketConn 27 | context.Context 28 | } 29 | 30 | func (l *Listener) ListenTCP(fn func(request *Request)) error { 31 | if l.Addr == "" { 32 | return errors.New("listener Addr is empty") 33 | } 34 | 35 | listen, err := net.Listen("tcp", l.Addr) 36 | if err != nil { 37 | return err 38 | } 39 | logrus.Infof("Listener listen on: %s", l.Addr) 40 | l.TCP = listen.(*net.TCPListener) 41 | go func() { 42 | defer func() { 43 | if e := recover(); e != nil { 44 | logrus.Errorf("ListenTCP crashed , err : %s , \ntrace:%s", e, string(debug.Stack())) 45 | } 46 | }() 47 | for { 48 | 49 | con, err := l.TCP.Accept() 50 | // TODO: https://liudanking.com/network/go-%E4%B8%AD%E5%A6%82%E4%BD%95%E5%87%86%E7%A1%AE%E5%9C%B0%E5%88%A4%E6%96%AD%E5%92%8C%E8%AF%86%E5%88%AB%E5%90%84%E7%A7%8D%E7%BD%91%E7%BB%9C%E9%94%99%E8%AF%AF/ 51 | if err != nil { 52 | errString := err.Error() 53 | switch { 54 | case strings.Contains(errString, "timeout"): 55 | logrus.Infof("accept conn timeout: %s", errString) 56 | return 57 | case strings.Contains(errString, " use of closed network connection"): 58 | logrus.Infof("service %v close", addrx.SplitPortFromAddr(l.Addr)) 59 | return 60 | default: 61 | logrus.Errorf("listener Unknown error:%s", errString) 62 | return 63 | } 64 | } 65 | go func() { 66 | defer func() { 67 | if e := recover(); e != nil { 68 | logrus.WithFields(logrus.Fields{}).Errorf("connection handle crashed , err : %s , \ntrace:%s", e, string(debug.Stack())) 69 | } 70 | }() 71 | fn(NewRequestWithTCP(con)) 72 | }() 73 | } 74 | }() 75 | return nil 76 | } 77 | 78 | func (l *Listener) ListenUDP(fn func(request *Request)) error { 79 | if l.Addr == "" { 80 | return errors.New("listener Addr is empty") 81 | } 82 | 83 | listen, err := net.ListenPacket("udp", l.Addr) 84 | if err != nil { 85 | return err 86 | } 87 | l.UDP = listen 88 | go func() { 89 | defer func() { 90 | if e := recover(); e != nil { 91 | logrus.Errorf("ListenUDP crashed , err : %s , \ntrace:%s", e, string(debug.Stack())) 92 | } 93 | }() 94 | fn(NewRequestWithUDP(l.UDP)) 95 | }() 96 | return nil 97 | 98 | } 99 | 100 | func (l *Listener) Close() error { 101 | if l.TCP != nil { 102 | if err := l.TCP.Close(); err != nil { 103 | log.Error("listener close tcp error: %+v", err) 104 | return err 105 | } 106 | log.Info("listener %s tcp close", l.Addr) 107 | } 108 | 109 | if l.UDP != nil { 110 | if err := l.UDP.Close(); err != nil { 111 | log.Error("listener close udp error: %+v", err) 112 | return err 113 | } 114 | log.Info("listener %s udp close", l.Addr) 115 | } 116 | 117 | return nil 118 | } 119 | -------------------------------------------------------------------------------- /common/network/listener_test.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "context" 5 | "github.com/ProxyPanel/VNet-SSR/common/log" 6 | "github.com/ProxyPanel/VNet-SSR/utils/osx" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | func ExampleTest(){ 12 | ctx,cancel := context.WithCancel(context.Background()) 13 | 14 | tmpFunc := func (){ 15 | select { 16 | case <- ctx.Done(): 17 | println("done") 18 | } 19 | } 20 | go tmpFunc() 21 | go tmpFunc() 22 | 23 | tick := time.After(3*time.Second) 24 | println("start ...") 25 | <-tick 26 | cancel() 27 | //Output: 28 | } 29 | 30 | func ExampleTest2(){ 31 | done := make(chan struct{}) 32 | 33 | tmpFunc := func(){ 34 | select{ 35 | case <- done: 36 | println("done") 37 | } 38 | } 39 | go tmpFunc() 40 | go tmpFunc() 41 | 42 | println("start...") 43 | tick := time.After(3*time.Second) 44 | <-tick 45 | close(done) 46 | //Output: 47 | } 48 | 49 | func ExampleListener(){ 50 | listener := NewListener("127.0.0.1:1000",5*time.Second) 51 | listener.ListenUDP(func(request *Request){ 52 | for{ 53 | buf := make([]byte,2048) 54 | _,_,err := request.ReadFrom(buf) 55 | if err != nil{ 56 | if strings.Contains(err.Error(), " use of closed network connection"){ 57 | println(err.Error()) 58 | return 59 | } 60 | log.Err(err) 61 | } 62 | log.Info(string(buf)) 63 | } 64 | }) 65 | listener.ListenTCP(func(request *Request){ 66 | buf := make([]byte,2048) 67 | for{ 68 | n,err:=request.Read(buf) 69 | if err != nil{ 70 | log.Err(err) 71 | } 72 | log.Info(string(buf[:n])) 73 | } 74 | }) 75 | osx.WaitSignal() 76 | println("close listener") 77 | listener.Close() 78 | osx.WaitSignal() 79 | //Output: 80 | } -------------------------------------------------------------------------------- /common/network/request.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "github.com/rs/xid" 5 | "net" 6 | "time" 7 | ) 8 | 9 | type IRequest interface { 10 | GetRequestId() string 11 | net.Conn 12 | } 13 | 14 | func NewRequestWithTCP(con net.Conn) *Request { 15 | request := new(Request) 16 | request.RequestID = xid.New().String() 17 | request.RequestTime = time.Now() 18 | request.ISStream = true 19 | request.Conn = con 20 | return request 21 | } 22 | func NewRequestWithUDP(con net.PacketConn) *Request { 23 | request := new(Request) 24 | request.RequestID = xid.New().String() 25 | request.RequestTime = time.Now() 26 | request.ISStream = false 27 | request.PacketConn = con 28 | return request 29 | } 30 | 31 | type Request struct { 32 | ISStream bool 33 | net.Conn 34 | net.PacketConn 35 | RequestID string 36 | RequestTime time.Time 37 | Data interface{} 38 | } 39 | 40 | func (r *Request) GetRequestId()string{ 41 | return r.RequestID; 42 | } 43 | 44 | func (r *Request) Close() error { 45 | if r.ISStream { 46 | return r.Conn.Close() 47 | } else { 48 | return r.PacketConn.Close() 49 | } 50 | } 51 | 52 | func (r *Request) LocalAddr() net.Addr { 53 | if r.ISStream { 54 | return r.Conn.LocalAddr() 55 | } else { 56 | return r.PacketConn.LocalAddr() 57 | } 58 | } 59 | 60 | func (r *Request) RemoteAddr() net.Addr { 61 | return r.Conn.RemoteAddr() 62 | } 63 | 64 | func (r *Request) SetDeadline(t time.Time) error { 65 | if r.ISStream { 66 | return r.Conn.SetDeadline(t) 67 | } else { 68 | return r.PacketConn.SetDeadline(t) 69 | } 70 | } 71 | 72 | func (r *Request) SetReadDeadline(t time.Time) error { 73 | if r.ISStream { 74 | return r.Conn.SetReadDeadline(t) 75 | } else { 76 | return r.PacketConn.SetReadDeadline(t) 77 | } 78 | } 79 | 80 | func (r *Request) SetWriteDeadline(t time.Time) error { 81 | if r.ISStream { 82 | return r.Conn.SetWriteDeadline(t) 83 | } else { 84 | return r.PacketConn.SetWriteDeadline(t) 85 | } 86 | } 87 | 88 | func (r *Request) SetKeepAlive(keepAlive bool) error{ 89 | if tcpConn,ok := r.Conn.(*net.TCPConn);ok{ 90 | return tcpConn.SetKeepAlive(keepAlive) 91 | } 92 | return nil 93 | } 94 | -------------------------------------------------------------------------------- /common/obfs/auth_aes128_test.go: -------------------------------------------------------------------------------- 1 | package obfs 2 | 3 | import ( 4 | "bytes" 5 | "crypto/md5" 6 | "fmt" 7 | "github.com/ProxyPanel/VNet-SSR/utils/binaryx" 8 | "github.com/sirupsen/logrus" 9 | "net" 10 | "reflect" 11 | "strings" 12 | "testing" 13 | ) 14 | 15 | func ExampleTest() { 16 | fmt.Println(reflect.ValueOf(md5.New).Pointer() == reflect.ValueOf(md5.New).Pointer()) 17 | //Output: 18 | } 19 | 20 | func TestAuthAes128(t *testing.T){ 21 | logrus.SetLevel(logrus.DebugLevel) 22 | client := GetAuthAes128() 23 | server := GetAuthAes128() 24 | data := []byte(strings.Repeat("hello",100)) 25 | clientCipherData,err := client.ClientPreEncrypt(data) 26 | if err != nil{ 27 | t.Error(err) 28 | } 29 | 30 | serverClearData,_,err := server.ServerPostDecrypt(clientCipherData) 31 | if err != nil{ 32 | t.Error(err) 33 | } 34 | 35 | if !bytes.Equal(serverClearData,data){ 36 | t.Fatal("serverClearData compare error") 37 | } 38 | fmt.Print(serverClearData) 39 | 40 | 41 | serverCipherData,err := server.ServerPreEncrypt(data) 42 | if err != nil{ 43 | t.Fatal(err) 44 | } 45 | clientClearData,err := client.ClientPostDecrypt(serverCipherData) 46 | if err != nil{ 47 | t.Fatal(err) 48 | } 49 | 50 | if !bytes.Equal(clientClearData,data){ 51 | t.Error("clientClearData compare error") 52 | } 53 | fmt.Print(clientClearData) 54 | 55 | } 56 | 57 | 58 | func GetAuthAes128() Plain{ 59 | auth, _ := AuthAes128Md5Factory("auth_aes128_md5") 60 | serverInfo := NewServerInfo() 61 | serverInfo.GetUsers()[string(binaryx.LEUint32ToBytes(1024))] = "killer" 62 | serverInfo.SetClient(net.ParseIP("127.0.0.1")) 63 | serverInfo.SetPort(8080) 64 | serverInfo.SetProtocolParam("1024:killer") 65 | serverInfo.SetIv(MustHexDecode("271d7f17d03ed7cd1f44327456aebfa2")) 66 | serverInfo.SetRecvIv(MustHexDecode("271d7f17d03ed7cd1f44327456aebfa2")) 67 | serverInfo.SetKeyStr("killer") 68 | serverInfo.SetKey(MustHexDecode("b36d331451a61eb2d76860e00c347396")) 69 | serverInfo.SetHeadLen(30) 70 | serverInfo.SetTCPMss(1460) 71 | serverInfo.SetBufferSize(32*1024 - 5 - 4) 72 | serverInfo.SetOverhead(9) 73 | serverInfo.SetUpdateUserFunc(UpdateUser) 74 | auth.SetServerInfo(serverInfo) 75 | return auth 76 | } -------------------------------------------------------------------------------- /common/obfs/auth_chain_test.go: -------------------------------------------------------------------------------- 1 | package obfs 2 | 3 | import ( 4 | "bytes" 5 | "crypto/md5" 6 | "encoding/hex" 7 | "fmt" 8 | "github.com/ProxyPanel/VNet-SSR/utils/binaryx" 9 | "net" 10 | "testing" 11 | ) 12 | 13 | func ExampleAuthChain() { 14 | a,_ := NewAuthBase("acda") 15 | fmt.Println(a.GetMethod()) 16 | s := NewServerInfo() 17 | a.SetServerInfo(s) 18 | s.SetHost("baidu.com") 19 | fmt.Println(a.GetServerInfo().GetHost()) 20 | //Output: 21 | } 22 | 23 | func ExampleXorShift128Plus() { 24 | a := NewXorShift128Plus() 25 | a.InitFromBin(bytes.Repeat([]byte{byte(0x01)}, 16)) 26 | fmt.Println(a) 27 | a = NewXorShift128Plus() 28 | a.InitFromBinLen(bytes.Repeat([]byte{byte(0x01)}, 16), 2) 29 | fmt.Println(a) 30 | fmt.Printf("%v\n", a.Next()) 31 | 32 | a = NewXorShift128Plus() 33 | a.InitFromBinLen(MustHexDecode("1c61777508c444705f7ec9092de53b7e"), 5) 34 | fmt.Printf("%v\n", a.Next()) 35 | 36 | //Output: 37 | //&{72340172838076673 72340172838076673} 38 | //&{11655686789823302041 3472380973156407715} 39 | //5864189510468454840 40 | 41 | } 42 | 43 | func ExampleHashMd5() { 44 | md5Data := md5.Sum([]byte("aaa")) 45 | fmt.Println(hex.EncodeToString(md5Data[:])) 46 | 47 | //Output: 48 | //47bce5c74f589f4867dbd57e9ca9f808 49 | } 50 | 51 | func ExampleAuthChainA() { 52 | server := GetAuth() 53 | testData, _ := hex.DecodeString("1f1f645b1c61777508c444705f7ac909ce549d8859f4959996b9b817cfccf89560c0716f3e7ec8ad809fea1785b0f2ae162789d30cec6a589cb71d75a796584d4e085b15189bee7f1daff841d802a70e36dc9a541937c38cbf2cb8252fc06f5f2a374bd12454316d7265921e84d8e662749a1fa93198dc3cc946a9aafe0b30b2c013a5eaa2bf9b9e74aee69d7996b62e9e3302e87cefe9137c0d4572e50944e1c6a7856549bf1ef4b10ee1b2c370420c53e9e7e78f975d022257236da092e424c96746d88d28b764ddc45d4f1b061b94424cc02b4d6b4a8ce0f97d30235a4dfa0fb1acd51ade9fe78be6991c5c2d6caad8028cf7a0660ca3cf0f805c42b4c59ad1628813df92866b9007000fc017b4995e087aa35e0d77a6b2e7d934672b37a925cca50ed9ff715aab68a9b812afa93228bf54e0ded51d989bdd9aa86a52a05a9283e955dd34f81af06d43650a43651ebdd5e36c9b42697f636d1275b2d61389a62c3cafed92e532316ce4d3d2e913c81d6f225192da176cfa007307f11b605918240c5d3ebed516f3c1ea657e6c6337f1bd1cb34e01d34660f592408cf50b936f9beec5409f8718ac4e3f833bb400cb01ceec7d67fa34b7971167f56dc8d335d9865d11e52d78c773a99ce2755fa10d537556252cc0604640bb63d98140edfa2def15d316e183dab62e393aa7a3b885a2eb25e0d2476b6a008c1987772898ad4777b75a36b22a8c52ebb248cf9db6916ad622e3bd049c0b1acf8610d5f1ce94e8b47df03e2168b11d067916ce6465501f4844fc5c8cec9e95455703c1330ce62045fd3ff0ef6d3107a7c7d5c01ff3f48dae7582a1c80779c36a433f24d919d4b1528e7cac85790df311c4231907e5989f11053603d53952b7d8ef815ac831a3a4e37e467327b50042f087a3bce89f347b98e9cdbf7f395b00d43d384455ae8fbcc98ddebbddaac8a0f4e784916ee12c4abd2a7fc3f792d39b242b57c67ebe22867c92460bbe15450eca27aa1c8a0df86522e050bda1f8a3d80352f5e4017d8b1e06e0df6d0f230b4aefdb7d7ab517725d347582c766963a7f30759f9194ba431decf37f422c2716ac4dfcf3cb1785243012a095b3c3276329a3f12effd50a2696eb79ceec8b245a7605b84b76e0f9ac1eeb1694e723285cc16ba613f8407905090aa0088b1b18e16abb6cc6ce22f8cb8a6858546cbe18eb277df693be995e3b2a61a4278820cba983e64b9397444edc239ee5bd06bb69d370383cb13f5d23cca1b078996a91550e74578b2215c2aeaf5a6a00267f878d58860fbc415ce3b9239f93ad97acfb9d49e3eca3a5b9832b773224ab54ba60af74e098465fbee992f4639cb6dcd43b648441ae89dc8039b9d72a8af09ca33f2d5b3056e4d193ae8488092b95573ac84e49542b63c75ca9e982109b56b7f1850974f5b7ad0eb3fe9e856e6513801608fdc9cc125fc35cc23faf17c265ff05f18676659bef8082fcc6b78fd07d83b765f0b11333af84498d27675fd3e6de77d8b1fe3c630fe36f538535adc8f6cbf8ae3e6671b81b59fb14830d5d72e008d41e032f8aefc3f3a588a7e47f86ad479912d11a4b96d840a316837b082a2bd50f8322ebee431c1bd20f2ddbe9b8f59d749c8da29f90d4f28314e9fea064292e049cbb6d7e7d3aa389c6343af69710f4a3be10913c21a10c2922564f6d1a66be2366a95697515b4bf2291e427aa46eb81c5aa4e71022d3aaab458d556b13dd632ec6a2bf311b4a085fd91210415c7c0ce3b0e03de498bd41a0b046efcf91b8654370b302b509acc995a3c99a700fdb50d9eed0260d62a8568c837f10d06865cd7ddcbf57ca3f2c3beac7137c5a6d22aba73fcfbd03b16b6a4407026b46e7c1cad47c1064cfac076c") 54 | result, sendback, err := server.ServerPostDecrypt(testData) 55 | if err != nil { 56 | fmt.Print(err) 57 | return 58 | } 59 | fmt.Printf("sendback: %v,result: %s\n", sendback, result) 60 | //Output: 61 | // 62 | } 63 | 64 | func ExampleAuthChainAClient() { 65 | client := GetAuth() 66 | server := GetAuth() 67 | ciphertext, err := client.ClientPreEncrypt([]byte("hello")) 68 | if err != nil { 69 | fmt.Println(err) 70 | return 71 | } 72 | fmt.Println(hex.EncodeToString(ciphertext)) 73 | result, sendback, err := server.ServerPostDecrypt(ciphertext) 74 | if err != nil { 75 | fmt.Println(err) 76 | return 77 | } 78 | fmt.Printf("sendback: %v,result: %s\n", sendback, result) 79 | 80 | result, err = server.ServerPreEncrypt([]byte("hello")) 81 | if err != nil { 82 | fmt.Println(err) 83 | return 84 | } 85 | fmt.Println(hex.EncodeToString(result)) 86 | 87 | result, err = client.ClientPostDecrypt(result) 88 | if err != nil { 89 | fmt.Println(err) 90 | return 91 | } 92 | fmt.Printf("%s|%s", string(result), hex.EncodeToString(result)) 93 | //Output: 94 | } 95 | 96 | func TestTestAuthChainUDP(t *testing.T) { 97 | client := GetAuth() 98 | server := GetAuth() 99 | result, err := client.ClientUDPPreEncrypt([]byte("hello")) 100 | if err != nil { 101 | fmt.Println(err) 102 | return 103 | } 104 | result, uid, err := server.ServerUDPPostDecrypt(result) 105 | if err != nil { 106 | fmt.Println(err) 107 | return 108 | } 109 | if string(result) != "hello" && binaryx.LEBytesToUInt32([]byte(uid)) != 1024 { 110 | t.Fatal("service decrypt result is not equal hello") 111 | return 112 | } 113 | result, err = server.ServerUDPPreEncrypt([]byte("hello"), []byte(uid)) 114 | if err != nil { 115 | fmt.Println(err) 116 | return 117 | } 118 | //fmt.Printf("service encrypt# %s | %s\n",string(result),hex.EncodeToString(result)) 119 | 120 | result, err = client.ClientUDPPostDecrypt(result) 121 | if err != nil { 122 | fmt.Println(err) 123 | return 124 | } 125 | if string(result) != "hello" { 126 | t.Fatal("service decrypt result is not equal hello") 127 | return 128 | } 129 | 130 | //Output: 131 | } 132 | func UpdateUser(uid []byte) { 133 | fmt.Printf("uid:%v \n", binaryx.LEBytesToUInt32(uid)) 134 | } 135 | 136 | func GetAuth() Plain { 137 | authChainA,_ := NewAuthChainA("auth_chain_a") 138 | serverInfo := NewServerInfo() 139 | serverInfo.GetUsers()[string(binaryx.LEUint32ToBytes(1024))] = "killer" 140 | serverInfo.SetClient(net.ParseIP("127.0.0.1")) 141 | serverInfo.SetPort(8080) 142 | serverInfo.SetProtocolParam("1024:killer") 143 | serverInfo.SetIv(MustHexDecode("271d7f17d03ed7cd1f44327456aebfa2")) 144 | serverInfo.SetRecvIv(MustHexDecode("271d7f17d03ed7cd1f44327456aebfa2")) 145 | serverInfo.SetKeyStr("killer") 146 | serverInfo.SetKey(MustHexDecode("b36d331451a61eb2d76860e00c347396")) 147 | serverInfo.SetHeadLen(30) 148 | serverInfo.SetTCPMss(1460) 149 | serverInfo.SetBufferSize(32*1024 - 5 - 4) 150 | serverInfo.SetOverhead(9) 151 | serverInfo.SetUpdateUserFunc(UpdateUser) 152 | authChainA.SetServerInfo(serverInfo) 153 | return authChainA 154 | } 155 | -------------------------------------------------------------------------------- /common/obfs/http_simple_test.go: -------------------------------------------------------------------------------- 1 | package obfs 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | ) 7 | 8 | func ExampleClientEncode(){ 9 | h := NewHttpSimple("http_simple").(*HttpSimple) 10 | data := h.encodeHead([]byte("helloa")) 11 | fmt.Printf(hex.EncodeToString(data)) 12 | //Output: 13 | //253638253635253663253663253666 14 | } 15 | -------------------------------------------------------------------------------- /common/obfs/obfs.go: -------------------------------------------------------------------------------- 1 | package obfs 2 | 3 | type PlainFactory func(string) (Plain,error) 4 | 5 | // Plain interface 6 | type Plain interface { 7 | InitData() []byte 8 | GetMethod() string 9 | SetMethod(method string) 10 | GetOverhead(direction bool) int 11 | GetServerInfo() ServerInfo 12 | SetServerInfo(s ServerInfo) 13 | ClientPreEncrypt(buf []byte) ([]byte, error) 14 | ClientEncode(buf []byte) ([]byte, error) 15 | ClientDecode(buf []byte) ([]byte, bool, error) 16 | ClientPostDecrypt(buf []byte) ([]byte, error) 17 | ServerPreEncrypt(buf []byte) ([]byte, error) 18 | ServerEncode(buf []byte) ([]byte, error) 19 | // ServerDecode return buffer_to_recv, is_need_decrypt, is_need_to_encode_and_send_back 20 | ServerDecode(buf []byte) ([]byte, bool, bool, error) 21 | ServerPostDecrypt(buf []byte) ([]byte, bool, error) 22 | ClientUDPPreEncrypt(buf []byte) ([]byte, error) 23 | ClientUDPPostDecrypt(buf []byte) ([]byte, error) 24 | ServerUDPPreEncrypt(buf,uid []byte) ([]byte, error) 25 | ServerUDPPostDecrypt(buf []byte) ([]byte, string, error) 26 | Dispose() 27 | GetHeadSize(buf []byte, defaultValue int) int 28 | } 29 | 30 | var ( 31 | method_supported = make(map[string]PlainFactory) 32 | ) 33 | 34 | func registerMethod(method string, factory PlainFactory) { 35 | method_supported[method] = factory 36 | } 37 | 38 | func GetObfs(method string) (Plain,error){ 39 | return method_supported[method](method) 40 | } 41 | -------------------------------------------------------------------------------- /common/obfs/obfs_test.go: -------------------------------------------------------------------------------- 1 | package obfs 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func ExampleTime() { 9 | fmt.Println(time.Now().Unix()) 10 | //Output: 11 | } 12 | 13 | 14 | func ExampleGetObfs(){ 15 | GetObfs("obfs") 16 | //Output: 17 | } -------------------------------------------------------------------------------- /common/obfs/plain.go: -------------------------------------------------------------------------------- 1 | package obfs 2 | 3 | func init() { 4 | registerMethod("plain", NewPlain) 5 | registerMethod("origin", NewPlain) 6 | } 7 | 8 | type plain struct { 9 | ServerInfo 10 | Method string 11 | } 12 | 13 | // NewPlain construct a new plain and initliza default value 14 | func NewPlain(method string) (Plain,error) { 15 | return &plain{ 16 | Method: method, 17 | },nil 18 | } 19 | 20 | func (p *plain) GetMethod() string { 21 | return p.Method 22 | } 23 | 24 | func (p *plain) SetMethod(method string) { 25 | p.Method = method 26 | } 27 | 28 | func (p *plain) InitData() []byte { 29 | return []byte{} 30 | } 31 | 32 | func (p *plain) GetOverhead(direction bool) int { 33 | return 0 34 | } 35 | 36 | func (p *plain) GetServerInfo() ServerInfo { 37 | return p.ServerInfo 38 | } 39 | 40 | func (p *plain) SetServerInfo(s ServerInfo) { 41 | p.ServerInfo = s 42 | } 43 | 44 | func (p *plain) ClientPreEncrypt(buf []byte) ([]byte, error) { 45 | return buf, nil 46 | } 47 | 48 | func (p *plain) ClientEncode(buf []byte) ([]byte, error) { 49 | return buf, nil 50 | } 51 | 52 | //ClientDecode buffer_to_recv, is_need_to_encode_and_send_back 53 | func (p *plain) ClientDecode(buf []byte) ([]byte, bool, error) { 54 | return buf, false, nil 55 | } 56 | 57 | func (p *plain) ClientPostDecrypt(buf []byte) ([]byte, error) { 58 | return buf, nil 59 | } 60 | 61 | func (p *plain) ServerPreEncrypt(buf []byte) ([]byte, error) { 62 | return buf, nil 63 | } 64 | 65 | func (p *plain) ServerEncode(buf []byte) ([]byte, error) { 66 | return buf, nil 67 | } 68 | 69 | //buffer_to_recv, is_need_decrypt, is_need_to_encode_and_send_back 70 | func (p *plain) ServerDecode(buf []byte) ([]byte, bool, bool, error) { 71 | return buf, true, false, nil 72 | } 73 | 74 | func (p *plain) ServerPostDecrypt(buf []byte) ([]byte, bool, error) { 75 | return buf, false, nil 76 | } 77 | 78 | func (p *plain) ClientUDPPreEncrypt(buf []byte) ([]byte, error) { 79 | return buf, nil 80 | } 81 | 82 | func (p *plain) ClientUDPPostDecrypt(buf []byte) ([]byte, error) { 83 | return buf, nil 84 | } 85 | 86 | func (p *plain) ServerUDPPreEncrypt(buf,uid []byte) ([]byte, error) { 87 | return buf, nil 88 | } 89 | 90 | func (p *plain) ServerUDPPostDecrypt(buf []byte) ([]byte, string, error) { 91 | return buf, "", nil 92 | } 93 | 94 | func (p *plain) Dispose() { 95 | 96 | } 97 | 98 | func (p *plain) GetHeadSize(buf []byte, defaultValue int) int { 99 | if len(buf) < 2 { 100 | return defaultValue 101 | } 102 | headType := int(buf[0]) & 0x7 103 | switch headType { 104 | case 1: 105 | return 7 106 | case 4: 107 | return 19 108 | case 3: 109 | return 4 + int(buf[1]) 110 | } 111 | return defaultValue 112 | } 113 | -------------------------------------------------------------------------------- /common/obfs/server_info.go: -------------------------------------------------------------------------------- 1 | package obfs 2 | 3 | import "net" 4 | 5 | const ( 6 | NETWORK_MTU = 1500 7 | TCP_MSS = 1460 8 | BUF_SIZE = 32 * 1024 9 | UDP_MAX_BUF_SIZE = 65536 10 | DEFAULT_HEAD_LEN = 30 11 | ) 12 | 13 | type ServerInfo interface { 14 | GetHost() string 15 | SetHost(host string) 16 | GetPort() int 17 | SetPort(port int) 18 | GetClient() net.IP 19 | SetClient(client net.IP) 20 | GetClientPort() int 21 | SetClientPort(port int) 22 | GetProtocolParam() string 23 | SetProtocolParam(protocolParam string) 24 | GetObfsParam() string 25 | SetObfsParam(obfsParam string) 26 | SetIv(iv []byte) 27 | GetIv() []byte 28 | SetRecvIv(iv []byte) 29 | GetRecvIv() []byte 30 | SetKeyStr(key string) 31 | GetKeyStr() string 32 | SetKey(key []byte) 33 | GetKey() []byte 34 | SetHeadLen(len int) 35 | GetHeadLen() int 36 | SetTCPMss(mss int) 37 | GetTCPMss() int 38 | SetBufferSize(size int) 39 | GetBufferSize() int 40 | SetOverhead(size int) 41 | GetOverhead() int 42 | GetUsers() map[string]string 43 | SetUsers(users map[string]string) 44 | UpdateUser(uid []byte) 45 | SetUpdateUserFunc(func(uid []byte)) 46 | } 47 | 48 | type serverInfo struct { 49 | Host string 50 | Port int 51 | Client net.IP 52 | ClientPort int 53 | ProtocolParam string 54 | ObfsParam string 55 | Iv []byte 56 | RecvIv []byte 57 | KeyStr string 58 | Key []byte 59 | HeadLen int 60 | TCPMss int 61 | BufferSize int 62 | Overhead int 63 | Users map[string]string 64 | updateUser func(uid []byte) 65 | } 66 | 67 | // InitServerInfo init ServerInfo default value 68 | func NewServerInfo() ServerInfo { 69 | return &serverInfo{ 70 | TCPMss: TCP_MSS, 71 | HeadLen: DEFAULT_HEAD_LEN, 72 | Users: make(map[string]string), 73 | } 74 | } 75 | 76 | func (s *serverInfo) GetHost() string { 77 | return s.Host 78 | } 79 | 80 | func (s *serverInfo) SetHost(host string) { 81 | s.Host = host 82 | } 83 | 84 | func (s *serverInfo) GetPort() int { 85 | return s.Port 86 | } 87 | 88 | func (s *serverInfo) SetPort(port int) { 89 | s.Port = port 90 | } 91 | 92 | func (s *serverInfo) GetClient() net.IP { 93 | return s.Client 94 | } 95 | 96 | func (s *serverInfo) SetClient(client net.IP) { 97 | s.Client = client 98 | } 99 | 100 | func (s *serverInfo) GetClientPort() int { 101 | return s.ClientPort 102 | } 103 | 104 | func (s *serverInfo) SetClientPort(port int) { 105 | s.ClientPort = port 106 | } 107 | 108 | func (s *serverInfo) GetProtocolParam() string { 109 | return s.ProtocolParam 110 | } 111 | 112 | func (s *serverInfo) SetProtocolParam(protocolParam string) { 113 | s.ProtocolParam = protocolParam 114 | } 115 | 116 | func (s *serverInfo) GetObfsParam() string { 117 | return s.ObfsParam 118 | } 119 | 120 | func (s *serverInfo) SetObfsParam(obfsParam string) { 121 | s.ObfsParam = obfsParam 122 | } 123 | 124 | func (s *serverInfo) SetIv(iv []byte) { 125 | s.Iv = iv 126 | } 127 | 128 | func (s *serverInfo) GetIv() []byte { 129 | return s.Iv 130 | } 131 | 132 | func (s *serverInfo) SetRecvIv(iv []byte) { 133 | s.RecvIv = iv 134 | } 135 | 136 | func (s *serverInfo) GetRecvIv() []byte { 137 | return s.RecvIv 138 | } 139 | 140 | func (s *serverInfo) SetKeyStr(key string) { 141 | s.KeyStr = key 142 | } 143 | 144 | func (s *serverInfo) GetKeyStr() string { 145 | return s.KeyStr 146 | } 147 | 148 | func (s *serverInfo) SetKey(key []byte) { 149 | s.Key = key 150 | } 151 | 152 | func (s *serverInfo) GetKey() []byte { 153 | return s.Key 154 | } 155 | 156 | func (s *serverInfo) SetHeadLen(len int) { 157 | s.HeadLen = len 158 | } 159 | 160 | func (s *serverInfo) GetHeadLen() int { 161 | return s.HeadLen 162 | } 163 | 164 | func (s *serverInfo) SetTCPMss(mss int) { 165 | s.TCPMss = mss 166 | } 167 | 168 | func (s *serverInfo) GetTCPMss() int { 169 | return s.TCPMss 170 | } 171 | 172 | func (s *serverInfo) SetBufferSize(size int) { 173 | s.BufferSize = size 174 | } 175 | 176 | func (s *serverInfo) GetBufferSize() int { 177 | return s.BufferSize 178 | } 179 | 180 | func (s *serverInfo) SetOverhead(size int) { 181 | s.Overhead = size 182 | } 183 | 184 | func (s *serverInfo) GetOverhead() int { 185 | return s.Overhead 186 | } 187 | 188 | func (s *serverInfo) GetUsers() map[string]string { 189 | return s.Users 190 | } 191 | 192 | func (s *serverInfo) SetUsers(users map[string]string) { 193 | s.Users = users 194 | } 195 | 196 | func (s *serverInfo) UpdateUser(uid []byte) { 197 | if s.updateUser != nil { 198 | s.updateUser(uid) 199 | } 200 | } 201 | 202 | func (s *serverInfo) SetUpdateUserFunc(f func([]byte)) { 203 | s.updateUser = f 204 | } 205 | -------------------------------------------------------------------------------- /common/pool/pool.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import "sync" 4 | 5 | const UDP_MAX_PACKET_SIZE = 64 * 1204 6 | const BufferSize = 4096 7 | 8 | var ( 9 | poolMap map[int]*sync.Pool 10 | getBufLock *sync.Mutex 11 | ) 12 | 13 | func init() { 14 | poolMap = make(map[int]*sync.Pool) 15 | getBufLock = new(sync.Mutex) 16 | } 17 | 18 | func GetBuf() []byte { 19 | pool := poolMap[BufferSize] 20 | if pool == nil { 21 | getBufLock.Lock() 22 | poolMap[BufferSize] = &sync.Pool{ 23 | New: createAllocFunc(BufferSize), 24 | } 25 | getBufLock.Unlock() 26 | } 27 | buf := poolMap[BufferSize].Get().([]byte) 28 | buf = buf[:cap(buf)] 29 | return buf 30 | } 31 | 32 | func GetBufBySize(size int) []byte { 33 | pool := poolMap[size] 34 | if pool == nil { 35 | poolMap[size] = &sync.Pool{ 36 | New: createAllocFunc(size), 37 | } 38 | } 39 | buf := poolMap[size].Get().([]byte) 40 | buf = buf[:cap(buf)] 41 | return buf 42 | } 43 | 44 | func PutBuf(buf []byte) { 45 | poolMap[cap(buf)].Put(buf) 46 | } 47 | 48 | func createAllocFunc(size int) func() interface{} { 49 | return func() interface{} { 50 | return make([]byte, size) 51 | } 52 | } 53 | 54 | // type BytesPool struct { 55 | // Size int 56 | // *sync.Pool 57 | // } 58 | 59 | // func NewBytesPool(size int) *BytesPool { 60 | // return &BytesPool{ 61 | // Size: size, 62 | // Pool: &sync.Pool{ 63 | // New: func() interface{} { 64 | // return make([]byte, size) 65 | // }, 66 | // }, 67 | // } 68 | // } 69 | 70 | // func (this *BytesPool) Get() []byte { 71 | // buf := this.Pool.Get().([]byte) 72 | // buf = buf[:cap(buf)] 73 | // return buf 74 | // } 75 | 76 | // func (this *BytesPool) Put(buf []byte) { 77 | // this.Pool.Put(buf) 78 | // } 79 | -------------------------------------------------------------------------------- /common/pool/pool_test.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | ) 7 | 8 | func ExampleGetBuf() { 9 | buf := GetBuf() 10 | fmt.Printf("len: %v\n", len(buf)) 11 | fmt.Printf("cap: %v\n", cap(buf)) 12 | 13 | buf2 := buf[1024:] 14 | fmt.Printf("len: %v\n", len(buf2)) 15 | fmt.Printf("cap: %v\n", cap(buf2)) 16 | 17 | bufReader := bytes.NewBuffer(buf2) 18 | bufReader.Reset() 19 | fmt.Println(bufReader.Len()) 20 | fmt.Printf("len: %v\n", len(buf2)) 21 | fmt.Printf("cap: %v\n", cap(buf2)) 22 | //Output: 23 | //len: 4096 24 | //cap: 4096 25 | //len: 3072 26 | //cap: 3072 27 | } 28 | -------------------------------------------------------------------------------- /core/global_data.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "github.com/ProxyPanel/VNet-SSR/model" 5 | "github.com/robfig/cron" 6 | "github.com/stackimpact/stackimpact-go" 7 | ) 8 | 9 | var ( 10 | app = NewApp() 11 | ) 12 | 13 | func NewApp() *App { 14 | app := new(App) 15 | app.cron = cron.New() 16 | app.cron.Start() 17 | return app 18 | } 19 | 20 | func GetApp() *App { 21 | return app 22 | } 23 | 24 | type App struct { 25 | nodeInfo *model.NodeInfo 26 | userInfos []*model.UserInfo 27 | nodeId int 28 | apiHost string 29 | key string 30 | host string 31 | publicIP string 32 | cron *cron.Cron 33 | agent *stackimpact.Agent 34 | obfsProtocolService ObfsProtocolService 35 | } 36 | 37 | func (a *App) Init() error { 38 | return nil 39 | } 40 | 41 | func (a *App) Cron() *cron.Cron { 42 | return a.cron 43 | } 44 | 45 | func (a *App) SetCron(cron *cron.Cron) { 46 | a.cron = cron 47 | } 48 | 49 | func (a *App) UserInfos() []*model.UserInfo { 50 | return a.userInfos 51 | } 52 | 53 | func (a *App) SetUserInfos(userInfos []*model.UserInfo) { 54 | a.userInfos = userInfos 55 | } 56 | 57 | func (a *App) Host() string { 58 | return a.host 59 | } 60 | 61 | func (a *App) SetHost(host string) { 62 | a.host = host 63 | } 64 | 65 | func (a *App) Key() string { 66 | return a.key 67 | } 68 | 69 | func (a *App) SetKey(key string) { 70 | a.key = key 71 | } 72 | 73 | func (a *App) SetNodeInfo(nodeInfo *model.NodeInfo) { 74 | a.nodeInfo = nodeInfo 75 | } 76 | 77 | func (a *App) NodeInfo() *model.NodeInfo { 78 | return a.nodeInfo 79 | } 80 | 81 | func (a *App) NodeId() int { 82 | return a.nodeId 83 | } 84 | 85 | func (a *App) SetNodeId(nodeId int) { 86 | a.nodeId = nodeId 87 | } 88 | 89 | func (a *App) ApiHost() string { 90 | return a.apiHost 91 | } 92 | 93 | func (a *App) SetApiHost(apiHost string) { 94 | a.apiHost = apiHost 95 | } 96 | 97 | func (a *App) SetPublicIP(publicIp string) { 98 | a.publicIP = publicIp 99 | } 100 | 101 | func (a *App) GetPublicIP() string { 102 | return a.publicIP 103 | } 104 | 105 | func (a *App) SetAgent(agent *stackimpact.Agent) { 106 | a.agent = agent 107 | } 108 | 109 | func (a *App) GetAgent() *stackimpact.Agent { 110 | return a.agent 111 | } 112 | 113 | func (a *App) SetObfsProtocolService(obfsProtocolService ObfsProtocolService) { 114 | a.obfsProtocolService = obfsProtocolService 115 | } 116 | 117 | func (a *App) GetObfsProtocolService() ObfsProtocolService { 118 | return a.obfsProtocolService 119 | } 120 | -------------------------------------------------------------------------------- /core/interface.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | type Closeable interface { 4 | Close() error; 5 | } 6 | 7 | type Runable interface { 8 | Start() error; 9 | } 10 | 11 | type Reloadable interface { 12 | Reload() error; 13 | } 14 | 15 | type HostFirewall interface { 16 | JudgeHostWithReport(ipOrDomain string, uid int) bool 17 | } 18 | 19 | type ObfsProtocolService interface { 20 | Update(userID []byte, clientID, connectionID int); 21 | SetMaxClient(maxClient int); 22 | Insert(userID []byte, clientID, connectionID int) bool; 23 | Remove(userID string, clientID int); 24 | AuthData() []byte; 25 | } 26 | -------------------------------------------------------------------------------- /core/version.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | const ( 4 | APP_VERSION = "v2.1.0" 5 | ) 6 | -------------------------------------------------------------------------------- /error_formatting.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 错误格式化 9 | 28 | 29 | 30 |
31 |
32 | 33 |
34 | 35 |
36 | 37 |
38 | 49 |
50 | 51 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ProxyPanel/VNet-SSR 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/StackExchange/wmi v1.2.1 // indirect 7 | github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef 8 | github.com/dustin/go-humanize v1.0.0 9 | github.com/gin-gonic/gin v1.6.3 10 | github.com/pkg/errors v0.9.1 11 | github.com/robfig/cron v1.2.0 12 | github.com/rs/xid v1.2.1 13 | github.com/shirou/gopsutil v3.21.7+incompatible 14 | github.com/sirupsen/logrus v1.8.1 15 | github.com/spf13/cobra v1.2.1 16 | github.com/spf13/viper v1.8.1 17 | github.com/stackimpact/stackimpact-go v2.3.10+incompatible 18 | github.com/tidwall/gjson v1.8.1 19 | github.com/tklauser/go-sysconf v0.3.7 // indirect 20 | gitlab.com/yawning/chacha20.git v0.0.0-20190903091407-6d1cb28dc72c 21 | golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e 22 | golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac 23 | gopkg.in/resty.v1 v1.12.0 24 | ) 25 | -------------------------------------------------------------------------------- /model/model.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type NodeInfo struct { 4 | ID int `json:"id"` 5 | Port string `json:"port"` 6 | Passwd string `json:"passwd"` 7 | Method string `json:"method"` 8 | Protocol string `json:"protocol"` 9 | Obfs string `json:"obfs"` 10 | ProtocolParam string `json:"protocol_param"` 11 | ObfsParam string `json:"obfs_param"` 12 | PushPort int `json:"push_port"` 13 | Single int `json:"single"` 14 | Secret string `json:"secret"` 15 | SpeedLimit uint64 `json:"speed_limit"` 16 | IsUDP int `json:"is_udp"` 17 | ClientLimit int `json:"client_limit"` 18 | } 19 | 20 | type UserInfo struct { 21 | Uid int `json:"uid"` 22 | Port int `json:"port"` 23 | Passwd string `json:"passwd"` 24 | Limit uint64 `json:"speed_limit"` 25 | Enable int `json:"enable"` 26 | } 27 | 28 | type UserTraffic struct { 29 | Uid int `json:"uid"` 30 | Upload int64 `json:"upload"'` 31 | Download int64 `json:"download"` 32 | UpSpeed int64 `json:"upspeed"` 33 | DownSpeed int64 `json:"downspeed"` 34 | } 35 | 36 | type NodeOnline struct { 37 | Uid int `json:"uid"` 38 | IP string `json:"ip"` 39 | } 40 | 41 | type NodeStatus struct { 42 | CPU string `json:"cpu"` 43 | MEM string `json:"mem"` 44 | NET string `json:"net"` 45 | DISK string `json:"disk"` 46 | UPTIME int `json:"uptime"` 47 | } 48 | 49 | type Rule struct { 50 | Model string `json:"mode"` 51 | Rules []RuleItem `json:"rules"` 52 | } 53 | 54 | type RuleItem struct { 55 | Id int `json:"id"` 56 | Type string `json:"type"` 57 | Pattern string `json:"pattern"` 58 | } 59 | 60 | type Trigger struct { 61 | Uid int `json:"uid"` 62 | RuleId int `json:"rule_id"` 63 | Reason string `json:"reason"` 64 | } 65 | -------------------------------------------------------------------------------- /model/model_test.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/tidwall/gjson" 7 | "testing" 8 | ) 9 | 10 | func TestRuleConvert(t *testing.T){ 11 | res := `{ 12 | "status": "success", 13 | "code": 200, 14 | "data": { 15 | "mode": "reject", 16 | "rules": [ 17 | { 18 | "id": 2, 19 | "type": "reg", 20 | "pattern": "(Subject|HELO|SMTP)" 21 | }, 22 | { 23 | "id": 3, 24 | "type": "reg", 25 | "pattern": "BitTorrent protocol" 26 | }, 27 | { 28 | "id": 4, 29 | "type": "reg", 30 | "pattern": "(api|ps|sv|offnavi|newvector|ulog\\.imap|newloc)(\\.map|)\\.(baidu|n\\.shifen)\\.com" 31 | }, 32 | { 33 | "id": 5, 34 | "type": "reg", 35 | "pattern": "(.*\\.||)(dafahao|minghui|dongtaiwang|epochtimes|ntdtv|falundafa|wujieliulan|zhengjian)\\.(org|com|net)" 36 | }, 37 | { 38 | "id": 7, 39 | "type": "reg", 40 | "pattern": "(^.*\\@)(guerrillamail|guerrillamailblock|sharklasers|grr|pokemail|spam4|bccto|chacuo|027168)\\.(info|biz|com|de|net|org|me|la)" 41 | } 42 | ] 43 | }, 44 | "message": "获取节点审计规则成功" 45 | }` 46 | rule := Rule{} 47 | err := json.Unmarshal([]byte(gjson.Get(res,"data").String()),&rule) 48 | if err != nil{ 49 | t.Fatal(err) 50 | } 51 | fmt.Printf("%+v\n",rule) 52 | } 53 | -------------------------------------------------------------------------------- /proxy/client/shadowsocksr.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | type ShadowsocksClient struct { 4 | Host string 5 | Port int 6 | Passwd string 7 | Method string 8 | Protocol string 9 | ProtocolParam string 10 | Obfs string 11 | ObfsParam string 12 | } 13 | 14 | func (s *ShadowsocksClient) Proxy(host string,port int){ 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /proxy/server/shadowsocksr_test.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | "github.com/ProxyPanel/VNet-SSR/common/network/ciphers" 6 | "github.com/ProxyPanel/VNet-SSR/utils/socksproxy" 7 | "io/ioutil" 8 | "net" 9 | "net/http" 10 | "time" 11 | ) 12 | 13 | func ExampleShadowsocksR() { 14 | host := "127.0.0.1" 15 | port := 9090 16 | method := "aes-128-cfb" 17 | password := "killer" 18 | 19 | transport := &http.Transport{ 20 | Proxy: nil, 21 | Dial: func(network, addr string) (net.Conn, error) { 22 | con, _ := net.Dial("tcp", fmt.Sprintf("%s:%v", host, port)) 23 | 24 | c, err := ciphers.CipherDecorate(password, method, con) 25 | _,_ = c.Write(socksproxy.ParseAddr(addr).Raw) 26 | return c, err 27 | }, 28 | TLSHandshakeTimeout: 10 * time.Second, 29 | } 30 | client := &http.Client{ 31 | Transport: transport, 32 | } 33 | response, err := client.Get("httpserver://163.com") 34 | if err != nil { 35 | return 36 | } 37 | if response.StatusCode < 200 && response.StatusCode > 400 { 38 | fmt.Println("httpserver status error") 39 | } 40 | 41 | text, _ := ioutil.ReadAll(response.Body) 42 | fmt.Println(string(text)) 43 | //Output: 44 | } 45 | -------------------------------------------------------------------------------- /publish/oss-cn-hongkong.aliyuncs.com: -------------------------------------------------------------------------------- 1 | [Credentials] 2 | language=CH 3 | accessKeyID=LTAIkywxe8G1wlyH 4 | accessKeySecret=7iSJS68cBDqMK2NB3u6sa5wR07a7e6 5 | endpoint=oss-cn-hongkong.aliyuncs.com 6 | -------------------------------------------------------------------------------- /publish/ossutil64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProxyPanel/VNet-SSR/d00ae73b45defec5703d449962680cdc006755b7/publish/ossutil64 -------------------------------------------------------------------------------- /publish/publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd ../ 3 | tar --czvf vnet_v4.tar.gz vnet vnet.service 4 | cd publish 5 | ./ossutil64 -c oss-cn-hongkong.aliyuncs.com cp ../vnet_v4.tar.gz oss://kitami-hk -------------------------------------------------------------------------------- /release/BUILD: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//visibility:public"]) 2 | 3 | load("//release/bazel:zip.bzl", "pkg_zip") 4 | load("//release:mapping.bzl", "gen_mappings") 5 | 6 | filegroup( 7 | name = "config_json", 8 | srcs = [ 9 | "config.json", 10 | ], 11 | ) 12 | 13 | filegroup( 14 | name = "systemd", 15 | srcs = [ 16 | "systemd/vnet.service", 17 | "systemv/vnet", 18 | ], 19 | ) 20 | 21 | 22 | 23 | pkg_zip( 24 | name = "vnet_darwin_amd64_package", 25 | srcs = [ 26 | ":config_json", 27 | ":systemd", 28 | "//cmd/shadowsocksr-server:vnet_darwin_amd64", 29 | "//cmd/shadowsocksr-server:vnet_darwin_amd64_sig", 30 | ], 31 | out = "vnet-macos.zip", 32 | mappings = gen_mappings("darwin", "amd64"), 33 | ) 34 | 35 | pkg_zip( 36 | name = "vnet_windows_amd64_package", 37 | srcs = [ 38 | ":config_json", 39 | ":systemd", 40 | "//cmd/shadowsocksr-server:vnet_windows_amd64", 41 | "//cmd/shadowsocksr-server:vnet_windows_amd64_sig", 42 | ], 43 | out = "vnet-windows-64.zip", 44 | mappings = gen_mappings("windows", "amd64"), 45 | ) 46 | 47 | pkg_zip( 48 | name = "vnet_windows_x86_package", 49 | srcs = [ 50 | ":config_json", 51 | ":systemd", 52 | "//cmd/shadowsocksr-server:vnet_windows_386", 53 | "//cmd/shadowsocksr-server:vnet_windows_386_sig", 54 | ], 55 | out = "vnet-windows-32.zip", 56 | mappings = gen_mappings("windows", "386"), 57 | ) 58 | 59 | pkg_zip( 60 | name = "vnet_freebsd_amd64_package", 61 | srcs = [ 62 | ":config_json", 63 | ":systemd", 64 | "//cmd/shadowsocksr-server:vnet_freebsd_amd64", 65 | "//cmd/shadowsocksr-server:vnet_freebsd_amd64_sig", 66 | ], 67 | out = "vnet-freebsd-64.zip", 68 | mappings = gen_mappings("freebsd", "amd64"), 69 | ) 70 | 71 | pkg_zip( 72 | name = "vnet_freebsd_x86_package", 73 | srcs = [ 74 | ":config_json", 75 | ":systemd", 76 | "//cmd/shadowsocksr-server:vnet_freebsd_386", 77 | "//cmd/shadowsocksr-server:vnet_freebsd_386_sig", 78 | ], 79 | out = "vnet-freebsd-32.zip", 80 | mappings = gen_mappings("freebsd", "386"), 81 | ) 82 | 83 | 84 | pkg_zip( 85 | name = "vnet_openbsd_amd64_package", 86 | srcs = [ 87 | ":config_json", 88 | ":systemd", 89 | "//cmd/shadowsocksr-server:vnet_openbsd_amd64", 90 | "//cmd/shadowsocksr-server:vnet_openbsd_amd64_sig", 91 | ], 92 | out = "vnet-openbsd-64.zip", 93 | mappings = gen_mappings("openbsd", "amd64"), 94 | ) 95 | 96 | pkg_zip( 97 | name = "vnet_openbsd_x86_package", 98 | srcs = [ 99 | ":config_json", 100 | ":systemd", 101 | "//cmd/shadowsocksr-server:vnet_openbsd_386", 102 | "//cmd/shadowsocksr-server:vnet_openbsd_386_sig", 103 | ], 104 | out = "vnet-openbsd-32.zip", 105 | mappings = gen_mappings("openbsd", "386"), 106 | ) 107 | 108 | pkg_zip( 109 | name = "vnet_dragonfly_amd64_package", 110 | srcs = [ 111 | ":config_json", 112 | ":systemd", 113 | "//cmd/shadowsocksr-server:vnet_dragonfly_amd64", 114 | "//cmd/shadowsocksr-server:vnet_dragonfly_amd64_sig", 115 | ], 116 | out = "vnet-dragonfly-64.zip", 117 | mappings = gen_mappings("dragonfly", "amd64"), 118 | ) 119 | 120 | pkg_zip( 121 | name = "vnet_linux_amd64_package", 122 | srcs = [ 123 | ":config_json", 124 | ":systemd", 125 | "//cmd/shadowsocksr-server:vnet_linux_amd64", 126 | "//cmd/shadowsocksr-server:vnet_linux_amd64_sig", 127 | ], 128 | out = "vnet-linux-64.zip", 129 | mappings = gen_mappings("linux", "amd64"), 130 | ) 131 | 132 | pkg_zip( 133 | name = "vnet_linux_x86_package", 134 | srcs = [ 135 | ":config_json", 136 | ":systemd", 137 | "//cmd/shadowsocksr-server:vnet_linux_386", 138 | "//cmd/shadowsocksr-server:vnet_linux_386_sig", 139 | ], 140 | out = "vnet-linux-32.zip", 141 | mappings = gen_mappings("linux", "386"), 142 | ) 143 | 144 | pkg_zip( 145 | name = "vnet_linux_arm_package", 146 | srcs = [ 147 | ":config_json", 148 | ":systemd", 149 | "//cmd/shadowsocksr-server:vnet_linux_arm", 150 | "//cmd/shadowsocksr-server:vnet_linux_arm_armv6", 151 | "//cmd/shadowsocksr-server:vnet_linux_arm_armv6_sig", 152 | "//cmd/shadowsocksr-server:vnet_linux_arm_armv7", 153 | "//cmd/shadowsocksr-server:vnet_linux_arm_armv7_sig", 154 | "//cmd/shadowsocksr-server:vnet_linux_arm_sig", 155 | ], 156 | out = "vnet-linux-arm.zip", 157 | mappings = gen_mappings("linux", "arm"), 158 | ) 159 | 160 | pkg_zip( 161 | name = "vnet_linux_arm64_package", 162 | srcs = [ 163 | ":config_json", 164 | ":systemd", 165 | "//cmd/shadowsocksr-server:vnet_linux_arm64", 166 | "//cmd/shadowsocksr-server:vnet_linux_arm64_sig", 167 | ], 168 | out = "vnet-linux-arm64.zip", 169 | mappings = gen_mappings("linux", "arm64"), 170 | ) 171 | 172 | pkg_zip( 173 | name = "vnet_linux_mips_package", 174 | srcs = [ 175 | ":config_json", 176 | ":systemd", 177 | "//cmd/shadowsocksr-server:vnet_linux_mips", 178 | "//cmd/shadowsocksr-server:vnet_linux_mips_sig", 179 | "//cmd/shadowsocksr-server:vnet_linux_mips_softfloat", 180 | "//cmd/shadowsocksr-server:vnet_linux_mips_softfloat_sig", 181 | ], 182 | out = "vnet-linux-mips.zip", 183 | mappings = gen_mappings("linux", "mips"), 184 | ) 185 | 186 | pkg_zip( 187 | name = "vnet_linux_mipsle_package", 188 | srcs = [ 189 | ":config_json", 190 | ":systemd", 191 | "//cmd/shadowsocksr-server:vnet_linux_mipsle", 192 | "//cmd/shadowsocksr-server:vnet_linux_mipsle_sig", 193 | "//cmd/shadowsocksr-server:vnet_linux_mipsle_softfloat", 194 | "//cmd/shadowsocksr-server:vnet_linux_mipsle_softfloat_sig", 195 | ], 196 | out = "vnet-linux-mipsle.zip", 197 | mappings = gen_mappings("linux", "mipsle"), 198 | ) 199 | 200 | pkg_zip( 201 | name = "vnet_linux_mips64_package", 202 | srcs = [ 203 | ":config_json", 204 | ":systemd", 205 | "//cmd/shadowsocksr-server:vnet_linux_mips64", 206 | "//cmd/shadowsocksr-server:vnet_linux_mips64_sig", 207 | ], 208 | out = "vnet-linux-mips64.zip", 209 | mappings = gen_mappings("linux", "mips64"), 210 | ) 211 | 212 | pkg_zip( 213 | name = "vnet_linux_mips64le_package", 214 | srcs = [ 215 | ":config_json", 216 | ":systemd", 217 | "//cmd/shadowsocksr-server:vnet_linux_mips64le", 218 | "//cmd/shadowsocksr-server:vnet_linux_mips64le_sig", 219 | ], 220 | out = "vnet-linux-mips64le.zip", 221 | mappings = gen_mappings("linux", "mips64le"), 222 | ) 223 | 224 | pkg_zip( 225 | name = "vnet_linux_s390x_package", 226 | srcs = [ 227 | ":config_json", 228 | ":systemd", 229 | "//cmd/shadowsocksr-server:vnet_linux_s390x", 230 | "//cmd/shadowsocksr-server:vnet_linux_s390x_sig", 231 | ], 232 | out = "vnet-linux-s390x.zip", 233 | mappings = gen_mappings("linux", "s390x"), 234 | ) 235 | 236 | pkg_zip( 237 | name = "vnet_linux_ppc64_package", 238 | srcs = [ 239 | ":config_json", 240 | ":systemd", 241 | "//cmd/shadowsocksr-server:vnet_linux_ppc64", 242 | "//cmd/shadowsocksr-server:vnet_linux_ppc64_sig", 243 | ], 244 | out = "vnet-linux-ppc64.zip", 245 | mappings = gen_mappings("linux", "ppc64"), 246 | ) 247 | 248 | pkg_zip( 249 | name = "vnet_linux_ppc64le_package", 250 | srcs = [ 251 | ":config_json", 252 | ":systemd", 253 | "//cmd/shadowsocksr-server:vnet_linux_ppc64le", 254 | "//cmd/shadowsocksr-server:vnet_linux_ppc64le_sig", 255 | ], 256 | out = "vnet-linux-ppc64le.zip", 257 | mappings = gen_mappings("linux", "ppc64le"), 258 | ) 259 | -------------------------------------------------------------------------------- /release/bazel/BUILD: -------------------------------------------------------------------------------- 1 | filegroup( 2 | name = "rules", 3 | srcs = glob(["*.bzl"]), 4 | visibility = ["//visibility:public"], 5 | ) 6 | -------------------------------------------------------------------------------- /release/bazel/build.bzl: -------------------------------------------------------------------------------- 1 | def _go_command(ctx): 2 | output = ctx.attr.output 3 | if ctx.attr.os == "windows": 4 | output = output + ".exe" 5 | 6 | output_file = ctx.actions.declare_file(ctx.attr.os + "/" + ctx.attr.arch + "/" + output) 7 | pkg = ctx.attr.pkg 8 | 9 | ld_flags = "-s -w" 10 | 11 | if ctx.attr.ld: 12 | ld_flags = ld_flags + " " + ctx.attr.ld 13 | 14 | options = [ 15 | "go", 16 | "build", 17 | "-o", output_file.path, 18 | "-compiler", "gc", 19 | "-gcflags", '"all=-trimpath=${GOPATH}/src"', 20 | "-asmflags", '"all=-trimpath=${GOPATH}/src"', 21 | "-ldflags", "'%s'" % ld_flags, 22 | "-tags", "'%s'" % ctx.attr.gotags, 23 | pkg, 24 | ] 25 | 26 | command = " ".join(options) 27 | 28 | envs = [ 29 | "CGO_ENABLED=0", 30 | "GOOS="+ctx.attr.os, 31 | "GOARCH="+ctx.attr.arch, 32 | "GOROOT_FINAL=/go", 33 | "GOCACHE=${TMPDIR}/gocache" 34 | ] 35 | 36 | if ctx.attr.mips: # https://github.com/golang/go/issues/27260 37 | envs+=["GOMIPS="+ctx.attr.mips] 38 | envs+=["GOMIPS64="+ctx.attr.mips] 39 | envs+=["GOMIPSLE="+ctx.attr.mips] 40 | envs+=["GOMIPS64LE="+ctx.attr.mips] 41 | if ctx.attr.arm: 42 | envs+=["GOARM="+ctx.attr.arm] 43 | 44 | command = " ".join(envs) + " " + command 45 | 46 | print(command) 47 | ctx.actions.run_shell( 48 | outputs = [output_file], 49 | command = command, 50 | use_default_shell_env = True, 51 | ) 52 | runfiles = ctx.runfiles(files = [output_file]) 53 | return [DefaultInfo(executable = output_file, runfiles = runfiles)] 54 | 55 | 56 | foreign_go_binary = rule( 57 | _go_command, 58 | attrs = { 59 | 'pkg': attr.string(), 60 | 'output': attr.string(), 61 | 'os': attr.string(mandatory=True), 62 | 'arch': attr.string(mandatory=True), 63 | 'mips': attr.string(), 64 | 'arm': attr.string(), 65 | 'ld': attr.string(), 66 | 'gotags': attr.string(), 67 | }, 68 | executable = True, 69 | ) 70 | -------------------------------------------------------------------------------- /release/bazel/gpg.bzl: -------------------------------------------------------------------------------- 1 | def _gpg_sign_impl(ctx): 2 | output_file = ctx.actions.declare_file(ctx.file.base.basename + ctx.attr.suffix, sibling = ctx.file.base) 3 | if not ctx.configuration.default_shell_env.get("GPG_PASS"): 4 | ctx.actions.write(output_file, "") 5 | else: 6 | command = "echo ${GPG_PASS} | gpg --pinentry-mode loopback --digest-algo SHA512 --passphrase-fd 0 --output %s --detach-sig %s" % (output_file.path, ctx.file.base.path) 7 | ctx.actions.run_shell( 8 | command = command, 9 | use_default_shell_env = True, 10 | inputs = [ctx.file.base], 11 | outputs = [output_file], 12 | progress_message = "Signing binary", 13 | mnemonic = "gpg", 14 | ) 15 | return [DefaultInfo(files = depset([output_file]))] 16 | 17 | gpg_sign = rule( 18 | implementation = _gpg_sign_impl, 19 | attrs = { 20 | "base": attr.label(allow_single_file=True), 21 | "suffix": attr.string(default=".sig"), 22 | }, 23 | ) 24 | -------------------------------------------------------------------------------- /release/bazel/matrix.bzl: -------------------------------------------------------------------------------- 1 | SUPPORTED_MATRIX = [ 2 | ("windows", "amd64"), 3 | ("windows", "386"), 4 | ("darwin", "amd64"), 5 | ("linux", "amd64"), 6 | ("linux", "386"), 7 | ("linux", "arm64"), 8 | ("linux", "arm"), 9 | ("linux", "mips64"), 10 | ("linux", "mips"), 11 | ("linux", "mips64le"), 12 | ("linux", "mipsle"), 13 | ("linux", "ppc64"), 14 | ("linux", "ppc64le"), 15 | ("linux", "s390x"), 16 | ("freebsd", "amd64"), 17 | ("freebsd", "386"), 18 | ("openbsd", "amd64"), 19 | ("openbsd", "386"), 20 | ("dragonfly", "amd64"), 21 | ] 22 | -------------------------------------------------------------------------------- /release/bazel/zip.bzl: -------------------------------------------------------------------------------- 1 | # Copied from google/nomulus project as we don't want to import the whole repository. 2 | 3 | ZIPPER = "@bazel_tools//tools/zip:zipper" 4 | 5 | def long_path(ctx, file_): 6 | """Constructs canonical runfile path relative to TEST_SRCDIR. 7 | Args: 8 | ctx: A Skylark rule context. 9 | file_: A File object that should appear in the runfiles for the test. 10 | Returns: 11 | A string path relative to TEST_SRCDIR suitable for use in tests and 12 | testing infrastructure. 13 | """ 14 | if file_.short_path.startswith("../"): 15 | return file_.short_path[3:] 16 | if file_.owner and file_.owner.workspace_root: 17 | return file_.owner.workspace_root + "/" + file_.short_path 18 | return ctx.workspace_name + "/" + file_.short_path 19 | 20 | def collect_runfiles(targets): 21 | """Aggregates runfiles from targets. 22 | Args: 23 | targets: A list of Bazel targets. 24 | Returns: 25 | A list of Bazel files. 26 | """ 27 | data = depset() 28 | for target in targets: 29 | if hasattr(target, "runfiles"): 30 | data += target.runfiles.files 31 | continue 32 | if hasattr(target, "data_runfiles"): 33 | data += target.data_runfiles.files 34 | if hasattr(target, "default_runfiles"): 35 | data += target.default_runfiles.files 36 | return data 37 | 38 | def _get_runfiles(target, attribute): 39 | runfiles = getattr(target, attribute, None) 40 | if runfiles: 41 | return runfiles.files 42 | return [] 43 | 44 | def _zip_file(ctx): 45 | """Implementation of zip_file() rule.""" 46 | for s, d in ctx.attr.mappings.items(): 47 | if (s.startswith("/") or s.endswith("/") or 48 | d.startswith("/") or d.endswith("/")): 49 | fail("mappings should not begin or end with slash") 50 | srcs = depset(transitive = [depset(ctx.files.srcs),depset(ctx.files.data),depset(collect_runfiles(ctx.attr.data))]) 51 | mapped = _map_sources(ctx, srcs, ctx.attr.mappings) 52 | cmd = [ 53 | "#!/bin/sh", 54 | "set -e", 55 | 'repo="$(pwd)"', 56 | 'zipper="${repo}/%s"' % ctx.file._zipper.path, 57 | 'archive="${repo}/%s"' % ctx.outputs.out.path, 58 | 'tmp="$(mktemp -d "${TMPDIR:-/tmp}/zip_file.XXXXXXXXXX")"', 59 | 'cd "${tmp}"', 60 | ] 61 | cmd += [ 62 | '"${zipper}" x "${repo}/%s"' % dep.zip_file.path 63 | for dep in ctx.attr.deps 64 | ] 65 | cmd += ["rm %s" % filename for filename in ctx.attr.exclude] 66 | cmd += [ 67 | 'mkdir -p "${tmp}/%s"' % zip_path 68 | for zip_path in depset( 69 | [ 70 | zip_path[:zip_path.rindex("/")] 71 | for _, zip_path in mapped 72 | if "/" in zip_path 73 | ], 74 | ).to_list() 75 | ] 76 | cmd += [ 77 | 'ln -sf "${repo}/%s" "${tmp}/%s"' % (path, zip_path) 78 | for path, zip_path in mapped 79 | ] 80 | cmd += [ 81 | ("find . | sed 1d | cut -c 3- | LC_ALL=C sort" + 82 | ' | xargs "${zipper}" cC "${archive}"'), 83 | 'cd "${repo}"', 84 | 'rm -rf "${tmp}"', 85 | ] 86 | script = ctx.actions.declare_file("%s/%s.sh" % (ctx.bin_dir, ctx.label.name)) 87 | print(script) 88 | ctx.actions.write(output = script, content = "\n".join(cmd), is_executable = True) 89 | inputs = [ctx.file._zipper] 90 | inputs += [dep.zip_file for dep in ctx.attr.deps] 91 | inputs += list(srcs.to_list()) 92 | ctx.actions.run( 93 | inputs = inputs, 94 | outputs = [ctx.outputs.out], 95 | executable = script, 96 | mnemonic = "zip", 97 | progress_message = "Creating zip with %d inputs %s" % ( 98 | len(inputs), 99 | ctx.label, 100 | ), 101 | ) 102 | return struct(files = depset([ctx.outputs.out]), zip_file = ctx.outputs.out) 103 | 104 | def _map_sources(ctx, srcs, mappings): 105 | """Calculates paths in zip file for srcs.""" 106 | 107 | # order mappings with more path components first 108 | mappings = sorted([ 109 | (-len(source.split("/")), source, dest) 110 | for source, dest in mappings.items() 111 | ]) 112 | 113 | # get rid of the integer part of tuple used for sorting 114 | mappings = [(source, dest) for _, source, dest in mappings] 115 | mappings_indexes = range(len(mappings)) 116 | used = {i: False for i in mappings_indexes} 117 | mapped = [] 118 | for file_ in srcs.to_list(): 119 | run_path = long_path(ctx, file_) 120 | zip_path = None 121 | for i in mappings_indexes: 122 | source = mappings[i][0] 123 | dest = mappings[i][1] 124 | if not source: 125 | if dest: 126 | zip_path = dest + "/" + run_path 127 | else: 128 | zip_path = run_path 129 | elif source == run_path: 130 | if dest: 131 | zip_path = dest 132 | else: 133 | zip_path = run_path 134 | elif run_path.startswith(source + "/"): 135 | if dest: 136 | zip_path = dest + run_path[len(source):] 137 | else: 138 | zip_path = run_path[len(source) + 1:] 139 | else: 140 | continue 141 | used[i] = True 142 | break 143 | if not zip_path: 144 | fail("no mapping matched: " + run_path) 145 | mapped += [(file_.path, zip_path)] 146 | for i in mappings_indexes: 147 | if not used[i]: 148 | fail('superfluous mapping: "%s" -> "%s"' % mappings[i]) 149 | return mapped 150 | 151 | pkg_zip = rule( 152 | implementation = _zip_file, 153 | attrs = { 154 | "out": attr.output(mandatory = True), 155 | "srcs": attr.label_list(allow_files = True), 156 | "data": attr.label_list(allow_files = True), 157 | "deps": attr.label_list(providers = ["zip_file"]), 158 | "exclude": attr.string_list(), 159 | "mappings": attr.string_dict(), 160 | "_zipper": attr.label(default = Label(ZIPPER), allow_single_file = True), 161 | }, 162 | ) 163 | -------------------------------------------------------------------------------- /release/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "node_id": 0, 3 | "key": "", 4 | "api_host": "" 5 | } -------------------------------------------------------------------------------- /release/mapping.bzl: -------------------------------------------------------------------------------- 1 | def gen_mappings(os, arch): 2 | return { 3 | "vnet/release": "", 4 | "vnet/release/systemd": "systemd", 5 | "vnet/release/systemv": "systemv", 6 | "vnet/cmd/shadowsocksr-server/" + os + "/" + arch: "", 7 | } 8 | -------------------------------------------------------------------------------- /release/systemd/vnet.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=VNet Service 3 | After=network.target 4 | Wants=network.target 5 | 6 | [Service] 7 | Type=simple 8 | ExecStart=/usr/bin/vnet/vnet --config /etc/vnet/config.json 9 | Restart=on-failure 10 | # Don't restart in the case of configuration error 11 | #RestartPreventExitStatus=23 12 | LimitNOFILE=20480000 13 | RestartSec=5 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /release/systemv/vnet: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: vnet 4 | # Required-Start: $network $local_fs $remote_fs 5 | # Required-Stop: $remote_fs 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 0 1 6 8 | # Short-Description: vnet proxy services 9 | # Description: vnet proxy services 10 | ### END INIT INFO 11 | 12 | DESC=vnet 13 | NAME=vnet 14 | DAEMON=/usr/bin/vnet/vnet 15 | PIDFILE=/var/run/$NAME.pid 16 | SCRIPTNAME=/etc/init.d/$NAME 17 | 18 | 19 | # Exit if the package is not installed 20 | [ -x $DAEMON ] || exit 0 21 | 22 | # Read configuration variable file if it is present 23 | [ -r /etc/default/$NAME ] && . /etc/default/$NAME 24 | 25 | # Load the VERBOSE setting and other rcS variables 26 | . /lib/init/vars.sh 27 | 28 | # Define LSB log_* functions. 29 | # Depend on lsb-base (>= 3.0-6) to ensure that this file is present. 30 | . /lib/lsb/init-functions 31 | 32 | 33 | # 34 | # Function that starts the daemon/service 35 | # 36 | do_start() 37 | { 38 | mkdir -p /var/log/vnet 39 | # Return 40 | # 0 if daemon has been started 41 | # 1 if daemon was already running 42 | # 2 if daemon could not be started 43 | # 3 if configuration file not ready for daemon 44 | start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ 45 | || return 1 46 | start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --background -m -- $DAEMON_OPTS \ 47 | || return 2 48 | # Add code here, if necessary, that waits for the process to be ready 49 | # to handle requests from services started subsequently which depend 50 | # on this one. As a last resort, sleep for some time. 51 | } 52 | 53 | # 54 | # Function that stops the daemon/service 55 | # 56 | do_stop() 57 | { 58 | # Return 59 | # 0 if daemon has been stopped 60 | # 1 if daemon was already stopped 61 | # 2 if daemon could not be stopped 62 | # other if a failure occurred 63 | start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE 64 | RETVAL="$?" 65 | [ "$RETVAL" = 2 ] && return 2 66 | # Wait for children to finish too if this is a daemon that forks 67 | # and if the daemon is only ever run from this initscript. 68 | # If the above conditions are not satisfied then add some other code 69 | # that waits for the process to drop all resources that could be 70 | # needed by services started subsequently. A last resort is to 71 | # sleep for some time. 72 | start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON 73 | [ "$?" = 2 ] && return 2 74 | # Many daemons don't delete their pidfiles when they exit. 75 | rm -f $PIDFILE 76 | return "$RETVAL" 77 | } 78 | 79 | # 80 | # Function that sends a SIGHUP to the daemon/service 81 | # 82 | do_reload() { 83 | # 84 | # If the daemon can reload its configuration without 85 | # restarting (for example, when it is sent a SIGHUP), 86 | # then implement that here. 87 | # 88 | start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE 89 | return 0 90 | } 91 | 92 | case "$1" in 93 | start) 94 | [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME" 95 | do_start 96 | case "$?" in 97 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 98 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 99 | esac 100 | ;; 101 | stop) 102 | [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" 103 | do_stop 104 | case "$?" in 105 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 106 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 107 | esac 108 | ;; 109 | status) 110 | status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? 111 | ;; 112 | reload|force-reload) 113 | # 114 | # If do_reload() is not implemented then leave this commented out 115 | # and leave 'force-reload' as an alias for 'restart'. 116 | # 117 | log_daemon_msg "Reloading $DESC" "$NAME" 118 | do_reload 119 | log_end_msg $? 120 | ;; 121 | restart|force-reload) 122 | # 123 | # If the "reload" option is implemented then remove the 124 | # 'force-reload' alias 125 | # 126 | log_daemon_msg "Restarting $DESC" "$NAME" 127 | do_stop 128 | case "$?" in 129 | 0|1) 130 | do_start 131 | case "$?" in 132 | 0) log_end_msg 0 ;; 133 | 1) log_end_msg 1 ;; # Old process is still running 134 | *) log_end_msg 1 ;; # Failed to start 135 | esac 136 | ;; 137 | *) 138 | # Failed to stop 139 | log_end_msg 1 140 | ;; 141 | esac 142 | ;; 143 | *) 144 | #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 145 | echo "Usage: $SCRIPTNAME {start|stop|status|reload|restart|force-reload}" >&2 146 | exit 3 147 | ;; 148 | esac 149 | 150 | -------------------------------------------------------------------------------- /release/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "latest": "v2.1.0", 3 | "history": [ 4 | "v2.1.0" 5 | ] 6 | } -------------------------------------------------------------------------------- /remote: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProxyPanel/VNet-SSR/d00ae73b45defec5703d449962680cdc006755b7/remote -------------------------------------------------------------------------------- /service/limit.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "github.com/ProxyPanel/VNet-SSR/core" 6 | "github.com/ProxyPanel/VNet-SSR/model" 7 | "github.com/sirupsen/logrus" 8 | "golang.org/x/time/rate" 9 | "sync" 10 | ) 11 | 12 | var ( 13 | limitInstance = NewLimit() 14 | ) 15 | 16 | func GetLimitInstance() *Limit { 17 | return limitInstance; 18 | } 19 | 20 | func init() { 21 | GetSSRManager().RegisterAddUserHandle(func(userInfo *model.UserInfo) { 22 | // prefer use node limit when node limit less then user limit 23 | if core.GetApp().NodeInfo().SpeedLimit == 0 || 24 | (core.GetApp().NodeInfo().SpeedLimit > userInfo.Limit && userInfo.Limit != 0) { 25 | limitInstance.Set(userInfo.Port, int(userInfo.Limit)) 26 | } else { 27 | limitInstance.Set(userInfo.Port, int(core.GetApp().NodeInfo().SpeedLimit)) 28 | } 29 | }) 30 | 31 | GetSSRManager().RegisterDelUserHandle(func(uid int) { 32 | limitInstance.Del(uid) 33 | }) 34 | } 35 | 36 | type Limit struct { 37 | gLocker sync.Locker 38 | upLimits map[int]*rate.Limiter 39 | downLimits map[int]*rate.Limiter 40 | } 41 | 42 | func NewLimit() *Limit { 43 | return &Limit{ 44 | gLocker: new(sync.Mutex), 45 | upLimits: make(map[int]*rate.Limiter), 46 | downLimits: make(map[int]*rate.Limiter), 47 | } 48 | } 49 | 50 | func (l *Limit) Set(uid int, limition int) { 51 | l.gLocker.Lock() 52 | defer l.gLocker.Unlock() 53 | 54 | if limition != 0 { 55 | logrus.Infof("limit add %v:%v", uid, limition) 56 | l.upLimits[uid] = rate.NewLimiter(rate.Limit(limition), int(limition)) 57 | l.downLimits[uid] = rate.NewLimiter(rate.Limit(limition), int(limition)) 58 | } else { 59 | logrus.Infof("limit ignore zero limit uid: %v", uid) 60 | } 61 | 62 | } 63 | 64 | func (l *Limit) Del(uid int) { 65 | l.gLocker.Lock() 66 | defer l.gLocker.Unlock() 67 | logrus.Infof("limit remove %v", uid) 68 | delete(l.upLimits, uid) 69 | } 70 | 71 | func (l *Limit) UpLimit(uid, n int) error { 72 | if l.upLimits[uid] != nil { 73 | return l.upLimits[uid].WaitN(context.Background(), n) 74 | } 75 | return nil 76 | } 77 | 78 | func (l *Limit) DownLimit(uid, n int) error { 79 | if l.downLimits[uid] != nil { 80 | return l.downLimits[uid].WaitN(context.Background(), n) 81 | } 82 | return nil 83 | } 84 | 85 | func (l *Limit) Wait(uid, n int) error { 86 | if l.upLimits[uid] != nil { 87 | return l.upLimits[uid].WaitN(context.Background(), n) 88 | } 89 | return nil 90 | } 91 | -------------------------------------------------------------------------------- /service/rule.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "fmt" 5 | "github.com/ProxyPanel/VNet-SSR/api/client" 6 | "github.com/ProxyPanel/VNet-SSR/common/cache" 7 | "github.com/ProxyPanel/VNet-SSR/common/log" 8 | "github.com/ProxyPanel/VNet-SSR/model" 9 | "regexp" 10 | "time" 11 | ) 12 | 13 | const ( 14 | RuleTypeReg = "reg" 15 | RuleTypeDomain = "domain" 16 | RuleTypeIp = "ip" 17 | 18 | RuleModeAllow = "allow" 19 | RuleModeReject = "reject" 20 | RuleModeAll = "all" 21 | ) 22 | 23 | var ( 24 | ruleServiceInstance = NewRuleService() 25 | ) 26 | 27 | func GetRuleService() *RuleService { 28 | return ruleServiceInstance 29 | } 30 | 31 | type RuleItemComiled struct { 32 | model.RuleItem 33 | compile interface{} 34 | } 35 | type RuleService struct { 36 | mode string 37 | rules []*RuleItemComiled 38 | cache *cache.LRU 39 | } 40 | 41 | func NewRuleService() *RuleService { 42 | r := new(RuleService) 43 | r.Reset() 44 | return r 45 | } 46 | 47 | // Reset RuleService set all field to default. 48 | func (r *RuleService) Reset() { 49 | r.cache = cache.NewLruCache(5 * time.Second) 50 | r.rules = make([]*RuleItemComiled, 0, 256) 51 | r.mode = RuleModeAll 52 | } 53 | 54 | func (r *RuleService) LoadFromApi() error { 55 | rule, err := client.GetNodeRule() 56 | if err != nil { 57 | return err 58 | } 59 | r.Load(rule) 60 | return nil 61 | } 62 | 63 | // Load RuleService load rule 64 | func (r *RuleService) Load(rule *model.Rule) { 65 | r.Reset() 66 | r.mode = rule.Model 67 | for _, item := range rule.Rules { 68 | switch item.Type { 69 | case RuleTypeReg: 70 | regexCompiled, err := regexp.Compile(item.Pattern) 71 | if err != nil { 72 | log.Error("compile regex %s error: %s ", item.Pattern, err.Error()) 73 | continue 74 | } 75 | 76 | r.rules = append(r.rules, &RuleItemComiled{ 77 | RuleItem: item, 78 | compile: regexCompiled, 79 | }) 80 | case RuleTypeIp: 81 | r.rules = append(r.rules, &RuleItemComiled{ 82 | RuleItem: item, 83 | compile: item.Pattern, 84 | }) 85 | case RuleTypeDomain: 86 | r.rules = append(r.rules, &RuleItemComiled{ 87 | RuleItem: item, 88 | compile: item.Pattern, 89 | }) 90 | } 91 | } 92 | log.Info("loaded rule set: %+v", *rule) 93 | } 94 | 95 | func (r *RuleService) JudgeHostWithReport(ipOrDomain string, port int) bool { 96 | ruleId, result, isFromCache := r.judgeWithCache(ipOrDomain, port) 97 | 98 | if isFromCache { 99 | return result 100 | } 101 | 102 | uid := GetSSRManager().PortToUid(port) 103 | if !result { 104 | go func() { 105 | err := client.PostTrigger(model.Trigger{ 106 | Uid: uid, 107 | RuleId: ruleId, 108 | Reason: ipOrDomain, 109 | }) 110 | if err != nil { 111 | log.Err(err) 112 | } 113 | }() 114 | } 115 | return result 116 | } 117 | 118 | // add cache because this function has a lot invoke 119 | func (r *RuleService) judgeWithCache(ipOrDomain string, port int) (ruleId int, result bool, isFromCache bool) { 120 | cacheKey := fmt.Sprintf("%s:%v", ipOrDomain, port) 121 | value, isFromCache := r.cache.Get(cacheKey).(struct { 122 | RuleId int 123 | Result bool 124 | }) 125 | 126 | if isFromCache { 127 | return value.RuleId, value.Result, isFromCache 128 | } 129 | 130 | ruleId, result = r.judge(ipOrDomain) 131 | 132 | r.cache.Put(cacheKey, struct { 133 | RuleId int 134 | Result bool 135 | }{ 136 | ruleId, result, 137 | }) 138 | 139 | return ruleId, result, isFromCache 140 | } 141 | 142 | func (r *RuleService) judge(host string) (int, bool) { 143 | // check if cache have value then return cache value 144 | if r.mode == RuleModeAll { 145 | return 0, true 146 | } 147 | 148 | for _, regexItem := range r.rules { 149 | switch regexItem.Type { 150 | case RuleTypeReg: 151 | regexCompiled, ok := regexItem.compile.(*regexp.Regexp) 152 | if !ok { 153 | log.Error("regex %s break", regexItem.Pattern) 154 | continue 155 | } 156 | 157 | if r.mode == RuleModeAllow && regexCompiled.Match([]byte(host)) { 158 | return 0, true 159 | } 160 | 161 | if r.mode == RuleModeReject && regexCompiled.Match([]byte(host)) { 162 | return regexItem.Id, false 163 | } 164 | 165 | case RuleTypeDomain, RuleTypeIp: 166 | if r.mode == RuleModeAllow && regexItem.Pattern == host { 167 | return 0, true 168 | } 169 | 170 | if r.mode == RuleModeReject && regexItem.Pattern == host { 171 | return regexItem.Id, false 172 | } 173 | default: 174 | continue 175 | } 176 | } 177 | 178 | if r.mode == RuleModeReject { 179 | return 0, true 180 | } 181 | return 0, false 182 | } 183 | -------------------------------------------------------------------------------- /service/rule_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/ProxyPanel/VNet-SSR/model" 7 | "github.com/tidwall/gjson" 8 | "regexp" 9 | "testing" 10 | ) 11 | 12 | func BenchmarkTest(t *testing.B) { 13 | regs := make([]*regexp.Regexp, 0, 10000) 14 | for i := 0; i < 10000; i++ { 15 | reg, _ := regexp.Compile(fmt.Sprintf("www.test%d.com", i)) 16 | regs = append(regs, reg) 17 | } 18 | t.ResetTimer() 19 | for i := 0; i < t.N; i++ { 20 | index := t.N % 2048 21 | test := fmt.Sprintf("asdasdasdadasdaswww.test%d.com", index) 22 | for _, item := range regs { 23 | item.Match([]byte(test)) 24 | } 25 | } 26 | } 27 | 28 | func TestRuleServiceBlackList(t *testing.T) { 29 | content := `{ 30 | "status": "success", 31 | "code": 200, 32 | "data": { 33 | "mode": "reject", 34 | "rules": [ 35 | { 36 | "id": 2, 37 | "type": "reg", 38 | "pattern": "(Subject|HELO|SMTP)" 39 | }, 40 | { 41 | "id": 3, 42 | "type": "domain", 43 | "pattern": "baidu.com" 44 | }, 45 | { 46 | "id": 4, 47 | "type": "reg", 48 | "pattern": "(api|ps|sv|offnavi|newvector|ulog\\.imap|newloc)(\\.map|)\\.(baidu|n\\.shifen)\\.com" 49 | }, 50 | { 51 | "id": 5, 52 | "type": "reg", 53 | "pattern": "(.*\\.||)(dafahao|minghui|dongtaiwang|epochtimes|ntdtv|falundafa|wujieliulan|zhengjian)\\.(org|com|net)" 54 | }, 55 | { 56 | "id": 7, 57 | "type": "ip", 58 | "pattern": "192.168.1.1" 59 | } 60 | ] 61 | }, 62 | "message": "获取节点审计规则成功" 63 | }` 64 | 65 | rule := new(model.Rule) 66 | err := json.Unmarshal([]byte(gjson.Get(content, "data").String()), rule) 67 | if err != nil { 68 | t.Fatal(err) 69 | } 70 | GetRuleService().Load(rule) 71 | if _, ok, _ := GetRuleService().judgeWithCache("ntdtv.com",0); ok { 72 | t.Fatal("ntd.tv test fail") 73 | } 74 | 75 | if _, ok, _ := GetRuleService().judgeWithCache("baidu.com",0); ok { 76 | t.Fatal("baidu.com test fail") 77 | } 78 | 79 | if _, ok, _ := GetRuleService().judgeWithCache("192.168.1.1",0); !ok { 80 | t.Fatal("192.168.1.1 test fail") 81 | } 82 | } 83 | 84 | func TestRuleServiceWhiteList(t *testing.T) { 85 | content := `{ 86 | "status": "success", 87 | "code": 200, 88 | "data": { 89 | "mode": "allow", 90 | "rules": [ 91 | { 92 | "id": 2, 93 | "type": "reg", 94 | "pattern": "(Subject|HELO|SMTP)" 95 | }, 96 | { 97 | "id": 3, 98 | "type": "domain", 99 | "pattern": "baidu.com" 100 | }, 101 | { 102 | "id": 4, 103 | "type": "reg", 104 | "pattern": "(api|ps|sv|offnavi|newvector|ulog\\.imap|newloc)(\\.map|)\\.(baidu|n\\.shifen)\\.com" 105 | }, 106 | { 107 | "id": 5, 108 | "type": "reg", 109 | "pattern": "(.*\\.||)(dafahao|minghui|dongtaiwang|epochtimes|ntdtv|falundafa|wujieliulan|zhengjian)\\.(org|com|net)" 110 | }, 111 | { 112 | "id": 7, 113 | "type": "ip", 114 | "pattern": "192.168.1.1" 115 | } 116 | ] 117 | }, 118 | "message": "获取节点审计规则成功" 119 | }` 120 | 121 | rule := new(model.Rule) 122 | err := json.Unmarshal([]byte(gjson.Get(content, "data").String()), rule) 123 | if err != nil { 124 | t.Fatal(err) 125 | } 126 | GetRuleService().Load(rule) 127 | if _, ok, _ := GetRuleService().judgeWithCache("ntdtv.com",0); !ok { 128 | t.Fatal("ntd.tv test fail") 129 | } 130 | 131 | if _, ok, _ := GetRuleService().judgeWithCache("baidu.com",0); !ok { 132 | t.Fatal("baidu.com test fail") 133 | } 134 | 135 | if _, ok, _ := GetRuleService().judgeWithCache("192.168.1.1",0); !ok { 136 | t.Fatal("192.168.1.1 test fail") 137 | } 138 | 139 | if _, ok, _ := GetRuleService().judgeWithCache("google.com",0); ok { 140 | t.Fatal("google.com test fail") 141 | } 142 | } 143 | 144 | func TestJudgeCache(t *testing.T){ 145 | content := `{ 146 | "status": "success", 147 | "code": 200, 148 | "data": { 149 | "mode": "allow", 150 | "rules": [ 151 | { 152 | "id": 2, 153 | "type": "reg", 154 | "pattern": "(Subject|HELO|SMTP)" 155 | }, 156 | { 157 | "id": 3, 158 | "type": "domain", 159 | "pattern": "baidu.com" 160 | }, 161 | { 162 | "id": 4, 163 | "type": "reg", 164 | "pattern": "(api|ps|sv|offnavi|newvector|ulog\\.imap|newloc)(\\.map|)\\.(baidu|n\\.shifen)\\.com" 165 | }, 166 | { 167 | "id": 5, 168 | "type": "reg", 169 | "pattern": "(.*\\.||)(dafahao|minghui|dongtaiwang|epochtimes|ntdtv|falundafa|wujieliulan|zhengjian)\\.(org|com|net)" 170 | }, 171 | { 172 | "id": 7, 173 | "type": "ip", 174 | "pattern": "192.168.1.1" 175 | } 176 | ] 177 | }, 178 | "message": "获取节点审计规则成功" 179 | }` 180 | 181 | rule := new(model.Rule) 182 | err := json.Unmarshal([]byte(gjson.Get(content, "data").String()), rule) 183 | if err != nil { 184 | t.Fatal(err) 185 | } 186 | GetRuleService().Load(rule) 187 | 188 | _, ok, isCache := GetRuleService().judgeWithCache("ntdtv.com",0) 189 | if !ok && !isCache { 190 | t.Fatal("ntd.tv cache test fail") 191 | } 192 | 193 | _, ok, isCache = GetRuleService().judgeWithCache("ntdtv.com",0) 194 | if !ok && isCache { 195 | t.Fatal("ntd.tv cache test fail") 196 | } 197 | 198 | _, ok, isCache = GetRuleService().judgeWithCache("ntdtv.com",1) 199 | if !ok && !isCache { 200 | t.Fatal("ntd.tv cache test fail") 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /service/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | func Start() (err error) { 4 | if err = GetSSRManager().Start(); err != nil { 5 | return err 6 | } 7 | 8 | if err = GetRuleService().LoadFromApi(); err != nil { 9 | return err 10 | } 11 | 12 | return err 13 | } 14 | 15 | func Reload() error { 16 | if err := GetSSRManager().Reload(); err != nil { 17 | return err 18 | } 19 | if err := GetRuleService().LoadFromApi(); err != nil { 20 | return err 21 | } 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /service/shadowsocksr_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | func ExampleS(){ 4 | 5 | //Output: 6 | } 7 | -------------------------------------------------------------------------------- /test_all.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | go test ./... -------------------------------------------------------------------------------- /testing/servers/http/http.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "crypto/rand" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "time" 9 | 10 | "github.com/ProxyPanel/VNet-SSR/common/log" 11 | "github.com/ProxyPanel/VNet-SSR/utils/datasize" 12 | "github.com/gin-gonic/gin" 13 | ) 14 | 15 | type FakeFile struct { 16 | Size int64 17 | Offset int64 18 | } 19 | 20 | func NewFakeFile(size int64) *FakeFile { 21 | return &FakeFile{ 22 | Size: size, 23 | Offset: 0, 24 | } 25 | } 26 | func (f *FakeFile) Read(p []byte) (n int, err error) { 27 | if f.Offset >= f.Size { 28 | return 0, io.EOF 29 | } 30 | remain := f.Size - f.Offset 31 | if int64(cap(p)) < remain { 32 | n, err = rand.Read(p) 33 | } else { 34 | n, err = rand.Read(p[:int(remain)]) 35 | } 36 | 37 | f.Offset = f.Offset + int64(n) 38 | return n, err 39 | } 40 | 41 | func (f *FakeFile) Seek(offset int64, whence int) (int64, error) { 42 | if whence == io.SeekEnd { 43 | f.Offset = f.Size + offset 44 | 45 | } 46 | if whence == io.SeekStart { 47 | f.Offset = 0 + offset 48 | } 49 | if whence == io.SeekCurrent { 50 | f.Offset = f.Offset + offset 51 | } 52 | if f.Offset > f.Size || f.Offset < 0 { 53 | return 0, fmt.Errorf("offset is out of bounds") 54 | } 55 | return f.Offset, nil 56 | } 57 | 58 | func StartFakeFileServer() *http.Server { 59 | r := gin.Default() 60 | r.GET("download", func(c *gin.Context) { 61 | sizeStr := c.Query("size") 62 | size, err := datasize.Parse(sizeStr) 63 | if err != nil { 64 | log.Err(err) 65 | } 66 | file := NewFakeFile(int64(size)) 67 | c.Writer.Header().Add("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.test"`, sizeStr)) 68 | http.ServeContent(c.Writer, c.Request, fmt.Sprintf("%s.test", sizeStr), time.Now(), file) 69 | }) 70 | server := &http.Server{ 71 | Addr: ":8080", 72 | Handler: r, 73 | } 74 | go func() { 75 | if err := server.ListenAndServe(); err != nil { 76 | if err == http.ErrServerClosed { 77 | log.Info("FakeFileServer closed") 78 | } else { 79 | log.Error("Server closed unexpect") 80 | } 81 | } 82 | }() 83 | return server 84 | } 85 | -------------------------------------------------------------------------------- /utils/addrx/addrx.go: -------------------------------------------------------------------------------- 1 | package addrx 2 | 3 | import ( 4 | "github.com/ProxyPanel/VNet-SSR/utils/langx" 5 | "io/ioutil" 6 | "net" 7 | "net/http" 8 | "strconv" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | func GetIPFromAddr(addr net.Addr) string { 14 | switch addr.(type) { 15 | case *net.TCPAddr: 16 | tcpAddr := addr.(*net.TCPAddr) 17 | return tcpAddr.IP.String() 18 | case *net.UDPAddr: 19 | udpAddr := addr.(*net.UDPAddr) 20 | return udpAddr.IP.String() 21 | case nil: 22 | return "" 23 | default: 24 | return "" 25 | } 26 | } 27 | 28 | func GetPortFromAddr(addr net.Addr) int { 29 | switch addr.(type) { 30 | case *net.TCPAddr: 31 | tcpAddr := addr.(*net.TCPAddr) 32 | return tcpAddr.Port 33 | case *net.UDPAddr: 34 | udpAddr := addr.(*net.UDPAddr) 35 | return udpAddr.Port 36 | case nil: 37 | return 0 38 | default: 39 | return 0 40 | } 41 | } 42 | 43 | func GetNetworkFromAddr(addr net.Addr) string { 44 | return addr.Network() 45 | } 46 | 47 | func ParseAddrFromString(network, addr string) (net.Addr, error) { 48 | var addrConvert net.Addr 49 | var err error 50 | switch network { 51 | case "tcp", "tcp4", "tcp6": 52 | addrConvert, err = net.ResolveTCPAddr(network, addr) 53 | case "udp", "udp4", "udp6": 54 | addrConvert, err = net.ResolveUDPAddr(network, addr) 55 | } 56 | if err != nil { 57 | return nil, err 58 | } 59 | return addrConvert, nil 60 | } 61 | 62 | func SplitIpFromAddr(addr string) string { 63 | ip, _, err := net.SplitHostPort(addr) 64 | if err != nil { 65 | return "" 66 | } 67 | return ip 68 | } 69 | 70 | func SplitPortFromAddr(addr string) int { 71 | _, port, err := net.SplitHostPort(addr) 72 | if err != nil { 73 | return 0 74 | } 75 | return langx.FirstResult(strconv.Atoi, port).(int) 76 | } 77 | 78 | func GetPublicIp() (string, error) { 79 | client := http.Client{ 80 | Timeout: time.Duration(3 * time.Second), 81 | } 82 | res, err := client.Get("https://api.ip.sb/ip") 83 | if err != nil { 84 | return "", err 85 | } 86 | 87 | ip, err := ioutil.ReadAll(res.Body) 88 | if err != nil { 89 | return "", err 90 | } 91 | 92 | return strings.Trim(string(ip), "\n"), nil 93 | } 94 | 95 | // func GetAddressType(addrx string) string { 96 | // var ( 97 | // ipv4 = `^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?):?([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]?)$` 98 | // ipv6 = `^(([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)):?([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]?)$` 99 | // domain = `^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|\b-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|\b-){0,61}[0-9A-Za-z])?)*\.?:?([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]?)$` 100 | // ) 101 | // } 102 | -------------------------------------------------------------------------------- /utils/addrx/addrx_test.go: -------------------------------------------------------------------------------- 1 | package addrx 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | ) 7 | 8 | func Benchmark_GetIPFromAddr(t *testing.B) { 9 | client, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:808") 10 | for i := 0; i < t.N; i++ { 11 | GetIPFromAddr(client) 12 | } 13 | t.Log(GetIPFromAddr(client)) 14 | t.ReportAllocs() 15 | } 16 | 17 | func Benchmark_GetPortFromAddr(t *testing.B) { 18 | client, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:808") 19 | for i := 0; i < t.N; i++ { 20 | GetPortFromAddr(client) 21 | } 22 | t.Log(GetPortFromAddr(client)) 23 | t.ReportAllocs() 24 | } 25 | 26 | func Benchmark_GetNetworkFromAddr(t *testing.B) { 27 | client, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:808") 28 | for i := 0; i < t.N; i++ { 29 | GetNetworkFromAddr(client) 30 | } 31 | t.Log(GetNetworkFromAddr(client)) 32 | t.ReportAllocs() 33 | } 34 | -------------------------------------------------------------------------------- /utils/arrayx/arrayx.go: -------------------------------------------------------------------------------- 1 | package arrayx 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | func FindStringInArray(obj string, target []string) bool { 8 | for _, item := range target { 9 | if obj == item { 10 | return true 11 | } 12 | } 13 | return false 14 | } 15 | 16 | func In(obj interface{}, collections interface{}) bool { 17 | refCollections := reflect.ValueOf(collections) 18 | refObj := reflect.ValueOf(obj) 19 | if refCollections.Kind() != reflect.Slice { 20 | return false 21 | } 22 | 23 | for i := 0; i < refCollections.Len(); i++ { 24 | items := refCollections.Index(i) 25 | if items.Kind() != refObj.Kind() { 26 | return false 27 | } 28 | 29 | switch items.Kind() { 30 | case reflect.String: 31 | if items.String() == refObj.String() { 32 | return true 33 | } 34 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 35 | if items.Int() == refObj.Int() { 36 | return true 37 | } 38 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 39 | if items.Uint() == refObj.Uint() { 40 | return true 41 | } 42 | } 43 | } 44 | 45 | return false 46 | 47 | } 48 | -------------------------------------------------------------------------------- /utils/arrayx/arrayx_test.go: -------------------------------------------------------------------------------- 1 | package arrayx 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestFindStringInArray(t *testing.T) { 8 | type args struct { 9 | obj string 10 | target []string 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | want bool 16 | }{ 17 | // TODO: Add test cases. 18 | { 19 | name: "test", 20 | args: args{ 21 | obj: "abc", 22 | target: []string{"abc", "bcd"}, 23 | }, 24 | want: true, 25 | }, 26 | { 27 | name: "test", 28 | args: args{ 29 | obj: "abc", 30 | target: []string{"cbd", "bcd"}, 31 | }, 32 | want: false, 33 | }, 34 | } 35 | for _, tt := range tests { 36 | t.Run(tt.name, func(t *testing.T) { 37 | if got := FindStringInArray(tt.args.obj, tt.args.target); got != tt.want { 38 | t.Errorf("FindStringInArray() = %v, want %v", got, tt.want) 39 | } 40 | }) 41 | } 42 | } 43 | 44 | func TestIn(t *testing.T) { 45 | 46 | if In("asdasd", []string{"a", "b", "c"}) { 47 | t.Fatal("test faild") 48 | } 49 | 50 | if !In("asdasd", []string{"a", "b", "asdasd"}) { 51 | t.Fatal("test faild") 52 | } 53 | 54 | if !In(1, []int{1, 2, 3}) { 55 | t.Fatal("test faild") 56 | } 57 | 58 | if In(5, []int{1, 2, 3}) { 59 | t.Fatal("test faild") 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /utils/auto_increment.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "sync/atomic" 4 | 5 | var id int64 6 | 7 | func getNextID() int64 { 8 | reply := atomic.AddInt64(&id, 1) 9 | return reply 10 | } 11 | 12 | func NextID() int64 { 13 | return getNextID() 14 | } 15 | -------------------------------------------------------------------------------- /utils/binaryx/binaryx.go: -------------------------------------------------------------------------------- 1 | package binaryx 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | ) 7 | 8 | func BEBytesToInt(data []byte) (ret int) { 9 | buf := bytes.NewBuffer(data) 10 | binary.Read(buf, binary.BigEndian, &ret) 11 | return 12 | } 13 | 14 | func BEBytesToInt32(data []byte) (ret int32) { 15 | buf := bytes.NewBuffer(data) 16 | binary.Read(buf, binary.BigEndian, &ret) 17 | return 18 | } 19 | 20 | func BEBytesToUint32(data []byte) (ret uint32) { 21 | buf := bytes.NewBuffer(data) 22 | binary.Read(buf, binary.BigEndian, &ret) 23 | return 24 | } 25 | 26 | func LEBytesToUint64(data []byte) uint64 { 27 | if len(data) < 8 { 28 | return 0 29 | } 30 | return binary.LittleEndian.Uint64(data) 31 | } 32 | 33 | func LEUInt16ToBytes(data uint16) []byte { 34 | buf := make([]byte, 2) 35 | binary.LittleEndian.PutUint16(buf, data) 36 | return buf 37 | } 38 | 39 | func LEBytesToUint16(data []byte) uint16{ 40 | if len(data) < 2{ 41 | return 0 42 | } 43 | return binary.LittleEndian.Uint16(data) 44 | } 45 | 46 | func LEBytesToUInt32(data []byte) uint32{ 47 | if len(data) < 4{ 48 | return 0 49 | } 50 | return binary.LittleEndian.Uint32(data) 51 | } 52 | 53 | func LEUint32ToBytes(data uint32) []byte{ 54 | buf := make([]byte, 4) 55 | binary.LittleEndian.PutUint32(buf, data) 56 | return buf 57 | } 58 | 59 | func BEUInt32ToBytes(data uint32) []byte{ 60 | buf := make([]byte, 4) 61 | binary.LittleEndian.PutUint32(buf, data) 62 | return buf 63 | } -------------------------------------------------------------------------------- /utils/bytesx/bytesx.go: -------------------------------------------------------------------------------- 1 | package bytesx 2 | 3 | 4 | import "encoding/hex" 5 | 6 | func ContactSlice(datas ...[]byte) []byte { 7 | totalLen := 0 8 | for _, item := range datas { 9 | totalLen += len(item) 10 | } 11 | result := make([]byte, totalLen) 12 | count := 0 13 | for _, item := range datas { 14 | count += copy(result[count:], item) 15 | } 16 | return result 17 | } 18 | 19 | 20 | func MustHexDecode(data string) []byte { 21 | result, err := hex.DecodeString(data) 22 | if err != nil { 23 | return []byte{} 24 | } 25 | return result 26 | } -------------------------------------------------------------------------------- /utils/bytesx/bytesx_test.go: -------------------------------------------------------------------------------- 1 | package bytesx 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func ExampleContactSlice() { 9 | rst := ContactSlice([]byte{0x01, 0x02}, []byte{0x03, 0x04}) 10 | fmt.Println(rst) 11 | //Output: 12 | //[1 2 3 4] 13 | } 14 | 15 | func BenchmarkContactSlice(t *testing.B) { 16 | for i := 0; i < t.N; i++ { 17 | ContactSlice([]byte{0x01, 0x02}, []byte{0x03, 0x04}) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /utils/datasize/datasize.go: -------------------------------------------------------------------------------- 1 | package datasize 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | type ByteSize uint64 11 | 12 | const ( 13 | B ByteSize = 1 14 | KB = B << 10 15 | MB = KB << 10 16 | GB = MB << 10 17 | TB = GB << 10 18 | PB = TB << 10 19 | EB = PB << 10 20 | 21 | fnUnmarshalText string = "UnmarshalText" 22 | maxUint64 uint64 = (1 << 64) - 1 23 | cutoff uint64 = maxUint64 / 10 24 | ) 25 | 26 | var ErrBits = errors.New("unit with capital unit prefix and lower case unit (b) - bits, not bytes ") 27 | 28 | var defaultDatasize ByteSize 29 | 30 | func MustParse(s string) uint64 { 31 | size, err := Parse(s) 32 | if err != nil { 33 | return 0 34 | } 35 | return size 36 | } 37 | 38 | func Parse(s string) (bytes uint64, err error) { 39 | err = defaultDatasize.UnmarshalText([]byte(s)) 40 | if err != nil { 41 | return 42 | } 43 | bytes = defaultDatasize.Bytes() 44 | return 45 | } 46 | 47 | func HumanSize(bytes uint64) (s string, err error) { 48 | err = defaultDatasize.UnmarshalText([]byte(fmt.Sprintf("%d", bytes))) 49 | if err != nil { 50 | return 51 | } 52 | s = defaultDatasize.HumanReadable() 53 | return 54 | } 55 | func (b ByteSize) Bytes() uint64 { 56 | return uint64(b) 57 | } 58 | 59 | func (b ByteSize) KBytes() float64 { 60 | v := b / KB 61 | r := b % KB 62 | return float64(v) + float64(r)/float64(KB) 63 | } 64 | 65 | func (b ByteSize) MBytes() float64 { 66 | v := b / MB 67 | r := b % MB 68 | return float64(v) + float64(r)/float64(MB) 69 | } 70 | 71 | func (b ByteSize) GBytes() float64 { 72 | v := b / GB 73 | r := b % GB 74 | return float64(v) + float64(r)/float64(GB) 75 | } 76 | 77 | func (b ByteSize) TBytes() float64 { 78 | v := b / TB 79 | r := b % TB 80 | return float64(v) + float64(r)/float64(TB) 81 | } 82 | 83 | func (b ByteSize) PBytes() float64 { 84 | v := b / PB 85 | r := b % PB 86 | return float64(v) + float64(r)/float64(PB) 87 | } 88 | 89 | func (b ByteSize) EBytes() float64 { 90 | v := b / EB 91 | r := b % EB 92 | return float64(v) + float64(r)/float64(EB) 93 | } 94 | 95 | func (b ByteSize) String() string { 96 | switch { 97 | case b == 0: 98 | return fmt.Sprint("0B") 99 | case b%EB == 0: 100 | return fmt.Sprintf("%dEB", b/EB) 101 | case b%PB == 0: 102 | return fmt.Sprintf("%dPB", b/PB) 103 | case b%TB == 0: 104 | return fmt.Sprintf("%dTB", b/TB) 105 | case b%GB == 0: 106 | return fmt.Sprintf("%dGB", b/GB) 107 | case b%MB == 0: 108 | return fmt.Sprintf("%dMB", b/MB) 109 | case b%KB == 0: 110 | return fmt.Sprintf("%dKB", b/KB) 111 | default: 112 | return fmt.Sprintf("%dB", b) 113 | } 114 | } 115 | 116 | func (b ByteSize) HR() string { 117 | return b.HumanReadable() 118 | } 119 | 120 | func (b ByteSize) HumanReadable() string { 121 | switch { 122 | case b > EB: 123 | return fmt.Sprintf("%.2f EB", b.EBytes()) 124 | case b > PB: 125 | return fmt.Sprintf("%.2f PB", b.PBytes()) 126 | case b > TB: 127 | return fmt.Sprintf("%.2f TB", b.TBytes()) 128 | case b > GB: 129 | return fmt.Sprintf("%.2f GB", b.GBytes()) 130 | case b > MB: 131 | return fmt.Sprintf("%.2f MB", b.MBytes()) 132 | case b > KB: 133 | return fmt.Sprintf("%.2f KB", b.KBytes()) 134 | default: 135 | return fmt.Sprintf("%d B", b) 136 | } 137 | } 138 | 139 | func (b ByteSize) MarshalText() ([]byte, error) { 140 | return []byte(b.String()), nil 141 | } 142 | 143 | func (b *ByteSize) UnmarshalText(t []byte) error { 144 | var val uint64 145 | var unit string 146 | var negative bool 147 | var nval float64 148 | // copy for error message 149 | t0 := t 150 | 151 | var c byte 152 | var i int 153 | 154 | ParseLoop: 155 | for i < len(t) { 156 | c = t[i] 157 | switch { 158 | case '0' <= c && c <= '9': 159 | if val > cutoff { 160 | goto Overflow 161 | } 162 | 163 | c = c - '0' 164 | 165 | val *= 10 166 | if val > val+uint64(c) { 167 | // val+v overflows 168 | goto Overflow 169 | } 170 | if negative { 171 | nval *= 10 172 | nval += float64(c) 173 | } else { 174 | val += uint64(c) 175 | } 176 | i++ 177 | case '.' == c: 178 | negative = true 179 | i++ 180 | default: 181 | if i == 0 { 182 | goto SyntaxError 183 | } 184 | break ParseLoop 185 | } 186 | } 187 | 188 | for nval > 1 { 189 | nval /= 10 190 | } 191 | 192 | unit = strings.TrimSpace(string(t[i:])) 193 | switch unit { 194 | case "Kb", "Mb", "Gb", "Tb", "Pb", "Eb": 195 | goto BitsError 196 | } 197 | unit = strings.ToLower(unit) 198 | switch unit { 199 | case "", "b", "byte": 200 | // do nothing - already in bytes 201 | 202 | case "k", "kb", "kilo", "kilobyte", "kilobytes": 203 | if val > maxUint64/uint64(KB) { 204 | goto Overflow 205 | } 206 | val *= uint64(KB) 207 | val += uint64(nval * float64(KB)) 208 | case "m", "mb", "mega", "megabyte", "megabytes": 209 | if val > maxUint64/uint64(MB) { 210 | goto Overflow 211 | } 212 | val *= uint64(MB) 213 | val += uint64(nval * float64(MB)) 214 | 215 | case "g", "gb", "giga", "gigabyte", "gigabytes": 216 | if val > maxUint64/uint64(GB) { 217 | goto Overflow 218 | } 219 | val *= uint64(GB) 220 | val += uint64(nval * float64(GB)) 221 | 222 | case "t", "tb", "tera", "terabyte", "terabytes": 223 | if val > maxUint64/uint64(TB) { 224 | goto Overflow 225 | } 226 | val *= uint64(TB) 227 | val += uint64(nval * float64(TB)) 228 | 229 | case "p", "pb", "peta", "petabyte", "petabytes": 230 | if val > maxUint64/uint64(PB) { 231 | goto Overflow 232 | } 233 | val *= uint64(PB) 234 | val += uint64(nval * float64(PB)) 235 | 236 | case "E", "EB", "e", "eb", "eB": 237 | if val > maxUint64/uint64(EB) { 238 | goto Overflow 239 | } 240 | val *= uint64(EB) 241 | val += uint64(nval * float64(EB)) 242 | 243 | default: 244 | goto SyntaxError 245 | } 246 | 247 | *b = ByteSize(val) 248 | return nil 249 | 250 | Overflow: 251 | *b = ByteSize(maxUint64) 252 | return &strconv.NumError{fnUnmarshalText, string(t0), strconv.ErrRange} 253 | 254 | SyntaxError: 255 | *b = 0 256 | return &strconv.NumError{fnUnmarshalText, string(t0), strconv.ErrSyntax} 257 | 258 | BitsError: 259 | *b = 0 260 | return &strconv.NumError{fnUnmarshalText, string(t0), ErrBits} 261 | } 262 | -------------------------------------------------------------------------------- /utils/goroutine/goroutine.go: -------------------------------------------------------------------------------- 1 | package goroutine 2 | 3 | import ( 4 | "github.com/sirupsen/logrus" 5 | "runtime/debug" 6 | ) 7 | 8 | func Protect(g func()) { 9 | defer func() { 10 | if err := recover(); err != nil { 11 | logrus.Errorf("run time panic: %s stack: %s", err, string(debug.Stack())) 12 | } 13 | }() 14 | g() 15 | } 16 | -------------------------------------------------------------------------------- /utils/iox/io_test.go: -------------------------------------------------------------------------------- 1 | package iox 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func Test_IsFileExist(t *testing.T) { 10 | if IsFileExist("./io.go") { 11 | t.Log("exist") 12 | } else { 13 | t.Error("not exist") 14 | } 15 | 16 | if IsFileExist("./io_.go") { 17 | t.Error("exist") 18 | } else { 19 | t.Log("not exist") 20 | } 21 | } 22 | 23 | func Test_OpenFileWrite(t *testing.T) { 24 | file, err := os.OpenFile("aaa.txt", os.O_APPEND|os.O_CREATE, 0666) 25 | defer file.Close() 26 | if err != nil { 27 | panic(err) 28 | } 29 | for i := 0; i < 20; i++ { 30 | file.WriteString("aaa\n") 31 | time.Sleep(1 * time.Second) 32 | } 33 | } 34 | 35 | func Benchmark_OpenFile(t *testing.B) { 36 | t.ResetTimer() 37 | for i := 0; i < t.N; i++ { 38 | file, _ := os.OpenFile("aaa.txt", os.O_APPEND|os.O_CREATE, 0666) 39 | file.Close() 40 | } 41 | 42 | } 43 | 44 | func Benchmark_IsFileExist(t *testing.B) { 45 | t.ResetTimer() 46 | for i := 0; i < t.N; i++ { 47 | IsFileExist("./io.go") 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /utils/iox/iox.go: -------------------------------------------------------------------------------- 1 | package iox 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | func IsFileExist(file string) bool { 8 | if _, err := os.Stat(file); os.IsNotExist(err) { 9 | return false 10 | } 11 | return true 12 | } 13 | 14 | // remember after used need close file 15 | func OpenFile(file string) (*os.File, error) { 16 | return os.OpenFile(file, os.O_APPEND|os.O_CREATE, 0644) 17 | } 18 | -------------------------------------------------------------------------------- /utils/ip.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "io/ioutil" 8 | "net" 9 | "net/http" 10 | ) 11 | 12 | var ipCache = make(map[string]*IP) 13 | 14 | type IPInfo struct { 15 | Code int `json:"code"` 16 | Data IP `json:"data` 17 | } 18 | 19 | type IP struct { 20 | IP string `json:"ip"` 21 | Country string `json:"country"` 22 | Area string `json:"area"` 23 | Region string `json:"region"` 24 | City string `json:"city"` 25 | Isp string `json:"isp"` 26 | CountryID string `json:"country_id"` 27 | AreaID string `json:"area_id"` 28 | RegionID string `json:"region_id"` 29 | CityID string `json:"city_id"` 30 | IspID string `json:"isp_id"` 31 | } 32 | 33 | func tabaoAPI(ip string) (*IPInfo, error) { 34 | tr := &http.Transport{ 35 | DialContext: func(_ context.Context, network, addr string) (net.Conn, error) { 36 | return net.Dial(network, addr) 37 | }, 38 | } 39 | client := &http.Client{Transport: tr} 40 | url := fmt.Sprintf("httpserver://ip.taobao.com/service/getIpInfo.php?ip=%s", ip) 41 | resp, err := client.Get(url) 42 | if err != nil { 43 | return nil, err 44 | } 45 | defer resp.Body.Close() 46 | 47 | out, err := ioutil.ReadAll(resp.Body) 48 | if err != nil { 49 | return nil, err 50 | } 51 | var result IPInfo 52 | if err := json.Unmarshal(out, &result); err != nil { 53 | return nil, err 54 | } 55 | 56 | return &result, nil 57 | } 58 | 59 | func WatchIP(addr string) (*IP, error) { 60 | if ip, ok := ipCache[addr]; ok { 61 | return ip, nil 62 | } 63 | reply, err := tabaoAPI(addr) 64 | if err != nil { 65 | return nil, err 66 | } 67 | if reply != nil { 68 | ipCache[addr] = &(reply.Data) 69 | return &(reply.Data), nil 70 | } 71 | return nil, nil 72 | } 73 | 74 | func GetPublicIP() string { 75 | ip, err := http.Get("httpserver://ipinfo.io/ip") 76 | if err != nil { 77 | return "" 78 | } 79 | defer ip.Body.Close() 80 | context, err := ioutil.ReadAll(ip.Body) 81 | if err != nil { 82 | return "" 83 | } 84 | return string(context) 85 | } 86 | -------------------------------------------------------------------------------- /utils/langx/langx.go: -------------------------------------------------------------------------------- 1 | package langx 2 | 3 | import ( 4 | "log" 5 | "reflect" 6 | ) 7 | 8 | func Must(fn func() (interface{}, error)) interface{} { 9 | v, err := fn() 10 | if err != nil { 11 | log.Fatalln(err) 12 | } 13 | return v 14 | } 15 | 16 | // get one result from multi result function 17 | // example: 18 | // func TestFunc() (string,string){ 19 | // return "a","b" 20 | // } 21 | // 22 | // func ExampleFirstResult(){ 23 | // FirstResult(TestFunc) 24 | // } 25 | // Output: 26 | // a 27 | func FirstResult(function interface{}, args... interface{}) interface{}{ 28 | inputs := make([]reflect.Value, len(args)) 29 | for i, _ := range args { 30 | inputs[i] = reflect.ValueOf(args[i]) 31 | } 32 | if v := reflect.ValueOf(function); v.Kind() == reflect.Func { 33 | results := v.Call(inputs) 34 | if len(results) > 0{ 35 | return results[0].Interface() 36 | }else{ 37 | return nil 38 | } 39 | }else { 40 | return nil 41 | } 42 | } 43 | 44 | func InArray(val interface{}, array interface{}) (exists bool, index int) { 45 | exists = false 46 | index = -1 47 | 48 | switch reflect.TypeOf(array).Kind() { 49 | case reflect.Slice: 50 | s := reflect.ValueOf(array) 51 | 52 | for i := 0; i < s.Len(); i++ { 53 | if reflect.DeepEqual(val, s.Index(i).Interface()) == true { 54 | index = i 55 | exists = true 56 | return 57 | } 58 | } 59 | } 60 | return 61 | } 62 | 63 | 64 | 65 | // IntIn implement python in operator 66 | func IntIn(src int, to []int) bool { 67 | for _, item := range to { 68 | if src == item { 69 | return true 70 | } 71 | } 72 | return false 73 | } -------------------------------------------------------------------------------- /utils/langx/langx_test.go: -------------------------------------------------------------------------------- 1 | package langx 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestInArray(t *testing.T){ 9 | r,_ := InArray("a",[]string{"a","b"}) 10 | if !r { 11 | t.Fatal("r not exist") 12 | } 13 | } 14 | 15 | func ExampleFirstResult() { 16 | fmt.Println(FirstResult(Abc)) 17 | 18 | // Output: 19 | } 20 | func Abc() (string,string){ 21 | return "a","b" 22 | } 23 | 24 | 25 | func TestIntIn(t *testing.T) { 26 | type args struct { 27 | src int 28 | to []int 29 | } 30 | tests := []struct { 31 | name string 32 | args args 33 | want bool 34 | }{ 35 | // TODO: Add test cases. 36 | { 37 | name:"test1", 38 | args:args{ 39 | src: 7, 40 | to: []int{7,8}, 41 | }, 42 | want:true, 43 | }, 44 | { 45 | name:"test2", 46 | args:args{ 47 | src: 7, 48 | to: []int{8,9}, 49 | }, 50 | want:false, 51 | }, 52 | } 53 | for _, tt := range tests { 54 | t.Run(tt.name, func(t *testing.T) { 55 | if got := IntIn(tt.args.src, tt.args.to); got != tt.want { 56 | t.Errorf("IntIn() = %v, want %v", got, tt.want) 57 | } 58 | }) 59 | } 60 | } -------------------------------------------------------------------------------- /utils/lock.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "sync" 4 | 5 | var lockMap = &sync.Map{} 6 | 7 | func getLock(key interface{}) *sync.RWMutex { 8 | v, ok := lockMap.Load(key) 9 | if !ok { 10 | v = &sync.RWMutex{} 11 | lockMap.Store(key, v) 12 | } 13 | return v.(*sync.RWMutex) 14 | } 15 | 16 | func RLock(key string) { 17 | lock := getLock(key) 18 | lock.RLock() 19 | } 20 | 21 | func RUnLock(key string) { 22 | v, ok := lockMap.Load(key) 23 | if ok { 24 | lock := v.(*sync.RWMutex) 25 | lock.RUnlock() 26 | } 27 | } 28 | func Lock(key string) { 29 | lock := getLock(key) 30 | lock.Lock() 31 | } 32 | 33 | func UnLock(key string) { 34 | v, ok := lockMap.Load(key) 35 | if ok { 36 | lock := v.(*sync.RWMutex) 37 | lock.Unlock() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /utils/monitor/monitor.go: -------------------------------------------------------------------------------- 1 | package monitor 2 | 3 | 4 | import ( 5 | "github.com/ProxyPanel/VNet-SSR/common/log" 6 | "github.com/shirou/gopsutil/cpu" 7 | "github.com/shirou/gopsutil/disk" 8 | "github.com/shirou/gopsutil/mem" 9 | "github.com/shirou/gopsutil/net" 10 | ) 11 | 12 | // GetCpuUsage get cpu usage 13 | func GetCPUUsage() int { 14 | percent, err := cpu.Percent(0, false) 15 | if err != nil { 16 | log.Err(err) 17 | return 0 18 | } 19 | if len(percent) > 0 { 20 | return int(percent[0]) 21 | } else { 22 | log.Error("get cpu usage fail") 23 | return 0 24 | } 25 | } 26 | 27 | //GetMemUsage get mem usage 28 | func GetMemUsage() int { 29 | m, err := mem.VirtualMemory() 30 | if err != nil { 31 | log.Err(err) 32 | return 0 33 | } 34 | return int(m.UsedPercent) 35 | } 36 | 37 | //GetNetwork get Network traffic up and down 38 | func GetNetwork() (up uint64, down uint64) { 39 | ni, err := net.IOCounters(false) 40 | if err != nil { 41 | log.Err(err) 42 | return 0, 0 43 | } 44 | if len(ni) > 0 { 45 | return ni[0].BytesSent, ni[0].BytesRecv 46 | } else { 47 | log.Error("can't get network tarffic") 48 | return 0, 0 49 | } 50 | } 51 | 52 | //GetDiskUsage get disk usage 53 | func GetDiskUsage() int { 54 | d, err := disk.Usage("/") 55 | if err != nil { 56 | log.Err(err) 57 | return 0 58 | } 59 | return int(d.UsedPercent) 60 | } 61 | 62 | -------------------------------------------------------------------------------- /utils/netx/netx.go: -------------------------------------------------------------------------------- 1 | package netx 2 | 3 | import ( 4 | "github.com/ProxyPanel/VNet-SSR/common/log" 5 | "github.com/ProxyPanel/VNet-SSR/common/network" 6 | "github.com/ProxyPanel/VNet-SSR/common/pool" 7 | "github.com/ProxyPanel/VNet-SSR/utils/goroutine" 8 | "github.com/ProxyPanel/VNet-SSR/utils/socksproxy" 9 | "github.com/pkg/errors" 10 | "io" 11 | "net" 12 | "sync" 13 | "time" 14 | ) 15 | 16 | func Copy(dst,src network.IRequest) (written int64, err error){ 17 | buf := pool.GetBuf() 18 | for { 19 | nr, er := src.Read(buf) 20 | //if nr == 0{ 21 | // log.Debug("%s n is zero ---------------------",dst.GetRequestId()) 22 | //} 23 | if nr > 0 { 24 | nw, ew := dst.Write(buf[0:nr]) 25 | if nw > 0 { 26 | written += int64(nw) 27 | } 28 | if ew != nil { 29 | err = ew 30 | break 31 | } 32 | if nr != nw { 33 | err = io.ErrShortWrite 34 | break 35 | } 36 | } 37 | if er != nil { 38 | //if er != io.EOF { 39 | // err = er 40 | //} 41 | //log.Error("%s is error ---------------------",dst.GetRequestId()) 42 | err = er 43 | break 44 | } 45 | } 46 | pool.PutBuf(buf) 47 | //log.Debug("%s written %d err %v",dst.GetRequestId(),written,err) 48 | return written, err 49 | } 50 | // DuplexCopyTcp will return 3 result 51 | // up means left connection to right connection transfer data count 52 | // down means right connection to left connections transfer data count 53 | // and the last result is error 54 | func DuplexCopyTcp(left, right network.IRequest) (up, down int64, err error) { 55 | type res struct { 56 | N int64 57 | Err error 58 | } 59 | ch := make(chan res) 60 | defer func() { 61 | if e := recover(); e != nil { 62 | log.Error("panic in timedCopy: %v", e) 63 | } 64 | }() 65 | 66 | go goroutine.Protect(func() { 67 | n, err := Copy(right, left) 68 | _ = right.SetDeadline(time.Now()) // wake up the other goroutine blocking on right 69 | _ = left.SetDeadline(time.Now()) // wake up the other goroutine blocking on left 70 | ch <- res{n, err} 71 | }) 72 | 73 | up, err = Copy(left, right) 74 | _ = right.SetDeadline(time.Now()) // wake up the other goroutine blocking on right 75 | _ = left.SetDeadline(time.Now()) // wake up the other goroutine blocking on left 76 | rs := <-ch 77 | 78 | if rs.Err != nil { 79 | log.Error("netx copy %s <- %s : %s",right.RemoteAddr(),left.RemoteAddr(),rs.Err.Error()) 80 | } 81 | if err != nil{ 82 | log.Error("netx copy %s -> %s : %s",right.RemoteAddr(),left.RemoteAddr(),err.Error()) 83 | } 84 | return up, rs.N, errors.Cause(err) 85 | } 86 | 87 | // Packet NAT table 88 | type NatMap struct { 89 | sync.RWMutex 90 | m map[string]net.PacketConn 91 | timeout time.Duration 92 | } 93 | 94 | func NewNatMap(timeout time.Duration) *NatMap { 95 | m := &NatMap{} 96 | m.m = make(map[string]net.PacketConn) 97 | m.timeout = timeout 98 | return m 99 | } 100 | 101 | func (m *NatMap) Get(key string) net.PacketConn { 102 | m.RLock() 103 | defer m.RUnlock() 104 | return m.m[key] 105 | } 106 | 107 | func (m *NatMap) Set(key string, pc net.PacketConn) { 108 | m.Lock() 109 | defer m.Unlock() 110 | m.m[key] = pc 111 | } 112 | 113 | func (m *NatMap) Del(key string) net.PacketConn { 114 | m.Lock() 115 | defer m.Unlock() 116 | 117 | pc, ok := m.m[key] 118 | if ok { 119 | delete(m.m, key) 120 | return pc 121 | } 122 | return nil 123 | } 124 | 125 | func (m *NatMap) Add(peer net.Addr, dst, src net.PacketConn) { 126 | m.Set(peer.String(), src) 127 | go goroutine.Protect(func() { 128 | _ = timedCopy(dst, peer, src, m.timeout) 129 | if pc := m.Del(peer.String()); pc != nil { 130 | _ = pc.Close() 131 | } 132 | }) 133 | } 134 | 135 | // copy from src to dst at target with read timeout 136 | func timedCopy(dst net.PacketConn, target net.Addr, src net.PacketConn, timeout time.Duration) error { 137 | buf := pool.GetBuf() 138 | defer pool.PutBuf(buf) 139 | defer func() { 140 | if e := recover(); e != nil { 141 | log.Error("panic in timedCopy: %v", e) 142 | } 143 | }() 144 | 145 | for { 146 | _ = src.SetReadDeadline(time.Now().Add(timeout)) 147 | n, raddr, err := src.ReadFrom(buf) 148 | if err != nil { 149 | return errors.Cause(err) 150 | } 151 | 152 | srcAddr := socksproxy.ParseAddr(raddr.String()) 153 | srcAddrByte := srcAddr.Raw 154 | copy(buf[len(srcAddrByte):], buf[:n]) 155 | copy(buf, srcAddrByte) 156 | _, err = dst.WriteTo(buf[:len(srcAddrByte)+n], target) 157 | 158 | if err != nil { 159 | return errors.Cause(err) 160 | } 161 | } 162 | } 163 | 164 | -------------------------------------------------------------------------------- /utils/osx/osx.go: -------------------------------------------------------------------------------- 1 | package osx 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | "syscall" 7 | ) 8 | 9 | func WaitSignal()os.Signal{ 10 | signalChan := make(chan os.Signal,1) 11 | signal.Notify(signalChan,syscall.SIGINT, syscall.SIGTERM,syscall.SIGQUIT,syscall.SIGKILL,syscall.SIGHUP) 12 | if sig,ok := <- signalChan;ok{ 13 | return sig 14 | } 15 | return nil 16 | } 17 | -------------------------------------------------------------------------------- /utils/randomx/randomx.go: -------------------------------------------------------------------------------- 1 | package randomx 2 | 3 | import ( 4 | "crypto/rand" 5 | "math" 6 | mrand "math/rand" 7 | "time" 8 | ) 9 | 10 | var ( 11 | r = mrand.New(mrand.NewSource(time.Now().UnixNano())) 12 | ) 13 | 14 | func RandomBytes(size int) []byte { 15 | byte := make([]byte, size) 16 | rand.Read(byte) 17 | return byte 18 | } 19 | 20 | func RandomStringsChoice(data []string) string { 21 | return data[r.Intn(len(data))] 22 | } 23 | 24 | func RandomIntChoice(data []int) int { 25 | return data[r.Intn(len(data))] 26 | } 27 | 28 | // Generate random integer between min and max 29 | func RandIntRange(min, max int) int { 30 | if min == max { 31 | return min 32 | } 33 | return r.Intn((max+1)-min) + min 34 | } 35 | 36 | func RandFloat32Range(min, max float32) float32 { 37 | if min == max { 38 | return min 39 | } 40 | return r.Float32()*(max-min) + min 41 | } 42 | 43 | func RandFloat64Range(min, max float64) float64 { 44 | if min == max { 45 | return min 46 | } 47 | return r.Float64()*(max-min) + min 48 | } 49 | 50 | // Number will generate a random number between given min And max 51 | func Number(min int, max int) int { 52 | return RandIntRange(min, max) 53 | } 54 | 55 | // Uint8 will generate a random uint8 value 56 | func Uint8() uint8 { 57 | return uint8(RandIntRange(0, math.MaxUint8)) 58 | } 59 | 60 | // Uint16 will generate a random uint16 value 61 | func Uint16() uint16 { 62 | return uint16(RandIntRange(0, math.MaxUint16)) 63 | } 64 | 65 | // Uint32 will generate a random uint32 value 66 | func Uint32() uint32 { 67 | return uint32(RandIntRange(0, math.MaxInt32)) 68 | } 69 | 70 | // Uint64 will generate a random uint64 value 71 | func Uint64() uint64 { 72 | return uint64(r.Int63n(math.MaxInt64)) 73 | } 74 | 75 | // Int8 will generate a random Int8 value 76 | func Int8() int8 { 77 | return int8(RandIntRange(math.MinInt8, math.MaxInt8)) 78 | } 79 | 80 | // Int16 will generate a random int16 value 81 | func Int16() int16 { 82 | return int16(RandIntRange(math.MinInt16, math.MaxInt16)) 83 | } 84 | 85 | // Int32 will generate a random int32 value 86 | func Int32() int32 { 87 | return int32(RandIntRange(math.MinInt32, math.MaxInt32)) 88 | } 89 | 90 | // Int64 will generate a random int64 value 91 | func Int64() int64 { 92 | return r.Int63n(math.MaxInt64) + math.MinInt64 93 | } 94 | 95 | // Float32 will generate a random float32 value 96 | func Float32() float32 { 97 | return RandFloat32Range(math.SmallestNonzeroFloat32, math.MaxFloat32) 98 | } 99 | 100 | // Float32Range will generate a random float32 value between min and max 101 | func Float32Range(min, max float32) float32 { 102 | return RandFloat32Range(min, max) 103 | } 104 | 105 | // Float64 will generate a random float64 value 106 | func Float64() float64 { 107 | return RandFloat64Range(math.SmallestNonzeroFloat64, math.MaxFloat64) 108 | } 109 | 110 | // Float64Range will generate a random float64 value between min and max 111 | func Float64Range(min, max float64) float64 { 112 | return RandFloat64Range(min, max) 113 | } 114 | -------------------------------------------------------------------------------- /utils/randomx/randomx_test.go: -------------------------------------------------------------------------------- 1 | package randomx 2 | 3 | import "fmt" 4 | 5 | func ExampleRandom(){ 6 | for i := 0;i<1000;i++{ 7 | fmt.Println(RandIntRange(0,1024)) 8 | } 9 | // Output: 10 | } 11 | -------------------------------------------------------------------------------- /utils/rsax/rsax.go: -------------------------------------------------------------------------------- 1 | package rsax 2 | 3 | import ( 4 | "bytes" 5 | "crypto" 6 | "crypto/rand" 7 | "crypto/rsa" 8 | "crypto/x509" 9 | "encoding/asn1" 10 | "encoding/base64" 11 | "encoding/pem" 12 | "github.com/pkg/errors" 13 | "io" 14 | ) 15 | 16 | const ( 17 | CHAR_SET = "UTF-8" 18 | BASE_64_FORMAT = "UrlSafeNoPadding" 19 | RSA_ALGORITHM_KEY_TYPE = "PKCS8" 20 | RSA_ALGORITHM_SIGN = crypto.SHA256 21 | ) 22 | 23 | type XRsa struct { 24 | publicKey *rsa.PublicKey 25 | privateKey *rsa.PrivateKey 26 | } 27 | 28 | // 生成密钥对 29 | func CreateKeys(publicKeyWriter, privateKeyWriter io.Writer, keyLength int) error { 30 | // 生成私钥文件 31 | privateKey, err := rsa.GenerateKey(rand.Reader, keyLength) 32 | if err != nil { 33 | return err 34 | } 35 | derStream := MarshalPKCS8PrivateKey(privateKey) 36 | block := &pem.Block{ 37 | Type: "PRIVATE KEY", 38 | Bytes: derStream, 39 | } 40 | err = pem.Encode(privateKeyWriter, block) 41 | if err != nil { 42 | return err 43 | } 44 | 45 | // 生成公钥文件 46 | publicKey := &privateKey.PublicKey 47 | derPkix, err := x509.MarshalPKIXPublicKey(publicKey) 48 | if err != nil { 49 | return err 50 | } 51 | block = &pem.Block{ 52 | Type: "PUBLIC KEY", 53 | Bytes: derPkix, 54 | } 55 | err = pem.Encode(publicKeyWriter, block) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | return nil 61 | } 62 | 63 | func NewXRsa(publicKey []byte, privateKey []byte) (*XRsa, error) { 64 | block, _ := pem.Decode(publicKey) 65 | if block == nil { 66 | return nil, errors.New("public key error") 67 | } 68 | pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) 69 | if err != nil { 70 | return nil, err 71 | } 72 | pub := pubInterface.(*rsa.PublicKey) 73 | 74 | block, _ = pem.Decode(privateKey) 75 | if block == nil { 76 | return nil, errors.New("private key error!") 77 | } 78 | priv, err := x509.ParsePKCS8PrivateKey(block.Bytes) 79 | if err != nil { 80 | return nil, err 81 | } 82 | 83 | pri, ok := priv.(*rsa.PrivateKey) 84 | if ok { 85 | return &XRsa{ 86 | publicKey: pub, 87 | privateKey: pri, 88 | }, nil 89 | } else { 90 | return nil, errors.New("private key not supported") 91 | } 92 | } 93 | 94 | // public key encode 95 | func (r *XRsa) PublicEncrypt(data string) (string, error) { 96 | partLen := r.publicKey.N.BitLen()/8 - 11 97 | chunks := split([]byte(data), partLen) 98 | 99 | buffer := bytes.NewBufferString("") 100 | for _, chunk := range chunks { 101 | bytes, err := rsa.EncryptPKCS1v15(rand.Reader, r.publicKey, chunk) 102 | if err != nil { 103 | return "", err 104 | } 105 | buffer.Write(bytes) 106 | } 107 | 108 | return base64.RawURLEncoding.EncodeToString(buffer.Bytes()), nil 109 | } 110 | 111 | // private key decrypt 112 | func (r *XRsa) PrivateDecrypt(encrypted string) (string, error) { 113 | partLen := r.publicKey.N.BitLen() / 8 114 | raw, err := base64.RawURLEncoding.DecodeString(encrypted) 115 | if err != nil { 116 | return "", errors.Wrap(err, "base64 decode error") 117 | } 118 | chunks := split([]byte(raw), partLen) 119 | 120 | buffer := bytes.NewBufferString("") 121 | for _, chunk := range chunks { 122 | decrypted, err := rsa.DecryptPKCS1v15(rand.Reader, r.privateKey, chunk) 123 | if err != nil { 124 | return "", err 125 | } 126 | buffer.Write(decrypted) 127 | } 128 | 129 | return buffer.String(), err 130 | } 131 | 132 | // data sign 133 | func (r *XRsa) Sign(data string) (string, error) { 134 | h := RSA_ALGORITHM_SIGN.New() 135 | h.Write([]byte(data)) 136 | hashed := h.Sum(nil) 137 | 138 | sign, err := rsa.SignPKCS1v15(rand.Reader, r.privateKey, RSA_ALGORITHM_SIGN, hashed) 139 | if err != nil { 140 | return "", err 141 | } 142 | return base64.RawURLEncoding.EncodeToString(sign), err 143 | } 144 | 145 | // verify data sign 146 | func (r *XRsa) Verify(data string, sign string) error { 147 | h := RSA_ALGORITHM_SIGN.New() 148 | h.Write([]byte(data)) 149 | hashed := h.Sum(nil) 150 | 151 | decodedSign, err := base64.RawURLEncoding.DecodeString(sign) 152 | if err != nil { 153 | return err 154 | } 155 | 156 | return rsa.VerifyPKCS1v15(r.publicKey, RSA_ALGORITHM_SIGN, hashed, decodedSign) 157 | } 158 | 159 | func MarshalPKCS8PrivateKey(key *rsa.PrivateKey) []byte { 160 | info := struct { 161 | Version int 162 | PrivateKeyAlgorithm []asn1.ObjectIdentifier 163 | PrivateKey []byte 164 | }{} 165 | info.Version = 0 166 | info.PrivateKeyAlgorithm = make([]asn1.ObjectIdentifier, 1) 167 | info.PrivateKeyAlgorithm[0] = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} 168 | info.PrivateKey = x509.MarshalPKCS1PrivateKey(key) 169 | 170 | k, _ := asn1.Marshal(info) 171 | return k 172 | } 173 | 174 | func split(buf []byte, lim int) [][]byte { 175 | var chunk []byte 176 | chunks := make([][]byte, 0, len(buf)/lim+1) 177 | for len(buf) >= lim { 178 | chunk, buf = buf[:lim], buf[lim:] 179 | chunks = append(chunks, chunk) 180 | } 181 | if len(buf) > 0 { 182 | chunks = append(chunks, buf[:len(buf)]) 183 | } 184 | return chunks 185 | } 186 | -------------------------------------------------------------------------------- /utils/rsax/rsax_test.go: -------------------------------------------------------------------------------- 1 | package rsax 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func ExampleRSA(){ 8 | publicKey := `-----BEGIN PUBLIC KEY----- 9 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJuoMtHAg3HJS39kRdMFt1TOa4 10 | L4pVegG3886Glv9Ndp2fsH5sEqpG4M9sxOi4etlo77J8CousYfDZsM/oLs66KwQ2 11 | YHLJII6LnLatoW7/lXS2woCrEmHoLepHJVZ6P/mbV42+DL1WWzm9jaXSq5nTFekD 12 | hpR9OkauiT/VwVkMmwIDAQAB 13 | -----END PUBLIC KEY-----` 14 | privateKey := `-----BEGIN PRIVATE KEY----- 15 | MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMm6gy0cCDcclLf2 16 | RF0wW3VM5rgvilV6AbfzzoaW/012nZ+wfmwSqkbgz2zE6Lh62WjvsnwKi6xh8Nmw 17 | z+guzrorBDZgcskgjouctq2hbv+VdLbCgKsSYegt6kclVno/+ZtXjb4MvVZbOb2N 18 | pdKrmdMV6QOGlH06Rq6JP9XBWQybAgMBAAECgYAv5SCP7T/mFdsZclb46SpNx1xg 19 | DqmBcd5GlpRKUD99XNQ/vd/GOQhEm8ujv3yhkEleKMrvuHFBFF/iz6ANOE/MZ3MV 20 | FpTlLtdlng6I9TnbqeFj1K/0EraGEVejIQI5O2ykhpKJPC77p3ff1Ax2CrHyCgkj 21 | teRzhRN9L0NC0zaNQQJBAPbKad0F+ER0m8mpo+Z7Z9mOe6TL2+XywfPbHx+VHo31 22 | Y8MrkvhhJYUFgmEbKIHNuGl/JGqgnJK6Kvh03x1XJt0CQQDRQZ8Tx3CVnAkBcb5A 23 | GqtIpit8JZg+jhErfLjLGLd5D2fUwse6VComTWHOO0ODj+BM7+W0wqDs7b5asJrt 24 | S/3XAkEAqiyjWSRPsKyT7DgM69Z2ot8MRXPJO0PtGAEl8fo6qnrmguNeIeWjIJnO 25 | 8LTwdqlrm1tvuhLsRIUZMmAspae+BQJALbAbMHFaJoA0Aym3dT2dajZFxkxbCkVw 26 | gEMyAb36ySbQ78Y7X3Zi4YwBr8qGuiHewk2apLXd9v0Nk7V9jhQKbwJADOu6RS45 27 | 92FrcCuJvvxjMsW9cLWd22xcwnyJXRZSgI7jwef7/rTPtrhFVv/AjLisl4E8SKiC 28 | I10Obt912YV2zA== 29 | -----END PRIVATE KEY-----` 30 | fmt.Println("aa") 31 | rsaInstance,err := NewXRsa([]byte(publicKey),[]byte(privateKey)) 32 | if err != nil{ 33 | fmt.Printf("%v\n",err) 34 | return 35 | } 36 | clearData,err := rsaInstance.PrivateDecrypt("UQqPOeIZ9qX3zEX-no0dI5rs-PJ9uRfCS36SkNvPNDJ0XOxuYv_CwEdggkuyJLFVYzkQgallqTUBqd8xWp_Xt3FjnyW9lX4SG_6usWWQ6rZH7tg9fIQozR34sGpDPAr9JYJil-JsRPjLtCGqnEDWlV6sbwe9FGk6nw8HBd_0shhbydCLCLASI4uuKLy2-xgK9oNft-vhJ8ApuQrRuvQ_RAaEtwc_QxttUl2xw-IYdkelcU5GN6hAptALYFkZQ4yerMEQanB7wDNypKdiHaDNifv6P2_wk_V8hMBfoJZagILZG8MJ5nuCzVsijlLdY03btHtT8A92UvikSul-fGLSzKZG2VpDQpDL8NPcIyZ00nPXlsLjNtLW8xEcTH4xyn4J7RBuMcD9AHVmk91qWEGx1VnIBo41phFqpWerFgA5S3oqDtK1bwmIt3Zp094DKTfYM0bMlCyby6VkBAU0i1bU19kCd9qbwLGM-XjNppzgj5QBWKWTqQdmlXCC2fwOQnrW") 37 | if err != nil{ 38 | fmt.Printf("%v\n",err) 39 | return 40 | } 41 | fmt.Println(clearData) 42 | fmt.Println(len(clearData)) 43 | //Output: 44 | } 45 | 46 | -------------------------------------------------------------------------------- /utils/scheme.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/base64" 5 | "errors" 6 | "fmt" 7 | "strings" 8 | ) 9 | 10 | type SS_Scheme struct { 11 | Method string `json:"method"` 12 | Password string `json:"password"` 13 | Hostname string `json:"hostname"` 14 | Port int `json:"port"` 15 | } 16 | 17 | func Parse_SIP002_URI_Scheme(s string) (*SS_Scheme, error) { 18 | if !strings.HasPrefix(s, "ss://") { 19 | return nil, errors.New("not sip002 scheme!") 20 | } 21 | sInfo := s[5:] 22 | decodeByte, err := base64.StdEncoding.DecodeString(sInfo) 23 | if err != nil { 24 | return nil, err 25 | } 26 | decodeStr := string(decodeByte) 27 | fmt.Println(decodeStr) 28 | return nil, err 29 | } 30 | -------------------------------------------------------------------------------- /utils/scheme_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "testing" 4 | 5 | func Test_Parse_SIP002_URI_Scheme(t *testing.T) { 6 | Parse_SIP002_URI_Scheme("ss://cmM0LW1kNTo1ckVuN21AMTMuMTE1LjIzMS44NToxMDI1") 7 | } 8 | -------------------------------------------------------------------------------- /utils/shadowsocksx/shadowsocksx.go: -------------------------------------------------------------------------------- 1 | package shadowsocksx 2 | 3 | import ( 4 | "crypto/sha1" 5 | "golang.org/x/crypto/hkdf" 6 | "io" 7 | ) 8 | 9 | func HKDF_SHA1(secret, salt, info, key []byte) error { 10 | _, err := io.ReadFull(hkdf.New(sha1.New, secret, salt, info), key) 11 | return err 12 | } 13 | -------------------------------------------------------------------------------- /utils/snowflake.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "errors" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | var IW *IdWorker 10 | 11 | func init() { 12 | var err error 13 | IW, err = NewIdWorker(1) 14 | if err != nil { 15 | panic(err) 16 | } 17 | } 18 | 19 | func GetLongID() (i int64) { 20 | i, _ = IW.NextId() 21 | return 22 | } 23 | 24 | const ( 25 | CEpoch = 1474802888000 26 | CWorkerIdBits = 10 // Num of WorkerId Bits 27 | CSenquenceBits = 12 // Num of Sequence Bits 28 | 29 | CWorkerIdShift = 12 30 | CTimeStampShift = 22 31 | 32 | CSequenceMask = 0xfff // equal as getSequenceMask() 33 | CMaxWorker = 0x3ff // equal as getMaxWorkerId() 34 | ) 35 | 36 | // IdWorker Struct 37 | type IdWorker struct { 38 | workerId int64 39 | lastTimeStamp int64 40 | sequence int64 41 | maxWorkerId int64 42 | lock *sync.Mutex 43 | } 44 | 45 | // NewIdWorker Func: Generate NewIdWorker with Given workerid 46 | func NewIdWorker(workerid int64) (iw *IdWorker, err error) { 47 | iw = new(IdWorker) 48 | 49 | iw.maxWorkerId = getMaxWorkerId() 50 | 51 | if workerid > iw.maxWorkerId || workerid < 0 { 52 | return nil, errors.New("worker not fit") 53 | } 54 | iw.workerId = workerid 55 | iw.lastTimeStamp = -1 56 | iw.sequence = 0 57 | iw.lock = new(sync.Mutex) 58 | return iw, nil 59 | } 60 | 61 | func getMaxWorkerId() int64 { 62 | return -1 ^ -1<> CWorkerIdShift) & CMaxWorker 113 | ts = (id >> CTimeStampShift) + CEpoch 114 | t = time.Unix(ts/1000, (ts%1000)*1000000) 115 | return 116 | } 117 | -------------------------------------------------------------------------------- /utils/socksproxy/socks.go: -------------------------------------------------------------------------------- 1 | // Package socksproxy implements essential parts of SOCKS protocol. 2 | package socksproxy 3 | 4 | import ( 5 | "bytes" 6 | "encoding/binary" 7 | "fmt" 8 | "io" 9 | "net" 10 | "strconv" 11 | 12 | "github.com/pkg/errors" 13 | ) 14 | 15 | // UDPEnabled is the toggle for UDP support 16 | var UDPEnabled = true 17 | 18 | // SOCKS request commands as defined in RFC 1928 section 4. 19 | const ( 20 | CmdConnect = 1 21 | CmdBind = 2 22 | CmdUDPAssociate = 3 23 | ) 24 | 25 | // SOCKS address types as defined in RFC 1928 section 5. 26 | const ( 27 | AtypIPv4 = 1 28 | AtypDomainName = 3 29 | AtypIPv6 = 4 30 | ) 31 | 32 | // Error represents a SOCKS error 33 | type Error string 34 | 35 | func (err Error) Error() string { 36 | return "SOCKS error: " + string(err) 37 | } 38 | 39 | // SOCKS errors as defined in RFC 1928 section 6. 40 | const ( 41 | ErrGeneralFailure = Error("ErrGeneralFailure") 42 | ErrConnectionNotAllowed = Error("ErrConnectionNotAllowed") 43 | ErrNetworkUnreachable = Error("ErrNetworkUnreachable") 44 | ErrHostUnreachable = Error("ErrHostUnreachable") 45 | ErrConnectionRefused = Error("ErrConnectionRefused") 46 | ErrTTLExpired = Error("ErrTTLExpired") 47 | ErrCommandNotSupported = Error("ErrCommandNotSupported") 48 | ErrAddressNotSupported = Error("ErrAddressNotSupported") 49 | InfoUDPAssociate = Error("InfoUDPAssociate") 50 | ) 51 | 52 | // MaxAddrLen is the maximum size of SOCKS address in bytes. 53 | const MaxAddrLen = 1 + 1 + 255 + 2 54 | 55 | // Socks5Addr represents a SOCKS address as defined in RFC 1928 section 5. 56 | type Socks5Addr struct { 57 | Raw []byte 58 | AType int 59 | Address string 60 | Port int 61 | } 62 | 63 | func NewSocks5Addr(raw []byte, atype int) *Socks5Addr { 64 | addr := &Socks5Addr{ 65 | Raw: raw, 66 | AType: atype, 67 | } 68 | addr.process() 69 | return addr 70 | } 71 | 72 | func NewSSProtocol(atype, port int, address string) *Socks5Addr { 73 | ss := &Socks5Addr{ 74 | AType: atype, 75 | Port: port, 76 | Address: address, 77 | } 78 | ss.GetRaw() 79 | return ss 80 | } 81 | 82 | func (ss *Socks5Addr) GetRaw() (raw []byte, err error) { 83 | if ss.Raw != nil { 84 | return ss.Raw, nil 85 | } 86 | 87 | buf := new(bytes.Buffer) 88 | var data []interface{} 89 | switch ss.AType { 90 | case AtypDomainName: 91 | domainLen := len(ss.Address) 92 | data = []interface{}{ 93 | uint8(ss.AType), 94 | uint8(domainLen), 95 | []byte(ss.Address), 96 | uint16(ss.Port), 97 | } 98 | case AtypIPv4: 99 | data = []interface{}{ 100 | uint8(ss.AType), 101 | []byte(net.ParseIP(ss.Address).To4()), 102 | uint16(ss.Port), 103 | } 104 | case AtypIPv6: 105 | data = []interface{}{ 106 | uint8(ss.AType), 107 | []byte(net.ParseIP(ss.Address).To16()), 108 | uint16(ss.Port), 109 | } 110 | } 111 | for _, v := range data { 112 | err := binary.Write(buf, binary.BigEndian, v) 113 | if err != nil { 114 | return nil, errors.Wrap(errors.WithStack(err), "GetRaw is errors") 115 | } 116 | } 117 | raw = make([]byte, buf.Len()) 118 | copy(raw, buf.Bytes()) 119 | ss.Raw = raw 120 | return raw, nil 121 | } 122 | 123 | func (ss *Socks5Addr) MustGetRaw() []byte { 124 | raw, err := ss.GetRaw() 125 | if err != nil { 126 | return nil 127 | } 128 | return raw 129 | } 130 | 131 | func (s *Socks5Addr) GetAddress() string { 132 | return s.Address 133 | } 134 | 135 | func (s *Socks5Addr) GetPort() int { 136 | return s.Port 137 | } 138 | 139 | func (s *Socks5Addr) GetAType() int { 140 | return s.AType 141 | } 142 | 143 | func (s *Socks5Addr) process() { 144 | switch s.AType { // address type 145 | case AtypDomainName: 146 | s.Address = string(s.Raw[2 : 2+int(s.Raw[1])]) 147 | s.Port = (int(s.Raw[2+int(s.Raw[1])]) << 8) | int(s.Raw[2+int(s.Raw[1])+1]) 148 | case AtypIPv4: 149 | s.Address = net.IP(s.Raw[1:1+net.IPv4len]).String() 150 | s.Port = (int(s.Raw[1+net.IPv4len]) << 8) | int(s.Raw[1+net.IPv4len+1]) 151 | case AtypIPv6: 152 | s.Address = net.IP(s.Raw[1:1+net.IPv6len]).String() 153 | s.Port = (int(s.Raw[1+net.IPv6len]) << 8) | int(s.Raw[1+net.IPv6len+1]) 154 | } 155 | } 156 | 157 | func (s *Socks5Addr) String() string { 158 | return fmt.Sprintf("%s:%v", s.Address, s.Port) 159 | } 160 | 161 | func readAddr(r io.Reader, b []byte) (*Socks5Addr, error) { 162 | if len(b) < MaxAddrLen { 163 | return nil, io.ErrShortBuffer 164 | } 165 | _, err := io.ReadFull(r, b[:1]) // read 1st byte for address type 166 | if err != nil { 167 | return nil, err 168 | } 169 | 170 | switch b[0] { 171 | case AtypDomainName: 172 | _, err = io.ReadFull(r, b[1:2]) // read 2nd byte for domain length 173 | if err != nil { 174 | return nil, err 175 | } 176 | _, err = io.ReadFull(r, b[2:2+int(b[1])+2]) 177 | return NewSocks5Addr(b[:1+1+int(b[1])+2], AtypDomainName), err 178 | case AtypIPv4: 179 | _, err = io.ReadFull(r, b[1:1+net.IPv4len+2]) 180 | return NewSocks5Addr(b[:1+net.IPv4len+2], AtypIPv4), err 181 | case AtypIPv6: 182 | _, err = io.ReadFull(r, b[1:1+net.IPv6len+2]) 183 | return NewSocks5Addr(b[:1+net.IPv6len+2], AtypIPv6), err 184 | } 185 | 186 | return nil, ErrAddressNotSupported 187 | } 188 | 189 | // ReadAddr reads just enough bytes from r to get a valid Addr. 190 | func ReadAddr(r io.Reader) (*Socks5Addr, error) { 191 | return readAddr(r, make([]byte, MaxAddrLen)) 192 | } 193 | 194 | // SplitAddr slices a SOCKS address from beginning of b. Returns nil if failed. 195 | func SplitAddr(b []byte) (*Socks5Addr, error) { 196 | addrLen := 1 197 | if len(b) < addrLen { 198 | return nil, io.ErrShortBuffer 199 | } 200 | 201 | var atype int 202 | switch b[0] { 203 | case AtypDomainName: 204 | if len(b) < 2 { 205 | return nil, io.ErrShortBuffer 206 | } 207 | addrLen = 1 + 1 + int(b[1]) + 2 208 | atype = AtypDomainName 209 | case AtypIPv4: 210 | addrLen = 1 + net.IPv4len + 2 211 | atype = AtypIPv4 212 | case AtypIPv6: 213 | addrLen = 1 + net.IPv6len + 2 214 | atype = AtypIPv6 215 | default: 216 | return nil, errors.New("atype error") 217 | 218 | } 219 | 220 | if len(b) < addrLen { 221 | return nil, io.ErrShortBuffer 222 | } 223 | 224 | return NewSocks5Addr(b[:addrLen], atype), nil 225 | } 226 | 227 | // ParseAddr parses the address in string s. Returns nil if failed. 228 | func ParseAddr(s string) *Socks5Addr { 229 | var ( 230 | addr []byte 231 | aType int 232 | ) 233 | host, port, err := net.SplitHostPort(s) 234 | if err != nil { 235 | return nil 236 | } 237 | if ip := net.ParseIP(host); ip != nil { 238 | if ip4 := ip.To4(); ip4 != nil { 239 | addr = make([]byte, 1+net.IPv4len+2) 240 | addr[0] = AtypIPv4 241 | copy(addr[1:], ip4) 242 | aType = AtypIPv4 243 | } else { 244 | addr = make([]byte, 1+net.IPv6len+2) 245 | addr[0] = AtypIPv6 246 | copy(addr[1:], ip) 247 | aType = AtypIPv6 248 | } 249 | } else { 250 | if len(host) > 255 { 251 | return nil 252 | } 253 | addr = make([]byte, 1+1+len(host)+2) 254 | addr[0] = AtypDomainName 255 | addr[1] = byte(len(host)) 256 | copy(addr[2:], host) 257 | aType = AtypDomainName 258 | } 259 | 260 | portnum, err := strconv.ParseUint(port, 10, 16) 261 | if err != nil { 262 | return nil 263 | } 264 | 265 | addr[len(addr)-2], addr[len(addr)-1] = byte(portnum>>8), byte(portnum) 266 | 267 | return NewSocks5Addr(addr, aType) 268 | } 269 | -------------------------------------------------------------------------------- /utils/socksproxy/socks_test.go: -------------------------------------------------------------------------------- 1 | // Package socksproxy implements essential parts of SOCKS protocol. 2 | package socksproxy 3 | 4 | import ( 5 | "fmt" 6 | "reflect" 7 | "testing" 8 | ) 9 | 10 | func TestSocks5Addr_GetRaw(t *testing.T) { 11 | tests := []struct { 12 | name string 13 | ss *Socks5Addr 14 | wantRaw []byte 15 | wantErr bool 16 | }{ 17 | { 18 | "aaa", 19 | NewSSProtocol(AtypIPv4, 3306, "127.0.0.1"), 20 | NewSSProtocol(AtypIPv4, 3306, "127.0.0.1").MustGetRaw(), 21 | false, 22 | }, 23 | } 24 | for _, tt := range tests { 25 | t.Run(tt.name, func(t *testing.T) { 26 | gotRaw, err := tt.ss.GetRaw() 27 | if (err != nil) != tt.wantErr { 28 | t.Errorf("Socks5Addr.GetRaw() error = %v, wantErr %v", err, tt.wantErr) 29 | return 30 | } 31 | if !reflect.DeepEqual(gotRaw, tt.wantRaw) { 32 | t.Errorf("Socks5Addr.GetRaw() = %v, want %v", gotRaw, tt.wantRaw) 33 | } 34 | }) 35 | } 36 | } 37 | 38 | func ExampleSocks5Addr_GetRaw() { 39 | fmt.Printf("%v\n", NewSSProtocol(AtypIPv4, 3306, "127.0.0.1").MustGetRaw()) 40 | ss, _ := SplitAddr(NewSSProtocol(AtypIPv4, 3306, "127.0.0.1").MustGetRaw()) 41 | if ss == nil { 42 | fmt.Println("ss is null") 43 | } 44 | fmt.Printf("%v\n", ss.MustGetRaw()) 45 | 46 | fmt.Printf("%v\n", NewSSProtocol(AtypDomainName, 3306, "baidu.com").MustGetRaw()) 47 | ss, _ = SplitAddr(NewSSProtocol(AtypDomainName, 3306, "baidu.com").MustGetRaw()) 48 | if ss == nil { 49 | fmt.Println("ss is null") 50 | } 51 | fmt.Printf("%v\n", ss.MustGetRaw()) 52 | //Output: 53 | //[1 127 0 0 1 12 234] 54 | //[1 127 0 0 1 12 234] 55 | //[3 9 98 97 105 100 117 46 99 111 109 12 234] 56 | //[3 9 98 97 105 100 117 46 99 111 109 12 234] 57 | } 58 | -------------------------------------------------------------------------------- /utils/stringx/stringx.go: -------------------------------------------------------------------------------- 1 | package stringx 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "encoding/hex" 7 | "fmt" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | // IsDigit judement data is any of 1234567890 13 | func IsDigit(data string) bool { 14 | if len(data) != 1 { 15 | return false 16 | } 17 | if strings.IndexAny(data, "1234567890") != -1 { 18 | return true 19 | } 20 | return false 21 | } 22 | 23 | func U2S(form string) (to string, err error) { 24 | bs, err := hex.DecodeString(strings.Replace(form, `\u`, ``, -1)) 25 | if err != nil { 26 | return 27 | } 28 | for i, bl, br, r := 0, len(bs), bytes.NewReader(bs), uint16(0); i < bl; i += 2 { 29 | binary.Read(br, binary.BigEndian, &r) 30 | to += string(r) 31 | } 32 | return 33 | } 34 | 35 | func MustU2S(from string) string { 36 | result, err := U2S(from) 37 | if err != nil { 38 | return "" 39 | } 40 | return result 41 | } 42 | 43 | func MustUnquote(s string) string { 44 | r, err := strconv.Unquote(s) 45 | if err != nil { 46 | return "" 47 | } 48 | return r 49 | } 50 | 51 | // Convert string like \u4f60\u597d to utf-8 encode 52 | // \u4f60\u597d means 你好(hello) 53 | func UnicodeToUtf8(s string) string { 54 | slen := len(s) 55 | i := 0 56 | stringBuffer := new(bytes.Buffer) 57 | for i < slen { 58 | if s[i] == 92 && (s[i+1] == 85 || s[i+1] == 117) { 59 | temp, err := strconv.ParseInt(s[i+2:i+6], 16, 32) 60 | if err != nil { 61 | panic(err) 62 | } 63 | stringBuffer.WriteString(fmt.Sprintf("%c", temp)) 64 | i += 6 65 | continue 66 | } else { 67 | stringBuffer.WriteByte(s[i]) 68 | i++ 69 | continue 70 | } 71 | } 72 | return stringBuffer.String() 73 | } 74 | 75 | // Convert string like \u4f60\u597d to utf-8 encode 76 | // \u4f60\u597d means 你好(hello) 77 | func BUnicodeToUtf8(s []byte) string { 78 | slen := len(s) 79 | i := 0 80 | stringBuffer := new(bytes.Buffer) 81 | for i < slen { 82 | if s[i] == 92 && (s[i+1] == 85 || s[i+1] == 117) { 83 | temp, err := strconv.ParseInt(string(s[i+2:i+6]), 16, 32) 84 | if err != nil { 85 | panic(err) 86 | } 87 | stringBuffer.WriteString(fmt.Sprintf("%c", temp)) 88 | i += 6 89 | continue 90 | } else { 91 | stringBuffer.WriteByte(s[i]) 92 | i++ 93 | continue 94 | } 95 | } 96 | return stringBuffer.String() 97 | } 98 | -------------------------------------------------------------------------------- /utils/stringx/stringx_test.go: -------------------------------------------------------------------------------- 1 | package stringx 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func Test_isDigits(t *testing.T) { 9 | type args struct { 10 | data string 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | want bool 16 | }{ 17 | // TODO: Add test cases. 18 | { 19 | "test", 20 | args{ 21 | data: "1", 22 | }, 23 | true, 24 | }, 25 | { 26 | "test", 27 | args{ 28 | data: "2", 29 | }, 30 | true, 31 | }, 32 | { 33 | "test", 34 | args{ 35 | data: "22", 36 | }, 37 | false, 38 | }, 39 | { 40 | "test", 41 | args{ 42 | data: "a", 43 | }, 44 | false, 45 | }, 46 | } 47 | for _, tt := range tests { 48 | t.Run(tt.name, func(t *testing.T) { 49 | if got := IsDigit(tt.args.data); got != tt.want { 50 | t.Errorf("isDigits() = %v, want %v", got, tt.want) 51 | } 52 | }) 53 | } 54 | } 55 | 56 | 57 | func ExampleMustUnquote() { 58 | test := "aaa\\u6388\\u6743\\u9a8c\\u8bc1\\u5931\\u8d25\\uff1a\\u8bf7\\u6c42\\u53d7\\u9650" 59 | fmt.Println(UnicodeToUtf8(test)) 60 | //Output: 61 | } -------------------------------------------------------------------------------- /utils/time_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func Test_Format(t *testing.T) { 10 | fmt.Println(Format("yyyy-MM-dd HH:mm:ss", time.Now())) 11 | } 12 | 13 | func Benchmark_Format(t *testing.B) { 14 | t.ResetTimer() 15 | for i := 0; i < t.N; i++ { 16 | Format("yyyy-MM-dd HH:mm:ss", time.Now()) 17 | } 18 | t.Log(Format("yyyy-MM-dd HH:mm:ss", time.Now())) 19 | t.ReportAllocs() 20 | } 21 | 22 | func Benchmark_format_string(t *testing.B) { 23 | a := []byte{0x45, 0x46} 24 | t.ResetTimer() 25 | for i := 1; i < t.N; i++ { 26 | _ = string(a) 27 | } 28 | t.ReportAllocs() 29 | } 30 | 31 | func Benchmark_OriginFormat(t *testing.B) { 32 | t.ResetTimer() 33 | for i := 0; i < t.N; i++ { 34 | time.Now().Format("2006-01-02 15:04:05") 35 | } 36 | t.ReportAllocs() 37 | } 38 | --------------------------------------------------------------------------------