Barely working

This commit is contained in:
Qian Wang 2018-10-09 21:53:55 +01:00
parent ae30ed6ba4
commit b9f2aa4ed0
9 changed files with 144 additions and 100 deletions

View File

@ -31,7 +31,7 @@ func pipe(dst io.ReadWriteCloser, src io.ReadWriteCloser) {
} }
// This establishes a connection with ckserver and performs a handshake // This establishes a connection with ckserver and performs a handshake
func makeRemoteConn(sta *client.State) net.Conn { func makeRemoteConn(sta *client.State) (net.Conn, error) {
d := net.Dialer{Control: protector} d := net.Dialer{Control: protector}
@ -39,12 +39,12 @@ func makeRemoteConn(sta *client.State) net.Conn {
remoteConn, err := d.Dial("tcp", sta.SS_REMOTE_HOST+":"+sta.SS_REMOTE_PORT) remoteConn, err := d.Dial("tcp", sta.SS_REMOTE_HOST+":"+sta.SS_REMOTE_PORT)
if err != nil { if err != nil {
log.Printf("Connecting to remote: %v\n", err) log.Printf("Connecting to remote: %v\n", err)
return nil return nil, err
} }
_, err = remoteConn.Write(clientHello) _, err = remoteConn.Write(clientHello)
if err != nil { if err != nil {
log.Printf("Sending ClientHello: %v\n", err) log.Printf("Sending ClientHello: %v\n", err)
return nil return nil, err
} }
// Three discarded messages: ServerHello, ChangeCipherSpec and Finished // Three discarded messages: ServerHello, ChangeCipherSpec and Finished
@ -53,7 +53,7 @@ func makeRemoteConn(sta *client.State) net.Conn {
_, err = util.ReadTillDrain(remoteConn, discardBuf) _, err = util.ReadTillDrain(remoteConn, discardBuf)
if err != nil { if err != nil {
log.Printf("Reading discarded message %v: %v\n", c, err) log.Printf("Reading discarded message %v: %v\n", c, err)
return nil return nil, err
} }
} }
@ -61,10 +61,10 @@ func makeRemoteConn(sta *client.State) net.Conn {
_, err = remoteConn.Write(reply) _, err = remoteConn.Write(reply)
if err != nil { if err != nil {
log.Printf("Sending reply to remote: %v\n", err) log.Printf("Sending reply to remote: %v\n", err)
return nil return nil, err
} }
return remoteConn return remoteConn, nil
} }
@ -134,7 +134,10 @@ func main() {
log.Fatal("TicketTimeHint cannot be empty or 0") log.Fatal("TicketTimeHint cannot be empty or 0")
} }
initRemoteConn := makeRemoteConn(sta) initRemoteConn, err := makeRemoteConn(sta)
if err != nil {
log.Fatalf("Failed to establish connection to remote: %v\n", err)
}
obfs := util.MakeObfs(sta.SID) obfs := util.MakeObfs(sta.SID)
deobfs := util.MakeDeobfs(sta.SID) deobfs := util.MakeDeobfs(sta.SID)
@ -143,7 +146,11 @@ func main() {
for i := 0; i < sta.NumConn-1; i++ { for i := 0; i < sta.NumConn-1; i++ {
go func() { go func() {
conn := makeRemoteConn(sta) conn, err := makeRemoteConn(sta)
if err != nil {
log.Printf("Failed to establish new connections to remote: %v\n", err)
return
}
sesh.AddConnection(conn) sesh.AddConnection(conn)
}() }()
} }
@ -159,10 +166,17 @@ func main() {
continue continue
} }
go func() { go func() {
data := make([]byte, 10240)
i, err := io.ReadAtLeast(ssConn, data, 1)
if err != nil {
ssConn.Close()
return
}
stream, err := sesh.OpenStream() stream, err := sesh.OpenStream()
if err != nil { if err != nil {
ssConn.Close() ssConn.Close()
} }
stream.Write(data[:i])
go pipe(ssConn, stream) go pipe(ssConn, stream)
pipe(stream, ssConn) pipe(stream, ssConn)
}() }()

View File

@ -89,11 +89,11 @@ func dispatchConnection(conn net.Conn, sta *server.State) {
go func() { go func() {
var arrSID [32]byte var arrSID [32]byte
copy(arrSID[:], SID) copy(arrSID[:], SID)
sesh := sta.GetSession(arrSID) var sesh *mux.Session
if sesh == nil { if sesh = sta.GetSession(arrSID); sesh != nil {
sesh.AddConnection(conn) sesh.AddConnection(conn)
} else { } else {
sesh := mux.MakeSession(0, conn, util.MakeObfs(SID), util.MakeDeobfs(SID), util.ReadTillDrain) sesh = mux.MakeSession(0, conn, util.MakeObfs(SID), util.MakeDeobfs(SID), util.ReadTillDrain)
sta.PutSession(arrSID, sesh) sta.PutSession(arrSID, sesh)
} }
go func() { go func() {
@ -166,6 +166,7 @@ func main() {
SS_REMOTE_PORT: remotePort, SS_REMOTE_PORT: remotePort,
Now: time.Now, Now: time.Now,
UsedRandom: map[[32]byte]int{}, UsedRandom: map[[32]byte]int{},
Sessions: map[[32]byte]*mux.Session{},
} }
err := sta.ParseConfig(pluginOpts) err := sta.ParseConfig(pluginOpts)
if err != nil { if err != nil {

View File

@ -1,6 +1,7 @@
{ {
"ServerName":"www.bing.com", "ServerName":"www.bing.com",
"Key":"UNhY4JhezH9gQYqvDMWrWH9CwlcKiECVqejMrND2VFwEOF8c8XRX8iYVdjKW2BAfym2zppExMPteovDB/Q8phdD53FnH39tQ1daaVLn9+FIGOAdk+UZZ2aOt5jSK638YPg==", "Key":"UNhY4JhezH9gQYqvDMWrWH9CwlcKiECVqejMrND2VFwEOF8c8XRX8iYVdjKW2BAfym2zppExMPteovDB/Q8phdD53FnH39tQ1daaVLn9+FIGOAdk+UZZ2aOt5jSK638YPg==",
"TicketTimeHint":3600, "TicketTimeHint":3600,
"Browser":"chrome" "NumConn":4,
"MaskBrowser":"chrome"
} }

View File

@ -83,6 +83,7 @@ func (sta *State) ParseConfig(conf string) (err error) {
sta.ServerName = preParse.ServerName sta.ServerName = preParse.ServerName
sta.TicketTimeHint = preParse.TicketTimeHint sta.TicketTimeHint = preParse.TicketTimeHint
sta.MaskBrowser = preParse.MaskBrowser sta.MaskBrowser = preParse.MaskBrowser
sta.NumConn = preParse.NumConn
sid, pub, err := parseKey(preParse.Key) sid, pub, err := parseKey(preParse.Key)
if err != nil { if err != nil {
return errors.New("Failed to parse Key: " + err.Error()) return errors.New("Failed to parse Key: " + err.Error())

View File

@ -53,43 +53,49 @@ func (sh *sorterHeap) Pop() interface{} {
return x return x
} }
func (s *Stream) recvNewFrame(f *Frame) { func (s *Stream) recvNewFrame() {
// For the ease of demonstration, assume seq is uint8, i.e. it wraps around after 255 for {
fs := &frameNode{ f := <-s.newFrameCh
f.Seq, if f == nil {
0, continue
f, }
} // For the ease of demonstration, assume seq is uint8, i.e. it wraps around after 255
fs := &frameNode{
// TODO: if a malicious client resend a previously sent seq number, what will happen? f.Seq,
if fs.seq < s.nextRecvSeq { 0,
// e.g. we are on rev=0 (wrap has not happened yet) f,
// and we get the order of recv as 253 254 0 1
// after 254, nextN should be 255, but 0 is received and 0 < 255
// now 0 should have a trueSeq of 256
if !s.wrapMode {
// wrapMode is true when the latest seq is wrapped but nextN is not
s.wrapMode = true
} }
fs.trueSeq = uint64(2<<16*(s.rev+1)) + uint64(fs.seq) + 1
// +1 because wrapped 0 should have trueSeq of 256 instead of 255
// when this bit was run on 1, the trueSeq of 1 would become 256
} else {
fs.trueSeq = uint64(2<<16*s.rev) + uint64(fs.seq)
// when this bit was run on 255, the trueSeq of 255 would be 255
}
heap.Push(&s.sh, fs)
// Keep popping from the heap until empty or to the point that the wanted seq was not received // TODO: if a malicious client resend a previously sent seq number, what will happen?
for len(s.sh) > 0 && s.sh[0].seq == s.nextRecvSeq { if fs.seq < s.nextRecvSeq {
// e.g. we are on rev=0 (wrap has not happened yet)
// and we get the order of recv as 253 254 0 1
// after 254, nextN should be 255, but 0 is received and 0 < 255
// now 0 should have a trueSeq of 256
if !s.wrapMode {
// wrapMode is true when the latest seq is wrapped but nextN is not
s.wrapMode = true
}
fs.trueSeq = uint64(2<<16*(s.rev+1)) + uint64(fs.seq) + 1
// +1 because wrapped 0 should have trueSeq of 256 instead of 255
// when this bit was run on 1, the trueSeq of 1 would become 256
} else {
fs.trueSeq = uint64(2<<16*s.rev) + uint64(fs.seq)
// when this bit was run on 255, the trueSeq of 255 would be 255
}
heap.Push(&s.sh, fs)
s.sortedBufCh <- heap.Pop(&s.sh).(*frameNode).frame.Payload // Keep popping from the heap until empty or to the point that the wanted seq was not received
for len(s.sh) > 0 && s.sh[0].seq == s.nextRecvSeq {
s.nextRecvSeq += 1 s.sortedBufCh <- heap.Pop(&s.sh).(*frameNode).frame.Payload
if s.nextRecvSeq == 0 {
// when nextN is wrapped, wrapMode becomes false and rev+1 s.nextRecvSeq += 1
s.rev += 1 if s.nextRecvSeq == 0 {
s.wrapMode = false // when nextN is wrapped, wrapMode becomes false and rev+1
s.rev += 1
s.wrapMode = false
}
} }
} }

View File

@ -1,6 +1,7 @@
package multiplex package multiplex
import ( import (
"log"
"net" "net"
"sync" "sync"
) )
@ -49,13 +50,12 @@ func MakeSession(id int, conn net.Conn, obfs func(*Frame) []byte, deobfs func([]
obfs: obfs, obfs: obfs,
deobfs: deobfs, deobfs: deobfs,
obfsedReader: obfsedReader, obfsedReader: obfsedReader,
nextStreamID: 0, nextStreamID: 1,
streams: make(map[uint32]*Stream), streams: make(map[uint32]*Stream),
acceptCh: make(chan *Stream, acceptBacklog), acceptCh: make(chan *Stream, acceptBacklog),
closeQCh: make(chan uint32, closeBacklog), closeQCh: make(chan uint32, closeBacklog),
} }
sesh.sb = makeSwitchboard(conn, sesh) sesh.sb = makeSwitchboard(conn, sesh)
sesh.sb.run()
return sesh return sesh
} }
@ -80,6 +80,7 @@ func (sesh *Session) OpenStream() (*Stream, error) {
func (sesh *Session) AcceptStream() (*Stream, error) { func (sesh *Session) AcceptStream() (*Stream, error) {
stream := <-sesh.acceptCh stream := <-sesh.acceptCh
return stream, nil return stream, nil
} }
func (sesh *Session) delStream(id uint32) { func (sesh *Session) delStream(id uint32) {
@ -101,7 +102,13 @@ func (sesh *Session) getStream(id uint32) *Stream {
return sesh.streams[id] return sesh.streams[id]
} }
func (sesh *Session) addStream(id uint32) { // addStream is used when the remote opened a new stream and we got notified
func (sesh *Session) addStream(id uint32) *Stream {
log.Printf("Adding stream %v", id)
stream := makeStream(id, sesh) stream := makeStream(id, sesh)
sesh.streamsM.Lock()
sesh.streams[id] = stream
sesh.streamsM.Unlock()
sesh.acceptCh <- stream sesh.acceptCh <- stream
return stream
} }

View File

@ -7,7 +7,7 @@ import (
) )
const ( const (
readBuffer = 10240 readBuffer = 102400
) )
type Stream struct { type Stream struct {
@ -25,6 +25,7 @@ type Stream struct {
sh sorterHeap sh sorterHeap
wrapMode bool wrapMode bool
newFrameCh chan *Frame
sortedBufCh chan []byte sortedBufCh chan []byte
nextSendSeqM sync.Mutex nextSendSeqM sync.Mutex
@ -36,14 +37,19 @@ type Stream struct {
func makeStream(id uint32, sesh *Session) *Stream { func makeStream(id uint32, sesh *Session) *Stream {
stream := &Stream{ stream := &Stream{
id: id, id: id,
session: sesh, session: sesh,
die: make(chan struct{}),
sh: []*frameNode{},
newFrameCh: make(chan *Frame, 1024),
sortedBufCh: make(chan []byte, readBuffer),
} }
go stream.recvNewFrame()
return stream return stream
} }
func (stream *Stream) Read(buf []byte) (n int, err error) { func (stream *Stream) Read(buf []byte) (n int, err error) {
if len(buf) == 0 { if len(buf) != 0 {
select { select {
case <-stream.die: case <-stream.die:
return 0, errors.New(errBrokenPipe) return 0, errors.New(errBrokenPipe)
@ -79,8 +85,8 @@ func (stream *Stream) Write(in []byte) (n int, err error) {
StreamID: stream.id, StreamID: stream.id,
Seq: stream.nextSendSeq, Seq: stream.nextSendSeq,
ClosingStreamID: closingID, ClosingStreamID: closingID,
Payload: in,
} }
copy(f.Payload, in)
stream.nextSendSeqM.Lock() stream.nextSendSeqM.Lock()
stream.nextSendSeq += 1 stream.nextSendSeq += 1
@ -103,7 +109,6 @@ func (stream *Stream) Close() error {
stream.closing = true stream.closing = true
stream.session.delStream(stream.id) stream.session.delStream(stream.id)
close(stream.die) close(stream.die)
close(stream.sortedBufCh)
stream.session.closeQCh <- stream.id stream.session.closeQCh <- stream.id
return nil return nil
} }

View File

@ -1,13 +1,15 @@
package multiplex package multiplex
import ( import (
"log"
"net" "net"
"sort" "sort"
) )
const ( const (
sentNotifyBacklog = 1024 sentNotifyBacklog = 1024
dispatchBacklog = 10240 dispatchBacklog = 102400
newConnBacklog = 8
) )
type switchboard struct { type switchboard struct {
@ -19,6 +21,7 @@ type switchboard struct {
sentNotifyCh chan *sentNotifier sentNotifyCh chan *sentNotifier
dispatCh chan []byte dispatCh chan []byte
newConnCh chan net.Conn newConnCh chan net.Conn
closingCECh chan *connEnclave
} }
// Some data comes from a Stream to be sent through one of the many // Some data comes from a Stream to be sent through one of the many
@ -51,6 +54,8 @@ func makeSwitchboard(conn net.Conn, sesh *Session) *switchboard {
ces: []*connEnclave{}, ces: []*connEnclave{},
sentNotifyCh: make(chan *sentNotifier, sentNotifyBacklog), sentNotifyCh: make(chan *sentNotifier, sentNotifyBacklog),
dispatCh: make(chan []byte, dispatchBacklog), dispatCh: make(chan []byte, dispatchBacklog),
newConnCh: make(chan net.Conn, newConnBacklog),
closingCECh: make(chan *connEnclave, 5),
} }
ce := &connEnclave{ ce := &connEnclave{
sb: sb, sb: sb,
@ -58,15 +63,12 @@ func makeSwitchboard(conn net.Conn, sesh *Session) *switchboard {
sendQueue: 0, sendQueue: 0,
} }
sb.ces = append(sb.ces, ce) sb.ces = append(sb.ces, ce)
go sb.deplex(ce)
go sb.dispatch()
return sb return sb
} }
func (sb *switchboard) run() {
go sb.startDispatcher()
go sb.startDeplexer()
}
// Everytime after a remoteConn sends something, it constructs this struct // Everytime after a remoteConn sends something, it constructs this struct
// Which is sent back to dispatch() through sentNotifyCh to tell dispatch // Which is sent back to dispatch() through sentNotifyCh to tell dispatch
// how many bytes it has sent // how many bytes it has sent
@ -87,7 +89,7 @@ func (ce *connEnclave) send(data []byte) {
// Dispatcher sends data coming from a stream to a remote connection // Dispatcher sends data coming from a stream to a remote connection
// I used channels here because I didn't want to use mutex // I used channels here because I didn't want to use mutex
func (sb *switchboard) startDispatcher() { func (sb *switchboard) dispatch() {
for { for {
select { select {
// dispatCh receives data from stream.Write // dispatCh receives data from stream.Write
@ -104,25 +106,38 @@ func (sb *switchboard) startDispatcher() {
sendQueue: 0, sendQueue: 0,
} }
sb.ces = append(sb.ces, newCe) sb.ces = append(sb.ces, newCe)
go sb.deplex(newCe)
sort.Sort(byQ(sb.ces)) sort.Sort(byQ(sb.ces))
case closing := <-sb.closingCECh:
for i, ce := range sb.ces {
if closing == ce {
sb.ces = append(sb.ces[:i], sb.ces[i+1:]...)
break
}
}
// TODO: when all connections closed
} }
} }
} }
// Deplexer sends data coming from a remote connection to a stream func (sb *switchboard) deplex(ce *connEnclave) {
func (sb *switchboard) startDeplexer() { buf := make([]byte, 20480)
for _, ce := range sb.ces { for {
go func() { i, err := sb.session.obfsedReader(ce.remoteConn, buf)
buf := make([]byte, 20480) if err != nil {
for { log.Println(err)
sb.session.obfsedReader(ce.remoteConn, buf) go ce.remoteConn.Close()
frame := sb.session.deobfs(buf) sb.closingCECh <- ce
if !sb.session.isStream(frame.StreamID) { return
sb.session.addStream(frame.StreamID) }
} frame := sb.session.deobfs(buf[:i])
sb.session.getStream(frame.ClosingStreamID).Close() var stream *Stream
sb.session.getStream(frame.StreamID).recvNewFrame(frame) if stream = sb.session.getStream(frame.StreamID); stream == nil {
} stream = sb.session.addStream(frame.StreamID)
}() }
if closing := sb.session.getStream(frame.ClosingStreamID); closing != nil {
closing.Close()
}
stream.newFrameCh <- frame
} }
} }

View File

@ -11,7 +11,7 @@ import (
func encrypt(iv []byte, key []byte, plaintext []byte) []byte { func encrypt(iv []byte, key []byte, plaintext []byte) []byte {
block, _ := aes.NewCipher(key) block, _ := aes.NewCipher(key)
ciphertext := make([]byte, len(plaintext)) ciphertext := make([]byte, len(plaintext))
stream := cipher.NewCFBEncrypter(block, iv) stream := cipher.NewCTR(block, iv)
stream.XORKeyStream(ciphertext, plaintext) stream.XORKeyStream(ciphertext, plaintext)
return ciphertext return ciphertext
} }
@ -20,9 +20,8 @@ func decrypt(iv []byte, key []byte, ciphertext []byte) []byte {
ret := make([]byte, len(ciphertext)) ret := make([]byte, len(ciphertext))
copy(ret, ciphertext) // Because XORKeyStream is inplace, but we don't want the input to be changed copy(ret, ciphertext) // Because XORKeyStream is inplace, but we don't want the input to be changed
block, _ := aes.NewCipher(key) block, _ := aes.NewCipher(key)
stream := cipher.NewCFBDecrypter(block, iv) stream := cipher.NewCTR(block, iv)
stream.XORKeyStream(ret, ret) stream.XORKeyStream(ret, ret)
// ret is now plaintext
return ret return ret
} }
@ -33,17 +32,13 @@ func MakeObfs(key []byte) func(*mux.Frame) []byte {
binary.BigEndian.PutUint32(header[4:8], f.Seq) binary.BigEndian.PutUint32(header[4:8], f.Seq)
binary.BigEndian.PutUint32(header[8:12], f.ClosingStreamID) binary.BigEndian.PutUint32(header[8:12], f.ClosingStreamID)
// header: [StreamID 4 bytes][Seq 4 bytes][ClosingStreamID 4 bytes] // header: [StreamID 4 bytes][Seq 4 bytes][ClosingStreamID 4 bytes]
plainheader := make([]byte, 16) iv := CryptoRandBytes(16)
copy(plainheader[0:12], header) cipherheader := encrypt(iv, key, header)
copy(plainheader[12:], []byte{0x00, 0x00, 0x00, 0x00}) obfsed := make([]byte, len(f.Payload)+12+16)
// plainheader: [header 12 bytes][0x00,0x00,0x00,0x00]
iv := f.Payload[0:16]
cipherheader := encrypt(iv, key, plainheader)
obfsed := make([]byte, len(f.Payload)+12+4)
copy(obfsed[0:16], iv) copy(obfsed[0:16], iv)
copy(obfsed[16:32], cipherheader) copy(obfsed[16:28], cipherheader)
copy(obfsed[32:], f.Payload[16:]) copy(obfsed[28:], f.Payload)
// obfsed: [iv 16 bytes][cipherheader 16 bytes][payload w/o iv] // obfsed: [iv 16 bytes][cipherheader 12 bytes][payload]
ret := AddRecordLayer(obfsed, []byte{0x17}, []byte{0x03, 0x03}) ret := AddRecordLayer(obfsed, []byte{0x17}, []byte{0x03, 0x03})
return ret return ret
} }
@ -53,14 +48,13 @@ func MakeObfs(key []byte) func(*mux.Frame) []byte {
func MakeDeobfs(key []byte) func([]byte) *mux.Frame { func MakeDeobfs(key []byte) func([]byte) *mux.Frame {
deobfs := func(in []byte) *mux.Frame { deobfs := func(in []byte) *mux.Frame {
peeled := PeelRecordLayer(in) peeled := PeelRecordLayer(in)
plainheader := decrypt(peeled[0:16], key, peeled[16:32]) header := decrypt(peeled[0:16], key, peeled[16:28])
// plainheader: [header 12 bytes][0x00,0x00,0x00,0x00] streamID := binary.BigEndian.Uint32(header[0:4])
streamID := binary.BigEndian.Uint32(plainheader[0:4]) seq := binary.BigEndian.Uint32(header[4:8])
seq := binary.BigEndian.Uint32(plainheader[4:8]) closingStreamID := binary.BigEndian.Uint32(header[8:12])
closingStreamID := binary.BigEndian.Uint32(plainheader[8:12]) payload := make([]byte, len(peeled)-12-16)
payload := make([]byte, len(peeled)-12-4) //log.Printf("Payload: %x\n", payload)
copy(payload[0:16], peeled[0:16]) copy(payload, peeled[28:])
copy(payload[16:], peeled[32:])
ret := &mux.Frame{ ret := &mux.Frame{
StreamID: streamID, StreamID: streamID,
Seq: seq, Seq: seq,