Cloak/internal/multiplex/stream_test.go

405 lines
9.4 KiB
Go
Raw Normal View History

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"
"io/ioutil"
2019-07-28 10:58:45 +00:00
"math/rand"
"testing"
"time"
"github.com/cbeuw/connutil"
2019-07-28 10:58:45 +00:00
)
2020-04-13 14:17:19 +00:00
const payloadLen = 1000
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) {
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) {
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()
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
}
}
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),
}
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
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),
}
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
}