Skip to content

Commit fa9cf05

Browse files
Merge pull request #4 from shadowy-pycoder/sniffing
added traffic sniffing functionality
2 parents bec8ce1 + 2d60cce commit fa9cf05

File tree

7 files changed

+315
-35
lines changed

7 files changed

+315
-35
lines changed

README.md

Lines changed: 163 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
- [Usage](#usage)
1616
- [Configuration via CLI flags](#configuration-via-cli-flags)
1717
- [Configuration via YAML file](#configuration-via-yaml-file)
18-
- [Transparent proxy](#usage)
18+
- [Transparent proxy](#transparent-proxy)
1919
- [redirect (via NAT and SO_ORIGINAL_DST)](#redirect-via-nat-and-so_original_dst)
2020
- [tproxy (via MANGLE and IP_TRANSPARENT)](#tproxy-via-mangle-and-ip_transparent)
21+
- [Traffic sniffing](#traffic-sniffing)
2122
- [Links](#links)
2223
- [Contributing](#contributing)
2324
- [License](#license)
@@ -56,6 +57,9 @@ Specify http server in proxy configuration of Postman
5657
- **Transparent proxy**
5758
Supports `redirect` (SO_ORIGINAL_DST) and `tproxy` (IP_TRANSPARENT) modes
5859

60+
- **Traffic sniffing**
61+
Proxy is able to parse HTTP headers and TLS handshake metadata
62+
5963
- **DNS Leak Protection**
6064
DNS resolution occurs on SOCKS5 server side.
6165

@@ -89,7 +93,7 @@ You can download the binary for your platform from [Releases](https://github.com
8993
Example:
9094

9195
```shell
92-
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
96+
HPTS_RELEASE=v1.7.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
9397
```
9498

9599
Alternatively, you can install it using `go install` command (requires Go [1.24](https://go.dev/doc/install) or later):
@@ -128,32 +132,36 @@ GitHub: https://github.com/shadowy-pycoder/go-http-proxy-to-socks
128132
Usage: gohpts [OPTIONS]
129133
Options:
130134
-h Show this help message and exit.
131-
-D Run as a daemon (provide -logfile to see logs)
135+
-D Run as a daemon (provide -logfile to see logs)
132136
-M value
133-
Transparent proxy mode: [redirect tproxy]
137+
Transparent proxy mode: [redirect tproxy]
134138
-T string
135-
Address of transparent proxy server (no HTTP)
139+
Address of transparent proxy server (no HTTP)
136140
-U string
137-
User for HTTP proxy (basic auth). This flag invokes prompt for password (not echoed to terminal)
141+
User for HTTP proxy (basic auth). This flag invokes prompt for password (not echoed to terminal)
138142
-c string
139-
Path to certificate PEM encoded file
140-
-d Show logs in DEBUG mode
143+
Path to certificate PEM encoded file
144+
-d Show logs in DEBUG mode
141145
-f string
142-
Path to server configuration file in YAML format
143-
-j Show logs in JSON format
146+
Path to server configuration file in YAML format
147+
-j Show logs in JSON format
144148
-k string
145-
Path to private key PEM encoded file
149+
Path to private key PEM encoded file
146150
-l string
147-
Address of HTTP proxy server (default "127.0.0.1:8080")
151+
Address of HTTP proxy server (default "127.0.0.1:8080")
148152
-logfile string
149-
Log file path (Default: stdout)
153+
Log file path (Default: stdout)
150154
-s string
151-
Address of SOCKS5 proxy server (default "127.0.0.1:1080")
155+
Address of SOCKS5 proxy server (default "127.0.0.1:1080")
156+
-sniff
157+
Enable traffic sniffing for HTTP and TLS
158+
-snifflog string
159+
Sniffed traffic log file path (Default: the same as -logfile)
152160
-t string
153-
Address of transparent proxy server (it starts along with HTTP proxy server)
161+
Address of transparent proxy server (it starts along with HTTP proxy server)
154162
-u string
155-
User for SOCKS5 proxy authentication. This flag invokes prompt for password (not echoed to terminal)
156-
-v print version
163+
User for SOCKS5 proxy authentication. This flag invokes prompt for password (not echoed to terminal)
164+
-v print version
157165
```
158166

159167
### Configuration via CLI flags
@@ -414,6 +422,144 @@ ip netns del ns-client
414422
ip link del veth1
415423
```
416424

425+
## Traffic sniffing
426+
427+
[[Back]](#table-of-contents)
428+
429+
`GoHPTS` proxy allows one to capture and monitor traffic that goes through the service. This procces is known as `traffic sniffing`, `packet sniffing` or just `sniffing`. In particular, proxy tries to identify whether it is a plain text (HTTP) or TLS traffic, and after identification is done, it parses request/response metadata and writes it to the file or console. In the case of `GoHTPS` proxy a parsed metadata looks like the following (TLS Handshake):
430+
431+
```json
432+
[
433+
{
434+
"connection": {
435+
"tproxy_mode": "redirect",
436+
"src_local": "127.0.0.1:8888",
437+
"src_remote": "192.168.0.107:51142",
438+
"dst_local": "127.0.0.1:56256",
439+
"dst_remote": "127.0.0.1:1080",
440+
"original_dst": "216.58.209.206:443"
441+
}
442+
},
443+
{
444+
"tls_request": {
445+
"sni": "www.youtube.com",
446+
"type": "Client hello (1)",
447+
"version": "TLS 1.2 (0x0303)",
448+
"session_id": "2670a6779b4346e5e84d46890ad2aaf7a53b08adcfe0c9f6868c2d9882242e39",
449+
"cipher_suites": [
450+
"TLS_AES_128_GCM_SHA256 (0x1301)",
451+
"TLS_CHACHA20_POLY1305_SHA256 (0x1303)",
452+
"TLS_AES_256_GCM_SHA384 (0x1302)",
453+
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)",
454+
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)",
455+
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca9)",
456+
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8)",
457+
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)",
458+
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)",
459+
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a)",
460+
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009)",
461+
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)",
462+
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)",
463+
"TLS_RSA_WITH_AES_128_GCM_SHA256 (0x9c)",
464+
"TLS_RSA_WITH_AES_256_GCM_SHA384 (0x9d)",
465+
"TLS_RSA_WITH_AES_128_CBC_SHA (0x2f)",
466+
"TLS_RSA_WITH_AES_256_CBC_SHA (0x35)"
467+
],
468+
"extensions": [
469+
"server_name (0)",
470+
"extended_master_secret (23)",
471+
"renegotiation_info (65281)",
472+
"supported_groups (10)",
473+
"ec_point_formats (11)",
474+
"session_ticket (35)",
475+
"application_layer_protocol_negotiation (16)",
476+
"status_request (5)",
477+
"delegated_credential (34)",
478+
"signed_certificate_timestamp (18)",
479+
"key_share (51)",
480+
"supported_versions (43)",
481+
"signature_algorithms (13)",
482+
"psk_key_exchange_modes (45)",
483+
"record_size_limit (28)",
484+
"compress_certificate (27)",
485+
"encrypted_client_hello (65037)"
486+
],
487+
"alpn": ["h2", "http/1.1"]
488+
}
489+
},
490+
{
491+
"tls_response": {
492+
"type": "Server hello (2)",
493+
"version": "TLS 1.2 (0x0303)",
494+
"session_id": "2670a6779b4346e5e84d46890ad2aaf7a53b08adcfe0c9f6868c2d9882242e39",
495+
"cipher_suite": "TLS_AES_128_GCM_SHA256 (0x1301)",
496+
"extensions": ["key_share (51)", "supported_versions (43)"],
497+
"supported_version": "TLS 1.3 (0x0304)"
498+
}
499+
}
500+
]
501+
```
502+
503+
And HTTP request with curl:
504+
505+
```json
506+
[
507+
{
508+
"connection": {
509+
"tproxy_mode": "redirect",
510+
"src_local": "127.0.0.1:8888",
511+
"src_remote": "192.168.0.107:45736",
512+
"dst_local": "127.0.0.1:37640",
513+
"dst_remote": "127.0.0.1:1080",
514+
"original_dst": "96.7.128.198:80"
515+
}
516+
},
517+
{
518+
"http_request": {
519+
"host": "example.com",
520+
"uri": "/",
521+
"method": "GET",
522+
"proto": "HTTP/1.1",
523+
"header": {
524+
"Accept": ["*/*"],
525+
"My": ["Header"],
526+
"User-Agent": ["curl/7.81.0"]
527+
}
528+
}
529+
},
530+
{
531+
"http_response": {
532+
"proto": "HTTP/1.1",
533+
"status": "200 OK",
534+
"content-length": 1256,
535+
"header": {
536+
"Cache-Control": ["max-age=2880"],
537+
"Connection": ["keep-alive"],
538+
"Content-Length": ["1256"],
539+
"Content-Type": ["text/html"],
540+
"Date": ["Tue, 17 Jun 2025 14:43:24 GMT"],
541+
"Etag": ["\"84238dfc8092e5d9c0dac8ef93371a07:1736799080.121134\""],
542+
"Last-Modified": ["Mon, 13 Jan 2025 20:11:20 GMT"]
543+
}
544+
}
545+
}
546+
]
547+
```
548+
549+
Usage as simple as specifying `-sniff` flag along with regular flags
550+
551+
```shell
552+
gohpts -d -t 8888 -M redirect -sniff
553+
```
554+
555+
You can also specify a file to which write sniffed traffic:
556+
557+
```shell
558+
gohpts -d -sniff -snifflog ~/sniff.log
559+
```
560+
561+
Please note that for now sniffing only visible with `-d` flag, it may change in the future.
562+
417563
## Links
418564

419565
[[Back]](#table-of-contents)

cmd/gohpts/cli.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ func root(args []string) error {
5959
flags.StringVar(&conf.LogFilePath, "logfile", "", "Log file path (Default: stdout)")
6060
flags.BoolVar(&conf.Debug, "d", false, "Show logs in DEBUG mode")
6161
flags.BoolVar(&conf.Json, "j", false, "Show logs in JSON format")
62+
flags.BoolVar(&conf.Sniff, "sniff", false, "Enable traffic sniffing for HTTP and TLS")
63+
flags.StringVar(&conf.SniffLogFile, "snifflog", "", "Sniffed traffic log file path (Default: the same as -logfile)")
6264
flags.BoolFunc("v", "print version", func(flagValue string) error {
6365
fmt.Println(gohpts.Version)
6466
os.Exit(0)
@@ -86,7 +88,7 @@ func root(args []string) error {
8688
if seen["T"] {
8789
for _, da := range []string{"U", "c", "k", "l"} {
8890
if seen[da] {
89-
return fmt.Errorf("-T flag only works with -s, -u, -f, -M, -d, -D, -logfile and -j flags")
91+
return fmt.Errorf("-T flag only works with -s, -u, -f, -M, -d, -D, -logfile, -sniff, -snifflog and -j flags")
9092
}
9193
}
9294
if !seen["M"] {
@@ -102,9 +104,9 @@ func root(args []string) error {
102104
for _, da := range []string{"s", "u", "U", "c", "k", "l"} {
103105
if seen[da] {
104106
if runtime.GOOS == tproxyOS {
105-
return fmt.Errorf("-f flag only works with -t, -T, -M, -d, -D, -logfile and -j flags")
107+
return fmt.Errorf("-f flag only works with -t, -T, -M, -d, -D, -logfile, -sniff, -snifflog and -j flags")
106108
}
107-
return fmt.Errorf("-f flag only works with -d, -D, -logfile and -j flags")
109+
return fmt.Errorf("-f flag only works with -d, -D, -logfile, -sniff, -snifflog and -j flags")
108110
}
109111
}
110112
}
@@ -131,6 +133,16 @@ func root(args []string) error {
131133
conf.ServerPass = string(bytepw)
132134
fmt.Print("\033[2K\r")
133135
}
136+
if seen["sniff"] {
137+
if !seen["d"] {
138+
return fmt.Errorf("Traffic sniffing requires debug mode")
139+
}
140+
}
141+
if seen["snifflog"] {
142+
if !seen["sniff"] {
143+
return fmt.Errorf("-snifflog only works with -sniff flag")
144+
}
145+
}
134146

135147
if *daemon {
136148
if os.Getenv("GOHPTS_DAEMON") != "1" {

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.24.1
55
require (
66
github.com/goccy/go-yaml v1.18.0
77
github.com/rs/zerolog v1.34.0
8+
github.com/shadowy-pycoder/mshark v0.0.4
89
golang.org/x/net v0.40.0
910
golang.org/x/sys v0.33.0
1011
golang.org/x/term v0.32.0

go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
2+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
24
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
35
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
46
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@@ -8,9 +10,15 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
810
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
911
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
1012
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
13+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
14+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
1115
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
1216
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
1317
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
18+
github.com/shadowy-pycoder/mshark v0.0.4 h1:2yw6am1jt6n1GPHdLfFU1oDajv+zQ/23V0l0imFAeJY=
19+
github.com/shadowy-pycoder/mshark v0.0.4/go.mod h1:fRWGQuU4BFjz9pTfrvwIT2AtmWWd99PEvdlgv+24vTE=
20+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
21+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
1422
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
1523
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
1624
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -20,3 +28,5 @@ golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
2028
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
2129
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
2230
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
31+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
32+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)