diff --git a/cmd/shadowsocks-local/local.go b/cmd/shadowsocks-local/local.go index 1dae00e..51894f4 100644 --- a/cmd/shadowsocks-local/local.go +++ b/cmd/shadowsocks-local/local.go @@ -8,6 +8,7 @@ import ( ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" "io" "log" + "math/rand" "net" "os" "path" @@ -159,7 +160,7 @@ func handleConnection(conn net.Conn, server string, encTbl *ss.EncryptTable) { return } - debug.Println("connecting to", addr) + debug.Printf("connecting to %s via %s\n", addr, server) remote, err := ss.DialWithRawAddr(rawaddr, server, encTbl) if err != nil { log.Println("error connect to shadowsocks server:", err) @@ -174,7 +175,14 @@ func handleConnection(conn net.Conn, server string, encTbl *ss.EncryptTable) { debug.Println("closing") } -func run(port, password, server string) { +func getServer(server []string) string { + if len(server) == 0 { + return server[0] + } + return server[rand.Intn(len(server))] +} + +func run(port, password string, server []string) { ln, err := net.Listen("tcp", ":"+port) if err != nil { log.Fatal(err) @@ -187,27 +195,28 @@ func run(port, password, server string) { log.Println("accept:", err) continue } - go handleConnection(conn, server, encTbl) + go handleConnection(conn, getServer(server), encTbl) } } func enoughOptions(config *ss.Config) bool { - return config.Server != "" && config.ServerPort != 0 && + return config.Server != nil && config.ServerPort != 0 && config.LocalPort != 0 && config.Password != "" } func main() { - var configFile string + var configFile, cmdServer string var cmdConfig ss.Config flag.StringVar(&configFile, "c", "config.json", "specify config file") - flag.StringVar(&cmdConfig.Server, "s", "", "server address") + flag.StringVar(&cmdServer, "s", "", "server address") 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.BoolVar((*bool)(&debug), "d", false, "print debug message") flag.Parse() + cmdConfig.Server = cmdServer exists, err := ss.IsFileExists(configFile) // If no config file in current directory, try search it in the binary directory @@ -223,11 +232,7 @@ func main() { if err != nil { config = &cmdConfig if os.IsNotExist(err) { - if !enoughOptions(config) { - log.Println("must specify server address, password and both server/local port") - os.Exit(1) - } - log.Println("using all options from command line") + log.Println("config file not found, using all options from command line") } else { log.Printf("error reading config file: %v\n", err) os.Exit(1) @@ -235,8 +240,17 @@ func main() { } else { ss.UpdateConfig(config, &cmdConfig) } + if !enoughOptions(config) { + log.Println("must specify server address, password and both server/local port") + os.Exit(1) + } ss.SetDebug(debug) - run(strconv.Itoa(config.LocalPort), config.Password, - config.Server+":"+strconv.Itoa(config.ServerPort)) + srvArr := config.GetServerArray() + srvPort := strconv.Itoa(config.ServerPort) + for i, _ := range srvArr { + srvArr[i] += ":" + srvPort + } + + run(strconv.Itoa(config.LocalPort), config.Password, srvArr) } diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index c74d255..f7508e7 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -368,7 +368,7 @@ func main() { config, err = ss.ParseConfig(configFile) if err != nil { if os.IsNotExist(err) { - log.Println("using all options from command line") + log.Println("config file not found, using all options from command line") } else { log.Printf("error reading %s: %v\n", configFile, err) os.Exit(1) diff --git a/config.json b/config.json index afacf8d..0611624 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,5 @@ { - "server":"127.0.0.1", + "server":["127.0.0.1"], "server_port":8388, "local_port":1080, "password":"barfoo!", diff --git a/shadowsocks/config.go b/shadowsocks/config.go index 2112501..2b98002 100644 --- a/shadowsocks/config.go +++ b/shadowsocks/config.go @@ -9,6 +9,7 @@ package shadowsocks import ( "encoding/json" + "fmt" "io/ioutil" "os" "reflect" @@ -16,7 +17,7 @@ import ( ) type Config struct { - Server string `json:"server"` + Server interface{} `json:"server"` ServerPort int `json:"server_port"` LocalPort int `json:"local_port"` Password string `json:"password"` @@ -27,6 +28,29 @@ type Config struct { var readTimeout time.Duration +func (config *Config) GetServerArray() []string { + if config.Server == nil { + return nil + } + single, ok := config.Server.(string) + if ok { + return []string{single} + } + arr, ok := config.Server.([]interface{}) + if ok { + serverArr := make([]string, len(arr), len(arr)) + for i, s := range arr { + serverArr[i], ok = s.(string) + if !ok { + goto typeError + } + } + return serverArr + } +typeError: + panic(fmt.Sprintf("Config.Server type error %v", reflect.TypeOf(config.Server))) +} + func ParseConfig(path string) (config *Config, err error) { file, err := os.Open(path) // For read access. if err != nil { diff --git a/shadowsocks/config_test.go b/shadowsocks/config_test.go index 2cb8749..5340d6f 100644 --- a/shadowsocks/config_test.go +++ b/shadowsocks/config_test.go @@ -19,6 +19,10 @@ func TestParseConfig1Password(t *testing.T) { if !config.CacheEncTable { t.Error("cache_enctable should be true") } + srvArr := config.GetServerArray() + if len(srvArr) != 1 || srvArr[0] != "127.0.0.1" { + t.Error("server option is not set correctly") + } } func TestParseConfigMultiPassword(t *testing.T) { @@ -33,4 +37,28 @@ func TestParseConfigMultiPassword(t *testing.T) { if config.PortPassword["8387"] != "foobar" { t.Error("wrong multiple password for port 8387") } + + srvArr := config.GetServerArray() + if len(srvArr) != 2 { + t.Error("server option is not set correctly") + } + if srvArr[0] != "127.0.0.1" { + t.Error("1st server wrong") + } + if srvArr[1] != "127.0.1.1" { + t.Error("2nd server wrong") + } +} + +func TestParseConfigEmpty(t *testing.T) { + // make sure we will not crash + config, err := ParseConfig("testdata/noserver.json") + if err != nil { + t.Error("error parsing noserver config:", err) + } + + srvArr := config.GetServerArray() + if srvArr != nil { + t.Error("GetServerArray should return nil if no server option is given") + } } diff --git a/shadowsocks/testdata/config-multi-passwd.json b/shadowsocks/testdata/config-multi-passwd.json index 2d2aa3a..c26f47b 100644 --- a/shadowsocks/testdata/config-multi-passwd.json +++ b/shadowsocks/testdata/config-multi-passwd.json @@ -1,5 +1,5 @@ { - "server":"127.0.0.1", + "server":["127.0.0.1", "127.0.1.1"], "server_port":8388, "local_port":1081, "password":"barfoo!", diff --git a/shadowsocks/testdata/noserver.json b/shadowsocks/testdata/noserver.json new file mode 100644 index 0000000..f12f3ee --- /dev/null +++ b/shadowsocks/testdata/noserver.json @@ -0,0 +1,7 @@ +{ + "server_port":8388, + "local_port":1081, + "password":"barfoo!", + "timeout":60, + "cache_enctable": true +}