mirror of https://github.com/cbeuw/Cloak
Merge pull request #242 from notsure2/random-sni
Support ServerName randomization (by setting ServerName=random) using ProtonVPN algo
This commit is contained in:
commit
de4dab6bf3
|
|
@ -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).**
|
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
|
`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
|
`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
|
connection. **This may conflict with `CDN` Transport mode** if the CDN provider prohibits domain fronting and rejects
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/cbeuw/Cloak/internal/common"
|
||||||
utls "github.com/refraction-networking/utls"
|
utls "github.com/refraction-networking/utls"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
"github.com/cbeuw/Cloak/internal/common"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const appDataMaxLength = 16401
|
const appDataMaxLength = 16401
|
||||||
|
|
@ -30,6 +30,40 @@ type DirectTLS struct {
|
||||||
browser browser
|
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) {
|
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 don't use utls to handle connections (as it'll attempt a real TLS negotiation)
|
||||||
// We only want it to build the ClientHello locally
|
// 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,
|
serverName: authInfo.MockDomain,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.EqualFold(fields.serverName, "random") {
|
||||||
|
fields.serverName = randomServerName()
|
||||||
|
}
|
||||||
|
|
||||||
var ch []byte
|
var ch []byte
|
||||||
ch, err = buildClientHello(tls.browser, fields)
|
ch, err = buildClientHello(tls.browser, fields)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
@ -52,8 +53,8 @@ func CryptoRandRead(buf []byte) {
|
||||||
RandRead(rand.Reader, buf)
|
RandRead(rand.Reader, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RandRead(randSource io.Reader, buf []byte) {
|
func backoff(f func() error) {
|
||||||
_, err := randSource.Read(buf)
|
err := f()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -61,12 +62,36 @@ func RandRead(randSource io.Reader, buf []byte) {
|
||||||
100 * time.Millisecond, 300 * time.Millisecond, 500 * time.Millisecond, 1 * time.Second,
|
100 * time.Millisecond, 300 * time.Millisecond, 500 * time.Millisecond, 1 * time.Second,
|
||||||
3 * time.Second, 5 * time.Second}
|
3 * time.Second, 5 * time.Second}
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
log.Errorf("Failed to get random bytes: %v. Retrying...", err)
|
log.Errorf("Failed to get random: %v. Retrying...", err)
|
||||||
_, err = randSource.Read(buf)
|
err = f()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
time.Sleep(waitDur[i])
|
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…
Reference in New Issue