Skip to content
This repository has been archived by the owner on Jun 16, 2019. It is now read-only.

Commit

Permalink
add shadowsocks
Browse files Browse the repository at this point in the history
  • Loading branch information
orvice committed Aug 18, 2016
1 parent d91f509 commit 476b93c
Show file tree
Hide file tree
Showing 12 changed files with 1,213 additions and 0 deletions.
136 changes: 136 additions & 0 deletions shadowsocks/config.go
@@ -0,0 +1,136 @@
/**
* Created with IntelliJ IDEA.
* User: clowwindy
* Date: 12-11-2
* Time: 上午10:31
* To change this template use File | Settings | File Templates.
*/
package shadowsocks

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

type Config struct {
Server interface{} `json:"server"`
ServerPort int `json:"server_port"`
LocalPort int `json:"local_port"`
Password string `json:"password"`
Method string `json:"method"` // encryption method
Auth bool `json:"auth"` // one time auth

// following options are only used by server
PortPassword map[string]string `json:"port_password"`
Timeout int `json:"timeout"`

// following options are only used by client

// The order of servers in the client config is significant, so use array
// instead of map to preserve the order.
ServerPassword [][]string `json:"server_password"`
}

var readTimeout time.Duration

func (config *Config) GetServerArray() []string {
// Specifying multiple servers in the "server" options is deprecated.
// But for backward compatiblity, keep this.
if config.Server == nil {
return nil
}
single, ok := config.Server.(string)
if ok {
return []string{single}
}
arr, ok := config.Server.([]interface{})
if ok {
/*
if len(arr) > 1 {
log.Println("Multiple servers in \"server\" option is deprecated. " +
"Please use \"server_password\" instead.")
}
*/
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 {
return
}
defer file.Close()

data, err := ioutil.ReadAll(file)
if err != nil {
return
}

config = &Config{}
if err = json.Unmarshal(data, config); err != nil {
return nil, err
}
readTimeout = time.Duration(config.Timeout) * time.Second
if strings.HasSuffix(strings.ToLower(config.Method), "-ota") {
config.Method = config.Method[:len(config.Method) - 4]
config.Auth = true
}
return
}

func SetDebug(d DebugLog) {
Debug = d
}

// Useful for command line to override options specified in config file
// Debug is not updated.
func UpdateConfig(old, new *Config) {
// Using reflection here is not necessary, but it's a good exercise.
// For more information on reflections in Go, read "The Laws of Reflection"
// http://golang.org/doc/articles/laws_of_reflection.html
newVal := reflect.ValueOf(new).Elem()
oldVal := reflect.ValueOf(old).Elem()

// typeOfT := newVal.Type()
for i := 0; i < newVal.NumField(); i++ {
newField := newVal.Field(i)
oldField := oldVal.Field(i)
// log.Printf("%d: %s %s = %v\n", i,
// typeOfT.Field(i).Name, newField.Type(), newField.Interface())
switch newField.Kind() {
case reflect.Interface:
if fmt.Sprintf("%v", newField.Interface()) != "" {
oldField.Set(newField)
}
case reflect.String:
s := newField.String()
if s != "" {
oldField.SetString(s)
}
case reflect.Int:
i := newField.Int()
if i != 0 {
oldField.SetInt(i)
}
}
}

old.Timeout = new.Timeout
readTimeout = time.Duration(old.Timeout) * time.Second
}
107 changes: 107 additions & 0 deletions shadowsocks/config_test.go
@@ -0,0 +1,107 @@
package shadowsocks

import (
"testing"
)

func TestConfigJson(t *testing.T) {
config, err := ParseConfig("../config.json")
if err != nil {
t.Fatal("error parsing config.json:", err)
}

if config.Password != "barfoo!" {
t.Error("wrong password from config")
}
if config.Timeout != 600 {
t.Error("timeout should be 600")
}
if config.Method != "aes-128-cfb" {
t.Error("method should be aes-128-cfb")
}
srvArr := config.GetServerArray()
if len(srvArr) != 1 || srvArr[0] != "127.0.0.1" {
t.Error("server option is not set correctly")
}
}

func TestServerMultiPort(t *testing.T) {
config, err := ParseConfig("../sample-config/server-multi-port.json")
if err != nil {
t.Fatal("error parsing ../sample-config/server-multi-port.json:", err)
}

if config.PortPassword["8387"] != "foobar" {
t.Error("wrong multiple password for port 8387")
}
if config.PortPassword["8388"] != "barfoo" {
t.Error("wrong multiple password for port 8388")
}
if config.PortPassword["8389"] != "" {
t.Error("should have no password for port 8389")
}
}

func TestDeprecatedClientMultiServerArray(t *testing.T) {
// This form of config is deprecated. Provided only for backward compatibility.
config, err := ParseConfig("testdata/deprecated-client-multi-server.json")
if err != nil {
t.Fatal("error parsing deprecated-client-multi-server.json:", err)
}

srvArr := config.GetServerArray()
if len(srvArr) != 2 {
t.Error("server option is not set correctly")
}
if srvArr[0] != "127.0.0.1" {
t.Errorf("1st server wrong, got %v", srvArr[0])
}
if srvArr[1] != "127.0.1.1" {
t.Errorf("2nd server wrong, got %v", srvArr[0])
}
}

func TestClientMultiServerArray(t *testing.T) {
config, err := ParseConfig("../sample-config/client-multi-server.json")
if err != nil {
t.Fatal("error parsing client-multi-server.json:", err)
}

sv := config.ServerPassword[0]
if len(sv) != 2 {
t.Fatalf("server_password 1st server wrong, have %d items\n", len(sv[0]))
}
if sv[0] != "127.0.0.1:8387" {
t.Error("server_password 1st server wrong")
}
if sv[1] != "foobar" {
t.Error("server_password 1st server passwd wrong")
}

sv = config.ServerPassword[1]
if len(sv) != 3 {
t.Fatalf("server_password 2nd server wrong, have %d items\n", len(sv[0]))
}
if sv[0] != "127.0.0.1:8388" {
t.Error("server_password 2nd server wrong")
}
if sv[1] != "barfoo" {
t.Error("server_password 2nd server passwd wrong")
}
if sv[2] != "aes-128-cfb" {
t.Error("server_password 2nd server enc method wrong")
}
}

func TestParseConfigEmpty(t *testing.T) {
// make sure we will not crash
config, err := ParseConfig("testdata/noserver.json")
if err != nil {
t.Fatal("error parsing noserver config:", err)
}

srvArr := config.GetServerArray()
if srvArr != nil {
t.Error("GetServerArray should return nil if no server option is given")
}
}

0 comments on commit 476b93c

Please sign in to comment.