diff --git a/README.md b/README.md index 48225a7..de6dbbc 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ random-like. **You may only leave it as `plain` if you are certain that your und encryption and authentication (via AEAD or similar techniques).** `ServerName` is the domain you want to make your ISP or firewall _think_ you are visiting. Ideally it should -match `RedirAddr` in the server's configuration, a major site the censor allows, but it doesn't have to. +match `RedirAddr` in the server's configuration, a major site the censor allows, but it doesn't have to. Use `random` to randomize the server name for every connection made. `AlternativeNames` is an array used alongside `ServerName` to shuffle between different ServerNames for every new connection. **This may conflict with `CDN` Transport mode** if the CDN provider prohibits domain fronting and rejects diff --git a/internal/client/TLS.go b/internal/client/TLS.go index 6c97ab9..469ad28 100644 --- a/internal/client/TLS.go +++ b/internal/client/TLS.go @@ -1,9 +1,14 @@ package client import ( + cryptoRand "crypto/rand" utls "github.com/refraction-networking/utls" log "github.com/sirupsen/logrus" + "math/big" + "math/rand" "net" + "strings" + "time" "github.com/cbeuw/Cloak/internal/common" ) @@ -30,6 +35,33 @@ type DirectTLS struct { browser browser } +var topLevelDomains = []string{"com", "net", "org", "it", "fr", "me", "ru", "cn", "es", "tr", "top", "xyz", "info"} + +// https://github.com/ProtonVPN/wireguard-go/commit/bcf344b39b213c1f32147851af0d2a8da9266883 +func randomServerName() string { + charNum := int('z') - int('a') + 1 + size := 3 + randInt(10) + name := make([]byte, size) + for i := range name { + name[i] = byte(int('a') + randInt(charNum)) + } + return string(name) + "." + randItem(topLevelDomains) +} + +func randItem(list []string) string { + return list[randInt(len(list))] +} + +func randInt(n int) int { + size, err := cryptoRand.Int(cryptoRand.Reader, big.NewInt(int64(n))) + if err == nil { + return int(size.Int64()) + } + //goland:noinspection GoDeprecation + rand.Seed(time.Now().UnixNano()) + return rand.Intn(n) +} + func buildClientHello(browser browser, fields clientHelloFields) ([]byte, error) { // We don't use utls to handle connections (as it'll attempt a real TLS negotiation) // We only want it to build the ClientHello locally @@ -89,6 +121,10 @@ func (tls *DirectTLS) Handshake(rawConn net.Conn, authInfo AuthInfo) (sessionKey serverName: authInfo.MockDomain, } + if strings.EqualFold(fields.serverName, "random") { + fields.serverName = randomServerName() + } + var ch []byte ch, err = buildClientHello(tls.browser, fields) if err != nil {