From 55d6dc16d629406774c3438f0a851135ad67fbdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ib=C3=A1=C3=B1ez?= Date: Fri, 30 Apr 2021 20:47:14 +0200 Subject: [PATCH 1/3] [cfg] Make parseBaseConfig() independent of cmd/gost/main.go --- cmd/gost/cfg.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/gost/cfg.go b/cmd/gost/cfg.go index b8fda231..05071997 100644 --- a/cmd/gost/cfg.go +++ b/cmd/gost/cfg.go @@ -25,18 +25,18 @@ type baseConfig struct { Debug bool } -func parseBaseConfig(s string) (*baseConfig, error) { +func parseBaseConfig(s string, baseCfg *baseConfig) error { file, err := os.Open(s) if err != nil { - return nil, err + return err } defer file.Close() if err := json.NewDecoder(file).Decode(baseCfg); err != nil { - return nil, err + return err } - return baseCfg, nil + return nil } var ( From 7e5358bcf3152dc126d5f82d8d4bec9fba40dd70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ib=C3=A1=C3=B1ez?= Date: Sat, 1 May 2021 01:36:14 +0200 Subject: [PATCH 2/3] [main] Add -- cmd flag separator to start different Gost instances --- cmd/gost/main.go | 194 ++++++++++++++++++++++++++++++----------------- 1 file changed, 125 insertions(+), 69 deletions(-) diff --git a/cmd/gost/main.go b/cmd/gost/main.go index ce9184c5..0567002c 100644 --- a/cmd/gost/main.go +++ b/cmd/gost/main.go @@ -7,6 +7,8 @@ import ( "fmt" "net/http" "os" + "sync" + "strings" "runtime" _ "net/http/pprof" @@ -16,58 +18,148 @@ import ( ) var ( - configureFile string - baseCfg = &baseConfig{} - pprofAddr string pprofEnabled = os.Getenv("PROFILING") != "" ) func init() { gost.SetLogger(&gost.LogLogger{}) + // TODO - Generate different certificates for each worker + generateTLSCertificate() +} + +func main() { + var wg sync.WaitGroup + wg.Add(1) // Gost must exit if any of the workers exit + + // Split os.Args using -- and create a worker with each slice + args := strings.Split(" " + strings.Join(os.Args[1:], " ") + " ", " -- ") + if strings.Join(args, "") == "" { + // Fix to show gost help if the resulting array is empty + args[0] = " " + } + for wid, wargs := range args { + if wargs != "" { + go worker(wid, wargs, &wg) + } + } + wg.Wait() +} + +func worker(id int, args string, wg *sync.WaitGroup) { + defer wg.Done() + var ( - printVersion bool + configureFile string + baseCfg = &baseConfig{} + pprofAddr string ) - flag.Var(&baseCfg.route.ChainNodes, "F", "forward address, can make a forward chain") - flag.Var(&baseCfg.route.ServeNodes, "L", "listen address, can listen on multiple ports (required)") - flag.IntVar(&baseCfg.route.Mark, "M", 0, "Specify out connection mark") - flag.StringVar(&configureFile, "C", "", "configure file") - flag.StringVar(&baseCfg.route.Interface, "I", "", "Interface to bind") - flag.BoolVar(&baseCfg.Debug, "D", false, "enable debug log") - flag.BoolVar(&printVersion, "V", false, "print version") - if pprofEnabled { - flag.StringVar(&pprofAddr, "P", ":6060", "profiling HTTP server address") - } - flag.Parse() + init := func () error { + var printVersion bool + + wf := flag.NewFlagSet(os.Args[0], flag.ExitOnError) + + wf.Var(&baseCfg.route.ChainNodes, "F", "forward address, can make a forward chain") + wf.Var(&baseCfg.route.ServeNodes, "L", "listen address, can listen on multiple ports (required)") + wf.StringVar(&configureFile, "C", "", "configure file") + wf.BoolVar(&baseCfg.Debug, "D", false, "enable debug log") + wf.BoolVar(&printVersion, "V", false, "print version") + + if pprofEnabled { + // Every worker uses a different profiling server by default + wf.StringVar(&pprofAddr, "P", fmt.Sprintf(":606%d", id), "profiling HTTP server address") + } + + wf.Parse(strings.Fields(args)) + + if printVersion { + fmt.Fprintf(os.Stdout, "gost %s (%s %s/%s)\n", gost.Version, runtime.Version(), runtime.GOOS, runtime.GOARCH) + os.Exit(0) + } else if wf.NFlag() == 0 { + wf.Usage() + os.Exit(0) + } else if configureFile != "" { + err := parseBaseConfig(configureFile, baseCfg) + if err != nil { + return err + } + } - if printVersion { - fmt.Fprintf(os.Stdout, "gost %s (%s %s/%s)\n", - gost.Version, runtime.Version(), runtime.GOOS, runtime.GOARCH) - os.Exit(0) + if baseCfg.route.ServeNodes.String() == "[]" { + configErrMsg := "" + if configureFile != "" { + configErrMsg = " or ServeNodes inside config file (-C)" + } + fmt.Fprintf(os.Stderr, "\n[!] Error: Missing -L flag%s\n\n", configErrMsg) + wf.Usage() + os.Exit(1) + } + + return nil } - if configureFile != "" { - _, err := parseBaseConfig(configureFile) + start := func () error { + // TODO - Make debug worker independent + if ! gost.Debug { + gost.Debug = baseCfg.Debug + } + + var routers []router + rts, err := baseCfg.route.GenRouters() if err != nil { - log.Log(err) - os.Exit(1) + return err + } + routers = append(routers, rts...) + + for _, route := range baseCfg.Routes { + rts, err := route.GenRouters() + if err != nil { + return err + } + routers = append(routers, rts...) + } + + if len(routers) == 0 { + return errors.New("invalid config") } + for i := range routers { + go routers[i].Serve() + } + + return nil } - if flag.NFlag() == 0 { - flag.PrintDefaults() - os.Exit(0) + + main := func () error { + if pprofEnabled { + go func() { + log.Log("profiling server on", pprofAddr) + log.Log(http.ListenAndServe(pprofAddr, nil)) + }() + } + + err := start() + return err } -} -func main() { - if pprofEnabled { - go func() { - log.Log("profiling server on", pprofAddr) - log.Log(http.ListenAndServe(pprofAddr, nil)) - }() + if err := init(); err != nil { + log.Log(err) + return + } + if err := main(); err != nil { + log.Log(err) + return } + // Allow local functions to be garbage-collected + init = nil + main = nil + start = nil + + select {} +} + +func generateTLSCertificate() { // NOTE: as of 2.6, you can use custom cert/key files to initialize the default certificate. tlsConfig, err := tlsConfig(defaultCertFile, defaultKeyFile, "") if err != nil { @@ -83,41 +175,5 @@ func main() { } else { log.Log("load TLS certificate files OK") } - gost.DefaultTLSConfig = tlsConfig - - if err := start(); err != nil { - log.Log(err) - os.Exit(1) - } - - select {} -} - -func start() error { - gost.Debug = baseCfg.Debug - - var routers []router - rts, err := baseCfg.route.GenRouters() - if err != nil { - return err - } - routers = append(routers, rts...) - - for _, route := range baseCfg.Routes { - rts, err := route.GenRouters() - if err != nil { - return err - } - routers = append(routers, rts...) - } - - if len(routers) == 0 { - return errors.New("invalid config") - } - for i := range routers { - go routers[i].Serve() - } - - return nil } From a7f9bbef63fe739dde47b1f87d64f4881acbae12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ib=C3=A1=C3=B1ez?= Date: Sat, 1 May 2021 13:55:02 +0200 Subject: [PATCH 3/3] [README_en] Add Multi-Instance (-- flag) explanation and examples --- README_en.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/README_en.md b/README_en.md index 3fe6b9a8..fcb9a667 100644 --- a/README_en.md +++ b/README_en.md @@ -28,6 +28,7 @@ Features * [Routing control](https://v2.gost.run/en/bypass/) * DNS [resolver](https://v2.gost.run/resolver/) and [proxy](https://v2.gost.run/dns/) * [TUN/TAP device](https://v2.gost.run/en/tuntap/) +* [Multi-Instance](#Multi-Instance) Wiki: [v2.gost.run](https://v2.gost.run/en/) @@ -418,3 +419,47 @@ gost -L=:8080 -F="http2://:443?ca=ca.pem" ``` Certificate Pinning is contributed by [@sheerun](https://github.com/sheerun). + +Multi-Instance +------ + +Run multiple gost instances with different rules and configuration files by separating each with `--` + +#### Reverse SOCKS5 over SSH tunnel +```bash +# Server +gost -L forward+ssh://:2222 + +# Client +gost -L socks5://127.0.0.1:1111 -- -L rtcp://127.0.0.1:3333/127.0.0.1:1111 -F forward+ssh://:2222 + +# Test from Server +curl -s -L -x socks5://127.0.0.1:3333 https://example.com +``` + +#### Multiple port-forwarding through different proxies +```bash +gost -- -L tcp://:2222/192.168.1.9:22 -F forward+ssh://172.25.10.3:22 -F forward+ssh://70.9.17.2:22 \ + -- -L tcp://:8080/10.10.10.10:80 -F forward+tls://90.33.2.11:443 \ + -- -L udp://:5353/192.10.16.8:53 -F socks5://189.155.221.25:1080 +``` + +#### Multiple configuration files +```bash +gost -C tls.json -- -C hyper-proxy.json -- -C reverse-nc.json -- -C happy-vpn.json +``` + +#### A mix of everything +```bash +gost -L rudp://:5353/192.168.1.1:53?ttl=60s -F socks5://172.24.10.1:1080 -- \ + -C my-proxy.json -- \ + -L redirect://:1234 -F 1.2.3.4:1080 -- \ + -L udp://:5353 -C forward-servers.json -- \ + -L :8080 -F http://localhost:8080?ip=192.168.1.2:8081,192.168.1.3:8082 \ + -F socks5://localhost:1080?ip=172.20.1.1:1080,172.20.1.2:1081 -- \ + -L socks5://localhost:1080 -- \ + -L :2020 -F kcp://10.16.1.10:8388?peer=peer1.txt \ + -F http2://12.20.1.3:443?peer=peer2.txt +``` + +Multi-Instance was contributed by [@caribpa](https://github.com/caribpa).