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

Commit 6f7f790

Browse files
committed
Merge branch 'develop'
2 parents 5c9897e + d7780ed commit 6f7f790

File tree

9 files changed

+624
-38
lines changed

9 files changed

+624
-38
lines changed

.travis.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
language: go
22
go:
3-
- 1.4.3
3+
- 1.7.4
44
install:
55
- go get golang.org/x/crypto/blowfish
66
- go get golang.org/x/crypto/cast5
77
- go get golang.org/x/crypto/salsa20
8-
- go get github.com/codahale/chacha20
8+
- go get github.com/Yawning/chacha20
99
- go install ./cmd/shadowsocks-local
1010
- go install ./cmd/shadowsocks-server
1111
script:

CHANGELOG

+10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
1.2.0 (2017-01-20)
2+
* Support UDP reley on server side, and OTA
3+
* Support "aes-[128/192/256]-ctr" encryption method (Thanks for @slurin)
4+
* Support "chacha20-ietf" encryption method
5+
* Improve performance of "chacha20" encryption method
6+
* Corrently close connection if handshake failed
7+
8+
1.1.5 (2016-05-04)
9+
* Support OTA (Thanks for @ayanamist for implementing this feature)
10+
111
1.1.4 (2015-05-10)
212
* Support "chacha20" encryption method, thanks to @defia
313
* Support "salsa20" encryption method, thanks to @genzj

README.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# shadowsocks-go
22

3-
Current version: 1.1.5 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=master)](https://travis-ci.org/shadowsocks/shadowsocks-go)
3+
Current version: 1.2.0 [![Build Status](https://travis-ci.org/shadowsocks/shadowsocks-go.png?branch=master)](https://travis-ci.org/shadowsocks/shadowsocks-go)
44

55
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).
66

@@ -62,6 +62,12 @@ Append `-auth` to the encryption method to enable [One Time Auth (OTA)](https://
6262
- For server: this will **force client use OTA**, non-OTA connection will be dropped. Otherwise, both OTA and non-OTA clients can connect
6363
- For client: the `-A` command line option can also enable OTA
6464

65+
### UDP relay
66+
67+
Use `-u` command line options when starting server to enable UDP relay.
68+
69+
Currently only tested with Shadowsocks-Android, if you have encountered any problem, please report.
70+
6571
## Command line options
6672

6773
Command line options can override settings from configuration files. Use `-h` option to see all available options.

cmd/shadowsocks-server/server.go

+79-6
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,15 @@ const (
3737
)
3838

3939
var debug ss.DebugLog
40+
var udp bool
4041

4142
func getRequest(conn *ss.Conn, auth bool) (host string, ota bool, err error) {
4243
ss.SetReadTimeout(conn)
4344

4445
// buf size should at least have the same size with the largest possible
4546
// request size (when addrType is 3, domain name has at most 256 bytes)
46-
// 1(addrType) + 1(lenByte) + 256(max length address) + 2(port) + 10(hmac-sha1)
47-
buf := make([]byte, 270)
47+
// 1(addrType) + 1(lenByte) + 255(max length address) + 2(port) + 10(hmac-sha1)
48+
buf := make([]byte, 269)
4849
// read till we get possible domain length field
4950
if _, err = io.ReadFull(conn, buf[:idType+1]); err != nil {
5051
return
@@ -61,7 +62,7 @@ func getRequest(conn *ss.Conn, auth bool) (host string, ota bool, err error) {
6162
if _, err = io.ReadFull(conn, buf[idType+1:idDmLen+1]); err != nil {
6263
return
6364
}
64-
reqStart, reqEnd = idDm0, int(idDm0+buf[idDmLen]+lenDmBase)
65+
reqStart, reqEnd = idDm0, idDm0+int(buf[idDmLen])+lenDmBase
6566
default:
6667
err = fmt.Errorf("addr type %d not supported", addrType&ss.AddrMask)
6768
return
@@ -80,7 +81,7 @@ func getRequest(conn *ss.Conn, auth bool) (host string, ota bool, err error) {
8081
case typeIPv6:
8182
host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String()
8283
case typeDm:
83-
host = string(buf[idDm0 : idDm0+buf[idDmLen]])
84+
host = string(buf[idDm0 : idDm0+int(buf[idDmLen])])
8485
}
8586
// parse port
8687
port := binary.BigEndian.Uint16(buf[reqEnd-2 : reqEnd])
@@ -138,6 +139,13 @@ func handleConnection(conn *ss.Conn, auth bool) {
138139
host, ota, err := getRequest(conn, auth)
139140
if err != nil {
140141
log.Println("error getting request", conn.RemoteAddr(), conn.LocalAddr(), err)
142+
closed = true
143+
return
144+
}
145+
// ensure the host does not contain some illegal characters, NUL may panic on Win32
146+
if strings.ContainsRune(host, 0x00) {
147+
log.Println("invalid domain name.")
148+
closed = true
141149
return
142150
}
143151
debug.Println("connecting", host)
@@ -175,9 +183,15 @@ type PortListener struct {
175183
listener net.Listener
176184
}
177185

186+
type UDPListener struct {
187+
password string
188+
listener *net.UDPConn
189+
}
190+
178191
type PasswdManager struct {
179192
sync.Mutex
180193
portListener map[string]*PortListener
194+
udpListener map[string]*UDPListener
181195
}
182196

183197
func (pm *PasswdManager) add(port, password string, listener net.Listener) {
@@ -186,21 +200,44 @@ func (pm *PasswdManager) add(port, password string, listener net.Listener) {
186200
pm.Unlock()
187201
}
188202

203+
func (pm *PasswdManager) addUDP(port, password string, listener *net.UDPConn) {
204+
pm.Lock()
205+
pm.udpListener[port] = &UDPListener{password, listener}
206+
pm.Unlock()
207+
}
208+
189209
func (pm *PasswdManager) get(port string) (pl *PortListener, ok bool) {
190210
pm.Lock()
191211
pl, ok = pm.portListener[port]
192212
pm.Unlock()
193213
return
194214
}
195215

216+
func (pm *PasswdManager) getUDP(port string) (pl *UDPListener, ok bool) {
217+
pm.Lock()
218+
pl, ok = pm.udpListener[port]
219+
pm.Unlock()
220+
return
221+
}
222+
196223
func (pm *PasswdManager) del(port string) {
197224
pl, ok := pm.get(port)
198225
if !ok {
199226
return
200227
}
228+
if udp {
229+
upl, ok := pm.getUDP(port)
230+
if !ok {
231+
return
232+
}
233+
upl.listener.Close()
234+
}
201235
pl.listener.Close()
202236
pm.Lock()
203237
delete(pm.portListener, port)
238+
if udp {
239+
delete(pm.udpListener, port)
240+
}
204241
pm.Unlock()
205242
}
206243

@@ -222,9 +259,14 @@ func (pm *PasswdManager) updatePortPasswd(port, password string, auth bool) {
222259
// run will add the new port listener to passwdManager.
223260
// So there maybe concurrent access to passwdManager and we need lock to protect it.
224261
go run(port, password, auth)
262+
if udp {
263+
pl, _ := pm.getUDP(port)
264+
pl.listener.Close()
265+
go runUDP(port, password, auth)
266+
}
225267
}
226268

227-
var passwdManager = PasswdManager{portListener: map[string]*PortListener{}}
269+
var passwdManager = PasswdManager{portListener: map[string]*PortListener{}, udpListener: map[string]*UDPListener{}}
228270

229271
func updatePasswd() {
230272
log.Println("updating password")
@@ -297,6 +339,33 @@ func run(port, password string, auth bool) {
297339
}
298340
}
299341

342+
func runUDP(port, password string, auth bool) {
343+
var cipher *ss.Cipher
344+
port_i, _ := strconv.Atoi(port)
345+
log.Printf("listening udp port %v\n", port)
346+
conn, err := net.ListenUDP("udp", &net.UDPAddr{
347+
IP: net.IPv6zero,
348+
Port: port_i,
349+
})
350+
passwdManager.addUDP(port, password, conn)
351+
if err != nil {
352+
log.Printf("error listening udp port %v: %v\n", port, err)
353+
return
354+
}
355+
defer conn.Close()
356+
cipher, err = ss.NewCipher(config.Method, password)
357+
if err != nil {
358+
log.Printf("Error generating cipher for udp port: %s %v\n", port, err)
359+
conn.Close()
360+
}
361+
SecurePacketConn := ss.NewSecurePacketConn(conn, cipher.Copy(), auth)
362+
for {
363+
if err := ss.ReadAndHandleUDPReq(SecurePacketConn); err != nil {
364+
debug.Println(err)
365+
}
366+
}
367+
}
368+
300369
func enoughOptions(config *ss.Config) bool {
301370
return config.ServerPort != 0 && config.Password != ""
302371
}
@@ -335,7 +404,7 @@ func main() {
335404
flag.StringVar(&cmdConfig.Method, "m", "", "encryption method, default: aes-256-cfb")
336405
flag.IntVar(&core, "core", 0, "maximum number of CPU cores to use, default is determinied by Go runtime")
337406
flag.BoolVar((*bool)(&debug), "d", false, "print debug message")
338-
407+
flag.BoolVar(&udp, "u", false, "UDP Relay")
339408
flag.Parse()
340409

341410
if printVer {
@@ -358,6 +427,7 @@ func main() {
358427
os.Exit(1)
359428
}
360429
config = &cmdConfig
430+
ss.UpdateConfig(config, config)
361431
} else {
362432
ss.UpdateConfig(config, &cmdConfig)
363433
}
@@ -376,6 +446,9 @@ func main() {
376446
}
377447
for port, password := range config.PortPassword {
378448
go run(port, password, config.Auth)
449+
if udp {
450+
go runUDP(port, password, config.Auth)
451+
}
379452
}
380453

381454
waitSignal()

shadowsocks/encrypt.go

+28-12
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
"io"
1313
"strings"
1414

15-
"github.com/codahale/chacha20"
15+
"github.com/Yawning/chacha20"
1616
"golang.org/x/crypto/blowfish"
1717
"golang.org/x/crypto/cast5"
1818
"golang.org/x/crypto/salsa20/salsa"
@@ -65,11 +65,19 @@ func newStream(block cipher.Block, err error, key, iv []byte,
6565
}
6666
}
6767

68-
func newAESStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) {
68+
func newAESCFBStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) {
6969
block, err := aes.NewCipher(key)
7070
return newStream(block, err, key, iv, doe)
7171
}
7272

73+
func newAESCTRStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) {
74+
block, err := aes.NewCipher(key)
75+
if err != nil {
76+
return nil, err
77+
}
78+
return cipher.NewCTR(block, iv), nil
79+
}
80+
7381
func newDESStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) {
7482
block, err := des.NewCipher(key)
7583
return newStream(block, err, key, iv, doe)
@@ -95,7 +103,11 @@ func newRC4MD5Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) {
95103
}
96104

97105
func newChaCha20Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) {
98-
return chacha20.New(key, iv)
106+
return chacha20.NewCipher(key, iv)
107+
}
108+
109+
func newChaCha20IETFStream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) {
110+
return chacha20.NewCipher(key, iv)
99111
}
100112

101113
type salsaStreamCipher struct {
@@ -145,15 +157,19 @@ type cipherInfo struct {
145157
}
146158

147159
var cipherMethod = map[string]*cipherInfo{
148-
"aes-128-cfb": {16, 16, newAESStream},
149-
"aes-192-cfb": {24, 16, newAESStream},
150-
"aes-256-cfb": {32, 16, newAESStream},
151-
"des-cfb": {8, 8, newDESStream},
152-
"bf-cfb": {16, 8, newBlowFishStream},
153-
"cast5-cfb": {16, 8, newCast5Stream},
154-
"rc4-md5": {16, 16, newRC4MD5Stream},
155-
"chacha20": {32, 8, newChaCha20Stream},
156-
"salsa20": {32, 8, newSalsa20Stream},
160+
"aes-128-cfb": {16, 16, newAESCFBStream},
161+
"aes-192-cfb": {24, 16, newAESCFBStream},
162+
"aes-256-cfb": {32, 16, newAESCFBStream},
163+
"aes-128-ctr": {16, 16, newAESCTRStream},
164+
"aes-192-ctr": {24, 16, newAESCTRStream},
165+
"aes-256-ctr": {32, 16, newAESCTRStream},
166+
"des-cfb": {8, 8, newDESStream},
167+
"bf-cfb": {16, 8, newBlowFishStream},
168+
"cast5-cfb": {16, 8, newCast5Stream},
169+
"rc4-md5": {16, 16, newRC4MD5Stream},
170+
"chacha20": {32, 8, newChaCha20Stream},
171+
"chacha20-ietf": {32, 12, newChaCha20IETFStream},
172+
"salsa20": {32, 8, newSalsa20Stream},
157173
}
158174

159175
func CheckCipherMethod(method string) error {

0 commit comments

Comments
 (0)