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)
|
||||
goto makeconn
|
||||
}
|
||||
sk, err := client.PrepareConnection(sta, remoteConn)
|
||||
sk, err := sta.Transport.PrepareConnection(sta, remoteConn)
|
||||
if err != nil {
|
||||
remoteConn.Close()
|
||||
log.Errorf("Failed to prepare connection to remote: %v", err)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"Transport": "TLS",
|
||||
"ProxyMethod":"shadowsocks",
|
||||
"EncryptionMethod":"plain",
|
||||
"UID":"5nneblJy6lniPJfr81LuYQ==",
|
||||
|
|
|
|||
1
go.mod
1
go.mod
|
|
@ -5,6 +5,7 @@ go 1.12
|
|||
require (
|
||||
github.com/boltdb/bolt v1.3.1
|
||||
github.com/gorilla/mux v1.7.3
|
||||
github.com/gorilla/websocket v1.4.1
|
||||
github.com/juju/ratelimit v1.0.1
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
|
|
|
|||
|
|
@ -37,9 +37,13 @@ func addExtRec(typ []byte, data []byte) []byte {
|
|||
return ret
|
||||
}
|
||||
|
||||
type TLS struct {
|
||||
Transport
|
||||
}
|
||||
|
||||
// PrepareConnection handles the TLS handshake for a given conn and returns the sessionKey
|
||||
// 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)
|
||||
chOnly := sta.browser.composeClientHello(hd)
|
||||
chWithRecordLayer := util.AddRecordLayer(chOnly, []byte{0x16}, []byte{0x03, 0x01})
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ const (
|
|||
)
|
||||
|
||||
type chHiddenData struct {
|
||||
rawCiphertextWithTag []byte
|
||||
chRandom []byte
|
||||
chSessionId []byte
|
||||
chX25519KeyShare []byte
|
||||
|
|
@ -49,6 +50,7 @@ func makeHiddenData(sta *State) (ret chHiddenData, sharedSecret []byte) {
|
|||
sharedSecret = ecdh.GenerateSharedSecret(ephPv, sta.staticPub)
|
||||
nonce := ret.chRandom[0:12]
|
||||
ciphertextWithTag, _ := util.AESGCMEncrypt(nonce, sharedSecret, plaintext)
|
||||
ret.rawCiphertextWithTag = ciphertextWithTag
|
||||
ret.chSessionId = ciphertextWithTag[0:32]
|
||||
ret.chX25519KeyShare = ciphertextWithTag[32:64]
|
||||
ret.chExtSNI = makeServerName(sta.ServerName)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ type rawConfig struct {
|
|||
UID string
|
||||
PublicKey string
|
||||
BrowserSig string
|
||||
Transport string
|
||||
NumConn int
|
||||
StreamTimeout int
|
||||
}
|
||||
|
|
@ -33,6 +34,8 @@ type State struct {
|
|||
RemotePort string
|
||||
Unordered bool
|
||||
|
||||
Transport Transport
|
||||
|
||||
SessionID uint32
|
||||
UID []byte
|
||||
|
||||
|
|
@ -115,6 +118,15 @@ func (sta *State) ParseConfig(conf string) (err error) {
|
|||
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.ServerName = preParse.ServerName
|
||||
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