From ae14e289991cf72fe6da6a90b9d1269896a9ebd3 Mon Sep 17 00:00:00 2001 From: Selim <47898885+lima0@users.noreply.github.com> Date: Thu, 25 Mar 2021 22:06:16 +0200 Subject: [PATCH] ServerName rotation (#158) * inital servername rotation * Move MockDomainList to LocalConnConfig as the list doesn't need to be sent to the remote * Use CSPRNG to pick the next candidate of MockDomains Co-authored-by: Andy Wang --- README.md | 2 ++ cmd/ck-client/ck-client.go | 5 +++++ cmd/ck-client/protector_android.go | 1 + internal/client/state.go | 31 +++++++++++++++++++++++------- 4 files changed, 32 insertions(+), 7 deletions(-) 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..38bf95d 100644 --- a/cmd/ck-client/ck-client.go +++ b/cmd/ck-client/ck-client.go @@ -173,6 +173,11 @@ func main() { log.Infof("Listening on %v %v for %v client", network, localConfig.LocalAddr, authInfo.ProxyMethod) seshMaker = func() *mux.Session { authInfo := authInfo // copy the struct because we are overwriting SessionId + + randByte := make([]byte, 1) + common.RandRead(authInfo.WorldState.Rand, randByte) + authInfo.MockDomain = localConfig.MockDomainList[int(randByte[0])%len(localConfig.MockDomainList)] + // sessionID is usergenerated. There shouldn't be a security concern because the scope of // sessionID is limited to its UID. quad := make([]byte, 4) diff --git a/cmd/ck-client/protector_android.go b/cmd/ck-client/protector_android.go index 639b98c..fbaea7b 100644 --- a/cmd/ck-client/protector_android.go +++ b/cmd/ck-client/protector_android.go @@ -1,4 +1,5 @@ // +build android + package main // Stolen from https://github.com/shadowsocks/overture/blob/shadowsocks/core/utils/utils_android.go diff --git a/internal/client/state.go b/internal/client/state.go index 0ee914c..0ab8270 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 @@ -49,8 +49,9 @@ type RemoteConnConfig struct { } type LocalConnConfig struct { - LocalAddr string - Timeout time.Duration + LocalAddr string + Timeout time.Duration + MockDomainList []string } type AuthInfo struct { @@ -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 + local.MockDomainList = raw.AlternativeNames + local.MockDomainList = append(local.MockDomainList, auth.MockDomain) if raw.ProxyMethod == "" { return nullErr("ServerName") }