Refactor MakeObfs and MakeDeobfs

This commit is contained in:
Qian Wang 2018-12-09 23:45:06 +00:00
parent 3abef6dbad
commit ed1253ff4c
8 changed files with 73 additions and 47 deletions

View File

@ -15,7 +15,6 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"log"
"net" "net"
"github.com/cbeuw/Cloak/internal/client" "github.com/cbeuw/Cloak/internal/client"
@ -39,7 +38,7 @@ type administrator struct {
adminUID []byte adminUID []byte
} }
func adminHandshake(sta *client.State) *administrator { func adminHandshake(sta *client.State) (*administrator, error) {
fmt.Println("Enter the ip:port of your server") fmt.Println("Enter the ip:port of your server")
var addr string var addr string
fmt.Scanln(&addr) fmt.Scanln(&addr)
@ -48,16 +47,14 @@ func adminHandshake(sta *client.State) *administrator {
fmt.Scanln(&b64AdminUID) fmt.Scanln(&b64AdminUID)
adminUID, err := base64.StdEncoding.DecodeString(b64AdminUID) adminUID, err := base64.StdEncoding.DecodeString(b64AdminUID)
if err != nil { if err != nil {
log.Println(err) return nil, err
return nil
} }
sta.UID = adminUID sta.UID = adminUID
remoteConn, err := net.Dial("tcp", addr) remoteConn, err := net.Dial("tcp", addr)
if err != nil { if err != nil {
log.Println(err) return nil, err
return nil
} }
clientHello := TLS.ComposeInitHandshake(sta) clientHello := TLS.ComposeInitHandshake(sta)
@ -68,15 +65,14 @@ func adminHandshake(sta *client.State) *administrator {
for c := 0; c < 3; c++ { for c := 0; c < 3; c++ {
_, err = util.ReadTLS(remoteConn, discardBuf) _, err = util.ReadTLS(remoteConn, discardBuf)
if err != nil { if err != nil {
log.Printf("Reading discarded message %v: %v\n", c, err) return nil, err
return nil
} }
} }
reply := TLS.ComposeReply() reply := TLS.ComposeReply()
_, err = remoteConn.Write(reply) _, err = remoteConn.Write(reply)
a := &administrator{remoteConn, adminUID} a := &administrator{remoteConn, adminUID}
return a return a, nil
} }
func (a *administrator) getCommand() []byte { func (a *administrator) getCommand() []byte {

View File

@ -81,6 +81,27 @@ func makeRemoteConn(sta *client.State) (net.Conn, error) {
} }
func adminPrompt(sta *client.State) error {
a, err := adminHandshake(sta)
if err != nil {
return err
}
buf := make([]byte, 16000)
for {
req := a.getCommand()
a.adminConn.Write(req)
n, err := a.adminConn.Read(buf)
if err != nil {
return err
}
resp, err := a.checkAndDecrypt(buf[:n])
if err != nil {
return err
}
fmt.Println(string(resp))
}
}
func main() { func main() {
// Should be 127.0.0.1 to listen to ss-local on this machine // Should be 127.0.0.1 to listen to ss-local on this machine
var localHost string var localHost string
@ -91,8 +112,7 @@ func main() {
// The proxy port,should be 443 // The proxy port,should be 443
var remotePort string var remotePort string
var pluginOpts string var pluginOpts string
var isAdmin bool
var isAdmin *bool
log.SetFlags(log.LstdFlags | log.Lshortfile) log.SetFlags(log.LstdFlags | log.Lshortfile)
@ -111,7 +131,7 @@ func main() {
flag.StringVar(&remotePort, "p", "443", "remotePort: proxy port, should be 443") flag.StringVar(&remotePort, "p", "443", "remotePort: proxy port, should be 443")
flag.StringVar(&pluginOpts, "c", "ckclient.json", "pluginOpts: path to ckclient.json or options seperated with semicolons") flag.StringVar(&pluginOpts, "c", "ckclient.json", "pluginOpts: path to ckclient.json or options seperated with semicolons")
askVersion := flag.Bool("v", false, "Print the version number") askVersion := flag.Bool("v", false, "Print the version number")
isAdmin = flag.Bool("a", false, "Admin mode") isAdmin = *flag.Bool("a", false, "Admin mode")
printUsage := flag.Bool("h", false, "Print this message") printUsage := flag.Bool("h", false, "Print this message")
flag.Parse() flag.Parse()
@ -140,25 +160,14 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
if *isAdmin { if isAdmin {
a := adminHandshake(sta) err = adminPrompt(sta)
buf := make([]byte, 16000) if err != nil {
for { log.Println(err)
req := a.getCommand()
a.adminConn.Write(req)
n, err := a.adminConn.Read(buf)
if err != nil {
log.Println(err)
return
}
resp, err := a.checkAndDecrypt(buf[:n])
if err != nil {
log.Println(err)
}
fmt.Println(string(resp))
} }
return return
} }
if sta.SS_LOCAL_PORT == "" { if sta.SS_LOCAL_PORT == "" {
log.Fatal("Must specify localPort") log.Fatal("Must specify localPort")
} }
@ -171,8 +180,8 @@ func main() {
var UNLIMITED int64 = 1e12 var UNLIMITED int64 = 1e12
valve := mux.MakeValve(1e12, 1e12, &UNLIMITED, &UNLIMITED) valve := mux.MakeValve(1e12, 1e12, &UNLIMITED, &UNLIMITED)
obfs := util.MakeObfs(sta.UID) obfs := mux.MakeObfs(sta.UID)
deobfs := util.MakeDeobfs(sta.UID) deobfs := mux.MakeDeobfs(sta.UID)
sesh := mux.MakeSession(0, valve, obfs, deobfs, util.ReadTLS) sesh := mux.MakeSession(0, valve, obfs, deobfs, util.ReadTLS)
var wg sync.WaitGroup var wg sync.WaitGroup
@ -205,17 +214,22 @@ func main() {
data := make([]byte, 10240) data := make([]byte, 10240)
i, err := io.ReadAtLeast(ssConn, data, 1) i, err := io.ReadAtLeast(ssConn, data, 1)
if err != nil { if err != nil {
log.Println(err)
ssConn.Close() ssConn.Close()
return return
} }
stream, err := sesh.OpenStream() stream, err := sesh.OpenStream()
if err != nil { if err != nil {
log.Println(err)
ssConn.Close() ssConn.Close()
return return
} }
_, err = stream.Write(data[:i]) _, err = stream.Write(data[:i])
if err != nil { if err != nil {
log.Println(err) log.Println(err)
ssConn.Close()
stream.Close()
return
} }
go pipe(ssConn, stream) go pipe(ssConn, stream)
pipe(stream, ssConn) pipe(stream, ssConn)

View File

@ -143,7 +143,7 @@ func dispatchConnection(conn net.Conn, sta *server.State) {
} }
} }
if sesh, existing := user.GetOrCreateSession(sessionID, util.MakeObfs(UID), util.MakeDeobfs(UID), util.ReadTLS); existing { if sesh, existing := user.GetOrCreateSession(sessionID, mux.MakeObfs(UID), mux.MakeDeobfs(UID), util.ReadTLS); existing {
sesh.AddConnection(conn) sesh.AddConnection(conn)
return return
} else { } else {

View File

@ -1,12 +1,15 @@
package util package multiplex
import ( import (
"encoding/binary" "encoding/binary"
"errors"
xxhash "github.com/OneOfOne/xxhash" xxhash "github.com/OneOfOne/xxhash"
mux "github.com/cbeuw/Cloak/internal/multiplex"
) )
type Obfser func(*Frame) ([]byte, error)
type Deobfser func([]byte) (*Frame, error)
// For each frame, the three parts of the header is xored with three keys. // For each frame, the three parts of the header is xored with three keys.
// The keys are generated from the SID and the payload of the frame. // The keys are generated from the SID and the payload of the frame.
// FIXME: this code will panic if len(data)<18. // FIXME: this code will panic if len(data)<18.
@ -23,8 +26,11 @@ func genXorKeys(secret []byte, data []byte) (i uint32, ii uint32, iii uint32) {
return ret[0], ret[1], ret[2] return ret[0], ret[1], ret[2]
} }
func MakeObfs(key []byte) func(*mux.Frame) []byte { func MakeObfs(key []byte) Obfser {
obfs := func(f *mux.Frame) []byte { obfs := func(f *Frame) ([]byte, error) {
if len(f.Payload) < 18 {
return nil, errors.New("Payload cannot be shorter than 18 bytes")
}
obfsedHeader := make([]byte, 12) obfsedHeader := make([]byte, 12)
// header: [StreamID 4 bytes][Seq 4 bytes][Closing 4 bytes] // header: [StreamID 4 bytes][Seq 4 bytes][Closing 4 bytes]
i, ii, iii := genXorKeys(key, f.Payload[0:18]) i, ii, iii := genXorKeys(key, f.Payload[0:18])
@ -42,13 +48,16 @@ func MakeObfs(key []byte) func(*mux.Frame) []byte {
copy(obfsed[5:17], obfsedHeader) copy(obfsed[5:17], obfsedHeader)
copy(obfsed[17:], f.Payload) copy(obfsed[17:], f.Payload)
// obfsed: [record layer 5 bytes][cipherheader 12 bytes][payload] // obfsed: [record layer 5 bytes][cipherheader 12 bytes][payload]
return obfsed return obfsed, nil
} }
return obfs return obfs
} }
func MakeDeobfs(key []byte) func([]byte) *mux.Frame { func MakeDeobfs(key []byte) Deobfser {
deobfs := func(in []byte) *mux.Frame { deobfs := func(in []byte) (*Frame, error) {
if len(in) < 30 {
return nil, errors.New("Input cannot be shorter than 30 bytes")
}
peeled := in[5:] peeled := in[5:]
i, ii, iii := genXorKeys(key, peeled[12:30]) i, ii, iii := genXorKeys(key, peeled[12:30])
streamID := binary.BigEndian.Uint32(peeled[0:4]) ^ i streamID := binary.BigEndian.Uint32(peeled[0:4]) ^ i
@ -56,13 +65,13 @@ func MakeDeobfs(key []byte) func([]byte) *mux.Frame {
closing := binary.BigEndian.Uint32(peeled[8:12]) ^ iii closing := binary.BigEndian.Uint32(peeled[8:12]) ^ iii
payload := make([]byte, len(peeled)-12) payload := make([]byte, len(peeled)-12)
copy(payload, peeled[12:]) copy(payload, peeled[12:])
ret := &mux.Frame{ ret := &Frame{
StreamID: streamID, StreamID: streamID,
Seq: seq, Seq: seq,
Closing: closing, Closing: closing,
Payload: payload, Payload: payload,
} }
return ret return ret, nil
} }
return deobfs return deobfs
} }

View File

@ -20,9 +20,9 @@ type Session struct {
id uint32 // This field isn't acutally used id uint32 // This field isn't acutally used
// Used in Stream.Write. Add multiplexing headers, encrypt and add TLS header // Used in Stream.Write. Add multiplexing headers, encrypt and add TLS header
obfs func(*Frame) []byte obfs Obfser
// Remove TLS header, decrypt and unmarshall multiplexing headers // Remove TLS header, decrypt and unmarshall multiplexing headers
deobfs func([]byte) *Frame deobfs Deobfser
// This is supposed to read one TLS message, the same as GoQuiet's ReadTillDrain // This is supposed to read one TLS message, the same as GoQuiet's ReadTillDrain
obfsedRead func(net.Conn, []byte) (int, error) obfsedRead func(net.Conn, []byte) (int, error)
@ -43,7 +43,7 @@ type Session struct {
} }
// 1 conn is needed to make a session // 1 conn is needed to make a session
func MakeSession(id uint32, valve *Valve, obfs func(*Frame) []byte, deobfs func([]byte) *Frame, obfsedRead func(net.Conn, []byte) (int, error)) *Session { func MakeSession(id uint32, valve *Valve, obfs Obfser, deobfs Deobfser, obfsedRead func(net.Conn, []byte) (int, error)) *Session {
sesh := &Session{ sesh := &Session{
id: id, id: id,
obfs: obfs, obfs: obfs,

View File

@ -98,7 +98,10 @@ func (stream *Stream) Write(in []byte) (n int, err error) {
Payload: in, Payload: in,
} }
tlsRecord := stream.session.obfs(f) tlsRecord, err := stream.session.obfs(f)
if err != nil {
return 0, err
}
n, err = stream.session.sb.send(tlsRecord) n, err = stream.session.sb.send(tlsRecord)
stream.writingM.RUnlock() stream.writingM.RUnlock()
@ -132,7 +135,7 @@ func (stream *Stream) Close() error {
Closing: 1, Closing: 1,
Payload: pad, Payload: pad,
} }
tlsRecord := stream.session.obfs(f) tlsRecord, _ := stream.session.obfs(f)
stream.session.sb.send(tlsRecord) stream.session.sb.send(tlsRecord)
stream.session.delStream(stream.id) stream.session.delStream(stream.id)

View File

@ -152,7 +152,11 @@ func (sb *switchboard) deplex(ce *connEnclave) {
sb.session.Close() sb.session.Close()
return return
} }
frame := sb.session.deobfs(buf[:n]) frame, err := sb.session.deobfs(buf[:n])
if err != nil {
log.Println(err)
continue
}
// FIXME: there has been a bug in which a packet has // FIXME: there has been a bug in which a packet has
// a seemingly corrupted StreamID (e.g. when the largest streamID is something like 3000 // a seemingly corrupted StreamID (e.g. when the largest streamID is something like 3000

View File

@ -89,7 +89,7 @@ func (u *User) DelSession(sessionID uint32) {
u.sessionsM.Unlock() u.sessionsM.Unlock()
} }
func (u *User) GetOrCreateSession(sessionID uint32, obfs func(*mux.Frame) []byte, deobfs func([]byte) *mux.Frame, obfsedRead func(net.Conn, []byte) (int, error)) (sesh *mux.Session, existing bool) { func (u *User) GetOrCreateSession(sessionID uint32, obfs mux.Obfser, deobfs mux.Deobfser, obfsedRead func(net.Conn, []byte) (int, error)) (sesh *mux.Session, existing bool) {
// TODO: session cap // TODO: session cap
u.sessionsM.Lock() u.sessionsM.Lock()
if sesh = u.sessions[sessionID]; sesh != nil { if sesh = u.sessions[sessionID]; sesh != nil {