func main() { log.SetOutput(os.Stdout) var configFile, cmdServer, cmdLocal string var cmdConfig ss.Config var printVer bool flag.BoolVar(&printVer, "version", false, "print version") flag.StringVar(&configFile, "c", "config.json", "specify config file") flag.StringVar(&cmdServer, "s", "", "server address") flag.StringVar(&cmdLocal, "b", "", "local address, listen only to this address if specified") flag.StringVar(&cmdConfig.Password, "k", "", "password") flag.IntVar(&cmdConfig.ServerPort, "p", 0, "server port") flag.IntVar(&cmdConfig.Timeout, "t", 300, "timeout in seconds") flag.IntVar(&cmdConfig.LocalPort, "l", 0, "local socks5 proxy port") flag.StringVar(&cmdConfig.Method, "m", "", "encryption method, default: aes-256-cfb") flag.BoolVar((*bool)(&debug), "d", false, "print debug message") flag.Parse() if printVer { ss.PrintVersion() os.Exit(0) } cmdConfig.Server = cmdServer ss.SetDebug(debug) exists, err := ss.IsFileExists(configFile) // If no config file in current directory, try search it in the binary directory // Note there's no portable way to detect the binary directory. binDir := path.Dir(os.Args[0]) if (!exists || err != nil) && binDir != "" && binDir != "." { oldConfig := configFile configFile = path.Join(binDir, "config.json") log.Printf("%s not found, try config file %s\n", oldConfig, configFile) } config, err := ss.ParseConfig(configFile) if err != nil { config = &cmdConfig if !os.IsNotExist(err) { fmt.Fprintf(os.Stderr, "error reading %s: %v\n", configFile, err) os.Exit(1) } } else { ss.UpdateConfig(config, &cmdConfig) } if config.Method == "" { config.Method = "aes-256-cfb" } if len(config.ServerPassword) == 0 { if !enoughOptions(config) { fmt.Fprintln(os.Stderr, "must specify server address, password and both server/local port") os.Exit(1) } } else { if config.Password != "" || config.ServerPort != 0 || config.GetServerArray() != nil { fmt.Fprintln(os.Stderr, "given server_password, ignore server, server_port and password option:", config) } if config.LocalPort == 0 { fmt.Fprintln(os.Stderr, "must specify local port") os.Exit(1) } } parseServerConfig(config) run(cmdLocal + ":" + strconv.Itoa(config.LocalPort)) }
func parseServerConfig(config *ss.Config) { hasPort := func(s string) bool { _, port, err := net.SplitHostPort(s) if err != nil { return false } return port != "" } if len(config.ServerPassword) == 0 { method := config.Method if config.Auth { method += "-ota" } // only one encryption table cipher, err := ss.NewCipher(method, config.Password) if err != nil { log.Fatal("Failed generating ciphers:", err) } srvPort := strconv.Itoa(config.ServerPort) srvArr := config.GetServerArray() n := len(srvArr) servers.srvCipher = make([]*ServerCipher, n) for i, s := range srvArr { if hasPort(s) { log.Println("ignore server_port option for server", s) servers.srvCipher[i] = &ServerCipher{s, cipher} } else { servers.srvCipher[i] = &ServerCipher{net.JoinHostPort(s, srvPort), cipher} } } } else { // multiple servers n := len(config.ServerPassword) servers.srvCipher = make([]*ServerCipher, n) cipherCache := make(map[string]*ss.Cipher) i := 0 for _, serverInfo := range config.ServerPassword { if len(serverInfo) < 2 || len(serverInfo) > 3 { log.Fatalf("server %v syntax error\n", serverInfo) } server := serverInfo[0] passwd := serverInfo[1] encmethod := "" if len(serverInfo) == 3 { encmethod = serverInfo[2] } if !hasPort(server) { log.Fatalf("no port for server %s\n", server) } // Using "|" as delimiter is safe here, since no encryption // method contains it in the name. cacheKey := encmethod + "|" + passwd cipher, ok := cipherCache[cacheKey] if !ok { var err error cipher, err = ss.NewCipher(encmethod, passwd) if err != nil { log.Fatal("Failed generating ciphers:", err) } cipherCache[cacheKey] = cipher } servers.srvCipher[i] = &ServerCipher{server, cipher} i++ } } servers.failCnt = make([]int, len(servers.srvCipher)) for _, se := range servers.srvCipher { log.Println("available remote server", se.server) } return }