mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-07 09:00:47 +00:00
feat: HTTP add TLS fingerprint fallback (#1444)
This commit is contained in:
68 files changed
+7243
-1604
No files matched your search
@@ -0,0 +1,100 @@
|
||||
package httpclienttest
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type Profile string
|
||||
|
||||
const (
|
||||
ProfileNative Profile = "native"
|
||||
ProfileChrome Profile = "chrome"
|
||||
ProfileFirefox Profile = "firefox"
|
||||
ProfileSafari Profile = "safari"
|
||||
ProfileAnyBrowser Profile = "browser"
|
||||
)
|
||||
|
||||
type FingerprintServerOptions struct {
|
||||
RequiredProfile Profile
|
||||
Handler http.Handler
|
||||
}
|
||||
|
||||
type FingerprintServer struct {
|
||||
*httptest.Server
|
||||
|
||||
mu sync.Mutex
|
||||
profiles []Profile
|
||||
}
|
||||
|
||||
func NewFingerprintServer(t testing.TB, options FingerprintServerOptions) *FingerprintServer {
|
||||
t.Helper()
|
||||
|
||||
handler := options.Handler
|
||||
if handler == nil {
|
||||
handler = http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
}
|
||||
server := &FingerprintServer{
|
||||
Server: httptest.NewUnstartedServer(handler),
|
||||
}
|
||||
server.EnableHTTP2 = true
|
||||
server.Config.ErrorLog = log.New(io.Discard, "", 0)
|
||||
server.TLS = &tls.Config{
|
||||
GetConfigForClient: func(hello *tls.ClientHelloInfo) (*tls.Config, error) {
|
||||
profile := classifyProfile(hello.CipherSuites)
|
||||
server.mu.Lock()
|
||||
server.profiles = append(server.profiles, profile)
|
||||
server.mu.Unlock()
|
||||
if profileMatches(profile, options.RequiredProfile) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.New("required TLS fingerprint was not presented")
|
||||
},
|
||||
}
|
||||
server.StartTLS()
|
||||
t.Cleanup(server.Close)
|
||||
return server
|
||||
}
|
||||
|
||||
func (s *FingerprintServer) Profiles() []Profile {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return append([]Profile(nil), s.profiles...)
|
||||
}
|
||||
|
||||
func profileMatches(actual, required Profile) bool {
|
||||
if required == ProfileAnyBrowser {
|
||||
return actual != ProfileNative
|
||||
}
|
||||
return actual == required
|
||||
}
|
||||
|
||||
func classifyProfile(cipherSuites []uint16) Profile {
|
||||
if len(cipherSuites) >= 5 && isGREASE(cipherSuites[0]) {
|
||||
switch cipherSuites[4] {
|
||||
case tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
|
||||
return ProfileChrome
|
||||
case tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
|
||||
return ProfileSafari
|
||||
}
|
||||
}
|
||||
if len(cipherSuites) >= 3 &&
|
||||
cipherSuites[0] == tls.TLS_AES_128_GCM_SHA256 &&
|
||||
cipherSuites[1] == tls.TLS_CHACHA20_POLY1305_SHA256 &&
|
||||
cipherSuites[2] == tls.TLS_AES_256_GCM_SHA384 {
|
||||
return ProfileFirefox
|
||||
}
|
||||
return ProfileNative
|
||||
}
|
||||
|
||||
func isGREASE(value uint16) bool {
|
||||
return value&0x0f0f == 0x0a0a && byte(value) == byte(value>>8)
|
||||
}
|
||||
Reference in new issue
Block a user