-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Conversation
Instead of using net/proxy, can you directly use the standard library (https://pkg.go.dev/net/http#Transport), where they states that support socks5 as a proxy schema? |
Maybe you can make a variant of cascadeproxy where the end proxy is a socks server, it's up to you |
Oh, last thing, I've rewritten the cascadeproxy in the rewrite-examples branch. rewrite-examples If you decide to base the code on this example, use the code from this branch and not from the master, please |
Thanks alot for your help. I'm having issue on creating ConnectDial without the socks client |
What's the issue? |
Im doing sth like: proxyServer.Tr.Proxy = func(r *http.Request) (*url.URL, error) {
Url := &url.URL{
Scheme: "socks5",
Host: socksAddr,
}
if auth.User != "" {
Url.User = url.UserPassword(auth.User, auth.Password)
}
return Url, nil
} Without the ConnectDial, it will only redirect the http request to socks5. And in the case that instead of using net/proxy, Ill try to do the same thing with func (proxy *ProxyHttpServer) NewConnectDialToProxyWithHandler, pass in the socks5 proxy address, and then implement the raw handshake with socks and connect to it |
Yes, ConnectDial is required, this morning I updated the cascadeproxy example to include it, I confirm that it's required |
…rd party libraries
I have rewritten the code and implemented the ConnectDial |
Can you call the exampe |
return nil, err | ||
} | ||
if b[0] != Version5 { | ||
return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0]))) | ||
} | ||
if cmdErr := Reply(b[1]); cmdErr != StatusSucceeded { | ||
return nil, errors.New("unknown error " + cmdErr.String()) | ||
} | ||
if b[2] != 0 { | ||
return nil, errors.New("non-zero reserved field") | ||
} | ||
l := 2 | ||
var a Addr | ||
switch b[3] { | ||
case AddrTypeIPv4: | ||
l += net.IPv4len | ||
a.IP = make(net.IP, net.IPv4len) | ||
case AddrTypeIPv6: | ||
l += net.IPv6len | ||
a.IP = make(net.IP, net.IPv6len) | ||
case AddrTypeFQDN: | ||
if _, err := io.ReadFull(c, b[:1]); err != nil { | ||
return nil, err | ||
} | ||
l += int(b[0]) | ||
default: | ||
return nil, errors.New("unknown address type " + strconv.Itoa(int(b[3]))) | ||
} | ||
if cap(b) < l { | ||
b = make([]byte, l) | ||
} else { | ||
b = b[:l] | ||
} | ||
if _, err = io.ReadFull(c, b); err != nil { | ||
return nil, err | ||
} | ||
if a.IP != nil { | ||
copy(a.IP, b) | ||
} else { | ||
a.Name = string(b[:len(b)-2]) | ||
} | ||
a.Port = int(b[len(b)-2])<<8 | int(b[len(b)-1]) | ||
return c, nil | ||
}, nil | ||
} | ||
|
||
const ( | ||
authUsernamePasswordVersion = 0x01 | ||
authStatusSucceeded = 0x00 | ||
) | ||
|
||
func (up *SocksAuth) Authenticate(ctx context.Context, rw io.ReadWriter, auth AuthMethod) error { | ||
switch auth { | ||
case AuthMethodNotRequired: | ||
return nil | ||
case AuthMethodUsernamePassword: | ||
if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) > 255 { | ||
return errors.New("invalid username/password") | ||
} | ||
b := []byte{authUsernamePasswordVersion} | ||
b = append(b, byte(len(up.Username))) | ||
b = append(b, up.Username...) | ||
b = append(b, byte(len(up.Password))) | ||
b = append(b, up.Password...) | ||
|
||
if _, err := rw.Write(b); err != nil { | ||
return err | ||
} | ||
if _, err := io.ReadFull(rw, b[:2]); err != nil { | ||
return err | ||
} | ||
if b[0] != authUsernamePasswordVersion { | ||
return errors.New("invalid username/password version") | ||
} | ||
if b[1] != authStatusSucceeded { | ||
return errors.New("username/password authentication failed") | ||
} | ||
return nil | ||
} | ||
return errors.New("unsupported authentication method " + strconv.Itoa(int(auth))) | ||
} | ||
|
||
func splitHostPort(address string) (string, int, error) { | ||
host, port, err := net.SplitHostPort(address) | ||
if err != nil { | ||
return "", 0, err | ||
} | ||
portnum, err := strconv.Atoi(port) | ||
if err != nil { | ||
return "", 0, err | ||
} | ||
if 1 > portnum || portnum > 0xffff { | ||
return "", 0, errors.New("port number out of range " + port) | ||
} | ||
ip, err := net.ResolveIPAddr("ip", host) | ||
if err != nil { | ||
return "", 0, err | ||
} | ||
return ip.String(), portnum, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of copying the whole file, isn't there something exposed in that packages (or other packages of standard library) that just allows the opening of a dialed connection through a target SOCKS server?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the "net/http" does support but in order to redirect https request I need a socks5 client that can satisfy the ConnectDial
I dont know why but in my case if I dont resolve the ip address myself it would have something like this: |
with the standard library I can do something like(work for both http and https): package main
import (
"crypto/tls"
"flag"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"net/url"
"strings"
"time"
)
type SocksAuth struct {
User, Password string
}
func createSocksProxy(socksAddr string, auth SocksAuth) func(r *http.Request) (*url.URL, error) {
return func(r *http.Request) (*url.URL, error) {
host := r.URL.Host
if !strings.ContainsRune(host, ':') {
host += ":" + r.URL.Port()
}
resolvedAddr, _ := resolveAddress(host)
r.URL.Host = resolvedAddr
Url := &url.URL{
Scheme: "socks5",
Host: socksAddr,
}
if auth.User != "" {
Url.User = url.UserPassword(auth.User, auth.Password)
}
return Url, nil
}
}
func main() {
target := flag.String("target", "https://www.google.com", "URL to get")
proxyAddr := flag.String("proxy", "localhost:1080", "SOCKS5 proxy address to use")
username := flag.String("user", "", "username for SOCKS5 proxy")
password := flag.String("pass", "", "password for SOCKS5 proxy")
flag.Parse()
auth := SocksAuth{
User: *username,
Password: *password,
}
client := &http.Client{
Transport: &http.Transport{
Proxy: createSocksProxy(*proxyAddr, auth),
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
Timeout: 30 * time.Second,
}
resolvedAddr, err := resolveAddress(*target)
if err != nil {
log.Fatal(err)
}
req, err := http.NewRequest("GET", *target, nil)
if err != nil {
log.Fatalf("Failed to create request: %v", err)
}
req.URL.Host = resolvedAddr
r, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer r.Body.Close()
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(body))
}
func resolveAddress(target string) (string, error) {
u, err := url.Parse(target)
if err != nil {
return "", fmt.Errorf("invalid URL: %w", err)
}
host, port := u.Hostname(), u.Port()
ipAddr, err := net.ResolveIPAddr("ip", host)
if err != nil {
return "", fmt.Errorf("failed to resolve hostname: %w", err)
}
return net.JoinHostPort(ipAddr.String(), port), nil
} But I havent figured out how to implement ConnectDial with this |
We can try to use a different approach:
What do you think? |
Hmm, Ill try this approach |
If you want, you can also launch a test local SOCKS5 proxy in your example, using this library: https://github.com/things-go/go-socks5 |
Im having a local socks5 proxy using dante on linux |
Yes, but the example should be used by anyone that wants to. |
I agreed, I will add it, and by the way I think I will use the HijackConnect to redirect the request to socks proxy |
|
I have removed the socks5 client |
Done! |
Co-authored-by: Erik Pellizzon <[email protected]>
# Conflicts: # examples/cascadeproxy-socks/socksproxy.go
Done |
Ive added |
Ive added |
Co-authored-by: Erik Pellizzon <[email protected]>
Thanks! Now it looks good to me. Sorry If I have bothered you with all the requested changes! |
I add an example demonstrating how to configure goproxy to forward both HTTP and HTTPS traffic through a SOCKS5 proxy. Since the SOCKS5 dialer doesn't handle DNS resolution, I manually resolve the target addresses before passing them to the SOCKS5 proxy.