Skip to content

feat: add SOCKS4 and SOCKS4a proxy support (#473) - #510

Merged
imroc merged 2 commits into
imroc:masterfrom
ManuelReschke:feat_473
Jul 30, 2026
Merged

feat: add SOCKS4 and SOCKS4a proxy support (#473)#510
imroc merged 2 commits into
imroc:masterfrom
ManuelReschke:feat_473

Conversation

@ManuelReschke

Copy link
Copy Markdown
Contributor

Summary

Adds SOCKS4 and SOCKS4a proxy support so req can tunnel HTTP(S) traffic through SOCKS4 proxies, in addition to the existing HTTP and SOCKS5 schemes.

Fixes #473 #473

Changes

  • Extend internal/socks with a SOCKS4/SOCKS4a CONNECT client (IPv4-only).
  • Wire socks4:// and socks4a:// into Transport (default port 1080).
  • Map the proxy URL username to the SOCKS4 user ID.
  • socks4: resolve domain names locally to IPv4 before the handshake.
  • socks4a: send the domain name to the proxy via the SOCKS4a extension.
  • Reject IPv6 destinations, embedded NULs in user ID/domain, and overlong SOCKS4a domains (>255).
  • Document supported schemes and DNS behavior on SetProxy / transport options.

Usage

// SOCKS4 (local DNS → IPv4)
client.SetProxyURL("socks4://127.0.0.1:1080")

// SOCKS4a (remote DNS via proxy)
client.SetProxyURL("socks4a://127.0.0.1:1080")

// Optional user ID
client.SetProxyURL("socks4://myuser@proxy.example:1080")

Support socks4:// and socks4a:// proxy URLs so clients can tunnel
HTTP(S) through SOCKS4 proxies, with optional user ID and remote DNS
via the SOCKS4a extension.
Reject NUL bytes and overlong domain names in SOCKS4a requests, align
SetProxy docs with socks4/socks4a DNS behavior, and assert the public
client path actually sends a domain through the SOCKS4a extension.

@imroc imroc left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review (PR Review Loop):

Scope: Adds SOCKS4 and SOCKS4a proxy support (fixes #473). Files: internal/socks/client.go, internal/socks/socks.go, internal/socks/socks4_test.go (new, 543 lines), internal/transport/option.go, transport.go, proxy_socks4_test.go (new, 237 lines).

Verification:

  • Checked out the PR branch locally: go build ./... clean, go test ./... -count=1 -short all 22 packages pass (including internal/socks with the new SOCKS4 tests).
  • CI passes on both Go 1.25.x and 1.26.x.

Design review:

  • SOCKS4 protocol implementation (connect4) — correct wire format: VN(4) | CD | DSTPORT(2) | DSTIP(4) | USERID | NULL [| DOMAIN | NULL]. SOCKS4a uses the invalid IP 0.0.0.1 and appends the domain after the userid null terminator. Matches the SOCKS4 and SOCKS4a protocol specs.
  • SOCKS4a domain validation — rejects empty domains, NUL-embedded strings (validateSocks4CString), and domains >255 chars. IPv6 destinations are rejected before writing. Solid input validation.
  • Local DNS for socks4 — when Socks4A is false and the host is a domain, resolves locally to IPv4 via net.DefaultResolver.LookupIP. Correct.
  • Reply parsing — accepts VN of 0 (spec) or 4 (some servers incorrectly echo 4). Pragmatic tolerance. Reply codes (90-93) mapped to descriptive strings.
  • Refactor of connect5 — the SOCKS5 path was cleanly extracted from the original combined connect method. Error handling was changed from named returns (ctxErr) to explicit err returns. No behavioral change to the SOCKS5 path.

Modified stdlib file (transport.go):

  • transport.go carries Go Authors copyright but req has heavily customized it (dump, middleware, etc.). The PR adds a socks4/socks4a case branch in dialConn and entries in portMap — this is req-specific customization (Go stdlib has no SOCKS4 support), not upstream net/http logic. No conflict with future upstream syncs.
  • The dialConn addition maps the proxy URL username to the SOCKS4 user ID. Password from the URL userinfo is ignored (SOCKS4 has no password field), which is the correct behavior.
  • connectMethod.scheme() and comments updated to list the new schemes. Documentation is thorough.

Test coverage: Comprehensive — 14 tests in socks4_test.go covering IPv4 connect, SOCKS4a domain, empty userid, rejection, IPv6 rejection, NUL validation, domain too long, local resolve, DialWithConn, reply version tolerance, unexpected version, relay HTTP, reply string. 3 E2E tests in proxy_socks4_test.go covering socks4 and socks4a through the full Transport stack. No existing tests deleted or weakened.

No blockers. Approving.

@imroc
imroc merged commit a43b760 into imroc:master Jul 30, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Socks4 Proxy Support

2 participants