diff --git a/cmd/ck-server/ck-server.go b/cmd/ck-server/ck-server.go index 0e5f9a6..666cb63 100644 --- a/cmd/ck-server/ck-server.go +++ b/cmd/ck-server/ck-server.go @@ -17,19 +17,25 @@ import ( var version string +func parseBindAddr(bindAddrs []string) ([]net.Addr, error) { + var addrs []net.Addr + for _, addr := range bindAddrs { + bindAddr, err := net.ResolveTCPAddr("tcp", addr) + if err != nil { + return nil, err + } + addrs = append(addrs, bindAddr) + } + return addrs, nil +} + func main() { - // set TLS bind host through commandline for legacy support, default 0.0.0,0 - var ssRemoteHost string - // set TLS bind port through commandline for legacy support, default 443 - var ssRemotePort string var config string var pluginMode bool if os.Getenv("SS_LOCAL_HOST") != "" && os.Getenv("SS_LOCAL_PORT") != "" { pluginMode = true - ssRemoteHost = os.Getenv("SS_REMOTE_HOST") - ssRemotePort = os.Getenv("SS_REMOTE_PORT") config = os.Getenv("SS_PLUGIN_OPTIONS") } else { flag.StringVar(&config, "c", "server.json", "config: path to the configuration file or its content") @@ -79,29 +85,31 @@ func main() { log.Infof("Starting standalone mode") } - sta, _ := server.InitState(time.Now) - err := sta.ParseConfig(config) + raw, err := server.ParseConfig(config) if err != nil { log.Fatalf("Configuration file error: %v", err) } - if !pluginMode && len(sta.BindAddr) == 0 { + bindAddr, err := parseBindAddr(raw.BindAddr) + if err != nil { + err = fmt.Errorf("unable to parse BindAddr: %v", err) + return + } + if !pluginMode && len(bindAddr) == 0 { https, _ := net.ResolveTCPAddr("tcp", ":443") http, _ := net.ResolveTCPAddr("tcp", ":80") - sta.BindAddr = []net.Addr{https, http} + bindAddr = []net.Addr{https, http} } // when cloak is started as a shadowsocks plugin if pluginMode { ssLocalHost := os.Getenv("SS_LOCAL_HOST") ssLocalPort := os.Getenv("SS_LOCAL_PORT") + raw.ProxyBook["shadowsocks"] = []string{"tcp", net.JoinHostPort(ssLocalHost, ssLocalPort)} - sta.ProxyBook["shadowsocks"], err = net.ResolveTCPAddr("tcp", net.JoinHostPort(ssLocalHost, ssLocalPort)) - if err != nil { - log.Fatal(err) - } - + ssRemoteHost := os.Getenv("SS_REMOTE_HOST") + ssRemotePort := os.Getenv("SS_REMOTE_PORT") var ssBind string // When listening on an IPv6 and IPv4, SS gives REMOTE_HOST as e.g. ::|0.0.0.0 v4nv6 := len(strings.Split(ssRemoteHost, "|")) == 2 @@ -116,7 +124,7 @@ func main() { } shouldAppend := true - for i, addr := range sta.BindAddr { + for i, addr := range bindAddr { if addr.String() == ssBindAddr.String() { shouldAppend = false } @@ -128,15 +136,20 @@ func main() { // listen on both if ssBindAddr.String() == ":"+ssRemotePort { shouldAppend = true - sta.BindAddr[i] = ssBindAddr + bindAddr[i] = ssBindAddr } } } if shouldAppend { - sta.BindAddr = append(sta.BindAddr, ssBindAddr) + bindAddr = append(bindAddr, ssBindAddr) } } + sta, err := server.InitState(raw, time.Now) + if err != nil { + log.Fatalf("unable to initialise server state: %v", err) + } + listen := func(bindAddr net.Addr) { listener, err := net.Listen("tcp", bindAddr.String()) log.Infof("Listening on %v", bindAddr) @@ -153,8 +166,8 @@ func main() { } } - for i, addr := range sta.BindAddr { - if i != len(sta.BindAddr)-1 { + for i, addr := range bindAddr { + if i != len(bindAddr)-1 { go listen(addr) } else { listen(addr) diff --git a/internal/server/state.go b/internal/server/state.go index 3fb8d46..facab60 100644 --- a/internal/server/state.go +++ b/internal/server/state.go @@ -16,7 +16,7 @@ import ( gmux "github.com/gorilla/mux" ) -type rawConfig struct { +type RawConfig struct { ProxyBook map[string][]string BindAddr []string BypassUID [][]byte @@ -31,7 +31,6 @@ type rawConfig struct { // State type stores the global state of the program type State struct { - BindAddr []net.Addr ProxyBook map[string]net.Addr ProxyDialer common.Dialer @@ -55,17 +54,6 @@ type State struct { LocalAPIRouter *gmux.Router } -func InitState(nowFunc func() time.Time) (*State, error) { - ret := &State{ - Now: nowFunc, - BypassUID: make(map[[16]byte]struct{}), - ProxyBook: map[string]net.Addr{}, - usedRandom: map[[32]byte]int64{}, - } - go ret.UsedRandomCleaner() - return ret, nil -} - func parseRedirAddr(redirAddr string) (net.Addr, string, error) { var host string var port string @@ -109,18 +97,6 @@ func parseLocalPanel(databasePath string) (*userPanel, *gmux.Router, error) { } -func parseBindAddr(bindAddrs []string) ([]net.Addr, error) { - var addrs []net.Addr - for _, addr := range bindAddrs { - bindAddr, err := net.ResolveTCPAddr("tcp", addr) - if err != nil { - return nil, err - } - addrs = append(addrs, bindAddr) - } - return addrs, nil -} - func parseProxyBook(bookEntries map[string][]string) (map[string]net.Addr, error) { proxyBook := map[string]net.Addr{} for name, pair := range bookEntries { @@ -149,28 +125,40 @@ func parseProxyBook(bookEntries map[string][]string) (map[string]net.Addr, error return proxyBook, nil } -// ParseConfig parses the config (either a path to json or the json itself as argument) into a State variable -func (sta *State) ParseConfig(conf string) (err error) { - var content []byte - var preParse rawConfig - +func ParseConfig(conf string) (raw RawConfig, err error) { content, errPath := ioutil.ReadFile(conf) if errPath != nil { - errJson := json.Unmarshal(content, &preParse) + errJson := json.Unmarshal(content, &raw) if errJson != nil { - return errors.New("Failed to read/unmarshal configuration, path is invalid or " + errJson.Error()) + err = fmt.Errorf("failed to read/unmarshal configuration, path is invalid or %v", errJson) + return } } else { - errJson := json.Unmarshal(content, &preParse) + errJson := json.Unmarshal(content, &raw) if errJson != nil { - return errors.New("Failed to read configuration file: " + errJson.Error()) + err = fmt.Errorf("failed to read configuration file: %v", errJson) + return } } + return +} +// ParseConfig parses the config (either a path to json or the json itself as argument) into a State variable +func InitState(preParse RawConfig, nowFunc func() time.Time) (sta *State, err error) { + sta = &State{ + Now: nowFunc, + BypassUID: make(map[[16]byte]struct{}), + ProxyBook: map[string]net.Addr{}, + usedRandom: map[[32]byte]int64{}, + } if preParse.CncMode { - return errors.New("command & control mode not implemented") + err = errors.New("command & control mode not implemented") + return } else { sta.Panel, sta.LocalAPIRouter, err = parseLocalPanel(preParse.DatabasePath) + if err != nil { + return + } } if preParse.StreamTimeout == 0 { @@ -187,17 +175,14 @@ func (sta *State) ParseConfig(conf string) (err error) { sta.RedirHost, sta.RedirPort, err = parseRedirAddr(preParse.RedirAddr) if err != nil { - return fmt.Errorf("unable to parse RedirAddr: %v", err) - } - - sta.BindAddr, err = parseBindAddr(preParse.BindAddr) - if err != nil { - return fmt.Errorf("unable to parse BindAddr: %v", err) + err = fmt.Errorf("unable to parse RedirAddr: %v", err) + return } sta.ProxyBook, err = parseProxyBook(preParse.ProxyBook) if err != nil { - return fmt.Errorf("unable to parse ProxyBook: %v", err) + err = fmt.Errorf("unable to parse ProxyBook: %v", err) + return } var pv [32]byte @@ -214,7 +199,8 @@ func (sta *State) ParseConfig(conf string) (err error) { copy(arrUID[:], sta.AdminUID) sta.BypassUID[arrUID] = struct{}{} - return nil + go sta.UsedRandomCleaner() + return sta, nil } // IsBypass checks if a UID is a bypass user