Skip to content

added traffic sniffing functionality #4

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 1 commit into from
Jun 17, 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
180 changes: 163 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
- [Usage](#usage)
- [Configuration via CLI flags](#configuration-via-cli-flags)
- [Configuration via YAML file](#configuration-via-yaml-file)
- [Transparent proxy](#usage)
- [Transparent proxy](#transparent-proxy)
- [redirect (via NAT and SO_ORIGINAL_DST)](#redirect-via-nat-and-so_original_dst)
- [tproxy (via MANGLE and IP_TRANSPARENT)](#tproxy-via-mangle-and-ip_transparent)
- [Traffic sniffing](#traffic-sniffing)
- [Links](#links)
- [License](#license)

Expand Down Expand Up @@ -51,6 +52,9 @@ Specify http server in proxy configuration of Postman
- **Transparent proxy**
Supports `redirect` (SO_ORIGINAL_DST) and `tproxy` (IP_TRANSPARENT) modes

- **Traffic sniffing**
Proxy is able to parse HTTP headers and TLS handshake metadata

- **DNS Leak Protection**
DNS resolution occurs on SOCKS5 server side.

Expand Down Expand Up @@ -82,7 +86,7 @@ You can download the binary for your platform from [Releases](https://github.com
Example:

```shell
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
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
```

Alternatively, you can install it using `go install` command (requires Go [1.24](https://go.dev/doc/install) or later):
Expand Down Expand Up @@ -119,32 +123,36 @@ 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)
-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)
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")
-sniff
Enable traffic sniffing for HTTP and TLS
-snifflog string
Sniffed traffic log file path (Default: the same as -logfile)
-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 @@ -397,6 +405,144 @@ ip netns del ns-client
ip link del veth1
```

## Traffic sniffing

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

`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):

```json
[
{
"connection": {
"tproxy_mode": "redirect",
"src_local": "127.0.0.1:8888",
"src_remote": "192.168.0.107:51142",
"dst_local": "127.0.0.1:56256",
"dst_remote": "127.0.0.1:1080",
"original_dst": "216.58.209.206:443"
}
},
{
"tls_request": {
"sni": "www.youtube.com",
"type": "Client hello (1)",
"version": "TLS 1.2 (0x0303)",
"session_id": "2670a6779b4346e5e84d46890ad2aaf7a53b08adcfe0c9f6868c2d9882242e39",
"cipher_suites": [
"TLS_AES_128_GCM_SHA256 (0x1301)",
"TLS_CHACHA20_POLY1305_SHA256 (0x1303)",
"TLS_AES_256_GCM_SHA384 (0x1302)",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca9)",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8)",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a)",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009)",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)",
"TLS_RSA_WITH_AES_128_GCM_SHA256 (0x9c)",
"TLS_RSA_WITH_AES_256_GCM_SHA384 (0x9d)",
"TLS_RSA_WITH_AES_128_CBC_SHA (0x2f)",
"TLS_RSA_WITH_AES_256_CBC_SHA (0x35)"
],
"extensions": [
"server_name (0)",
"extended_master_secret (23)",
"renegotiation_info (65281)",
"supported_groups (10)",
"ec_point_formats (11)",
"session_ticket (35)",
"application_layer_protocol_negotiation (16)",
"status_request (5)",
"delegated_credential (34)",
"signed_certificate_timestamp (18)",
"key_share (51)",
"supported_versions (43)",
"signature_algorithms (13)",
"psk_key_exchange_modes (45)",
"record_size_limit (28)",
"compress_certificate (27)",
"encrypted_client_hello (65037)"
],
"alpn": ["h2", "http/1.1"]
}
},
{
"tls_response": {
"type": "Server hello (2)",
"version": "TLS 1.2 (0x0303)",
"session_id": "2670a6779b4346e5e84d46890ad2aaf7a53b08adcfe0c9f6868c2d9882242e39",
"cipher_suite": "TLS_AES_128_GCM_SHA256 (0x1301)",
"extensions": ["key_share (51)", "supported_versions (43)"],
"supported_version": "TLS 1.3 (0x0304)"
}
}
]
```

And HTTP request with curl:

```json
[
{
"connection": {
"tproxy_mode": "redirect",
"src_local": "127.0.0.1:8888",
"src_remote": "192.168.0.107:45736",
"dst_local": "127.0.0.1:37640",
"dst_remote": "127.0.0.1:1080",
"original_dst": "96.7.128.198:80"
}
},
{
"http_request": {
"host": "example.com",
"uri": "/",
"method": "GET",
"proto": "HTTP/1.1",
"header": {
"Accept": ["*/*"],
"My": ["Header"],
"User-Agent": ["curl/7.81.0"]
}
}
},
{
"http_response": {
"proto": "HTTP/1.1",
"status": "200 OK",
"content-length": 1256,
"header": {
"Cache-Control": ["max-age=2880"],
"Connection": ["keep-alive"],
"Content-Length": ["1256"],
"Content-Type": ["text/html"],
"Date": ["Tue, 17 Jun 2025 14:43:24 GMT"],
"Etag": ["\"84238dfc8092e5d9c0dac8ef93371a07:1736799080.121134\""],
"Last-Modified": ["Mon, 13 Jan 2025 20:11:20 GMT"]
}
}
}
]
```

Usage as simple as specifying `-sniff` flag along with regular flags

```shell
gohpts -d -t 8888 -M redirect -sniff
```

You can also specify a file to which write sniffed traffic:

```shell
gohpts -d -sniff -snifflog ~/sniff.log
```

Please note that for now sniffing only visible with `-d` flag, it may change in the future.

## Links

Learn more about transparent proxies by visiting the following links:
Expand Down
18 changes: 15 additions & 3 deletions cmd/gohpts/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ func root(args []string) error {
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.BoolVar(&conf.Sniff, "sniff", false, "Enable traffic sniffing for HTTP and TLS")
flags.StringVar(&conf.SniffLogFile, "snifflog", "", "Sniffed traffic log file path (Default: the same as -logfile)")
flags.BoolFunc("v", "print version", func(flagValue string) error {
fmt.Println(gohpts.Version)
os.Exit(0)
Expand Down Expand Up @@ -86,7 +88,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, -D, -logfile and -j flags")
return fmt.Errorf("-T flag only works with -s, -u, -f, -M, -d, -D, -logfile, -sniff, -snifflog and -j flags")
}
}
if !seen["M"] {
Expand All @@ -102,9 +104,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, -D, -logfile and -j flags")
return fmt.Errorf("-f flag only works with -t, -T, -M, -d, -D, -logfile, -sniff, -snifflog and -j flags")
}
return fmt.Errorf("-f flag only works with -d, -D, -logfile and -j flags")
return fmt.Errorf("-f flag only works with -d, -D, -logfile, -sniff, -snifflog and -j flags")
}
}
}
Expand All @@ -131,6 +133,16 @@ func root(args []string) error {
conf.ServerPass = string(bytepw)
fmt.Print("\033[2K\r")
}
if seen["sniff"] {
if !seen["d"] {
return fmt.Errorf("Traffic sniffing requires debug mode")
}
}
if seen["snifflog"] {
if !seen["sniff"] {
return fmt.Errorf("-snifflog only works with -sniff flag")
}
}

if *daemon {
if os.Getenv("GOHPTS_DAEMON") != "1" {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.24.1
require (
github.com/goccy/go-yaml v1.18.0
github.com/rs/zerolog v1.34.0
github.com/shadowy-pycoder/mshark v0.0.4
golang.org/x/net v0.40.0
golang.org/x/sys v0.33.0
golang.org/x/term v0.32.0
Expand Down
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
Expand All @@ -8,9 +10,15 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/shadowy-pycoder/mshark v0.0.4 h1:2yw6am1jt6n1GPHdLfFU1oDajv+zQ/23V0l0imFAeJY=
github.com/shadowy-pycoder/mshark v0.0.4/go.mod h1:fRWGQuU4BFjz9pTfrvwIT2AtmWWd99PEvdlgv+24vTE=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand All @@ -20,3 +28,5 @@ golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading