diff --git a/cmd/ck-client/ck-client.go b/cmd/ck-client/ck-client.go index 0dca965..4b79a31 100644 --- a/cmd/ck-client/ck-client.go +++ b/cmd/ck-client/ck-client.go @@ -48,12 +48,12 @@ func makeRemoteConn(sta *client.State) (net.Conn, error) { d := net.Dialer{Control: protector} clientHello := TLS.ComposeInitHandshake(sta) - connectingIP := sta.SS_REMOTE_HOST + connectingIP := sta.RemoteHost if net.ParseIP(connectingIP).To4() == nil { // IPv6 needs square brackets connectingIP = "[" + connectingIP + "]" } - remoteConn, err := d.Dial("tcp", connectingIP+":"+sta.SS_REMOTE_PORT) + remoteConn, err := d.Dial("tcp", connectingIP+":"+sta.RemotePort) if err != nil { log.Printf("Connecting to remote: %v\n", err) return nil, err @@ -86,15 +86,15 @@ func makeRemoteConn(sta *client.State) (net.Conn, error) { } func main() { - // Should be 127.0.0.1 to listen to ss-local on this machine + // Should be 127.0.0.1 to listen to a proxy client on this machine var localHost string - // server_port in ss config, ss sends data on loopback using this port + // port used by proxy clients to communicate with cloak client var localPort string // The ip of the proxy server var remoteHost string // The proxy port,should be 443 var remotePort string - var pluginOpts string + var config string isAdmin := new(bool) log.SetFlags(log.LstdFlags | log.Lshortfile) @@ -106,13 +106,13 @@ func main() { localPort = os.Getenv("SS_LOCAL_PORT") remoteHost = os.Getenv("SS_REMOTE_HOST") remotePort = os.Getenv("SS_REMOTE_PORT") - pluginOpts = os.Getenv("SS_PLUGIN_OPTIONS") + config = os.Getenv("SS_PLUGIN_OPTIONS") } else { localHost = "127.0.0.1" - flag.StringVar(&localPort, "l", "", "localPort: same as server_port in ss config, the plugin listens to SS using this") + flag.StringVar(&localPort, "l", "", "localPort: Cloak listens to proxy clients on this port") flag.StringVar(&remoteHost, "s", "", "remoteHost: IP of your proxy server") flag.StringVar(&remotePort, "p", "443", "remotePort: proxy port, should be 443") - flag.StringVar(&pluginOpts, "c", "ckclient.json", "pluginOpts: path to ckclient.json or options seperated with semicolons") + flag.StringVar(&config, "c", "ckclient.json", "config: path to the configuration file or options seperated with semicolons") askVersion := flag.Bool("v", false, "Print the version number") isAdmin = flag.Bool("a", false, "Admin mode") printUsage := flag.Bool("h", false, "Print this message") @@ -133,7 +133,7 @@ func main() { if *isAdmin { sta := client.InitState("", "", "", "", time.Now) - err := sta.ParseConfig(pluginOpts) + err := sta.ParseConfig(config) if err != nil { log.Fatal(err) } @@ -145,27 +145,27 @@ func main() { } sta := client.InitState(localHost, localPort, remoteHost, remotePort, time.Now) - err := sta.ParseConfig(pluginOpts) + err := sta.ParseConfig(config) if err != nil { log.Fatal(err) } - if sta.SS_LOCAL_PORT == "" { + if sta.LocalPort == "" { log.Fatal("Must specify localPort") } - if sta.SS_REMOTE_HOST == "" { + if sta.RemoteHost == "" { log.Fatal("Must specify remoteHost") } if sta.TicketTimeHint == 0 { log.Fatal("TicketTimeHint cannot be empty or 0") } - listeningIP := sta.SS_LOCAL_HOST + listeningIP := sta.LocalHost if net.ParseIP(listeningIP).To4() == nil { // IPv6 needs square brackets listeningIP = "[" + listeningIP + "]" } - listener, err := net.Listen("tcp", listeningIP+":"+sta.SS_LOCAL_PORT) - log.Println("Listening for ss on " + listeningIP + ":" + sta.SS_LOCAL_PORT) + listener, err := net.Listen("tcp", listeningIP+":"+sta.LocalPort) + log.Println("Listening for proxy clients on " + listeningIP + ":" + sta.LocalPort) if err != nil { log.Fatal(err) } @@ -207,34 +207,34 @@ start: if sesh.IsBroken() { goto start } - ssConn, err := listener.Accept() + localConn, err := listener.Accept() if err != nil { log.Println(err) continue } go func() { data := make([]byte, 10240) - i, err := io.ReadAtLeast(ssConn, data, 1) + i, err := io.ReadAtLeast(localConn, data, 1) if err != nil { log.Println(err) - ssConn.Close() + localConn.Close() return } stream, err := sesh.OpenStream() if err != nil { log.Println(err) - ssConn.Close() + localConn.Close() return } _, err = stream.Write(data[:i]) if err != nil { log.Println(err) - ssConn.Close() + localConn.Close() stream.Close() return } - go pipe(ssConn, stream) - pipe(stream, ssConn) + go pipe(localConn, stream) + pipe(stream, localConn) }() } diff --git a/cmd/ck-server/ck-server.go b/cmd/ck-server/ck-server.go index 6e28be7..76f0942 100644 --- a/cmd/ck-server/ck-server.go +++ b/cmd/ck-server/ck-server.go @@ -42,7 +42,7 @@ func pipe(dst io.ReadWriteCloser, src io.ReadWriteCloser) { func dispatchConnection(conn net.Conn, sta *server.State) { goWeb := func(data []byte) { - webConn, err := net.Dial("tcp", sta.WebServerAddr) + webConn, err := net.Dial("tcp", sta.RedirAddr) if err != nil { log.Printf("Making connection to redirection server: %v\n", err) return @@ -64,14 +64,19 @@ func dispatchConnection(conn net.Conn, sta *server.State) { data := buf[:i] ch, err := server.ParseClientHello(data) if err != nil { - log.Printf("+1 non SS non (or malformed) TLS traffic from %v\n", conn.RemoteAddr()) + log.Printf("+1 non Cloak non (or malformed) TLS traffic from %v\n", conn.RemoteAddr()) goWeb(data) return } - isSS, UID, sessionID := server.TouchStone(ch, sta) - if !isSS { - log.Printf("+1 non SS TLS traffic from %v\n", conn.RemoteAddr()) + isCloak, UID, sessionID, proxyMethod := server.TouchStone(ch, sta) + if !isCloak { + log.Printf("+1 non Cloak TLS traffic from %v\n", conn.RemoteAddr()) + goWeb(data) + return + } + if _, ok := sta.ProxyBook[proxyMethod]; !ok { + log.Printf("+1 Cloak TLS traffic with invalid proxy method `%v` from %v\n", proxyMethod, conn.RemoteAddr()) goWeb(data) return } @@ -167,47 +172,35 @@ func dispatchConnection(conn net.Conn, sta *server.State) { continue } } - ssIP := sta.SS_LOCAL_HOST - if net.ParseIP(ssIP).To4() == nil { - // IPv6 needs square brackets - ssIP = "[" + ssIP + "]" - } - ssConn, err := net.Dial("tcp", ssIP+":"+sta.SS_LOCAL_PORT) + localConn, err := net.Dial("tcp", sta.ProxyBook[proxyMethod]) if err != nil { - log.Printf("Failed to connect to ssserver: %v\n", err) + log.Printf("Failed to connect to %v: %v\n", proxyMethod, err) continue } - go pipe(ssConn, newStream) - go pipe(newStream, ssConn) + go pipe(localConn, newStream) + go pipe(newStream, localConn) } } } func main() { - // Should be 127.0.0.1 to listen to ss-server on this machine - var localHost string - // server_port in ss config, same as remotePort in plugin mode - var localPort string // server in ss config, the outbound listening ip - var remoteHost string + var bindHost string // Outbound listening ip, should be 443 - var remotePort string - var pluginOpts string + var bindPort string + var config string log.SetFlags(log.LstdFlags | log.Lshortfile) if os.Getenv("SS_LOCAL_HOST") != "" { - localHost = os.Getenv("SS_LOCAL_HOST") - localPort = os.Getenv("SS_LOCAL_PORT") - remoteHost = os.Getenv("SS_REMOTE_HOST") - remotePort = os.Getenv("SS_REMOTE_PORT") - pluginOpts = os.Getenv("SS_PLUGIN_OPTIONS") + bindHost = os.Getenv("SS_REMOTE_HOST") + bindPort = os.Getenv("SS_REMOTE_PORT") + config = os.Getenv("SS_PLUGIN_OPTIONS") } else { - localAddr := flag.String("r", "", "localAddr: the ip:port ss-server is listening on, set in Shadowsocks' configuration. If ss-server is running locally, it should be 127.0.0.1:some port") - flag.StringVar(&remoteHost, "s", "0.0.0.0", "remoteHost: outbound listing ip, set to 0.0.0.0 to listen to everything") - flag.StringVar(&remotePort, "p", "443", "remotePort: outbound listing port, should be 443") - flag.StringVar(&pluginOpts, "c", "server.json", "pluginOpts: path to server.json or options seperated by semicolons") + flag.StringVar(&bindHost, "s", "0.0.0.0", "bindHost: ip to bind to, set to 0.0.0.0 to listen to everything") + flag.StringVar(&bindPort, "p", "443", "bindPort: port to bind to, should be 443") + flag.StringVar(&config, "c", "server.json", "config: path to the configuration file or its content") askVersion := flag.Bool("v", false, "Print the version number") printUsage := flag.Bool("h", false, "Print this message") @@ -240,20 +233,25 @@ func main() { startPprof(*pprofAddr) } - if *localAddr == "" { - log.Fatal("Must specify localAddr") - } - localHost = strings.Split(*localAddr, ":")[0] - localPort = strings.Split(*localAddr, ":")[1] - log.Printf("Starting standalone mode, listening on %v:%v to ss at %v:%v\n", remoteHost, remotePort, localHost, localPort) + log.Printf("Starting standalone mode, listening on %v:%v", bindHost, bindPort) } - sta, _ := server.InitState(localHost, localPort, remoteHost, remotePort, time.Now) + sta, _ := server.InitState(bindHost, bindPort, time.Now) - err := sta.ParseConfig(pluginOpts) + err := sta.ParseConfig(config) if err != nil { log.Fatalf("Configuration file error: %v", err) } + // when cloak is started as a shadowsocks plugin + if os.Getenv("SS_LOCAL_HOST") != "" && os.Getenv("SS_LOCAL_PORT") != "" { + ssLocalHost := os.Getenv("SS_LOCAL_HOST") + ssLocalPort := os.Getenv("SS_LOCAL_PORT") + if net.ParseIP(ssLocalHost).To4() == nil { + ssLocalHost = "[" + ssLocalHost + "]" + } + sta.ProxyBook["shadowsocks"] = ssLocalHost + ":" + ssLocalPort + } + if sta.AdminUID == nil { log.Fatalln("AdminUID cannot be empty!") } @@ -277,7 +275,7 @@ func main() { } // When listening on an IPv6 and IPv4, SS gives REMOTE_HOST as e.g. ::|0.0.0.0 - listeningIP := strings.Split(sta.SS_REMOTE_HOST, "|") + listeningIP := strings.Split(sta.BindHost, "|") for i, ip := range listeningIP { if net.ParseIP(ip).To4() == nil { // IPv6 needs square brackets @@ -286,9 +284,9 @@ func main() { // The last listener must block main() because the program exits on main return. if i == len(listeningIP)-1 { - listen(ip, sta.SS_REMOTE_PORT) + listen(ip, sta.BindPort) } else { - go listen(ip, sta.SS_REMOTE_PORT) + go listen(ip, sta.BindPort) } } diff --git a/config/ckclient.json b/config/ckclient.json index d7171b9..b564245 100644 --- a/config/ckclient.json +++ b/config/ckclient.json @@ -1,8 +1,9 @@ { + "ProxyMethod":"shadowsocks", "UID":"iGAO85zysIyR4c09CyZSLdNhtP/ckcYu7nIPI082AHA=", "PublicKey":"IYoUzkle/T/kriE+Ufdm7AHQtIeGnBWbhhlTbmDpUUI=", "ServerName":"www.bing.com", "TicketTimeHint":3600, "NumConn":4, - "MaskBrowser":"chrome" + "BrowserSig":"chrome" } diff --git a/config/ckserver.json b/config/ckserver.json index 557cf8a..8637a76 100644 --- a/config/ckserver.json +++ b/config/ckserver.json @@ -1,5 +1,10 @@ { - "WebServerAddr":"204.79.197.200:443", + "ProxyBook":{ + "shadowsocks":"127.0.0.1:8388", + "openvpn":"127.0.0.1:8389", + "tor":"127.0.0.1:9001" + }, + "RedirAddr":"204.79.197.200:443", "PrivateKey":"EN5aPEpNBO+vw+BtFQY2OnK9bQU7rvEj5qmnmgwEtUc=", "AdminUID":"ugDmcEmxWf0pKxfkZ/8EoP35Ht+wQnqf3L0xYgyQFlQ=", "DatabasePath":"userinfo.db", diff --git a/internal/client/TLS/TLS.go b/internal/client/TLS/TLS.go index 9acab54..c672317 100644 --- a/internal/client/TLS/TLS.go +++ b/internal/client/TLS/TLS.go @@ -49,13 +49,13 @@ func addExtRec(typ []byte, data []byte) []byte { // ComposeInitHandshake composes ClientHello with record layer func ComposeInitHandshake(sta *client.State) []byte { var ch []byte - switch sta.MaskBrowser { + switch sta.BrowserSig { case "chrome": ch = (&chrome{}).composeClientHello(sta) case "firefox": ch = (&firefox{}).composeClientHello(sta) default: - panic("Unsupported browser:" + sta.MaskBrowser) + panic("Unsupported browser:" + sta.BrowserSig) } return util.AddRecordLayer(ch, []byte{0x16}, []byte{0x03, 0x01}) } diff --git a/internal/client/auth.go b/internal/client/auth.go index 8fee4b2..c5fb401 100644 --- a/internal/client/auth.go +++ b/internal/client/auth.go @@ -34,7 +34,7 @@ func MakeRandomField(sta *State) []byte { } func MakeSessionTicket(sta *State) []byte { - // sessionTicket: [marshalled ephemeral pub key 32 bytes][encrypted UID+sessionID 36 bytes][padding 124 bytes] + // sessionTicket: [marshalled ephemeral pub key 32 bytes][encrypted UID+sessionID 36 bytes and proxy method 16 bytes][padding 108 bytes] // The first 16 bytes of the marshalled ephemeral public key is used as the IV // for encrypting the UID tthInterval := sta.Now().Unix() / int64(sta.TicketTimeHint) @@ -52,11 +52,12 @@ func MakeSessionTicket(sta *State) []byte { ticket := make([]byte, 192) copy(ticket[0:32], ecdh.Marshal(ephKP.PublicKey)) key := ecdh.GenerateSharedSecret(ephKP.PrivateKey, sta.staticPub) - plainUIDsID := make([]byte, 36) - copy(plainUIDsID, sta.UID) - binary.BigEndian.PutUint32(plainUIDsID[32:36], sta.sessionID) - cipherUIDsID := util.AESEncrypt(ticket[0:16], key, plainUIDsID) - copy(ticket[32:68], cipherUIDsID) + plain := make([]byte, 52) + copy(plain, sta.UID) + binary.BigEndian.PutUint32(plain[32:36], sta.sessionID) + copy(plain[36:52], []byte(sta.ProxyMethod)) + cipher := util.AESEncrypt(ticket[0:16], key, plain) + copy(ticket[32:84], cipher) // The purpose of adding sessionID is that, the generated padding of sessionTicket needs to be unpredictable. // As shown in auth.go, the padding is generated by a psudo random generator. The seed // needs to be the same for each TicketTimeHint interval. However the value of epoch/TicketTimeHint @@ -67,6 +68,6 @@ func MakeSessionTicket(sta *State) []byte { // With the sessionID value generated at startup of ckclient and used as a part of the seed, the // sessionTicket is still identical for each TicketTimeHint interval, but others won't be able to know // how it was generated. It will also be different for each client. - copy(ticket[68:192], util.PsudoRandBytes(124, tthInterval+int64(sta.sessionID))) + copy(ticket[84:192], util.PsudoRandBytes(108, tthInterval+int64(sta.sessionID))) return ticket } diff --git a/internal/client/state.go b/internal/client/state.go index 2d25cc3..d2ac28d 100644 --- a/internal/client/state.go +++ b/internal/client/state.go @@ -15,19 +15,20 @@ import ( type rawConfig struct { ServerName string + ProxyMethod string UID string PublicKey string TicketTimeHint int - MaskBrowser string + BrowserSig string NumConn int } // State stores global variables type State struct { - SS_LOCAL_HOST string - SS_LOCAL_PORT string - SS_REMOTE_HOST string - SS_REMOTE_PORT string + LocalHost string + LocalPort string + RemoteHost string + RemotePort string Now func() time.Time sessionID uint32 @@ -36,19 +37,20 @@ type State struct { keyPairsM sync.RWMutex keyPairs map[int64]*keyPair + ProxyMethod string TicketTimeHint int ServerName string - MaskBrowser string + BrowserSig string NumConn int } func InitState(localHost, localPort, remoteHost, remotePort string, nowFunc func() time.Time) *State { ret := &State{ - SS_LOCAL_HOST: localHost, - SS_LOCAL_PORT: localPort, - SS_REMOTE_HOST: remoteHost, - SS_REMOTE_PORT: remotePort, - Now: nowFunc, + LocalHost: localHost, + LocalPort: localPort, + RemoteHost: remoteHost, + RemotePort: remotePort, + Now: nowFunc, } ret.keyPairs = make(map[int64]*keyPair) return ret @@ -102,9 +104,10 @@ func (sta *State) ParseConfig(conf string) (err error) { if err != nil { return err } + sta.ProxyMethod = preParse.ProxyMethod sta.ServerName = preParse.ServerName sta.TicketTimeHint = preParse.TicketTimeHint - sta.MaskBrowser = preParse.MaskBrowser + sta.BrowserSig = preParse.BrowserSig sta.NumConn = preParse.NumConn uid, err := base64.StdEncoding.DecodeString(preParse.UID) if err != nil { diff --git a/internal/multiplex/frameSorter.go b/internal/multiplex/frameSorter.go index 1cf35f6..88ba386 100644 --- a/internal/multiplex/frameSorter.go +++ b/internal/multiplex/frameSorter.go @@ -9,13 +9,11 @@ import ( // order of arrival is not guaranteed. A stream's first packet may be sent through // connection0 and its second packet may be sent through connection1. Although both // packets are transmitted reliably (as TCP is reliable), packet1 may arrive to the -// remote side before packet0. -// -// However, shadowsocks' protocol does not provide sequence control. We must therefore -// make sure packets arrive in order. +// remote side before packet0. Cloak have to therefore sequence the packets so that they +// arrive in order as they were sent by the proxy software // // Cloak packets will have a 32-bit sequence number on them, so we know in which order -// they should be sent to shadowsocks. The code in this file provides buffering and sorting. +// they should be sent to the proxy software. The code in this file provides buffering and sorting. // // Similar to TCP, the next seq number after 2^32-1 is 0. This is called wrap around. // diff --git a/internal/server/auth.go b/internal/server/auth.go index f778b45..ac354a7 100644 --- a/internal/server/auth.go +++ b/internal/server/auth.go @@ -12,12 +12,12 @@ import ( ) // input ticket, return UID -func decryptSessionTicket(staticPv crypto.PrivateKey, ticket []byte) ([]byte, uint32) { +func decryptSessionTicket(staticPv crypto.PrivateKey, ticket []byte) ([]byte, uint32, string) { ephPub, _ := ecdh.Unmarshal(ticket[0:32]) key := ecdh.GenerateSharedSecret(staticPv, ephPub) - UIDsID := util.AESDecrypt(ticket[0:16], key, ticket[32:68]) - sessionID := binary.BigEndian.Uint32(UIDsID[32:36]) - return UIDsID[0:32], sessionID + plain := util.AESDecrypt(ticket[0:16], key, ticket[32:84]) + sessionID := binary.BigEndian.Uint32(plain[32:36]) + return plain[0:32], sessionID, string(bytes.Trim(plain[36:52], "\x00")) } func validateRandom(random []byte, UID []byte, time int64) bool { @@ -32,7 +32,7 @@ func validateRandom(random []byte, UID []byte, time int64) bool { h.Write(preHash) return bytes.Equal(h.Sum(nil)[0:16], random[16:32]) } -func TouchStone(ch *ClientHello, sta *State) (isSS bool, UID []byte, sessionID uint32) { +func TouchStone(ch *ClientHello, sta *State) (isCK bool, UID []byte, sessionID uint32, proxyMethod string) { var random [32]byte copy(random[:], ch.random) @@ -43,17 +43,17 @@ func TouchStone(ch *ClientHello, sta *State) (isSS bool, UID []byte, sessionID u if used != 0 { log.Println("Replay! Duplicate random") - return false, nil, 0 + return false, nil, 0, "" } ticket := ch.extensions[[2]byte{0x00, 0x23}] if len(ticket) < 68 { - return false, nil, 0 + return false, nil, 0, "" } - UID, sessionID = decryptSessionTicket(sta.staticPv, ticket) - isSS = validateRandom(ch.random, UID, sta.Now().Unix()) - if !isSS { - return false, nil, 0 + UID, sessionID, proxyMethod = decryptSessionTicket(sta.staticPv, ticket) + isCK = validateRandom(ch.random, UID, sta.Now().Unix()) + if !isCK { + return false, nil, 0, "" } return diff --git a/internal/server/auth_test.go b/internal/server/auth_test.go index b7f0293..8d0233d 100644 --- a/internal/server/auth_test.go +++ b/internal/server/auth_test.go @@ -16,7 +16,7 @@ func TestDecryptSessionTicket(t *testing.T) { staticPv, _ := ecdh.Unmarshal(pvb) sessionTicket, _ := hex.DecodeString("f586223b50cada583d61dc9bf3d01cc3a45aab4b062ed6a31ead0badb87f7761aab4f9f737a1d8ff2a2aa4d50ceb808844588ee3c8fdf36c33a35ef5003e287337659c8164a7949e9e63623090763fc24d0386c8904e47bdd740e09dd9b395c72de669629c2a865ed581452d23306adf26de0c8a46ee05e3dac876f2bcd9a2de946d319498f579383d06b3e66b3aca05f533fdc5f017eeba45b42080aabd4f71151fa0dfc1b0e23be4ed3abdb47adc0d5740ca7b7689ad34426309fb6984a086") - decryUID, decrySessionID := decryptSessionTicket(staticPv, sessionTicket) + decryUID, decrySessionID, _ := decryptSessionTicket(staticPv, sessionTicket) if !bytes.Equal(decryUID, UID) { t.Error( "For", "UID", diff --git a/internal/server/state.go b/internal/server/state.go index 9235394..d4f4178 100644 --- a/internal/server/state.go +++ b/internal/server/state.go @@ -6,7 +6,6 @@ import ( "encoding/json" "errors" "io/ioutil" - "strings" "sync" "time" @@ -14,7 +13,8 @@ import ( ) type rawConfig struct { - WebServerAddr string + ProxyBook map[string]string + RedirAddr string PrivateKey string AdminUID string DatabasePath string @@ -23,10 +23,10 @@ type rawConfig struct { // State type stores the global state of the program type State struct { - SS_LOCAL_HOST string - SS_LOCAL_PORT string - SS_REMOTE_HOST string - SS_REMOTE_PORT string + ProxyBook map[string]string + + BindHost string + BindPort string Now func() time.Time AdminUID []byte @@ -35,62 +35,35 @@ type State struct { usedRandomM sync.RWMutex usedRandom map[[32]byte]int - WebServerAddr string + RedirAddr string } -func InitState(localHost, localPort, remoteHost, remotePort string, nowFunc func() time.Time) (*State, error) { +func InitState(bindHost, bindPort string, nowFunc func() time.Time) (*State, error) { ret := &State{ - SS_LOCAL_HOST: localHost, - SS_LOCAL_PORT: localPort, - SS_REMOTE_HOST: remoteHost, - SS_REMOTE_PORT: remotePort, - Now: nowFunc, + BindHost: bindHost, + BindPort: bindPort, + Now: nowFunc, } ret.usedRandom = make(map[[32]byte]int) return ret, nil } -// semi-colon separated value. -func ssvToJson(ssv string) (ret []byte) { - unescape := func(s string) string { - r := strings.Replace(s, `\\`, `\`, -1) - r = strings.Replace(r, `\=`, `=`, -1) - r = strings.Replace(r, `\;`, `;`, -1) - return r - } - lines := strings.Split(unescape(ssv), ";") - ret = []byte("{") - for _, ln := range lines { - if ln == "" { - break - } - sp := strings.SplitN(ln, "=", 2) - key := sp[0] - value := sp[1] - ret = append(ret, []byte(`"`+key+`":"`+value+`",`)...) - - } - ret = ret[:len(ret)-1] // remove the last comma - ret = append(ret, '}') - return ret -} - // ParseConfig parses the config (either a path to json or in-line ssv config) into a State variable func (sta *State) ParseConfig(conf string) (err error) { var content []byte - if strings.Contains(conf, ";") && strings.Contains(conf, "=") { - content = ssvToJson(conf) - } else { - content, err = ioutil.ReadFile(conf) - if err != nil { - return err - } - } - var preParse rawConfig - err = json.Unmarshal(content, &preParse) - if err != nil { - return errors.New("Failed to unmarshal: " + err.Error()) + + content, errPath := ioutil.ReadFile(conf) + if errPath != nil { + errJson := json.Unmarshal(content, &preParse) + if errJson != nil { + return errors.New("Failed to read/unmarshal configuration, path is invalid or " + errJson.Error()) + } + } else { + errJson := json.Unmarshal(content, &preParse) + if errJson != nil { + return errors.New("Failed to read configuration file: " + errJson.Error()) + } } up, err := usermanager.MakeUserpanel(preParse.DatabasePath, preParse.BackupDirPath) @@ -99,7 +72,8 @@ func (sta *State) ParseConfig(conf string) (err error) { } sta.Userpanel = up - sta.WebServerAddr = preParse.WebServerAddr + sta.RedirAddr = preParse.RedirAddr + sta.ProxyBook = preParse.ProxyBook pvBytes, err := base64.StdEncoding.DecodeString(preParse.PrivateKey) if err != nil { diff --git a/internal/server/state_test.go b/internal/server/state_test.go deleted file mode 100644 index 7f76cd6..0000000 --- a/internal/server/state_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package server - -import ( - "bytes" - "testing" -) - -func TestSSVtoJson(t *testing.T) { - ssv := "WebServerAddr=204.79.197.200:443;PrivateKey=EN5aPEpNBO+vw+BtFQY2OnK9bQU7rvEj5qmnmgwEtUc=;AdminUID=ugDmcEmxWf0pKxfkZ/8EoP35Ht+wQnqf3L0xYgyQFlQ=;DatabasePath=userinfo.db;BackupDirPath=;" - json := ssvToJson(ssv) - expected := []byte(`{"WebServerAddr":"204.79.197.200:443","PrivateKey":"EN5aPEpNBO+vw+BtFQY2OnK9bQU7rvEj5qmnmgwEtUc=","AdminUID":"ugDmcEmxWf0pKxfkZ/8EoP35Ht+wQnqf3L0xYgyQFlQ=","DatabasePath":"userinfo.db","BackupDirPath":""}`) - if !bytes.Equal(expected, json) { - t.Error( - "For", "ssvToJson", - "expecting", string(expected), - "got", string(json), - ) - } - -}