Skip to content

Commit

Permalink
Merge branch 'develop', version 1.1.4
Browse files Browse the repository at this point in the history
  • Loading branch information
cyfdecyf committed May 10, 2015
2 parents 8920bf8 + d4a6a12 commit 4a42939
Show file tree
Hide file tree
Showing 14 changed files with 273 additions and 60 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
*.deb
script/http
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
language: go
go:
- 1.3
- 1.4.2
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 install ./cmd/shadowsocks-local
- go install ./cmd/shadowsocks-server
script:
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -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

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# shadowsocks-go

Current version: 1.1.3 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=master)](https://travis-ci.org/shadowsocks/shadowsocks-go)
Current version: 1.1.4 [![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).

Expand Down Expand Up @@ -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
```
Expand Down
7 changes: 5 additions & 2 deletions cmd/shadowsocks-local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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")
Expand Down
11 changes: 4 additions & 7 deletions cmd/shadowsocks-server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -261,7 +258,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
Expand Down Expand Up @@ -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")
Expand Down
4 changes: 3 additions & 1 deletion script/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ 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
}

start_http_server
Expand All @@ -119,7 +121,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

Expand Down
3 changes: 3 additions & 0 deletions shadowsocks/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
47 changes: 36 additions & 11 deletions shadowsocks/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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])
Expand All @@ -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
}
56 changes: 52 additions & 4 deletions shadowsocks/encrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package shadowsocks

import (
"bytes"
"golang.org/x/crypto/blowfish"
"golang.org/x/crypto/cast5"
"crypto/aes"
"crypto/cipher"
"crypto/des"
Expand All @@ -12,6 +10,10 @@ import (
"crypto/rc4"
"encoding/binary"
"errors"
"github.com/codahale/chacha20"
"golang.org/x/crypto/blowfish"
"golang.org/x/crypto/cast5"
"golang.org/x/crypto/salsa20/salsa"
"io"
)

Expand Down Expand Up @@ -137,22 +139,68 @@ 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 salsaStreamCipher struct {
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
copy(subNonce[:], c.nonce[:])
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:])

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
newStream func(key, iv []byte, doe DecOrEnc) (cipher.Stream, error)
}

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},
"des-cfb": {8, 8, newDESStream},
"bf-cfb": {16, 8, newBlowFishStream},
"cast5-cfb": {16, 8, newCast5Stream},
"rc4-md5": {16, 16, newRC4MD5Stream},
"rc4": {16, 0, nil},
"table": {16, 0, nil},
"chacha20": {32, 8, newChaCha20Stream},
"salsa20": {32, 8, newSalsa20Stream},
}

func CheckCipherMethod(method string) error {
Expand Down
Loading

0 comments on commit 4a42939

Please sign in to comment.