From 83788b0f17e6590cee20eecf3a8e0a70c6fc4b2b Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Sat, 27 Sep 2014 23:00:07 +0800 Subject: [PATCH 01/90] Fix method overridden by default command option. --- cmd/shadowsocks-local/local.go | 6 ++++-- cmd/shadowsocks-server/server.go | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/cmd/shadowsocks-local/local.go b/cmd/shadowsocks-local/local.go index e0336a4f..78d09957 100644 --- a/cmd/shadowsocks-local/local.go +++ b/cmd/shadowsocks-local/local.go @@ -355,7 +355,7 @@ func main() { flag.StringVar(&cmdConfig.Password, "k", "", "password") flag.IntVar(&cmdConfig.ServerPort, "p", 0, "server port") flag.IntVar(&cmdConfig.LocalPort, "l", 0, "local socks5 proxy port") - flag.StringVar(&cmdConfig.Method, "m", "aes-256-cfb", "encryption method") + flag.StringVar(&cmdConfig.Method, "m", "", "encryption method, default: aes-256-cfb") flag.BoolVar((*bool)(&debug), "d", false, "print debug message") flag.Parse() @@ -388,7 +388,9 @@ func main() { } else { ss.UpdateConfig(config, &cmdConfig) } - + if config.Method == "" { + config.Method = "aes-256-cfb" + } if len(config.ServerPassword) == 0 { if !enoughOptions(config) { fmt.Fprintln(os.Stderr, "must specify server address, password and both server/local port") diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index 391d2b21..6962b0b1 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -322,7 +322,7 @@ func main() { flag.StringVar(&cmdConfig.Password, "k", "", "password") flag.IntVar(&cmdConfig.ServerPort, "p", 0, "server port") flag.IntVar(&cmdConfig.Timeout, "t", 60, "connection timeout (in seconds)") - flag.StringVar(&cmdConfig.Method, "m", "aes-256-cfb", "encryption method") + flag.StringVar(&cmdConfig.Method, "m", "", "encryption method, default: aes-256-cfb") flag.IntVar(&core, "core", 0, "maximum number of CPU cores to use, default is determinied by Go runtime") flag.BoolVar((*bool)(&debug), "d", false, "print debug message") @@ -346,6 +346,9 @@ func main() { } else { ss.UpdateConfig(config, &cmdConfig) } + if config.Method == "" { + config.Method = "aes-256-cfb" + } if err = ss.CheckCipherMethod(config.Method); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) From b51b1ba2e743f97a558641d300dbe7edd5ecd926 Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Sun, 28 Sep 2014 09:49:26 +0800 Subject: [PATCH 02/90] Use netcat to wait server start. --- script/test.sh | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/script/test.sh b/script/test.sh index 4a288327..1e2cf768 100755 --- a/script/test.sh +++ b/script/test.sh @@ -1,16 +1,30 @@ #!/bin/bash +# Use [ -n "$TRAVIS" ] to test for running on Travis-CI. + # Run in the scripts directory. cd "$( dirname "${BASH_SOURCE[0]}" )" -OPTION="-p 8389 -k foobar" LOCAL_PORT="1090" +SERVER_PORT="8389" +OPTION="-p $SERVER_PORT -k foobar" SOCKS="127.0.0.1:$LOCAL_PORT" HTTP_PORT="8123" +wait_server() { + local port + port=$1 + for i in {1..20}; do + # sleep first because this maybe called immediately after server start + sleep 0.1 + nc -z -w 4 127.0.0.1 $port && break + done +} + start_http_server() { go build http.go ./http $HTTP_PORT & + wait_server $HTTP_PORT http_pid=$! } @@ -20,6 +34,8 @@ stop_http_server() { test_get() { local url + local target + local code url=$1 target=$2 code=$3 @@ -56,24 +72,11 @@ test_shadowsocks() { $SERVER $OPTION -m "$method" & server_pid=$! + wait_server $SERVER_PORT + $LOCAL $OPTION -s 127.0.0.1 -l $LOCAL_PORT -m "$method" & local_pid=$! - - # Wait server and client finish startup. - sleeptime=0.1 - if [ -n "$TRAVIS" ]; then - # On Travis we need to wait a little longer. - sleeptime=1 - elif echo $SERVER $LOCAL | grep 'py'; then - # The python version is slow to start. - if [[ $method == "table" ]]; then - sleeptime=2 - else - sleeptime=0.5 - fi - fi - echo $sleeptime - sleep $sleeptime + wait_server $LOCAL_PORT for i in {1..3}; do if ! test_get $url "shadowsocks-go"; then From 144fa0f0d18b1fafcd94852e448a25c202154e41 Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Sun, 28 Sep 2014 10:09:54 +0800 Subject: [PATCH 03/90] Bump version to 1.1.3 --- CHANGELOG | 3 +++ README.md | 2 +- shadowsocks/util.go | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 72826e95..07fb962e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +1.1.3 (2014-09-28) + * Fix can't specify encryption method in config file + 1.1.2 (2014-09-21) * Support new encryption method "rc4-md5" * Use aes-256-cfb as default encryption method for command line app diff --git a/README.md b/README.md index 7106909a..4939dd0b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # shadowsocks-go -Current version: 1.1.2 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=develop)](https://travis-ci.org/shadowsocks/shadowsocks-go) +Current version: 1.1.3 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=develop)](https://travis-ci.org/shadowsocks/shadowsocks-go) 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). diff --git a/shadowsocks/util.go b/shadowsocks/util.go index a026e9f2..732a5a57 100644 --- a/shadowsocks/util.go +++ b/shadowsocks/util.go @@ -7,7 +7,7 @@ import ( ) func PrintVersion() { - const version = "1.1.2" + const version = "1.1.3" fmt.Println("shadowsocks-go version", version) } From ac0380509f47684b92d019628a27ee6e025b1be6 Mon Sep 17 00:00:00 2001 From: defia Date: Wed, 14 Jan 2015 22:32:14 +0800 Subject: [PATCH 04/90] fix test and benchmark. add test and benchmark for rc4-md5 --- shadowsocks/encrypt_test.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/shadowsocks/encrypt_test.go b/shadowsocks/encrypt_test.go index b2583d3a..e9ff06d7 100644 --- a/shadowsocks/encrypt_test.go +++ b/shadowsocks/encrypt_test.go @@ -139,6 +139,10 @@ func TestDES(t *testing.T) { testBlockCipher(t, "des-cfb") } +func TestRC4MD5(t *testing.T) { + testBlockCipher(t, "rc4-md5") +} + var cipherKey = make([]byte, 64) func init() { @@ -156,8 +160,9 @@ func BenchmarkRC4Init(b *testing.B) { func benchmarkCipherInit(b *testing.B, ci *cipherInfo) { key := cipherKey[:ci.keyLen] + buf := make([]byte, ci.ivLen) for i := 0; i < b.N; i++ { - ci.newBlock(key) + ci.newStream(key, buf, Encrypt) } } @@ -190,3 +195,8 @@ func BenchmarkDESInit(b *testing.B) { ci := cipherMethod["des-cfb"] benchmarkCipherInit(b, ci) } + +func BenchmarkRC4MD5Init(b *testing.B) { + ci := cipherMethod["rc4-md5"] + benchmarkCipherInit(b, ci) +} From 7e0747270a7a5c07475d293e66a1d21a1116a4b6 Mon Sep 17 00:00:00 2001 From: ddatsh Date: Fri, 23 Jan 2015 11:44:56 +0800 Subject: [PATCH 05/90] use github.com/golang/crypto --- .travis.yml | 4 ++-- shadowsocks/encrypt.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index a9648dbb..9439c346 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,8 @@ language: go go: - 1.3 install: - - go get code.google.com/p/go.crypto/blowfish - - go get code.google.com/p/go.crypto/cast5 + - go get github.com/golang/crypto/blowfish + - go get github.com/golang/crypto/cast5 - go install ./cmd/shadowsocks-local - go install ./cmd/shadowsocks-server script: diff --git a/shadowsocks/encrypt.go b/shadowsocks/encrypt.go index a5bf34b6..fba62382 100644 --- a/shadowsocks/encrypt.go +++ b/shadowsocks/encrypt.go @@ -2,8 +2,8 @@ package shadowsocks import ( "bytes" - "code.google.com/p/go.crypto/blowfish" - "code.google.com/p/go.crypto/cast5" + "github.com/golang/crypto/blowfish" + "github.com/golang/crypto/cast5" "crypto/aes" "crypto/cipher" "crypto/des" From 896ea0a6e532e7bdd9d76a4ac3fbf1aecfb03e33 Mon Sep 17 00:00:00 2001 From: ddatsh Date: Sat, 24 Jan 2015 21:16:12 +0800 Subject: [PATCH 06/90] fix #64 go 1.4 canonical import paths --- .travis.yml | 4 ++-- shadowsocks/encrypt.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9439c346..c68e325b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,8 @@ language: go go: - 1.3 install: - - go get github.com/golang/crypto/blowfish - - go get github.com/golang/crypto/cast5 + - go get golang.org/x/crypto/blowfish + - go get golang.org/x/crypto/cast5 - go install ./cmd/shadowsocks-local - go install ./cmd/shadowsocks-server script: diff --git a/shadowsocks/encrypt.go b/shadowsocks/encrypt.go index fba62382..3dbb6735 100644 --- a/shadowsocks/encrypt.go +++ b/shadowsocks/encrypt.go @@ -2,8 +2,8 @@ package shadowsocks import ( "bytes" - "github.com/golang/crypto/blowfish" - "github.com/golang/crypto/cast5" + "golang.org/x/crypto/blowfish" + "golang.org/x/crypto/cast5" "crypto/aes" "crypto/cipher" "crypto/des" From 45e1575108688eeab34e97a1b313e4974d692eb8 Mon Sep 17 00:00:00 2001 From: defia Date: Tue, 3 Feb 2015 12:22:18 +0800 Subject: [PATCH 07/90] add support and test for chacha20 encrypt --- shadowsocks/encrypt.go | 11 +++++++++-- shadowsocks/encrypt_test.go | 9 +++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/shadowsocks/encrypt.go b/shadowsocks/encrypt.go index 3dbb6735..e9a563b5 100644 --- a/shadowsocks/encrypt.go +++ b/shadowsocks/encrypt.go @@ -2,8 +2,6 @@ package shadowsocks import ( "bytes" - "golang.org/x/crypto/blowfish" - "golang.org/x/crypto/cast5" "crypto/aes" "crypto/cipher" "crypto/des" @@ -13,6 +11,10 @@ import ( "encoding/binary" "errors" "io" + + "github.com/codahale/chacha20" + "golang.org/x/crypto/blowfish" + "golang.org/x/crypto/cast5" ) var errEmptyPassword = errors.New("empty key") @@ -137,6 +139,10 @@ func newRC4MD5Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { return rc4.NewCipher(rc4key) } +func newChaCha20Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { + return chacha20.New(key, iv) +} + type cipherInfo struct { keyLen int ivLen int @@ -153,6 +159,7 @@ var cipherMethod = map[string]*cipherInfo{ "rc4-md5": {16, 16, newRC4MD5Stream}, "rc4": {16, 0, nil}, "table": {16, 0, nil}, + "chacha20": {32, 8, newChaCha20Stream}, } func CheckCipherMethod(method string) error { diff --git a/shadowsocks/encrypt_test.go b/shadowsocks/encrypt_test.go index e9ff06d7..e135f645 100644 --- a/shadowsocks/encrypt_test.go +++ b/shadowsocks/encrypt_test.go @@ -143,6 +143,10 @@ func TestRC4MD5(t *testing.T) { testBlockCipher(t, "rc4-md5") } +func TestChaCha20(t *testing.T) { + testBlockCipher(t, "chacha20") +} + var cipherKey = make([]byte, 64) func init() { @@ -200,3 +204,8 @@ func BenchmarkRC4MD5Init(b *testing.B) { ci := cipherMethod["rc4-md5"] benchmarkCipherInit(b, ci) } + +func BenchmarkChaCha20Init(b *testing.B) { + ci := cipherMethod["chacha20"] + benchmarkCipherInit(b, ci) +} From dcb3b78febd9ee3b54b1a90620c64aad9f119135 Mon Sep 17 00:00:00 2001 From: defia Date: Tue, 3 Feb 2015 18:37:59 +0800 Subject: [PATCH 08/90] fix travis build failed --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c68e325b..646aed19 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ go: install: - go get golang.org/x/crypto/blowfish - go get golang.org/x/crypto/cast5 + - go get github.com/codahale/chacha20 - go install ./cmd/shadowsocks-local - go install ./cmd/shadowsocks-server script: From 08ad3023b032bc50c1e0566703899c4004837d2d Mon Sep 17 00:00:00 2001 From: defia Date: Wed, 11 Feb 2015 16:42:16 +0800 Subject: [PATCH 09/90] fix PipeThenClose will read once more when eof occurs --- shadowsocks/pipe.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks/pipe.go b/shadowsocks/pipe.go index 35f356f6..9e53bb72 100644 --- a/shadowsocks/pipe.go +++ b/shadowsocks/pipe.go @@ -35,7 +35,7 @@ func PipeThenClose(src, dst net.Conn, timeoutOpt int) { // read may return EOF with n > 0 // should always process n > 0 bytes before handling error if n > 0 { - if _, err = dst.Write(buf[0:n]); err != nil { + if _, err := dst.Write(buf[0:n]); err != nil { Debug.Println("write:", err) break } From 55f57aa9ac7a4144caa1a9bebc1354c01b3c9699 Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Thu, 12 Feb 2015 23:44:30 +0800 Subject: [PATCH 10/90] Fix #76: Write call overwrite err returned by Read. Thanks for defia pointing out this bug. --- shadowsocks/pipe.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shadowsocks/pipe.go b/shadowsocks/pipe.go index 35f356f6..7801a8fb 100644 --- a/shadowsocks/pipe.go +++ b/shadowsocks/pipe.go @@ -35,7 +35,8 @@ func PipeThenClose(src, dst net.Conn, timeoutOpt int) { // read may return EOF with n > 0 // should always process n > 0 bytes before handling error if n > 0 { - if _, err = dst.Write(buf[0:n]); err != nil { + // Note: avoid overwrite err returned by Read. + if _, err := dst.Write(buf[0:n]); err != nil { Debug.Println("write:", err) break } From a84c46b1ac23feba0edb8526f59bbe8ea66ec6e1 Mon Sep 17 00:00:00 2001 From: genzj Date: Sun, 8 Mar 2015 02:16:50 +0800 Subject: [PATCH 11/90] support salsa20 crypto --- shadowsocks/encrypt.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/shadowsocks/encrypt.go b/shadowsocks/encrypt.go index 3dbb6735..6ddf131c 100644 --- a/shadowsocks/encrypt.go +++ b/shadowsocks/encrypt.go @@ -4,6 +4,7 @@ import ( "bytes" "golang.org/x/crypto/blowfish" "golang.org/x/crypto/cast5" + "golang.org/x/crypto/salsa20/salsa" "crypto/aes" "crypto/cipher" "crypto/des" @@ -137,6 +138,31 @@ func newRC4MD5Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { return rc4.NewCipher(rc4key) } +type salsaStreamCipher struct { + nonce [8]byte + key [32]byte + counter int +} + +func (c *salsaStreamCipher) XORKeyStream(dst, src []byte) { + var subNonce [16]byte + padlen := c.counter % 64 + buf := make([]byte, padlen+len(src)) + copy(buf[padlen:], src[:]) + copy(subNonce[:], c.nonce[:]) + binary.LittleEndian.PutUint64(subNonce[len(c.nonce):], uint64(c.counter / 64)) + salsa.XORKeyStream(buf, buf, &subNonce, &c.key) + copy(dst, buf[padlen:]) + c.counter += len(src) +} + +func newSalsa20Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { + var c salsaStreamCipher + copy(c.nonce[:], iv[:8]) + copy(c.key[:], key[:32]) + return &c, nil +} + type cipherInfo struct { keyLen int ivLen int @@ -153,6 +179,7 @@ var cipherMethod = map[string]*cipherInfo{ "rc4-md5": {16, 16, newRC4MD5Stream}, "rc4": {16, 0, nil}, "table": {16, 0, nil}, + "salsa20": {32, 8, newSalsa20Stream}, } func CheckCipherMethod(method string) error { From 7d1eff995b30aff34066ab2f00ce58f5fb941c1f Mon Sep 17 00:00:00 2001 From: genzj Date: Sun, 8 Mar 2015 02:37:34 +0800 Subject: [PATCH 12/90] add salsa20 dependency to travis installation --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c68e325b..7eb7ebc0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ go: install: - go get golang.org/x/crypto/blowfish - go get golang.org/x/crypto/cast5 + - go get golang.org/x/crypto/salsa20 - go install ./cmd/shadowsocks-local - go install ./cmd/shadowsocks-server script: From ec0eca979c99f691779b82f54339ec7c39fe99ca Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Mon, 16 Mar 2015 23:00:49 +0800 Subject: [PATCH 13/90] Add --forbidden-ip= for test with shadowsocks python. --- script/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/test.sh b/script/test.sh index 1e2cf768..6a0aafd2 100755 --- a/script/test.sh +++ b/script/test.sh @@ -119,7 +119,7 @@ LOCAL="shadowsocks-local" test_server_local_pair if [[ -n $SS_PYTHON ]]; then - SERVER="$SS_PYTHON/server.py" + SERVER="$SS_PYTHON/server.py --forbidden-ip=" LOCAL="shadowsocks-local" test_server_local_pair From 5fcd2fdce2d9335609803d50d9c8ca0cbae5a227 Mon Sep 17 00:00:00 2001 From: genzj Date: Mon, 30 Mar 2015 03:22:09 +0000 Subject: [PATCH 14/90] add en-/decrypt tests and salsa20 init test --- shadowsocks/encrypt_test.go | 127 ++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/shadowsocks/encrypt_test.go b/shadowsocks/encrypt_test.go index e9ff06d7..ced22477 100644 --- a/shadowsocks/encrypt_test.go +++ b/shadowsocks/encrypt_test.go @@ -3,6 +3,8 @@ package shadowsocks import ( "crypto/rc4" "reflect" + "io" + "crypto/rand" "testing" ) @@ -144,11 +146,15 @@ func TestRC4MD5(t *testing.T) { } var cipherKey = make([]byte, 64) +var cipherIv = make([]byte, 64) + +const CIPHER_BENCHMARK_BUFFER_LEN = 4096 func init() { for i := 0; i < len(cipherKey); i++ { cipherKey[i] = byte(i) } + io.ReadFull(rand.Reader, cipherIv) } func BenchmarkRC4Init(b *testing.B) { @@ -200,3 +206,124 @@ func BenchmarkRC4MD5Init(b *testing.B) { ci := cipherMethod["rc4-md5"] benchmarkCipherInit(b, ci) } + +func BenchmarkSalsa20Init(b *testing.B) { + ci := cipherMethod["salsa20"] + benchmarkCipherInit(b, ci) +} + +func benchmarkCipherEncrypt(b *testing.B, ci *cipherInfo) { + key := cipherKey[:ci.keyLen] + iv := cipherIv[:ci.ivLen] + enc, err := ci.newStream(key, iv, Encrypt) + if err != nil { + b.Error(err) + } + src := make([]byte, CIPHER_BENCHMARK_BUFFER_LEN) + dst := make([]byte, CIPHER_BENCHMARK_BUFFER_LEN) + io.ReadFull(rand.Reader, src) + for i := 0; i < b.N; i++ { + enc.XORKeyStream(dst, src) + } +} + +func BenchmarkAES128Encrypt(b *testing.B) { + ci := cipherMethod["aes-128-cfb"] + benchmarkCipherEncrypt(b, ci) +} + +func BenchmarkAES192Encrypt(b *testing.B) { + ci := cipherMethod["aes-192-cfb"] + benchmarkCipherEncrypt(b, ci) +} + +func BenchmarkAES256Encrypt(b *testing.B) { + ci := cipherMethod["aes-256-cfb"] + benchmarkCipherEncrypt(b, ci) +} + +func BenchmarkBlowFishEncrypt(b *testing.B) { + ci := cipherMethod["bf-cfb"] + benchmarkCipherEncrypt(b, ci) +} + +func BenchmarkCast5Encrypt(b *testing.B) { + ci := cipherMethod["bf-cfb"] + benchmarkCipherEncrypt(b, ci) +} + +func BenchmarkDESEncrypt(b *testing.B) { + ci := cipherMethod["des-cfb"] + benchmarkCipherEncrypt(b, ci) +} + +func BenchmarkRC4MD5Encrypt(b *testing.B) { + ci := cipherMethod["rc4-md5"] + benchmarkCipherEncrypt(b, ci) +} + +func BenchmarkSalsa20Encrypt(b *testing.B) { + ci := cipherMethod["salsa20"] + benchmarkCipherEncrypt(b, ci) +} + +func benchmarkCipherDecrypt(b *testing.B, ci *cipherInfo) { + key := cipherKey[:ci.keyLen] + iv := cipherIv[:ci.ivLen] + enc, err := ci.newStream(key, iv, Encrypt) + if err != nil { + b.Error(err) + } + dec, err := ci.newStream(key, iv, Decrypt) + if err != nil { + b.Error(err) + } + src := make([]byte, CIPHER_BENCHMARK_BUFFER_LEN) + dst := make([]byte, CIPHER_BENCHMARK_BUFFER_LEN) + io.ReadFull(rand.Reader, src) + enc.XORKeyStream(dst, src) + for i := 0; i < b.N; i++ { + dec.XORKeyStream(src, dst) + } +} + +func BenchmarkAES128Decrypt(b *testing.B) { + ci := cipherMethod["aes-128-cfb"] + benchmarkCipherDecrypt(b, ci) +} + +func BenchmarkAES192Decrypt(b *testing.B) { + ci := cipherMethod["aes-192-cfb"] + benchmarkCipherDecrypt(b, ci) +} + +func BenchmarkAES256Decrypt(b *testing.B) { + ci := cipherMethod["aes-256-cfb"] + benchmarkCipherDecrypt(b, ci) +} + +func BenchmarkBlowFishDecrypt(b *testing.B) { + ci := cipherMethod["bf-cfb"] + benchmarkCipherDecrypt(b, ci) +} + +func BenchmarkCast5Decrypt(b *testing.B) { + ci := cipherMethod["bf-cfb"] + benchmarkCipherDecrypt(b, ci) +} + +func BenchmarkDESDecrypt(b *testing.B) { + ci := cipherMethod["des-cfb"] + benchmarkCipherDecrypt(b, ci) +} + +func BenchmarkRC4MD5Decrypt(b *testing.B) { + ci := cipherMethod["rc4-md5"] + benchmarkCipherDecrypt(b, ci) +} + +func BenchmarkSalsa20Decrypt(b *testing.B) { + ci := cipherMethod["salsa20"] + benchmarkCipherDecrypt(b, ci) +} + From 0f58b477fa0068dc3fd845acf071aa8b36e4a685 Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Sat, 25 Apr 2015 22:34:10 +0800 Subject: [PATCH 15/90] Tweak encrypt benchmark code. --- shadowsocks/encrypt_test.go | 86 ++++++++++++++----------------------- 1 file changed, 32 insertions(+), 54 deletions(-) diff --git a/shadowsocks/encrypt_test.go b/shadowsocks/encrypt_test.go index ced22477..3be50540 100644 --- a/shadowsocks/encrypt_test.go +++ b/shadowsocks/encrypt_test.go @@ -1,10 +1,10 @@ package shadowsocks import ( + "crypto/rand" "crypto/rc4" - "reflect" "io" - "crypto/rand" + "reflect" "testing" ) @@ -164,7 +164,8 @@ func BenchmarkRC4Init(b *testing.B) { } } -func benchmarkCipherInit(b *testing.B, ci *cipherInfo) { +func benchmarkCipherInit(b *testing.B, method string) { + ci := cipherMethod[method] key := cipherKey[:ci.keyLen] buf := make([]byte, ci.ivLen) for i := 0; i < b.N; i++ { @@ -173,46 +174,39 @@ func benchmarkCipherInit(b *testing.B, ci *cipherInfo) { } func BenchmarkAES128Init(b *testing.B) { - ci := cipherMethod["aes-128-cfb"] - benchmarkCipherInit(b, ci) + benchmarkCipherInit(b, "aes-128-cfb") } func BenchmarkAES192Init(b *testing.B) { - ci := cipherMethod["aes-192-cfb"] - benchmarkCipherInit(b, ci) + benchmarkCipherInit(b, "aes-192-cfb") } func BenchmarkAES256Init(b *testing.B) { - ci := cipherMethod["aes-256-cfb"] - benchmarkCipherInit(b, ci) + benchmarkCipherInit(b, "aes-256-cfb") } func BenchmarkBlowFishInit(b *testing.B) { - ci := cipherMethod["bf-cfb"] - benchmarkCipherInit(b, ci) + benchmarkCipherInit(b, "bf-cfb") } func BenchmarkCast5Init(b *testing.B) { - ci := cipherMethod["bf-cfb"] - benchmarkCipherInit(b, ci) + benchmarkCipherInit(b, "cast5-cfb") } func BenchmarkDESInit(b *testing.B) { - ci := cipherMethod["des-cfb"] - benchmarkCipherInit(b, ci) + benchmarkCipherInit(b, "des-cfb") } func BenchmarkRC4MD5Init(b *testing.B) { - ci := cipherMethod["rc4-md5"] - benchmarkCipherInit(b, ci) + benchmarkCipherInit(b, "rc4-md5") } func BenchmarkSalsa20Init(b *testing.B) { - ci := cipherMethod["salsa20"] - benchmarkCipherInit(b, ci) + benchmarkCipherInit(b, "salsa20") } -func benchmarkCipherEncrypt(b *testing.B, ci *cipherInfo) { +func benchmarkCipherEncrypt(b *testing.B, method string) { + ci := cipherMethod[method] key := cipherKey[:ci.keyLen] iv := cipherIv[:ci.ivLen] enc, err := ci.newStream(key, iv, Encrypt) @@ -228,46 +222,39 @@ func benchmarkCipherEncrypt(b *testing.B, ci *cipherInfo) { } func BenchmarkAES128Encrypt(b *testing.B) { - ci := cipherMethod["aes-128-cfb"] - benchmarkCipherEncrypt(b, ci) + benchmarkCipherEncrypt(b, "aes-128-cfb") } func BenchmarkAES192Encrypt(b *testing.B) { - ci := cipherMethod["aes-192-cfb"] - benchmarkCipherEncrypt(b, ci) + benchmarkCipherEncrypt(b, "aes-192-cfb") } func BenchmarkAES256Encrypt(b *testing.B) { - ci := cipherMethod["aes-256-cfb"] - benchmarkCipherEncrypt(b, ci) + benchmarkCipherEncrypt(b, "aes-256-cfb") } func BenchmarkBlowFishEncrypt(b *testing.B) { - ci := cipherMethod["bf-cfb"] - benchmarkCipherEncrypt(b, ci) + benchmarkCipherEncrypt(b, "bf-cfb") } func BenchmarkCast5Encrypt(b *testing.B) { - ci := cipherMethod["bf-cfb"] - benchmarkCipherEncrypt(b, ci) + benchmarkCipherEncrypt(b, "bf-cfb") } func BenchmarkDESEncrypt(b *testing.B) { - ci := cipherMethod["des-cfb"] - benchmarkCipherEncrypt(b, ci) + benchmarkCipherEncrypt(b, "des-cfb") } func BenchmarkRC4MD5Encrypt(b *testing.B) { - ci := cipherMethod["rc4-md5"] - benchmarkCipherEncrypt(b, ci) + benchmarkCipherEncrypt(b, "rc4-md5") } func BenchmarkSalsa20Encrypt(b *testing.B) { - ci := cipherMethod["salsa20"] - benchmarkCipherEncrypt(b, ci) + benchmarkCipherEncrypt(b, "salsa20") } -func benchmarkCipherDecrypt(b *testing.B, ci *cipherInfo) { +func benchmarkCipherDecrypt(b *testing.B, method string) { + ci := cipherMethod[method] key := cipherKey[:ci.keyLen] iv := cipherIv[:ci.ivLen] enc, err := ci.newStream(key, iv, Encrypt) @@ -288,42 +275,33 @@ func benchmarkCipherDecrypt(b *testing.B, ci *cipherInfo) { } func BenchmarkAES128Decrypt(b *testing.B) { - ci := cipherMethod["aes-128-cfb"] - benchmarkCipherDecrypt(b, ci) + benchmarkCipherDecrypt(b, "aes-128-cfb") } func BenchmarkAES192Decrypt(b *testing.B) { - ci := cipherMethod["aes-192-cfb"] - benchmarkCipherDecrypt(b, ci) + benchmarkCipherDecrypt(b, "aes-192-cfb") } func BenchmarkAES256Decrypt(b *testing.B) { - ci := cipherMethod["aes-256-cfb"] - benchmarkCipherDecrypt(b, ci) + benchmarkCipherDecrypt(b, "aes-256-cfb") } func BenchmarkBlowFishDecrypt(b *testing.B) { - ci := cipherMethod["bf-cfb"] - benchmarkCipherDecrypt(b, ci) + benchmarkCipherDecrypt(b, "bf-cfb") } func BenchmarkCast5Decrypt(b *testing.B) { - ci := cipherMethod["bf-cfb"] - benchmarkCipherDecrypt(b, ci) + benchmarkCipherDecrypt(b, "bf-cfb") } func BenchmarkDESDecrypt(b *testing.B) { - ci := cipherMethod["des-cfb"] - benchmarkCipherDecrypt(b, ci) + benchmarkCipherDecrypt(b, "des-cfb") } func BenchmarkRC4MD5Decrypt(b *testing.B) { - ci := cipherMethod["rc4-md5"] - benchmarkCipherDecrypt(b, ci) + benchmarkCipherDecrypt(b, "rc4-md5") } func BenchmarkSalsa20Decrypt(b *testing.B) { - ci := cipherMethod["salsa20"] - benchmarkCipherDecrypt(b, ci) + benchmarkCipherDecrypt(b, "salsa20") } - From 2241f28c578925fbc060ed931ca8c4bf0da60de4 Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Sun, 26 Apr 2015 00:18:34 +0800 Subject: [PATCH 16/90] Add salsa20 encryption in test script. --- script/test.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/script/test.sh b/script/test.sh index 6a0aafd2..021644d9 100755 --- a/script/test.sh +++ b/script/test.sh @@ -110,6 +110,7 @@ test_server_local_pair() { test_shadowsocks $url bf-cfb test_shadowsocks $url des-cfb test_shadowsocks $url cast5-cfb + test_shadowsocks $url salsa20 } start_http_server From a457853cd09a89eca41cd97d57f2f38a75728fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Fr=C3=B6ssman?= Date: Fri, 13 Feb 2015 12:42:21 +0100 Subject: [PATCH 17/90] Exit if port is not bindable --- cmd/shadowsocks-server/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index 6962b0b1..7214d956 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -261,7 +261,7 @@ func run(port, password string) { ln, err := net.Listen("tcp", ":"+port) if err != nil { log.Printf("error listening port %v: %v\n", port, err) - return + os.Exit(1) } passwdManager.add(port, password, ln) var cipher *ss.Cipher From c045c82e7ee3b7fc260b44abeb2544806407b64f Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Sun, 26 Apr 2015 00:44:50 +0800 Subject: [PATCH 18/90] Avoid create new slice for each Read and Write. --- shadowsocks/conn.go | 47 +++++++++++++++++++++++++++++++---------- shadowsocks/leakybuf.go | 5 +++++ shadowsocks/pipe.go | 9 ++------ 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/shadowsocks/conn.go b/shadowsocks/conn.go index b0b58e75..eacb41fb 100644 --- a/shadowsocks/conn.go +++ b/shadowsocks/conn.go @@ -11,10 +11,22 @@ import ( type Conn struct { net.Conn *Cipher + readBuf []byte + writeBuf []byte } -func NewConn(cn net.Conn, cipher *Cipher) *Conn { - return &Conn{cn, cipher} +func NewConn(c net.Conn, cipher *Cipher) *Conn { + return &Conn{ + Conn: c, + Cipher: cipher, + readBuf: leakyBuf.Get(), + writeBuf: leakyBuf.Get()} +} + +func (c *Conn) Close() error { + leakyBuf.Put(c.readBuf) + leakyBuf.Put(c.writeBuf) + return c.Conn.Close() } func RawAddr(addr string) (buf []byte, err error) { @@ -72,7 +84,14 @@ func (c *Conn) Read(b []byte) (n int, err error) { return } } - cipherData := make([]byte, len(b)) + + cipherData := c.readBuf + if len(b) > len(cipherData) { + cipherData = make([]byte, len(b)) + } else { + cipherData = cipherData[:len(b)] + } + n, err = c.Conn.Read(cipherData) if n > 0 { c.decrypt(b[0:n], cipherData[0:n]) @@ -81,23 +100,29 @@ func (c *Conn) Read(b []byte) (n int, err error) { } func (c *Conn) Write(b []byte) (n int, err error) { - var cipherData []byte - dataStart := 0 + var iv []byte if c.enc == nil { - var iv []byte iv, err = c.initEncrypt() if err != nil { return } + } + + cipherData := c.writeBuf + dataSize := len(b) + len(iv) + if dataSize > len(cipherData) { + cipherData = make([]byte, dataSize) + } else { + cipherData = cipherData[:dataSize] + } + + if iv != nil { // Put initialization vector in buffer, do a single write to send both // iv and data. - cipherData = make([]byte, len(b)+len(iv)) copy(cipherData, iv) - dataStart = len(iv) - } else { - cipherData = make([]byte, len(b)) } - c.encrypt(cipherData[dataStart:], b) + + c.encrypt(cipherData[len(iv):], b) n, err = c.Conn.Write(cipherData) return } diff --git a/shadowsocks/leakybuf.go b/shadowsocks/leakybuf.go index 3c7dd8c5..f673cc3b 100644 --- a/shadowsocks/leakybuf.go +++ b/shadowsocks/leakybuf.go @@ -6,6 +6,11 @@ type LeakyBuf struct { freeList chan []byte } +const bufSize = 4096 +const maxNBuf = 2048 + +var leakyBuf = NewLeakyBuf(maxNBuf, bufSize) + // NewLeakyBuf creates a leaky buffer which can hold at most n buffer, each // with bufSize bytes. func NewLeakyBuf(n, bufSize int) *LeakyBuf { diff --git a/shadowsocks/pipe.go b/shadowsocks/pipe.go index 7801a8fb..877853b9 100644 --- a/shadowsocks/pipe.go +++ b/shadowsocks/pipe.go @@ -17,16 +17,11 @@ func SetReadTimeout(c net.Conn) { } } -const bufSize = 4096 -const nBuf = 2048 - -var pipeBuf = NewLeakyBuf(nBuf, bufSize) - // PipeThenClose copies data from src to dst, closes dst when done. func PipeThenClose(src, dst net.Conn, timeoutOpt int) { defer dst.Close() - buf := pipeBuf.Get() - defer pipeBuf.Put(buf) + buf := leakyBuf.Get() + defer leakyBuf.Put(buf) for { if timeoutOpt == SET_TIMEOUT { SetReadTimeout(src) From 67ddbe40371a486c21666b7f2564d34f7c29aab7 Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Sun, 26 Apr 2015 22:11:54 +0800 Subject: [PATCH 19/90] Reuse buffer when possible for salsa20 cipher. --- shadowsocks/encrypt.go | 35 +++++++++++++++++++++++++---------- shadowsocks/leakybuf.go | 4 ++-- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/shadowsocks/encrypt.go b/shadowsocks/encrypt.go index 6ddf131c..e0bdc145 100644 --- a/shadowsocks/encrypt.go +++ b/shadowsocks/encrypt.go @@ -2,9 +2,6 @@ package shadowsocks import ( "bytes" - "golang.org/x/crypto/blowfish" - "golang.org/x/crypto/cast5" - "golang.org/x/crypto/salsa20/salsa" "crypto/aes" "crypto/cipher" "crypto/des" @@ -13,6 +10,9 @@ import ( "crypto/rc4" "encoding/binary" "errors" + "golang.org/x/crypto/blowfish" + "golang.org/x/crypto/cast5" + "golang.org/x/crypto/salsa20/salsa" "io" ) @@ -139,20 +139,35 @@ func newRC4MD5Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { } type salsaStreamCipher struct { - nonce [8]byte - key [32]byte + nonce [8]byte + key [32]byte counter int } func (c *salsaStreamCipher) XORKeyStream(dst, src []byte) { + var buf []byte + padLen := c.counter % 64 + dataSize := len(src) + padLen + if cap(dst) >= dataSize { + buf = dst[:dataSize] + } else if leakyBufSize >= dataSize { + buf = leakyBuf.Get() + defer leakyBuf.Put(buf) + buf = buf[:dataSize] + } else { + buf = make([]byte, dataSize) + } + var subNonce [16]byte - padlen := c.counter % 64 - buf := make([]byte, padlen+len(src)) - copy(buf[padlen:], src[:]) copy(subNonce[:], c.nonce[:]) - binary.LittleEndian.PutUint64(subNonce[len(c.nonce):], uint64(c.counter / 64)) + binary.LittleEndian.PutUint64(subNonce[len(c.nonce):], uint64(c.counter/64)) + + // It's difficult to avoid data copy here. src or dst maybe slice from + // Conn.Read/Write, which can't have padding. + copy(buf[padLen:], src[:]) salsa.XORKeyStream(buf, buf, &subNonce, &c.key) - copy(dst, buf[padlen:]) + copy(dst, buf[padLen:]) + c.counter += len(src) } diff --git a/shadowsocks/leakybuf.go b/shadowsocks/leakybuf.go index f673cc3b..3b55832d 100644 --- a/shadowsocks/leakybuf.go +++ b/shadowsocks/leakybuf.go @@ -6,10 +6,10 @@ type LeakyBuf struct { freeList chan []byte } -const bufSize = 4096 +const leakyBufSize = 4096 const maxNBuf = 2048 -var leakyBuf = NewLeakyBuf(maxNBuf, bufSize) +var leakyBuf = NewLeakyBuf(maxNBuf, leakyBufSize) // NewLeakyBuf creates a leaky buffer which can hold at most n buffer, each // with bufSize bytes. From 7aed8e85ef555067e861c5e542b665eca92193a4 Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Sun, 26 Apr 2015 22:57:15 +0800 Subject: [PATCH 20/90] Add -t for local, always set timeout for data transfer. --- cmd/shadowsocks-local/local.go | 7 +++++-- cmd/shadowsocks-server/server.go | 9 +++------ shadowsocks/config.go | 3 +++ shadowsocks/pipe.go | 11 ++--------- 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/cmd/shadowsocks-local/local.go b/cmd/shadowsocks-local/local.go index 78d09957..03bd8efa 100644 --- a/cmd/shadowsocks-local/local.go +++ b/cmd/shadowsocks-local/local.go @@ -49,6 +49,7 @@ func handShake(conn net.Conn) (err error) { buf := make([]byte, 258) var n int + ss.SetReadTimeout(conn) // make sure we get the nmethod field if n, err = io.ReadAtLeast(conn, buf, idNmethod+1); err != nil { return @@ -92,6 +93,7 @@ func getRequest(conn net.Conn) (rawaddr []byte, host string, err error) { // refer to getRequest in server.go for why set buffer size to 263 buf := make([]byte, 263) var n int + ss.SetReadTimeout(conn) // read till we get possible domain length field if n, err = io.ReadAtLeast(conn, buf, idDmLen+1); err != nil { return @@ -314,8 +316,8 @@ func handleConnection(conn net.Conn) { } }() - go ss.PipeThenClose(conn, remote, ss.NO_TIMEOUT) - ss.PipeThenClose(remote, conn, ss.NO_TIMEOUT) + go ss.PipeThenClose(conn, remote) + ss.PipeThenClose(remote, conn) closed = true debug.Println("closed connection to", addr) } @@ -354,6 +356,7 @@ func main() { flag.StringVar(&cmdLocal, "b", "", "local address, listen only to this address if specified") flag.StringVar(&cmdConfig.Password, "k", "", "password") flag.IntVar(&cmdConfig.ServerPort, "p", 0, "server port") + flag.IntVar(&cmdConfig.Timeout, "t", 300, "timeout in seconds") flag.IntVar(&cmdConfig.LocalPort, "l", 0, "local socks5 proxy port") flag.StringVar(&cmdConfig.Method, "m", "", "encryption method, default: aes-256-cfb") flag.BoolVar((*bool)(&debug), "d", false, "print debug message") diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index 7214d956..982fcaf3 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -19,8 +19,6 @@ import ( var debug ss.DebugLog -const dnsGoroutineNum = 64 - func getRequest(conn *ss.Conn) (host string, extra []byte, err error) { const ( idType = 0 // address type index @@ -62,7 +60,6 @@ func getRequest(conn *ss.Conn) (host string, extra []byte, err error) { } if n < reqLen { // rare case - ss.SetReadTimeout(conn) if _, err = io.ReadFull(conn, buf[n:reqLen]); err != nil { return } @@ -154,8 +151,8 @@ func handleConnection(conn *ss.Conn) { if debug { debug.Printf("piping %s<->%s", conn.RemoteAddr(), host) } - go ss.PipeThenClose(conn, remote, ss.SET_TIMEOUT) - ss.PipeThenClose(remote, conn, ss.NO_TIMEOUT) + go ss.PipeThenClose(conn, remote) + ss.PipeThenClose(remote, conn) closed = true return } @@ -321,7 +318,7 @@ func main() { flag.StringVar(&configFile, "c", "config.json", "specify config file") flag.StringVar(&cmdConfig.Password, "k", "", "password") flag.IntVar(&cmdConfig.ServerPort, "p", 0, "server port") - flag.IntVar(&cmdConfig.Timeout, "t", 60, "connection timeout (in seconds)") + flag.IntVar(&cmdConfig.Timeout, "t", 300, "timeout in seconds") flag.StringVar(&cmdConfig.Method, "m", "", "encryption method, default: aes-256-cfb") flag.IntVar(&core, "core", 0, "maximum number of CPU cores to use, default is determinied by Go runtime") flag.BoolVar((*bool)(&debug), "d", false, "print debug message") diff --git a/shadowsocks/config.go b/shadowsocks/config.go index f96ff504..9d3696b4 100644 --- a/shadowsocks/config.go +++ b/shadowsocks/config.go @@ -127,4 +127,7 @@ func UpdateConfig(old, new *Config) { if old.Method == "table" { old.Method = "" } + + old.Timeout = new.Timeout + readTimeout = time.Duration(old.Timeout) * time.Second } diff --git a/shadowsocks/pipe.go b/shadowsocks/pipe.go index 877853b9..515c99f8 100644 --- a/shadowsocks/pipe.go +++ b/shadowsocks/pipe.go @@ -6,11 +6,6 @@ import ( "time" ) -const ( - NO_TIMEOUT = iota - SET_TIMEOUT -) - func SetReadTimeout(c net.Conn) { if readTimeout != 0 { c.SetReadDeadline(time.Now().Add(readTimeout)) @@ -18,14 +13,12 @@ func SetReadTimeout(c net.Conn) { } // PipeThenClose copies data from src to dst, closes dst when done. -func PipeThenClose(src, dst net.Conn, timeoutOpt int) { +func PipeThenClose(src, dst net.Conn) { defer dst.Close() buf := leakyBuf.Get() defer leakyBuf.Put(buf) for { - if timeoutOpt == SET_TIMEOUT { - SetReadTimeout(src) - } + SetReadTimeout(src) n, err := src.Read(buf) // read may return EOF with n > 0 // should always process n > 0 bytes before handling error From 6b502a4abe6d3a9ff0ef17b08586bf3e5763aefb Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Sun, 10 May 2015 22:23:12 +0800 Subject: [PATCH 21/90] Add compatibity test for chacha20. --- script/test.sh | 1 + shadowsocks/encrypt_test.go | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/script/test.sh b/script/test.sh index 021644d9..f9b46169 100755 --- a/script/test.sh +++ b/script/test.sh @@ -110,6 +110,7 @@ test_server_local_pair() { test_shadowsocks $url bf-cfb test_shadowsocks $url des-cfb test_shadowsocks $url cast5-cfb + test_shadowsocks $url chacha20 test_shadowsocks $url salsa20 } diff --git a/shadowsocks/encrypt_test.go b/shadowsocks/encrypt_test.go index 58f7e8c7..24da305e 100644 --- a/shadowsocks/encrypt_test.go +++ b/shadowsocks/encrypt_test.go @@ -205,6 +205,10 @@ func BenchmarkRC4MD5Init(b *testing.B) { benchmarkCipherInit(b, "rc4-md5") } +func BenchmarkChaCha20Init(b *testing.B) { + benchmarkCipherInit(b, "chacha20") +} + func BenchmarkSalsa20Init(b *testing.B) { benchmarkCipherInit(b, "salsa20") } @@ -253,6 +257,10 @@ func BenchmarkRC4MD5Encrypt(b *testing.B) { benchmarkCipherEncrypt(b, "rc4-md5") } +func BenchmarkChacha20Encrypt(b *testing.B) { + benchmarkCipherEncrypt(b, "chacha20") +} + func BenchmarkSalsa20Encrypt(b *testing.B) { benchmarkCipherEncrypt(b, "salsa20") } @@ -306,11 +314,10 @@ func BenchmarkRC4MD5Decrypt(b *testing.B) { benchmarkCipherDecrypt(b, "rc4-md5") } -func BenchmarkSalsa20Decrypt(b *testing.B) { - benchmarkCipherDecrypt(b, "salsa20") +func BenchmarkChaCha20Decrypt(b *testing.B) { + benchmarkCipherDecrypt(b, "chacha20") } -func BenchmarkChaCha20Init(b *testing.B) { - ci := cipherMethod["chacha20"] - benchmarkCipherInit(b, ci) +func BenchmarkSalsa20Decrypt(b *testing.B) { + benchmarkCipherDecrypt(b, "salsa20") } From 0cb0001fa237d6b1b4970c589863be6fb92b07eb Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Sun, 10 May 2015 22:27:26 +0800 Subject: [PATCH 22/90] Use go 1.4.2 in travis. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 06381d15..2cc770b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: go go: - - 1.3 + - 1.4.2 install: - go get golang.org/x/crypto/blowfish - go get golang.org/x/crypto/cast5 From d4a6a1223c87e2b8f1e6568b187af5d0bd93d5b3 Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Sun, 10 May 2015 20:40:26 +0800 Subject: [PATCH 23/90] Bump version to 1.1.4 --- .gitignore | 1 + CHANGELOG | 7 +++++++ README.md | 4 ++-- shadowsocks/util.go | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index c00df136..5966b0f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.deb +script/http diff --git a/CHANGELOG b/CHANGELOG index 07fb962e..9558b98c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,10 @@ +1.1.4 (2015-05-10) + * Support "chacha20" encryption method, thanks to @defia + * Support "salsa20" encryption method, thanks to @genzj + * Fix go 1.4 canonical import paths, thanks to @ddatsh + * Exit if port not bindable, thanks to @thomasf + * More buffer reuse + 1.1.3 (2014-09-28) * Fix can't specify encryption method in config file diff --git a/README.md b/README.md index 4939dd0b..f3736a38 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # shadowsocks-go -Current version: 1.1.3 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=develop)](https://travis-ci.org/shadowsocks/shadowsocks-go) +Current version: 1.1.4 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=develop)](https://travis-ci.org/shadowsocks/shadowsocks-go) 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). @@ -36,7 +36,7 @@ server your server ip or hostname server_port server port local_port local socks5 proxy port method encryption method, null by default (table), the following methods are supported: - aes-128-cfb, aes-192-cfb, aes-256-cfb, bf-cfb, cast5-cfb, des-cfb, rc4-md5, rc4, table + aes-128-cfb, aes-192-cfb, aes-256-cfb, bf-cfb, cast5-cfb, des-cfb, rc4-md5, chacha20, salsa20, rc4, table password a password used to encrypt transfer timeout server option, in seconds ``` diff --git a/shadowsocks/util.go b/shadowsocks/util.go index 732a5a57..2c5198d2 100644 --- a/shadowsocks/util.go +++ b/shadowsocks/util.go @@ -7,7 +7,7 @@ import ( ) func PrintVersion() { - const version = "1.1.3" + const version = "1.1.4" fmt.Println("shadowsocks-go version", version) } From 2cd721de5279be5c74331ae69318ba6d4e6ed5a3 Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Sun, 10 May 2015 23:00:15 +0800 Subject: [PATCH 24/90] Remove no longer used google code upload script. --- script/upload.sh | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100755 script/upload.sh diff --git a/script/upload.sh b/script/upload.sh deleted file mode 100755 index b3edacf6..00000000 --- a/script/upload.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -cd "$( dirname "${BASH_SOURCE[0]}" )/.." - -if [[ $# != 2 ]]; then - echo "upload.sh " - exit 1 -fi - -version=$(awk '/\tconst version =/ { print $4 }' shadowsocks/util.go | sed -e 's/"//g') -username=$1 -passwd=$2 - -upload() { - summary=$1 - file=$2 - googlecode_upload.py -l Featured -u "$username" -w "$passwd" -s "$summary" -p "shadowsocks-go" "$file" -} - -upload "$version OS X Client 64bit" bin/shadowsocks-local-mac64-$version.gz -upload "$version Linux Client 32bit" bin/shadowsocks-local-linux32-$version.gz -upload "$version Linux Client 64bit" bin/shadowsocks-local-linux64-$version.gz -upload "$version Windows Client 64bit" bin/shadowsocks-local-win64-$version.zip -upload "$version Windows Client 32bit" bin/shadowsocks-local-win32-$version.zip - -upload "$version Linux Server 32bit" bin/shadowsocks-server-linux32-$version.gz -upload "$version Linux Server 64bit" bin/shadowsocks-server-linux64-$version.gz - -upload "$version Linux Server deb 32bit" bin/shadowsocks-go_$version-1-386.deb -upload "$version Linux Server deb 64bit" bin/shadowsocks-go_$version-1-amd64.deb - From 2afd0884bcf7a72acd1526ebdbcf769e71f81272 Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Sun, 10 May 2015 23:07:52 +0800 Subject: [PATCH 25/90] Add missing change in CHANGELOG. --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 9558b98c..b6886994 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,7 @@ * Support "salsa20" encryption method, thanks to @genzj * Fix go 1.4 canonical import paths, thanks to @ddatsh * Exit if port not bindable, thanks to @thomasf + * Always set timeout for data transfer * More buffer reuse 1.1.3 (2014-09-28) From fef07e2ae11405560c1f043d01545ccbd581b254 Mon Sep 17 00:00:00 2001 From: Yifu Yu Date: Fri, 7 Aug 2015 01:36:51 +0800 Subject: [PATCH 26/90] Add proxy api specified in golang.org/x/net/proxy Other golang projects can now integrate with shadowsocks-go much easier. --- shadowsocks/proxy.go | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 shadowsocks/proxy.go diff --git a/shadowsocks/proxy.go b/shadowsocks/proxy.go new file mode 100644 index 00000000..25f6040d --- /dev/null +++ b/shadowsocks/proxy.go @@ -0,0 +1,34 @@ +package shadowsocks + +import ( + "errors" + "strings" + "fmt" +) + +type Dialer struct { + cipher *Cipher + server string + support_udp bool +} + +var ErrNilCipher = errors.New("cipher can't be nil.") + +func NewDialer(server string, cipher *Cipher) (dialer *Dialer, err error) { + // Currently shadowsocks-go do not support UDP + if cipher == nil { + return nil, ErrNilCipher + } + return &Dialer { + cipher: cipher, + server: server, + support_udp: false, + }, nil +} + +func (d *Dialer) Dial(network, addr string) (c *Conn, err error) { + if strings.HasPrefix(network, "tcp") { + return Dial(addr, d.server, d.cipher) + } + return nil, fmt.Errorf("unsupported connection type: %s", network) +} From 9f6e7123423e8fc4e976628f8c67eb7169dcd297 Mon Sep 17 00:00:00 2001 From: Yifu Yu Date: Fri, 7 Aug 2015 02:32:24 +0800 Subject: [PATCH 27/90] Fix net.Conn interface. --- shadowsocks/proxy.go | 54 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/shadowsocks/proxy.go b/shadowsocks/proxy.go index 25f6040d..9758a6df 100644 --- a/shadowsocks/proxy.go +++ b/shadowsocks/proxy.go @@ -4,6 +4,8 @@ import ( "errors" "strings" "fmt" + "net" + "time" ) type Dialer struct { @@ -12,6 +14,16 @@ type Dialer struct { support_udp bool } +type ProxyConn struct { + *Conn + raddr *ProxyAddr +} + +type ProxyAddr struct { + network string + address string +} + var ErrNilCipher = errors.New("cipher can't be nil.") func NewDialer(server string, cipher *Cipher) (dialer *Dialer, err error) { @@ -26,9 +38,47 @@ func NewDialer(server string, cipher *Cipher) (dialer *Dialer, err error) { }, nil } -func (d *Dialer) Dial(network, addr string) (c *Conn, err error) { +func (d *Dialer) Dial(network, addr string) (c *ProxyConn, err error) { if strings.HasPrefix(network, "tcp") { - return Dial(addr, d.server, d.cipher) + conn, err := Dial(addr, d.server, d.cipher) + if err != nil { + return nil, err + } + return &ProxyConn { + Conn: conn, + raddr: &ProxyAddr { + network: network, + address: addr, + }, + }, nil } return nil, fmt.Errorf("unsupported connection type: %s", network) } + +func (c *ProxyConn) LocalAddr() net.Addr { + return c.Conn.LocalAddr() +} + +func (c *ProxyConn) RemoteAddr() net.Addr { + return c.raddr +} + +func (c *ProxyConn) SetDeadline(t time.Time) error { + return c.Conn.SetDeadline(t) +} + +func (c *ProxyConn) SetReadDeadline(t time.Time) error { + return c.Conn.SetReadDeadline(t) +} + +func (c *ProxyConn) SetWriteDeadline(t time.Time) error { + return c.Conn.SetWriteDeadline(t) +} + +func (a *ProxyAddr) Network() string { + return a.network +} + +func (a *ProxyAddr) String() string { + return a.address +} From a119059c5850ac25c8061a9ee816b067065e66d1 Mon Sep 17 00:00:00 2001 From: Yifu Yu Date: Fri, 7 Aug 2015 02:36:19 +0800 Subject: [PATCH 28/90] Fix return type. --- shadowsocks/proxy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks/proxy.go b/shadowsocks/proxy.go index 9758a6df..83049a8a 100644 --- a/shadowsocks/proxy.go +++ b/shadowsocks/proxy.go @@ -38,7 +38,7 @@ func NewDialer(server string, cipher *Cipher) (dialer *Dialer, err error) { }, nil } -func (d *Dialer) Dial(network, addr string) (c *ProxyConn, err error) { +func (d *Dialer) Dial(network, addr string) (c net.Conn, err error) { if strings.HasPrefix(network, "tcp") { conn, err := Dial(addr, d.server, d.cipher) if err != nil { From ad930372543d473907ab4f46771691c239a9af13 Mon Sep 17 00:00:00 2001 From: Yifu Yu Date: Fri, 7 Aug 2015 04:43:44 +0800 Subject: [PATCH 29/90] Fix cipher reuse problem. --- shadowsocks/proxy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks/proxy.go b/shadowsocks/proxy.go index 83049a8a..0b6145bc 100644 --- a/shadowsocks/proxy.go +++ b/shadowsocks/proxy.go @@ -40,7 +40,7 @@ func NewDialer(server string, cipher *Cipher) (dialer *Dialer, err error) { func (d *Dialer) Dial(network, addr string) (c net.Conn, err error) { if strings.HasPrefix(network, "tcp") { - conn, err := Dial(addr, d.server, d.cipher) + conn, err := Dial(addr, d.server, d.cipher.Copy()) if err != nil { return nil, err } From 97d83ee63c6fade19398697b236172540c3b5b03 Mon Sep 17 00:00:00 2001 From: Xiaodu Date: Fri, 14 Aug 2015 22:54:26 +0800 Subject: [PATCH 30/90] Cipher cache should include encryption method in key ... otherwise two servers with different encryption method but same password will share the same cipher, and client will fail to authenticate. Instead of using map[string]map[string]*ss.Cipher, just prepend password with encryption method name and a delimiter "|" as cache key. Since there are no encryption methods with "|" in their names (and unlikely any further ones), it's safe to use this delimiter. Another problem is that, no method provided ("") is equivalent to "table", but will have two entries in cipher cache. But it's hard to know the default encryption method from package ss, and "table" is rarely used, this won't be a big problem. --- cmd/shadowsocks-local/local.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd/shadowsocks-local/local.go b/cmd/shadowsocks-local/local.go index 03bd8efa..cfcdbf81 100644 --- a/cmd/shadowsocks-local/local.go +++ b/cmd/shadowsocks-local/local.go @@ -208,14 +208,17 @@ func parseServerConfig(config *ss.Config) { if !hasPort(server) { log.Fatalf("no port for server %s\n", server) } - cipher, ok := cipherCache[passwd] + // Using "|" as delimiter is safe here, since no encryption + // method contains it in the name. + cacheKey := encmethod + "|" + passwd + cipher, ok := cipherCache[cacheKey] if !ok { var err error cipher, err = ss.NewCipher(encmethod, passwd) if err != nil { log.Fatal("Failed generating ciphers:", err) } - cipherCache[passwd] = cipher + cipherCache[cacheKey] = cipher } servers.srvCipher[i] = &ServerCipher{server, cipher} i++ From 8e7c0f511623ef0497140b7e08afb7e4c300de49 Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Tue, 25 Aug 2015 22:49:20 +0800 Subject: [PATCH 31/90] Add apache license. --- LICENSE | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From 8bd4f179bb6a5c83c801b901cc0b1db2ab59899b Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Tue, 25 Aug 2015 22:49:20 +0800 Subject: [PATCH 32/90] Add apache license. --- LICENSE | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From 2390b7f5e6a59962a2b690937884e644b4734893 Mon Sep 17 00:00:00 2001 From: amyangfei Date: Wed, 16 Sep 2015 20:26:20 +0800 Subject: [PATCH 33/90] Fix encrypt test code --- shadowsocks/encrypt_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shadowsocks/encrypt_test.go b/shadowsocks/encrypt_test.go index 24da305e..680e73c2 100644 --- a/shadowsocks/encrypt_test.go +++ b/shadowsocks/encrypt_test.go @@ -246,7 +246,7 @@ func BenchmarkBlowFishEncrypt(b *testing.B) { } func BenchmarkCast5Encrypt(b *testing.B) { - benchmarkCipherEncrypt(b, "bf-cfb") + benchmarkCipherEncrypt(b, "cast5-cfb") } func BenchmarkDESEncrypt(b *testing.B) { @@ -303,7 +303,7 @@ func BenchmarkBlowFishDecrypt(b *testing.B) { } func BenchmarkCast5Decrypt(b *testing.B) { - benchmarkCipherDecrypt(b, "bf-cfb") + benchmarkCipherDecrypt(b, "cast5-cfb") } func BenchmarkDESDecrypt(b *testing.B) { From dbe517874c00920e61772524f14a04e857cf2aaa Mon Sep 17 00:00:00 2001 From: ayanamist Date: Wed, 2 Dec 2015 21:11:33 +0800 Subject: [PATCH 34/90] use travis ci new infrastructure use go 1.4.3 in travis ci, add timeout for curl in test.sh --- .travis.yml | 3 ++- script/test.sh | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2cc770b2..dea91125 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: go go: - - 1.4.2 + - 1.4.3 install: - go get golang.org/x/crypto/blowfish - go get golang.org/x/crypto/cast5 @@ -10,3 +10,4 @@ install: - go install ./cmd/shadowsocks-server script: - PATH=$PATH:$HOME/gopath/bin bash -x ./script/test.sh +sudo: false \ No newline at end of file diff --git a/script/test.sh b/script/test.sh index f9b46169..2342952d 100755 --- a/script/test.sh +++ b/script/test.sh @@ -47,7 +47,7 @@ test_get() { # -s silent to disable progress meter, but enable --show-error # -i to include http header # -L to follow redirect so we should always get HTTP 200 - cont=`curl --socks5 $SOCKS -s --show-error -i -L $url 2>&1` + cont=`curl -m 5 --socks5 $SOCKS -s --show-error -i -L $url 2>&1` ok=`echo $cont | grep -E -o "HTTP/1\.1 +$code"` html=`echo $cont | grep -E -o -i "$target"` if [[ -z $ok || -z $html ]] ; then From 07f4a06d619f331c117cc05e2fff351c2a822795 Mon Sep 17 00:00:00 2001 From: ayanamist Date: Sat, 5 Dec 2015 00:18:32 +0800 Subject: [PATCH 35/90] deprecate table and rc4 --- script/test.sh | 2 - shadowsocks/config.go | 3 -- shadowsocks/encrypt.go | 80 +++---------------------------------- shadowsocks/encrypt_test.go | 69 -------------------------------- 4 files changed, 6 insertions(+), 148 deletions(-) diff --git a/script/test.sh b/script/test.sh index 2342952d..99608030 100755 --- a/script/test.sh +++ b/script/test.sh @@ -101,8 +101,6 @@ test_server_local_pair() { local url url=http://127.0.0.1:$HTTP_PORT/README.md - test_shadowsocks $url table - test_shadowsocks $url rc4 test_shadowsocks $url rc4-md5 test_shadowsocks $url aes-128-cfb test_shadowsocks $url aes-192-cfb diff --git a/shadowsocks/config.go b/shadowsocks/config.go index 9d3696b4..ec5d2f22 100644 --- a/shadowsocks/config.go +++ b/shadowsocks/config.go @@ -124,9 +124,6 @@ func UpdateConfig(old, new *Config) { } } } - if old.Method == "table" { - old.Method = "" - } old.Timeout = new.Timeout readTimeout = time.Duration(old.Timeout) * time.Second diff --git a/shadowsocks/encrypt.go b/shadowsocks/encrypt.go index 5a79a25f..0e06bd9f 100644 --- a/shadowsocks/encrypt.go +++ b/shadowsocks/encrypt.go @@ -1,7 +1,6 @@ package shadowsocks import ( - "bytes" "crypto/aes" "crypto/cipher" "crypto/des" @@ -19,8 +18,6 @@ import ( var errEmptyPassword = errors.New("empty key") -type tableCipher []byte - func md5sum(d []byte) []byte { h := md5.New() h.Write(d) @@ -47,50 +44,6 @@ func evpBytesToKey(password string, keyLen int) (key []byte) { return m[:keyLen] } -func (tbl tableCipher) XORKeyStream(dst, src []byte) { - for i := 0; i < len(src); i++ { - dst[i] = tbl[src[i]] - } -} - -// NewTableCipher creates a new table based cipher. -func newTableCipher(s []byte) (enc, dec tableCipher) { - const tbl_size = 256 - enc = make([]byte, tbl_size) - dec = make([]byte, tbl_size) - table := make([]uint64, tbl_size) - - var a uint64 - buf := bytes.NewBuffer(s) - binary.Read(buf, binary.LittleEndian, &a) - var i uint64 - for i = 0; i < tbl_size; i++ { - table[i] = i - } - for i = 1; i < 1024; i++ { - table = Sort(table, func(x, y uint64) int64 { - return int64(a%uint64(x+i) - a%uint64(y+i)) - }) - } - for i = 0; i < tbl_size; i++ { - enc[i] = byte(table[i]) - } - for i = 0; i < tbl_size; i++ { - dec[enc[i]] = byte(i) - } - return enc, dec -} - -func newRC4Cipher(key []byte) (enc, dec cipher.Stream, err error) { - rc4Enc, err := rc4.NewCipher(key) - if err != nil { - return - } - // create a copy, as RC4 encrypt and decrypt uses the same keystream - rc4Dec := *rc4Enc - return rc4Enc, &rc4Dec, nil -} - type DecOrEnc int const ( @@ -190,8 +143,6 @@ type cipherInfo struct { } var cipherMethod = map[string]*cipherInfo{ - "rc4": {16, 0, nil}, - "table": {16, 0, nil}, "aes-128-cfb": {16, 16, newAESStream}, "aes-192-cfb": {24, 16, newAESStream}, "aes-256-cfb": {32, 16, newAESStream}, @@ -205,7 +156,7 @@ var cipherMethod = map[string]*cipherInfo{ func CheckCipherMethod(method string) error { if method == "" { - method = "table" + method = "aes-256-cfb" } _, ok := cipherMethod[method] if !ok { @@ -228,9 +179,6 @@ func NewCipher(method, password string) (c *Cipher, err error) { if password == "" { return nil, errEmptyPassword } - if method == "" { - method = "table" - } mi, ok := cipherMethod[method] if !ok { return nil, errors.New("Unsupported encryption method: " + method) @@ -240,13 +188,6 @@ func NewCipher(method, password string) (c *Cipher, err error) { c = &Cipher{key: key, info: mi} - if mi.newStream == nil { - if method == "table" { - c.enc, c.dec = newTableCipher(key) - } else if method == "rc4" { - c.enc, c.dec, err = newRC4Cipher(key) - } - } if err != nil { return nil, err } @@ -294,18 +235,9 @@ func (c *Cipher) Copy() *Cipher { // because the current implementation is not highly optimized, or this is // the nature of the algorithm.) - switch c.enc.(type) { - case tableCipher: - return c - case *rc4.Cipher: - enc, _ := c.enc.(*rc4.Cipher) - encCpy := *enc - decCpy := *enc - return &Cipher{enc: &encCpy, dec: &decCpy} - default: - nc := *c - nc.enc = nil - nc.dec = nil - return &nc - } + nc := *c + nc.enc = nil + nc.dec = nil + nc.ota = c.ota + return &nc } diff --git a/shadowsocks/encrypt_test.go b/shadowsocks/encrypt_test.go index 680e73c2..9ff24286 100644 --- a/shadowsocks/encrypt_test.go +++ b/shadowsocks/encrypt_test.go @@ -2,38 +2,11 @@ package shadowsocks import ( "crypto/rand" - "crypto/rc4" "io" "reflect" "testing" ) -func TestEncrypTable1(t *testing.T) { - encTarget := []byte{60, 53, 84, 138, 217, 94, 88, 23, 39, 242, 219, 35, 12, 157, 165, 181, 255, 143, 83, 247, 162, 16, 31, 209, 190, 171, 115, 65, 38, 41, 21, 245, 236, 46, 121, 62, 166, 233, 44, 154, 153, 145, 230, 49, 128, 216, 173, 29, 241, 119, 64, 229, 194, 103, 131, 110, 26, 197, 218, 59, 204, 56, 27, 34, 141, 221, 149, 239, 192, 195, 24, 155, 170, 183, 11, 254, 213, 37, 137, 226, 75, 203, 55, 19, 72, 248, 22, 129, 33, 175, 178, 10, 198, 71, 77, 36, 113, 167, 48, 2, 117, 140, 142, 66, 199, 232, 243, 32, 123, 54, 51, 82, 57, 177, 87, 251, 150, 196, 133, 5, 253, 130, 8, 184, 14, 152, 231, 3, 186, 159, 76, 89, 228, 205, 156, 96, 163, 146, 18, 91, 132, 85, 80, 109, 172, 176, 105, 13, 50, 235, 127, 0, 189, 95, 98, 136, 250, 200, 108, 179, 211, 214, 106, 168, 78, 79, 74, 210, 30, 73, 201, 151, 208, 114, 101, 174, 92, 52, 120, 240, 15, 169, 220, 182, 81, 224, 43, 185, 40, 99, 180, 17, 212, 158, 42, 90, 9, 191, 45, 6, 25, 4, 222, 67, 126, 1, 116, 124, 206, 69, 61, 7, 68, 97, 202, 63, 244, 20, 28, 58, 93, 134, 104, 144, 227, 147, 102, 118, 135, 148, 47, 238, 86, 112, 122, 70, 107, 215, 100, 139, 223, 225, 164, 237, 111, 125, 207, 160, 187, 246, 234, 161, 188, 193, 249, 252} - decTarget := []byte{151, 205, 99, 127, 201, 119, 199, 211, 122, 196, 91, 74, 12, 147, 124, 180, 21, 191, 138, 83, 217, 30, 86, 7, 70, 200, 56, 62, 218, 47, 168, 22, 107, 88, 63, 11, 95, 77, 28, 8, 188, 29, 194, 186, 38, 198, 33, 230, 98, 43, 148, 110, 177, 1, 109, 82, 61, 112, 219, 59, 0, 210, 35, 215, 50, 27, 103, 203, 212, 209, 235, 93, 84, 169, 166, 80, 130, 94, 164, 165, 142, 184, 111, 18, 2, 141, 232, 114, 6, 131, 195, 139, 176, 220, 5, 153, 135, 213, 154, 189, 238, 174, 226, 53, 222, 146, 162, 236, 158, 143, 55, 244, 233, 96, 173, 26, 206, 100, 227, 49, 178, 34, 234, 108, 207, 245, 204, 150, 44, 87, 121, 54, 140, 118, 221, 228, 155, 78, 3, 239, 101, 64, 102, 17, 223, 41, 137, 225, 229, 66, 116, 171, 125, 40, 39, 71, 134, 13, 193, 129, 247, 251, 20, 136, 242, 14, 36, 97, 163, 181, 72, 25, 144, 46, 175, 89, 145, 113, 90, 159, 190, 15, 183, 73, 123, 187, 128, 248, 252, 152, 24, 197, 68, 253, 52, 69, 117, 57, 92, 104, 157, 170, 214, 81, 60, 133, 208, 246, 172, 23, 167, 160, 192, 76, 161, 237, 45, 4, 58, 10, 182, 65, 202, 240, 185, 241, 79, 224, 132, 51, 42, 126, 105, 37, 250, 149, 32, 243, 231, 67, 179, 48, 9, 106, 216, 31, 249, 19, 85, 254, 156, 115, 255, 120, 75, 16} - key := evpBytesToKey("foobar!", 16) - enc, dec := newTableCipher(key) - if !reflect.DeepEqual([]byte(enc), encTarget) { - t.Error("Password foobar encrypt table wrong") - } - if !reflect.DeepEqual([]byte(dec), decTarget) { - t.Error("Password foobar decrypt table wrong") - } -} - -func TestEncryptTable2(t *testing.T) { - encTarget := []byte{124, 30, 170, 247, 27, 127, 224, 59, 13, 22, 196, 76, 72, 154, 32, 209, 4, 2, 131, 62, 101, 51, 230, 9, 166, 11, 99, 80, 208, 112, 36, 248, 81, 102, 130, 88, 218, 38, 168, 15, 241, 228, 167, 117, 158, 41, 10, 180, 194, 50, 204, 243, 246, 251, 29, 198, 219, 210, 195, 21, 54, 91, 203, 221, 70, 57, 183, 17, 147, 49, 133, 65, 77, 55, 202, 122, 162, 169, 188, 200, 190, 125, 63, 244, 96, 31, 107, 106, 74, 143, 116, 148, 78, 46, 1, 137, 150, 110, 181, 56, 95, 139, 58, 3, 231, 66, 165, 142, 242, 43, 192, 157, 89, 175, 109, 220, 128, 0, 178, 42, 255, 20, 214, 185, 83, 160, 253, 7, 23, 92, 111, 153, 26, 226, 33, 176, 144, 18, 216, 212, 28, 151, 71, 206, 222, 182, 8, 174, 205, 201, 152, 240, 155, 108, 223, 104, 239, 98, 164, 211, 184, 34, 193, 14, 114, 187, 40, 254, 12, 67, 93, 217, 6, 94, 16, 19, 82, 86, 245, 24, 197, 134, 132, 138, 229, 121, 5, 235, 238, 85, 47, 103, 113, 179, 69, 250, 45, 135, 156, 25, 61, 75, 44, 146, 189, 84, 207, 172, 119, 53, 123, 186, 120, 171, 68, 227, 145, 136, 100, 90, 48, 79, 159, 149, 39, 213, 236, 126, 52, 60, 225, 199, 105, 73, 233, 252, 118, 215, 35, 115, 64, 37, 97, 129, 161, 177, 87, 237, 141, 173, 191, 163, 140, 234, 232, 249} - decTarget := []byte{117, 94, 17, 103, 16, 186, 172, 127, 146, 23, 46, 25, 168, 8, 163, 39, 174, 67, 137, 175, 121, 59, 9, 128, 179, 199, 132, 4, 140, 54, 1, 85, 14, 134, 161, 238, 30, 241, 37, 224, 166, 45, 119, 109, 202, 196, 93, 190, 220, 69, 49, 21, 228, 209, 60, 73, 99, 65, 102, 7, 229, 200, 19, 82, 240, 71, 105, 169, 214, 194, 64, 142, 12, 233, 88, 201, 11, 72, 92, 221, 27, 32, 176, 124, 205, 189, 177, 246, 35, 112, 219, 61, 129, 170, 173, 100, 84, 242, 157, 26, 218, 20, 33, 191, 155, 232, 87, 86, 153, 114, 97, 130, 29, 192, 164, 239, 90, 43, 236, 208, 212, 185, 75, 210, 0, 81, 227, 5, 116, 243, 34, 18, 182, 70, 181, 197, 217, 95, 183, 101, 252, 248, 107, 89, 136, 216, 203, 68, 91, 223, 96, 141, 150, 131, 13, 152, 198, 111, 44, 222, 125, 244, 76, 251, 158, 106, 24, 42, 38, 77, 2, 213, 207, 249, 147, 113, 135, 245, 118, 193, 47, 98, 145, 66, 160, 123, 211, 165, 78, 204, 80, 250, 110, 162, 48, 58, 10, 180, 55, 231, 79, 149, 74, 62, 50, 148, 143, 206, 28, 15, 57, 159, 139, 225, 122, 237, 138, 171, 36, 56, 115, 63, 144, 154, 6, 230, 133, 215, 41, 184, 22, 104, 254, 234, 253, 187, 226, 247, 188, 156, 151, 40, 108, 51, 83, 178, 52, 3, 31, 255, 195, 53, 235, 126, 167, 120} - key := evpBytesToKey("barfoo!", 16) - enc, dec := newTableCipher(key) - if !reflect.DeepEqual([]byte(enc), encTarget) { - t.Error("Password barfoo! encrypt table wrong") - } - if !reflect.DeepEqual([]byte(dec), decTarget) { - t.Error("Password barfoo! decrypt table wrong") - } -} - const text = "Don't tell me the moon is shining; show me the glint of light on broken glass." func testCiphter(t *testing.T, c *Cipher, msg string) { @@ -49,41 +22,6 @@ func testCiphter(t *testing.T, c *Cipher, msg string) { } } -func TestTableCipher(t *testing.T) { - cipher, err := NewCipher("", "OpenSesame!") - if err != nil { - t.Fatal("Should not get error generating table cipher") - } - if _, ok := cipher.enc.(tableCipher); !ok { - t.Error("Should get table cipher") - } else { - testCiphter(t, cipher, "TableCipher") - } -} - -func TestRC4Cipher(t *testing.T) { - cipher, err := NewCipher("no-such-method", "foobar") - if err == nil { - t.Error("Should return error for unsupported encryption method") - } - - cipher, err = NewCipher("rc4", "") - if err == nil { - t.Error("Should get error for empty key creating rc4 cipher") - } - cipher, err = NewCipher("rc4", "Alibaba") - ciphercopy := cipher.Copy() - if err != nil { - t.Error("Should not error creating rc4 cipher with key Alibaba") - } - if _, ok := cipher.enc.(*rc4.Cipher); !ok { - t.Error("Should get rc4 cipher") - } else { - testCiphter(t, cipher, "RC4Cipher") - testCiphter(t, ciphercopy, "RC4Cipher copy") - } -} - func TestEvpBytesToKey(t *testing.T) { // key, iv := evpBytesToKey("foobar", 32, 16) key := evpBytesToKey("foobar", 32) @@ -161,13 +99,6 @@ func init() { io.ReadFull(rand.Reader, cipherIv) } -func BenchmarkRC4Init(b *testing.B) { - key := cipherKey[:16] - for i := 0; i < b.N; i++ { - rc4.NewCipher(key) - } -} - func benchmarkCipherInit(b *testing.B, method string) { ci := cipherMethod[method] key := cipherKey[:ci.keyLen] From 89460d2e476940f0b702e4bdff4991152d9cdbbe Mon Sep 17 00:00:00 2001 From: ayanamist Date: Sat, 5 Dec 2015 17:12:14 +0800 Subject: [PATCH 36/90] support one time auth in client & server append "-ota" suffix in method name to enable one time auth --- cmd/shadowsocks-local/local.go | 6 +- cmd/shadowsocks-server/server.go | 119 +++++++++++++++++-------------- shadowsocks/config.go | 6 ++ shadowsocks/conn.go | 56 ++++++++++++++- shadowsocks/encrypt.go | 30 ++++++-- shadowsocks/leakybuf.go | 2 +- shadowsocks/pipe.go | 60 +++++++++++++++- shadowsocks/util.go | 34 +++++++++ 8 files changed, 249 insertions(+), 64 deletions(-) diff --git a/cmd/shadowsocks-local/local.go b/cmd/shadowsocks-local/local.go index cfcdbf81..72514b60 100644 --- a/cmd/shadowsocks-local/local.go +++ b/cmd/shadowsocks-local/local.go @@ -170,8 +170,12 @@ func parseServerConfig(config *ss.Config) { } if len(config.ServerPassword) == 0 { + method := config.Method + if config.Auth { + method += "-ota" + } // only one encryption table - cipher, err := ss.NewCipher(config.Method, config.Password) + cipher, err := ss.NewCipher(method, config.Password) if err != nil { log.Fatal("Failed generating ciphers:", err) } diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index 982fcaf3..77155f83 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "encoding/binary" "errors" "flag" @@ -17,61 +18,61 @@ import ( "syscall" ) -var debug ss.DebugLog +const ( + idType = 0 // address type index + idIP0 = 1 // ip addres start index + idDmLen = 1 // domain address length index + idDm0 = 2 // domain address start index + + typeIPv4 = 1 // type is ipv4 address + typeDm = 3 // type is domain address + typeIPv6 = 4 // type is ipv6 address -func getRequest(conn *ss.Conn) (host string, extra []byte, err error) { - const ( - idType = 0 // address type index - idIP0 = 1 // ip addres start index - idDmLen = 1 // domain address length index - idDm0 = 2 // domain address start index + lenIPv4 = net.IPv4len + 2 // ipv4 + 2port + lenIPv6 = net.IPv6len + 2 // ipv6 + 2port + lenDmBase = 2 // 1addrLen + 2port, plus addrLen + lenHmacSha1 = 10 +) - typeIPv4 = 1 // type is ipv4 address - typeDm = 3 // type is domain address - typeIPv6 = 4 // type is ipv6 address +var debug ss.DebugLog - lenIPv4 = 1 + net.IPv4len + 2 // 1addrType + ipv4 + 2port - lenIPv6 = 1 + net.IPv6len + 2 // 1addrType + ipv6 + 2port - lenDmBase = 1 + 1 + 2 // 1addrType + 1addrLen + 2port, plus addrLen - ) +func getRequest(conn *ss.Conn, auth bool) (host string, ota bool, err error) { + ss.SetReadTimeout(conn) // buf size should at least have the same size with the largest possible // request size (when addrType is 3, domain name has at most 256 bytes) - // 1(addrType) + 1(lenByte) + 256(max length address) + 2(port) - buf := make([]byte, 260) - var n int + // 1(addrType) + 1(lenByte) + 256(max length address) + 2(port) + 10(hmac-sha1) + buf := make([]byte, 270) // read till we get possible domain length field - ss.SetReadTimeout(conn) - if n, err = io.ReadAtLeast(conn, buf, idDmLen+1); err != nil { + if _, err = io.ReadFull(conn, buf[:idType+1]); err != nil { return } - reqLen := -1 - switch buf[idType] { + var reqStart, reqEnd int + addrType := buf[idType] + switch addrType & ss.AddrMask { case typeIPv4: - reqLen = lenIPv4 + reqStart, reqEnd = idIP0, idIP0+lenIPv4 case typeIPv6: - reqLen = lenIPv6 + reqStart, reqEnd = idIP0, idIP0+lenIPv6 case typeDm: - reqLen = int(buf[idDmLen]) + lenDmBase + if _, err = io.ReadFull(conn, buf[idType+1:idDmLen+1]); err != nil { + return + } + reqStart, reqEnd = idDm0, int(idDm0+buf[idDmLen]+lenDmBase) default: - err = fmt.Errorf("addr type %d not supported", buf[idType]) + err = fmt.Errorf("addr type %d not supported", addrType&ss.AddrMask) return } - if n < reqLen { // rare case - if _, err = io.ReadFull(conn, buf[n:reqLen]); err != nil { - return - } - } else if n > reqLen { - // it's possible to read more than just the request head - extra = buf[reqLen:n] + if _, err = io.ReadFull(conn, buf[reqStart:reqEnd]); err != nil { + return } // Return string for typeIP is not most efficient, but browsers (Chrome, // Safari, Firefox) all seems using typeDm exclusively. So this is not a // big problem. - switch buf[idType] { + switch addrType & ss.AddrMask { case typeIPv4: host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() case typeIPv6: @@ -80,8 +81,22 @@ func getRequest(conn *ss.Conn) (host string, extra []byte, err error) { host = string(buf[idDm0 : idDm0+buf[idDmLen]]) } // parse port - port := binary.BigEndian.Uint16(buf[reqLen-2 : reqLen]) + port := binary.BigEndian.Uint16(buf[reqEnd-2 : reqEnd]) host = net.JoinHostPort(host, strconv.Itoa(int(port))) + // if specified one time auth enabled, we should verify this + if auth || addrType&ss.OneTimeAuthMask > 0 { + ota = true + if _, err = io.ReadFull(conn, buf[reqEnd:reqEnd+lenHmacSha1]); err != nil { + return + } + iv := conn.GetIv() + key := conn.GetKey() + actualHmacSha1Buf := ss.HmacSha1(append(iv, key...), buf[:reqEnd]) + if !bytes.Equal(buf[reqEnd:reqEnd+lenHmacSha1], actualHmacSha1Buf) { + err = fmt.Errorf("verify one time auth failed, iv=%v key=%v data=%v", iv, key, buf[:reqEnd]) + return + } + } return } @@ -90,7 +105,11 @@ const logCntDelta = 100 var connCnt int var nextLogConnCnt int = logCntDelta -func handleConnection(conn *ss.Conn) { +type isClosed struct { + isClosed bool +} + +func handleConnection(conn *ss.Conn, auth bool) { var host string connCnt++ // this maybe not accurate, but should be enough @@ -118,7 +137,7 @@ func handleConnection(conn *ss.Conn) { } }() - host, extra, err := getRequest(conn) + host, ota, err := getRequest(conn, auth) if err != nil { log.Println("error getting request", conn.RemoteAddr(), conn.LocalAddr(), err) return @@ -140,18 +159,14 @@ func handleConnection(conn *ss.Conn) { remote.Close() } }() - // write extra bytes read from - if extra != nil { - // debug.Println("getRequest read extra data, writing to remote, len", len(extra)) - if _, err = remote.Write(extra); err != nil { - debug.Println("write request extra error:", err) - return - } - } if debug { - debug.Printf("piping %s<->%s", conn.RemoteAddr(), host) + debug.Printf("piping %s<->%s ota=%v connOta=%v", conn.RemoteAddr(), host, ota, conn.IsOta()) + } + if ota { + go ss.PipeThenCloseOta(conn, remote) + } else { + go ss.PipeThenClose(conn, remote) } - go ss.PipeThenClose(conn, remote) ss.PipeThenClose(remote, conn) closed = true return @@ -195,7 +210,7 @@ func (pm *PasswdManager) del(port string) { // port. A different approach would be directly change the password used by // that port, but that requires **sharing** password between the port listener // and password manager. -func (pm *PasswdManager) updatePortPasswd(port, password string) { +func (pm *PasswdManager) updatePortPasswd(port, password string, auth bool) { pl, ok := pm.get(port) if !ok { log.Printf("new port %s added\n", port) @@ -208,7 +223,7 @@ func (pm *PasswdManager) updatePortPasswd(port, password string) { } // run will add the new port listener to passwdManager. // So there maybe concurrent access to passwdManager and we need lock to protect it. - go run(port, password) + go run(port, password, auth) } var passwdManager = PasswdManager{portListener: map[string]*PortListener{}} @@ -227,7 +242,7 @@ func updatePasswd() { return } for port, passwd := range config.PortPassword { - passwdManager.updatePortPasswd(port, passwd) + passwdManager.updatePortPasswd(port, passwd, config.Auth) if oldconfig.PortPassword != nil { delete(oldconfig.PortPassword, port) } @@ -254,7 +269,7 @@ func waitSignal() { } } -func run(port, password string) { +func run(port, password string, auth bool) { ln, err := net.Listen("tcp", ":"+port) if err != nil { log.Printf("error listening port %v: %v\n", port, err) @@ -280,7 +295,7 @@ func run(port, password string) { continue } } - go handleConnection(ss.NewConn(conn, cipher.Copy())) + go handleConnection(ss.NewConn(conn, cipher.Copy()), auth) } } @@ -357,7 +372,7 @@ func main() { runtime.GOMAXPROCS(core) } for port, password := range config.PortPassword { - go run(port, password) + go run(port, password, config.Auth) } waitSignal() diff --git a/shadowsocks/config.go b/shadowsocks/config.go index ec5d2f22..7794d9fa 100644 --- a/shadowsocks/config.go +++ b/shadowsocks/config.go @@ -15,6 +15,7 @@ import ( "os" "reflect" "time" + "strings" ) type Config struct { @@ -23,6 +24,7 @@ type Config struct { LocalPort int `json:"local_port"` Password string `json:"password"` Method string `json:"method"` // encryption method + Auth bool `json:"auth"` // one time auth // following options are only used by server PortPassword map[string]string `json:"port_password"` @@ -85,6 +87,10 @@ func ParseConfig(path string) (config *Config, err error) { return nil, err } readTimeout = time.Duration(config.Timeout) * time.Second + if strings.HasSuffix(strings.ToLower(config.Method), "-ota") { + config.Method = config.Method[:len(config.Method) - 4] + config.Auth = true + } return } diff --git a/shadowsocks/conn.go b/shadowsocks/conn.go index eacb41fb..2069f9f8 100644 --- a/shadowsocks/conn.go +++ b/shadowsocks/conn.go @@ -8,11 +8,17 @@ import ( "strconv" ) +const ( + OneTimeAuthMask byte = 0x10 + AddrMask byte = 0xf +) + type Conn struct { net.Conn *Cipher - readBuf []byte - writeBuf []byte + readBuf []byte + writeBuf []byte + chunkId uint32 } func NewConn(c net.Conn, cipher *Cipher) *Conn { @@ -58,7 +64,18 @@ func DialWithRawAddr(rawaddr []byte, server string, cipher *Cipher) (c *Conn, er return } c = NewConn(conn, cipher) - if _, err = c.Write(rawaddr); err != nil { + if cipher.ota { + if c.enc == nil { + if _, err = c.initEncrypt(); err != nil { + return + } + } + // since we have initEncrypt, we must send iv manually + conn.Write(cipher.iv) + rawaddr[0] |= OneTimeAuthMask + rawaddr = otaConnectAuth(cipher.iv, cipher.key, rawaddr) + } + if _, err = c.write(rawaddr); err != nil { c.Close() return nil, err } @@ -74,6 +91,28 @@ func Dial(addr, server string, cipher *Cipher) (c *Conn, err error) { return DialWithRawAddr(ra, server, cipher) } +func (c *Conn) GetIv() (iv []byte) { + iv = make([]byte, len(c.iv)) + copy(iv, c.iv) + return +} + +func (c *Conn) GetKey() (key []byte) { + key = make([]byte, len(c.key)) + copy(key, c.key) + return +} + +func (c *Conn) IsOta() bool { + return c.ota +} + +func (c *Conn) GetAndIncrChunkId() (chunkId uint32) { + chunkId = c.chunkId + c.chunkId += 1 + return +} + func (c *Conn) Read(b []byte) (n int, err error) { if c.dec == nil { iv := make([]byte, c.info.ivLen) @@ -83,6 +122,9 @@ func (c *Conn) Read(b []byte) (n int, err error) { if err = c.initDecrypt(iv); err != nil { return } + if len(c.iv) == 0 { + c.iv = iv + } } cipherData := c.readBuf @@ -100,6 +142,14 @@ func (c *Conn) Read(b []byte) (n int, err error) { } func (c *Conn) Write(b []byte) (n int, err error) { + if c.ota { + chunkId := c.GetAndIncrChunkId() + b = otaReqChunkAuth(c.iv, chunkId, b) + } + return c.write(b) +} + +func (c *Conn) write(b []byte) (n int, err error) { var iv []byte if c.enc == nil { iv, err = c.initEncrypt() diff --git a/shadowsocks/encrypt.go b/shadowsocks/encrypt.go index 0e06bd9f..ea23e759 100644 --- a/shadowsocks/encrypt.go +++ b/shadowsocks/encrypt.go @@ -14,6 +14,7 @@ import ( "golang.org/x/crypto/cast5" "golang.org/x/crypto/salsa20/salsa" "io" + "strings" ) var errEmptyPassword = errors.New("empty key") @@ -170,6 +171,8 @@ type Cipher struct { dec cipher.Stream key []byte info *cipherInfo + ota bool // one-time auth + iv []byte } // NewCipher creates a cipher that can be used in Dial() etc. @@ -179,6 +182,13 @@ func NewCipher(method, password string) (c *Cipher, err error) { if password == "" { return nil, errEmptyPassword } + var ota bool + if strings.HasSuffix(strings.ToLower(method), "-ota") { + method = method[:len(method) - 4] // len("-ota") = 4 + ota = true + } else { + ota = false + } mi, ok := cipherMethod[method] if !ok { return nil, errors.New("Unsupported encryption method: " + method) @@ -191,18 +201,26 @@ func NewCipher(method, password string) (c *Cipher, err error) { if err != nil { return nil, err } + c.ota = ota return c, nil } // Initializes the block cipher with CFB mode, returns IV. func (c *Cipher) initEncrypt() (iv []byte, err error) { - iv = make([]byte, c.info.ivLen) - if _, err := io.ReadFull(rand.Reader, iv); err != nil { - return nil, err + if c.iv == nil { + iv = make([]byte, c.info.ivLen) + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + return nil, err + } + c.iv = iv + } else { + iv = c.iv } - c.enc, err = c.info.newStream(c.key, iv, Encrypt) - if err != nil { - return nil, err + if c.enc == nil { + c.enc, err = c.info.newStream(c.key, iv, Encrypt) + if err != nil { + return nil, err + } } return } diff --git a/shadowsocks/leakybuf.go b/shadowsocks/leakybuf.go index 3b55832d..b6922eb9 100644 --- a/shadowsocks/leakybuf.go +++ b/shadowsocks/leakybuf.go @@ -6,7 +6,7 @@ type LeakyBuf struct { freeList chan []byte } -const leakyBufSize = 4096 +const leakyBufSize = 4108 // data.len(2) + hmacsha1(10) + data(4096) const maxNBuf = 2048 var leakyBuf = NewLeakyBuf(maxNBuf, leakyBufSize) diff --git a/shadowsocks/pipe.go b/shadowsocks/pipe.go index 515c99f8..e0fa9756 100644 --- a/shadowsocks/pipe.go +++ b/shadowsocks/pipe.go @@ -1,7 +1,9 @@ package shadowsocks import ( - // "io" + "bytes" + "encoding/binary" + "io" "net" "time" ) @@ -42,3 +44,59 @@ func PipeThenClose(src, dst net.Conn) { } } } + +// PipeThenClose copies data from src to dst, closes dst when done, with ota verification. +func PipeThenCloseOta(src *Conn, dst net.Conn) { + const ( + dataLenLen = 2 + hmacSha1Len = 10 + idxData0 = dataLenLen + hmacSha1Len + ) + + defer func() { + dst.Close() + }() + // sometimes it have to fill large block + buf := leakyBuf.Get() + defer leakyBuf.Put(buf) + i := 0 + for { + i += 1 + SetReadTimeout(src) + if n, err := io.ReadFull(src, buf[:dataLenLen+hmacSha1Len]); err != nil { + if err == io.EOF { + break + } + Debug.Printf("conn=%p #%v read header error n=%v: %v", src, i, n, err) + break + } + dataLen := binary.BigEndian.Uint16(buf[:dataLenLen]) + expectedHmacSha1 := buf[dataLenLen:idxData0] + + var dataBuf []byte + if len(buf) < int(idxData0+dataLen) { + dataBuf = make([]byte, dataLen) + } else { + dataBuf = buf[idxData0:idxData0+dataLen] + } + if n, err := io.ReadFull(src, dataBuf); err != nil { + if err == io.EOF { + break + } + Debug.Printf("conn=%p #%v read data error n=%v: %v", src, i, n, err) + break + } + chunkIdBytes := make([]byte, 4) + chunkId := src.GetAndIncrChunkId() + binary.BigEndian.PutUint32(chunkIdBytes, chunkId) + actualHmacSha1 := HmacSha1(append(src.GetIv(), chunkIdBytes...), dataBuf) + if !bytes.Equal(expectedHmacSha1, actualHmacSha1) { + 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) + break + } + if n, err := dst.Write(dataBuf); err != nil { + Debug.Printf("conn=%p #%v write data error n=%v: %v", dst, i, n, err) + break + } + } +} diff --git a/shadowsocks/util.go b/shadowsocks/util.go index 2c5198d2..378f24da 100644 --- a/shadowsocks/util.go +++ b/shadowsocks/util.go @@ -4,6 +4,9 @@ import ( "errors" "fmt" "os" + "crypto/hmac" + "crypto/sha1" + "encoding/binary" ) func PrintVersion() { @@ -24,3 +27,34 @@ func IsFileExists(path string) (bool, error) { } return false, err } + +func HmacSha1(key []byte, data []byte) []byte { + hmacSha1 := hmac.New(sha1.New, key) + hmacSha1.Write(data) + return hmacSha1.Sum(nil)[:10] +} + +func otaConnectAuth(iv, key, data []byte) []byte { + return append(data, HmacSha1(append(iv, key...), data)...) +} + +func otaReqChunkAuth(iv []byte, chunkId uint32, data []byte) []byte { + nb := make([]byte, 2) + binary.BigEndian.PutUint16(nb, uint16(len(data))) + chunkIdBytes := make([]byte, 4) + binary.BigEndian.PutUint32(chunkIdBytes, chunkId) + header := append(nb, HmacSha1(append(iv, chunkIdBytes...), data)...) + return append(header, data...) +} + +type ClosedFlag struct { + flag bool +} + +func (flag *ClosedFlag) SetClosed() { + flag.flag = true +} + +func (flag *ClosedFlag) IsClosed() bool { + return flag.flag +} \ No newline at end of file From cb20fe4563719cb0c670f2c1ca5a9af8f4dbc9a1 Mon Sep 17 00:00:00 2001 From: ayanamist Date: Wed, 16 Dec 2015 18:17:43 +0800 Subject: [PATCH 37/90] remove some useless code introduced since ota support --- cmd/shadowsocks-server/server.go | 4 ---- shadowsocks/encrypt.go | 7 +------ shadowsocks/pipe.go | 6 ++---- 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index 77155f83..e8b27c80 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -105,10 +105,6 @@ const logCntDelta = 100 var connCnt int var nextLogConnCnt int = logCntDelta -type isClosed struct { - isClosed bool -} - func handleConnection(conn *ss.Conn, auth bool) { var host string diff --git a/shadowsocks/encrypt.go b/shadowsocks/encrypt.go index ea23e759..28ea0ed3 100644 --- a/shadowsocks/encrypt.go +++ b/shadowsocks/encrypt.go @@ -216,12 +216,7 @@ func (c *Cipher) initEncrypt() (iv []byte, err error) { } else { iv = c.iv } - if c.enc == nil { - c.enc, err = c.info.newStream(c.key, iv, Encrypt) - if err != nil { - return nil, err - } - } + c.enc, err = c.info.newStream(c.key, iv, Encrypt) return } diff --git a/shadowsocks/pipe.go b/shadowsocks/pipe.go index e0fa9756..ca17c002 100644 --- a/shadowsocks/pipe.go +++ b/shadowsocks/pipe.go @@ -59,9 +59,7 @@ func PipeThenCloseOta(src *Conn, dst net.Conn) { // sometimes it have to fill large block buf := leakyBuf.Get() defer leakyBuf.Put(buf) - i := 0 - for { - i += 1 + for i := 1; ; i += 1 { SetReadTimeout(src) if n, err := io.ReadFull(src, buf[:dataLenLen+hmacSha1Len]); err != nil { if err == io.EOF { @@ -77,7 +75,7 @@ func PipeThenCloseOta(src *Conn, dst net.Conn) { if len(buf) < int(idxData0+dataLen) { dataBuf = make([]byte, dataLen) } else { - dataBuf = buf[idxData0:idxData0+dataLen] + dataBuf = buf[idxData0 : idxData0+dataLen] } if n, err := io.ReadFull(src, dataBuf); err != nil { if err == io.EOF { From b651734b28ccf49e6fd218105fc10b08f847d222 Mon Sep 17 00:00:00 2001 From: Shuai Lin Date: Sun, 3 Apr 2016 22:58:56 +0800 Subject: [PATCH 38/90] Add flags for ota. --- cmd/shadowsocks-local/local.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/shadowsocks-local/local.go b/cmd/shadowsocks-local/local.go index 72514b60..05316999 100644 --- a/cmd/shadowsocks-local/local.go +++ b/cmd/shadowsocks-local/local.go @@ -17,6 +17,7 @@ import ( ) var debug ss.DebugLog +var oneTimeAuth bool var ( errAddrType = errors.New("socks addr type not supported") @@ -367,6 +368,7 @@ func main() { flag.IntVar(&cmdConfig.LocalPort, "l", 0, "local socks5 proxy port") flag.StringVar(&cmdConfig.Method, "m", "", "encryption method, default: aes-256-cfb") flag.BoolVar((*bool)(&debug), "d", false, "print debug message") + flag.BoolVar(&cmdConfig.Auth, "A", false, "one time auth") flag.Parse() From 94e33e7d22d76f900bef25ed0e06101404459f43 Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Wed, 4 May 2016 18:54:39 +0800 Subject: [PATCH 39/90] Allow specify -m -ota on command line. --- cmd/shadowsocks-local/local.go | 10 ++++++++-- cmd/shadowsocks-server/server.go | 9 ++++++++- shadowsocks/config.go | 4 ++-- shadowsocks/encrypt.go | 7 ++++--- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/cmd/shadowsocks-local/local.go b/cmd/shadowsocks-local/local.go index 05316999..17b7762b 100644 --- a/cmd/shadowsocks-local/local.go +++ b/cmd/shadowsocks-local/local.go @@ -5,7 +5,6 @@ import ( "errors" "flag" "fmt" - ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" "io" "log" "math/rand" @@ -13,11 +12,13 @@ import ( "os" "path" "strconv" + "strings" "time" + + ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" ) var debug ss.DebugLog -var oneTimeAuth bool var ( errAddrType = errors.New("socks addr type not supported") @@ -380,6 +381,11 @@ func main() { cmdConfig.Server = cmdServer ss.SetDebug(debug) + if strings.HasSuffix(cmdConfig.Method, "-ota") { + cmdConfig.Method = cmdConfig.Method[:len(cmdConfig.Method)-4] + cmdConfig.Auth = true + } + exists, err := ss.IsFileExists(configFile) // If no config file in current directory, try search it in the binary directory // Note there's no portable way to detect the binary directory. diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index e8b27c80..19dfc0b2 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -6,7 +6,6 @@ import ( "errors" "flag" "fmt" - ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" "io" "log" "net" @@ -14,8 +13,11 @@ import ( "os/signal" "runtime" "strconv" + "strings" "sync" "syscall" + + ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" ) const ( @@ -343,6 +345,11 @@ func main() { ss.SetDebug(debug) + if strings.HasSuffix(cmdConfig.Method, "-ota") { + cmdConfig.Method = cmdConfig.Method[:len(cmdConfig.Method)-4] + cmdConfig.Auth = true + } + var err error config, err = ss.ParseConfig(configFile) if err != nil { diff --git a/shadowsocks/config.go b/shadowsocks/config.go index 7794d9fa..88f7c16f 100644 --- a/shadowsocks/config.go +++ b/shadowsocks/config.go @@ -14,8 +14,8 @@ import ( // "log" "os" "reflect" - "time" "strings" + "time" ) type Config struct { @@ -88,7 +88,7 @@ func ParseConfig(path string) (config *Config, err error) { } readTimeout = time.Duration(config.Timeout) * time.Second if strings.HasSuffix(strings.ToLower(config.Method), "-ota") { - config.Method = config.Method[:len(config.Method) - 4] + config.Method = config.Method[:len(config.Method)-4] config.Auth = true } return diff --git a/shadowsocks/encrypt.go b/shadowsocks/encrypt.go index 28ea0ed3..45b2eb95 100644 --- a/shadowsocks/encrypt.go +++ b/shadowsocks/encrypt.go @@ -9,12 +9,13 @@ import ( "crypto/rc4" "encoding/binary" "errors" + "io" + "strings" + "github.com/codahale/chacha20" "golang.org/x/crypto/blowfish" "golang.org/x/crypto/cast5" "golang.org/x/crypto/salsa20/salsa" - "io" - "strings" ) var errEmptyPassword = errors.New("empty key") @@ -184,7 +185,7 @@ func NewCipher(method, password string) (c *Cipher, err error) { } var ota bool if strings.HasSuffix(strings.ToLower(method), "-ota") { - method = method[:len(method) - 4] // len("-ota") = 4 + method = method[:len(method)-4] // len("-ota") = 4 ota = true } else { ota = false From 0206d55920e7ef3e376efc10b41c8e609c253d91 Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Wed, 4 May 2016 20:08:52 +0800 Subject: [PATCH 40/90] Bump version to 1.1.5 --- .gitignore | 1 + README.md | 11 +++++++++-- config.json | 2 +- script/build.sh | 4 ++-- script/test.sh | 6 +++--- shadowsocks/util.go | 2 +- 6 files changed, 17 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 5966b0f5..b4278f4b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.deb script/http +bin diff --git a/README.md b/README.md index f3736a38..37f3811a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # shadowsocks-go -Current version: 1.1.4 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=develop)](https://travis-ci.org/shadowsocks/shadowsocks-go) +Current version: 1.1.5 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=develop)](https://travis-ci.org/shadowsocks/shadowsocks-go) 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). @@ -12,7 +12,7 @@ The protocol is compatible with the origin shadowsocks (if both have been upgrad # Install -Compiled client binaries can be download [here](http://dl.chenyufei.info/shadowsocks/). (All compiled with cgo disabled, except the mac version.) +Download precompiled binarys from the [release page](https://github.com/shadowsocks/shadowsocks-go/releases). (All compiled with cgo disabled, except the mac version.) You can also install from source (assume you have go installed): @@ -55,6 +55,13 @@ AES is recommended for shadowsocks-go. [Intel AES Instruction Set](http://en.wik **rc4 and table encryption methods are deprecated because they are not secure.** +### One Time Auth + +Append `-ota` to the encryption method to enable [One Time Auth (OTA)](https://shadowsocks.org/en/spec/one-time-auth.html). + +- For server: this will **force client use OTA**, non-OTA connection will be dropped. Otherwise, both OTA and non-OTA clients can connect +- For client: the `-A` command line option can also enable OTA + ## Command line options Command line options can override settings from configuration files. Use `-h` option to see all available options. diff --git a/config.json b/config.json index 8475f08d..7b783612 100644 --- a/config.json +++ b/config.json @@ -3,6 +3,6 @@ "server_port":8388, "local_port":1080, "password":"barfoo!", - "method": "aes-128-cfb", + "method": "aes-128-cfb-ota", "timeout":600 } diff --git a/script/build.sh b/script/build.sh index 207b4cfa..14a4cd27 100755 --- a/script/build.sh +++ b/script/build.sh @@ -49,13 +49,13 @@ build linux 386 linux32 local build windows amd64 win64 local build windows 386 win32 local +#build darwin amd64 mac64 server build linux amd64 linux64 server build linux 386 linux32 server -build darwin amd64 mac64 server build windows amd64 win64 server build windows 386 win32 server #script/createdeb.sh amd64 -#script/createdeb.sh i386 +#script/createdeb.sh 386 #mv shadowsocks-go_$version-1-*.deb bin/ #rm -rf shadowsocks-go_$version-1* diff --git a/script/test.sh b/script/test.sh index 99608030..1f99481b 100755 --- a/script/test.sh +++ b/script/test.sh @@ -74,7 +74,7 @@ test_shadowsocks() { server_pid=$! wait_server $SERVER_PORT - $LOCAL $OPTION -s 127.0.0.1 -l $LOCAL_PORT -m "$method" & + $LOCAL $OPTION -s 127.0.0.1 -l $LOCAL_PORT -m "$method" -A & local_pid=$! wait_server $LOCAL_PORT @@ -103,8 +103,8 @@ test_server_local_pair() { url=http://127.0.0.1:$HTTP_PORT/README.md test_shadowsocks $url rc4-md5 test_shadowsocks $url aes-128-cfb - test_shadowsocks $url aes-192-cfb - test_shadowsocks $url aes-256-cfb + #test_shadowsocks $url aes-192-cfb + #test_shadowsocks $url aes-256-cfb test_shadowsocks $url bf-cfb test_shadowsocks $url des-cfb test_shadowsocks $url cast5-cfb diff --git a/shadowsocks/util.go b/shadowsocks/util.go index 378f24da..21c01c6a 100644 --- a/shadowsocks/util.go +++ b/shadowsocks/util.go @@ -10,7 +10,7 @@ import ( ) func PrintVersion() { - const version = "1.1.4" + const version = "1.1.5" fmt.Println("shadowsocks-go version", version) } From 0fd1c39d569b4a378df4293f41d0810ecff07b4c Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Wed, 4 May 2016 20:31:19 +0800 Subject: [PATCH 41/90] Use method-auth to enable OTA. --- README.md | 2 +- cmd/shadowsocks-local/local.go | 4 ++-- cmd/shadowsocks-server/server.go | 2 +- config.json | 2 +- shadowsocks/config.go | 4 ++-- shadowsocks/encrypt.go | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 37f3811a..5b3a3182 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ AES is recommended for shadowsocks-go. [Intel AES Instruction Set](http://en.wik ### One Time Auth -Append `-ota` to the encryption method to enable [One Time Auth (OTA)](https://shadowsocks.org/en/spec/one-time-auth.html). +Append `-auth` to the encryption method to enable [One Time Auth (OTA)](https://shadowsocks.org/en/spec/one-time-auth.html). - For server: this will **force client use OTA**, non-OTA connection will be dropped. Otherwise, both OTA and non-OTA clients can connect - For client: the `-A` command line option can also enable OTA diff --git a/cmd/shadowsocks-local/local.go b/cmd/shadowsocks-local/local.go index 17b7762b..1de4262f 100644 --- a/cmd/shadowsocks-local/local.go +++ b/cmd/shadowsocks-local/local.go @@ -174,7 +174,7 @@ func parseServerConfig(config *ss.Config) { if len(config.ServerPassword) == 0 { method := config.Method if config.Auth { - method += "-ota" + method += "-auth" } // only one encryption table cipher, err := ss.NewCipher(method, config.Password) @@ -381,7 +381,7 @@ func main() { cmdConfig.Server = cmdServer ss.SetDebug(debug) - if strings.HasSuffix(cmdConfig.Method, "-ota") { + if strings.HasSuffix(cmdConfig.Method, "-auth") { cmdConfig.Method = cmdConfig.Method[:len(cmdConfig.Method)-4] cmdConfig.Auth = true } diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index 19dfc0b2..d2969e90 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -345,7 +345,7 @@ func main() { ss.SetDebug(debug) - if strings.HasSuffix(cmdConfig.Method, "-ota") { + if strings.HasSuffix(cmdConfig.Method, "-auth") { cmdConfig.Method = cmdConfig.Method[:len(cmdConfig.Method)-4] cmdConfig.Auth = true } diff --git a/config.json b/config.json index 7b783612..35d08271 100644 --- a/config.json +++ b/config.json @@ -3,6 +3,6 @@ "server_port":8388, "local_port":1080, "password":"barfoo!", - "method": "aes-128-cfb-ota", + "method": "aes-128-cfb-auth", "timeout":600 } diff --git a/shadowsocks/config.go b/shadowsocks/config.go index 88f7c16f..4db50ac2 100644 --- a/shadowsocks/config.go +++ b/shadowsocks/config.go @@ -87,8 +87,8 @@ func ParseConfig(path string) (config *Config, err error) { return nil, err } readTimeout = time.Duration(config.Timeout) * time.Second - if strings.HasSuffix(strings.ToLower(config.Method), "-ota") { - config.Method = config.Method[:len(config.Method)-4] + if strings.HasSuffix(strings.ToLower(config.Method), "-auth") { + config.Method = config.Method[:len(config.Method)-5] config.Auth = true } return diff --git a/shadowsocks/encrypt.go b/shadowsocks/encrypt.go index 45b2eb95..146947d3 100644 --- a/shadowsocks/encrypt.go +++ b/shadowsocks/encrypt.go @@ -184,8 +184,8 @@ func NewCipher(method, password string) (c *Cipher, err error) { return nil, errEmptyPassword } var ota bool - if strings.HasSuffix(strings.ToLower(method), "-ota") { - method = method[:len(method)-4] // len("-ota") = 4 + if strings.HasSuffix(strings.ToLower(method), "-auth") { + method = method[:len(method)-5] // len("-auth") = 5 ota = true } else { ota = false From d5dda7fe317511fe87a15463e2a78ea88370fe53 Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Thu, 5 May 2016 21:37:14 +0800 Subject: [PATCH 42/90] Fix #127: command line specifying -auth not working. --- cmd/shadowsocks-local/local.go | 2 +- cmd/shadowsocks-server/server.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/shadowsocks-local/local.go b/cmd/shadowsocks-local/local.go index 1de4262f..2c76da3a 100644 --- a/cmd/shadowsocks-local/local.go +++ b/cmd/shadowsocks-local/local.go @@ -382,7 +382,7 @@ func main() { ss.SetDebug(debug) if strings.HasSuffix(cmdConfig.Method, "-auth") { - cmdConfig.Method = cmdConfig.Method[:len(cmdConfig.Method)-4] + cmdConfig.Method = cmdConfig.Method[:len(cmdConfig.Method)-5] cmdConfig.Auth = true } diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index d2969e90..3f74fe68 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -346,7 +346,7 @@ func main() { ss.SetDebug(debug) if strings.HasSuffix(cmdConfig.Method, "-auth") { - cmdConfig.Method = cmdConfig.Method[:len(cmdConfig.Method)-4] + cmdConfig.Method = cmdConfig.Method[:len(cmdConfig.Method)-5] cmdConfig.Auth = true } From 932ea6a5aebb6dd766f7080e01254a2b509e7684 Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Wed, 15 Jun 2016 22:07:55 +0800 Subject: [PATCH 43/90] Make Conn.Write obey io.Write interface contract. --- shadowsocks/conn.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/shadowsocks/conn.go b/shadowsocks/conn.go index 2069f9f8..1a79f1d1 100644 --- a/shadowsocks/conn.go +++ b/shadowsocks/conn.go @@ -16,9 +16,9 @@ const ( type Conn struct { net.Conn *Cipher - readBuf []byte - writeBuf []byte - chunkId uint32 + readBuf []byte + writeBuf []byte + chunkId uint32 } func NewConn(c net.Conn, cipher *Cipher) *Conn { @@ -142,11 +142,19 @@ func (c *Conn) Read(b []byte) (n int, err error) { } func (c *Conn) Write(b []byte) (n int, err error) { + nn := len(b) if c.ota { chunkId := c.GetAndIncrChunkId() b = otaReqChunkAuth(c.iv, chunkId, b) } - return c.write(b) + headerLen := len(b) - nn + + n, err = c.write(b) + // Make sure <= 0 <= len(b), where b is the slice passed in. + if n >= headerLen { + n -= headerLen + } + return } func (c *Conn) write(b []byte) (n int, err error) { From 40c0e7f8d85b20fca781dd5ca7d8bfa47daebf01 Mon Sep 17 00:00:00 2001 From: andwxh Date: Thu, 5 Jan 2017 13:36:16 +0800 Subject: [PATCH 44/90] fix slice bounds out of range panic when domain length larger than 251 --- cmd/shadowsocks-server/server.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index e8b27c80..d66a897e 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -41,8 +41,8 @@ func getRequest(conn *ss.Conn, auth bool) (host string, ota bool, err error) { // buf size should at least have the same size with the largest possible // request size (when addrType is 3, domain name has at most 256 bytes) - // 1(addrType) + 1(lenByte) + 256(max length address) + 2(port) + 10(hmac-sha1) - buf := make([]byte, 270) + // 1(addrType) + 1(lenByte) + 255(max length address) + 2(port) + 10(hmac-sha1) + buf := make([]byte, 269) // read till we get possible domain length field if _, err = io.ReadFull(conn, buf[:idType+1]); err != nil { return @@ -59,7 +59,7 @@ func getRequest(conn *ss.Conn, auth bool) (host string, ota bool, err error) { if _, err = io.ReadFull(conn, buf[idType+1:idDmLen+1]); err != nil { return } - reqStart, reqEnd = idDm0, int(idDm0+buf[idDmLen]+lenDmBase) + reqStart, reqEnd = idDm0, idDm0+int(buf[idDmLen])+lenDmBase default: err = fmt.Errorf("addr type %d not supported", addrType&ss.AddrMask) return From 73d78855e7f6ac713f3b2d045dbabba533616b87 Mon Sep 17 00:00:00 2001 From: andwxh Date: Mon, 9 Jan 2017 17:37:57 +0800 Subject: [PATCH 45/90] fix slice panic caused by byte overflow --- cmd/shadowsocks-server/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index d66a897e..3a931301 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -78,7 +78,7 @@ func getRequest(conn *ss.Conn, auth bool) (host string, ota bool, err error) { case typeIPv6: host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() case typeDm: - host = string(buf[idDm0 : idDm0+buf[idDmLen]]) + host = string(buf[idDm0 : idDm0+int(buf[idDmLen])]) } // parse port port := binary.BigEndian.Uint16(buf[reqEnd-2 : reqEnd]) From 33a4e114b5405ed0f14d686b60bcaefef65598b0 Mon Sep 17 00:00:00 2001 From: lucus lee Date: Wed, 18 Jan 2017 18:00:38 +0900 Subject: [PATCH 46/90] Full UDP support (#72) Only server side support. --- README.md | 6 + cmd/shadowsocks-server/server.go | 68 ++++++++- shadowsocks/udp.go | 139 +++++++++++++++++ shadowsocks/udprelay.go | 252 +++++++++++++++++++++++++++++++ 4 files changed, 463 insertions(+), 2 deletions(-) create mode 100644 shadowsocks/udp.go create mode 100644 shadowsocks/udprelay.go diff --git a/README.md b/README.md index 5b3a3182..9e2e64ba 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,12 @@ Append `-auth` to the encryption method to enable [One Time Auth (OTA)](https:// - For server: this will **force client use OTA**, non-OTA connection will be dropped. Otherwise, both OTA and non-OTA clients can connect - For client: the `-A` command line option can also enable OTA +### UDP relay + +Use `-u` command line options when starting server to enable UDP relay. + +Currently only tested with Shadowsocks-Android, if you have encountered any problem, please report. + ## Command line options Command line options can override settings from configuration files. Use `-h` option to see all available options. diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index b0b7e514..aa8acde8 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -37,6 +37,7 @@ const ( ) var debug ss.DebugLog +var udp bool func getRequest(conn *ss.Conn, auth bool) (host string, ota bool, err error) { ss.SetReadTimeout(conn) @@ -175,9 +176,15 @@ type PortListener struct { listener net.Listener } +type UDPListener struct { + password string + listener *net.UDPConn +} + type PasswdManager struct { sync.Mutex portListener map[string]*PortListener + udpListener map[string]*UDPListener } func (pm *PasswdManager) add(port, password string, listener net.Listener) { @@ -186,6 +193,12 @@ func (pm *PasswdManager) add(port, password string, listener net.Listener) { pm.Unlock() } +func (pm *PasswdManager) addUDP(port, password string, listener *net.UDPConn) { + pm.Lock() + pm.udpListener[port] = &UDPListener{password, listener} + pm.Unlock() +} + func (pm *PasswdManager) get(port string) (pl *PortListener, ok bool) { pm.Lock() pl, ok = pm.portListener[port] @@ -193,14 +206,31 @@ func (pm *PasswdManager) get(port string) (pl *PortListener, ok bool) { return } +func (pm *PasswdManager) getUDP(port string) (pl *UDPListener, ok bool) { + pm.Lock() + pl, ok = pm.udpListener[port] + pm.Unlock() + return +} + func (pm *PasswdManager) del(port string) { pl, ok := pm.get(port) if !ok { return } + if udp { + upl, ok := pm.getUDP(port) + if !ok { + return + } + upl.listener.Close() + } pl.listener.Close() pm.Lock() delete(pm.portListener, port) + if udp { + delete(pm.udpListener, port) + } pm.Unlock() } @@ -222,9 +252,14 @@ func (pm *PasswdManager) updatePortPasswd(port, password string, auth bool) { // run will add the new port listener to passwdManager. // So there maybe concurrent access to passwdManager and we need lock to protect it. go run(port, password, auth) + if udp { + pl, _ := pm.getUDP(port) + pl.listener.Close() + go runUDP(port, password, auth) + } } -var passwdManager = PasswdManager{portListener: map[string]*PortListener{}} +var passwdManager = PasswdManager{portListener: map[string]*PortListener{}, udpListener: map[string]*UDPListener{}} func updatePasswd() { log.Println("updating password") @@ -297,6 +332,31 @@ func run(port, password string, auth bool) { } } +func runUDP(port, password string, auth bool) { + var cipher *ss.Cipher + port_i, _ := strconv.Atoi(port) + log.Printf("listening udp port %v\n", port) + conn, err := net.ListenUDP("udp", &net.UDPAddr{ + IP: net.IPv6zero, + Port: port_i, + }) + passwdManager.addUDP(port, password, conn) + if err != nil { + log.Printf("error listening udp port %v: %v\n", port, err) + return + } + defer conn.Close() + cipher, err = ss.NewCipher(config.Method, password) + if err != nil { + log.Printf("Error generating cipher for udp port: %s %v\n", port, err) + conn.Close() + } + SecurePacketConn := ss.NewSecurePacketConn(conn, cipher.Copy(), auth) + for { + ss.ReadAndHandleUDPReq(SecurePacketConn) + } +} + func enoughOptions(config *ss.Config) bool { return config.ServerPort != 0 && config.Password != "" } @@ -335,7 +395,7 @@ func main() { flag.StringVar(&cmdConfig.Method, "m", "", "encryption method, default: aes-256-cfb") flag.IntVar(&core, "core", 0, "maximum number of CPU cores to use, default is determinied by Go runtime") flag.BoolVar((*bool)(&debug), "d", false, "print debug message") - + flag.BoolVar(&udp, "u", false, "UDP Relay") flag.Parse() if printVer { @@ -358,6 +418,7 @@ func main() { os.Exit(1) } config = &cmdConfig + ss.UpdateConfig(config, config) } else { ss.UpdateConfig(config, &cmdConfig) } @@ -376,6 +437,9 @@ func main() { } for port, password := range config.PortPassword { go run(port, password, config.Auth) + if udp { + go runUDP(port, password, config.Auth) + } } waitSignal() diff --git a/shadowsocks/udp.go b/shadowsocks/udp.go new file mode 100644 index 00000000..dff5d29f --- /dev/null +++ b/shadowsocks/udp.go @@ -0,0 +1,139 @@ +package shadowsocks + +import ( + "bytes" + "fmt" + "net" + "time" +) + +const ( + maxPacketSize = 4096 // increase it if error occurs +) + +var ( + errPacketTooSmall = fmt.Errorf("[udp]read error: cannot decrypt, received packet is smaller than ivLen") + errPacketTooLarge = fmt.Errorf("[udp]read error: received packet is latger than maxPacketSize(%d)", maxPacketSize) + errBufferTooSmall = fmt.Errorf("[udp]read error: given buffer is too small to hold data") + errPacketOtaFailed = fmt.Errorf("[udp]read error: received packet has invalid ota") +) + +type SecurePacketConn struct { + net.PacketConn + *Cipher + ota bool +} + +func NewSecurePacketConn(c net.PacketConn, cipher *Cipher, ota bool) *SecurePacketConn { + return &SecurePacketConn{ + PacketConn: c, + Cipher: cipher, + ota: ota, + } +} + +func (c *SecurePacketConn) Close() error { + return c.PacketConn.Close() +} + +func (c *SecurePacketConn) ReadFrom(b []byte) (n int, src net.Addr, err error) { + cipher := c.Copy() + buf := make([]byte, 4096) + n, src, err = c.PacketConn.ReadFrom(buf) + if err != nil { + return + } + + if n < c.info.ivLen { + return 0, nil, errPacketTooSmall + } + + if len(b) < n-c.info.ivLen { + err = errBufferTooSmall // just a warning + } + + iv := make([]byte, c.info.ivLen) + copy(iv, buf[:c.info.ivLen]) + + if err = cipher.initDecrypt(iv); err != nil { + return + } + + cipher.decrypt(b[0:], buf[c.info.ivLen:n]) + n -= c.info.ivLen + if c.ota { + key := cipher.key + actualHmacSha1Buf := HmacSha1(append(iv, key...), b[:n-lenHmacSha1]) + if !bytes.Equal(b[n-lenHmacSha1:n], actualHmacSha1Buf) { + Debug.Printf("verify one time auth failed, iv=%v key=%v data=%v", iv, key, b) + return 0, nil, errPacketOtaFailed + } + n -= lenHmacSha1 + } + + return +} + +func (c *SecurePacketConn) WriteTo(b []byte, dst net.Addr) (n int, err error) { + cipher := c.Copy() + iv, err := cipher.initEncrypt() + if err != nil { + return + } + packetLen := len(b) + len(iv) + + if c.ota { + packetLen += lenHmacSha1 + key := cipher.key + actualHmacSha1Buf := HmacSha1(append(iv, key...), b) + b = append(b, actualHmacSha1Buf...) + } + + cipherData := make([]byte, packetLen) + copy(cipherData, iv) + + cipher.encrypt(cipherData[len(iv):], b) + n, err = c.PacketConn.WriteTo(cipherData, dst) + return +} + +func (c *SecurePacketConn) LocalAddr() net.Addr { + return c.PacketConn.LocalAddr() +} + +func (c *SecurePacketConn) SetDeadline(t time.Time) error { + return c.PacketConn.SetDeadline(t) +} + +func (c *SecurePacketConn) SetReadDeadline(t time.Time) error { + return c.PacketConn.SetReadDeadline(t) +} + +func (c *SecurePacketConn) SetWriteDeadline(t time.Time) error { + return c.PacketConn.SetWriteDeadline(t) +} + +func (c *SecurePacketConn) IsOta() bool { + return c.ota +} + +func (c *SecurePacketConn) ForceOTAWriteTo(b []byte, dst net.Addr) (n int, err error) { + cipher := c.Copy() + iv, err := cipher.initEncrypt() + if err != nil { + return + } + packetLen := len(b) + len(iv) + + packetLen += lenHmacSha1 + key := cipher.key + actualHmacSha1Buf := HmacSha1(append(iv, key...), b) + b = append(b, actualHmacSha1Buf...) + + cipherData := make([]byte, packetLen) + copy(cipherData, iv) + + cipher.encrypt(cipherData[len(iv):], b) + n, err = c.PacketConn.WriteTo(cipherData, dst) + return +} diff --git a/shadowsocks/udprelay.go b/shadowsocks/udprelay.go new file mode 100644 index 00000000..bdbe74a9 --- /dev/null +++ b/shadowsocks/udprelay.go @@ -0,0 +1,252 @@ +package shadowsocks + +import ( + "encoding/binary" + "fmt" + "net" + "strconv" + "sync" + "syscall" + "time" +) + +const ( + idType = 0 // address type index + idIP0 = 1 // ip addres start index + idDmLen = 1 // domain address length index + idDm0 = 2 // domain address start index + + typeIPv4 = 1 // type is ipv4 address + typeDm = 3 // type is domain address + typeIPv6 = 4 // type is ipv6 address + + lenIPv4 = 1 + net.IPv4len + 2 // 1addrType + ipv4 + 2port + lenIPv6 = 1 + net.IPv6len + 2 // 1addrType + ipv6 + 2port + lenDmBase = 1 + 1 + 2 // 1addrType + 1addrLen + 2port, plus addrLen + lenHmacSha1 = 10 +) + +var ( + reqList = newReqList() + natlist = newNatTable() + udpTimeout = 30 * time.Second + reqListRefreshTime = 5 * time.Minute +) + +type natTable struct { + sync.Mutex + conns map[string]net.PacketConn +} + +func newNatTable() *natTable { + return &natTable{conns: map[string]net.PacketConn{}} +} + +func (table *natTable) DeleteAndClose(index string) { + table.Lock() + defer table.Unlock() + c, ok := table.conns[index] + if ok { + c.Close() + delete(table.conns, index) + } +} + +func (table *natTable) Get(index string) (c net.PacketConn, ok bool, err error) { + table.Lock() + defer table.Unlock() + c, ok = table.conns[index] + if !ok { + c, err = net.ListenPacket("udp", "") + if err != nil { + return nil, false, err + } + table.conns[index] = c + } + return +} + +type ReqList struct { + sync.Mutex + List map[string]([]byte) +} + +func newReqList() *ReqList { + ret := &ReqList{List: map[string]([]byte){}} + go func() { + for { + time.Sleep(reqListRefreshTime) + ret.Refresh() + } + }() + return ret +} + +func (r *ReqList) Refresh() { + r.Lock() + defer r.Unlock() + for k, _ := range r.List { + delete(r.List, k) + } +} + +func (r *ReqList) Get(dstaddr string) (req []byte, ok bool) { + r.Lock() + defer r.Unlock() + req, ok = r.List[dstaddr] + return +} + +func (r *ReqList) Put(dstaddr string, req []byte) { + r.Lock() + defer r.Unlock() + r.List[dstaddr] = req + return +} + +func ParseHeader(addr net.Addr) ([]byte, int) { + // if the request address type is domain, it cannot be reverselookuped + ip, port, err := net.SplitHostPort(addr.String()) + if err != nil { + return nil, 0 + } + buf := make([]byte, 20) + IP := net.ParseIP(ip) + b1 := IP.To4() + iplen := 0 + if b1 == nil { //ipv6 + b1 = IP.To16() + buf[0] = typeIPv6 + iplen = net.IPv6len + } else { //ipv4 + buf[0] = typeIPv4 + iplen = net.IPv4len + } + copy(buf[1:], b1) + port_i, _ := strconv.Atoi(port) + binary.BigEndian.PutUint16(buf[1+iplen:], uint16(port_i)) + return buf[:1+iplen+2], 1 + iplen + 2 +} + +func Pipeloop(ss *SecurePacketConn, addr net.Addr, in net.PacketConn, compatiblemode bool) { + buf := leakyBuf.Get() + defer leakyBuf.Put(buf) + defer in.Close() + for { + in.SetDeadline(time.Now().Add(udpTimeout)) + n, raddr, err := in.ReadFrom(buf) + if err != nil { + if ne, ok := err.(*net.OpError); ok { + if ne.Err == syscall.EMFILE || ne.Err == syscall.ENFILE { + // log too many open file error + // EMFILE is process reaches open file limits, ENFILE is system limit + Debug.Println("[udp]read error:", err) + } + } + Debug.Printf("[udp]closed pipe %s<-%s\n", addr, in.LocalAddr()) + return + } + // need improvement here + if req, ok := reqList.Get(raddr.String()); ok { + if compatiblemode { + ss.ForceOTAWriteTo(append(req, buf[:n]...), addr) + } else { + ss.WriteTo(append(req, buf[:n]...), addr) + } + } else { + header, hlen := ParseHeader(raddr) + if compatiblemode { + ss.ForceOTAWriteTo(append(header[:hlen], buf[:n]...), addr) + } else { + ss.WriteTo(append(header[:hlen], buf[:n]...), addr) + } + + } + } +} + +func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive []byte) { + var dstIP net.IP + var reqLen int + var ota bool + addrType := receive[idType] + defer leakyBuf.Put(receive) + + if addrType&OneTimeAuthMask > 0 { + ota = true + } + if handle.IsOta() && !ota { + Debug.Println("[udp]incoming connection dropped, due to ota enforcement") + return + } + compatiblemode := !handle.IsOta() && ota + + switch addrType & AddrMask { + case typeIPv4: + reqLen = lenIPv4 + dstIP = net.IP(receive[idIP0 : idIP0+net.IPv4len]) + case typeIPv6: + reqLen = lenIPv6 + dstIP = net.IP(receive[idIP0 : idIP0+net.IPv6len]) + case typeDm: + reqLen = int(receive[idDmLen]) + lenDmBase + dIP, err := net.ResolveIPAddr("ip", string(receive[idDm0:idDm0+int(receive[idDmLen])])) // carefully with const type + if err != nil { + Debug.Printf("[udp]failed to resolve domain name: %s\n", string(receive[idDm0:idDm0+receive[idDmLen]])) + return + } + dstIP = dIP.IP + default: + Debug.Printf("[udp]addrType %d not supported", addrType) + return + } + dst := &net.UDPAddr{ + IP: dstIP, + Port: int(binary.BigEndian.Uint16(receive[reqLen-2 : reqLen])), + } + if _, ok := reqList.Get(dst.String()); !ok { + req := make([]byte, reqLen) + copy(req, receive) + reqList.Put(dst.String(), req) + } + + remote, exist, err := natlist.Get(src.String()) + if err != nil { + return + } + if !exist { + Debug.Printf("[udp]new client %s->%s via %s ota=%v\n", src, dst, remote.LocalAddr(), ota) + go func() { + Pipeloop(handle, src, remote, compatiblemode) + natlist.DeleteAndClose(src.String()) + }() + } else { + Debug.Printf("[udp]using cached client %s->%s via %s ota=%v\n", src, dst, remote.LocalAddr(), ota) + } + if remote == nil { + fmt.Println("WTF") + } + remote.SetDeadline(time.Now().Add(udpTimeout)) + _, err = remote.WriteTo(receive[reqLen:n], dst) + if err != nil { + if ne, ok := err.(*net.OpError); ok && (ne.Err == syscall.EMFILE || ne.Err == syscall.ENFILE) { + // log too many open file error + // EMFILE is process reaches open file limits, ENFILE is system limit + Debug.Println("[udp]write error:", err) + } else { + Debug.Println("[udp]error connecting to:", dst, err) + } + natlist.DeleteAndClose(src.String()) + } + // Pipeloop + return +} + +func ReadAndHandleUDPReq(c *SecurePacketConn) { + buf := leakyBuf.Get() + n, src, err := c.ReadFrom(buf[0:]) + if err != nil { + return + } + go handleUDPConnection(c, n, src, buf) +} From d83094c93e96b89a16fe1312d892f7c69cb669cf Mon Sep 17 00:00:00 2001 From: Enex Slurin Date: Thu, 29 Sep 2016 17:57:33 +0800 Subject: [PATCH 47/90] Add AES-[128/192/256]-CTR encrypt method. Signed-off-by: lucus --- shadowsocks/encrypt.go | 19 +++++++--- shadowsocks/encrypt_test.go | 72 ++++++++++++++++++++++++++++++------- 2 files changed, 75 insertions(+), 16 deletions(-) diff --git a/shadowsocks/encrypt.go b/shadowsocks/encrypt.go index 146947d3..de16ef2a 100644 --- a/shadowsocks/encrypt.go +++ b/shadowsocks/encrypt.go @@ -65,11 +65,19 @@ func newStream(block cipher.Block, err error, key, iv []byte, } } -func newAESStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { +func newAESCFBStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { block, err := aes.NewCipher(key) return newStream(block, err, key, iv, doe) } +func newAESCTRStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + return cipher.NewCTR(block, iv), nil +} + func newDESStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { block, err := des.NewCipher(key) return newStream(block, err, key, iv, doe) @@ -145,9 +153,12 @@ type cipherInfo struct { } var cipherMethod = map[string]*cipherInfo{ - "aes-128-cfb": {16, 16, newAESStream}, - "aes-192-cfb": {24, 16, newAESStream}, - "aes-256-cfb": {32, 16, newAESStream}, + "aes-128-cfb": {16, 16, newAESCFBStream}, + "aes-192-cfb": {24, 16, newAESCFBStream}, + "aes-256-cfb": {32, 16, newAESCFBStream}, + "aes-128-ctr": {16, 16, newAESCTRStream}, + "aes-192-ctr": {24, 16, newAESCTRStream}, + "aes-256-ctr": {32, 16, newAESCTRStream}, "des-cfb": {8, 8, newDESStream}, "bf-cfb": {16, 8, newBlowFishStream}, "cast5-cfb": {16, 8, newCast5Stream}, diff --git a/shadowsocks/encrypt_test.go b/shadowsocks/encrypt_test.go index 9ff24286..09673538 100644 --- a/shadowsocks/encrypt_test.go +++ b/shadowsocks/encrypt_test.go @@ -63,18 +63,30 @@ func testBlockCipher(t *testing.T, method string) { testCiphter(t, cipherCopy, method+" copy") } -func TestAES128(t *testing.T) { +func TestAES128CFB(t *testing.T) { testBlockCipher(t, "aes-128-cfb") } -func TestAES192(t *testing.T) { +func TestAES192CFB(t *testing.T) { testBlockCipher(t, "aes-192-cfb") } -func TestAES256(t *testing.T) { +func TestAES256CFB(t *testing.T) { testBlockCipher(t, "aes-256-cfb") } +func TestAES128CTR(t *testing.T) { + testBlockCipher(t, "aes-128-ctr") +} + +func TestAES192CTR(t *testing.T) { + testBlockCipher(t, "aes-192-ctr") +} + +func TestAES256CTR(t *testing.T) { + testBlockCipher(t, "aes-256-ctr") +} + func TestDES(t *testing.T) { testBlockCipher(t, "des-cfb") } @@ -108,18 +120,30 @@ func benchmarkCipherInit(b *testing.B, method string) { } } -func BenchmarkAES128Init(b *testing.B) { +func BenchmarkAES128CFBInit(b *testing.B) { benchmarkCipherInit(b, "aes-128-cfb") } -func BenchmarkAES192Init(b *testing.B) { +func BenchmarkAES19CFB2Init(b *testing.B) { benchmarkCipherInit(b, "aes-192-cfb") } -func BenchmarkAES256Init(b *testing.B) { +func BenchmarkAES256CFBInit(b *testing.B) { benchmarkCipherInit(b, "aes-256-cfb") } +func BenchmarkAES128CTRInit(b *testing.B) { + benchmarkCipherInit(b, "aes-128-ctr") +} + +func BenchmarkAES192CTRInit(b *testing.B) { + benchmarkCipherInit(b, "aes-192-ctr") +} + +func BenchmarkAES256CTRInit(b *testing.B) { + benchmarkCipherInit(b, "aes-256-ctr") +} + func BenchmarkBlowFishInit(b *testing.B) { benchmarkCipherInit(b, "bf-cfb") } @@ -160,18 +184,30 @@ func benchmarkCipherEncrypt(b *testing.B, method string) { } } -func BenchmarkAES128Encrypt(b *testing.B) { +func BenchmarkAES128CFBEncrypt(b *testing.B) { benchmarkCipherEncrypt(b, "aes-128-cfb") } -func BenchmarkAES192Encrypt(b *testing.B) { +func BenchmarkAES192CFBEncrypt(b *testing.B) { benchmarkCipherEncrypt(b, "aes-192-cfb") } -func BenchmarkAES256Encrypt(b *testing.B) { +func BenchmarkAES256CFBEncrypt(b *testing.B) { benchmarkCipherEncrypt(b, "aes-256-cfb") } +func BenchmarkAES128CTREncrypt(b *testing.B) { + benchmarkCipherEncrypt(b, "aes-128-ctr") +} + +func BenchmarkAES192CTREncrypt(b *testing.B) { + benchmarkCipherEncrypt(b, "aes-192-ctr") +} + +func BenchmarkAES256CTREncrypt(b *testing.B) { + benchmarkCipherEncrypt(b, "aes-256-ctr") +} + func BenchmarkBlowFishEncrypt(b *testing.B) { benchmarkCipherEncrypt(b, "bf-cfb") } @@ -217,18 +253,30 @@ func benchmarkCipherDecrypt(b *testing.B, method string) { } } -func BenchmarkAES128Decrypt(b *testing.B) { +func BenchmarkAES128CFBDecrypt(b *testing.B) { benchmarkCipherDecrypt(b, "aes-128-cfb") } -func BenchmarkAES192Decrypt(b *testing.B) { +func BenchmarkAES192CFBDecrypt(b *testing.B) { benchmarkCipherDecrypt(b, "aes-192-cfb") } -func BenchmarkAES256Decrypt(b *testing.B) { +func BenchmarkAES256CFBDecrypt(b *testing.B) { benchmarkCipherDecrypt(b, "aes-256-cfb") } +func BenchmarkAES128CTRDecrypt(b *testing.B) { + benchmarkCipherDecrypt(b, "aes-128-ctr") +} + +func BenchmarkAES192CTRDecrypt(b *testing.B) { + benchmarkCipherDecrypt(b, "aes-192-ctr") +} + +func BenchmarkAES256CTRDecrypt(b *testing.B) { + benchmarkCipherDecrypt(b, "aes-256-ctr") +} + func BenchmarkBlowFishDecrypt(b *testing.B) { benchmarkCipherDecrypt(b, "bf-cfb") } From 4bb1d5df51317c2119bb016a0c3a25c1266b67b4 Mon Sep 17 00:00:00 2001 From: lucus Date: Wed, 18 Jan 2017 18:16:31 +0900 Subject: [PATCH 48/90] Close conn if handshake failed. --- cmd/shadowsocks-server/server.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index aa8acde8..58a34990 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -139,6 +139,7 @@ func handleConnection(conn *ss.Conn, auth bool) { host, ota, err := getRequest(conn, auth) if err != nil { log.Println("error getting request", conn.RemoteAddr(), conn.LocalAddr(), err) + conn.Close() return } debug.Println("connecting", host) From 485697d78306a009da6bdab264749de284f5ce8b Mon Sep 17 00:00:00 2001 From: lucus Date: Wed, 18 Jan 2017 21:02:30 +0900 Subject: [PATCH 49/90] Correctly close the conn. --- cmd/shadowsocks-server/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index 58a34990..5139f65a 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -139,7 +139,7 @@ func handleConnection(conn *ss.Conn, auth bool) { host, ota, err := getRequest(conn, auth) if err != nil { log.Println("error getting request", conn.RemoteAddr(), conn.LocalAddr(), err) - conn.Close() + closed = true return } debug.Println("connecting", host) From f6fcfb027b9ab14e1567df9e90ee446df9994f07 Mon Sep 17 00:00:00 2001 From: lucus Date: Wed, 18 Jan 2017 20:37:33 +0900 Subject: [PATCH 50/90] Avoid syscall panic on windows. When receiving false data. --- shadowsocks/udprelay.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/shadowsocks/udprelay.go b/shadowsocks/udprelay.go index bdbe74a9..b5c6e177 100644 --- a/shadowsocks/udprelay.go +++ b/shadowsocks/udprelay.go @@ -5,6 +5,7 @@ import ( "fmt" "net" "strconv" + "strings" "sync" "syscall" "time" @@ -184,13 +185,28 @@ func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive switch addrType & AddrMask { case typeIPv4: reqLen = lenIPv4 + if len(receive) < reqLen { + Debug.Println("[udp]invalid received message.") + } dstIP = net.IP(receive[idIP0 : idIP0+net.IPv4len]) case typeIPv6: reqLen = lenIPv6 + if len(receive) < reqLen { + Debug.Println("[udp]invalid received message.") + } dstIP = net.IP(receive[idIP0 : idIP0+net.IPv6len]) case typeDm: reqLen = int(receive[idDmLen]) + lenDmBase - dIP, err := net.ResolveIPAddr("ip", string(receive[idDm0:idDm0+int(receive[idDmLen])])) // carefully with const type + if len(receive) < reqLen { + Debug.Println("[udp]invalid received message.") + } + name := string(receive[idDm0 : idDm0+int(receive[idDmLen])]) + // avoid panic: syscall: string with NUL passed to StringToUTF16 on windows. + if strings.ContainsRune(name, 0x00) { + fmt.Println("[udp]invalid domain name.") + return + } + dIP, err := net.ResolveIPAddr("ip", name) // carefully with const type if err != nil { Debug.Printf("[udp]failed to resolve domain name: %s\n", string(receive[idDm0:idDm0+receive[idDmLen]])) return From 6d467e2680512ead5a01183e5576e2c62bf744a9 Mon Sep 17 00:00:00 2001 From: lucus Date: Thu, 19 Jan 2017 01:33:29 +0900 Subject: [PATCH 51/90] Make sure the tcp handling won't crash on Win32 --- cmd/shadowsocks-server/server.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index 5139f65a..737a6f9d 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -142,6 +142,12 @@ func handleConnection(conn *ss.Conn, auth bool) { closed = true return } + // ensure the host does not contain some illegal characters, NUL may panic on Win32 + if strings.ContainsRune(host, 0x00) { + log.Println("invalid domain name.") + closed = true + return + } debug.Println("connecting", host) remote, err := net.Dial("tcp", host) if err != nil { From 5ba846eb3bec3bcd431635258f2ea10d5220d738 Mon Sep 17 00:00:00 2001 From: lucus lee Date: Thu, 19 Jan 2017 01:41:04 +0900 Subject: [PATCH 52/90] Supporting chacha20-ietf. (#187) * Supporting `chacha20-ietf`. Using a new package providing higher performance. --- .travis.yml | 4 ++-- shadowsocks/encrypt.go | 33 +++++++++++++++++++-------------- shadowsocks/encrypt_test.go | 16 ++++++++++++++++ 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index dea91125..30d8e2b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,11 @@ language: go go: - - 1.4.3 + - 1.7.4 install: - go get golang.org/x/crypto/blowfish - go get golang.org/x/crypto/cast5 - go get golang.org/x/crypto/salsa20 - - go get github.com/codahale/chacha20 + - go get github.com/Yawning/chacha20 - go install ./cmd/shadowsocks-local - go install ./cmd/shadowsocks-server script: diff --git a/shadowsocks/encrypt.go b/shadowsocks/encrypt.go index de16ef2a..44d96a74 100644 --- a/shadowsocks/encrypt.go +++ b/shadowsocks/encrypt.go @@ -12,7 +12,7 @@ import ( "io" "strings" - "github.com/codahale/chacha20" + "github.com/Yawning/chacha20" "golang.org/x/crypto/blowfish" "golang.org/x/crypto/cast5" "golang.org/x/crypto/salsa20/salsa" @@ -103,7 +103,11 @@ func newRC4MD5Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { } func newChaCha20Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { - return chacha20.New(key, iv) + return chacha20.NewCipher(key, iv) +} + +func newChaCha20IETFStream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { + return chacha20.NewCipher(key, iv) } type salsaStreamCipher struct { @@ -153,18 +157,19 @@ type cipherInfo struct { } var cipherMethod = map[string]*cipherInfo{ - "aes-128-cfb": {16, 16, newAESCFBStream}, - "aes-192-cfb": {24, 16, newAESCFBStream}, - "aes-256-cfb": {32, 16, newAESCFBStream}, - "aes-128-ctr": {16, 16, newAESCTRStream}, - "aes-192-ctr": {24, 16, newAESCTRStream}, - "aes-256-ctr": {32, 16, newAESCTRStream}, - "des-cfb": {8, 8, newDESStream}, - "bf-cfb": {16, 8, newBlowFishStream}, - "cast5-cfb": {16, 8, newCast5Stream}, - "rc4-md5": {16, 16, newRC4MD5Stream}, - "chacha20": {32, 8, newChaCha20Stream}, - "salsa20": {32, 8, newSalsa20Stream}, + "aes-128-cfb": {16, 16, newAESCFBStream}, + "aes-192-cfb": {24, 16, newAESCFBStream}, + "aes-256-cfb": {32, 16, newAESCFBStream}, + "aes-128-ctr": {16, 16, newAESCTRStream}, + "aes-192-ctr": {24, 16, newAESCTRStream}, + "aes-256-ctr": {32, 16, newAESCTRStream}, + "des-cfb": {8, 8, newDESStream}, + "bf-cfb": {16, 8, newBlowFishStream}, + "cast5-cfb": {16, 8, newCast5Stream}, + "rc4-md5": {16, 16, newRC4MD5Stream}, + "chacha20": {32, 8, newChaCha20Stream}, + "chacha20-ietf": {32, 12, newChaCha20IETFStream}, + "salsa20": {32, 8, newSalsa20Stream}, } func CheckCipherMethod(method string) error { diff --git a/shadowsocks/encrypt_test.go b/shadowsocks/encrypt_test.go index 09673538..9a2e6ce6 100644 --- a/shadowsocks/encrypt_test.go +++ b/shadowsocks/encrypt_test.go @@ -99,6 +99,10 @@ func TestChaCha20(t *testing.T) { testBlockCipher(t, "chacha20") } +func TestChaCha20IETF(t *testing.T) { + testBlockCipher(t, "chacha20-ietf") +} + var cipherKey = make([]byte, 64) var cipherIv = make([]byte, 64) @@ -164,6 +168,10 @@ func BenchmarkChaCha20Init(b *testing.B) { benchmarkCipherInit(b, "chacha20") } +func BenchmarkChaCha20IETFInit(b *testing.B) { + benchmarkCipherInit(b, "chacha20-ietf") +} + func BenchmarkSalsa20Init(b *testing.B) { benchmarkCipherInit(b, "salsa20") } @@ -228,6 +236,10 @@ func BenchmarkChacha20Encrypt(b *testing.B) { benchmarkCipherEncrypt(b, "chacha20") } +func BenchmarkChacha20IETFEncrypt(b *testing.B) { + benchmarkCipherEncrypt(b, "chacha20-ietf") +} + func BenchmarkSalsa20Encrypt(b *testing.B) { benchmarkCipherEncrypt(b, "salsa20") } @@ -297,6 +309,10 @@ func BenchmarkChaCha20Decrypt(b *testing.B) { benchmarkCipherDecrypt(b, "chacha20") } +func BenchmarkChaCha20IETFDecrypt(b *testing.B) { + benchmarkCipherDecrypt(b, "chacha20-ietf") +} + func BenchmarkSalsa20Decrypt(b *testing.B) { benchmarkCipherDecrypt(b, "salsa20") } From 964fa2793578330ab7ff01a26f3644c45bb64316 Mon Sep 17 00:00:00 2001 From: lucus Date: Thu, 19 Jan 2017 03:01:48 +0900 Subject: [PATCH 53/90] Better handling UDP packets with OTA. --- cmd/shadowsocks-server/server.go | 4 +++- shadowsocks/udp.go | 17 +++++++++++++++-- shadowsocks/udprelay.go | 9 +++------ 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index 737a6f9d..0477b67b 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -360,7 +360,9 @@ func runUDP(port, password string, auth bool) { } SecurePacketConn := ss.NewSecurePacketConn(conn, cipher.Copy(), auth) for { - ss.ReadAndHandleUDPReq(SecurePacketConn) + if err := ss.ReadAndHandleUDPReq(SecurePacketConn); err != nil { + debug.Println(err) + } } } diff --git a/shadowsocks/udp.go b/shadowsocks/udp.go index dff5d29f..058e93e1 100644 --- a/shadowsocks/udp.go +++ b/shadowsocks/udp.go @@ -37,6 +37,7 @@ func (c *SecurePacketConn) Close() error { } func (c *SecurePacketConn) ReadFrom(b []byte) (n int, src net.Addr, err error) { + ota := false cipher := c.Copy() buf := make([]byte, 4096) n, src, err = c.PacketConn.ReadFrom(buf) @@ -61,12 +62,20 @@ func (c *SecurePacketConn) ReadFrom(b []byte) (n int, src net.Addr, err error) { cipher.decrypt(b[0:], buf[c.info.ivLen:n]) n -= c.info.ivLen - if c.ota { + if b[idType]&OneTimeAuthMask > 0 { + ota = true + } + + if c.ota && !ota { + return 0, src, errPacketOtaFailed + } + + if ota { key := cipher.key actualHmacSha1Buf := HmacSha1(append(iv, key...), b[:n-lenHmacSha1]) if !bytes.Equal(b[n-lenHmacSha1:n], actualHmacSha1Buf) { Debug.Printf("verify one time auth failed, iv=%v key=%v data=%v", iv, key, b) - return 0, nil, errPacketOtaFailed + return 0, src, errPacketOtaFailed } n -= lenHmacSha1 } @@ -94,6 +103,9 @@ func (c *SecurePacketConn) WriteTo(b []byte, dst net.Addr) (n int, err error) { cipher.encrypt(cipherData[len(iv):], b) n, err = c.PacketConn.WriteTo(cipherData, dst) + if c.ota { + n -= lenHmacSha1 + } return } @@ -135,5 +147,6 @@ func (c *SecurePacketConn) ForceOTAWriteTo(b []byte, dst net.Addr) (n int, err e cipher.encrypt(cipherData[len(iv):], b) n, err = c.PacketConn.WriteTo(cipherData, dst) + n -= lenHmacSha1 return } diff --git a/shadowsocks/udprelay.go b/shadowsocks/udprelay.go index b5c6e177..c38dbe0a 100644 --- a/shadowsocks/udprelay.go +++ b/shadowsocks/udprelay.go @@ -176,10 +176,6 @@ func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive if addrType&OneTimeAuthMask > 0 { ota = true } - if handle.IsOta() && !ota { - Debug.Println("[udp]incoming connection dropped, due to ota enforcement") - return - } compatiblemode := !handle.IsOta() && ota switch addrType & AddrMask { @@ -258,11 +254,12 @@ func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive return } -func ReadAndHandleUDPReq(c *SecurePacketConn) { +func ReadAndHandleUDPReq(c *SecurePacketConn) error { buf := leakyBuf.Get() n, src, err := c.ReadFrom(buf[0:]) if err != nil { - return + return err } go handleUDPConnection(c, n, src, buf) + return nil } From d7780ed1168107df607ca12ebc6ac0b2bfb29663 Mon Sep 17 00:00:00 2001 From: lucus Date: Fri, 20 Jan 2017 00:17:02 +0900 Subject: [PATCH 54/90] Bump to 1.2.0. --- CHANGELOG | 10 ++++++++++ README.md | 2 +- shadowsocks/util.go | 10 +++++----- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b6886994..4d5ecc4a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,13 @@ +1.2.0 (2017-01-20) + * Support UDP reley on server side, and OTA + * Support "aes-[128/192/256]-ctr" encryption method (Thanks for @slurin) + * Support "chacha20-ietf" encryption method + * Improve performance of "chacha20" encryption method + * Corrently close connection if handshake failed + +1.1.5 (2016-05-04) + * Support OTA (Thanks for @ayanamist for implementing this feature) + 1.1.4 (2015-05-10) * Support "chacha20" encryption method, thanks to @defia * Support "salsa20" encryption method, thanks to @genzj diff --git a/README.md b/README.md index 9e2e64ba..2d926906 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # shadowsocks-go -Current version: 1.1.5 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=develop)](https://travis-ci.org/shadowsocks/shadowsocks-go) +Current version: 1.2.0 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=develop)](https://travis-ci.org/shadowsocks/shadowsocks-go) 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). diff --git a/shadowsocks/util.go b/shadowsocks/util.go index 21c01c6a..671fe6cb 100644 --- a/shadowsocks/util.go +++ b/shadowsocks/util.go @@ -1,16 +1,16 @@ package shadowsocks import ( - "errors" - "fmt" - "os" "crypto/hmac" "crypto/sha1" "encoding/binary" + "errors" + "fmt" + "os" ) func PrintVersion() { - const version = "1.1.5" + const version = "1.2.0" fmt.Println("shadowsocks-go version", version) } @@ -57,4 +57,4 @@ func (flag *ClosedFlag) SetClosed() { func (flag *ClosedFlag) IsClosed() bool { return flag.flag -} \ No newline at end of file +} From 6943010274f03661b99c19f82aa1284996c6ed02 Mon Sep 17 00:00:00 2001 From: lucus Date: Sun, 22 Jan 2017 05:20:06 +0900 Subject: [PATCH 55/90] Fix typos. --- CHANGELOG | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 4d5ecc4a..5e25e8ea 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,9 +1,9 @@ 1.2.0 (2017-01-20) - * Support UDP reley on server side, and OTA + * Support UDP relay on server side, and OTA * Support "aes-[128/192/256]-ctr" encryption method (Thanks for @slurin) * Support "chacha20-ietf" encryption method * Improve performance of "chacha20" encryption method - * Corrently close connection if handshake failed + * Correctly close connection if handshake failed 1.1.5 (2016-05-04) * Support OTA (Thanks for @ayanamist for implementing this feature) From 789556a3039b5272cebc1183da2a96fd355c9a83 Mon Sep 17 00:00:00 2001 From: lucus Date: Sun, 22 Jan 2017 03:20:39 +0900 Subject: [PATCH 56/90] Refine the UDP OTA code. --- shadowsocks/udp.go | 22 ++-------------- shadowsocks/udprelay.go | 57 ++++++++++++++++++++--------------------- 2 files changed, 30 insertions(+), 49 deletions(-) diff --git a/shadowsocks/udp.go b/shadowsocks/udp.go index 058e93e1..898092da 100644 --- a/shadowsocks/udp.go +++ b/shadowsocks/udp.go @@ -129,24 +129,6 @@ func (c *SecurePacketConn) IsOta() bool { return c.ota } -func (c *SecurePacketConn) ForceOTAWriteTo(b []byte, dst net.Addr) (n int, err error) { - cipher := c.Copy() - iv, err := cipher.initEncrypt() - if err != nil { - return - } - packetLen := len(b) + len(iv) - - packetLen += lenHmacSha1 - key := cipher.key - actualHmacSha1Buf := HmacSha1(append(iv, key...), b) - b = append(b, actualHmacSha1Buf...) - - cipherData := make([]byte, packetLen) - copy(cipherData, iv) - - cipher.encrypt(cipherData[len(iv):], b) - n, err = c.PacketConn.WriteTo(cipherData, dst) - n -= lenHmacSha1 - return +func (c *SecurePacketConn) ForceOTA() net.PacketConn { + return NewSecurePacketConn(c.PacketConn, c.Cipher.Copy(), true) } diff --git a/shadowsocks/udprelay.go b/shadowsocks/udprelay.go index c38dbe0a..37f48589 100644 --- a/shadowsocks/udprelay.go +++ b/shadowsocks/udprelay.go @@ -43,14 +43,15 @@ func newNatTable() *natTable { return &natTable{conns: map[string]net.PacketConn{}} } -func (table *natTable) DeleteAndClose(index string) { +func (table *natTable) Delete(index string) net.PacketConn { table.Lock() defer table.Unlock() c, ok := table.conns[index] if ok { - c.Close() delete(table.conns, index) + return c } + return nil } func (table *natTable) Get(index string) (c net.PacketConn, ok bool, err error) { @@ -67,13 +68,13 @@ func (table *natTable) Get(index string) (c net.PacketConn, ok bool, err error) return } -type ReqList struct { +type requestHeaderList struct { sync.Mutex List map[string]([]byte) } -func newReqList() *ReqList { - ret := &ReqList{List: map[string]([]byte){}} +func newReqList() *requestHeaderList { + ret := &requestHeaderList{List: map[string]([]byte){}} go func() { for { time.Sleep(reqListRefreshTime) @@ -83,7 +84,7 @@ func newReqList() *ReqList { return ret } -func (r *ReqList) Refresh() { +func (r *requestHeaderList) Refresh() { r.Lock() defer r.Unlock() for k, _ := range r.List { @@ -91,21 +92,21 @@ func (r *ReqList) Refresh() { } } -func (r *ReqList) Get(dstaddr string) (req []byte, ok bool) { +func (r *requestHeaderList) Get(dstaddr string) (req []byte, ok bool) { r.Lock() defer r.Unlock() req, ok = r.List[dstaddr] return } -func (r *ReqList) Put(dstaddr string, req []byte) { +func (r *requestHeaderList) Put(dstaddr string, req []byte) { r.Lock() defer r.Unlock() r.List[dstaddr] = req return } -func ParseHeader(addr net.Addr) ([]byte, int) { +func parseHeaderFromAddr(addr net.Addr) ([]byte, int) { // if the request address type is domain, it cannot be reverselookuped ip, port, err := net.SplitHostPort(addr.String()) if err != nil { @@ -129,13 +130,13 @@ func ParseHeader(addr net.Addr) ([]byte, int) { return buf[:1+iplen+2], 1 + iplen + 2 } -func Pipeloop(ss *SecurePacketConn, addr net.Addr, in net.PacketConn, compatiblemode bool) { +func Pipeloop(write net.PacketConn, writeAddr net.Addr, readClose net.PacketConn) { buf := leakyBuf.Get() defer leakyBuf.Put(buf) - defer in.Close() + defer readClose.Close() for { - in.SetDeadline(time.Now().Add(udpTimeout)) - n, raddr, err := in.ReadFrom(buf) + readClose.SetDeadline(time.Now().Add(udpTimeout)) + n, raddr, err := readClose.ReadFrom(buf) if err != nil { if ne, ok := err.(*net.OpError); ok { if ne.Err == syscall.EMFILE || ne.Err == syscall.ENFILE { @@ -144,24 +145,15 @@ func Pipeloop(ss *SecurePacketConn, addr net.Addr, in net.PacketConn, compatible Debug.Println("[udp]read error:", err) } } - Debug.Printf("[udp]closed pipe %s<-%s\n", addr, in.LocalAddr()) + Debug.Printf("[udp]closed pipe %s<-%s\n", writeAddr, readClose.LocalAddr()) return } // need improvement here if req, ok := reqList.Get(raddr.String()); ok { - if compatiblemode { - ss.ForceOTAWriteTo(append(req, buf[:n]...), addr) - } else { - ss.WriteTo(append(req, buf[:n]...), addr) - } + write.WriteTo(append(req, buf[:n]...), writeAddr) } else { - header, hlen := ParseHeader(raddr) - if compatiblemode { - ss.ForceOTAWriteTo(append(header[:hlen], buf[:n]...), addr) - } else { - ss.WriteTo(append(header[:hlen], buf[:n]...), addr) - } - + header, hlen := parseHeaderFromAddr(raddr) + write.WriteTo(append(header[:hlen], buf[:n]...), writeAddr) } } } @@ -229,8 +221,13 @@ func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive if !exist { Debug.Printf("[udp]new client %s->%s via %s ota=%v\n", src, dst, remote.LocalAddr(), ota) go func() { - Pipeloop(handle, src, remote, compatiblemode) - natlist.DeleteAndClose(src.String()) + if compatiblemode { + Pipeloop(handle.ForceOTA(), src, remote) + } else { + Pipeloop(handle, src, remote) + } + + natlist.Delete(src.String()) }() } else { Debug.Printf("[udp]using cached client %s->%s via %s ota=%v\n", src, dst, remote.LocalAddr(), ota) @@ -248,7 +245,9 @@ func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive } else { Debug.Println("[udp]error connecting to:", dst, err) } - natlist.DeleteAndClose(src.String()) + if conn := natlist.Delete(src.String()); conn != nil { + conn.Close() + } } // Pipeloop return From 0e4e35a508994820876562ddf083bfc2190a8046 Mon Sep 17 00:00:00 2001 From: lucus Date: Sun, 22 Jan 2017 05:31:42 +0900 Subject: [PATCH 57/90] Fix bug handling UDP OTA. --- shadowsocks/udp.go | 1 + shadowsocks/udprelay.go | 1 + 2 files changed, 2 insertions(+) diff --git a/shadowsocks/udp.go b/shadowsocks/udp.go index 898092da..62a55d84 100644 --- a/shadowsocks/udp.go +++ b/shadowsocks/udp.go @@ -92,6 +92,7 @@ func (c *SecurePacketConn) WriteTo(b []byte, dst net.Addr) (n int, err error) { packetLen := len(b) + len(iv) if c.ota { + b[idType] |= OneTimeAuthMask packetLen += lenHmacSha1 key := cipher.key actualHmacSha1Buf := HmacSha1(append(iv, key...), b) diff --git a/shadowsocks/udprelay.go b/shadowsocks/udprelay.go index 37f48589..727efb67 100644 --- a/shadowsocks/udprelay.go +++ b/shadowsocks/udprelay.go @@ -168,6 +168,7 @@ func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive if addrType&OneTimeAuthMask > 0 { ota = true } + receive[idType] &= ^OneTimeAuthMask compatiblemode := !handle.IsOta() && ota switch addrType & AddrMask { From 9b9860fed36451fa7842da1d9220f75c63eae447 Mon Sep 17 00:00:00 2001 From: lucus Date: Sun, 22 Jan 2017 05:33:13 +0900 Subject: [PATCH 58/90] Bump to 1.2.1 --- CHANGELOG | 3 +++ README.md | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 5e25e8ea..dca89d19 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +1.2.1 (2017-01-22) + * Fix bug in handling of UDP OTA packets + 1.2.0 (2017-01-20) * Support UDP relay on server side, and OTA * Support "aes-[128/192/256]-ctr" encryption method (Thanks for @slurin) diff --git a/README.md b/README.md index 2d926906..9b54b1dc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # shadowsocks-go -Current version: 1.2.0 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=develop)](https://travis-ci.org/shadowsocks/shadowsocks-go) +Current version: 1.2.1 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=develop)](https://travis-ci.org/shadowsocks/shadowsocks-go) 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). From 226224f444521115e901ad616bc43f2ee710882f Mon Sep 17 00:00:00 2001 From: lucus Date: Mon, 23 Jan 2017 16:38:15 +0900 Subject: [PATCH 59/90] Fix version. --- shadowsocks/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks/util.go b/shadowsocks/util.go index 671fe6cb..7ce7a6bd 100644 --- a/shadowsocks/util.go +++ b/shadowsocks/util.go @@ -10,7 +10,7 @@ import ( ) func PrintVersion() { - const version = "1.2.0" + const version = "1.2.1" fmt.Println("shadowsocks-go version", version) } From 2d0b6a36704751763021de8cea6b576a0123f85b Mon Sep 17 00:00:00 2001 From: Eric Fu Date: Sat, 28 Jan 2017 16:55:40 +0800 Subject: [PATCH 60/90] Support Management API and flow statistics --- cmd/shadowsocks-server/server.go | 157 +++++++++++++++++++++++++++++-- shadowsocks/pipe.go | 10 +- 2 files changed, 158 insertions(+), 9 deletions(-) diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index 0477b67b..6d434c57 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -3,6 +3,7 @@ package main import ( "bytes" "encoding/binary" + "encoding/json" "errors" "flag" "fmt" @@ -38,6 +39,7 @@ const ( var debug ss.DebugLog var udp bool +var managerAddr string func getRequest(conn *ss.Conn, auth bool) (host string, ota bool, err error) { ss.SetReadTimeout(conn) @@ -108,7 +110,7 @@ const logCntDelta = 100 var connCnt int var nextLogConnCnt int = logCntDelta -func handleConnection(conn *ss.Conn, auth bool) { +func handleConnection(conn *ss.Conn, auth bool, port string) { var host string connCnt++ // this maybe not accurate, but should be enough @@ -169,11 +171,19 @@ func handleConnection(conn *ss.Conn, auth bool) { debug.Printf("piping %s<->%s ota=%v connOta=%v", conn.RemoteAddr(), host, ota, conn.IsOta()) } if ota { - go ss.PipeThenCloseOta(conn, remote) + go func() { + flow := ss.PipeThenCloseOta(conn, remote) + passwdManager.addFlow(port, flow) + }() } else { - go ss.PipeThenClose(conn, remote) - } - ss.PipeThenClose(remote, conn) + go func() { + flow := ss.PipeThenClose(conn, remote) + passwdManager.addFlow(port, flow) + }() + } + flow := ss.PipeThenClose(remote, conn) + passwdManager.addFlow(port, flow) + closed = true return } @@ -192,11 +202,13 @@ type PasswdManager struct { sync.Mutex portListener map[string]*PortListener udpListener map[string]*UDPListener + flowStats map[string]int64 } func (pm *PasswdManager) add(port, password string, listener net.Listener) { pm.Lock() pm.portListener[port] = &PortListener{password, listener} + pm.flowStats[port] = 0 pm.Unlock() } @@ -235,12 +247,30 @@ func (pm *PasswdManager) del(port string) { pl.listener.Close() pm.Lock() delete(pm.portListener, port) + delete(pm.flowStats, port) if udp { delete(pm.udpListener, port) } pm.Unlock() } +func (pm *PasswdManager) addFlow(port string, n int) { + pm.Lock() + pm.flowStats[port] = pm.flowStats[port] + int64(n) + pm.Unlock() + return +} + +func (pm *PasswdManager) getFlowStats() map[string]int64 { + pm.Lock() + copy := make(map[string]int64) + for k, v := range pm.flowStats { + copy[k] = v + } + pm.Unlock() + return copy +} + // Update port password would first close a port and restart listening on that // port. A different approach would be directly change the password used by // that port, but that requires **sharing** password between the port listener @@ -266,7 +296,11 @@ func (pm *PasswdManager) updatePortPasswd(port, password string, auth bool) { } } -var passwdManager = PasswdManager{portListener: map[string]*PortListener{}, udpListener: map[string]*UDPListener{}} +var passwdManager = PasswdManager{ + portListener: map[string]*PortListener{}, + udpListener: map[string]*UDPListener{}, + flowStats: map[string]int64{}, +} func updatePasswd() { log.Println("updating password") @@ -335,7 +369,7 @@ func run(port, password string, auth bool) { continue } } - go handleConnection(ss.NewConn(conn, cipher.Copy()), auth) + go handleConnection(ss.NewConn(conn, cipher.Copy()), auth, port) } } @@ -405,6 +439,7 @@ func main() { flag.IntVar(&core, "core", 0, "maximum number of CPU cores to use, default is determinied by Go runtime") flag.BoolVar((*bool)(&debug), "d", false, "print debug message") flag.BoolVar(&udp, "u", false, "UDP Relay") + flag.StringVar(&managerAddr, "manager-address", "", "shadowsocks manager listening address") flag.Parse() if printVer { @@ -451,5 +486,113 @@ func main() { } } + if managerAddr != "" { + addr, err := net.ResolveUDPAddr("udp", managerAddr) + if err != nil { + fmt.Fprintln(os.Stderr, "Can't resolve address: ", err) + os.Exit(1) + } + conn, err := net.ListenUDP("udp", addr) + if err != nil { + fmt.Fprintln(os.Stderr, "Error listening:", err) + os.Exit(1) + } + log.Printf("manager listening udp addr %v ...\n", managerAddr) + defer conn.Close() + go managerDaemon(conn) + } + waitSignal() } + +func managerDaemon(conn *net.UDPConn) { + for { + data := make([]byte, 300) + _, remote, err := conn.ReadFromUDP(data) + if err != nil { + fmt.Fprintln(os.Stderr, "Failed to read UDP manage msg, error: ", err.Error()) + continue + } + command := string(data) + var res []byte + switch { + case strings.HasPrefix(command, "add:"): + res = handleAddPort(bytes.Trim(data[4:], "\x00\r\n ")) + case strings.HasPrefix(command, "remove:"): + res = handleRemovePort(bytes.Trim(data[7:], "\x00\r\n ")) + case strings.HasPrefix(command, "ping"): + res = handlePing() + } + if len(res) == 0 { + continue + } + _, err = conn.WriteToUDP(res, remote) + if err != nil { + fmt.Fprintln(os.Stderr, "Failed to write UDP manage msg, error: ", err.Error()) + continue + } + } +} + +func handleAddPort(payload []byte) []byte { + var params struct { + ServerPort interface{} `json:"server_port"` // may be string or int + Password string `json:"password"` + } + json.Unmarshal(payload, ¶ms) + if params.ServerPort == nil || params.Password == "" { + fmt.Fprintln(os.Stderr, "Failed to parse add req: ", string(payload)) + return []byte("err") + } + port := parsePortNum(params.ServerPort) + if port == "" { + return []byte("err") + } + passwdManager.updatePortPasswd(port, params.Password, config.Auth) + return []byte("ok") +} + +func handleRemovePort(payload []byte) []byte { + var params struct { + ServerPort interface{} `json:"server_port"` // may be string or int + } + json.Unmarshal(payload, ¶ms) + if params.ServerPort == nil { + fmt.Fprintln(os.Stderr, "Failed to parse remove req: ", string(payload)) + return []byte("err") + } + port := parsePortNum(params.ServerPort) + if port == "" { + return []byte("err") + } + log.Printf("closing port %s\n", port) + passwdManager.del(port) + return []byte("ok") +} + +func handlePing() []byte { + stats := passwdManager.getFlowStats() + var buf bytes.Buffer + buf.WriteString("stat: ") + ret, _ := json.Marshal(stats) + buf.Write(ret) + return buf.Bytes() +} + +func parsePortNum(in interface{}) string { + var port string + switch in.(type) { + case string: + // try to convert to number then convert back, to ensure valid value + portNum, err := strconv.Atoi(in.(string)) + if portNum == 0 || err != nil { + return "" + } + port = strconv.Itoa(portNum) + case float64: + port = strconv.Itoa(int(in.(float64))) + default: + return "" + } + return port +} \ No newline at end of file diff --git a/shadowsocks/pipe.go b/shadowsocks/pipe.go index ca17c002..f9f7b11e 100644 --- a/shadowsocks/pipe.go +++ b/shadowsocks/pipe.go @@ -15,13 +15,15 @@ func SetReadTimeout(c net.Conn) { } // PipeThenClose copies data from src to dst, closes dst when done. -func PipeThenClose(src, dst net.Conn) { +func PipeThenClose(src, dst net.Conn) int { defer dst.Close() buf := leakyBuf.Get() defer leakyBuf.Put(buf) + var flow int for { SetReadTimeout(src) n, err := src.Read(buf) + flow += n // read may return EOF with n > 0 // should always process n > 0 bytes before handling error if n > 0 { @@ -43,10 +45,11 @@ func PipeThenClose(src, dst net.Conn) { break } } + return flow } // PipeThenClose copies data from src to dst, closes dst when done, with ota verification. -func PipeThenCloseOta(src *Conn, dst net.Conn) { +func PipeThenCloseOta(src *Conn, dst net.Conn) int { const ( dataLenLen = 2 hmacSha1Len = 10 @@ -59,6 +62,7 @@ func PipeThenCloseOta(src *Conn, dst net.Conn) { // sometimes it have to fill large block buf := leakyBuf.Get() defer leakyBuf.Put(buf) + var flow int for i := 1; ; i += 1 { SetReadTimeout(src) if n, err := io.ReadFull(src, buf[:dataLenLen+hmacSha1Len]); err != nil { @@ -84,6 +88,7 @@ func PipeThenCloseOta(src *Conn, dst net.Conn) { Debug.Printf("conn=%p #%v read data error n=%v: %v", src, i, n, err) break } + flow += int(dataLen) chunkIdBytes := make([]byte, 4) chunkId := src.GetAndIncrChunkId() binary.BigEndian.PutUint32(chunkIdBytes, chunkId) @@ -97,4 +102,5 @@ func PipeThenCloseOta(src *Conn, dst net.Conn) { break } } + return flow } From 7d875108c42a54c3e384fb087b86ac8333f2431e Mon Sep 17 00:00:00 2001 From: Eric Fu Date: Tue, 31 Jan 2017 19:07:00 +0800 Subject: [PATCH 61/90] Support flow count for UDP relay --- cmd/shadowsocks-server/server.go | 34 ++++++++++++++++++++++---------- shadowsocks/pipe.go | 14 ++++++------- shadowsocks/udprelay.go | 21 +++++++++++--------- 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index 6d434c57..98ed5e5c 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -172,17 +172,20 @@ func handleConnection(conn *ss.Conn, auth bool, port string) { } if ota { go func() { - flow := ss.PipeThenCloseOta(conn, remote) - passwdManager.addFlow(port, flow) + ss.PipeThenCloseOta(conn, remote, func(flow int){ + passwdManager.addFlow(port, flow) + }) }() } else { go func() { - flow := ss.PipeThenClose(conn, remote) - passwdManager.addFlow(port, flow) + ss.PipeThenClose(conn, remote, func(flow int){ + passwdManager.addFlow(port, flow) + }) }() } - flow := ss.PipeThenClose(remote, conn) - passwdManager.addFlow(port, flow) + ss.PipeThenClose(remote, conn, func(flow int) { + passwdManager.addFlow(port, flow) + }) closed = true return @@ -290,8 +293,16 @@ func (pm *PasswdManager) updatePortPasswd(port, password string, auth bool) { // So there maybe concurrent access to passwdManager and we need lock to protect it. go run(port, password, auth) if udp { - pl, _ := pm.getUDP(port) - pl.listener.Close() + pl, ok := pm.getUDP(port) + if !ok { + log.Printf("new udp port %s added\n", port) + } else { + if pl.password == password { + return + } + log.Printf("closing udp port %s to update password\n", port) + pl.listener.Close() + } go runUDP(port, password, auth) } } @@ -394,8 +405,11 @@ func runUDP(port, password string, auth bool) { } SecurePacketConn := ss.NewSecurePacketConn(conn, cipher.Copy(), auth) for { - if err := ss.ReadAndHandleUDPReq(SecurePacketConn); err != nil { - debug.Println(err) + if err := ss.ReadAndHandleUDPReq(SecurePacketConn, func(flow int) { + passwdManager.addFlow(port, flow) + }); err != nil { + debug.Printf("udp read error: %v\n", err) + return } } } diff --git a/shadowsocks/pipe.go b/shadowsocks/pipe.go index f9f7b11e..95a8e338 100644 --- a/shadowsocks/pipe.go +++ b/shadowsocks/pipe.go @@ -15,15 +15,14 @@ func SetReadTimeout(c net.Conn) { } // PipeThenClose copies data from src to dst, closes dst when done. -func PipeThenClose(src, dst net.Conn) int { +func PipeThenClose(src, dst net.Conn, addFlow func(int)) { defer dst.Close() buf := leakyBuf.Get() defer leakyBuf.Put(buf) - var flow int for { SetReadTimeout(src) n, err := src.Read(buf) - flow += n + addFlow(n) // read may return EOF with n > 0 // should always process n > 0 bytes before handling error if n > 0 { @@ -45,11 +44,11 @@ func PipeThenClose(src, dst net.Conn) int { break } } - return flow + return } // PipeThenClose copies data from src to dst, closes dst when done, with ota verification. -func PipeThenCloseOta(src *Conn, dst net.Conn) int { +func PipeThenCloseOta(src *Conn, dst net.Conn, addFlow func(int)) { const ( dataLenLen = 2 hmacSha1Len = 10 @@ -62,7 +61,6 @@ func PipeThenCloseOta(src *Conn, dst net.Conn) int { // sometimes it have to fill large block buf := leakyBuf.Get() defer leakyBuf.Put(buf) - var flow int for i := 1; ; i += 1 { SetReadTimeout(src) if n, err := io.ReadFull(src, buf[:dataLenLen+hmacSha1Len]); err != nil { @@ -88,7 +86,7 @@ func PipeThenCloseOta(src *Conn, dst net.Conn) int { Debug.Printf("conn=%p #%v read data error n=%v: %v", src, i, n, err) break } - flow += int(dataLen) + addFlow(int(dataLen)) chunkIdBytes := make([]byte, 4) chunkId := src.GetAndIncrChunkId() binary.BigEndian.PutUint32(chunkIdBytes, chunkId) @@ -102,5 +100,5 @@ func PipeThenCloseOta(src *Conn, dst net.Conn) int { break } } - return flow + return } diff --git a/shadowsocks/udprelay.go b/shadowsocks/udprelay.go index 727efb67..b3557b11 100644 --- a/shadowsocks/udprelay.go +++ b/shadowsocks/udprelay.go @@ -130,7 +130,7 @@ func parseHeaderFromAddr(addr net.Addr) ([]byte, int) { return buf[:1+iplen+2], 1 + iplen + 2 } -func Pipeloop(write net.PacketConn, writeAddr net.Addr, readClose net.PacketConn) { +func Pipeloop(write net.PacketConn, writeAddr net.Addr, readClose net.PacketConn, addFlow func(int)) { buf := leakyBuf.Get() defer leakyBuf.Put(buf) defer readClose.Close() @@ -150,15 +150,17 @@ func Pipeloop(write net.PacketConn, writeAddr net.Addr, readClose net.PacketConn } // need improvement here if req, ok := reqList.Get(raddr.String()); ok { - write.WriteTo(append(req, buf[:n]...), writeAddr) + n, _ := write.WriteTo(append(req, buf[:n]...), writeAddr) + addFlow(n) } else { header, hlen := parseHeaderFromAddr(raddr) - write.WriteTo(append(header[:hlen], buf[:n]...), writeAddr) + n, _ := write.WriteTo(append(header[:hlen], buf[:n]...), writeAddr) + addFlow(n) } } } -func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive []byte) { +func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive []byte, addFlow func(int)) { var dstIP net.IP var reqLen int var ota bool @@ -223,9 +225,9 @@ func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive Debug.Printf("[udp]new client %s->%s via %s ota=%v\n", src, dst, remote.LocalAddr(), ota) go func() { if compatiblemode { - Pipeloop(handle.ForceOTA(), src, remote) + Pipeloop(handle.ForceOTA(), src, remote, addFlow) } else { - Pipeloop(handle, src, remote) + Pipeloop(handle, src, remote, addFlow) } natlist.Delete(src.String()) @@ -237,7 +239,8 @@ func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive fmt.Println("WTF") } remote.SetDeadline(time.Now().Add(udpTimeout)) - _, err = remote.WriteTo(receive[reqLen:n], dst) + n, err = remote.WriteTo(receive[reqLen:n], dst) + addFlow(n) if err != nil { if ne, ok := err.(*net.OpError); ok && (ne.Err == syscall.EMFILE || ne.Err == syscall.ENFILE) { // log too many open file error @@ -254,12 +257,12 @@ func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive return } -func ReadAndHandleUDPReq(c *SecurePacketConn) error { +func ReadAndHandleUDPReq(c *SecurePacketConn, addFlow func(int)) error { buf := leakyBuf.Get() n, src, err := c.ReadFrom(buf[0:]) if err != nil { return err } - go handleUDPConnection(c, n, src, buf) + go handleUDPConnection(c, n, src, buf, addFlow) return nil } From d57130f0246f8731cf93cee261c139d67e4668b5 Mon Sep 17 00:00:00 2001 From: Eric Fu Date: Tue, 31 Jan 2017 19:47:30 +0800 Subject: [PATCH 62/90] Fix sslocal --- cmd/shadowsocks-local/local.go | 4 ++-- shadowsocks/pipe.go | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cmd/shadowsocks-local/local.go b/cmd/shadowsocks-local/local.go index 2c76da3a..f3e6603d 100644 --- a/cmd/shadowsocks-local/local.go +++ b/cmd/shadowsocks-local/local.go @@ -325,8 +325,8 @@ func handleConnection(conn net.Conn) { } }() - go ss.PipeThenClose(conn, remote) - ss.PipeThenClose(remote, conn) + go ss.PipeThenClose(conn, remote, nil) + ss.PipeThenClose(remote, conn, nil) closed = true debug.Println("closed connection to", addr) } diff --git a/shadowsocks/pipe.go b/shadowsocks/pipe.go index 95a8e338..c025ccc0 100644 --- a/shadowsocks/pipe.go +++ b/shadowsocks/pipe.go @@ -22,7 +22,9 @@ func PipeThenClose(src, dst net.Conn, addFlow func(int)) { for { SetReadTimeout(src) n, err := src.Read(buf) - addFlow(n) + if addFlow != nil { + addFlow(n) + } // read may return EOF with n > 0 // should always process n > 0 bytes before handling error if n > 0 { From cac0fbac34a095651ec5000190088fd7cc5c2362 Mon Sep 17 00:00:00 2001 From: Yorick Terweijden Date: Sat, 20 Jan 2018 17:30:01 +0100 Subject: [PATCH 63/90] Implements reading the local_address config option --- cmd/shadowsocks-local/local.go | 7 +++---- config.json | 3 ++- shadowsocks/config.go | 13 +++++++------ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/cmd/shadowsocks-local/local.go b/cmd/shadowsocks-local/local.go index 2c76da3a..1a23cea4 100644 --- a/cmd/shadowsocks-local/local.go +++ b/cmd/shadowsocks-local/local.go @@ -355,14 +355,14 @@ func enoughOptions(config *ss.Config) bool { func main() { log.SetOutput(os.Stdout) - var configFile, cmdServer, cmdLocal string + var configFile, cmdServer string var cmdConfig ss.Config var printVer bool flag.BoolVar(&printVer, "version", false, "print version") flag.StringVar(&configFile, "c", "config.json", "specify config file") flag.StringVar(&cmdServer, "s", "", "server address") - flag.StringVar(&cmdLocal, "b", "", "local address, listen only to this address if specified") + flag.StringVar(&cmdConfig.LocalAddress, "b", "", "local address, listen only to this address if specified") flag.StringVar(&cmdConfig.Password, "k", "", "password") flag.IntVar(&cmdConfig.ServerPort, "p", 0, "server port") flag.IntVar(&cmdConfig.Timeout, "t", 300, "timeout in seconds") @@ -425,6 +425,5 @@ func main() { } parseServerConfig(config) - - run(cmdLocal + ":" + strconv.Itoa(config.LocalPort)) + run(config.LocalAddress + ":" + strconv.Itoa(config.LocalPort)) } diff --git a/config.json b/config.json index 35d08271..7bcf08d3 100644 --- a/config.json +++ b/config.json @@ -2,7 +2,8 @@ "server":"127.0.0.1", "server_port":8388, "local_port":1080, + "local_address":"127.0.0.1", "password":"barfoo!", "method": "aes-128-cfb-auth", "timeout":600 -} +} \ No newline at end of file diff --git a/shadowsocks/config.go b/shadowsocks/config.go index 4db50ac2..3de804cb 100644 --- a/shadowsocks/config.go +++ b/shadowsocks/config.go @@ -19,12 +19,13 @@ import ( ) type Config struct { - Server interface{} `json:"server"` - ServerPort int `json:"server_port"` - LocalPort int `json:"local_port"` - Password string `json:"password"` - Method string `json:"method"` // encryption method - Auth bool `json:"auth"` // one time auth + Server interface{} `json:"server"` + ServerPort int `json:"server_port"` + LocalPort int `json:"local_port"` + LocalAddress string `json:"local_address"` + Password string `json:"password"` + Method string `json:"method"` // encryption method + Auth bool `json:"auth"` // one time auth // following options are only used by server PortPassword map[string]string `json:"port_password"` From d477ace774927e579ec52745bfad28d46d9d3f2c Mon Sep 17 00:00:00 2001 From: muzea Date: Mon, 5 Feb 2018 13:19:30 +0800 Subject: [PATCH 64/90] add rc4-md5-6 --- README.md | 2 +- shadowsocks/encrypt.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dfc24f59..902fd6a8 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ server your server ip or hostname server_port server port local_port local socks5 proxy port method encryption method, null by default (table), the following methods are supported: - aes-128-cfb, aes-192-cfb, aes-256-cfb, bf-cfb, cast5-cfb, des-cfb, rc4-md5, chacha20, salsa20, rc4, table + aes-128-cfb, aes-192-cfb, aes-256-cfb, bf-cfb, cast5-cfb, des-cfb, rc4-md5, rc4-md5-6, chacha20, salsa20, rc4, table password a password used to encrypt transfer timeout server option, in seconds ``` diff --git a/shadowsocks/encrypt.go b/shadowsocks/encrypt.go index 44d96a74..8ef13c65 100644 --- a/shadowsocks/encrypt.go +++ b/shadowsocks/encrypt.go @@ -167,6 +167,7 @@ var cipherMethod = map[string]*cipherInfo{ "bf-cfb": {16, 8, newBlowFishStream}, "cast5-cfb": {16, 8, newCast5Stream}, "rc4-md5": {16, 16, newRC4MD5Stream}, + "rc4-md5-6": {16, 6, newRC4MD5Stream}, "chacha20": {32, 8, newChaCha20Stream}, "chacha20-ietf": {32, 12, newChaCha20IETFStream}, "salsa20": {32, 8, newSalsa20Stream}, From d5b9576818eeb37ab61be5a68a8d1f86a05c0e25 Mon Sep 17 00:00:00 2001 From: muzea Date: Mon, 5 Feb 2018 13:25:47 +0800 Subject: [PATCH 65/90] add rc4-md5-6 test --- shadowsocks/encrypt_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/shadowsocks/encrypt_test.go b/shadowsocks/encrypt_test.go index 9a2e6ce6..eebbde35 100644 --- a/shadowsocks/encrypt_test.go +++ b/shadowsocks/encrypt_test.go @@ -95,6 +95,10 @@ func TestRC4MD5(t *testing.T) { testBlockCipher(t, "rc4-md5") } +func TestRC4MD56(t *testing.T) { + testBlockCipher(t, "rc4-md5-6") +} + func TestChaCha20(t *testing.T) { testBlockCipher(t, "chacha20") } @@ -164,6 +168,10 @@ func BenchmarkRC4MD5Init(b *testing.B) { benchmarkCipherInit(b, "rc4-md5") } +func BenchmarkRC4MD56Init(b *testing.B) { + benchmarkCipherInit(b, "rc4-md5-5") +} + func BenchmarkChaCha20Init(b *testing.B) { benchmarkCipherInit(b, "chacha20") } @@ -232,6 +240,10 @@ func BenchmarkRC4MD5Encrypt(b *testing.B) { benchmarkCipherEncrypt(b, "rc4-md5") } +func BenchmarkRC4MD56Encrypt(b *testing.B) { + benchmarkCipherEncrypt(b, "rc4-md5-6") +} + func BenchmarkChacha20Encrypt(b *testing.B) { benchmarkCipherEncrypt(b, "chacha20") } @@ -305,6 +317,10 @@ func BenchmarkRC4MD5Decrypt(b *testing.B) { benchmarkCipherDecrypt(b, "rc4-md5") } +func BenchmarkRC4MD56Decrypt(b *testing.B) { + benchmarkCipherDecrypt(b, "rc4-md5-6") +} + func BenchmarkChaCha20Decrypt(b *testing.B) { benchmarkCipherDecrypt(b, "chacha20") } From 1a456844744a9a48bebf436bef7e62ac3e3267dd Mon Sep 17 00:00:00 2001 From: Gyt Date: Wed, 7 Mar 2018 13:46:54 +0800 Subject: [PATCH 66/90] fix handlePing function --- cmd/shadowsocks-server/server.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index 98ed5e5c..0d645a09 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -585,12 +585,7 @@ func handleRemovePort(payload []byte) []byte { } func handlePing() []byte { - stats := passwdManager.getFlowStats() - var buf bytes.Buffer - buf.WriteString("stat: ") - ret, _ := json.Marshal(stats) - buf.Write(ret) - return buf.Bytes() + return []byte("pong") } func parsePortNum(in interface{}) string { From f0101aad7ceabb2d54251bed667574328184bc96 Mon Sep 17 00:00:00 2001 From: arthur Date: Mon, 12 Mar 2018 11:00:11 +0800 Subject: [PATCH 67/90] add status report code --- cmd/shadowsocks-server/server.go | 50 ++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index 0d645a09..49823ec1 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -17,6 +17,7 @@ import ( "strings" "sync" "syscall" + "time" ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" ) @@ -172,13 +173,13 @@ func handleConnection(conn *ss.Conn, auth bool, port string) { } if ota { go func() { - ss.PipeThenCloseOta(conn, remote, func(flow int){ + ss.PipeThenCloseOta(conn, remote, func(flow int) { passwdManager.addFlow(port, flow) }) }() } else { go func() { - ss.PipeThenClose(conn, remote, func(flow int){ + ss.PipeThenClose(conn, remote, func(flow int) { passwdManager.addFlow(port, flow) }) }() @@ -186,7 +187,7 @@ func handleConnection(conn *ss.Conn, auth bool, port string) { ss.PipeThenClose(remote, conn, func(flow int) { passwdManager.addFlow(port, flow) }) - + closed = true return } @@ -520,6 +521,30 @@ func main() { } func managerDaemon(conn *net.UDPConn) { + // add a report address set for ping response + // according to https://github.com/shadowsocks/shadowsocks/wiki/Manage-Multiple-Users#example-code + ctx := make(chan bool, 1) + defer close(ctx) + reportconnSet := make(map[string]*net.UDPAddr, 1024) + go func() { + timer := time.Tick(10 * time.Second) + for { + <-timer + switch { + case <-ctx: + return + default: + for _, addr := range reportconnSet { + res := reportStat() + if len(res) == 0 { + continue + } + conn.WriteToUDP(res, addr) + } + } + } + }() + for { data := make([]byte, 300) _, remote, err := conn.ReadFromUDP(data) @@ -535,7 +560,11 @@ func managerDaemon(conn *net.UDPConn) { case strings.HasPrefix(command, "remove:"): res = handleRemovePort(bytes.Trim(data[7:], "\x00\r\n ")) case strings.HasPrefix(command, "ping"): - res = handlePing() + conn.WriteToUDP(handlePing(), remote) + reportconnSet[remote.String()] = remote // append the host into the report list + case strings.HasPrefix(command, "ping-stop"): // add the stop ping command + conn.WriteToUDP(handlePing(), remote) + delete(reportconnSet, remote.String()) } if len(res) == 0 { continue @@ -588,6 +617,17 @@ func handlePing() []byte { return []byte("pong") } +// reportStat get the stat:flowStat and return avery 10 sec as for the protocol +// https://github.com/shadowsocks/shadowsocks/wiki/Manage-Multiple-Users +func reportStat() []byte { + stats := passwdManager.getFlowStats() + var buf bytes.Buffer + buf.WriteString("stat: ") + ret, _ := json.Marshal(stats) + buf.Write(ret) + return buf.Bytes() +} + func parsePortNum(in interface{}) string { var port string switch in.(type) { @@ -604,4 +644,4 @@ func parsePortNum(in interface{}) string { return "" } return port -} \ No newline at end of file +} From 3b41f4e61a4cc9b4a50d7b4ee09c41d5cd4425d3 Mon Sep 17 00:00:00 2001 From: Abel Luck Date: Wed, 14 Feb 2018 12:56:36 +0200 Subject: [PATCH 68/90] Add server option to disable logging of client ips fixes #265 --- cmd/shadowsocks-server/server.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index 49823ec1..8914d11f 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -39,6 +39,7 @@ const ( ) var debug ss.DebugLog +var sanitizeIps bool var udp bool var managerAddr string @@ -111,6 +112,14 @@ const logCntDelta = 100 var connCnt int var nextLogConnCnt int = logCntDelta +func sanitizeAddr(addr net.Addr) string { + if sanitizeIps { + return "x.x.x.x:zzzz" + } else { + return addr.String() + } +} + func handleConnection(conn *ss.Conn, auth bool, port string) { var host string @@ -126,12 +135,12 @@ func handleConnection(conn *ss.Conn, auth bool, port string) { // function arguments are always evaluated, so surround debug statement // with if statement if debug { - debug.Printf("new client %s->%s\n", conn.RemoteAddr().String(), conn.LocalAddr()) + debug.Printf("new client %s->%s\n", sanitizeAddr(conn.RemoteAddr()), conn.LocalAddr()) } closed := false defer func() { if debug { - debug.Printf("closed pipe %s<->%s\n", conn.RemoteAddr(), host) + debug.Printf("closed pipe %s<->%s\n", sanitizeAddr(conn.RemoteAddr()), host) } connCnt-- if !closed { @@ -141,7 +150,7 @@ func handleConnection(conn *ss.Conn, auth bool, port string) { host, ota, err := getRequest(conn, auth) if err != nil { - log.Println("error getting request", conn.RemoteAddr(), conn.LocalAddr(), err) + log.Println("error getting request", sanitizeAddr(conn.RemoteAddr()), conn.LocalAddr(), err) closed = true return } @@ -169,7 +178,7 @@ func handleConnection(conn *ss.Conn, auth bool, port string) { } }() if debug { - debug.Printf("piping %s<->%s ota=%v connOta=%v", conn.RemoteAddr(), host, ota, conn.IsOta()) + debug.Printf("piping %s<->%s ota=%v connOta=%v", sanitizeAddr(conn.RemoteAddr()), host, ota, conn.IsOta()) } if ota { go func() { @@ -453,6 +462,7 @@ func main() { flag.StringVar(&cmdConfig.Method, "m", "", "encryption method, default: aes-256-cfb") flag.IntVar(&core, "core", 0, "maximum number of CPU cores to use, default is determinied by Go runtime") flag.BoolVar((*bool)(&debug), "d", false, "print debug message") + flag.BoolVar((*bool)(&sanitizeIps), "noip-log", false, "suppress client ip addresses in all output") flag.BoolVar(&udp, "u", false, "UDP Relay") flag.StringVar(&managerAddr, "manager-address", "", "shadowsocks manager listening address") flag.Parse() From fa26b00f0d981a8ffda1640f2d7e02de8a92a600 Mon Sep 17 00:00:00 2001 From: Abel Luck Date: Mon, 26 Mar 2018 10:34:18 +0200 Subject: [PATCH 69/90] change sanitize ip flag to -A --- cmd/shadowsocks-server/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index 8914d11f..7a296a51 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -462,7 +462,7 @@ func main() { flag.StringVar(&cmdConfig.Method, "m", "", "encryption method, default: aes-256-cfb") flag.IntVar(&core, "core", 0, "maximum number of CPU cores to use, default is determinied by Go runtime") flag.BoolVar((*bool)(&debug), "d", false, "print debug message") - flag.BoolVar((*bool)(&sanitizeIps), "noip-log", false, "suppress client ip addresses in all output") + flag.BoolVar((*bool)(&sanitizeIps), "A", false, "anonymize client ip addresses in all output") flag.BoolVar(&udp, "u", false, "UDP Relay") flag.StringVar(&managerAddr, "manager-address", "", "shadowsocks manager listening address") flag.Parse() From 6db716930ecd2d01c56cad8b1e869307ec383e1a Mon Sep 17 00:00:00 2001 From: halulu Date: Mon, 21 May 2018 22:18:10 +0800 Subject: [PATCH 70/90] correct misspell --- cmd/shadowsocks-local/local.go | 4 ++-- cmd/shadowsocks-server/server.go | 2 +- shadowsocks/config.go | 2 +- shadowsocks/pipe.go | 2 +- shadowsocks/udprelay.go | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/shadowsocks-local/local.go b/cmd/shadowsocks-local/local.go index 444b2415..0d6e1559 100644 --- a/cmd/shadowsocks-local/local.go +++ b/cmd/shadowsocks-local/local.go @@ -80,7 +80,7 @@ func getRequest(conn net.Conn) (rawaddr []byte, host string, err error) { idVer = 0 idCmd = 1 idType = 3 // address type index - idIP0 = 4 // ip addres start index + idIP0 = 4 // ip address start index idDmLen = 4 // domain address length index idDm0 = 5 // domain address start index @@ -315,7 +315,7 @@ func handleConnection(conn net.Conn) { remote, err := createServerConn(rawaddr, addr) if err != nil { if len(servers.srvCipher) > 1 { - log.Println("Failed connect to all avaiable shadowsocks server") + log.Println("Failed connect to all available shadowsocks server") } return } diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index 7a296a51..d253aeb3 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -24,7 +24,7 @@ import ( const ( idType = 0 // address type index - idIP0 = 1 // ip addres start index + idIP0 = 1 // ip address start index idDmLen = 1 // domain address length index idDm0 = 2 // domain address start index diff --git a/shadowsocks/config.go b/shadowsocks/config.go index 3de804cb..347d7a5b 100644 --- a/shadowsocks/config.go +++ b/shadowsocks/config.go @@ -42,7 +42,7 @@ var readTimeout time.Duration func (config *Config) GetServerArray() []string { // Specifying multiple servers in the "server" options is deprecated. - // But for backward compatiblity, keep this. + // But for backward compatibility, keep this. if config.Server == nil { return nil } diff --git a/shadowsocks/pipe.go b/shadowsocks/pipe.go index c025ccc0..4528e25a 100644 --- a/shadowsocks/pipe.go +++ b/shadowsocks/pipe.go @@ -94,7 +94,7 @@ func PipeThenCloseOta(src *Conn, dst net.Conn, addFlow func(int)) { binary.BigEndian.PutUint32(chunkIdBytes, chunkId) actualHmacSha1 := HmacSha1(append(src.GetIv(), chunkIdBytes...), dataBuf) if !bytes.Equal(expectedHmacSha1, actualHmacSha1) { - 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) + Debug.Printf("conn=%p #%v read data hmac-sha1 mismatch, iv=%v chunkId=%v src=%v dst=%v len=%v expected=%v actual=%v", src, i, src.GetIv(), chunkId, src.RemoteAddr(), dst.RemoteAddr(), dataLen, expectedHmacSha1, actualHmacSha1) break } if n, err := dst.Write(dataBuf); err != nil { diff --git a/shadowsocks/udprelay.go b/shadowsocks/udprelay.go index b3557b11..c3586b84 100644 --- a/shadowsocks/udprelay.go +++ b/shadowsocks/udprelay.go @@ -13,7 +13,7 @@ import ( const ( idType = 0 // address type index - idIP0 = 1 // ip addres start index + idIP0 = 1 // ip address start index idDmLen = 1 // domain address length index idDm0 = 2 // domain address start index From d394e710b90baa588286a59d0f36ee17de582a79 Mon Sep 17 00:00:00 2001 From: dylanchu Date: Sun, 10 Jun 2018 08:52:51 +0800 Subject: [PATCH 71/90] deprecate OTA function and clean up related codes --- .gitignore | 1 + README.md | 13 +----- cmd/shadowsocks-local/local.go | 13 +----- cmd/shadowsocks-server/server.go | 72 ++++++++++---------------------- config.json | 2 +- script/build.sh | 2 +- script/test.sh | 2 +- shadowsocks/config.go | 6 --- shadowsocks/conn.go | 41 +----------------- shadowsocks/encrypt.go | 12 ------ shadowsocks/pipe.go | 59 -------------------------- shadowsocks/udp.go | 43 +------------------ shadowsocks/udprelay.go | 22 +++------- shadowsocks/util.go | 14 ------- 14 files changed, 37 insertions(+), 265 deletions(-) diff --git a/.gitignore b/.gitignore index b4278f4b..a53134e0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.deb script/http bin +.idea diff --git a/README.md b/README.md index 902fd6a8..a050464d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # shadowsocks-go -Current version: 1.2.1 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=master)](https://travis-ci.org/shadowsocks/shadowsocks-go) +Current version: 1.2.2 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=master)](https://travis-ci.org/shadowsocks/shadowsocks-go) 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). @@ -57,16 +57,7 @@ AES is recommended for shadowsocks-go. [Intel AES Instruction Set](http://en.wik ### One Time Auth -Append `-auth` to the encryption method to enable [One Time Auth (OTA)](https://shadowsocks.org/en/spec/one-time-auth.html). - -- For server: this will **force client use OTA**, non-OTA connection will be dropped. Otherwise, both OTA and non-OTA clients can connect -- For client: the `-A` command line option can also enable OTA - -### UDP relay - -Use `-u` command line options when starting server to enable UDP relay. - -Currently only tested with Shadowsocks-Android, if you have encountered any problem, please report. +OTA function is deprecated because it is reported to have potential security risk. ## Command line options diff --git a/cmd/shadowsocks-local/local.go b/cmd/shadowsocks-local/local.go index 0d6e1559..4533500f 100644 --- a/cmd/shadowsocks-local/local.go +++ b/cmd/shadowsocks-local/local.go @@ -12,7 +12,6 @@ import ( "os" "path" "strconv" - "strings" "time" ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" @@ -172,12 +171,8 @@ func parseServerConfig(config *ss.Config) { } if len(config.ServerPassword) == 0 { - method := config.Method - if config.Auth { - method += "-auth" - } // only one encryption table - cipher, err := ss.NewCipher(method, config.Password) + cipher, err := ss.NewCipher(config.Method, config.Password) if err != nil { log.Fatal("Failed generating ciphers:", err) } @@ -369,7 +364,6 @@ func main() { flag.IntVar(&cmdConfig.LocalPort, "l", 0, "local socks5 proxy port") flag.StringVar(&cmdConfig.Method, "m", "", "encryption method, default: aes-256-cfb") flag.BoolVar((*bool)(&debug), "d", false, "print debug message") - flag.BoolVar(&cmdConfig.Auth, "A", false, "one time auth") flag.Parse() @@ -381,11 +375,6 @@ func main() { cmdConfig.Server = cmdServer ss.SetDebug(debug) - if strings.HasSuffix(cmdConfig.Method, "-auth") { - cmdConfig.Method = cmdConfig.Method[:len(cmdConfig.Method)-5] - cmdConfig.Auth = true - } - exists, err := ss.IsFileExists(configFile) // If no config file in current directory, try search it in the binary directory // Note there's no portable way to detect the binary directory. diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index d253aeb3..624f908e 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -35,7 +35,7 @@ const ( lenIPv4 = net.IPv4len + 2 // ipv4 + 2port lenIPv6 = net.IPv6len + 2 // ipv6 + 2port lenDmBase = 2 // 1addrLen + 2port, plus addrLen - lenHmacSha1 = 10 + // lenHmacSha1 = 10 ) var debug ss.DebugLog @@ -43,7 +43,7 @@ var sanitizeIps bool var udp bool var managerAddr string -func getRequest(conn *ss.Conn, auth bool) (host string, ota bool, err error) { +func getRequest(conn *ss.Conn) (host string, err error) { ss.SetReadTimeout(conn) // buf size should at least have the same size with the largest possible @@ -90,20 +90,6 @@ func getRequest(conn *ss.Conn, auth bool) (host string, ota bool, err error) { // parse port port := binary.BigEndian.Uint16(buf[reqEnd-2 : reqEnd]) host = net.JoinHostPort(host, strconv.Itoa(int(port))) - // if specified one time auth enabled, we should verify this - if auth || addrType&ss.OneTimeAuthMask > 0 { - ota = true - if _, err = io.ReadFull(conn, buf[reqEnd:reqEnd+lenHmacSha1]); err != nil { - return - } - iv := conn.GetIv() - key := conn.GetKey() - actualHmacSha1Buf := ss.HmacSha1(append(iv, key...), buf[:reqEnd]) - if !bytes.Equal(buf[reqEnd:reqEnd+lenHmacSha1], actualHmacSha1Buf) { - err = fmt.Errorf("verify one time auth failed, iv=%v key=%v data=%v", iv, key, buf[:reqEnd]) - return - } - } return } @@ -120,7 +106,7 @@ func sanitizeAddr(addr net.Addr) string { } } -func handleConnection(conn *ss.Conn, auth bool, port string) { +func handleConnection(conn *ss.Conn, port string) { var host string connCnt++ // this maybe not accurate, but should be enough @@ -148,7 +134,7 @@ func handleConnection(conn *ss.Conn, auth bool, port string) { } }() - host, ota, err := getRequest(conn, auth) + host, err := getRequest(conn) if err != nil { log.Println("error getting request", sanitizeAddr(conn.RemoteAddr()), conn.LocalAddr(), err) closed = true @@ -178,21 +164,14 @@ func handleConnection(conn *ss.Conn, auth bool, port string) { } }() if debug { - debug.Printf("piping %s<->%s ota=%v connOta=%v", sanitizeAddr(conn.RemoteAddr()), host, ota, conn.IsOta()) - } - if ota { - go func() { - ss.PipeThenCloseOta(conn, remote, func(flow int) { - passwdManager.addFlow(port, flow) - }) - }() - } else { - go func() { - ss.PipeThenClose(conn, remote, func(flow int) { - passwdManager.addFlow(port, flow) - }) - }() + debug.Printf("piping %s<->%s", sanitizeAddr(conn.RemoteAddr()), host) } + go func() { + ss.PipeThenClose(conn, remote, func(flow int) { + passwdManager.addFlow(port, flow) + }) + }() + ss.PipeThenClose(remote, conn, func(flow int) { passwdManager.addFlow(port, flow) }) @@ -288,7 +267,7 @@ func (pm *PasswdManager) getFlowStats() map[string]int64 { // port. A different approach would be directly change the password used by // that port, but that requires **sharing** password between the port listener // and password manager. -func (pm *PasswdManager) updatePortPasswd(port, password string, auth bool) { +func (pm *PasswdManager) updatePortPasswd(port, password string) { pl, ok := pm.get(port) if !ok { log.Printf("new port %s added\n", port) @@ -301,7 +280,7 @@ func (pm *PasswdManager) updatePortPasswd(port, password string, auth bool) { } // run will add the new port listener to passwdManager. // So there maybe concurrent access to passwdManager and we need lock to protect it. - go run(port, password, auth) + go run(port, password) if udp { pl, ok := pm.getUDP(port) if !ok { @@ -313,7 +292,7 @@ func (pm *PasswdManager) updatePortPasswd(port, password string, auth bool) { log.Printf("closing udp port %s to update password\n", port) pl.listener.Close() } - go runUDP(port, password, auth) + go runUDP(port, password) } } @@ -337,13 +316,13 @@ func updatePasswd() { return } for port, passwd := range config.PortPassword { - passwdManager.updatePortPasswd(port, passwd, config.Auth) + passwdManager.updatePortPasswd(port, passwd) if oldconfig.PortPassword != nil { delete(oldconfig.PortPassword, port) } } // port password still left in the old config should be closed - for port, _ := range oldconfig.PortPassword { + for port := range oldconfig.PortPassword { log.Printf("closing port %s as it's deleted\n", port) passwdManager.del(port) } @@ -364,7 +343,7 @@ func waitSignal() { } } -func run(port, password string, auth bool) { +func run(port, password string) { ln, err := net.Listen("tcp", ":"+port) if err != nil { log.Printf("error listening port %v: %v\n", port, err) @@ -390,11 +369,11 @@ func run(port, password string, auth bool) { continue } } - go handleConnection(ss.NewConn(conn, cipher.Copy()), auth, port) + go handleConnection(ss.NewConn(conn, cipher.Copy()), port) } } -func runUDP(port, password string, auth bool) { +func runUDP(port, password string) { var cipher *ss.Cipher port_i, _ := strconv.Atoi(port) log.Printf("listening udp port %v\n", port) @@ -413,7 +392,7 @@ func runUDP(port, password string, auth bool) { log.Printf("Error generating cipher for udp port: %s %v\n", port, err) conn.Close() } - SecurePacketConn := ss.NewSecurePacketConn(conn, cipher.Copy(), auth) + SecurePacketConn := ss.NewSecurePacketConn(conn, cipher.Copy()) for { if err := ss.ReadAndHandleUDPReq(SecurePacketConn, func(flow int) { passwdManager.addFlow(port, flow) @@ -474,11 +453,6 @@ func main() { ss.SetDebug(debug) - if strings.HasSuffix(cmdConfig.Method, "-auth") { - cmdConfig.Method = cmdConfig.Method[:len(cmdConfig.Method)-5] - cmdConfig.Auth = true - } - var err error config, err = ss.ParseConfig(configFile) if err != nil { @@ -505,9 +479,9 @@ func main() { runtime.GOMAXPROCS(core) } for port, password := range config.PortPassword { - go run(port, password, config.Auth) + go run(port, password) if udp { - go runUDP(port, password, config.Auth) + go runUDP(port, password) } } @@ -601,7 +575,7 @@ func handleAddPort(payload []byte) []byte { if port == "" { return []byte("err") } - passwdManager.updatePortPasswd(port, params.Password, config.Auth) + passwdManager.updatePortPasswd(port, params.Password) return []byte("ok") } diff --git a/config.json b/config.json index 7bcf08d3..9198c2c9 100644 --- a/config.json +++ b/config.json @@ -4,6 +4,6 @@ "local_port":1080, "local_address":"127.0.0.1", "password":"barfoo!", - "method": "aes-128-cfb-auth", + "method": "aes-128-cfb", "timeout":600 } \ No newline at end of file diff --git a/script/build.sh b/script/build.sh index 14a4cd27..c8de1d8a 100755 --- a/script/build.sh +++ b/script/build.sh @@ -56,6 +56,6 @@ build windows amd64 win64 server build windows 386 win32 server #script/createdeb.sh amd64 -#script/createdeb.sh 386 +#script/createdeb.sh i386 #mv shadowsocks-go_$version-1-*.deb bin/ #rm -rf shadowsocks-go_$version-1* diff --git a/script/test.sh b/script/test.sh index 1f99481b..557f10c6 100755 --- a/script/test.sh +++ b/script/test.sh @@ -74,7 +74,7 @@ test_shadowsocks() { server_pid=$! wait_server $SERVER_PORT - $LOCAL $OPTION -s 127.0.0.1 -l $LOCAL_PORT -m "$method" -A & + $LOCAL $OPTION -s 127.0.0.1 -l $LOCAL_PORT -m "$method" & local_pid=$! wait_server $LOCAL_PORT diff --git a/shadowsocks/config.go b/shadowsocks/config.go index 347d7a5b..00e409bb 100644 --- a/shadowsocks/config.go +++ b/shadowsocks/config.go @@ -14,7 +14,6 @@ import ( // "log" "os" "reflect" - "strings" "time" ) @@ -25,7 +24,6 @@ type Config struct { LocalAddress string `json:"local_address"` Password string `json:"password"` Method string `json:"method"` // encryption method - Auth bool `json:"auth"` // one time auth // following options are only used by server PortPassword map[string]string `json:"port_password"` @@ -88,10 +86,6 @@ func ParseConfig(path string) (config *Config, err error) { return nil, err } readTimeout = time.Duration(config.Timeout) * time.Second - if strings.HasSuffix(strings.ToLower(config.Method), "-auth") { - config.Method = config.Method[:len(config.Method)-5] - config.Auth = true - } return } diff --git a/shadowsocks/conn.go b/shadowsocks/conn.go index 1a79f1d1..bd8ff5b1 100644 --- a/shadowsocks/conn.go +++ b/shadowsocks/conn.go @@ -9,7 +9,6 @@ import ( ) const ( - OneTimeAuthMask byte = 0x10 AddrMask byte = 0xf ) @@ -18,7 +17,6 @@ type Conn struct { *Cipher readBuf []byte writeBuf []byte - chunkId uint32 } func NewConn(c net.Conn, cipher *Cipher) *Conn { @@ -64,18 +62,7 @@ func DialWithRawAddr(rawaddr []byte, server string, cipher *Cipher) (c *Conn, er return } c = NewConn(conn, cipher) - if cipher.ota { - if c.enc == nil { - if _, err = c.initEncrypt(); err != nil { - return - } - } - // since we have initEncrypt, we must send iv manually - conn.Write(cipher.iv) - rawaddr[0] |= OneTimeAuthMask - rawaddr = otaConnectAuth(cipher.iv, cipher.key, rawaddr) - } - if _, err = c.write(rawaddr); err != nil { + if _, err = c.Write(rawaddr); err != nil { c.Close() return nil, err } @@ -103,16 +90,6 @@ func (c *Conn) GetKey() (key []byte) { return } -func (c *Conn) IsOta() bool { - return c.ota -} - -func (c *Conn) GetAndIncrChunkId() (chunkId uint32) { - chunkId = c.chunkId - c.chunkId += 1 - return -} - func (c *Conn) Read(b []byte) (n int, err error) { if c.dec == nil { iv := make([]byte, c.info.ivLen) @@ -142,22 +119,6 @@ func (c *Conn) Read(b []byte) (n int, err error) { } func (c *Conn) Write(b []byte) (n int, err error) { - nn := len(b) - if c.ota { - chunkId := c.GetAndIncrChunkId() - b = otaReqChunkAuth(c.iv, chunkId, b) - } - headerLen := len(b) - nn - - n, err = c.write(b) - // Make sure <= 0 <= len(b), where b is the slice passed in. - if n >= headerLen { - n -= headerLen - } - return -} - -func (c *Conn) write(b []byte) (n int, err error) { var iv []byte if c.enc == nil { iv, err = c.initEncrypt() diff --git a/shadowsocks/encrypt.go b/shadowsocks/encrypt.go index 8ef13c65..e13002da 100644 --- a/shadowsocks/encrypt.go +++ b/shadowsocks/encrypt.go @@ -10,8 +10,6 @@ import ( "encoding/binary" "errors" "io" - "strings" - "github.com/Yawning/chacha20" "golang.org/x/crypto/blowfish" "golang.org/x/crypto/cast5" @@ -189,7 +187,6 @@ type Cipher struct { dec cipher.Stream key []byte info *cipherInfo - ota bool // one-time auth iv []byte } @@ -200,13 +197,6 @@ func NewCipher(method, password string) (c *Cipher, err error) { if password == "" { return nil, errEmptyPassword } - var ota bool - if strings.HasSuffix(strings.ToLower(method), "-auth") { - method = method[:len(method)-5] // len("-auth") = 5 - ota = true - } else { - ota = false - } mi, ok := cipherMethod[method] if !ok { return nil, errors.New("Unsupported encryption method: " + method) @@ -219,7 +209,6 @@ func NewCipher(method, password string) (c *Cipher, err error) { if err != nil { return nil, err } - c.ota = ota return c, nil } @@ -269,6 +258,5 @@ func (c *Cipher) Copy() *Cipher { nc := *c nc.enc = nil nc.dec = nil - nc.ota = c.ota return &nc } diff --git a/shadowsocks/pipe.go b/shadowsocks/pipe.go index 4528e25a..f49f4c3e 100644 --- a/shadowsocks/pipe.go +++ b/shadowsocks/pipe.go @@ -1,9 +1,6 @@ package shadowsocks import ( - "bytes" - "encoding/binary" - "io" "net" "time" ) @@ -48,59 +45,3 @@ func PipeThenClose(src, dst net.Conn, addFlow func(int)) { } return } - -// PipeThenClose copies data from src to dst, closes dst when done, with ota verification. -func PipeThenCloseOta(src *Conn, dst net.Conn, addFlow func(int)) { - const ( - dataLenLen = 2 - hmacSha1Len = 10 - idxData0 = dataLenLen + hmacSha1Len - ) - - defer func() { - dst.Close() - }() - // sometimes it have to fill large block - buf := leakyBuf.Get() - defer leakyBuf.Put(buf) - for i := 1; ; i += 1 { - SetReadTimeout(src) - if n, err := io.ReadFull(src, buf[:dataLenLen+hmacSha1Len]); err != nil { - if err == io.EOF { - break - } - Debug.Printf("conn=%p #%v read header error n=%v: %v", src, i, n, err) - break - } - dataLen := binary.BigEndian.Uint16(buf[:dataLenLen]) - expectedHmacSha1 := buf[dataLenLen:idxData0] - - var dataBuf []byte - if len(buf) < int(idxData0+dataLen) { - dataBuf = make([]byte, dataLen) - } else { - dataBuf = buf[idxData0 : idxData0+dataLen] - } - if n, err := io.ReadFull(src, dataBuf); err != nil { - if err == io.EOF { - break - } - Debug.Printf("conn=%p #%v read data error n=%v: %v", src, i, n, err) - break - } - addFlow(int(dataLen)) - chunkIdBytes := make([]byte, 4) - chunkId := src.GetAndIncrChunkId() - binary.BigEndian.PutUint32(chunkIdBytes, chunkId) - actualHmacSha1 := HmacSha1(append(src.GetIv(), chunkIdBytes...), dataBuf) - if !bytes.Equal(expectedHmacSha1, actualHmacSha1) { - Debug.Printf("conn=%p #%v read data hmac-sha1 mismatch, iv=%v chunkId=%v src=%v dst=%v len=%v expected=%v actual=%v", src, i, src.GetIv(), chunkId, src.RemoteAddr(), dst.RemoteAddr(), dataLen, expectedHmacSha1, actualHmacSha1) - break - } - if n, err := dst.Write(dataBuf); err != nil { - Debug.Printf("conn=%p #%v write data error n=%v: %v", dst, i, n, err) - break - } - } - return -} diff --git a/shadowsocks/udp.go b/shadowsocks/udp.go index 62a55d84..911ed6d4 100644 --- a/shadowsocks/udp.go +++ b/shadowsocks/udp.go @@ -1,7 +1,6 @@ package shadowsocks import ( - "bytes" "fmt" "net" "time" @@ -15,20 +14,17 @@ var ( errPacketTooSmall = fmt.Errorf("[udp]read error: cannot decrypt, received packet is smaller than ivLen") errPacketTooLarge = fmt.Errorf("[udp]read error: received packet is latger than maxPacketSize(%d)", maxPacketSize) errBufferTooSmall = fmt.Errorf("[udp]read error: given buffer is too small to hold data") - errPacketOtaFailed = fmt.Errorf("[udp]read error: received packet has invalid ota") ) type SecurePacketConn struct { net.PacketConn *Cipher - ota bool } -func NewSecurePacketConn(c net.PacketConn, cipher *Cipher, ota bool) *SecurePacketConn { +func NewSecurePacketConn(c net.PacketConn, cipher *Cipher) *SecurePacketConn { return &SecurePacketConn{ PacketConn: c, Cipher: cipher, - ota: ota, } } @@ -37,7 +33,6 @@ func (c *SecurePacketConn) Close() error { } func (c *SecurePacketConn) ReadFrom(b []byte) (n int, src net.Addr, err error) { - ota := false cipher := c.Copy() buf := make([]byte, 4096) n, src, err = c.PacketConn.ReadFrom(buf) @@ -62,23 +57,6 @@ func (c *SecurePacketConn) ReadFrom(b []byte) (n int, src net.Addr, err error) { cipher.decrypt(b[0:], buf[c.info.ivLen:n]) n -= c.info.ivLen - if b[idType]&OneTimeAuthMask > 0 { - ota = true - } - - if c.ota && !ota { - return 0, src, errPacketOtaFailed - } - - if ota { - key := cipher.key - actualHmacSha1Buf := HmacSha1(append(iv, key...), b[:n-lenHmacSha1]) - if !bytes.Equal(b[n-lenHmacSha1:n], actualHmacSha1Buf) { - Debug.Printf("verify one time auth failed, iv=%v key=%v data=%v", iv, key, b) - return 0, src, errPacketOtaFailed - } - n -= lenHmacSha1 - } return } @@ -91,22 +69,11 @@ func (c *SecurePacketConn) WriteTo(b []byte, dst net.Addr) (n int, err error) { } packetLen := len(b) + len(iv) - if c.ota { - b[idType] |= OneTimeAuthMask - packetLen += lenHmacSha1 - key := cipher.key - actualHmacSha1Buf := HmacSha1(append(iv, key...), b) - b = append(b, actualHmacSha1Buf...) - } - cipherData := make([]byte, packetLen) copy(cipherData, iv) cipher.encrypt(cipherData[len(iv):], b) n, err = c.PacketConn.WriteTo(cipherData, dst) - if c.ota { - n -= lenHmacSha1 - } return } @@ -125,11 +92,3 @@ func (c *SecurePacketConn) SetReadDeadline(t time.Time) error { func (c *SecurePacketConn) SetWriteDeadline(t time.Time) error { return c.PacketConn.SetWriteDeadline(t) } - -func (c *SecurePacketConn) IsOta() bool { - return c.ota -} - -func (c *SecurePacketConn) ForceOTA() net.PacketConn { - return NewSecurePacketConn(c.PacketConn, c.Cipher.Copy(), true) -} diff --git a/shadowsocks/udprelay.go b/shadowsocks/udprelay.go index c3586b84..033812b7 100644 --- a/shadowsocks/udprelay.go +++ b/shadowsocks/udprelay.go @@ -24,7 +24,7 @@ const ( lenIPv4 = 1 + net.IPv4len + 2 // 1addrType + ipv4 + 2port lenIPv6 = 1 + net.IPv6len + 2 // 1addrType + ipv6 + 2port lenDmBase = 1 + 1 + 2 // 1addrType + 1addrLen + 2port, plus addrLen - lenHmacSha1 = 10 + // lenHmacSha1 = 10 ) var ( @@ -87,7 +87,7 @@ func newReqList() *requestHeaderList { func (r *requestHeaderList) Refresh() { r.Lock() defer r.Unlock() - for k, _ := range r.List { + for k := range r.List { delete(r.List, k) } } @@ -163,16 +163,9 @@ func Pipeloop(write net.PacketConn, writeAddr net.Addr, readClose net.PacketConn func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive []byte, addFlow func(int)) { var dstIP net.IP var reqLen int - var ota bool addrType := receive[idType] defer leakyBuf.Put(receive) - if addrType&OneTimeAuthMask > 0 { - ota = true - } - receive[idType] &= ^OneTimeAuthMask - compatiblemode := !handle.IsOta() && ota - switch addrType & AddrMask { case typeIPv4: reqLen = lenIPv4 @@ -222,18 +215,13 @@ func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive return } if !exist { - Debug.Printf("[udp]new client %s->%s via %s ota=%v\n", src, dst, remote.LocalAddr(), ota) + Debug.Printf("[udp]new client %s->%s via %s\n", src, dst, remote.LocalAddr()) go func() { - if compatiblemode { - Pipeloop(handle.ForceOTA(), src, remote, addFlow) - } else { - Pipeloop(handle, src, remote, addFlow) - } - + Pipeloop(handle, src, remote, addFlow) natlist.Delete(src.String()) }() } else { - Debug.Printf("[udp]using cached client %s->%s via %s ota=%v\n", src, dst, remote.LocalAddr(), ota) + Debug.Printf("[udp]using cached client %s->%s via %s\n", src, dst, remote.LocalAddr()) } if remote == nil { fmt.Println("WTF") diff --git a/shadowsocks/util.go b/shadowsocks/util.go index 7ce7a6bd..1a91e84a 100644 --- a/shadowsocks/util.go +++ b/shadowsocks/util.go @@ -3,7 +3,6 @@ package shadowsocks import ( "crypto/hmac" "crypto/sha1" - "encoding/binary" "errors" "fmt" "os" @@ -34,19 +33,6 @@ func HmacSha1(key []byte, data []byte) []byte { return hmacSha1.Sum(nil)[:10] } -func otaConnectAuth(iv, key, data []byte) []byte { - return append(data, HmacSha1(append(iv, key...), data)...) -} - -func otaReqChunkAuth(iv []byte, chunkId uint32, data []byte) []byte { - nb := make([]byte, 2) - binary.BigEndian.PutUint16(nb, uint16(len(data))) - chunkIdBytes := make([]byte, 4) - binary.BigEndian.PutUint32(chunkIdBytes, chunkId) - header := append(nb, HmacSha1(append(iv, chunkIdBytes...), data)...) - return append(header, data...) -} - type ClosedFlag struct { flag bool } From 1801975a28fc330a5168bc56f85dfdc810301eef Mon Sep 17 00:00:00 2001 From: dylanchu Date: Sun, 10 Jun 2018 09:30:13 +0800 Subject: [PATCH 72/90] code style related clear up --- cmd/shadowsocks-server/server.go | 34 ++++++++++++++++---------------- shadowsocks/encrypt_test.go | 6 +++--- shadowsocks/mergesort.go | 6 +++--- shadowsocks/pipe.go | 6 +++--- shadowsocks/proxy.go | 2 +- shadowsocks/udprelay.go | 18 +++++++++-------- 6 files changed, 37 insertions(+), 35 deletions(-) diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index 624f908e..210361a6 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -96,7 +96,7 @@ func getRequest(conn *ss.Conn) (host string, err error) { const logCntDelta = 100 var connCnt int -var nextLogConnCnt int = logCntDelta +var nextLogConnCnt = logCntDelta func sanitizeAddr(addr net.Addr) string { if sanitizeIps { @@ -167,13 +167,13 @@ func handleConnection(conn *ss.Conn, port string) { debug.Printf("piping %s<->%s", sanitizeAddr(conn.RemoteAddr()), host) } go func() { - ss.PipeThenClose(conn, remote, func(flow int) { - passwdManager.addFlow(port, flow) + ss.PipeThenClose(conn, remote, func(Traffic int) { + passwdManager.addTraffic(port, Traffic) }) }() - ss.PipeThenClose(remote, conn, func(flow int) { - passwdManager.addFlow(port, flow) + ss.PipeThenClose(remote, conn, func(Traffic int) { + passwdManager.addTraffic(port, Traffic) }) closed = true @@ -194,13 +194,13 @@ type PasswdManager struct { sync.Mutex portListener map[string]*PortListener udpListener map[string]*UDPListener - flowStats map[string]int64 + trafficStats map[string]int64 } func (pm *PasswdManager) add(port, password string, listener net.Listener) { pm.Lock() pm.portListener[port] = &PortListener{password, listener} - pm.flowStats[port] = 0 + pm.trafficStats[port] = 0 pm.Unlock() } @@ -239,24 +239,24 @@ func (pm *PasswdManager) del(port string) { pl.listener.Close() pm.Lock() delete(pm.portListener, port) - delete(pm.flowStats, port) + delete(pm.trafficStats, port) if udp { delete(pm.udpListener, port) } pm.Unlock() } -func (pm *PasswdManager) addFlow(port string, n int) { +func (pm *PasswdManager) addTraffic(port string, n int) { pm.Lock() - pm.flowStats[port] = pm.flowStats[port] + int64(n) + pm.trafficStats[port] = pm.trafficStats[port] + int64(n) pm.Unlock() return } -func (pm *PasswdManager) getFlowStats() map[string]int64 { +func (pm *PasswdManager) getTrafficStats() map[string]int64 { pm.Lock() copy := make(map[string]int64) - for k, v := range pm.flowStats { + for k, v := range pm.trafficStats { copy[k] = v } pm.Unlock() @@ -299,7 +299,7 @@ func (pm *PasswdManager) updatePortPasswd(port, password string) { var passwdManager = PasswdManager{ portListener: map[string]*PortListener{}, udpListener: map[string]*UDPListener{}, - flowStats: map[string]int64{}, + trafficStats: map[string]int64{}, } func updatePasswd() { @@ -394,8 +394,8 @@ func runUDP(port, password string) { } SecurePacketConn := ss.NewSecurePacketConn(conn, cipher.Copy()) for { - if err := ss.ReadAndHandleUDPReq(SecurePacketConn, func(flow int) { - passwdManager.addFlow(port, flow) + if err := ss.ReadAndHandleUDPReq(SecurePacketConn, func(traffic int) { + passwdManager.addTraffic(port, traffic) }); err != nil { debug.Printf("udp read error: %v\n", err) return @@ -601,10 +601,10 @@ func handlePing() []byte { return []byte("pong") } -// reportStat get the stat:flowStat and return avery 10 sec as for the protocol +// reportStat get the stat:trafficStat and return avery 10 sec as for the protocol // https://github.com/shadowsocks/shadowsocks/wiki/Manage-Multiple-Users func reportStat() []byte { - stats := passwdManager.getFlowStats() + stats := passwdManager.getTrafficStats() var buf bytes.Buffer buf.WriteString("stat: ") ret, _ := json.Marshal(stats) diff --git a/shadowsocks/encrypt_test.go b/shadowsocks/encrypt_test.go index eebbde35..d5cd3b2f 100644 --- a/shadowsocks/encrypt_test.go +++ b/shadowsocks/encrypt_test.go @@ -9,7 +9,7 @@ import ( const text = "Don't tell me the moon is shining; show me the glint of light on broken glass." -func testCiphter(t *testing.T, c *Cipher, msg string) { +func testCipher(t *testing.T, c *Cipher, msg string) { n := len(text) cipherBuf := make([]byte, n) originTxt := make([]byte, n) @@ -51,7 +51,7 @@ func testBlockCipher(t *testing.T, method string) { if err = cipher.initDecrypt(iv); err != nil { t.Error(method, "initDecrypt:", err) } - testCiphter(t, cipher, method) + testCipher(t, cipher, method) iv, err = cipherCopy.initEncrypt() if err != nil { @@ -60,7 +60,7 @@ func testBlockCipher(t *testing.T, method string) { if err = cipherCopy.initDecrypt(iv); err != nil { t.Error(method, "copy initDecrypt:", err) } - testCiphter(t, cipherCopy, method+" copy") + testCipher(t, cipherCopy, method+" copy") } func TestAES128CFB(t *testing.T) { diff --git a/shadowsocks/mergesort.go b/shadowsocks/mergesort.go index 606fd90a..d85c26de 100644 --- a/shadowsocks/mergesort.go +++ b/shadowsocks/mergesort.go @@ -12,11 +12,11 @@ func merge(left, right []uint64, comparison func (uint64, uint64) int64) []uint6 r++ } } - for (l < len(left)) { + for l < len(left) { result[l + r] = left[l] l++ } - for (r < len(right)) { + for r < len(right) { result[l + r] = right[r] r++ } @@ -27,6 +27,6 @@ func Sort(arr []uint64, comparison func (uint64, uint64) int64) []uint64 { if len(arr) < 2 { return arr } - var middle uint64 = uint64(len(arr)/2) + var middle = uint64(len(arr)/2) return merge(Sort(arr[0:middle], comparison), Sort(arr[middle:], comparison), comparison) } diff --git a/shadowsocks/pipe.go b/shadowsocks/pipe.go index f49f4c3e..51f52006 100644 --- a/shadowsocks/pipe.go +++ b/shadowsocks/pipe.go @@ -12,15 +12,15 @@ func SetReadTimeout(c net.Conn) { } // PipeThenClose copies data from src to dst, closes dst when done. -func PipeThenClose(src, dst net.Conn, addFlow func(int)) { +func PipeThenClose(src, dst net.Conn, addTraffic func(int)) { defer dst.Close() buf := leakyBuf.Get() defer leakyBuf.Put(buf) for { SetReadTimeout(src) n, err := src.Read(buf) - if addFlow != nil { - addFlow(n) + if addTraffic != nil { + addTraffic(n) } // read may return EOF with n > 0 // should always process n > 0 bytes before handling error diff --git a/shadowsocks/proxy.go b/shadowsocks/proxy.go index 0b6145bc..e29c87be 100644 --- a/shadowsocks/proxy.go +++ b/shadowsocks/proxy.go @@ -24,7 +24,7 @@ type ProxyAddr struct { address string } -var ErrNilCipher = errors.New("cipher can't be nil.") +var ErrNilCipher = errors.New("cipher can't be nil") func NewDialer(server string, cipher *Cipher) (dialer *Dialer, err error) { // Currently shadowsocks-go do not support UDP diff --git a/shadowsocks/udprelay.go b/shadowsocks/udprelay.go index 033812b7..47b191ce 100644 --- a/shadowsocks/udprelay.go +++ b/shadowsocks/udprelay.go @@ -68,11 +68,13 @@ func (table *natTable) Get(index string) (c net.PacketConn, ok bool, err error) return } +//noinspection GoRedundantParens type requestHeaderList struct { sync.Mutex List map[string]([]byte) } +//noinspection GoRedundantParens func newReqList() *requestHeaderList { ret := &requestHeaderList{List: map[string]([]byte){}} go func() { @@ -130,7 +132,7 @@ func parseHeaderFromAddr(addr net.Addr) ([]byte, int) { return buf[:1+iplen+2], 1 + iplen + 2 } -func Pipeloop(write net.PacketConn, writeAddr net.Addr, readClose net.PacketConn, addFlow func(int)) { +func Pipeloop(write net.PacketConn, writeAddr net.Addr, readClose net.PacketConn, addTraffic func(int)) { buf := leakyBuf.Get() defer leakyBuf.Put(buf) defer readClose.Close() @@ -151,16 +153,16 @@ func Pipeloop(write net.PacketConn, writeAddr net.Addr, readClose net.PacketConn // need improvement here if req, ok := reqList.Get(raddr.String()); ok { n, _ := write.WriteTo(append(req, buf[:n]...), writeAddr) - addFlow(n) + addTraffic(n) } else { header, hlen := parseHeaderFromAddr(raddr) n, _ := write.WriteTo(append(header[:hlen], buf[:n]...), writeAddr) - addFlow(n) + addTraffic(n) } } } -func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive []byte, addFlow func(int)) { +func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive []byte, addTraffic func(int)) { var dstIP net.IP var reqLen int addrType := receive[idType] @@ -217,7 +219,7 @@ func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive if !exist { Debug.Printf("[udp]new client %s->%s via %s\n", src, dst, remote.LocalAddr()) go func() { - Pipeloop(handle, src, remote, addFlow) + Pipeloop(handle, src, remote, addTraffic) natlist.Delete(src.String()) }() } else { @@ -228,7 +230,7 @@ func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive } remote.SetDeadline(time.Now().Add(udpTimeout)) n, err = remote.WriteTo(receive[reqLen:n], dst) - addFlow(n) + addTraffic(n) if err != nil { if ne, ok := err.(*net.OpError); ok && (ne.Err == syscall.EMFILE || ne.Err == syscall.ENFILE) { // log too many open file error @@ -245,12 +247,12 @@ func handleUDPConnection(handle *SecurePacketConn, n int, src net.Addr, receive return } -func ReadAndHandleUDPReq(c *SecurePacketConn, addFlow func(int)) error { +func ReadAndHandleUDPReq(c *SecurePacketConn, addTraffic func(int)) error { buf := leakyBuf.Get() n, src, err := c.ReadFrom(buf[0:]) if err != nil { return err } - go handleUDPConnection(c, n, src, buf, addFlow) + go handleUDPConnection(c, n, src, buf, addTraffic) return nil } From 8d59b0c45eb92a054004a69c942f18c9df35057e Mon Sep 17 00:00:00 2001 From: dylanchu Date: Sun, 10 Jun 2018 09:51:03 +0800 Subject: [PATCH 73/90] delete mergesort.go --- shadowsocks/mergesort.go | 32 -------------------------------- 1 file changed, 32 deletions(-) delete mode 100644 shadowsocks/mergesort.go diff --git a/shadowsocks/mergesort.go b/shadowsocks/mergesort.go deleted file mode 100644 index d85c26de..00000000 --- a/shadowsocks/mergesort.go +++ /dev/null @@ -1,32 +0,0 @@ -package shadowsocks - -func merge(left, right []uint64, comparison func (uint64, uint64) int64) []uint64 { - result := make([]uint64, len(left) + len(right)) - l, r := 0, 0 - for (l < len(left)) && (r < len(right)) { - if comparison(left[l], right[r]) <= 0 { - result[l + r] = left[l] - l++ - } else { - result[l + r] = right[r] - r++ - } - } - for l < len(left) { - result[l + r] = left[l] - l++ - } - for r < len(right) { - result[l + r] = right[r] - r++ - } - return result -} - -func Sort(arr []uint64, comparison func (uint64, uint64) int64) []uint64 { - if len(arr) < 2 { - return arr - } - var middle = uint64(len(arr)/2) - return merge(Sort(arr[0:middle], comparison), Sort(arr[middle:], comparison), comparison) -} From f1543453752196e50a33469d6128343fde841150 Mon Sep 17 00:00:00 2001 From: dylanchu Date: Sun, 10 Jun 2018 10:29:28 +0800 Subject: [PATCH 74/90] update version information and CHANGELOG --- CHANGELOG | 4 ++++ shadowsocks/util.go | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index dca89d19..4ac4ef09 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +1.2.2 (2018-06-10) + * OTA is finally deprecated. + * Codes clear up. + 1.2.1 (2017-01-22) * Fix bug in handling of UDP OTA packets diff --git a/shadowsocks/util.go b/shadowsocks/util.go index 1a91e84a..3baadaf1 100644 --- a/shadowsocks/util.go +++ b/shadowsocks/util.go @@ -9,7 +9,7 @@ import ( ) func PrintVersion() { - const version = "1.2.1" + const version = "1.2.2" fmt.Println("shadowsocks-go version", version) } From 323704eac4ea17cd427d4438a14725ab839acec1 Mon Sep 17 00:00:00 2001 From: arthur Date: Mon, 23 Jul 2018 13:06:16 +0800 Subject: [PATCH 75/90] FIX #417 #418, switch should be changed into select --- cmd/shadowsocks-server/server.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index 210361a6..28099981 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -32,9 +32,9 @@ const ( typeDm = 3 // type is domain address typeIPv6 = 4 // type is ipv6 address - lenIPv4 = net.IPv4len + 2 // ipv4 + 2port - lenIPv6 = net.IPv6len + 2 // ipv6 + 2port - lenDmBase = 2 // 1addrLen + 2port, plus addrLen + lenIPv4 = net.IPv4len + 2 // ipv4 + 2port + lenIPv6 = net.IPv6len + 2 // ipv6 + 2port + lenDmBase = 2 // 1addrLen + 2port, plus addrLen // lenHmacSha1 = 10 ) @@ -99,11 +99,11 @@ var connCnt int var nextLogConnCnt = logCntDelta func sanitizeAddr(addr net.Addr) string { - if sanitizeIps { - return "x.x.x.x:zzzz" - } else { - return addr.String() - } + if sanitizeIps { + return "x.x.x.x:zzzz" + } else { + return addr.String() + } } func handleConnection(conn *ss.Conn, port string) { @@ -513,11 +513,10 @@ func managerDaemon(conn *net.UDPConn) { go func() { timer := time.Tick(10 * time.Second) for { - <-timer - switch { + select { case <-ctx: return - default: + case <-timer: for _, addr := range reportconnSet { res := reportStat() if len(res) == 0 { From 6bcbad8fcfa4a8bba373d701b82a9bf64eca5594 Mon Sep 17 00:00:00 2001 From: Andreas Auernhammer Date: Mon, 28 Aug 2017 00:05:44 +0800 Subject: [PATCH 76/90] Better chacha20 implementation PR#205 --- .travis.yml | 2 +- shadowsocks/encrypt.go | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 30d8e2b7..3de1eed1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ install: - go get golang.org/x/crypto/blowfish - go get golang.org/x/crypto/cast5 - go get golang.org/x/crypto/salsa20 - - go get github.com/Yawning/chacha20 + - go get github.com/aead/chacha20 - go install ./cmd/shadowsocks-local - go install ./cmd/shadowsocks-server script: diff --git a/shadowsocks/encrypt.go b/shadowsocks/encrypt.go index e13002da..eee4c366 100644 --- a/shadowsocks/encrypt.go +++ b/shadowsocks/encrypt.go @@ -10,7 +10,8 @@ import ( "encoding/binary" "errors" "io" - "github.com/Yawning/chacha20" + + "github.com/aead/chacha20" "golang.org/x/crypto/blowfish" "golang.org/x/crypto/cast5" "golang.org/x/crypto/salsa20/salsa" @@ -101,11 +102,11 @@ func newRC4MD5Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { } func newChaCha20Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { - return chacha20.NewCipher(key, iv) + return chacha20.NewCipher(iv, key) } func newChaCha20IETFStream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { - return chacha20.NewCipher(key, iv) + return chacha20.NewCipher(iv, key) } type salsaStreamCipher struct { From bb41440a95d8cd19efa83a30bda123d39ab2bc30 Mon Sep 17 00:00:00 2001 From: arthur Date: Thu, 26 Jul 2018 22:13:29 +0800 Subject: [PATCH 77/90] update ci version --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3de1eed1..aa8b5e62 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: go go: - - 1.7.4 + - 1.8 + - 1.9 install: - go get golang.org/x/crypto/blowfish - go get golang.org/x/crypto/cast5 @@ -10,4 +11,4 @@ install: - go install ./cmd/shadowsocks-server script: - PATH=$PATH:$HOME/gopath/bin bash -x ./script/test.sh -sudo: false \ No newline at end of file +sudo: false From c5d5382ecfbc7e95a172000b395efc0a831b1bf5 Mon Sep 17 00:00:00 2001 From: liyiheng Date: Mon, 27 Aug 2018 16:21:17 +0800 Subject: [PATCH 78/90] cherry-pick:URI support for shadowsocks-local --- cmd/shadowsocks-local/local.go | 65 +++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/cmd/shadowsocks-local/local.go b/cmd/shadowsocks-local/local.go index 4533500f..604bf9f7 100644 --- a/cmd/shadowsocks-local/local.go +++ b/cmd/shadowsocks-local/local.go @@ -1,6 +1,8 @@ package main import ( + "bytes" + "encoding/base64" "encoding/binary" "errors" "flag" @@ -12,6 +14,7 @@ import ( "os" "path" "strconv" + "strings" "time" ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" @@ -347,10 +350,61 @@ func enoughOptions(config *ss.Config) bool { config.LocalPort != 0 && config.Password != "" } +func parseURI(u string, cfg *ss.Config) (string, error) { + if u == "" { + return "", nil + } + invalidURI := errors.New("invalid URI") + // ss://base64(method:password)@host:port + // ss://base64(method:password@host:port) + u = strings.TrimLeft(u, "ss://") + i := strings.IndexRune(u, '@') + var headParts, tailParts [][]byte + if i == -1 { + dat, err := base64.StdEncoding.DecodeString(u) + if err != nil { + return "", err + } + parts := bytes.Split(dat, []byte("@")) + if len(parts) != 2 { + return "", invalidURI + } + headParts = bytes.SplitN(parts[0], []byte(":"), 2) + tailParts = bytes.SplitN(parts[1], []byte(":"), 2) + + } else { + if i+1 >= len(u) { + return "", invalidURI + } + tailParts = bytes.SplitN([]byte(u[i+1:]), []byte(":"), 2) + dat, err := base64.StdEncoding.DecodeString(u[:i]) + if err != nil { + return "", err + } + headParts = bytes.SplitN(dat, []byte(":"), 2) + } + if len(headParts) != 2 { + return "", invalidURI + } + + if len(tailParts) != 2 { + return "", invalidURI + } + cfg.Method = string(headParts[0]) + cfg.Password = string(headParts[1]) + p, e := strconv.Atoi(string(tailParts[1])) + if e != nil { + return "", e + } + cfg.ServerPort = p + return string(tailParts[0]), nil + +} + func main() { log.SetOutput(os.Stdout) - var configFile, cmdServer string + var configFile, cmdServer, cmdURI string var cmdConfig ss.Config var printVer bool @@ -364,9 +418,18 @@ func main() { flag.IntVar(&cmdConfig.LocalPort, "l", 0, "local socks5 proxy port") flag.StringVar(&cmdConfig.Method, "m", "", "encryption method, default: aes-256-cfb") flag.BoolVar((*bool)(&debug), "d", false, "print debug message") + flag.StringVar(&cmdURI, "u", "", "shadowsocks URI") flag.Parse() + if s, e := parseURI(cmdURI, &cmdConfig); e != nil { + log.Printf("invalid URI: %s\n", e.Error()) + flag.Usage() + os.Exit(1) + } else if s != "" { + cmdServer = s + } + if printVer { ss.PrintVersion() os.Exit(0) From 4c20f3168c004d386c460948f002644ea98d7adc Mon Sep 17 00:00:00 2001 From: Harald Nordgren Date: Sat, 20 Oct 2018 12:04:39 +0200 Subject: [PATCH 79/90] Bump Travis versions --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index aa8b5e62..4c3fd435 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,9 @@ language: go go: - - 1.8 - - 1.9 + - "1.8" + - "1.9" + - "1.10" + - "1.11" install: - go get golang.org/x/crypto/blowfish - go get golang.org/x/crypto/cast5 From 4ee7c0fca3a947434fca5f16006415c554a51940 Mon Sep 17 00:00:00 2001 From: Harald Nordgren Date: Sun, 28 Oct 2018 16:16:32 +0100 Subject: [PATCH 80/90] Bump Go versions and use '.x' to always get latest patch versions --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4c3fd435..2860be22 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,9 @@ language: go go: - - "1.8" - - "1.9" - - "1.10" - - "1.11" + - "1.8.x" + - "1.9.x" + - "1.10.x" + - "1.11.x" install: - go get golang.org/x/crypto/blowfish - go get golang.org/x/crypto/cast5 From d105a20a82f1b97e418597527ab1a1d345a7f167 Mon Sep 17 00:00:00 2001 From: CodeLingo Bot Date: Wed, 13 Feb 2019 00:49:35 +0000 Subject: [PATCH 81/90] Fix function comments based on best practices from Effective Go Signed-off-by: CodeLingo Bot --- shadowsocks/config.go | 2 +- shadowsocks/conn.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/shadowsocks/config.go b/shadowsocks/config.go index 00e409bb..a02a30d7 100644 --- a/shadowsocks/config.go +++ b/shadowsocks/config.go @@ -93,7 +93,7 @@ func SetDebug(d DebugLog) { Debug = d } -// Useful for command line to override options specified in config file +// UpdateConfig: Useful for command line to override options specified in config file // Debug is not updated. func UpdateConfig(old, new *Config) { // Using reflection here is not necessary, but it's a good exercise. diff --git a/shadowsocks/conn.go b/shadowsocks/conn.go index bd8ff5b1..84b461a3 100644 --- a/shadowsocks/conn.go +++ b/shadowsocks/conn.go @@ -53,7 +53,7 @@ func RawAddr(addr string) (buf []byte, err error) { return } -// This is intended for use by users implementing a local socks proxy. +// DialWithRawAddr is intended for use by users implementing a local socks proxy. // rawaddr shoud contain part of the data in socks request, starting from the // ATYP field. (Refer to rfc1928 for more information.) func DialWithRawAddr(rawaddr []byte, server string, cipher *Cipher) (c *Conn, err error) { @@ -69,7 +69,7 @@ func DialWithRawAddr(rawaddr []byte, server string, cipher *Cipher) (c *Conn, er return } -// addr should be in the form of host:port +// Dial: addr should be in the form of host:port func Dial(addr, server string, cipher *Cipher) (c *Conn, err error) { ra, err := RawAddr(addr) if err != nil { From f99b9626af4244b7d1218f8f463bf1386ef356df Mon Sep 17 00:00:00 2001 From: CodeLingo Bot Date: Thu, 21 Feb 2019 15:57:41 +1300 Subject: [PATCH 82/90] CodeLingo Setup Signed-off-by: CodeLingo Bot --- codelingo.yaml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 codelingo.yaml diff --git a/codelingo.yaml b/codelingo.yaml new file mode 100644 index 00000000..2e105641 --- /dev/null +++ b/codelingo.yaml @@ -0,0 +1,3 @@ +tenets: + - import: codelingo/code-review-comments + - import: codelingo/effective-go From 44a50e7c8ba07801be00ac93d7a5f2848fadaab9 Mon Sep 17 00:00:00 2001 From: Arthur Lee Date: Fri, 22 Feb 2019 16:42:25 +0800 Subject: [PATCH 83/90] Revert "CodeLingo Setup" --- codelingo.yaml | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 codelingo.yaml diff --git a/codelingo.yaml b/codelingo.yaml deleted file mode 100644 index 2e105641..00000000 --- a/codelingo.yaml +++ /dev/null @@ -1,3 +0,0 @@ -tenets: - - import: codelingo/code-review-comments - - import: codelingo/effective-go From d7f504d2bb1c780579aa7ad37ad0c45f871033b0 Mon Sep 17 00:00:00 2001 From: tyuan Date: Thu, 7 Mar 2019 15:15:27 +0800 Subject: [PATCH 84/90] Added a sample configuration for multiple IPv6 servers --- sample-config/client-multi-server-ipv6.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 sample-config/client-multi-server-ipv6.json diff --git a/sample-config/client-multi-server-ipv6.json b/sample-config/client-multi-server-ipv6.json new file mode 100644 index 00000000..f93ae503 --- /dev/null +++ b/sample-config/client-multi-server-ipv6.json @@ -0,0 +1,7 @@ +{ + "local_port": 1081, + "server_password": [ + ["[::1]:8387", "foobar"], + ["[::1]:8388", "barfoo", "aes-128-cfb"] + ] +} From 62190e5f6501dd3841f5694cd0ad8814dd04ae65 Mon Sep 17 00:00:00 2001 From: tyuan Date: Thu, 7 Mar 2019 15:58:16 +0800 Subject: [PATCH 85/90] Delete client-multi-server-ipv6.json --- sample-config/client-multi-server-ipv6.json | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 sample-config/client-multi-server-ipv6.json diff --git a/sample-config/client-multi-server-ipv6.json b/sample-config/client-multi-server-ipv6.json deleted file mode 100644 index f93ae503..00000000 --- a/sample-config/client-multi-server-ipv6.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "local_port": 1081, - "server_password": [ - ["[::1]:8387", "foobar"], - ["[::1]:8388", "barfoo", "aes-128-cfb"] - ] -} From ed7a71c371b71c8083ec652bd5a09e1a2cf6a982 Mon Sep 17 00:00:00 2001 From: tyuan Date: Thu, 7 Mar 2019 16:00:35 +0800 Subject: [PATCH 86/90] Add a prompt for IPv6 --- sample-config/client-multi-server.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sample-config/client-multi-server.json b/sample-config/client-multi-server.json index 21e20580..37869730 100644 --- a/sample-config/client-multi-server.json +++ b/sample-config/client-multi-server.json @@ -2,6 +2,7 @@ "local_port": 1081, "server_password": [ ["127.0.0.1:8387", "foobar"], - ["127.0.0.1:8388", "barfoo", "aes-128-cfb"] + ["127.0.0.1:8388", "barfoo", "aes-128-cfb"], + ["[::1]:8389", "foobarfoo", "aes-128-cfb"] ] } From 9008f91097f0e614f9d589f5e4ceea4ace0e54d0 Mon Sep 17 00:00:00 2001 From: ssoxer Date: Thu, 13 Jun 2019 19:34:36 -0600 Subject: [PATCH 87/90] Add a test to check that client and server IVs are different. --- shadowsocks/conn_test.go | 89 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 shadowsocks/conn_test.go diff --git a/shadowsocks/conn_test.go b/shadowsocks/conn_test.go new file mode 100644 index 00000000..b24aed75 --- /dev/null +++ b/shadowsocks/conn_test.go @@ -0,0 +1,89 @@ +package shadowsocks + +import ( + "bytes" + "io" + "net" + "testing" +) + +func mustNewCipher(method string) *Cipher { + const testPassword = "password" + cipher, err := NewCipher(method, testPassword) + if err != nil { + panic(err) + } + return cipher +} + +type transcriptConn struct { + net.Conn + ReadTranscript []byte +} + +func (conn *transcriptConn) Read(p []byte) (int, error) { + n, err := conn.Conn.Read(p) + conn.ReadTranscript = append(conn.ReadTranscript, p[:n]...) + return n, err +} + +func connIVs(method string) (clientIV, serverIV []byte, err error) { + // underlying network connection + clientConn, serverConn := net.Pipe() + // make a transcript of bytes at the network level + clientTranscriptConn := &transcriptConn{Conn: clientConn} + serverTranscriptConn := &transcriptConn{Conn: serverConn} + // connection at the ShadowSocks level + clientSSConn := NewConn(clientTranscriptConn, mustNewCipher(method)) + serverSSConn := NewConn(serverTranscriptConn, mustNewCipher(method)) + + clientToServerData := []byte("clientToServerData") + serverToClientData := []byte("serverToClientData") + + go func() { + defer serverSSConn.Close() + buf := make([]byte, len(clientToServerData)) + // read the client IV + _, err := io.ReadFull(serverSSConn, buf) + if err != nil { + return + } + // send the server IV + _, err = serverSSConn.Write(serverToClientData) + if err != nil { + return + } + }() + + // send the client IV + _, err = clientSSConn.Write(clientToServerData) + if err != nil { + return + } + // read the server IV + buf := make([]byte, len(serverToClientData)) + _, err = io.ReadFull(clientSSConn, buf) + if err != nil { + return + } + + // pull the IVs out of the network transcripts + clientIV = serverTranscriptConn.ReadTranscript[:clientSSConn.Cipher.info.ivLen] + serverIV = clientTranscriptConn.ReadTranscript[:serverSSConn.Cipher.info.ivLen] + + return +} + +func TestIndependentIVs(t *testing.T) { + for method := range cipherMethod { + clientIV, serverIV, err := connIVs(method) + if err != nil { + t.Errorf("%s connection error: %s", method, err) + continue + } + if bytes.Equal(clientIV, serverIV) { + t.Errorf("%s equal client and server IVs", method) + continue + } + } +} From c3326cd0c0ac3b371d687fc3bdcd22bb9d69fc0f Mon Sep 17 00:00:00 2001 From: ssoxer Date: Thu, 13 Jun 2019 18:26:29 -0600 Subject: [PATCH 88/90] Generate a fresh server IV, do not copy the client's. Copying the client IV makes connections easily detectable, and allows decryption of one direction if the plaintext of the other direction can be guessed. --- shadowsocks/conn.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/shadowsocks/conn.go b/shadowsocks/conn.go index 84b461a3..e6e3ce95 100644 --- a/shadowsocks/conn.go +++ b/shadowsocks/conn.go @@ -99,9 +99,6 @@ func (c *Conn) Read(b []byte) (n int, err error) { if err = c.initDecrypt(iv); err != nil { return } - if len(c.iv) == 0 { - c.iv = iv - } } cipherData := c.readBuf From 1c9f75773bcd72a5bbeecfb2f31529582ffcfccf Mon Sep 17 00:00:00 2001 From: ssoxer Date: Thu, 13 Jun 2019 18:52:30 -0600 Subject: [PATCH 89/90] Remove `iv` from the `Cipher` type. It is dangerous to have a single `iv` member, because there must be separate IVs for encryption and decryption. Instead, store the IVs implicitly inside the `cipher.Stream` objects. The `GetIv` and `GetKey` functions were unused leftovers from OTA support, added in commit 89460d2e476940f0b702e4bdff4991152d9cdbbe. --- shadowsocks/conn.go | 12 ------------ shadowsocks/encrypt.go | 12 +++--------- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/shadowsocks/conn.go b/shadowsocks/conn.go index e6e3ce95..5d264b74 100644 --- a/shadowsocks/conn.go +++ b/shadowsocks/conn.go @@ -78,18 +78,6 @@ func Dial(addr, server string, cipher *Cipher) (c *Conn, err error) { return DialWithRawAddr(ra, server, cipher) } -func (c *Conn) GetIv() (iv []byte) { - iv = make([]byte, len(c.iv)) - copy(iv, c.iv) - return -} - -func (c *Conn) GetKey() (key []byte) { - key = make([]byte, len(c.key)) - copy(key, c.key) - return -} - func (c *Conn) Read(b []byte) (n int, err error) { if c.dec == nil { iv := make([]byte, c.info.ivLen) diff --git a/shadowsocks/encrypt.go b/shadowsocks/encrypt.go index eee4c366..60790d8a 100644 --- a/shadowsocks/encrypt.go +++ b/shadowsocks/encrypt.go @@ -188,7 +188,6 @@ type Cipher struct { dec cipher.Stream key []byte info *cipherInfo - iv []byte } // NewCipher creates a cipher that can be used in Dial() etc. @@ -215,14 +214,9 @@ func NewCipher(method, password string) (c *Cipher, err error) { // Initializes the block cipher with CFB mode, returns IV. func (c *Cipher) initEncrypt() (iv []byte, err error) { - if c.iv == nil { - iv = make([]byte, c.info.ivLen) - if _, err := io.ReadFull(rand.Reader, iv); err != nil { - return nil, err - } - c.iv = iv - } else { - iv = c.iv + iv = make([]byte, c.info.ivLen) + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + return nil, err } c.enc, err = c.info.newStream(c.key, iv, Encrypt) return From 3e585ff90601765510d31ee1d05b6f63548c7d44 Mon Sep 17 00:00:00 2001 From: John Hu Date: Thu, 9 Apr 2020 14:44:50 +0800 Subject: [PATCH 90/90] Add deprecation notice to README.md (#491) --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index a050464d..0e13eca9 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +# Deprecated + +Use https://github.com/shadowsocks/go-shadowsocks2 instead. + # shadowsocks-go Current version: 1.2.2 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=master)](https://travis-ci.org/shadowsocks/shadowsocks-go)