Skip to content

Commit

Permalink
Merge pull request #242 from notsure2/random-sni
Browse files Browse the repository at this point in the history
Support ServerName randomization (by setting ServerName=random) using ProtonVPN algo
  • Loading branch information
cbeuw committed Apr 13, 2024
2 parents a848d2f + d5da5d0 commit de4dab6
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 8 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
42 changes: 40 additions & 2 deletions internal/client/TLS.go
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
35 changes: 30 additions & 5 deletions internal/common/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"crypto/rand"
"errors"
"io"
"math/big"
"time"

log "github.com/sirupsen/logrus"
Expand Down Expand Up @@ -52,21 +53,45 @@ 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
}
waitDur := [10]time.Duration{5 * time.Millisecond, 10 * time.Millisecond, 30 * time.Millisecond, 50 * time.Millisecond,
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
}

0 comments on commit de4dab6

Please sign in to comment.