Skip to content

Commit

Permalink
Remove table cache, generate table on demand instead.
Browse files Browse the repository at this point in the history
  • Loading branch information
cyfdecyf committed Jan 15, 2013
1 parent 7617bef commit 9100498
Show file tree
Hide file tree
Showing 7 changed files with 22 additions and 118 deletions.
3 changes: 0 additions & 3 deletions README.md
Expand Up @@ -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.
109 changes: 12 additions & 97 deletions 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"
Expand All @@ -16,7 +14,6 @@ import (
"sync"
"sync/atomic"
"syscall"
"time"
)

var debug ss.DebugLog
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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()
Expand All @@ -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))
}
}
Expand Down Expand Up @@ -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()
}
5 changes: 2 additions & 3 deletions shadowsocks/config.go
Expand Up @@ -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"`
Expand Down
7 changes: 0 additions & 7 deletions shadowsocks/config_test.go
Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions shadowsocks/conn.go
Expand Up @@ -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
}
8 changes: 4 additions & 4 deletions shadowsocks/encrypt.go
Expand Up @@ -8,8 +8,8 @@ import (
)

type EncryptTable struct {
EncTbl []byte
DecTbl []byte
encTbl []byte
decTbl []byte
}

func GetTable(key string) (tbl *EncryptTable) {
Expand Down Expand Up @@ -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
}
Expand Down
4 changes: 2 additions & 2 deletions shadowsocks/encrypt_test.go
Expand Up @@ -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)
}
}
Expand Down

0 comments on commit 9100498

Please sign in to comment.