From 8a43914987658beec259740ad8554a3e67da5a10 Mon Sep 17 00:00:00 2001 From: shadowy-pycoder <35629483+shadowy-pycoder@users.noreply.github.com> Date: Fri, 13 Jun 2025 08:58:10 +0300 Subject: [PATCH 1/4] added daemon mode --- cmd/gohpts/cli.go | 51 +++++++++++++++++++++++++++++++++++++++-------- gohpts.go | 29 +++++++++++++++++++-------- 2 files changed, 64 insertions(+), 16 deletions(-) diff --git a/cmd/gohpts/cli.go b/cmd/gohpts/cli.go index 3a0e573..2db0e0f 100644 --- a/cmd/gohpts/cli.go +++ b/cmd/gohpts/cli.go @@ -43,6 +43,7 @@ func root(args []string) error { flags.StringVar(&conf.CertFile, "c", "", "Path to certificate PEM encoded file") flags.StringVar(&conf.KeyFile, "k", "", "Path to private key PEM encoded file") flags.StringVar(&conf.ServerConfPath, "f", "", "Path to server configuration file in YAML format") + daemon := flags.Bool("D", false, "Run as a daemon (provide -logfile to see logs)") if runtime.GOOS == tproxyOS { flags.StringVar(&conf.TProxy, "t", "", "Address of transparent proxy server (it starts along with HTTP proxy server)") flags.StringVar(&conf.TProxyOnly, "T", "", "Address of transparent proxy server (no HTTP)") @@ -55,14 +56,9 @@ func root(args []string) error { return nil }) } - flags.BoolFunc("d", "Show logs in DEBUG mode", func(flagValue string) error { - conf.Debug = true - return nil - }) - flags.BoolFunc("j", "Show logs in JSON format", func(flagValue string) error { - conf.Json = true - return nil - }) + flags.StringVar(&conf.LogFilePath, "logfile", "", "Log file path (Default: stdout") + flags.BoolVar(&conf.Debug, "d", false, "Show logs in DEBUG mode") + flags.BoolVar(&conf.Json, "j", false, "Show logs in JSON format") flags.BoolFunc("v", "print version", func(flagValue string) error { fmt.Println(gohpts.Version) os.Exit(0) @@ -112,6 +108,11 @@ func root(args []string) error { } } } + if seen["D"] { + if seen["u"] || seen["U"] { + return fmt.Errorf("-u and -U flags do not work in daemon mode") + } + } if seen["u"] { fmt.Print("SOCKS5 Password: ") bytepw, err := term.ReadPassword(int(os.Stdin.Fd())) @@ -131,6 +132,40 @@ func root(args []string) error { fmt.Print("\033[2K\r") } + if *daemon { + if os.Getenv("GOHPTS_DAEMON") != "1" { + env := os.Environ() + files := [3]*os.File{} + env = append(env, "GOHPTS_DAEMON=1") + files[0], _ = os.Open(os.DevNull) + files[1], _ = os.Open(os.DevNull) + files[2], _ = os.Open(os.DevNull) + attr := &os.ProcAttr{ + Files: []*os.File{ + files[0], // stdin + files[1], // stdout + files[2], // stderr + }, + Dir: ".", + Env: env, + } + path, err := os.Executable() + if err != nil { + return err + } + process, err := os.StartProcess( + path, + os.Args, + attr, + ) + if err != nil { + return err + } + fmt.Println("gohpts pid:", process.Pid) + process.Release() + os.Exit(0) + } + } p := gohpts.New(&conf) p.Run() return nil diff --git a/gohpts.go b/gohpts.go index defe8e7..0991f4b 100644 --- a/gohpts.go +++ b/gohpts.go @@ -696,8 +696,6 @@ func (p *proxyapp) Run() { type Config struct { AddrHTTP string AddrSOCKS string - Debug bool - Json bool User string Pass string ServerUser string @@ -708,20 +706,25 @@ type Config struct { TProxy string TProxyOnly string TProxyMode string + LogFilePath string + Debug bool + Json bool } type logWriter struct { + file *os.File } func (writer logWriter) Write(bytes []byte) (int, error) { - return fmt.Print(fmt.Sprintf("%s | ERROR | %s", time.Now().Format(time.RFC3339), string(bytes))) + return fmt.Fprintf(writer.file, fmt.Sprintf("%s | ERROR | %s", time.Now().Format(time.RFC3339), string(bytes))) } type jsonLogWriter struct { + file *os.File } func (writer jsonLogWriter) Write(bytes []byte) (int, error) { - return fmt.Print(fmt.Sprintf("{\"level\":\"error\",\"time\":\"%s\",\"message\":\"%s\"}\n", + return fmt.Fprintf(writer.file, fmt.Sprintf("{\"level\":\"error\",\"time\":\"%s\",\"message\":\"%s\"}\n", time.Now().Format(time.RFC3339), strings.TrimRight(string(bytes), "\n"))) } @@ -782,14 +785,24 @@ func expandPath(p string) string { func New(conf *Config) *proxyapp { var logger zerolog.Logger var p proxyapp + var logfile *os.File = os.Stdout + if conf.LogFilePath != "" { + f, err := os.OpenFile(conf.LogFilePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) + if err != nil { + log.Fatalf("Failed to open log file: %v", err) + } + logfile = f + } if conf.Json { log.SetFlags(0) - log.SetOutput(new(jsonLogWriter)) - logger = zerolog.New(os.Stdout).With().Timestamp().Logger() + jsonWriter := jsonLogWriter{file: logfile} + log.SetOutput(jsonWriter) + logger = zerolog.New(logfile).With().Timestamp().Logger() } else { log.SetFlags(0) - log.SetOutput(new(logWriter)) - output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339, NoColor: true} + logWriter := logWriter{file: logfile} + log.SetOutput(logWriter) + output := zerolog.ConsoleWriter{Out: logfile, TimeFormat: time.RFC3339, NoColor: true} output.FormatLevel = func(i any) string { return strings.ToUpper(fmt.Sprintf("| %-6s|", i)) } From 347b9190bdfa695e9a2ea0974845376c559ed1b2 Mon Sep 17 00:00:00 2001 From: shadowy-pycoder <35629483+shadowy-pycoder@users.noreply.github.com> Date: Fri, 13 Jun 2025 09:05:00 +0300 Subject: [PATCH 2/4] added new flags to error messages --- cmd/gohpts/cli.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/gohpts/cli.go b/cmd/gohpts/cli.go index 2db0e0f..1bf5427 100644 --- a/cmd/gohpts/cli.go +++ b/cmd/gohpts/cli.go @@ -86,7 +86,7 @@ func root(args []string) error { if seen["T"] { for _, da := range []string{"U", "c", "k", "l"} { if seen[da] { - return fmt.Errorf("-T flag only works with -s, -u, -f, -M, -d and -j flags") + return fmt.Errorf("-T flag only works with -s, -u, -f, -M, -d, -D, -logfile and -j flags") } } if !seen["M"] { @@ -102,9 +102,9 @@ func root(args []string) error { for _, da := range []string{"s", "u", "U", "c", "k", "l"} { if seen[da] { if runtime.GOOS == tproxyOS { - return fmt.Errorf("-f flag only works with -t, -T, -M, -d and -j flags") + return fmt.Errorf("-f flag only works with -t, -T, -M, -d, -D, -logfile and -j flags") } - return fmt.Errorf("-f flag only works with -d and -j flags") + return fmt.Errorf("-f flag only works with -d, -D, -logfile and -j flags") } } } From 64974b84bcc396b3674a0bd6a1924fb9031f21b3 Mon Sep 17 00:00:00 2001 From: shadowy-pycoder <35629483+shadowy-pycoder@users.noreply.github.com> Date: Fri, 13 Jun 2025 09:06:52 +0300 Subject: [PATCH 3/4] changed hardcoded name to variable --- cmd/gohpts/cli.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/gohpts/cli.go b/cmd/gohpts/cli.go index 1bf5427..1a1c3d2 100644 --- a/cmd/gohpts/cli.go +++ b/cmd/gohpts/cli.go @@ -161,7 +161,7 @@ func root(args []string) error { if err != nil { return err } - fmt.Println("gohpts pid:", process.Pid) + fmt.Println("%s pid:", app, process.Pid) process.Release() os.Exit(0) } From 555f3b228b3397beefac5ad200281207e91544d3 Mon Sep 17 00:00:00 2001 From: shadowy-pycoder <35629483+shadowy-pycoder@users.noreply.github.com> Date: Fri, 13 Jun 2025 12:14:23 +0300 Subject: [PATCH 4/4] updated README with daemon info --- README.md | 51 ++++++++++++++++++++++++++++++++++------------- cmd/gohpts/cli.go | 4 ++-- version.go | 2 +- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index bc70ce8..df703e0 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ You can download the binary for your platform from [Releases](https://github.com Example: ```shell -HPTS_RELEASE=v1.6.0; wget -v https://github.com/shadowy-pycoder/go-http-proxy-to-socks/releases/download/$HPTS_RELEASE/gohpts-$HPTS_RELEASE-linux-amd64.tar.gz -O gohpts && tar xvzf gohpts && mv -f gohpts-$HPTS_RELEASE-linux-amd64 gohpts && ./gohpts -h +HPTS_RELEASE=v1.6.1; wget -v https://github.com/shadowy-pycoder/go-http-proxy-to-socks/releases/download/$HPTS_RELEASE/gohpts-$HPTS_RELEASE-linux-amd64.tar.gz -O gohpts && tar xvzf gohpts && mv -f gohpts-$HPTS_RELEASE-linux-amd64 gohpts && ./gohpts -h ``` Alternatively, you can install it using `go install` command (requires Go [1.24](https://go.dev/doc/install) or later): @@ -105,29 +105,32 @@ GitHub: https://github.com/shadowy-pycoder/go-http-proxy-to-socks Usage: gohpts [OPTIONS] Options: -h Show this help message and exit. + -D Run as a daemon (provide -logfile to see logs) -M value - Transparent proxy mode: [redirect tproxy] + Transparent proxy mode: [redirect tproxy] -T string - Address of transparent proxy server (no HTTP) + Address of transparent proxy server (no HTTP) -U string - User for HTTP proxy (basic auth). This flag invokes prompt for password (not echoed to terminal) + User for HTTP proxy (basic auth). This flag invokes prompt for password (not echoed to terminal) -c string - Path to certificate PEM encoded file - -d Show logs in DEBUG mode + Path to certificate PEM encoded file + -d Show logs in DEBUG mode -f string - Path to server configuration file in YAML format - -j Show logs in JSON format + Path to server configuration file in YAML format + -j Show logs in JSON format -k string - Path to private key PEM encoded file + Path to private key PEM encoded file -l string - Address of HTTP proxy server (default "127.0.0.1:8080") + Address of HTTP proxy server (default "127.0.0.1:8080") + -logfile string + Log file path (Default: stdout) -s string - Address of SOCKS5 proxy server (default "127.0.0.1:1080") + Address of SOCKS5 proxy server (default "127.0.0.1:1080") -t string - Address of transparent proxy server (it starts along with HTTP proxy server) + Address of transparent proxy server (it starts along with HTTP proxy server) -u string - User for SOCKS5 proxy authentication. This flag invokes prompt for password (not echoed to terminal) - -v print version + User for SOCKS5 proxy authentication. This flag invokes prompt for password (not echoed to terminal) + -v print version ``` ### Configuration via CLI flags @@ -166,6 +169,26 @@ Run http proxy over TLS connection gohpts -s 1080 -l 8080 -c "path/to/certificate" -k "path/to/private/key" ``` +Run proxy as a daemon (logfile is needed for logging output, otherwise you will see nothing) + +```shell +gohpts -D -logfile /tmp/gohpts.log +``` + +```shell +# output +gohpts pid: +``` + +```shell +# kill the process +kill +#or +kill $(pidof gohpts) +``` + +`-u` and `-U` flags do not work in a daemon mode (and therefore authentication), but you can provide a config file (see below) + ### Configuration via YAML file Run http proxy in SOCKS5 proxy chain mode (specify server settings via YAML configuration file) diff --git a/cmd/gohpts/cli.go b/cmd/gohpts/cli.go index 1a1c3d2..0e90468 100644 --- a/cmd/gohpts/cli.go +++ b/cmd/gohpts/cli.go @@ -56,7 +56,7 @@ func root(args []string) error { return nil }) } - flags.StringVar(&conf.LogFilePath, "logfile", "", "Log file path (Default: stdout") + flags.StringVar(&conf.LogFilePath, "logfile", "", "Log file path (Default: stdout)") flags.BoolVar(&conf.Debug, "d", false, "Show logs in DEBUG mode") flags.BoolVar(&conf.Json, "j", false, "Show logs in JSON format") flags.BoolFunc("v", "print version", func(flagValue string) error { @@ -161,7 +161,7 @@ func root(args []string) error { if err != nil { return err } - fmt.Println("%s pid:", app, process.Pid) + fmt.Printf("%s pid: %d\n", app, process.Pid) process.Release() os.Exit(0) } diff --git a/version.go b/version.go index a176753..f9be0c1 100644 --- a/version.go +++ b/version.go @@ -1,3 +1,3 @@ package gohpts -const Version string = "gohpts v1.6.0" +const Version string = "gohpts v1.6.1"