use uTLS to parrot TLS handshake
Let's try to wrap the TLS library used by the standard http client with uTLS. The goal is to avoid having a very distinctive tls signature during the initial handshake, since this can be very easily detected (and blocked).
We are going to use https://github.com/refraction-networking/utls
First, have a look at the examples - we can use Roller (but we want to reuse the roller).
For that, we can use a custom DialTLSContext, see: https://pkg.go.dev/net/http#Transport.DialTLSContext
So, we identify the point in which we instantiate the http client in bonafide: https://0xacab.org/leap/bitmask-vpn/-/blob/main/pkg/vpn/bonafide/bonafide.go#L102
And in there we create a function that returns the wrapped net.Conn and any error. inside that func we pass the uTLS client:
client := &http.Client{
Jar: cookieJar,
Transport: &http.Transport{
DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
// Note that hardcoding the address is not necessary here. Only
// do that if you want to ignore the DNS lookup that already
// happened behind the scenes.
tcpConn, err := (&net.Dialer{}).DialContext(ctx, network, addr)
if err != nil {
return nil, err
}
config := tls.Config{ServerName: "www.stackoverflow.com"}
tlsConn := tls.UClient(tcpConn, &config, tls.HelloChrome_Auto)
err = tlsConn.Handshake()
if err != nil {
return nil, fmt.Errorf("uTlsConn.Handshake() error: %w", err)
}
return tlsConn, nil
},
},
}