valyala / fasthttp

Fast HTTP package for Go. Tuned for high performance. Zero memory allocations in hot paths. Up to 10x faster than net/http
MIT License
21.91k stars 1.76k forks source link

How to use fasthttp to change tls fingerprint? #1620

Closed JobberRT closed 1 year ago

JobberRT commented 1 year ago

As for now, I'm using fasthttp v1.48.0, and the result of tls.browserleaks.com/json shows a fixed tls fingerprint(JA3 hash).

I want to change the tls fingerprint for each fasthttp.Client with proxy. is it possiable? Below is some request I need to satisfy:

  1. I need the use it along with fasthttpproxy. When I change proxy, Client.CloseIdleConnection() will be called, and then change the Client.Dial.
  2. I want to change the fingerprint when I change the proxy.

Tried using utls but couldn't get it to work, Any help would be appreciated!

Broken code, error: "invalid value length: expected 32, got 0"

// tls package here is actually "github.com/refraction-networking/utls"

dialer := func(addr string) (net.Conn, error) {
        conn, err := tls.Dial("tcp", addr, &tls.Config{InsecureSkipVerify: true})
        if err != nil {
            logrus.WithError(err).Error("failed to create tls conn")
            return nil, err
        }
        utlsConn := tls.UClient(conn, &tls.Config{InsecureSkipVerify: true}, tls.HelloRandomized)
        return utlsConn.Conn, nil
    }

    httpClient.CloseIdleConnections()
    httpClient.Dial = dialer

    req := fasthttp.AcquireRequest()
    res := fasthttp.AcquireResponse()
    defer func() {
        fasthttp.ReleaseRequest(req)
        fasthttp.ReleaseResponse(res)
    }()

    req.SetRequestURI("https://tls.browserleaks.com/json")
    req.Header.SetMethod(fasthttp.MethodGet)

    if err := httpClient.DoTimeout(req, res, 15*time.Second); err != nil {
        logrus.WithError(err).Error("failed to send tls request")
        return
    }

    jd, err := fastjson.ParseBytes(res.Body())
    if err != nil {
        logrus.WithError(err).Error("failed to parse tls response")
        return
    }
    fmt.Println(string(jd.GetStringBytes("ja3_hash")))
JobberRT commented 1 year ago

Anser my own question

Working example, remember the tls is actually utls

var (
    helloIds = []tls.ClientHelloID{
        tls.HelloRandomized,
        tls.HelloRandomizedALPN,
        tls.HelloRandomizedNoALPN,
        tls.HelloFirefox_Auto,
        tls.HelloFirefox_55,
        tls.HelloFirefox_56,
        tls.HelloFirefox_63,
        tls.HelloFirefox_65,
        tls.HelloFirefox_99,
        tls.HelloFirefox_102,
        tls.HelloFirefox_105,
        tls.HelloChrome_Auto,
        tls.HelloChrome_58,
        tls.HelloChrome_62,
        tls.HelloChrome_70,
        tls.HelloChrome_72,
        tls.HelloChrome_83,
        tls.HelloChrome_87,
        tls.HelloChrome_96,
        tls.HelloChrome_100,
        tls.HelloChrome_102,
        tls.HelloChrome_106_Shuffle,
        tls.HelloChrome_100_PSK,
        tls.HelloChrome_112_PSK_Shuf,
        tls.HelloChrome_114_Padding_PSK_Shuf,
        tls.HelloChrome_115_PQ,
        tls.HelloChrome_115_PQ_PSK,
        tls.HelloEdge_Auto,
        tls.HelloEdge_85,
        tls.HelloEdge_106,
        tls.HelloSafari_Auto,
        tls.HelloSafari_16_0,
    }
    uHttpClient = &fasthttp.Client{}
)

func testUTLSWithFasthttp() {
    for i := 0; i < 30; i++ {
        doUTLSWithFasthttp()
    }
}

func doUTLSWithFasthttp() {
    dialer := func(addr string) (net.Conn, error) {
        conn, err := net.DialTimeout("tcp", addr, 10*time.Second)
        if err != nil {
            panic(err)
        }

        id := helloIds[time.Now().UnixMilli()%int64(len(helloIds))]
        fmt.Printf("%+v\n", id)
        tlsConn := tls.UClient(conn, &tls.Config{MaxVersion: tls.VersionTLS12, ServerName: "tls.browserleaks.com", OmitEmptyPsk: true}, id)
        if err := tlsConn.Handshake(); err != nil {
            panic(err)
        }

        return tlsConn, nil
        // conn, err := roller.Dial("tcp4", addr, strings.Split(addr, ":")[0])
        // if err != nil {
        //  logrus.WithError(err).Error("failed to create roller connection")
        //  return nil, err
        // }
        // uTLSConn = conn
        // return conn, nil
    }

    uHttpClient.Dial = dialer

    req := fasthttp.AcquireRequest()
    res := fasthttp.AcquireResponse()
    defer func() {
        fasthttp.ReleaseRequest(req)
        fasthttp.ReleaseResponse(res)
    }()

    req.SetRequestURI("https://tls.browserleaks.com/json")
    req.Header.SetMethod(fasthttp.MethodGet)

    if err := uHttpClient.DoTimeout(req, res, 15*time.Second); err != nil {
        logrus.WithError(err).Error("failed to send request")
        return
    }

    jd, err := fastjson.ParseBytes(res.Body())
    if err != nil {
        logrus.WithError(err).Error("failed to parse response")
        return
    }

    fmt.Println(string(jd.GetStringBytes("ja3_hash")))
    uHttpClient.CloseIdleConnections()
}