Skip to content

Commit e2987c6

Browse files
authored
Merge pull request #13 from jay7x/socks5
Implement SOCKS5 support
2 parents 2ad5d4d + 99a32d4 commit e2987c6

File tree

4 files changed

+86
-34
lines changed

4 files changed

+86
-34
lines changed

README.md

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,23 @@ This proxy implements the 3rd way. Nor admin privileges neither any system-wide
1414

1515
A proxy-switching extension for your browser (FoxyProxy or SwitchyOmega e.g.) is highly recommended.
1616

17+
## WARNING & DISCLAIMER
18+
19+
⚠️ DO NOT RUN this server on a publicly available network interface! This may compromise your network, privacy, wallet, etc. Never run this proxy on `0.0.0.0` if you do not understand what you're doing!
20+
21+
Contributors are not responsible for any damage incurred by using this proxy server.
22+
1723
## What this proxy does and what's not
1824

19-
- :white_check_mark: HTTP proxy protocol is supported
20-
- :white_check_mark: HTTPS CONNECT method is supported
21-
- :green_square: SOCKS5 support is expected
22-
- :green_square: HTTP proxy-chaining support is expected
23-
- :x: Config file support is possible but not planned
24-
- :x: Daemon mode support is possible but not planned
25-
- :x: HTTPS MitM support is not planned (use mitmproxy)
26-
- :x: Request/response rewrite support not planned (use mitmproxy)
27-
- :x: ACL support is not planned
25+
- HTTP proxy protocol is supported
26+
- HTTPS CONNECT method is supported
27+
- SOCKS5 protocol is supported
28+
- HTTP proxy-chaining is supported (use `HTTP_PROXY` environment variable)
29+
- Config file support is possible but not planned
30+
- Daemon mode support is possible but not planned
31+
- HTTPS MitM support is not planned (use mitmproxy)
32+
- Request/response rewrite support is not planned (use mitmproxy)
33+
- ACL support is not planned
2834

2935
## Usage
3036

@@ -34,46 +40,55 @@ Run the HTTP proxy on 127.0.0.1:8080 and redirect some hostnames to a local web
3440
etc-hosts-proxy run -H example.com=127.0.0.1 -H www.example.com=127.0.0.1
3541
```
3642

37-
Note: you may use comma-separated list of `<host>=<ip>` pairs in a single `-H` option too: `-H example.com=127.0.0.1,www.example.com=127.0.0.1`
38-
3943
Test the above with curl:
4044

4145
```bash
4246
curl -v -x 127.0.0.1:8080 http://example.com
4347
curl -v -x 127.0.0.1:8080 http://www.example.com
4448
```
4549

46-
Proxy listens on `127.0.0.1:8080` by default. Use `-L` (or `--listen-address`) CLI option to change this.
50+
NOTE: Proxy listens on `127.0.0.1:8080` by default. Use `-L` (or `--listen-address`) CLI option to change this.
51+
52+
Run the SOCKS5 proxy on 127.0.0.1:1080 and redirect some hostnames to a local web server:
53+
54+
```bash
55+
etc-hosts-proxy run -M socks5 -L 127.0.0.1:1080 -H example.com=127.0.0.1,www.example.com=127.0.0.1
56+
57+
curl -v -x socks5h://127.0.0.1:1080 http://example.com
58+
curl -v -x socks5h://127.0.0.1:1080 http://www.example.com
59+
```
60+
61+
See `etc-hosts-proxy --help` for general CLI usage information.
4762

4863
## Docker usage
4964

50-
You may use a docker image too if you'd like:
65+
You may also use a Docker image if you'd prefer:
5166

5267
```bash
53-
# Run the proxy in background
54-
docker run -d --name=etc-hosts-proxy -p 8080:8080 --rm \
68+
docker run -d --name=etc-hosts-proxy -p 127.0.0.1:8080:8080 --rm \
5569
-e ETC_HOSTS_PROXY_HOSTS_LIST="akamai.com=2.21.250.7,www.akamai.com=2.21.250.7" \
5670
-e ETC_HOSTS_PROXY_DEBUG=true \
5771
ghcr.io/jay7x/etc-hosts-proxy:latest
5872

5973
curl -v -x 127.0.0.1:8080 http://akamai.com
6074

6175
docker logs etc-hosts-proxy
76+
docker stop etc-hosts-proxy
6277
```
6378

64-
NOTE: You should not use 127.0.0.1 (or ::1) as your redirection target in the hosts list while running in a container. This will redirect the request to the container's localhost, which is not what you might expect.
79+
NOTE: You should not use 127.0.0.1 (or ::1) as your redirection destination in the hosts list while running in a container. This will redirect the request to the container's localhost, which is not what you might expect. You can use `host` Docker network if you really need it.
6580

6681
A bit more complex example to redirect some domains to a nginx container:
6782

6883
```bash
6984
# Create a docker network
7085
docker network create somenet
7186

72-
# Run a web server exposed in somenet and on 0.0.0.0:8080 on the host
73-
docker run -d --name=nginx --net=somenet -p 8080:80 --rm nginx:latest
87+
# Run a web server exposed in somenet and on 127.0.0.1:8080 on the host
88+
docker run -d --name=nginx --net=somenet -p 127.0.0.1:8080:80 --rm nginx:latest
7489

75-
# Run the proxy connected to somenet and exposed on 0.0.0.0:3128 on the host
76-
docker run -d --name=etc-hosts-proxy --net=somenet -p 3128:8080 --rm \
90+
# Run the proxy connected to somenet and exposed on 127.0.0.1:3128 on the host
91+
docker run -d --name=etc-hosts-proxy --net=somenet -p 127.0.0.1:3128:8080 --rm \
7792
-e ETC_HOSTS_PROXY_HOSTS_LIST="example.com=nginx,www.example.com=nginx" \
7893
-e ETC_HOSTS_PROXY_DEBUG=true \
7994
ghcr.io/jay7x/etc-hosts-proxy:latest
@@ -88,11 +103,11 @@ curl -v -x 127.0.0.1:3128 http://www.example.com
88103
curl -v -x 127.0.0.1:3128 http://example.net
89104

90105
# Check logs
91-
docker logs proxy
106+
docker logs etc-hosts-proxy
92107
docker logs nginx
93108

94109
# Cleanup
95-
docker stop proxy
110+
docker stop etc-hosts-proxy
96111
docker stop nginx
97112
docker network rm somenet
98113
```
@@ -109,4 +124,4 @@ See [etc-hosts-proxy Github Container registry](https://github.com/jay7x/etc-hos
109124
| `ETC_HOSTS_PROXY_LOG_LEVEL` | Set the logging level [`trace`, `debug`, `info`, `warn`, `error`] |
110125
| `ETC_HOSTS_PROXY_MODE` | Mode to start proxy in (`http` or `socks5`) |
111126
| `ETC_HOSTS_PROXY_LISTEN_ADDRESS` | [`<host>`]:`<port>` to listen for proxy requests on |
112-
| `ETC_HOSTS_PROXY_HOSTS_LIST` | comma-separated list of `<host>=<ip>` pairs to resolve `<host>` to `<ip>` |
127+
| `ETC_HOSTS_PROXY_HOSTS_LIST` | comma-separated list of `<host>=<ip>` pairs to redirect `<host>` to `<ip>` |

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819
77
github.com/sirupsen/logrus v1.9.3
88
github.com/spf13/cobra v1.7.0
9+
github.com/things-go/go-socks5 v0.0.3
910
)
1011

1112
require (

go.sum

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@ github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRM
1919
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
2020
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
2121
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
22-
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
2322
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
23+
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
24+
github.com/things-go/go-socks5 v0.0.3 h1:QtlIhkwDuLNCwW3wnt2uTjn1mQzpyjnwct2xdPuqroI=
25+
github.com/things-go/go-socks5 v0.0.3/go.mod h1:f8Zx+n8kfzyT90hXM767cP6sysAud93+t9rV90IgMcg=
26+
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
2427
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
2528
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
2629
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

run.go

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
package main
22

33
import (
4+
"context"
45
"fmt"
56
"net"
67
"net/http"
8+
"strconv"
79

810
"github.com/elazarl/goproxy"
911
"github.com/sirupsen/logrus"
1012
"github.com/spf13/cobra"
13+
"github.com/things-go/go-socks5"
14+
"github.com/things-go/go-socks5/statute"
1115
)
1216

1317
func newRunCommand() *cobra.Command {
@@ -34,7 +38,7 @@ func newRunCommand() *cobra.Command {
3438

3539
runCommand.Flags().StringToStringP("hosts", "H",
3640
GetEnvStrMap("ETC_HOSTS_PROXY_HOSTS_LIST"),
37-
"<host>=<ip> pairs to resolve <host> to <ip> (ETC_HOSTS_PROXY_HOSTS_LIST)")
41+
"<host>=<ip> pairs to redirect <host> to <ip> (ETC_HOSTS_PROXY_HOSTS_LIST)")
3842
runCommand.Flags().StringP("mode", "M",
3943
GetEnvWithDefault("ETC_HOSTS_PROXY_MODE", "http"),
4044
"Mode to start proxy in (http or socks5) (ETC_HOSTS_PROXY_MODE)")
@@ -44,6 +48,27 @@ func newRunCommand() *cobra.Command {
4448
return runCommand
4549
}
4650

51+
// SOCKS5 destination address rewriter
52+
type HostRewriter struct {
53+
hostsMap map[string]string
54+
}
55+
56+
func (r HostRewriter) Rewrite(ctx context.Context, request *socks5.Request) (context.Context, *statute.AddrSpec) {
57+
dst, found := r.hostsMap[request.DestAddr.FQDN]
58+
if !found {
59+
dst, found = r.hostsMap[request.DestAddr.IP.String()]
60+
}
61+
62+
if found {
63+
daSpec, err := statute.ParseAddrSpec(net.JoinHostPort(dst, strconv.Itoa(request.DestAddr.Port)))
64+
if err == nil {
65+
return ctx, &daSpec
66+
}
67+
logrus.Warnf("Unable to parse AddrSpec(%v:%v), skipping...", dst, request.DestAddr.Port)
68+
}
69+
return ctx, request.DestAddr
70+
}
71+
4772
func runAction(cmd *cobra.Command, args []string) error {
4873
listenAddress, err := cmd.Flags().GetString("listen-address")
4974
if err != nil {
@@ -54,37 +79,45 @@ func runAction(cmd *cobra.Command, args []string) error {
5479
if err != nil {
5580
return err
5681
}
57-
for host, ip := range hostsMap {
58-
logrus.Debugf("Mapping %s to %s", host, ip)
82+
for src, dst := range hostsMap {
83+
logrus.Debugf("Mapping %s to %s", src, dst)
5984
}
6085

6186
switch proxyMode, _ := cmd.Flags().GetString("mode"); proxyMode {
6287
case "http":
63-
logrus.Debugln("Starting HTTP proxy...")
88+
logrus.Debugf("Starting HTTP proxy on %s", listenAddress)
6489
proxy := goproxy.NewProxyHttpServer()
6590
proxy.Logger = logrus.StandardLogger()
6691
if logrus.GetLevel() >= logrus.DebugLevel {
6792
proxy.Verbose = true
6893
}
6994
proxy.OnRequest().DoFunc(
7095
func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
71-
if ip, found := hostsMap[r.Host]; found {
72-
r.URL.Host = ip
96+
if dst, found := hostsMap[r.Host]; found {
97+
r.URL.Host = dst
7398
}
7499
return r, nil
75100
})
76101
proxy.OnRequest().HandleConnectFunc(
77102
func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) {
78103
h, port, _ := net.SplitHostPort(host)
79-
if ip, found := hostsMap[h]; found {
80-
return goproxy.OkConnect, net.JoinHostPort(ip, port)
104+
if dst, found := hostsMap[h]; found {
105+
return goproxy.OkConnect, net.JoinHostPort(dst, port)
81106
}
82107
return goproxy.OkConnect, host
83108
})
84109
logrus.Fatal(http.ListenAndServe(listenAddress, proxy))
110+
85111
case "socks5":
86-
logrus.Debugln("Starting SOCKS5 proxy...")
87-
logrus.Fatalln("SOCKS5 proxy is not implemented yet...")
112+
logrus.Debugf("Starting SOCKS5 proxy on %s", listenAddress)
113+
proxy := socks5.NewServer(
114+
socks5.WithLogger(logrus.StandardLogger()),
115+
socks5.WithRewriter(HostRewriter{hostsMap: hostsMap}),
116+
)
117+
if err := proxy.ListenAndServe("tcp", listenAddress); err != nil {
118+
logrus.Fatal(err)
119+
}
120+
88121
default:
89122
logrus.Fatalf("Unsupported proxy mode %v", proxyMode)
90123
}

0 commit comments

Comments
 (0)