Merge pull request #242 from notsure2/random-sni

Support ServerName randomization (by setting ServerName=random) using ProtonVPN algo
master
Andy Wang 1 month ago committed by GitHub
commit de4dab6bf3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -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

@ -1,11 +1,11 @@
package client
import (
"github.com/cbeuw/Cloak/internal/common"
utls "github.com/refraction-networking/utls"
log "github.com/sirupsen/logrus"
"net"
"github.com/cbeuw/Cloak/internal/common"
"strings"
)
const appDataMaxLength = 16401
@ -30,6 +30,40 @@ type DirectTLS struct {
browser browser
}
var topLevelDomains = []string{"com", "net", "org", "it", "fr", "me", "ru", "cn", "es", "tr", "top", "xyz", "info"}
func randomServerName() string {
/*
Copyright: Proton AG
https://github.com/ProtonVPN/wireguard-go/commit/bcf344b39b213c1f32147851af0d2a8da9266883
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
charNum := int('z') - int('a') + 1
size := 3 + common.RandInt(10)
name := make([]byte, size)
for i := range name {
name[i] = byte(int('a') + common.RandInt(charNum))
}
return string(name) + "." + common.RandItem(topLevelDomains)
}
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 +123,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 {

@ -6,6 +6,7 @@ import (
"crypto/rand"
"errors"
"io"
"math/big"
"time"
log "github.com/sirupsen/logrus"
@ -52,8 +53,8 @@ func CryptoRandRead(buf []byte) {
RandRead(rand.Reader, buf)
}
func RandRead(randSource io.Reader, buf []byte) {
_, err := randSource.Read(buf)
func backoff(f func() error) {
err := f()
if err == nil {
return
}
@ -61,12 +62,36 @@ func RandRead(randSource io.Reader, buf []byte) {
100 * time.Millisecond, 300 * time.Millisecond, 500 * time.Millisecond, 1 * time.Second,
3 * time.Second, 5 * time.Second}
for i := 0; i < 10; i++ {
log.Errorf("Failed to get random bytes: %v. Retrying...", err)
_, err = randSource.Read(buf)
log.Errorf("Failed to get random: %v. Retrying...", err)
err = f()
if err == nil {
return
}
time.Sleep(waitDur[i])
}
log.Fatal("Cannot get random bytes after 10 retries")
log.Fatal("Cannot get random after 10 retries")
}
func RandRead(randSource io.Reader, buf []byte) {
backoff(func() error {
_, err := randSource.Read(buf)
return err
})
}
func RandItem[T any](list []T) T {
return list[RandInt(len(list))]
}
func RandInt(n int) int {
s := new(int)
backoff(func() error {
size, err := rand.Int(rand.Reader, big.NewInt(int64(n)))
if err != nil {
return err
}
*s = int(size.Int64())
return nil
})
return *s
}

Loading…
Cancel
Save