Skip to content
This repository has been archived by the owner on Apr 9, 2020. It is now read-only.

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
lixin9311 committed Jan 19, 2017
2 parents 5c9897e + d7780ed commit 6f7f790
Show file tree
Hide file tree
Showing 9 changed files with 624 additions and 38 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -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:
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -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
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# shadowsocks-go

Current version: 1.1.5 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=master)](https://travis-ci.org/shadowsocks/shadowsocks-go)
Current version: 1.2.0 [![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 @@ -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.
Expand Down
85 changes: 79 additions & 6 deletions cmd/shadowsocks-server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,15 @@ const (
)

var debug ss.DebugLog
var udp bool

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) + 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
Expand All @@ -61,7 +62,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
Expand All @@ -80,7 +81,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])
Expand Down Expand Up @@ -138,6 +139,13 @@ 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)
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)
Expand Down Expand Up @@ -175,9 +183,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) {
Expand All @@ -186,21 +200,44 @@ 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]
pm.Unlock()
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()
}

Expand All @@ -222,9 +259,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")
Expand Down Expand Up @@ -297,6 +339,33 @@ 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 {
if err := ss.ReadAndHandleUDPReq(SecurePacketConn); err != nil {
debug.Println(err)
}
}
}

func enoughOptions(config *ss.Config) bool {
return config.ServerPort != 0 && config.Password != ""
}
Expand Down Expand Up @@ -335,7 +404,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 {
Expand All @@ -358,6 +427,7 @@ func main() {
os.Exit(1)
}
config = &cmdConfig
ss.UpdateConfig(config, config)
} else {
ss.UpdateConfig(config, &cmdConfig)
}
Expand All @@ -376,6 +446,9 @@ func main() {
}
for port, password := range config.PortPassword {
go run(port, password, config.Auth)
if udp {
go runUDP(port, password, config.Auth)
}
}

waitSignal()
Expand Down
40 changes: 28 additions & 12 deletions shadowsocks/encrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand All @@ -95,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 {
Expand Down Expand Up @@ -145,15 +157,19 @@ 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},
"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 {
Expand Down
Loading

0 comments on commit 6f7f790

Please sign in to comment.