Skip to content

Commit

Permalink
Merge pull request #13 from jay7x/socks5
Browse files Browse the repository at this point in the history
Implement SOCKS5 support
  • Loading branch information
jay7x authored Jun 29, 2023
2 parents 2ad5d4d + 99a32d4 commit e2987c6
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 34 deletions.
61 changes: 38 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,23 @@ This proxy implements the 3rd way. Nor admin privileges neither any system-wide

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

## WARNING & DISCLAIMER

⚠️ 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!

Contributors are not responsible for any damage incurred by using this proxy server.

## What this proxy does and what's not

- :white_check_mark: HTTP proxy protocol is supported
- :white_check_mark: HTTPS CONNECT method is supported
- :green_square: SOCKS5 support is expected
- :green_square: HTTP proxy-chaining support is expected
- :x: Config file support is possible but not planned
- :x: Daemon mode support is possible but not planned
- :x: HTTPS MitM support is not planned (use mitmproxy)
- :x: Request/response rewrite support not planned (use mitmproxy)
- :x: ACL support is not planned
- HTTP proxy protocol is supported
- HTTPS CONNECT method is supported
- SOCKS5 protocol is supported
- HTTP proxy-chaining is supported (use `HTTP_PROXY` environment variable)
- Config file support is possible but not planned
- Daemon mode support is possible but not planned
- HTTPS MitM support is not planned (use mitmproxy)
- Request/response rewrite support is not planned (use mitmproxy)
- ACL support is not planned

## Usage

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

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`

Test the above with curl:

```bash
curl -v -x 127.0.0.1:8080 http://example.com
curl -v -x 127.0.0.1:8080 http://www.example.com
```

Proxy listens on `127.0.0.1:8080` by default. Use `-L` (or `--listen-address`) CLI option to change this.
NOTE: Proxy listens on `127.0.0.1:8080` by default. Use `-L` (or `--listen-address`) CLI option to change this.

Run the SOCKS5 proxy on 127.0.0.1:1080 and redirect some hostnames to a local web server:

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

curl -v -x socks5h://127.0.0.1:1080 http://example.com
curl -v -x socks5h://127.0.0.1:1080 http://www.example.com
```

See `etc-hosts-proxy --help` for general CLI usage information.

## Docker usage

You may use a docker image too if you'd like:
You may also use a Docker image if you'd prefer:

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

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

docker logs etc-hosts-proxy
docker stop etc-hosts-proxy
```

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.
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.

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

```bash
# Create a docker network
docker network create somenet

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

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

# Check logs
docker logs proxy
docker logs etc-hosts-proxy
docker logs nginx

# Cleanup
docker stop proxy
docker stop etc-hosts-proxy
docker stop nginx
docker network rm somenet
```
Expand All @@ -109,4 +124,4 @@ See [etc-hosts-proxy Github Container registry](https://github.com/jay7x/etc-hos
| `ETC_HOSTS_PROXY_LOG_LEVEL` | Set the logging level [`trace`, `debug`, `info`, `warn`, `error`] |
| `ETC_HOSTS_PROXY_MODE` | Mode to start proxy in (`http` or `socks5`) |
| `ETC_HOSTS_PROXY_LISTEN_ADDRESS` | [`<host>`]:`<port>` to listen for proxy requests on |
| `ETC_HOSTS_PROXY_HOSTS_LIST` | comma-separated list of `<host>=<ip>` pairs to resolve `<host>` to `<ip>` |
| `ETC_HOSTS_PROXY_HOSTS_LIST` | comma-separated list of `<host>=<ip>` pairs to redirect `<host>` to `<ip>` |
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.7.0
github.com/things-go/go-socks5 v0.0.3
)

require (
Expand Down
5 changes: 4 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRM
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/things-go/go-socks5 v0.0.3 h1:QtlIhkwDuLNCwW3wnt2uTjn1mQzpyjnwct2xdPuqroI=
github.com/things-go/go-socks5 v0.0.3/go.mod h1:f8Zx+n8kfzyT90hXM767cP6sysAud93+t9rV90IgMcg=
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
53 changes: 43 additions & 10 deletions run.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package main

import (
"context"
"fmt"
"net"
"net/http"
"strconv"

"github.com/elazarl/goproxy"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/things-go/go-socks5"
"github.com/things-go/go-socks5/statute"
)

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

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

// SOCKS5 destination address rewriter
type HostRewriter struct {
hostsMap map[string]string
}

func (r HostRewriter) Rewrite(ctx context.Context, request *socks5.Request) (context.Context, *statute.AddrSpec) {
dst, found := r.hostsMap[request.DestAddr.FQDN]
if !found {
dst, found = r.hostsMap[request.DestAddr.IP.String()]
}

if found {
daSpec, err := statute.ParseAddrSpec(net.JoinHostPort(dst, strconv.Itoa(request.DestAddr.Port)))
if err == nil {
return ctx, &daSpec
}
logrus.Warnf("Unable to parse AddrSpec(%v:%v), skipping...", dst, request.DestAddr.Port)
}
return ctx, request.DestAddr
}

func runAction(cmd *cobra.Command, args []string) error {
listenAddress, err := cmd.Flags().GetString("listen-address")
if err != nil {
Expand All @@ -54,37 +79,45 @@ func runAction(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
for host, ip := range hostsMap {
logrus.Debugf("Mapping %s to %s", host, ip)
for src, dst := range hostsMap {
logrus.Debugf("Mapping %s to %s", src, dst)
}

switch proxyMode, _ := cmd.Flags().GetString("mode"); proxyMode {
case "http":
logrus.Debugln("Starting HTTP proxy...")
logrus.Debugf("Starting HTTP proxy on %s", listenAddress)
proxy := goproxy.NewProxyHttpServer()
proxy.Logger = logrus.StandardLogger()
if logrus.GetLevel() >= logrus.DebugLevel {
proxy.Verbose = true
}
proxy.OnRequest().DoFunc(
func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
if ip, found := hostsMap[r.Host]; found {
r.URL.Host = ip
if dst, found := hostsMap[r.Host]; found {
r.URL.Host = dst
}
return r, nil
})
proxy.OnRequest().HandleConnectFunc(
func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) {
h, port, _ := net.SplitHostPort(host)
if ip, found := hostsMap[h]; found {
return goproxy.OkConnect, net.JoinHostPort(ip, port)
if dst, found := hostsMap[h]; found {
return goproxy.OkConnect, net.JoinHostPort(dst, port)
}
return goproxy.OkConnect, host
})
logrus.Fatal(http.ListenAndServe(listenAddress, proxy))

case "socks5":
logrus.Debugln("Starting SOCKS5 proxy...")
logrus.Fatalln("SOCKS5 proxy is not implemented yet...")
logrus.Debugf("Starting SOCKS5 proxy on %s", listenAddress)
proxy := socks5.NewServer(
socks5.WithLogger(logrus.StandardLogger()),
socks5.WithRewriter(HostRewriter{hostsMap: hostsMap}),
)
if err := proxy.ListenAndServe("tcp", listenAddress); err != nil {
logrus.Fatal(err)
}

default:
logrus.Fatalf("Unsupported proxy mode %v", proxyMode)
}
Expand Down

0 comments on commit e2987c6

Please sign in to comment.