mirror of https://github.com/cbeuw/Cloak
Client side plain websocket
This commit is contained in:
parent
75dc91c15c
commit
a3c3c5dc07
|
|
@ -53,7 +53,7 @@ func makeSession(sta *client.State, isAdmin bool) *mux.Session {
|
||||||
time.Sleep(time.Second * 3)
|
time.Sleep(time.Second * 3)
|
||||||
goto makeconn
|
goto makeconn
|
||||||
}
|
}
|
||||||
sk, err := client.PrepareConnection(sta, remoteConn)
|
sk, err := sta.Transport.PrepareConnection(sta, remoteConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remoteConn.Close()
|
remoteConn.Close()
|
||||||
log.Errorf("Failed to prepare connection to remote: %v", err)
|
log.Errorf("Failed to prepare connection to remote: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"Transport": "TLS",
|
||||||
"ProxyMethod":"shadowsocks",
|
"ProxyMethod":"shadowsocks",
|
||||||
"EncryptionMethod":"plain",
|
"EncryptionMethod":"plain",
|
||||||
"UID":"5nneblJy6lniPJfr81LuYQ==",
|
"UID":"5nneblJy6lniPJfr81LuYQ==",
|
||||||
|
|
|
||||||
1
go.mod
1
go.mod
|
|
@ -5,6 +5,7 @@ go 1.12
|
||||||
require (
|
require (
|
||||||
github.com/boltdb/bolt v1.3.1
|
github.com/boltdb/bolt v1.3.1
|
||||||
github.com/gorilla/mux v1.7.3
|
github.com/gorilla/mux v1.7.3
|
||||||
|
github.com/gorilla/websocket v1.4.1
|
||||||
github.com/juju/ratelimit v1.0.1
|
github.com/juju/ratelimit v1.0.1
|
||||||
github.com/kr/pretty v0.1.0 // indirect
|
github.com/kr/pretty v0.1.0 // indirect
|
||||||
github.com/sirupsen/logrus v1.4.2
|
github.com/sirupsen/logrus v1.4.2
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,13 @@ func addExtRec(typ []byte, data []byte) []byte {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TLS struct {
|
||||||
|
Transport
|
||||||
|
}
|
||||||
|
|
||||||
// PrepareConnection handles the TLS handshake for a given conn and returns the sessionKey
|
// PrepareConnection handles the TLS handshake for a given conn and returns the sessionKey
|
||||||
// if the server proceed with Cloak authentication
|
// if the server proceed with Cloak authentication
|
||||||
func PrepareConnection(sta *State, conn net.Conn) (sessionKey []byte, err error) {
|
func (*TLS) PrepareConnection(sta *State, conn net.Conn) (sessionKey []byte, err error) {
|
||||||
hd, sharedSecret := makeHiddenData(sta)
|
hd, sharedSecret := makeHiddenData(sta)
|
||||||
chOnly := sta.browser.composeClientHello(hd)
|
chOnly := sta.browser.composeClientHello(hd)
|
||||||
chWithRecordLayer := util.AddRecordLayer(chOnly, []byte{0x16}, []byte{0x03, 0x01})
|
chWithRecordLayer := util.AddRecordLayer(chOnly, []byte{0x16}, []byte{0x03, 0x01})
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,11 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type chHiddenData struct {
|
type chHiddenData struct {
|
||||||
chRandom []byte
|
rawCiphertextWithTag []byte
|
||||||
chSessionId []byte
|
chRandom []byte
|
||||||
chX25519KeyShare []byte
|
chSessionId []byte
|
||||||
chExtSNI []byte
|
chX25519KeyShare []byte
|
||||||
|
chExtSNI []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeHiddenData generates the ephemeral key pair, calculates the shared secret, and then compose and
|
// makeHiddenData generates the ephemeral key pair, calculates the shared secret, and then compose and
|
||||||
|
|
@ -49,6 +50,7 @@ func makeHiddenData(sta *State) (ret chHiddenData, sharedSecret []byte) {
|
||||||
sharedSecret = ecdh.GenerateSharedSecret(ephPv, sta.staticPub)
|
sharedSecret = ecdh.GenerateSharedSecret(ephPv, sta.staticPub)
|
||||||
nonce := ret.chRandom[0:12]
|
nonce := ret.chRandom[0:12]
|
||||||
ciphertextWithTag, _ := util.AESGCMEncrypt(nonce, sharedSecret, plaintext)
|
ciphertextWithTag, _ := util.AESGCMEncrypt(nonce, sharedSecret, plaintext)
|
||||||
|
ret.rawCiphertextWithTag = ciphertextWithTag
|
||||||
ret.chSessionId = ciphertextWithTag[0:32]
|
ret.chSessionId = ciphertextWithTag[0:32]
|
||||||
ret.chX25519KeyShare = ciphertextWithTag[32:64]
|
ret.chX25519KeyShare = ciphertextWithTag[32:64]
|
||||||
ret.chExtSNI = makeServerName(sta.ServerName)
|
ret.chExtSNI = makeServerName(sta.ServerName)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ type rawConfig struct {
|
||||||
UID string
|
UID string
|
||||||
PublicKey string
|
PublicKey string
|
||||||
BrowserSig string
|
BrowserSig string
|
||||||
|
Transport string
|
||||||
NumConn int
|
NumConn int
|
||||||
StreamTimeout int
|
StreamTimeout int
|
||||||
}
|
}
|
||||||
|
|
@ -33,6 +34,8 @@ type State struct {
|
||||||
RemotePort string
|
RemotePort string
|
||||||
Unordered bool
|
Unordered bool
|
||||||
|
|
||||||
|
Transport Transport
|
||||||
|
|
||||||
SessionID uint32
|
SessionID uint32
|
||||||
UID []byte
|
UID []byte
|
||||||
|
|
||||||
|
|
@ -115,6 +118,15 @@ func (sta *State) ParseConfig(conf string) (err error) {
|
||||||
return errors.New("unsupported browser signature")
|
return errors.New("unsupported browser signature")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch strings.ToLower(preParse.Transport) {
|
||||||
|
case "tls":
|
||||||
|
sta.Transport = &TLS{}
|
||||||
|
case "websocket":
|
||||||
|
sta.Transport = &WebSocket{}
|
||||||
|
default:
|
||||||
|
sta.Transport = &TLS{}
|
||||||
|
}
|
||||||
|
|
||||||
sta.ProxyMethod = preParse.ProxyMethod
|
sta.ProxyMethod = preParse.ProxyMethod
|
||||||
sta.ServerName = preParse.ServerName
|
sta.ServerName = preParse.ServerName
|
||||||
sta.NumConn = preParse.NumConn
|
sta.NumConn = preParse.NumConn
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
type Transport interface {
|
||||||
|
PrepareConnection(*State, net.Conn) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"github.com/cbeuw/Cloak/internal/util"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WebSocketConn struct {
|
||||||
|
c *websocket.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ws *WebSocketConn) Write(data []byte) (int, error) {
|
||||||
|
err := ws.c.WriteMessage(websocket.BinaryMessage, data)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else {
|
||||||
|
return len(data), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ws *WebSocketConn) Read(buf []byte) (int, error) {
|
||||||
|
_, r, err := ws.c.NextReader()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return r.Read(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ws *WebSocketConn) Close() error { return ws.c.Close() }
|
||||||
|
func (ws *WebSocketConn) LocalAddr() net.Addr { return ws.c.LocalAddr() }
|
||||||
|
func (ws *WebSocketConn) RemoteAddr() net.Addr { return ws.c.RemoteAddr() }
|
||||||
|
func (ws *WebSocketConn) SetDeadline(t time.Time) error {
|
||||||
|
err := ws.c.SetReadDeadline(t)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ws.c.SetWriteDeadline(t)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (ws *WebSocketConn) SetReadDeadline(t time.Time) error { return ws.c.SetReadDeadline(t) }
|
||||||
|
func (ws *WebSocketConn) SetWriteDeadline(t time.Time) error { return ws.c.SetWriteDeadline(t) }
|
||||||
|
|
||||||
|
type WebSocket struct {
|
||||||
|
Transport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (WebSocket) PrepareConnection(sta *State, conn net.Conn) (sessionKey []byte, err error) {
|
||||||
|
u, err := url.Parse("ws://" + sta.RemoteHost + ":" + sta.RemotePort) //TODO IPv6
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hd, sharedSecret := makeHiddenData(sta)
|
||||||
|
header := http.Header{}
|
||||||
|
header.Add("hidden", base64.StdEncoding.EncodeToString(hd.rawCiphertextWithTag))
|
||||||
|
c, resp, err := websocket.NewClient(conn, u, header, 16480, 16480)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
reply, err := base64.StdEncoding.DecodeString(resp.Header.Get("reply"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(reply) != 60 {
|
||||||
|
return nil, errors.New("reply must be 60 bytes")
|
||||||
|
}
|
||||||
|
sessionKey, err = util.AESGCMDecrypt(reply[:12], sharedSecret, reply[12:])
|
||||||
|
|
||||||
|
conn = &WebSocketConn{c: c}
|
||||||
|
return
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue