├── .github └── workflows │ └── go.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG ├── LICENSE ├── Makefile ├── README.md ├── TODO ├── cmd ├── shadowsocks-httpget │ └── httpget.go ├── shadowsocks-local │ └── local.go └── shadowsocks-server │ └── server.go ├── config.json ├── deb ├── DEBIAN │ ├── conffiles │ ├── control │ ├── postinst │ ├── postrm │ └── prerm └── etc │ ├── init.d │ └── shadowsocks │ └── shadowsocks │ └── config.json ├── go.mod ├── go.sum ├── goreleaser.yml ├── sample-config ├── client-multi-server.json └── server-multi-port.json ├── script ├── README.md ├── build.sh ├── createdeb.sh ├── curl.sh ├── http.go ├── set-version.sh ├── shadowsocks.exe ├── test.sh └── win32build.bat └── shadowsocks ├── config.go ├── config_test.go ├── conn.go ├── encrypt.go ├── encrypt_test.go ├── leakybuf.go ├── log.go ├── mergesort.go ├── pipe.go ├── testdata ├── deprecated-client-multi-server.json └── noserver.json └── util.go /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | 10 | build: 11 | name: GoReleaser build # ref: https://goreleaser.com/ci/actions/ 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Check out code into the Go module directory 16 | uses: actions/checkout@v2 17 | with: 18 | fetch-depth: 0 # See: https://goreleaser.com/ci/actions/ 19 | 20 | - name: Set up Go 1.15 21 | uses: actions/setup-go@v2 22 | with: 23 | go-version: 1.15 24 | id: go 25 | 26 | - name: Release 27 | uses: goreleaser/goreleaser-action@master 28 | with: 29 | version: latest 30 | args: release --rm-dist 31 | env: 32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.deb 2 | script/http 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.4.2 4 | install: 5 | - go get golang.org/x/crypto/blowfish 6 | - go get golang.org/x/crypto/cast5 7 | - go get golang.org/x/crypto/salsa20 8 | - go get github.com/codahale/chacha20 9 | - go install ./cmd/shadowsocks-local 10 | - go install ./cmd/shadowsocks-server 11 | script: 12 | - PATH=$PATH:$HOME/gopath/bin bash -x ./script/test.sh 13 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 1.1.4 (2015-05-10) 2 | * Support "chacha20" encryption method, thanks to @defia 3 | * Support "salsa20" encryption method, thanks to @genzj 4 | * Fix go 1.4 canonical import paths, thanks to @ddatsh 5 | * Exit if port not bindable, thanks to @thomasf 6 | * Always set timeout for data transfer 7 | * More buffer reuse 8 | 9 | 1.1.3 (2014-09-28) 10 | * Fix can't specify encryption method in config file 11 | 12 | 1.1.2 (2014-09-21) 13 | * Support new encryption method "rc4-md5" 14 | * Use aes-256-cfb as default encryption method for command line app 15 | 16 | 1.1.1 (2013-07-12) 17 | * Add -b option to limit listen address for client 18 | * Fix can't override server address on command line 19 | 20 | 1.1 (2013-05-26) 21 | * Add more encryption methods 22 | * Enable CGO for OS X when building 23 | 24 | 1.0 (2013-05-17) 25 | * Support specify servers with IPv6 address 26 | * Support IPv6 address in socks requests 27 | * Better handling of configuration file for debian package 28 | 29 | 0.6.2 (2013-03-15) 30 | * Connect to multiple servers in the order specified in config 31 | * More information in server error log to help error diagnosing 32 | 33 | 0.6.1 (2013-03-04) 34 | * Small performance improvement 35 | * For windows: provide shadowsocks-tray.exe to hide shadowsocks-local.exe in 36 | system tray. (Thanks to phuslu's taskbar.) 37 | 38 | 0.6 (2013-01-23) 39 | 40 | * Generate ciphers on demand (encryption table cache is removed) 41 | * Support RC4 encryption 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Use shadowsocks as command prefix to avoid name conflict 2 | # Maybe ss-local/server is better because easier to type 3 | PREFIX := shadowsocks 4 | LOCAL := $(GOPATH)/bin/$(PREFIX)-local 5 | SERVER := $(GOPATH)/bin/$(PREFIX)-server 6 | CGO := CGO_ENABLED=1 7 | 8 | all: $(LOCAL) $(SERVER) $(TEST) 9 | 10 | .PHONY: clean 11 | 12 | clean: 13 | rm -f $(LOCAL) $(SERVER) $(TEST) 14 | 15 | # -a option is needed to ensure we disabled CGO 16 | $(LOCAL): shadowsocks/*.go cmd/$(PREFIX)-local/*.go 17 | cd cmd/$(PREFIX)-local; $(CGO) go install 18 | 19 | $(SERVER): shadowsocks/*.go cmd/$(PREFIX)-server/*.go 20 | cd cmd/$(PREFIX)-server; $(CGO) go install 21 | 22 | local: $(LOCAL) 23 | 24 | server: $(SERVER) 25 | 26 | test: 27 | cd shadowsocks; go test 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fast-shadowsocks 2 | 3 | ## 新功能 4 | 5 | - 快速传输协议:特性参见[dearplain/penet](http://github.com/dearplain/penet) 6 | - http代理 7 | - 增加加密方式(aes-128-gcm, aes-192-gcm, aes-256-gcm, chacha20-ietf-poly1305) 8 | 9 | ## 速度测试 10 | 11 | ### kcptun 12 | 13 | 参数: 14 | client_windows_amd64 -r "xxxx:2001" -l ":8388" -mode fast3 -nocomp -autoexpire 900 -sockbuf 16777217 -dscp 46 15 | 16 | ![kcptun](https://user-images.githubusercontent.com/1262589/109282700-d8854580-7858-11eb-9cd1-671cf5b8b84c.png) 17 | 18 | ### fast-shadowsocks 19 | 20 | ![fast-shadowsocks](https://user-images.githubusercontent.com/1262589/109282722-df13bd00-7858-11eb-96cd-988fcfdf78c5.png) 21 | 22 | # shadowsocks-go 23 | 24 | Current version: 1.1.4 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=master)](https://travis-ci.org/shadowsocks/shadowsocks-go) 25 | 26 | shadowsocks-go is a lightweight tunnel proxy which can help you get through firewalls. It is a port of [shadowsocks](https://github.com/clowwindy/shadowsocks). 27 | 28 | The protocol is compatible with the origin shadowsocks (if both have been upgraded to the latest version). 29 | 30 | **Note `server_password` option syntax changed in 0.6.2, the client now connects to servers in the order specified in the config.** 31 | 32 | **Please develop on the latest develop branch if you want to send pull request.** 33 | 34 | # Install 35 | 36 | Compiled client binaries can be download [here](http://dl.chenyufei.info/shadowsocks/). (All compiled with cgo disabled, except the mac version.) 37 | 38 | You can also install from source (assume you have go installed): 39 | 40 | ``` 41 | # on server 42 | go get github.com/shadowsocks/shadowsocks-go/cmd/shadowsocks-server 43 | # on client 44 | go get github.com/shadowsocks/shadowsocks-go/cmd/shadowsocks-local 45 | ``` 46 | 47 | It's recommended to disable cgo when compiling shadowsocks-go. This will prevent the go runtime from creating too many threads for dns lookup. 48 | 49 | # Usage 50 | 51 | Both the server and client program will look for `config.json` in the current directory. You can use `-c` option to specify another configuration file. 52 | 53 | Configuration file is in json format and has the same syntax with [shadowsocks-nodejs](https://github.com/clowwindy/shadowsocks-nodejs/). You can download the sample [`config.json`](https://github.com/shadowsocks/shadowsocks-go/blob/master/config.json), change the following values: 54 | 55 | ``` 56 | server your server ip or hostname 57 | server_port server port 58 | local_port local socks5 proxy port 59 | method encryption method, null by default (table), the following methods are supported: 60 | aes-128-cfb, aes-192-cfb, aes-256-cfb, bf-cfb, cast5-cfb, des-cfb, rc4-md5, chacha20, salsa20, rc4, table 61 | password a password used to encrypt transfer 62 | timeout server option, in seconds 63 | ``` 64 | 65 | Run `shadowsocks-server` on your server. To run it in the background, run `shadowsocks-server > log &`. 66 | 67 | On client, run `shadowsocks-local`. Change proxy settings of your browser to 68 | 69 | ``` 70 | SOCKS5 127.0.0.1:local_port 71 | ``` 72 | 73 | ## About encryption methods 74 | 75 | AES is recommended for shadowsocks-go. [Intel AES Instruction Set](http://en.wikipedia.org/wiki/AES_instruction_set) will be used if available and can make encryption/decryption very fast. To be more specific, **`aes-128-cfb` is recommended as it is faster and [secure enough](https://www.schneier.com/blog/archives/2009/07/another_new_aes.html)**. 76 | 77 | **rc4 and table encryption methods are deprecated because they are not secure.** 78 | 79 | ## Command line options 80 | 81 | Command line options can override settings from configuration files. Use `-h` option to see all available options. 82 | 83 | ``` 84 | shadowsocks-local -s server_address -p server_port -k password 85 | -m aes-128-cfb -c config.json 86 | -b local_address -l local_port 87 | shadowsocks-server -p server_port -k password 88 | -m aes-128-cfb -c config.json 89 | -t timeout 90 | ``` 91 | 92 | Use `-d` option to enable debug message. 93 | 94 | ## Use multiple servers on client 95 | 96 | ``` 97 | server_password specify multiple server and password, server should be in the form of host:port 98 | ``` 99 | 100 | Here's a sample configuration [`client-multi-server.json`](https://github.com/shadowsocks/shadowsocks-go/blob/master/sample-config/client-multi-server.json). Given `server_password`, client program will ignore `server_port`, `server` and `password` options. 101 | 102 | Servers are chosen in the order specified in the config. If a server can't be connected (connection failure), the client will try the next one. (Client will retry failed server with some probability to discover server recovery.) 103 | 104 | ## Multiple users with different passwords on server 105 | 106 | The server can support users with different passwords. Each user will be served by a unique port. Use the following options on the server for such setup: 107 | 108 | ``` 109 | port_password specify multiple ports and passwords to support multiple users 110 | ``` 111 | 112 | Here's a sample configuration [`server-multi-port.json`](https://github.com/shadowsocks/shadowsocks-go/blob/master/sample-config/server-multi-port.json). Given `port_password`, server program will ignore `server_port` and `password` options. 113 | 114 | ### Update port password for a running server 115 | 116 | Edit the config file used to start the server, then send `SIGHUP` to the server process. 117 | 118 | # Note to OpenVZ users 119 | 120 | **Use OpenVZ VM that supports vswap**. Otherwise, the OS will incorrectly account much more memory than actually used. shadowsocks-go on OpenVZ VM with vswap takes about 3MB memory after startup. (Refer to [this issue](https://github.com/shadowsocks/shadowsocks-go/issues/3) for more details.) 121 | 122 | If vswap is not an option and memory usage is a problem for you, try [shadowsocks-libev](https://github.com/madeye/shadowsocks-libev). 123 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dearplain/fast-shadowsocks/059381059954ee63cfacbac672fb14e9690bf42c/TODO -------------------------------------------------------------------------------- /cmd/shadowsocks-httpget/httpget.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" 7 | "io" 8 | "math" 9 | "net" 10 | "net/http" 11 | "net/url" 12 | "os" 13 | "runtime" 14 | "strconv" 15 | "strings" 16 | "time" 17 | ) 18 | 19 | var config struct { 20 | server string 21 | port int 22 | passwd string 23 | method string 24 | core int 25 | nconn int 26 | nreq int 27 | // nsec int 28 | } 29 | 30 | var debug ss.DebugLog 31 | 32 | func doOneRequest(client *http.Client, uri string, buf []byte) (err error) { 33 | resp, err := client.Get(uri) 34 | if err != nil { 35 | fmt.Printf("GET %s error: %v\n", uri, err) 36 | return err 37 | } 38 | for err == nil { 39 | _, err = resp.Body.Read(buf) 40 | if debug { 41 | debug.Println(string(buf)) 42 | } 43 | } 44 | if err != io.EOF { 45 | fmt.Printf("Read %s response error: %v\n", uri, err) 46 | } else { 47 | err = nil 48 | } 49 | return 50 | } 51 | 52 | func get(connid int, uri, serverAddr string, rawAddr []byte, cipher *ss.Cipher, done chan []time.Duration) { 53 | reqDone := 0 54 | reqTime := make([]time.Duration, config.nreq) 55 | defer func() { 56 | done <- reqTime[:reqDone] 57 | }() 58 | tr := &http.Transport{ 59 | Dial: func(_, _ string) (net.Conn, error) { 60 | return ss.DialWithRawAddr(rawAddr, serverAddr, cipher.Copy()) 61 | }, 62 | } 63 | 64 | buf := make([]byte, 8192) 65 | client := &http.Client{Transport: tr} 66 | for ; reqDone < config.nreq; reqDone++ { 67 | start := time.Now() 68 | if err := doOneRequest(client, uri, buf); err != nil { 69 | return 70 | } 71 | reqTime[reqDone] = time.Now().Sub(start) 72 | 73 | if (reqDone+1)%1000 == 0 { 74 | fmt.Printf("conn %d finished %d get requests\n", connid, reqDone+1) 75 | } 76 | } 77 | } 78 | 79 | func main() { 80 | flag.StringVar(&config.server, "s", "127.0.0.1", "server:port") 81 | flag.IntVar(&config.port, "p", 0, "server:port") 82 | flag.IntVar(&config.core, "core", 1, "number of CPU cores to use") 83 | flag.StringVar(&config.passwd, "k", "", "password") 84 | flag.StringVar(&config.method, "m", "", "encryption method, use empty string or rc4") 85 | flag.IntVar(&config.nconn, "nc", 1, "number of connection to server") 86 | flag.IntVar(&config.nreq, "nr", 1, "number of request for each connection") 87 | // flag.IntVar(&config.nsec, "ns", 0, "run how many seconds for each connection") 88 | flag.BoolVar((*bool)(&debug), "d", false, "print http response body for debugging") 89 | 90 | flag.Parse() 91 | 92 | if config.server == "" || config.port == 0 || config.passwd == "" || len(flag.Args()) != 1 { 93 | fmt.Printf("Usage: %s -s -p -k \n", os.Args[0]) 94 | os.Exit(1) 95 | } 96 | 97 | runtime.GOMAXPROCS(config.core) 98 | uri := flag.Arg(0) 99 | if strings.HasPrefix(uri, "https://") { 100 | fmt.Println("https not supported") 101 | os.Exit(1) 102 | } 103 | if !strings.HasPrefix(uri, "http://") { 104 | uri = "http://" + uri 105 | } 106 | 107 | cipher, err := ss.NewCipher(config.method, config.passwd) 108 | if err != nil { 109 | fmt.Println("Error creating cipher:", err) 110 | os.Exit(1) 111 | } 112 | serverAddr := net.JoinHostPort(config.server, strconv.Itoa(config.port)) 113 | 114 | parsedURL, err := url.Parse(uri) 115 | if err != nil { 116 | fmt.Println("Error parsing url:", err) 117 | os.Exit(1) 118 | } 119 | host, _, err := net.SplitHostPort(parsedURL.Host) 120 | if err != nil { 121 | host = net.JoinHostPort(parsedURL.Host, "80") 122 | } else { 123 | host = parsedURL.Host 124 | } 125 | // fmt.Println(host) 126 | rawAddr, err := ss.RawAddr(host) 127 | if err != nil { 128 | panic("Error getting raw address.") 129 | } 130 | 131 | done := make(chan []time.Duration) 132 | for i := 1; i <= config.nconn; i++ { 133 | go get(i, uri, serverAddr, rawAddr, cipher, done) 134 | } 135 | 136 | // collect request finish time 137 | reqTime := make([]int64, config.nconn*config.nreq) 138 | reqDone := 0 139 | for i := 1; i <= config.nconn; i++ { 140 | rt := <-done 141 | for _, t := range rt { 142 | reqTime[reqDone] = int64(t) 143 | reqDone++ 144 | } 145 | } 146 | 147 | fmt.Println("number of total requests:", config.nconn*config.nreq) 148 | fmt.Println("number of finished requests:", reqDone) 149 | if reqDone == 0 { 150 | return 151 | } 152 | 153 | // calculate average an standard deviation 154 | reqTime = reqTime[:reqDone] 155 | var sum int64 156 | for _, d := range reqTime { 157 | sum += d 158 | } 159 | avg := float64(sum) / float64(reqDone) 160 | 161 | varSum := float64(0) 162 | for _, d := range reqTime { 163 | di := math.Abs(float64(d) - avg) 164 | di *= di 165 | varSum += di 166 | } 167 | stddev := math.Sqrt(varSum / float64(reqDone)) 168 | fmt.Println("\naverage time per request:", time.Duration(avg)) 169 | fmt.Println("standard deviation:", time.Duration(stddev)) 170 | } 171 | -------------------------------------------------------------------------------- /cmd/shadowsocks-local/local.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "flag" 7 | "fmt" 8 | "io" 9 | "log" 10 | "math/rand" 11 | "net" 12 | "net/url" 13 | "os" 14 | "path" 15 | "strconv" 16 | "strings" 17 | "time" 18 | 19 | ss "github.com/dearplain/fast-shadowsocks/shadowsocks" 20 | "github.com/dearplain/penet" 21 | ) 22 | 23 | var debug ss.DebugLog 24 | 25 | var ( 26 | errAddrType = errors.New("socks addr type not supported") 27 | errVer = errors.New("socks version not supported") 28 | errMethod = errors.New("socks only support 1 method now") 29 | errAuthExtraData = errors.New("socks authentication get extra data") 30 | errReqExtraData = errors.New("socks request get extra data") 31 | errCmd = errors.New("socks command not supported") 32 | errHttpFormat = errors.New("http proxy request format error") 33 | ) 34 | 35 | const ( 36 | socksVer5 = 5 37 | socksCmdConnect = 1 38 | ) 39 | 40 | func init() { 41 | rand.Seed(time.Now().Unix()) 42 | } 43 | 44 | func handShake(conn net.Conn) (err error) { 45 | const ( 46 | idVer = 0 47 | idNmethod = 1 48 | ) 49 | // version identification and method selection message in theory can have 50 | // at most 256 methods, plus version and nmethod field in total 258 bytes 51 | // the current rfc defines only 3 authentication methods (plus 2 reserved), 52 | // so it won't be such long in practice 53 | 54 | buf := make([]byte, 258) 55 | 56 | var n int 57 | ss.SetReadTimeout(conn) 58 | // make sure we get the nmethod field 59 | if n, err = io.ReadAtLeast(conn, buf, idNmethod+1); err != nil { 60 | return 61 | } 62 | if buf[idVer] != socksVer5 { 63 | return errVer 64 | } 65 | nmethod := int(buf[idNmethod]) 66 | msgLen := nmethod + 2 67 | if n == msgLen { // handshake done, common case 68 | // do nothing, jump directly to send confirmation 69 | } else if n < msgLen { // has more methods to read, rare case 70 | if _, err = io.ReadFull(conn, buf[n:msgLen]); err != nil { 71 | return 72 | } 73 | } else { // error, should not get extra data 74 | return errAuthExtraData 75 | } 76 | // send confirmation: version 5, no authentication required 77 | _, err = conn.Write([]byte{socksVer5, 0}) 78 | return 79 | } 80 | 81 | func getRequest(conn net.Conn) (rawaddr []byte, host string, err error) { 82 | const ( 83 | idVer = 0 84 | idCmd = 1 85 | idType = 3 // address type index 86 | idIP0 = 4 // ip addres start index 87 | idDmLen = 4 // domain address length index 88 | idDm0 = 5 // domain address start index 89 | 90 | typeIPv4 = 1 // type is ipv4 address 91 | typeDm = 3 // type is domain address 92 | typeIPv6 = 4 // type is ipv6 address 93 | 94 | lenIPv4 = 3 + 1 + net.IPv4len + 2 // 3(ver+cmd+rsv) + 1addrType + ipv4 + 2port 95 | lenIPv6 = 3 + 1 + net.IPv6len + 2 // 3(ver+cmd+rsv) + 1addrType + ipv6 + 2port 96 | lenDmBase = 3 + 1 + 1 + 2 // 3 + 1addrType + 1addrLen + 2port, plus addrLen 97 | ) 98 | // refer to getRequest in server.go for why set buffer size to 263 99 | buf := make([]byte, 263) 100 | var n int 101 | ss.SetReadTimeout(conn) 102 | // read till we get possible domain length field 103 | if n, err = io.ReadAtLeast(conn, buf, idDmLen+1); err != nil { 104 | return 105 | } 106 | // check version and cmd 107 | if buf[idVer] != socksVer5 { 108 | err = errVer 109 | return 110 | } 111 | if buf[idCmd] != socksCmdConnect { 112 | err = errCmd 113 | return 114 | } 115 | 116 | reqLen := -1 117 | switch buf[idType] { 118 | case typeIPv4: 119 | reqLen = lenIPv4 120 | case typeIPv6: 121 | reqLen = lenIPv6 122 | case typeDm: 123 | reqLen = int(buf[idDmLen]) + lenDmBase 124 | default: 125 | err = errAddrType 126 | return 127 | } 128 | 129 | if n == reqLen { 130 | // common case, do nothing 131 | } else if n < reqLen { // rare case 132 | if _, err = io.ReadFull(conn, buf[n:reqLen]); err != nil { 133 | return 134 | } 135 | } else { 136 | err = errReqExtraData 137 | return 138 | } 139 | 140 | rawaddr = buf[idType:reqLen] 141 | 142 | if debug { 143 | switch buf[idType] { 144 | case typeIPv4: 145 | host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() 146 | case typeIPv6: 147 | host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() 148 | case typeDm: 149 | host = string(buf[idDm0 : idDm0+buf[idDmLen]]) 150 | } 151 | port := binary.BigEndian.Uint16(buf[reqLen-2 : reqLen]) 152 | host = net.JoinHostPort(host, strconv.Itoa(int(port))) 153 | } 154 | 155 | return 156 | } 157 | 158 | type ServerCipher struct { 159 | server string 160 | cipher *ss.Cipher 161 | } 162 | 163 | var servers struct { 164 | srvCipher []*ServerCipher 165 | failCnt []int // failed connection count 166 | } 167 | 168 | func parseServerConfig(config *ss.Config) { 169 | hasPort := func(s string) bool { 170 | _, port, err := net.SplitHostPort(s) 171 | if err != nil { 172 | return false 173 | } 174 | return port != "" 175 | } 176 | 177 | if len(config.ServerPassword) == 0 { 178 | // only one encryption table 179 | cipher, err := ss.NewCipher(config.Method, config.Password) 180 | if err != nil { 181 | log.Fatal("Failed generating ciphers:", err) 182 | } 183 | srvPort := strconv.Itoa(config.ServerPort) 184 | srvArr := config.GetServerArray() 185 | n := len(srvArr) 186 | servers.srvCipher = make([]*ServerCipher, n) 187 | 188 | for i, s := range srvArr { 189 | if hasPort(s) { 190 | log.Println("ignore server_port option for server", s) 191 | servers.srvCipher[i] = &ServerCipher{s, cipher} 192 | } else { 193 | servers.srvCipher[i] = &ServerCipher{net.JoinHostPort(s, srvPort), cipher} 194 | } 195 | } 196 | } else { 197 | // multiple servers 198 | n := len(config.ServerPassword) 199 | servers.srvCipher = make([]*ServerCipher, n) 200 | 201 | cipherCache := make(map[string]*ss.Cipher) 202 | i := 0 203 | for _, serverInfo := range config.ServerPassword { 204 | if len(serverInfo) < 2 || len(serverInfo) > 3 { 205 | log.Fatalf("server %v syntax error\n", serverInfo) 206 | } 207 | server := serverInfo[0] 208 | passwd := serverInfo[1] 209 | encmethod := "" 210 | if len(serverInfo) == 3 { 211 | encmethod = serverInfo[2] 212 | } 213 | if !hasPort(server) { 214 | log.Fatalf("no port for server %s\n", server) 215 | } 216 | cipher, ok := cipherCache[passwd] 217 | if !ok { 218 | var err error 219 | cipher, err = ss.NewCipher(encmethod, passwd) 220 | if err != nil { 221 | log.Fatal("Failed generating ciphers:", err) 222 | } 223 | cipherCache[passwd] = cipher 224 | } 225 | servers.srvCipher[i] = &ServerCipher{server, cipher} 226 | i++ 227 | } 228 | } 229 | servers.failCnt = make([]int, len(servers.srvCipher)) 230 | for _, se := range servers.srvCipher { 231 | log.Println("available remote server", se.server) 232 | } 233 | return 234 | } 235 | 236 | func DialWithRawAddr(rawaddr []byte, server string, cipher *ss.Cipher) (c *ss.Conn, err error) { 237 | conn, err := penet.Dial("tcp", server) 238 | if err != nil { 239 | return 240 | } 241 | c = ss.NewConn(conn, cipher) 242 | if _, err = c.Write(rawaddr); err != nil { 243 | c.Close() 244 | return nil, err 245 | } 246 | return 247 | } 248 | 249 | func connectToServer(serverId int, rawaddr []byte, addr string) (remote *ss.Conn, err error) { 250 | se := servers.srvCipher[serverId] 251 | remote, err = DialWithRawAddr(rawaddr, se.server, se.cipher.Copy()) 252 | if err != nil { 253 | log.Println("error connecting to shadowsocks server:", err) 254 | const maxFailCnt = 30 255 | if servers.failCnt[serverId] < maxFailCnt { 256 | servers.failCnt[serverId]++ 257 | } 258 | return nil, err 259 | } 260 | debug.Printf("connected to %s via %s\n", addr, se.server) 261 | servers.failCnt[serverId] = 0 262 | return 263 | } 264 | 265 | // Connection to the server in the order specified in the config. On 266 | // connection failure, try the next server. A failed server will be tried with 267 | // some probability according to its fail count, so we can discover recovered 268 | // servers. 269 | func createServerConn(rawaddr []byte, addr string) (remote *ss.Conn, err error) { 270 | const baseFailCnt = 20 271 | n := len(servers.srvCipher) 272 | skipped := make([]int, 0) 273 | for i := 0; i < n; i++ { 274 | // skip failed server, but try it with some probability 275 | if servers.failCnt[i] > 0 && rand.Intn(servers.failCnt[i]+baseFailCnt) != 0 { 276 | skipped = append(skipped, i) 277 | continue 278 | } 279 | remote, err = connectToServer(i, rawaddr, addr) 280 | if err == nil { 281 | return 282 | } 283 | } 284 | // last resort, try skipped servers, not likely to succeed 285 | for _, i := range skipped { 286 | remote, err = connectToServer(i, rawaddr, addr) 287 | if err == nil { 288 | return 289 | } 290 | } 291 | return nil, err 292 | } 293 | 294 | func getSocksRequest(buf []byte) (rawaddr []byte, host string, err error) { 295 | const ( 296 | idVer = 0 297 | idCmd = 1 298 | idType = 3 // address type index 299 | idIP0 = 4 // ip addres start index 300 | idDmLen = 4 // domain address length index 301 | idDm0 = 5 // domain address start index 302 | 303 | typeIPv4 = 1 // type is ipv4 address 304 | typeDm = 3 // type is domain address 305 | typeIPv6 = 4 // type is ipv6 address 306 | 307 | lenIPv4 = 3 + 1 + net.IPv4len + 2 // 3(ver+cmd+rsv) + 1addrType + ipv4 + 2port 308 | lenIPv6 = 3 + 1 + net.IPv6len + 2 // 3(ver+cmd+rsv) + 1addrType + ipv6 + 2port 309 | lenDmBase = 3 + 1 + 1 + 2 // 3 + 1addrType + 1addrLen + 2port, plus addrLen 310 | ) 311 | 312 | n := len(buf) 313 | if buf[idVer] != socksVer5 { 314 | err = errVer 315 | return 316 | } 317 | if buf[idCmd] != socksCmdConnect { 318 | err = errCmd 319 | return 320 | } 321 | 322 | reqLen := -1 323 | switch buf[idType] { 324 | case typeIPv4: 325 | reqLen = lenIPv4 326 | case typeIPv6: 327 | reqLen = lenIPv6 328 | case typeDm: 329 | reqLen = int(buf[idDmLen]) + lenDmBase 330 | default: 331 | err = errAddrType 332 | return 333 | } 334 | 335 | if n == reqLen { 336 | // common case, do nothing 337 | } else if n < reqLen { // rare case 338 | //if _, err = io.ReadFull(conn, buf[n:reqLen]); err != nil { 339 | // return 340 | //} 341 | //err = errReqExtraData 342 | //return 343 | } else { 344 | err = errReqExtraData 345 | return 346 | } 347 | 348 | rawaddr = buf[idType:reqLen] 349 | 350 | if debug { 351 | switch buf[idType] { 352 | case typeIPv4: 353 | host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() 354 | case typeIPv6: 355 | host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() 356 | case typeDm: 357 | host = string(buf[idDm0 : idDm0+buf[idDmLen]]) 358 | } 359 | port := binary.BigEndian.Uint16(buf[reqLen-2 : reqLen]) 360 | host = net.JoinHostPort(host, strconv.Itoa(int(port))) 361 | } 362 | 363 | return 364 | } 365 | 366 | func getHttpRequest(buf []byte) (rawaddr []byte, host string, toWrite, writeBack []byte, err error) { 367 | if buf[0] == 'C' { 368 | sbuf := string(buf) 369 | ss := strings.Split(sbuf, " ") // CONNECT host:port 370 | hp := strings.Split(ss[1], ":") // [host, port] 371 | if debug { 372 | log.Println(hp) 373 | } 374 | if len(hp) != 2 { 375 | err = errHttpFormat 376 | return 377 | } 378 | // [3, len(host), host] 379 | rawaddr = []byte{3, byte(len(hp[0]))} 380 | rawaddr = append(rawaddr, []byte(hp[0])...) 381 | // port 382 | var b [2]byte 383 | i, _ := strconv.Atoi(hp[1]) 384 | binary.BigEndian.PutUint16(b[:], uint16(i)) 385 | rawaddr = append(rawaddr, b[:]...) 386 | 387 | host = ss[1] 388 | if debug { 389 | log.Println(rawaddr) 390 | } 391 | writeBack = []byte("HTTP/1.0 200 Connection established\r\nProxy-agent: Shadowsocks/1.1\r\n\r\n") 392 | } else if buf[0] == 'G' { 393 | sbuf := string(buf) 394 | if debug { 395 | log.Println(sbuf) 396 | } 397 | // [3, len(host), host, port] 398 | ss := strings.Split(sbuf, " ") 399 | u, err2 := url.Parse(ss[1]) 400 | if err2 != nil { 401 | err = err2 402 | return 403 | } 404 | host = u.Host 405 | rawaddr = []byte{3, byte(len(u.Host))} 406 | rawaddr = append(rawaddr, []byte(u.Host)...) 407 | var b [2]byte 408 | var port int 409 | if u.Port() == "" { 410 | if u.Scheme == "http" { 411 | port = 80 412 | } else if u.Scheme == "https" { 413 | port = 443 414 | } else { 415 | err = errHttpFormat 416 | return 417 | } 418 | } else { 419 | port, _ = strconv.Atoi(u.Port()) 420 | } 421 | if debug { 422 | log.Println(u.Scheme, u.Host, port, u.RequestURI()) 423 | } 424 | binary.BigEndian.PutUint16(b[:], uint16(port)) 425 | rawaddr = append(rawaddr, b[:]...) 426 | sbuf = strings.Replace(sbuf, ss[1], u.RequestURI(), 1) 427 | sbuf = strings.Replace(sbuf, "Proxy-Connection: keep-alive\r\n", "", 1) 428 | toWrite = []byte(sbuf) 429 | if debug { 430 | log.Println("toWrite:", string(toWrite)) 431 | } 432 | } else { 433 | err = errHttpFormat 434 | } 435 | 436 | return 437 | } 438 | 439 | func handSocksShakeByte(buf []byte, conn net.Conn) (err error) { 440 | const ( 441 | idVer = 0 442 | idNmethod = 1 443 | ) 444 | 445 | var n int 446 | if buf[idVer] != socksVer5 { 447 | return errVer 448 | } 449 | nmethod := int(buf[idNmethod]) 450 | msgLen := nmethod + 2 451 | if n == msgLen { // handshake done, common case 452 | // do nothing, jump directly to send confirmation 453 | } else if n < msgLen { // has more methods to read, rare case 454 | //if _, err = io.ReadFull(conn, buf[n:msgLen]); err != nil { 455 | // return 456 | //} 457 | //return errAuthExtraData 458 | } else { // error, should not get extra data 459 | return errAuthExtraData 460 | } 461 | // send confirmation: version 5, no authentication required 462 | _, err = conn.Write([]byte{socksVer5, 0}) 463 | return 464 | } 465 | 466 | func handleConnection(conn net.Conn) { 467 | if debug { 468 | debug.Printf("socks connect from %s\n", conn.RemoteAddr().String()) 469 | } 470 | closed := false 471 | defer func() { 472 | if !closed { 473 | conn.Close() 474 | } 475 | }() 476 | 477 | buf := make([]byte, 4096) 478 | var n int 479 | var err error 480 | if n, err = conn.Read(buf); err != nil { 481 | return 482 | } 483 | 484 | var rawaddr []byte 485 | var addr string 486 | var toWrite []byte 487 | var writeBack []byte 488 | //log.Println(string(buf[:n])) 489 | if buf[0] == 'C' || buf[0] == 'G' { 490 | rawaddr, addr, toWrite, writeBack, err = getHttpRequest(buf[:n]) 491 | if err != nil { 492 | log.Println("error getting http request:", err) 493 | return 494 | } 495 | } else { 496 | if err = handSocksShakeByte(buf[:n], conn); err != nil { 497 | log.Println("socks handshake:", err) 498 | return 499 | } 500 | if n, err = conn.Read(buf); err != nil { 501 | log.Println("read:", err) 502 | return 503 | } 504 | rawaddr, addr, err = getSocksRequest(buf[:n]) 505 | if debug { 506 | log.Println(string(rawaddr), "--", rawaddr, addr) 507 | } 508 | if err != nil { 509 | log.Println("error getting request:", err) 510 | return 511 | } 512 | // Sending connection established message immediately to client. 513 | // This some round trip time for creating socks connection with the client. 514 | // But if connection failed, the client will get connection reset error. 515 | _, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43}) 516 | if err != nil { 517 | debug.Println("send connection confirmation:", err) 518 | return 519 | } 520 | } 521 | buf = nil 522 | 523 | remote, err := createServerConn(rawaddr, addr) 524 | if err != nil { 525 | if len(servers.srvCipher) > 1 { 526 | log.Println("Failed connect to all avaiable shadowsocks server") 527 | } 528 | return 529 | } 530 | defer func() { 531 | if !closed { 532 | remote.Close() 533 | } 534 | }() 535 | if len(writeBack) > 0 { 536 | _, err = conn.Write(writeBack) 537 | if err != nil { 538 | debug.Println("send connection confirmation:", err) 539 | return 540 | } 541 | } 542 | if len(toWrite) > 0 { 543 | _, err = remote.Write(toWrite) 544 | if err != nil { 545 | log.Println("http write req:", err) 546 | return 547 | } 548 | } 549 | 550 | go ss.PipeThenClose(conn, remote) 551 | ss.PipeThenClose(remote, conn) 552 | closed = true 553 | debug.Println("closed connection to", addr) 554 | } 555 | 556 | func run(listenAddr string) { 557 | ln, err := net.Listen("tcp", listenAddr) 558 | if err != nil { 559 | log.Fatal(err) 560 | } 561 | log.Printf("starting local socks5 server at %v ...\n", listenAddr) 562 | for { 563 | conn, err := ln.Accept() 564 | if err != nil { 565 | log.Println("accept:", err) 566 | continue 567 | } 568 | go handleConnection(conn) 569 | } 570 | } 571 | 572 | func enoughOptions(config *ss.Config) bool { 573 | return config.Server != nil && config.ServerPort != 0 && 574 | config.LocalPort != 0 && config.Password != "" 575 | } 576 | 577 | func main() { 578 | log.SetOutput(os.Stdout) 579 | 580 | var configFile, cmdServer, cmdLocal string 581 | var cmdConfig ss.Config 582 | var printVer bool 583 | 584 | flag.BoolVar(&printVer, "version", false, "print version") 585 | flag.StringVar(&configFile, "c", "config.json", "specify config file") 586 | flag.StringVar(&cmdServer, "s", "", "server address") 587 | flag.StringVar(&cmdLocal, "b", "", "local address, listen only to this address if specified") 588 | flag.StringVar(&cmdConfig.Password, "k", "", "password") 589 | flag.IntVar(&cmdConfig.ServerPort, "p", 0, "server port") 590 | flag.IntVar(&cmdConfig.Timeout, "t", 300, "timeout in seconds") 591 | flag.IntVar(&cmdConfig.LocalPort, "l", 0, "local socks5 proxy port") 592 | flag.StringVar(&cmdConfig.Method, "m", "", "encryption method, default: aes-256-cfb") 593 | flag.BoolVar((*bool)(&debug), "d", false, "print debug message") 594 | 595 | flag.Parse() 596 | 597 | if printVer { 598 | ss.PrintVersion() 599 | os.Exit(0) 600 | } 601 | 602 | cmdConfig.Server = cmdServer 603 | ss.SetDebug(debug) 604 | 605 | exists, err := ss.IsFileExists(configFile) 606 | // If no config file in current directory, try search it in the binary directory 607 | // Note there's no portable way to detect the binary directory. 608 | binDir := path.Dir(os.Args[0]) 609 | if (!exists || err != nil) && binDir != "" && binDir != "." { 610 | oldConfig := configFile 611 | configFile = path.Join(binDir, "config.json") 612 | log.Printf("%s not found, try config file %s\n", oldConfig, configFile) 613 | } 614 | 615 | config, err := ss.ParseConfig(configFile) 616 | if err != nil { 617 | config = &cmdConfig 618 | if !os.IsNotExist(err) { 619 | fmt.Fprintf(os.Stderr, "error reading %s: %v\n", configFile, err) 620 | os.Exit(1) 621 | } 622 | } else { 623 | ss.UpdateConfig(config, &cmdConfig) 624 | } 625 | if config.Method == "" { 626 | config.Method = "aes-256-cfb" 627 | } 628 | if len(config.ServerPassword) == 0 { 629 | if !enoughOptions(config) { 630 | fmt.Fprintln(os.Stderr, "must specify server address, password and both server/local port") 631 | os.Exit(1) 632 | } 633 | } else { 634 | if config.Password != "" || config.ServerPort != 0 || config.GetServerArray() != nil { 635 | fmt.Fprintln(os.Stderr, "given server_password, ignore server, server_port and password option:", config) 636 | } 637 | if config.LocalPort == 0 { 638 | fmt.Fprintln(os.Stderr, "must specify local port") 639 | os.Exit(1) 640 | } 641 | } 642 | 643 | parseServerConfig(config) 644 | 645 | run(cmdLocal + ":" + strconv.Itoa(config.LocalPort)) 646 | } 647 | -------------------------------------------------------------------------------- /cmd/shadowsocks-server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "flag" 7 | "fmt" 8 | "io" 9 | "log" 10 | "net" 11 | "os" 12 | "os/signal" 13 | "runtime" 14 | "strconv" 15 | "sync" 16 | "syscall" 17 | "time" 18 | 19 | ss "github.com/dearplain/fast-shadowsocks/shadowsocks" 20 | "github.com/dearplain/penet" 21 | ) 22 | 23 | var debug ss.DebugLog 24 | 25 | func getRequest(conn *ss.Conn) (host string, extra []byte, err error) { 26 | const ( 27 | idType = 0 // address type index 28 | idIP0 = 1 // ip addres start index 29 | idDmLen = 1 // domain address length index 30 | idDm0 = 2 // domain address start index 31 | 32 | typeIPv4 = 1 // type is ipv4 address 33 | typeDm = 3 // type is domain address 34 | typeIPv6 = 4 // type is ipv6 address 35 | 36 | lenIPv4 = 1 + net.IPv4len + 2 // 1addrType + ipv4 + 2port 37 | lenIPv6 = 1 + net.IPv6len + 2 // 1addrType + ipv6 + 2port 38 | lenDmBase = 1 + 1 + 2 // 1addrType + 1addrLen + 2port, plus addrLen 39 | ) 40 | 41 | // buf size should at least have the same size with the largest possible 42 | // request size (when addrType is 3, domain name has at most 256 bytes) 43 | // 1(addrType) + 1(lenByte) + 256(max length address) + 2(port) 44 | buf := make([]byte, 260) 45 | var n int 46 | // read till we get possible domain length field 47 | ss.SetReadTimeout(conn) 48 | if n, err = io.ReadAtLeast(conn, buf, idDmLen+1); err != nil { 49 | return 50 | } 51 | 52 | reqLen := -1 53 | switch buf[idType] { 54 | case typeIPv4: 55 | reqLen = lenIPv4 56 | case typeIPv6: 57 | reqLen = lenIPv6 58 | case typeDm: 59 | reqLen = int(buf[idDmLen]) + lenDmBase 60 | default: 61 | err = fmt.Errorf("addr type %d not supported", buf[idType]) 62 | return 63 | } 64 | 65 | if n < reqLen { // rare case 66 | if _, err = io.ReadFull(conn, buf[n:reqLen]); err != nil { 67 | return 68 | } 69 | } else if n > reqLen { 70 | // it's possible to read more than just the request head 71 | extra = buf[reqLen:n] 72 | } 73 | 74 | // Return string for typeIP is not most efficient, but browsers (Chrome, 75 | // Safari, Firefox) all seems using typeDm exclusively. So this is not a 76 | // big problem. 77 | switch buf[idType] { 78 | case typeIPv4: 79 | host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() 80 | case typeIPv6: 81 | host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() 82 | case typeDm: 83 | host = string(buf[idDm0 : idDm0+buf[idDmLen]]) 84 | } 85 | // parse port 86 | port := binary.BigEndian.Uint16(buf[reqLen-2 : reqLen]) 87 | host = net.JoinHostPort(host, strconv.Itoa(int(port))) 88 | return 89 | } 90 | 91 | const logCntDelta = 100 92 | 93 | var connCnt int 94 | var nextLogConnCnt int = logCntDelta 95 | 96 | func handleConnection(conn *ss.Conn) { 97 | var host string 98 | 99 | connCnt++ // this maybe not accurate, but should be enough 100 | if connCnt-nextLogConnCnt >= 0 { 101 | // XXX There's no xadd in the atomic package, so it's difficult to log 102 | // the message only once with low cost. Also note nextLogConnCnt maybe 103 | // added twice for current peak connection number level. 104 | log.Printf("Number of client connections reaches %d\n", nextLogConnCnt) 105 | nextLogConnCnt += logCntDelta 106 | } 107 | 108 | // function arguments are always evaluated, so surround debug statement 109 | // with if statement 110 | if debug { 111 | debug.Printf("new client %s->%s\n", conn.RemoteAddr().String(), conn.LocalAddr()) 112 | } 113 | closed := false 114 | defer func() { 115 | if debug { 116 | debug.Printf("closed pipe %s<->%s\n", conn.RemoteAddr(), host) 117 | } 118 | connCnt-- 119 | if !closed { 120 | conn.Close() 121 | } 122 | }() 123 | 124 | host, extra, err := getRequest(conn) 125 | if err != nil { 126 | log.Println("error getting request", conn.RemoteAddr(), conn.LocalAddr(), err) 127 | return 128 | } 129 | if debug { 130 | debug.Println("connecting", host) 131 | } 132 | remote, err := net.DialTimeout("tcp", host, time.Second*2) 133 | if err != nil { 134 | if ne, ok := err.(*net.OpError); ok && (ne.Err == syscall.EMFILE || ne.Err == syscall.ENFILE) { 135 | // log too many open file error 136 | // EMFILE is process reaches open file limits, ENFILE is system limit 137 | log.Println("dial error:", err) 138 | } else { 139 | log.Println("error connecting to:", host, err) 140 | } 141 | return 142 | } 143 | defer func() { 144 | if !closed { 145 | remote.Close() 146 | } 147 | }() 148 | // write extra bytes read from 149 | if extra != nil { 150 | // debug.Println("getRequest read extra data, writing to remote, len", len(extra)) 151 | if _, err = remote.Write(extra); err != nil { 152 | debug.Println("write request extra error:", err) 153 | return 154 | } 155 | } 156 | if debug { 157 | debug.Printf("piping %s<->%s", conn.RemoteAddr(), host) 158 | } 159 | go ss.PipeThenClose(conn, remote) 160 | ss.PipeThenClose(remote, conn) 161 | closed = true 162 | return 163 | } 164 | 165 | type PortListener struct { 166 | password string 167 | listener net.Listener 168 | } 169 | 170 | type PasswdManager struct { 171 | sync.Mutex 172 | portListener map[string]*PortListener 173 | } 174 | 175 | func (pm *PasswdManager) add(port, password string, listener net.Listener) { 176 | pm.Lock() 177 | pm.portListener[port] = &PortListener{password, listener} 178 | pm.Unlock() 179 | } 180 | 181 | func (pm *PasswdManager) get(port string) (pl *PortListener, ok bool) { 182 | pm.Lock() 183 | pl, ok = pm.portListener[port] 184 | pm.Unlock() 185 | return 186 | } 187 | 188 | func (pm *PasswdManager) del(port string) { 189 | pl, ok := pm.get(port) 190 | if !ok { 191 | return 192 | } 193 | pl.listener.Close() 194 | pm.Lock() 195 | delete(pm.portListener, port) 196 | pm.Unlock() 197 | } 198 | 199 | // Update port password would first close a port and restart listening on that 200 | // port. A different approach would be directly change the password used by 201 | // that port, but that requires **sharing** password between the port listener 202 | // and password manager. 203 | func (pm *PasswdManager) updatePortPasswd(port, password string) { 204 | pl, ok := pm.get(port) 205 | if !ok { 206 | log.Printf("new port %s added\n", port) 207 | } else { 208 | if pl.password == password { 209 | return 210 | } 211 | log.Printf("closing port %s to update password\n", port) 212 | pl.listener.Close() 213 | } 214 | // run will add the new port listener to passwdManager. 215 | // So there maybe concurrent access to passwdManager and we need lock to protect it. 216 | go run(port, password) 217 | } 218 | 219 | var passwdManager = PasswdManager{portListener: map[string]*PortListener{}} 220 | 221 | func updatePasswd() { 222 | log.Println("updating password") 223 | newconfig, err := ss.ParseConfig(configFile) 224 | if err != nil { 225 | log.Printf("error parsing config file %s to update password: %v\n", configFile, err) 226 | return 227 | } 228 | oldconfig := config 229 | config = newconfig 230 | 231 | if err = unifyPortPassword(config); err != nil { 232 | return 233 | } 234 | for port, passwd := range config.PortPassword { 235 | passwdManager.updatePortPasswd(port, passwd) 236 | if oldconfig.PortPassword != nil { 237 | delete(oldconfig.PortPassword, port) 238 | } 239 | } 240 | // port password still left in the old config should be closed 241 | for port, _ := range oldconfig.PortPassword { 242 | log.Printf("closing port %s as it's deleted\n", port) 243 | passwdManager.del(port) 244 | } 245 | log.Println("password updated") 246 | } 247 | 248 | func waitSignal() { 249 | var sigChan = make(chan os.Signal, 1) 250 | signal.Notify(sigChan, syscall.SIGHUP) 251 | for sig := range sigChan { 252 | if sig == syscall.SIGHUP { 253 | updatePasswd() 254 | } else { 255 | // is this going to happen? 256 | log.Printf("caught signal %v, exit", sig) 257 | os.Exit(0) 258 | } 259 | } 260 | } 261 | 262 | func run(port, password string) { 263 | ln, err := penet.Listen("tcp", ":"+port) 264 | if err != nil { 265 | log.Printf("error listening port %v: %v\n", port, err) 266 | os.Exit(1) 267 | } 268 | passwdManager.add(port, password, ln) 269 | var cipher *ss.Cipher 270 | log.Printf("server listening port %v ...\n", port) 271 | for { 272 | conn, err := ln.Accept() 273 | if err != nil { 274 | // listener maybe closed to update password 275 | debug.Printf("accept error: %v\n", err) 276 | return 277 | } 278 | // Creating cipher upon first connection. 279 | if cipher == nil { 280 | log.Println("creating cipher for port:", port) 281 | cipher, err = ss.NewCipher(config.Method, password) 282 | if err != nil { 283 | log.Printf("Error generating cipher for port: %s %v\n", port, err) 284 | conn.Close() 285 | continue 286 | } 287 | } 288 | go handleConnection(ss.NewConn(conn, cipher.Copy())) 289 | } 290 | } 291 | 292 | func enoughOptions(config *ss.Config) bool { 293 | return config.ServerPort != 0 && config.Password != "" 294 | } 295 | 296 | func unifyPortPassword(config *ss.Config) (err error) { 297 | if len(config.PortPassword) == 0 { // this handles both nil PortPassword and empty one 298 | if !enoughOptions(config) { 299 | fmt.Fprintln(os.Stderr, "must specify both port and password") 300 | return errors.New("not enough options") 301 | } 302 | port := strconv.Itoa(config.ServerPort) 303 | config.PortPassword = map[string]string{port: config.Password} 304 | } else { 305 | if config.Password != "" || config.ServerPort != 0 { 306 | fmt.Fprintln(os.Stderr, "given port_password, ignore server_port and password option") 307 | } 308 | } 309 | return 310 | } 311 | 312 | var configFile string 313 | var config *ss.Config 314 | 315 | func main() { 316 | log.SetOutput(os.Stdout) 317 | 318 | var cmdConfig ss.Config 319 | var printVer bool 320 | var core int 321 | 322 | flag.BoolVar(&printVer, "version", false, "print version") 323 | flag.StringVar(&configFile, "c", "config.json", "specify config file") 324 | flag.StringVar(&cmdConfig.Password, "k", "", "password") 325 | flag.IntVar(&cmdConfig.ServerPort, "p", 0, "server port") 326 | flag.IntVar(&cmdConfig.Timeout, "t", 300, "timeout in seconds") 327 | flag.StringVar(&cmdConfig.Method, "m", "", "encryption method, default: aes-256-cfb") 328 | flag.IntVar(&core, "core", 0, "maximum number of CPU cores to use, default is determinied by Go runtime") 329 | flag.BoolVar((*bool)(&debug), "d", false, "print debug message") 330 | 331 | flag.Parse() 332 | 333 | if printVer { 334 | ss.PrintVersion() 335 | os.Exit(0) 336 | } 337 | 338 | ss.SetDebug(debug) 339 | 340 | var err error 341 | config, err = ss.ParseConfig(configFile) 342 | if err != nil { 343 | if !os.IsNotExist(err) { 344 | fmt.Fprintf(os.Stderr, "error reading %s: %v\n", configFile, err) 345 | os.Exit(1) 346 | } 347 | config = &cmdConfig 348 | } else { 349 | ss.UpdateConfig(config, &cmdConfig) 350 | } 351 | if config.Method == "" { 352 | config.Method = "aes-256-cfb" 353 | } 354 | // if err = ss.CheckCipherMethod(config.Method); err != nil { 355 | // fmt.Fprintln(os.Stderr, err) 356 | // os.Exit(1) 357 | // } 358 | if err = unifyPortPassword(config); err != nil { 359 | os.Exit(1) 360 | } 361 | if core > 0 { 362 | runtime.GOMAXPROCS(core) 363 | } 364 | for port, password := range config.PortPassword { 365 | go run(port, password) 366 | } 367 | 368 | waitSignal() 369 | } 370 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "server":"127.0.0.1", 3 | "server_port":8388, 4 | "local_port":1080, 5 | "password":"barfoo!", 6 | "method": "aes-128-cfb", 7 | "timeout":600 8 | } 9 | -------------------------------------------------------------------------------- /deb/DEBIAN/conffiles: -------------------------------------------------------------------------------- 1 | /etc/shadowsocks/config.json 2 | /etc/init.d/shadowsocks 3 | -------------------------------------------------------------------------------- /deb/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Package: shadowsocks-go 2 | Version: VER-1 3 | Section: net 4 | Priority: optional 5 | Architecture: any 6 | Depends: 7 | Maintainer: CYF 8 | Description: shadowsocks-go is the go port of shadowsocks, a light weight tunneling proxy 9 | This package contains only the server. 10 | -------------------------------------------------------------------------------- /deb/DEBIAN/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | if [ -x "/etc/init.d/shadowsocks" ]; then 4 | update-rc.d shadowsocks defaults >/dev/null 5 | fi 6 | -------------------------------------------------------------------------------- /deb/DEBIAN/postrm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | # Automatically added by dh_installinit 4 | if [ "$1" = "purge" ] ; then 5 | update-rc.d shadowsocks remove >/dev/null 6 | fi 7 | # End automatically added section 8 | -------------------------------------------------------------------------------- /deb/DEBIAN/prerm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | # Automatically added by dh_installinit 4 | if [ -x "/etc/init.d/shadowsocks" ]; then 5 | invoke-rc.d shadowsocks stop || exit $? 6 | fi 7 | # End automatically added section 8 | -------------------------------------------------------------------------------- /deb/etc/init.d/shadowsocks: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Start/stop shadowsocks. 3 | # 4 | ### BEGIN INIT INFO 5 | # Provides: shadowsocks 6 | # Required-Start: 7 | # Required-Stop: 8 | # Should-Start: 9 | # Should-Stop: 10 | # Default-Start: 2 3 4 5 11 | # Default-Stop: 0 1 6 12 | # Short-Description: shadowsocks is a lightweight tunneling proxy 13 | # Description: Modified from Linode's nginx fastcgi startup script 14 | ### END INIT INFO 15 | 16 | # Note: this script requires sudo in order to run shadowsocks as the specified 17 | # user. 18 | 19 | BIN=/usr/bin/shadowsocks-server 20 | CONFIG_FILE=/etc/shadowsocks/config.json 21 | LOG_FILE=/var/log/shadowsocks 22 | USER=nobody 23 | GROUP=nobody 24 | PID_DIR=/var/run 25 | PID_FILE=$PID_DIR/shadowsocks.pid 26 | RET_VAL=0 27 | 28 | [ -x $BIN ] || exit 0 29 | 30 | check_running() { 31 | if [[ -r $PID_FILE ]]; then 32 | read PID <$PID_FILE 33 | if [[ -d "/proc/$PID" ]]; then 34 | return 0 35 | else 36 | rm -f $PID_FILE 37 | return 1 38 | fi 39 | else 40 | return 2 41 | fi 42 | } 43 | 44 | do_status() { 45 | check_running 46 | case $? in 47 | 0) 48 | echo "shadowsocks running with PID $PID" 49 | ;; 50 | 1) 51 | echo "shadowsocks not running, remove PID file $PID_FILE" 52 | ;; 53 | 2) 54 | echo "Could not find PID file $PID_FILE, shadowsocks does not appear to be running" 55 | ;; 56 | esac 57 | return 0 58 | } 59 | 60 | do_start() { 61 | if [[ ! -d $PID_DIR ]]; then 62 | echo "creating PID dir" 63 | mkdir $PID_DIR || echo "failed creating PID directory $PID_DIR"; exit 1 64 | chown $USER:$GROUP $PID_DIR || echo "failed creating PID directory $PID_DIR"; exit 1 65 | chmod 0770 $PID_DIR 66 | fi 67 | if check_running; then 68 | echo "shadowsocks already running with PID $PID" 69 | return 0 70 | fi 71 | if [[ ! -r $CONFIG_FILE ]]; then 72 | echo "config file $CONFIG_FILE not found" 73 | return 1 74 | fi 75 | echo "starting shadowsocks" 76 | # sudo will set the group to the primary group of $USER 77 | sudo -u $USER $BIN -c $CONFIG_FILE >>$LOG_FILE & 78 | PID=$! 79 | echo $PID > $PID_FILE 80 | sleep 0.3 81 | if ! check_running; then 82 | echo "start failed" 83 | return 1 84 | fi 85 | echo "shadowsocks running with PID $PID" 86 | return 0 87 | } 88 | 89 | do_stop() { 90 | if check_running; then 91 | echo "stopping shadowsocks with PID $PID" 92 | kill $PID 93 | rm -f $PID_FILE 94 | else 95 | echo "Could not find PID file $PID_FILE" 96 | fi 97 | } 98 | 99 | do_restart() { 100 | do_stop 101 | do_start 102 | } 103 | 104 | case "$1" in 105 | start|stop|restart|status) 106 | do_$1 107 | ;; 108 | *) 109 | echo "Usage: shadowsocks {start|stop|restart|status}" 110 | RET_VAL=1 111 | ;; 112 | esac 113 | 114 | exit $RET_VAL 115 | -------------------------------------------------------------------------------- /deb/etc/shadowsocks/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "server":"127.0.0.1", 3 | "server_port":8388, 4 | "local_port":1080, 5 | "password":"barfoo!", 6 | "timeout":0 7 | } 8 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dearplain/fast-shadowsocks 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/codahale/chacha20 v0.0.0-20151107025005-ec07b4f69a3f 7 | github.com/dearplain/penet v0.1.2 8 | github.com/isayme/go-shadowsocks v0.2.1 9 | github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601 10 | golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 11 | ) 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= 2 | github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= 3 | github.com/codahale/chacha20 v0.0.0-20151107025005-ec07b4f69a3f h1:GnkFLgLBj4MDb+UdR1yFlznatawt2U7tEGF1HCwcMSs= 4 | github.com/codahale/chacha20 v0.0.0-20151107025005-ec07b4f69a3f/go.mod h1:2EU+1emidIWL7uTbVXfPFlgYxYo3TGHz+ElH1Tp5GT0= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/dearplain/penet v0.1.2 h1:j2VaV2YeEzgMIpk5yG3319k16OBr3ryWDGUqzQv6PC8= 8 | github.com/dearplain/penet v0.1.2/go.mod h1:fzV78whJUqjPe4mRPhm7gAIyDIw3If+h5Bcd96Uq1Dk= 9 | github.com/isayme/go-config v0.0.0-20190209132515-066212c0ce1b/go.mod h1:gCBIBrmj+N81NzvevhA/FGMY9jUR+WpvwuS3mP5fmC4= 10 | github.com/isayme/go-logger v0.1.0/go.mod h1:53zvc0cz+ZjVqZgq1Sb4HEEzl/BPjoLA9xKv5L/XvE4= 11 | github.com/isayme/go-shadowsocks v0.2.1 h1:b/h+iWExyBLWe4WUOGfdbmregV+0OpVqYt7sfFQTYOs= 12 | github.com/isayme/go-shadowsocks v0.2.1/go.mod h1:3F2whVaNfYWgXBUSdDy726isLVwtqEjX/rFj667etxA= 13 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 14 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 15 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 16 | github.com/panjf2000/ants v0.0.0-20181002135227-29730bb70343/go.mod h1:AaACblRPzq35m1g3enqYcxspbbiOJJYaxU2wMpm1cXY= 17 | github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= 18 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 19 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 20 | github.com/rs/zerolog v1.12.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= 21 | github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601 h1:XU9hik0exChEmY92ALW4l9WnDodxLVS9yOSNh2SizaQ= 22 | github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601/go.mod h1:mttDPaeLm87u74HMrP+n2tugXvIKWcwff/cqSX0lehY= 23 | github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 h1:xT+JlYxNGqyT+XcU8iUrN18JYed2TvG9yN5ULG2jATM= 24 | github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= 25 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 26 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 27 | golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 28 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 29 | golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= 30 | golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 31 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 32 | golang.org/x/sys v0.0.0-20190213121743-983097b1a8a3/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 33 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 34 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= 35 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 36 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 37 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 38 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 39 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 40 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 41 | -------------------------------------------------------------------------------- /goreleaser.yml: -------------------------------------------------------------------------------- 1 | 2 | builds: 3 | - id: shadowsocks-local 4 | main: ./cmd/shadowsocks-local/local.go 5 | binary: fast-shadowsocks-local 6 | goos: 7 | - darwin 8 | - linux 9 | - windows 10 | - id: shadowsocks-server 11 | main: ./cmd/shadowsocks-server/server.go 12 | binary: fast-shadowsocks-server 13 | goos: 14 | - darwin 15 | - linux 16 | - windows -------------------------------------------------------------------------------- /sample-config/client-multi-server.json: -------------------------------------------------------------------------------- 1 | { 2 | "local_port": 1081, 3 | "server_password": [ 4 | ["127.0.0.1:8387", "foobar"], 5 | ["127.0.0.1:8388", "barfoo", "aes-128-cfb"] 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /sample-config/server-multi-port.json: -------------------------------------------------------------------------------- 1 | { 2 | "port_password": { 3 | "8387": "foobar", 4 | "8388": "barfoo" 5 | }, 6 | "method": "aes-128-cfb", 7 | "timeout": 600 8 | } 9 | -------------------------------------------------------------------------------- /script/README.md: -------------------------------------------------------------------------------- 1 | # About shadowsocks.exe 2 | 3 | Copied `goagent.exe`, modified the string table and icon using reshack. 4 | 5 | Thanks for the taskbar project created by @phuslu. 6 | 7 | -------------------------------------------------------------------------------- /script/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$( dirname "${BASH_SOURCE[0]}" )/.." 4 | 5 | version=`grep 'const version = ' ./shadowsocks/util.go | sed -e 's/.*= //' | sed -e 's/"//g'` 6 | echo "creating shadowsocks binary version $version" 7 | 8 | ROOT=`pwd` 9 | bindir=$ROOT/bin 10 | mkdir -p $bindir 11 | 12 | build() { 13 | local name 14 | local GOOS 15 | local GOARCH 16 | 17 | if [[ $1 == "darwin" ]]; then 18 | # Enable CGO for OS X so change network location will not cause problem. 19 | export CGO_ENABLED=1 20 | else 21 | export CGO_ENABLED=0 22 | fi 23 | 24 | prog=shadowsocks-$4 25 | pushd cmd/$prog 26 | name=$prog-$3-$version 27 | echo "building $name" 28 | GOOS=$1 GOARCH=$2 go build -a || exit 1 29 | if [[ $1 == "windows" ]]; then 30 | mv $prog.exe $ROOT/script/ 31 | pushd $ROOT/script/ 32 | cp $ROOT/config.json sample-config.json 33 | cp $ROOT/sample-config/client-multi-server.json multi-server.json 34 | zip $name.zip $prog.exe shadowsocks.exe sample-config.json multi-server.json 35 | rm -f $prog.exe sample-config.json multi-server.json 36 | mv $name.zip $bindir 37 | popd 38 | else 39 | mv $prog $name 40 | gzip -f $name 41 | mv $name.gz $bindir 42 | fi 43 | popd 44 | } 45 | 46 | build darwin amd64 mac64 local 47 | build linux amd64 linux64 local 48 | build linux 386 linux32 local 49 | build windows amd64 win64 local 50 | build windows 386 win32 local 51 | 52 | build linux amd64 linux64 server 53 | build linux 386 linux32 server 54 | build darwin amd64 mac64 server 55 | build windows amd64 win64 server 56 | build windows 386 win32 server 57 | 58 | #script/createdeb.sh amd64 59 | #script/createdeb.sh i386 60 | #mv shadowsocks-go_$version-1-*.deb bin/ 61 | #rm -rf shadowsocks-go_$version-1* 62 | -------------------------------------------------------------------------------- /script/createdeb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$( dirname "${BASH_SOURCE[0]}" )/.." 4 | ver=$(awk '/\tconst version =/ { print $4 }' shadowsocks/util.go | sed -e 's/"//g') 5 | 6 | if [[ $# != 1 ]]; then 7 | echo "$0 " 8 | exit 1 9 | fi 10 | 11 | export CGO_ENABLED=0 12 | export GOOS=linux 13 | 14 | arch=$1 15 | case $arch in 16 | i386) 17 | export GOARCH=386 18 | ;; 19 | amd64) 20 | export GOARCH=amd64 21 | ;; 22 | *) 23 | echo "arch $i not supported" 24 | exit 1 25 | ;; 26 | esac 27 | 28 | # build shadowsocks server 29 | pushd cmd/shadowsocks-server 30 | go build -a -v || exit 1 31 | popd 32 | 33 | # create debian package 34 | DEBDIR=shadowsocks-go_$ver-1-$arch 35 | rm -rf $DEBDIR 36 | cp -r deb $DEBDIR 37 | 38 | sed -i -e "s/VER/$ver/" $DEBDIR/DEBIAN/control || exit 1 39 | sed -i -e "s/^Architecture.*$/Architecture: $arch/" $DEBDIR/DEBIAN/control || exit 1 40 | 41 | mkdir -p $DEBDIR/usr/bin 42 | cp cmd/shadowsocks-server/shadowsocks-server $DEBDIR/usr/bin/ 43 | rm -f cmd/shadowsocks-server/shadowsocks-server 44 | 45 | fakeroot dpkg-deb --build $DEBDIR 46 | 47 | -------------------------------------------------------------------------------- /script/curl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# != 3 ]; then 4 | echo "$0 " 5 | exit 1 6 | fi 7 | 8 | socks=$1 9 | n=$2 10 | url=$3 11 | 12 | for i in `seq 1 $n`; do 13 | curl -s --socks5 $socks $url >/dev/null 14 | if [ $(($i % 1000)) -eq 0 ]; then 15 | echo "finished $i request" 16 | fi 17 | done 18 | 19 | -------------------------------------------------------------------------------- /script/http.go: -------------------------------------------------------------------------------- 1 | /* Simple http server for testing. */ 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "net/http" 7 | "os" 8 | ) 9 | 10 | func handler(w http.ResponseWriter, r *http.Request) { 11 | fmt.Fprintf(w, "Hello, shadowsocks-go!") 12 | } 13 | 14 | func main() { 15 | if len(os.Args) != 2 { 16 | fmt.Println("Usage: http ") 17 | os.Exit(1) 18 | } 19 | http.HandleFunc("/", handler) 20 | http.ListenAndServe("127.0.0.1:"+os.Args[1], nil) 21 | } 22 | -------------------------------------------------------------------------------- /script/set-version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$( dirname "${BASH_SOURCE[0]}" )/.." 4 | 5 | if [ $# != 1 ]; then 6 | echo "Usage: $0 " 7 | exit 1 8 | fi 9 | 10 | version=$1 11 | #echo $version 12 | 13 | sed -i -e "s,\(\tconst version \+= \)\".*\"$,\1\"$version\"," shadowsocks/util.go 14 | sed -i -e "s,^\(Current version: \)[^ ]\+,\1$version," README.md 15 | 16 | -------------------------------------------------------------------------------- /script/shadowsocks.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dearplain/fast-shadowsocks/059381059954ee63cfacbac672fb14e9690bf42c/script/shadowsocks.exe -------------------------------------------------------------------------------- /script/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Use [ -n "$TRAVIS" ] to test for running on Travis-CI. 4 | 5 | # Run in the scripts directory. 6 | cd "$( dirname "${BASH_SOURCE[0]}" )" 7 | 8 | LOCAL_PORT="1090" 9 | SERVER_PORT="8389" 10 | OPTION="-p $SERVER_PORT -k foobar" 11 | SOCKS="127.0.0.1:$LOCAL_PORT" 12 | HTTP_PORT="8123" 13 | 14 | wait_server() { 15 | local port 16 | port=$1 17 | for i in {1..20}; do 18 | # sleep first because this maybe called immediately after server start 19 | sleep 0.1 20 | nc -z -w 4 127.0.0.1 $port && break 21 | done 22 | } 23 | 24 | start_http_server() { 25 | go build http.go 26 | ./http $HTTP_PORT & 27 | wait_server $HTTP_PORT 28 | http_pid=$! 29 | } 30 | 31 | stop_http_server() { 32 | kill -SIGTERM $http_pid 33 | } 34 | 35 | test_get() { 36 | local url 37 | local target 38 | local code 39 | url=$1 40 | target=$2 41 | code=$3 42 | 43 | if [[ -z $code ]]; then 44 | code="200" 45 | fi 46 | 47 | # -s silent to disable progress meter, but enable --show-error 48 | # -i to include http header 49 | # -L to follow redirect so we should always get HTTP 200 50 | cont=`curl --socks5 $SOCKS -s --show-error -i -L $url 2>&1` 51 | ok=`echo $cont | grep -E -o "HTTP/1\.1 +$code"` 52 | html=`echo $cont | grep -E -o -i "$target"` 53 | if [[ -z $ok || -z $html ]] ; then 54 | echo "==============================" 55 | echo "GET $url FAILED!!!" 56 | echo "$ok" 57 | echo "$html" 58 | echo $cont 59 | echo "==============================" 60 | return 1 61 | fi 62 | return 0 63 | } 64 | 65 | test_shadowsocks() { 66 | local url 67 | local method 68 | local server_pid 69 | local local_pid 70 | url=$1 71 | method=$2 72 | 73 | $SERVER $OPTION -m "$method" & 74 | server_pid=$! 75 | wait_server $SERVER_PORT 76 | 77 | $LOCAL $OPTION -s 127.0.0.1 -l $LOCAL_PORT -m "$method" & 78 | local_pid=$! 79 | wait_server $LOCAL_PORT 80 | 81 | for i in {1..3}; do 82 | if ! test_get $url "shadowsocks-go"; then 83 | kill -SIGTERM $server_pid 84 | kill -SIGTERM $local_pid 85 | stop_http_server 86 | exit 1 87 | fi 88 | done 89 | echo "==============================" 90 | echo "GET $url $method passed" 91 | echo "==============================" 92 | kill -SIGTERM $server_pid 93 | kill -SIGTERM $local_pid 94 | sleep 0.1 95 | } 96 | 97 | test_server_local_pair() { 98 | echo "============================================================" 99 | echo "server: $SERVER, local: $LOCAL" 100 | echo "============================================================" 101 | 102 | local url 103 | url=http://127.0.0.1:$HTTP_PORT/README.md 104 | test_shadowsocks $url table 105 | test_shadowsocks $url rc4 106 | test_shadowsocks $url rc4-md5 107 | test_shadowsocks $url aes-128-cfb 108 | test_shadowsocks $url aes-192-cfb 109 | test_shadowsocks $url aes-256-cfb 110 | test_shadowsocks $url bf-cfb 111 | test_shadowsocks $url des-cfb 112 | test_shadowsocks $url cast5-cfb 113 | test_shadowsocks $url chacha20 114 | test_shadowsocks $url salsa20 115 | } 116 | 117 | start_http_server 118 | 119 | SERVER="shadowsocks-server" 120 | LOCAL="shadowsocks-local" 121 | test_server_local_pair 122 | 123 | if [[ -n $SS_PYTHON ]]; then 124 | SERVER="$SS_PYTHON/server.py --forbidden-ip=" 125 | LOCAL="shadowsocks-local" 126 | test_server_local_pair 127 | 128 | SERVER="shadowsocks-server" 129 | LOCAL="$SS_PYTHON/local.py" 130 | test_server_local_pair 131 | fi 132 | 133 | stop_http_server 134 | -------------------------------------------------------------------------------- /script/win32build.bat: -------------------------------------------------------------------------------- 1 | pushd cmd\shadowsocks-local 2 | go install 3 | popd 4 | pushd cmd\shadowsocks-server 5 | go install 6 | popd 7 | -------------------------------------------------------------------------------- /shadowsocks/config.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Created with IntelliJ IDEA. 3 | * User: clowwindy 4 | * Date: 12-11-2 5 | * Time: 上午10:31 6 | * To change this template use File | Settings | File Templates. 7 | */ 8 | package shadowsocks 9 | 10 | import ( 11 | "encoding/json" 12 | "fmt" 13 | "io/ioutil" 14 | // "log" 15 | "os" 16 | "reflect" 17 | "time" 18 | ) 19 | 20 | type Config struct { 21 | Server interface{} `json:"server"` 22 | ServerPort int `json:"server_port"` 23 | LocalPort int `json:"local_port"` 24 | Password string `json:"password"` 25 | Method string `json:"method"` // encryption method 26 | 27 | // following options are only used by server 28 | PortPassword map[string]string `json:"port_password"` 29 | Timeout int `json:"timeout"` 30 | 31 | // following options are only used by client 32 | 33 | // The order of servers in the client config is significant, so use array 34 | // instead of map to preserve the order. 35 | ServerPassword [][]string `json:"server_password"` 36 | } 37 | 38 | var readTimeout time.Duration 39 | 40 | func (config *Config) GetServerArray() []string { 41 | // Specifying multiple servers in the "server" options is deprecated. 42 | // But for backward compatiblity, keep this. 43 | if config.Server == nil { 44 | return nil 45 | } 46 | single, ok := config.Server.(string) 47 | if ok { 48 | return []string{single} 49 | } 50 | arr, ok := config.Server.([]interface{}) 51 | if ok { 52 | /* 53 | if len(arr) > 1 { 54 | log.Println("Multiple servers in \"server\" option is deprecated. " + 55 | "Please use \"server_password\" instead.") 56 | } 57 | */ 58 | serverArr := make([]string, len(arr), len(arr)) 59 | for i, s := range arr { 60 | serverArr[i], ok = s.(string) 61 | if !ok { 62 | goto typeError 63 | } 64 | } 65 | return serverArr 66 | } 67 | typeError: 68 | panic(fmt.Sprintf("Config.Server type error %v", reflect.TypeOf(config.Server))) 69 | } 70 | 71 | func ParseConfig(path string) (config *Config, err error) { 72 | file, err := os.Open(path) // For read access. 73 | if err != nil { 74 | return 75 | } 76 | defer file.Close() 77 | 78 | data, err := ioutil.ReadAll(file) 79 | if err != nil { 80 | return 81 | } 82 | 83 | config = &Config{} 84 | if err = json.Unmarshal(data, config); err != nil { 85 | return nil, err 86 | } 87 | readTimeout = time.Duration(config.Timeout) * time.Second 88 | return 89 | } 90 | 91 | func SetDebug(d DebugLog) { 92 | Debug = d 93 | } 94 | 95 | // Useful for command line to override options specified in config file 96 | // Debug is not updated. 97 | func UpdateConfig(old, new *Config) { 98 | // Using reflection here is not necessary, but it's a good exercise. 99 | // For more information on reflections in Go, read "The Laws of Reflection" 100 | // http://golang.org/doc/articles/laws_of_reflection.html 101 | newVal := reflect.ValueOf(new).Elem() 102 | oldVal := reflect.ValueOf(old).Elem() 103 | 104 | // typeOfT := newVal.Type() 105 | for i := 0; i < newVal.NumField(); i++ { 106 | newField := newVal.Field(i) 107 | oldField := oldVal.Field(i) 108 | // log.Printf("%d: %s %s = %v\n", i, 109 | // typeOfT.Field(i).Name, newField.Type(), newField.Interface()) 110 | switch newField.Kind() { 111 | case reflect.Interface: 112 | if fmt.Sprintf("%v", newField.Interface()) != "" { 113 | oldField.Set(newField) 114 | } 115 | case reflect.String: 116 | s := newField.String() 117 | if s != "" { 118 | oldField.SetString(s) 119 | } 120 | case reflect.Int: 121 | i := newField.Int() 122 | if i != 0 { 123 | oldField.SetInt(i) 124 | } 125 | } 126 | } 127 | if old.Method == "table" { 128 | old.Method = "" 129 | } 130 | 131 | old.Timeout = new.Timeout 132 | readTimeout = time.Duration(old.Timeout) * time.Second 133 | } 134 | -------------------------------------------------------------------------------- /shadowsocks/config_test.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestConfigJson(t *testing.T) { 8 | config, err := ParseConfig("../config.json") 9 | if err != nil { 10 | t.Fatal("error parsing config.json:", err) 11 | } 12 | 13 | if config.Password != "barfoo!" { 14 | t.Error("wrong password from config") 15 | } 16 | if config.Timeout != 600 { 17 | t.Error("timeout should be 600") 18 | } 19 | if config.Method != "aes-128-cfb" { 20 | t.Error("method should be aes-128-cfb") 21 | } 22 | srvArr := config.GetServerArray() 23 | if len(srvArr) != 1 || srvArr[0] != "127.0.0.1" { 24 | t.Error("server option is not set correctly") 25 | } 26 | } 27 | 28 | func TestServerMultiPort(t *testing.T) { 29 | config, err := ParseConfig("../sample-config/server-multi-port.json") 30 | if err != nil { 31 | t.Fatal("error parsing ../sample-config/server-multi-port.json:", err) 32 | } 33 | 34 | if config.PortPassword["8387"] != "foobar" { 35 | t.Error("wrong multiple password for port 8387") 36 | } 37 | if config.PortPassword["8388"] != "barfoo" { 38 | t.Error("wrong multiple password for port 8388") 39 | } 40 | if config.PortPassword["8389"] != "" { 41 | t.Error("should have no password for port 8389") 42 | } 43 | } 44 | 45 | func TestDeprecatedClientMultiServerArray(t *testing.T) { 46 | // This form of config is deprecated. Provided only for backward compatibility. 47 | config, err := ParseConfig("testdata/deprecated-client-multi-server.json") 48 | if err != nil { 49 | t.Fatal("error parsing deprecated-client-multi-server.json:", err) 50 | } 51 | 52 | srvArr := config.GetServerArray() 53 | if len(srvArr) != 2 { 54 | t.Error("server option is not set correctly") 55 | } 56 | if srvArr[0] != "127.0.0.1" { 57 | t.Errorf("1st server wrong, got %v", srvArr[0]) 58 | } 59 | if srvArr[1] != "127.0.1.1" { 60 | t.Errorf("2nd server wrong, got %v", srvArr[0]) 61 | } 62 | } 63 | 64 | func TestClientMultiServerArray(t *testing.T) { 65 | config, err := ParseConfig("../sample-config/client-multi-server.json") 66 | if err != nil { 67 | t.Fatal("error parsing client-multi-server.json:", err) 68 | } 69 | 70 | sv := config.ServerPassword[0] 71 | if len(sv) != 2 { 72 | t.Fatalf("server_password 1st server wrong, have %d items\n", len(sv[0])) 73 | } 74 | if sv[0] != "127.0.0.1:8387" { 75 | t.Error("server_password 1st server wrong") 76 | } 77 | if sv[1] != "foobar" { 78 | t.Error("server_password 1st server passwd wrong") 79 | } 80 | 81 | sv = config.ServerPassword[1] 82 | if len(sv) != 3 { 83 | t.Fatalf("server_password 2nd server wrong, have %d items\n", len(sv[0])) 84 | } 85 | if sv[0] != "127.0.0.1:8388" { 86 | t.Error("server_password 2nd server wrong") 87 | } 88 | if sv[1] != "barfoo" { 89 | t.Error("server_password 2nd server passwd wrong") 90 | } 91 | if sv[2] != "aes-128-cfb" { 92 | t.Error("server_password 2nd server enc method wrong") 93 | } 94 | } 95 | 96 | func TestParseConfigEmpty(t *testing.T) { 97 | // make sure we will not crash 98 | config, err := ParseConfig("testdata/noserver.json") 99 | if err != nil { 100 | t.Fatal("error parsing noserver config:", err) 101 | } 102 | 103 | srvArr := config.GetServerArray() 104 | if srvArr != nil { 105 | t.Error("GetServerArray should return nil if no server option is given") 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /shadowsocks/conn.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "io" 7 | "net" 8 | "strconv" 9 | ) 10 | 11 | type Conn struct { 12 | net.Conn 13 | *Cipher 14 | readBuf []byte 15 | writeBuf []byte 16 | } 17 | 18 | func NewConn(c net.Conn, cipher *Cipher) *Conn { 19 | cc := &Conn{ 20 | Conn: c, 21 | Cipher: cipher, 22 | readBuf: leakyBuf.Get(), 23 | writeBuf: leakyBuf.Get(), 24 | } 25 | cc.Cipher.Init(c) 26 | return cc 27 | } 28 | 29 | func (c *Conn) Close() error { 30 | leakyBuf.Put(c.readBuf) 31 | leakyBuf.Put(c.writeBuf) 32 | return c.Conn.Close() 33 | } 34 | 35 | func RawAddr(addr string) (buf []byte, err error) { 36 | host, portStr, err := net.SplitHostPort(addr) 37 | if err != nil { 38 | return nil, fmt.Errorf("shadowsocks: address error %s %v", addr, err) 39 | } 40 | port, err := strconv.Atoi(portStr) 41 | if err != nil { 42 | return nil, fmt.Errorf("shadowsocks: invalid port %s", addr) 43 | } 44 | 45 | hostLen := len(host) 46 | l := 1 + 1 + hostLen + 2 // addrType + lenByte + address + port 47 | buf = make([]byte, l) 48 | buf[0] = 3 // 3 means the address is domain name 49 | buf[1] = byte(hostLen) // host address length followed by host address 50 | copy(buf[2:], host) 51 | binary.BigEndian.PutUint16(buf[2+hostLen:2+hostLen+2], uint16(port)) 52 | return 53 | } 54 | 55 | // This is intended for use by users implementing a local socks proxy. 56 | // rawaddr shoud contain part of the data in socks request, starting from the 57 | // ATYP field. (Refer to rfc1928 for more information.) 58 | func DialWithRawAddr(rawaddr []byte, server string, cipher *Cipher) (c *Conn, err error) { 59 | conn, err := net.Dial("tcp", server) 60 | if err != nil { 61 | return 62 | } 63 | c = NewConn(conn, cipher) 64 | if _, err = c.Write(rawaddr); err != nil { 65 | c.Close() 66 | return nil, err 67 | } 68 | return 69 | } 70 | 71 | // addr should be in the form of host:port 72 | func Dial(addr, server string, cipher *Cipher) (c *Conn, err error) { 73 | ra, err := RawAddr(addr) 74 | if err != nil { 75 | return 76 | } 77 | return DialWithRawAddr(ra, server, cipher) 78 | } 79 | 80 | func (c *Conn) Read(b []byte) (n int, err error) { 81 | if c.Cipher.AC != nil { 82 | return c.Cipher.AC.Read(b) 83 | } 84 | if c.dec == nil { 85 | iv := make([]byte, c.info.ivLen) 86 | if _, err = io.ReadFull(c.Conn, iv); err != nil { 87 | return 88 | } 89 | if err = c.initDecrypt(iv); err != nil { 90 | return 91 | } 92 | } 93 | 94 | cipherData := c.readBuf 95 | if len(b) > len(cipherData) { 96 | cipherData = make([]byte, len(b)) 97 | } else { 98 | cipherData = cipherData[:len(b)] 99 | } 100 | 101 | n, err = c.Conn.Read(cipherData) 102 | if n > 0 { 103 | c.decrypt(b[0:n], cipherData[0:n]) 104 | } 105 | return 106 | } 107 | 108 | func (c *Conn) Write(b []byte) (n int, err error) { 109 | if c.Cipher.AC != nil { 110 | return c.Cipher.AC.Write(b) 111 | } 112 | 113 | var iv []byte 114 | if c.enc == nil { 115 | iv, err = c.initEncrypt() 116 | if err != nil { 117 | return 118 | } 119 | } 120 | 121 | cipherData := c.writeBuf 122 | dataSize := len(b) + len(iv) 123 | if dataSize > len(cipherData) { 124 | cipherData = make([]byte, dataSize) 125 | } else { 126 | cipherData = cipherData[:dataSize] 127 | } 128 | 129 | if iv != nil { 130 | // Put initialization vector in buffer, do a single write to send both 131 | // iv and data. 132 | copy(cipherData, iv) 133 | } 134 | 135 | c.encrypt(cipherData[len(iv):], b) 136 | n, err = c.Conn.Write(cipherData) 137 | return 138 | } 139 | -------------------------------------------------------------------------------- /shadowsocks/encrypt.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | "bytes" 5 | "crypto/aes" 6 | "crypto/cipher" 7 | "crypto/des" 8 | "crypto/md5" 9 | "crypto/rand" 10 | "crypto/rc4" 11 | "encoding/binary" 12 | "errors" 13 | "fmt" 14 | "io" 15 | "net" 16 | 17 | "github.com/codahale/chacha20" 18 | "github.com/isayme/go-shadowsocks/shadowsocks/aead" 19 | 20 | "golang.org/x/crypto/blowfish" 21 | "golang.org/x/crypto/cast5" 22 | "golang.org/x/crypto/salsa20/salsa" 23 | ) 24 | 25 | var errEmptyPassword = errors.New("empty key") 26 | 27 | type tableCipher []byte 28 | 29 | func md5sum(d []byte) []byte { 30 | h := md5.New() 31 | h.Write(d) 32 | return h.Sum(nil) 33 | } 34 | 35 | func evpBytesToKey(password string, keyLen int) (key []byte) { 36 | const md5Len = 16 37 | 38 | cnt := (keyLen-1)/md5Len + 1 39 | m := make([]byte, cnt*md5Len) 40 | copy(m, md5sum([]byte(password))) 41 | 42 | // Repeatedly call md5 until bytes generated is enough. 43 | // Each call to md5 uses data: prev md5 sum + password. 44 | d := make([]byte, md5Len+len(password)) 45 | start := 0 46 | for i := 1; i < cnt; i++ { 47 | start += md5Len 48 | copy(d, m[start-md5Len:start]) 49 | copy(d[md5Len:], password) 50 | copy(m[start:], md5sum(d)) 51 | } 52 | return m[:keyLen] 53 | } 54 | 55 | func (tbl tableCipher) XORKeyStream(dst, src []byte) { 56 | for i := 0; i < len(src); i++ { 57 | dst[i] = tbl[src[i]] 58 | } 59 | } 60 | 61 | // NewTableCipher creates a new table based cipher. 62 | func newTableCipher(s []byte) (enc, dec tableCipher) { 63 | const tbl_size = 256 64 | enc = make([]byte, tbl_size) 65 | dec = make([]byte, tbl_size) 66 | table := make([]uint64, tbl_size) 67 | 68 | var a uint64 69 | buf := bytes.NewBuffer(s) 70 | binary.Read(buf, binary.LittleEndian, &a) 71 | var i uint64 72 | for i = 0; i < tbl_size; i++ { 73 | table[i] = i 74 | } 75 | for i = 1; i < 1024; i++ { 76 | table = Sort(table, func(x, y uint64) int64 { 77 | return int64(a%uint64(x+i) - a%uint64(y+i)) 78 | }) 79 | } 80 | for i = 0; i < tbl_size; i++ { 81 | enc[i] = byte(table[i]) 82 | } 83 | for i = 0; i < tbl_size; i++ { 84 | dec[enc[i]] = byte(i) 85 | } 86 | return enc, dec 87 | } 88 | 89 | func newRC4Cipher(key []byte) (enc, dec cipher.Stream, err error) { 90 | rc4Enc, err := rc4.NewCipher(key) 91 | if err != nil { 92 | return 93 | } 94 | // create a copy, as RC4 encrypt and decrypt uses the same keystream 95 | rc4Dec := *rc4Enc 96 | return rc4Enc, &rc4Dec, nil 97 | } 98 | 99 | type DecOrEnc int 100 | 101 | const ( 102 | Decrypt DecOrEnc = iota 103 | Encrypt 104 | ) 105 | 106 | func newStream(block cipher.Block, err error, key, iv []byte, 107 | doe DecOrEnc) (cipher.Stream, error) { 108 | if err != nil { 109 | return nil, err 110 | } 111 | if doe == Encrypt { 112 | return cipher.NewCFBEncrypter(block, iv), nil 113 | } else { 114 | return cipher.NewCFBDecrypter(block, iv), nil 115 | } 116 | } 117 | 118 | func newAESStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { 119 | block, err := aes.NewCipher(key) 120 | return newStream(block, err, key, iv, doe) 121 | } 122 | 123 | func newDESStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { 124 | block, err := des.NewCipher(key) 125 | return newStream(block, err, key, iv, doe) 126 | } 127 | 128 | func newBlowFishStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { 129 | block, err := blowfish.NewCipher(key) 130 | return newStream(block, err, key, iv, doe) 131 | } 132 | 133 | func newCast5Stream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { 134 | block, err := cast5.NewCipher(key) 135 | return newStream(block, err, key, iv, doe) 136 | } 137 | 138 | func newRC4MD5Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { 139 | h := md5.New() 140 | h.Write(key) 141 | h.Write(iv) 142 | rc4key := h.Sum(nil) 143 | 144 | return rc4.NewCipher(rc4key) 145 | } 146 | 147 | func newChaCha20Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { 148 | return chacha20.New(key, iv) 149 | } 150 | 151 | type salsaStreamCipher struct { 152 | nonce [8]byte 153 | key [32]byte 154 | counter int 155 | } 156 | 157 | func (c *salsaStreamCipher) XORKeyStream(dst, src []byte) { 158 | var buf []byte 159 | padLen := c.counter % 64 160 | dataSize := len(src) + padLen 161 | if cap(dst) >= dataSize { 162 | buf = dst[:dataSize] 163 | } else if leakyBufSize >= dataSize { 164 | buf = leakyBuf.Get() 165 | defer leakyBuf.Put(buf) 166 | buf = buf[:dataSize] 167 | } else { 168 | buf = make([]byte, dataSize) 169 | } 170 | 171 | var subNonce [16]byte 172 | copy(subNonce[:], c.nonce[:]) 173 | binary.LittleEndian.PutUint64(subNonce[len(c.nonce):], uint64(c.counter/64)) 174 | 175 | // It's difficult to avoid data copy here. src or dst maybe slice from 176 | // Conn.Read/Write, which can't have padding. 177 | copy(buf[padLen:], src[:]) 178 | salsa.XORKeyStream(buf, buf, &subNonce, &c.key) 179 | copy(dst, buf[padLen:]) 180 | 181 | c.counter += len(src) 182 | } 183 | 184 | func newSalsa20Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { 185 | var c salsaStreamCipher 186 | copy(c.nonce[:], iv[:8]) 187 | copy(c.key[:], key[:32]) 188 | return &c, nil 189 | } 190 | 191 | type cipherInfo struct { 192 | keyLen int 193 | ivLen int 194 | newStream func(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) 195 | } 196 | 197 | var cipherMethod = map[string]*cipherInfo{ 198 | "rc4": {16, 0, nil}, 199 | "table": {16, 0, nil}, 200 | "aes-128-cfb": {16, 16, newAESStream}, 201 | "aes-192-cfb": {24, 16, newAESStream}, 202 | "aes-256-cfb": {32, 16, newAESStream}, 203 | "des-cfb": {8, 8, newDESStream}, 204 | "bf-cfb": {16, 8, newBlowFishStream}, 205 | "cast5-cfb": {16, 8, newCast5Stream}, 206 | "rc4-md5": {16, 16, newRC4MD5Stream}, 207 | "chacha20": {32, 8, newChaCha20Stream}, 208 | "salsa20": {32, 8, newSalsa20Stream}, 209 | } 210 | 211 | func CheckCipherMethod(method string) error { 212 | if method == "" { 213 | method = "table" 214 | } 215 | _, ok := cipherMethod[method] 216 | if !ok { 217 | return errors.New("Unsupported encryption method: " + method) 218 | } 219 | return nil 220 | } 221 | 222 | type Cipher struct { 223 | method string 224 | enc cipher.Stream 225 | dec cipher.Stream 226 | key []byte 227 | info *cipherInfo 228 | AC *aead.Cipher 229 | } 230 | 231 | // NewCipher creates a cipher that can be used in Dial() etc. 232 | // Use cipher.Copy() to create a new cipher with the same method and password 233 | // to avoid the cost of repeated cipher initialization. 234 | func NewCipher(method, password string) (c *Cipher, err error) { 235 | if password == "" { 236 | return nil, errEmptyPassword 237 | } 238 | if method == "" { 239 | method = "table" 240 | } 241 | mi, ok := cipherMethod[method] 242 | if !ok { 243 | defer func() { 244 | if e := recover(); e != nil { 245 | c = nil 246 | err = errors.New(fmt.Sprint(e)) 247 | } 248 | }() 249 | c = &Cipher{} 250 | c.method = method 251 | c.AC = aead.NewCipher(method) 252 | c.key = []byte(password) 253 | return c, nil 254 | } 255 | 256 | key := evpBytesToKey(password, mi.keyLen) 257 | 258 | c = &Cipher{key: key, info: mi} 259 | 260 | if mi.newStream == nil { 261 | if method == "table" { 262 | c.enc, c.dec = newTableCipher(key) 263 | } else if method == "rc4" { 264 | c.enc, c.dec, err = newRC4Cipher(key) 265 | } 266 | } 267 | if err != nil { 268 | return nil, err 269 | } 270 | return c, nil 271 | } 272 | 273 | func (c *Cipher) Init(conn net.Conn) { 274 | if c.AC != nil { 275 | keySize := c.AC.KeySize() 276 | c.AC.Init(evpBytesToKey(string(c.key), keySize), conn) 277 | } 278 | } 279 | 280 | // Initializes the block cipher with CFB mode, returns IV. 281 | func (c *Cipher) initEncrypt() (iv []byte, err error) { 282 | iv = make([]byte, c.info.ivLen) 283 | if _, err := io.ReadFull(rand.Reader, iv); err != nil { 284 | return nil, err 285 | } 286 | c.enc, err = c.info.newStream(c.key, iv, Encrypt) 287 | if err != nil { 288 | return nil, err 289 | } 290 | return 291 | } 292 | 293 | func (c *Cipher) initDecrypt(iv []byte) (err error) { 294 | c.dec, err = c.info.newStream(c.key, iv, Decrypt) 295 | return 296 | } 297 | 298 | func (c *Cipher) encrypt(dst, src []byte) { 299 | c.enc.XORKeyStream(dst, src) 300 | } 301 | 302 | func (c *Cipher) decrypt(dst, src []byte) { 303 | c.dec.XORKeyStream(dst, src) 304 | } 305 | 306 | // Copy creates a new cipher at it's initial state. 307 | func (c *Cipher) Copy() *Cipher { 308 | // This optimization maybe not necessary. But without this function, we 309 | // need to maintain a table cache for newTableCipher and use lock to 310 | // protect concurrent access to that cache. 311 | 312 | // AES and DES ciphers does not return specific types, so it's difficult 313 | // to create copy. But their initizliation time is less than 4000ns on my 314 | // 2.26 GHz Intel Core 2 Duo processor. So no need to worry. 315 | 316 | // Currently, blow-fish and cast5 initialization cost is an order of 317 | // maganitude slower than other ciphers. (I'm not sure whether this is 318 | // because the current implementation is not highly optimized, or this is 319 | // the nature of the algorithm.) 320 | 321 | if c.AC != nil { 322 | cc := &Cipher{} 323 | *cc = *c 324 | cc.AC = aead.NewCipher(c.method) 325 | return cc 326 | } 327 | 328 | switch c.enc.(type) { 329 | case tableCipher: 330 | return c 331 | case *rc4.Cipher: 332 | enc, _ := c.enc.(*rc4.Cipher) 333 | encCpy := *enc 334 | decCpy := *enc 335 | return &Cipher{enc: &encCpy, dec: &decCpy} 336 | default: 337 | nc := *c 338 | nc.enc = nil 339 | nc.dec = nil 340 | return &nc 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /shadowsocks/encrypt_test.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/rc4" 6 | "io" 7 | "reflect" 8 | "testing" 9 | ) 10 | 11 | func TestEncrypTable1(t *testing.T) { 12 | encTarget := []byte{60, 53, 84, 138, 217, 94, 88, 23, 39, 242, 219, 35, 12, 157, 165, 181, 255, 143, 83, 247, 162, 16, 31, 209, 190, 171, 115, 65, 38, 41, 21, 245, 236, 46, 121, 62, 166, 233, 44, 154, 153, 145, 230, 49, 128, 216, 173, 29, 241, 119, 64, 229, 194, 103, 131, 110, 26, 197, 218, 59, 204, 56, 27, 34, 141, 221, 149, 239, 192, 195, 24, 155, 170, 183, 11, 254, 213, 37, 137, 226, 75, 203, 55, 19, 72, 248, 22, 129, 33, 175, 178, 10, 198, 71, 77, 36, 113, 167, 48, 2, 117, 140, 142, 66, 199, 232, 243, 32, 123, 54, 51, 82, 57, 177, 87, 251, 150, 196, 133, 5, 253, 130, 8, 184, 14, 152, 231, 3, 186, 159, 76, 89, 228, 205, 156, 96, 163, 146, 18, 91, 132, 85, 80, 109, 172, 176, 105, 13, 50, 235, 127, 0, 189, 95, 98, 136, 250, 200, 108, 179, 211, 214, 106, 168, 78, 79, 74, 210, 30, 73, 201, 151, 208, 114, 101, 174, 92, 52, 120, 240, 15, 169, 220, 182, 81, 224, 43, 185, 40, 99, 180, 17, 212, 158, 42, 90, 9, 191, 45, 6, 25, 4, 222, 67, 126, 1, 116, 124, 206, 69, 61, 7, 68, 97, 202, 63, 244, 20, 28, 58, 93, 134, 104, 144, 227, 147, 102, 118, 135, 148, 47, 238, 86, 112, 122, 70, 107, 215, 100, 139, 223, 225, 164, 237, 111, 125, 207, 160, 187, 246, 234, 161, 188, 193, 249, 252} 13 | decTarget := []byte{151, 205, 99, 127, 201, 119, 199, 211, 122, 196, 91, 74, 12, 147, 124, 180, 21, 191, 138, 83, 217, 30, 86, 7, 70, 200, 56, 62, 218, 47, 168, 22, 107, 88, 63, 11, 95, 77, 28, 8, 188, 29, 194, 186, 38, 198, 33, 230, 98, 43, 148, 110, 177, 1, 109, 82, 61, 112, 219, 59, 0, 210, 35, 215, 50, 27, 103, 203, 212, 209, 235, 93, 84, 169, 166, 80, 130, 94, 164, 165, 142, 184, 111, 18, 2, 141, 232, 114, 6, 131, 195, 139, 176, 220, 5, 153, 135, 213, 154, 189, 238, 174, 226, 53, 222, 146, 162, 236, 158, 143, 55, 244, 233, 96, 173, 26, 206, 100, 227, 49, 178, 34, 234, 108, 207, 245, 204, 150, 44, 87, 121, 54, 140, 118, 221, 228, 155, 78, 3, 239, 101, 64, 102, 17, 223, 41, 137, 225, 229, 66, 116, 171, 125, 40, 39, 71, 134, 13, 193, 129, 247, 251, 20, 136, 242, 14, 36, 97, 163, 181, 72, 25, 144, 46, 175, 89, 145, 113, 90, 159, 190, 15, 183, 73, 123, 187, 128, 248, 252, 152, 24, 197, 68, 253, 52, 69, 117, 57, 92, 104, 157, 170, 214, 81, 60, 133, 208, 246, 172, 23, 167, 160, 192, 76, 161, 237, 45, 4, 58, 10, 182, 65, 202, 240, 185, 241, 79, 224, 132, 51, 42, 126, 105, 37, 250, 149, 32, 243, 231, 67, 179, 48, 9, 106, 216, 31, 249, 19, 85, 254, 156, 115, 255, 120, 75, 16} 14 | key := evpBytesToKey("foobar!", 16) 15 | enc, dec := newTableCipher(key) 16 | if !reflect.DeepEqual([]byte(enc), encTarget) { 17 | t.Error("Password foobar encrypt table wrong") 18 | } 19 | if !reflect.DeepEqual([]byte(dec), decTarget) { 20 | t.Error("Password foobar decrypt table wrong") 21 | } 22 | } 23 | 24 | func TestEncryptTable2(t *testing.T) { 25 | encTarget := []byte{124, 30, 170, 247, 27, 127, 224, 59, 13, 22, 196, 76, 72, 154, 32, 209, 4, 2, 131, 62, 101, 51, 230, 9, 166, 11, 99, 80, 208, 112, 36, 248, 81, 102, 130, 88, 218, 38, 168, 15, 241, 228, 167, 117, 158, 41, 10, 180, 194, 50, 204, 243, 246, 251, 29, 198, 219, 210, 195, 21, 54, 91, 203, 221, 70, 57, 183, 17, 147, 49, 133, 65, 77, 55, 202, 122, 162, 169, 188, 200, 190, 125, 63, 244, 96, 31, 107, 106, 74, 143, 116, 148, 78, 46, 1, 137, 150, 110, 181, 56, 95, 139, 58, 3, 231, 66, 165, 142, 242, 43, 192, 157, 89, 175, 109, 220, 128, 0, 178, 42, 255, 20, 214, 185, 83, 160, 253, 7, 23, 92, 111, 153, 26, 226, 33, 176, 144, 18, 216, 212, 28, 151, 71, 206, 222, 182, 8, 174, 205, 201, 152, 240, 155, 108, 223, 104, 239, 98, 164, 211, 184, 34, 193, 14, 114, 187, 40, 254, 12, 67, 93, 217, 6, 94, 16, 19, 82, 86, 245, 24, 197, 134, 132, 138, 229, 121, 5, 235, 238, 85, 47, 103, 113, 179, 69, 250, 45, 135, 156, 25, 61, 75, 44, 146, 189, 84, 207, 172, 119, 53, 123, 186, 120, 171, 68, 227, 145, 136, 100, 90, 48, 79, 159, 149, 39, 213, 236, 126, 52, 60, 225, 199, 105, 73, 233, 252, 118, 215, 35, 115, 64, 37, 97, 129, 161, 177, 87, 237, 141, 173, 191, 163, 140, 234, 232, 249} 26 | decTarget := []byte{117, 94, 17, 103, 16, 186, 172, 127, 146, 23, 46, 25, 168, 8, 163, 39, 174, 67, 137, 175, 121, 59, 9, 128, 179, 199, 132, 4, 140, 54, 1, 85, 14, 134, 161, 238, 30, 241, 37, 224, 166, 45, 119, 109, 202, 196, 93, 190, 220, 69, 49, 21, 228, 209, 60, 73, 99, 65, 102, 7, 229, 200, 19, 82, 240, 71, 105, 169, 214, 194, 64, 142, 12, 233, 88, 201, 11, 72, 92, 221, 27, 32, 176, 124, 205, 189, 177, 246, 35, 112, 219, 61, 129, 170, 173, 100, 84, 242, 157, 26, 218, 20, 33, 191, 155, 232, 87, 86, 153, 114, 97, 130, 29, 192, 164, 239, 90, 43, 236, 208, 212, 185, 75, 210, 0, 81, 227, 5, 116, 243, 34, 18, 182, 70, 181, 197, 217, 95, 183, 101, 252, 248, 107, 89, 136, 216, 203, 68, 91, 223, 96, 141, 150, 131, 13, 152, 198, 111, 44, 222, 125, 244, 76, 251, 158, 106, 24, 42, 38, 77, 2, 213, 207, 249, 147, 113, 135, 245, 118, 193, 47, 98, 145, 66, 160, 123, 211, 165, 78, 204, 80, 250, 110, 162, 48, 58, 10, 180, 55, 231, 79, 149, 74, 62, 50, 148, 143, 206, 28, 15, 57, 159, 139, 225, 122, 237, 138, 171, 36, 56, 115, 63, 144, 154, 6, 230, 133, 215, 41, 184, 22, 104, 254, 234, 253, 187, 226, 247, 188, 156, 151, 40, 108, 51, 83, 178, 52, 3, 31, 255, 195, 53, 235, 126, 167, 120} 27 | key := evpBytesToKey("barfoo!", 16) 28 | enc, dec := newTableCipher(key) 29 | if !reflect.DeepEqual([]byte(enc), encTarget) { 30 | t.Error("Password barfoo! encrypt table wrong") 31 | } 32 | if !reflect.DeepEqual([]byte(dec), decTarget) { 33 | t.Error("Password barfoo! decrypt table wrong") 34 | } 35 | } 36 | 37 | const text = "Don't tell me the moon is shining; show me the glint of light on broken glass." 38 | 39 | func testCiphter(t *testing.T, c *Cipher, msg string) { 40 | n := len(text) 41 | cipherBuf := make([]byte, n) 42 | originTxt := make([]byte, n) 43 | 44 | c.encrypt(cipherBuf, []byte(text)) 45 | c.decrypt(originTxt, cipherBuf) 46 | 47 | if string(originTxt) != text { 48 | t.Error(msg, "encrypt then decrytp does not get original text") 49 | } 50 | } 51 | 52 | func TestTableCipher(t *testing.T) { 53 | cipher, err := NewCipher("", "OpenSesame!") 54 | if err != nil { 55 | t.Fatal("Should not get error generating table cipher") 56 | } 57 | if _, ok := cipher.enc.(tableCipher); !ok { 58 | t.Error("Should get table cipher") 59 | } else { 60 | testCiphter(t, cipher, "TableCipher") 61 | } 62 | } 63 | 64 | func TestRC4Cipher(t *testing.T) { 65 | cipher, err := NewCipher("no-such-method", "foobar") 66 | if err == nil { 67 | t.Error("Should return error for unsupported encryption method") 68 | } 69 | 70 | cipher, err = NewCipher("rc4", "") 71 | if err == nil { 72 | t.Error("Should get error for empty key creating rc4 cipher") 73 | } 74 | cipher, err = NewCipher("rc4", "Alibaba") 75 | ciphercopy := cipher.Copy() 76 | if err != nil { 77 | t.Error("Should not error creating rc4 cipher with key Alibaba") 78 | } 79 | if _, ok := cipher.enc.(*rc4.Cipher); !ok { 80 | t.Error("Should get rc4 cipher") 81 | } else { 82 | testCiphter(t, cipher, "RC4Cipher") 83 | testCiphter(t, ciphercopy, "RC4Cipher copy") 84 | } 85 | } 86 | 87 | func TestEvpBytesToKey(t *testing.T) { 88 | // key, iv := evpBytesToKey("foobar", 32, 16) 89 | key := evpBytesToKey("foobar", 32) 90 | keyTarget := []byte{0x38, 0x58, 0xf6, 0x22, 0x30, 0xac, 0x3c, 0x91, 0x5f, 0x30, 0x0c, 0x66, 0x43, 0x12, 0xc6, 0x3f, 0x56, 0x83, 0x78, 0x52, 0x96, 0x14, 0xd2, 0x2d, 0xdb, 0x49, 0x23, 0x7d, 0x2f, 0x60, 0xbf, 0xdf} 91 | // ivTarget := []byte{0x0e, 0xbf, 0x58, 0x78, 0xe8, 0x2a, 0xf7, 0xda, 0x61, 0x8e, 0xd5, 0x6f, 0xc6, 0x7d, 0x4a, 0xb7} 92 | if !reflect.DeepEqual(key, keyTarget) { 93 | t.Errorf("key not correct\n\texpect: %v\n\tgot: %v\n", keyTarget, key) 94 | } 95 | // if !reflect.DeepEqual(iv, ivTarget) { 96 | // t.Errorf("iv not correct\n\texpect: %v\n\tgot: %v\n", ivTarget, iv) 97 | // } 98 | } 99 | 100 | func testBlockCipher(t *testing.T, method string) { 101 | var cipher *Cipher 102 | var err error 103 | 104 | cipher, err = NewCipher(method, "foobar") 105 | if err != nil { 106 | t.Fatal(method, "NewCipher:", err) 107 | } 108 | cipherCopy := cipher.Copy() 109 | iv, err := cipher.initEncrypt() 110 | if err != nil { 111 | t.Error(method, "initEncrypt:", err) 112 | } 113 | if err = cipher.initDecrypt(iv); err != nil { 114 | t.Error(method, "initDecrypt:", err) 115 | } 116 | testCiphter(t, cipher, method) 117 | 118 | iv, err = cipherCopy.initEncrypt() 119 | if err != nil { 120 | t.Error(method, "copy initEncrypt:", err) 121 | } 122 | if err = cipherCopy.initDecrypt(iv); err != nil { 123 | t.Error(method, "copy initDecrypt:", err) 124 | } 125 | testCiphter(t, cipherCopy, method+" copy") 126 | } 127 | 128 | func TestAES128(t *testing.T) { 129 | testBlockCipher(t, "aes-128-cfb") 130 | } 131 | 132 | func TestAES192(t *testing.T) { 133 | testBlockCipher(t, "aes-192-cfb") 134 | } 135 | 136 | func TestAES256(t *testing.T) { 137 | testBlockCipher(t, "aes-256-cfb") 138 | } 139 | 140 | func TestDES(t *testing.T) { 141 | testBlockCipher(t, "des-cfb") 142 | } 143 | 144 | func TestRC4MD5(t *testing.T) { 145 | testBlockCipher(t, "rc4-md5") 146 | } 147 | 148 | func TestChaCha20(t *testing.T) { 149 | testBlockCipher(t, "chacha20") 150 | } 151 | 152 | var cipherKey = make([]byte, 64) 153 | var cipherIv = make([]byte, 64) 154 | 155 | const CIPHER_BENCHMARK_BUFFER_LEN = 4096 156 | 157 | func init() { 158 | for i := 0; i < len(cipherKey); i++ { 159 | cipherKey[i] = byte(i) 160 | } 161 | io.ReadFull(rand.Reader, cipherIv) 162 | } 163 | 164 | func BenchmarkRC4Init(b *testing.B) { 165 | key := cipherKey[:16] 166 | for i := 0; i < b.N; i++ { 167 | rc4.NewCipher(key) 168 | } 169 | } 170 | 171 | func benchmarkCipherInit(b *testing.B, method string) { 172 | ci := cipherMethod[method] 173 | key := cipherKey[:ci.keyLen] 174 | buf := make([]byte, ci.ivLen) 175 | for i := 0; i < b.N; i++ { 176 | ci.newStream(key, buf, Encrypt) 177 | } 178 | } 179 | 180 | func BenchmarkAES128Init(b *testing.B) { 181 | benchmarkCipherInit(b, "aes-128-cfb") 182 | } 183 | 184 | func BenchmarkAES192Init(b *testing.B) { 185 | benchmarkCipherInit(b, "aes-192-cfb") 186 | } 187 | 188 | func BenchmarkAES256Init(b *testing.B) { 189 | benchmarkCipherInit(b, "aes-256-cfb") 190 | } 191 | 192 | func BenchmarkBlowFishInit(b *testing.B) { 193 | benchmarkCipherInit(b, "bf-cfb") 194 | } 195 | 196 | func BenchmarkCast5Init(b *testing.B) { 197 | benchmarkCipherInit(b, "cast5-cfb") 198 | } 199 | 200 | func BenchmarkDESInit(b *testing.B) { 201 | benchmarkCipherInit(b, "des-cfb") 202 | } 203 | 204 | func BenchmarkRC4MD5Init(b *testing.B) { 205 | benchmarkCipherInit(b, "rc4-md5") 206 | } 207 | 208 | func BenchmarkChaCha20Init(b *testing.B) { 209 | benchmarkCipherInit(b, "chacha20") 210 | } 211 | 212 | func BenchmarkSalsa20Init(b *testing.B) { 213 | benchmarkCipherInit(b, "salsa20") 214 | } 215 | 216 | func benchmarkCipherEncrypt(b *testing.B, method string) { 217 | ci := cipherMethod[method] 218 | key := cipherKey[:ci.keyLen] 219 | iv := cipherIv[:ci.ivLen] 220 | enc, err := ci.newStream(key, iv, Encrypt) 221 | if err != nil { 222 | b.Error(err) 223 | } 224 | src := make([]byte, CIPHER_BENCHMARK_BUFFER_LEN) 225 | dst := make([]byte, CIPHER_BENCHMARK_BUFFER_LEN) 226 | io.ReadFull(rand.Reader, src) 227 | for i := 0; i < b.N; i++ { 228 | enc.XORKeyStream(dst, src) 229 | } 230 | } 231 | 232 | func BenchmarkAES128Encrypt(b *testing.B) { 233 | benchmarkCipherEncrypt(b, "aes-128-cfb") 234 | } 235 | 236 | func BenchmarkAES192Encrypt(b *testing.B) { 237 | benchmarkCipherEncrypt(b, "aes-192-cfb") 238 | } 239 | 240 | func BenchmarkAES256Encrypt(b *testing.B) { 241 | benchmarkCipherEncrypt(b, "aes-256-cfb") 242 | } 243 | 244 | func BenchmarkBlowFishEncrypt(b *testing.B) { 245 | benchmarkCipherEncrypt(b, "bf-cfb") 246 | } 247 | 248 | func BenchmarkCast5Encrypt(b *testing.B) { 249 | benchmarkCipherEncrypt(b, "bf-cfb") 250 | } 251 | 252 | func BenchmarkDESEncrypt(b *testing.B) { 253 | benchmarkCipherEncrypt(b, "des-cfb") 254 | } 255 | 256 | func BenchmarkRC4MD5Encrypt(b *testing.B) { 257 | benchmarkCipherEncrypt(b, "rc4-md5") 258 | } 259 | 260 | func BenchmarkChacha20Encrypt(b *testing.B) { 261 | benchmarkCipherEncrypt(b, "chacha20") 262 | } 263 | 264 | func BenchmarkSalsa20Encrypt(b *testing.B) { 265 | benchmarkCipherEncrypt(b, "salsa20") 266 | } 267 | 268 | func benchmarkCipherDecrypt(b *testing.B, method string) { 269 | ci := cipherMethod[method] 270 | key := cipherKey[:ci.keyLen] 271 | iv := cipherIv[:ci.ivLen] 272 | enc, err := ci.newStream(key, iv, Encrypt) 273 | if err != nil { 274 | b.Error(err) 275 | } 276 | dec, err := ci.newStream(key, iv, Decrypt) 277 | if err != nil { 278 | b.Error(err) 279 | } 280 | src := make([]byte, CIPHER_BENCHMARK_BUFFER_LEN) 281 | dst := make([]byte, CIPHER_BENCHMARK_BUFFER_LEN) 282 | io.ReadFull(rand.Reader, src) 283 | enc.XORKeyStream(dst, src) 284 | for i := 0; i < b.N; i++ { 285 | dec.XORKeyStream(src, dst) 286 | } 287 | } 288 | 289 | func BenchmarkAES128Decrypt(b *testing.B) { 290 | benchmarkCipherDecrypt(b, "aes-128-cfb") 291 | } 292 | 293 | func BenchmarkAES192Decrypt(b *testing.B) { 294 | benchmarkCipherDecrypt(b, "aes-192-cfb") 295 | } 296 | 297 | func BenchmarkAES256Decrypt(b *testing.B) { 298 | benchmarkCipherDecrypt(b, "aes-256-cfb") 299 | } 300 | 301 | func BenchmarkBlowFishDecrypt(b *testing.B) { 302 | benchmarkCipherDecrypt(b, "bf-cfb") 303 | } 304 | 305 | func BenchmarkCast5Decrypt(b *testing.B) { 306 | benchmarkCipherDecrypt(b, "bf-cfb") 307 | } 308 | 309 | func BenchmarkDESDecrypt(b *testing.B) { 310 | benchmarkCipherDecrypt(b, "des-cfb") 311 | } 312 | 313 | func BenchmarkRC4MD5Decrypt(b *testing.B) { 314 | benchmarkCipherDecrypt(b, "rc4-md5") 315 | } 316 | 317 | func BenchmarkChaCha20Decrypt(b *testing.B) { 318 | benchmarkCipherDecrypt(b, "chacha20") 319 | } 320 | 321 | func BenchmarkSalsa20Decrypt(b *testing.B) { 322 | benchmarkCipherDecrypt(b, "salsa20") 323 | } 324 | -------------------------------------------------------------------------------- /shadowsocks/leakybuf.go: -------------------------------------------------------------------------------- 1 | // Provides leaky buffer, based on the example in Effective Go. 2 | package shadowsocks 3 | 4 | type LeakyBuf struct { 5 | bufSize int // size of each buffer 6 | freeList chan []byte 7 | } 8 | 9 | const leakyBufSize = 4096 10 | const maxNBuf = 2048 11 | 12 | var leakyBuf = NewLeakyBuf(maxNBuf, leakyBufSize) 13 | 14 | // NewLeakyBuf creates a leaky buffer which can hold at most n buffer, each 15 | // with bufSize bytes. 16 | func NewLeakyBuf(n, bufSize int) *LeakyBuf { 17 | return &LeakyBuf{ 18 | bufSize: bufSize, 19 | freeList: make(chan []byte, n), 20 | } 21 | } 22 | 23 | // Get returns a buffer from the leaky buffer or create a new buffer. 24 | func (lb *LeakyBuf) Get() (b []byte) { 25 | select { 26 | case b = <-lb.freeList: 27 | default: 28 | b = make([]byte, lb.bufSize) 29 | } 30 | return 31 | } 32 | 33 | // Put add the buffer into the free buffer pool for reuse. Panic if the buffer 34 | // size is not the same with the leaky buffer's. This is intended to expose 35 | // error usage of leaky buffer. 36 | func (lb *LeakyBuf) Put(b []byte) { 37 | if len(b) != lb.bufSize { 38 | panic("invalid buffer size that's put into leaky buffer") 39 | } 40 | select { 41 | case lb.freeList <- b: 42 | default: 43 | } 44 | return 45 | } 46 | -------------------------------------------------------------------------------- /shadowsocks/log.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | "log" 5 | "os" 6 | ) 7 | 8 | type DebugLog bool 9 | 10 | var Debug DebugLog 11 | 12 | var dbgLog = log.New(os.Stdout, "[DEBUG] ", log.Ltime) 13 | 14 | func (d DebugLog) Printf(format string, args ...interface{}) { 15 | if d { 16 | dbgLog.Printf(format, args...) 17 | } 18 | } 19 | 20 | func (d DebugLog) Println(args ...interface{}) { 21 | if d { 22 | dbgLog.Println(args...) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /shadowsocks/mergesort.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | func merge(left, right []uint64, comparison func (uint64, uint64) int64) []uint64 { 4 | result := make([]uint64, len(left) + len(right)) 5 | l, r := 0, 0 6 | for (l < len(left)) && (r < len(right)) { 7 | if comparison(left[l], right[r]) <= 0 { 8 | result[l + r] = left[l] 9 | l++ 10 | } else { 11 | result[l + r] = right[r] 12 | r++ 13 | } 14 | } 15 | for (l < len(left)) { 16 | result[l + r] = left[l] 17 | l++ 18 | } 19 | for (r < len(right)) { 20 | result[l + r] = right[r] 21 | r++ 22 | } 23 | return result 24 | } 25 | 26 | func Sort(arr []uint64, comparison func (uint64, uint64) int64) []uint64 { 27 | if len(arr) < 2 { 28 | return arr 29 | } 30 | var middle uint64 = uint64(len(arr)/2) 31 | return merge(Sort(arr[0:middle], comparison), Sort(arr[middle:], comparison), comparison) 32 | } 33 | -------------------------------------------------------------------------------- /shadowsocks/pipe.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | // "io" 5 | "net" 6 | "time" 7 | ) 8 | 9 | func SetReadTimeout(c net.Conn) { 10 | if readTimeout != 0 { 11 | c.SetReadDeadline(time.Now().Add(readTimeout)) 12 | } 13 | } 14 | 15 | // PipeThenClose copies data from src to dst, closes dst when done. 16 | func PipeThenClose(src, dst net.Conn) { 17 | defer dst.Close() 18 | buf := leakyBuf.Get() 19 | defer leakyBuf.Put(buf) 20 | for { 21 | SetReadTimeout(src) 22 | n, err := src.Read(buf) 23 | // read may return EOF with n > 0 24 | // should always process n > 0 bytes before handling error 25 | if n > 0 { 26 | // Note: avoid overwrite err returned by Read. 27 | if _, err := dst.Write(buf[0:n]); err != nil { 28 | Debug.Println("write:", err) 29 | break 30 | } 31 | } 32 | if err != nil { 33 | // Always "use of closed network connection", but no easy way to 34 | // identify this specific error. So just leave the error along for now. 35 | // More info here: https://code.google.com/p/go/issues/detail?id=4373 36 | /* 37 | if bool(Debug) && err != io.EOF { 38 | Debug.Println("read:", err) 39 | } 40 | */ 41 | break 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /shadowsocks/testdata/deprecated-client-multi-server.json: -------------------------------------------------------------------------------- 1 | { 2 | "server":["127.0.0.1", "127.0.1.1"], 3 | "server_port":8388, 4 | "local_port":1081, 5 | "password":"barfoo!", 6 | "timeout":60 7 | } 8 | -------------------------------------------------------------------------------- /shadowsocks/testdata/noserver.json: -------------------------------------------------------------------------------- 1 | { 2 | "server_port":8388, 3 | "local_port":1081, 4 | "password":"barfoo!", 5 | "timeout":60, 6 | "cache_enctable": true 7 | } 8 | -------------------------------------------------------------------------------- /shadowsocks/util.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func PrintVersion() { 10 | const version = "1.1.4" 11 | fmt.Println("shadowsocks-go version", version) 12 | } 13 | 14 | func IsFileExists(path string) (bool, error) { 15 | stat, err := os.Stat(path) 16 | if err == nil { 17 | if stat.Mode()&os.ModeType == 0 { 18 | return true, nil 19 | } 20 | return false, errors.New(path + " exists but is not regular file") 21 | } 22 | if os.IsNotExist(err) { 23 | return false, nil 24 | } 25 | return false, err 26 | } 27 | --------------------------------------------------------------------------------