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"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/cbeuw/Cloak/internal/cli_client"
|
||||
"github.com/cbeuw/Cloak/internal/common"
|
||||
"github.com/cbeuw/Cloak/libcloak/client"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/cbeuw/Cloak/internal/common"
|
||||
|
||||
"github.com/cbeuw/Cloak/internal/client"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
|
@ -83,7 +83,7 @@ func main() {
|
|||
}
|
||||
log.SetLevel(lvl)
|
||||
|
||||
rawConfig, err := client.ParseConfig(config)
|
||||
rawConfig, err := cli_client.ParseConfig(config)
|
||||
if err != nil {
|
||||
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 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
@ -194,12 +194,12 @@ func main() {
|
|||
return net.ListenUDP("udp", udpAddr)
|
||||
}
|
||||
|
||||
client.RouteUDP(acceptor, localConfig.Timeout, remoteConfig.Singleplex, seshMaker)
|
||||
cli_client.RouteUDP(acceptor, localConfig.Timeout, remoteConfig.Singleplex, seshMaker)
|
||||
} else {
|
||||
listener, err := net.Listen("tcp", localConfig.LocalAddr)
|
||||
if err != nil {
|
||||
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 (
|
||||
"io/ioutil"
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
package client
|
||||
package cli_client
|
||||
|
||||
import (
|
||||
"github.com/cbeuw/Cloak/internal/common"
|
||||
"github.com/cbeuw/Cloak/libcloak/client"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cbeuw/Cloak/internal/common"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func RouteUDP(bindFunc func() (*net.UDPConn, error), streamTimeout time.Duration, singleplex bool, newSeshFunc func() *CloakClient) {
|
||||
var cloakClient *CloakClient
|
||||
func RouteUDP(bindFunc func() (*net.UDPConn, error), streamTimeout time.Duration, singleplex bool, newSeshFunc func() *client.CloakClient) {
|
||||
var cloakClient *client.CloakClient
|
||||
localConn, err := bindFunc()
|
||||
if err != nil {
|
||||
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) {
|
||||
var cloakClient *CloakClient
|
||||
func RouteTCP(listener net.Listener, streamTimeout time.Duration, singleplex bool, newSeshFunc func() *client.CloakClient) {
|
||||
var cloakClient *client.CloakClient
|
||||
for {
|
||||
localConn, err := listener.Accept()
|
||||
if err != nil {
|
||||
|
|
@ -105,7 +105,7 @@ func RouteTCP(listener net.Listener, streamTimeout time.Duration, singleplex boo
|
|||
if !singleplex && (cloakClient == nil || cloakClient.IsClosed()) {
|
||||
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 {
|
||||
sesh = newSeshFunc()
|
||||
}
|
||||
|
|
@ -5,6 +5,13 @@ import (
|
|||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"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"
|
||||
"math/rand"
|
||||
"net"
|
||||
|
|
@ -12,13 +19,6 @@ import (
|
|||
"testing"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
|
@ -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 publicKey, _ = base64.StdEncoding.DecodeString("7f7TuKrs264VNSgMno8PkDlyhGhVuOSR8JHLE6H4Ljc=")
|
||||
var privateKey, _ = base64.StdEncoding.DecodeString("SMWeC6VuZF8S/id65VuFQFlfa7hTEJBpL6wWhqPP100=")
|
||||
var four = 4
|
||||
var zero = 0
|
||||
|
||||
var basicUDPConfig = client.RawConfig{
|
||||
ServerName: "www.example.com",
|
||||
|
|
@ -83,13 +85,11 @@ var basicUDPConfig = client.RawConfig{
|
|||
EncryptionMethod: "plain",
|
||||
UID: bypassUID[:],
|
||||
PublicKey: publicKey,
|
||||
NumConn: 4,
|
||||
NumConn: &four,
|
||||
UDP: true,
|
||||
Transport: "direct",
|
||||
RemoteHost: "fake.com",
|
||||
RemotePort: "9999",
|
||||
LocalHost: "127.0.0.1",
|
||||
LocalPort: "9999",
|
||||
}
|
||||
|
||||
var basicTCPConfig = client.RawConfig{
|
||||
|
|
@ -98,13 +98,11 @@ var basicTCPConfig = client.RawConfig{
|
|||
EncryptionMethod: "plain",
|
||||
UID: bypassUID[:],
|
||||
PublicKey: publicKey,
|
||||
NumConn: 4,
|
||||
NumConn: &four,
|
||||
UDP: false,
|
||||
Transport: "direct",
|
||||
RemoteHost: "fake.com",
|
||||
RemotePort: "9999",
|
||||
LocalHost: "127.0.0.1",
|
||||
LocalPort: "9999",
|
||||
BrowserSig: "firefox",
|
||||
}
|
||||
|
||||
|
|
@ -114,22 +112,20 @@ var singleplexTCPConfig = client.RawConfig{
|
|||
EncryptionMethod: "plain",
|
||||
UID: bypassUID[:],
|
||||
PublicKey: publicKey,
|
||||
NumConn: 0,
|
||||
NumConn: &zero,
|
||||
UDP: false,
|
||||
Transport: "direct",
|
||||
RemoteHost: "fake.com",
|
||||
RemotePort: "9999",
|
||||
LocalHost: "127.0.0.1",
|
||||
LocalPort: "9999",
|
||||
BrowserSig: "safari",
|
||||
BrowserSig: "chrome",
|
||||
}
|
||||
|
||||
func generateClientConfigs(rawConfig client.RawConfig, state common.WorldState) (client.LocalConnConfig, client.RemoteConnConfig, client.AuthInfo) {
|
||||
lcl, rmt, auth, err := rawConfig.ProcessRawConfig(state)
|
||||
func generateClientConfigs(rawConfig client.RawConfig, state common.WorldState) (client.RemoteConnConfig, client.AuthInfo) {
|
||||
rmt, auth, err := rawConfig.ProcessRawConfig(state)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return lcl, rmt, auth
|
||||
return rmt, auth
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
// ^
|
||||
// |
|
||||
|
|
@ -202,12 +198,12 @@ func establishSession(lcc client.LocalConnConfig, rcc client.RemoteConnConfig, a
|
|||
addrCh <- conn.LocalAddr().(*net.UDPAddr)
|
||||
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
|
||||
} else {
|
||||
var proxyToCkClientL *connutil.PipeListener
|
||||
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
|
||||
|
|
@ -259,11 +255,11 @@ func TestUDP(t *testing.T) {
|
|||
log.SetLevel(log.ErrorLevel)
|
||||
|
||||
worldState := common.WorldOfTime(time.Unix(10, 0))
|
||||
lcc, rcc, ai := generateClientConfigs(basicUDPConfig, worldState)
|
||||
rcc, ai := generateClientConfigs(basicUDPConfig, worldState)
|
||||
sta := basicServerState(worldState)
|
||||
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -300,7 +296,7 @@ func TestUDP(t *testing.T) {
|
|||
|
||||
const echoMsgLen = 1024
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -320,9 +316,9 @@ func TestUDP(t *testing.T) {
|
|||
func TestTCPSingleplex(t *testing.T) {
|
||||
log.SetLevel(log.ErrorLevel)
|
||||
worldState := common.WorldOfTime(time.Unix(10, 0))
|
||||
lcc, rcc, ai := generateClientConfigs(singleplexTCPConfig, worldState)
|
||||
rcc, ai := generateClientConfigs(singleplexTCPConfig, worldState)
|
||||
sta := basicServerState(worldState)
|
||||
proxyToCkClientD, proxyFromCkServerL, _, _, err := establishSession(lcc, rcc, ai, sta)
|
||||
proxyToCkClientD, proxyFromCkServerL, _, _, err := establishSession(rcc, ai, sta)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -381,7 +377,7 @@ func TestTCPMultiplex(t *testing.T) {
|
|||
log.SetLevel(log.ErrorLevel)
|
||||
worldState := common.WorldOfTime(time.Unix(10, 0))
|
||||
|
||||
lcc, rcc, ai := generateClientConfigs(basicTCPConfig, worldState)
|
||||
rcc, ai := generateClientConfigs(basicTCPConfig, worldState)
|
||||
sta := basicServerState(worldState)
|
||||
|
||||
t.Run("user echo single", func(t *testing.T) {
|
||||
|
|
@ -390,7 +386,7 @@ func TestTCPMultiplex(t *testing.T) {
|
|||
writeData := make([]byte, dataLen)
|
||||
rand.Read(writeData)
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -423,7 +419,7 @@ func TestTCPMultiplex(t *testing.T) {
|
|||
|
||||
const echoMsgLen = 16384
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -441,7 +437,7 @@ func TestTCPMultiplex(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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -466,9 +462,9 @@ func TestClosingStreamsFromProxy(t *testing.T) {
|
|||
clientConfig := clientConfig
|
||||
clientConfigName := clientConfigName
|
||||
t.Run(clientConfigName, func(t *testing.T) {
|
||||
lcc, rcc, ai := generateClientConfigs(clientConfig, worldState)
|
||||
rcc, ai := generateClientConfigs(clientConfig, worldState)
|
||||
sta := basicServerState(worldState)
|
||||
proxyToCkClientD, proxyFromCkServerL, _, _, err := establishSession(lcc, rcc, ai, sta)
|
||||
proxyToCkClientD, proxyFromCkServerL, _, _, err := establishSession(rcc, ai, sta)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -528,7 +524,7 @@ func TestClosingStreamsFromProxy(t *testing.T) {
|
|||
func BenchmarkIntegration(b *testing.B) {
|
||||
log.SetLevel(log.ErrorLevel)
|
||||
worldState := common.WorldOfTime(time.Unix(10, 0))
|
||||
lcc, rcc, ai := generateClientConfigs(basicTCPConfig, worldState)
|
||||
rcc, ai := generateClientConfigs(basicTCPConfig, worldState)
|
||||
sta := basicServerState(worldState)
|
||||
const bufSize = 16 * 1024
|
||||
|
||||
|
|
@ -542,7 +538,7 @@ func BenchmarkIntegration(b *testing.B) {
|
|||
for name, method := range encryptionMethods {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
ai.EncryptionMethod = method
|
||||
proxyToCkClientD, proxyFromCkServerL, _, _, err := establishSession(lcc, rcc, ai, sta)
|
||||
proxyToCkClientD, proxyFromCkServerL, _, _, err := establishSession(rcc, ai, sta)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ import (
|
|||
"crypto"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cbeuw/Cloak/internal/common"
|
||||
"github.com/cbeuw/Cloak/libcloak/client/browsers"
|
||||
"github.com/cbeuw/Cloak/libcloak/client/transports"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"strings"
|
||||
|
|
@ -65,22 +68,12 @@ type RawConfig struct {
|
|||
// is set to `cdn`
|
||||
// Defaults to RemoteHost
|
||||
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
|
||||
// Defaults to -1, which means no TCP KeepAlive is ever sent
|
||||
KeepAlive int
|
||||
// RemotePort is the port Cloak server is listening to
|
||||
// Defaults to 443
|
||||
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 {
|
||||
|
|
@ -91,93 +84,10 @@ type RemoteConnConfig struct {
|
|||
TransportMaker func() transports.Transport
|
||||
}
|
||||
|
||||
type LocalConnConfig struct {
|
||||
LocalAddr string
|
||||
Timeout time.Duration
|
||||
MockDomainList []string
|
||||
}
|
||||
|
||||
type AuthInfo = transports.AuthInfo
|
||||
|
||||
// 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 *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) {
|
||||
func (raw *RawConfig) ProcessRawConfig(worldState common.WorldState) (remote RemoteConnConfig, auth AuthInfo, err error) {
|
||||
nullErr := func(field string) (remote RemoteConnConfig, auth AuthInfo, err error) {
|
||||
err = fmt.Errorf("%v cannot be empty", field)
|
||||
return
|
||||
}
|
||||
|
|
@ -189,18 +99,8 @@ func (raw *RawConfig) ProcessRawConfig(worldState common.WorldState) (local Loca
|
|||
}
|
||||
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 == "" {
|
||||
return nullErr("ServerName")
|
||||
return nullErr("ProxyMethod")
|
||||
}
|
||||
auth.ProxyMethod = raw.ProxyMethod
|
||||
if len(raw.UID) == 0 {
|
||||
|
|
@ -302,19 +202,5 @@ func (raw *RawConfig) ProcessRawConfig(worldState common.WorldState) (local Loca
|
|||
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
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package transports
|
||||
|
||||
import (
|
||||
"github.com/cbeuw/Cloak/internal/common"
|
||||
"github.com/cbeuw/Cloak/libcloak/client/browsers"
|
||||
utls "github.com/refraction-networking/utls"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"net"
|
||||
Loading…
Reference in New Issue