diff --git a/README.md b/README.md index ebf70db..7ad0a2e 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,8 @@ 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. +`AlternativeNames` is an array used alongside `ServerName` to shuffle between different ServerNames for every new connection + `CDNOriginHost` is the domain name of the _origin_ server (i.e. the server running Cloak) under `CDN` mode. This only has effect when `Transport` is set to `CDN`. If unset, it will default to the remote hostname supplied via the commandline argument (in standalone mode), or by Shadowsocks (in plugin mode). After a TLS session is established with diff --git a/cmd/ck-client/ck-client.go b/cmd/ck-client/ck-client.go index 2c19cd3..01c5cba 100644 --- a/cmd/ck-client/ck-client.go +++ b/cmd/ck-client/ck-client.go @@ -8,6 +8,7 @@ import ( "flag" "fmt" "github.com/cbeuw/Cloak/internal/common" + "math/rand" "net" "os" @@ -175,6 +176,8 @@ func main() { authInfo := authInfo // copy the struct because we are overwriting SessionId // sessionID is usergenerated. There shouldn't be a security concern because the scope of // sessionID is limited to its UID. + + authInfo.MockDomain = authInfo.MockDomainList[rand.Intn(len(authInfo.MockDomainList))] quad := make([]byte, 4) common.RandRead(authInfo.WorldState.Rand, quad) authInfo.SessionId = binary.BigEndian.Uint32(quad) diff --git a/internal/client/state.go b/internal/client/state.go index 0ee914c..ff84a2e 100644 --- a/internal/client/state.go +++ b/internal/client/state.go @@ -26,11 +26,11 @@ type RawConfig struct { UID []byte PublicKey []byte NumConn int - LocalHost string // jsonOptional - LocalPort string // jsonOptional - RemoteHost string // jsonOptional - RemotePort string // jsonOptional - + LocalHost string // jsonOptional + LocalPort string // jsonOptional + RemoteHost string // jsonOptional + RemotePort string // jsonOptional + AlternativeNames []string // jsonOptional // defaults set in ProcessRawConfig UDP bool // nullable BrowserSig string // nullable @@ -61,6 +61,7 @@ type AuthInfo struct { Unordered bool ServerPubKey crypto.PublicKey MockDomain string + MockDomainList []string WorldState common.WorldState } @@ -94,6 +95,20 @@ func ssvToJson(ssv string) (ret []byte) { } key := sp[0] value := sp[1] + if strings.HasPrefix(key, "AlternativeNames") { + switch strings.Contains(value, ",") { + case true: + domains := strings.Split(value, ",") + for index, domain := range domains { + domains[index] = `"` + domain + `"` + } + value = strings.Join(domains, ",") + ret = append(ret, []byte(`"`+key+`":[`+value+`],`)...) + case false: + ret = append(ret, []byte(`"`+key+`":["`+value+`"],`)...) + } + continue + } // JSON doesn't like quotation marks around int and bool // This is extremely ugly but it's still better than writing a tokeniser if elem(key, unquoted) { @@ -139,6 +154,8 @@ func (raw *RawConfig) ProcessRawConfig(worldState common.WorldState) (local Loca return nullErr("ServerName") } auth.MockDomain = raw.ServerName + auth.MockDomainList = raw.AlternativeNames + auth.MockDomainList = append(auth.MockDomainList, auth.MockDomain) if raw.ProxyMethod == "" { return nullErr("ServerName") }