├── .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 ├── 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 ├── conn_test.go ├── encrypt.go ├── encrypt_test.go ├── leakybuf.go ├── log.go ├── pipe.go ├── proxy.go ├── testdata ├── deprecated-client-multi-server.json └── noserver.json ├── udp.go ├── udprelay.go └── util.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.deb 2 | script/http 3 | bin 4 | .idea 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - "1.8.x" 4 | - "1.9.x" 5 | - "1.10.x" 6 | - "1.11.x" 7 | install: 8 | - go get golang.org/x/crypto/blowfish 9 | - go get golang.org/x/crypto/cast5 10 | - go get golang.org/x/crypto/salsa20 11 | - go get github.com/aead/chacha20 12 | - go install ./cmd/shadowsocks-local 13 | - go install ./cmd/shadowsocks-server 14 | script: 15 | - PATH=$PATH:$HOME/gopath/bin bash -x ./script/test.sh 16 | sudo: false 17 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 1.2.2 (2018-06-10) 2 | * OTA is finally deprecated. 3 | * Codes clear up. 4 | 5 | 1.2.1 (2017-01-22) 6 | * Fix bug in handling of UDP OTA packets 7 | 8 | 1.2.0 (2017-01-20) 9 | * Support UDP relay on server side, and OTA 10 | * Support "aes-[128/192/256]-ctr" encryption method (Thanks for @slurin) 11 | * Support "chacha20-ietf" encryption method 12 | * Improve performance of "chacha20" encryption method 13 | * Correctly close connection if handshake failed 14 | 15 | 1.1.5 (2016-05-04) 16 | * Support OTA (Thanks for @ayanamist for implementing this feature) 17 | 18 | 1.1.4 (2015-05-10) 19 | * Support "chacha20" encryption method, thanks to @defia 20 | * Support "salsa20" encryption method, thanks to @genzj 21 | * Fix go 1.4 canonical import paths, thanks to @ddatsh 22 | * Exit if port not bindable, thanks to @thomasf 23 | * Always set timeout for data transfer 24 | * More buffer reuse 25 | 26 | 1.1.3 (2014-09-28) 27 | * Fix can't specify encryption method in config file 28 | 29 | 1.1.2 (2014-09-21) 30 | * Support new encryption method "rc4-md5" 31 | * Use aes-256-cfb as default encryption method for command line app 32 | 33 | 1.1.1 (2013-07-12) 34 | * Add -b option to limit listen address for client 35 | * Fix can't override server address on command line 36 | 37 | 1.1 (2013-05-26) 38 | * Add more encryption methods 39 | * Enable CGO for OS X when building 40 | 41 | 1.0 (2013-05-17) 42 | * Support specify servers with IPv6 address 43 | * Support IPv6 address in socks requests 44 | * Better handling of configuration file for debian package 45 | 46 | 0.6.2 (2013-03-15) 47 | * Connect to multiple servers in the order specified in config 48 | * More information in server error log to help error diagnosing 49 | 50 | 0.6.1 (2013-03-04) 51 | * Small performance improvement 52 | * For windows: provide shadowsocks-tray.exe to hide shadowsocks-local.exe in 53 | system tray. (Thanks to phuslu's taskbar.) 54 | 55 | 0.6 (2013-01-23) 56 | 57 | * Generate ciphers on demand (encryption table cache is removed) 58 | * Support RC4 encryption 59 | -------------------------------------------------------------------------------- /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 | # Deprecated 2 | 3 | Use https://github.com/shadowsocks/go-shadowsocks2 instead. 4 | 5 | # shadowsocks-go 6 | 7 | Current version: 1.2.2 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=master)](https://travis-ci.org/shadowsocks/shadowsocks-go) 8 | 9 | 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). 10 | 11 | The protocol is compatible with the origin shadowsocks (if both have been upgraded to the latest version). 12 | 13 | **Note `server_password` option syntax changed in 0.6.2, the client now connects to servers in the order specified in the config.** 14 | 15 | **Please develop on the latest develop branch if you want to send pull request.** 16 | 17 | # Install 18 | 19 | Download precompiled binarys from the [release page](https://github.com/shadowsocks/shadowsocks-go/releases). (All compiled with cgo disabled, except the mac version.) 20 | 21 | You can also install from source (assume you have go installed): 22 | 23 | ``` 24 | # on server 25 | go get github.com/shadowsocks/shadowsocks-go/cmd/shadowsocks-server 26 | # on client 27 | go get github.com/shadowsocks/shadowsocks-go/cmd/shadowsocks-local 28 | ``` 29 | 30 | It's recommended to disable cgo when compiling shadowsocks-go. This will prevent the go runtime from creating too many threads for dns lookup. 31 | 32 | # Usage 33 | 34 | 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. 35 | 36 | 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: 37 | 38 | ``` 39 | server your server ip or hostname 40 | server_port server port 41 | local_port local socks5 proxy port 42 | method encryption method, null by default (table), the following methods are supported: 43 | aes-128-cfb, aes-192-cfb, aes-256-cfb, bf-cfb, cast5-cfb, des-cfb, rc4-md5, rc4-md5-6, chacha20, salsa20, rc4, table 44 | password a password used to encrypt transfer 45 | timeout server option, in seconds 46 | ``` 47 | 48 | Run `shadowsocks-server` on your server. To run it in the background, run `shadowsocks-server > log &`. 49 | 50 | On client, run `shadowsocks-local`. Change proxy settings of your browser to 51 | 52 | ``` 53 | SOCKS5 127.0.0.1:local_port 54 | ``` 55 | 56 | ## About encryption methods 57 | 58 | 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)**. 59 | 60 | **rc4 and table encryption methods are deprecated because they are not secure.** 61 | 62 | ### One Time Auth 63 | 64 | OTA function is deprecated because it is reported to have potential security risk. 65 | 66 | ## Command line options 67 | 68 | Command line options can override settings from configuration files. Use `-h` option to see all available options. 69 | 70 | ``` 71 | shadowsocks-local -s server_address -p server_port -k password 72 | -m aes-128-cfb -c config.json 73 | -b local_address -l local_port 74 | shadowsocks-server -p server_port -k password 75 | -m aes-128-cfb -c config.json 76 | -t timeout 77 | ``` 78 | 79 | Use `-d` option to enable debug message. 80 | 81 | ## Use multiple servers on client 82 | 83 | ``` 84 | server_password specify multiple server and password, server should be in the form of host:port 85 | ``` 86 | 87 | 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. 88 | 89 | 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.) 90 | 91 | ## Multiple users with different passwords on server 92 | 93 | 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: 94 | 95 | ``` 96 | port_password specify multiple ports and passwords to support multiple users 97 | ``` 98 | 99 | 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. 100 | 101 | ### Update port password for a running server 102 | 103 | Edit the config file used to start the server, then send `SIGHUP` to the server process. 104 | 105 | # Note to OpenVZ users 106 | 107 | **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.) 108 | 109 | If vswap is not an option and memory usage is a problem for you, try [shadowsocks-libev](https://github.com/madeye/shadowsocks-libev). 110 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowsocks/shadowsocks-go/3e585ff90601765510d31ee1d05b6f63548c7d44/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 | "bytes" 5 | "encoding/base64" 6 | "encoding/binary" 7 | "errors" 8 | "flag" 9 | "fmt" 10 | "io" 11 | "log" 12 | "math/rand" 13 | "net" 14 | "os" 15 | "path" 16 | "strconv" 17 | "strings" 18 | "time" 19 | 20 | ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" 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 | ) 33 | 34 | const ( 35 | socksVer5 = 5 36 | socksCmdConnect = 1 37 | ) 38 | 39 | func init() { 40 | rand.Seed(time.Now().Unix()) 41 | } 42 | 43 | func handShake(conn net.Conn) (err error) { 44 | const ( 45 | idVer = 0 46 | idNmethod = 1 47 | ) 48 | // version identification and method selection message in theory can have 49 | // at most 256 methods, plus version and nmethod field in total 258 bytes 50 | // the current rfc defines only 3 authentication methods (plus 2 reserved), 51 | // so it won't be such long in practice 52 | 53 | buf := make([]byte, 258) 54 | 55 | var n int 56 | ss.SetReadTimeout(conn) 57 | // make sure we get the nmethod field 58 | if n, err = io.ReadAtLeast(conn, buf, idNmethod+1); err != nil { 59 | return 60 | } 61 | if buf[idVer] != socksVer5 { 62 | return errVer 63 | } 64 | nmethod := int(buf[idNmethod]) 65 | msgLen := nmethod + 2 66 | if n == msgLen { // handshake done, common case 67 | // do nothing, jump directly to send confirmation 68 | } else if n < msgLen { // has more methods to read, rare case 69 | if _, err = io.ReadFull(conn, buf[n:msgLen]); err != nil { 70 | return 71 | } 72 | } else { // error, should not get extra data 73 | return errAuthExtraData 74 | } 75 | // send confirmation: version 5, no authentication required 76 | _, err = conn.Write([]byte{socksVer5, 0}) 77 | return 78 | } 79 | 80 | func getRequest(conn net.Conn) (rawaddr []byte, host string, err error) { 81 | const ( 82 | idVer = 0 83 | idCmd = 1 84 | idType = 3 // address type index 85 | idIP0 = 4 // ip address start index 86 | idDmLen = 4 // domain address length index 87 | idDm0 = 5 // domain address start index 88 | 89 | typeIPv4 = 1 // type is ipv4 address 90 | typeDm = 3 // type is domain address 91 | typeIPv6 = 4 // type is ipv6 address 92 | 93 | lenIPv4 = 3 + 1 + net.IPv4len + 2 // 3(ver+cmd+rsv) + 1addrType + ipv4 + 2port 94 | lenIPv6 = 3 + 1 + net.IPv6len + 2 // 3(ver+cmd+rsv) + 1addrType + ipv6 + 2port 95 | lenDmBase = 3 + 1 + 1 + 2 // 3 + 1addrType + 1addrLen + 2port, plus addrLen 96 | ) 97 | // refer to getRequest in server.go for why set buffer size to 263 98 | buf := make([]byte, 263) 99 | var n int 100 | ss.SetReadTimeout(conn) 101 | // read till we get possible domain length field 102 | if n, err = io.ReadAtLeast(conn, buf, idDmLen+1); err != nil { 103 | return 104 | } 105 | // check version and cmd 106 | if buf[idVer] != socksVer5 { 107 | err = errVer 108 | return 109 | } 110 | if buf[idCmd] != socksCmdConnect { 111 | err = errCmd 112 | return 113 | } 114 | 115 | reqLen := -1 116 | switch buf[idType] { 117 | case typeIPv4: 118 | reqLen = lenIPv4 119 | case typeIPv6: 120 | reqLen = lenIPv6 121 | case typeDm: 122 | reqLen = int(buf[idDmLen]) + lenDmBase 123 | default: 124 | err = errAddrType 125 | return 126 | } 127 | 128 | if n == reqLen { 129 | // common case, do nothing 130 | } else if n < reqLen { // rare case 131 | if _, err = io.ReadFull(conn, buf[n:reqLen]); err != nil { 132 | return 133 | } 134 | } else { 135 | err = errReqExtraData 136 | return 137 | } 138 | 139 | rawaddr = buf[idType:reqLen] 140 | 141 | if debug { 142 | switch buf[idType] { 143 | case typeIPv4: 144 | host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() 145 | case typeIPv6: 146 | host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() 147 | case typeDm: 148 | host = string(buf[idDm0 : idDm0+buf[idDmLen]]) 149 | } 150 | port := binary.BigEndian.Uint16(buf[reqLen-2 : reqLen]) 151 | host = net.JoinHostPort(host, strconv.Itoa(int(port))) 152 | } 153 | 154 | return 155 | } 156 | 157 | type ServerCipher struct { 158 | server string 159 | cipher *ss.Cipher 160 | } 161 | 162 | var servers struct { 163 | srvCipher []*ServerCipher 164 | failCnt []int // failed connection count 165 | } 166 | 167 | func parseServerConfig(config *ss.Config) { 168 | hasPort := func(s string) bool { 169 | _, port, err := net.SplitHostPort(s) 170 | if err != nil { 171 | return false 172 | } 173 | return port != "" 174 | } 175 | 176 | if len(config.ServerPassword) == 0 { 177 | // only one encryption table 178 | cipher, err := ss.NewCipher(config.Method, config.Password) 179 | if err != nil { 180 | log.Fatal("Failed generating ciphers:", err) 181 | } 182 | srvPort := strconv.Itoa(config.ServerPort) 183 | srvArr := config.GetServerArray() 184 | n := len(srvArr) 185 | servers.srvCipher = make([]*ServerCipher, n) 186 | 187 | for i, s := range srvArr { 188 | if hasPort(s) { 189 | log.Println("ignore server_port option for server", s) 190 | servers.srvCipher[i] = &ServerCipher{s, cipher} 191 | } else { 192 | servers.srvCipher[i] = &ServerCipher{net.JoinHostPort(s, srvPort), cipher} 193 | } 194 | } 195 | } else { 196 | // multiple servers 197 | n := len(config.ServerPassword) 198 | servers.srvCipher = make([]*ServerCipher, n) 199 | 200 | cipherCache := make(map[string]*ss.Cipher) 201 | i := 0 202 | for _, serverInfo := range config.ServerPassword { 203 | if len(serverInfo) < 2 || len(serverInfo) > 3 { 204 | log.Fatalf("server %v syntax error\n", serverInfo) 205 | } 206 | server := serverInfo[0] 207 | passwd := serverInfo[1] 208 | encmethod := "" 209 | if len(serverInfo) == 3 { 210 | encmethod = serverInfo[2] 211 | } 212 | if !hasPort(server) { 213 | log.Fatalf("no port for server %s\n", server) 214 | } 215 | // Using "|" as delimiter is safe here, since no encryption 216 | // method contains it in the name. 217 | cacheKey := encmethod + "|" + passwd 218 | cipher, ok := cipherCache[cacheKey] 219 | if !ok { 220 | var err error 221 | cipher, err = ss.NewCipher(encmethod, passwd) 222 | if err != nil { 223 | log.Fatal("Failed generating ciphers:", err) 224 | } 225 | cipherCache[cacheKey] = cipher 226 | } 227 | servers.srvCipher[i] = &ServerCipher{server, cipher} 228 | i++ 229 | } 230 | } 231 | servers.failCnt = make([]int, len(servers.srvCipher)) 232 | for _, se := range servers.srvCipher { 233 | log.Println("available remote server", se.server) 234 | } 235 | return 236 | } 237 | 238 | func connectToServer(serverId int, rawaddr []byte, addr string) (remote *ss.Conn, err error) { 239 | se := servers.srvCipher[serverId] 240 | remote, err = ss.DialWithRawAddr(rawaddr, se.server, se.cipher.Copy()) 241 | if err != nil { 242 | log.Println("error connecting to shadowsocks server:", err) 243 | const maxFailCnt = 30 244 | if servers.failCnt[serverId] < maxFailCnt { 245 | servers.failCnt[serverId]++ 246 | } 247 | return nil, err 248 | } 249 | debug.Printf("connected to %s via %s\n", addr, se.server) 250 | servers.failCnt[serverId] = 0 251 | return 252 | } 253 | 254 | // Connection to the server in the order specified in the config. On 255 | // connection failure, try the next server. A failed server will be tried with 256 | // some probability according to its fail count, so we can discover recovered 257 | // servers. 258 | func createServerConn(rawaddr []byte, addr string) (remote *ss.Conn, err error) { 259 | const baseFailCnt = 20 260 | n := len(servers.srvCipher) 261 | skipped := make([]int, 0) 262 | for i := 0; i < n; i++ { 263 | // skip failed server, but try it with some probability 264 | if servers.failCnt[i] > 0 && rand.Intn(servers.failCnt[i]+baseFailCnt) != 0 { 265 | skipped = append(skipped, i) 266 | continue 267 | } 268 | remote, err = connectToServer(i, rawaddr, addr) 269 | if err == nil { 270 | return 271 | } 272 | } 273 | // last resort, try skipped servers, not likely to succeed 274 | for _, i := range skipped { 275 | remote, err = connectToServer(i, rawaddr, addr) 276 | if err == nil { 277 | return 278 | } 279 | } 280 | return nil, err 281 | } 282 | 283 | func handleConnection(conn net.Conn) { 284 | if debug { 285 | debug.Printf("socks connect from %s\n", conn.RemoteAddr().String()) 286 | } 287 | closed := false 288 | defer func() { 289 | if !closed { 290 | conn.Close() 291 | } 292 | }() 293 | 294 | var err error = nil 295 | if err = handShake(conn); err != nil { 296 | log.Println("socks handshake:", err) 297 | return 298 | } 299 | rawaddr, addr, err := getRequest(conn) 300 | if err != nil { 301 | log.Println("error getting request:", err) 302 | return 303 | } 304 | // Sending connection established message immediately to client. 305 | // This some round trip time for creating socks connection with the client. 306 | // But if connection failed, the client will get connection reset error. 307 | _, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43}) 308 | if err != nil { 309 | debug.Println("send connection confirmation:", err) 310 | return 311 | } 312 | 313 | remote, err := createServerConn(rawaddr, addr) 314 | if err != nil { 315 | if len(servers.srvCipher) > 1 { 316 | log.Println("Failed connect to all available shadowsocks server") 317 | } 318 | return 319 | } 320 | defer func() { 321 | if !closed { 322 | remote.Close() 323 | } 324 | }() 325 | 326 | go ss.PipeThenClose(conn, remote, nil) 327 | ss.PipeThenClose(remote, conn, nil) 328 | closed = true 329 | debug.Println("closed connection to", addr) 330 | } 331 | 332 | func run(listenAddr string) { 333 | ln, err := net.Listen("tcp", listenAddr) 334 | if err != nil { 335 | log.Fatal(err) 336 | } 337 | log.Printf("starting local socks5 server at %v ...\n", listenAddr) 338 | for { 339 | conn, err := ln.Accept() 340 | if err != nil { 341 | log.Println("accept:", err) 342 | continue 343 | } 344 | go handleConnection(conn) 345 | } 346 | } 347 | 348 | func enoughOptions(config *ss.Config) bool { 349 | return config.Server != nil && config.ServerPort != 0 && 350 | config.LocalPort != 0 && config.Password != "" 351 | } 352 | 353 | func parseURI(u string, cfg *ss.Config) (string, error) { 354 | if u == "" { 355 | return "", nil 356 | } 357 | invalidURI := errors.New("invalid URI") 358 | // ss://base64(method:password)@host:port 359 | // ss://base64(method:password@host:port) 360 | u = strings.TrimLeft(u, "ss://") 361 | i := strings.IndexRune(u, '@') 362 | var headParts, tailParts [][]byte 363 | if i == -1 { 364 | dat, err := base64.StdEncoding.DecodeString(u) 365 | if err != nil { 366 | return "", err 367 | } 368 | parts := bytes.Split(dat, []byte("@")) 369 | if len(parts) != 2 { 370 | return "", invalidURI 371 | } 372 | headParts = bytes.SplitN(parts[0], []byte(":"), 2) 373 | tailParts = bytes.SplitN(parts[1], []byte(":"), 2) 374 | 375 | } else { 376 | if i+1 >= len(u) { 377 | return "", invalidURI 378 | } 379 | tailParts = bytes.SplitN([]byte(u[i+1:]), []byte(":"), 2) 380 | dat, err := base64.StdEncoding.DecodeString(u[:i]) 381 | if err != nil { 382 | return "", err 383 | } 384 | headParts = bytes.SplitN(dat, []byte(":"), 2) 385 | } 386 | if len(headParts) != 2 { 387 | return "", invalidURI 388 | } 389 | 390 | if len(tailParts) != 2 { 391 | return "", invalidURI 392 | } 393 | cfg.Method = string(headParts[0]) 394 | cfg.Password = string(headParts[1]) 395 | p, e := strconv.Atoi(string(tailParts[1])) 396 | if e != nil { 397 | return "", e 398 | } 399 | cfg.ServerPort = p 400 | return string(tailParts[0]), nil 401 | 402 | } 403 | 404 | func main() { 405 | log.SetOutput(os.Stdout) 406 | 407 | var configFile, cmdServer, cmdURI string 408 | var cmdConfig ss.Config 409 | var printVer bool 410 | 411 | flag.BoolVar(&printVer, "version", false, "print version") 412 | flag.StringVar(&configFile, "c", "config.json", "specify config file") 413 | flag.StringVar(&cmdServer, "s", "", "server address") 414 | flag.StringVar(&cmdConfig.LocalAddress, "b", "", "local address, listen only to this address if specified") 415 | flag.StringVar(&cmdConfig.Password, "k", "", "password") 416 | flag.IntVar(&cmdConfig.ServerPort, "p", 0, "server port") 417 | flag.IntVar(&cmdConfig.Timeout, "t", 300, "timeout in seconds") 418 | flag.IntVar(&cmdConfig.LocalPort, "l", 0, "local socks5 proxy port") 419 | flag.StringVar(&cmdConfig.Method, "m", "", "encryption method, default: aes-256-cfb") 420 | flag.BoolVar((*bool)(&debug), "d", false, "print debug message") 421 | flag.StringVar(&cmdURI, "u", "", "shadowsocks URI") 422 | 423 | flag.Parse() 424 | 425 | if s, e := parseURI(cmdURI, &cmdConfig); e != nil { 426 | log.Printf("invalid URI: %s\n", e.Error()) 427 | flag.Usage() 428 | os.Exit(1) 429 | } else if s != "" { 430 | cmdServer = s 431 | } 432 | 433 | if printVer { 434 | ss.PrintVersion() 435 | os.Exit(0) 436 | } 437 | 438 | cmdConfig.Server = cmdServer 439 | ss.SetDebug(debug) 440 | 441 | exists, err := ss.IsFileExists(configFile) 442 | // If no config file in current directory, try search it in the binary directory 443 | // Note there's no portable way to detect the binary directory. 444 | binDir := path.Dir(os.Args[0]) 445 | if (!exists || err != nil) && binDir != "" && binDir != "." { 446 | oldConfig := configFile 447 | configFile = path.Join(binDir, "config.json") 448 | log.Printf("%s not found, try config file %s\n", oldConfig, configFile) 449 | } 450 | 451 | config, err := ss.ParseConfig(configFile) 452 | if err != nil { 453 | config = &cmdConfig 454 | if !os.IsNotExist(err) { 455 | fmt.Fprintf(os.Stderr, "error reading %s: %v\n", configFile, err) 456 | os.Exit(1) 457 | } 458 | } else { 459 | ss.UpdateConfig(config, &cmdConfig) 460 | } 461 | if config.Method == "" { 462 | config.Method = "aes-256-cfb" 463 | } 464 | if len(config.ServerPassword) == 0 { 465 | if !enoughOptions(config) { 466 | fmt.Fprintln(os.Stderr, "must specify server address, password and both server/local port") 467 | os.Exit(1) 468 | } 469 | } else { 470 | if config.Password != "" || config.ServerPort != 0 || config.GetServerArray() != nil { 471 | fmt.Fprintln(os.Stderr, "given server_password, ignore server, server_port and password option:", config) 472 | } 473 | if config.LocalPort == 0 { 474 | fmt.Fprintln(os.Stderr, "must specify local port") 475 | os.Exit(1) 476 | } 477 | } 478 | 479 | parseServerConfig(config) 480 | run(config.LocalAddress + ":" + strconv.Itoa(config.LocalPort)) 481 | } 482 | -------------------------------------------------------------------------------- /cmd/shadowsocks-server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "encoding/json" 7 | "errors" 8 | "flag" 9 | "fmt" 10 | "io" 11 | "log" 12 | "net" 13 | "os" 14 | "os/signal" 15 | "runtime" 16 | "strconv" 17 | "strings" 18 | "sync" 19 | "syscall" 20 | "time" 21 | 22 | ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" 23 | ) 24 | 25 | const ( 26 | idType = 0 // address type index 27 | idIP0 = 1 // ip address start index 28 | idDmLen = 1 // domain address length index 29 | idDm0 = 2 // domain address start index 30 | 31 | typeIPv4 = 1 // type is ipv4 address 32 | typeDm = 3 // type is domain address 33 | typeIPv6 = 4 // type is ipv6 address 34 | 35 | lenIPv4 = net.IPv4len + 2 // ipv4 + 2port 36 | lenIPv6 = net.IPv6len + 2 // ipv6 + 2port 37 | lenDmBase = 2 // 1addrLen + 2port, plus addrLen 38 | // lenHmacSha1 = 10 39 | ) 40 | 41 | var debug ss.DebugLog 42 | var sanitizeIps bool 43 | var udp bool 44 | var managerAddr string 45 | 46 | func getRequest(conn *ss.Conn) (host string, err error) { 47 | ss.SetReadTimeout(conn) 48 | 49 | // buf size should at least have the same size with the largest possible 50 | // request size (when addrType is 3, domain name has at most 256 bytes) 51 | // 1(addrType) + 1(lenByte) + 255(max length address) + 2(port) + 10(hmac-sha1) 52 | buf := make([]byte, 269) 53 | // read till we get possible domain length field 54 | if _, err = io.ReadFull(conn, buf[:idType+1]); err != nil { 55 | return 56 | } 57 | 58 | var reqStart, reqEnd int 59 | addrType := buf[idType] 60 | switch addrType & ss.AddrMask { 61 | case typeIPv4: 62 | reqStart, reqEnd = idIP0, idIP0+lenIPv4 63 | case typeIPv6: 64 | reqStart, reqEnd = idIP0, idIP0+lenIPv6 65 | case typeDm: 66 | if _, err = io.ReadFull(conn, buf[idType+1:idDmLen+1]); err != nil { 67 | return 68 | } 69 | reqStart, reqEnd = idDm0, idDm0+int(buf[idDmLen])+lenDmBase 70 | default: 71 | err = fmt.Errorf("addr type %d not supported", addrType&ss.AddrMask) 72 | return 73 | } 74 | 75 | if _, err = io.ReadFull(conn, buf[reqStart:reqEnd]); err != nil { 76 | return 77 | } 78 | 79 | // Return string for typeIP is not most efficient, but browsers (Chrome, 80 | // Safari, Firefox) all seems using typeDm exclusively. So this is not a 81 | // big problem. 82 | switch addrType & ss.AddrMask { 83 | case typeIPv4: 84 | host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() 85 | case typeIPv6: 86 | host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() 87 | case typeDm: 88 | host = string(buf[idDm0 : idDm0+int(buf[idDmLen])]) 89 | } 90 | // parse port 91 | port := binary.BigEndian.Uint16(buf[reqEnd-2 : reqEnd]) 92 | host = net.JoinHostPort(host, strconv.Itoa(int(port))) 93 | return 94 | } 95 | 96 | const logCntDelta = 100 97 | 98 | var connCnt int 99 | var nextLogConnCnt = logCntDelta 100 | 101 | func sanitizeAddr(addr net.Addr) string { 102 | if sanitizeIps { 103 | return "x.x.x.x:zzzz" 104 | } else { 105 | return addr.String() 106 | } 107 | } 108 | 109 | func handleConnection(conn *ss.Conn, port string) { 110 | var host string 111 | 112 | connCnt++ // this maybe not accurate, but should be enough 113 | if connCnt-nextLogConnCnt >= 0 { 114 | // XXX There's no xadd in the atomic package, so it's difficult to log 115 | // the message only once with low cost. Also note nextLogConnCnt maybe 116 | // added twice for current peak connection number level. 117 | log.Printf("Number of client connections reaches %d\n", nextLogConnCnt) 118 | nextLogConnCnt += logCntDelta 119 | } 120 | 121 | // function arguments are always evaluated, so surround debug statement 122 | // with if statement 123 | if debug { 124 | debug.Printf("new client %s->%s\n", sanitizeAddr(conn.RemoteAddr()), conn.LocalAddr()) 125 | } 126 | closed := false 127 | defer func() { 128 | if debug { 129 | debug.Printf("closed pipe %s<->%s\n", sanitizeAddr(conn.RemoteAddr()), host) 130 | } 131 | connCnt-- 132 | if !closed { 133 | conn.Close() 134 | } 135 | }() 136 | 137 | host, err := getRequest(conn) 138 | if err != nil { 139 | log.Println("error getting request", sanitizeAddr(conn.RemoteAddr()), conn.LocalAddr(), err) 140 | closed = true 141 | return 142 | } 143 | // ensure the host does not contain some illegal characters, NUL may panic on Win32 144 | if strings.ContainsRune(host, 0x00) { 145 | log.Println("invalid domain name.") 146 | closed = true 147 | return 148 | } 149 | debug.Println("connecting", host) 150 | remote, err := net.Dial("tcp", host) 151 | if err != nil { 152 | if ne, ok := err.(*net.OpError); ok && (ne.Err == syscall.EMFILE || ne.Err == syscall.ENFILE) { 153 | // log too many open file error 154 | // EMFILE is process reaches open file limits, ENFILE is system limit 155 | log.Println("dial error:", err) 156 | } else { 157 | log.Println("error connecting to:", host, err) 158 | } 159 | return 160 | } 161 | defer func() { 162 | if !closed { 163 | remote.Close() 164 | } 165 | }() 166 | if debug { 167 | debug.Printf("piping %s<->%s", sanitizeAddr(conn.RemoteAddr()), host) 168 | } 169 | go func() { 170 | ss.PipeThenClose(conn, remote, func(Traffic int) { 171 | passwdManager.addTraffic(port, Traffic) 172 | }) 173 | }() 174 | 175 | ss.PipeThenClose(remote, conn, func(Traffic int) { 176 | passwdManager.addTraffic(port, Traffic) 177 | }) 178 | 179 | closed = true 180 | return 181 | } 182 | 183 | type PortListener struct { 184 | password string 185 | listener net.Listener 186 | } 187 | 188 | type UDPListener struct { 189 | password string 190 | listener *net.UDPConn 191 | } 192 | 193 | type PasswdManager struct { 194 | sync.Mutex 195 | portListener map[string]*PortListener 196 | udpListener map[string]*UDPListener 197 | trafficStats map[string]int64 198 | } 199 | 200 | func (pm *PasswdManager) add(port, password string, listener net.Listener) { 201 | pm.Lock() 202 | pm.portListener[port] = &PortListener{password, listener} 203 | pm.trafficStats[port] = 0 204 | pm.Unlock() 205 | } 206 | 207 | func (pm *PasswdManager) addUDP(port, password string, listener *net.UDPConn) { 208 | pm.Lock() 209 | pm.udpListener[port] = &UDPListener{password, listener} 210 | pm.Unlock() 211 | } 212 | 213 | func (pm *PasswdManager) get(port string) (pl *PortListener, ok bool) { 214 | pm.Lock() 215 | pl, ok = pm.portListener[port] 216 | pm.Unlock() 217 | return 218 | } 219 | 220 | func (pm *PasswdManager) getUDP(port string) (pl *UDPListener, ok bool) { 221 | pm.Lock() 222 | pl, ok = pm.udpListener[port] 223 | pm.Unlock() 224 | return 225 | } 226 | 227 | func (pm *PasswdManager) del(port string) { 228 | pl, ok := pm.get(port) 229 | if !ok { 230 | return 231 | } 232 | if udp { 233 | upl, ok := pm.getUDP(port) 234 | if !ok { 235 | return 236 | } 237 | upl.listener.Close() 238 | } 239 | pl.listener.Close() 240 | pm.Lock() 241 | delete(pm.portListener, port) 242 | delete(pm.trafficStats, port) 243 | if udp { 244 | delete(pm.udpListener, port) 245 | } 246 | pm.Unlock() 247 | } 248 | 249 | func (pm *PasswdManager) addTraffic(port string, n int) { 250 | pm.Lock() 251 | pm.trafficStats[port] = pm.trafficStats[port] + int64(n) 252 | pm.Unlock() 253 | return 254 | } 255 | 256 | func (pm *PasswdManager) getTrafficStats() map[string]int64 { 257 | pm.Lock() 258 | copy := make(map[string]int64) 259 | for k, v := range pm.trafficStats { 260 | copy[k] = v 261 | } 262 | pm.Unlock() 263 | return copy 264 | } 265 | 266 | // Update port password would first close a port and restart listening on that 267 | // port. A different approach would be directly change the password used by 268 | // that port, but that requires **sharing** password between the port listener 269 | // and password manager. 270 | func (pm *PasswdManager) updatePortPasswd(port, password string) { 271 | pl, ok := pm.get(port) 272 | if !ok { 273 | log.Printf("new port %s added\n", port) 274 | } else { 275 | if pl.password == password { 276 | return 277 | } 278 | log.Printf("closing port %s to update password\n", port) 279 | pl.listener.Close() 280 | } 281 | // run will add the new port listener to passwdManager. 282 | // So there maybe concurrent access to passwdManager and we need lock to protect it. 283 | go run(port, password) 284 | if udp { 285 | pl, ok := pm.getUDP(port) 286 | if !ok { 287 | log.Printf("new udp port %s added\n", port) 288 | } else { 289 | if pl.password == password { 290 | return 291 | } 292 | log.Printf("closing udp port %s to update password\n", port) 293 | pl.listener.Close() 294 | } 295 | go runUDP(port, password) 296 | } 297 | } 298 | 299 | var passwdManager = PasswdManager{ 300 | portListener: map[string]*PortListener{}, 301 | udpListener: map[string]*UDPListener{}, 302 | trafficStats: map[string]int64{}, 303 | } 304 | 305 | func updatePasswd() { 306 | log.Println("updating password") 307 | newconfig, err := ss.ParseConfig(configFile) 308 | if err != nil { 309 | log.Printf("error parsing config file %s to update password: %v\n", configFile, err) 310 | return 311 | } 312 | oldconfig := config 313 | config = newconfig 314 | 315 | if err = unifyPortPassword(config); err != nil { 316 | return 317 | } 318 | for port, passwd := range config.PortPassword { 319 | passwdManager.updatePortPasswd(port, passwd) 320 | if oldconfig.PortPassword != nil { 321 | delete(oldconfig.PortPassword, port) 322 | } 323 | } 324 | // port password still left in the old config should be closed 325 | for port := range oldconfig.PortPassword { 326 | log.Printf("closing port %s as it's deleted\n", port) 327 | passwdManager.del(port) 328 | } 329 | log.Println("password updated") 330 | } 331 | 332 | func waitSignal() { 333 | var sigChan = make(chan os.Signal, 1) 334 | signal.Notify(sigChan, syscall.SIGHUP) 335 | for sig := range sigChan { 336 | if sig == syscall.SIGHUP { 337 | updatePasswd() 338 | } else { 339 | // is this going to happen? 340 | log.Printf("caught signal %v, exit", sig) 341 | os.Exit(0) 342 | } 343 | } 344 | } 345 | 346 | func run(port, password string) { 347 | ln, err := net.Listen("tcp", ":"+port) 348 | if err != nil { 349 | log.Printf("error listening port %v: %v\n", port, err) 350 | os.Exit(1) 351 | } 352 | passwdManager.add(port, password, ln) 353 | var cipher *ss.Cipher 354 | log.Printf("server listening port %v ...\n", port) 355 | for { 356 | conn, err := ln.Accept() 357 | if err != nil { 358 | // listener maybe closed to update password 359 | debug.Printf("accept error: %v\n", err) 360 | return 361 | } 362 | // Creating cipher upon first connection. 363 | if cipher == nil { 364 | log.Println("creating cipher for port:", port) 365 | cipher, err = ss.NewCipher(config.Method, password) 366 | if err != nil { 367 | log.Printf("Error generating cipher for port: %s %v\n", port, err) 368 | conn.Close() 369 | continue 370 | } 371 | } 372 | go handleConnection(ss.NewConn(conn, cipher.Copy()), port) 373 | } 374 | } 375 | 376 | func runUDP(port, password string) { 377 | var cipher *ss.Cipher 378 | port_i, _ := strconv.Atoi(port) 379 | log.Printf("listening udp port %v\n", port) 380 | conn, err := net.ListenUDP("udp", &net.UDPAddr{ 381 | IP: net.IPv6zero, 382 | Port: port_i, 383 | }) 384 | passwdManager.addUDP(port, password, conn) 385 | if err != nil { 386 | log.Printf("error listening udp port %v: %v\n", port, err) 387 | return 388 | } 389 | defer conn.Close() 390 | cipher, err = ss.NewCipher(config.Method, password) 391 | if err != nil { 392 | log.Printf("Error generating cipher for udp port: %s %v\n", port, err) 393 | conn.Close() 394 | } 395 | SecurePacketConn := ss.NewSecurePacketConn(conn, cipher.Copy()) 396 | for { 397 | if err := ss.ReadAndHandleUDPReq(SecurePacketConn, func(traffic int) { 398 | passwdManager.addTraffic(port, traffic) 399 | }); err != nil { 400 | debug.Printf("udp read error: %v\n", err) 401 | return 402 | } 403 | } 404 | } 405 | 406 | func enoughOptions(config *ss.Config) bool { 407 | return config.ServerPort != 0 && config.Password != "" 408 | } 409 | 410 | func unifyPortPassword(config *ss.Config) (err error) { 411 | if len(config.PortPassword) == 0 { // this handles both nil PortPassword and empty one 412 | if !enoughOptions(config) { 413 | fmt.Fprintln(os.Stderr, "must specify both port and password") 414 | return errors.New("not enough options") 415 | } 416 | port := strconv.Itoa(config.ServerPort) 417 | config.PortPassword = map[string]string{port: config.Password} 418 | } else { 419 | if config.Password != "" || config.ServerPort != 0 { 420 | fmt.Fprintln(os.Stderr, "given port_password, ignore server_port and password option") 421 | } 422 | } 423 | return 424 | } 425 | 426 | var configFile string 427 | var config *ss.Config 428 | 429 | func main() { 430 | log.SetOutput(os.Stdout) 431 | 432 | var cmdConfig ss.Config 433 | var printVer bool 434 | var core int 435 | 436 | flag.BoolVar(&printVer, "version", false, "print version") 437 | flag.StringVar(&configFile, "c", "config.json", "specify config file") 438 | flag.StringVar(&cmdConfig.Password, "k", "", "password") 439 | flag.IntVar(&cmdConfig.ServerPort, "p", 0, "server port") 440 | flag.IntVar(&cmdConfig.Timeout, "t", 300, "timeout in seconds") 441 | flag.StringVar(&cmdConfig.Method, "m", "", "encryption method, default: aes-256-cfb") 442 | flag.IntVar(&core, "core", 0, "maximum number of CPU cores to use, default is determinied by Go runtime") 443 | flag.BoolVar((*bool)(&debug), "d", false, "print debug message") 444 | flag.BoolVar((*bool)(&sanitizeIps), "A", false, "anonymize client ip addresses in all output") 445 | flag.BoolVar(&udp, "u", false, "UDP Relay") 446 | flag.StringVar(&managerAddr, "manager-address", "", "shadowsocks manager listening address") 447 | flag.Parse() 448 | 449 | if printVer { 450 | ss.PrintVersion() 451 | os.Exit(0) 452 | } 453 | 454 | ss.SetDebug(debug) 455 | 456 | var err error 457 | config, err = ss.ParseConfig(configFile) 458 | if err != nil { 459 | if !os.IsNotExist(err) { 460 | fmt.Fprintf(os.Stderr, "error reading %s: %v\n", configFile, err) 461 | os.Exit(1) 462 | } 463 | config = &cmdConfig 464 | ss.UpdateConfig(config, config) 465 | } else { 466 | ss.UpdateConfig(config, &cmdConfig) 467 | } 468 | if config.Method == "" { 469 | config.Method = "aes-256-cfb" 470 | } 471 | if err = ss.CheckCipherMethod(config.Method); err != nil { 472 | fmt.Fprintln(os.Stderr, err) 473 | os.Exit(1) 474 | } 475 | if err = unifyPortPassword(config); err != nil { 476 | os.Exit(1) 477 | } 478 | if core > 0 { 479 | runtime.GOMAXPROCS(core) 480 | } 481 | for port, password := range config.PortPassword { 482 | go run(port, password) 483 | if udp { 484 | go runUDP(port, password) 485 | } 486 | } 487 | 488 | if managerAddr != "" { 489 | addr, err := net.ResolveUDPAddr("udp", managerAddr) 490 | if err != nil { 491 | fmt.Fprintln(os.Stderr, "Can't resolve address: ", err) 492 | os.Exit(1) 493 | } 494 | conn, err := net.ListenUDP("udp", addr) 495 | if err != nil { 496 | fmt.Fprintln(os.Stderr, "Error listening:", err) 497 | os.Exit(1) 498 | } 499 | log.Printf("manager listening udp addr %v ...\n", managerAddr) 500 | defer conn.Close() 501 | go managerDaemon(conn) 502 | } 503 | 504 | waitSignal() 505 | } 506 | 507 | func managerDaemon(conn *net.UDPConn) { 508 | // add a report address set for ping response 509 | // according to https://github.com/shadowsocks/shadowsocks/wiki/Manage-Multiple-Users#example-code 510 | ctx := make(chan bool, 1) 511 | defer close(ctx) 512 | reportconnSet := make(map[string]*net.UDPAddr, 1024) 513 | go func() { 514 | timer := time.Tick(10 * time.Second) 515 | for { 516 | select { 517 | case <-ctx: 518 | return 519 | case <-timer: 520 | for _, addr := range reportconnSet { 521 | res := reportStat() 522 | if len(res) == 0 { 523 | continue 524 | } 525 | conn.WriteToUDP(res, addr) 526 | } 527 | } 528 | } 529 | }() 530 | 531 | for { 532 | data := make([]byte, 300) 533 | _, remote, err := conn.ReadFromUDP(data) 534 | if err != nil { 535 | fmt.Fprintln(os.Stderr, "Failed to read UDP manage msg, error: ", err.Error()) 536 | continue 537 | } 538 | command := string(data) 539 | var res []byte 540 | switch { 541 | case strings.HasPrefix(command, "add:"): 542 | res = handleAddPort(bytes.Trim(data[4:], "\x00\r\n ")) 543 | case strings.HasPrefix(command, "remove:"): 544 | res = handleRemovePort(bytes.Trim(data[7:], "\x00\r\n ")) 545 | case strings.HasPrefix(command, "ping"): 546 | conn.WriteToUDP(handlePing(), remote) 547 | reportconnSet[remote.String()] = remote // append the host into the report list 548 | case strings.HasPrefix(command, "ping-stop"): // add the stop ping command 549 | conn.WriteToUDP(handlePing(), remote) 550 | delete(reportconnSet, remote.String()) 551 | } 552 | if len(res) == 0 { 553 | continue 554 | } 555 | _, err = conn.WriteToUDP(res, remote) 556 | if err != nil { 557 | fmt.Fprintln(os.Stderr, "Failed to write UDP manage msg, error: ", err.Error()) 558 | continue 559 | } 560 | } 561 | } 562 | 563 | func handleAddPort(payload []byte) []byte { 564 | var params struct { 565 | ServerPort interface{} `json:"server_port"` // may be string or int 566 | Password string `json:"password"` 567 | } 568 | json.Unmarshal(payload, ¶ms) 569 | if params.ServerPort == nil || params.Password == "" { 570 | fmt.Fprintln(os.Stderr, "Failed to parse add req: ", string(payload)) 571 | return []byte("err") 572 | } 573 | port := parsePortNum(params.ServerPort) 574 | if port == "" { 575 | return []byte("err") 576 | } 577 | passwdManager.updatePortPasswd(port, params.Password) 578 | return []byte("ok") 579 | } 580 | 581 | func handleRemovePort(payload []byte) []byte { 582 | var params struct { 583 | ServerPort interface{} `json:"server_port"` // may be string or int 584 | } 585 | json.Unmarshal(payload, ¶ms) 586 | if params.ServerPort == nil { 587 | fmt.Fprintln(os.Stderr, "Failed to parse remove req: ", string(payload)) 588 | return []byte("err") 589 | } 590 | port := parsePortNum(params.ServerPort) 591 | if port == "" { 592 | return []byte("err") 593 | } 594 | log.Printf("closing port %s\n", port) 595 | passwdManager.del(port) 596 | return []byte("ok") 597 | } 598 | 599 | func handlePing() []byte { 600 | return []byte("pong") 601 | } 602 | 603 | // reportStat get the stat:trafficStat and return avery 10 sec as for the protocol 604 | // https://github.com/shadowsocks/shadowsocks/wiki/Manage-Multiple-Users 605 | func reportStat() []byte { 606 | stats := passwdManager.getTrafficStats() 607 | var buf bytes.Buffer 608 | buf.WriteString("stat: ") 609 | ret, _ := json.Marshal(stats) 610 | buf.Write(ret) 611 | return buf.Bytes() 612 | } 613 | 614 | func parsePortNum(in interface{}) string { 615 | var port string 616 | switch in.(type) { 617 | case string: 618 | // try to convert to number then convert back, to ensure valid value 619 | portNum, err := strconv.Atoi(in.(string)) 620 | if portNum == 0 || err != nil { 621 | return "" 622 | } 623 | port = strconv.Itoa(portNum) 624 | case float64: 625 | port = strconv.Itoa(int(in.(float64))) 626 | default: 627 | return "" 628 | } 629 | return port 630 | } 631 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "server":"127.0.0.1", 3 | "server_port":8388, 4 | "local_port":1080, 5 | "local_address":"127.0.0.1", 6 | "password":"barfoo!", 7 | "method": "aes-128-cfb", 8 | "timeout":600 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 | -------------------------------------------------------------------------------- /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 | ["[::1]:8389", "foobarfoo", "aes-128-cfb"] 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /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 darwin amd64 mac64 server 53 | build linux amd64 linux64 server 54 | build linux 386 linux32 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/shadowsocks/shadowsocks-go/3e585ff90601765510d31ee1d05b6f63548c7d44/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 -m 5 --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 rc4-md5 105 | test_shadowsocks $url aes-128-cfb 106 | #test_shadowsocks $url aes-192-cfb 107 | #test_shadowsocks $url aes-256-cfb 108 | test_shadowsocks $url bf-cfb 109 | test_shadowsocks $url des-cfb 110 | test_shadowsocks $url cast5-cfb 111 | test_shadowsocks $url chacha20 112 | test_shadowsocks $url salsa20 113 | } 114 | 115 | start_http_server 116 | 117 | SERVER="shadowsocks-server" 118 | LOCAL="shadowsocks-local" 119 | test_server_local_pair 120 | 121 | if [[ -n $SS_PYTHON ]]; then 122 | SERVER="$SS_PYTHON/server.py --forbidden-ip=" 123 | LOCAL="shadowsocks-local" 124 | test_server_local_pair 125 | 126 | SERVER="shadowsocks-server" 127 | LOCAL="$SS_PYTHON/local.py" 128 | test_server_local_pair 129 | fi 130 | 131 | stop_http_server 132 | -------------------------------------------------------------------------------- /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 | LocalAddress string `json:"local_address"` 25 | Password string `json:"password"` 26 | Method string `json:"method"` // encryption method 27 | 28 | // following options are only used by server 29 | PortPassword map[string]string `json:"port_password"` 30 | Timeout int `json:"timeout"` 31 | 32 | // following options are only used by client 33 | 34 | // The order of servers in the client config is significant, so use array 35 | // instead of map to preserve the order. 36 | ServerPassword [][]string `json:"server_password"` 37 | } 38 | 39 | var readTimeout time.Duration 40 | 41 | func (config *Config) GetServerArray() []string { 42 | // Specifying multiple servers in the "server" options is deprecated. 43 | // But for backward compatibility, keep this. 44 | if config.Server == nil { 45 | return nil 46 | } 47 | single, ok := config.Server.(string) 48 | if ok { 49 | return []string{single} 50 | } 51 | arr, ok := config.Server.([]interface{}) 52 | if ok { 53 | /* 54 | if len(arr) > 1 { 55 | log.Println("Multiple servers in \"server\" option is deprecated. " + 56 | "Please use \"server_password\" instead.") 57 | } 58 | */ 59 | serverArr := make([]string, len(arr), len(arr)) 60 | for i, s := range arr { 61 | serverArr[i], ok = s.(string) 62 | if !ok { 63 | goto typeError 64 | } 65 | } 66 | return serverArr 67 | } 68 | typeError: 69 | panic(fmt.Sprintf("Config.Server type error %v", reflect.TypeOf(config.Server))) 70 | } 71 | 72 | func ParseConfig(path string) (config *Config, err error) { 73 | file, err := os.Open(path) // For read access. 74 | if err != nil { 75 | return 76 | } 77 | defer file.Close() 78 | 79 | data, err := ioutil.ReadAll(file) 80 | if err != nil { 81 | return 82 | } 83 | 84 | config = &Config{} 85 | if err = json.Unmarshal(data, config); err != nil { 86 | return nil, err 87 | } 88 | readTimeout = time.Duration(config.Timeout) * time.Second 89 | return 90 | } 91 | 92 | func SetDebug(d DebugLog) { 93 | Debug = d 94 | } 95 | 96 | // UpdateConfig: Useful for command line to override options specified in config file 97 | // Debug is not updated. 98 | func UpdateConfig(old, new *Config) { 99 | // Using reflection here is not necessary, but it's a good exercise. 100 | // For more information on reflections in Go, read "The Laws of Reflection" 101 | // http://golang.org/doc/articles/laws_of_reflection.html 102 | newVal := reflect.ValueOf(new).Elem() 103 | oldVal := reflect.ValueOf(old).Elem() 104 | 105 | // typeOfT := newVal.Type() 106 | for i := 0; i < newVal.NumField(); i++ { 107 | newField := newVal.Field(i) 108 | oldField := oldVal.Field(i) 109 | // log.Printf("%d: %s %s = %v\n", i, 110 | // typeOfT.Field(i).Name, newField.Type(), newField.Interface()) 111 | switch newField.Kind() { 112 | case reflect.Interface: 113 | if fmt.Sprintf("%v", newField.Interface()) != "" { 114 | oldField.Set(newField) 115 | } 116 | case reflect.String: 117 | s := newField.String() 118 | if s != "" { 119 | oldField.SetString(s) 120 | } 121 | case reflect.Int: 122 | i := newField.Int() 123 | if i != 0 { 124 | oldField.SetInt(i) 125 | } 126 | } 127 | } 128 | 129 | old.Timeout = new.Timeout 130 | readTimeout = time.Duration(old.Timeout) * time.Second 131 | } 132 | -------------------------------------------------------------------------------- /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 | const ( 12 | AddrMask byte = 0xf 13 | ) 14 | 15 | type Conn struct { 16 | net.Conn 17 | *Cipher 18 | readBuf []byte 19 | writeBuf []byte 20 | } 21 | 22 | func NewConn(c net.Conn, cipher *Cipher) *Conn { 23 | return &Conn{ 24 | Conn: c, 25 | Cipher: cipher, 26 | readBuf: leakyBuf.Get(), 27 | writeBuf: leakyBuf.Get()} 28 | } 29 | 30 | func (c *Conn) Close() error { 31 | leakyBuf.Put(c.readBuf) 32 | leakyBuf.Put(c.writeBuf) 33 | return c.Conn.Close() 34 | } 35 | 36 | func RawAddr(addr string) (buf []byte, err error) { 37 | host, portStr, err := net.SplitHostPort(addr) 38 | if err != nil { 39 | return nil, fmt.Errorf("shadowsocks: address error %s %v", addr, err) 40 | } 41 | port, err := strconv.Atoi(portStr) 42 | if err != nil { 43 | return nil, fmt.Errorf("shadowsocks: invalid port %s", addr) 44 | } 45 | 46 | hostLen := len(host) 47 | l := 1 + 1 + hostLen + 2 // addrType + lenByte + address + port 48 | buf = make([]byte, l) 49 | buf[0] = 3 // 3 means the address is domain name 50 | buf[1] = byte(hostLen) // host address length followed by host address 51 | copy(buf[2:], host) 52 | binary.BigEndian.PutUint16(buf[2+hostLen:2+hostLen+2], uint16(port)) 53 | return 54 | } 55 | 56 | // DialWithRawAddr is intended for use by users implementing a local socks proxy. 57 | // rawaddr shoud contain part of the data in socks request, starting from the 58 | // ATYP field. (Refer to rfc1928 for more information.) 59 | func DialWithRawAddr(rawaddr []byte, server string, cipher *Cipher) (c *Conn, err error) { 60 | conn, err := net.Dial("tcp", server) 61 | if err != nil { 62 | return 63 | } 64 | c = NewConn(conn, cipher) 65 | if _, err = c.Write(rawaddr); err != nil { 66 | c.Close() 67 | return nil, err 68 | } 69 | return 70 | } 71 | 72 | // Dial: addr should be in the form of host:port 73 | func Dial(addr, server string, cipher *Cipher) (c *Conn, err error) { 74 | ra, err := RawAddr(addr) 75 | if err != nil { 76 | return 77 | } 78 | return DialWithRawAddr(ra, server, cipher) 79 | } 80 | 81 | func (c *Conn) Read(b []byte) (n int, err error) { 82 | if c.dec == nil { 83 | iv := make([]byte, c.info.ivLen) 84 | if _, err = io.ReadFull(c.Conn, iv); err != nil { 85 | return 86 | } 87 | if err = c.initDecrypt(iv); err != nil { 88 | return 89 | } 90 | } 91 | 92 | cipherData := c.readBuf 93 | if len(b) > len(cipherData) { 94 | cipherData = make([]byte, len(b)) 95 | } else { 96 | cipherData = cipherData[:len(b)] 97 | } 98 | 99 | n, err = c.Conn.Read(cipherData) 100 | if n > 0 { 101 | c.decrypt(b[0:n], cipherData[0:n]) 102 | } 103 | return 104 | } 105 | 106 | func (c *Conn) Write(b []byte) (n int, err error) { 107 | var iv []byte 108 | if c.enc == nil { 109 | iv, err = c.initEncrypt() 110 | if err != nil { 111 | return 112 | } 113 | } 114 | 115 | cipherData := c.writeBuf 116 | dataSize := len(b) + len(iv) 117 | if dataSize > len(cipherData) { 118 | cipherData = make([]byte, dataSize) 119 | } else { 120 | cipherData = cipherData[:dataSize] 121 | } 122 | 123 | if iv != nil { 124 | // Put initialization vector in buffer, do a single write to send both 125 | // iv and data. 126 | copy(cipherData, iv) 127 | } 128 | 129 | c.encrypt(cipherData[len(iv):], b) 130 | n, err = c.Conn.Write(cipherData) 131 | return 132 | } 133 | -------------------------------------------------------------------------------- /shadowsocks/conn_test.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "net" 7 | "testing" 8 | ) 9 | 10 | func mustNewCipher(method string) *Cipher { 11 | const testPassword = "password" 12 | cipher, err := NewCipher(method, testPassword) 13 | if err != nil { 14 | panic(err) 15 | } 16 | return cipher 17 | } 18 | 19 | type transcriptConn struct { 20 | net.Conn 21 | ReadTranscript []byte 22 | } 23 | 24 | func (conn *transcriptConn) Read(p []byte) (int, error) { 25 | n, err := conn.Conn.Read(p) 26 | conn.ReadTranscript = append(conn.ReadTranscript, p[:n]...) 27 | return n, err 28 | } 29 | 30 | func connIVs(method string) (clientIV, serverIV []byte, err error) { 31 | // underlying network connection 32 | clientConn, serverConn := net.Pipe() 33 | // make a transcript of bytes at the network level 34 | clientTranscriptConn := &transcriptConn{Conn: clientConn} 35 | serverTranscriptConn := &transcriptConn{Conn: serverConn} 36 | // connection at the ShadowSocks level 37 | clientSSConn := NewConn(clientTranscriptConn, mustNewCipher(method)) 38 | serverSSConn := NewConn(serverTranscriptConn, mustNewCipher(method)) 39 | 40 | clientToServerData := []byte("clientToServerData") 41 | serverToClientData := []byte("serverToClientData") 42 | 43 | go func() { 44 | defer serverSSConn.Close() 45 | buf := make([]byte, len(clientToServerData)) 46 | // read the client IV 47 | _, err := io.ReadFull(serverSSConn, buf) 48 | if err != nil { 49 | return 50 | } 51 | // send the server IV 52 | _, err = serverSSConn.Write(serverToClientData) 53 | if err != nil { 54 | return 55 | } 56 | }() 57 | 58 | // send the client IV 59 | _, err = clientSSConn.Write(clientToServerData) 60 | if err != nil { 61 | return 62 | } 63 | // read the server IV 64 | buf := make([]byte, len(serverToClientData)) 65 | _, err = io.ReadFull(clientSSConn, buf) 66 | if err != nil { 67 | return 68 | } 69 | 70 | // pull the IVs out of the network transcripts 71 | clientIV = serverTranscriptConn.ReadTranscript[:clientSSConn.Cipher.info.ivLen] 72 | serverIV = clientTranscriptConn.ReadTranscript[:serverSSConn.Cipher.info.ivLen] 73 | 74 | return 75 | } 76 | 77 | func TestIndependentIVs(t *testing.T) { 78 | for method := range cipherMethod { 79 | clientIV, serverIV, err := connIVs(method) 80 | if err != nil { 81 | t.Errorf("%s connection error: %s", method, err) 82 | continue 83 | } 84 | if bytes.Equal(clientIV, serverIV) { 85 | t.Errorf("%s equal client and server IVs", method) 86 | continue 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /shadowsocks/encrypt.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "crypto/des" 7 | "crypto/md5" 8 | "crypto/rand" 9 | "crypto/rc4" 10 | "encoding/binary" 11 | "errors" 12 | "io" 13 | 14 | "github.com/aead/chacha20" 15 | "golang.org/x/crypto/blowfish" 16 | "golang.org/x/crypto/cast5" 17 | "golang.org/x/crypto/salsa20/salsa" 18 | ) 19 | 20 | var errEmptyPassword = errors.New("empty key") 21 | 22 | func md5sum(d []byte) []byte { 23 | h := md5.New() 24 | h.Write(d) 25 | return h.Sum(nil) 26 | } 27 | 28 | func evpBytesToKey(password string, keyLen int) (key []byte) { 29 | const md5Len = 16 30 | 31 | cnt := (keyLen-1)/md5Len + 1 32 | m := make([]byte, cnt*md5Len) 33 | copy(m, md5sum([]byte(password))) 34 | 35 | // Repeatedly call md5 until bytes generated is enough. 36 | // Each call to md5 uses data: prev md5 sum + password. 37 | d := make([]byte, md5Len+len(password)) 38 | start := 0 39 | for i := 1; i < cnt; i++ { 40 | start += md5Len 41 | copy(d, m[start-md5Len:start]) 42 | copy(d[md5Len:], password) 43 | copy(m[start:], md5sum(d)) 44 | } 45 | return m[:keyLen] 46 | } 47 | 48 | type DecOrEnc int 49 | 50 | const ( 51 | Decrypt DecOrEnc = iota 52 | Encrypt 53 | ) 54 | 55 | func newStream(block cipher.Block, err error, key, iv []byte, 56 | doe DecOrEnc) (cipher.Stream, error) { 57 | if err != nil { 58 | return nil, err 59 | } 60 | if doe == Encrypt { 61 | return cipher.NewCFBEncrypter(block, iv), nil 62 | } else { 63 | return cipher.NewCFBDecrypter(block, iv), nil 64 | } 65 | } 66 | 67 | func newAESCFBStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { 68 | block, err := aes.NewCipher(key) 69 | return newStream(block, err, key, iv, doe) 70 | } 71 | 72 | func newAESCTRStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { 73 | block, err := aes.NewCipher(key) 74 | if err != nil { 75 | return nil, err 76 | } 77 | return cipher.NewCTR(block, iv), nil 78 | } 79 | 80 | func newDESStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { 81 | block, err := des.NewCipher(key) 82 | return newStream(block, err, key, iv, doe) 83 | } 84 | 85 | func newBlowFishStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { 86 | block, err := blowfish.NewCipher(key) 87 | return newStream(block, err, key, iv, doe) 88 | } 89 | 90 | func newCast5Stream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { 91 | block, err := cast5.NewCipher(key) 92 | return newStream(block, err, key, iv, doe) 93 | } 94 | 95 | func newRC4MD5Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { 96 | h := md5.New() 97 | h.Write(key) 98 | h.Write(iv) 99 | rc4key := h.Sum(nil) 100 | 101 | return rc4.NewCipher(rc4key) 102 | } 103 | 104 | func newChaCha20Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { 105 | return chacha20.NewCipher(iv, key) 106 | } 107 | 108 | func newChaCha20IETFStream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { 109 | return chacha20.NewCipher(iv, key) 110 | } 111 | 112 | type salsaStreamCipher struct { 113 | nonce [8]byte 114 | key [32]byte 115 | counter int 116 | } 117 | 118 | func (c *salsaStreamCipher) XORKeyStream(dst, src []byte) { 119 | var buf []byte 120 | padLen := c.counter % 64 121 | dataSize := len(src) + padLen 122 | if cap(dst) >= dataSize { 123 | buf = dst[:dataSize] 124 | } else if leakyBufSize >= dataSize { 125 | buf = leakyBuf.Get() 126 | defer leakyBuf.Put(buf) 127 | buf = buf[:dataSize] 128 | } else { 129 | buf = make([]byte, dataSize) 130 | } 131 | 132 | var subNonce [16]byte 133 | copy(subNonce[:], c.nonce[:]) 134 | binary.LittleEndian.PutUint64(subNonce[len(c.nonce):], uint64(c.counter/64)) 135 | 136 | // It's difficult to avoid data copy here. src or dst maybe slice from 137 | // Conn.Read/Write, which can't have padding. 138 | copy(buf[padLen:], src[:]) 139 | salsa.XORKeyStream(buf, buf, &subNonce, &c.key) 140 | copy(dst, buf[padLen:]) 141 | 142 | c.counter += len(src) 143 | } 144 | 145 | func newSalsa20Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { 146 | var c salsaStreamCipher 147 | copy(c.nonce[:], iv[:8]) 148 | copy(c.key[:], key[:32]) 149 | return &c, nil 150 | } 151 | 152 | type cipherInfo struct { 153 | keyLen int 154 | ivLen int 155 | newStream func(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) 156 | } 157 | 158 | var cipherMethod = map[string]*cipherInfo{ 159 | "aes-128-cfb": {16, 16, newAESCFBStream}, 160 | "aes-192-cfb": {24, 16, newAESCFBStream}, 161 | "aes-256-cfb": {32, 16, newAESCFBStream}, 162 | "aes-128-ctr": {16, 16, newAESCTRStream}, 163 | "aes-192-ctr": {24, 16, newAESCTRStream}, 164 | "aes-256-ctr": {32, 16, newAESCTRStream}, 165 | "des-cfb": {8, 8, newDESStream}, 166 | "bf-cfb": {16, 8, newBlowFishStream}, 167 | "cast5-cfb": {16, 8, newCast5Stream}, 168 | "rc4-md5": {16, 16, newRC4MD5Stream}, 169 | "rc4-md5-6": {16, 6, newRC4MD5Stream}, 170 | "chacha20": {32, 8, newChaCha20Stream}, 171 | "chacha20-ietf": {32, 12, newChaCha20IETFStream}, 172 | "salsa20": {32, 8, newSalsa20Stream}, 173 | } 174 | 175 | func CheckCipherMethod(method string) error { 176 | if method == "" { 177 | method = "aes-256-cfb" 178 | } 179 | _, ok := cipherMethod[method] 180 | if !ok { 181 | return errors.New("Unsupported encryption method: " + method) 182 | } 183 | return nil 184 | } 185 | 186 | type Cipher struct { 187 | enc cipher.Stream 188 | dec cipher.Stream 189 | key []byte 190 | info *cipherInfo 191 | } 192 | 193 | // NewCipher creates a cipher that can be used in Dial() etc. 194 | // Use cipher.Copy() to create a new cipher with the same method and password 195 | // to avoid the cost of repeated cipher initialization. 196 | func NewCipher(method, password string) (c *Cipher, err error) { 197 | if password == "" { 198 | return nil, errEmptyPassword 199 | } 200 | mi, ok := cipherMethod[method] 201 | if !ok { 202 | return nil, errors.New("Unsupported encryption method: " + method) 203 | } 204 | 205 | key := evpBytesToKey(password, mi.keyLen) 206 | 207 | c = &Cipher{key: key, info: mi} 208 | 209 | if err != nil { 210 | return nil, err 211 | } 212 | return c, nil 213 | } 214 | 215 | // Initializes the block cipher with CFB mode, returns IV. 216 | func (c *Cipher) initEncrypt() (iv []byte, err error) { 217 | iv = make([]byte, c.info.ivLen) 218 | if _, err := io.ReadFull(rand.Reader, iv); err != nil { 219 | return nil, err 220 | } 221 | c.enc, err = c.info.newStream(c.key, iv, Encrypt) 222 | return 223 | } 224 | 225 | func (c *Cipher) initDecrypt(iv []byte) (err error) { 226 | c.dec, err = c.info.newStream(c.key, iv, Decrypt) 227 | return 228 | } 229 | 230 | func (c *Cipher) encrypt(dst, src []byte) { 231 | c.enc.XORKeyStream(dst, src) 232 | } 233 | 234 | func (c *Cipher) decrypt(dst, src []byte) { 235 | c.dec.XORKeyStream(dst, src) 236 | } 237 | 238 | // Copy creates a new cipher at it's initial state. 239 | func (c *Cipher) Copy() *Cipher { 240 | // This optimization maybe not necessary. But without this function, we 241 | // need to maintain a table cache for newTableCipher and use lock to 242 | // protect concurrent access to that cache. 243 | 244 | // AES and DES ciphers does not return specific types, so it's difficult 245 | // to create copy. But their initizliation time is less than 4000ns on my 246 | // 2.26 GHz Intel Core 2 Duo processor. So no need to worry. 247 | 248 | // Currently, blow-fish and cast5 initialization cost is an order of 249 | // maganitude slower than other ciphers. (I'm not sure whether this is 250 | // because the current implementation is not highly optimized, or this is 251 | // the nature of the algorithm.) 252 | 253 | nc := *c 254 | nc.enc = nil 255 | nc.dec = nil 256 | return &nc 257 | } 258 | -------------------------------------------------------------------------------- /shadowsocks/encrypt_test.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | "crypto/rand" 5 | "io" 6 | "reflect" 7 | "testing" 8 | ) 9 | 10 | const text = "Don't tell me the moon is shining; show me the glint of light on broken glass." 11 | 12 | func testCipher(t *testing.T, c *Cipher, msg string) { 13 | n := len(text) 14 | cipherBuf := make([]byte, n) 15 | originTxt := make([]byte, n) 16 | 17 | c.encrypt(cipherBuf, []byte(text)) 18 | c.decrypt(originTxt, cipherBuf) 19 | 20 | if string(originTxt) != text { 21 | t.Error(msg, "encrypt then decrytp does not get original text") 22 | } 23 | } 24 | 25 | func TestEvpBytesToKey(t *testing.T) { 26 | // key, iv := evpBytesToKey("foobar", 32, 16) 27 | key := evpBytesToKey("foobar", 32) 28 | 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} 29 | // ivTarget := []byte{0x0e, 0xbf, 0x58, 0x78, 0xe8, 0x2a, 0xf7, 0xda, 0x61, 0x8e, 0xd5, 0x6f, 0xc6, 0x7d, 0x4a, 0xb7} 30 | if !reflect.DeepEqual(key, keyTarget) { 31 | t.Errorf("key not correct\n\texpect: %v\n\tgot: %v\n", keyTarget, key) 32 | } 33 | // if !reflect.DeepEqual(iv, ivTarget) { 34 | // t.Errorf("iv not correct\n\texpect: %v\n\tgot: %v\n", ivTarget, iv) 35 | // } 36 | } 37 | 38 | func testBlockCipher(t *testing.T, method string) { 39 | var cipher *Cipher 40 | var err error 41 | 42 | cipher, err = NewCipher(method, "foobar") 43 | if err != nil { 44 | t.Fatal(method, "NewCipher:", err) 45 | } 46 | cipherCopy := cipher.Copy() 47 | iv, err := cipher.initEncrypt() 48 | if err != nil { 49 | t.Error(method, "initEncrypt:", err) 50 | } 51 | if err = cipher.initDecrypt(iv); err != nil { 52 | t.Error(method, "initDecrypt:", err) 53 | } 54 | testCipher(t, cipher, method) 55 | 56 | iv, err = cipherCopy.initEncrypt() 57 | if err != nil { 58 | t.Error(method, "copy initEncrypt:", err) 59 | } 60 | if err = cipherCopy.initDecrypt(iv); err != nil { 61 | t.Error(method, "copy initDecrypt:", err) 62 | } 63 | testCipher(t, cipherCopy, method+" copy") 64 | } 65 | 66 | func TestAES128CFB(t *testing.T) { 67 | testBlockCipher(t, "aes-128-cfb") 68 | } 69 | 70 | func TestAES192CFB(t *testing.T) { 71 | testBlockCipher(t, "aes-192-cfb") 72 | } 73 | 74 | func TestAES256CFB(t *testing.T) { 75 | testBlockCipher(t, "aes-256-cfb") 76 | } 77 | 78 | func TestAES128CTR(t *testing.T) { 79 | testBlockCipher(t, "aes-128-ctr") 80 | } 81 | 82 | func TestAES192CTR(t *testing.T) { 83 | testBlockCipher(t, "aes-192-ctr") 84 | } 85 | 86 | func TestAES256CTR(t *testing.T) { 87 | testBlockCipher(t, "aes-256-ctr") 88 | } 89 | 90 | func TestDES(t *testing.T) { 91 | testBlockCipher(t, "des-cfb") 92 | } 93 | 94 | func TestRC4MD5(t *testing.T) { 95 | testBlockCipher(t, "rc4-md5") 96 | } 97 | 98 | func TestRC4MD56(t *testing.T) { 99 | testBlockCipher(t, "rc4-md5-6") 100 | } 101 | 102 | func TestChaCha20(t *testing.T) { 103 | testBlockCipher(t, "chacha20") 104 | } 105 | 106 | func TestChaCha20IETF(t *testing.T) { 107 | testBlockCipher(t, "chacha20-ietf") 108 | } 109 | 110 | var cipherKey = make([]byte, 64) 111 | var cipherIv = make([]byte, 64) 112 | 113 | const CIPHER_BENCHMARK_BUFFER_LEN = 4096 114 | 115 | func init() { 116 | for i := 0; i < len(cipherKey); i++ { 117 | cipherKey[i] = byte(i) 118 | } 119 | io.ReadFull(rand.Reader, cipherIv) 120 | } 121 | 122 | func benchmarkCipherInit(b *testing.B, method string) { 123 | ci := cipherMethod[method] 124 | key := cipherKey[:ci.keyLen] 125 | buf := make([]byte, ci.ivLen) 126 | for i := 0; i < b.N; i++ { 127 | ci.newStream(key, buf, Encrypt) 128 | } 129 | } 130 | 131 | func BenchmarkAES128CFBInit(b *testing.B) { 132 | benchmarkCipherInit(b, "aes-128-cfb") 133 | } 134 | 135 | func BenchmarkAES19CFB2Init(b *testing.B) { 136 | benchmarkCipherInit(b, "aes-192-cfb") 137 | } 138 | 139 | func BenchmarkAES256CFBInit(b *testing.B) { 140 | benchmarkCipherInit(b, "aes-256-cfb") 141 | } 142 | 143 | func BenchmarkAES128CTRInit(b *testing.B) { 144 | benchmarkCipherInit(b, "aes-128-ctr") 145 | } 146 | 147 | func BenchmarkAES192CTRInit(b *testing.B) { 148 | benchmarkCipherInit(b, "aes-192-ctr") 149 | } 150 | 151 | func BenchmarkAES256CTRInit(b *testing.B) { 152 | benchmarkCipherInit(b, "aes-256-ctr") 153 | } 154 | 155 | func BenchmarkBlowFishInit(b *testing.B) { 156 | benchmarkCipherInit(b, "bf-cfb") 157 | } 158 | 159 | func BenchmarkCast5Init(b *testing.B) { 160 | benchmarkCipherInit(b, "cast5-cfb") 161 | } 162 | 163 | func BenchmarkDESInit(b *testing.B) { 164 | benchmarkCipherInit(b, "des-cfb") 165 | } 166 | 167 | func BenchmarkRC4MD5Init(b *testing.B) { 168 | benchmarkCipherInit(b, "rc4-md5") 169 | } 170 | 171 | func BenchmarkRC4MD56Init(b *testing.B) { 172 | benchmarkCipherInit(b, "rc4-md5-5") 173 | } 174 | 175 | func BenchmarkChaCha20Init(b *testing.B) { 176 | benchmarkCipherInit(b, "chacha20") 177 | } 178 | 179 | func BenchmarkChaCha20IETFInit(b *testing.B) { 180 | benchmarkCipherInit(b, "chacha20-ietf") 181 | } 182 | 183 | func BenchmarkSalsa20Init(b *testing.B) { 184 | benchmarkCipherInit(b, "salsa20") 185 | } 186 | 187 | func benchmarkCipherEncrypt(b *testing.B, method string) { 188 | ci := cipherMethod[method] 189 | key := cipherKey[:ci.keyLen] 190 | iv := cipherIv[:ci.ivLen] 191 | enc, err := ci.newStream(key, iv, Encrypt) 192 | if err != nil { 193 | b.Error(err) 194 | } 195 | src := make([]byte, CIPHER_BENCHMARK_BUFFER_LEN) 196 | dst := make([]byte, CIPHER_BENCHMARK_BUFFER_LEN) 197 | io.ReadFull(rand.Reader, src) 198 | for i := 0; i < b.N; i++ { 199 | enc.XORKeyStream(dst, src) 200 | } 201 | } 202 | 203 | func BenchmarkAES128CFBEncrypt(b *testing.B) { 204 | benchmarkCipherEncrypt(b, "aes-128-cfb") 205 | } 206 | 207 | func BenchmarkAES192CFBEncrypt(b *testing.B) { 208 | benchmarkCipherEncrypt(b, "aes-192-cfb") 209 | } 210 | 211 | func BenchmarkAES256CFBEncrypt(b *testing.B) { 212 | benchmarkCipherEncrypt(b, "aes-256-cfb") 213 | } 214 | 215 | func BenchmarkAES128CTREncrypt(b *testing.B) { 216 | benchmarkCipherEncrypt(b, "aes-128-ctr") 217 | } 218 | 219 | func BenchmarkAES192CTREncrypt(b *testing.B) { 220 | benchmarkCipherEncrypt(b, "aes-192-ctr") 221 | } 222 | 223 | func BenchmarkAES256CTREncrypt(b *testing.B) { 224 | benchmarkCipherEncrypt(b, "aes-256-ctr") 225 | } 226 | 227 | func BenchmarkBlowFishEncrypt(b *testing.B) { 228 | benchmarkCipherEncrypt(b, "bf-cfb") 229 | } 230 | 231 | func BenchmarkCast5Encrypt(b *testing.B) { 232 | benchmarkCipherEncrypt(b, "cast5-cfb") 233 | } 234 | 235 | func BenchmarkDESEncrypt(b *testing.B) { 236 | benchmarkCipherEncrypt(b, "des-cfb") 237 | } 238 | 239 | func BenchmarkRC4MD5Encrypt(b *testing.B) { 240 | benchmarkCipherEncrypt(b, "rc4-md5") 241 | } 242 | 243 | func BenchmarkRC4MD56Encrypt(b *testing.B) { 244 | benchmarkCipherEncrypt(b, "rc4-md5-6") 245 | } 246 | 247 | func BenchmarkChacha20Encrypt(b *testing.B) { 248 | benchmarkCipherEncrypt(b, "chacha20") 249 | } 250 | 251 | func BenchmarkChacha20IETFEncrypt(b *testing.B) { 252 | benchmarkCipherEncrypt(b, "chacha20-ietf") 253 | } 254 | 255 | func BenchmarkSalsa20Encrypt(b *testing.B) { 256 | benchmarkCipherEncrypt(b, "salsa20") 257 | } 258 | 259 | func benchmarkCipherDecrypt(b *testing.B, method string) { 260 | ci := cipherMethod[method] 261 | key := cipherKey[:ci.keyLen] 262 | iv := cipherIv[:ci.ivLen] 263 | enc, err := ci.newStream(key, iv, Encrypt) 264 | if err != nil { 265 | b.Error(err) 266 | } 267 | dec, err := ci.newStream(key, iv, Decrypt) 268 | if err != nil { 269 | b.Error(err) 270 | } 271 | src := make([]byte, CIPHER_BENCHMARK_BUFFER_LEN) 272 | dst := make([]byte, CIPHER_BENCHMARK_BUFFER_LEN) 273 | io.ReadFull(rand.Reader, src) 274 | enc.XORKeyStream(dst, src) 275 | for i := 0; i < b.N; i++ { 276 | dec.XORKeyStream(src, dst) 277 | } 278 | } 279 | 280 | func BenchmarkAES128CFBDecrypt(b *testing.B) { 281 | benchmarkCipherDecrypt(b, "aes-128-cfb") 282 | } 283 | 284 | func BenchmarkAES192CFBDecrypt(b *testing.B) { 285 | benchmarkCipherDecrypt(b, "aes-192-cfb") 286 | } 287 | 288 | func BenchmarkAES256CFBDecrypt(b *testing.B) { 289 | benchmarkCipherDecrypt(b, "aes-256-cfb") 290 | } 291 | 292 | func BenchmarkAES128CTRDecrypt(b *testing.B) { 293 | benchmarkCipherDecrypt(b, "aes-128-ctr") 294 | } 295 | 296 | func BenchmarkAES192CTRDecrypt(b *testing.B) { 297 | benchmarkCipherDecrypt(b, "aes-192-ctr") 298 | } 299 | 300 | func BenchmarkAES256CTRDecrypt(b *testing.B) { 301 | benchmarkCipherDecrypt(b, "aes-256-ctr") 302 | } 303 | 304 | func BenchmarkBlowFishDecrypt(b *testing.B) { 305 | benchmarkCipherDecrypt(b, "bf-cfb") 306 | } 307 | 308 | func BenchmarkCast5Decrypt(b *testing.B) { 309 | benchmarkCipherDecrypt(b, "cast5-cfb") 310 | } 311 | 312 | func BenchmarkDESDecrypt(b *testing.B) { 313 | benchmarkCipherDecrypt(b, "des-cfb") 314 | } 315 | 316 | func BenchmarkRC4MD5Decrypt(b *testing.B) { 317 | benchmarkCipherDecrypt(b, "rc4-md5") 318 | } 319 | 320 | func BenchmarkRC4MD56Decrypt(b *testing.B) { 321 | benchmarkCipherDecrypt(b, "rc4-md5-6") 322 | } 323 | 324 | func BenchmarkChaCha20Decrypt(b *testing.B) { 325 | benchmarkCipherDecrypt(b, "chacha20") 326 | } 327 | 328 | func BenchmarkChaCha20IETFDecrypt(b *testing.B) { 329 | benchmarkCipherDecrypt(b, "chacha20-ietf") 330 | } 331 | 332 | func BenchmarkSalsa20Decrypt(b *testing.B) { 333 | benchmarkCipherDecrypt(b, "salsa20") 334 | } 335 | -------------------------------------------------------------------------------- /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 = 4108 // data.len(2) + hmacsha1(10) + data(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/pipe.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | "net" 5 | "time" 6 | ) 7 | 8 | func SetReadTimeout(c net.Conn) { 9 | if readTimeout != 0 { 10 | c.SetReadDeadline(time.Now().Add(readTimeout)) 11 | } 12 | } 13 | 14 | // PipeThenClose copies data from src to dst, closes dst when done. 15 | func PipeThenClose(src, dst net.Conn, addTraffic func(int)) { 16 | defer dst.Close() 17 | buf := leakyBuf.Get() 18 | defer leakyBuf.Put(buf) 19 | for { 20 | SetReadTimeout(src) 21 | n, err := src.Read(buf) 22 | if addTraffic != nil { 23 | addTraffic(n) 24 | } 25 | // read may return EOF with n > 0 26 | // should always process n > 0 bytes before handling error 27 | if n > 0 { 28 | // Note: avoid overwrite err returned by Read. 29 | if _, err := dst.Write(buf[0:n]); err != nil { 30 | Debug.Println("write:", err) 31 | break 32 | } 33 | } 34 | if err != nil { 35 | // Always "use of closed network connection", but no easy way to 36 | // identify this specific error. So just leave the error along for now. 37 | // More info here: https://code.google.com/p/go/issues/detail?id=4373 38 | /* 39 | if bool(Debug) && err != io.EOF { 40 | Debug.Println("read:", err) 41 | } 42 | */ 43 | break 44 | } 45 | } 46 | return 47 | } 48 | -------------------------------------------------------------------------------- /shadowsocks/proxy.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | "fmt" 7 | "net" 8 | "time" 9 | ) 10 | 11 | type Dialer struct { 12 | cipher *Cipher 13 | server string 14 | support_udp bool 15 | } 16 | 17 | type ProxyConn struct { 18 | *Conn 19 | raddr *ProxyAddr 20 | } 21 | 22 | type ProxyAddr struct { 23 | network string 24 | address string 25 | } 26 | 27 | var ErrNilCipher = errors.New("cipher can't be nil") 28 | 29 | func NewDialer(server string, cipher *Cipher) (dialer *Dialer, err error) { 30 | // Currently shadowsocks-go do not support UDP 31 | if cipher == nil { 32 | return nil, ErrNilCipher 33 | } 34 | return &Dialer { 35 | cipher: cipher, 36 | server: server, 37 | support_udp: false, 38 | }, nil 39 | } 40 | 41 | func (d *Dialer) Dial(network, addr string) (c net.Conn, err error) { 42 | if strings.HasPrefix(network, "tcp") { 43 | conn, err := Dial(addr, d.server, d.cipher.Copy()) 44 | if err != nil { 45 | return nil, err 46 | } 47 | return &ProxyConn { 48 | Conn: conn, 49 | raddr: &ProxyAddr { 50 | network: network, 51 | address: addr, 52 | }, 53 | }, nil 54 | } 55 | return nil, fmt.Errorf("unsupported connection type: %s", network) 56 | } 57 | 58 | func (c *ProxyConn) LocalAddr() net.Addr { 59 | return c.Conn.LocalAddr() 60 | } 61 | 62 | func (c *ProxyConn) RemoteAddr() net.Addr { 63 | return c.raddr 64 | } 65 | 66 | func (c *ProxyConn) SetDeadline(t time.Time) error { 67 | return c.Conn.SetDeadline(t) 68 | } 69 | 70 | func (c *ProxyConn) SetReadDeadline(t time.Time) error { 71 | return c.Conn.SetReadDeadline(t) 72 | } 73 | 74 | func (c *ProxyConn) SetWriteDeadline(t time.Time) error { 75 | return c.Conn.SetWriteDeadline(t) 76 | } 77 | 78 | func (a *ProxyAddr) Network() string { 79 | return a.network 80 | } 81 | 82 | func (a *ProxyAddr) String() string { 83 | return a.address 84 | } 85 | -------------------------------------------------------------------------------- /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/udp.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "time" 7 | ) 8 | 9 | const ( 10 | maxPacketSize = 4096 // increase it if error occurs 11 | ) 12 | 13 | var ( 14 | errPacketTooSmall = fmt.Errorf("[udp]read error: cannot decrypt, received packet is smaller than ivLen") 15 | errPacketTooLarge = fmt.Errorf("[udp]read error: received packet is latger than maxPacketSize(%d)", maxPacketSize) 16 | errBufferTooSmall = fmt.Errorf("[udp]read error: given buffer is too small to hold data") 17 | ) 18 | 19 | type SecurePacketConn struct { 20 | net.PacketConn 21 | *Cipher 22 | } 23 | 24 | func NewSecurePacketConn(c net.PacketConn, cipher *Cipher) *SecurePacketConn { 25 | return &SecurePacketConn{ 26 | PacketConn: c, 27 | Cipher: cipher, 28 | } 29 | } 30 | 31 | func (c *SecurePacketConn) Close() error { 32 | return c.PacketConn.Close() 33 | } 34 | 35 | func (c *SecurePacketConn) ReadFrom(b []byte) (n int, src net.Addr, err error) { 36 | cipher := c.Copy() 37 | buf := make([]byte, 4096) 38 | n, src, err = c.PacketConn.ReadFrom(buf) 39 | if err != nil { 40 | return 41 | } 42 | 43 | if n < c.info.ivLen { 44 | return 0, nil, errPacketTooSmall 45 | } 46 | 47 | if len(b) < n-c.info.ivLen { 48 | err = errBufferTooSmall // just a warning 49 | } 50 | 51 | iv := make([]byte, c.info.ivLen) 52 | copy(iv, buf[:c.info.ivLen]) 53 | 54 | if err = cipher.initDecrypt(iv); err != nil { 55 | return 56 | } 57 | 58 | cipher.decrypt(b[0:], buf[c.info.ivLen:n]) 59 | n -= c.info.ivLen 60 | 61 | return 62 | } 63 | 64 | func (c *SecurePacketConn) WriteTo(b []byte, dst net.Addr) (n int, err error) { 65 | cipher := c.Copy() 66 | iv, err := cipher.initEncrypt() 67 | if err != nil { 68 | return 69 | } 70 | packetLen := len(b) + len(iv) 71 | 72 | cipherData := make([]byte, packetLen) 73 | copy(cipherData, iv) 74 | 75 | cipher.encrypt(cipherData[len(iv):], b) 76 | n, err = c.PacketConn.WriteTo(cipherData, dst) 77 | return 78 | } 79 | 80 | func (c *SecurePacketConn) LocalAddr() net.Addr { 81 | return c.PacketConn.LocalAddr() 82 | } 83 | 84 | func (c *SecurePacketConn) SetDeadline(t time.Time) error { 85 | return c.PacketConn.SetDeadline(t) 86 | } 87 | 88 | func (c *SecurePacketConn) SetReadDeadline(t time.Time) error { 89 | return c.PacketConn.SetReadDeadline(t) 90 | } 91 | 92 | func (c *SecurePacketConn) SetWriteDeadline(t time.Time) error { 93 | return c.PacketConn.SetWriteDeadline(t) 94 | } 95 | -------------------------------------------------------------------------------- /shadowsocks/udprelay.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "net" 7 | "strconv" 8 | "strings" 9 | "sync" 10 | "syscall" 11 | "time" 12 | ) 13 | 14 | const ( 15 | idType = 0 // address type index 16 | idIP0 = 1 // ip address start index 17 | idDmLen = 1 // domain address length index 18 | idDm0 = 2 // domain address start index 19 | 20 | typeIPv4 = 1 // type is ipv4 address 21 | typeDm = 3 // type is domain address 22 | typeIPv6 = 4 // type is ipv6 address 23 | 24 | lenIPv4 = 1 + net.IPv4len + 2 // 1addrType + ipv4 + 2port 25 | lenIPv6 = 1 + net.IPv6len + 2 // 1addrType + ipv6 + 2port 26 | lenDmBase = 1 + 1 + 2 // 1addrType + 1addrLen + 2port, plus addrLen 27 | // lenHmacSha1 = 10 28 | ) 29 | 30 | var ( 31 | reqList = newReqList() 32 | natlist = newNatTable() 33 | udpTimeout = 30 * time.Second 34 | reqListRefreshTime = 5 * time.Minute 35 | ) 36 | 37 | type natTable struct { 38 | sync.Mutex 39 | conns map[string]net.PacketConn 40 | } 41 | 42 | func newNatTable() *natTable { 43 | return &natTable{conns: map[string]net.PacketConn{}} 44 | } 45 | 46 | func (table *natTable) Delete(index string) net.PacketConn { 47 | table.Lock() 48 | defer table.Unlock() 49 | c, ok := table.conns[index] 50 | if ok { 51 | delete(table.conns, index) 52 | return c 53 | } 54 | return nil 55 | } 56 | 57 | func (table *natTable) Get(index string) (c net.PacketConn, ok bool, err error) { 58 | table.Lock() 59 | defer table.Unlock() 60 | c, ok = table.conns[index] 61 | if !ok { 62 | c, err = net.ListenPacket("udp", "") 63 | if err != nil { 64 | return nil, false, err 65 | } 66 | table.conns[index] = c 67 | } 68 | return 69 | } 70 | 71 | //noinspection GoRedundantParens 72 | type requestHeaderList struct { 73 | sync.Mutex 74 | List map[string]([]byte) 75 | } 76 | 77 | //noinspection GoRedundantParens 78 | func newReqList() *requestHeaderList { 79 | ret := &requestHeaderList{List: map[string]([]byte){}} 80 | go func() { 81 | for { 82 | time.Sleep(reqListRefreshTime) 83 | ret.Refresh() 84 | } 85 | }() 86 | return ret 87 | } 88 | 89 | func (r *requestHeaderList) Refresh() { 90 | r.Lock() 91 | defer r.Unlock() 92 | for k := range r.List { 93 | delete(r.List, k) 94 | } 95 | } 96 | 97 | func (r *requestHeaderList) Get(dstaddr string) (req []byte, ok bool) { 98 | r.Lock() 99 | defer r.Unlock() 100 | req, ok = r.List[dstaddr] 101 | return 102 | } 103 | 104 | func (r *requestHeaderList) Put(dstaddr string, req []byte) { 105 | r.Lock() 106 | defer r.Unlock() 107 | r.List[dstaddr] = req 108 | return 109 | } 110 | 111 | func parseHeaderFromAddr(addr net.Addr) ([]byte, int) { 112 | // if the request address type is domain, it cannot be reverselookuped 113 | ip, port, err := net.SplitHostPort(addr.String()) 114 | if err != nil { 115 | return nil, 0 116 | } 117 | buf := make([]byte, 20) 118 | IP := net.ParseIP(ip) 119 | b1 := IP.To4() 120 | iplen := 0 121 | if b1 == nil { //ipv6 122 | b1 = IP.To16() 123 | buf[0] = typeIPv6 124 | iplen = net.IPv6len 125 | } else { //ipv4 126 | buf[0] = typeIPv4 127 | iplen = net.IPv4len 128 | } 129 | copy(buf[1:], b1) 130 | port_i, _ := strconv.Atoi(port) 131 | binary.BigEndian.PutUint16(buf[1+iplen:], uint16(port_i)) 132 | return buf[:1+iplen+2], 1 + iplen + 2 133 | } 134 | 135 | func Pipeloop(write net.PacketConn, writeAddr net.Addr, readClose net.PacketConn, addTraffic func(int)) { 136 | buf := leakyBuf.Get() 137 | defer leakyBuf.Put(buf) 138 | defer readClose.Close() 139 | for { 140 | readClose.SetDeadline(time.Now().Add(udpTimeout)) 141 | n, raddr, err := readClose.ReadFrom(buf) 142 | if err != nil { 143 | if ne, ok := err.(*net.OpError); ok { 144 | if ne.Err == syscall.EMFILE || ne.Err == syscall.ENFILE { 145 | // log too many open file error 146 | // EMFILE is process reaches open file limits, ENFILE is system limit 147 | Debug.Println("[udp]read error:", err) 148 | } 149 | } 150 | Debug.Printf("[udp]closed pipe %s<-%s\n", writeAddr, readClose.LocalAddr()) 151 | return 152 | } 153 | // need improvement here 154 | if req, ok := reqList.Get(raddr.String()); ok { 155 | n, _ := write.WriteTo(append(req, buf[:n]...), writeAddr) 156 | addTraffic(n) 157 | } else { 158 | header, hlen := parseHeaderFromAddr(raddr) 159 | n, _ := write.WriteTo(append(header[:hlen], buf[:n]...), writeAddr) 160 | addTraffic(n) 161 | } 162 | } 163 | } 164 | 165 | func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive []byte, addTraffic func(int)) { 166 | var dstIP net.IP 167 | var reqLen int 168 | addrType := receive[idType] 169 | defer leakyBuf.Put(receive) 170 | 171 | switch addrType & AddrMask { 172 | case typeIPv4: 173 | reqLen = lenIPv4 174 | if len(receive) < reqLen { 175 | Debug.Println("[udp]invalid received message.") 176 | } 177 | dstIP = net.IP(receive[idIP0 : idIP0+net.IPv4len]) 178 | case typeIPv6: 179 | reqLen = lenIPv6 180 | if len(receive) < reqLen { 181 | Debug.Println("[udp]invalid received message.") 182 | } 183 | dstIP = net.IP(receive[idIP0 : idIP0+net.IPv6len]) 184 | case typeDm: 185 | reqLen = int(receive[idDmLen]) + lenDmBase 186 | if len(receive) < reqLen { 187 | Debug.Println("[udp]invalid received message.") 188 | } 189 | name := string(receive[idDm0 : idDm0+int(receive[idDmLen])]) 190 | // avoid panic: syscall: string with NUL passed to StringToUTF16 on windows. 191 | if strings.ContainsRune(name, 0x00) { 192 | fmt.Println("[udp]invalid domain name.") 193 | return 194 | } 195 | dIP, err := net.ResolveIPAddr("ip", name) // carefully with const type 196 | if err != nil { 197 | Debug.Printf("[udp]failed to resolve domain name: %s\n", string(receive[idDm0:idDm0+receive[idDmLen]])) 198 | return 199 | } 200 | dstIP = dIP.IP 201 | default: 202 | Debug.Printf("[udp]addrType %d not supported", addrType) 203 | return 204 | } 205 | dst := &net.UDPAddr{ 206 | IP: dstIP, 207 | Port: int(binary.BigEndian.Uint16(receive[reqLen-2 : reqLen])), 208 | } 209 | if _, ok := reqList.Get(dst.String()); !ok { 210 | req := make([]byte, reqLen) 211 | copy(req, receive) 212 | reqList.Put(dst.String(), req) 213 | } 214 | 215 | remote, exist, err := natlist.Get(src.String()) 216 | if err != nil { 217 | return 218 | } 219 | if !exist { 220 | Debug.Printf("[udp]new client %s->%s via %s\n", src, dst, remote.LocalAddr()) 221 | go func() { 222 | Pipeloop(handle, src, remote, addTraffic) 223 | natlist.Delete(src.String()) 224 | }() 225 | } else { 226 | Debug.Printf("[udp]using cached client %s->%s via %s\n", src, dst, remote.LocalAddr()) 227 | } 228 | if remote == nil { 229 | fmt.Println("WTF") 230 | } 231 | remote.SetDeadline(time.Now().Add(udpTimeout)) 232 | n, err = remote.WriteTo(receive[reqLen:n], dst) 233 | addTraffic(n) 234 | if err != nil { 235 | if ne, ok := err.(*net.OpError); ok && (ne.Err == syscall.EMFILE || ne.Err == syscall.ENFILE) { 236 | // log too many open file error 237 | // EMFILE is process reaches open file limits, ENFILE is system limit 238 | Debug.Println("[udp]write error:", err) 239 | } else { 240 | Debug.Println("[udp]error connecting to:", dst, err) 241 | } 242 | if conn := natlist.Delete(src.String()); conn != nil { 243 | conn.Close() 244 | } 245 | } 246 | // Pipeloop 247 | return 248 | } 249 | 250 | func ReadAndHandleUDPReq(c *SecurePacketConn, addTraffic func(int)) error { 251 | buf := leakyBuf.Get() 252 | n, src, err := c.ReadFrom(buf[0:]) 253 | if err != nil { 254 | return err 255 | } 256 | go handleUDPConnection(c, n, src, buf, addTraffic) 257 | return nil 258 | } 259 | -------------------------------------------------------------------------------- /shadowsocks/util.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | "crypto/hmac" 5 | "crypto/sha1" 6 | "errors" 7 | "fmt" 8 | "os" 9 | ) 10 | 11 | func PrintVersion() { 12 | const version = "1.2.2" 13 | fmt.Println("shadowsocks-go version", version) 14 | } 15 | 16 | func IsFileExists(path string) (bool, error) { 17 | stat, err := os.Stat(path) 18 | if err == nil { 19 | if stat.Mode()&os.ModeType == 0 { 20 | return true, nil 21 | } 22 | return false, errors.New(path + " exists but is not regular file") 23 | } 24 | if os.IsNotExist(err) { 25 | return false, nil 26 | } 27 | return false, err 28 | } 29 | 30 | func HmacSha1(key []byte, data []byte) []byte { 31 | hmacSha1 := hmac.New(sha1.New, key) 32 | hmacSha1.Write(data) 33 | return hmacSha1.Sum(nil)[:10] 34 | } 35 | 36 | type ClosedFlag struct { 37 | flag bool 38 | } 39 | 40 | func (flag *ClosedFlag) SetClosed() { 41 | flag.flag = true 42 | } 43 | 44 | func (flag *ClosedFlag) IsClosed() bool { 45 | return flag.flag 46 | } 47 | --------------------------------------------------------------------------------