Skip to content

Commit f5233cb

Browse files
Merge pull request #3 from shadowy-pycoder/features
Daemon mode
2 parents d778eac + 555f3b2 commit f5233cb

File tree

4 files changed

+105
-34
lines changed

4 files changed

+105
-34
lines changed

README.md

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ You can download the binary for your platform from [Releases](https://github.com
6868
Example:
6969

7070
```shell
71-
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
71+
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
7272
```
7373

7474
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
105105
Usage: gohpts [OPTIONS]
106106
Options:
107107
-h Show this help message and exit.
108+
-D Run as a daemon (provide -logfile to see logs)
108109
-M value
109-
Transparent proxy mode: [redirect tproxy]
110+
Transparent proxy mode: [redirect tproxy]
110111
-T string
111-
Address of transparent proxy server (no HTTP)
112+
Address of transparent proxy server (no HTTP)
112113
-U string
113-
User for HTTP proxy (basic auth). This flag invokes prompt for password (not echoed to terminal)
114+
User for HTTP proxy (basic auth). This flag invokes prompt for password (not echoed to terminal)
114115
-c string
115-
Path to certificate PEM encoded file
116-
-d Show logs in DEBUG mode
116+
Path to certificate PEM encoded file
117+
-d Show logs in DEBUG mode
117118
-f string
118-
Path to server configuration file in YAML format
119-
-j Show logs in JSON format
119+
Path to server configuration file in YAML format
120+
-j Show logs in JSON format
120121
-k string
121-
Path to private key PEM encoded file
122+
Path to private key PEM encoded file
122123
-l string
123-
Address of HTTP proxy server (default "127.0.0.1:8080")
124+
Address of HTTP proxy server (default "127.0.0.1:8080")
125+
-logfile string
126+
Log file path (Default: stdout)
124127
-s string
125-
Address of SOCKS5 proxy server (default "127.0.0.1:1080")
128+
Address of SOCKS5 proxy server (default "127.0.0.1:1080")
126129
-t string
127-
Address of transparent proxy server (it starts along with HTTP proxy server)
130+
Address of transparent proxy server (it starts along with HTTP proxy server)
128131
-u string
129-
User for SOCKS5 proxy authentication. This flag invokes prompt for password (not echoed to terminal)
130-
-v print version
132+
User for SOCKS5 proxy authentication. This flag invokes prompt for password (not echoed to terminal)
133+
-v print version
131134
```
132135

133136
### Configuration via CLI flags
@@ -166,6 +169,26 @@ Run http proxy over TLS connection
166169
gohpts -s 1080 -l 8080 -c "path/to/certificate" -k "path/to/private/key"
167170
```
168171

172+
Run proxy as a daemon (logfile is needed for logging output, otherwise you will see nothing)
173+
174+
```shell
175+
gohpts -D -logfile /tmp/gohpts.log
176+
```
177+
178+
```shell
179+
# output
180+
gohpts pid: <pid>
181+
```
182+
183+
```shell
184+
# kill the process
185+
kill <pid>
186+
#or
187+
kill $(pidof gohpts)
188+
```
189+
190+
`-u` and `-U` flags do not work in a daemon mode (and therefore authentication), but you can provide a config file (see below)
191+
169192
### Configuration via YAML file
170193

171194
Run http proxy in SOCKS5 proxy chain mode (specify server settings via YAML configuration file)

cmd/gohpts/cli.go

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ func root(args []string) error {
4343
flags.StringVar(&conf.CertFile, "c", "", "Path to certificate PEM encoded file")
4444
flags.StringVar(&conf.KeyFile, "k", "", "Path to private key PEM encoded file")
4545
flags.StringVar(&conf.ServerConfPath, "f", "", "Path to server configuration file in YAML format")
46+
daemon := flags.Bool("D", false, "Run as a daemon (provide -logfile to see logs)")
4647
if runtime.GOOS == tproxyOS {
4748
flags.StringVar(&conf.TProxy, "t", "", "Address of transparent proxy server (it starts along with HTTP proxy server)")
4849
flags.StringVar(&conf.TProxyOnly, "T", "", "Address of transparent proxy server (no HTTP)")
@@ -55,14 +56,9 @@ func root(args []string) error {
5556
return nil
5657
})
5758
}
58-
flags.BoolFunc("d", "Show logs in DEBUG mode", func(flagValue string) error {
59-
conf.Debug = true
60-
return nil
61-
})
62-
flags.BoolFunc("j", "Show logs in JSON format", func(flagValue string) error {
63-
conf.Json = true
64-
return nil
65-
})
59+
flags.StringVar(&conf.LogFilePath, "logfile", "", "Log file path (Default: stdout)")
60+
flags.BoolVar(&conf.Debug, "d", false, "Show logs in DEBUG mode")
61+
flags.BoolVar(&conf.Json, "j", false, "Show logs in JSON format")
6662
flags.BoolFunc("v", "print version", func(flagValue string) error {
6763
fmt.Println(gohpts.Version)
6864
os.Exit(0)
@@ -90,7 +86,7 @@ func root(args []string) error {
9086
if seen["T"] {
9187
for _, da := range []string{"U", "c", "k", "l"} {
9288
if seen[da] {
93-
return fmt.Errorf("-T flag only works with -s, -u, -f, -M, -d and -j flags")
89+
return fmt.Errorf("-T flag only works with -s, -u, -f, -M, -d, -D, -logfile and -j flags")
9490
}
9591
}
9692
if !seen["M"] {
@@ -106,12 +102,17 @@ func root(args []string) error {
106102
for _, da := range []string{"s", "u", "U", "c", "k", "l"} {
107103
if seen[da] {
108104
if runtime.GOOS == tproxyOS {
109-
return fmt.Errorf("-f flag only works with -t, -T, -M, -d and -j flags")
105+
return fmt.Errorf("-f flag only works with -t, -T, -M, -d, -D, -logfile and -j flags")
110106
}
111-
return fmt.Errorf("-f flag only works with -d and -j flags")
107+
return fmt.Errorf("-f flag only works with -d, -D, -logfile and -j flags")
112108
}
113109
}
114110
}
111+
if seen["D"] {
112+
if seen["u"] || seen["U"] {
113+
return fmt.Errorf("-u and -U flags do not work in daemon mode")
114+
}
115+
}
115116
if seen["u"] {
116117
fmt.Print("SOCKS5 Password: ")
117118
bytepw, err := term.ReadPassword(int(os.Stdin.Fd()))
@@ -131,6 +132,40 @@ func root(args []string) error {
131132
fmt.Print("\033[2K\r")
132133
}
133134

135+
if *daemon {
136+
if os.Getenv("GOHPTS_DAEMON") != "1" {
137+
env := os.Environ()
138+
files := [3]*os.File{}
139+
env = append(env, "GOHPTS_DAEMON=1")
140+
files[0], _ = os.Open(os.DevNull)
141+
files[1], _ = os.Open(os.DevNull)
142+
files[2], _ = os.Open(os.DevNull)
143+
attr := &os.ProcAttr{
144+
Files: []*os.File{
145+
files[0], // stdin
146+
files[1], // stdout
147+
files[2], // stderr
148+
},
149+
Dir: ".",
150+
Env: env,
151+
}
152+
path, err := os.Executable()
153+
if err != nil {
154+
return err
155+
}
156+
process, err := os.StartProcess(
157+
path,
158+
os.Args,
159+
attr,
160+
)
161+
if err != nil {
162+
return err
163+
}
164+
fmt.Printf("%s pid: %d\n", app, process.Pid)
165+
process.Release()
166+
os.Exit(0)
167+
}
168+
}
134169
p := gohpts.New(&conf)
135170
p.Run()
136171
return nil

gohpts.go

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -696,8 +696,6 @@ func (p *proxyapp) Run() {
696696
type Config struct {
697697
AddrHTTP string
698698
AddrSOCKS string
699-
Debug bool
700-
Json bool
701699
User string
702700
Pass string
703701
ServerUser string
@@ -708,20 +706,25 @@ type Config struct {
708706
TProxy string
709707
TProxyOnly string
710708
TProxyMode string
709+
LogFilePath string
710+
Debug bool
711+
Json bool
711712
}
712713

713714
type logWriter struct {
715+
file *os.File
714716
}
715717

716718
func (writer logWriter) Write(bytes []byte) (int, error) {
717-
return fmt.Print(fmt.Sprintf("%s | ERROR | %s", time.Now().Format(time.RFC3339), string(bytes)))
719+
return fmt.Fprintf(writer.file, fmt.Sprintf("%s | ERROR | %s", time.Now().Format(time.RFC3339), string(bytes)))
718720
}
719721

720722
type jsonLogWriter struct {
723+
file *os.File
721724
}
722725

723726
func (writer jsonLogWriter) Write(bytes []byte) (int, error) {
724-
return fmt.Print(fmt.Sprintf("{\"level\":\"error\",\"time\":\"%s\",\"message\":\"%s\"}\n",
727+
return fmt.Fprintf(writer.file, fmt.Sprintf("{\"level\":\"error\",\"time\":\"%s\",\"message\":\"%s\"}\n",
725728
time.Now().Format(time.RFC3339), strings.TrimRight(string(bytes), "\n")))
726729
}
727730

@@ -782,14 +785,24 @@ func expandPath(p string) string {
782785
func New(conf *Config) *proxyapp {
783786
var logger zerolog.Logger
784787
var p proxyapp
788+
var logfile *os.File = os.Stdout
789+
if conf.LogFilePath != "" {
790+
f, err := os.OpenFile(conf.LogFilePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
791+
if err != nil {
792+
log.Fatalf("Failed to open log file: %v", err)
793+
}
794+
logfile = f
795+
}
785796
if conf.Json {
786797
log.SetFlags(0)
787-
log.SetOutput(new(jsonLogWriter))
788-
logger = zerolog.New(os.Stdout).With().Timestamp().Logger()
798+
jsonWriter := jsonLogWriter{file: logfile}
799+
log.SetOutput(jsonWriter)
800+
logger = zerolog.New(logfile).With().Timestamp().Logger()
789801
} else {
790802
log.SetFlags(0)
791-
log.SetOutput(new(logWriter))
792-
output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339, NoColor: true}
803+
logWriter := logWriter{file: logfile}
804+
log.SetOutput(logWriter)
805+
output := zerolog.ConsoleWriter{Out: logfile, TimeFormat: time.RFC3339, NoColor: true}
793806
output.FormatLevel = func(i any) string {
794807
return strings.ToUpper(fmt.Sprintf("| %-6s|", i))
795808
}

version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
package gohpts
22

3-
const Version string = "gohpts v1.6.0"
3+
const Version string = "gohpts v1.6.1"

0 commit comments

Comments
 (0)