2019-07-28 10:58:45 +00:00
|
|
|
package multiplex
|
|
|
|
|
|
|
|
import (
|
2019-08-02 22:23:54 +00:00
|
|
|
"bytes"
|
2020-04-13 14:17:19 +00:00
|
|
|
"github.com/cbeuw/Cloak/internal/common"
|
2020-04-08 17:16:54 +00:00
|
|
|
"io"
|
2020-04-14 12:31:24 +00:00
|
|
|
"io/ioutil"
|
2019-07-28 10:58:45 +00:00
|
|
|
"math/rand"
|
|
|
|
"testing"
|
|
|
|
"time"
|
2020-04-07 19:59:32 +00:00
|
|
|
|
|
|
|
"github.com/cbeuw/connutil"
|
2019-07-28 10:58:45 +00:00
|
|
|
)
|
|
|
|
|
2020-04-13 14:17:19 +00:00
|
|
|
const payloadLen = 1000
|
|
|
|
|
2020-04-14 12:31:24 +00:00
|
|
|
var emptyKey [32]byte
|
|
|
|
|
2020-04-13 21:48:28 +00:00
|
|
|
func setupSesh(unordered bool, key [32]byte, encryptionMethod byte) *Session {
|
|
|
|
obfuscator, _ := MakeObfuscator(encryptionMethod, key)
|
2019-08-11 23:22:15 +00:00
|
|
|
|
2020-04-08 14:17:33 +00:00
|
|
|
seshConfig := SessionConfig{
|
2019-08-11 23:22:15 +00:00
|
|
|
Obfuscator: obfuscator,
|
|
|
|
Valve: nil,
|
2019-08-14 09:04:27 +00:00
|
|
|
Unordered: unordered,
|
2019-08-11 23:22:15 +00:00
|
|
|
}
|
|
|
|
return MakeSession(0, seshConfig)
|
2019-07-28 10:58:45 +00:00
|
|
|
}
|
|
|
|
|
2019-08-16 22:39:41 +00:00
|
|
|
func BenchmarkStream_Write_Ordered(b *testing.B) {
|
2020-04-07 19:59:32 +00:00
|
|
|
hole := connutil.Discard()
|
2020-04-13 14:17:19 +00:00
|
|
|
var sessionKey [32]byte
|
|
|
|
rand.Read(sessionKey[:])
|
2020-04-13 21:48:28 +00:00
|
|
|
|
|
|
|
const testDataLen = 65536
|
|
|
|
testData := make([]byte, testDataLen)
|
2019-07-28 10:58:45 +00:00
|
|
|
rand.Read(testData)
|
2020-04-13 21:48:28 +00:00
|
|
|
eMethods := map[string]byte{
|
2020-10-21 15:42:24 +00:00
|
|
|
"plain": EncryptionMethodPlain,
|
|
|
|
"chacha20-poly1305": EncryptionMethodChaha20Poly1305,
|
|
|
|
"aes-gcm": EncryptionMethodAESGCM,
|
2020-04-13 21:48:28 +00:00
|
|
|
}
|
2019-07-28 10:58:45 +00:00
|
|
|
|
2020-04-13 21:48:28 +00:00
|
|
|
for name, method := range eMethods {
|
|
|
|
b.Run(name, func(b *testing.B) {
|
|
|
|
sesh := setupSesh(false, sessionKey, method)
|
|
|
|
sesh.AddConnection(hole)
|
|
|
|
stream, _ := sesh.OpenStream()
|
|
|
|
b.SetBytes(testDataLen)
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
stream.Write(testData)
|
|
|
|
}
|
|
|
|
})
|
2019-07-28 10:58:45 +00:00
|
|
|
}
|
|
|
|
}
|
2019-08-02 22:23:54 +00:00
|
|
|
|
2019-10-15 20:59:13 +00:00
|
|
|
func TestStream_Write(t *testing.T) {
|
2020-04-07 19:59:32 +00:00
|
|
|
hole := connutil.Discard()
|
2020-04-13 14:17:19 +00:00
|
|
|
var sessionKey [32]byte
|
|
|
|
rand.Read(sessionKey[:])
|
2020-10-21 15:42:24 +00:00
|
|
|
sesh := setupSesh(false, sessionKey, EncryptionMethodPlain)
|
2019-10-15 20:59:13 +00:00
|
|
|
sesh.AddConnection(hole)
|
2020-04-13 14:17:19 +00:00
|
|
|
testData := make([]byte, payloadLen)
|
2019-10-15 20:59:13 +00:00
|
|
|
rand.Read(testData)
|
|
|
|
|
|
|
|
stream, _ := sesh.OpenStream()
|
|
|
|
_, err := stream.Write(testData)
|
|
|
|
if err != nil {
|
|
|
|
t.Error(
|
|
|
|
"For", "stream write",
|
|
|
|
"got", err,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-13 14:17:19 +00:00
|
|
|
func TestStream_WriteSync(t *testing.T) {
|
|
|
|
// Close calls made after write MUST have a higher seq
|
|
|
|
var sessionKey [32]byte
|
|
|
|
rand.Read(sessionKey[:])
|
2020-10-21 15:42:24 +00:00
|
|
|
clientSesh := setupSesh(false, sessionKey, EncryptionMethodPlain)
|
|
|
|
serverSesh := setupSesh(false, sessionKey, EncryptionMethodPlain)
|
2020-04-13 14:17:19 +00:00
|
|
|
w, r := connutil.AsyncPipe()
|
2020-10-17 12:46:22 +00:00
|
|
|
clientSesh.AddConnection(common.NewTLSConn(w))
|
|
|
|
serverSesh.AddConnection(common.NewTLSConn(r))
|
2020-04-13 14:17:19 +00:00
|
|
|
testData := make([]byte, payloadLen)
|
|
|
|
rand.Read(testData)
|
|
|
|
|
|
|
|
t.Run("test single", func(t *testing.T) {
|
|
|
|
go func() {
|
|
|
|
stream, _ := clientSesh.OpenStream()
|
|
|
|
stream.Write(testData)
|
|
|
|
stream.Close()
|
|
|
|
}()
|
|
|
|
|
|
|
|
recvBuf := make([]byte, payloadLen)
|
|
|
|
serverStream, _ := serverSesh.Accept()
|
|
|
|
_, err := io.ReadFull(serverStream, recvBuf)
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("test multiple", func(t *testing.T) {
|
|
|
|
const numStreams = 100
|
|
|
|
for i := 0; i < numStreams; i++ {
|
|
|
|
go func() {
|
|
|
|
stream, _ := clientSesh.OpenStream()
|
|
|
|
stream.Write(testData)
|
|
|
|
stream.Close()
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
for i := 0; i < numStreams; i++ {
|
|
|
|
recvBuf := make([]byte, payloadLen)
|
|
|
|
serverStream, _ := serverSesh.Accept()
|
|
|
|
_, err := io.ReadFull(serverStream, recvBuf)
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-10-15 20:59:13 +00:00
|
|
|
func TestStream_Close(t *testing.T) {
|
2020-04-13 14:17:19 +00:00
|
|
|
var sessionKey [32]byte
|
|
|
|
rand.Read(sessionKey[:])
|
2019-10-15 20:59:13 +00:00
|
|
|
testPayload := []byte{42, 42, 42}
|
|
|
|
|
2020-10-17 18:28:03 +00:00
|
|
|
dataFrame := &Frame{
|
2020-04-08 17:16:54 +00:00
|
|
|
1,
|
2019-10-15 20:59:13 +00:00
|
|
|
0,
|
|
|
|
0,
|
|
|
|
testPayload,
|
|
|
|
}
|
2020-03-16 11:37:09 +00:00
|
|
|
|
2020-10-17 18:28:03 +00:00
|
|
|
t.Run("active closing", func(t *testing.T) {
|
2020-10-21 15:42:24 +00:00
|
|
|
sesh := setupSesh(false, sessionKey, EncryptionMethodPlain)
|
2020-10-17 18:28:03 +00:00
|
|
|
rawConn, rawWritingEnd := connutil.AsyncPipe()
|
|
|
|
sesh.AddConnection(common.NewTLSConn(rawConn))
|
|
|
|
writingEnd := common.NewTLSConn(rawWritingEnd)
|
2019-10-15 20:59:13 +00:00
|
|
|
|
2020-10-17 18:28:03 +00:00
|
|
|
obfsBuf := make([]byte, 512)
|
|
|
|
i, _ := sesh.Obfs(dataFrame, obfsBuf, 0)
|
|
|
|
_, err := writingEnd.Write(obfsBuf[:i])
|
|
|
|
if err != nil {
|
|
|
|
t.Error("failed to write from remote end")
|
|
|
|
}
|
|
|
|
stream, err := sesh.Accept()
|
|
|
|
if err != nil {
|
|
|
|
t.Error("failed to accept stream", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err = stream.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Error("failed to actively close stream", err)
|
|
|
|
return
|
|
|
|
}
|
2020-04-08 17:16:54 +00:00
|
|
|
|
2020-10-17 18:28:03 +00:00
|
|
|
if sI, _ := sesh.streams.Load(stream.(*Stream).id); sI != nil {
|
|
|
|
t.Error("stream still exists")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
readBuf := make([]byte, len(testPayload))
|
|
|
|
_, err = io.ReadFull(stream, readBuf)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("can't read residual data %v", err)
|
|
|
|
}
|
|
|
|
if !bytes.Equal(readBuf, testPayload) {
|
|
|
|
t.Errorf("read wrong data")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("passive closing", func(t *testing.T) {
|
2020-10-21 15:42:24 +00:00
|
|
|
sesh := setupSesh(false, sessionKey, EncryptionMethodPlain)
|
2020-10-17 18:28:03 +00:00
|
|
|
rawConn, rawWritingEnd := connutil.AsyncPipe()
|
|
|
|
sesh.AddConnection(common.NewTLSConn(rawConn))
|
|
|
|
writingEnd := common.NewTLSConn(rawWritingEnd)
|
|
|
|
|
|
|
|
obfsBuf := make([]byte, 512)
|
|
|
|
i, err := sesh.Obfs(dataFrame, obfsBuf, 0)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("failed to obfuscate frame %v", err)
|
|
|
|
}
|
|
|
|
_, err = writingEnd.Write(obfsBuf[:i])
|
|
|
|
if err != nil {
|
|
|
|
t.Error("failed to write from remote end")
|
|
|
|
}
|
|
|
|
|
|
|
|
stream, err := sesh.Accept()
|
|
|
|
if err != nil {
|
|
|
|
t.Error("failed to accept stream", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
closingFrame := &Frame{
|
|
|
|
1,
|
|
|
|
dataFrame.Seq + 1,
|
2020-10-21 15:42:24 +00:00
|
|
|
closingStream,
|
2020-10-17 18:28:03 +00:00
|
|
|
testPayload,
|
|
|
|
}
|
|
|
|
|
|
|
|
i, err = sesh.Obfs(closingFrame, obfsBuf, 0)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("failed to obfuscate frame %v", err)
|
|
|
|
}
|
|
|
|
_, err = writingEnd.Write(obfsBuf[:i])
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("failed to write from remote end %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
closingFrameDup := &Frame{
|
|
|
|
1,
|
|
|
|
dataFrame.Seq + 2,
|
2020-10-21 15:42:24 +00:00
|
|
|
closingStream,
|
2020-10-17 18:28:03 +00:00
|
|
|
testPayload,
|
|
|
|
}
|
|
|
|
|
|
|
|
i, err = sesh.Obfs(closingFrameDup, obfsBuf, 0)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("failed to obfuscate frame %v", err)
|
|
|
|
}
|
|
|
|
_, err = writingEnd.Write(obfsBuf[:i])
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("failed to write from remote end %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
readBuf := make([]byte, len(testPayload))
|
|
|
|
_, err = io.ReadFull(stream, readBuf)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("can't read residual data %v", err)
|
|
|
|
}
|
2020-12-19 19:30:53 +00:00
|
|
|
time.Sleep(eventualConsistencyTolerance)
|
2020-10-17 18:28:03 +00:00
|
|
|
if sI, _ := sesh.streams.Load(stream.(*Stream).id); sI != nil {
|
|
|
|
t.Error("stream still exists")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
2019-10-15 20:59:13 +00:00
|
|
|
}
|
|
|
|
|
2019-08-02 22:23:54 +00:00
|
|
|
func TestStream_Read(t *testing.T) {
|
2020-04-14 13:13:42 +00:00
|
|
|
seshes := map[string]bool{
|
|
|
|
"ordered": false,
|
|
|
|
"unordered": true,
|
|
|
|
}
|
2019-08-14 09:04:27 +00:00
|
|
|
testPayload := []byte{42, 42, 42}
|
2020-04-13 14:17:19 +00:00
|
|
|
const smallPayloadLen = 3
|
2019-08-14 09:04:27 +00:00
|
|
|
|
|
|
|
f := &Frame{
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
testPayload,
|
|
|
|
}
|
|
|
|
|
|
|
|
var streamID uint32
|
|
|
|
buf := make([]byte, 10)
|
|
|
|
|
|
|
|
obfsBuf := make([]byte, 512)
|
2019-08-02 22:23:54 +00:00
|
|
|
|
2020-04-14 13:13:42 +00:00
|
|
|
for name, unordered := range seshes {
|
2020-10-21 15:42:24 +00:00
|
|
|
sesh := setupSesh(unordered, emptyKey, EncryptionMethodPlain)
|
2020-10-17 18:28:03 +00:00
|
|
|
rawConn, rawWritingEnd := connutil.AsyncPipe()
|
|
|
|
sesh.AddConnection(common.NewTLSConn(rawConn))
|
|
|
|
writingEnd := common.NewTLSConn(rawWritingEnd)
|
2020-04-14 13:13:42 +00:00
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
t.Run("Plain read", func(t *testing.T) {
|
|
|
|
f.StreamID = streamID
|
|
|
|
i, _ := sesh.Obfs(f, obfsBuf, 0)
|
|
|
|
streamID++
|
|
|
|
writingEnd.Write(obfsBuf[:i])
|
|
|
|
stream, err := sesh.Accept()
|
|
|
|
if err != nil {
|
|
|
|
t.Error("failed to accept stream", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
i, err = stream.Read(buf)
|
|
|
|
if err != nil {
|
|
|
|
t.Error("failed to read", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if i != smallPayloadLen {
|
|
|
|
t.Errorf("expected read %v, got %v", smallPayloadLen, i)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !bytes.Equal(buf[:i], testPayload) {
|
|
|
|
t.Error("expected", testPayload,
|
|
|
|
"got", buf[:i])
|
|
|
|
return
|
|
|
|
}
|
|
|
|
})
|
|
|
|
t.Run("Nil buf", func(t *testing.T) {
|
|
|
|
f.StreamID = streamID
|
|
|
|
i, _ := sesh.Obfs(f, obfsBuf, 0)
|
|
|
|
streamID++
|
|
|
|
writingEnd.Write(obfsBuf[:i])
|
|
|
|
stream, _ := sesh.Accept()
|
|
|
|
i, err := stream.Read(nil)
|
|
|
|
if i != 0 || err != nil {
|
|
|
|
t.Error("expecting", 0, nil,
|
|
|
|
"got", i, err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
t.Run("Read after stream close", func(t *testing.T) {
|
|
|
|
f.StreamID = streamID
|
|
|
|
i, _ := sesh.Obfs(f, obfsBuf, 0)
|
|
|
|
streamID++
|
|
|
|
writingEnd.Write(obfsBuf[:i])
|
|
|
|
stream, _ := sesh.Accept()
|
|
|
|
stream.Close()
|
|
|
|
i, err := stream.Read(buf)
|
|
|
|
if err != nil {
|
|
|
|
t.Error("failed to read", err)
|
|
|
|
}
|
|
|
|
if i != smallPayloadLen {
|
|
|
|
t.Errorf("expected read %v, got %v", smallPayloadLen, i)
|
|
|
|
}
|
|
|
|
if !bytes.Equal(buf[:i], testPayload) {
|
|
|
|
t.Error("expected", testPayload,
|
|
|
|
"got", buf[:i])
|
|
|
|
}
|
|
|
|
_, err = stream.Read(buf)
|
|
|
|
if err == nil {
|
|
|
|
t.Error("expecting error", ErrBrokenStream,
|
|
|
|
"got nil error")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
t.Run("Read after session close", func(t *testing.T) {
|
|
|
|
f.StreamID = streamID
|
|
|
|
i, _ := sesh.Obfs(f, obfsBuf, 0)
|
|
|
|
streamID++
|
|
|
|
writingEnd.Write(obfsBuf[:i])
|
|
|
|
stream, _ := sesh.Accept()
|
|
|
|
sesh.Close()
|
|
|
|
i, err := stream.Read(buf)
|
|
|
|
if err != nil {
|
|
|
|
t.Error("failed to read", err)
|
|
|
|
}
|
|
|
|
if i != smallPayloadLen {
|
|
|
|
t.Errorf("expected read %v, got %v", smallPayloadLen, i)
|
|
|
|
}
|
|
|
|
if !bytes.Equal(buf[:i], testPayload) {
|
|
|
|
t.Error("expected", testPayload,
|
|
|
|
"got", buf[:i])
|
|
|
|
}
|
|
|
|
_, err = stream.Read(buf)
|
|
|
|
if err == nil {
|
|
|
|
t.Error("expecting error", ErrBrokenStream,
|
|
|
|
"got nil error")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
2019-08-02 22:23:54 +00:00
|
|
|
}
|
2020-04-14 12:31:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestStream_SetWriteToTimeout(t *testing.T) {
|
|
|
|
seshes := map[string]*Session{
|
2020-10-21 15:42:24 +00:00
|
|
|
"ordered": setupSesh(false, emptyKey, EncryptionMethodPlain),
|
|
|
|
"unordered": setupSesh(true, emptyKey, EncryptionMethodPlain),
|
2020-04-14 12:31:24 +00:00
|
|
|
}
|
|
|
|
for name, sesh := range seshes {
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
stream, _ := sesh.OpenStream()
|
|
|
|
stream.SetWriteToTimeout(100 * time.Millisecond)
|
|
|
|
done := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
stream.WriteTo(ioutil.Discard)
|
|
|
|
done <- struct{}{}
|
|
|
|
}()
|
2019-08-02 22:23:54 +00:00
|
|
|
|
2020-04-14 12:31:24 +00:00
|
|
|
select {
|
|
|
|
case <-done:
|
|
|
|
return
|
|
|
|
case <-time.After(500 * time.Millisecond):
|
|
|
|
t.Error("didn't timeout")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStream_SetReadFromTimeout(t *testing.T) {
|
|
|
|
seshes := map[string]*Session{
|
2020-10-21 15:42:24 +00:00
|
|
|
"ordered": setupSesh(false, emptyKey, EncryptionMethodPlain),
|
|
|
|
"unordered": setupSesh(true, emptyKey, EncryptionMethodPlain),
|
2020-04-14 12:31:24 +00:00
|
|
|
}
|
|
|
|
for name, sesh := range seshes {
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
stream, _ := sesh.OpenStream()
|
|
|
|
stream.SetReadFromTimeout(100 * time.Millisecond)
|
|
|
|
done := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
stream.ReadFrom(connutil.Discard())
|
|
|
|
done <- struct{}{}
|
|
|
|
}()
|
|
|
|
select {
|
|
|
|
case <-done:
|
|
|
|
return
|
|
|
|
case <-time.After(500 * time.Millisecond):
|
|
|
|
t.Error("didn't timeout")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2019-08-02 22:23:54 +00:00
|
|
|
}
|