Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Cloak to version v2.9.0 #4

Closed
wants to merge 13 commits into from
Closed
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: '^1.17' # The Go version to download (if necessary) and use.
go-version: '^1.22' # The Go version to download (if necessary) and use.
- run: go test -race -coverprofile coverage.txt -coverpkg ./... -covermode atomic ./...
- uses: codecov/codecov-action@v1
with:
Expand Down
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
24 changes: 19 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
module github.com/cbeuw/Cloak

go 1.14
go 1.21

toolchain go1.22.2

require (
github.com/cbeuw/connutil v0.0.0-20200411215123-966bfaa51ee3
github.com/gorilla/mux v1.8.1
github.com/gorilla/websocket v1.5.1
github.com/juju/ratelimit v1.0.2
github.com/refraction-networking/utls v1.6.2
github.com/refraction-networking/utls v1.6.4
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.8.1
go.etcd.io/bbolt v1.3.8
golang.org/x/crypto v0.19.0
github.com/stretchr/testify v1.9.0
go.etcd.io/bbolt v1.3.9
golang.org/x/crypto v0.22.0
)

require (
github.com/andybalholm/brotli v1.0.6 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/klauspost/compress v1.17.4 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.19.0 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
400 changes: 20 additions & 380 deletions go.sum

Large diffs are not rendered by default.

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
}
1 change: 1 addition & 0 deletions internal/multiplex/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ func (sesh *Session) recvDataFromRemote(data []byte) error {
}

func (sesh *Session) SetTerminalMsg(msg string) {
log.Debug("terminal message set to " + msg)
sesh.terminalMsgSetter.Do(func() {
sesh.terminalMsg = msg
})
Expand Down
52 changes: 21 additions & 31 deletions internal/multiplex/switchboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ package multiplex

import (
"errors"
"math/rand"
"github.com/cbeuw/Cloak/internal/common"
log "github.com/sirupsen/logrus"
"math/rand/v2"
"net"
"sync"
"sync/atomic"
"time"

log "github.com/sirupsen/logrus"
)

type switchboardStrategy int
Expand Down Expand Up @@ -39,19 +38,14 @@ type switchboard struct {
}

func makeSwitchboard(sesh *Session) *switchboard {
var strategy switchboardStrategy
if sesh.Unordered {
log.Debug("Connection is unordered")
strategy = uniformSpread
} else {
strategy = fixedConnMapping
}
sb := &switchboard{
session: sesh,
strategy: strategy,
strategy: uniformSpread,
valve: sesh.Valve,
randPool: sync.Pool{New: func() interface{} {
return rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
var state [32]byte
common.CryptoRandRead(state[:])
return rand.New(rand.NewChaCha8(state))
}},
}
return sb
Expand All @@ -60,8 +54,8 @@ func makeSwitchboard(sesh *Session) *switchboard {
var errBrokenSwitchboard = errors.New("the switchboard is broken")

func (sb *switchboard) addConn(conn net.Conn) {
atomic.AddUint32(&sb.connsCount, 1)
sb.conns.Store(conn, conn)
connId := atomic.AddUint32(&sb.connsCount, 1) - 1
sb.conns.Store(connId, conn)
go sb.deplex(conn)
}

Expand All @@ -86,6 +80,9 @@ func (sb *switchboard) send(data []byte, assignedConn *net.Conn) (n int, err err
return n, err
}
case fixedConnMapping:
// FIXME: this strategy has a tendency to cause a TLS conn socket buffer to fill up,
// which is a problem when multiple streams are mapped to the same conn, resulting
// in all such streams being blocked.
conn = *assignedConn
if conn == nil {
conn, err = sb.pickRandConn()
Expand All @@ -110,7 +107,7 @@ func (sb *switchboard) send(data []byte, assignedConn *net.Conn) (n int, err err
return n, nil
}

// returns a random connId
// returns a random conn. This function can be called concurrently.
func (sb *switchboard) pickRandConn() (net.Conn, error) {
if atomic.LoadUint32(&sb.broken) == 1 {
return nil, errBrokenSwitchboard
Expand All @@ -122,33 +119,26 @@ func (sb *switchboard) pickRandConn() (net.Conn, error) {
}

randReader := sb.randPool.Get().(*rand.Rand)

r := randReader.Intn(int(connsCount))
connId := randReader.Uint32N(connsCount)
sb.randPool.Put(randReader)

var c int
var ret net.Conn
sb.conns.Range(func(_, conn interface{}) bool {
if r == c {
ret = conn.(net.Conn)
return false
}
c++
return true
})

return ret, nil
ret, ok := sb.conns.Load(connId)
if !ok {
log.Errorf("failed to get conn %d", connId)
return nil, errBrokenSwitchboard
}
return ret.(net.Conn), nil
}

// actively triggered by session.Close()
func (sb *switchboard) closeAll() {
if !atomic.CompareAndSwapUint32(&sb.broken, 0, 1) {
return
}
atomic.StoreUint32(&sb.connsCount, 0)
sb.conns.Range(func(_, conn interface{}) bool {
conn.(net.Conn).Close()
sb.conns.Delete(conn)
atomic.AddUint32(&sb.connsCount, ^uint32(0))
return true
})
}
Expand Down
4 changes: 1 addition & 3 deletions internal/server/TLS.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"io"
"math/rand"
"net"

"github.com/cbeuw/Cloak/internal/common"
Expand Down Expand Up @@ -46,8 +45,7 @@ func (TLS) makeResponder(clientHelloSessionId []byte, sharedSecret [32]byte) Res
// the cert length needs to be the same for all handshakes belonging to the same session
// we can use sessionKey as a seed here to ensure consistency
possibleCertLengths := []int{42, 27, 68, 59, 36, 44, 46}
rand.Seed(int64(sessionKey[0]))
cert := make([]byte, possibleCertLengths[rand.Intn(len(possibleCertLengths))])
cert := make([]byte, possibleCertLengths[common.RandInt(len(possibleCertLengths))])
common.RandRead(randSource, cert)

var nonce [12]byte
Expand Down
6 changes: 4 additions & 2 deletions release.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/usr/bin/env bash

set -eu

go install github.com/mitchellh/gox@latest

mkdir -p release
Expand All @@ -18,7 +20,7 @@ echo "Compiling:"

os="windows linux darwin"
arch="amd64 386 arm arm64 mips mips64 mipsle mips64le"
pushd cmd/ck-client || exit 1
pushd cmd/ck-client
CGO_ENABLED=0 gox -ldflags "-X main.version=${v}" -os="$os" -arch="$arch" -osarch="$osarch" -output="$output"
CGO_ENABLED=0 GOOS="linux" GOARCH="mips" GOMIPS="softfloat" go build -ldflags "-X main.version=${v}" -o ck-client-linux-mips_softfloat-"${v}"
CGO_ENABLED=0 GOOS="linux" GOARCH="mipsle" GOMIPS="softfloat" go build -ldflags "-X main.version=${v}" -o ck-client-linux-mipsle_softfloat-"${v}"
Expand All @@ -27,7 +29,7 @@ popd

os="linux"
arch="amd64 386 arm arm64"
pushd cmd/ck-server || exit 1
pushd cmd/ck-server
CGO_ENABLED=0 gox -ldflags "-X main.version=${v}" -os="$os" -arch="$arch" -osarch="$osarch" -output="$output"
mv ck-server-* ../../release
popd
Expand Down
Loading