A lightweight SOCKS5 traffic routing and aggregation proxy filter.
- Standard SOCKS5 Server: Provides standard SOCKS5 proxy service with TCP CONNECT and UDP ASSOCIATE support (RFC 1928 / RFC 1929), along with optional username and password authentication.
- Full UDP Relay & Forwarding: Transparently routes UDP datagrams: domestic and private destinations are relayed directly, while foreign destinations are forwarded through upstream SOCKS5 proxies using UDP ASSOCIATE relaying with load balancing.
- Smart Traffic Routing & Bypass:
- Unified
gohome/dnsEngine: Powered bygohome/dnswith zero-config defaults, anti-pollution parallel queries, and instant bypass classification. - Domestic Domain Whitelist Direct: Built-in accelerated domain whitelist (
accelerated-domains.china.conf) and domain suffix tree for instantaneous direct connection. - Private & LAN IP Direct: Automatically identifies and bypasses private IP ranges (e.g.
127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16). - GeoIP Country Filtering: Filters based on destination IP country code (default
CN). Matches connect directly; non-matches are routed to upstream SOCKS5 servers. - High-Performance DNS Cache: Built-in multi-core concurrent LRU cache and singleflight deduplication to reduce redundant DNS lookups.
- Unified
- Upstream SOCKS5 Authentication: Forwards username/password credentials (RFC 1929) to upstream SOCKS5 servers for both TCP CONNECT and UDP ASSOCIATE.
- JSON Config File:
-config file.jsoncan supply every flag; explicit command-line flags take precedence. - Upstream Health Checks (Active + Passive): Actively probes each upstream periodically (TCP connect + SOCKS5 handshake, using
-susername/-spassword), and also learns from real traffic — TCP/UDP dial, handshake, or UDP relay-write failures feed the same health state while successes clear it. An upstream failing two consecutive checks is removed from candidate selection. After a cooldown it enters half-open state and retries with a single request, recovering on success or re-entering cooldown on failure (cooldown equals the probe interval, or 10s when active probing is disabled). If every upstream is down, selection fails open to on-demand failover. - Upstream Multi-Server Load Balancing: Aggregates multiple backend SOCKS5 servers with flexible load balancing strategies:
robin: Thread-safe Round-Robin rotation.rand: Random selection.hash_by_dst_ip: Hash based on destination IP.hash_by_src_ip: Hash based on client source IP.hash_all: Combined source and destination hash.- Automatically skips unreachable backend servers.
- Graceful Shutdown & Leak Prevention: Handles
SIGINT/SIGTERMsignals for clean listener termination, and features TCP half-close propagation and connection leak prevention. - Concurrent Session Limit: With
-max-conn, caps concurrent SOCKS sessions (TCP CONNECT and UDP ASSOCIATE share one pool). Overflow sessions receive a proper SOCKS5 failure reply instead of being silently reset, and the rejected count is exposed in stats. Disabled (0) by default. - Runtime Observability Endpoint: With
-stats-addr(e.g.127.0.0.1:7070), serves plain-text HTTP endpoints (disabled by default):GET /stats: uptime, active/total/rejected TCP connections, active/total UDP associations, cumulative relayed bytes per direction, per-egress-route payload bytes (direct bypass plus each upstream, TCP and UDP combined), per-egress-route session counts (route_sessions: active gauge and cumulative established total for direct;sessions_activeper upstream, TCP CONNECT tunnels and UDP sessions combined), route DNS resolution counters (dns_lookupsby outcome — literal IP / cache hit / resolved / failure — plusdns_resolved_sourcesmart-resolver vs system-fallback breakdown and livecache_entries), idle-timeout eviction counts (idle_timeout_closed: TCP tunnels and UDP associations locally reaped after bidirectional silence), SOCKS5 entry-stage rejections (entry_errors: handshake/auth failures, malformed request frames, unsupported commands — all rejected before a session is admitted), and per-upstream health state (alive/half_open/deadwith failure count and removal duration) plus cumulative per-upstream session countersattempts/established/connect_fails(TCP CONNECT and UDP ASSOCIATE combined; UDP session reuse is not counted again) and active health-check probe countersprobe_attempts/probe_fails(success count is the difference; passive traffic failures are not mixed in) plus probe latencyprobe_last_ms/probe_avg_ms(most recent and cumulative-average probe time, TCP connect plus SOCKS5 handshake, successes and failures both timed) and real-traffic session establishment latencyestablish_last_ms/establish_avg_ms(most recent and cumulative-average time to establish a session via this upstream — TCP connect plus SOCKS5 handshake plus CONNECT/UDP-ASSOCIATE request, timed to the success instant or the failure point, successes and failures both observed) and cumulative health state flipsmarked_dead/recovered(alive→removed and removed→alive transitions, driven by both active probes and passive traffic feedback, useful for flapping detection) plus passive real-traffic failures by stagetraffic_dial/traffic_handshake/traffic_request/traffic_write(TCP connect, SOCKS5 method/auth handshake, CONNECT/UDP-ASSOCIATE request phase including target-unreachable replies — counted without affecting upstream health, and post-session relay write failures; active probes are excluded).GET /metrics: Prometheus text exposition (version 0.0.4) withsocksfilter_*gauges/counters for sessions, relayed bytes (includingsocksfilter_relay_bytes_total{route,direction}broken down by egress route —director the upstream address — for both directions), per-route session accounting (socksfilter_route_sessions_active{route}gauge andsocksfilter_route_sessions_total{route}counter; the upstream-route total equalssocksfilter_upstream_sessions_established_totalunder the unified route label), DNS resolution metrics (socksfilter_dns_lookups_total{outcome}counter for literal/cache_hit/resolved/failure,socksfilter_dns_resolutions_total{source}counter for smart_resolver/system_fallback, andsocksfilter_dns_cache_entriesgauge), idle-timeout eviction counters (socksfilter_idle_timeout_closed_total{transport}for tcp/udp), pre-admission entry errors (socksfilter_entry_errors_total{reason}for handshake/bad_request/unsupported_command), the configured session limit, and per-upstream three-way health state (socksfilter_upstream_health_state{upstream,state="alive"|"half_open"|"dead"}, one-hot gauge; the binarysocksfilter_upstream_upstays 1 for both alive and half_open), consecutive failures, cumulative session attempts/established/connect failures, and active health-check probes (socksfilter_upstream_health_probes_total{upstream,outcome="success"|"failure"}) with the most recent probe latency gauge (socksfilter_upstream_health_probe_last_duration_seconds{upstream}) and a latency histogram (socksfilter_upstream_health_probe_duration_seconds{upstream}with_bucket/_sum/_count, default Prometheus latency buckets), real-traffic session establishment latency (the most recent attempt gaugesocksfilter_upstream_session_establish_last_duration_seconds{upstream}plus a histogramsocksfilter_upstream_session_establish_duration_seconds{upstream}with_bucket/_sum/_counton the same default buckets — TCP connect plus SOCKS5 handshake plus CONNECT/UDP-ASSOCIATE request, successes and failures both observed, count equals session attempts), plus cumulative health state transitions (socksfilter_upstream_health_transitions_total{upstream,event="marked_dead"|"recovered"}) and passive real-traffic failures by stage (socksfilter_upstream_traffic_failures_total{upstream,stage="dial"|"handshake"|"request"|"write"}); scrape it directly from Prometheus.GET /healthz: liveness probe, always200 okwhile the process can serve HTTP.GET /readyz: readiness probe for container orchestrators —200when at least one upstream is selectable,503when all upstreams are removed (the data plane itself stays fail-open and keeps trying on demand). The plain-text body carries analive/dead/half_open/totalsummary plus one line per upstream (state=alive|half_open|dead, withremoved_sincefor non-alive ones), so a503response identifies the failed nodes directly;HEADreturns status only.
Listen on port 1080, bypass CN traffic directly, and route non-CN traffic through backend SOCKS5 servers:
./socksfilter -s "yourserver1:1080 yourserver2:1080 yourserver3:1080"All flags can optionally be supplied as a JSON file via -config:
{
"l": ":1080",
"s": "yourserver1:1080 yourserver2:1080",
"select": "robin",
"dial-timeout": 10,
"tcp-keepalive": 30,
"max-conn": 1000,
"stats-addr": "127.0.0.1:7070",
"susername": "user",
"spassword": "pass"
}./socksfilter -config /etc/socksfilter/config.jsonRules:
- JSON keys are flag names exactly as shown by
-h; values are strings, numbers, or booleans. Multi-value flags such as-sstay space-separated inside one string. - Precedence: explicit command-line flags > config file > built-in defaults (e.g.
-s cli:1 -config config.jsonkeepscli:1). - Unknown keys, malformed JSON,
null, array/object values, or theconfigkey itself make the process exit with an error; an empty{}is a no-op.
Run directly using the pre-built Docker image:
Option 1: Port mapping (recommended for macOS, Windows, Linux)
docker run --name socksfilter -d --restart=always \
-p 1080:1080 \
esrrhs/socksfilter -s "yourserver1:1080 yourserver2:1080 yourserver3:1080"Option 2: Host network mode (best performance on Linux)
docker run --name socksfilter -d --restart=always \
--network host \
esrrhs/socksfilter -s "yourserver1:1080 yourserver2:1080 yourserver3:1080"Option 3: Mount custom rules or GeoIP database
docker run --name socksfilter -d --restart=always \
-p 1080:1080 \
-v $(pwd)/GeoLite2-Country.mmdb:/app/GeoLite2-Country.mmdb \
-v $(pwd)/accelerated-domains.china.conf:/app/accelerated-domains.china.conf \
esrrhs/socksfilter -s "yourserver1:1080 yourserver2:1080 yourserver3:1080"The project includes a Makefile for streamlined development:
# Build local binary with version info
make build
# Run unit tests with race detection
make test
# Build lightweight Docker image
make docker
# Cross-compile and package release zips (bundles GeoIP and domain rules)
make pack
# Update China domain rules from upstream
make update-rules
# Clean build artifacts
make cleanRun ./socksfilter -h to see all available flags:
Usage of ./socksfilter:
-cache_expire int
cache expire seconds for dns (default 3600)
-cache_size int
cache size for dns (default 1000)
-china_domains string
china domains file (default "accelerated-domains.china.conf")
-config string
load flags from a JSON config file; explicit command-line flags take precedence
-dial-timeout int
tcp dial timeout in seconds for upstream and direct targets (default 10)
-file string
ip file (default "GeoLite2-Country.mmdb")
-health-check-interval int
upstream health check interval in seconds, 0 to disable (default 30)
-health-check-timeout int
upstream health check timeout in seconds (default 5)
-idle-timeout int
idle timeout in seconds for relayed tcp connections, 0 to disable
-l string
listen addr (default ":1080")
-loglevel string
log level (default "info")
-max-conn int
max concurrent socks sessions (tcp connect and udp associate), 0 to disable
-nolog int
write log file
-noprint int
print stdout
-password string
password
-s string
server addr (default "server1 server2 server3")
-select string
select server robin/rand/hash_by_dst_ip/hash_by_src_ip/hash_all (default "robin")
-shutdown-timeout int
grace period in seconds to wait for in-flight connections on shutdown, 0 to force close immediately (default 10)
-skip string
skip country (default "CN")
-spassword string
upstream server password
-stats-addr string
http listen addr for runtime stats, e.g. 127.0.0.1:7070, empty to disable
-susername string
upstream server username
-tcp-keepalive int
tcp keepalive idle time in seconds for client and upstream connections, 0 to disable (default 30)
-udp-idle-timeout int
idle timeout in seconds for udp associations, 0 to disable (default 300)
-username string
username
-version
show version and exit