mirror of https://github.com/cbeuw/Cloak
Separate Client out into its own library package
This commit is contained in:
parent
fe78c7b713
commit
2aa49ce543
|
|
@ -8,12 +8,12 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/cbeuw/Cloak/internal/cli_client"
|
||||||
|
"github.com/cbeuw/Cloak/internal/common"
|
||||||
|
"github.com/cbeuw/Cloak/libcloak/client"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/cbeuw/Cloak/internal/common"
|
|
||||||
|
|
||||||
"github.com/cbeuw/Cloak/internal/client"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -83,7 +83,7 @@ func main() {
|
||||||
}
|
}
|
||||||
log.SetLevel(lvl)
|
log.SetLevel(lvl)
|
||||||
|
|
||||||
rawConfig, err := client.ParseConfig(config)
|
rawConfig, err := cli_client.ParseConfig(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -138,7 +138,7 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
localConfig, remoteConfig, authInfo, err := rawConfig.ProcessRawConfig(common.RealWorldState)
|
localConfig, remoteConfig, authInfo, err := rawConfig.ProcessCLIConfig(common.RealWorldState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -194,12 +194,12 @@ func main() {
|
||||||
return net.ListenUDP("udp", udpAddr)
|
return net.ListenUDP("udp", udpAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
client.RouteUDP(acceptor, localConfig.Timeout, remoteConfig.Singleplex, seshMaker)
|
cli_client.RouteUDP(acceptor, localConfig.Timeout, remoteConfig.Singleplex, seshMaker)
|
||||||
} else {
|
} else {
|
||||||
listener, err := net.Listen("tcp", localConfig.LocalAddr)
|
listener, err := net.Listen("tcp", localConfig.LocalAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
client.RouteTCP(listener, localConfig.Timeout, remoteConfig.Singleplex, seshMaker)
|
cli_client.RouteTCP(listener, localConfig.Timeout, remoteConfig.Singleplex, seshMaker)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
package cli_client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/cbeuw/Cloak/internal/common"
|
||||||
|
"github.com/cbeuw/Cloak/libcloak/client"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CLIConfig struct {
|
||||||
|
client.RawConfig
|
||||||
|
|
||||||
|
// LocalHost is the hostname or IP address to listen for incoming proxy client connections
|
||||||
|
LocalHost string // jsonOptional
|
||||||
|
// LocalPort is the port to listen for incomig proxy client connections
|
||||||
|
LocalPort string // jsonOptional
|
||||||
|
// AlternativeNames is a list of ServerName Cloak may randomly pick from for different sessions
|
||||||
|
AlternativeNames []string
|
||||||
|
// StreamTimeout is the duration, in seconds, for an incoming connection to be automatically closed after the last
|
||||||
|
// piece of incoming data .
|
||||||
|
// Defaults to 300
|
||||||
|
StreamTimeout int
|
||||||
|
}
|
||||||
|
|
||||||
|
// semi-colon separated value. This is for Android plugin options
|
||||||
|
func ssvToJson(ssv string) (ret []byte) {
|
||||||
|
elem := func(val string, lst []string) bool {
|
||||||
|
for _, v := range lst {
|
||||||
|
if val == v {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
unescape := func(s string) string {
|
||||||
|
r := strings.Replace(s, `\\`, `\`, -1)
|
||||||
|
r = strings.Replace(r, `\=`, `=`, -1)
|
||||||
|
r = strings.Replace(r, `\;`, `;`, -1)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
unquoted := []string{"NumConn", "StreamTimeout", "KeepAlive", "UDP"}
|
||||||
|
lines := strings.Split(unescape(ssv), ";")
|
||||||
|
ret = []byte("{")
|
||||||
|
for _, ln := range lines {
|
||||||
|
if ln == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
sp := strings.SplitN(ln, "=", 2)
|
||||||
|
if len(sp) < 2 {
|
||||||
|
log.Errorf("Malformed config option: %v", ln)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
ret = append(ret, []byte(`"`+key+`":`+value+`,`)...)
|
||||||
|
} else {
|
||||||
|
ret = append(ret, []byte(`"`+key+`":"`+value+`",`)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = ret[:len(ret)-1] // remove the last comma
|
||||||
|
ret = append(ret, '}')
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseConfig(conf string) (raw *CLIConfig, err error) {
|
||||||
|
var content []byte
|
||||||
|
// Checking if it's a path to json or a ssv string
|
||||||
|
if strings.Contains(conf, ";") && strings.Contains(conf, "=") {
|
||||||
|
content = ssvToJson(conf)
|
||||||
|
} else {
|
||||||
|
content, err = ioutil.ReadFile(conf)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
raw = new(CLIConfig)
|
||||||
|
err = json.Unmarshal(content, &raw)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type LocalConnConfig struct {
|
||||||
|
LocalAddr string
|
||||||
|
Timeout time.Duration
|
||||||
|
MockDomainList []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (raw *CLIConfig) ProcessCLIConfig(worldState common.WorldState) (local LocalConnConfig, remote client.RemoteConnConfig, auth client.AuthInfo, err error) {
|
||||||
|
remote, auth, err = raw.RawConfig.ProcessRawConfig(worldState)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var filteredAlternativeNames []string
|
||||||
|
for _, alternativeName := range raw.AlternativeNames {
|
||||||
|
if len(alternativeName) > 0 {
|
||||||
|
filteredAlternativeNames = append(filteredAlternativeNames, alternativeName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
raw.AlternativeNames = filteredAlternativeNames
|
||||||
|
|
||||||
|
local.MockDomainList = raw.AlternativeNames
|
||||||
|
local.MockDomainList = append(local.MockDomainList, auth.MockDomain)
|
||||||
|
|
||||||
|
if raw.LocalHost == "" {
|
||||||
|
err = fmt.Errorf("LocalHost cannot be empty")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if raw.LocalPort == "" {
|
||||||
|
err = fmt.Errorf("LocalPort cannot be empty")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
local.LocalAddr = net.JoinHostPort(raw.LocalHost, raw.LocalPort)
|
||||||
|
// stream no write timeout
|
||||||
|
if raw.StreamTimeout == 0 {
|
||||||
|
local.Timeout = 300 * time.Second
|
||||||
|
} else {
|
||||||
|
local.Timeout = time.Duration(raw.StreamTimeout) * time.Second
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package client
|
package cli_client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
package client
|
package cli_client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/cbeuw/Cloak/internal/common"
|
||||||
|
"github.com/cbeuw/Cloak/libcloak/client"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/cbeuw/Cloak/internal/common"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RouteUDP(bindFunc func() (*net.UDPConn, error), streamTimeout time.Duration, singleplex bool, newSeshFunc func() *CloakClient) {
|
func RouteUDP(bindFunc func() (*net.UDPConn, error), streamTimeout time.Duration, singleplex bool, newSeshFunc func() *client.CloakClient) {
|
||||||
var cloakClient *CloakClient
|
var cloakClient *client.CloakClient
|
||||||
localConn, err := bindFunc()
|
localConn, err := bindFunc()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|
@ -94,8 +94,8 @@ func RouteUDP(bindFunc func() (*net.UDPConn, error), streamTimeout time.Duration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func RouteTCP(listener net.Listener, streamTimeout time.Duration, singleplex bool, newSeshFunc func() *CloakClient) {
|
func RouteTCP(listener net.Listener, streamTimeout time.Duration, singleplex bool, newSeshFunc func() *client.CloakClient) {
|
||||||
var cloakClient *CloakClient
|
var cloakClient *client.CloakClient
|
||||||
for {
|
for {
|
||||||
localConn, err := listener.Accept()
|
localConn, err := listener.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -105,7 +105,7 @@ func RouteTCP(listener net.Listener, streamTimeout time.Duration, singleplex boo
|
||||||
if !singleplex && (cloakClient == nil || cloakClient.IsClosed()) {
|
if !singleplex && (cloakClient == nil || cloakClient.IsClosed()) {
|
||||||
cloakClient = newSeshFunc()
|
cloakClient = newSeshFunc()
|
||||||
}
|
}
|
||||||
go func(sesh *CloakClient, localConn net.Conn, timeout time.Duration) {
|
go func(sesh *client.CloakClient, localConn net.Conn, timeout time.Duration) {
|
||||||
if singleplex {
|
if singleplex {
|
||||||
sesh = newSeshFunc()
|
sesh = newSeshFunc()
|
||||||
}
|
}
|
||||||
|
|
@ -5,6 +5,13 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/cbeuw/Cloak/internal/cli_client"
|
||||||
|
"github.com/cbeuw/Cloak/internal/common"
|
||||||
|
mux "github.com/cbeuw/Cloak/internal/multiplex"
|
||||||
|
"github.com/cbeuw/Cloak/internal/server"
|
||||||
|
"github.com/cbeuw/Cloak/libcloak/client"
|
||||||
|
"github.com/cbeuw/connutil"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
|
|
@ -12,13 +19,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/cbeuw/Cloak/internal/client"
|
|
||||||
"github.com/cbeuw/Cloak/internal/common"
|
|
||||||
mux "github.com/cbeuw/Cloak/internal/multiplex"
|
|
||||||
"github.com/cbeuw/Cloak/internal/server"
|
|
||||||
"github.com/cbeuw/connutil"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -76,6 +76,8 @@ func serveUDPEcho(listener *connutil.PipeListener) {
|
||||||
var bypassUID = [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
var bypassUID = [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||||
var publicKey, _ = base64.StdEncoding.DecodeString("7f7TuKrs264VNSgMno8PkDlyhGhVuOSR8JHLE6H4Ljc=")
|
var publicKey, _ = base64.StdEncoding.DecodeString("7f7TuKrs264VNSgMno8PkDlyhGhVuOSR8JHLE6H4Ljc=")
|
||||||
var privateKey, _ = base64.StdEncoding.DecodeString("SMWeC6VuZF8S/id65VuFQFlfa7hTEJBpL6wWhqPP100=")
|
var privateKey, _ = base64.StdEncoding.DecodeString("SMWeC6VuZF8S/id65VuFQFlfa7hTEJBpL6wWhqPP100=")
|
||||||
|
var four = 4
|
||||||
|
var zero = 0
|
||||||
|
|
||||||
var basicUDPConfig = client.RawConfig{
|
var basicUDPConfig = client.RawConfig{
|
||||||
ServerName: "www.example.com",
|
ServerName: "www.example.com",
|
||||||
|
|
@ -83,13 +85,11 @@ var basicUDPConfig = client.RawConfig{
|
||||||
EncryptionMethod: "plain",
|
EncryptionMethod: "plain",
|
||||||
UID: bypassUID[:],
|
UID: bypassUID[:],
|
||||||
PublicKey: publicKey,
|
PublicKey: publicKey,
|
||||||
NumConn: 4,
|
NumConn: &four,
|
||||||
UDP: true,
|
UDP: true,
|
||||||
Transport: "direct",
|
Transport: "direct",
|
||||||
RemoteHost: "fake.com",
|
RemoteHost: "fake.com",
|
||||||
RemotePort: "9999",
|
RemotePort: "9999",
|
||||||
LocalHost: "127.0.0.1",
|
|
||||||
LocalPort: "9999",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var basicTCPConfig = client.RawConfig{
|
var basicTCPConfig = client.RawConfig{
|
||||||
|
|
@ -98,13 +98,11 @@ var basicTCPConfig = client.RawConfig{
|
||||||
EncryptionMethod: "plain",
|
EncryptionMethod: "plain",
|
||||||
UID: bypassUID[:],
|
UID: bypassUID[:],
|
||||||
PublicKey: publicKey,
|
PublicKey: publicKey,
|
||||||
NumConn: 4,
|
NumConn: &four,
|
||||||
UDP: false,
|
UDP: false,
|
||||||
Transport: "direct",
|
Transport: "direct",
|
||||||
RemoteHost: "fake.com",
|
RemoteHost: "fake.com",
|
||||||
RemotePort: "9999",
|
RemotePort: "9999",
|
||||||
LocalHost: "127.0.0.1",
|
|
||||||
LocalPort: "9999",
|
|
||||||
BrowserSig: "firefox",
|
BrowserSig: "firefox",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,22 +112,20 @@ var singleplexTCPConfig = client.RawConfig{
|
||||||
EncryptionMethod: "plain",
|
EncryptionMethod: "plain",
|
||||||
UID: bypassUID[:],
|
UID: bypassUID[:],
|
||||||
PublicKey: publicKey,
|
PublicKey: publicKey,
|
||||||
NumConn: 0,
|
NumConn: &zero,
|
||||||
UDP: false,
|
UDP: false,
|
||||||
Transport: "direct",
|
Transport: "direct",
|
||||||
RemoteHost: "fake.com",
|
RemoteHost: "fake.com",
|
||||||
RemotePort: "9999",
|
RemotePort: "9999",
|
||||||
LocalHost: "127.0.0.1",
|
BrowserSig: "chrome",
|
||||||
LocalPort: "9999",
|
|
||||||
BrowserSig: "safari",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateClientConfigs(rawConfig client.RawConfig, state common.WorldState) (client.LocalConnConfig, client.RemoteConnConfig, client.AuthInfo) {
|
func generateClientConfigs(rawConfig client.RawConfig, state common.WorldState) (client.RemoteConnConfig, client.AuthInfo) {
|
||||||
lcl, rmt, auth, err := rawConfig.ProcessRawConfig(state)
|
rmt, auth, err := rawConfig.ProcessRawConfig(state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
return lcl, rmt, auth
|
return rmt, auth
|
||||||
}
|
}
|
||||||
|
|
||||||
func basicServerState(ws common.WorldState) *server.State {
|
func basicServerState(ws common.WorldState) *server.State {
|
||||||
|
|
@ -161,7 +157,7 @@ func (m *mockUDPDialer) Dial(network, address string) (net.Conn, error) {
|
||||||
return net.DialUDP("udp", nil, m.raddr)
|
return net.DialUDP("udp", nil, m.raddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func establishSession(lcc client.LocalConnConfig, rcc client.RemoteConnConfig, ai client.AuthInfo, serverState *server.State) (common.Dialer, *connutil.PipeListener, common.Dialer, net.Listener, error) {
|
func establishSession(rcc client.RemoteConnConfig, ai client.AuthInfo, serverState *server.State) (common.Dialer, *connutil.PipeListener, common.Dialer, net.Listener, error) {
|
||||||
// redirecting web server
|
// redirecting web server
|
||||||
// ^
|
// ^
|
||||||
// |
|
// |
|
||||||
|
|
@ -202,12 +198,12 @@ func establishSession(lcc client.LocalConnConfig, rcc client.RemoteConnConfig, a
|
||||||
addrCh <- conn.LocalAddr().(*net.UDPAddr)
|
addrCh <- conn.LocalAddr().(*net.UDPAddr)
|
||||||
return conn, err
|
return conn, err
|
||||||
}
|
}
|
||||||
go client.RouteUDP(acceptor, lcc.Timeout, rcc.Singleplex, clientSeshMaker)
|
go cli_client.RouteUDP(acceptor, 300*time.Second, rcc.Singleplex, clientSeshMaker)
|
||||||
proxyToCkClientD = mDialer
|
proxyToCkClientD = mDialer
|
||||||
} else {
|
} else {
|
||||||
var proxyToCkClientL *connutil.PipeListener
|
var proxyToCkClientL *connutil.PipeListener
|
||||||
proxyToCkClientD, proxyToCkClientL = connutil.DialerListener(10 * 1024)
|
proxyToCkClientD, proxyToCkClientL = connutil.DialerListener(10 * 1024)
|
||||||
go client.RouteTCP(proxyToCkClientL, lcc.Timeout, rcc.Singleplex, clientSeshMaker)
|
go cli_client.RouteTCP(proxyToCkClientL, 300*time.Second, rcc.Singleplex, clientSeshMaker)
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up server
|
// set up server
|
||||||
|
|
@ -259,11 +255,11 @@ func TestUDP(t *testing.T) {
|
||||||
log.SetLevel(log.ErrorLevel)
|
log.SetLevel(log.ErrorLevel)
|
||||||
|
|
||||||
worldState := common.WorldOfTime(time.Unix(10, 0))
|
worldState := common.WorldOfTime(time.Unix(10, 0))
|
||||||
lcc, rcc, ai := generateClientConfigs(basicUDPConfig, worldState)
|
rcc, ai := generateClientConfigs(basicUDPConfig, worldState)
|
||||||
sta := basicServerState(worldState)
|
sta := basicServerState(worldState)
|
||||||
|
|
||||||
t.Run("simple send", func(t *testing.T) {
|
t.Run("simple send", func(t *testing.T) {
|
||||||
proxyToCkClientD, proxyFromCkServerL, _, _, err := establishSession(lcc, rcc, ai, sta)
|
proxyToCkClientD, proxyFromCkServerL, _, _, err := establishSession(rcc, ai, sta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -300,7 +296,7 @@ func TestUDP(t *testing.T) {
|
||||||
|
|
||||||
const echoMsgLen = 1024
|
const echoMsgLen = 1024
|
||||||
t.Run("user echo", func(t *testing.T) {
|
t.Run("user echo", func(t *testing.T) {
|
||||||
proxyToCkClientD, proxyFromCkServerL, _, _, err := establishSession(lcc, rcc, ai, sta)
|
proxyToCkClientD, proxyFromCkServerL, _, _, err := establishSession(rcc, ai, sta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -320,9 +316,9 @@ func TestUDP(t *testing.T) {
|
||||||
func TestTCPSingleplex(t *testing.T) {
|
func TestTCPSingleplex(t *testing.T) {
|
||||||
log.SetLevel(log.ErrorLevel)
|
log.SetLevel(log.ErrorLevel)
|
||||||
worldState := common.WorldOfTime(time.Unix(10, 0))
|
worldState := common.WorldOfTime(time.Unix(10, 0))
|
||||||
lcc, rcc, ai := generateClientConfigs(singleplexTCPConfig, worldState)
|
rcc, ai := generateClientConfigs(singleplexTCPConfig, worldState)
|
||||||
sta := basicServerState(worldState)
|
sta := basicServerState(worldState)
|
||||||
proxyToCkClientD, proxyFromCkServerL, _, _, err := establishSession(lcc, rcc, ai, sta)
|
proxyToCkClientD, proxyFromCkServerL, _, _, err := establishSession(rcc, ai, sta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -381,7 +377,7 @@ func TestTCPMultiplex(t *testing.T) {
|
||||||
log.SetLevel(log.ErrorLevel)
|
log.SetLevel(log.ErrorLevel)
|
||||||
worldState := common.WorldOfTime(time.Unix(10, 0))
|
worldState := common.WorldOfTime(time.Unix(10, 0))
|
||||||
|
|
||||||
lcc, rcc, ai := generateClientConfigs(basicTCPConfig, worldState)
|
rcc, ai := generateClientConfigs(basicTCPConfig, worldState)
|
||||||
sta := basicServerState(worldState)
|
sta := basicServerState(worldState)
|
||||||
|
|
||||||
t.Run("user echo single", func(t *testing.T) {
|
t.Run("user echo single", func(t *testing.T) {
|
||||||
|
|
@ -390,7 +386,7 @@ func TestTCPMultiplex(t *testing.T) {
|
||||||
writeData := make([]byte, dataLen)
|
writeData := make([]byte, dataLen)
|
||||||
rand.Read(writeData)
|
rand.Read(writeData)
|
||||||
t.Run(fmt.Sprintf("data length %v", dataLen), func(t *testing.T) {
|
t.Run(fmt.Sprintf("data length %v", dataLen), func(t *testing.T) {
|
||||||
proxyToCkClientD, proxyFromCkServerL, _, _, err := establishSession(lcc, rcc, ai, sta)
|
proxyToCkClientD, proxyFromCkServerL, _, _, err := establishSession(rcc, ai, sta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -423,7 +419,7 @@ func TestTCPMultiplex(t *testing.T) {
|
||||||
|
|
||||||
const echoMsgLen = 16384
|
const echoMsgLen = 16384
|
||||||
t.Run("user echo", func(t *testing.T) {
|
t.Run("user echo", func(t *testing.T) {
|
||||||
proxyToCkClientD, proxyFromCkServerL, _, _, err := establishSession(lcc, rcc, ai, sta)
|
proxyToCkClientD, proxyFromCkServerL, _, _, err := establishSession(rcc, ai, sta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -441,7 +437,7 @@ func TestTCPMultiplex(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("redir echo", func(t *testing.T) {
|
t.Run("redir echo", func(t *testing.T) {
|
||||||
_, _, netToCkServerD, redirFromCkServerL, err := establishSession(lcc, rcc, ai, sta)
|
_, _, netToCkServerD, redirFromCkServerL, err := establishSession(rcc, ai, sta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -466,9 +462,9 @@ func TestClosingStreamsFromProxy(t *testing.T) {
|
||||||
clientConfig := clientConfig
|
clientConfig := clientConfig
|
||||||
clientConfigName := clientConfigName
|
clientConfigName := clientConfigName
|
||||||
t.Run(clientConfigName, func(t *testing.T) {
|
t.Run(clientConfigName, func(t *testing.T) {
|
||||||
lcc, rcc, ai := generateClientConfigs(clientConfig, worldState)
|
rcc, ai := generateClientConfigs(clientConfig, worldState)
|
||||||
sta := basicServerState(worldState)
|
sta := basicServerState(worldState)
|
||||||
proxyToCkClientD, proxyFromCkServerL, _, _, err := establishSession(lcc, rcc, ai, sta)
|
proxyToCkClientD, proxyFromCkServerL, _, _, err := establishSession(rcc, ai, sta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -528,7 +524,7 @@ func TestClosingStreamsFromProxy(t *testing.T) {
|
||||||
func BenchmarkIntegration(b *testing.B) {
|
func BenchmarkIntegration(b *testing.B) {
|
||||||
log.SetLevel(log.ErrorLevel)
|
log.SetLevel(log.ErrorLevel)
|
||||||
worldState := common.WorldOfTime(time.Unix(10, 0))
|
worldState := common.WorldOfTime(time.Unix(10, 0))
|
||||||
lcc, rcc, ai := generateClientConfigs(basicTCPConfig, worldState)
|
rcc, ai := generateClientConfigs(basicTCPConfig, worldState)
|
||||||
sta := basicServerState(worldState)
|
sta := basicServerState(worldState)
|
||||||
const bufSize = 16 * 1024
|
const bufSize = 16 * 1024
|
||||||
|
|
||||||
|
|
@ -542,7 +538,7 @@ func BenchmarkIntegration(b *testing.B) {
|
||||||
for name, method := range encryptionMethods {
|
for name, method := range encryptionMethods {
|
||||||
b.Run(name, func(b *testing.B) {
|
b.Run(name, func(b *testing.B) {
|
||||||
ai.EncryptionMethod = method
|
ai.EncryptionMethod = method
|
||||||
proxyToCkClientD, proxyFromCkServerL, _, _, err := establishSession(lcc, rcc, ai, sta)
|
proxyToCkClientD, proxyFromCkServerL, _, _, err := establishSession(rcc, ai, sta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@ import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/cbeuw/Cloak/internal/common"
|
||||||
|
"github.com/cbeuw/Cloak/libcloak/client/browsers"
|
||||||
|
"github.com/cbeuw/Cloak/libcloak/client/transports"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -65,22 +68,12 @@ type RawConfig struct {
|
||||||
// is set to `cdn`
|
// is set to `cdn`
|
||||||
// Defaults to RemoteHost
|
// Defaults to RemoteHost
|
||||||
CDNOriginHost string
|
CDNOriginHost string
|
||||||
// StreamTimeout is the duration, in seconds, for a stream to be automatically closed after the last write.
|
|
||||||
// Defaults to 300
|
|
||||||
StreamTimeout int
|
|
||||||
// KeepAlive is the interval between TCP KeepAlive packets to be sent over the underlying TLS connections
|
// KeepAlive is the interval between TCP KeepAlive packets to be sent over the underlying TLS connections
|
||||||
// Defaults to -1, which means no TCP KeepAlive is ever sent
|
// Defaults to -1, which means no TCP KeepAlive is ever sent
|
||||||
KeepAlive int
|
KeepAlive int
|
||||||
// RemotePort is the port Cloak server is listening to
|
// RemotePort is the port Cloak server is listening to
|
||||||
// Defaults to 443
|
// Defaults to 443
|
||||||
RemotePort string
|
RemotePort string
|
||||||
|
|
||||||
// LocalHost is the hostname or IP address to listen for incoming proxy client connections
|
|
||||||
LocalHost string // jsonOptional
|
|
||||||
// LocalPort is the port to listen for incomig proxy client connections
|
|
||||||
LocalPort string // jsonOptional
|
|
||||||
// AlternativeNames is a list of ServerName Cloak may randomly pick from for different sessions
|
|
||||||
AlternativeNames []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type RemoteConnConfig struct {
|
type RemoteConnConfig struct {
|
||||||
|
|
@ -91,93 +84,10 @@ type RemoteConnConfig struct {
|
||||||
TransportMaker func() transports.Transport
|
TransportMaker func() transports.Transport
|
||||||
}
|
}
|
||||||
|
|
||||||
type LocalConnConfig struct {
|
|
||||||
LocalAddr string
|
|
||||||
Timeout time.Duration
|
|
||||||
MockDomainList []string
|
|
||||||
}
|
|
||||||
|
|
||||||
type AuthInfo = transports.AuthInfo
|
type AuthInfo = transports.AuthInfo
|
||||||
|
|
||||||
// semi-colon separated value. This is for Android plugin options
|
func (raw *RawConfig) ProcessRawConfig(worldState common.WorldState) (remote RemoteConnConfig, auth AuthInfo, err error) {
|
||||||
func ssvToJson(ssv string) (ret []byte) {
|
nullErr := func(field string) (remote RemoteConnConfig, auth AuthInfo, err error) {
|
||||||
elem := func(val string, lst []string) bool {
|
|
||||||
for _, v := range lst {
|
|
||||||
if val == v {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
unescape := func(s string) string {
|
|
||||||
r := strings.Replace(s, `\\`, `\`, -1)
|
|
||||||
r = strings.Replace(r, `\=`, `=`, -1)
|
|
||||||
r = strings.Replace(r, `\;`, `;`, -1)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
unquoted := []string{"NumConn", "StreamTimeout", "KeepAlive", "UDP"}
|
|
||||||
lines := strings.Split(unescape(ssv), ";")
|
|
||||||
ret = []byte("{")
|
|
||||||
for _, ln := range lines {
|
|
||||||
if ln == "" {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
sp := strings.SplitN(ln, "=", 2)
|
|
||||||
if len(sp) < 2 {
|
|
||||||
log.Errorf("Malformed config option: %v", ln)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
ret = append(ret, []byte(`"`+key+`":`+value+`,`)...)
|
|
||||||
} else {
|
|
||||||
ret = append(ret, []byte(`"`+key+`":"`+value+`",`)...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret = ret[:len(ret)-1] // remove the last comma
|
|
||||||
ret = append(ret, '}')
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseConfig(conf string) (raw *RawConfig, err error) {
|
|
||||||
var content []byte
|
|
||||||
// Checking if it's a path to json or a ssv string
|
|
||||||
if strings.Contains(conf, ";") && strings.Contains(conf, "=") {
|
|
||||||
content = ssvToJson(conf)
|
|
||||||
} else {
|
|
||||||
content, err = ioutil.ReadFile(conf)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
raw = new(RawConfig)
|
|
||||||
err = json.Unmarshal(content, &raw)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (raw *RawConfig) ProcessRawConfig(worldState common.WorldState) (local LocalConnConfig, remote RemoteConnConfig, auth AuthInfo, err error) {
|
|
||||||
nullErr := func(field string) (local LocalConnConfig, remote RemoteConnConfig, auth AuthInfo, err error) {
|
|
||||||
err = fmt.Errorf("%v cannot be empty", field)
|
err = fmt.Errorf("%v cannot be empty", field)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -189,18 +99,8 @@ func (raw *RawConfig) ProcessRawConfig(worldState common.WorldState) (local Loca
|
||||||
}
|
}
|
||||||
auth.MockDomain = raw.ServerName
|
auth.MockDomain = raw.ServerName
|
||||||
|
|
||||||
var filteredAlternativeNames []string
|
|
||||||
for _, alternativeName := range raw.AlternativeNames {
|
|
||||||
if len(alternativeName) > 0 {
|
|
||||||
filteredAlternativeNames = append(filteredAlternativeNames, alternativeName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
raw.AlternativeNames = filteredAlternativeNames
|
|
||||||
|
|
||||||
local.MockDomainList = raw.AlternativeNames
|
|
||||||
local.MockDomainList = append(local.MockDomainList, auth.MockDomain)
|
|
||||||
if raw.ProxyMethod == "" {
|
if raw.ProxyMethod == "" {
|
||||||
return nullErr("ServerName")
|
return nullErr("ProxyMethod")
|
||||||
}
|
}
|
||||||
auth.ProxyMethod = raw.ProxyMethod
|
auth.ProxyMethod = raw.ProxyMethod
|
||||||
if len(raw.UID) == 0 {
|
if len(raw.UID) == 0 {
|
||||||
|
|
@ -302,19 +202,5 @@ func (raw *RawConfig) ProcessRawConfig(worldState common.WorldState) (local Loca
|
||||||
remote.KeepAlive = remote.KeepAlive * time.Second
|
remote.KeepAlive = remote.KeepAlive * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
if raw.LocalHost == "" {
|
|
||||||
return nullErr("LocalHost")
|
|
||||||
}
|
|
||||||
if raw.LocalPort == "" {
|
|
||||||
return nullErr("LocalPort")
|
|
||||||
}
|
|
||||||
local.LocalAddr = net.JoinHostPort(raw.LocalHost, raw.LocalPort)
|
|
||||||
// stream no write timeout
|
|
||||||
if raw.StreamTimeout == 0 {
|
|
||||||
local.Timeout = 300 * time.Second
|
|
||||||
} else {
|
|
||||||
local.Timeout = time.Duration(raw.StreamTimeout) * time.Second
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package transports
|
package transports
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/cbeuw/Cloak/internal/common"
|
||||||
|
"github.com/cbeuw/Cloak/libcloak/client/browsers"
|
||||||
utls "github.com/refraction-networking/utls"
|
utls "github.com/refraction-networking/utls"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"net"
|
"net"
|
||||||
Loading…
Reference in New Issue