Skip to content
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

Add example for SOCKS proxy chaining #573

Merged
merged 20 commits into from
Dec 20, 2024
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
29 changes: 29 additions & 0 deletions examples/cascadeproxy-socks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# CascadeSocksProxy

`CascadeSocksProxy` is an example that shows an aggregator server that forwards
the requests to another socks proxy server. This example is written base on `cascadeproxy` example.

Diagram:
```
client --> goproxy --> socks5 proxy --> internet
```

This example starts a HTTP/HTTPS proxy using goproxy that listens on port `8080`, and forward the requests to the socks5 proxy on `socks5://localhost:1080`.
It uses MITM (Man in the Middle) proxy mode to retriece and parse the request, and then forwards it to the destination using the socks5 proxy client implemented in the standard Go `net/http` library.

### Example usage:

Aggregator server that have HTTP proxy server run on port `8080` and forward the requests to socks proxy listens on `socks5://localhost:1080` with no auth
```shell
./socks -v -addr ":8080" -socks "localhost:1080"
```

With auth:
```shell
./socks -v -addr ":8080" -socks "localhost:1080" -user "bob" -pass "123"
```

You can run the socks proxy server locally for testing with the following command - this will start a socks5 proxy server on port `1080` with no auth:
```shell
./socks5proxyserver/socks5proxyserver
```
5 changes: 5 additions & 0 deletions examples/cascadeproxy-socks/socks5proxyserver/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module socks5proxyserver

go 1.20

require github.com/things-go/go-socks5 v0.0.5
7 changes: 7 additions & 0 deletions examples/cascadeproxy-socks/socks5proxyserver/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/things-go/go-socks5 v0.0.5 h1:qvKaGcBkfDrUL33SchHN93srAmYGzb4CxSM2DPYufe8=
github.com/things-go/go-socks5 v0.0.5/go.mod h1:mtzInf8v5xmsBpHZVbIw2YQYhc4K0jRwzfsH64Uh0IQ=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
25 changes: 25 additions & 0 deletions examples/cascadeproxy-socks/socks5proxyserver/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package main

import (
"context"
"github.com/things-go/go-socks5"
"log"
"net"
"os"
)

func main() {
// Create a SOCKS5 server
server := socks5.NewServer(
socks5.WithLogger(socks5.NewLogger(log.New(os.Stdout, "socks5: ", log.LstdFlags))),
socks5.WithDialAndRequest(func(ctx context.Context, network, addr string, request *socks5.Request) (net.Conn, error) {
log.Printf("Request from %s to %s", request.RemoteAddr, request.DestAddr)
return net.Dial(network, addr)
}),
)

// Create SOCKS5 proxy on localhost port 1080
if err := server.ListenAndServe("tcp", ":1080"); err != nil {
panic(err)
}
}
70 changes: 70 additions & 0 deletions examples/cascadeproxy-socks/socksproxy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package main

import (
"crypto/tls"
"flag"
"log"
"net/http"
"net/url"

"github.com/elazarl/goproxy"
)

type SocksAuth struct {
Username, Password string
}

func createSocksProxy(socksAddr string, auth SocksAuth) func(r *http.Request) (*url.URL, error) {
return func(r *http.Request) (*url.URL, error) {
Url := &url.URL{
Scheme: "socks5",
Host: socksAddr,
}
if auth.Username != "" {
Url.User = url.UserPassword(auth.Username, auth.Password)
}
return Url, nil
}
}

func main() {
verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
addr := flag.String("addr", ":8080", "proxy listen address")
socksAddr := flag.String("socks", "127.0.0.1:1080", "socks proxy address")
username := flag.String("user", "", "username for SOCKS5 proxy if auth is required")
password := flag.String("pass", "", "password for SOCKS5 proxy")
flag.Parse()

auth := SocksAuth{
Username: *username,
Password: *password,
}
proxyServer := goproxy.NewProxyHttpServer()

proxyServer.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
client := &http.Client{
Transport: &http.Transport{
Proxy: createSocksProxy(*socksAddr, auth),
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}

// https://stackoverflow.com/questions/19595860/http-request-requesturi-field-when-making-request-in-go
req.RequestURI = ""
ErikPelli marked this conversation as resolved.
Show resolved Hide resolved

resp, err := client.Do(req)
if err != nil {
ctx.Logf("Failed to forward request: " + err.Error())
return nil, nil
}
ctx.Logf("Succesfully forwarded request to socks proxy")
return req, resp
})

proxyServer.OnRequest().HandleConnect(goproxy.AlwaysMitm)
proxyServer.Verbose = *verbose

log.Fatalln(http.ListenAndServe(*addr, proxyServer))
}
Loading