├── .gitignore ├── .travis.yml ├── CHANGELOG ├── Godeps ├── Godeps.json └── Readme ├── LICENSE ├── Makefile ├── README.md ├── TODO ├── build ├── README.md └── build-image │ ├── Dockerfile │ └── files │ ├── entrypoint.sh │ ├── mu-config.conf │ └── multi-user.json ├── cmd ├── shadowsocks-httpget │ └── httpget.go ├── shadowsocks-local │ └── local.go └── shadowss │ ├── password_mgr.go │ ├── server │ └── main.go │ ├── server_option.go │ ├── shadowss │ ├── tcp_server.go │ └── udp_server.go ├── config.json ├── deb ├── DEBIAN │ ├── conffiles │ ├── control │ ├── postinst │ ├── postrm │ └── prerm └── etc │ ├── init.d │ └── shadowsocks │ └── shadowsocks │ └── config.json ├── docs └── shadowsocks-libev-client.md ├── mu ├── .gitignore ├── README.md ├── boot.go ├── build_linux.sh ├── client.go ├── config.go ├── config │ └── config.go ├── example.conf ├── flag.go ├── func.go ├── log.go ├── log │ ├── client.go │ └── log.go ├── main.go ├── mysql │ ├── mysql.go │ └── mysql_boot.go ├── redis.go ├── storage.go ├── system │ ├── client.go │ └── client_test.go ├── user │ └── user.go ├── utils.go └── webapi │ ├── ret.go │ ├── user.go │ ├── web.go │ ├── web_boot.go │ ├── web_net.go │ └── web_test.go ├── pkg ├── config │ ├── config_client.go │ ├── config_server.go │ └── interface.go ├── connection │ ├── conn.go │ ├── encrypt.go │ ├── leakybuf.go │ ├── pipe.go │ └── udp_conn.go ├── testdata │ ├── deprecated-client-multi-server.json │ └── noserver.json └── util │ ├── flag │ ├── flags.go │ └── tristate.go │ ├── mergesort.go │ └── util.go ├── sample-config ├── client-multi-server.json └── server-multi-port.json ├── script ├── README.md ├── build.sh ├── createdeb.sh ├── curl.sh ├── http.go ├── set-version.sh ├── shadowsocks.exe ├── test.sh └── win32build.bat ├── shadowsocks ├── config.go ├── config_test.go ├── conn.go ├── encrypt.go ├── encrypt_test.go ├── leakybuf.go ├── log.go ├── mergesort.go ├── pipe.go ├── testdata │ ├── deprecated-client-multi-server.json │ └── noserver.json └── util.go └── vendor └── golang.org └── x ├── crypto ├── LICENSE └── PATENTS └── sys ├── LICENSE └── PATENTS /.gitignore: -------------------------------------------------------------------------------- 1 | *.deb 2 | script/http 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.5 4 | - 1.6 5 | - 1.6.3 6 | - 1.7 7 | install: 8 | - go get github.com/tools/godep 9 | - go get golang.org/x/crypto/blowfish 10 | - go get golang.org/x/crypto/cast5 11 | - go get golang.org/x/crypto/salsa20 12 | - go get github.com/codahale/chacha20 13 | - go get github.com/Sirupsen/logrus 14 | - go get github.com/Terry-Mao/goconf 15 | - go get github.com/go-sql-driver/mysql 16 | - go get github.com/jinzhu/gorm 17 | - go get gopkg.in/redis.v3 18 | - go get github.com/cyfdecyf/leakybuf 19 | - go get github.com/lib/pq 20 | - go get github.com/golang/glog 21 | script: 22 | - cd mu && go build 23 | - go test -cover ./... 24 | sudo: required 25 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 1.1.4 (2015-05-10) 2 | * Support "chacha20" encryption method, thanks to @defia 3 | * Support "salsa20" encryption method, thanks to @genzj 4 | * Fix go 1.4 canonical import paths, thanks to @ddatsh 5 | * Exit if port not bindable, thanks to @thomasf 6 | * Always set timeout for data transfer 7 | * More buffer reuse 8 | 9 | 1.1.3 (2014-09-28) 10 | * Fix can't specify encryption method in config file 11 | 12 | 1.1.2 (2014-09-21) 13 | * Support new encryption method "rc4-md5" 14 | * Use aes-256-cfb as default encryption method for command line app 15 | 16 | 1.1.1 (2013-07-12) 17 | * Add -b option to limit listen address for client 18 | * Fix can't override server address on command line 19 | 20 | 1.1 (2013-05-26) 21 | * Add more encryption methods 22 | * Enable CGO for OS X when building 23 | 24 | 1.0 (2013-05-17) 25 | * Support specify servers with IPv6 address 26 | * Support IPv6 address in socks requests 27 | * Better handling of configuration file for debian package 28 | 29 | 0.6.2 (2013-03-15) 30 | * Connect to multiple servers in the order specified in config 31 | * More information in server error log to help error diagnosing 32 | 33 | 0.6.1 (2013-03-04) 34 | * Small performance improvement 35 | * For windows: provide shadowsocks-tray.exe to hide shadowsocks-local.exe in 36 | system tray. (Thanks to phuslu's taskbar.) 37 | 38 | 0.6 (2013-01-23) 39 | 40 | * Generate ciphers on demand (encryption table cache is removed) 41 | * Support RC4 encryption 42 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/orvice/shadowsocks-go", 3 | "GoVersion": "go1.6", 4 | "GodepVersion": "v74", 5 | "Deps": [] 6 | } 7 | -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /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 | # shadowsocks-go 2 | 3 | [![Join the chat at https://gitter.im/orvice/shadowsocks-go](https://badges.gitter.im/orvice/shadowsocks-go.svg)](https://gitter.im/orvice/shadowsocks-go?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | Current version: 1.1.4 [![Build Status](https://travis-ci.org/orvice/shadowsocks-go.svg?branch=master)](https://travis-ci.org/orvice/shadowsocks-go) 6 | 7 | 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). 8 | 9 | The protocol is compatible with the origin shadowsocks (if both have been upgraded to the latest version). 10 | 11 | **Note `server_password` option syntax changed in 0.6.2, the client now connects to servers in the order specified in the config.** 12 | 13 | **Please develop on the latest develop branch if you want to send pull request.** 14 | 15 | # Install 16 | 17 | Compiled client binaries can be download [here](http://dl.chenyufei.info/shadowsocks/). (All compiled with cgo disabled, except the mac version.) 18 | 19 | You can also install from source (assume you have go installed): 20 | 21 | ``` 22 | # on server 23 | go get shadowsocks/shadowsocks-go/cmd/shadowsocks-server 24 | # on client 25 | go get shadowsocks/shadowsocks-go/cmd/shadowsocks-local 26 | ``` 27 | 28 | It's recommended to disable cgo when compiling shadowsocks-go. This will prevent the go runtime from creating too many threads for dns lookup. 29 | 30 | # Usage 31 | 32 | 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. 33 | 34 | 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://shadowsocks/shadowsocks-go/blob/master/config.json), change the following values: 35 | 36 | ``` 37 | server your server ip or hostname 38 | server_port server port 39 | local_port local socks5 proxy port 40 | method encryption method, null by default (table), the following methods are supported: 41 | aes-128-cfb, aes-192-cfb, aes-256-cfb, bf-cfb, cast5-cfb, des-cfb, rc4-md5, chacha20, salsa20, rc4, table 42 | password a password used to encrypt transfer 43 | timeout server option, in seconds 44 | ``` 45 | 46 | Run `shadowsocks-server` on your server. To run it in the background, run `shadowsocks-server > log &`. 47 | 48 | On client, run `shadowsocks-local`. Change proxy settings of your browser to 49 | 50 | ``` 51 | SOCKS5 127.0.0.1:local_port 52 | ``` 53 | 54 | ## About encryption methods 55 | 56 | 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)**. 57 | 58 | **rc4 and table encryption methods are deprecated because they are not secure.** 59 | 60 | ## Command line options 61 | 62 | Command line options can override settings from configuration files. Use `-h` option to see all available options. 63 | 64 | ``` 65 | shadowsocks-local -s server_address -p server_port -k password 66 | -m aes-128-cfb -c config.json 67 | -b local_address -l local_port 68 | shadowsocks-server -p server_port -k password 69 | -m aes-128-cfb -c config.json 70 | -t timeout 71 | ``` 72 | 73 | Use `-d` option to enable debug message. 74 | 75 | ## Use multiple servers on client 76 | 77 | ``` 78 | server_password specify multiple server and password, server should be in the form of host:port 79 | ``` 80 | 81 | Here's a sample configuration [`client-multi-server.json`](https://shadowsocks/shadowsocks-go/blob/master/sample-config/client-multi-server.json). Given `server_password`, client program will ignore `server_port`, `server` and `password` options. 82 | 83 | 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.) 84 | 85 | ## Multiple users with different passwords on server 86 | 87 | 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: 88 | 89 | ``` 90 | port_password specify multiple ports and passwords to support multiple users 91 | ``` 92 | 93 | Here's a sample configuration [`server-multi-port.json`](https://shadowsocks/shadowsocks-go/blob/master/sample-config/server-multi-port.json). Given `port_password`, server program will ignore `server_port` and `password` options. 94 | 95 | ### Update port password for a running server 96 | 97 | Edit the config file used to start the server, then send `SIGHUP` to the server process. 98 | 99 | # Note to OpenVZ users 100 | 101 | **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://shadowsocks/shadowsocks-go/issues/3) for more details.) 102 | 103 | If vswap is not an option and memory usage is a problem for you, try [shadowsocks-libev](https://github.com/madeye/shadowsocks-libev). 104 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orvice/shadowsocks-go/df93b5d1852ac8eda875df0380c7464de142eca9/TODO -------------------------------------------------------------------------------- /build/README.md: -------------------------------------------------------------------------------- 1 | # build shadowsocket with docker 2 | 3 | ## build image for shadowsocket server 4 | 5 | ``` 6 | cd /shadowsocket-go/build/build-image 7 | docker build --rm -t bjjyd/shadowsocket-go -f Dockerfile ../../ 8 | ``` 9 | ## startup shadowsocket server 10 | 11 | ``` 12 | docker run --rm -it bjjyd/shadowsocket-go /bin/bash 13 | docker run -d -p 20000-20100:20000-20100 bjjyd/shadowsocket-go 14 | ``` 15 | -------------------------------------------------------------------------------- /build/build-image/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine 2 | 3 | MAINTAINER seanchann 4 | 5 | RUN set -ex \ 6 | && apk add --no-cache --virtual bash 7 | 8 | COPY . /go/src/shadowsocks/shadowsocks-go/ 9 | COPY /build/build-image/files/entrypoint.sh /entrypoint.sh 10 | COPY /build/build-image/files/mu-config.conf /conf/mu-config.conf 11 | COPY /build/build-image/files/multi-user.json /conf/config.json 12 | 13 | RUN cd /go/src/shadowsocks/shadowsocks-go/ \ 14 | && cp vendor/* /go/src/ -rf \ 15 | && cp sample-config/server-multi-port.json /config.json \ 16 | && cd cmd/shadowsocks-server/ \ 17 | && go build -v -o /go/bin/shadowss \ 18 | && cd mu \ 19 | && go build -v -o /go/bin/shadowss-mu \ 20 | && chmod +x /entrypoint.sh 21 | 22 | 23 | CMD ["entrypoint.sh"] 24 | -------------------------------------------------------------------------------- /build/build-image/files/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | #/go/bin/shadowss -c /conf/config.json & 6 | 7 | /go/bin/shadowss-mu -config_path /conf/ & 8 | -------------------------------------------------------------------------------- /build/build-image/files/mu-config.conf: -------------------------------------------------------------------------------- 1 | ## 配置说明 https://shadowsocks/shadowsocks-go/wiki/Config 2 | [base] 3 | N 1 4 | ip 0.0.0.0 5 | # webapi or mysql: where get users 6 | client mysql 7 | checktime 60 8 | synctime 60 9 | 10 | [webapi] 11 | url http://sspanel.dev/mu 12 | key 5a4a845e025e11d959c34fb604bcc06f 13 | node_id 1 14 | 15 | [mysql] 16 | host mysql 17 | user sspanel 18 | pass sspanel 19 | db sspanel 20 | table user 21 | 22 | [redis] 23 | host redis 24 | # if no passwd set,comment this line 25 | #pass "" 26 | db 1 27 | -------------------------------------------------------------------------------- /build/build-image/files/multi-user.json: -------------------------------------------------------------------------------- 1 | { 2 | "port_password": { 3 | "8387": "44c096c8e1e5fd7a85ea0bbe4623778d" 4 | }, 5 | "method": "aes-256-cfb", 6 | "timeout": 600 7 | } 8 | -------------------------------------------------------------------------------- /cmd/shadowsocks-httpget/httpget.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | ss "shadowsocks/shadowsocks-go/shadowsocks" 7 | "io" 8 | "math" 9 | "net" 10 | "net/http" 11 | "net/url" 12 | "os" 13 | "runtime" 14 | "strconv" 15 | "strings" 16 | "time" 17 | ) 18 | 19 | var config struct { 20 | server string 21 | port int 22 | passwd string 23 | method string 24 | core int 25 | nconn int 26 | nreq int 27 | // nsec int 28 | } 29 | 30 | var debug ss.DebugLog 31 | 32 | func doOneRequest(client *http.Client, uri string, buf []byte) (err error) { 33 | resp, err := client.Get(uri) 34 | if err != nil { 35 | fmt.Printf("GET %s error: %v\n", uri, err) 36 | return err 37 | } 38 | for err == nil { 39 | _, err = resp.Body.Read(buf) 40 | if debug { 41 | debug.Println(string(buf)) 42 | } 43 | } 44 | if err != io.EOF { 45 | fmt.Printf("Read %s response error: %v\n", uri, err) 46 | } else { 47 | err = nil 48 | } 49 | return 50 | } 51 | 52 | func get(connid int, uri, serverAddr string, rawAddr []byte, cipher *ss.Cipher, done chan []time.Duration) { 53 | reqDone := 0 54 | reqTime := make([]time.Duration, config.nreq) 55 | defer func() { 56 | done <- reqTime[:reqDone] 57 | }() 58 | tr := &http.Transport{ 59 | Dial: func(_, _ string) (net.Conn, error) { 60 | return ss.DialWithRawAddr(rawAddr, serverAddr, cipher.Copy()) 61 | }, 62 | } 63 | 64 | buf := make([]byte, 8192) 65 | client := &http.Client{Transport: tr} 66 | for ; reqDone < config.nreq; reqDone++ { 67 | start := time.Now() 68 | if err := doOneRequest(client, uri, buf); err != nil { 69 | return 70 | } 71 | reqTime[reqDone] = time.Now().Sub(start) 72 | 73 | if (reqDone+1)%1000 == 0 { 74 | fmt.Printf("conn %d finished %d get requests\n", connid, reqDone+1) 75 | } 76 | } 77 | } 78 | 79 | func main() { 80 | flag.StringVar(&config.server, "s", "127.0.0.1", "server:port") 81 | flag.IntVar(&config.port, "p", 0, "server:port") 82 | flag.IntVar(&config.core, "core", 1, "number of CPU cores to use") 83 | flag.StringVar(&config.passwd, "k", "", "password") 84 | flag.StringVar(&config.method, "m", "", "encryption method, use empty string or rc4") 85 | flag.IntVar(&config.nconn, "nc", 1, "number of connection to server") 86 | flag.IntVar(&config.nreq, "nr", 1, "number of request for each connection") 87 | // flag.IntVar(&config.nsec, "ns", 0, "run how many seconds for each connection") 88 | flag.BoolVar((*bool)(&debug), "d", false, "print http response body for debugging") 89 | 90 | flag.Parse() 91 | 92 | if config.server == "" || config.port == 0 || config.passwd == "" || len(flag.Args()) != 1 { 93 | fmt.Printf("Usage: %s -s -p -k \n", os.Args[0]) 94 | os.Exit(1) 95 | } 96 | 97 | runtime.GOMAXPROCS(config.core) 98 | uri := flag.Arg(0) 99 | if strings.HasPrefix(uri, "https://") { 100 | fmt.Println("https not supported") 101 | os.Exit(1) 102 | } 103 | if !strings.HasPrefix(uri, "http://") { 104 | uri = "http://" + uri 105 | } 106 | 107 | cipher, err := ss.NewCipher(config.method, config.passwd) 108 | if err != nil { 109 | fmt.Println("Error creating cipher:", err) 110 | os.Exit(1) 111 | } 112 | serverAddr := net.JoinHostPort(config.server, strconv.Itoa(config.port)) 113 | 114 | parsedURL, err := url.Parse(uri) 115 | if err != nil { 116 | fmt.Println("Error parsing url:", err) 117 | os.Exit(1) 118 | } 119 | host, _, err := net.SplitHostPort(parsedURL.Host) 120 | if err != nil { 121 | host = net.JoinHostPort(parsedURL.Host, "80") 122 | } else { 123 | host = parsedURL.Host 124 | } 125 | // fmt.Println(host) 126 | rawAddr, err := ss.RawAddr(host) 127 | if err != nil { 128 | panic("Error getting raw address.") 129 | } 130 | 131 | done := make(chan []time.Duration) 132 | for i := 1; i <= config.nconn; i++ { 133 | go get(i, uri, serverAddr, rawAddr, cipher, done) 134 | } 135 | 136 | // collect request finish time 137 | reqTime := make([]int64, config.nconn*config.nreq) 138 | reqDone := 0 139 | for i := 1; i <= config.nconn; i++ { 140 | rt := <-done 141 | for _, t := range rt { 142 | reqTime[reqDone] = int64(t) 143 | reqDone++ 144 | } 145 | } 146 | 147 | fmt.Println("number of total requests:", config.nconn*config.nreq) 148 | fmt.Println("number of finished requests:", reqDone) 149 | if reqDone == 0 { 150 | return 151 | } 152 | 153 | // calculate average an standard deviation 154 | reqTime = reqTime[:reqDone] 155 | var sum int64 156 | for _, d := range reqTime { 157 | sum += d 158 | } 159 | avg := float64(sum) / float64(reqDone) 160 | 161 | varSum := float64(0) 162 | for _, d := range reqTime { 163 | di := math.Abs(float64(d) - avg) 164 | di *= di 165 | varSum += di 166 | } 167 | stddev := math.Sqrt(varSum / float64(reqDone)) 168 | fmt.Println("\naverage time per request:", time.Duration(avg)) 169 | fmt.Println("standard deviation:", time.Duration(stddev)) 170 | } 171 | -------------------------------------------------------------------------------- /cmd/shadowsocks-local/local.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "flag" 7 | "fmt" 8 | ss "shadowsocks/shadowsocks-go/shadowsocks" 9 | "io" 10 | "log" 11 | "math/rand" 12 | "net" 13 | "os" 14 | "path" 15 | "strconv" 16 | "time" 17 | ) 18 | 19 | var debug ss.DebugLog 20 | 21 | var ( 22 | errAddrType = errors.New("socks addr type not supported") 23 | errVer = errors.New("socks version not supported") 24 | errMethod = errors.New("socks only support 1 method now") 25 | errAuthExtraData = errors.New("socks authentication get extra data") 26 | errReqExtraData = errors.New("socks request get extra data") 27 | errCmd = errors.New("socks command not supported") 28 | ) 29 | 30 | const ( 31 | socksVer5 = 5 32 | socksCmdConnect = 1 33 | ) 34 | 35 | func init() { 36 | rand.Seed(time.Now().Unix()) 37 | } 38 | 39 | func handShake(conn net.Conn) (err error) { 40 | const ( 41 | idVer = 0 42 | idNmethod = 1 43 | ) 44 | // version identification and method selection message in theory can have 45 | // at most 256 methods, plus version and nmethod field in total 258 bytes 46 | // the current rfc defines only 3 authentication methods (plus 2 reserved), 47 | // so it won't be such long in practice 48 | 49 | buf := make([]byte, 258) 50 | 51 | var n int 52 | ss.SetReadTimeout(conn) 53 | // make sure we get the nmethod field 54 | if n, err = io.ReadAtLeast(conn, buf, idNmethod+1); err != nil { 55 | return 56 | } 57 | if buf[idVer] != socksVer5 { 58 | return errVer 59 | } 60 | nmethod := int(buf[idNmethod]) 61 | msgLen := nmethod + 2 62 | if n == msgLen { // handshake done, common case 63 | // do nothing, jump directly to send confirmation 64 | } else if n < msgLen { // has more methods to read, rare case 65 | if _, err = io.ReadFull(conn, buf[n:msgLen]); err != nil { 66 | return 67 | } 68 | } else { // error, should not get extra data 69 | return errAuthExtraData 70 | } 71 | // send confirmation: version 5, no authentication required 72 | _, err = conn.Write([]byte{socksVer5, 0}) 73 | return 74 | } 75 | 76 | func getRequest(conn net.Conn) (rawaddr []byte, host string, err error) { 77 | const ( 78 | idVer = 0 79 | idCmd = 1 80 | idType = 3 // address type index 81 | idIP0 = 4 // ip addres start index 82 | idDmLen = 4 // domain address length index 83 | idDm0 = 5 // domain address start index 84 | 85 | typeIPv4 = 1 // type is ipv4 address 86 | typeDm = 3 // type is domain address 87 | typeIPv6 = 4 // type is ipv6 address 88 | 89 | lenIPv4 = 3 + 1 + net.IPv4len + 2 // 3(ver+cmd+rsv) + 1addrType + ipv4 + 2port 90 | lenIPv6 = 3 + 1 + net.IPv6len + 2 // 3(ver+cmd+rsv) + 1addrType + ipv6 + 2port 91 | lenDmBase = 3 + 1 + 1 + 2 // 3 + 1addrType + 1addrLen + 2port, plus addrLen 92 | ) 93 | // refer to getRequest in server.go for why set buffer size to 263 94 | buf := make([]byte, 263) 95 | var n int 96 | ss.SetReadTimeout(conn) 97 | // read till we get possible domain length field 98 | if n, err = io.ReadAtLeast(conn, buf, idDmLen+1); err != nil { 99 | return 100 | } 101 | // check version and cmd 102 | if buf[idVer] != socksVer5 { 103 | err = errVer 104 | return 105 | } 106 | if buf[idCmd] != socksCmdConnect { 107 | err = errCmd 108 | return 109 | } 110 | 111 | reqLen := -1 112 | switch buf[idType] { 113 | case typeIPv4: 114 | reqLen = lenIPv4 115 | case typeIPv6: 116 | reqLen = lenIPv6 117 | case typeDm: 118 | reqLen = int(buf[idDmLen]) + lenDmBase 119 | default: 120 | err = errAddrType 121 | return 122 | } 123 | 124 | if n == reqLen { 125 | // common case, do nothing 126 | } else if n < reqLen { // rare case 127 | if _, err = io.ReadFull(conn, buf[n:reqLen]); err != nil { 128 | return 129 | } 130 | } else { 131 | err = errReqExtraData 132 | return 133 | } 134 | 135 | rawaddr = buf[idType:reqLen] 136 | 137 | if debug { 138 | switch buf[idType] { 139 | case typeIPv4: 140 | host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() 141 | case typeIPv6: 142 | host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() 143 | case typeDm: 144 | host = string(buf[idDm0 : idDm0+buf[idDmLen]]) 145 | } 146 | port := binary.BigEndian.Uint16(buf[reqLen-2 : reqLen]) 147 | host = net.JoinHostPort(host, strconv.Itoa(int(port))) 148 | } 149 | 150 | return 151 | } 152 | 153 | type ServerCipher struct { 154 | server string 155 | cipher *ss.Cipher 156 | } 157 | 158 | var servers struct { 159 | srvCipher []*ServerCipher 160 | failCnt []int // failed connection count 161 | } 162 | 163 | func parseServerConfig(config *ss.Config) { 164 | hasPort := func(s string) bool { 165 | _, port, err := net.SplitHostPort(s) 166 | if err != nil { 167 | return false 168 | } 169 | return port != "" 170 | } 171 | 172 | if len(config.ServerPassword) == 0 { 173 | method := config.Method 174 | if config.Auth { 175 | method += "-ota" 176 | } 177 | // only one encryption table 178 | cipher, err := ss.NewCipher(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 | glog.V(5).Infof("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 | glog.V(5).Infof("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 avaiable shadowsocks server") 317 | } 318 | return 319 | } 320 | defer func() { 321 | if !closed { 322 | remote.Close() 323 | } 324 | }() 325 | 326 | go ss.PipeThenClose(conn, remote) 327 | ss.PipeThenClose(remote, conn) 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 main() { 354 | log.SetOutput(os.Stdout) 355 | 356 | var configFile, cmdServer, cmdLocal string 357 | var cmdConfig ss.Config 358 | var printVer bool 359 | 360 | flag.BoolVar(&printVer, "version", false, "print version") 361 | flag.StringVar(&configFile, "c", "config.json", "specify config file") 362 | flag.StringVar(&cmdServer, "s", "", "server address") 363 | flag.StringVar(&cmdLocal, "b", "", "local address, listen only to this address if specified") 364 | flag.StringVar(&cmdConfig.Password, "k", "", "password") 365 | flag.IntVar(&cmdConfig.ServerPort, "p", 0, "server port") 366 | flag.IntVar(&cmdConfig.Timeout, "t", 300, "timeout in seconds") 367 | flag.IntVar(&cmdConfig.LocalPort, "l", 0, "local socks5 proxy port") 368 | flag.StringVar(&cmdConfig.Method, "m", "", "encryption method, default: aes-256-cfb") 369 | flag.BoolVar((*bool)(&debug), "d", false, "print debug message") 370 | 371 | flag.Parse() 372 | 373 | if printVer { 374 | ss.PrintVersion() 375 | os.Exit(0) 376 | } 377 | 378 | cmdConfig.Server = cmdServer 379 | ss.SetDebug(debug) 380 | 381 | exists, err := ss.IsFileExists(configFile) 382 | // If no config file in current directory, try search it in the binary directory 383 | // Note there's no portable way to detect the binary directory. 384 | binDir := path.Dir(os.Args[0]) 385 | if (!exists || err != nil) && binDir != "" && binDir != "." { 386 | oldConfig := configFile 387 | configFile = path.Join(binDir, "config.json") 388 | log.Printf("%s not found, try config file %s\n", oldConfig, configFile) 389 | } 390 | 391 | config, err := ss.ParseConfig(configFile) 392 | if err != nil { 393 | config = &cmdConfig 394 | if !os.IsNotExist(err) { 395 | fmt.Fprintf(os.Stderr, "error reading %s: %v\n", configFile, err) 396 | os.Exit(1) 397 | } 398 | } else { 399 | ss.UpdateConfig(config, &cmdConfig) 400 | } 401 | if config.Method == "" { 402 | config.Method = "aes-256-cfb" 403 | } 404 | if len(config.ServerPassword) == 0 { 405 | if !enoughOptions(config) { 406 | fmt.Fprintln(os.Stderr, "must specify server address, password and both server/local port") 407 | os.Exit(1) 408 | } 409 | } else { 410 | if config.Password != "" || config.ServerPort != 0 || config.GetServerArray() != nil { 411 | fmt.Fprintln(os.Stderr, "given server_password, ignore server, server_port and password option:", config) 412 | } 413 | if config.LocalPort == 0 { 414 | fmt.Fprintln(os.Stderr, "must specify local port") 415 | os.Exit(1) 416 | } 417 | } 418 | 419 | parseServerConfig(config) 420 | 421 | run(cmdLocal + ":" + strconv.Itoa(config.LocalPort)) 422 | } 423 | -------------------------------------------------------------------------------- /cmd/shadowss/password_mgr.go: -------------------------------------------------------------------------------- 1 | package shadowss 2 | 3 | import ( 4 | "net" 5 | "sync" 6 | ) 7 | 8 | // PasswdManager is hold user port and password 9 | type PasswdManager struct { 10 | sync.Mutex 11 | portListener map[int]*PortListener 12 | udpListener map[int]*UDPListener 13 | } 14 | 15 | var passwdManager = PasswdManager{portListener: map[int]*PortListener{}} 16 | 17 | func (pm *PasswdManager) add(password string, port int, listener net.Listener) { 18 | pm.Lock() 19 | pm.portListener[port] = &PortListener{password, listener} 20 | pm.Unlock() 21 | } 22 | 23 | func (pm *PasswdManager) addUDP(password string, port int, listener *net.UDPConn) { 24 | pm.Lock() 25 | pm.udpListener[port] = &UDPListener{password, listener} 26 | pm.Unlock() 27 | } 28 | 29 | func (pm *PasswdManager) get(port int) (pl *PortListener, ok bool) { 30 | pm.Lock() 31 | pl, ok = pm.portListener[port] 32 | pm.Unlock() 33 | return 34 | } 35 | 36 | func (pm *PasswdManager) getUDP(port int) (pl *UDPListener, ok bool) { 37 | pm.Lock() 38 | pl, ok = pm.udpListener[port] 39 | pm.Unlock() 40 | return 41 | } 42 | 43 | func (pm *PasswdManager) del(port int) { 44 | pl, ok := pm.get(port) 45 | if !ok { 46 | return 47 | } 48 | pl.listener.Close() 49 | pm.Lock() 50 | delete(pm.portListener, port) 51 | pm.Unlock() 52 | } 53 | 54 | // Update port password would first close a port and restart listening on that 55 | // port. A different approach would be directly change the password used by 56 | // that port, but that requires **sharing** password between the port listener 57 | // and password manager. 58 | // func (pm *PasswdManager) updatePortPasswd(password, method string, port int, auth bool) { 59 | // pl, ok := pm.get(port) 60 | // if !ok { 61 | // log.Printf("new port %s added\n", port) 62 | // } else { 63 | // if pl.password == password { 64 | // return 65 | // } 66 | // log.Printf("closing port %s to update password\n", port) 67 | // pl.listener.Close() 68 | // } 69 | // // run will add the new port listener to passwdManager. 70 | // // So there maybe concurrent access to passwdManager and we need lock to protect it. 71 | // go run(password, method, port, auth) 72 | // if udp { 73 | // pl, _ := pm.getUDP(port) 74 | // pl.listener.Close() 75 | // go runUDP(password, method, port) 76 | // } 77 | // } 78 | -------------------------------------------------------------------------------- /cmd/shadowss/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "os/signal" 7 | "runtime" 8 | "syscall" 9 | "time" 10 | 11 | "shadowsocks-go/cmd/shadowss" 12 | "shadowsocks-go/pkg/config" 13 | "shadowsocks-go/pkg/util/flag" 14 | 15 | "github.com/golang/glog" 16 | "github.com/spf13/pflag" 17 | ) 18 | 19 | func waitSignal() { 20 | var sigChan = make(chan os.Signal, 1) 21 | signal.Notify(sigChan, syscall.SIGHUP) 22 | for sig := range sigChan { 23 | if sig == syscall.SIGHUP { 24 | //updatePasswd() 25 | } else { 26 | // is this going to happen? 27 | log.Printf("caught signal %v, exit", sig) 28 | os.Exit(0) 29 | } 30 | } 31 | } 32 | 33 | func start(udp bool) { 34 | for _, v := range config.ServerCfg.Clients { 35 | glog.V(5).Infof("listen pair(%v:%v) with auth:%v", v.Port, v.Password, v.EnableOTA) 36 | go shadowss.Run(v.Password, v.EncryptMethod, v.Port, v.EnableOTA, time.Duration(v.Timeout)*time.Second) 37 | if udp { 38 | go shadowss.RunUDP(v.Password, v.EncryptMethod, v.Port, time.Duration(v.Timeout)*time.Second) 39 | } 40 | } 41 | } 42 | 43 | func main() { 44 | serverRunOptions := shadowss.NewServerOption() 45 | 46 | // Parse command line flags. 47 | serverRunOptions.AddFlags(pflag.CommandLine) 48 | flag.InitFlags() 49 | 50 | if serverRunOptions.CpuCoreNum > 0 { 51 | runtime.GOMAXPROCS(serverRunOptions.CpuCoreNum) 52 | } 53 | 54 | glog.V(5).Infoln("tset") 55 | err := serverRunOptions.LoadConfigFile() 56 | if err != nil { 57 | glog.Fatalln("load user configure error:\r\n", err) 58 | } 59 | 60 | go start(serverRunOptions.EnableUDPRelay) 61 | 62 | waitSignal() 63 | } 64 | -------------------------------------------------------------------------------- /cmd/shadowss/server_option.go: -------------------------------------------------------------------------------- 1 | package shadowss 2 | 3 | import ( 4 | "shadowsocks-go/pkg/config" 5 | 6 | "github.com/golang/glog" 7 | "github.com/spf13/pflag" 8 | ) 9 | 10 | type serverOption struct { 11 | ConfigFile string 12 | CpuCoreNum int 13 | EnableUDPRelay bool 14 | } 15 | 16 | func NewServerOption() *serverOption { 17 | return &serverOption{ 18 | ConfigFile: string(""), 19 | CpuCoreNum: 1, 20 | EnableUDPRelay: false, 21 | } 22 | } 23 | 24 | func (s *serverOption) LoadConfigFile() error { 25 | glog.V(5).Infof("Parse %s file\r\n", s.ConfigFile) 26 | err := config.ServerCfg.Parse(s.ConfigFile) 27 | if err != nil { 28 | glog.Fatalf("error reading %s: %v\n", s.ConfigFile, err) 29 | return err 30 | } 31 | 32 | return nil 33 | } 34 | 35 | func (s *serverOption) AddFlags(fs *pflag.FlagSet) { 36 | 37 | fs.StringVar(&s.ConfigFile, "config-file", s.ConfigFile, ""+ 38 | "specify a configure file for server run. ") 39 | 40 | fs.IntVar(&s.CpuCoreNum, "cpu-core-num", s.CpuCoreNum, ""+ 41 | "specify how many cpu core will be alloc for program") 42 | 43 | fs.BoolVar(&s.EnableUDPRelay, "enable-udp-relay", s.EnableUDPRelay, ""+ 44 | "enable udp relay") 45 | } 46 | -------------------------------------------------------------------------------- /cmd/shadowss/shadowss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orvice/shadowsocks-go/df93b5d1852ac8eda875df0380c7464de142eca9/cmd/shadowss/shadowss -------------------------------------------------------------------------------- /cmd/shadowss/tcp_server.go: -------------------------------------------------------------------------------- 1 | package shadowss 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | "io" 8 | "log" 9 | "net" 10 | "os" 11 | "strconv" 12 | "syscall" 13 | "time" 14 | 15 | connection "shadowsocks-go/pkg/connection" 16 | encrypt "shadowsocks-go/pkg/connection" 17 | "shadowsocks-go/pkg/util" 18 | 19 | "github.com/golang/glog" 20 | ) 21 | 22 | const ( 23 | idType = 0 // address type index 24 | idIP0 = 1 // ip addres start index 25 | idDmLen = 1 // domain address length index 26 | idDm0 = 2 // domain address start index 27 | 28 | typeIPv4 = 1 // type is ipv4 address 29 | typeDm = 3 // type is domain address 30 | typeIPv6 = 4 // type is ipv6 address 31 | 32 | lenIPv4 = net.IPv4len + 2 // ipv4 + 2port 33 | lenIPv6 = net.IPv6len + 2 // ipv6 + 2port 34 | lenDmBase = 2 // 1addrLen + 2port, plus addrLen 35 | lenHmacSha1 = 10 36 | ) 37 | 38 | // PortListener is a listener 39 | type PortListener struct { 40 | password string 41 | listener net.Listener 42 | } 43 | 44 | func getRequest(conn *connection.Conn, auth bool, timeout time.Duration) (host string, ota bool, err error) { 45 | glog.V(5).Infoln("getRequest from remote34342") 46 | 47 | connection.SetReadTimeout(conn, timeout) 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) + 256(max length address) + 2(port) + 10(hmac-sha1) 52 | buf := make([]byte, 270) 53 | // read till we get possible domain length field 54 | if _, err = io.ReadFull(conn, buf[:idType+1]); err != nil { 55 | glog.Errorln("read buffer from remote connection error:", err) 56 | return 57 | } 58 | 59 | // if _, err = io.ReadFull(conn, buf[:64]); err != nil { 60 | // glog.Errorln("read buffer from remote connection error:", err) 61 | // return 62 | // } 63 | // glog.V(5).Infof("Got a Request string is byte:%v string:%v)\r\n", buf, string(buf)) 64 | // return 65 | 66 | var reqStart, reqEnd int 67 | addrType := buf[idType] 68 | switch addrType & connection.AddrMask { 69 | case typeIPv4: 70 | reqStart, reqEnd = idIP0, idIP0+lenIPv4 71 | case typeIPv6: 72 | reqStart, reqEnd = idIP0, idIP0+lenIPv6 73 | case typeDm: 74 | glog.V(5).Infoln("Got a Domain Addr Type, read start(%v) end(%v)\r\n", idType+1, idDmLen+1) 75 | if _, err = io.ReadFull(conn, buf[idType+1:idDmLen+1]); err != nil { 76 | glog.Errorf("Read from remote err:%v\r\n", err) 77 | return 78 | } 79 | reqStart, reqEnd = idDm0, int(idDm0+buf[idDmLen]+lenDmBase) 80 | default: 81 | err = fmt.Errorf("addr type %d not supported", addrType&connection.AddrMask) 82 | return 83 | } 84 | 85 | if _, err = io.ReadFull(conn, buf[reqStart:reqEnd]); err != nil { 86 | return 87 | } 88 | 89 | glog.V(5).Infof("Got string from remote %v \r\n", buf[reqStart:reqEnd]) 90 | // Return string for typeIP is not most efficient, but browsers (Chrome, 91 | // Safari, Firefox) all seems using typeDm exclusively. So this is not a 92 | // big problem. 93 | switch addrType & connection.AddrMask { 94 | case typeIPv4: 95 | host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() 96 | case typeIPv6: 97 | host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() 98 | case typeDm: 99 | host = string(buf[idDm0 : idDm0+buf[idDmLen]]) 100 | } 101 | glog.V(5).Infof("Got host from remote: %v\r\n", host) 102 | // parse port 103 | port := binary.BigEndian.Uint16(buf[reqEnd-2 : reqEnd]) 104 | host = net.JoinHostPort(host, strconv.Itoa(int(port))) 105 | // if specified one time auth enabled, we should verify this 106 | if auth || addrType&connection.OneTimeAuthMask > 0 { 107 | ota = true 108 | if _, err = io.ReadFull(conn, buf[reqEnd:reqEnd+lenHmacSha1]); err != nil { 109 | return 110 | } 111 | iv := conn.GetIv() 112 | key := conn.GetKey() 113 | actualHmacSha1Buf := util.HmacSha1(append(iv, key...), buf[:reqEnd]) 114 | if !bytes.Equal(buf[reqEnd:reqEnd+lenHmacSha1], actualHmacSha1Buf) { 115 | err = fmt.Errorf("verify one time auth failed, iv=%v key=%v data=%v", iv, key, buf[:reqEnd]) 116 | return 117 | } 118 | } 119 | return 120 | } 121 | 122 | const logCntDelta = 100 123 | 124 | var connCnt int 125 | var nextLogConnCnt int = logCntDelta 126 | 127 | type isClosed struct { 128 | isClosed bool 129 | } 130 | 131 | func handleConnection(conn *connection.Conn, auth bool, timeout time.Duration) { 132 | var host string 133 | 134 | connCnt++ // this maybe not accurate, but should be enough 135 | if connCnt-nextLogConnCnt >= 0 { 136 | // XXX There's no xadd in the atomic package, so it's difficult to log 137 | // the message only once with low cost. Also note nextLogConnCnt maybe 138 | // added twice for current peak connection number level. 139 | log.Printf("Number of client connections reaches %d\n", nextLogConnCnt) 140 | nextLogConnCnt += logCntDelta 141 | } 142 | 143 | // function arguments are always evaluated, so surround debug statement 144 | // with if statement 145 | glog.V(5).Infof("new client %s->%s\n", conn.RemoteAddr().String(), conn.LocalAddr()) 146 | 147 | closed := false 148 | defer func() { 149 | glog.V(5).Infof("closed pipe %s<->%s\n", conn.RemoteAddr(), host) 150 | connCnt-- 151 | if !closed { 152 | conn.Close() 153 | } 154 | }() 155 | 156 | host, ota, err := getRequest(conn, auth, timeout) 157 | if err != nil { 158 | glog.Errorf("error getting request %v<->%v err:%v", conn.RemoteAddr(), conn.LocalAddr(), err) 159 | return 160 | } 161 | glog.V(5).Infof("connection host:%v ota:%v \r\n", host, ota) 162 | remote, err := net.Dial("tcp", host) 163 | if err != nil { 164 | if ne, ok := err.(*net.OpError); ok && (ne.Err == syscall.EMFILE || ne.Err == syscall.ENFILE) { 165 | // log too many open file error 166 | // EMFILE is process reaches open file limits, ENFILE is system limit 167 | glog.Errorf("dial error:%v\r\n", err) 168 | } else { 169 | glog.Errorf(" connecting to:%v occur err:%v", host, err) 170 | } 171 | return 172 | } 173 | defer func() { 174 | if !closed { 175 | remote.Close() 176 | } 177 | }() 178 | 179 | glog.V(5).Infof("piping %s<->%s ota=%v connOta=%v", conn.RemoteAddr(), host, ota, conn.IsOta()) 180 | 181 | if ota { 182 | go connection.PipeThenCloseOta(conn, remote, timeout) 183 | } else { 184 | go connection.PipeThenClose(conn, remote, timeout) 185 | } 186 | connection.PipeThenClose(remote, conn, timeout) 187 | closed = true 188 | return 189 | } 190 | 191 | // func updatePasswd() { 192 | // log.Println("updating password") 193 | // newconfig, err := ss.ParseConfig(configFile) 194 | // if err != nil { 195 | // log.Printf("error parsing config file %s to update password: %v\n", configFile, err) 196 | // return 197 | // } 198 | // oldconfig := config 199 | // config = newconfig 200 | // 201 | // if err = unifyPortPassword(config); err != nil { 202 | // return 203 | // } 204 | // for port, passwd := range config.PortPassword { 205 | // passwdManager.updatePortPasswd(port, passwd, config.Auth) 206 | // if oldconfig.PortPassword != nil { 207 | // delete(oldconfig.PortPassword, port) 208 | // } 209 | // } 210 | // // port password still left in the old config should be closed 211 | // for port, _ := range oldconfig.PortPassword { 212 | // log.Printf("closing port %s as it's deleted\n", port) 213 | // passwdManager.del(port) 214 | // } 215 | // log.Println("password updated") 216 | // } 217 | 218 | func Run(password, method string, port int, auth bool, timeout time.Duration) { 219 | portStr := strconv.Itoa(port) 220 | ln, err := net.Listen("tcp", ":"+portStr) 221 | if err != nil { 222 | log.Printf("error listening port %v: %v\n", port, err) 223 | os.Exit(1) 224 | } 225 | passwdManager.add(password, port, ln) 226 | var cipher *encrypt.Cipher 227 | log.Printf("server listening port %v ...\n", port) 228 | for { 229 | conn, err := ln.Accept() 230 | if err != nil { 231 | // listener maybe closed to update password 232 | glog.V(5).Infof("accept error: %v\n", err) 233 | return 234 | } 235 | // Creating cipher upon first connection. 236 | if cipher == nil { 237 | glog.Infof("creating cipher for port :%v and method: %v\r\n", port, method) 238 | cipher, err = encrypt.NewCipher(method, password) 239 | if err != nil { 240 | glog.Errorf("Error generating cipher for port: %s %v\n", port, err) 241 | conn.Close() 242 | continue 243 | } 244 | } 245 | go handleConnection(connection.NewConn(conn, cipher.Copy()), auth, timeout) 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /cmd/shadowss/udp_server.go: -------------------------------------------------------------------------------- 1 | package shadowss 2 | 3 | import ( 4 | "log" 5 | "net" 6 | "time" 7 | 8 | "shadowsocks-go/pkg/connection" 9 | encrypt "shadowsocks-go/pkg/connection" 10 | 11 | "github.com/golang/glog" 12 | ) 13 | 14 | var udp bool 15 | 16 | const dnsGoroutineNum = 64 17 | 18 | type UDPListener struct { 19 | password string 20 | listener *net.UDPConn 21 | } 22 | 23 | func RunUDP(password, method string, port int, timeout time.Duration) { 24 | var cipher *encrypt.Cipher 25 | listenPort := port 26 | 27 | conn, err := net.ListenUDP("udp", &net.UDPAddr{ 28 | IP: net.IPv6zero, 29 | Port: listenPort, 30 | }) 31 | defer conn.Close() 32 | if err != nil { 33 | glog.Fatalf("listening upd port: %v error:%v\r\n", port, err) 34 | } 35 | 36 | passwdManager.addUDP(password, port, conn) 37 | if err != nil { 38 | glog.Fatalf("error listening udp port %v: %v\n", port, err) 39 | return 40 | } 41 | cipher, err = encrypt.NewCipher(method, password) 42 | if err != nil { 43 | log.Printf("Error generating cipher for udp port: %s %v\n", port, err) 44 | conn.Close() 45 | } 46 | 47 | UDPConn := connection.NewUDPConn(conn, cipher, timeout) 48 | for { 49 | UDPConn.ReadAndHandleUDPReq() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "server":"127.0.0.1", 3 | "server_port":8388, 4 | "local_port":1080, 5 | "password":"barfoo!", 6 | "method": "aes-128-cfb", 7 | "timeout":600 8 | } 9 | -------------------------------------------------------------------------------- /deb/DEBIAN/conffiles: -------------------------------------------------------------------------------- 1 | /etc/shadowsocks/config.json 2 | /etc/init.d/shadowsocks 3 | -------------------------------------------------------------------------------- /deb/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Package: shadowsocks-go 2 | Version: VER-1 3 | Section: net 4 | Priority: optional 5 | Architecture: any 6 | Depends: 7 | Maintainer: CYF 8 | Description: shadowsocks-go is the go port of shadowsocks, a light weight tunneling proxy 9 | This package contains only the server. 10 | -------------------------------------------------------------------------------- /deb/DEBIAN/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | if [ -x "/etc/init.d/shadowsocks" ]; then 4 | update-rc.d shadowsocks defaults >/dev/null 5 | fi 6 | -------------------------------------------------------------------------------- /deb/DEBIAN/postrm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | # Automatically added by dh_installinit 4 | if [ "$1" = "purge" ] ; then 5 | update-rc.d shadowsocks remove >/dev/null 6 | fi 7 | # End automatically added section 8 | -------------------------------------------------------------------------------- /deb/DEBIAN/prerm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | # Automatically added by dh_installinit 4 | if [ -x "/etc/init.d/shadowsocks" ]; then 5 | invoke-rc.d shadowsocks stop || exit $? 6 | fi 7 | # End automatically added section 8 | -------------------------------------------------------------------------------- /deb/etc/init.d/shadowsocks: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Start/stop shadowsocks. 3 | # 4 | ### BEGIN INIT INFO 5 | # Provides: shadowsocks 6 | # Required-Start: 7 | # Required-Stop: 8 | # Should-Start: 9 | # Should-Stop: 10 | # Default-Start: 2 3 4 5 11 | # Default-Stop: 0 1 6 12 | # Short-Description: shadowsocks is a lightweight tunneling proxy 13 | # Description: Modified from Linode's nginx fastcgi startup script 14 | ### END INIT INFO 15 | 16 | # Note: this script requires sudo in order to run shadowsocks as the specified 17 | # user. 18 | 19 | BIN=/usr/bin/shadowsocks-server 20 | CONFIG_FILE=/etc/shadowsocks/config.json 21 | LOG_FILE=/var/log/shadowsocks 22 | USER=nobody 23 | GROUP=nobody 24 | PID_DIR=/var/run 25 | PID_FILE=$PID_DIR/shadowsocks.pid 26 | RET_VAL=0 27 | 28 | [ -x $BIN ] || exit 0 29 | 30 | check_running() { 31 | if [[ -r $PID_FILE ]]; then 32 | read PID <$PID_FILE 33 | if [[ -d "/proc/$PID" ]]; then 34 | return 0 35 | else 36 | rm -f $PID_FILE 37 | return 1 38 | fi 39 | else 40 | return 2 41 | fi 42 | } 43 | 44 | do_status() { 45 | check_running 46 | case $? in 47 | 0) 48 | echo "shadowsocks running with PID $PID" 49 | ;; 50 | 1) 51 | echo "shadowsocks not running, remove PID file $PID_FILE" 52 | ;; 53 | 2) 54 | echo "Could not find PID file $PID_FILE, shadowsocks does not appear to be running" 55 | ;; 56 | esac 57 | return 0 58 | } 59 | 60 | do_start() { 61 | if [[ ! -d $PID_DIR ]]; then 62 | echo "creating PID dir" 63 | mkdir $PID_DIR || echo "failed creating PID directory $PID_DIR"; exit 1 64 | chown $USER:$GROUP $PID_DIR || echo "failed creating PID directory $PID_DIR"; exit 1 65 | chmod 0770 $PID_DIR 66 | fi 67 | if check_running; then 68 | echo "shadowsocks already running with PID $PID" 69 | return 0 70 | fi 71 | if [[ ! -r $CONFIG_FILE ]]; then 72 | echo "config file $CONFIG_FILE not found" 73 | return 1 74 | fi 75 | echo "starting shadowsocks" 76 | # sudo will set the group to the primary group of $USER 77 | sudo -u $USER $BIN -c $CONFIG_FILE >>$LOG_FILE & 78 | PID=$! 79 | echo $PID > $PID_FILE 80 | sleep 0.3 81 | if ! check_running; then 82 | echo "start failed" 83 | return 1 84 | fi 85 | echo "shadowsocks running with PID $PID" 86 | return 0 87 | } 88 | 89 | do_stop() { 90 | if check_running; then 91 | echo "stopping shadowsocks with PID $PID" 92 | kill $PID 93 | rm -f $PID_FILE 94 | else 95 | echo "Could not find PID file $PID_FILE" 96 | fi 97 | } 98 | 99 | do_restart() { 100 | do_stop 101 | do_start 102 | } 103 | 104 | case "$1" in 105 | start|stop|restart|status) 106 | do_$1 107 | ;; 108 | *) 109 | echo "Usage: shadowsocks {start|stop|restart|status}" 110 | RET_VAL=1 111 | ;; 112 | esac 113 | 114 | exit $RET_VAL 115 | -------------------------------------------------------------------------------- /deb/etc/shadowsocks/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "server":"127.0.0.1", 3 | "server_port":8388, 4 | "local_port":1080, 5 | "password":"barfoo!", 6 | "timeout":0 7 | } 8 | -------------------------------------------------------------------------------- /docs/shadowsocks-libev-client.md: -------------------------------------------------------------------------------- 1 | # shadowsocks libev client test 2 | 3 | shadowss --alsologtostderr=true --config-file="/home/seanchann/pgitlab/cloud/src/shadowsocks-go/sample-config/server-multi-port.json" -v=6 4 | 5 | 6 | go run main.go --alsologtostderr=true --config-file="/home/seanchann/pgitlab/cloud/src/shadowsocks-go/sample-config/server-multi-port.json" -v=6 7 | 8 | https://github.com/EasyPi/docker-shadowsocks-libev/blob/master/docker-compose.yml 9 | 10 | shadowsocks-server -p 18387 -k barfoo -m aes-128-cfb -c config.json -t 60 11 | ``` 12 | ./ss-local -s 47.89.189.237 -p 18087 -k 44c096c8e1e5fd7a85ea0bbe4623778d -m aes-256-cfb -l 11080 -b 0.0.0.0 13 | ./ss-local -s 192.168.137.191 -p 18387 -k barfoo -m aes-128-cfb -l 11080 -b 0.0.0.0 14 | ``` 15 | "host":"0.0.0.0", 16 | "port":18387, 17 | "encrypt":"aes-128-cfb", 18 | "password":"barfoo", 19 | "enableOTA":false, 20 | "timeout":60 21 | -------------------------------------------------------------------------------- /mu/.gitignore: -------------------------------------------------------------------------------- 1 | mu 2 | config.conf 3 | ss.log -------------------------------------------------------------------------------- /mu/README.md: -------------------------------------------------------------------------------- 1 | # shadowsocks-go mu 2 | 3 | Shadowsocks-go multiple user port. 4 | 5 | 6 | ### Require 7 | 8 | * Redis >= 3.0 9 | 10 | 11 | ### Install 12 | 13 | ``` 14 | go get 15 | go build 16 | ./mu 17 | ``` 18 | 19 | 20 | ### Config 21 | 22 | ``` 23 | cp examlple.conf config.conf 24 | ``` 25 | 26 | 27 | ### FAQ 28 | 29 | #### enable debug log 30 | 31 | ``` 32 | ./mu -debug 33 | ``` 34 | 35 | 36 | ### known issue 37 | 38 | * mysql mode cant update traffic 39 | 40 | 41 | -------------------------------------------------------------------------------- /mu/boot.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | "time" 8 | 9 | "github.com/golang/glog" 10 | muconfig "github.com/orvice/shadowsocks-go/mu/config" 11 | "github.com/orvice/shadowsocks-go/mu/user" 12 | ss "github.com/orvice/shadowsocks-go/shadowsocks" 13 | ) 14 | 15 | var configFile string 16 | var config *ss.Config 17 | 18 | func boot() { 19 | var err error 20 | 21 | // log.SetOutput(os.Stdout) 22 | 23 | switch muconfig.Conf.Base.Client { 24 | case "mysql": 25 | InitMySqlClient() 26 | case "webapi": 27 | InitWebApi() 28 | default: 29 | InitWebApi() 30 | } 31 | 32 | glog.Infoln("Get User from", muconfig.Conf.Base.Client) 33 | client := user.GetClient() 34 | users, err := client.GetUsers() 35 | if err != nil { 36 | glog.Errorf("Get User Error", err) 37 | os.Exit(0) 38 | } 39 | Log.Info(len(users)) 40 | // clear storage 41 | storage.ClearAll() 42 | bootUsers(users) 43 | time.Sleep(muconfig.Conf.Base.CheckTime * time.Second) 44 | 45 | go func() { 46 | for { 47 | go func() { 48 | // check users 49 | users, err = client.GetUsers() 50 | if err != nil { 51 | Log.Error("Get User Error", err) 52 | // os.Exit(0) 53 | } 54 | Log.Debug(users) 55 | checkUsers(users) 56 | Log.Info("check finish...") 57 | }() 58 | time.Sleep(muconfig.Conf.Base.CheckTime * time.Second) 59 | } 60 | }() 61 | 62 | go func() { 63 | for { 64 | go func() { 65 | // sync users 66 | users, err = client.GetUsers() 67 | if err != nil { 68 | Log.Error(err) 69 | // os.Exit(0) 70 | } 71 | syncUsers(users) 72 | Log.Info("sync finish...start logging online count") 73 | err = client.LogNodeOnlineUser(storage.GetOnlineUsersCount(users)) 74 | if err != nil { 75 | Log.Error(err) 76 | } 77 | 78 | err = client.UpdateNodeInfo() 79 | if err != nil { 80 | Log.Error(err) 81 | } 82 | 83 | }() 84 | time.Sleep(muconfig.Conf.Base.SyncTime * time.Second) 85 | } 86 | }() 87 | 88 | } 89 | 90 | // 第一次启动 91 | func bootUsers(users []user.User) { 92 | for _, user := range users { 93 | var err error 94 | Log.Info(user.GetUserInfo()) 95 | 96 | isExists, err := storage.Exists(user) 97 | if err != nil { 98 | Log.Error("check exists error: ", err) 99 | continue 100 | } 101 | if isExists { 102 | Log.Info("user is exists....skip...", user.GetPort()) 103 | continue 104 | } 105 | go runWithCustomMethod(user) 106 | 107 | err = storage.StoreUser(user.GetUserInfo()) 108 | if err != nil { 109 | Log.Error(err) 110 | } 111 | 112 | // 首次启动睡眠 113 | time.Sleep(100 * time.Millisecond) 114 | } 115 | } 116 | 117 | // check users 118 | func checkUsers(users []user.User) { 119 | for _, user := range users { 120 | Log.Debug("check user for ", user.GetPort()) 121 | 122 | isExists, err := storage.Exists(user) 123 | if err != nil { 124 | Log.Error("check exists error: ", err) 125 | continue 126 | } 127 | if !isExists && user.IsEnable() { 128 | Log.Info("new user to run", user) 129 | err := storage.StoreUser(user.GetUserInfo()) 130 | if err != nil { 131 | Log.Error("store error: ", err) 132 | } 133 | go runWithCustomMethod(user) 134 | continue 135 | } 136 | if !user.IsEnable() && isExists { 137 | Log.Info("user would be disable,port: ", user.GetPort()) 138 | passwdManager.del(strconv.Itoa(user.GetPort())) 139 | err := storage.Del(user) 140 | if err != nil { 141 | Log.Error(err) 142 | } 143 | continue 144 | } 145 | 146 | if !user.IsEnable() { 147 | continue 148 | } 149 | 150 | sUser, err := storage.GetUserInfo(user) 151 | if err != nil { 152 | Log.Error("get user error: ", err) 153 | continue 154 | } 155 | if sUser.Passwd != user.GetPasswd() || sUser.Method != user.GetMethod() { 156 | Log.Info(fmt.Sprintf("user port [%v] passwd or method change ,restart user...", user.GetPort())) 157 | passwdManager.del(strconv.Itoa(user.GetPort())) 158 | err := storage.StoreUser(user.GetUserInfo()) 159 | if err != nil { 160 | Log.Error("store error: ", err) 161 | } 162 | go runWithCustomMethod(user) 163 | } 164 | } 165 | } 166 | 167 | // sync users traffic 168 | func syncUsers(users []user.User) { 169 | for _, user := range users { 170 | size, err := storage.GetSize(user) 171 | if err != nil { 172 | Log.Error(fmt.Sprintf("get size fail for port:%d", user.GetPort()), err) 173 | continue 174 | } 175 | if size < 1024 { 176 | continue 177 | } 178 | err = user.UpdateTraffic(int(size)) 179 | if err != nil { 180 | Log.Error(fmt.Sprintf("update size fail for port:%d", user.GetPort()), err) 181 | continue 182 | } 183 | Log.Info(fmt.Sprintf("success update traffic usage for port %d,total update size %d", user.GetPort(), size)) 184 | err = storage.SetSize(user, 0) 185 | if err != nil { 186 | Log.Error(fmt.Sprintf("set storage size to 0 fail for port:%d", user.GetPort()), err) 187 | continue 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /mu/build_linux.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -------------------------------------------------------------------------------- /mu/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | muconfig "github.com/orvice/shadowsocks-go/mu/config" 7 | "github.com/orvice/shadowsocks-go/mu/mysql" 8 | "github.com/orvice/shadowsocks-go/mu/user" 9 | webapi "github.com/orvice/shadowsocks-go/mu/webapi" 10 | ) 11 | 12 | func InitMySqlClient() error { 13 | initMysqlClientConfig() 14 | conf := muconfig.GetConf().Mysql 15 | client := new(mysql.Client) 16 | dbType := "mysql" 17 | dbuser := conf.User 18 | password := conf.Pass 19 | host := conf.Host 20 | dbname := conf.Db 21 | table := conf.Table 22 | 23 | err := client.Boot(dbType, dbuser, password, host, dbname) 24 | if err != nil { 25 | return err 26 | } 27 | client.SetTable(table) 28 | mysql.SetClient(client) 29 | user.SetClient(client) 30 | if err != nil { 31 | Log.Error(err) 32 | os.Exit(0) 33 | } 34 | return nil 35 | } 36 | 37 | func InitWebApi() error { 38 | err := initWebApiConfig() 39 | if err != nil { 40 | Log.Error(err) 41 | os.Exit(0) 42 | } 43 | conf := muconfig.GetConf().WebApi 44 | webapi.SetClient(webapi.NewClient()) 45 | webapi.SetBaseUrl(conf.Url) 46 | webapi.SetKey(conf.Key) 47 | webapi.SetNodeId(conf.NodeId) 48 | user.SetClient(webapi.GetClient()) 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /mu/config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/Terry-Mao/goconf" 5 | muconfig "github.com/orvice/shadowsocks-go/mu/config" 6 | ) 7 | 8 | var ( 9 | conf = goconf.New() 10 | ) 11 | 12 | func initMysqlClientConfig() error { 13 | mysql := new(muconfig.MySql) 14 | if err := conf.Unmarshal(mysql); err != nil { 15 | return err 16 | } 17 | muconfig.Conf.SetMysql(mysql) 18 | return nil 19 | } 20 | 21 | func initWebApiConfig() error { 22 | webapi := new(muconfig.WebApi) 23 | if err := conf.Unmarshal(webapi); err != nil { 24 | return err 25 | } 26 | muconfig.Conf.SetWebApi(webapi) 27 | return nil 28 | } 29 | 30 | func InitConfig() error { 31 | 32 | if err := conf.Parse("config.conf"); err != nil { 33 | return err 34 | } 35 | 36 | redis := new(muconfig.Redis) 37 | base := new(muconfig.Base) 38 | 39 | if err := conf.Unmarshal(redis); err != nil { 40 | return err 41 | } 42 | 43 | if err := conf.Unmarshal(base); err != nil { 44 | return err 45 | } 46 | muconfig.Conf.SetRedis(redis) 47 | muconfig.Conf.SetBase(base) 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /mu/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | var Conf = new(Config) 8 | 9 | type Config struct { 10 | WebApi *WebApi 11 | Mysql *MySql 12 | Redis *Redis 13 | Base *Base 14 | } 15 | 16 | func GetConf() *Config { 17 | return Conf 18 | } 19 | 20 | func (c *Config) SetMysql(m *MySql) { 21 | c.Mysql = m 22 | } 23 | 24 | func (c *Config) SetRedis(r *Redis) { 25 | c.Redis = r 26 | } 27 | 28 | func (c *Config) SetBase(b *Base) { 29 | c.Base = b 30 | } 31 | 32 | func (c *Config) SetWebApi(w *WebApi) { 33 | c.WebApi = w 34 | } 35 | 36 | type Base struct { 37 | N float32 `goconf:"base:N"` 38 | IP string `goconf:"base:ip"` 39 | Client string `goconf:"base:client"` 40 | CheckTime time.Duration `goconf:"base:checktime"` 41 | SyncTime time.Duration `goconf:"base:synctime"` 42 | } 43 | 44 | type WebApi struct { 45 | Url string `goconf:"webapi:url"` 46 | Key string `goconf:"webapi:key"` 47 | NodeId int `goconf:"webapi:node_id"` 48 | } 49 | 50 | type MySql struct { 51 | Host string `goconf:"mysql:host"` 52 | User string `goconf:"mysql:user"` 53 | Pass string `goconf:"mysql:pass"` 54 | Db string `goconf:"mysql:db"` 55 | Table string `goconf:"mysql:table"` 56 | } 57 | 58 | type Redis struct { 59 | Host string `goconf:"redis:host"` 60 | Pass string `goconf:"redis:pass"` 61 | Db int64 `goconf:"redis:db"` 62 | } 63 | -------------------------------------------------------------------------------- /mu/example.conf: -------------------------------------------------------------------------------- 1 | ## 配置说明 https://github.com/orvice/shadowsocks-go/wiki/Config 2 | [base] 3 | N 1 4 | ip 0.0.0.0 5 | client webapi 6 | checktime 60 7 | synctime 60 8 | 9 | [webapi] 10 | url http://sspanel.dev/mu 11 | key key 12 | node_id 1 13 | 14 | [mysql] 15 | host 127.0.0.1:3306 16 | user user 17 | pass pass 18 | db db 19 | table table 20 | 21 | [redis] 22 | host localhost:6379 23 | # if no passwd set,comment this line 24 | pass "" 25 | db 1 26 | -------------------------------------------------------------------------------- /mu/flag.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | ) 6 | 7 | var ( 8 | debug bool 9 | logPath string 10 | configPath string 11 | ) 12 | 13 | func InitFlag() { 14 | // flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname") 15 | flag.StringVar(&logPath, "log_path", "./ss.log", "log file path") 16 | flag.StringVar(&configPath, "config_path", "./config.conf", "log file path") 17 | flag.BoolVar(&debug, "debug", false, "debug") 18 | flag.Parse() 19 | } 20 | -------------------------------------------------------------------------------- /mu/func.go: -------------------------------------------------------------------------------- 1 | /** 2 | @author orvice https://github.com/orvice/shadowsocks-go 3 | @author Lupino https://github.com/Lupino/shadowsocks-auth 4 | */ 5 | 6 | package main 7 | 8 | import ( 9 | "bufio" 10 | "bytes" 11 | "encoding/binary" 12 | "fmt" 13 | "github.com/cyfdecyf/leakybuf" 14 | "github.com/orvice/shadowsocks-go/mu/user" 15 | ss "github.com/orvice/shadowsocks-go/shadowsocks" 16 | "io" 17 | "net" 18 | "net/http" 19 | "os" 20 | "os/signal" 21 | "strconv" 22 | "strings" 23 | "sync" 24 | "syscall" 25 | "time" 26 | ) 27 | 28 | const ( 29 | idType = 0 // address type index 30 | idIP0 = 1 // ip addres start index 31 | idDmLen = 1 // domain address length index 32 | idDm0 = 2 // domain address start index 33 | 34 | typeIPv4 = 1 // type is ipv4 address 35 | typeDm = 3 // type is domain address 36 | typeIPv6 = 4 // type is ipv6 address 37 | 38 | lenIPv4 = net.IPv4len + 2 // ipv4 + 2port 39 | lenIPv6 = net.IPv6len + 2 // ipv6 + 2port 40 | lenDmBase = 2 // 1addrLen + 2port, plus addrLen 41 | lenHmacSha1 = 10 42 | ) 43 | 44 | var ssdebug ss.DebugLog 45 | 46 | func getRequest(conn *ss.Conn, auth bool) (host string, res_size int, ota bool, err error) { 47 | var n int 48 | ss.SetReadTimeout(conn) 49 | 50 | // buf size should at least have the same size with the largest possible 51 | // request size (when addrType is 3, domain name has at most 256 bytes) 52 | // 1(addrType) + 1(lenByte) + 256(max length address) + 2(port) + 10(hmac-sha1) 53 | buf := make([]byte, 270) 54 | // read till we get possible domain length field 55 | if n, err = io.ReadFull(conn, buf[:idType+1]); err != nil { 56 | return 57 | } 58 | res_size += n 59 | 60 | var reqStart, reqEnd int 61 | addrType := buf[idType] 62 | switch addrType & ss.AddrMask { 63 | case typeIPv4: 64 | reqStart, reqEnd = idIP0, idIP0+lenIPv4 65 | case typeIPv6: 66 | reqStart, reqEnd = idIP0, idIP0+lenIPv6 67 | case typeDm: 68 | if n, err = io.ReadFull(conn, buf[idType+1:idDmLen+1]); err != nil { 69 | return 70 | } 71 | reqStart, reqEnd = idDm0, int(idDm0+buf[idDmLen]+lenDmBase) 72 | default: 73 | err = fmt.Errorf("addr type %d not supported", addrType&ss.AddrMask) 74 | return 75 | } 76 | res_size += n 77 | 78 | if n, err = io.ReadFull(conn, buf[reqStart:reqEnd]); err != nil { 79 | return 80 | } 81 | res_size += n 82 | 83 | // Return string for typeIP is not most efficient, but browsers (Chrome, 84 | // Safari, Firefox) all seems using typeDm exclusively. So this is not a 85 | // big problem. 86 | switch addrType & ss.AddrMask { 87 | case typeIPv4: 88 | host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() 89 | case typeIPv6: 90 | host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() 91 | case typeDm: 92 | host = string(buf[idDm0 : idDm0+buf[idDmLen]]) 93 | } 94 | // parse port 95 | port := binary.BigEndian.Uint16(buf[reqEnd-2 : reqEnd]) 96 | host = net.JoinHostPort(host, strconv.Itoa(int(port))) 97 | // if specified one time auth enabled, we should verify this 98 | if auth || addrType&ss.OneTimeAuthMask > 0 { 99 | ota = true 100 | if n, err = io.ReadFull(conn, buf[reqEnd:reqEnd+lenHmacSha1]); err != nil { 101 | return 102 | } 103 | iv := conn.GetIv() 104 | key := conn.GetKey() 105 | actualHmacSha1Buf := ss.HmacSha1(append(iv, key...), buf[:reqEnd]) 106 | if !bytes.Equal(buf[reqEnd:reqEnd+lenHmacSha1], actualHmacSha1Buf) { 107 | err = fmt.Errorf("verify one time auth failed, iv=%v key=%v data=%v", iv, key, buf[:reqEnd]) 108 | return 109 | } 110 | res_size += n 111 | } 112 | return 113 | } 114 | 115 | const logCntDelta = 100 116 | 117 | var connCnt int 118 | var nextLogConnCnt int = logCntDelta 119 | 120 | func handleConnection(user user.User, conn *ss.Conn, auth bool) { 121 | var host string 122 | 123 | connCnt++ // this maybe not accurate, but should be enough 124 | if connCnt-nextLogConnCnt >= 0 { 125 | // XXX There's no xadd in the atomic package, so it's difficult to log 126 | // the message only once with low cost. Also note nextLogConnCnt maybe 127 | // added twice for current peak connection number level. 128 | Log.Debug("Number of client connections reaches %d\n", nextLogConnCnt) 129 | nextLogConnCnt += logCntDelta 130 | } 131 | 132 | // function arguments are always evaluated, so surround debug statement 133 | // with if statement 134 | Log.Debug(fmt.Sprintf("new client %s->%s\n", conn.RemoteAddr().String(), conn.LocalAddr())) 135 | closed := false 136 | defer func() { 137 | if ssdebug { 138 | Log.Debug(fmt.Sprintf("closed pipe %s<->%s\n", conn.RemoteAddr(), host)) 139 | } 140 | connCnt-- 141 | if !closed { 142 | conn.Close() 143 | } 144 | }() 145 | 146 | host, res_size, ota, err := getRequest(conn, auth) 147 | if err != nil { 148 | Log.Error("error getting request", conn.RemoteAddr(), conn.LocalAddr(), err) 149 | return 150 | } 151 | Log.Info(fmt.Sprintf("[port-%d]connecting %s ", user.GetPort(), host)) 152 | remote, err := net.Dial("tcp", host) 153 | if err != nil { 154 | if ne, ok := err.(*net.OpError); ok && (ne.Err == syscall.EMFILE || ne.Err == syscall.ENFILE) { 155 | // log too many open file error 156 | // EMFILE is process reaches open file limits, ENFILE is system limit 157 | Log.Error("dial error:", err) 158 | } else { 159 | Log.Error("error connecting to:", host, err) 160 | } 161 | return 162 | } 163 | defer func() { 164 | if !closed { 165 | remote.Close() 166 | } 167 | }() 168 | 169 | // debug conn info 170 | Log.Debug(fmt.Sprintf("%d conn debug: local addr: %s | remote addr: %s network: %s ", user.GetPort(), 171 | conn.LocalAddr().String(), conn.RemoteAddr().String(), conn.RemoteAddr().Network())) 172 | err = storage.IncrSize(user, res_size) 173 | if err != nil { 174 | Log.Error(err) 175 | return 176 | } 177 | err = storage.MarkUserOnline(user) 178 | if err != nil { 179 | Log.Error(err) 180 | return 181 | } 182 | Log.Debug(fmt.Sprintf("[port-%d] store size: %d", user.GetPort(), res_size)) 183 | 184 | Log.Info(fmt.Sprintf("piping %s<->%s ota=%v connOta=%v", conn.RemoteAddr(), host, ota, conn.IsOta())) 185 | 186 | if ota { 187 | go PipeThenCloseOta(conn, remote, false, host, user) 188 | } else { 189 | go PipeThenClose(conn, remote, false, host, user) 190 | } 191 | 192 | PipeThenClose(remote, conn, true, host, user) 193 | closed = true 194 | return 195 | } 196 | 197 | type PortListener struct { 198 | password string 199 | listener net.Listener 200 | } 201 | 202 | type PasswdManager struct { 203 | sync.Mutex 204 | portListener map[string]*PortListener 205 | } 206 | 207 | func (pm *PasswdManager) add(port, password string, listener net.Listener) { 208 | pm.Lock() 209 | pm.portListener[port] = &PortListener{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) del(port string) { 221 | pl, ok := pm.get(port) 222 | if !ok { 223 | return 224 | } 225 | pl.listener.Close() 226 | pm.Lock() 227 | delete(pm.portListener, port) 228 | pm.Unlock() 229 | } 230 | 231 | var passwdManager = PasswdManager{portListener: map[string]*PortListener{}} 232 | 233 | func waitSignal() { 234 | var sigChan = make(chan os.Signal, 1) 235 | signal.Notify(sigChan, syscall.SIGHUP) 236 | for sig := range sigChan { 237 | if sig == syscall.SIGHUP { 238 | } else { 239 | // is this going to happen? 240 | Log.Printf("caught signal %v, exit", sig) 241 | os.Exit(0) 242 | } 243 | } 244 | } 245 | 246 | func runWithCustomMethod(user user.User) { 247 | // port, password string, Cipher *ss.Cipher 248 | port := strconv.Itoa(user.GetPort()) 249 | password := user.GetPasswd() 250 | ln, err := net.Listen("tcp", ":"+port) 251 | if err != nil { 252 | Log.Error(fmt.Sprintf("error listening port %v: %v\n", port, err)) 253 | os.Exit(1) 254 | } 255 | passwdManager.add(port, password, ln) 256 | cipher, err, auth := user.GetCipher() 257 | if err != nil { 258 | return 259 | } 260 | Log.Info(fmt.Sprintf("server listening port %v ...\n", port)) 261 | for { 262 | conn, err := ln.Accept() 263 | if err != nil { 264 | // listener maybe closed to update password 265 | Log.Debug(fmt.Sprintf("accept error: %v\n", err)) 266 | return 267 | } 268 | // Creating cipher upon first connection. 269 | if cipher == nil { 270 | Log.Debug("creating cipher for port:", port) 271 | method := user.GetMethod() 272 | 273 | if strings.HasSuffix(method, "-auth") { 274 | method = method[:len(method)-5] 275 | auth = true 276 | } else { 277 | auth = false 278 | } 279 | 280 | cipher, err = ss.NewCipher(method, password) 281 | if err != nil { 282 | Log.Error(fmt.Sprintf("Error generating cipher for port: %s %v\n", port, err)) 283 | conn.Close() 284 | continue 285 | } 286 | } 287 | go handleConnection(user, ss.NewConn(conn, cipher.Copy()), auth) 288 | } 289 | } 290 | 291 | const bufSize = 4096 292 | const nBuf = 2048 293 | 294 | func PipeThenClose(src, dst net.Conn, is_res bool, host string, user user.User) { 295 | var pipeBuf = leakybuf.NewLeakyBuf(nBuf, bufSize) 296 | defer dst.Close() 297 | buf := pipeBuf.Get() 298 | // defer pipeBuf.Put(buf) 299 | var size int 300 | 301 | for { 302 | SetReadTimeout(src) 303 | n, err := src.Read(buf) 304 | // read may return EOF with n > 0 305 | // should always process n > 0 bytes before handling error 306 | if n > 0 { 307 | size, err = dst.Write(buf[0:n]) 308 | if is_res { 309 | err = storage.IncrSize(user, size) 310 | if err != nil { 311 | Log.Error(err) 312 | } 313 | Log.Debug(fmt.Sprintf("[port-%d] store size: %d", user.GetPort(), size)) 314 | } 315 | if err != nil { 316 | Log.Debug("write:", err) 317 | break 318 | } 319 | } 320 | if err != nil || n == 0 { 321 | // Always "use of closed network connection", but no easy way to 322 | // identify this specific error. So just leave the error along for now. 323 | // More info here: https://code.google.com/p/go/issues/detail?id=4373 324 | break 325 | } 326 | } 327 | return 328 | } 329 | 330 | func PipeThenCloseOta(src *ss.Conn, dst net.Conn, is_res bool, host string, user user.User) { 331 | const ( 332 | dataLenLen = 2 333 | hmacSha1Len = 10 334 | idxData0 = dataLenLen + hmacSha1Len 335 | ) 336 | 337 | defer func() { 338 | dst.Close() 339 | }() 340 | var pipeBuf = leakybuf.NewLeakyBuf(nBuf, bufSize) 341 | buf := pipeBuf.Get() 342 | // sometimes it have to fill large block 343 | for i := 1; ; i += 1 { 344 | SetReadTimeout(src) 345 | n, err := io.ReadFull(src, buf[:dataLenLen+hmacSha1Len]) 346 | if err != nil { 347 | if err == io.EOF { 348 | break 349 | } 350 | Log.Debug(fmt.Sprintf("conn=%p #%v read header error n=%v: %v", src, i, n, err)) 351 | break 352 | } 353 | dataLen := binary.BigEndian.Uint16(buf[:dataLenLen]) 354 | expectedHmacSha1 := buf[dataLenLen:idxData0] 355 | 356 | var dataBuf []byte 357 | if len(buf) < int(idxData0+dataLen) { 358 | dataBuf = make([]byte, dataLen) 359 | } else { 360 | dataBuf = buf[idxData0 : idxData0+dataLen] 361 | } 362 | if n, err := io.ReadFull(src, dataBuf); err != nil { 363 | if err == io.EOF { 364 | break 365 | } 366 | Log.Debug(fmt.Sprintf("conn=%p #%v read data error n=%v: %v", src, i, n, err)) 367 | break 368 | } 369 | chunkIdBytes := make([]byte, 4) 370 | chunkId := src.GetAndIncrChunkId() 371 | binary.BigEndian.PutUint32(chunkIdBytes, chunkId) 372 | actualHmacSha1 := ss.HmacSha1(append(src.GetIv(), chunkIdBytes...), dataBuf) 373 | if !bytes.Equal(expectedHmacSha1, actualHmacSha1) { 374 | Log.Debug(fmt.Sprintf("conn=%p #%v read data hmac-sha1 mismatch, iv=%v chunkId=%v src=%v dst=%v len=%v expeced=%v actual=%v", src, i, src.GetIv(), chunkId, src.RemoteAddr(), dst.RemoteAddr(), dataLen, expectedHmacSha1, actualHmacSha1)) 375 | break 376 | } 377 | 378 | if n, err := dst.Write(dataBuf); err != nil { 379 | Log.Debug(fmt.Sprintf("conn=%p #%v write data error n=%v: %v", dst, i, n, err)) 380 | break 381 | } 382 | if is_res { 383 | err := storage.IncrSize(user, n) 384 | if err != nil { 385 | Log.Error(err) 386 | } 387 | Log.Debug(fmt.Sprintf("[port-%d] store size: %d", user.GetPort(), n)) 388 | } 389 | } 390 | return 391 | } 392 | 393 | var readTimeout time.Duration 394 | 395 | func SetReadTimeout(c net.Conn) { 396 | if readTimeout != 0 { 397 | c.SetReadDeadline(time.Now().Add(readTimeout)) 398 | } 399 | } 400 | 401 | func showConn(raw_req_header, raw_res_header []byte, host string, user user.User, size int, is_http bool) { 402 | if size == 0 { 403 | Log.Error(fmt.Sprintf("[port-%d] Error: request %s cancel", user.GetPort(), host)) 404 | return 405 | } 406 | if is_http { 407 | req, _ := http.ReadRequest(bufio.NewReader(bytes.NewReader(raw_req_header))) 408 | if req == nil { 409 | lines := bytes.SplitN(raw_req_header, []byte(" "), 2) 410 | Log.Debug(fmt.Sprintf("%s http://%s/ \"Unknow\" HTTP/1.1 unknow user-port: %d size: %d\n", lines[0], host, user.GetPort(), size)) 411 | return 412 | } 413 | res, _ := http.ReadResponse(bufio.NewReader(bytes.NewReader(raw_res_header)), req) 414 | statusCode := 200 415 | if res != nil { 416 | statusCode = res.StatusCode 417 | } 418 | Log.Debug(fmt.Sprintf("%s http://%s%s \"%s\" %s %d user-port: %d size: %d\n", req.Method, req.Host, req.URL.String(), req.Header.Get("user-agent"), req.Proto, statusCode, user.GetPort(), size)) 419 | } else { 420 | Log.Debug(fmt.Sprintf("CONNECT %s \"NONE\" NONE NONE user-port: %d size: %d\n", host, user.GetPort(), size)) 421 | } 422 | 423 | } 424 | 425 | func checkHttp(extra []byte, conn *ss.Conn) (is_http bool, data []byte, err error) { 426 | var buf []byte 427 | var methods = []string{"GET", "HEAD", "POST", "PUT", "TRACE", "OPTIONS", "DELETE"} 428 | is_http = false 429 | if extra == nil || len(extra) < 10 { 430 | buf = make([]byte, 10) 431 | if _, err = io.ReadFull(conn, buf); err != nil { 432 | return 433 | } 434 | } 435 | 436 | if buf == nil { 437 | data = extra 438 | } else if extra == nil { 439 | data = buf 440 | } else { 441 | buffer := bytes.NewBuffer(extra) 442 | buffer.Write(buf) 443 | data = buffer.Bytes() 444 | } 445 | 446 | for _, method := range methods { 447 | if bytes.HasPrefix(data, []byte(method)) { 448 | is_http = true 449 | break 450 | } 451 | } 452 | return 453 | } 454 | -------------------------------------------------------------------------------- /mu/log.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "os" 6 | 7 | "github.com/Sirupsen/logrus" 8 | "github.com/orvice/shadowsocks-go/mu/log" 9 | ) 10 | 11 | var Log = logrus.New() 12 | 13 | func InitLog() { 14 | // Log.Formatter = new(logrus.JSONFormatter) 15 | if logPath != "" { 16 | f, err := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600) 17 | if err != nil { 18 | panic(err) 19 | } 20 | writer := io.MultiWriter(os.Stdout, f) 21 | Log.Out = writer 22 | } 23 | if debug { 24 | Log.Level = logrus.DebugLevel 25 | Log.Debug("debug on") 26 | } 27 | log.SetLogClient(Log) 28 | } 29 | -------------------------------------------------------------------------------- /mu/log/client.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "github.com/orvice/shadowsocks-go/mu/user" 5 | ) 6 | 7 | type Client interface { 8 | Info(user user.User, args ...interface{}) 9 | Error(user user.User, args ...interface{}) 10 | Debug(user user.User, args ...interface{}) 11 | } 12 | -------------------------------------------------------------------------------- /mu/log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "github.com/Sirupsen/logrus" 5 | ) 6 | 7 | var ( 8 | Log = logrus.New() 9 | ) 10 | 11 | func SetLogClient(client *logrus.Logger) { 12 | Log = client 13 | } 14 | -------------------------------------------------------------------------------- /mu/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | _ "net/http/pprof" 7 | "os" 8 | ) 9 | 10 | func main() { 11 | var err error 12 | InitFlag() 13 | err = InitConfig() 14 | if err != nil { 15 | Log.Error(err) 16 | os.Exit(0) 17 | } 18 | InitLog() 19 | err = InitRedis() 20 | if err != nil { 21 | Log.Error("boot redis fail: ", err) 22 | os.Exit(0) 23 | } 24 | 25 | if debug { 26 | go func() { 27 | log.Println(http.ListenAndServe("127.0.0.1:6060", nil)) 28 | }() 29 | } 30 | 31 | boot() 32 | waitSignal() 33 | } 34 | -------------------------------------------------------------------------------- /mu/mysql/mysql.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "strings" 5 | "time" 6 | 7 | "github.com/golang/glog" 8 | "github.com/jinzhu/gorm" 9 | "github.com/orvice/shadowsocks-go/mu/log" 10 | "github.com/orvice/shadowsocks-go/mu/user" 11 | ss "github.com/orvice/shadowsocks-go/shadowsocks" 12 | ) 13 | 14 | var client *Client 15 | 16 | func SetClient(c *Client) { 17 | client = c 18 | } 19 | 20 | func NewClient() *Client { 21 | mclient := new(Client) 22 | return mclient 23 | } 24 | 25 | type Client struct { 26 | db *gorm.DB 27 | table string 28 | } 29 | 30 | type User struct { 31 | id int 32 | port int 33 | passwd string 34 | method string 35 | enable int 36 | transferEnable int 37 | u int 38 | d int 39 | } 40 | 41 | func (c *Client) SetDb(db *gorm.DB) { 42 | c.db = db 43 | } 44 | 45 | func (c User) TableName() string { 46 | return tableName 47 | } 48 | 49 | func (u *User) GetPort() int { 50 | return u.port 51 | } 52 | 53 | func (u *User) GetPasswd() string { 54 | return u.passwd 55 | } 56 | 57 | func (u *User) GetMethod() string { 58 | return u.method 59 | } 60 | 61 | func (u *User) IsEnable() bool { 62 | if u.enable == 0 { 63 | return false 64 | } 65 | if u.u+u.d > u.transferEnable { 66 | return false 67 | } 68 | return true 69 | } 70 | 71 | func (u *User) GetCipher() (*ss.Cipher, error, bool) { 72 | method := u.method 73 | auth := false 74 | 75 | if strings.HasSuffix(method, "-auth") { 76 | method = method[:len(method)-5] 77 | auth = true 78 | } 79 | s, e := ss.NewCipher(method, u.passwd) 80 | return s, e, auth 81 | } 82 | 83 | func (u *User) UpdateTraffic(storageSize int) error { 84 | return client.db.Model(u).Where("id = ?", u.id).UpdateColumn("d", gorm.Expr("d + ?", storageSize)).UpdateColumn("t", time.Now().Unix()).Error 85 | } 86 | 87 | func (u *User) GetUserInfo() user.UserInfo { 88 | return user.UserInfo{ 89 | Passwd: u.passwd, 90 | Port: u.port, 91 | Method: u.method, 92 | } 93 | } 94 | 95 | func (c *Client) GetUsers() ([]user.User, error) { 96 | glog.Infoln("get mysql users") 97 | var datas []*User 98 | rows, err := c.db.Model(User{}).Select("id, passwd, port, method,enable,transfer_enable,u,d").Rows() 99 | if err != nil { 100 | log.Log.Error(err) 101 | var users []user.User 102 | return users, err 103 | } 104 | defer rows.Close() 105 | for rows.Next() { 106 | var data User 107 | err := rows.Scan(&data.id, &data.passwd, &data.port, &data.method, &data.enable, &data.transferEnable, &data.u, &data.d) 108 | if err != nil { 109 | log.Log.Error(err) 110 | continue 111 | } 112 | datas = append(datas, &data) 113 | } 114 | log.Log.Info(len(datas)) 115 | users := make([]user.User, len(datas)) 116 | for k, v := range datas { 117 | users[k] = v 118 | } 119 | return users, nil 120 | } 121 | 122 | func (c *Client) LogNodeOnlineUser(onlineUserCount int) error { 123 | return nil 124 | } 125 | 126 | func (c *Client) UpdateNodeInfo() error { 127 | return nil 128 | } 129 | -------------------------------------------------------------------------------- /mu/mysql/mysql_boot.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "fmt" 5 | _ "github.com/go-sql-driver/mysql" 6 | "github.com/jinzhu/gorm" 7 | _ "github.com/lib/pq" 8 | ) 9 | 10 | var ( 11 | tableName string 12 | ) 13 | 14 | func genConnStr(user, password, host, dbname string) string { 15 | return fmt.Sprintf("%s:%s@tcp(%s)/%s?parseTime=True", user, password, host, dbname) 16 | } 17 | 18 | func (c *Client) Boot(dbType, user, password, host, dbname string) error { 19 | var err error 20 | db, err := gorm.Open(dbType, genConnStr(user, password, host, dbname)) 21 | if err != nil { 22 | return err 23 | } 24 | c.SetDb(db) 25 | c.db.DB() 26 | return c.db.DB().Ping() 27 | } 28 | 29 | func (c *Client) SetTable(table string) { 30 | tableName = table 31 | c.table = table 32 | } 33 | -------------------------------------------------------------------------------- /mu/redis.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | 7 | muconfig "github.com/orvice/shadowsocks-go/mu/config" 8 | "github.com/orvice/shadowsocks-go/mu/user" 9 | redis "gopkg.in/redis.v3" 10 | ) 11 | 12 | const ( 13 | DefaultExpireTime = 0 14 | DefaultOnlineKeyExpireTime = time.Minute * 5 15 | ) 16 | 17 | var Redis = new(RedisClient) 18 | 19 | type RedisClient struct { 20 | client *redis.Client 21 | } 22 | 23 | func (r *RedisClient) SetClient(client *redis.Client) { 24 | r.client = client 25 | } 26 | 27 | func (r *RedisClient) GetUserInfo(u user.User) (user.UserInfo, error) { 28 | var user user.UserInfo 29 | val, err := r.client.Get(genUserInfoKey(u.GetUserInfo())).Result() 30 | if err != nil { 31 | return user, err 32 | } 33 | err = json.Unmarshal([]byte(val), &user) 34 | return user, err 35 | } 36 | 37 | func (r *RedisClient) StoreUser(user user.UserInfo) error { 38 | data, err := json.Marshal(user) 39 | if err != nil { 40 | return err 41 | } 42 | err = r.client.Set(genUserInfoKey(user), data, DefaultExpireTime).Err() 43 | return err 44 | } 45 | 46 | func (r *RedisClient) Exists(u user.User) (bool, error) { 47 | return r.client.Exists(genUserInfoKey(u.GetUserInfo())).Result() 48 | } 49 | 50 | func (r *RedisClient) Del(u user.User) error { 51 | return r.client.Del(genUserInfoKey(u.GetUserInfo())).Err() 52 | } 53 | 54 | func (r *RedisClient) ClearAll() error { 55 | return r.client.FlushAll().Err() 56 | } 57 | 58 | // traffic 59 | func (r *RedisClient) IncrSize(u user.User, size int) error { 60 | key := genUserFlowKey(u.GetUserInfo()) 61 | incrSize := int(float32(size)) 62 | isExits, err := r.client.Exists(key).Result() 63 | if err != nil { 64 | return err 65 | } 66 | if !isExits { 67 | return r.client.Set(key, incrSize, DefaultExpireTime).Err() 68 | } 69 | return r.client.IncrBy(key, int64(incrSize)).Err() 70 | } 71 | 72 | func (r *RedisClient) GetSize(u user.User) (int64, error) { 73 | key := genUserFlowKey(u.GetUserInfo()) 74 | isExits, err := r.client.Exists(key).Result() 75 | if err != nil { 76 | return 0, err 77 | } 78 | if !isExits { 79 | return 0, nil 80 | } 81 | return r.client.Get(key).Int64() 82 | } 83 | 84 | func (r *RedisClient) SetSize(u user.User, size int) error { 85 | key := genUserFlowKey(u.GetUserInfo()) 86 | return r.client.Set(key, size, DefaultExpireTime).Err() 87 | } 88 | 89 | func (r *RedisClient) ClearSize() error { 90 | return nil 91 | } 92 | 93 | func (r *RedisClient) MarkUserOnline(u user.User) error { 94 | key := genUserOnlineKey(u.GetUserInfo()) 95 | return r.client.Set(key, "1", DefaultOnlineKeyExpireTime).Err() 96 | } 97 | 98 | func (r *RedisClient) IsUserOnline(u user.User) bool { 99 | key := genUserOnlineKey(u.GetUserInfo()) 100 | isExits, err := r.client.Exists(key).Result() 101 | if err != nil { 102 | return false 103 | } 104 | return isExits 105 | } 106 | 107 | func (r *RedisClient) GetOnlineUsersCount(users []user.User) int { 108 | count := 0 109 | for _, v := range users { 110 | if r.IsUserOnline(v) { 111 | count++ 112 | } 113 | } 114 | return count 115 | } 116 | 117 | func InitRedis() error { 118 | client := redis.NewClient(&redis.Options{ 119 | Addr: muconfig.Conf.Redis.Host, 120 | Password: muconfig.Conf.Redis.Pass, // no password set 121 | DB: muconfig.Conf.Redis.Db, // use default DB 122 | }) 123 | 124 | pong, err := client.Ping().Result() 125 | if err != nil { 126 | return err 127 | } 128 | Log.Info(pong) 129 | Redis.SetClient(client) 130 | // set storage 131 | SetStorage(Redis) 132 | return nil 133 | } 134 | -------------------------------------------------------------------------------- /mu/storage.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/orvice/shadowsocks-go/mu/user" 5 | ) 6 | 7 | var storage Storage 8 | 9 | func SetStorage(s Storage) { 10 | storage = s 11 | } 12 | 13 | type Storage interface { 14 | GetUserInfo(user.User) (user.UserInfo, error) 15 | StoreUser(user.UserInfo) error 16 | Exists(user.User) (bool, error) 17 | Del(user.User) error 18 | ClearAll() error 19 | IncrSize(u user.User, size int) error 20 | GetSize(u user.User) (int64, error) 21 | SetSize(u user.User, size int) error 22 | MarkUserOnline(u user.User) error 23 | IsUserOnline(u user.User) bool 24 | GetOnlineUsersCount(u []user.User) int 25 | } 26 | -------------------------------------------------------------------------------- /mu/system/client.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "os/exec" 5 | "strings" 6 | ) 7 | 8 | type Client struct { 9 | } 10 | 11 | func GetLoad() (string, error) { 12 | output, err := exec.Command("cat", "/proc/loadavg").Output() 13 | if err != nil { 14 | return "", err 15 | } 16 | 17 | return string(output), nil 18 | } 19 | 20 | func GetUptime() (string, error) { 21 | output, err := exec.Command("cat", "/proc/uptime").Output() 22 | if err != nil { 23 | return "", err 24 | } 25 | loadAry := strings.Split(string(output), " ") 26 | return loadAry[0], nil 27 | } 28 | -------------------------------------------------------------------------------- /mu/system/client_test.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestGetUptime(t *testing.T) { 8 | output, err := GetUptime() 9 | if err != nil { 10 | t.Error(err) 11 | } 12 | t.Log(output) 13 | } 14 | 15 | func TestGetLoad(t *testing.T) { 16 | output, err := GetLoad() 17 | if err != nil { 18 | t.Error(err) 19 | } 20 | t.Log(output) 21 | } 22 | -------------------------------------------------------------------------------- /mu/user/user.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | ss "github.com/orvice/shadowsocks-go/shadowsocks" 5 | ) 6 | 7 | var ( 8 | client = NewClient() 9 | ) 10 | 11 | func NewClient() Client { 12 | var client Client 13 | return client 14 | } 15 | 16 | func GetClient() Client { 17 | return client 18 | } 19 | 20 | func SetClient(c Client) { 21 | client = c 22 | } 23 | 24 | type Client interface { 25 | GetUsers() ([]User, error) 26 | LogNodeOnlineUser(onlineUserCount int) error 27 | UpdateNodeInfo() error 28 | } 29 | 30 | type User interface { 31 | GetPort() int 32 | GetPasswd() string 33 | GetMethod() string 34 | IsEnable() bool 35 | GetCipher() (*ss.Cipher, error, bool) 36 | UpdateTraffic(storageSize int) error 37 | GetUserInfo() UserInfo 38 | } 39 | 40 | type UserInfo struct { 41 | Passwd string 42 | Port int 43 | Method string 44 | } 45 | -------------------------------------------------------------------------------- /mu/utils.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/orvice/shadowsocks-go/mu/user" 6 | ) 7 | 8 | func genUserInfoKey(user user.UserInfo) string { 9 | return fmt.Sprintf("userinfo:%v", user.Port) 10 | } 11 | 12 | func genUserFlowKey(user user.UserInfo) string { 13 | return fmt.Sprintf("userflow:%v", user.Port) 14 | } 15 | 16 | func genUserOnlineKey(user user.UserInfo) string { 17 | return fmt.Sprintf("useronline:%v", user.Port) 18 | } 19 | -------------------------------------------------------------------------------- /mu/webapi/ret.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | type BaseRet struct { 4 | Ret int `json:"ret"` 5 | Msg string `json:"msg"` 6 | } 7 | 8 | type UserDataRet struct { 9 | BaseRet 10 | Data []User `json:"data"` 11 | } 12 | -------------------------------------------------------------------------------- /mu/webapi/user.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | 7 | "github.com/orvice/shadowsocks-go/mu/user" 8 | ss "github.com/orvice/shadowsocks-go/shadowsocks" 9 | ) 10 | 11 | type User struct { 12 | Id int `json:"id"` 13 | Port int `json:"port"` 14 | Passwd string `json:"passwd"` 15 | Method string `json:"method"` 16 | Enable int `json:"enable"` 17 | TransferEnable int64 `json:"transfer_enable"` 18 | U int64 `json:"u"` 19 | D int64 `json:"d"` 20 | } 21 | 22 | func (u User) GetPort() int { 23 | return u.Port 24 | } 25 | 26 | func (u User) GetPasswd() string { 27 | return u.Passwd 28 | } 29 | 30 | func (u User) GetMethod() string { 31 | return u.Method 32 | } 33 | 34 | func (u User) IsEnable() bool { 35 | if u.Enable == 0 { 36 | return false 37 | } 38 | if u.TransferEnable < (u.U + u.D) { 39 | return false 40 | } 41 | return true 42 | } 43 | 44 | func (u User) GetCipher() (*ss.Cipher, error, bool) { 45 | method := u.Method 46 | auth := false 47 | 48 | if strings.HasSuffix(method, "-auth") { 49 | method = method[:len(method)-5] 50 | auth = true 51 | } 52 | s, e := ss.NewCipher(method, u.Passwd) 53 | return s, e, auth 54 | } 55 | 56 | func (u User) UpdateTraffic(storageSize int) error { 57 | dStr := strconv.Itoa(storageSize) 58 | uStr := string('0') 59 | return client.UpdateTraffic(u.Id, uStr, dStr) 60 | } 61 | 62 | func (u User) GetUserInfo() user.UserInfo { 63 | user := user.UserInfo{ 64 | Passwd: u.Passwd, 65 | Port: u.Port, 66 | Method: u.Method, 67 | } 68 | return user 69 | } 70 | -------------------------------------------------------------------------------- /mu/webapi/web.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | 7 | "github.com/golang/glog" 8 | "github.com/orvice/shadowsocks-go/mu/log" 9 | "github.com/orvice/shadowsocks-go/mu/system" 10 | "github.com/orvice/shadowsocks-go/mu/user" 11 | ) 12 | 13 | var ( 14 | client = new(Client) 15 | UpdateTrafficFail = errors.New("Update Traffic Failed ") 16 | UpdateOnlineCountFail = errors.New("Update Online Count Failed") 17 | ) 18 | 19 | type Client struct { 20 | baseUrl string 21 | key string 22 | nodeId int 23 | } 24 | 25 | func NewClient() *Client { 26 | return new(Client) 27 | } 28 | 29 | func SetClient(c *Client) { 30 | client = c 31 | } 32 | 33 | func GetClient() *Client { 34 | return client 35 | } 36 | 37 | func (c *Client) setBaseUrl(baseUrl string) { 38 | c.baseUrl = baseUrl 39 | } 40 | 41 | func (c *Client) setKey(key string) { 42 | c.key = key 43 | } 44 | 45 | func (c *Client) setNodeId(id int) { 46 | c.nodeId = id 47 | } 48 | 49 | func (c *Client) GetUsers() ([]user.User, error) { 50 | var tempUser []user.User 51 | glog.Infoln("request to remote server:", c.genGetUsersUrl()) 52 | res, err := c.httpGet(c.genGetUsersUrl()) 53 | if err != nil { 54 | return tempUser, err 55 | } 56 | log.Log.Debug(res) 57 | var resData UserDataRet 58 | err = json.Unmarshal([]byte(res), &resData) 59 | if err != nil { 60 | return tempUser, err 61 | } 62 | userData := resData.Data 63 | log.Log.Info(len(userData)) 64 | users := make([]user.User, len(userData)) 65 | for k, v := range userData { 66 | users[k] = v 67 | } 68 | return users, nil 69 | } 70 | 71 | func (c *Client) UpdateTraffic(userId int, u, d string) error { 72 | res, err := c.httpPostUserTraffic(userId, u, d) 73 | if err != nil { 74 | return nil 75 | } 76 | var ret BaseRet 77 | err = json.Unmarshal([]byte(res), &ret) 78 | if err != nil { 79 | return err 80 | } 81 | if ret.Ret == 0 { 82 | return errors.New(ret.Msg) 83 | } 84 | log.Log.Debug("update traffic debug:", ret.Msg) 85 | return nil 86 | } 87 | 88 | func (c *Client) LogNodeOnlineUser(onlineUserCount int) error { 89 | res, err := c.httpPostNodeOnlineCount(onlineUserCount) 90 | if err != nil { 91 | return nil 92 | } 93 | var ret BaseRet 94 | err = json.Unmarshal([]byte(res), &ret) 95 | if err != nil { 96 | return err 97 | } 98 | if ret.Ret == 0 { 99 | return UpdateOnlineCountFail 100 | } 101 | return nil 102 | } 103 | 104 | func (c *Client) UpdateNodeInfo() error { 105 | uptime, err := system.GetUptime() 106 | if err != nil { 107 | log.Log.Error(err) 108 | uptime = "0" 109 | } 110 | 111 | load, err := system.GetLoad() 112 | if err != nil { 113 | load = "0 0 0" 114 | } 115 | 116 | res, err := c.httpPostNodeInfo(load, uptime) 117 | if err != nil { 118 | return nil 119 | } 120 | var ret BaseRet 121 | err = json.Unmarshal([]byte(res), &ret) 122 | if err != nil { 123 | return err 124 | } 125 | if ret.Ret == 0 { 126 | return UpdateOnlineCountFail 127 | } 128 | return nil 129 | } 130 | -------------------------------------------------------------------------------- /mu/webapi/web_boot.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | func SetBaseUrl(baseUrl string) { 4 | client.setBaseUrl(baseUrl) 5 | } 6 | 7 | func SetKey(key string) { 8 | client.setKey(key) 9 | } 10 | 11 | func SetNodeId(nodeId int) { 12 | client.setNodeId(nodeId) 13 | } 14 | -------------------------------------------------------------------------------- /mu/webapi/web_net.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "net/url" 8 | "strconv" 9 | ) 10 | 11 | func (c *Client) genGetUsersUrl() string { 12 | return fmt.Sprintf("%s/users?key=%s", c.baseUrl, c.key) 13 | } 14 | 15 | func (c *Client) genUserTrafficUrl(id int) string { 16 | return fmt.Sprintf("%s/users/%d/traffic?key=%s", c.baseUrl, id, c.key) 17 | } 18 | 19 | func (c *Client) genNodeOnlineCountUrl(id int) string { 20 | return fmt.Sprintf("%s/nodes/%d/online_count?key=%s", c.baseUrl, id, c.key) 21 | } 22 | 23 | func (c *Client) genNodeInfoUrl(id int) string { 24 | return fmt.Sprintf("%s/nodes/%d/info?key=%s", c.baseUrl, id, c.key) 25 | } 26 | 27 | func (c *Client) httpGet(urlStr string) (string, error) { 28 | resp, err := http.Get(urlStr) 29 | if err != nil { 30 | return "", err 31 | } 32 | defer resp.Body.Close() 33 | body, err := ioutil.ReadAll(resp.Body) 34 | if err != nil { 35 | return "", err 36 | } 37 | return string(body), nil 38 | } 39 | 40 | func (c *Client) httpPostUserTraffic(userId int, u, d string) (string, error) { 41 | nodeId := strconv.Itoa(c.nodeId) 42 | urlStr := c.genUserTrafficUrl(userId) 43 | resp, err := http.PostForm(urlStr, 44 | url.Values{"u": {u}, "d": {d}, "node_id": {nodeId}}) 45 | 46 | if err != nil { 47 | return "", err 48 | } 49 | 50 | defer resp.Body.Close() 51 | body, err := ioutil.ReadAll(resp.Body) 52 | if err != nil { 53 | return "", err 54 | } 55 | return string(body), nil 56 | } 57 | 58 | func (c *Client) httpPostNodeOnlineCount(count int) (string, error) { 59 | urlStr := c.genNodeOnlineCountUrl(c.nodeId) 60 | countStr := strconv.Itoa(count) 61 | resp, err := http.PostForm(urlStr, 62 | url.Values{"count": {countStr}}) 63 | 64 | if err != nil { 65 | return "", err 66 | } 67 | 68 | defer resp.Body.Close() 69 | body, err := ioutil.ReadAll(resp.Body) 70 | if err != nil { 71 | return "", err 72 | } 73 | return string(body), nil 74 | } 75 | 76 | func (c *Client) httpPostNodeInfo(load, uptime string) (string, error) { 77 | urlStr := c.genNodeInfoUrl(c.nodeId) 78 | resp, err := http.PostForm(urlStr, 79 | url.Values{"load": {load}, "uptime": {uptime}}) 80 | 81 | if err != nil { 82 | return "", err 83 | } 84 | 85 | defer resp.Body.Close() 86 | body, err := ioutil.ReadAll(resp.Body) 87 | if err != nil { 88 | return "", err 89 | } 90 | return string(body), nil 91 | } 92 | -------------------------------------------------------------------------------- /mu/webapi/web_test.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestWeb(t *testing.T) { 8 | t.Log("test") 9 | } 10 | -------------------------------------------------------------------------------- /pkg/config/config_client.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "os" 7 | ) 8 | 9 | // ClientConfig manage client configure file 10 | type ClientConfig struct { 11 | ListenPort int `json:"listenPort"` 12 | Server []ConnectionInfo `json:"servers"` 13 | } 14 | 15 | // NewClientConfig new a ClientConfig handler 16 | func NewClientConfig() *ClientConfig { 17 | return &ClientConfig{ 18 | ListenPort: 1080, 19 | Server: make([]ConnectionInfo, 1, 10), 20 | } 21 | } 22 | 23 | // Parse input a config file for parse 24 | func (c *ClientConfig) Parse(file string) error { 25 | fileHandle, err := os.Open(file) // For read access. 26 | if err != nil { 27 | return err 28 | } 29 | defer fileHandle.Close() 30 | 31 | data, err := ioutil.ReadAll(fileHandle) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | config := &ClientConfig{} 37 | if err = json.Unmarshal(data, config); err != nil { 38 | return err 39 | } 40 | 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /pkg/config/config_server.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "os" 7 | 8 | "github.com/golang/glog" 9 | ) 10 | 11 | // ServerConfig for server configure 12 | type ServerConfig struct { 13 | Clients []ConnectionInfo `json:"clients"` 14 | } 15 | 16 | // ServerCfg server configure handle 17 | var ServerCfg = NewServerConfig() 18 | 19 | // NewServerConfig new a ClientConfig handler 20 | func NewServerConfig() *ServerConfig { 21 | return &ServerConfig{ 22 | Clients: make([]ConnectionInfo, 1, 30), 23 | } 24 | } 25 | 26 | func (s *ServerConfig) verifyConfig() error { 27 | return nil 28 | } 29 | 30 | // Parse input a config file for parse 31 | func (s *ServerConfig) Parse(file string) error { 32 | fileHandle, err := os.Open(file) // For read access. 33 | if err != nil { 34 | return err 35 | } 36 | defer fileHandle.Close() 37 | 38 | data, err := ioutil.ReadAll(fileHandle) 39 | if err != nil { 40 | return err 41 | } 42 | 43 | //config := &ServerConfig{} 44 | if err = json.Unmarshal(data, s); err != nil { 45 | return err 46 | } 47 | //copy(s, config) 48 | glog.V(5).Infoln("Got Configure clients:%+v", s) 49 | 50 | return s.verifyConfig() 51 | } 52 | -------------------------------------------------------------------------------- /pkg/config/interface.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | // ConnectionInfo description connection base information 4 | type ConnectionInfo struct { 5 | Host string `json:"host"` 6 | Port int `json:"port"` 7 | EncryptMethod string `json:"encrypt"` 8 | Password string `json:"password"` 9 | EnableOTA bool `json:"enableOTA"` 10 | Timeout int `json:"timeout"` 11 | } 12 | 13 | // Config as a interface for configure file implement 14 | type Config interface { 15 | Parse(file string) error 16 | } 17 | -------------------------------------------------------------------------------- /pkg/connection/conn.go: -------------------------------------------------------------------------------- 1 | package connection 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "io" 7 | "net" 8 | "strconv" 9 | 10 | "shadowsocks-go/pkg/util" 11 | ) 12 | 13 | const ( 14 | OneTimeAuthMask byte = 0x10 15 | AddrMask byte = 0xf 16 | ) 17 | 18 | type Conn struct { 19 | net.Conn 20 | *Cipher 21 | readBuf []byte 22 | writeBuf []byte 23 | chunkId uint32 24 | } 25 | 26 | func NewConn(c net.Conn, cipher *Cipher) *Conn { 27 | return &Conn{ 28 | Conn: c, 29 | Cipher: cipher, 30 | readBuf: leakyBuf.Get(), 31 | writeBuf: leakyBuf.Get()} 32 | } 33 | 34 | func (c *Conn) Close() error { 35 | leakyBuf.Put(c.readBuf) 36 | leakyBuf.Put(c.writeBuf) 37 | return c.Conn.Close() 38 | } 39 | 40 | func RawAddr(addr string) (buf []byte, err error) { 41 | host, portStr, err := net.SplitHostPort(addr) 42 | if err != nil { 43 | return nil, fmt.Errorf("shadowsocks: address error %s %v", addr, err) 44 | } 45 | port, err := strconv.Atoi(portStr) 46 | if err != nil { 47 | return nil, fmt.Errorf("shadowsocks: invalid port %s", addr) 48 | } 49 | 50 | hostLen := len(host) 51 | l := 1 + 1 + hostLen + 2 // addrType + lenByte + address + port 52 | buf = make([]byte, l) 53 | buf[0] = 3 // 3 means the address is domain name 54 | buf[1] = byte(hostLen) // host address length followed by host address 55 | copy(buf[2:], host) 56 | binary.BigEndian.PutUint16(buf[2+hostLen:2+hostLen+2], uint16(port)) 57 | return 58 | } 59 | 60 | // This is intended for use by users implementing a local socks proxy. 61 | // rawaddr shoud contain part of the data in socks request, starting from the 62 | // ATYP field. (Refer to rfc1928 for more information.) 63 | func DialWithRawAddr(rawaddr []byte, server string, cipher *Cipher) (c *Conn, err error) { 64 | conn, err := net.Dial("tcp", server) 65 | if err != nil { 66 | return 67 | } 68 | c = NewConn(conn, cipher) 69 | if cipher.ota { 70 | if c.enc == nil { 71 | if _, err = c.initEncrypt(); err != nil { 72 | return 73 | } 74 | } 75 | // since we have initEncrypt, we must send iv manually 76 | conn.Write(cipher.iv) 77 | rawaddr[0] |= OneTimeAuthMask 78 | rawaddr = util.OtaConnectAuth(cipher.iv, cipher.key, rawaddr) 79 | } 80 | if _, err = c.write(rawaddr); err != nil { 81 | c.Close() 82 | return nil, err 83 | } 84 | return 85 | } 86 | 87 | // addr should be in the form of host:port 88 | func Dial(addr, server string, cipher *Cipher) (c *Conn, err error) { 89 | ra, err := RawAddr(addr) 90 | if err != nil { 91 | return 92 | } 93 | return DialWithRawAddr(ra, server, cipher) 94 | } 95 | 96 | func (c *Conn) GetIv() (iv []byte) { 97 | iv = make([]byte, len(c.iv)) 98 | copy(iv, c.iv) 99 | return 100 | } 101 | 102 | func (c *Conn) GetKey() (key []byte) { 103 | key = make([]byte, len(c.key)) 104 | copy(key, c.key) 105 | return 106 | } 107 | 108 | func (c *Conn) IsOta() bool { 109 | return c.ota 110 | } 111 | 112 | func (c *Conn) GetAndIncrChunkId() (chunkId uint32) { 113 | chunkId = c.chunkId 114 | c.chunkId += 1 115 | return 116 | } 117 | 118 | func (c *Conn) Read(b []byte) (n int, err error) { 119 | if c.dec == nil { 120 | iv := make([]byte, c.info.ivLen) 121 | if _, err = io.ReadFull(c.Conn, iv); err != nil { 122 | return 123 | } 124 | if err = c.initDecrypt(iv); err != nil { 125 | return 126 | } 127 | if len(c.iv) == 0 { 128 | c.iv = iv 129 | } 130 | } 131 | 132 | cipherData := c.readBuf 133 | if len(b) > len(cipherData) { 134 | cipherData = make([]byte, len(b)) 135 | } else { 136 | cipherData = cipherData[:len(b)] 137 | } 138 | 139 | n, err = c.Conn.Read(cipherData) 140 | if n > 0 { 141 | c.decrypt(b[0:n], cipherData[0:n]) 142 | } 143 | return 144 | } 145 | 146 | func (c *Conn) Write(b []byte) (n int, err error) { 147 | if c.ota { 148 | chunkId := c.GetAndIncrChunkId() 149 | b = util.OtaReqChunkAuth(c.iv, chunkId, b) 150 | } 151 | return c.write(b) 152 | } 153 | 154 | func (c *Conn) write(b []byte) (n int, err error) { 155 | var iv []byte 156 | if c.enc == nil { 157 | iv, err = c.initEncrypt() 158 | if err != nil { 159 | return 160 | } 161 | } 162 | 163 | cipherData := c.writeBuf 164 | dataSize := len(b) + len(iv) 165 | if dataSize > len(cipherData) { 166 | cipherData = make([]byte, dataSize) 167 | } else { 168 | cipherData = cipherData[:dataSize] 169 | } 170 | 171 | if iv != nil { 172 | // Put initialization vector in buffer, do a single write to send both 173 | // iv and data. 174 | copy(cipherData, iv) 175 | } 176 | 177 | c.encrypt(cipherData[len(iv):], b) 178 | n, err = c.Conn.Write(cipherData) 179 | return 180 | } 181 | -------------------------------------------------------------------------------- /pkg/connection/encrypt.go: -------------------------------------------------------------------------------- 1 | package connection 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 | "strings" 14 | 15 | "github.com/codahale/chacha20" 16 | "golang.org/x/crypto/blowfish" 17 | "golang.org/x/crypto/cast5" 18 | "golang.org/x/crypto/salsa20/salsa" 19 | ) 20 | 21 | var errEmptyPassword = errors.New("empty key") 22 | 23 | func md5sum(d []byte) []byte { 24 | h := md5.New() 25 | h.Write(d) 26 | return h.Sum(nil) 27 | } 28 | 29 | func evpBytesToKey(password string, keyLen int) (key []byte) { 30 | const md5Len = 16 31 | 32 | cnt := (keyLen-1)/md5Len + 1 33 | m := make([]byte, cnt*md5Len) 34 | copy(m, md5sum([]byte(password))) 35 | 36 | // Repeatedly call md5 until bytes generated is enough. 37 | // Each call to md5 uses data: prev md5 sum + password. 38 | d := make([]byte, md5Len+len(password)) 39 | start := 0 40 | for i := 1; i < cnt; i++ { 41 | start += md5Len 42 | copy(d, m[start-md5Len:start]) 43 | copy(d[md5Len:], password) 44 | copy(m[start:], md5sum(d)) 45 | } 46 | return m[:keyLen] 47 | } 48 | 49 | type DecOrEnc int 50 | 51 | const ( 52 | Decrypt DecOrEnc = iota 53 | Encrypt 54 | ) 55 | 56 | func newStream(block cipher.Block, err error, key, iv []byte, 57 | doe DecOrEnc) (cipher.Stream, error) { 58 | if err != nil { 59 | return nil, err 60 | } 61 | if doe == Encrypt { 62 | return cipher.NewCFBEncrypter(block, iv), nil 63 | } else { 64 | return cipher.NewCFBDecrypter(block, iv), nil 65 | } 66 | } 67 | 68 | func newAESStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { 69 | block, err := aes.NewCipher(key) 70 | return newStream(block, err, key, iv, doe) 71 | } 72 | 73 | func newDESStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { 74 | block, err := des.NewCipher(key) 75 | return newStream(block, err, key, iv, doe) 76 | } 77 | 78 | func newBlowFishStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { 79 | block, err := blowfish.NewCipher(key) 80 | return newStream(block, err, key, iv, doe) 81 | } 82 | 83 | func newCast5Stream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { 84 | block, err := cast5.NewCipher(key) 85 | return newStream(block, err, key, iv, doe) 86 | } 87 | 88 | func newRC4MD5Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { 89 | h := md5.New() 90 | h.Write(key) 91 | h.Write(iv) 92 | rc4key := h.Sum(nil) 93 | 94 | return rc4.NewCipher(rc4key) 95 | } 96 | 97 | func newChaCha20Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { 98 | return chacha20.New(key, iv) 99 | } 100 | 101 | type salsaStreamCipher struct { 102 | nonce [8]byte 103 | key [32]byte 104 | counter int 105 | } 106 | 107 | func (c *salsaStreamCipher) XORKeyStream(dst, src []byte) { 108 | var buf []byte 109 | padLen := c.counter % 64 110 | dataSize := len(src) + padLen 111 | if cap(dst) >= dataSize { 112 | buf = dst[:dataSize] 113 | } else if leakyBufSize >= dataSize { 114 | buf = leakyBuf.Get() 115 | defer leakyBuf.Put(buf) 116 | buf = buf[:dataSize] 117 | } else { 118 | buf = make([]byte, dataSize) 119 | } 120 | 121 | var subNonce [16]byte 122 | copy(subNonce[:], c.nonce[:]) 123 | binary.LittleEndian.PutUint64(subNonce[len(c.nonce):], uint64(c.counter/64)) 124 | 125 | // It's difficult to avoid data copy here. src or dst maybe slice from 126 | // Conn.Read/Write, which can't have padding. 127 | copy(buf[padLen:], src[:]) 128 | salsa.XORKeyStream(buf, buf, &subNonce, &c.key) 129 | copy(dst, buf[padLen:]) 130 | 131 | c.counter += len(src) 132 | } 133 | 134 | func newSalsa20Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { 135 | var c salsaStreamCipher 136 | copy(c.nonce[:], iv[:8]) 137 | copy(c.key[:], key[:32]) 138 | return &c, nil 139 | } 140 | 141 | type cipherInfo struct { 142 | keyLen int 143 | ivLen int 144 | newStream func(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) 145 | } 146 | 147 | var cipherMethod = map[string]*cipherInfo{ 148 | "aes-128-cfb": {16, 16, newAESStream}, 149 | "aes-192-cfb": {24, 16, newAESStream}, 150 | "aes-256-cfb": {32, 16, newAESStream}, 151 | "des-cfb": {8, 8, newDESStream}, 152 | "bf-cfb": {16, 8, newBlowFishStream}, 153 | "cast5-cfb": {16, 8, newCast5Stream}, 154 | "rc4-md5": {16, 16, newRC4MD5Stream}, 155 | "chacha20": {32, 8, newChaCha20Stream}, 156 | "salsa20": {32, 8, newSalsa20Stream}, 157 | } 158 | 159 | func CheckCipherMethod(method string) error { 160 | if method == "" { 161 | method = "aes-256-cfb" 162 | } 163 | _, ok := cipherMethod[method] 164 | if !ok { 165 | return errors.New("Unsupported encryption method: " + method) 166 | } 167 | return nil 168 | } 169 | 170 | type Cipher struct { 171 | enc cipher.Stream 172 | dec cipher.Stream 173 | key []byte 174 | info *cipherInfo 175 | ota bool // one-time auth 176 | iv []byte 177 | } 178 | 179 | // NewCipher creates a cipher that can be used in Dial() etc. 180 | // Use cipher.Copy() to create a new cipher with the same method and password 181 | // to avoid the cost of repeated cipher initialization. 182 | func NewCipher(method, password string) (c *Cipher, err error) { 183 | if password == "" { 184 | return nil, errEmptyPassword 185 | } 186 | var ota bool 187 | if strings.HasSuffix(strings.ToLower(method), "-ota") { 188 | method = method[:len(method)-4] // len("-ota") = 4 189 | ota = true 190 | } else { 191 | ota = false 192 | } 193 | mi, ok := cipherMethod[method] 194 | if !ok { 195 | return nil, errors.New("Unsupported encryption method: " + method) 196 | } 197 | 198 | key := evpBytesToKey(password, mi.keyLen) 199 | 200 | c = &Cipher{key: key, info: mi} 201 | 202 | if err != nil { 203 | return nil, err 204 | } 205 | c.ota = ota 206 | return c, nil 207 | } 208 | 209 | // Initializes the block cipher with CFB mode, returns IV. 210 | func (c *Cipher) initEncrypt() (iv []byte, err error) { 211 | if c.iv == nil { 212 | iv = make([]byte, c.info.ivLen) 213 | if _, err := io.ReadFull(rand.Reader, iv); err != nil { 214 | return nil, err 215 | } 216 | c.iv = iv 217 | } else { 218 | iv = c.iv 219 | } 220 | if c.enc == nil { 221 | c.enc, err = c.info.newStream(c.key, iv, Encrypt) 222 | if err != nil { 223 | return nil, err 224 | } 225 | } 226 | return 227 | } 228 | 229 | func (c *Cipher) initDecrypt(iv []byte) (err error) { 230 | c.dec, err = c.info.newStream(c.key, iv, Decrypt) 231 | return 232 | } 233 | 234 | func (c *Cipher) encrypt(dst, src []byte) { 235 | c.enc.XORKeyStream(dst, src) 236 | } 237 | 238 | func (c *Cipher) decrypt(dst, src []byte) { 239 | c.dec.XORKeyStream(dst, src) 240 | } 241 | 242 | // Copy creates a new cipher at it's initial state. 243 | func (c *Cipher) Copy() *Cipher { 244 | // This optimization maybe not necessary. But without this function, we 245 | // need to maintain a table cache for newTableCipher and use lock to 246 | // protect concurrent access to that cache. 247 | 248 | // AES and DES ciphers does not return specific types, so it's difficult 249 | // to create copy. But their initizliation time is less than 4000ns on my 250 | // 2.26 GHz Intel Core 2 Duo processor. So no need to worry. 251 | 252 | // Currently, blow-fish and cast5 initialization cost is an order of 253 | // maganitude slower than other ciphers. (I'm not sure whether this is 254 | // because the current implementation is not highly optimized, or this is 255 | // the nature of the algorithm.) 256 | 257 | nc := *c 258 | nc.enc = nil 259 | nc.dec = nil 260 | nc.ota = c.ota 261 | return &nc 262 | } 263 | -------------------------------------------------------------------------------- /pkg/connection/leakybuf.go: -------------------------------------------------------------------------------- 1 | // Provides leaky buffer, based on the example in Effective Go. 2 | package connection 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 | -------------------------------------------------------------------------------- /pkg/connection/pipe.go: -------------------------------------------------------------------------------- 1 | package connection 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "io" 7 | "net" 8 | "time" 9 | 10 | "shadowsocks-go/pkg/util" 11 | 12 | "github.com/golang/glog" 13 | ) 14 | 15 | func SetReadTimeout(c net.Conn, timeout time.Duration) { 16 | if timeout != 0 { 17 | c.SetReadDeadline(time.Now().Add(timeout)) 18 | } 19 | } 20 | 21 | // PipeThenClose copies data from src to dst, closes dst when done. 22 | func PipeThenClose(src, dst net.Conn, timeout time.Duration) { 23 | defer dst.Close() 24 | buf := leakyBuf.Get() 25 | defer leakyBuf.Put(buf) 26 | for { 27 | SetReadTimeout(src, timeout) 28 | n, err := src.Read(buf) 29 | // read may return EOF with n > 0 30 | // should always process n > 0 bytes before handling error 31 | if n > 0 { 32 | // Note: avoid overwrite err returned by Read. 33 | if _, err := dst.Write(buf[0:n]); err != nil { 34 | glog.Errorf("write err:%v", err) 35 | break 36 | } 37 | } 38 | if err != nil { 39 | // Always "use of closed network connection", but no easy way to 40 | // identify this specific error. So just leave the error along for now. 41 | // More info here: https://code.google.com/p/go/issues/detail?id=4373 42 | /* 43 | if bool(Debug) && err != io.EOF { 44 | Debug.Println("read:", err) 45 | } 46 | */ 47 | break 48 | } 49 | } 50 | } 51 | 52 | // PipeThenClose copies data from src to dst, closes dst when done, with ota verification. 53 | func PipeThenCloseOta(src *Conn, dst net.Conn, timeout time.Duration) { 54 | const ( 55 | dataLenLen = 2 56 | hmacSha1Len = 10 57 | idxData0 = dataLenLen + hmacSha1Len 58 | ) 59 | 60 | defer func() { 61 | dst.Close() 62 | }() 63 | // sometimes it have to fill large block 64 | buf := leakyBuf.Get() 65 | defer leakyBuf.Put(buf) 66 | i := 0 67 | for { 68 | i += 1 69 | SetReadTimeout(src, timeout) 70 | if n, err := io.ReadFull(src, buf[:dataLenLen+hmacSha1Len]); err != nil { 71 | if err == io.EOF { 72 | break 73 | } 74 | 75 | glog.Errorf("conn=%p #%v read header error n=%v: %v", src, i, n, err) 76 | break 77 | } 78 | dataLen := binary.BigEndian.Uint16(buf[:dataLenLen]) 79 | expectedHmacSha1 := buf[dataLenLen:idxData0] 80 | 81 | var dataBuf []byte 82 | if len(buf) < int(idxData0+dataLen) { 83 | dataBuf = make([]byte, dataLen) 84 | } else { 85 | dataBuf = buf[idxData0 : idxData0+dataLen] 86 | } 87 | if n, err := io.ReadFull(src, dataBuf); err != nil { 88 | if err == io.EOF { 89 | break 90 | } 91 | glog.V(5).Infof("conn=%p #%v read data error n=%v: %v", src, i, n, err) 92 | break 93 | } 94 | chunkIdBytes := make([]byte, 4) 95 | chunkId := src.GetAndIncrChunkId() 96 | binary.BigEndian.PutUint32(chunkIdBytes, chunkId) 97 | actualHmacSha1 := util.HmacSha1(append(src.GetIv(), chunkIdBytes...), dataBuf) 98 | if !bytes.Equal(expectedHmacSha1, actualHmacSha1) { 99 | glog.V(5).Infof("conn=%p #%v read data hmac-sha1 mismatch, iv=%v chunkId=%v src=%v dst=%v len=%v expeced=%v actual=%v", src, i, src.GetIv(), chunkId, src.RemoteAddr(), dst.RemoteAddr(), dataLen, expectedHmacSha1, actualHmacSha1) 100 | break 101 | } 102 | if n, err := dst.Write(dataBuf); err != nil { 103 | glog.V(5).Infof("conn=%p #%v write data error n=%v: %v", dst, i, n, err) 104 | break 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /pkg/connection/udp_conn.go: -------------------------------------------------------------------------------- 1 | package connection 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "net" 7 | "strconv" 8 | "sync" 9 | "syscall" 10 | "time" 11 | ///encrypt "shadowsocks-go/pkg/connection" 12 | ) 13 | 14 | const ( 15 | idType = 0 // address type index 16 | idIP0 = 1 // ip addres 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 | udpLeakyBufSize = 4108 // data.len(2) + hmacsha1(10) + data(4096) 28 | udpMaxNBuf = 2048 29 | ) 30 | 31 | // UDP is a udp relay interface 32 | type UDP interface { 33 | ReadFromUDP(b []byte) (n int, src *net.UDPAddr, err error) 34 | Read(b []byte) (n int, err error) 35 | WriteToUDP(b []byte, src *net.UDPAddr) (n int, err error) 36 | Write(b []byte) (n int, err error) 37 | Close() error 38 | SetWriteDeadline(t time.Time) error 39 | SetReadDeadline(t time.Time) error 40 | LocalAddr() net.Addr 41 | RemoteAddr() net.Addr 42 | ReadFrom(b []byte) (int, net.Addr, error) 43 | } 44 | 45 | // UDPConn maintain a udp connection list 46 | type UDPConn struct { 47 | UDP 48 | *Cipher 49 | timeout time.Duration 50 | } 51 | 52 | func NewUDPConn(cn UDP, cipher *Cipher, timeout time.Duration) *UDPConn { 53 | return &UDPConn{ 54 | UDP: cn, 55 | Cipher: cipher, 56 | timeout: timeout, 57 | } 58 | } 59 | 60 | type CachedUDPConn struct { 61 | timer *time.Timer 62 | UDP 63 | i string 64 | } 65 | 66 | func NewCachedUDPConn(cn UDP) *CachedUDPConn { 67 | return &CachedUDPConn{nil, cn, ""} 68 | } 69 | 70 | func (c *CachedUDPConn) Check() { 71 | go nl.Delete(c.i) 72 | } 73 | 74 | func (c *CachedUDPConn) Close() error { 75 | c.timer.Stop() 76 | return c.UDP.Close() 77 | } 78 | 79 | func (c *CachedUDPConn) SetTimer(index string) { 80 | c.i = index 81 | c.timer = time.AfterFunc(120*time.Second, c.Check) 82 | } 83 | 84 | func (c *CachedUDPConn) Refresh() bool { 85 | return c.timer.Reset(120 * time.Second) 86 | } 87 | 88 | type NATlist struct { 89 | sync.Mutex 90 | Conns map[string]*CachedUDPConn 91 | AliveConns int 92 | } 93 | 94 | func (nl *NATlist) Delete(srcaddr string) { 95 | nl.Lock() 96 | c, ok := nl.Conns[srcaddr] 97 | if ok { 98 | c.Close() 99 | delete(nl.Conns, srcaddr) 100 | nl.AliveConns -= 1 101 | } 102 | for k, _ := range ReqList { 103 | delete(ReqList, k) 104 | } 105 | defer nl.Unlock() 106 | } 107 | 108 | func (nl *NATlist) Get(srcaddr *net.UDPAddr, ss *UDPConn) (c *CachedUDPConn, ok bool, err error) { 109 | nl.Lock() 110 | defer nl.Unlock() 111 | index := srcaddr.String() 112 | _, ok = nl.Conns[index] 113 | if !ok { 114 | //NAT not exists or expired 115 | nl.AliveConns += 1 116 | delete(nl.Conns, index) 117 | ok = false 118 | //full cone 119 | conn, err := net.ListenUDP("udp", &net.UDPAddr{ 120 | IP: net.IPv6zero, 121 | Port: 0, 122 | }) 123 | if err != nil { 124 | return nil, false, err 125 | } 126 | nl.Conns[index] = NewCachedUDPConn(conn) 127 | c, _ = nl.Conns[index] 128 | c.SetTimer(index) 129 | go Pipeloop(ss, srcaddr, c) 130 | } else { 131 | //NAT exists 132 | c, _ = nl.Conns[index] 133 | c.Refresh() 134 | } 135 | err = nil 136 | return 137 | } 138 | 139 | func ParseHeader(addr net.Addr) ([]byte, int) { 140 | //what if the request address type is domain??? 141 | ip, port, err := net.SplitHostPort(addr.String()) 142 | if err != nil { 143 | return nil, 0 144 | } 145 | buf := make([]byte, 20) 146 | IP := net.ParseIP(ip) 147 | b1 := IP.To4() 148 | iplen := 0 149 | if b1 == nil { //ipv6 150 | b1 = IP.To16() 151 | buf[0] = typeIPv6 152 | iplen = net.IPv6len 153 | } else { //ipv4 154 | buf[0] = typeIPv4 155 | iplen = net.IPv4len 156 | } 157 | copy(buf[1:], b1) 158 | port_i, _ := strconv.Atoi(port) 159 | binary.BigEndian.PutUint16(buf[1+iplen:], uint16(port_i)) 160 | return buf[:1+iplen+2], 1 + iplen + 2 161 | } 162 | 163 | func Pipeloop(ss *UDPConn, srcaddr *net.UDPAddr, remote UDP) { 164 | buf := udpBuf.Get() 165 | defer udpBuf.Put(buf) 166 | defer nl.Delete(srcaddr.String()) 167 | for { 168 | remote.SetReadDeadline(time.Now().Add(ss.timeout)) 169 | n, raddr, err := remote.ReadFrom(buf) 170 | if err != nil { 171 | if ne, ok := err.(*net.OpError); ok && (ne.Err == syscall.EMFILE || ne.Err == syscall.ENFILE) { 172 | // log too many open file error 173 | // EMFILE is process reaches open file limits, ENFILE is system limit 174 | fmt.Println("[udp]read error:", err) 175 | } else if ne.Err.Error() == "use of closed network connection" { 176 | fmt.Println("[udp]Connection Closing:", remote.LocalAddr()) 177 | } else { 178 | fmt.Println("[udp]error reading from:", remote.LocalAddr(), err) 179 | } 180 | return 181 | } 182 | // need improvement here 183 | if N, ok := ReqList[raddr.String()]; ok { 184 | go ss.WriteToUDP(append(N.Req[:N.ReqLen], buf[:n]...), srcaddr) 185 | } else { 186 | header, hlen := ParseHeader(raddr) 187 | go ss.WriteToUDP(append(header[:hlen], buf[:n]...), srcaddr) 188 | } 189 | } 190 | } 191 | 192 | type ReqNode struct { 193 | Req []byte 194 | ReqLen int 195 | } 196 | 197 | var ReqList = map[string]*ReqNode{} 198 | 199 | var nl = NATlist{Conns: map[string]*CachedUDPConn{}} 200 | 201 | var udpBuf = NewLeakyBuf(udpMaxNBuf, udpLeakyBufSize) 202 | 203 | func (c *UDPConn) handleUDPConnection(n int, src *net.UDPAddr, receive []byte) { 204 | var dstIP net.IP 205 | var reqLen int 206 | defer udpBuf.Put(receive) 207 | 208 | switch receive[idType] { 209 | case typeIPv4: 210 | reqLen = lenIPv4 211 | dstIP = net.IP(receive[idIP0 : idIP0+net.IPv4len]) 212 | case typeIPv6: 213 | reqLen = lenIPv6 214 | dstIP = net.IP(receive[idIP0 : idIP0+net.IPv6len]) 215 | case typeDm: 216 | reqLen = int(receive[idDmLen]) + lenDmBase 217 | dIP, err := net.ResolveIPAddr("ip", string(receive[idDm0:idDm0+receive[idDmLen]])) 218 | if err != nil { 219 | fmt.Sprintf("[udp]failed to resolve domain name: %s\n", string(receive[idDm0:idDm0+receive[idDmLen]])) 220 | return 221 | } 222 | dstIP = dIP.IP 223 | default: 224 | fmt.Sprintf("[udp]addr type %d not supported", receive[idType]) 225 | return 226 | } 227 | dst := &net.UDPAddr{ 228 | IP: dstIP, 229 | Port: int(binary.BigEndian.Uint16(receive[reqLen-2 : reqLen])), 230 | } 231 | if _, ok := ReqList[dst.String()]; !ok { 232 | req := make([]byte, reqLen) 233 | for i := 0; i < reqLen; i++ { 234 | req[i] = receive[i] 235 | } 236 | ReqList[dst.String()] = &ReqNode{req, reqLen} 237 | } 238 | 239 | remote, _, err := nl.Get(src, c) 240 | if err != nil { 241 | return 242 | } 243 | remote.SetWriteDeadline(time.Now().Add(c.timeout)) 244 | _, err = remote.WriteToUDP(receive[reqLen:n], dst) 245 | if err != nil { 246 | if ne, ok := err.(*net.OpError); ok && (ne.Err == syscall.EMFILE || ne.Err == syscall.ENFILE) { 247 | // log too many open file error 248 | // EMFILE is process reaches open file limits, ENFILE is system limit 249 | fmt.Println("[udp]write error:", err) 250 | } else { 251 | fmt.Println("[udp]error connecting to:", dst, err) 252 | } 253 | return 254 | } 255 | // Pipeloop 256 | return 257 | } 258 | 259 | func (c *UDPConn) ReadAndHandleUDPReq() { 260 | buf := udpBuf.Get() 261 | n, src, err := c.ReadFromUDP(buf[0:]) 262 | if err != nil { 263 | return 264 | } 265 | go c.handleUDPConnection(n, src, buf) 266 | } 267 | 268 | //n is the size of the payload 269 | func (c *UDPConn) ReadFromUDP(b []byte) (n int, src *net.UDPAddr, err error) { 270 | buf := udpBuf.Get() 271 | n, src, err = c.UDP.ReadFromUDP(buf[0:]) 272 | if err != nil { 273 | return 274 | } 275 | defer udpBuf.Put(buf) 276 | 277 | iv := buf[:c.info.ivLen] 278 | if err = c.initDecrypt(iv); err != nil { 279 | return 280 | } 281 | c.decrypt(b[0:n-c.info.ivLen], buf[c.info.ivLen:n]) 282 | n = n - c.info.ivLen 283 | return 284 | } 285 | 286 | func (c *UDPConn) Read(b []byte) (n int, err error) { 287 | buf := udpBuf.Get() 288 | n, err = c.UDP.Read(buf[0:]) 289 | if err != nil { 290 | return 291 | } 292 | defer udpBuf.Put(buf) 293 | 294 | iv := buf[:c.info.ivLen] 295 | if err = c.initDecrypt(iv); err != nil { 296 | return 297 | } 298 | c.decrypt(b[0:n-c.info.ivLen], buf[c.info.ivLen:n]) 299 | n = n - c.info.ivLen 300 | return 301 | } 302 | 303 | //n = iv + payload 304 | func (c *UDPConn) WriteToUDP(b []byte, src *net.UDPAddr) (n int, err error) { 305 | var cipherData []byte 306 | dataStart := 0 307 | 308 | var iv []byte 309 | iv, err = c.initEncrypt() 310 | if err != nil { 311 | return 312 | } 313 | // Put initialization vector in buffer, do a single write to send both 314 | // iv and data. 315 | cipherData = make([]byte, len(b)+len(iv)) 316 | copy(cipherData, iv) 317 | dataStart = len(iv) 318 | 319 | c.encrypt(cipherData[dataStart:], b) 320 | n, err = c.UDP.WriteToUDP(cipherData, src) 321 | return 322 | } 323 | 324 | func (c *UDPConn) Write(b []byte) (n int, err error) { 325 | var cipherData []byte 326 | dataStart := 0 327 | 328 | var iv []byte 329 | iv, err = c.initEncrypt() 330 | if err != nil { 331 | return 332 | } 333 | // Put initialization vector in buffer, do a single write to send both 334 | // iv and data. 335 | cipherData = make([]byte, len(b)+len(iv)) 336 | copy(cipherData, iv) 337 | dataStart = len(iv) 338 | 339 | c.encrypt(cipherData[dataStart:], b) 340 | n, err = c.UDP.Write(cipherData) 341 | return 342 | } 343 | -------------------------------------------------------------------------------- /pkg/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 | -------------------------------------------------------------------------------- /pkg/testdata/noserver.json: -------------------------------------------------------------------------------- 1 | { 2 | "server_port":8388, 3 | "local_port":1081, 4 | "password":"barfoo!", 5 | "timeout":60, 6 | "cache_enctable": true 7 | } 8 | -------------------------------------------------------------------------------- /pkg/util/flag/flags.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package flag 18 | 19 | import ( 20 | goflag "flag" 21 | "strings" 22 | 23 | "github.com/golang/glog" 24 | "github.com/spf13/pflag" 25 | ) 26 | 27 | // WordSepNormalizeFunc changes all flags that contain "_" separators 28 | func WordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName { 29 | if strings.Contains(name, "_") { 30 | return pflag.NormalizedName(strings.Replace(name, "_", "-", -1)) 31 | } 32 | return pflag.NormalizedName(name) 33 | } 34 | 35 | // WarnWordSepNormalizeFunc changes and warns for flags that contain "_" separators 36 | func WarnWordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName { 37 | if strings.Contains(name, "_") { 38 | nname := strings.Replace(name, "_", "-", -1) 39 | glog.Warningf("%s is DEPRECATED and will be removed in a future version. Use %s instead.", name, nname) 40 | 41 | return pflag.NormalizedName(nname) 42 | } 43 | return pflag.NormalizedName(name) 44 | } 45 | 46 | // InitFlags normalizes and parses the command line flags 47 | func InitFlags() { 48 | pflag.CommandLine.SetNormalizeFunc(WordSepNormalizeFunc) 49 | pflag.CommandLine.AddGoFlagSet(goflag.CommandLine) 50 | pflag.Parse() 51 | } 52 | -------------------------------------------------------------------------------- /pkg/util/flag/tristate.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package flag 18 | 19 | import ( 20 | "fmt" 21 | "strconv" 22 | ) 23 | 24 | // Tristate is a flag compatible with flags and pflags that 25 | // keeps track of whether it had a value supplied or not. 26 | type Tristate int 27 | 28 | const ( 29 | Unset Tristate = iota // 0 30 | True 31 | False 32 | ) 33 | 34 | func (f *Tristate) Default(value bool) { 35 | *f = triFromBool(value) 36 | } 37 | 38 | func (f Tristate) String() string { 39 | b := boolFromTri(f) 40 | return fmt.Sprintf("%t", b) 41 | } 42 | 43 | func (f Tristate) Value() bool { 44 | b := boolFromTri(f) 45 | return b 46 | } 47 | 48 | func (f *Tristate) Set(value string) error { 49 | boolVal, err := strconv.ParseBool(value) 50 | if err != nil { 51 | return err 52 | } 53 | 54 | *f = triFromBool(boolVal) 55 | return nil 56 | } 57 | 58 | func (f Tristate) Provided() bool { 59 | if f != Unset { 60 | return true 61 | } 62 | return false 63 | } 64 | 65 | func (f *Tristate) Type() string { 66 | return "tristate" 67 | } 68 | 69 | func boolFromTri(t Tristate) bool { 70 | if t == True { 71 | return true 72 | } else { 73 | return false 74 | } 75 | } 76 | 77 | func triFromBool(b bool) Tristate { 78 | if b { 79 | return True 80 | } else { 81 | return False 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /pkg/util/mergesort.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | func merge(left, right []uint64, comparison func(uint64, uint64) int64) []uint64 { 4 | result := make([]uint64, len(left)+len(right)) 5 | l, r := 0, 0 6 | for (l < len(left)) && (r < len(right)) { 7 | if comparison(left[l], right[r]) <= 0 { 8 | result[l+r] = left[l] 9 | l++ 10 | } else { 11 | result[l+r] = right[r] 12 | r++ 13 | } 14 | } 15 | for l < len(left) { 16 | result[l+r] = left[l] 17 | l++ 18 | } 19 | for r < len(right) { 20 | result[l+r] = right[r] 21 | r++ 22 | } 23 | return result 24 | } 25 | 26 | func Sort(arr []uint64, comparison func(uint64, uint64) int64) []uint64 { 27 | if len(arr) < 2 { 28 | return arr 29 | } 30 | var middle uint64 = uint64(len(arr) / 2) 31 | return merge(Sort(arr[0:middle], comparison), Sort(arr[middle:], comparison), comparison) 32 | } 33 | -------------------------------------------------------------------------------- /pkg/util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "crypto/hmac" 5 | "crypto/sha1" 6 | "encoding/binary" 7 | "errors" 8 | "fmt" 9 | "os" 10 | ) 11 | 12 | func PrintVersion() { 13 | const version = "1.1.4" 14 | fmt.Println("shadowsocks-go version", version) 15 | } 16 | 17 | func IsFileExists(path string) (bool, error) { 18 | stat, err := os.Stat(path) 19 | if err == nil { 20 | if stat.Mode()&os.ModeType == 0 { 21 | return true, nil 22 | } 23 | return false, errors.New(path + " exists but is not regular file") 24 | } 25 | if os.IsNotExist(err) { 26 | return false, nil 27 | } 28 | return false, err 29 | } 30 | 31 | func HmacSha1(key []byte, data []byte) []byte { 32 | hmacSha1 := hmac.New(sha1.New, key) 33 | hmacSha1.Write(data) 34 | return hmacSha1.Sum(nil)[:10] 35 | } 36 | 37 | func OtaConnectAuth(iv, key, data []byte) []byte { 38 | return append(data, HmacSha1(append(iv, key...), data)...) 39 | } 40 | 41 | func OtaReqChunkAuth(iv []byte, chunkId uint32, data []byte) []byte { 42 | nb := make([]byte, 2) 43 | binary.BigEndian.PutUint16(nb, uint16(len(data))) 44 | chunkIdBytes := make([]byte, 4) 45 | binary.BigEndian.PutUint32(chunkIdBytes, chunkId) 46 | header := append(nb, HmacSha1(append(iv, chunkIdBytes...), data)...) 47 | return append(header, data...) 48 | } 49 | 50 | type ClosedFlag struct { 51 | flag bool 52 | } 53 | 54 | func (flag *ClosedFlag) SetClosed() { 55 | flag.flag = true 56 | } 57 | 58 | func (flag *ClosedFlag) IsClosed() bool { 59 | return flag.flag 60 | } 61 | -------------------------------------------------------------------------------- /sample-config/client-multi-server.json: -------------------------------------------------------------------------------- 1 | { 2 | "listenPort": 1081, 3 | "servers": [ 4 | { 5 | "host":"127.0.0.1", 6 | "port":8387, 7 | "encrypt":"aes-128-cfb", 8 | "password":"barfoo", 9 | "enableOTA":false, 10 | "timeout":60 11 | }, 12 | { 13 | "host":"127.0.0.1", 14 | "port":8388, 15 | "encrypt":"aes-128-cfb", 16 | "password":"foobar", 17 | "enableOTA":false, 18 | "timeout":60 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /sample-config/server-multi-port.json: -------------------------------------------------------------------------------- 1 | { 2 | "clients": [ 3 | { 4 | "host":"0.0.0.0", 5 | "port":18387, 6 | "encrypt":"aes-128-cfb", 7 | "password":"barfoo", 8 | "enableOTA":false, 9 | "timeout":60 10 | }, 11 | { 12 | "host":"0.0.0.0", 13 | "port":18388, 14 | "encrypt":"aes-128-cfb", 15 | "password":"foobar", 16 | "enableOTA":false, 17 | "timeout":60 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /script/README.md: -------------------------------------------------------------------------------- 1 | # About shadowsocks.exe 2 | 3 | Copied `goagent.exe`, modified the string table and icon using reshack. 4 | 5 | Thanks for the taskbar project created by @phuslu. 6 | 7 | -------------------------------------------------------------------------------- /script/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$( dirname "${BASH_SOURCE[0]}" )/.." 4 | 5 | version=`grep 'const version = ' ./shadowsocks/util.go | sed -e 's/.*= //' | sed -e 's/"//g'` 6 | echo "creating shadowsocks binary version $version" 7 | 8 | ROOT=`pwd` 9 | bindir=$ROOT/bin 10 | mkdir -p $bindir 11 | 12 | build() { 13 | local name 14 | local GOOS 15 | local GOARCH 16 | 17 | if [[ $1 == "darwin" ]]; then 18 | # Enable CGO for OS X so change network location will not cause problem. 19 | export CGO_ENABLED=1 20 | else 21 | export CGO_ENABLED=0 22 | fi 23 | 24 | prog=shadowsocks-$4 25 | pushd cmd/$prog 26 | name=$prog-$3-$version 27 | echo "building $name" 28 | GOOS=$1 GOARCH=$2 go build -a || exit 1 29 | if [[ $1 == "windows" ]]; then 30 | mv $prog.exe $ROOT/script/ 31 | pushd $ROOT/script/ 32 | cp $ROOT/config.json sample-config.json 33 | cp $ROOT/sample-config/client-multi-server.json multi-server.json 34 | zip $name.zip $prog.exe shadowsocks.exe sample-config.json multi-server.json 35 | rm -f $prog.exe sample-config.json multi-server.json 36 | mv $name.zip $bindir 37 | popd 38 | else 39 | mv $prog $name 40 | gzip -f $name 41 | mv $name.gz $bindir 42 | fi 43 | popd 44 | } 45 | 46 | build darwin amd64 mac64 local 47 | build linux amd64 linux64 local 48 | build linux 386 linux32 local 49 | build windows amd64 win64 local 50 | build windows 386 win32 local 51 | 52 | build linux amd64 linux64 server 53 | build linux 386 linux32 server 54 | build darwin amd64 mac64 server 55 | build windows amd64 win64 server 56 | build windows 386 win32 server 57 | 58 | #script/createdeb.sh amd64 59 | #script/createdeb.sh i386 60 | #mv shadowsocks-go_$version-1-*.deb bin/ 61 | #rm -rf shadowsocks-go_$version-1* 62 | -------------------------------------------------------------------------------- /script/createdeb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$( dirname "${BASH_SOURCE[0]}" )/.." 4 | ver=$(awk '/\tconst version =/ { print $4 }' shadowsocks/util.go | sed -e 's/"//g') 5 | 6 | if [[ $# != 1 ]]; then 7 | echo "$0 " 8 | exit 1 9 | fi 10 | 11 | export CGO_ENABLED=0 12 | export GOOS=linux 13 | 14 | arch=$1 15 | case $arch in 16 | i386) 17 | export GOARCH=386 18 | ;; 19 | amd64) 20 | export GOARCH=amd64 21 | ;; 22 | *) 23 | echo "arch $i not supported" 24 | exit 1 25 | ;; 26 | esac 27 | 28 | # build shadowsocks server 29 | pushd cmd/shadowsocks-server 30 | go build -a -v || exit 1 31 | popd 32 | 33 | # create debian package 34 | DEBDIR=shadowsocks-go_$ver-1-$arch 35 | rm -rf $DEBDIR 36 | cp -r deb $DEBDIR 37 | 38 | sed -i -e "s/VER/$ver/" $DEBDIR/DEBIAN/control || exit 1 39 | sed -i -e "s/^Architecture.*$/Architecture: $arch/" $DEBDIR/DEBIAN/control || exit 1 40 | 41 | mkdir -p $DEBDIR/usr/bin 42 | cp cmd/shadowsocks-server/shadowsocks-server $DEBDIR/usr/bin/ 43 | rm -f cmd/shadowsocks-server/shadowsocks-server 44 | 45 | fakeroot dpkg-deb --build $DEBDIR 46 | 47 | -------------------------------------------------------------------------------- /script/curl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# != 3 ]; then 4 | echo "$0 " 5 | exit 1 6 | fi 7 | 8 | socks=$1 9 | n=$2 10 | url=$3 11 | 12 | for i in `seq 1 $n`; do 13 | curl -s --socks5 $socks $url >/dev/null 14 | if [ $(($i % 1000)) -eq 0 ]; then 15 | echo "finished $i request" 16 | fi 17 | done 18 | 19 | -------------------------------------------------------------------------------- /script/http.go: -------------------------------------------------------------------------------- 1 | /* Simple http server for testing. */ 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "net/http" 7 | "os" 8 | ) 9 | 10 | func handler(w http.ResponseWriter, r *http.Request) { 11 | fmt.Fprintf(w, "Hello, shadowsocks-go!") 12 | } 13 | 14 | func main() { 15 | if len(os.Args) != 2 { 16 | fmt.Println("Usage: http ") 17 | os.Exit(1) 18 | } 19 | http.HandleFunc("/", handler) 20 | http.ListenAndServe("127.0.0.1:"+os.Args[1], nil) 21 | } 22 | -------------------------------------------------------------------------------- /script/set-version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$( dirname "${BASH_SOURCE[0]}" )/.." 4 | 5 | if [ $# != 1 ]; then 6 | echo "Usage: $0 " 7 | exit 1 8 | fi 9 | 10 | version=$1 11 | #echo $version 12 | 13 | sed -i -e "s,\(\tconst version \+= \)\".*\"$,\1\"$version\"," shadowsocks/util.go 14 | sed -i -e "s,^\(Current version: \)[^ ]\+,\1$version," README.md 15 | 16 | -------------------------------------------------------------------------------- /script/shadowsocks.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orvice/shadowsocks-go/df93b5d1852ac8eda875df0380c7464de142eca9/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 | "strings" 19 | ) 20 | 21 | type Config struct { 22 | Server interface{} `json:"server"` 23 | ServerPort int `json:"server_port"` 24 | LocalPort int `json:"local_port"` 25 | Password string `json:"password"` 26 | Method string `json:"method"` // encryption method 27 | Auth bool `json:"auth"` // one time auth 28 | 29 | // following options are only used by server 30 | PortPassword map[string]string `json:"port_password"` 31 | Timeout int `json:"timeout"` 32 | 33 | // following options are only used by client 34 | 35 | // The order of servers in the client config is significant, so use array 36 | // instead of map to preserve the order. 37 | ServerPassword [][]string `json:"server_password"` 38 | } 39 | 40 | var readTimeout time.Duration 41 | 42 | func (config *Config) GetServerArray() []string { 43 | // Specifying multiple servers in the "server" options is deprecated. 44 | // But for backward compatiblity, keep this. 45 | if config.Server == nil { 46 | return nil 47 | } 48 | single, ok := config.Server.(string) 49 | if ok { 50 | return []string{single} 51 | } 52 | arr, ok := config.Server.([]interface{}) 53 | if ok { 54 | /* 55 | if len(arr) > 1 { 56 | log.Println("Multiple servers in \"server\" option is deprecated. " + 57 | "Please use \"server_password\" instead.") 58 | } 59 | */ 60 | serverArr := make([]string, len(arr), len(arr)) 61 | for i, s := range arr { 62 | serverArr[i], ok = s.(string) 63 | if !ok { 64 | goto typeError 65 | } 66 | } 67 | return serverArr 68 | } 69 | typeError: 70 | panic(fmt.Sprintf("Config.Server type error %v", reflect.TypeOf(config.Server))) 71 | } 72 | 73 | func ParseConfig(path string) (config *Config, err error) { 74 | file, err := os.Open(path) // For read access. 75 | if err != nil { 76 | return 77 | } 78 | defer file.Close() 79 | 80 | data, err := ioutil.ReadAll(file) 81 | if err != nil { 82 | return 83 | } 84 | 85 | config = &Config{} 86 | if err = json.Unmarshal(data, config); err != nil { 87 | return nil, err 88 | } 89 | readTimeout = time.Duration(config.Timeout) * time.Second 90 | if strings.HasSuffix(strings.ToLower(config.Method), "-ota") { 91 | config.Method = config.Method[:len(config.Method) - 4] 92 | config.Auth = true 93 | } 94 | return 95 | } 96 | 97 | func SetDebug(d DebugLog) { 98 | Debug = d 99 | } 100 | 101 | // Useful for command line to override options specified in config file 102 | // Debug is not updated. 103 | func UpdateConfig(old, new *Config) { 104 | // Using reflection here is not necessary, but it's a good exercise. 105 | // For more information on reflections in Go, read "The Laws of Reflection" 106 | // http://golang.org/doc/articles/laws_of_reflection.html 107 | newVal := reflect.ValueOf(new).Elem() 108 | oldVal := reflect.ValueOf(old).Elem() 109 | 110 | // typeOfT := newVal.Type() 111 | for i := 0; i < newVal.NumField(); i++ { 112 | newField := newVal.Field(i) 113 | oldField := oldVal.Field(i) 114 | // log.Printf("%d: %s %s = %v\n", i, 115 | // typeOfT.Field(i).Name, newField.Type(), newField.Interface()) 116 | switch newField.Kind() { 117 | case reflect.Interface: 118 | if fmt.Sprintf("%v", newField.Interface()) != "" { 119 | oldField.Set(newField) 120 | } 121 | case reflect.String: 122 | s := newField.String() 123 | if s != "" { 124 | oldField.SetString(s) 125 | } 126 | case reflect.Int: 127 | i := newField.Int() 128 | if i != 0 { 129 | oldField.SetInt(i) 130 | } 131 | } 132 | } 133 | 134 | old.Timeout = new.Timeout 135 | readTimeout = time.Duration(old.Timeout) * time.Second 136 | } 137 | -------------------------------------------------------------------------------- /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 | OneTimeAuthMask byte = 0x10 13 | AddrMask byte = 0xf 14 | ) 15 | 16 | type Conn struct { 17 | net.Conn 18 | *Cipher 19 | readBuf []byte 20 | writeBuf []byte 21 | chunkId uint32 22 | } 23 | 24 | func NewConn(c net.Conn, cipher *Cipher) *Conn { 25 | return &Conn{ 26 | Conn: c, 27 | Cipher: cipher, 28 | readBuf: leakyBuf.Get(), 29 | writeBuf: leakyBuf.Get()} 30 | } 31 | 32 | func (c *Conn) Close() error { 33 | leakyBuf.Put(c.readBuf) 34 | leakyBuf.Put(c.writeBuf) 35 | return c.Conn.Close() 36 | } 37 | 38 | func RawAddr(addr string) (buf []byte, err error) { 39 | host, portStr, err := net.SplitHostPort(addr) 40 | if err != nil { 41 | return nil, fmt.Errorf("shadowsocks: address error %s %v", addr, err) 42 | } 43 | port, err := strconv.Atoi(portStr) 44 | if err != nil { 45 | return nil, fmt.Errorf("shadowsocks: invalid port %s", addr) 46 | } 47 | 48 | hostLen := len(host) 49 | l := 1 + 1 + hostLen + 2 // addrType + lenByte + address + port 50 | buf = make([]byte, l) 51 | buf[0] = 3 // 3 means the address is domain name 52 | buf[1] = byte(hostLen) // host address length followed by host address 53 | copy(buf[2:], host) 54 | binary.BigEndian.PutUint16(buf[2+hostLen:2+hostLen+2], uint16(port)) 55 | return 56 | } 57 | 58 | // This is intended for use by users implementing a local socks proxy. 59 | // rawaddr shoud contain part of the data in socks request, starting from the 60 | // ATYP field. (Refer to rfc1928 for more information.) 61 | func DialWithRawAddr(rawaddr []byte, server string, cipher *Cipher) (c *Conn, err error) { 62 | conn, err := net.Dial("tcp", server) 63 | if err != nil { 64 | return 65 | } 66 | c = NewConn(conn, cipher) 67 | if cipher.ota { 68 | if c.enc == nil { 69 | if _, err = c.initEncrypt(); err != nil { 70 | return 71 | } 72 | } 73 | // since we have initEncrypt, we must send iv manually 74 | conn.Write(cipher.iv) 75 | rawaddr[0] |= OneTimeAuthMask 76 | rawaddr = otaConnectAuth(cipher.iv, cipher.key, rawaddr) 77 | } 78 | if _, err = c.write(rawaddr); err != nil { 79 | c.Close() 80 | return nil, err 81 | } 82 | return 83 | } 84 | 85 | // addr should be in the form of host:port 86 | func Dial(addr, server string, cipher *Cipher) (c *Conn, err error) { 87 | ra, err := RawAddr(addr) 88 | if err != nil { 89 | return 90 | } 91 | return DialWithRawAddr(ra, server, cipher) 92 | } 93 | 94 | func (c *Conn) GetIv() (iv []byte) { 95 | iv = make([]byte, len(c.iv)) 96 | copy(iv, c.iv) 97 | return 98 | } 99 | 100 | func (c *Conn) GetKey() (key []byte) { 101 | key = make([]byte, len(c.key)) 102 | copy(key, c.key) 103 | return 104 | } 105 | 106 | func (c *Conn) IsOta() bool { 107 | return c.ota 108 | } 109 | 110 | func (c *Conn) GetAndIncrChunkId() (chunkId uint32) { 111 | chunkId = c.chunkId 112 | c.chunkId += 1 113 | return 114 | } 115 | 116 | func (c *Conn) Read(b []byte) (n int, err error) { 117 | if c.dec == nil { 118 | iv := make([]byte, c.info.ivLen) 119 | if _, err = io.ReadFull(c.Conn, iv); err != nil { 120 | return 121 | } 122 | if err = c.initDecrypt(iv); err != nil { 123 | return 124 | } 125 | if len(c.iv) == 0 { 126 | c.iv = iv 127 | } 128 | } 129 | 130 | cipherData := c.readBuf 131 | if len(b) > len(cipherData) { 132 | cipherData = make([]byte, len(b)) 133 | } else { 134 | cipherData = cipherData[:len(b)] 135 | } 136 | 137 | n, err = c.Conn.Read(cipherData) 138 | if n > 0 { 139 | c.decrypt(b[0:n], cipherData[0:n]) 140 | } 141 | return 142 | } 143 | 144 | func (c *Conn) Write(b []byte) (n int, err error) { 145 | if c.ota { 146 | chunkId := c.GetAndIncrChunkId() 147 | b = otaReqChunkAuth(c.iv, chunkId, b) 148 | } 149 | return c.write(b) 150 | } 151 | 152 | func (c *Conn) write(b []byte) (n int, err error) { 153 | var iv []byte 154 | if c.enc == nil { 155 | iv, err = c.initEncrypt() 156 | if err != nil { 157 | return 158 | } 159 | } 160 | 161 | cipherData := c.writeBuf 162 | dataSize := len(b) + len(iv) 163 | if dataSize > len(cipherData) { 164 | cipherData = make([]byte, dataSize) 165 | } else { 166 | cipherData = cipherData[:dataSize] 167 | } 168 | 169 | if iv != nil { 170 | // Put initialization vector in buffer, do a single write to send both 171 | // iv and data. 172 | copy(cipherData, iv) 173 | } 174 | 175 | c.encrypt(cipherData[len(iv):], b) 176 | n, err = c.Conn.Write(cipherData) 177 | return 178 | } 179 | -------------------------------------------------------------------------------- /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 | "github.com/codahale/chacha20" 13 | "golang.org/x/crypto/blowfish" 14 | "golang.org/x/crypto/cast5" 15 | "golang.org/x/crypto/salsa20/salsa" 16 | "io" 17 | "strings" 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 newAESStream(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 newDESStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { 73 | block, err := des.NewCipher(key) 74 | return newStream(block, err, key, iv, doe) 75 | } 76 | 77 | func newBlowFishStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { 78 | block, err := blowfish.NewCipher(key) 79 | return newStream(block, err, key, iv, doe) 80 | } 81 | 82 | func newCast5Stream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { 83 | block, err := cast5.NewCipher(key) 84 | return newStream(block, err, key, iv, doe) 85 | } 86 | 87 | func newRC4MD5Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { 88 | h := md5.New() 89 | h.Write(key) 90 | h.Write(iv) 91 | rc4key := h.Sum(nil) 92 | 93 | return rc4.NewCipher(rc4key) 94 | } 95 | 96 | func newChaCha20Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { 97 | return chacha20.New(key, iv) 98 | } 99 | 100 | type salsaStreamCipher struct { 101 | nonce [8]byte 102 | key [32]byte 103 | counter int 104 | } 105 | 106 | func (c *salsaStreamCipher) XORKeyStream(dst, src []byte) { 107 | var buf []byte 108 | padLen := c.counter % 64 109 | dataSize := len(src) + padLen 110 | if cap(dst) >= dataSize { 111 | buf = dst[:dataSize] 112 | } else if leakyBufSize >= dataSize { 113 | buf = leakyBuf.Get() 114 | defer leakyBuf.Put(buf) 115 | buf = buf[:dataSize] 116 | } else { 117 | buf = make([]byte, dataSize) 118 | } 119 | 120 | var subNonce [16]byte 121 | copy(subNonce[:], c.nonce[:]) 122 | binary.LittleEndian.PutUint64(subNonce[len(c.nonce):], uint64(c.counter/64)) 123 | 124 | // It's difficult to avoid data copy here. src or dst maybe slice from 125 | // Conn.Read/Write, which can't have padding. 126 | copy(buf[padLen:], src[:]) 127 | salsa.XORKeyStream(buf, buf, &subNonce, &c.key) 128 | copy(dst, buf[padLen:]) 129 | 130 | c.counter += len(src) 131 | } 132 | 133 | func newSalsa20Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { 134 | var c salsaStreamCipher 135 | copy(c.nonce[:], iv[:8]) 136 | copy(c.key[:], key[:32]) 137 | return &c, nil 138 | } 139 | 140 | type cipherInfo struct { 141 | keyLen int 142 | ivLen int 143 | newStream func(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) 144 | } 145 | 146 | var cipherMethod = map[string]*cipherInfo{ 147 | "aes-128-cfb": {16, 16, newAESStream}, 148 | "aes-192-cfb": {24, 16, newAESStream}, 149 | "aes-256-cfb": {32, 16, newAESStream}, 150 | "des-cfb": {8, 8, newDESStream}, 151 | "bf-cfb": {16, 8, newBlowFishStream}, 152 | "cast5-cfb": {16, 8, newCast5Stream}, 153 | "rc4-md5": {16, 16, newRC4MD5Stream}, 154 | "chacha20": {32, 8, newChaCha20Stream}, 155 | "salsa20": {32, 8, newSalsa20Stream}, 156 | } 157 | 158 | func CheckCipherMethod(method string) error { 159 | if method == "" { 160 | method = "aes-256-cfb" 161 | } 162 | _, ok := cipherMethod[method] 163 | if !ok { 164 | return errors.New("Unsupported encryption method: " + method) 165 | } 166 | return nil 167 | } 168 | 169 | type Cipher struct { 170 | enc cipher.Stream 171 | dec cipher.Stream 172 | key []byte 173 | info *cipherInfo 174 | ota bool // one-time auth 175 | iv []byte 176 | } 177 | 178 | // NewCipher creates a cipher that can be used in Dial() etc. 179 | // Use cipher.Copy() to create a new cipher with the same method and password 180 | // to avoid the cost of repeated cipher initialization. 181 | func NewCipher(method, password string) (c *Cipher, err error) { 182 | if password == "" { 183 | return nil, errEmptyPassword 184 | } 185 | var ota bool 186 | if strings.HasSuffix(strings.ToLower(method), "-ota") { 187 | method = method[:len(method) - 4] // len("-ota") = 4 188 | ota = true 189 | } else { 190 | ota = false 191 | } 192 | mi, ok := cipherMethod[method] 193 | if !ok { 194 | return nil, errors.New("Unsupported encryption method: " + method) 195 | } 196 | 197 | key := evpBytesToKey(password, mi.keyLen) 198 | 199 | c = &Cipher{key: key, info: mi} 200 | 201 | if err != nil { 202 | return nil, err 203 | } 204 | c.ota = ota 205 | return c, nil 206 | } 207 | 208 | // Initializes the block cipher with CFB mode, returns IV. 209 | func (c *Cipher) initEncrypt() (iv []byte, err error) { 210 | if c.iv == nil { 211 | iv = make([]byte, c.info.ivLen) 212 | if _, err := io.ReadFull(rand.Reader, iv); err != nil { 213 | return nil, err 214 | } 215 | c.iv = iv 216 | } else { 217 | iv = c.iv 218 | } 219 | if c.enc == nil { 220 | c.enc, err = c.info.newStream(c.key, iv, Encrypt) 221 | if err != nil { 222 | return nil, err 223 | } 224 | } 225 | return 226 | } 227 | 228 | func (c *Cipher) initDecrypt(iv []byte) (err error) { 229 | c.dec, err = c.info.newStream(c.key, iv, Decrypt) 230 | return 231 | } 232 | 233 | func (c *Cipher) encrypt(dst, src []byte) { 234 | c.enc.XORKeyStream(dst, src) 235 | } 236 | 237 | func (c *Cipher) decrypt(dst, src []byte) { 238 | c.dec.XORKeyStream(dst, src) 239 | } 240 | 241 | // Copy creates a new cipher at it's initial state. 242 | func (c *Cipher) Copy() *Cipher { 243 | // This optimization maybe not necessary. But without this function, we 244 | // need to maintain a table cache for newTableCipher and use lock to 245 | // protect concurrent access to that cache. 246 | 247 | // AES and DES ciphers does not return specific types, so it's difficult 248 | // to create copy. But their initizliation time is less than 4000ns on my 249 | // 2.26 GHz Intel Core 2 Duo processor. So no need to worry. 250 | 251 | // Currently, blow-fish and cast5 initialization cost is an order of 252 | // maganitude slower than other ciphers. (I'm not sure whether this is 253 | // because the current implementation is not highly optimized, or this is 254 | // the nature of the algorithm.) 255 | 256 | nc := *c 257 | nc.enc = nil 258 | nc.dec = nil 259 | nc.ota = c.ota 260 | return &nc 261 | } 262 | -------------------------------------------------------------------------------- /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 testCiphter(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 | testCiphter(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 | testCiphter(t, cipherCopy, method+" copy") 64 | } 65 | 66 | func TestAES128(t *testing.T) { 67 | testBlockCipher(t, "aes-128-cfb") 68 | } 69 | 70 | func TestAES192(t *testing.T) { 71 | testBlockCipher(t, "aes-192-cfb") 72 | } 73 | 74 | func TestAES256(t *testing.T) { 75 | testBlockCipher(t, "aes-256-cfb") 76 | } 77 | 78 | func TestDES(t *testing.T) { 79 | testBlockCipher(t, "des-cfb") 80 | } 81 | 82 | func TestRC4MD5(t *testing.T) { 83 | testBlockCipher(t, "rc4-md5") 84 | } 85 | 86 | func TestChaCha20(t *testing.T) { 87 | testBlockCipher(t, "chacha20") 88 | } 89 | 90 | var cipherKey = make([]byte, 64) 91 | var cipherIv = make([]byte, 64) 92 | 93 | const CIPHER_BENCHMARK_BUFFER_LEN = 4096 94 | 95 | func init() { 96 | for i := 0; i < len(cipherKey); i++ { 97 | cipherKey[i] = byte(i) 98 | } 99 | io.ReadFull(rand.Reader, cipherIv) 100 | } 101 | 102 | func benchmarkCipherInit(b *testing.B, method string) { 103 | ci := cipherMethod[method] 104 | key := cipherKey[:ci.keyLen] 105 | buf := make([]byte, ci.ivLen) 106 | for i := 0; i < b.N; i++ { 107 | ci.newStream(key, buf, Encrypt) 108 | } 109 | } 110 | 111 | func BenchmarkAES128Init(b *testing.B) { 112 | benchmarkCipherInit(b, "aes-128-cfb") 113 | } 114 | 115 | func BenchmarkAES192Init(b *testing.B) { 116 | benchmarkCipherInit(b, "aes-192-cfb") 117 | } 118 | 119 | func BenchmarkAES256Init(b *testing.B) { 120 | benchmarkCipherInit(b, "aes-256-cfb") 121 | } 122 | 123 | func BenchmarkBlowFishInit(b *testing.B) { 124 | benchmarkCipherInit(b, "bf-cfb") 125 | } 126 | 127 | func BenchmarkCast5Init(b *testing.B) { 128 | benchmarkCipherInit(b, "cast5-cfb") 129 | } 130 | 131 | func BenchmarkDESInit(b *testing.B) { 132 | benchmarkCipherInit(b, "des-cfb") 133 | } 134 | 135 | func BenchmarkRC4MD5Init(b *testing.B) { 136 | benchmarkCipherInit(b, "rc4-md5") 137 | } 138 | 139 | func BenchmarkChaCha20Init(b *testing.B) { 140 | benchmarkCipherInit(b, "chacha20") 141 | } 142 | 143 | func BenchmarkSalsa20Init(b *testing.B) { 144 | benchmarkCipherInit(b, "salsa20") 145 | } 146 | 147 | func benchmarkCipherEncrypt(b *testing.B, method string) { 148 | ci := cipherMethod[method] 149 | key := cipherKey[:ci.keyLen] 150 | iv := cipherIv[:ci.ivLen] 151 | enc, err := ci.newStream(key, iv, Encrypt) 152 | if err != nil { 153 | b.Error(err) 154 | } 155 | src := make([]byte, CIPHER_BENCHMARK_BUFFER_LEN) 156 | dst := make([]byte, CIPHER_BENCHMARK_BUFFER_LEN) 157 | io.ReadFull(rand.Reader, src) 158 | for i := 0; i < b.N; i++ { 159 | enc.XORKeyStream(dst, src) 160 | } 161 | } 162 | 163 | func BenchmarkAES128Encrypt(b *testing.B) { 164 | benchmarkCipherEncrypt(b, "aes-128-cfb") 165 | } 166 | 167 | func BenchmarkAES192Encrypt(b *testing.B) { 168 | benchmarkCipherEncrypt(b, "aes-192-cfb") 169 | } 170 | 171 | func BenchmarkAES256Encrypt(b *testing.B) { 172 | benchmarkCipherEncrypt(b, "aes-256-cfb") 173 | } 174 | 175 | func BenchmarkBlowFishEncrypt(b *testing.B) { 176 | benchmarkCipherEncrypt(b, "bf-cfb") 177 | } 178 | 179 | func BenchmarkCast5Encrypt(b *testing.B) { 180 | benchmarkCipherEncrypt(b, "cast5-cfb") 181 | } 182 | 183 | func BenchmarkDESEncrypt(b *testing.B) { 184 | benchmarkCipherEncrypt(b, "des-cfb") 185 | } 186 | 187 | func BenchmarkRC4MD5Encrypt(b *testing.B) { 188 | benchmarkCipherEncrypt(b, "rc4-md5") 189 | } 190 | 191 | func BenchmarkChacha20Encrypt(b *testing.B) { 192 | benchmarkCipherEncrypt(b, "chacha20") 193 | } 194 | 195 | func BenchmarkSalsa20Encrypt(b *testing.B) { 196 | benchmarkCipherEncrypt(b, "salsa20") 197 | } 198 | 199 | func benchmarkCipherDecrypt(b *testing.B, method string) { 200 | ci := cipherMethod[method] 201 | key := cipherKey[:ci.keyLen] 202 | iv := cipherIv[:ci.ivLen] 203 | enc, err := ci.newStream(key, iv, Encrypt) 204 | if err != nil { 205 | b.Error(err) 206 | } 207 | dec, err := ci.newStream(key, iv, Decrypt) 208 | if err != nil { 209 | b.Error(err) 210 | } 211 | src := make([]byte, CIPHER_BENCHMARK_BUFFER_LEN) 212 | dst := make([]byte, CIPHER_BENCHMARK_BUFFER_LEN) 213 | io.ReadFull(rand.Reader, src) 214 | enc.XORKeyStream(dst, src) 215 | for i := 0; i < b.N; i++ { 216 | dec.XORKeyStream(src, dst) 217 | } 218 | } 219 | 220 | func BenchmarkAES128Decrypt(b *testing.B) { 221 | benchmarkCipherDecrypt(b, "aes-128-cfb") 222 | } 223 | 224 | func BenchmarkAES192Decrypt(b *testing.B) { 225 | benchmarkCipherDecrypt(b, "aes-192-cfb") 226 | } 227 | 228 | func BenchmarkAES256Decrypt(b *testing.B) { 229 | benchmarkCipherDecrypt(b, "aes-256-cfb") 230 | } 231 | 232 | func BenchmarkBlowFishDecrypt(b *testing.B) { 233 | benchmarkCipherDecrypt(b, "bf-cfb") 234 | } 235 | 236 | func BenchmarkCast5Decrypt(b *testing.B) { 237 | benchmarkCipherDecrypt(b, "cast5-cfb") 238 | } 239 | 240 | func BenchmarkDESDecrypt(b *testing.B) { 241 | benchmarkCipherDecrypt(b, "des-cfb") 242 | } 243 | 244 | func BenchmarkRC4MD5Decrypt(b *testing.B) { 245 | benchmarkCipherDecrypt(b, "rc4-md5") 246 | } 247 | 248 | func BenchmarkChaCha20Decrypt(b *testing.B) { 249 | benchmarkCipherDecrypt(b, "chacha20") 250 | } 251 | 252 | func BenchmarkSalsa20Decrypt(b *testing.B) { 253 | benchmarkCipherDecrypt(b, "salsa20") 254 | } 255 | -------------------------------------------------------------------------------- /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/mergesort.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | func merge(left, right []uint64, comparison func (uint64, uint64) int64) []uint64 { 4 | result := make([]uint64, len(left) + len(right)) 5 | l, r := 0, 0 6 | for (l < len(left)) && (r < len(right)) { 7 | if comparison(left[l], right[r]) <= 0 { 8 | result[l + r] = left[l] 9 | l++ 10 | } else { 11 | result[l + r] = right[r] 12 | r++ 13 | } 14 | } 15 | for (l < len(left)) { 16 | result[l + r] = left[l] 17 | l++ 18 | } 19 | for (r < len(right)) { 20 | result[l + r] = right[r] 21 | r++ 22 | } 23 | return result 24 | } 25 | 26 | func Sort(arr []uint64, comparison func (uint64, uint64) int64) []uint64 { 27 | if len(arr) < 2 { 28 | return arr 29 | } 30 | var middle uint64 = uint64(len(arr)/2) 31 | return merge(Sort(arr[0:middle], comparison), Sort(arr[middle:], comparison), comparison) 32 | } 33 | -------------------------------------------------------------------------------- /shadowsocks/pipe.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "io" 7 | "net" 8 | "time" 9 | ) 10 | 11 | func SetReadTimeout(c net.Conn) { 12 | if readTimeout != 0 { 13 | c.SetReadDeadline(time.Now().Add(readTimeout)) 14 | } 15 | } 16 | 17 | // PipeThenClose copies data from src to dst, closes dst when done. 18 | func PipeThenClose(src, dst net.Conn) { 19 | defer dst.Close() 20 | buf := leakyBuf.Get() 21 | defer leakyBuf.Put(buf) 22 | for { 23 | SetReadTimeout(src) 24 | n, err := src.Read(buf) 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 | } 47 | 48 | // PipeThenClose copies data from src to dst, closes dst when done, with ota verification. 49 | func PipeThenCloseOta(src *Conn, dst net.Conn) { 50 | const ( 51 | dataLenLen = 2 52 | hmacSha1Len = 10 53 | idxData0 = dataLenLen + hmacSha1Len 54 | ) 55 | 56 | defer func() { 57 | dst.Close() 58 | }() 59 | // sometimes it have to fill large block 60 | buf := leakyBuf.Get() 61 | defer leakyBuf.Put(buf) 62 | i := 0 63 | for { 64 | i += 1 65 | SetReadTimeout(src) 66 | if n, err := io.ReadFull(src, buf[:dataLenLen+hmacSha1Len]); err != nil { 67 | if err == io.EOF { 68 | break 69 | } 70 | Debug.Printf("conn=%p #%v read header error n=%v: %v", src, i, n, err) 71 | break 72 | } 73 | dataLen := binary.BigEndian.Uint16(buf[:dataLenLen]) 74 | expectedHmacSha1 := buf[dataLenLen:idxData0] 75 | 76 | var dataBuf []byte 77 | if len(buf) < int(idxData0+dataLen) { 78 | dataBuf = make([]byte, dataLen) 79 | } else { 80 | dataBuf = buf[idxData0:idxData0+dataLen] 81 | } 82 | if n, err := io.ReadFull(src, dataBuf); err != nil { 83 | if err == io.EOF { 84 | break 85 | } 86 | Debug.Printf("conn=%p #%v read data error n=%v: %v", src, i, n, err) 87 | break 88 | } 89 | chunkIdBytes := make([]byte, 4) 90 | chunkId := src.GetAndIncrChunkId() 91 | binary.BigEndian.PutUint32(chunkIdBytes, chunkId) 92 | actualHmacSha1 := HmacSha1(append(src.GetIv(), chunkIdBytes...), dataBuf) 93 | if !bytes.Equal(expectedHmacSha1, actualHmacSha1) { 94 | Debug.Printf("conn=%p #%v read data hmac-sha1 mismatch, iv=%v chunkId=%v src=%v dst=%v len=%v expeced=%v actual=%v", src, i, src.GetIv(), chunkId, src.RemoteAddr(), dst.RemoteAddr(), dataLen, expectedHmacSha1, actualHmacSha1) 95 | break 96 | } 97 | if n, err := dst.Write(dataBuf); err != nil { 98 | Debug.Printf("conn=%p #%v write data error n=%v: %v", dst, i, n, err) 99 | break 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /shadowsocks/testdata/deprecated-client-multi-server.json: -------------------------------------------------------------------------------- 1 | { 2 | "server":["127.0.0.1", "127.0.1.1"], 3 | "server_port":8388, 4 | "local_port":1081, 5 | "password":"barfoo!", 6 | "timeout":60 7 | } 8 | -------------------------------------------------------------------------------- /shadowsocks/testdata/noserver.json: -------------------------------------------------------------------------------- 1 | { 2 | "server_port":8388, 3 | "local_port":1081, 4 | "password":"barfoo!", 5 | "timeout":60, 6 | "cache_enctable": true 7 | } 8 | -------------------------------------------------------------------------------- /shadowsocks/util.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "crypto/hmac" 8 | "crypto/sha1" 9 | "encoding/binary" 10 | ) 11 | 12 | func PrintVersion() { 13 | const version = "1.1.4" 14 | fmt.Println("shadowsocks-go version", version) 15 | } 16 | 17 | func IsFileExists(path string) (bool, error) { 18 | stat, err := os.Stat(path) 19 | if err == nil { 20 | if stat.Mode()&os.ModeType == 0 { 21 | return true, nil 22 | } 23 | return false, errors.New(path + " exists but is not regular file") 24 | } 25 | if os.IsNotExist(err) { 26 | return false, nil 27 | } 28 | return false, err 29 | } 30 | 31 | func HmacSha1(key []byte, data []byte) []byte { 32 | hmacSha1 := hmac.New(sha1.New, key) 33 | hmacSha1.Write(data) 34 | return hmacSha1.Sum(nil)[:10] 35 | } 36 | 37 | func otaConnectAuth(iv, key, data []byte) []byte { 38 | return append(data, HmacSha1(append(iv, key...), data)...) 39 | } 40 | 41 | func otaReqChunkAuth(iv []byte, chunkId uint32, data []byte) []byte { 42 | nb := make([]byte, 2) 43 | binary.BigEndian.PutUint16(nb, uint16(len(data))) 44 | chunkIdBytes := make([]byte, 4) 45 | binary.BigEndian.PutUint32(chunkIdBytes, chunkId) 46 | header := append(nb, HmacSha1(append(iv, chunkIdBytes...), data)...) 47 | return append(header, data...) 48 | } 49 | 50 | type ClosedFlag struct { 51 | flag bool 52 | } 53 | 54 | func (flag *ClosedFlag) SetClosed() { 55 | flag.flag = true 56 | } 57 | 58 | func (flag *ClosedFlag) IsClosed() bool { 59 | return flag.flag 60 | } -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/golang.org/x/crypto/PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /vendor/golang.org/x/sys/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/golang.org/x/sys/PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | --------------------------------------------------------------------------------