Skip to content

Commit 3cec162

Browse files
committed
Merge branch 'release/0.5'
2 parents c3f87b1 + 9f26f09 commit 3cec162

17 files changed

Lines changed: 274 additions & 94 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
table.cache

README.md

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,16 @@ go get github.com/shadowsocks/shadowsocks-go/cmd/shadowsocks-local
2626

2727
Both the server and client program will look for `config.json` in the current directory. You can use `-c` option to specify another configuration file.
2828

29-
The configuration syntax is the same with [shadowsocks-nodejs](https://github.com/clowwindy/shadowsocks-nodejs/). You can download the sample [`config.json`](https://github.com/shadowsocks/shadowsocks-go/blob/master/config.json), change the following values:
29+
Configuration file is in json format and has the same syntax with [shadowsocks-nodejs](https://github.com/clowwindy/shadowsocks-nodejs/). You can download the sample [`config.json`](https://github.com/shadowsocks/shadowsocks-go/blob/master/config.json), change the following values:
3030

3131
```
3232
server your server ip or hostname
3333
server_port server port
3434
local_port local socks5 proxy port
3535
password a password used to encrypt transfer
36-
3736
timeout server option, in seconds
38-
port_password server option, specify multiple ports and passwords to support multiple users
39-
cache_enctable server option, store computed encryption table on disk to speedup server startup
4037
```
4138

42-
Given `port_password` option, server program will ignore `server_port` and `password` options.
43-
4439
Run `shadowsocks-server` on your server. To run it in the background, run `shadowsocks-server > log &`.
4540

4641
On client, run `shadowsocks-local`. Change proxy settings of your browser to
@@ -60,12 +55,30 @@ shadowsocks-server -p server_port -k password -t timeout -c config.json
6055

6156
Use `-d` option to enable debug message.
6257

63-
## Encryption table cache ##
6458

65-
If the server has many different passwords, startup would be slow because it takes much time to calculate encryption tables. It's recommended to enable the `cache_enctable` option if you have more than 20 different passwords. This will save the computed encryption table in the file `table.cache`.
59+
## Use multiple servers on client
60+
61+
```
62+
server_password specify multiple server and password, server should be in the form of host:port
63+
```
64+
65+
Here's a sample configuration [`client-multi-server.json`](https://github.com/shadowsocks/shadowsocks-go/blob/master/sample-config/client-multi-server.json). Given `server_password`, client program will ignore `server_port`, `server` and `password` options.
66+
67+
Servers are chosen in round robin fasion. If a server can't be connected, the client will try the next one. The client does not try to detect connection problems caused by incorrect password, this is intended for the user to notice the error.
68+
69+
## Multiple users with different passwords on server
70+
71+
The server can support users with different passwords. Each user will be served by a unique port. Use the following options on the server for such setup:
72+
73+
```
74+
port_password specify multiple ports and passwords to support multiple users
75+
cache_enctable store computed encryption table on disk to speedup server startup
76+
```
77+
78+
Here's a sample configuration [`server-multi-port.json`](https://github.com/shadowsocks/shadowsocks-go/blob/master/sample-config/server-multi-port.json). Given `port_password`, server program will ignore `server_port` and `password` options.
6679

67-
Note: unused password will not be deleted, so you may need to delete the table cache file if it grows too big.
80+
Enabling `cache_enctable` is recommended if you have more than 20 different passwords. Unused password will not be deleted, so you may need to delete the file `table.cache` if it grows too big.
6881

69-
## Updating port password for a running server ##
82+
### Update port password for a running server ###
7083

7184
Edit the config file used to start the server, then send `SIGHUP` to the server process.

cmd/shadowsocks-local/local.go

Lines changed: 111 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
99
"io"
1010
"log"
11-
"math/rand"
1211
"net"
1312
"os"
1413
"path"
@@ -137,7 +136,83 @@ func getRequest(conn net.Conn) (rawaddr []byte, host string, err error) {
137136
return
138137
}
139138

140-
func handleConnection(conn net.Conn, server string, encTbl *ss.EncryptTable) {
139+
type ServerEnctbl struct {
140+
server string
141+
enctbl *ss.EncryptTable
142+
}
143+
144+
var servers struct {
145+
srvenc []*ServerEnctbl
146+
idx uint8
147+
}
148+
149+
func initServers(config *ss.Config) {
150+
if len(config.ServerPassword) == 0 {
151+
// only one encryption table
152+
enctbl := ss.GetTable(config.Password)
153+
srvPort := strconv.Itoa(config.ServerPort)
154+
srvArr := config.GetServerArray()
155+
n := len(srvArr)
156+
servers.srvenc = make([]*ServerEnctbl, n, n)
157+
158+
for i, s := range srvArr {
159+
if ss.HasPort(s) {
160+
log.Println("ignore server_port option for server", s)
161+
servers.srvenc[i] = &ServerEnctbl{s, enctbl}
162+
} else {
163+
servers.srvenc[i] = &ServerEnctbl{s + ":" + srvPort, enctbl}
164+
}
165+
}
166+
} else {
167+
n := len(config.ServerPassword)
168+
servers.srvenc = make([]*ServerEnctbl, n, n)
169+
170+
tblCache := make(map[string]*ss.EncryptTable)
171+
i := 0
172+
for s, passwd := range config.ServerPassword {
173+
if !ss.HasPort(s) {
174+
log.Fatal("no port for server %s, please specify port in the form of %s:port", s, s)
175+
}
176+
tbl, ok := tblCache[passwd]
177+
if !ok {
178+
tbl = ss.GetTable(passwd)
179+
tblCache[passwd] = tbl
180+
}
181+
servers.srvenc[i] = &ServerEnctbl{s, tbl}
182+
i++
183+
}
184+
}
185+
for _, se := range servers.srvenc {
186+
log.Println("available remote server", se.server)
187+
}
188+
return
189+
}
190+
191+
// select one server to connect in round robin order
192+
func createServerConn(rawaddr []byte, addr string) (remote *ss.Conn, err error) {
193+
n := len(servers.srvenc)
194+
if n == 1 {
195+
se := servers.srvenc[0]
196+
debug.Printf("connecting to %s via %s\n", addr, se.server)
197+
return ss.DialWithRawAddr(rawaddr, se.server, se.enctbl)
198+
}
199+
200+
id := servers.idx
201+
servers.idx++ // it's ok for concurrent update
202+
for i := 0; i < n; i++ {
203+
se := servers.srvenc[(int(id)+i)%n]
204+
remote, err = ss.DialWithRawAddr(rawaddr, se.server, se.enctbl)
205+
if err == nil {
206+
debug.Printf("connected to %s via %s\n", addr, se.server)
207+
return
208+
} else {
209+
log.Println("error connecting to shadowsocks server:", err)
210+
}
211+
}
212+
return
213+
}
214+
215+
func handleConnection(conn net.Conn) {
141216
if debug {
142217
debug.Printf("socks connect from %s\n", conn.RemoteAddr().String())
143218
}
@@ -153,17 +228,20 @@ func handleConnection(conn net.Conn, server string, encTbl *ss.EncryptTable) {
153228
log.Println("error getting request:", err)
154229
return
155230
}
156-
// TODO should send error code to client if connect to server failed
231+
// Sending connection established message immediately to client.
232+
// This some round trip time for creating socks connection with the client.
233+
// But if connection failed, the client will get connection reset error.
157234
_, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43})
158235
if err != nil {
159236
debug.Println("send connection confirmation:", err)
160237
return
161238
}
162239

163-
debug.Printf("connecting to %s via %s\n", addr, server)
164-
remote, err := ss.DialWithRawAddr(rawaddr, server, encTbl)
240+
remote, err := createServerConn(rawaddr, addr)
165241
if err != nil {
166-
log.Println("error connect to shadowsocks server:", err)
242+
if len(servers.srvenc) > 1 {
243+
log.Println("Failed connect to all avaiable shadowsocks server")
244+
}
167245
return
168246
}
169247
defer remote.Close()
@@ -175,27 +253,19 @@ func handleConnection(conn net.Conn, server string, encTbl *ss.EncryptTable) {
175253
debug.Println("closing")
176254
}
177255

178-
func getServer(server []string) string {
179-
if len(server) == 0 {
180-
return server[0]
181-
}
182-
return server[rand.Intn(len(server))]
183-
}
184-
185-
func run(port, password string, server []string) {
256+
func run(port string) {
186257
ln, err := net.Listen("tcp", ":"+port)
187258
if err != nil {
188259
log.Fatal(err)
189260
}
190-
encTbl := ss.GetTable(password)
191-
log.Printf("starting local socks5 server at port %v, remote shadowsocks server %s ...\n", port, server)
261+
log.Printf("starting local socks5 server at port %v ...\n", port)
192262
for {
193263
conn, err := ln.Accept()
194264
if err != nil {
195265
log.Println("accept:", err)
196266
continue
197267
}
198-
go handleConnection(conn, getServer(server), encTbl)
268+
go handleConnection(conn)
199269
}
200270
}
201271

@@ -207,7 +277,9 @@ func enoughOptions(config *ss.Config) bool {
207277
func main() {
208278
var configFile, cmdServer string
209279
var cmdConfig ss.Config
280+
var printVer bool
210281

282+
flag.BoolVar(&printVer, "version", false, "print version")
211283
flag.StringVar(&configFile, "c", "config.json", "specify config file")
212284
flag.StringVar(&cmdServer, "s", "", "server address")
213285
flag.StringVar(&cmdConfig.Password, "k", "", "password")
@@ -216,7 +288,14 @@ func main() {
216288
flag.BoolVar((*bool)(&debug), "d", false, "print debug message")
217289

218290
flag.Parse()
291+
292+
if printVer {
293+
ss.PrintVersion()
294+
os.Exit(0)
295+
}
296+
219297
cmdConfig.Server = cmdServer
298+
ss.SetDebug(debug)
220299

221300
exists, err := ss.IsFileExists(configFile)
222301
// If no config file in current directory, try search it in the binary directory
@@ -240,17 +319,22 @@ func main() {
240319
} else {
241320
ss.UpdateConfig(config, &cmdConfig)
242321
}
243-
if !enoughOptions(config) {
244-
log.Println("must specify server address, password and both server/local port")
245-
os.Exit(1)
246-
}
247-
ss.SetDebug(debug)
248322

249-
srvArr := config.GetServerArray()
250-
srvPort := strconv.Itoa(config.ServerPort)
251-
for i, _ := range srvArr {
252-
srvArr[i] += ":" + srvPort
323+
if len(config.ServerPassword) == 0 {
324+
if !enoughOptions(config) {
325+
log.Println("must specify server address, password and both server/local port")
326+
os.Exit(1)
327+
}
328+
} else {
329+
if config.Password != "" || config.ServerPort != 0 || config.GetServerArray() != nil {
330+
log.Println("given server_password, ignore server, server_port and password option:", config)
331+
}
332+
if config.LocalPort == 0 {
333+
log.Fatal("must specify local port")
334+
}
253335
}
254336

255-
run(strconv.Itoa(config.LocalPort), config.Password, srvArr)
337+
initServers(config)
338+
339+
run(strconv.Itoa(config.LocalPort))
256340
}

cmd/shadowsocks-server/server.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,14 +354,22 @@ var config *ss.Config
354354

355355
func main() {
356356
var cmdConfig ss.Config
357+
var printVer bool
357358

359+
flag.BoolVar(&printVer, "version", false, "print version")
358360
flag.StringVar(&configFile, "c", "config.json", "specify config file")
359361
flag.StringVar(&cmdConfig.Password, "k", "", "password")
360362
flag.IntVar(&cmdConfig.ServerPort, "p", 0, "server port")
361363
flag.IntVar(&cmdConfig.Timeout, "t", 60, "connection timeout (in seconds)")
362364
flag.BoolVar((*bool)(&debug), "d", false, "print debug message")
363365

364366
flag.Parse()
367+
368+
if printVer {
369+
ss.PrintVersion()
370+
os.Exit(0)
371+
}
372+
365373
ss.SetDebug(debug)
366374

367375
var err error

config.json

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
1-
{
2-
"server":["127.0.0.1"],
3-
"server_port":8388,
4-
"local_port":1080,
5-
"password":"barfoo!",
6-
"port_password": {
7-
"8388": "barfoo!",
8-
"8387": "foobar!"
9-
},
10-
"timeout":60,
11-
"cache_enctable":false
12-
}
1+
{
2+
"server":"127.0.0.1",
3+
"server_port":8388,
4+
"local_port":1080,
5+
"password":"barfoo!",
6+
"timeout":60
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"local_port":1081,
3+
"server_password": {
4+
"127.0.0.1:8387": "foobar",
5+
"127.0.0.1:8388": "barfoo"
6+
}
7+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"port_password": {
3+
"8387": "foobar",
4+
"8388": "barfoo"
5+
},
6+
"timeout": 60,
7+
"cache_enctable": true
8+
}

shadowsocks/config.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,32 @@ import (
1111
"encoding/json"
1212
"fmt"
1313
"io/ioutil"
14+
"log"
1415
"os"
1516
"reflect"
1617
"time"
1718
)
1819

1920
type Config struct {
20-
Server interface{} `json:"server"`
21-
ServerPort int `json:"server_port"`
22-
LocalPort int `json:"local_port"`
23-
Password string `json:"password"`
21+
Server interface{} `json:"server"`
22+
ServerPort int `json:"server_port"`
23+
LocalPort int `json:"local_port"`
24+
Password string `json:"password"`
25+
26+
// following options are only used by server
2427
PortPassword map[string]string `json:"port_password"`
2528
Timeout int `json:"timeout"`
2629
CacheEncTable bool `json:"cache_enctable"`
30+
31+
// following options are only used by client
32+
ServerPassword map[string]string `json:"server_password"`
2733
}
2834

2935
var readTimeout time.Duration
3036

3137
func (config *Config) GetServerArray() []string {
38+
// Specifying multiple servers in the "server" options is deprecated.
39+
// But for backward compatiblity, keep this.
3240
if config.Server == nil {
3341
return nil
3442
}
@@ -38,6 +46,10 @@ func (config *Config) GetServerArray() []string {
3846
}
3947
arr, ok := config.Server.([]interface{})
4048
if ok {
49+
if len(arr) > 1 {
50+
log.Println("Multiple servers in \"server\" option is deprecated. " +
51+
"Please use \"server_password\" instead.")
52+
}
4153
serverArr := make([]string, len(arr), len(arr))
4254
for i, s := range arr {
4355
serverArr[i], ok = s.(string)

0 commit comments

Comments
 (0)