From 9100498dd1d717805b3b5fce16ed12c234e235ec Mon Sep 17 00:00:00 2001 From: Chen Yufei Date: Wed, 16 Jan 2013 01:11:33 +0800 Subject: [PATCH] Remove table cache, generate table on demand instead. --- README.md | 3 - cmd/shadowsocks-server/server.go | 109 ++++--------------------------- shadowsocks/config.go | 5 +- shadowsocks/config_test.go | 7 -- shadowsocks/conn.go | 4 +- shadowsocks/encrypt.go | 8 +-- shadowsocks/encrypt_test.go | 4 +- 7 files changed, 22 insertions(+), 118 deletions(-) diff --git a/README.md b/README.md index ae82aeb..a866818 100644 --- a/README.md +++ b/README.md @@ -72,13 +72,10 @@ The server can support users with different passwords. Each user will be served ``` port_password specify multiple ports and passwords to support multiple users -cache_enctable store computed encryption table on disk to speedup server startup ``` 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. -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. - ### Update port password for a running server ### Edit the config file used to start the server, then send `SIGHUP` to the server process. diff --git a/cmd/shadowsocks-server/server.go b/cmd/shadowsocks-server/server.go index d8abe96..b6bbb76 100644 --- a/cmd/shadowsocks-server/server.go +++ b/cmd/shadowsocks-server/server.go @@ -1,9 +1,7 @@ package main import ( - "bufio" "encoding/binary" - "encoding/gob" "errors" "flag" ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" @@ -16,7 +14,6 @@ import ( "sync" "sync/atomic" "syscall" - "time" ) var debug ss.DebugLog @@ -148,91 +145,13 @@ func handleConnection(conn *ss.Conn) { return } -const tableCacheFile = "table.cache" - -var table struct { - cache map[string]*ss.EncryptTable - getCnt int32 - hitCnt int32 -} - -func initTableCache(config *ss.Config) { - var exists bool - var err error - if !config.CacheEncTable { - goto emptyCache - } - exists, err = ss.IsFileExists(tableCacheFile) - if exists { - // load table cache from file - f, err := os.Open(tableCacheFile) - if err != nil { - log.Println("error opening table cache:", err) - goto emptyCache - } - defer f.Close() - - dec := gob.NewDecoder(bufio.NewReader(f)) - if err = dec.Decode(&table.cache); err != nil { - log.Println("error loading table cache:", err) - goto emptyCache - } - debug.Println("table cache loaded from disk") - return - } - if err != nil { - log.Println("table cache:", err) - } - -emptyCache: - debug.Println("creating empty table cache") - table.cache = map[string]*ss.EncryptTable{} -} - -func storeTableCache(config *ss.Config) { - if !config.CacheEncTable || table.getCnt == table.hitCnt { - return - } - - const tmpPath = "tmp.cache" - f, err := os.Create(tmpPath) - if err != nil { - log.Println("can't create tmp table cache") - return - } - defer f.Close() - - w := bufio.NewWriter(f) - enc := gob.NewEncoder(w) - if err = enc.Encode(table.cache); err != nil { - log.Println("error saving tmp table cache:", err) - return - } - if err = w.Flush(); err != nil { - log.Println("error flushing table cache:", err) - return - } - if err = os.Rename(tmpPath, tableCacheFile); err != nil { - log.Printf("error renaming %s to %s: %v\n", tmpPath, tableCacheFile, err) - return - } - debug.Println("table cache saved") -} - func getTable(password string) (tbl *ss.EncryptTable) { - if table.cache != nil { - var ok bool - tbl, ok = table.cache[password] - if ok { - atomic.AddInt32(&table.hitCnt, 1) - debug.Println("table cache hit for password:", password) - return - } - tbl = ss.GetTable(password) - table.cache[password] = tbl - } else { - tbl = ss.GetTable(password) - } + // I'm not using a map to cache ciphers with same password because map + // needs lock to protect concurrent access. Memory is not an issue + // because each cipher takes only a few more than 512 bytes. + // Besides, many same passwords for different users should be rare, and + // using cipher cache adds complexity with not much benefit. + tbl = ss.GetTable(password) return } @@ -336,8 +255,7 @@ func run(port, password string) { return } passwdManager.add(port, password, ln) - encTbl := getTable(password) - atomic.AddInt32(&table.getCnt, 1) + var encTbl *ss.EncryptTable log.Printf("server listening port %v ...\n", port) for { conn, err := ln.Accept() @@ -346,6 +264,11 @@ func run(port, password string) { debug.Printf("accept error: %v\n", err) return } + // Creating cipher upon first connection. + if encTbl == nil { + debug.Println("creating cipher for port:", port) + encTbl = getTable(password) + } go handleConnection(ss.NewConn(conn, encTbl)) } } @@ -413,17 +336,9 @@ func main() { os.Exit(1) } - initTableCache(config) for port, password := range config.PortPassword { go run(port, password) } - // Wait all ports have get it's encryption table - for int(table.getCnt) != len(config.PortPassword) { - time.Sleep(1 * time.Second) - } - storeTableCache(config) - log.Println("all ports ready") - table.cache = nil // release memory waitSignal() } diff --git a/shadowsocks/config.go b/shadowsocks/config.go index 392d6ec..9770ca4 100644 --- a/shadowsocks/config.go +++ b/shadowsocks/config.go @@ -24,9 +24,8 @@ type Config struct { Password string `json:"password"` // following options are only used by server - PortPassword map[string]string `json:"port_password"` - Timeout int `json:"timeout"` - CacheEncTable bool `json:"cache_enctable"` + PortPassword map[string]string `json:"port_password"` + Timeout int `json:"timeout"` // following options are only used by client ServerPassword map[string]string `json:"server_password"` diff --git a/shadowsocks/config_test.go b/shadowsocks/config_test.go index 5790a8e..d95eeb8 100644 --- a/shadowsocks/config_test.go +++ b/shadowsocks/config_test.go @@ -20,9 +20,6 @@ func TestConfigJson(t *testing.T) { if len(srvArr) != 1 || srvArr[0] != "127.0.0.1" { t.Error("server option is not set correctly") } - if config.CacheEncTable { - t.Error("cache_enctable should be false by default") - } } func TestServerMultiPort(t *testing.T) { @@ -40,10 +37,6 @@ func TestServerMultiPort(t *testing.T) { if config.PortPassword["8389"] != "" { t.Error("should have no password for port 8389") } - - if !config.CacheEncTable { - t.Error("cache_enctable should be true") - } } func TestDeprecatedClientMultiServerArray(t *testing.T) { diff --git a/shadowsocks/conn.go b/shadowsocks/conn.go index 4778bf6..e13aa02 100644 --- a/shadowsocks/conn.go +++ b/shadowsocks/conn.go @@ -70,13 +70,13 @@ func (c Conn) Read(b []byte) (n int, err error) { buf := make([]byte, len(b), len(b)) n, err = c.Conn.Read(buf) if n > 0 { - encrypt2(c.DecTbl, buf[0:n], b[0:n]) + encrypt2(c.decTbl, buf[0:n], b[0:n]) } return } func (c Conn) Write(b []byte) (n int, err error) { - buf := encrypt(c.EncTbl, b) + buf := encrypt(c.encTbl, b) n, err = c.Conn.Write(buf) return } diff --git a/shadowsocks/encrypt.go b/shadowsocks/encrypt.go index 32b1250..c7dcda8 100644 --- a/shadowsocks/encrypt.go +++ b/shadowsocks/encrypt.go @@ -8,8 +8,8 @@ import ( ) type EncryptTable struct { - EncTbl []byte - DecTbl []byte + encTbl []byte + decTbl []byte } func GetTable(key string) (tbl *EncryptTable) { @@ -38,10 +38,10 @@ func GetTable(key string) (tbl *EncryptTable) { }) } for i = 0; i < tbl_size; i++ { - tbl.EncTbl[i] = byte(table[i]) + tbl.encTbl[i] = byte(table[i]) } for i = 0; i < tbl_size; i++ { - tbl.DecTbl[tbl.EncTbl[i]] = byte(i) + tbl.decTbl[tbl.encTbl[i]] = byte(i) } return } diff --git a/shadowsocks/encrypt_test.go b/shadowsocks/encrypt_test.go index d039adb..7758493 100644 --- a/shadowsocks/encrypt_test.go +++ b/shadowsocks/encrypt_test.go @@ -8,10 +8,10 @@ const tbl_size = 256 func checkTable(t *testing.T, tbl *EncryptTable, encTarget, decTarget []byte, msg string) { for i := 0; i < tbl_size; i++ { - if encTarget[i] != tbl.EncTbl[i] { + if encTarget[i] != tbl.encTbl[i] { t.Fatalf("%s: encrypt table error at index %d\n", msg, i) } - if decTarget[i] != tbl.DecTbl[i] { + if decTarget[i] != tbl.decTbl[i] { t.Fatalf("%s: decrypt table error at index %d\n", msg, i) } }