Skip to content

Daemon mode #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 37 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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: <pid>
```

```shell
# kill the process
kill <pid>
#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)
Expand Down
57 changes: 46 additions & 11 deletions cmd/gohpts/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)")
Expand All @@ -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)
Expand Down Expand Up @@ -90,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"] {
Expand All @@ -106,12 +102,17 @@ 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")
}
}
}
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()))
Expand All @@ -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.Printf("%s pid: %d\n", app, process.Pid)
process.Release()
os.Exit(0)
}
}
p := gohpts.New(&conf)
p.Run()
return nil
Expand Down
29 changes: 21 additions & 8 deletions gohpts.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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")))
}

Expand Down Expand Up @@ -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))
}
Expand Down
2 changes: 1 addition & 1 deletion version.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package gohpts

const Version string = "gohpts v1.6.0"
const Version string = "gohpts v1.6.1"