Skip to content

Commit

Permalink
Multi-server support in local: randomly select one server.
Browse files Browse the repository at this point in the history
  • Loading branch information
cyfdecyf committed Dec 23, 2012
1 parent 2236e57 commit be70880
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 17 deletions.
40 changes: 27 additions & 13 deletions cmd/shadowsocks-local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
"io"
"log"
"math/rand"
"net"
"os"
"path"
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -223,20 +232,25 @@ 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)
}
} 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)
}
2 changes: 1 addition & 1 deletion cmd/shadowsocks-server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"server":"127.0.0.1",
"server":["127.0.0.1"],
"server_port":8388,
"local_port":1080,
"password":"barfoo!",
Expand Down
26 changes: 25 additions & 1 deletion shadowsocks/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ package shadowsocks

import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"reflect"
"time"
)

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"`
Expand All @@ -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 {
Expand Down
28 changes: 28 additions & 0 deletions shadowsocks/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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")
}
}
2 changes: 1 addition & 1 deletion shadowsocks/testdata/config-multi-passwd.json
Original file line number Diff line number Diff line change
@@ -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!",
Expand Down
7 changes: 7 additions & 0 deletions shadowsocks/testdata/noserver.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"server_port":8388,
"local_port":1081,
"password":"barfoo!",
"timeout":60,
"cache_enctable": true
}

0 comments on commit be70880

Please sign in to comment.